---
title: DRY, KISS & YAGNI
description: Three foundational principles that guide clean, maintainable software — avoid repetition, favour simplicity, and defer unnecessary work.
tokens: ~791
---

# 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_.

```typescript
// 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.

```typescript
// 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_.

```typescript
// 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.