Duplication Is the Root of All Evil
Every time you copy-paste code, you create two places that must change together — but won't. DRY is not just about code duplication. It's about knowledge duplication: if the same business rule lives in two places, every bug fix must be applied twice, and someone will miss one. The codebase lies until both copies are updated.
Tax rate duplicated
function calculateOrderTotal(order):
return order.subtotal * 1.08 // 8% tax
function calculateInvoiceTotal(invoice):
return invoice.amount * 1.08 // 8% tax — copy-pasted
function calculateRefundAmount(order):
return order.subtotal * 1.08 // 8% tax — now in 3 places
Single source of truth
TAX_RATE = 1.08 // change here → changes everywhere
function calculateOrderTotal(order):
return order.subtotal * TAX_RATE
function calculateInvoiceTotal(invoice):
return invoice.amount * TAX_RATE
function calculateRefundAmount(order):
return order.subtotal * TAX_RATE
Structural Duplication
Duplication isn't always an exact copy. Sometimes it's the same structure repeated: the same validation logic written with slightly different variable names, the same error-handling pattern copy-pasted across 10 API endpoints, the same DB query with a different WHERE clause. Abstract the pattern, parameterize the variation.
// ✗ Same structure, different subject — 3x duplication
function validateUserName(name):
if not name or length(name) < 2:
raise ValueError("Name must be at least 2 characters")
function validateUserEmail(email):
if not email or length(email) < 5:
raise ValueError("Email must be at least 5 characters")
function validateUserBio(bio):
if not bio or length(bio) < 10:
raise ValueError("Bio must be at least 10 characters")
// ✓ Abstract the pattern, parameterize the variation
function validateMinLength(value, fieldName, minLen):
if not value or length(value) < minLen:
raise ValueError(fieldName + " must be at least " + minLen + " characters")
function validateUser(user):
validateMinLength(user.name, "Name", minLen: 2)
validateMinLength(user.email, "Email", minLen: 5)
validateMinLength(user.bio, "Bio", minLen: 10)
The Wrong DRY — Don't Over-Abstract
DRY is about knowledge duplication, not textual similarity. Two functions that look the same but represent different business rules should stay separate — merging them couples concepts that may evolve differently. Ask: "If one changes, must the other change too?" If no, they're not the same knowledge. Premature DRY creates the wrong abstraction, which is worse than duplication.
Wrong abstraction
// Looks like duplication — but they're different rules
function calculateEmployeeBonus(salary):
return salary * 0.10
function calculateContractorBonus(rate):
return rate * 0.10
// "DRY" merge — now they're coupled:
function calculateBonus(amount):
return amount * 0.10
// Problem: employee rate changes to 12%,
// contractor stays at 10% — now you must un-merge
Same text, different knowledge → keep separate
EMPLOYEE_BONUS_RATE = 0.10
CONTRACTOR_BONUS_RATE = 0.10 // same today, may diverge tomorrow
function calculateEmployeeBonus(salary):
return salary * EMPLOYEE_BONUS_RATE
function calculateContractorBonus(rate):
return rate * CONTRACTOR_BONUS_RATE
Code Challenge
The validation logic appears 3 times. Extract it once. Then notice the creation pattern is also repeated — extract that too.
Key takeaway
DRY is about knowledge, not text. One business rule = one place in the code. But don't merge things that only look similar — they may diverge.