给 Claude Code 递上一张项目地图:CLAUDE.md 到底该写什么?
给 Claude Code 递上一张项目地图:CLAUDE.md 到底该写什么?
摘要:这篇文章从 Claude Code 在终端里阅读仓库、修改文件和执行命令的工作方式讲起,说清
CLAUDE.md为什么会成为项目长期上下文。随后拆开用户级、项目级和子目录级指令的分层加载,解释规则冲突时怎样判断;再完成/init初次生成、具体规则编写、@imports拆分、协作纠偏和 Insights 回顾。最后讨论CLAUDE.md的安全边界,并用 Codex 的AGENTS.md做交叉印证,帮读者理解项目说明文件背后的通用设计。
一、AI 进了终端,为什么还需要一张“项目地图”
把代码交给一个通用 AI 助手时,常见流程是:复制一段报错、贴几行代码、等它分析,再手动把答案搬回编辑器。信息量不大时还能应付,碰到跨目录调用、测试流程和构建顺序,聊天窗口就开始失忆。
Claude Code 换了个位置。它进入终端,能读取代码仓库、编辑文件、运行命令,并根据执行结果继续排查。它会经历一条更完整的工作链:
理解需求 → 查找相关文件 → 修改代码 → 运行验证 → 根据结果继续修正
这比只生成一段孤立的代码更接近真实开发,也带来了新的难点:它需要更快找到正确的上下文。
一个仓库通常都有这些“没写进 README 的常识”:
- 装依赖用
pnpm,而不是顺手跑一个npm install; - 改完接口协议,必须重新生成前端客户端;
- 某张表靠软删除保留历史,查询时不能忘记过滤状态;
- 自动生成的目录可以读,但不应该手改;
- 测试文件必须放在约定目录,不能散落在源码旁边。
人手写代码时,这些问题靠经验补上;Claude Code 第一次进入项目,却没有这些经验。它在每个会话开始时读取 CLAUDE.md,把项目稳定的背景信息带进后续协作。
所以,CLAUDE.md 不是另一种 README,也不是让 AI 自动写完整个项目的神秘提示词。它是一份面向执行的项目说明:哪些事实必须知道,哪些命令应该运行,哪些边界不能越过,出了问题应该去哪里找原因。

二、少装几个插件,先把项目说清楚
网上经常能看到“十个必备 Claude Code 插件”“二十条神级提示词”这样的清单。工具当然可以扩展能力,但普通开发任务里,最常卡住的往往是最基础的小事:测试怎么跑、依赖怎么装、哪些文件不能改。
工具不会凭空知道你的项目。仓库里可读的文件很多,不代表每个文件都重要;终端里能执行命令,也不代表每个命令都适合当前任务。
写一份靠谱的 CLAUDE.md,利用的是更踏实的能力:
- 把稳定事实写明确。 比如技术栈、目录职责、常用命令。
- 把隐性约束写出来。 比如生成文件不能手改、数据变更必须走迁移。
- 把多次纠正沉淀成规则。 同一个错误反复出现,就不必每次都靠人重新提醒。
这三件事看起来朴素,却能直接影响 AI 能不能找到正确的文件、用对命令、守住项目边界。插件可以补功能,项目背景只能由了解项目的人提供。

