What is premature abstraction (also called speculative generality), how does it relate to YAGNI, and how do you decide when an abstraction has earned its place?
answer
- interface with one implementation
- YAGNI: evidence, not foresight
- Rule of Three, not Rule of One
- wrong axis of variation costs twice
- exception: sticky decisions (schema, public API)
basics
~20 sPremature abstraction is adding interfaces, factories or configuration for requirements you only imagine. YAGNI — "You Aren't Gonna Need It" — says don't build it until a real need exists, because guessed abstractions usually fit the wrong axis and still cost reading and change effort.
solid answer
~50 sSpeculative generality is code built for a hypothetical future: an interface with one implementation, a plugin hook nobody plugs into, parameters no caller varies, an abstract base class "for later". It looks free but costs real money — extra indirection to read through, wider APIs to keep compatible, harder refactoring because the abstraction shape must be preserved, and false confidence that variation is handled. The deeper problem is that abstractions encode a guess about *which axis will vary*; when the real requirement arrives it usually varies along a different axis, so you pay twice: once to build the wrong seam and once to remove it. YAGNI is the discipline of deferring until evidence arrives. Practical tests for "earned": the Rule of Three (abstract on the third real duplication, not the second), a second real implementation exists today, or the seam is required for testing or an already-committed roadmap item. Concrete duplication is cheaper to fix than the wrong abstraction.
go deeper
Define it as building for imagined needs, cite YAGNI, and give the classic example: an interface with a single implementation.
Add the concrete costs (indirection, compatibility, untested paths) and the Rule of Three, and mention testability as a legitimate present-day reason for a seam.
Argue the 'wrong axis of variation' point, distinguish semantic from incidental duplication, and describe how to inline/collapse speculative abstractions safely.
Frame it as reversibility economics: defer cheap-to-reverse decisions, invest analysis in sticky ones (schemas, public contracts, security boundaries), and set team norms and review heuristics that prevent home-grown frameworks.
## Definitions - **Abstraction** — a name and interface that hides how something is done so callers depend on *what* rather than *how* (an interface, a base class, a generic type parameter, a configuration option, a plugin hook). - **Premature / speculative abstraction (speculative generality)** — introducing that machinery before any real requirement demands it, based on "we might need it later". - **YAGNI (You Aren't Gonna Need It)** — an Extreme Programming rule: implement things when you actually need them, never when you merely foresee needing them. - **Rule of Three** — tolerate duplication twice; on the third genuine occurrence, extract the shared abstraction, because by then you have enough samples to see the real variation. ## What it looks like in the wild 1. `interface PaymentProvider` with exactly one implementation, `StripePaymentProvider`, and no second one on the roadmap. 2. A factory or builder whose only job is to return `new TheOneThing()`. 3. A `strategy` parameter every caller passes the same value for. 4. Configuration keys that have never been changed from their default in any environment. 5. Deep inheritance hierarchies with abstract methods that have a single override. 6. Generic "framework" code inside a product repo — a home-grown ORM, event bus, or rules engine written before there were three rules. 7. Extension points, versioned envelopes, and hooks in an internal API that has exactly one internal consumer. ## Why it hurts (it is not "free optionality") - **Reading cost, paid forever.** Every reader must follow the indirection to find the single real implementation. Navigation, debugging and stack traces all get longer. - **The wrong seam.** An abstraction fixes *which dimension varies*. If you guessed "payment provider varies" but reality brings "currency and settlement timing vary", the interface obstructs rather than helps. Removing an abstraction is harder than adding one, because callers now depend on its shape. - **Compatibility drag.** Public extension points become contracts you must not break, especially across teams or releases. - **False safety.** People assume the variation point works because it exists, but code paths that never ran with a second implementation are untested by construction (leaky abstraction discovered on first real use). - **Opportunity cost and inventory.** Time spent on imagined needs is time not spent on known ones; the speculative code is unvalidated inventory that may be deleted unread. - **It attracts more of itself.** A speculative framework invites speculative plugins. ## The counter-force: don't swing to the other extreme YAGNI is about *speculative* work, not about skipping design. Some things are genuinely expensive to retrofit and deserve early thought: - Data model and persisted formats (migrations are costly; schemas are sticky). - Public/external API shapes and wire protocols (consumers you cannot edit). - Security and privacy boundaries, tenancy and identity model. - Anything with a legally or operationally irreversible commitment. The useful framing is **reversibility**: cheap-to-reverse decisions should be deferred and decided by evidence; hard-to-reverse decisions warrant up-front analysis. Note also that *testability* is a present-day need, not speculation: introducing an interface so you can substitute a fake clock or a fake gateway is justified today, not "for later". ## Decision checklist — has this abstraction earned its place? Ask, in order: 1. **Does a second real implementation exist right now**, or is one committed on a dated roadmap? 2. **Is it required to test something** you cannot otherwise test (network, clock, randomness)? 3. **Have I seen three genuine duplications** of the same behavior (Rule of Three) — not merely three pieces of code that look alike but change for different reasons? 4. **Is the duplication semantic or incidental?** Two blocks with identical text that belong to different concepts should stay duplicated; coupling them creates change ripples. 5. **Is it cheap to add later?** If yes, wait. If the decision is sticky (schema, external API), think now. If the honest answer is "no" across the board, write the concrete code. ## Unwinding existing speculative generality - **Inline** the single-implementation interface back into the concrete class (most IDEs automate this). - **Collapse hierarchy**: merge an abstract base with its lone subclass. - **Remove dead parameters and flags**: delete options no caller varies; delete configuration never overridden. - **Delete unused hooks/extension points**; version control remembers them if you are wrong. - **Sequence it**: remove leaves first (unused hooks), then flags, then interfaces, keeping each step behavior-preserving and covered by tests. The rule of thumb worth memorizing: prefer duplication over the wrong abstraction, and let the third real example — not the first imagined one — tell you where the seam belongs.
- Isn't extracting an interface always good practice for decoupling?No. An interface only decouples if something genuinely varies. With one implementation it adds indirection without options, and it fixes a guessed axis of variation. Justified reasons today: a real second implementation, a test seam, or a published boundary between teams.
- Where does YAGNI *not* apply?Where decisions are expensive to reverse: persisted data models and migrations, external/public API and wire formats, security, privacy and tenancy boundaries. Deferring there is not thrift, it is deferred debt at a much higher interest rate.
- How do you tell real duplication from coincidental duplication?Ask whether the copies will change for the same reason. Same reason means one concept — extract. Different reasons means they merely look alike; coupling them creates a shared abstraction that both sides fight, which is worse than the duplication.
Buying a house with four extra rooms because you might have guests. You pay the mortgage and cleaning every month, and when family actually arrives they need a ground-floor bedroom you didn't build.
saying these in an interview costs you the question
- "Extracting an interface is always good decoupling" — with one implementation it is indirection without options
- Treating YAGNI as an excuse to skip design of hard-to-reverse things (schemas, public APIs, security)
- Extracting on the first look-alike rather than the third same-reason duplication
- Assuming an unused extension point works — it has never executed with a second implementation
- Building an in-house framework before there are three concrete users of it