APC 및 APX에서 문서는 계약의 일부입니다
요약
문서를 단순한 설명이 아닌 코드와 동일한 '계약(contract)'으로 취급해야 한다는 개발 철학을 다룹니다. 문서와 코드의 불일치가 인간과 AI 에이전트 모두에게 미치는 부정적 영향을 경고하며, 이를 방지하기 위한 엄격한 관리 방식을 제안합니다.
핵심 포인트
- 문서는 마케팅 도구가 아닌 코드와 동등한 계약의 일부임
- 문서와 코드의 불일치는 AI 에이전트에게 잘못된 정보를 제공함
- 문서 업데이트를 코드 변경과 동일한 단계에서 수행해야 함
- 미러드 트리 구조를 통해 문서 간 불일치를 강제로 탐지할 수 있음
APC 및 APX에서 문서는 계약의 일부입니다
나는 예전에 문서를 정리 작업(cleanup)처럼 취급하곤 했습니다.
코드를 먼저 작성합니다. 동작을 수정합니다. 그러고 나서 시간이 남으면 무엇이 바뀌었는지 설명합니다.
이러한 습관은 프로젝트의 표면적(surface area)이 충분히 넓어져서 문서가 마치 두 번째 구현체(implementation)처럼 작동하기 시작할 때까지는 괜찮습니다. 그 시점이 되면, 오래된 산문(stale prose)은 무해하지 않습니다. 그것은 문법만 더 나은 버그입니다.
APC와 APX는 저를 다른 규칙으로 몰아넣었습니다: 문서는 계약 (contract)의 일부입니다. 마케팅이 아닙니다. 나중에 생각할 거리가 아닙니다. 계약입니다.
여러 표면(surfaces)에 걸쳐 실제 프로젝트의 정직함을 유지하려고 노력하기 전까지는 이 말이 당연하게 들립니다. APC에는 사양(spec), 리포지토리 소유 컨텍스트(repo-owned context), 그리고 호환성 계층(compatibility layer)이 있습니다. APX에는 런타임(runtime), CLI, 데몬(daemon), 웹 관리자(web admin), 그리고 ~/.apx/ 아래의 로컬 상태(local state)가 있습니다. 제가 하나의 표면을 다른 표면으로부터 벗어나게 방치하는 순간, 사람들과 에이전트(agents)는 동일한 시스템에 대해 두 가지 진실을 배우기 시작합니다.
그것은 잘못된 종류의 유연성입니다.
왜 문서를 별개의 작업으로 보지 않게 되었는가
전환점은 간단했습니다. 문서가 코드보다 뒤처지게 만들 때마다, 미래의 내가 동일한 답을 두 번 다시 도출하도록 강요하고 있다는 사실을 깨달았습니다.
한 번은 구현(implementation)으로부터.
한 번은 텍스트로부터.
이는 작은 리포지토리에서는 낭비이고, 성장하는 리포지토리에서는 비용이 많이 드는 일입니다.
APC/APX의 경우, 대상이 인간뿐만이 아니기 때문에 비용이 더 큽니다. 에이전트(Agents)들도 이 파일들을 읽습니다. 만약 README가 한 가지를 말하고 코드가 다른 것을 수행한다면, 모델은 "영리하게 행동하는 것"이 아닙니다. 그것은 나의 오래된 계약(stale contract)에 의해 오도되는 것입니다.
그래서 저는 기본 설정을 바꿨습니다.
변경 사항이 사용자에게 보이는 동작을 변경한다면, 저는 동일한 단계에서 문서를 업데이트합니다. 만약 어떤 페이지가 워크플로우(workflow)를 설명한다면, 저는 그 페이지를 퇴보(regress)할 수 있는 코드처럼 취급합니다. README에 명령어가 존재한다면, 저는 그 명령어가 작동할 것이라고 기대합니다. 만약 어떤 문서에서 파일이 .apc/에 있다고 말한다면, 실제로 다른 곳에 있어서는 안 됩니다.
이 규칙은 엄격하게 들립니다. 실제로 그렇습니다. 하지만 또한 시간을 절약해 줍니다.
미러드 트리(mirrored-tree) 규칙이 문제를 강제하다
가장 명확한 예 중 하나는 APC의 문서 구조입니다.
APX 프로젝트 규칙은 미러드 로케일 트리(mirrored locale trees)를 요구합니다: src/pages/en/과 src/pages/es/는 구조적으로 동일하고 의미론적으로 동등(semantically equivalent)하게 유지되어야 합니다. 동일한 페이지, 동일한 형태, _meta.js 내의 동일한 키, 그리고 로케일에 맞춘 내부 링크(internal links)가 필요합니다.
이것은 단순히 절차를 복잡하게 만드는 것이 아닙니다. 이는 내장된 드리프트 탐지기(drift detector)입니다.
만약 제가 영어 페이지를 추가하고 스페인어 버전을 잊어버린다면, 빌드(build) 과정에서 오류가 발생해야 합니다. 한 로케일의 섹션 이름을 변경하고 다른 로케일을 그대로 둔다면, 그 불일치는 단순한 번역 문제가 아닙니다. 그것은 계약 위반(contract break)입니다.
이러한 점은 제가 문서화 작업을 생각하는 방식을 바꾸어 놓았습니다.
이전에는 번역을 마지막 단계의 작업(late-stage pass)으로 생각했습니다.
이제 저는 미러드 문서(mirrored docs)를 설계 제약 조건(design constraint)으로 생각합니다.
이 제약 조건은 시스템을 정직하게 만들기 때문에 유용합니다. 단 한 그룹의 대상에게만 변경 사항이 적용되었을 때, 무심코 "문서가 업데이트되었습니다"라고 말할 수 없게 만듭니다. 저는 두 뷰(view)를 정렬된 상태로 유지해야 하며, 이는 도구와 상호작용하는 프로젝트라면 당연히 요구해야 할 사항입니다.
빌드 출력물은 단순한 패키징이 아니다
저는 또한 빌드를 배포를 위한 번거로운 작업(deployment chore)으로 취급하는 것을 그만두었습니다.
빌드는 드리프트(drift)를 조기에 포착하는 지점입니다.
APC의 경우, 규칙은 명시적입니다: 문서 작업을 완료했다고 간주하기 전에 npm run build를 실행해야 합니다. APX 문서의 경우, 동일한 개념이 다른 형태로 나타납니다: 문서 사이트를 빌드하고 두 로케일 트리가 모두 렌더링되는지 확인하는 것입니다. 이것은 오버헤드(overhead)가 아닙니다. 계약이 여전히 컴파일(compiles)되는지를 확인하는 검증(verification)입니다.
저는 이러한 프레이밍(framing)을 좋아하는데, 그 이유는 문서가 코드와 동일한 사고 범주(mental bucket)에 머물게 하기 때문입니다:
- 소스 변경은 리포지토리(repo)에서 발생합니다.
- 투영(projections)은 해당 소스로부터 렌더링됩니다.
- 빌드는 투영이 여전히 일치하는지 확인합니다.
이것은 제가 프로젝트의 다른 부분에서 사용하는 것과 동일한 로직입니다.
APC는 리포지토리가 소유한 계약(repo-owned contract)입니다.
APX는 이를 소비하는 런타임(runtime)입니다.
렌더링된 문서 사이트는 또 다른 투영(projection)입니다.
만약 어떤 투영이라도 스스로만의 진실을 만들어내기 시작한다면, 시스템을 신뢰하기가 더 어려워집니다.
진짜 버그는 오래된 문장이 아니다
진짜 버그는 불일치하는 동작(mismatched behavior)입니다.
오래된 README는 누군가에게 잘못된 명령어를 가르칠 수 있습니다.
오래된 로케일(locale) 페이지는 청중의 절반에게 특정 기능을 숨길 수 있습니다.
오래된 명세(spec)는 에이전트(agent)가 프로젝트 상태(project state)와 런타임 상태(runtime state) 사이의 잘못된 경계를 추론하게 만들 수 있습니다.
APC 및 APX에서 이러한 실수들은 더 중요하게 다뤄지는데, 그 이유는 프로젝트의 핵심 목적이 경계를 명확하게 만드는 것이기 때문입니다:
- 프로젝트(project) vs 런타임(runtime)
- 저장소의 진실(repo truth) vs 로컬의 진실(local truth)
- 계약(contract) vs 실행(execution)
- 소스(source) vs 투영(projection)
만약 문서가 이러한 경계들을 흐릿하게 만든다면, 그것은 시스템을 문서화하는 것이 아니라 시스템을 망가뜨리고 있는 것입니다.
그렇기 때문에 저는 이제 코드에 사용하는 것과 동일한 질문을 던지며 문서를 작성합니다: 여기서 실제 진실의 근원(source of truth)은 무엇인가?
만약 그 질문에 명확히 답할 수 없다면, 아직 그 문장을 게시해서는 안 됩니다.
내 워크플로(workflow)에서 바뀐 점
실질적인 효과는 지루한 수준이지만, 바로 그 점 때문에 효과가 있습니다.
이제 APC나 APX를 수정할 때, 저는 즉시 세 가지를 자문합니다:
- 동작(behavior)이 변했는가, 아니면 구현 세부 사항(implementation details)만 변했는가?
- 동작이 변했다면, 어떤 문서가 지금 동일한 변경 사항을 반영해야 하는가?
- 빌드(build) 결과가 문서가 여전히 저장소(repo)와 일치함을 증명하는가?
이를 통해 드리프트(drift, 괴리)를 유발하는 일반적인 "나중에 수정하기" 식의 행태를 방지합니다.
또한 글쓰기가 더 쉬워졌습니다. 저는 더 이상 사후에 다듬어진 설명을 쓰려고 애쓰지 않습니다. 변경 사항이 여전히 생생하고, 중요했던 트레이드오프(tradeoff)를 여전히 명명할 수 있을 때 설명을 작성합니다.
APX와 같은 프로젝트에서는 가치의 상당 부분이 단순한 기능이 아닌 결정(decisions)에 있기 때문에 이는 매우 중요합니다.
왜 ~/.apx/가 존재하는가? 런타임 상태(runtime state)는 로컬에 머물러야 하기 때문입니다.
왜 AGENTS.md가 중요한가? 도구에 구애받지 않는 컨텍스트(tool-agnostic context)에는 하나의 공유된 계약(contract)이 필요하기 때문입니다.
왜 웹 관리자(web admin)가 존재하는가? 그것은 별개의 제품이 아니라 동일한 런타임으로 들어가는 창이기 때문입니다.
이것들은 슬로건이 아닙니다. 설계 결정(design decisions)입니다. 문서는 바로 그 형태를 보존해야 합니다.
내가 지키고 있는 규칙
제 규칙은 간단합니다:
만약 어떤 문장이 APC나 APX가 어떻게 작동하는지 설명한다면, 그 문장은 제품의 일부입니다.
그 말은 제가 코드에 쏟는 것과 동일한 주의를 기울여 문서를 유지 관리해야 함을 의미합니다.
산문(prose)이 신성하기 때문이 아닙니다.
문서가 예쁘기 때문도 아닙니다.
모든 접점(surface)이 동일한 이야기를 전달할 때만 프로젝트가 일관성(coherent)을 유지할 수 있기 때문입니다.
그리고 제가 문서를 그런 방식으로 다루기 시작하자, APC와 APX를 추론(reason about)하기가 더 쉬워졌습니다.
더 적은 괴리(drift).
더 적은 재작업(rework).
더 적은 거짓된 정보(fake truths).
그것이 전부입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기