/ engineering ,patterns

Against premature abstraction

Three similar lines of code are better than a premature abstraction. On knowing when not to DRY.

The instinct to abstract is strong. You see two pieces of code that look similar, and your fingers itch to extract a function. Three similar blocks? Surely that’s a pattern worth naming.

But abstraction has a cost that duplication doesn’t: coupling.

When duplication wins

Duplicated code can evolve independently. If requirement A changes but requirement B doesn’t, you change one block and leave the other untouched. With an abstraction, you’re modifying shared code — and every caller is now a stakeholder.

The rule I follow: wait until you have three concrete use cases before abstracting. And even then, only if the shared logic is truly the same — not just superficially similar.

The abstraction test

Ask yourself: if I delete this abstraction and inline the code at every call site, does anything get worse? If the answer is “it gets slightly more repetitive but much clearer,” you have your answer.

A note on naming

If you can’t name the abstraction without using words like “util,” “helper,” “common,” or “shared,” it’s not a real abstraction. It’s a filing cabinet.

Good abstractions have names that describe what they do, not where they live.