Clarifying the Rule of Three in Refactoring
Do you know the Rule of Three in refactoring? (If not, then read the References section.) More than a quarter century after this heuristic received its name, it continues to confuse and divide programmers. Most notably, some programmers swear by it while others (and I count myself as one) take a more relaxed attitude towards it. This lies at the center of the ongoing debate between whether to favor removing duplication or to wait before extracting an abstraction, lest it become unhelpful.
So what’s up with that?
I think of the Rule of Three as a way to stop Advanced Beginners1 from greedily removing all duplication without considering for a moment whether that duplication is accidental or essential. We could argue about the need for mitigating this particular risk, but the rule seems to try to balance learning and performance. When the programmer is primarily learning about how to design by refactoring, rather than making choices up front, they might remove all duplication greedily in order to use regret as a tool for sharpening their judgment; when the programmer is primarily performing refactoring as a technique for guiding designs to evolve, they might delay removing duplication in order to avoid rework when later they reintroduce duplication that they regretted removing. With not too much effort, you could likely find many articles whose recommendations optimize for the performing case, but if the programmer routinely denies themself the chance to go overboard, then how will they learn?
Even beyond this question, I don’t mind removing duplication more aggressively, primarily because I don’t mind inlining when I regret my choice. I genuinely don’t know what stops other practitioners from making peace with undoing some of their refactorings once they experience significant regret. I seem to recall, in the early days of learning this stuff, plenty of advice about mentally preparing to do a lot of concern-inducing extracting, inlining, extracting, inlining, extracting, inlining as part of the learning process. And not to panic, but to let the learning happen. How else could it be?
What Now?!
Should you follow the Rule of Three? I don’t know. Instead, I feel pretty confident about these statements:
- Removing duplication aggressively, even “too aggressively”, plays a role in developing design judgment.
- Removing duplication leads to finding new, helpful abstractions that might have been hiding in the design.
- If you feel more comfortable inlining, then you don’t have to worry so much about whether you’ll regret extracting (and in particular, removing duplication).
If you prefer strongly (always?) to wait until you see three copies before removing duplication, then I encourage you to prefer this for reasons more helpful than some vague fear of “doing it wrong”.
References
Wikipedia, “Rule of three (computer programming)”. A brief introduction, then pointers to similar concepts.
Wikipedia, “No Silver Bullet”. About Fred Brooks’s essay in which he prominently uses the terms “essential” and “accidental” to distinguish two kinds of cost in software design.
J. B. Rainsberger, “Putting an Age-Old Battle to Rest”. An overview of the Simple Design Dynamo, which describes how removing duplication and improving names form a virtuous cycle that you could use to guide the evolution of your software designs with confidence.
J. B. Rainsberger, “7 minutes, 26 seconds, and the Fundamental Theorem of Agile Software Development”. A lightning talk centered on the importance of refactoring in addressing the risks inherent in forecasting the cost of software projects. Some light beat poetry.
Martin Fowler, Refactoring: Improving the Design of Existing Code. The classic text on refactoring, in which Fowler describes the Rule of Three.
-
This term refers to the Dreyfus Model of Skill Acquisition. An Advanced Beginner is just beginning to feel comfortable following a new set of steps and is starting to form value judgments about what they are doing.↩︎
Comments