"매 현재 요구사항을 넘어선 generality · abstraction · flexibility를 도입해 비용은 즉시 발생하나 benefit은 영원히 도래하지 않는 anti-pattern.". YAGNI (XP, Beck 1999) · KISS · Rule of Three의 모든 위반. 2026 현재 LLM-aided 코드의 흔한 실패 모드 — 매 AI가 "future-proof"한 코드를 과잉 생성.
매 핵심
매 증상
매 single-impl interface (premature abstraction).
매 사용 안 되는 config option · feature flag.
매 deep inheritance · 5-layer 아키텍처 for 1k LOC 앱.
매 generic을 위한 generic (<T extends BaseEntity<TId>>).
매 사용 안 되는 plugin/extension point.
매 진짜 cost
읽기 비용: 매 indirection 따라가야 의도 파악.
변경 비용: 매 abstraction 두께만큼 변경 surface 증가.
버그 surface: 매 unused path도 maintain.
인지 부하: 매 reviewer · 신규 입사자 onboard 비용.
lock-in: 매 잘못된 abstraction에 매몰.
매 응용 (피하기)
YAGNI: 매 actual demand 시점까지 미루기.
Rule of Three: 매 3번째 use case에서 abstract.
Fowler's Designed Decision Deferral: 매 결정 지연이 valid 전략.
💻 패턴
Anti: premature interface
// BAD: 단 하나의 impl, 매 single call site
interfaceUserRepository{findById(id: string):Promise<User>;}classPostgresUserRepositoryimplementsUserRepository{/* ... */}functiongetUser(repo: UserRepository,id: string){returnrepo.findById(id);}// GOOD: concrete, refactor when 2nd impl 등장
classUserRepository{asyncfindById(id: string):Promise<User>{/* postgres */}}
Anti: factory of factories
// BAD
classUserServiceFactoryFactory{createFactory(env: Env){returnnewUserServiceFactory(env);}}// GOOD
functioncreateUserService(db: Db){returnnewUserService(db);}
// BAD: 매 hypothetical flexibility
typePipeline={stages: Array<{type:'transform'|'filter'|'aggregate';config: Record<string,unknown>;next?: string;}>;};// GOOD: explicit, refactor when actual variants exist
asyncfunctionprocessOrder(o: Order):Promise<Result>{constvalidated=validate(o);constpriced=price(validated);returnpersist(priced);}
Anti: unnecessary abstraction layer
// BAD: 5 layers for CRUD
// Controller → Service → Manager → RepositoryFacade → Repository → ORM
// GOOD: thin, until pain
// Route handler → query function (sql-tagged template)
Anti: future-proof generics
// BAD
classCache<K,V,EextendsCacheEvent<K,V>,SextendsStorage<K,V>>{}// GOOD
classUserCache{privatemap=newMap<string,User>();get(id: string){returnthis.map.get(id);}}
Tracking signal — if(false) / dead branch
// 매 over-engineered 흔적: 사용 안 된 code path
if(config.experimentalFastPath){// never set
returnfastCompute(input);}returnslowCompute(input);// 1년 째 fastPath은 비활성 → 매 즉시 삭제 (YAGNI)
매 결정 기준
상황
Approach
1번 사용
inline / concrete
2번 사용
duplicate (acceptable)
3번 사용
abstract (Rule of Three)
미래 요구 가능성
YAGNI — 미루기
외부 API · 장기 contract
매 abstraction 정당화
명확한 plugin 요구
early extension point OK
기본값: concrete · simple · explicit — 매 abstraction은 actual pain에서 추출.