When we are introduced to programming, we are often exposed to the DRY programming principle. DRY appears to make our code simpler, easier to read and maintain. However, we have most likely misunderstood it from the beginning.

As developers, we should strive toward writing better code, so when we first heard about DRY our thought process was likely something like: “Let’s jump right in and refactor all our duplications with abstraction.”

DRY seems to deliver on all its promises until it doesn’t. Our abstraction is often wrong.

I have made this mistake myself. I’m doing some code that has some duplication of some logic from another place. I immediately think that I need to make an abstraction to avoid this duplication. I’m proud of the code that I did, because it’s cleaner and there are fewer lines of code. As time passes, new requirements pop up and we have to implement those. We just need to add an extra parameter to our abstraction, add a quick if else and the job is done.

What is the problem with this solution?

Instead of copying the abstraction which fits our new requirements perfectly, we’ve added an abstraction that behaves differently based on the input it receives. This may be fine for 1-2 more requirements but, sooner or later, you have an abstraction that handles too many different cases which will be hard to change and maintain. This is now an incorrect abstraction.

By copying the abstraction and adjust it to the specific requirement, you get an abstraction that has one purpose. It’s easy to understand and no one is afraid of changing it. This way, the abstraction is clear and it’s easy to change.

So, what can you do to avoid wrong abstractions?

Some people will suggest WET (Write Everything Twice) which is the opposite principle to DRY. You don’t know all future requirements, so we are inclined to create wrong abstractions. However, I don’t think writing the code twice is enough to warrant an abstraction. You can easily end up with wrong abstractions because you abstracted the wrong duplications.

Not all duplications are equal.

What do I mean by that?

There’s a difference between domain logic and common logic.

It may be easier to create an abstraction for common logic because it often does just one thing. For example, if you want to calculate the percentage between two numbers, it’s easy to create an abstraction that does just that.

When you want to create an abstraction for domain logic, I think you have to at least write that code three times before considering an abstraction. If you have domain logic that is duplicated that many times, you might have a better understanding of the future requirements. If you don’t, simply duplicate the code once more.

Duplication is far cheaper than the wrong abstraction.

If you already have wrong abstractions, all hope is not lost. The fastest way forward is usually to go back. Try to remove unused parameters, logic etc. At some point the abstraction might be redundant because all of the code using it has been improved.

Adding conditional statements to shared code should always feel wrong. If it does, your abstraction is probably wrong.