Last updated: Apr 29, 2026
DRY, KISS & YAGNI
These three principles often travel together because they reinforce the same idea: write only what is needed, only once, and as simply as possible.
DRY — Don’t Repeat Yourself
“Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.” — The Pragmatic Programmer
DRY targets knowledge duplication, not textual duplication. Two identical lines that represent different business rules are not a DRY violation; two diverging functions that encode the same rule are.
// Duplicated format logic
function logError(msg: string) {
console.log(`[${new Date().toISOString()}] ERROR: ${msg}`);
}
function logInfo(msg: string) {
console.log(`[${new Date().toISOString()}] INFO: ${msg}`);
}
// DRY — format expressed once
function log(level: string, msg: string) {
console.log(`[${new Date().toISOString()}] ${level}: ${msg}`);
}
When not to DRY
Premature abstraction to remove superficial duplication often creates tight coupling. Prefer the Rule of Three: tolerate duplication until the third occurrence reveals a true pattern.
KISS — Keep It Simple, Stupid
“Simplicity is the ultimate sophistication.” — Leonardo da Vinci
KISS says the simplest solution that meets the requirements is usually the best one. Complexity is a cost — every branch, abstraction layer, or indirection must justify its existence.
// Over-engineered
class StringReverserFactory {
create() {
return new StringReverser();
}
}
class StringReverser {
reverse(s: string) {
return s.split("").reduce((acc, ch) => ch + acc, "");
}
}
// KISS
function reverse(s: string): string {
return [...s].reverse().join("");
}
Signals you’re violating KISS
- You need a diagram to explain a helper function.
- A class exists only to wrap another class.
- Generics or abstractions were added “just in case.”
YAGNI — You Ain’t Gonna Need It
“Always implement things when you actually need them, never when you just foresee that you need them.” — Ron Jeffries
YAGNI guards against speculative development. Features, abstractions, and configuration options that nobody has asked for yet carry maintenance cost from day one but deliver value at best some day.
// YAGNI violation — pagination nobody asked for
interface FindUsersOptions {
page?: number;
pageSize?: number;
sortBy?: string;
sortOrder?: "asc" | "desc";
includeDeleted?: boolean;
}
// What was actually needed
function findActiveUsers(): Promise<User[]> {
return db.users.find({ active: true });
}
Trade-offs
| Principle | Benefit | Risk if over-applied |
|---|---|---|
| DRY | Single source of truth, easier updates | Premature abstraction, tight coupling |
| KISS | Readable, debuggable code | Under-engineering complex domains |
| YAGNI | Smaller codebase, faster delivery | Ignoring known upcoming requirements |
The key is balance: apply these as heuristics, not absolute laws.