2.1 它不是一个写满命令的“小抄”
可以把 CLAUDE.md 理解成一张给新同事看的项目地图。地图里当然要标注入口和交通方式,可只列几条命令远远不够。真正省时间的,是同时说明:
- “下车之后走哪扇门”;
- “哪条路现在封闭”;
- “看见旧路牌时,应该根据哪个目录的名字判断”;
- “搬运货物时,为什么必须保留原本的包装”。
对应到代码仓库,就是常用命令、禁区、目录职责和限制背后的原因。
README 更偏向向人介绍项目是什么、怎样开始使用;CLAUDE.md 更偏向告诉代理怎样在当前仓库里安全地执行任务。两者可以互相引用,但不必把同一大段内容抄两遍。
2.2 一个最朴素的例子
假设项目根目录有一份这样的说明:
# 项目说明
本项目是前后端分离的订单系统。
- 前端:`web/`
- 后端:`server/`
- 接口协议:`server/openapi.yaml`
- 测试:`pnpm test`
- 自动生成:`web/src/generated/`
修改接口后,运行:
```bash
pnpm generate:api
pnpm test
web/src/generated/ 由 OpenAPI 文档生成。需要调整接口时,
先修改 server/openapi.yaml,再重新生成客户端,不要直接改生成文件。
后面的任务如果涉及接口,Claude Code 就能沿着这些线索找到协议、生成命令和真正的写入位置。这里没有复杂的提示词,只有能直接落地的事实。
<!-- 图片:从项目根目录、命令到生成目录的项目地图与常用命令卡片 — 请手动插入 blog_images/day17/day17_03_first_claude_md.png -->
---
## 三、三张地图叠在一起,最具体的那张起决定作用
`CLAUDE.md` 并不局限于仓库根目录。实际使用时,它可以根据作用范围放在不同位置:
| 层级 | 常见位置 | 影响范围 | 适合放什么 |
| --- | --- | --- | --- |
| 用户级 | `~/.claude/CLAUDE.md` | 该用户参与的多个项目 | 个人偏好、通用习惯 |
| 项目级 | 仓库根目录的 `CLAUDE.md` | 当前仓库 | 技术栈、命令、架构边界 |
| 子目录级 | 特定目录内的 `CLAUDE.md` | 当前目录及更具体的任务上下文 | 模块专属规则 |
这里的路径是有意保留的示例。Claude Code 的具体配置目录和加载行为会随版本调整;真正写规则时,应结合当前版本的官方文档和 `/memory` 查看实际加载情况,不要把记忆中的路径当成永恒不变的接口。
层级的用意很清楚:个人习惯不必在每个仓库复制一遍,项目规则也不必污染其他项目;目录越具体,能写的规则通常也越具体。
<!-- 图片:用户级、项目级和子目录级三张嵌套的项目说明卡片 — 请手动插入 blog_images/day17/day17_04_instruction_layers.png -->
### 3.1 启动时加载什么,进入目录后又加载什么
大致可以先建立这个心智模型:
```text
用户级规则
↓
当前项目的上层规则
↓
当前任务所在目录的规则
用户级规则作为个人默认值参与协作;项目级规则描述当前仓库;更靠近工作目录的规则,在对应上下文中发挥作用。
子目录级规则不应被理解成“项目一打开,所有目录的规则就全部塞进上下文”。如果每个模块都有一份长说明,全量预加载会让上下文迅速膨胀。更合理的做法是:当工作落在某个目录时,再把该目录相关的补充信息带进来。
模型可以记成:越靠近手头任务的地图,越详细,也越优先;远处的默认地图不必提前全部展开。

3.2 “最近范围原则”不是只看谁写得更凶
不同层级的说明发生冲突时,先看作用范围。离当前任务更近、范围更窄的规则,通常应当优先。
| 用户级规则 | 项目级规则 | 当前执行位置 | 应优先理解哪条 |
|---|---|---|---|
| 使用 4 空格缩进 | 使用 2 空格缩进 | 该仓库内 | 项目级 2 空格 |
默认使用 npm | 本项目使用 pnpm | 该仓库内 | 项目级 pnpm |
| 代码必须注释解释意图 | 需求未要求改动代码 | 当前任务 | 这是适用条件差异,不是让两条规则互相消灭 |
第三行值得多想一步。一条规则讲代码风格,另一条规则讲任务范围,它们并非同一层面的直接冲突。遇到说法不一致时,先问:它们是在描述同一件事,还是在补充不同场景?
只有真的落到同一任务、同一动作上,才需要确定优先级。如果范围相似、表述也互相矛盾,最稳妥的处理方式仍是显式改掉说明文件,而不是期待模型猜出一套隐含的例外。

