Abstraction or Leverage?

A colleague of mine recently accused me of speaking too precisely. He was giving me a bit of grief, but I took it as a compliment. In this industry, we torture words until they give up all meaning. Keep your eyes open for the next time you read the word "significant." Does it actually refer to something that signifies? Or is it just salt, sprinkled in for flavor?

One of the words I see overused is "abstraction." I hear developers use it when they talk about the DRY principle and reducing common code. Reducing common code to eliminate repetition improves your leverage, but not necessarily abstraction. Leverage allows you to make a change in one place that affects all uses.

As an example, think about one of my favorite code-eating patterns in OO: the Null Object pattern. It concentrates a lot of "if not null" code into one place, which means you can change the logic in that one place. That's leverage: you can make a small changes with large-scale effects.

"Extract method" and "extract superclass" transformations fall into this camp. They add leverage, which is useful.

By drawing this distinction, I'm not criticizing leverage. It's useful and important. But there's an additional thing to consider. When you extract common code to gain leverage, you have little ability to ignore details. Indeed, the specific case is often tightly coupled to the implementation of the general case.

(I will admit that when I distill code down this way, it sometimes clears away the cobwebs enough to see the abstraction that was lurking in the corner. That's always a lovely feeling. So leverage can lead to abstraction.)

The essence of abstraction is cleaving. Abstraction separates a manifold thing into independent concepts. By separating the concepts, it also allows us to see connections between things that share one of those concepts. So abstraction also illuminates connections between things.

For example, consider Clojure's seq (pronounced "seek") abstraction. It calls out the concept "Sequential Iteration," thus bringing together everything that acts like an ordered sequence that can be walked. At the same time, it separates that concept from others. For a vector, seq separates that concept of "Sequential Iteration" from the concepts of "Random Access," "Stacklike Object," and "Lazy Evaluation." For a string, seq separates "Sequential Iteration" from "Unicode Storage" and "Regex Matching." That's the part about separating a manifold thing. At the same time, "seq" shows us that there is an important characteristic shared across all those implementations.

In each case, the abstraction allows us to emphasize a particular aspect of the thing while disregarding the rest. This is the dual nature of abstraction: it obscures and reveals.

As it happens, good abstractions also provide leverage, because they separate concerns. I think this is why the words get conflated. All abstractions offer leverage, but not all leverage is an abstraction.

As you work on your designs, think about where you can split those conceptual atoms. What ideas can you separate? How can you then reconnect the shared concepts?

Get In Touch