Cloud Parts

DRY: Don’t Repeat Yourself

Every fact a system knows should live in one place, so changing a rule is one edit; it is about duplicated knowledge, not about code that happens to look alike.

How it works

The Pragmatic Programmer states it as: “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.” Knowledge means more than code: a fee rule, a database schema, a build step, a test plan or a paragraph of documentation. If the same fact is written in two places, someone has to remember to change both, and sooner or later the copies disagree. The book’s acid test: when one facet of the system changes, do you make that change in several places, perhaps in different formats (code and docs, a schema and the class that mirrors it)? The fixes are ordinary: a named constant or function, a value computed from others instead of stored twice, or a generator that turns one authoritative source (a schema, an API spec) into the other forms. The opposite mistake is common too. Two functions with identical bodies can encode different facts; merging them couples things that will change for different reasons. That is why the rule of three waits for a third copy, and why Sandi Metz argues that duplication is cheaper than the wrong abstraction.

Think of it as

A shop keeps its prices in one list that the website, the till and the printed catalogue all read. Changing a price is one edit. If each department copied the prices into its own document, some copies would go stale. But two products that both cost €20 today don’t share a price: they just happen to match, and tying them together would be a mistake.

Reach for it when

  • A business rule (a tax rate, a password policy, a fee schedule) appears in several places and must always change together
  • A database schema, API spec or config is mirrored by hand in code: generate one from the other
  • You just fixed a bug and the same bug is waiting in other copies

Think twice when

  • Code that looks the same but encodes different facts, such as an age check and a quantity check that share rules by coincidence
  • You have only two copies and can’t yet see what varies between them: the rule of three says wait for the third
  • A shared function keeps gaining parameters and if branches for each caller; that abstraction is wrong, so inline it back

A real example

An online wine shop validates a customer’s age and an order quantity. Both must be integers greater than zero, so validate_age and validate_quantity have identical bodies, and a reviewer flags a DRY violation. The Pragmatic Programmer uses this exact case to say the reviewer is wrong: the code is the same, but the knowledge is different, a coincidence rather than a duplication. Its counterexample is a fee function whose comment restates the rules ($20 per returned check, $10 a day after 3 days of overdraft, half off above a $2,000 average balance). That is duplication: when the customer changes a fee, both the comment and the code must change, and they will drift apart. The fix is to delete the restatement and let a well-named function say it once.

DRY and its neighbours#

Rules of thumb that pull in different directions. Most of them exist to stop DRY being applied too early.

RuleStands forWhat it saysFrom
DRYDon’t Repeat YourselfEach piece of knowledge has one authoritative representationAndy Hunt and Dave Thomas, The Pragmatic Programmer (1999)
Rule of three“Three strikes and you refactor”Two similar copies are tolerable; extract the third timeDon Roberts, popularised in Martin Fowler’s Refactoring (1999)
WETWrite Everything TwiceAsk “haven’t I written this before?” twice, but never three timesConlin Durbin, as quoted by Kent C. Dodds
The wrong abstractionNot an acronym“Duplication is far cheaper than the wrong abstraction”Sandi Metz, RailsConf 2014 talk and a 2016 blog post
AHAAvoid Hasty AbstractionsAccept duplication until you are confident of the use cases; optimise for change firstKent C. Dodds, crediting the name to Cher Scarlett
KISSKeep it simple, stupidMake simplicity a design goalAssociated with Lockheed engineer Kelly Johnson; noted by the U.S. Navy in 1960
YAGNIYou Aren’t Gonna Need ItDon’t build a capability you only presume you’ll needExtreme Programming; explained by Martin Fowler (2015)

When the abstraction is wrong#

Sandi Metz describes how it happens. Programmer A sees duplication, extracts it and names it. A new requirement is almost, but not quite, what the abstraction does, so programmer B adds a parameter and a conditional. Then another, and another, until nobody understands the shared code, and everyone feels bound to keep it because of the effort already spent.

Her fix is to go back: inline the shared code into every caller, keep in each caller only the branch its parameters select, and delete the rest. Each caller then holds only the code it needs, and real duplication, if any, shows itself again. Her test for spotting the problem: if you find yourself passing parameters and adding conditional paths through shared code, the abstraction is incorrect.

