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/5Inside 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
Schema defined once (model/migration) — ORM generates queries, validations, and serialisation from the single source of truth
Schema is the single authoritative definition of the API — codegen produces TypeScript types, resolvers, and docs from one spec
Infrastructure patterns defined once as modules — teams instantiate them rather than copy-paste IaC blocks
Industry adoption
Related principles
Separation of Concerns
Divide a program into distinct sections where each section addresses one concern — so changes to one area don't ripple unexpectedly into unrelated areas.
Single Responsibility Principle
A class should have only one reason to change — keep each unit focused on a single job so unrelated concerns don't accidentally break each other.
Abstraction
Reduce complexity by modelling only the details that matter for your problem — hide everything else behind a stable interface.
KISS — Keep It Simple
Most systems work best when kept as simple as possible — choose the straightforward solution over the clever one, and add complexity only when the problem demands it.