和 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 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
# 项目规范

## TypeScript
- 使用 `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
## 创建新 API 端点

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
touch 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/ # 基础 UI
│ └── features/ # 功能组件
├── pages/ # 页面
├── hooks/ # 自定义 Hooks
├── stores/ # Zustand stores
├── api/ # 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
/memory

就会显示当前加载的所有记忆内容和来源。编辑记忆的命令参数如下:

1
2
3
/memory edit         # 编辑项目级 CLAUDE.md
/memory edit user # 编辑用户级记忆
/memory edit local # 编辑本地级记忆

你也可以通过自然语言指令,让 Claude 帮你更新记忆!例如:

1
2
3
4
你:请记住,我们项目使用 pnpm 而不是 npm

Claude:好的,我可以将这个信息添加到项目的 CLAUDE.md 中。
要我现在更新吗?

总结

CLAUDE.md 是有层次的记忆。它的意义,是把项目规范、编码风格和团队约定中反复强调的共识,从对话中抽离出来,变成一次配置、长期生效的默认规则。

然而记忆本身是有成本的。CLAUDE.md 会在每一次对话开始时自动加载。这意味着它并不适合承载所有信息,而只适合存放每次都必须知道的内容。

当记忆过多、层级混乱,Claude 的行为反而会变得迟钝甚至不稳定。

因此,理解加载顺序、控制记忆体量、区分团队规范与个人偏好,并不是简单的进阶技巧,而是能否长期使用这套机制的前提。