Derived data is duplication too#

The Pragmatic Programmer’s example: a line that stores its start, its end and its length holds the length twice, because start and end already define it. Compute it instead. The book allows a cached copy for performance, as long as it stays private to the class.

python
import math

class Line:
    def __init__(self, start, end):
        self.start, self.end = start, end

    @property
    def length(self):  # derived from start and end, never stored twice
        return math.dist(self.start, self.end)

line = Line((0, 0), (3, 4))
print(line.length)  # 5.0
line.end = (6, 8)
print(line.length)  # 10.0: no stale copy to forget

In code · python

# One authoritative place for a business rule...
FREE_SHIPPING_FROM = 50  # euros

def shipping_cost(total):
    return 0 if total >= FREE_SHIPPING_FROM else 5

def banner(total):
    missing = FREE_SHIPPING_FROM - total
    return "Free shipping!" if missing <= 0 else f"Add €{missing} for free shipping"

# ...but these two only look alike: the same rule today, two different facts.
def validate_age(value):
    return isinstance(value, int) and value > 0

def validate_quantity(value):
    return isinstance(value, int) and value > 0

print(shipping_cost(60), shipping_cost(20))   # 0 5
print(banner(20))                             # Add €30 for free shipping
print(validate_age(30), validate_quantity(0)) # True False

Often confused with

Where it came from1999–2016

Andy Hunt and Dave Thomas named the DRY principle in The Pragmatic Programmer (Addison-Wesley, 1999). Their worry was maintenance: programmers are “constantly in maintenance mode”, and every fact written twice is a place where a change can be missed. In a 2003 interview with Bill Venners, Thomas stressed that “a system’s knowledge is far broader than just its code”: database schemas, test plans, the build system, even documentation. Where one fact must exist in several forms, he recommended code generators and scripts that turn one authoritative representation into the others.

Readers mostly heard “don’t copy and paste”. By 2003 Thomas was calling DRY “probably one of the most misunderstood parts of the book”: “Most people take DRY to mean you shouldn’t duplicate code. That’s not its intention.” In the book’s second edition the authors admit that “in the first edition of this book we did a poor job of explaining just what we meant”, and add that DRY “is about the duplication of knowledge, of intent”, with an example of identical code that is not a duplication.

A counterweight grew alongside. Martin Fowler’s Refactoring (1999) passed on Don Roberts’s rule of three: wait for the third copy before extracting. Sandi Metz’s line “duplication is far cheaper than the wrong abstraction”, from a RailsConf 2014 talk, spread widely, and her 2016 post explained how shared code decays. Kent C. Dodds later packaged the same caution as AHA, “Avoid Hasty Abstractions”.

Why it’s built this way

Define it by knowledge, not by text
Copies of the same fact “will most likely fall out of step with each other”, whether they are lines of code, a schema and a class, or a comment and the function under it. Identical text that encodes different facts is not the problem.
Generate the copies you can’t avoid
Some facts must exist in several forms, such as a database schema and the code that reads it. Thomas’s answer was code generation and build scripts, so there is still one authoritative source and the rest are derived.
Wait for the third copy
Roberts’s guideline trades a little duplication for evidence: by the third copy you can see what actually varies, so the extracted function is more likely to fit.
Prefer duplication over the wrong abstraction
Metz observed that shared code which keeps gaining parameters and conditionals costs more than the duplication it removed, and that sunk cost keeps teams from undoing it.

Timeline

  1. 1999

    The Pragmatic Programmer names the DRY principle

  2. 1999

    Fowler’s Refactoring popularises Don Roberts’s rule of three

  3. 2003

    Thomas tells Bill Venners DRY covers schemas, tests, builds and docs, not just code

  4. 2014

    Sandi Metz at RailsConf: “duplication is far cheaper than the wrong abstraction”

  5. 2015

    Martin Fowler’s article on YAGNI and presumptive features

  6. 2016

    Metz’s post “The Wrong Abstraction”

Sources7

Improve this page on GitHub