DRY — "do not repeat yourself" — is one of the most quoted principles in software engineering, and one of the most frequently misapplied. WET is the tongue-in-cheek counterpart, commonly expanded as "write everything twice." The honest answer is that both principles are right in different situations, and treating DRY as an absolute rule is how codebases end up with abstractions nobody can safely change.
What changed in 2026
- AI pair programmers made it trivially cheap to generate near-duplicate code fast, which increased the temptation to over-abstract early just because an assistant offered to "extract this into a shared function."
- "Rule of three" thinking (duplicate twice is fine, abstract on the third occurrence) became the dominant guidance, replacing reflexive DRY-on-first-repeat habits from earlier style guides.
- Feature-flag-heavy codebases pushed teams toward accepting more duplication deliberately, since a shared abstraction touched by multiple flags becomes a coordination bottleneck.
What DRY actually means
The DRY principle, as originally stated, is about knowledge duplication, not text duplication. Two functions that happen to have similar-looking code but represent different business rules are not a DRY violation, even if the code looks nearly identical. A DRY violation is when the same piece of business knowledge — a tax rate, a validation rule, a pricing formula — is expressed in more than one place, so that changing the rule requires remembering to update every copy.
What WET code looks like
WET code repeats the same logic across the codebase with no shared source of truth. It is easy to write, easy to read in isolation, and easy to modify locally without breaking anything else. Its downside shows up later: a bug fix or rule change has to be applied in every duplicated location, and it is easy to miss one.
DRY vs WET: the tradeoff
| Factor |
DRY (abstracted) |
WET (duplicated) |
| Change cost when logic updates |
Low - change one place |
High - must find and update every copy |
| Risk of divergence/inconsistency |
Low |
High |
| Coupling between unrelated features |
Higher - shared code creates shared risk |
Lower - each copy can evolve independently |
| Readability in isolation |
Lower - logic is indirect, behind an abstraction |
Higher - everything needed is visible in one place |
| Best when |
The duplicated logic represents one true business rule |
The similarity is coincidental, or requirements are still shifting |
When to abstract, and when to wait
Duplication is not automatically bad; premature abstraction is often worse than duplication, because the wrong abstraction is expensive to unwind once several features depend on it. A practical rule: tolerate duplication the first and second time you see a pattern. On the third occurrence, evaluate whether the three instances truly represent the same rule — if they do, extract a shared function; if they only look similar today but are likely to diverge, keep them separate.
Common mistakes
- Abstracting two coincidentally similar blocks of code that represent unrelated business rules, creating a shared function that has to grow branches and flags to handle both cases as they diverge.
- Chasing DRY across unrelated modules just because a linter or an AI suggestion flagged similar-looking lines, ignoring whether the similarity is meaningful.
- Under-abstracting genuine duplication of a real business rule, such as a tax calculation copy-pasted into five places, so that a rule change requires a five-location hunt-and-fix.
- Confusing DRY with shortness. A one-line helper that saves three lines of typing but obscures what is happening is not a DRY win if it makes the code harder to follow.
FAQ
Is DRY the same as writing shorter code?
No. DRY is about eliminating duplicate knowledge, not duplicate characters. A verbose but clear function can be perfectly DRY; a terse one-liner reused everywhere can still be a bad abstraction if it forces unrelated callers to share behavior they should not share.
What is the rule of three?
A guideline that says duplication is acceptable the first two times a pattern appears. Only on the third occurrence should you seriously consider extracting a shared abstraction, since by then the pattern is more clearly a real, recurring rule rather than a coincidence.
Does DRY apply to test code the same way it applies to production code?
Mostly no. Tests often benefit from being WET and explicit, because a shared test helper that changes behavior across many tests can hide the actual assertion being made, making failures harder to diagnose.
How does this relate to design patterns like factory or singleton?
Design patterns are structured ways to share behavior once you have decided abstraction is warranted. See the factory pattern explained guide for one common approach to sharing object-creation logic without over-coupling callers.
Where to go next