<arch.design/>
Principles/DRY — Don't Repeat Yourself
{ }CodeArchitecturebeginner1999generalpragmatic-programmerduplicationknowledge

DRY — Don't Repeat Yourself

Every piece of knowledge should have a single, authoritative representation — duplication forces you to keep multiple copies in sync, and they inevitably drift.

5/5
{ }
Operates at: Code level

Inside a codebase — classes, modules, files

How it works

DRY was articulated by Andrew Hunt and David Thomas in The Pragmatic Programmer (1999): 'Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.'

Note what DRY is about: knowledge, not code. Two similar-looking for loops that happen to count to 10 are not a DRY violation — they represent different knowledge. Two places that encode 'a valid email must contain @' are a DRY violation — they represent the same knowledge in two places.

When knowledge is duplicated, any change requires finding and updating all copies. Bugs happen when you find two but miss the third. Tests pass for the copy you fixed but fail against the copy you missed.

DRY violations to watch: copy-pasted validation logic, magic numbers repeated across files, the same data transformation written slightly differently in three places, SQL queries that express the same business rule.

DRY does not mean 'never write two functions that look similar'. It means: if two pieces of code represent the same concept, they should share a single representation.

Implementation

TypeScript · Go · Rust
// ❌ DRY violation — the email validation rule lives in three places
function createUser(email: string, name: string): User {
  if (!email.includes("@") || !email.includes(".")) {
    throw new Error("Invalid email");
  }
  return { email, name };
}

function updateEmail(userId: string, email: string): void {
  if (!email.includes("@") || !email.includes(".")) { // ❌ copy-pasted
    throw new Error("Invalid email");
  }
  db.updateEmail(userId, email);
}

function inviteUser(email: string): void {
  if (!email.includes("@") || !email.includes(".")) { // ❌ again
    throw new Error("Invalid email");
  }
  mailer.sendInvite(email);
}

// ✓ Single authoritative source — change it once, applied everywhere
function validateEmail(email: string): void {
  if (!email.includes("@") || !email.includes(".")) {
    throw new Error("Invalid email");
  }
}

function createUser(email: string, name: string): User {
  validateEmail(email);
  return { email, name };
}

function updateEmail(userId: string, email: string): void {
  validateEmail(email);
  db.updateEmail(userId, email);
}

function inviteUser(email: string): void {
  validateEmail(email);
  mailer.sendInvite(email);
}

Why it matters

Duplicated knowledge leads to inconsistency. When the rule changes — and it will — you must change every copy and hope you found them all. A single authoritative source means changing the rule once changes it everywhere.

When to use

  • Validation rules, business constants, calculation formulas — one place only
  • Shared data transformations used across multiple modules
  • Configuration values referenced in multiple places (use a constant)
  • Any time you find yourself copying code and thinking 'I'll keep these in sync'

When NOT to use

  • Don't DRY across wrong abstraction boundaries — coupling unrelated modules to share code is worse than duplication
  • The 'rule of three': wait until something is duplicated three times before extracting it
  • Test code often benefits from duplication — clear and explicit tests are better than DRY test helpers

Trade-offs

+

Change the rule once — it propagates everywhere automatically

Wrong extraction couples unrelated things — coupling is often worse than duplication

+

Bugs fixed in the shared function are fixed everywhere

Shared abstractions can become complex trying to serve too many callers

+

Consistent behaviour across all call sites

Over-DRY can create premature abstractions that are expensive to change later

In production

Any ORM

Schema defined once (model/migration) — ORM generates queries, validations, and serialisation from the single source of truth

GraphQL

Schema is the single authoritative definition of the API — codegen produces TypeScript types, resolvers, and docs from one spec

Terraform modules

Infrastructure patterns defined once as modules — teams instantiate them rather than copy-paste IaC blocks

Industry adoption

5/5Ubiquitous — used at virtually every scale-focused company.

Related principles