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.
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
ifbranches 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.
| Rule | Stands for | What it says | From |
|---|---|---|---|
| DRY | Don’t Repeat Yourself | Each piece of knowledge has one authoritative representation | Andy Hunt and Dave Thomas, The Pragmatic Programmer (1999) |
| Rule of three | “Three strikes and you refactor” | Two similar copies are tolerable; extract the third time | Don Roberts, popularised in Martin Fowler’s Refactoring (1999) |
| WET | Write Everything Twice | Ask “haven’t I written this before?” twice, but never three times | Conlin Durbin, as quoted by Kent C. Dodds |
| The wrong abstraction | Not an acronym | “Duplication is far cheaper than the wrong abstraction” | Sandi Metz, RailsConf 2014 talk and a 2016 blog post |
| AHA | Avoid Hasty Abstractions | Accept duplication until you are confident of the use cases; optimise for change first | Kent C. Dodds, crediting the name to Cher Scarlett |
| KISS | Keep it simple, stupid | Make simplicity a design goal | Associated with Lockheed engineer Kelly Johnson; noted by the U.S. Navy in 1960 |
| YAGNI | You Aren’t Gonna Need It | Don’t build a capability you only presume you’ll need | Extreme 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.
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 forgetIn 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 FalseOften 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
- 1999
The Pragmatic Programmer names the DRY principle
- 1999
Fowler’s Refactoring popularises Don Roberts’s rule of three
- 2003
Thomas tells Bill Venners DRY covers schemas, tests, builds and docs, not just code
- 2014
Sandi Metz at RailsConf: “duplication is far cheaper than the wrong abstraction”
- 2015
Martin Fowler’s article on YAGNI and presumptive features
- 2016
Metz’s post “The Wrong Abstraction”
- Artima: Orthogonality and the DRY Principle, Bill Venners interviews Andy Hunt and Dave Thomas (Mar 2003)
- Dave Thomas and Andy Hunt: The Pragmatic Programmer, 2nd edition, “The Evils of Duplication” (publisher excerpt, PDF)
- Wikipedia: Rule of three (computer programming)
- Sandi Metz: The Wrong Abstraction (Jan 2016)
- Kent C. Dodds: AHA Programming
- Martin Fowler: Yagni (May 2015)
Sources7
- Dave Thomas and Andy Hunt: The Pragmatic Programmer, 2nd edition, “The Evils of Duplication” (publisher excerpt, PDF)
- Artima: Orthogonality and the DRY Principle, Bill Venners interviews Andy Hunt and Dave Thomas (Mar 2003)
- Wikipedia: Rule of three (computer programming)
- Sandi Metz: The Wrong Abstraction (Jan 2016)
- Kent C. Dodds: AHA Programming (WET, AHA)
- Wikipedia: KISS principle
- Martin Fowler: Yagni (May 2015)