DRY principle
Duplicated knowledge is a tax you pay on every change. The DRY principle from The Pragmatic Programmer is Hunt and Thomas’s name for how to keep that tax low without confusing “similar lines” with “the same fact twice.”
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
Andy Hunt and Dave Thomas frame DRY as “Don’t Repeat Yourself.” The alternative is the same thing living in two or more places. Change one copy and you have to remember the others. Their line is blunt: it is not whether you will remember, it is when you will forget. The 20th anniversary excerpt on duplication walks the same argument through code, comments, schemas, and APIs.
The easy misread is that DRY means “never copy-paste source.” That is a small slice. In the later edition they stress that DRY is about duplication of knowledge and intent, possibly expressed in totally different forms. Their acid test: when one facet of the system has to change, do you touch it in multiple places and multiple formats (code and docs, schema and struct, client and server)? If yes, you are carrying duplicate knowledge even when the text on screen does not match byte for byte.
That scope is why the principle shows up outside functions. A fee rule spelled out in a comment and again in the implementation is two representations of one policy. A line object that stores start, end, and a cached length duplicates geometry you could derive. Hunt and Thomas also treat representational overlap with external APIs and data sources as a cost you can shrink (generators, shared specs, introspected schemas) but rarely eliminate.
Maintenance is not a phase that starts at ship. Requirements move, regulations change, tests falsify an algorithm, and the environment shifts while you are still building. Each of those edits sends you hunting for every capsule of knowledge that must stay consistent. Duplicate capsules multiply the search and multiply the ways a fix can be half-applied.
The failure mode is not only bugs. It is coupling by accident. Two modules that were copy-pasted together now change together even when only one of them had a real reason to move. Teams feel this as “we fixed it in the service but forgot the admin UI / the migration / the runbook.” DRY is a strategy for making related knowledge change in one authoritative place so the blast radius of an update stays predictable.
Similar code is not always duplicate knowledge. Hunt and Thomas use two validators that share the same body but enforce different concepts (age versus quantity). Merging them because the lines match would couple unrelated business rules. Coincidence is not duplication.
The wrong abstraction is worse than repetition. Sandi Metz’s line (often summarized as “prefer duplication over the wrong abstraction”) is the practical corollary. If you extract a shared helper before you know what varies together, the next requirement splits the abstraction with if branches and flags. You have traded visible duplication for hidden coupling. Kent Dodds’s “AHA” write-up makes the same point from a change-first angle: abstract when the duplication itself has become the barrier, not when a linter counts two matching blocks.
Performance can justify a localized violation. Caching a derived field inside a class is a deliberate second representation of knowledge, as long as mutators keep it in sync and the inconsistency does not leak across the module boundary. That is a trade, not a license to scatter denormalized copies everywhere.
Forced DRY across boundaries that change for different reasons is where the principle collides with module design. If one layer knows another layer’s time window, fee schedule, or presentation format, a “shared” function often means one edit ripples through unrelated callers. That is the same failure mode as giving a module more than one reason to change; I wrote about it in what developers miss about the Single Responsibility Principle.
Treat DRY as a maintenance bet on stable knowledge. YAGNI is the sibling rule on the other axis. Do not duplicate knowledge you do not need yet, and do not build capabilities you are not using yet. When two pieces only look alike today, leave them separate until a real change proves they are the same fact. When they are the same fact, give them one home and let everything else import that truth.