--- 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; // 매 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]] - 응용: [[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 |