For most problems, we have a choice: implement current needs only, or generalize the solution to more easily handle variations we expect to need in the future. The evolutionary design community refers to the latter approach as speculative generality, and we’ll see why it is often a problem.
Examples
- The developer implemented a fancy data structure and algorithm to provide a sorted list – when the language library already had one, and it was unlikely to be a bottleneck.
- A developer took three extra days to modify a grid object to support multi-select. (It was an unneeded feature, and the extra code caused problems in regular selection.)
- The team consistently used “interface + abstract class + concrete class” for every class – “we might want to extend it someday”.
The Dilemma
Sometimes, generalization is obviously cheaper. Nobody objects to a better deal.
The problem comes when it’s “spend more now to save more later.” Is it a good deal?
Let’s consider “x” to be the current needs, and “y” to be the extra capabilities from generalization.
We’re looking at:
There are several possibilities.
1. Generalization Wasn’t Needed
If we never need the generalization, the extra time spent to get “y” is waste:
2. Wrong About Generalization Being Cheaper
We may be wrong about what’s cheaper – it may be cheaper to do “x then y” than “x + y”.
It is faster to make a four-inch mirror and then a six-inch mirror than to make a six-inch mirror
— Bill McKeeman
How can this be? Well, solving “x+y” is solving a bigger problem than solving “x”, and challenges may grow non-linearly. We may learn enough doing “x” that “y” becomes easier.
3. Oops, We Need “z”
We generalized to solve “x + y”, but it turns out “y” isn’t needed after all – we need “z” instead.
It’s a double whammy: we didn’t get the expected savings, and we had to pay to rip out “y”.
4. Yay! We Guessed Right! But…
We guessed right! “x+y” really was cheaper to build than “x then y”.
However, the surface savings only tell part of the story.
A. We spent the time to support “y”, at the cost of delaying more important stories. If we consistently make that tradeoff and spend 1/3 more to get the “future” functionality, we’re spending 25% of our time on (speculated) future rather than known current needs.
B. It’s slower to work in a bigger system, so we’ve slowed down current work.
C. Feature “x” is needed, and is under pressure to evolve (via refactoring). But the “y” part is not currently used or needed, so defects are less likely to be noticed, and its “future-oriented” design will tend toward decay.
Even when we win, we can lose.
Speculative Generality
Speculative generality is guessing about the future, subject to the challenges that brings.
With software, there’s little call to repeat projects: “Let’s rewrite this in the same language, with the same design, using the same tools, and targeting exactly the same platform!” Instead, we work in a changing world.
Years ago, Capers Jones found that requirements change > 1.5% / month (about 30%/year). I can’t believe that rate has slowed down!
So how does evolutionary design address the need for generality? “Code is easiest to extend when it’s well-factored with a simple design.” [Not sure where I heard it first.] If we create as simple a design as will work, and continually refactor as we change code, we usually find it’s easy to extend when new capabilities are needed.
We’re still placing a bet, but we’re betting the other way: save time and money on all the wrong guesses, so we have spare capacity to work on the challenging cases of today.
References
Fowler, Martin, et al. Refactoring: Improving the Design of Existing Code, Addison-Wesley, 1999. [Where I first saw the term “Speculative Generality” in print]