DEV Community

Cover image for When DRY Doesn't Work, Go WET
Nick Bull
Nick Bull

Posted on • Updated on • Originally published at Medium

When DRY Doesn't Work, Go WET

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:

  1. You see duplication.
  2. You extract duplication into a new abstraction (method, class).
  3. You replace the duplication with the new abstraction.
  4. You think your code is perfect.
  5. Time passes.
  6. The product manager has new requirements. Your abstraction is almost perfect for them.
  7. You start to implement the new requirements.
  8. 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..else for example, and pass a parameter, so your abstraction can perform different actions for different decisions.
  9. Now your abstraction behaves differently for different cases.
  10. Another new requirement arrives. Another additional parameter. Another new conditional. (Loop until code becomes very difficult to understand and maintain.)
  11. 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:

  1. Change Button. Rewrite 10% of the abstraction code (add logical conditions to support new fancy button logic).
  2. Create two abstractions: FancyButton and Button. Copy 90% code from Button to FancyButton.

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 It's Too Late

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:

  1. Move the abstracted code back.
  2. Delete the unused parameter that's passed to the abstraction to perform different actions for different decisions.
  3. Delete unused logic.

This removes the abstraction and conditional statements for each caller.

In the end…

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

Top comments (39)

pentacular profile image

The critical insight is that repetition isn't repetition when two things that mean different things happen to look the same.

In this case DRY becomes the process of introducing fragile dependencies that will break tomorrow.

macsikora profile image
Pragmatic Maciej • Edited

People say abstraction needs to see duplication, I would say abstraction needs to see how the dublication changes, as if the separated parts change together probably they can be considered as a one thing, if not it's not duplication anymore.

athomsfere profile image
Austin French

I really hope WET isn't really preached...

If you're having problems with DRY, it's probably because of a violation of something else; SOLID perhaps.

alainvanhout profile image
Alain Van Hout • Edited

It is 'preached', because the WET approach is what came up when people started getting into problems with DRY. Just like OOP came up when people started to get into problems with procedural code, and that procedural coding came up when people got into problems with unstructured code. These solutions themselves aren't perfect, but they improve upon their predecessor, based on hard earned practical experience.

stereoplegic profile image
Mike Bybee • Edited

Just so, @alainvanhout .

And what if you're having problems with SOLID? How far down this rabbit hole do you go to assign blame and find the violation?

And how long do you spend on that (rather than writing code that works, having something actually completed that you can step back and refactor)?

Should you learn and adhere to these principles as best as possible? Absolutely. Should they get in the way of getting actual work done? Never.

I often give teams a similar appraisal of PM methodologies: "They're nothing but sets of tools. Use the ones that make your job easier, and don't obsess over those which do not. If you're following a methodology like religious dogma, then you've missed the point entirely."

Thread Thread
alainvanhout profile image
Alain Van Hout

Indeed. When dealing with best practices, I try to always come back to and emphasize the 'why' behind them. If the why doesn't (fully) apply, then there's a good chance the best practice also doesn't (fully) apply. Usually both do apply, but not always.

Thread Thread
stereoplegic profile image
Mike Bybee

Why? Because it's a BEST PRACTICE™. 🙃

Thread Thread
stereoplegic profile image
Mike Bybee

I know there's a perfectly relevant YouTube spoof of best practices out there, but I can't remember who posted it.

Thread Thread
t15k profile image
Tonny Staunsbrink

I would love to see that video. Any hint on how to find it would be much appreciated 🙂.

Thread Thread
stereoplegic profile image
Mike Bybee

Not purely spoof, but I think Ben Awad's video is what I was thinking of.

poisonousjohn profile image
Ivan Fateev

Yes, the example violates the single responsibility principle. If some code abstraction is doing some branching depending on some boolean flag, it's the first sign of a single responsibility principle violation.

So in this case, when you've got new requirements and understood that you need to do different things, just split the code up for different entities (components, whatever you call them). Each should do just a single thing.

Branching should be done outside of those abstractions, and should be clear and explicit.

Otherwise, later, when you read the code, you will need to go deep down to the abstractions level just ot understand that the flag A causes N branching cases.

xanderyzwich profile image
Corey McCarty

If you keep abstracting the code out then you will have a bunch of very small methods. The new definition of 'fancy button' or whatever can inherit from 'button' and override the bit that is different.

alainvanhout profile image
Alain Van Hout

If you have a third level branch with a certain feature, and then you need that same feature in a very distant other 4 level branch, then inheritance will not save you. There's a reason why the main lessen of OOP design patterns (the gang of four book in particular) is ' favour composition over inheritance'.

stereoplegic profile image
Mike Bybee • Edited

