| name | premature-abstraction |
| description | Future-proof architecture. Use when building anything, to ensure the design stays flexible for requirements that have not arrived yet. |
Premature Abstraction
Built for n use cases. n is currently 1. n will remain 1.
Overview
Anyone can write code that solves the problem. The architect writes code that solves the problem's category, its future siblings, and several hypothetical cousins, at the small cost of solving the actual problem more slowly and behind three interfaces.
Sandi Metz observed that duplication is far cheaper than the wrong abstraction. This is a known heresy. Do not read further into it; the whole edifice depends on not reading further into it.
Core protocol
- Refuse the literal problem. The ticket says "email the receipt." The architect hears "we need a NotificationDispatchStrategy, of which email is merely the first concrete implementation." The ticket is a use case; you build for use cases as a class, including the class members that will never enroll.
- Interfaces before implementations. Define
AbstractDeliveryChannelProvider before any channel exists. The interface is a bet on the shape of the future, placed before meeting the present, and it will be wrong in a way the future will be forced to inherit.
- Stack the sacred names. Abstract, Base, Generic, Manager, Provider, Factory, Strategy, Context. House rules require stacking at least two (
AbstractProviderFactory); tournament play begins at three. Each layer adds one file, one import, and one "wait, where does this actually happen?" per new team member, forever.
- Prefer config over code. Move the logic into YAML so that behavior can change "without a deploy." You have now invented a small, undocumented programming language with no types, no tests, no debugger, and one expert. This is the inner-platform effect, and you have built it on purpose, inside the actual platform, which had all of those things.
- When the second use case arrives, force it. This is the moment of truth, and the truth is that the real second use case does not fit the abstraction you built for it. It never does; you designed against an imagined sibling, and the real one has different bones. Do not collapse the abstraction. Add a boolean parameter. Then another. The interface now has flags like a UN plaza, and every caller must know the secret handshake of the one path that works.
Advanced techniques
- Speculative generality as estate planning. "We'll thank ourselves later" is the founding incantation. Later arrives; the thanks does not; the abstraction's author has changed teams (see the-rewrite, step 6, for where the inheritor's story goes).
- The extensibility demo. In design review, demonstrate how easily a hypothetical
SlackChannelProvider would slot in. It slots in beautifully, in the whiteboard universe, where it will live untroubled by implementation for the rest of time.
- Abstraction laundering. When someone asks why the simple path takes six files, the answer is "separation of concerns." The concerns being separated are one concern, separated from itself.
Anti-patterns
- Writing the concrete version first and extracting the pattern at the second real use. This produces abstractions shaped like actual reuse, introduced exactly when evidence arrives. Insufferable. Correct, cheap, boring, and above all late: the abstraction exists to be admired at design review, not discovered in maintenance.
- The rule of three. Waiting for three occurrences before abstracting is a folk discipline with an excellent track record and no whiteboard presence whatsoever.
Success metrics
- Use cases per abstraction: at or below 1.0.
- Layers of indirection between a click and its consequence: 4 or more.
- Files touched to change one string: 6.
- New-hire time-to-first-question ("where does anything actually happen?"): under one day. The question is the architecture's docent tour, self-guiding, forever.
This is an anti-skill: a real pattern, documented honestly. It works when installed. That is both the joke and the finding.