How DRY Gets Misapplied
Premature Merging
Code that looks similar isn't always conceptually related. Merging unrelated code couples things that should evolve independently.
Tight Coupling
Shared helpers create invisible dependencies. Change one thing, break three others. This is how simple code turns into a flag‑driven maze.
Indirection
Every abstraction adds a layer of cognitive load. Too many layers make code impossible to trace for newcomers.
The Reality: Duplication is cheaper than the wrong abstraction. It is easier to fix duplication later than to dismantle a complex, incorrect abstraction.
Visual Similarity ≠ Conceptual Similarity
Code that looks the same is not necessarily the same thing.
Do these pieces change for the same reason?
Abstraction may be justified
Abstraction creates coupling debt
Duplication vs Wrong Abstraction
Duplication Costs
- A few extra lines
- Maybe an extra test
- A possible refactor later
Wrong Abstraction Costs
- Cognitive overhead forever
- Fear-driven changes
- Hidden dependencies
- "Action at a distance" bugs
Duplication is cheaper than the wrong abstraction.
A Practical Smell Test
Be cautious when an abstraction:
Requires boolean flags or "mode" parameters
Uses conditionals to support multiple cases
Can't be named without "and/or/depending on"
Breaks unrelated behavior when changed
These are signs of merged concepts.
Know When to Duplicate vs Abstract
When Duplication Is Right
- Behaviors are likely to diverge
- Readability is more important than reuse
- Extracting would introduce flags
- You're still learning the problem space
Clarity today beats hypothetical reuse tomorrow.
When to Abstract Safely
- The same rule is repeated
- Changes must happen together
- The shared concept has a clear name
- No branching or mode-switching is required
Good abstraction removes complexity — it doesn't relocate it.
Better Heuristics
AHA
Avoid Hasty Abstractions. Wait until you fully understand the pattern before you abstract.
WET
Write Everything Twice. It's okay to copy-paste. Abstract only on the third time (or fourth).
YAGNI
You Aren't Gonna Need It. Don't build for hypothetical futures. Build for today.
Same Reason to Change
Only extract when changes should happen together. If two pieces of code change for different reasons, keep them separate.
A Useful Compromise Pattern
Instead of one "do-everything" function:
✓ Do This
- Keep separate, explicit functions
- Share only the smallest true helper
- Prefer composition over configuration
- Use strategy maps instead of conditionals
✗ Avoid This
- One giant function with many parameters
- Boolean flags that change behavior
- Deep if/else or switch statements
- "Universal" abstractions that do everything
This preserves clarity without duplication of knowledge.
How to Use This in PR Reviews
Instead of saying:
"This should be DRY."
Try asking:
- "Do these change for the same reason?"
- "Does this abstraction reduce or increase complexity?"
- "What happens when one case evolves independently?"
This reframes the discussion away from dogma.
Resources
These talks and essays explore the cost of unnecessary abstraction and hidden complexity.