Code duplication is far cheaper than the wrong abstraction (2016)
The Perils of the Wrong Abstraction
Originally shared via the Chainline Newsletter and later published on her blog, Sandi Metz explores a counterintuitive truth in software engineering: duplication is far cheaper than the wrong abstraction.
During her 2014 RailsConf presentation, Metz asserted that developers should prefer duplication over the wrong abstraction. This claim sparked a polarized reaction—some thought she had lost her mind, while others felt it was a profound truth. This divide highlights how systemic the "wrong abstraction" problem is in the industry.
The Anatomy of a Failing Abstraction
Metz identifies a recurring pattern in how code degrades. This cycle typically involves three different developers over time:
The Psychological Trap
When you (Programmer C) encounter this code, you face a mental hurdle. Because the code is so complex, you assume it must be necessary. This is a classic case of the Sunk Cost Fallacy.
"Goodness, that's so confusing, it must have taken ages to get right. It would be a sin to let all that effort go to waste."
We tend to equate . In reality, the deeper the investment in a confusing piece of code, the more pressure we feel to keep it, even when it hinders progress.
How to Recover: The Path Forward is Backward
If you find yourself trying to force a new feature into a conditional-heavy shared method, you are dealing with a wrong abstraction. The fastest way to fix this is to rewind your decisions.
The Recovery Process
To break the cycle, follow these steps:
- Inline the code: Take the logic out of the abstraction and put it back into every single caller.
- Specialize: In each caller, use the existing parameters to identify which parts of the code are actually used.
- Prune: Delete the logic that isn't required for that specific caller.
By doing this, you remove both the abstraction and the if/else conditionals, leaving each caller with only the code it truly needs.
Example: From Abstraction to Duplication
Instead of a complex shared method:
# The Wrong Abstraction
def process_data(type, data)
if type == :user
# user logic
elsif type == :admin
# admin logic + extra stuff
end
end
You move back to:
# Caller 1 (User)
# Only the user logic remains here
# Caller 2 (Admin)
# Only the admin logic remains here
Once the code is duplicated, the actual commonalities become obvious. You can then re-extract a correct abstraction based on current requirements rather than legacy assumptions.
Summary Comparison
| Approach | Mindset | Result |
|---|---|---|
| Wrong Abstraction | "I must preserve the investment in this code." | Increasing complexity, slower feature delivery. |
| Strategic Duplication | "This code served us, but we've learned more now." | Clarity, agility, and easier refactoring. |
The Warning Sign: If you are constantly adding parameters and conditional paths to shared code, the abstraction is no longer correct. It may have been right once, but that time has passed.
Further Reading: 99 Bottles of OOP
For those interested in deeper object-oriented design, Sandi Metz offers 99 Bottles of OOP. The 2nd Edition is approximately the length of the first, adding three new chapters.
Because the book focuses on general design rather than a specific syntax, it is available in multiple versions. The combinations are as follows:
- Languages: Ruby, JavaScript, PHP (3 options)
- Beverages: Beer, Milk (2 options)
- Formats: (4 options)
This creates a total of unique download possibilities, though the core content remains the same.
