Don't Repeat Yourself
Every piece of knowledge should have a single, authoritative representation — and why that's harder than it sounds.
"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."
DRY is not about avoiding duplicated lines of code. It's about avoiding duplicated knowledge — business rules, validation logic, configuration values, data shapes. Two functions that happen to look similar are not necessarily a DRY violation. Two places that independently encode the same business rule absolutely are.
The practical test: when the rule changes, how many places do you have to update? One is DRY. More than one is a liability — and the second update you forget is a bug.
The failure mode: accidental similarity vs. duplicated knowledge
Avoid reflexively extracting code just because it looks the same. Two loops that iterate over different things for different reasons happen to share syntax — merging them couples unrelated logic. The question is always: do these represent the same knowledge?
go// Two functions that look similar but encode independent knowledge. // Do NOT merge these. They will diverge. func validateUserAge(age int) error { if age < 18 { return errors.New("user must be 18 or older") } return nil } func validateDriverAge(age int) error { if age < 16 { return errors.New("driver must be 16 or older") } return nil }
These look like duplication. They are not. The rules are independent. If the driving age changes, you don't want it to affect user registration — and a shared validateAge(min int) wrapper would hide that they're different rules.
Real duplication: the same rule in multiple places
go// BAD — order status logic duplicated across the codebase. // Every new status requires touching three functions. func CanCancel(o Order) bool { return o.Status == "pending" || o.Status == "processing" } func CanRefund(o Order) bool { return o.Status == "pending" || o.Status == "processing" } func IsActive(o Order) bool { return o.Status == "pending" || o.Status == "processing" }
go// GOOD — the knowledge lives in one place. // The rule changes in exactly one location. func IsMutable(o Order) bool { return o.Status == "pending" || o.Status == "processing" } func CanCancel(o Order) bool { return IsMutable(o) } func CanRefund(o Order) bool { return IsMutable(o) } func IsActive(o Order) bool { return IsMutable(o) }
Configuration duplication
Magic values are a common DRY violation. When a value appears in multiple places, a change requires a search-and-replace — and one missed instance is a silent bug.
go// BAD — the session duration is scattered across the codebase. func NewSession(userID string) Session { return Session{Expires: time.Now().Add(24 * time.Hour)} } func IsExpired(s Session) bool { return time.Since(s.CreatedAt) > 24*time.Hour } func RefreshSession(s Session) Session { return Session{Expires: time.Now().Add(24 * time.Hour)} }
go// GOOD — one authoritative constant. const sessionTTL = 24 * time.Hour func NewSession(userID string) Session { return Session{Expires: time.Now().Add(sessionTTL)} } func IsExpired(s Session) bool { return time.Since(s.CreatedAt) > sessionTTL } func RefreshSession(s Session) Session { return Session{Expires: time.Now().Add(sessionTTL)} }
The Rule of Three
Don't extract on the first duplication. Wait for three. The first instance is just code. The second is a coincidence. The third is a pattern worth naming.
Premature abstraction is its own problem: you create an abstraction before you understand the full shape of the rule, and you paint yourself into a corner. Three instances give you enough signal to design the right abstraction.
DRY and generated code
Generated code is an exception. If a struct is generated from a schema, and a corresponding SQL table definition also comes from that schema, the source of truth is the schema — not the two outputs. The outputs can look identical without violating DRY because neither encodes the knowledge; the generator does.
The principle is about knowledge, not bytes.
Smell: A business rule changes and you update it in one place, but a bug report comes in two weeks later because a second copy of the rule was missed. Or: you grep for a constant value and find it hardcoded in five files.
See also: Single Responsibility Principle, Strategy for encapsulating variable algorithms in one place.