에이전트를 위한 명세서(Specs) 작성을 중단하세요
요약
에이전트에게 전달하는 단일 명세서(Spec) 방식의 위험성을 경고하며, 정보의 수명에 따른 계층적 문서화 전략을 제안합니다. 제품 요구사항, 아키텍처, 구현 계획, 계약을 분리하여 관리해야 에이전트가 잘못된 정보를 바탕으로 코드를 생성하는 것을 방지할 수 있습니다.
핵심 포인트
- 단일 명세서는 구성 요소별로 다른 변화 속도 때문에 빠르게 부패함
- 부패한 명세서는 에이전트가 잘못된 시스템을 구축하도록 유도함
- 제품 요구사항은 티켓 시스템(Jira, Linear 등)에서 관리해야 함
- 아키텍처 결정은 ADR(Architecture Decision Records)을 통해 Git에서 관리 권장
명세서 기반 개발 (Spec-driven development)이 주목받고 있습니다. 상세한 명세서를 작성하여 에이전트에게 전달하면, 작동하는 코드를 얻게 됩니다. 이는 깔끔한 이야기이며, 우리가 이미 요구사항을 생각하는 방식과 깔끔하게 맞아떨어집니다.
하지만 저는 이것이 잘못된 형태라고 생각하며, 그 실패 모드 (failure mode)는 6개월이 지나기 전까지는 명확히 드러나지 않습니다.
명세서는 네 가지 서로 다른 속도로 부패합니다
명세서의 실제 문제는 이것이 하나의 문서가 아니라는 점입니다. 그것은 트렌치코트를 입은 네 개의 문서입니다.
전형적인 명세서에는 다음 내용들이 모두 뒤섞여 있습니다:
- 우리가 이것을 만드는 이유 — 제품 요구사항 (product requirement).
- 시스템이 어떻게 구조화되어 있는가 — 아키텍처 결정 (architectural decision).
- 작업의 순서 — 구현 계획 (implementation plan).
- "완료"가 어떤 모습인가 — 계약 (contract).
이 각각은 완전히 다른 반감기를 가집니다.
제품 요구사항은 고객이 불만을 제기할 때 변경됩니다. 아키텍처 결정은 수년간 유지되며, 설령 변경되더라도 그 기록은 여전히 중요합니다. 왜 예전 것이 그런 모습이었는지 누군가 물어볼 것이기 때문입니다. 구현 계획은 PR (Pull Request)이 머지(merge)되는 순간 죽습니다. 계약은 실행 가능할 때만 의미가 있습니다. 그렇지 않은 모든 것은 거짓말이 될 뿐이기 때문입니다.
이것들을 하나의 파일에 넣으면, 그 파일은 가장 빠르게 부패하는 구성 요소의 속도로 쇠퇴합니다. 몇 번의 스프린트 (sprint)가 지나면, 그 문서는 30%의 정확도만을 갖게 됩니다. 이는 가장 최악의 상태입니다. 자신 있게 틀린 상태가 되며, 이제는 맞았던 부분과 구별할 수 없게 됩니다.
인간만이 그것을 읽을 때도 충분히 나빴습니다. 이제는 에이전트가 그것을 그라운드 트루스 (ground truth)로 받아들이고 있습니다. 오래된 명세서는 Slack에서 후속 질문을 던질 신입 사원을 혼란스럽게 하는 데 그치지 않습니다. 그것은 에이전트가 더 이상 존재하지 않는 시스템을 기반으로 구축하도록 적극적으로 유도하며, 에이전트는 알 방법이 없기 때문에 반박하지도 않을 것입니다.
문서화는 주제가 아니라 수명에 따라 계층화되어야 합니다.
제가 실제로 운영하는 방식은 다음과 같습니다.
레이어 1: 제품 요구사항 → 티켓 시스템
GitHub Issues, Linear, Jira. 더 큰 조직 내에서는 PRD 및 RFC.
이것들은 _변동(churn)_되도록 설계된 것입니다. 그것은 결함이 아니라, 본연의 역할입니다. 티켓에는 종료 상태(closed states), 담당자(owners), 타임스탬프(timestamps)가 있어 그 노후화(staleness)를 명확히 읽을 수 있습니다. 8개월 전의 티켓은 눈에 띄게 8개월 전의 티켓임을 알 수 있습니다.
결정적으로: 이 레이어는 리포지토리(repo)에 속해서는 안 됩니다. 제품 요구사항을 코드 옆의 마크다운(markdown) 파일로 미러링(mirror)하는 순간, 서로 갈라지게 될 두 개의 진실의 원천(sources of truth)을 만들게 됩니다. 그리고 git에 있는 쪽이 패배하게 될 것입니다. 왜냐하면 아무도 마크다운 파일을 닫지(close) 않기 때문입니다.
레이어 2: 아키텍처 결정(Architectural decisions) → git 내의 ADRs
이 레이어는 버전 관리(version control)에 들어갈 자격이 실제로 있는 레이어입니다.
저는 MADR을 사용합니다. 각 결정은 번호가 매겨진 파일로 관리됩니다: 문맥(context), 고려된 옵션들(options considered), 결정(decision), 결과(consequences). 결정 사항은 새로운 현실을 반영하기 위해 수정되지 않습니다. 대신 이전 기록을 참조하는 새로운 기록에 의해 대체(superseded)됩니다.
여기서 도출되는 두 가지 사항은 에이전트(agents)에게 엄청나게 중요합니다:
추가 전용(append-only) 방식이므로, 부패(rot)할 수 없습니다. ADR은 특정 시점에 내려진 결정을 설명합니다. 구조적으로 영원히 정확합니다. "우리는 이러한 이유로 2024년 3월에 DynamoDB 대신 Postgres를 선택했다"라는 사실은 마이그레이션(migrate)을 한다고 해서 사실이 아니게 되지 않습니다. 단지 0017번을 대체(superseded)한다고 표시하는 ADR-0042를 추가할 뿐입니다.
에이전트가 추론할 수 없는 부분인 '이유(why)'를 포착합니다. 에이전트는 당신의 코드를 읽고 그것이 '무엇(what)'을 하는지 재구성할 수 있습니다. 하지만 왜 당신이 명백한 대안을 거부했는지는 재구성할 수 없습니다. 그것이 당신이 에이전트에게 전달할 수 있는 가장 가치 있는 문맥(context)이며, 명세서(specs)가 구현 세부 사항(implementation detail) 아래에 묻어버리는 바로 그 지점입니다.
에이전트가 "여기에 큐(queue)를 추가해야 할까요?"라고 물을 때, ADR 디렉토리가 그 답이 됩니다.
레이어 3: 구현 계획(Implementation plans) → 일시적(ephemeral), 절대 커밋하지 않음
이 부분이 제가 명세서 중심(spec-driven)의 사람들과 가장 날카롭게 대립하는 지점입니다.
구현 계획은 스캐폴딩(scaffolding, 발판)입니다. 그것은 "우리가 접근 방식에 합의했다"에서 "코드가 작동한다"로 넘어가기 위해 존재합니다. 그 후에는 사라져야 합니다.
현대의 에이전트(Agents)들은 이제 이를 네이티브하게 처리합니다. Claude와 Codex 모두 내부적으로 작동하는 계획(plans)을 유지하며 이를 바탕으로 반복(iterate)합니다. 그 전에는 제가 todo.md를 작성하고, 이를 수행한 뒤 삭제하곤 했습니다. 방식은 같지만, 사용성(ergonomics)은 더 나빴습니다.
중요한 점은 그것이 git에 남지 않아야 한다는 것입니다. 커밋된 구현 계획(implementation plan)은 지도처럼 보이는 화석입니다. 6개월 뒤 누군가 — 혹은 어떤 에이전트가 — 그것을 시스템에 대한 설명으로 읽게 되는데, 사실 그것은 단지 어느 오후의 상황을 설명한 것에 불과했습니다.
레이어 4: 계약(Contracts) → 블랙박스 통합 테스트 (black-box integration tests)
산문(Prose)은 계약을 정의할 수 없습니다. 산문은 계약을 설명할 뿐이며, 그 설명은 점차 어긋나게(drift) 됩니다.
계약 수준에서 실제로 제 코드를 정의하는 것은 외부에서 내부로 코드를 실행하는 통합 테스트(integration tests) 세트입니다. 내부 모킹(internal mocks)이나 프라이빗 상태(private state)에 접근하는 방식은 사용하지 않습니다. 공개 인터페이스(public surface)를 호출하고, 관찰 가능한 동작(observable behavior)을 검증(assert)합니다.
이것이 인터페이스 우선 설계(interface-first design)입니다. 이는 동시에 세 가지를 제공합니다:
- 낡은 상태가 될 수 없는 (cannot go stale) 명세(specification). 왜냐하면 명세가 어긋나면 CI(지속적 통합)가 실패하기 때문입니다.
- 리팩터링 경계(refactoring boundary) — 블랙박스(black box)가 유지되는 한 내부 구현은 자유롭게 변경될 수 있습니다.
- 에이전트가 인간의 개입 없이 스스로 실행할 수 있는 검증 루프(verification loop).
마지막 항목은 과소평가되어 있습니다. 실행 가능한 계약(executable contracts)을 가진 에이전트는 스스로 교정(self-correct)할 수 있습니다. 산문 명세(prose spec)를 가진 에이전트는 자신이 준수했는지 추측할 수밖에 없으며, 어떤 경우든 성공했다고 보고할 것입니다.
이것은 TDD가 아닙니다. TDD는 구현 전에 테스트를 작성하고 정답(correctness)을 미리 정의하는 것을 의미합니다. 때로는 그것이 옳습니다. 하지만 종종 충분히 구축하여 어디가 잘못되었는지 느껴질 때까지는 올바른 인터페이스를 알 수 없으며, 성급하게 테스트 세트에 매달리는 것은 코드 대신 테스트를 리팩터링하게 될 뿐입니다. 제가 주장하는 것은 더 좁은 범위입니다: 머지(merged)될 때쯤에는, 계약이 실행 가능한 블랙박스 테스트로 표현되어 있어야 한다는 것입니다. 그 과정에 어떻게 도달했는지는 여러분의 문제입니다.
이것이 실제로 어떻게 구현되는가
| 계층 (Layer) | 거주 위치 (Lives in) | 수명 (Lifetime) | 읽는 주체 (Who reads it) |
|---|---|---|---|
| 제품 요구사항 (Product requirements) | 티켓 시스템 (Ticket system) | 끊임없이 변화함 (Churns constantly) | 주로 인간 (Humans, mostly) |
| ... |
네 가지 산출물 (Artifacts). 네 가지 위치. 네 가지의 붕괴율 (Decay rates), 각각은 이를 처리할 수 있는 매체에 맞춰져 있습니다.
이 자체만으로는 새로운 것이 없습니다. ADR (Architecture Decision Records)은 10년 전부터 있었고, 블랙박스 테스트 (Black-box testing)는 그보다 더 오래되었습니다. 핵심 주장은 분리 그 자체에 있습니다. 즉, 계층화가 곧 방법론이며, 이를 단일 명세서 (Spec document)로 통합하는 것이 여러분의 에이전트 컨텍스트 윈도우 (Context window)를 조용히 오염시키는 원인이라는 것입니다.
불편한 부분
명세서 기반 개발 (Spec-driven development)의 제안은 문서 하나를 작성하면 에이전트가 나머지를 수행한다는 것입니다. 제가 설명하는 방식은 초기에 더 많은 작업이 필요합니다. 티켓 시스템을 유지 관리하고, ADR을 작성하며, 실제 통합 테스트 (Integration tests)를 구축해야 합니다.
하지만 여러분은 어차피 이 세 가지를 모두 수행할 예정이었습니다. 명세서 기반 개발은 그 작업을 없애는 것이 아닙니다. 오히려 소유자도 없고 만료일도 없는 다섯 번째 산출물로 그 작업을 복제한 다음, 그 산출물이 최신 상태가 아니라는 것을 구분하지 못하는 기계에게 그 산출물을 먹이는 것입니다.
문서화를 줄이세요. 각 조각을 여러분에게 거짓말을 할 수 없는 곳에 두세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기