Not to mention, how much could you have gotten done (duplicating early if necessary) for multiple features, rather than "keep extracting the code out" for one (while guessing at all of your abstractions' possible use/edge cases through some sort of crystal ball)?

Thread Thread
xanderyzwich profile image
Corey McCarty • Edited

My general rule for deduplicatimg is that 2 times is okay and three can be. Every rule for style and organization must be applied with knowledgeable consideration.

nombrekeff profile image

Nice advice, I like this!

Premature optimization/abstraction can be evil 😈

I tend to think this way, I usually wait until I have quite a bit of duplication before abstracting or removing it. And even if I have many duplications I wait a bit before and gather all information I can regarding that code, so I can properly abstract it.

After many years of programing, I sometimes know when something will be duplicated and should be abstracted from the beginning. And when and why to apply Design patterns, which if done well can help you reduce the amount of dupped code you write.

But it's always worth to wait a bit before abstracting and try to understand the reason. If it's only to reduce duplicate code you're thinking incorrectly IMO.

jrop profile image
Jonathan Apodaca

"Make it work, make it right, make it fast" sums up the optimization priority pretty well. Just get it working, and then go back and clean things up.

"If I had time I would have written you a shorter letter" -Mark Twain (or Pascal, who knows who actually said this)

lifelongthinker profile image

I agree, with one "but" (big but): The value of a software product usually hinges on all three of these criteria. The chain thus is only as strong as its weakest link.

If you make it work but fail to get it right or fast, the product holds only little or short-lived value.

Thread Thread
jrop profile image
Jonathan Apodaca

Definitely agreed. I believe the point that this mnemonic attempts to get across is not "don't make it right/fast", but rather to shift ones priority focus to getting a working solution first. Then, by all means, make certain that the program is correct, and then please, oh please, make sure it does not run in exponential time.

Thread Thread
lifelongthinker profile image

Very well said. The quoted version alone just makes me think of all the code where people stopped after the "make it work" phase. 😂😭

functional_js profile image
Functional Javascript • Edited

Nice write-up Nick.

Some more tips:

  • WEF (write everything four times) is fine advice
  • Don't rush to refactor (it's a good architectural stategy to defer as late as possible)
  • Unrefactoring is as important as refactoring
  • Keep your functions orthogonal, like a pipeline
  • Keep your functions monomorphic (take only one type, and return only one type)
  • The ideal function is a one-liner that takes one strongly-typed arg and returns one strongly-typed value
  • Keep your code as flat as possible but no flatter
  • Remove cyclomatic complexity (branching count)
  • Use "if statements" for simple validation and state checks, not for comprehensive use-case branching


cyclomatic complexity:

a measure for the complexity of code
related to the number of ways there are to traverse a piece of code
this determines the minimum number of inputs you need to test all ways to execute the program

cescquintero profile image
Francisco Quintero 🇨🇴

Duplication is far cheaper than the wrong abstraction.

It's gold. It's way easier to abstract when you can see all duplications and compare them.

Nice article!

lifelongthinker profile image
Sebastian • Edited

DRY is just a principle that competes with other principles. Repetition can happen on any level of abstraction, so you need to find a remedy applicable to the right level.

The problem here is not the abstraction itself. Any abstraction only holds for so long and then gets in the way of the implementation. The problem here is that adding new logic usually involves refactoring. With the right refactorings in place, new abstractions can replace old ones.

Besides WET, you could also use different approaches, make different trade offs, such as Composition over Inheritance.

phantas0s profile image
Matthieu Cneude • Edited

The DRY principle comes from the Pragmatic Programmer. And this book says: it's not about code duplication, but knowledge duplication, which is seriously different. Like very different. Very much.

Like any principle, DRY should be applied depending on a context. Like SOLID, or whatever else you venerate these days.

(I wrote an article about that).

Oh, and by the way, when you use a quote without saying it's a quote ("duplication is far cheaper than the wrong abstraction"), it's nice to say who said that before you did.

arekx profile image
Aleksandar Panic • Edited

If your abstraction doesnt work its not the end of the world. Fix the abstraction, learn from your mistakes and you wont make the same issue twice and the code will be better for next time. That is how you grow.
This approach is the opposite. "We dont know what will change so we will hardcode everything" when you do this enough times the code becomes almost impossible to change fast because of so many little nuances of which you dont know because they are in a separate copied logic. This makes your code buggy and even crashing your application which makes you look bad and your company look bad to the client.

Abstractions help in one MAJOR thing, keeping the same logic in one place. And this is incredibly important because you WILL forget that the same logic is copied across 5 different files causing you to fix something in 3 files but miss two or miss 3 because you didn't know your coworker copied the logic somewhere else. Abstractions can be fixed and improved over time to make changes faster... duplicated code and logic just gets worse over time making your client lose faith and your company not seeing you as anything other than a simple coder.

Now with that out of the way. There ARE some times when its beneficial to do the same things 2-3 times but until you get to the level to understand why, you should avoid it. You will know when you get enough experience in architecture design. There are some abstractions which feels so out of place to join (and through experience you know that caused problems) through inheritance that you know that they should be separated out in duplicated code. But these instances are VERY RARE. Basically, you will know it when you see it.

arnaudcortisse profile image
Arnaud Cortisse • Edited

I had never heard the WET acronym before, I like it :D
It's definitely a balancing act.

eljayadobe profile image

WET • Write Everything Twice

...hmm, in my circles it means something different.

WET • Write Expressive Tests

And that means that your unit tests should not be dependent on other tests, or as the starting state from a previous tests finishing state, or having a bunch of shared set-up or shared tear-down helper routines. Each unit test should be self-contained, independent, and stand-alone from other unit tests, and follow the arrange - act - assert pattern, and not have any branches or loops. (I'm using unit tests in the TDD-sense.)

qoyyuum profile image
Abdul Qoyyuum

I don't know. My experience is that I just write it until the feature I'm building works (and passes whatever tests). Once its completed, I can then reiterate the code to make it DRY again. I'm looking at it like building cement blocks. It needs to be wet to put into a form before it becomes dry. Although I would prefer to use dry cement blocks right away as it saves time from further reiteration.

abdisalan_js profile image

As I like to say, duplicated code is cheaper than bad abstractions (what happens when you go bone dry)

john__olson profile image
John Olson

I like Kent Dodds AHA Avoid Hasty Abstractions

stereoplegic profile image
Mike Bybee

Thank you. I couldn't remember the acronym Dodds used, though I just read (reread?) that recently.

bam92 profile image
Abel Lifaefi Mbula

Good article. I'd suggest adding examples for context.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.