提出CLAUDE.md文件中高价值指令的判别法则:新员工第一天需要且无法推断的内容。避免冗余指令占据上下文。
我写了一个 linter,用来统计 CLAUDE.md 文件中的指令数量。拿自己的项目跑了一遍后,我发现了一个令人不太舒服的事实:问题从来不在于这些文件太短。每个文件的内容都相当充足。真正的问题是,其中大部分内容根本不起作用——更糟糕的是,它们还在挤占真正重要的那十行内容。
如今,所有人都在重复“保持简短”这条建议,而且它确实没错。Anthropic 自己的最佳实践文档也建议保持内容简洁、便于人类阅读,尽量控制在 200 行以内,并警告文件越长,占用的 context 就越多,模型遵循指令的能力也会下降。HumanLayer 的工程博客写过一篇相当不错的相关文章,他们把根目录下的 CLAUDE.md 控制在 60 行以内。
但“简短”只是一项约束,并不是一个方案。真正的问题是:哪些内容值得在文件中占据一席之地,哪些内容却连自己占用的成本都负担不起?
现在,我会用下面这个标准审视每一行:一位能力合格的新员工入职第一天是否需要知道这件事?他是否无法通过代码推断出这件事?只要其中任意一个问题的答案是否定的,这一行就应该删除。
这个标准可以把 CLAUDE.md 中的所有内容分成两类。
这是任何 CLAUDE.md 中价值最高的内容。Claude 无法推断出你的测试运行器需要添加某个 flag,也无法知道 npm test 已经坏了,而团队里的所有人实际运行的是 npm run test:fast。
## Commands
- Build: `pnpm build` (NOT npm — lockfile is pnpm)
- Test single file: `pnpm vitest run path/to/file.test.ts`
- Typecheck: `pnpm tsc --noEmit` — run after every change
- DB migrations: `pnpm drizzle-kit push` (dev only, never in prod)
只有四行。请注意,每一行都包含一个无法轻易推断出的细节。单独的 pnpm build 可以通过 lockfile 推断出来;而“NOT npm”则能避免一种真实存在的失败情况。
用三到六行回答“我应该去哪里找?”这个问题。它不应该是目录清单,因为 Claude 可以自己运行 ls;它应该指出那些承载了设计意图的部分:
## Layout
- `src/core/` — pure business logic, no I/O, no framework imports
- `src/adapters/` — all external calls (DB, APIs) live here, nowhere else
- `legacy/` — frozen. Read for reference, never modify.
- Generated: `src/gen/**` — never edit by hand, run `pnpm codegen`
关于 legacy/ 和 src/gen/ 的说明属于边界标记。根据我的经验,它们能避免的问题比文件里的任何代码风格规则都多。Agent 一旦修改了生成文件,这些改动就会在下一次运行 codegen 时悄无声息地消失,而这类 bug 排查起来真的令人痛苦。
大多数文件在这个部分都会走向两个极端,而且都做错了。官方指南给出的经验法则是正确的:绝不要重复 linter 已经能够强制执行的内容。如果 ESLint 或 Prettier 能发现问题,那么写下这条规则就是纯粹的浪费——Claude Code 看到 lint 失败后,自然会修复它。
真正应该放在这里的,是那些没有任何自动化机制强制执行的事项:
## Conventions
- Errors: return `Result<T, E>` from core functions; `throw` only at adapter boundaries
- New endpoints follow the pattern in `src/api/users.ts` — copy it
- Feature flags: check `flags.ts`, never read env vars directly in components
请注意第二行:直接指向一个示例文件,比用文字描述整个模式划算得多。一行文件指引可以替代三十行解释,而且示例文件不会像文字说明那样逐渐过时,与实际代码脱节。
如果 Claude Code 能够检查自己的输出,它的可靠性会显著提高。因此,你需要明确告诉它应该如何验证:
## Verifying changes
- `pnpm tsc --noEmit && pnpm vitest run` must pass before you finish
- UI changes: `pnpm dev` runs on :3000; screenshot before claiming done
没有这个部分,Agent 就会自行决定什么才算“完成”。有了它,你便明确定义了“完成”的标准。
这里应该放置硬性边界,内容必须极其简短,而且每条规则都要说明原因。这是因为我在 instruction budget 那篇文章中提到过,原因可以帮助模型泛化:
## Never
- Never commit directly to `main` (branch protection will reject the push anyway)
- Never touch `*.generated.ts` (regenerated on build; edits are silently lost)
- Never add a dependency without asking (bundle budget is 250 KB, we're at 238)
最后一行正是值得照搬的模式:原因中说明了“我们已经用到 238 KB”,这能让模型在遇到你没有明确写进规则的情况时,仍然作出正确判断。
对于所有无法直接放进 CLAUDE.md 的内容,Anthropic 和 HumanLayer 最终都采用了同一种机制:不要把细节粘贴进来,而是指向它所在的位置。只有当任务确实需要这些信息时,Claude 才去读取相应文件。我已经在 instruction budget 那篇文章中介绍过这种机制,所以这里只展示它应该采用的形式:
## More detail (read only when relevant)
- Testing philosophy and fixtures: `docs/testing.md`
- Release process: `docs/release.md`
- DB schema decisions: `docs/adr/003-schema.md`
下面这些内容都无法通过“新员工入职第一天”的测试。我在实际项目中见过其中每一种——有好几种甚至就出现在我自己的文件里。
项目使命宣言。 用三个段落介绍应用是做什么的、服务于哪些人。Claude 最多只需要一句话,而且大多数时候一句都不需要。
粘贴进来的 API 文档。 你的框架文档要么已经存在于模型的训练数据中,要么只需执行一次 WebFetch 就能获得。粘贴五十行 Drizzle 文档,等于白白缴纳五十行的 context 税。
工具已经强制执行的代码风格规则。 “使用两个空格缩进。”Prettier 会处理这件事。删掉。
教程。 逐步讲解“如何添加一个功能”的操作指南,与示例文件已经免费展示的内容重复。
变更日志。 “2025-11:迁移到 App Router。”历史信息应该留在 git 中。
泛泛而谈的工程智慧。 “编写整洁、可维护、命名良好的代码。”这条指令没有提供任何实际信息。所有模型原本就会尝试这么做,这一行消耗了 budget,却没有增加任何信息量。
最隐蔽的问题是,这些内容单独看起来都没有什么危害。但这个文件会被加载到每一个 session 中,而且正如我在前面提到的 instruction budget 文章中所论证的那样,随着指令数量增加,模型对指令的遵循程度会下降。模型不会在处理第 212 条规则时报错,它只会悄无声息地不再遵循其中一些规则。这些填充内容不仅会消耗 token,还会与你真正重要的规则争夺模型的注意力。
下面是我今天对自己某个项目文件运行 claude-md-lint 后得到的真实输出,其中只截取了诊断部分:
claude-md-lint CLAUDE.md
────────────────────────────────────────────────
Instruction budget score: 🟢 84/100
Instructions counted: 56 (soft 150 / hard 200)
• Within budget: 56 instructions (target ≤ 150).
• 46 rule(s) appear to give no REASON (heuristic). A rule with a "why"
generalizes to unseen cases; a bare command doesn't. Add "— so that …".
• 3 "must-happen" rule(s) (always/never/must). A prose rule lands ~80% of
the time; a deterministic hook fires ~100%. Graduate the critical ones
into hooks.
看看中间那项发现:这个文件的指令数量远低于 budget,但在 56 条规则中,仍然有 46 条只是没有说明原因的命令。这个文件的问题从来不是长度,而是信息密度。这正是前面六个部分所要避免的失败模式。
修改前——下面这个“conventions”部分,是由我在 lint 这些文件时反复发现的内容拼接而成的。没有任何一个经过我检查的文件糟糕到完整包含所有这些内容,但下面的每一行都曾出现在真实文件中,其中几行还来自我自己的文件:
## Code Style
We care deeply about code quality. Always write clean, readable code.
Use TypeScript for all new files. Use meaningful variable names.
Follow the existing patterns in the codebase. Use 2-space indentation.
Prefer const over let. Use async/await instead of raw promises.
Write JSDoc comments for exported functions. Keep functions small.
Always handle errors appropriately. Use early returns to reduce nesting.
Prefer functional patterns where it makes sense. Avoid any.
Make sure imports are sorted. Remove unused imports before committing.
这八行一共承载了十六条规则。用“新员工入职第一天”的标准来检验一下:使用 TypeScript 可以直接推断出来,因为每个文件都是 .ts;缩进和 import 排序是 Prettier 与 ESLint 的工作;剩下的内容中,又有超过一半只是泛泛而谈的通用建议。最终能保留下来的只有:
## Conventions
- `noUncheckedIndexedAccess` is on — index access returns `T | undefined`, handle it
- Exported functions in `src/core/` get JSDoc (docs site generates from them)
十六条规则缩减到两条——而留下来的这两条,确实是模型在没有得到明确说明时可能做错的事情。每次都应该进行这样的取舍:简短版本并不是长版本的摘要,而是删除模型已经知道的内容,以及工具已经能够强制执行的内容之后,最终剩下的残留物。
打开你的 CLAUDE.md,逐行进行评估:
Claude 能否通过代码或 lockfile 推断出这件事?→ 删除
linter 或 formatter 是否已经能够强制执行它?→ 删除
它是不是不包含任何项目特定信息的通用建议?→ 删除
它是不是只有不到 20% 的任务才会用到的细节?→ 移到 docs/ 文件中,只留下指引
它是不是一条“必须发生”的规则?→ 保留,并在括号中补充原因
它是不是命令、边界或示例文件指引?→ 保留,这些才是文件的核心
我检查过的大多数文件,在不损失任何功能的情况下,都能缩减一半长度。
我把完整的检查清单,以及每次发布 Agent 前都会应用的可靠性规则,整理在免费的 Claude Code Field Guide 中:penloomstudio.com/field-guide.html
如果需要采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。