I've seen this mistake many times, and I've made it myself. When you first read about the DRY programming concept, you probably misunderstood it.
What was going on in your head was this:
Wikipedia: DRY stands for not repeating the same code twice.
You: Hmm, ok I'll replace all my duplications with abstraction.
And it seems like a good solution, but it's not. Your abstraction is often wrong.
Here is why:
- You see duplication.
- You extract duplication into a new abstraction (method, class).
- You replace the duplication with the new abstraction.
- You think your code is perfect.
- Time passes.
- The product manager has new requirements. Your abstraction is almost perfect for them.
- You start to implement the new requirements.
- And here's the little but:
Your abstraction is almost perfect. Why? The new requirements affect only 95% of the old code you extracted into abstraction. The other 5% is not affected. And instead of creating a new abstraction with 95% copied code from the current one, you decide to change the code of your abstraction.
You add a conditional statement,
if..elsefor example, and pass a parameter, so your abstraction can perform different actions for different decisions.
- Now your abstraction behaves differently for different cases.
- Another new requirement arrives. Another additional parameter. Another new conditional. (Loop until code becomes very difficult to understand and maintain.)
- Congrats, you've created a wrong abstraction.
The code no longer represents a single, common abstraction. It becomes a condition-laden procedure. It's hard to understand and easy to break. Adding new features is incredibly hard and every new feature complicates the code even more.
It's an infinite loop.
So what to do?
Write Everything Twice.
WET (Write Everything Twice) is the opposite concept to DRY. When you start to develop a new system you don't know all the future requirements. So don't rush into abstractions.
You need to remember: Duplication is far cheaper than the wrong abstraction.
For example, you are building a web app and don't have a UI design for all pages right now. But you also have many buttons, and they're similar on every page. You decide to move them to a component called
Button and reuse it on every page. Seems logical.
"Hey, hey!" the new page design came from the product manager. You look at it and find a new, fancy button at the bottom of the page.
It looks like the old buttons, but it has this "one little thing." To implement it, you need to rewrite 10% of your
Button component, and add conditional statements and new parameters.
Now you have a dilemma:
Button. Rewrite 10% of the abstraction code (add logical conditions to support new fancy button logic).
- Create two abstractions:
Button. Copy 90% code from
I know you want to choose the first option. You think that you can handle it. You're not going to build the wrong abstraction.
But the sad truth is, you will (except if you are an experienced programmer and know what you're doing).
Copy that code. Don't be afraid.
After a while, you will know what your buttons will look like in the future. Then you can analyze the current codebase, find duplicated code in the button components, and move them to the good abstraction.
If you find that it's too late to deal with a wrong abstraction, the fastest way to move forward is back.
Do the following:
- Move the abstracted code back.
- Delete the unused parameter that's passed to the abstraction to perform different actions for different decisions.
- Delete unused logic.
This removes the abstraction and conditional statements for each caller.
If you find yourself passing parameters and adding conditional statements through shared code, your abstraction is wrong.
Prefer duplication over the wrong abstraction.
🔴 If you like this article share it with your friends and follow me on Twitter
🔴 Get more coding tips, job interview advice, and the latest tech news 👉 Join my Newsletter