3.3 不要把“同层更具体”理解成永远通吃
原文还提到,同一层级内,具体明确的规则通常比模糊的通用表述更有指导价值。实际维护时,可以把它落到三个判断:
- 同一动作是否有两条不同要求? 比如一个让跑
npm test,一个让跑pnpm test。 - 同一路径是否一条允许改、一条禁止改? 比如普通源码目录与生成目录。
- 例外是否写明了触发条件? “数据库迁移文件必须同时更新快照”比“注意数据库”更容易执行。
比起给所有规则标一个抽象优先级,更实用的办法是改写冲突本身。项目统一用 pnpm,就删掉旧的 npm 命令;生成目录禁止手改,就写出唯一正确的生成入口。
好的说明不是让每条规则都争着当“最高命令”,而是删掉不必要的重复,让每条规则负责一个清楚的场景。
四、从 /init 开始,先写最值钱的那几行
Claude Code 一般会先读已有说明,因此一个已有项目通常不需要从空白文件硬写起。可以在项目根目录的 Claude Code 会话里运行 /init:
$ claude
> /init
它可以从仓库里找到部分技术栈、目录和命令,并生成一份初稿。初稿的价值在于省掉逐行搭建的力气,它不可能自动理解所有业务判断。
第一次生成之后,要像审代码一样认真看一遍。先修掉事实错误,再补它无法从文件名推出来的约束。
4.1 必须提供完整答案的三类问题
第一类:到底该运行什么
只写“运行测试”帮助有限。要提供真正会执行的入口:包管理器、工作目录、必要的前置步骤和适合当前任务的测试范围。
## 构建与验证
- 安装依赖:`pnpm install`
- 类型检查:`pnpm typecheck`
- 单元测试:`pnpm test --run`
- 只改 `server/` 时,也要运行 `pnpm test:contract`
- 提交前必须通过 `pnpm lint` 和 `pnpm typecheck`
这些信息能减少反复猜命令的成本,也让“改完再跑一下”变成可以检查的具体动作。
第二类:哪里碰不得,以及为什么碰不得
只写“禁止修改”容易让规则停留在表面。补上前因后果,模型在遇到相近任务时,才更可能做出安全判断。
## 不能直接修改的内容
- `web/src/generated/` 由 `server/openapi.yaml` 自动生成。
接口变化先修改 OpenAPI,再运行 `pnpm generate:api`。
- `migrations/` 是已经发布的历史记录。新增字段必须新建迁移,
不要回写旧迁移来“让测试通过”。
- `docs/archive/` 只保留归档材料,不是当前接口的事实来源。
这里并不需要写长篇业务设计。几句话交代“唯一事实来源在哪里”“合法改动入口是什么”,已经足够可执行。
第三类:目录之间如何协作
在模块较多的仓库里,目录名往往能说明它放什么,却说明不了哪些关系不能越过。例如:
## 架构边界
- `server/` 负责业务与数据库访问,`web/` 只通过 API 客户端调用后端。
- `server/domain/` 不直接依赖具体 Web 框架。
- 新增 API 时同步更新 `server/openapi.yaml` 与对应契约测试。
这样,下次看到报错时,Claude Code 不只会在眼前文件打补丁,还能判断改动是否越过了真正的模块边界。

4.2 可以运行,和应该运行,不是一回事
终端代理的优势是能执行命令。这也意味着,命令必须在说明里写清适用范围。
pnpm test 应该什么时候跑?npm publish 是否允许自动执行?清空数据库、删除目录、推送远端这类带副作用的操作,是否必须提前确认?
CLAUDE.md 适合说明项目层面的执行约定,但具体权限和审批设置应交给工具本身管理。不要把敏感密钥写进规则,再假设“不允许执行”就能保护它。

五、规则写得能执行,比写得气势足更重要
5.1 一条规则,能回答“下一步具体做什么”吗
先看一个常见但不够具体的版本:
写代码时要规范,注意测试,别破坏接口。
读完以后,读者仍然不知道:
- 规范由哪条检查命令负责?
- 测试放在哪个目录,怎么运行?
- 接口协议从哪里读取,在哪份文件里维护?
换成能执行的表述:
## 改动约束
- 新增接口先改 `server/openapi.yaml`,再运行 `pnpm generate:api`。
- 后端单元测试放在 `server/**/*.test.ts`,运行 `pnpm test:server`。
- 不允许直接修改 `web/src/generated/`。
- 修改认证中间件时,同时更新 `tests/auth.integration.ts`。
后者没有标语,却能让执行者立即定位文件、命令和边界。判断规则是否具体,可以问一句:如果没有模型经验,只看这段文字,能不能做出同样的动作?
5.2 解释意图,是为了应对没被写进清单的情况
规则清单永远不可能覆盖所有现实场景。只写“不要修改快照文件”,执行者遇到第三方库升级、快照变化时,可能只会机械地拒绝。
写出原因后,判断会扎实得多:
## 快照文件
`web/src/fixtures/snapshots/` 是组件渲染行为的回归基准。
如果改动只影响预期输出,先运行 `pnpm test:update`,再逐项检查差异;
如果渲染本身出现非预期变化,先修复组件,不要用更新快照掩盖问题。
限制还在,但下一步也更明确了。这比一句“禁止修改快照”更接近真实的工程协作。

