和 AI 协作,不知道你有没有这样的经历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 第一次对话: 你:帮我写一个用户登录接口 Claude:好的,这是一个基础的登录接口...(使用 Express + JavaScript)
你:我们项目用的是 Fastify 和 TypeScript Claude:好的,让我重新写...
第二次对话: 你:帮我写一个订单创建接口 Claude:好的,这是一个基础的订单接口...(又用 Express + JavaScript)
你:(崩溃)我们用 Fastify 和 TypeScript!
--------------------------------------------------------------
第一个项目: 你:帮我根据刚才的讨论做一套循环经济相关PPT,给老板直接看 Claude:好的,这是一份图文并茂,有技术深度的PPT... 你(看了PPT):内容挺好的,调一下格式,16:9 ,加Speaker Notes Claude:...
第二个项目: 你:帮我根据项目进展,做一套知识图谱的PPT,直接用于演示的标准 Claude:好的,这是一个份根据你的项目制作的详细知识图谱PPT... 你(看了PPT):16:9 ,加Speaker Notes!
|
在刚刚开始使用 Claude Code 时,这种情况常见。对于小项目,我多说几次需求,倒也无所谓。
但是时间长了,项目逐渐复杂的时候,如果每次新对话,Claude 都让我从零开始,如果它不记得你的项目用什么技术栈、什么代码风格、什么团队规范——那这种“失忆症”让人抓狂。
CLAUDE.md 就是治疗这种失忆症的药。它是一份给 Claude 的“项目入职手册”——Claude 每次开始对话时,都会自动阅读这份手册,了解你的项目背景,明确它在干活时应该遵循的一系列底层规则。
Claude Code 记忆系统的工作原理
当你在项目目录启动 Claude Code 时,发生的“记忆系统初始化”过程如下图所示。

