시스템 비대화와 더하기 편향: 아키텍처 코드 감산과 안전한 폐기 리팩터링 전략

이 글에서 먼저 가져갈 세 가지
시스템에 문제가 생겼을 때 기존 코드를 덜어내기보다 또 다른 래퍼와 플래그를 덧붙이게 만드는 인간의 신경학적 인지 기전을 규명하고, 기술 부채의 증식을 물리적으로 차단하는 아키텍처 감산 프로토콜을 제시합니다.
-
01
인간의 본능적 더하기 편향(Additive Bias)의 기전
인간의 뇌는 문제 해결 시 기존 요소를 삭제하는 감산보다 새로운 계층을 덧붙이는 가산을 무의식적으로 먼저 떠올리며, 이는 소프트웨어 복잡성 폭증의 근본 원인이 됩니다. 본문 1·2절
-
02
기본값 반전과 PR 감산 비율(Subtractive Ratio) 강제
새 기능 1줄 추가 시 구식 경로 1줄 삭제를 의무화하고, PR 템플릿에 삭제 대상 컴포넌트를 필수 명시하도록 강제하여 팀의 인지적 주의를 감산 경로로 유도합니다. 본문 3·4절
-
03
만료된 피처 플래그의 30일 강제 툼스톤 소각 프로토콜
롤아웃이 완료된 조건 분기문이 영구 레거시로 굳어지지 않도록, 30일의 수명 주기를 갖는 툼스톤(Tombstone) 메커니즘을 파이프라인에 구축합니다. 본문 5절
엔지니어링 팀이 몇 년 동안 공들여 구축한 프로덕션 시스템을 열어보면, 예외 없이 기괴한 풍경을 마주하게 됩니다. 단순했던 사용자 인증 로직 주변에는 세 번의 리팩터링 흔적인 AuthServiceV2, LegacyAuthAdapter, OAuthCompatWrapper가 겹겹이 둘러쳐져 있고, 설정 파일에는 이미 2년 전에 전사 적용이 끝난 피처 플래그 수십 개가 여전히 살아서 if (isFeatureEnabled) 분기를 태우고 있습니다.
모든 개발자는 복잡한 코드를 증오하고 클린 코드를 열망합니다. 하지만 왜 현장의 모든 시스템은 시간이 지날수록 비대해지고, 그 누구도 과거의 코드를 과감하게 지우지 못하는 것일까요? 엔지니어들이 게으르거나 실력이 부족해서가 아닙니다. 그 기저에는 인간의 뇌가 문제를 해결할 때 본능적으로 작동하는 잔인한 신경인지적 오류가 자리 잡고 있습니다.
Adams 등의 연구진이 세계적인 학술지 Nature에 발표한 기념비적인 인지 심리학 연구(2021)는 이 잔혹한 진실을 실험적으로 증명했습니다. 인간은 문제 상황에 직면했을 때 기존의 것을 덜어내거나 제거하는(Subtractive) 해결책을 인지적으로 거의 떠올리지 못하며, 무의식적으로 새로운 요소를 덧붙이는(Additive) 해결책을 압도적으로 선호한다는 사실입니다.
소프트웨어 아키텍처에서 이 '더하기 편향'이 방치될 때 발생하는 대가는 참혹합니다. 본 글에서는 왜 엔지니어가 본능적으로 코드 삭제를 두려워하고 래퍼를 덧붙이는지 그 인지 회로를 해부하고, 시스템의 복잡성을 물리적으로 덜어내는 아키텍처 코드 감산(Subtractive Refactoring) 설계 표준과 툼스톤 프로토콜을 체계화합니다.
1. Nature 연구가 밝혀낸 '더하기 편향(Additive Bias)'의 신경인지적 기전
Adams 등의 Nature 연구(2021)에 따르면, 인간은 문제 해결 과제에서 요소를 덜어내거나 제거하는(Subtractive) 방식을 무의식적으로 간과하고 기존 구조 위에 새로운 요소를 덧붙이는(Additive) 해결책을 압도적으로 선호하는 '더하기 편향(Additive Bias)'을 보입니다.
연구진은 레고 블록 지붕 받치기, 격자 무늬 대칭 맞추기, 여행 일정 최적화 등 수천 명의 참가자를 대상으로 8가지의 다양한 인지 실험을 수행했습니다. 결과는 충격적이었습니다.
* 레고 구조물을 안정화하기 위해 블록 하나를 빼기만 하면 되는 상황에서도, 참가자의 대다수는 새로운 블록을 추가로 쌓아 올렸습니다.
* 참가자들에게 "블록을 뺄 수도 있다"는 명시적인 힌트를 주거나, 추가할 때마다 비용 페널티를 부과했을 때에만 비로소 감산(Subtraction)을 시도했습니다.
* 연구진은 참가자들에게 동시에 숫자 계산을 시키는 등 인지 부하(Cognitive Load)를 높이자, 덜어내는 아이디어를 떠올리는 비율이 극단적으로 바닥을 쳤습니다.
[ 문제 해결 시 인간 뇌의 인지적 탐색 경로 ]
┌─────────────────────────┐
│ 문제 상황 발생 인식 │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ 1차 무의식 탐색 단계 │ ──► [더하기(Addition) 휴리스틱 자동 점화]
└────────────┬────────────┘ • 새 래퍼 클래스 생성
│ • if-else 분기 추가
│ • 새 환경 변수/플래그 선언
│
(명시적 개입이 없으면 여기서 종료)
│
▼
┌─────────────────────────┐
│ 2차 고차 인지 개입 │ ──► [빼기(Subtraction) 가능성 탐색]
└─────────────────────────┘ • 불필요한 기존 분기 삭제
• 레거시 모듈 영구 폐기
• 아키텍처 계약 통합
왜 뇌는 '빼기'를 회피하는가?
인지과학적으로 덜어내기는 덧붙이기보다 훨씬 더 많은 정신적 연산 비용을 요구합니다:
1. 가용성 휴리스틱(Availability Heuristic): 무언가를 더하는 행동은 눈에 보이는 단계적인 물리적 결과물을 만들어냅니다. 코드를 추가하면 커밋 로그에 녹색 라인이 찍히고, 내가 무언가 일했다는 단계적인 보상 회로(도파민)가 자극됩니다.
2. 손실 회피(Loss Aversion)와 현상 유지 편향: 기존 코드를 삭제하는 것은 잠재적인 회귀 버그(Regression Bug)라는 '손실'의 공포를 직접적으로 자극합니다. "이 코드가 왜 있는지 100% 확신할 수 없으니, 차라리 옆에 새 코드를 짜고 if문으로 우회하자"라는 비겁한 안도감을 선택하게 됩니다.
3. 인지 부하 하에서의 검색 실패: 일정 압박과 장애 상황에 쫓기는 엔지니어는 뇌의 인지 자원이 고갈된 상태입니다. Nature 연구가 입증했듯, 인지 부하가 높을수록 뇌는 감산이라는 대안 자체를 검색 목록에서 아예 누락시켜 버립니다.
2. 소프트웨어 아키텍처에서 더하기 편향이 남기는 3대 파괴적 흔적
이 무의식적인 편향이 코드베이스에 누적되면, 아키텍처는 서서히 괴물로 변해갑니다. 현장에서 발견되는 가장 대표적인 3가지 악순환 패턴은 다음과 같습니다.
1) 분기문의 화석화 (Branch Accretion)
요구사항이 변경될 때 기존 로직을 수정하거나 통합하지 않고, 상단에 if (isNewPolicy) 조건을 계속해서 덧씌웁니다. 3년이 지나면 조건문 중첩 깊이가 5단계를 넘어가고, 그 어떤 테스트 케이스도 전체 실행 경로(Cyclomatic Complexity)를 커버하지 못하는 죽음의 영역이 탄생합니다.
2) 래퍼 지옥 (The Wrapper Hell)
API 명세를 현대화하거나 비동기 처리를 도입할 때, 레거시 인터페이스를 폐기하는 고통을 피하기 위해 어댑터(Adapter)와 파사드(Facade)를 무한히 감쌉니다. 결과적으로 데이터가 클라이언트에 도달하기까지 아무런 실질적 비즈니스 로직도 수행하지 않는 4~5개의 빈 껍데기 프록시 객체를 거치게 되며, 디버깅 스택 트레이스만 50줄로 늘어납니다.
3) 만료되지 않는 피처 플래그 (Zombie Feature Flags)
새 기능을 A/B 테스트하거나 카나리 배포하기 위해 도입한 피처 플래그가 전사 롤아웃 100%를 달성한 뒤에도 영원히 코드베이스에 방치됩니다. 플래그가 코드 곳곳에 거미줄처럼 얽혀 있어, 이후 새로운 기능을 개발하는 엔지니어는 "이 플래그가 꺼졌을 때의 동작"까지 고려해야 하는 정신적 고문을 당합니다.
3. 아키텍처 감산(Subtractive Refactoring) 설계의 3대 실행 원칙
자연스러운 인간의 본능을 거스르기 위해서는, 개인의 결단에 기대는 것이 아니라 감산을 강제하는 시스템적 가드레일(Systemic Guardrails)을 아키텍처 거버넌스에 심어야 합니다.
▲ 새 기능이 들어올 때 기존 레거시 코드를 감산 대상으로 식별하고, 30일 격리 버퍼(툼스톤)를 거쳐 완전 소각하는 4단계 아키텍처 감산 파이프라인.
원칙 1: 기본값의 반전 (Default-to-Subtraction in PR)
엔지니어링 리더십은 PR 템플릿에 '이 변경으로 인해 시스템에서 삭제되거나 단순화된 요소는 무엇인가?'라는 감산 질문을 기본 필수 항목으로 배치하여 팀의 인지적 주의를 감산 경로로 유도해야 합니다.
Nature 연구에서 "뺄 수도 있다"는 힌트 하나만으로 감산율이 급증했듯이, PR 작성 시점에 엔지니어에게 감산 여부를 묻는 넛지(Nudge)를 배치해야 합니다. 만약 삭제 항목이 '없음'이라면, 왜 기존 코드를 재사용하거나 통합하지 못하고 가산 경로를 택했는지 정당한 아키텍처 사유를 기술하도록 만듭니다.
원칙 2: 1 In, 1 Out 감산 비율 (Subtractive Ratio)
아키텍처 변경이나 기능 추가 시 '기존 레거시 코드 1줄 추가 시 최소 1개 이상의 구식 경로 삭제'를 강제하는 감산 비율(Subtractive Ratio)과 만료된 피처 플래그의 30일 강제 툼스톤 규칙을 도입해야 합니다.
새로운 모듈이나 기능을 추가하는 스프린트에서는 우선적으로 그 기능으로 인해 용도 폐기될 구식 코드나 미사용 API 엔드포인트를 식별하여 티켓 범위에 묶어야 합니다. 신규 기능 배포와 레거시 코드 삭제를 분리된 별도 작업으로 미루면, 레거시 삭제는 영원히 백로그의 무덤 속으로 사라집니다.
원칙 3: 부채의 물리적 가시화 (Net Code Debt Metric)
주간 엔지니어링 대시보드에 단순히 '배포된 기능 수'나 '커밋 수'를 올리지 마십시오. 이는 가산 편향을 부추기는 최악의 메트릭입니다. 대신 '순 감산 라인 수(Net Lines Deleted)'와 '제거된 피처 플래그 수'를 1급 건강 지표(Tier-1 Health Metric)로 추적하고, 가장 많은 레거시 코드를 안전하게 소각한 엔지니어와 팀을 공식적으로 포상하십시오.
테스트 주도 개발(TDD)의 창시자 켄트 벡은 "문제를 풀기 위해 코드를 5줄 추가했는데도 테스트가 깨진다면, 코드를 더 추가하지 말고 직전 상태로 완전히 되돌려라(Revert)"라고 조언합니다. 뇌가 더하기 편향에 갇혔을 때 가장 현명한 탈출구는 앞으로 나아가는 것이 아니라, 직전 커밋으로 롤백하여 문제 자체를 다시 정의하는 것입니다.
4. 툼스톤(Tombstone) 라이프사이클 프로토콜
레거시 코드를 삭제할 때 엔지니어가 느끼는 가장 큰 두려움은 "혹시 어디선가 이 비공식 인터페이스나 플래그를 아직 호출하고 있으면 어쩌지?"라는 불확실성입니다. 이 두려움을 해소하기 위해 30일 툼스톤(Tombstone, 묘비) 프로토콜을 적용합니다.
[ 30일 툼스톤 수명 주기 상태 전이도 ]
[ 활성 상태 (Active) ]
│
▼ (기능 전사 롤아웃 100% 도달)
[ 툼스톤 1단계: 메트릭 경고 (Warn Tombstone) - Day 1~14 ]
• 본문 로직 유지, 호출 시 'Deprecated' 경고 로그 및 메트릭 카운터 전송
│
▼ (외부 인입 호출 0건 확인)
[ 툼스톤 2단계: 예외 투척 (Fail-Fast Tombstone) - Day 15~30 ]
• 내부 로직 제거, 호출 시 즉시 410 Gone 또는 DeprecatedException 발생
│
▼ (30일 경과 및 장애 무발생)
[ 툼스톤 3단계: 완전 소각 (Incineration) - Day 31 ]
• CI 파이프라인에서 파일 및 분기문 물리적 영구 삭제 (git rm)
- 1단계: 경고 툼스톤 (Day 1~14):
- 폐기 대상 함수나 분기에 툼스톤 어노테이션을 부착합니다. 로직은 정상 동작하지만 호출될 때마다 메트릭 시스템(Prometheus, Datadog)에 호출 주체와 카운터를 쏩니다.
- 2단계: 하드 툼스톤 (Day 15~30):
- 2주 동안 메트릭이 0건임을 확인했다면, 내부 비즈니스 로직을 모두 비우고 호출 즉시 명시적인
TombstoneException을 던지도록 바꿉니다. 만약 숨어 있던 크론 잡이 이를 호출한다면 스테이징이나 운영 환경에서 신속하게 감지됩니다. - 3단계: 영구 소각 (Day 31):
- 30일 동안 아무런 이상이 감지되지 않았다면, 코드베이스에서 해당 파일, 클래스, 데이터베이스 컬럼을 완전히 삭제하는 PR을 머지합니다.
5. 실무 아키텍처 감산 PR 템플릿과 적용 가이드
엔지니어링 조직이 오늘 당장 GitHub 또는 GitLab에 적용할 수 있는 감산 중심형 Pull Request 템플릿 표준입니다.
### Subtractive Engineering PR Template
## 1. 변경 요약 (Change Summary)
- 구현된 기능/수정 사항: [간결한 기술]
- 아키텍처 결정 유형: [ ] 신규 가산 (Additive) [x] 기존 감산 (Subtractive) [ ] 구조적 치환 (Replacement)
## 2. 감산 검증 체크리스트 (The Subtractive Checklist)
- [ ] **삭제된 레거시 요소**: 이 PR로 인해 삭제된 파일, 클래스, 또는 if 분기는 무엇인가?
* 삭제된 대상: (예: `src/auth/legacy_token_validator.py` 영구 제거)
- [ ] **피처 플래그 정리**: 100% 롤아웃되어 이번에 제거된 플래그가 있는가?
* 제거된 플래그: (예: `ENABLE_V2_PAYMENT_FLOW` 조건문 완전 소각)
- [ ] **순 변경 라인 분석**:
* 추가된 라인(+): 120줄
* 삭제된 라인(-): 245줄 (순 감산 -125줄 달성)
## 3. 가산 정당성 설명 (Justification for Addition)
- 만약 기존 코드를 삭제하지 않고 오직 추가(+)만 했다면, 왜 기존 추상화를 통합하거나 덜어내지 못했는지 사유를 서술하십시오:
* 사유: (예: 외부 결제사 구 버전 웹훅 하위 호환성 유지를 위해 30일 툼스톤 격리 후 폐기 예정)
결론: 훌륭한 아키텍처는 더 이상 더할 것이 없을 때가 아니라, 뺄 것이 없을 때 완성된다
생텍쥐페리는 "완벽함이란 더 이상 보탤 것이 없을 때가 아니라, 더 이상 뺄 것이 없을 때 달성된다"라는 불멸의 격언을 남겼습니다. 이 말은 항공 역학뿐 아니라 현대의 분산 소프트웨어 아키텍처에도 정확히 들어맞는 진리입니다.
복잡성은 외부에서 침략해 오는 적이 아닙니다. 문제 앞에서 멈추어 서서 기존 것을 덜어내는 고통을 피한 채, 손쉬운 래퍼와 분기문을 덧붙여 온 우리 자신의 뇌가 저지른 게으른 타협의 결과물입니다.
진정한 시니어 엔지니어의 품격은 현란한 신기술 프레임워크를 끌어와 코드 줄 수를 늘리는 데 있지 않습니다. 복잡하게 얽힌 시스템의 핵심을 꿰뚫어 보고, 과감하게 불필요한 레이어를 걷어내며, 내가 작성한 코드보다 더 많은 코드를 시스템에서 지워내는 용기에서 비롯됩니다. 지금 열려 있는 에디터 창을 보십시오. 그 문제를 해결하기 위해 당신이 오늘 '지워야 할 코드'는 무엇입니까?
참고 자료
- Nature: People systematically overlook subtractive changes (Adams et al., 2021)
- Nature DOI: https://doi.org/10.1038/s41586-021-03380-y
- PubMed Central Abstract: People systematically overlook subtractive changes
- Martin Fowler: Strangler Fig Application & Code Deprecation Patterns
참고 자료 (References)
원문 참고 자료
이 글의 사실 확인과 추가 읽기를 위한 원문입니다. People systematically overlook subtractive changes (Nature 2021)
댓글 0