The Case Against Premature Abstraction
Every senior engineer has deleted more abstraction than they've written. Here's why — and how to recognize the pattern before it costs you six months of architectural debt.
The most expensive code I've ever maintained was written by someone who was trying to be clever. Not malicious, not lazy — clever. They saw a pattern forming across three endpoints and immediately extracted it into a generic, configurable, extensible abstraction.
Eighteen months later, the abstraction was wrong. The three endpoints had diverged. The generic solution had seventeen configuration options, eleven of which existed only to handle exceptions that violated the original pattern. Removing it took two weeks.
Why It Happens
Premature abstraction is driven by a reasonable intuition: duplication is bad, patterns should be encoded, future requirements should be anticipated. The problem is that all three of these principles require accurate knowledge of the future — and the future is wrong.
Business requirements change. The three endpoints that looked identical at sprint one look very different at sprint eight. The abstraction you built for the common case becomes the obstacle to implementing the exception case.
The Rule
The rule I follow is simple: do not abstract until you have three real, production instances of the same pattern, and until you are confident the pattern is stable enough to encode. Two instances is coincidence. Three might be a pattern. But only extract when the cost of duplication clearly exceeds the cost of the abstraction itself.
Three similar lines of code is not a problem. A premature abstraction is.
What Good Abstraction Looks Like
Good abstractions are narrow, stable, and focused on a single responsibility. They encode something that is genuinely invariant across their call sites, not something that is merely similar-looking. They make their assumptions explicit. And they are easy to delete when the assumptions turn out to be wrong.
The best codebase I've ever worked in had very few abstractions — and all of them were load-bearing.