Skip to main content
Clean Code 60 XP · 7 min

Don't Repeat Yourself

Every piece of knowledge must have a single, authoritative representation in the codebase.

Showing
Ad (728×90)

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.

Done with this lesson?

Mark it complete to earn XP and track your progress.