平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“CLAUDE.md文件怎么写?Claude Code中CLAUDE.md……”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
在这个场景下,一次关于 CLAUDE.md「Prompt 债务」的清算实验:当模型越来越强,我们过去写下的大段规则,可能正在拖 AI 的后腿。
我把项目根目录那份 412 行的 CLAUDE.md 砍到了 118 行结合项目来看,(删掉约 71%),又把其中两大块流程迁去了 Skills 和 Hooks。用同一组任务做 A/B 对比后,新设置的结合项目来看,一次借助率更高、我中途打断纠正的次数更少,每个会话的输入 token 还明显下降。
这不是玄学,核心原因只有一句话:理解这一步时,CLAUDE.md 不是越长越好,真正重要的是高信噪比 —— 只保留 Claude 自己不可能知道的项目知识和硬约束。
落到代码里,官方最佳实践文档里有一个很狠的自检问题,建议现在就拿去对照你的 CLAUDE.md:
「删掉这一行,Claude 会犯错吗?」如果不会,就删掉。
(
Would removing this cause Claude to make mistakes? If not, cut it.
)
落到代码里,这篇文章记录我是怎么做这次「设置瘦身」的:Prompt 债务是怎么堆出来的、为什么删了反而更好、规则如何分类、怎么迁到 Skill / Hook、以及如何用 A/B Test 证明你没有删错。
从实现思路看,翻 git 历史,这份文件的膨胀路径很典型,几乎每一行都对应一次「我被坑过」:
半年后,文件里挤满了三类「坏规则」:
npm run test:unit 换成 vitest,规则里写的还是旧命令;而我当时的心态是:规则多总比少好,写了总比没写安全。
事实证明正好相反。
CLAUDE.md 会在每个会话启动时被完整读入上下文(官方限制:单文件最大 4 MiB,超过直接跳过不加载)。它和你的对话历史、读到的文件、命令输出共享同一个上下文窗口。
官方最佳实践开篇就点明了这个核心约束:
大多数最佳实践都基于同一个约束:
Claude 的上下文窗口很快会被填满,而性能会随着填满而退化
—— 它开始「遗忘」更早的指令、犯更多错误。
在这个场景下,也就是说,每塞一句正确的废话,都是在挤占真正重要信息的生存空间,还在烧输入 token。
理解这一步时,长指令存在注意力稀释问题:当几十条「必须」「严禁」「切记」排在一起,真正关键的那几条反而不再显眼。官方文档直接把 「过度规定的 CLAUDE.md」(The over-specified CLAUDE.md) 列为常用失败模式之一:
结合项目来看,若你的 CLAUDE.md 太长,Claude 会忽略其中一半,因为重要规则淹没在噪声里。
修复方法:无情地修剪。如果某条指令即使不写 Claude 也能做对,就删掉它,或者把它改造成一个 Hook。
我自己的体感完全吻合:文件 100 行时,红线规则它条条遵守;文件 400 行时,我最在意的「不要直接改 migrations 目录」反而开始被无视 —— 不是它叛逆,是那条规则被三百多行噪声淹没了。
Claude Code 的记忆加载方式是拼接(concatenate),不是覆盖:用户级、项目级、本地级 CLAUDE.md,以及 @import 进来的文件,会被依次拼进上下文。官方明确说:
用户规则和项目规则之间不存在谁覆盖谁:一旦两者冲突,Claude 可能遵循任意一条,所以请保持它们一致。
理解这一步时,文件内部同理。当你写下「用 npm」又从别处抄来「用 pnpm」,等于把选择题交给模型每次现场掷骰子 —— 表现出来就是「时好时坏、行为漂移」,极难排查。
无效规则浪费的是 token,过时规则制造的是 bug。
落到代码里,模型对 CLAUDE.md 里的显式指令权重很高。当里面写着旧命令、旧目录、旧框架约定,它会忠实地照做,哪怕代码库里的现实已经变了。我审计时就发现一条「所有接口请求走 src/utils/request.ts」,而那个文件在三个月前的重构中已经被拆成了 src/lib/http/ 下的三个模块 —— 这条规则在它每次处理网络请求时都会造成错误引导。
这是最容易被忽略的一点:在这个场景下,CLAUDE.md 里很多规则,是当年为了弥补旧模型能力而写的「prompt 拐杖」。
从实现思路看,比如「采用 TypeScript 时要标注类型」「写 Git 提交信息要遵循规范」「修改代码后补充对应测试」「不要提交 console.log」——2024 年可能还需反复叮嘱,2026 年的模型早已把这些当作默认行为。模型升级了,拐杖没有拆,它们就从「补能力」变成了「占上下文」。
Prompt 债务和技术债务一样:环境变了,旧债不还,利息越滚越高。
实际处理时,很多人(包括我)第一反应是:把长文件拆成几个文件,用 @import 引入,不就省上下文了吗?
并不会。 官方记忆文档写得很清楚:导入(Imports)能帮你组织长文件,但不会降低上下文成本 —— 被导入的文件同样在启动时加载。
@path/to/file 的本质是「启动时把文件内容展开拼进来」,兼容相对 / 绝对路径、最多 4 层嵌套递归,它解决的是可维护性问题,不是 token 问题。
落到代码里,真正能做到「按需加载、用的时候才花 token」的是另外三样东西,这也是后面迁移的目的地:
| 机制 | 位置 | 加载时机 |
|---|---|---|
| Skills | .claude/skills//SKILL.md | 只有 name/description 启动时加载,正文在被调用时才读取 |
| 子目录 CLAUDE.md/ 路径规则 | 子目录下的 CLAUDE.md、.claude/rules/*.md(带 paths frontmatter) | Claude 读写到匹配目录 / 类型的文件时才按需加载 |
| Subagents | .claude/agents/*.md | 被委派任务时在独立上下文里加载 |
记住这个区别,它决定了第 5 节的规则往哪里迁。
审计时我给每一条规则过了三个问题:
三个问题过后,规则分成四个去向:
通用工程常识和语言惯例:「代码要写注释」「函数命名要语义化」「采用 ES Module 而不是 CommonJS(如果这已是项目默认)」;
读代码就能知道的东西:逐文件的功能描述、目录里有什么(它自己会看);
教程和长篇解释:React 怎么写、Docker 是什么 —— 链接到官方文档即可;
频繁变动的信息:版本号、当前迭代进度(这些应该在 issue / 文档里,不该写死在记忆文件里);
一次性任务的临时约定。
判断标准:Claude 不可能靠自己知道,且不知道就会犯错。
官方给过一张经典的「收 / 放」对照表,翻译整理如下所示:
| 应该写进 CLAUDE.md | 不应该写 |
|---|---|
| Claude 猜不到的 Bash 命令 | 任何读代码就能搞清楚的事 |
| 与默认风格不同的代码规范 | Claude 已经知道的语言标准惯例 |
| 测试方式和首选 test runner | 详细的 API 文档(放链接即可) |
| 仓库礼仪(分支、PR 约定) | 频繁变化的信息 |
| 项目特有的架构决策 | 长篇解释或教程 |
| 开发环境怪癖(必需的环境变量等) | 对代码库逐文件的描述 |
| 不自明的常用坑 | 「要写干净代码」这类不言自明的话 |
| 规则的样子 | 迁去哪里 | 为什么 |
|---|---|---|
| 一套多步骤操作流程(发布、提 PR、数据迁移、代码审查清单) | Skill(.claude/skills/) | 流程不是事实;Skill 正文按需加载,不触发零成本 |
| 「每次都必须执行 / 绝对禁止做」的动作 | Hook(.claude/settings.json) | CLAUDE.md 是建议性的,Hook 是确定性强制执行的 |
| 个人的、跨所有项目的偏好 | 用户级 ~/.claude/CLAUDE.md、~/.claude/rules/ | 不污染团队共享文件,不用每个项目抄一遍 |
| 私人的、与机器相关的内容(本地沙箱 URL、测试账号) | CLAUDE.local.md(记得加进 .gitignore) | 不该提交到仓库 |
| 只对某个子目录 / 某类文件生效的规则 | 子目录 CLAUDE.md 或带 paths: 的 .claude/rules/ | 按需加载,不碰前端就不会读到后端规则 |
| 需读大量文件才能做的专项审查 | Subagent(.claude/agents/) | 在独立上下文里跑,不撑爆主会话 |
Claude Code 现在自带自动记忆(Auto Memory):你在对话中纠正它的偏好和模式,它会自己写进 ~/.claude/projects//memory/(MEMORY.md 索引 + 若干主题文件),新会话自动加载索引、按需读取详情。
所以「这次你记住,以后都用 pnpm」这类偏好性纠正,直接在对话里说就行实际处理时,,不必再手动堆进 CLAUDE.md。用 /memory 能够随时查看和编辑它记下了什么。
理解这一步时,所有改动在 git 分支上做,审计期间随时能够对比、回滚。CLAUDE.md 本身就应该纳入版本控制,团队共同维护。
先看现状,别凭感觉:
# 行数
wc -l CLAUDE.md
# 粗估 token(中文/代码混合,粗略按字符数估算量级即可)
wc -c CLAUDE.md
然后在 Claude Code 会话里:
/context,查看 Memory files 列表,确认到底哪些记忆文件在启动时被加载、各占多少;/doctor,它会针对纳入版本控制的 CLAUDE.md,主动提议删除那些能从代码库推导出来的内容—— 这是官方自带的审计助手,白捡的;/memory 能够总览用户级、项目级、本地级的全部记忆文件。结合项目来看,把每一条规则(或每个段落)按第 4 节的三问过一遍,打上标签:删 / 留 / Skill / Hook / 用户级 / 本地 / 子目录。
理解这一步时,我当时的 412 行,打标签的结果是:删除 214 行、迁移 80 行、保留 118 行。
给一个我文件里真实存在过的片段(已脱敏),改造前长这样:
## 开发规范
- 使用 TypeScript 编写,注意类型标注,不要用 any
- 代码要写清晰的注释,函数名要有意义
- 优先使用函数组件和 Hooks,不要写类组件
- 使用 ES Module 的 import/export,不要用 require
- 提交代码前记得跑测试,测试命令是 npm run test:unit
- 统一使用 npm,不要混用 yarn 和 pnpm
- (中段略)……
- 本项目统一使用 pnpm 安装依赖
- 所有网络请求统一走 src/utils/request.ts 封装
- 发布时:先改版本号 → 跑 lint → 跑全量测试 → npm run build →
登录制品库 → 手动上传 dist → 在群里通知测试(后面还有 12 行细节)
- 不要修改 database/migrations 目录下的任何文件
问题一目了然:前四条是常识;测试命令已过时(现在是 vitest);npm 和 pnpm 自相矛盾;request.ts 早已不存在;发布流程是典型的「流程性内容」;migrations 红线混在一大段文字里 根本没有强制力。
改造后,主文件里只留下真正需它「每次都看见」的两行:
## 红线
- 严禁直接修改 database/migrations/ 下的文件,数据库变更走
`.claude/skills/migration` 流程(由 Hook 强制拦截)
## 关键命令
- 单测:`pnpm vitest run <文件>`;全量:`pnpm test`
- 集成测试依赖本地 Redis:`docker compose up -d redis` 后再跑
从实现思路看,发布流程迁去 Skill(见第 6 节),migrations 禁令交给 Hook 强制执行(见第 7 节),个人偏好迁去 ~/.claude/CLAUDE.md。
瘦身后的项目级 CLAUDE.md,我固定为六段式骨架,总量控制在 200 行以内从实现思路看,(Anthropic 针对大型代码库的建议也是单文件保持在 200 行以下,长了就拆):
# 项目 CLAUDE.md
## 1. 项目一句话概览
这是什么、技术栈是什么(两行以内,细节让它自己读 README / package.json)
## 2. 红线(硬约束)
- 绝对不能做的事,3~5 条,每条一行
## 3. 架构中不显而易见的决策
- 关键决策 + 原因(只写代码里看不出来的)
## 4. 常用命令
- 构建 / 测试 / 运行,只写猜不到的、有前置条件的
## 5. 环境怪癖与已知坑
- 版本锁定、依赖服务、历史遗留问题
## 6. 按需加载的能力索引
- 发布:/release
- 数据库变更:/migration
- 代码审查:/code-review
- 详细约定见:@docs/ai/conventions.md
注意最后一段的 @import:它适合放组织性的索引和相对稳定、每次都需在场的约定结合项目来看,;但要清醒地知道,被 import 的内容启动时照样加载,别把它当成「藏起长文档」的垃圾桶 —— 真正低频的长文档应该进 Skill 的 references/。
「感觉变好了」不作数,我做了一组对照实验:
建任务集:从最近真实工作中挑 10 个有代表性的任务(修 bug、加功能、写测试、重构、跑发布流程各两个),把初始 prompt 固定写下来;
控制变量:同一模型、同一版本,每个任务都在全新会话里跑(避免历史上下文污染);
新旧各一轮:旧 CLAUDE.md(git 切回审计前)跑一遍,新设置跑一遍;
记录五个指标:
| 任务 | 一次借助(旧 / 新) | 中途纠正次数(旧 / 新) | 红线违规次数(旧 / 新) | 输入 token(旧 / 新) | 备注 |
|---|---|---|---|---|---|
| 修复登录态过期 bug | 否 / 是 | 3 / 0 | 0 / 0 | 41k / 28k | 旧设置误用了已废弃的 request.ts |
| 新增列表分页接口 | 是 / 是 | 1 / 0 | 0 / 0 | 38k / 26k | |
| 为工具函数补单测 | 否 / 是 | 2 / 1 | 0 / 0 | 35k / 22k | 旧设置用了过时的测试命令 |
| 执行一次发布 | 否 / 否 | 4 / 1 | 1 / 0 | 52k / 19k | 新设置走 /release,Hook 拦住误操作 |
| …(共 10 条) |
(以上为我个人项目的示例数据,具体数字因项目而异,重点是方法。)
判定:关注两类信号 —— 新设置下是否出现「因为缺了某条规则而犯的新错误」?如果出现,说明那条规则是被误删的有效规则从实现思路看,,从 git 历史里恢复它,同时且这本身说明它值得写得更醒目;如果没有,而纠正次数和 token 双双下降,瘦身即告成功。
实际处理时,我这 10 个任务的汇总结果:一次借助 6/10 → 9/10,平均纠正次数 2.1 → 0.6,启动即加载的记忆部分从约 3000 token 降到 800 token 左右。社区也有人公开过类似量级的实测:CLAUDE.md 从约 3000 token 瘦到 500 token,每次对话省下的都是纯开销。
官方对「什么时候该建 Skill」给过一个精确判据:
当你发现自己反复粘贴同一段指令、清单或多步骤流程,或者CLAUDE.md 的某个章节已经从「事实」长成了「流程」落到代码里,,就该建 Skill 了。Skill 的正文只在被采用时加载,所以再长的参考资料在不用时也几乎零成本。
以发布流程为例,新建 .claude/skills/release/SKILL.md:
---
name: release
description: 按发布清单完成版本发布——更新 changelog、跑全量检查、
构建、打 tag、推送制品库。当用户提到"发布 / release / 上线 / 发版"时使用
---
# 发布流程
1. 确认当前在 main 分支且工作区干净(git status)
2. 运行 pnpm test 与 pnpm lint,全部通过才继续
3. 按语义化版本更新 package.json 版本号与 CHANGELOG.md
4. pnpm build,确认 dist 产物生成
5. 打带注释的 tag:git tag -a vx.y.z -m "release vx.y.z"
6. 推送 commit 与 tag,按 references/publish.md 登录制品库并上传
7. 输出本次发布摘要,提示人工在群内通知测试
异常处理与回滚见 references/rollback.md
要点:
description 是触发器:模型靠它判断何时自动调用,务必写清「做什么 + 什么时候用」;references/publish.md、references/rollback.md 放在 Skill 目录下,只有流程走到那一步才会被读取,这叫渐进式披露(progressive disclosure);SKILL.md 正文控制在 500 行以内,超了就继续拆文件;/release。对于有副作用、只想手动触发的流程,在 frontmatter 加 disable-model-invocation: true,需传参时用 $ARGUMENTS 接收。在这个场景下,同样的手法适用来代码审查清单、数据库迁移规范、新模块脚手架等 —— 它们都是「偶尔才用、一用就要完整」的典型内容。
必须先建立一个认知:结合项目来看,CLAUDE.md 是上下文,不是强制设置(context, not enforced configuration)落到代码里,。它本质上是「建议」,模型绝大多数时候会听,但你不能把合规押在它每次都听上。
官方原话:CLAUDE.md 和自动记忆都被当作上下文对待,而非强制设置。如果你希望无论 Claude 如何判断都阻止某个动作,应该采用 PreToolUse Hook。
从实现思路看,Hook 在工作流的固定时点确定性地执行脚本,不以模型的注意力为转移。比如「禁止直接改 migrations 目录」,与其在 CLAUDE.md 里写十遍,不如在 .claude/settings.json 里配一个 PreToolUse 拦截:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/block-migrations.sh"
}
]
}
]
}
}.claude/hooks/block-migrations.sh:
#!/usr/bin/env bash
# Hook 通过 stdin 收到 JSON,其中 tool_input.file_path 是目标文件
target=$(jq -r '.tool_input.file_path // empty')
case "$target" in
*"/database/migrations/"*)
echo "禁止直接修改 migrations 目录,请走 /migration 流程" >&2
exit 2 # 退出码 2 = 阻止本次操作,stderr 会反馈给 Claude
;;
esac
exit 0
类似的常用法还有:每次编辑后自动跑 eslint /prettier(PostToolUse)、提交前自动检查是否误带密钥、测试失败时阻止结束回合(Stop hook)。结合项目来看,凡是你发现自己在 CLAUDE.md 里用「切记、必须、绝不」反复强调的机械性规则,基本都该问自己一句:这是不是该换成 Hook?
瘦身不是一次性运动,我加了三条机制防止它重新胖起来:
/doctor 的删减建议;把 CLAUDE.md 当代码而不是备忘录对待 —— 官方的说法是「像维护代码一样维护它:出问题时复盘、定期修剪、借助观察行为是否真的改变来验证每次修改」;IMPORTANT;当很多行都被强调,就没有任何一行是重要的。另外两个小知识:
/compact(上下文压缩)后会被重新读取注入,不用担心压缩后丢失;但只在对话里口头说过的规则会丢,需持久化的要落进文件;~/.claude/rules/(如 preferences.md),它们对本机所有项目生效,加载顺序早于项目规则 —— 所以跨项目通用的偏好别再往每个项目里复制。Q:删了这么多,万一删错了怎么办?
A:全程在 git 上操作,A/B Test 中一旦发现「缺规则导致的新错误」,立刻恢复对应条目。审计的第一周我确实恢复了两条 —— 它们借助了「删了会犯错」的检验,留下来也名正言顺,而且重写后位置更醒目。
Q:小项目有必要搞这么复杂吗?
A:方法能够裁剪。小项目做到 Step 1~3(体检、三问、删留)就够了;Skill/Hook 是在你确实有重复流程和强制约束时才引入,不要为了用而用。
Q:团队协作里,谁来维护?
A:项目级 CLAUDE.md 提交进 git,团队共用、共同 review;个人偏好放用户级或 CLAUDE.local.md,不要混进团队文件。大型 monorepo 里如果会被其他团队的 CLAUDE.md 干扰,可用 claudeMdExcludes 设置排除。
Q:用 AGENTS.md 的项目怎么办?
A:Claude Code 兼容读取通用的 AGENTS.md;两者共存时的加载优先级、@AGENTS.md 互引等规则较细,以官方记忆文档为准,别想当然。
下次打开你的 CLAUDE.md,照着过一遍:
/context 确认实际加载了哪些记忆文件,/doctor 跑一遍官方删减建议.claude/skills/(description 写清触发条件,正文 < 500 行,细节进 references/)~/.claude/CLAUDE.md / ~/.claude/rules/CLAUDE.local.md(确认已 gitignore)paths: 的 rules@import 只改善组织,不省 token;按需加载要用 Skill / 子目录规则 / Subagent;CLAUDE.md 的理想形态不是「AI 编程教材」,而是一份高信噪比的项目非公开知识索引。你删掉的每一行正确的废话,都是在给真正重要的规则让路。
落到代码里,本文基于 2026 年 10 月时点的 Claude Code 机制整理,命令、路径与设置项可能随版本调整,实操请以官方文档为准。
落到代码里,总的来说,Claude适合结合实际项目边做边理解。先抓住核心思路,再逐步补上细节和边界处理,最后效果会更稳定,也更容易复用。