Characterize 모드 — 레거시 코드 안전망¶
한 줄 요약
테스트가 없는 기존 코드를 리팩터하기 전에, 현재 구현된 실제 동작을 그대로 test로 고착화하여 안전망을 확보하는 모드입니다. 구현 코드는 변경하지 않고 ({source_root} 수정 금지) test만 작성해 회귀 방지(regression guard) 안전망을 구축합니다.
Characterize 모드란¶
해결하려는 문제¶
리팩터링하려는 대상 코드에 test가 존재하지 않는 경우, 이름을 변경하거나 구조를 분리하고 의존성을 업그레이드하는 등의 모든 작업에서 예기치 못한 부작용(regression)이 발생할 위험이 있습니다. 그렇다고 일반적인 test를 새로 작성하기도 어렵습니다. 해당 코드가 어떻게 동작해야 하는지 에 대한 명확한 사양서가 부재하기 때문입니다. 이때는 현재 코드의 실제 동작 자체가 사실상의 명세(Specification)가 됩니다. 설령 그 안에 버그가 포함되어 있거나 타 모듈이 그 버그에 의존하고 있더라도 마찬가지입니다.
해결 방식: 현재 동작의 포착 및 고정¶
Characterize 모드는 characterization test 를 작성하여 해결합니다. 코드가 원래 수행해야 하는 동작이 아니라, 현재 실제로 어떻게 동작하고 있는지 를 포착하여 고정하는 test입니다. 흐름은 다음과 같습니다:
- 대상 코드를 수정 없이 실행합니다.
- 실제로 출력되는 결과값과 사이드 이펙트(side effect)를 관찰합니다.
- 관찰된 실제 값을 그대로 assertion으로 작성합니다. (이 test는 작성 시점에 항상 통과합니다)
이후 리팩터링 등으로 인해 기존 동작에 변화가 생기면 test가 깨지게 되며, 이를 통해 의도치 않은 변경 사항을 즉시 감지할 수 있습니다.
TDD와의 비교¶
| 구분 | TDD | Characterize |
|---|---|---|
| 검증 대상 | 코드가 원래 해야 할 동작 | 코드가 현재 실제로 수행하는 동작 |
| 작성 시점 | 구현 전 (Red 상태 정의) | 코드 관찰 후 (측정된 결과를 기록) |
| 버그 발견 시 | 즉시 수정함 | 버그가 포함된 동작까지 그대로 포착함 |
| 성격 | project 전반에 걸친 지속적인 개발 방법론 | 리팩터 전 안전망 확보를 위한 임시 모드 |
가장 큰 차이는 버그 조차도 현재 동작의 일부로 포착한다는 점입니다. characterization test의 목적은 코드를 바로잡는 것이 아니라 현재 상태를 그대로 보존 하는 것입니다. 버그 수정 및 개선 작업은 이 안전망이 완전히 확보된 이후의 별도 cycle에서 진행되어야 합니다. 그래야 "의도한 수정"과 "실수로 유발된 회귀(regression)"를 정확히 구분할 수 있습니다.
characterization test 실패의 의미
test가 실패했다는 것은 코드가 틀렸다는 의미가 아니라, 이전 동작과 비교해 동작이 변경되었음을 의미합니다. 의도한 변경이라면 contract와 test를 함께 업데이트하고, 의도치 않은 변경이라면 코드를 이전 상태로 롤백해야 합니다.
구현 코드를 보존해야 하는 이유¶
안전망을 구축하는 과정에서 {source_root} 아래의 코드를 수정하지 않는 것이 신뢰성을 담보하는 핵심입니다. 안전망 작성 도중 구현 코드를 함께 변경해 버리면, test가 포착한 대상이 기존 동작인지 아니면 방금 수정한 동작인지 모호해집니다. 구현에 전혀 손대지 않아야만 "이 test들이 변경 이전의 원본 동작을 보장한다"는 사실을 확보할 수 있습니다. Generator가 test 헬퍼나 fixture 등의 test layer 코드를 변경하는 것은 허용되지만, {source_root} 내부의 제품 코드는 단 한 줄도 수정해서는 안 됩니다. 이 잠금은 이중으로 강제됩니다 — scope-guard.sh 훅이 Edit/Write 시도 시점에 {source_root} 하위 수정을 사전 차단하고 (test_path_convention 경로는 허용), Evaluator가 git diff를 통해 사후 재검증합니다.
언제 사용하는가¶
핵심 원칙: "조만간 수정할 코드인데 test 안전망이 없다면, 코드에 손을 대기 전에 바로 시작합니다."
구체적인 시작 시점 예시:
- test가 없는 코드를 리팩터링 및 구조 변경하기 직전
- 의존성 라이브러리 및 프레임워크를 업그레이드하기 직전 (부작용 감지용)
- test가 없는 코드의 특정 버그를 수정하기 직전 (주변 동작을 고정하여 파급 효과 감지)
- 모르는 레거시 코드를 인수하여 동작을 파악해야 할 때 (test 자체가 실행 가능한 명세서 역할 수행)
- planner 또는 critic 단계에서 "해당 작업이 test 없는 레거시 영역을 변경하므로 회귀 리스크가 높다"고 판단될 때 (본 작업에 앞서 characterize cycle을 선행적으로 실행)
반드시 변경 전에 시작
Characterize 모드는 반드시 코드를 수정하기 전에 적용해야 합니다. 이미 수정한 이후에 시작하면, 포착된 내용은 기존 동작이 아닌 이미 변경된 동작이 되어 안전망의 역할을 하지 못합니다.
사용하지 않아도 되는 예외 상황:
- 신규 코드를 처음부터 작성하는 경우 -> TDD 모드로 진행하는 것이 올바릅니다.
- 대상 코드에 이미 신뢰할 수 있는 test가 충분히 작성되어 있는 경우
- 코드를 단순 분석/조회만 하고 변경할 계획이 없는 경우
전제 조건¶
- 활성화된 project가 있고, 관찰 대상이 되는 기존 구현 코드가 이미 존재해야 합니다.
config.md에test_command와source_root설정이 정의되어 있어야 합니다 (자세한 내용은 워크스페이스 설정 참고).
작업 절차¶
1. Characterize 모드로 전환¶
이 명령을 실행하면 .agent-state.yml 의 mode: characterize 로 설정됩니다. 이후 cycle에서 세 에이전트의 역할과 동작이 다음과 같이 전환됩니다:
- Planner — Characterization Contract를 수립합니다. 포착 단계를 쪼개고 각 단계별 입력 / 현재 출력 / 관찰된 사이드 이펙트를 정의하되, "현재 출력" 값은 비워둡니다. Planner가 출력을 예상하여 채워 넣으면 명세를 자의적으로 추측하는 꼴이 되어 안전망을 오염시키므로, 실제 동작 출력값은 Generator가 코드를 직접 실행한 결과로 채우도록 유도합니다.
- Generator —
{source_root}아래의 코드는 수정하지 않은 채 대상 코드를 실행하고, 관찰된 출력과 사이드 이펙트를 test assertion으로 작성합니다. 각 단계에 실행 명령, 성공 시간, 소스 코드 미수정 여부를 증빙하는[Captured]기록을 남깁니다. - Evaluator —
{source_root}소스 코드가 실제로 변경되지 않았는지 (git diff가 비어 있는지), 작성된 test가 정상적으로 통과하는지, contract에 정의된 입력/사이드 이펙트가 실제 test 검증 구조와 정확히 일치하는지 검증합니다. 커버리지 수치보다는 회귀 방지 여부를 주 기준으로 판정합니다.
2. feature 추가 및 cycle 실행¶
/pilot:create-feature "결제 취소 시 환불 정책 포착"
@pilot-planner
@pilot-planner-critic # 선택사항 (권장)
@pilot-generator
@pilot-evaluator
수많은 대상을 한 번에 포착하려 하지 말고, 진입점(메소드, 엔드포인트, CLI 명령 등) 단위로 feature를 쪼개어 반복 실행합니다. tdd 와 characterize 설정이 동시에 켜져 있는 경우에는 characterize 모드가 우선적으로 적용되어 안전망부터 확보합니다.
추적하기 어려운 사이드 이펙트
비동기 큐 전송, 캐시 만료, 아웃바운드 웹훅 등은 테스트 환경에서 자동으로 탐지하기 어려울 수 있습니다. 처음부터 완벽한 계약을 작성하려 애쓰기보다는, contract 내에 "자동 검증 제외 영역"으로 표시한 뒤 cycle을 반복하면서 파악되는 대로 보완해 나갑니다.
3. 안전망 확보 후 일반 모드 복귀¶
충분히 기존 동작이 test로 고정되었다고 판단되면 모드를 복구합니다:
mode 설정을 비활성화합니다. 이후 cycle은 표준 방식으로 복귀(또는 tdd 활성화 시 TDD 모드로 전환)하며, 이때부터 안심하고 본격적인 리팩터링이나 기능 수정을 진행합니다. 직전에 생성해 둔 characterization test가 리팩터링의 튼튼한 안전망이 되어 줍니다.
다음 단계¶
- Reference:
/pilot:characterize· Characterize 모드 정책 - Explanation: 모드 — Standard / TDD / Characterize
- How-to: 안전망 구축이 완료되면 TDD 모드 로 복귀하여 실 구현 리팩터링을 진행하는 흐름을 추천합니다.