5.3 文件越长,越要问:这些信息每次都用得上吗
CLAUDE.md 会成为每次协作的额外上下文。它占用的不只是文件体积,还有模型理解任务的注意力。
如果根目录说明里塞满十个模块的部署细节、每个接口字段和整段业务规则,真正做小修改时,很多内容都不相关。无关说明越多,关键约束越容易被埋住。
可以先有这样几条删减原则:
- 能从代码里一眼确定的事实,不手抄一遍。 目录本来就写着模块名,没必要列出一长串。
- 只管一个目录的细节,优先放进该目录的说明。 根目录保留跨模块、经常要用的事实。
- 非常具体的整段流程,考虑拆到单独文档。 需要时再引用,而不是每次全量展开。
- 过时规则直接改或删。 两条命令并存时,模型无法替你判断哪条真的还能跑。
简洁不是越短越好,而是删掉低价值重复,把高价值内容放到合适的位置。

六、规则多了就拆分:让地图分页,而不是塞进一张纸
项目刚开始时,根目录一份 CLAUDE.md 够用。规则增长后,比较自然的做法不是无限往下追加,而是把局部的详细信息拆开。
文档里提到了 @imports:在主文件中引用其他说明,把更详细的指引放到独立文件里。可以写成:
# 项目说明
项目概览见 @README.md。
常用命令见 @package.json。
测试约定见 @docs/testing.md。
API 设计规则见 @docs/api-guidelines.md。
数据库变更规范见 @docs/database-guide.md。
路径前面的 @ 让相关文件成为说明系统的一部分,而不是一条普通的“请自己去打开”的链接。是否支持、嵌套引用和加载细节,应与当前工具版本保持一致。
不同 Claude Code 版本对 @imports 的解析和加载方式可能调整,所以调试上下文问题时,别只盯着 Markdown 文本。先查看 /memory 中实际加载了哪些说明,再判断引用是否生效。

6.1 拆分不是复制,谁负责哪段要说清
一份健康的拆分结构,通常像这样:
| 位置 | 职责 | 不应该塞什么 |
|---|---|---|
CLAUDE.md | 项目全局、高频规则和引用入口 | 所有模块的长篇细节 |
docs/testing.md | 测试目录、夹具、命令和常见问题 | 部署步骤 |
docs/api-guidelines.md | 接口命名、错误格式和兼容要求 | 数据库迁移命令 |
server/CLAUDE.md | 服务端局部约定 | 前端组件规范 |
web/CLAUDE.md | 前端局部约定 | 后端数据表规则 |
这种方式同时和第三部分的分层加载呼应起来:根文件讲公共原则,目录文件讲局部细节,长文档按需引用。地图负责给出路线,具体攻略可以放在正确的一页。
6.2 一个容易忽略的安全问题
CLAUDE.md 通常会被提交到版本库,与团队共享。既然是说明文件,就应该按公开代码的标准来写内容。
不要把 API 密钥、数据库密码、访问令牌、私有环境地址或个人敏感信息放进 CLAUDE.md。需要说明凭据放在哪时,可以只写名称和安全获取方式:
## 本地环境
从团队密码管理器获取 `DATABASE_URL`,写入本地 `.env.local`。
不要提交 `.env.local`,也不要把它复制进代码或说明文档。
这段话对执行有用,又不会把真正的秘密一并交出去。

七、真正好用的说明,是一轮轮纠正出来的
/init 只能给初稿一个起跑线。随着项目变化,说明文件也需要增删。最有价值的更新经常来自现实里出现的小问题。
7.1 同一个错误发生两次,就值得检查说明
下面几类现象很像“人记错了”,其实也可以改说明:
| 实际现象 | 容易补什么 | 更值得写什么 |
|---|---|---|
总是用 npm,项目实际使用 pnpm | “请用 pnpm” | 为什么统一用 pnpm,锁文件在哪,验证命令是什么 |
| 测试总放在源码旁边 | “测试放对位置” | 测试目录、命名规则和运行范围 |
| 改了数据库模型,却忘了重建模块 | “记得重建” | 模型文件、生成命令与必须验证的下游模块 |
| 改了生成文件,源头没有变化 | “不要改生成文件” | 唯一事实来源、生成入口和允许读的目录 |
| 修了页面,却漏掉接口兼容检查 | “注意兼容” | 接口协议位置、兼容测试和提交前命令 |
如果某条经验只出现一次,可以先留给人处理;同样的错误再出现,就说明它很可能应该成为项目规则。
代码之外的说明文件也值得这样处理。工具版本一旦变化,旧路径、命令和能力描述就可能过时,别因为规则被提交过就舍不得删。
7.2 会话结束时,把“这次的发现”转成“下次的约定”
任务结束后,可以请 Claude Code 回头总结这一轮协作:
请总结这次任务里值得长期保留的项目事实:
1. 哪些命令已经验证可用;
2. 哪些约束来自代码,而不是本次临时要求;
3. 哪些发现只对当前任务有效,不应写进 CLAUDE.md;
4. 给我一份最小改动建议,不要直接覆盖现有说明。
这份提示的核心不是让 AI 自己不断往文件里加字,而是把经验分成两类:能长期复用的项目事实,和只适用于一次任务的临时约定。前者才值得沉淀。
文档中提到了 /reflection 一类的回顾流程。它是帮助沉淀的入口,不是概念本身;更重要的是坚持复看,让新增内容真正简洁、准确,并且不与旧规则打架。

