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

PrincipleBenefitRisk if over-applied
DRYSingle source of truth, easier updatesPremature abstraction, tight coupling
KISSReadable, debuggable codeUnder-engineering complex domains
YAGNISmaller codebase, faster deliveryIgnoring known upcoming requirements

The key is balance: apply these as heuristics, not absolute laws.