---
id: wiki-2026-0508-dry-principle
title: DRY Principle (Don't Repeat Yourself)
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [DRY, Don't Repeat Yourself, single source of truth, WET, AHA]
duplicate_of: none
source_trust_level: A
confidence_score: 0.98
verification_status: applied
tags: [software-engineering, principles, dry, clean-code, abstraction, refactoring]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: Universal
  applicable_to: [Code, Schema, Config, Documentation]
---

# DRY Principle

## 매 한 줄
> **"매 every piece of knowledge 의 single, unambiguous, authoritative representation"**. Hunt & Thomas (Pragmatic Programmer). 매 duplication = 매 update bug. 매 modern caveat: 매 AHA (Avoid Hasty Abstraction) — 매 too-early DRY 의 wrong abstraction. 매 rule: 매 3rd repetition 의 abstract.

## 매 핵심

### 매 origin
- Hunt & Thomas, Pragmatic Programmer (1999).
- 매 "knowledge" 의 emphasize — 매 code line 의 X.

### 매 vs WET / AHA
- **WET**: Write Everything Twice / We Enjoy Typing.
- **AHA**: Avoid Hasty Abstraction (Sandi Metz).
- **Rule of 3**: 매 3 repetition 의 abstract.
- **Quote**: "Duplication is far cheaper than the wrong abstraction." — Sandi Metz.

### 매 type of duplication
- **Imposed**: 매 environment 의 force.
- **Inadvertent**: 매 unaware.
- **Impatient**: 매 deadline.
- **Interdeveloper**: 매 communication 의 X.

### 매 응용
1. **Constants**: 매 magic number 의 named.
2. **Functions**: 매 logic 의 extract.
3. **Templates / generics**: 매 type 의 abstract.
4. **Schema**: 매 single source.
5. **Config**: 매 env-specific override.
6. **Doc**: 매 generate from code.

### 매 modern context
- **Microservices**: 매 service 의 own data → 매 controlled duplication.
- **CQRS**: 매 read / write model 의 separate.
- **Type-driven**: 매 schema 의 source.
- **Codegen**: 매 OpenAPI → client / server.
- **LLM era**: 매 context-aware 의 inline 의 fine (token cost).

## 💻 패턴

### Extract function
```python
# 매 ❌ duplicated
def order_total(items):
    total = 0
    for i in items: total += i.price * i.qty
    tax = total * 0.08
    return total + tax

def cart_total(cart):
    total = 0
    for i in cart: total += i.price * i.qty
    tax = total * 0.08
    return total + tax

# 매 ✅ DRY
def calculate_total(items, tax_rate=0.08):
    subtotal = sum(i.price * i.qty for i in items)
    return subtotal * (1 + tax_rate)
```

### Single source of truth (schema)
```typescript
// 매 Zod schema → 매 type + 매 validation
import { z } from 'zod';
const UserSchema = z.object({ id: z.string(), email: z.string().email() });
type User = z.infer<typeof UserSchema>;
// 매 runtime validation + compile-time type
```

### Config (env override)
```typescript
const config = {
  apiUrl: process.env.API_URL ?? 'http://localhost:3000',
  timeout: Number(process.env.TIMEOUT ?? 5000),
};
// 매 single config object, env-specific override
```

### OpenAPI codegen
```yaml
# openapi.yaml — single source
paths:
  /users/{id}: { get: { responses: { 200: { schema: { $ref: '#/User' } } } } }

# 매 → openapi-typescript / openapi-generator → client + server stubs + docs
```

### Database migration as code
```typescript
// 매 schema 의 ORM model 의 single source
@Entity()
class User {
  @PrimaryColumn() id: string;
  @Column({ unique: true }) email: string;
}
// 매 migration 의 generate from model
```

### Template (Mustache / Liquid)
```handlebars
{{!-- email-template.hbs --}}
Hi {{user.name}}, your order #{{order.id}} ships {{order.shipDate}}.
```

### Inheritance / mixin (cautious)
```python
class TimestampMixin:
    created_at: datetime
    updated_at: datetime

class User(Base, TimestampMixin): pass
class Post(Base, TimestampMixin): pass
```

### Decorator (cross-cutting)
```python
def retry(times=3):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for attempt in range(times):
                try: return func(*args, **kwargs)
                except: if attempt == times - 1: raise
        return wrapper
    return decorator

@retry(times=3)
def fetch_data(url): ...
```

### AHA — when NOT to abstract
```python
# 매 ❌ premature abstraction
class GenericResourceHandler:
    def __init__(self, kind, ...18 params...): ...

# 매 ✅ duplicate first, abstract later
def handle_user(req): ...
def handle_post(req): ...
# 매 → 매 3rd similar handler 의 의 emerge 의 pattern 의 see → 매 then abstract
```

### Detect duplication (PMD / jscpd)
```bash
# 매 jscpd
npx jscpd --threshold 5 --reporters html src/
# 매 reports clones >= 5 lines
```

### Refactor — Pull Up Method
```python
# 매 before
class Dog:
    def name(self): return self._name
class Cat:
    def name(self): return self._name

# 매 after
class Animal:
    def name(self): return self._name
class Dog(Animal): pass
class Cat(Animal): pass
```

### Pragmatic limit (microservices)
```python
# 매 service A
class UserDTO_A: id: str; email: str

# 매 service B (separate codebase)
class UserDTO_B: id: str; email: str

# 매 ✅ controlled duplication — 매 services 의 independent deploy
# 매 ❌ shared library 의 coupling 의 anti-pattern
```

## 매 결정 기준
| 상황 | DRY Rule |
|---|---|
| 2nd occurrence | Wait |
| 3rd occurrence | Extract |
| Cross-service | Controlled duplicate |
| Schema | Single source + codegen |
| Config | Env-override |
| Wrong abstraction | Inline first |

**기본값**: 매 rule of 3 + 매 AHA (avoid hasty abstraction) + 매 wrong abstraction > duplication.

## 🔗 Graph
- 부모: [[Software-Engineering-Principles]] · [[Clean-Code]]
- 변형: [[AHA]] · [[Rule of Three]] · [[Single Source of Truth (SSoT)]]
- 응용: [[Refactoring_Best_Practices|Refactoring]] · [[Codegen]]
- Adjacent: [[SOLID]] · [[YAGNI]] · [[KISS]] · [[Conventional-Commits]]

## 🤖 LLM 활용
**언제**: 매 refactor. 매 abstraction decision. 매 code review.
**언제 X**: 매 < 3 occurrence. 매 cross-domain.

## ❌ 안티패턴
- **Premature DRY**: 매 wrong abstraction → 매 update 의 cascade.
- **Inheritance abuse**: 매 IS-A 의 X 의 force.
- **Cross-service shared lib**: 매 deploy coupling.
- **DRY at all cost**: 매 readability < deduplication.
- **Magic abstraction**: 매 reader 의 confuse.

## 🧪 검증 / 중복
- Verified (Pragmatic Programmer, Sandi Metz).
- 신뢰도 A.

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-04-20 | Auto-reinforced |
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — DRY + AHA + 매 extract / Zod / OpenAPI / detect / refactor code |
