DRY — Don’t Repeat Yourself

“Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.” — Andy Hunt and Dave Thomas, The Pragmatic Programmer (1999)

The Claim

Every fact the system knows — a business rule, an enum value, a validation pattern, a constant, a data-structure shape — should exist in exactly one place. If the same knowledge appears in two places, changes to it have to happen in both, and over time the two copies drift. Drift produces bugs.

The principle is about knowledge, not code. Two functions that look identical are not necessarily a DRY violation; two functions that encode the same rule are. Conversely, one function that encodes two unrelated rules is a DRY violation even if there’s only “one” of it.

What DRY Is Protecting Against

  • Shotgun surgery. When a requirement changes, you must edit ten files. If you miss one, you have a silent bug.
  • Silent drift. Two copies of the same rule, updated separately over years, diverge in ways no one notices until the diverged behaviour surfaces as an incident.
  • Inconsistent vocabulary. The same concept named customer_id in one place and user_uid in another; database schema vs. API contract vs. frontend model; seven different date-parsing regexes.
  • Incorrectly generalised duplication. Two places doing similar things but not identical — a refactor that unifies them introduces bugs that neither had individually.

In This Wiki

  • Instance of occams-razor applied to code. Among systems that solve the problem, prefer the one with fewer independent assumptions. A single source of truth is the fewest-assumption representation.
  • Sits in tension with KISS. Over-DRY code is often complex and indirect — abstractions created to avoid duplication that then become harder to understand than the duplication would have been. The canonical rule: DRY too early is worse than duplication. The “Rule of Three” is the usual heuristic — accept the second copy; refactor on the third.
  • Interacts with hyrums-law. Deduplication creates a shared dependency; once many callers depend on the shared component, every observable behaviour of it is a contract (Hyrum). Premature DRY creates load-bearing abstractions that are then hard to change.
  • Respects galls-law. Simple systems grow into complex ones; DRY applied incrementally (as duplication is observed) tracks this evolution. DRY applied speculatively often builds complex systems from the start and violates Gall.
  • Connects to principle-of-least-astonishment. When the same fact is encoded identically everywhere, behaviour is predictable. When encodings drift, behaviour becomes domain-specific surprise.
  • Connects to law-of-demeter. Both are about coupling hygiene: Demeter says don’t reach through objects to their internals; DRY says don’t spread knowledge across objects. Together they’re the shape of low-coupling, high-cohesion design.

The WET Counter-Weight

An increasingly common opposing view — “Write Everything Twice” or “We Enjoy Typing” — argues that duplication is cheaper than the wrong abstraction. Sandi Metz’s summary: “prefer duplication over the wrong abstraction.” An ill-fitting shared abstraction forces every new requirement to bend to the abstraction, and the resulting code is harder to reason about than three copies of the same function would have been.

The reconciliation: DRY applies to knowledge; duplication-tolerance applies to code that happens to look similar. Two functions with the same shape but encoding different rules are not a DRY violation. Merging them is a violation of a different principle — Single Responsibility.

Software Examples

  • Schema in one place. Generate database types, API types, frontend types from a single source (Protobuf, GraphQL schema, OpenAPI). Not generating from a source is inviting drift.
  • Enum in one place. Status codes, error codes, feature flags — all defined once, consumed everywhere. Copy-pasted enums are a future bug.
  • Build config in one place. Dependency versions specified once per project, not scattered across per-service pinning.
  • Ops runbook in one place. Link, don’t paste. Pasted runbooks go stale.

Limits

  1. “Same knowledge” is judgment-call territory. Two functions computing age — one from birthday, one from hire date — look similar but encode different rules. Merging them is a DRY violation, not a fix.
  2. Organisational boundaries complicate DRY. Shared code across team boundaries is subject to conways-law — the abstraction must not require cross-team coordination per release, or the coupling becomes its own problem.
  3. Copy-free is not always best. A duplicated implementation with a shared test suite can be more maintainable than a shared implementation with no tests. DRY is not a goal in itself; correctness and maintainability are.

Sources

  • source—laws-of-software-engineering — in the Design cluster.
  • Andy Hunt & Dave Thomas, The Pragmatic Programmer (1999, 2nd ed. 2019).
  • Sandi Metz, “The Wrong Abstraction” (2016) — influential counter-weight argument.