레포지토리가 곧 컨텍스트다: 에이전트에게 히스토리가 필요 없는 이유
요약
코딩 에이전트에게 과거의 히스토리 문서보다 현재의 레포지토리 상태가 더 중요한 진실의 원천(Source of Truth)임을 강조합니다. 에이전트가 불필요한 과거 기록에 의존해 오류를 범하지 않도록 현재의 스키마, API, 구조 등 검증된 사실 위주의 컨텍스트를 제공해야 합니다.
핵심 포인트
- 에이전트에게 히스토리는 인간을 위한 것이며, 현재의 상태가 에이전트를 위한 진실의 원천임
- 과거 문서에 대한 과도한 의존은 에이전트의 잘못된 행동과 환각을 유발할 수 있음
- 토큰 효율성은 단순히 양을 줄이는 것이 아니라 '무엇을 넘겨줄 것인가'의 문제임
- 에이전트 편집 작업에는 현재의 스키마, API, 모듈 경계 등 검증된 사실이 필요함
히스토리(History)는 여전히 중요합니다. 다만 저는 그것이 현재 시스템을 편집하는 에이전트의 기본 컨텍스트(Context)가 되는 것을 원하지 않을 뿐입니다.
코딩 에이전트는 당신이 눈앞에 놓아주는 것은 무엇이든 읽을 것입니다.
그것은 장점처럼 들립니다. 하지만 대부분의 경우 그것이 문제입니다.
한동안 저의 해답은 "더 많은 마크다운 (Markdown)"이었습니다. CLAUDE.md, AGENTS.md, 사양서 (specs), ADR (Architecture Decision Records), 계획 문서 (planning docs) 같은 것들 말이죠. 그것은 책임감 있게 느껴졌습니다. 에이전트에게는 컨텍스트가 필요하니, 컨텍스트를 제공하면 된다고 생각했기 때문입니다.
그러다 저는 한 에이전트가 아주 자신 있게 틀린 행동을 하는 것을 목격했습니다. 이미 몇 달 전에 사라진 시스템을 설명하는 문서를 충실히 신뢰했기 때문입니다. 그것은 환각 (hallucination)이 아니었습니다. 히스토리에 대한 복종이었습니다.
그때 저의 질문은 바뀌었습니다. "어떤 프롬프트 (prompt)를 작성해야 할까?"가 아니라, 그 밑에 깔린 더 조용한 질문인 "에이전트가 무엇을 읽어야 하는가?"로 말이죠. 저는 최근에 토큰 효율성 (token efficiency)은 사실 "무엇을 넘겨줄 것인가"의 문제라고 썼습니다. 각 토큰은 추측이 아닌 검증된 사실을 담아야 합니다. 이 포스트도 같은 아이디어를 다루고 있으며, 그 대상을 레포지토리 (repo) 자체로 향하고 있습니다.
히스토리는 인간을 위한 것이다. 현재의 진실의 원천 (source of truth)은 에이전트를 위한 것이다.
이것은 히스토리를 유지하는 것에 반대하는 주장이 아닙니다.
저는 여전히 ADR, 마이그레이션 로그 (migration logs), 오래된 사양서, 그리고 계획 문서들이 가치 있다고 생각합니다. 그것들은 시스템이 왜 현재의 형태를 갖게 되었는지 — 트레이드오프 (tradeoffs), 제약 조건 (constraints), 포기된 경로들, 조직의 기억 등을 설명해 줍니다. 인간에게는 그것이 필요합니다. 결정을 검토하거나, 도메인에 온보딩하거나, 이상한 제약 조건을 디버깅하거나, 혹은 왜 명백해 보이는 변경 사항이 의도적으로 피하게 되었는지 이해하려고 할 때, 히스토리는 읽기에 정확히 적절한 것입니다.
하지만 Claude Code, Codex, 그리고 유사한 코딩 에이전트들은 보통 다른 일을 하고 있습니다. 그들은 현재의 시스템을 편집하려 합니다.
그 작업을 위해서라면, 역사적인 산문 (historical prose)이 주요 컨텍스트가 되어서는 안 됩니다. 에이전트에게는 현재의 진실의 원천 (current source of truth)이 필요합니다: 현재의 스키마 (schema), 현재의 모듈 경계 (module boundaries), 현재의 공개 API (public APIs), 현재의 테스트 (tests), 현재의 상태 머신 (state machines), 현재의 설정 (configuration), 현재의 의존성 규칙 (dependency rules) 말입니다.
사람은 데이터베이스가 어떻게 진화했는지 이해하기 위해 마이그레이션 히스토리 (migration history)를 읽을 수도 있습니다. 하지만 저는 Claude Code가 모든 마이그레이션을 재현하며 현재의 데이터베이스를 학습하기를 원하지 않습니다. 저는 그것이 현재의 스키마 (schema)를 읽기를 원합니다. 사람은 결정 뒤에 숨겨진 논리를 따르기 위해 ADR (Architecture Decision Records)을 읽을 수도 있습니다. 하지만 저는 편집 에이전트 (editing agent)가 모든 ADR을 재현하며 현재의 아키텍처 (architecture)를 추론하기를 원하지 않습니다. 저는 그것이 현재의 모듈 그래프 (module graph)를 읽기를 원합니다.
히스토리는 시스템이 왜 이런 형태가 되었는지를 설명합니다. 현재의 진실의 원천 (source of truth)은 그것이 지금 어떤 형태를 갖추고 있는지를 설명합니다. 둘 다 중요합니다. 단지 서로 다른 독자를 대상으로 할 뿐입니다.
| 독자 | 읽어야 할 것 |
|---|---|
| 사람 — _왜_를 이해하려는 경우 | ADR, 오래된 사양 (specs), 마이그레이션 히스토리, 설계 노트 |
| 코딩 에이전트 — _현재 존재하는 것_을 변경하려는 경우 | 현재의 스키마, 테스트, 타입 (types), 모듈 그래프, 설정 (config), 공개 API (public APIs) |
따라서 규칙은 결코 "히스토리를 삭제하라"가 아니었습니다. 그것은 다음과 같습니다:
사람은 히스토리를 유지하라.
에이전트에게는 현재의 진실의 원천을 제공하라.
잘 구성된 레포지토리 (repository)는 에이전트가 과거를 재현하지 않고도 현재의 시스템을 이해할 수 있게 해야 합니다. 그리고 시스템이 커질수록 이 점은 더욱 중요해집니다. 히스토리가 쓸모없어지기 때문이 아니라, 산문 (prose) 형태의 히스토리는 쓸모없어진 진실과 뒤섞이게 되어 두 가지를 구별할 수 없게 되기 때문입니다. 위험한 것은 오래된 사실 그 자체가 아닙니다. 오래된 사실이 현재의 사실과 똑같은 옷을 입고 있는 것입니다.
드리프트 (Drift)는 실패 모드이며, 에이전트는 이에 충실하다
제가 계속해서 걸려 넘어지는 연쇄 고리는 다음과 같습니다:
중복 (duplication) → 드리프트 (drift) → 컨텍스트 오염 (context pollution)
어떤 사실이 두 곳에 존재하게 되는 순간 — 한 번은 코드에, 한 번은 코드에 대한 마크다운 (Markdown) 설명에 — 그것들은 서로 갈라지기 시작합니다. 코드는 압박 속에서 변경되지만, 산문은 누군가가 기억해낼 때 변경됩니다. 따라서 문서는 조용히 오래된 것이 되어버리고, 이제 당신은 동일한 질문에 대해 어느 것이 최신인지 표시되지 않은 채 두 가지 답변을 갖게 됩니다.
약간 잘못된 문서를 읽는 인간은 눈을 가늘게 뜨고 대조하며 확인합니다. 하지만 에이전트는 눈을 가늘게 뜨지 않습니다. 에이전트는 오염된 컨텍스트 (context)를 신뢰하고 그에 따라 단호하게 행동하는 데 매우 능숙합니다. 에이전트를 빠르게 만드는 요소인 '주어진 사실을 액면 그대로 받아들이는 특성'이 바로 드리프트 (drift)를 위험하게 만드는 요소입니다.
따라서 가장 유용한 조치는 더 나은 문서를 작성하는 것이 아닙니다. 사실이 부패할 수 있는 장소 자체를 줄이는 것입니다.
CLAUDE.md의 경계는 조언입니다. 빌드(build)의 경계는 아키텍처 (architecture)입니다.
이것은 제가 계속해서 되돌아오게 되는 지점입니다.
만약 CLAUDE.md에 "결제 모듈은 checkout에서 임포트(import)해서는 안 된다"라고 적혀 있다면, 그것은 정중한 요청일 뿐입니다. 이를 강제하는 것은 아무것도 없으며, 다음 수정 단계에서 이를 무시하더라도 인간이든 에이전트든 아무도 제지받지 않습니다.
하지만 동일한 경계가 ESLint 설정, package.json의 exports, 또는 TypeScript 프로젝트 참조 (project references)에 존재한다면, 그것은 더 이상 조언이 아닙니다. 그것은 벽입니다. 에이전트는 그 규칙을 기억할 필요가 없습니다. 왜냐하면 그 규칙은 에이전트가 편집하고 있는 시스템의 속성 그 자체이기 때문입니다.
이것은 저에게 "AI 가독성 (AI readability)"의 개념을 재정의해 주었습니다. 그것은 결코 더 긴 지침을 작성하는 것에 관한 것이 아니었습니다. 그것은 리포지토리 (repository) 자체가 구조적으로, 즉 드리프트 (drift)가 발생할 수 없는 방식으로 진실을 말하게 만드는 것에 관한 것입니다.
- 디렉토리 구조 (directory structure) 가 아키텍처를 전달합니다.
- 명명 (naming) 이 의도를 전달합니다.
- 패키지 익스포트 (package exports) 가 공개 API 표면 (public API surface)을 전달합니다.
- 린트 규칙 (lint rules) 이 의존성 경계 (dependency boundaries)를 전달합니다.
- 스키마 (schemas) 가 데이터 계약 (data contracts)을 전달합니다.
- 상태 머신 (state machines) 이 워크플로우 (workflows)를 전달합니다.
- 테스트 (tests) 가 기대 동작 (expected behavior)을 전달합니다.
- 설정 파일 (config files) 이 실제 스택 (stack)을 전달합니다.
- 생성된 파일 마커 (generated-file markers) 가 "수동으로 수정 금지"를 전달합니다.
이 각각의 요소는 시스템에 관한 그 어떤 문장보다 시스템에 더 가깝습니다. 그 근접성이 바로 핵심입니다. 사실이 그것이 설명하는 대상에 가까이 있을수록, 거짓이 끼어들 여지는 줄어듭니다.
Markdown은 영토가 아니라 지도여야 합니다
저는 CLAUDE.md와 AGENTS.md를 버린 것이 아닙니다. 크기를 줄였을 뿐입니다.
그들의 역할은 아키텍처를 재진술 (restate) 하는 것이 아닙니다. 현재 아키텍처가 실제로 어디에 존재하는지를 _가리키는 (point at) 것 입니다.
## Sources of truth
- Database schema: packages/database/schema
...
그것은 지도입니다. 지도는 영토를 복사하는 대신 영토를 가리키기 때문에 변화 속에서도 살아남습니다.
함정은 그 반대입니다. CLAUDE.md의 특정 섹션에 동작(체크아웃 상태, 결제 의존성, API 수락 사항 등)을 다시 기술하는 것입니다. 코드가 변경되는 날, 그 단락은 확신에 찬 거짓말이 됩니다. 그리고 에이전트는 진실을 믿는 것과 정확히 똑같은 신뢰를 가지고 그것을 읽을 것입니다.
스펙(Specs)은 비계(scaffolding)입니다 — 탈출 전략을 마련하세요
저는 여전히 기능 개발의 '시작' 단계에서 짧은 스펙을 작성하는 것을 선호합니다. 이는 Claude Code나 Codex에게 모호한 의도보다 더 날카로운 목표를 제공하며, 저 스스로 생각하는 데 도움을 줍니다. 그 부분은 진정으로 유용합니다.
실수는 코드가 존재하게 된 이후에도 스펙이 '두 번째 진실의 원천(second source of truth)'으로 남게 두는 것입니다. 기능이 배포되면, 스펙의 지속 가능한 부분들은 레포지토리의 현재 시제로 이관되어야 합니다. 바로 이 지점에서 레포지토리는 별도의 설명 없이도 읽기 가능한 상태가 되기 시작합니다.
| 스펙이 포착한 내용… | …다음으로 이동하세요 |
|---|---|
| 화면 상태 (screen states) | 상태 머신 (state machine) / Storybook 스토리 |
| ... |
스펙은 건물을 짓기 위한 비계였습니다. 비계를 그대로 둔 채 사람들에게 벽보다 비계를 더 신뢰하라고 요구해서는 안 됩니다.
네이밍은 레포지토리 내부에서 실행되는 프롬프트 엔지니어링입니다
이 부분은 제가 조용히 즐거움을 느끼는 대목입니다. 에이전트는 이름을 통해 의도를 추론합니다. 즉, 이름은 더 이상 인간만을 위한 것이 아닙니다.
createUser()
updateUser()
deleteUser()
위와 같은 이름은 도메인에 대해 에이전트에게 거의 아무것도 알려주지 않습니다. 반면 다음과 같은 방식은:
registerMember()
inviteMember()
deactivateMember()
...
에이전트에게 현재 위치가 어디인지를 알려줍니다. 구조(structure)도 마찬가지입니다. 에이전트가 동작이 어디에 속하는지 보여주는 지도로 읽을 수 있는 레이아웃을 구성해야 합니다.
| 경로 (Path) | 그곳에 존재하는 것 |
|---|---|
domains/billing/commands/ | 상태를 변경하는 작업 (state-changing operations) |
| ... |
이러한 레이아웃은 에이전트가 추측하기 전에 "이 변경 사항은 어디에 속하는가?"라는 질문에 답을 줍니다. 그리고 에이전트는 반드시 추측할 것입니다. 만약 레포지토리가 에이전트에게 특정 요소가 어디에 사는지 알려주지 않는다면, 에이전트는 그럴듯해 보이지만 틀린 곳에 기꺼이 새로운 집을 만들어낼 것입니다.
따라서 명명 규칙 (naming)과 레이아웃 (layout)은 일종의 상시 프롬프트 (standing prompt)와 같습니다. 한 번 구조 안에 작성되면, 매 실행 시마다 읽히기 때문입니다.
한 단계 위에서의 동일한 "무엇을 전달할 것인가"라는 질문
그래프 관련 포스트에서, 제가 에이전트에게 전달하고자 했던 사실들(facts)은 지배성 (dominance), 도달 가능성 (reachability), 순서 (ordering)와 같이 계산된 것들이었습니다. 그리고 그 사실들이 어디에서 왔는지에 따라 각각을 verified (검증됨) 또는 estimated (추정됨)로 라벨링하는 원칙을 따랐습니다.
이것은 파이프라인의 더 앞 단계에서 발생하는 동일한 질문입니다. 현재의 스키마 (schema)는 데이터에 대한 verified 된 사실입니다. 다시 재생(replay)해야 하는 마이그레이션 로그 (migration log)는 기껏해야 현재 상태에 대한 estimated 된 진실일 뿐입니다. 린트 (lint)로 강제된 경계는 검증된 것이지만, CLAUDE.md에 적힌 문장은 하나의 희망 사항입니다. 잘 설계된 레포지토리 (repo)는 그 자체로 신선함을 유지하는 사실의 원천입니다. 왜냐하면 그것들은 시스템을 뒤따라가는 설명이 아니라, 시스템 그 자체이기 때문입니다.
마이그레이션 히스토리 (migration history)보다는 현재의 스키마 (schema)를. ADR (Architecture Decision Records)보다는 현재의 모듈 그래프 (module graph)를. 오래된 인수 조건 (acceptance criteria)보다는 실행 가능한 테스트 (live tests)를. README의 산문보다는 실제 설정 (config)을. 워크플로우에 대한 문단보다는 상태 머신 (state machines)을 선택하십시오.
이 방식이 히스토리를 버리는 것은 아닙니다. 단지 편집 에이전트 (editing agent)에게 히스토리를 통해 현재를 재구성하라고 요구하는 것을 멈추는 것뿐입니다.
작년에는 지침 (instructions)을 조정했습니다. 올해는 레포지토리 (repo)를 설계했습니다.
이것이 어디에서 왔는지 솔직하게 말씀드리고 싶습니다. 이것은 벤치마크 (benchmark)가 아니라 경험에서 우러나온 것이기 때문입니다.
1년 전 저는 지침 계층 (instruction layer) — .claude/, .codex/, AGENTS.md, CLAUDE.md — 에 집착했습니다. 규칙을 조정하고, 기술 (skills)을 작성하고, 파일들이 계속 늘어나게 두면서, 레포지토리 (repo) '주변'의 프롬프트 (prompt)를 완벽하게 만드는 것에 집중했습니다.
그 과정 중 어느 지점에서 집착의 대상이 옮겨갔습니다. 지침을 조정하는 대신, 레포지토리 (repo) 자체를 조정하기 시작했습니다. 사실들을 단일 진실 공급원 (single source of truth)으로 끌어모으고, 에이전트가 읽을 수 있는 구조를 향해 리팩터링 (refactoring)하며, 처음부터 새로운 코드를 동일한 방식으로 작성하기 시작했습니다.
지금 제가 느끼는 점은 지침 파일(instruction files)이 예상했던 것보다 덜 중요하다는 것입니다. 수십만 줄의 코드가 담긴 레포지토리에서, 짧은 AGENTS.md와 완벽하게 조정되지 않은 규칙(rules) 및 기술(skills)만 가지고 있음에도 불구하고, Claude Code가 제가 의도한 방향에서 벗어나거나 실제 구현을 해야 할 시점에 정체되는 느낌을 거의 받지 못합니다. 예전에는 더 긴 지침으로 메워야 했던 마찰(friction)이 이제는 거의 존재하지 않습니다.
솔직한 주의사항을 덧붙이자면, 같은 기간 동안 모델과 에이전트 자체도 극적으로 발전했기 때문에, "내 레포지토리가 더 읽기 쉬워진 것"과 "도구들이 더 똑똑해진 것"을 명확하게 분리해서 말할 수는 없습니다.
하지만 느껴지는 방향성은 일관되었습니다. 현재의 상태가 텍스트로 둘러싸인 설명(prose)이 아닌 "레포지토리 안에" 존재할수록, 에이전트로부터 좋은 결과물을 얻기 위해 제가 해야 할 말이 줄어들었습니다.
향후 방향
요즘 저는 프롬프트에 "오래된 문서를 믿지 마세요", "실제 스키마(schema)를 확인하세요", "이것이 어디에 들어갈지 추측하지 마세요"와 같은 방어적인 문구들을 계속 추가하고 있습니다.
더 나은 해답은 더 나은 경고문을 작성하는 것이 아닙니다.
그러한 경고가 불필요하도록 레포지토리를 형성하는 것입니다.
히스토리는 사람을 위해 남겨두세요. 현재는 에이전트를 위해 형성하세요.
사람이 읽기 쉬운 히스토리는 시스템이 왜 이런 모습이 되었는지를 설명합니다.
AI가 읽기 쉬운 레포지토리는 시스템이 현재 무엇인지(what the system is now)를 드러냅니다.
에이전트가 과거로부터 현재를 재구성하게 만들지 마세요.
현재의 진실의 원천(source of truth)을 오해할 수 없도록 만드세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기