6

We have all heard of Don't Repeat Yourself and Write Everything Twice, but I've never understood why anyone would prefer the latter of those. Obviously you can go overkill with DRY, with the code ending up unreadable and hard to debug. But why would you want to strive to write more code than is required to perform a task? Isn't it always more preferable to have DRY code than WET code?

Maybe I just don't understand the Write Everything Twice idiom. What is meant by it?

5
  • 9
    There is no "Write Everything Twice" idiom; nobody does that. You're confusing a principle with pragmatism. Apr 8, 2021 at 13:21
  • 4
    You've essentially answered the question already - other important principles such as KISS, YAGNI and JFDI tend to get priority over DRY. Apr 8, 2021 at 13:22
  • 3
    Related to this - I once worked at an organisation where some of the most profitable contracts with customers included requiring source code (99% identical to the code used for other customers) to be kept in an isolated branch in SVN - no reuse at all between that customer's branch and our main trunk without their explicit permission (not even bug fixes). By far the worst violation of DRY I've ever seen, but we were legally bound to keep it that way; technically it was very poor, however the hugely increased fees charged to those customers helped keep the business afloat. Apr 8, 2021 at 13:39
  • this question is discussed at meta
    – gnat
    Apr 8, 2021 at 19:22
  • Who ever said you should?
    – Flater
    Apr 15 at 3:59

9 Answers 9

23

No one is really promoting unnecessary code duplication. You don't usually have a choice between orthogonal (DRY) and duplicate (WET) code. “WET” is a bit of a strawman for DRY-proponents to argue against. But most code gets messy and non-orthogonal over time, without any intention to make it worse. Kind of like the second law of thermodynamics applied to software maintainability: entropy/chaos increases. Thus, it needs conscious effort to make and keep code maintainable.

However, you will find many people that argue against DRY. Overly literal application of DRY principles leads to code that has no duplication, and lots of abstraction. However, the wrong abstraction can be more problematic than no abstraction at all. Some things look like duplication because they merely happen to appear the same, not because they are fundamentally and necessarily always the same. E.g. if I factor out two pieces of code that currently are the same but have nothing to do with each other, I'll have caused unnecessary coupling. If I then change that refactored code, I'll have caused changes in two unrelated places – making the system more difficult to debug and maintain. In this viewpoint, it might be better to hold off from creating abstractions unless you have a better idea of what a good abstraction could look like. That insight often requires that you've spelled out a few instances of the non-abstracted, duplicative version.

11

The name itself is a joke, but it is just an alternative name for the well known Rule of Three, which states that the third time you encounter a certain pattern, you abstract it into some reusable unit.

It is a question of balancing simplicity against conciseness. When code is extracted into a function to conform to DRY, you create an abstraction. But abstraction have a cost - using the wrong abstractions may increase complexity and make maintenance harder. So abstractions should not be created willy-nilly to save a few lines of code. The "rule of three" (or WET) basically says think twice before creating an abstraction.

6

WET is a word of caution against DRY. In many cases writing "duplicate" code is better because the code is actually different and will change separately. The two bad things that tend to happen when being overly dry is have a lot of functions that don't actually end up getting reused and just cause extra cognitive overhead for no real gain, or you have a method that is reused heavily but ends up with multiple execution paths for each different use. If you write everything twice, then you don't end up with extra methods and you don't end up combining things you thought were the same but ended up not being the same. The downside of WET is that when you go to write something a third time you should be combining at that point, but this fails in larger code bases because you may not know it's been written many times.

3

In my view, the principal reason why people Write Things Twice is shortsightedness.

Software 'engineering' is in many ways a much less structured activity than real engineering, but it does share an important property with highway or utility engineering: almost every decision you make involves a trade-off between two mutually incompatible goals (e.g. cost vs. reliability, strength vs. completion time, etc.)

Since writing software is a more abstract task than erecting a bridge, our trade-offs tend to be more abstract than the obvious ones that you can measure with your wallet and your calendar. Very often, copy&paste of a piece of codes that is known-good allows you to achieve one particular task with great speed and low risk. The trade-off here is that of a plus in immediate speed of achievement of one task against a slight minus in the maintainability of the entire code base. Because the minus is hypothetical and lies in the future, it is very easy to overlook it altogether and assume that it is negligible.

In general, I believe that if people had a proper grasp of the entirety of the project to which they contribute, they would see that copy&pasting code is a net minus. But as long as a contributor measures his success only by how fast the task at hand was completed, it can look very much like a plus. Understanding this requires long personal experience, or more faith into colleagues with more experience than the typical junior programmer can muster.

