代码库上下文文档 是做什么的?
生成位于仓库根目录的紧凑型 CLAUDE.md 文件,以及 .claude/docs/ 目录下的一组深度解读文件。
目标不是“文档化项目”。而是:让未来的代理在第一个回合就能高效工作,同时消耗尽可能少的令牌。 CLAUDE.md 会在每个交互回合都被加载到上下文中,永远如此。其中的每一行都是一种“税收”。一行只有当它能改变代理在未见过任务上的行为时,才有资格存在。其他所有内容都应归属于 .claude/docs/,仅在任务相关时才被加载。
这一取舍驱动着以下所有规则。
工作流程
请遵循此顺序。先写 CLAUDE.md 是最常见的失败模式——它会产生一个听起来很合理但充满猜测的文件,然后深度文档会被写成与这些猜测匹配,而非与代码匹配。
- 调查 仓库(使用以下命令机械执行)。
- 阅读 入口点、配置和代表性的源码片段。
- 编写
.claude/docs/*.md—— 细节存储在此。 - 最后编写
CLAUDE.md—— 它是你对所学内容的索引。 - 验证 —— 行数、每个命令、每个
file:line。
第一步 —— 调查
在阅读任何代码之前运行以下命令。它们几乎不消耗令牌,并能防止对仓库规模、语言组合和工具链的错误猜测。
# 形状与规模
git ls-files | wc -l
git ls-files | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn | head -40
git ls-files | grep -oE '\.[a-z0-9]+$' | sort | uniq -c | sort -rn | head -20
# 工具链和入口点(读取此命令找到的每个文件)
ls -a
git ls-files | grep -iE '(package\.json|pyproject\.toml|setup\.py|requirements.*\.txt|Cargo\.toml|go\.mod|pom\.xml|build\.gradle|Gemfile|composer\.json|pubspec\.yaml|Makefile|justfile|Taskfile|docker-compose|Dockerfile)'
git ls-files '.github/workflows/*' '.gitlab-ci.yml' '.circleci/*'
# 现有文档 —— 阅读这些,它们编码了你无法从代码推断出的意图
git ls-files | grep -iE '(readme|contributing|architecture|adr|docs/)' | head -30
# 近期活动显示了仓库中哪些部分实际上处于活跃状态
git log --oneline -15
git log --pretty=format: --name-only -n 200 | sort | uniq -c | sort -rn | head -20
注意调查未能发现的内容。一个没有 CI 配置和测试目录的仓库是一个真实的发现,而不是一个需要掩饰的缺口。
第二步 —— 阅读
按此顺序阅读:
- 清单文件(
package.json、pyproject.toml……)—— 从头到尾,包括 scripts/tasks 部分。这里存储着真正的命令。 - CI 工作流文件 —— CI 运行的命令才是确实有效的命令。
README.md以及任何CONTRIBUTING.md/docs/—— 用于理解 为什么,这是代码无法告诉你的。- 应用程序入口点,然后是分布在主要目录中的 5-15 个代表性源文件。
当仓库很大且子代理可用时,使用并行子代理进行源文件读取阶段 —— 每个主要目录一个,每个子代理报告该目录的功能、使用的模式以及这些模式的 file:line 锚点。这样做更快,并且能将原始文件转储排除在主上下文之外。
在阅读时,持续维护一个候选模式列表,并附上你看到每个模式的确切 path/to/file.ts:142。稍后你将需要这些锚点,回过头去寻找它们代价高昂。
第三步 —— 编写 .claude/docs/
仅创建代码库实际支持的文件。一个空的或充数的文档比缺失更糟糕,因为它会让代理对整个文档集产生不信任。
| 文件 | 创建条件 | 内容 |
|---|---|---|
architectural_patterns.md | 始终(除非仓库极其微小) | 反复出现的结构性决策:依赖注入、状态管理、错误处理、模块边界、API 设计约定、分层规则 |
testing_strategy.md | 存在测试 | 框架、测试文件位置、fixture/factory 约定、如何运行单个测试或整个套件、哪些被 mock 哪些是真实的 |
build_and_deploy.md | 构建工具链或 CI 不直观 | 构建流水线、环境变量及其声明位置、产物、发布/部署流程、本地开发环境设置的陷阱 |
data_model.md | 存在持久化数据、schema 或迁移文件 | 核心实体及其关系、schema/迁移文件位置和工作流、验证层 |
api_reference.md | 项目暴露或消费一个重要的 API | 路由/端点组织方式、请求/响应约定、认证、版本控制、错误格式 |
当一个代码库拥有无法归类到以上文件的大型子系统时,你可以添加一个领域特定的文件(例如 realtime_sync.md、billing.md)。优先使用少量但内容充实的文件,而不是许多薄弱的文件。
多文件证据规则
这是将有用的模式文档与虚构的文档区分开来的规则。
仅当你至少在三个不同的文件中看到某个模式,并且引用其中至少两个时,才记录该模式。 一个单一的巧妙抽象是“琐事”。一个在代码库中反复出现的约定才是新代理必须遵循的,以便编写适合该项目的代码。
当你发现某个看起来像模式但只出现一次的东西时,要么放弃它,要么使用 grep 确认它是否具有普遍性:
rg -n 'useAppDispatch' --stats # 它有多普遍?
rg -l 'implements Repository' | head # 哪些文件共享此形状?
如果一个约定未被一致应用——一半的代码库采用一种方式,另一半采用另一种——请明确说明这一点,并指出哪种方式更新(通过 git log 查看文件)。知道项目正在进行迁移的代理,比假设代码库统一的代理能编写出更好的代码。
使用锚点引用,而非代码片段
请写 见 src/services/base.py:34,而不是粘贴类定义。代码片段在任何人编辑文件的那一刻就会过时,而过时的片段会产生严重的误导——而过时的行号至少值得重新检查。这还能保持文档足够简短,以便被阅读。
在行号旁边包含符号名称,以便锚点能承受小幅编辑:PaymentProcessor.execute — src/billing/processor.py:88。
跳过代码风格
用户运行 linters 和格式化工具。不要记录缩进、引号样式、行长度、导入顺序或命名约定。这些由工具强制执行;在始终加载的文件中重述它们会消耗上下文来重复工具的功能。但请记录 linter 无法强制执行的约定——哪个层可以导入哪个层、何时添加迁移、新路由在哪里注册自身。
第四步 —— 编写 CLAUDE.md
严格控制在 150 行以内。使用此结构:
# <项目名称>
<2-4句话:这个项目是什么,为谁/什么服务。WHY — 它解决的问题。
这是无法从代码推断出的部分,因此是最值得仔细编写的部分。>
## 技术栈
<紧凑列表:语言 + 版本、框架、数据库、关键基础设施。
仅列出那些会改变代码编写方式的内容。跳过传递性依赖的转储。>
## 项目结构
<顶级目录的表格或列表,每行说明该目录的功能以及——如果不明显的话——不该放什么。>
## 命令
<仅列出已验证的、普遍有用的命令:安装、开发、构建、测试、
运行单个文件测试、lint、类型检查。如果包管理器不是该生态系统的默认选项,请注明。>
## 约定
<3-8行,仅列出适用于整个仓库且 linter 无法捕获的规则。
如果某个规则特定于某个子系统,它应归属于文档文件。>
## 附加文档
当任务需要时阅读以下文件——不要预先加载它们。
- `.claude/docs/architectural_patterns.md` — <何时阅读它>
- `.claude/docs/testing_strategy.md` — <何时阅读它>
“附加文档”部分能否奏效取决于两点:
- 每个条目应说明何时阅读它,而不是它包含什么内容。“在添加端点或更改响应格式前阅读”能正确引导代理;“包含 API 文档”则不行。
- 该列表是引用特定细节的唯一位置。如果你发现自己同时在
CLAUDE.md和testing_strategy.md中解释测试设置,请将其从CLAUDE.md中删除。
请参阅 references/templates.md 获取已填充好的 CLAUDE.md 及各文档文件的示例。
第五步 —— 验证
不要跳过此步骤。此任务的每个失败模式都是一个听起来很自信的错误陈述,而每一个都很容易捕获。
wc -l CLAUDE.md # 必须 < 150
然后:
- 运行你记下的每个命令,或者至少确认每个命令都存在于清单文件/CI 配置中。
CLAUDE.md中一个不存在于package.json的npm test会在未来的每个会话中浪费代理的时间。如果你无法运行一个命令,请将其标记为“从源码读取”而非“已验证”。 - 抽查每个
file:line—— 打开几个,确认引用的符号确实在那里。 - 以第一次见到该仓库的代理视角,重新通读
CLAUDE.md。删除任何不会改变你行为的行。泛泛的建议(“编写干净的代码”、“正确处理错误”)通常是罪魁祸首。 - 报告最终行数和创建的文件列表。
如果 CLAUDE.md 超过 150 行,则应进行裁剪而不是压缩——将整个部分移入一个文档文件,并只留下一个索引行。为了适应而压缩散文会产生一个没人会去解析的密集文件。
处理现有的 CLAUDE.md
如果文件已存在,不要无脑覆盖。先读取它,然后对照当前代码进行验证:已不存在的命令、已重命名的目录、已迁移的模式。保留任何编码了无法从代码推导出的人工偏好的手写指令(“永远不要碰供应商的 SDK”、“添加依赖前先询问”)——这些是文件中价值最高、也最容易被破坏的行。在删除任何重要内容之前,向用户展示你将要移除的内容及其原因。