7.3 Insights:把时间拉长,再看哪些问题在重复
单次会话能发现一个明确的错误,但不一定看得见一周内反复出现的低效。Insights 报告的价值,就在于从更长时间跨度回看使用记录,观察:
- 哪些说明被执行者反复忽略?
- 哪类命令经常失败?
- 哪些目录经常被找错?
- 哪些新增规则确实减少了返工?
它适合用来生成“候选改进项”,不适合把所有高频行为都自动写进规则。一条功能说明可能很重要,但只用了两次;一条看似普通的小规则,可能节省了几十个会话的时间。看过真实使用情况,再决定是否保留,比凭印象追加更可靠。

八、不同工具怎么记项目:看机制,别只比文件名
现在不少 AI 编码工具都有“项目级说明文件”,名称不一样,设计有相近之处,也有差异。
| 工具 | 常见说明文件 | 值得看的设计 |
|---|---|---|
| Claude Code | CLAUDE.md | 分层作用域、上下文加载与协作沉淀 |
| Codex | AGENTS.md | 从项目根向当前工作目录查找,越接近当前目录的说明越具体 |
| Gemini CLI | GEMINI.md | 同样把项目说明作为持久上下文使用 |
| 其他工具 | 各自的约定文件 | 有的兼容通用名称,有的增加导入或按需加载 |
文件名不是判断成熟度的标准。更值得比较的是:在什么位置查找、何时加载、冲突如何解释、是否支持引用以及怎样管理上下文体积。
8.1 拿 Codex 的 AGENTS.md 对照一下
OpenAI 的 Codex 使用 AGENTS.md 记录项目级指令。Codex 会从项目根目录向当前工作目录查找这类文件,把找到的说明与本次任务一起交给模型。离当前工作目录更近的说明通常更具体,也更应当优先。
所以,在仓库根目录写:
# 项目约定
- 使用 `pnpm`。
- 每次改接口都更新 `server/openapi.yaml`。
在 web/AGENTS.md 里可以再补充前端细节:
# Web 模块
- 状态逻辑放在 `web/src/features/`。
- 不要手动修改 `web/src/generated/`。
- 前端验证:`pnpm test:web`。
当前任务在 web/ 内时,模块说明能够给出更精确的路径和命令。它和 Claude Code 的分层思路相似:根说明负责全局,局部说明负责靠近任务的细节。
工具差异也同样明显。Codex 的查找规则与 Claude Code 的子目录加载、@imports 支持情况并不完全等价;要迁移说明文件之前,应先读对应版本的官方文档,而不是把文件名替换一下就宣布兼容成功。
这种比较最有用的地方,不是评出谁胜谁负,而是提炼共同的设计:
项目经验离开聊天窗口,变成可审查、可更新、可版本管理的长期上下文。

九、把说明文件当成项目的一部分来维护
一份能留下手的 CLAUDE.md,通常不需要第一天就写得很长。更可行的路径是:
- 用
/init得到初稿,并逐条核对事实; - 补上最常用的安装、启动、测试和检查命令;
- 写清生成目录、迁移文件等高风险区域的正确修改入口;
- 用目录级说明和引用文档承载局部细节;
- 每次出现重复错误,先查说明,再做一条最小更新;
- 定期用 Insights 和复盘清理过时、冲突和低价值内容;
- 始终把密钥和敏感信息挡在文件之外。
最后要记住:CLAUDE.md 不是 AI 的“自动记忆仓库”,也不是不用维护的配置魔法。它保存的是团队已经确认的事实与判断,仍然需要人审阅、修改和担责。
当项目说明足够清楚,Claude Code 才能把注意力花在真正的代码问题上,而不是每次重新猜构建命令、找错目录或者手改生成文件。把反复踩过的坑写进一张可靠的项目地图,下一次协作就会少绕几个弯。
创作不易,坚持难熬,留下个赞再走呗!
更多推荐

所有评论(0)