这就像你给新员工一份入职手册,他读完之后就知道公司的规矩。不同的是,Claude 每次对话都会重新“入职”——所以这份手册必须简洁有效。
CLAUDE.md 的内容会每次对话都加载,所以要精简。把“每次都需要”的内容放这里,把“偶尔需要”的内容放到 Skills 或文档里。
编写高效的 CLAUDE.md
如果你只能记住一句话,那就是 CLAUDE.md 写得好不好,直接决定了 Claude 是靠谱同事,还是每次都要重新培训的实习生。
下面我们就来讨论 CLAUDE.md 编写要遵循的核心原则,了解怎么写,才值得每次都被加载进上下文。
核心原则 1:Less is More
CLAUDE.md 的每一行,都会在每一次对话开始时被自动注入上下文。这意味着一件事:冗余不是无害的,而是持续消耗的。所以保持精简不是建议,而是必须。
核心原则 2:具体优于泛泛
先来看一个非常常见、但几乎没有任何效果的写法。
1 2 3 4
|
请写出高质量的代码。代码应该是可读的。使用有意义的变量名。 保持代码整洁。遵循最佳实践。不要写重复的代码。
|
这些话没有一句是错的,但问题在于——Claude 本来就知道这些。它们不会改变 Claude 的任何决策,只会白白占用上下文空间。这些话对人类尚且含糊,对模型来说,更是几乎等于什么都没说。
真正有价值的 CLAUDE.md,应该长这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
- 使用 `interface` 定义对象结构,`type` 用于联合类型 - 禁止 `any`,使用 `unknown` + 类型守卫 - 函数参数 > 3 个时,使用对象参数
```js // 业务错误 throw new BusinessError('ORDER_NOT_FOUND', '订单不存在');
// 验证错误(Zod 自动抛出) const data = orderSchema.parse(input);
// controller 中不要 try-catch // 由全局错误中间件统一处理
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 两者的差异非常明确。后者不是模糊要求“要高质量”,而是给出了如何做才算高质量;不是“注意错误处理”,而是具体的错误模型;不是抽象描述,而是可直接模仿的代码形态。
这里有个简单的判断标准——如果你不写,Claude 也大概率会做对,那就不要写。
## 核心原则 3:关键三问题 WHY / WHAT / HOW
一份真正“能用”的 CLAUDE.md,通常都在回答三个问题。不是一次性回答,而是在关键地方给出明确指引。
### WHY —— 为什么要这样做?
```bash ## 为什么使用 Zod? - TypeScript 只有编译时类型检查 - API 输入需要运行时验证 - Zod 可以同时生成 TS 类型和验证逻辑 - 错误信息自动生成,对用户友好
|
这一部分的作用,不是让 Claude “记住一个库”,而是让它理解背后的决策逻辑。当 Claude 明白了为什么,它在面对相似但不完全相同的场景时,才更可能做出一致的判断。
WHAT —— 具体要做什么,不要做什么?
1 2 3 4 5
| - 所有查询通过 Prisma ORM - 复杂查询封装在 `src/repositories/` - 禁止在 controller/service 中直接写 SQL - 事务使用 `prisma.$transaction()`
|
这一部分的重点是边界。什么是允许的,什么是禁止的,决策应该发生在哪一层?对 Claude 来说,这比“最佳实践”四个字重要得多。
HOW —— 按什么步骤去做?
1 2 3 4 5 6 7 8 9
|
1. 在 `src/schemas/` 创建请求/响应 Zod schema 2. 在 `src/routes/` 添加路由定义 3. 在 `src/controllers/` 实现请求处理 4. 在 `src/services/` 实现业务逻辑 5. 在 `tests/` 添加测试用例
示例参考: `src/routes/orders.ts`
|
当步骤清晰、路径明确、还有参考文件时,Claude 才会稳定复用同一套工作流,而不是每次自由发挥。
核心原则 4:渐进式披露:不要把一切都塞进 CLAUDE.md
CLAUDE.md 的职责是定义默认决策,而不是承载全部知识。对于非核心、但可能被用到的内容,正确的做法是引用,而不是复制。
1 2 3 4 5 6 7 8 9
|
[精简的核心规范]
- 数据库设计: 见 `docs/database.md` - API 规范: 见 `docs/api-spec.md` - 部署流程: 见 `docs/deployment.md`
|
这样做有两个好处:
1.CLAUDE.md 保持轻量,启动成本低 。
2.当 Claude 需要进一步的细节信息时,可以按需读取引用文件。
CLAUDE.md 实战演练
场景一:为新项目创建记忆
假设你刚接手一个 React + TypeScript 前端项目,让我们从零配置记忆。
Step 1:创建基础 CLAUDE.md
先通过 /init 命令自动初始化 CLAUDE.md 文件,或使用下面的命令在项目根目录手动创建记忆文件。
然后创建如下的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
- React 18 + TypeScript - Vite 构建 - TanStack Query(数据获取) - Zustand(状态管理) - Tailwind CSS
src/ ├── components/ │ ├── ui/ │ └── features/ ├── pages/ ├── hooks/ ├── stores/ ├── api/ └── types/
- 函数组件 + Hooks - Props 接口命名: `XxxProps` - 一个组件一个目录: `Button/index.tsx`
- 服务端状态: TanStack Query - 客户端状态: Zustand - 本地状态: useState
- `pnpm dev` - 开发服务器 - `pnpm build` - 构建 - `pnpm test` - 测试
|
Step 2:创建本地记忆
CLAUDE.local.md
1 2 3 4 5 6 7 8 9
| * 本地笔记
** 环境 - API: http://localhost:8080 - Mock: 使用 MSW
** 当前任务 - 重构购物车组件 - 截止: 本周五
|
Step 3:添加条件规则(可选)
创建如下.claude/rules/testing.md:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| --- paths: - "src/**/*.test.tsx" - "src/**/*.test.ts" ---
**测试规范
- 使用 Vitest + React Testing Library - 测试文件放在同目录: `Button.test.tsx` - 优先测试用户行为,而非实现细节
```typescript // ✅ 好 expect(screen.getByRole('button')).toBeEnabled();
// ❌ 不好 expect(component.state.isLoading).toBe(false);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| ## 场景二:优化已有的 CLAUDE.md
假设你的 CLAUDE.md 已经有 500 行,Claude 开始变慢。此时就需要给它瘦个身,做一些优化了。我们可以分三步走。
### Step 1:识别核心内容
可以问自己:哪些内容是每次对话都需要的?下面是对于项目整体的一个规划示例——目的是使 CLAUDE.md 保有一个简单而清晰的结构。
### Step 2:拆分成独立文件
详细的 API 文档、数据库表结构和部署流程虽然重要,但是完全没有必要每次都读入 Claude 内存,可以移动到单独文件,精简原来的 CLAUDE.md 。
```bash ## 核心规范 [精简内容]
## 详细参考 - API 端点清单: @docs/api.md - 数据库 Schema: @prisma/schema.prisma - 部署配置: @docs/deploy.md
|
Step 3:使用条件规则
可以考虑进一步把测试规范、前端规范、后端规范拆分到 .claude/rules/,并设置 paths条件。
场景三:记忆管理命令
要查看当前记忆,在 Claude Code 中输入:
就会显示当前加载的所有记忆内容和来源。编辑记忆的命令参数如下:
1 2 3
| /memory edit /memory edit user /memory edit local
|
你也可以通过自然语言指令,让 Claude 帮你更新记忆!例如:
1 2 3 4
| 你:请记住,我们项目使用 pnpm 而不是 npm
Claude:好的,我可以将这个信息添加到项目的 CLAUDE.md 中。 要我现在更新吗?
|
总结
CLAUDE.md 是有层次的记忆。它的意义,是把项目规范、编码风格和团队约定中反复强调的共识,从对话中抽离出来,变成一次配置、长期生效的默认规则。
然而记忆本身是有成本的。CLAUDE.md 会在每一次对话开始时自动加载。这意味着它并不适合承载所有信息,而只适合存放每次都必须知道的内容。
当记忆过多、层级混乱,Claude 的行为反而会变得迟钝甚至不稳定。
因此,理解加载顺序、控制记忆体量、区分团队规范与个人偏好,并不是简单的进阶技巧,而是能否长期使用这套机制的前提。