2
  • 5
    There is an additional benefit of copy/paste, and that is that it decouples. If I'm writing a module and I want it to run independently, I can copy/paste code instead of putting it into a central library and creating a new dependency. Also, a separate copy means I can modify one copy without affecting the other. So there are legitimate reasons for being wet. Apr 8, 2021 at 13:25
  • 8
    I would argue that this is not WET, and not a violation of DRY. If it's an independent module that evolves independently, then it was never a duplicate in the first place. A typical example, reduced almost to the point of absurdity, is when someone creates a single constant for sales tax and drinking age because both happen to be 18, and you need to remove that duplication. It was never duplication in the first place, because the two 18s mean different things. Apr 8, 2021 at 16:04
1

I think you're really asking about WET source code.  But you more generally wrote "WET code" without qualification as to source vs. other.

WET code may be more performant as it can be customized for the exact situation.  This approach is particularly appropriate when generating code, whether that is machine code or high level language code.  The generator already provides abstraction so that the generator source, whatever form it takes, is more DRY, so there's little need to have the generated code be as DRY or seek increased abstraction.

For example, a compiler may inline methods — this is literally copying/repeating the same code in different places (in the generated code).  Each copy can be optimized to take advantage of surrounding circumstances, while the original source code has the method implementation in only one place.

1

While I can't say I recommend it, I've seen this used in web shops focused on building white label marketing sites. They start by identifying the existing client that best matches what the new client wants, copy that directory over, and adjust to fit.

As a strategy, they push this because it keeps each client isolated from subsequent changes made for other clients.

I've also seen people promoting WET code as a strategy for keeping to concretions rather than abstractions. I have a feeling these people got infected by a "leet" coder at some point who created an abstract monstrosity no one could make sense of.

1

DRY postulates that a code modified in multiple places to achieve a single goal is prone to mistakes. This argument falls apart when you consider a large system with a complex graph of dependencies.

Dependency management

Adding a dependency to a component leads to surprising expansion of its scope - now author is responsible not only for monitoring and servicing the code of the component itself, but also for all of its dependencies (development effort is shared with dependencies authors, but the responsibility can not be shared same way).

False similarity

False similarities are hard to identify and lead to invalid dependencies between components, but those are covered in other answers and are trivial in nature, so I won't discuss them.

Accidental dependencies

When multiple components of identical function are discovered, DRY replaces them with a single one. That single component has to be placed somewhere in a dependency graph. In a complex dependency graph, it is next to impossible to introduce an additional dependency without adding accidental dependencies (those, that we do not need in a component we are modifying, i.e. when using a pow() function from math.h we accidentally implicitly depend on max() function in a sense, that our component can now use max() without adding any new dependencies and completely opaque to external components). Accidental dependencies drastically change the responsibilities of the resulting component without bringing any value, and have to be avoided.

To solve this issue either modules have to be split until modular system becomes redundant and useless, or components have to be duplicated.

If modules are split, the dependency graph becomes very large and hard to track. Long dependency chains make responsibility delegation impractical.

A nice illustration for the problem of dependency management is left_pad scandal, but the same problem happens with internal dependencies of any large project.

If components are duplicated, a module is self-sufficient, can guarantee its performance and ignore external changes (to a degree, DRY is based on a valid concern).

0

tl;dr Since there isn't any DRY code nor WET code this is just for the sake of discussing.

What is DRY? According to wikipedia...

"Don't repeat yourself" (DRY) is a principle of software development aimed at reducing repetition of information which is likely to change, replacing it with abstractions that are less likely to change, or using data normalization which avoids redundancy in the first place.

That applied would be when there is a repeating code snippet just encapsulate it in an abstraction, the way a method is, and call the abstraction instead of repeating the code snippet. That means repeat the abstraction call instead of repeating the code snippet, since method signature is something changeable. There isn't any DRY code, at best it is reduce the repetition dimension.

Why would you ever want WET code?

When the unknown is the reason a code snippet has to change repeating a code snippet might be a solution. When the unknown is whether two code snippets has to run both or both not to run or run independently of each other then repeating the code snippet is the simplest solution.

0

Where do we want to see WET code? In unit tests! (Within reason, as common fixtures certainly have their place.)

A test will do AAA, {arrange, act, assert}.

Often we wish to Arrange a bunch of similar input values, and copy-n-paste is an appropriate way to do that, both for Author and for Reviewer. Only when arranging starts to become a bit elaborate is it worthwhile to Extract Helper or rely on a common fixture.

The idea is to present something very simple, with few abstractions, to the Reviewer. Tests strive to be simple and very obvious. Abstractions can get in the way of that.


OTOH target code is subject to the Rule of Three, and should be DRYed up during routine refactorings.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.