CLAUDE.md项目说明书
> **时效说明**:本文内容以 2026 年 3 月为基准。
Claude Code:CLAUDE.md:给 Claude 的项目说明书
时效说明:本文内容以 2026 年 3 月为基准。
7.1 CLAUDE.md——它是什么,为什么重要
每次你在某个项目里启动Claude Code,它都会自动读取项目里的CLAUDE.md文件,把内容注入到上下文里。这相当于你在每次会话开始前,先给Claude做了一个项目背景汇报。
没有CLAUDE.md,Claude只能靠推断——它看到Spring Boot的代码,能猜出大概是个Web服务,但不知道你们的接口命名规范、不知道某些看起来奇怪的代码其实是有意为之、不知道测试要求。有了CLAUDE.md,这些背景信息就不用每次重复说了。
类比一下:CLAUDE.md相当于你们团队Wiki里的"项目README",但专门写给Claude看的版本——你会把那些"新来的程序员必须知道但代码里看不出来"的东西都写进去。
7.2 文件层级与加载顺序
CLAUDE.md有三个层级,优先级从低到高:
全局:~/.claude/CLAUDE.md,对所有项目生效。适合写你的个人偏好,比如"总是用中文回复"、"代码注释用中文"。
项目根目录:/your-project/CLAUDE.md,对整个项目生效。这是最常用的层级,应该提交到Git,团队共享。
子目录:比如/your-project/backend/CLAUDE.md,只对这个子目录里的操作生效。适合monorepo,不同模块有不同的规范。
当Claude在某个子目录里工作时,它会从当前目录向上逐层读取所有CLAUDE.md文件,合并生效。
7.2.1 加载顺序和覆盖规则
理解加载顺序很重要,因为有时候你会发现Claude的行为和你期望的不一样,原因可能就是层级之间的覆盖关系:
加载顺序(从先到后):
1. ~/.claude/CLAUDE.md(全局,最先加载)
2. 项目根目录/CLAUDE.md(项目级)
3. 中间目录/CLAUDE.md(如果有)
4. 当前工作目录/CLAUDE.md(最后加载,优先级最高)
如果全局CLAUDE.md写了"代码注释用中文",但项目CLAUDE.md写了"注释用英文",项目级的规则会覆盖全局规则。
7.2.2 加载时机
CLAUDE.md在每次会话开始时读取,不是实时监听的。如果你在会话进行中修改了CLAUDE.md,需要新开一个会话才能生效。这是一个容易踩的坑——修改了CLAUDE.md但Claude的行为没变,先检查是不是用的还是旧会话。
7.3 用/init自动生成
不知道从哪里下手?在项目根目录执行:
/init
Claude Code会分析你的项目结构,自动生成一份CLAUDE.md草稿。内容包括:检测到的技术栈、发现的构建命令、推断出的项目结构。
这份草稿不会很完整,但能帮你快速建立起一个框架,然后你在上面补充真正重要的信息。
生成后要做的事:
- 检查技术栈版本是否准确
- 确认构建命令是否正确
- 补充"重要背景"部分——这是草稿里最弱的地方,Claude靠代码结构无法推断业务约束
7.4 一份好的CLAUDE.md该写什么
从三个维度来想:
WHAT(项目是什么):技术栈、版本号、项目背景。不用太啰嗦,但关键信息要准确。
WHY(为什么这么做):解释那些看起来奇怪、但其实有原因的设计决策。比如"这个接口故意返回200而不是201,因为某个老版本的客户端不支持"。这类信息是最值得写进去的,因为这是Claude最容易犯错的地方。
HOW(怎么做事):代码规范、命名约定、测试要求、提交规范、构建命令。
7.4.1 完整的CLAUDE.md模板
下面是一个比较完整的模板,覆盖了大多数项目需要说明的内容:
# [服务名称] 项目说明
## 项目概述
[一两句话说明这个服务是干什么的]
**技术栈**:
- 语言:Java 17
- 框架:Spring Boot 3.2
- 持久层:MyBatis-Plus 3.5
- 数据库:MySQL 8.0(主从)
- 缓存:Redis 7(单机)
- 消息队列:RocketMQ 5.x
- 注册中心:Nacos 2.x
**依赖的外部服务**(含接口地址):
- 用户服务:user-service(获取用户信息)
- 支付服务:payment-service(支付回调)
## 开发环境
### 前置条件
- JDK 17+(不要用JDK 8,有些语法不兼容)
- Maven 3.8+
- 本地MySQL和Redis(Docker启动方式见下)
### 快速启动
```bash
# 启动依赖服务(MySQL + Redis)
docker-compose up -d
# 编译(跳过测试)
mvn clean package -DskipTests
# 启动(使用本地配置)
mvn spring-boot:run -Dspring-boot.run.profiles=local
# 运行所有测试
mvn test
# 运行单个测试类
mvn test -Dtest=OrderServiceTest
常用命令
# 生成MyBatis代码
mvn mybatis-generator:generate
# 检查代码规范
mvn checkstyle:check
# 打包Docker镜像
mvn jib:build
代码规范
分层规范
- Controller层:只做参数校验和响应封装,禁止写业务逻辑
- Service层:核心业务逻辑,使用构造器注入(不用@Autowired)
- Mapper层:只写数据库操作,不含业务判断
命名规范
- 接口路径:全小写 + 连字符,如
/api/order-items - 方法命名:动词+名词,如
createOrder、getOrderById - 常量:大写 + 下划线,统一放在
Constants类里
响应规范
- 所有接口响应用
Result<T>包装 - 错误码在
ErrorCode枚举里定义 - 分页响应用
PageResult<T>
重要背景(请务必阅读)
这一节记录了不从代码里能直接看出来、但非常重要的信息。
金额处理
- 所有金额字段用
BigDecimal,绝对不能用 double 或 float - 数据库里存的是分(整数),显示给用户时转成元
- 计算折扣时保留4位小数,最终金额四舍五入到分
订单状态机
- 状态流转:待支付 → 已支付 → 待发货 → 已发货 → 已完成
- 取消只能从"待支付"状态发起,已支付的订单走退款流程
OrderStatus枚举的整数值和数据库直接对应,不要改枚举值的整数
特殊接口
/api/payment/callback:支付回调接口,有幂等逻辑(同一笔支付重复回调会被忽略)
修改前先看懂再动,这里改错会导致重复扣款/api/order/cancel:取消订单需要同时释放库存,
整个逻辑在事务里,不要把库存操作移到事务外
已知的技术债
OrderService里的batchUpdateStatus方法性能差,后面要改,暂时别动它UserClient使用的是同步调用,后面要改成异步,现在不要加新的同步调用
测试规范
- 所有Service方法必须有单元测试
- 数据库测试用
@Transactional + @Rollback,不污染数据 - Controller测试用 MockMvc,不起真实服务
- 不要用
Thread.sleep做时间控制,用@MockBeanmock掉定时任务
禁止做的事
- 不要修改
BaseEntity(被所有实体继承,改了会影响全部表) - 不要在代码里写明文密钥或密码
- 不要在Service里直接操作订单表,必须通过 OrderDomainService
- 不要在测试里写固定的日期(比如 "2024-01-01"),用
LocalDate.now()
### 7.4.2 写CLAUDE.md的实用技巧
**优先写"坑"而不是写"常识"**:
Claude能从代码里推断出"这是个Spring Boot项目",但推断不出"这个字段不能用double"。CLAUDE.md的价值在于记录那些代码里看不出来的东西。
**给规范写上理由**:
```markdown
# 差:
- 不要用 double 存金额
# 好:
- 不要用 double 存金额(浮点精度问题,历史上出过一次生产事故,
1000元订单因精度误差显示为999.9999元)
有理由的规范,Claude在执行时会更认真,也不会"聪明地"觉得这条规范不合理然后忽略它。
用层次清晰的结构:
CLAUDE.md不是纯文字叙述,用Markdown标题、列表、代码块来组织,Claude读起来更准确。
7.5 Spring Boot项目完整示例
# 订单服务(order-service)项目说明
## 项目概述
电商平台的核心订单服务,负责订单全生命周期管理。
技术栈:Spring Boot 3.2 + MyBatis-Plus + MySQL 8.0 + Redis 7 + RocketMQ
## 构建与运行
```bash
# 编译并跳过测试(日常开发用)
mvn clean package -DskipTests
# 运行所有测试
mvn test
# 启动服务(需要本地MySQL和Redis)
mvn spring-boot:run -Dspring-boot.run.profiles=local
代码规范
- Controller层只做参数校验和响应封装,禁止写业务逻辑
- Service层禁止直接用@Autowired,统一用构造器注入
- 所有接口响应用统一的
Result<T>包装,不要直接返回业务对象 - 错误码统一在
ErrorCode枚举里定义,不要在代码里写魔法数字
重要背景(请务必读)
- 订单金额字段全部用
BigDecimal存储,绝对不能用 double 或 float OrderStatus枚举值和数据库里的整数映射关系见OrderStatus.java注释- 支付回调接口(/api/payment/callback)有幂等逻辑,修改前先看懂再动
- 测试环境的RocketMQ地址和生产不一样,配置在 application-local.yml 里
测试要求
- 新增Service方法必须有对应的单元测试
- 涉及数据库操作的测试用 @Transactional + @Rollback,不要污染测试数据
- Controller测试用 MockMvc,不要用真实HTTP
不要做的事
- 不要修改 BaseEntity 基类,它被所有实体继承
- 不要在代码里写日志密码或密钥,用配置文件
- 不要直接操作 order 表,所有写操作必须经过 OrderService
---
## 7.6 微服务项目的CLAUDE.md:多模块场景
当项目是微服务架构,多个服务在同一个仓库时(monorepo),CLAUDE.md的层级结构很重要。
### 7.6.1 目录结构和对应的CLAUDE.md
ecommerce-platform/
├── CLAUDE.md # 全局共享:通用规范、服务间关系
├── order-service/
│ ├── CLAUDE.md # 订单服务专属:订单状态机、金额规范
│ └── src/
├── user-service/
│ ├── CLAUDE.md # 用户服务专属:认证逻辑、隐私规范
│ └── src/
├── payment-service/
│ ├── CLAUDE.md # 支付服务专属:幂等规范、对账逻辑
│ └── src/
└── gateway/
├── CLAUDE.md # 网关专属:路由规则、限流配置
└── src/
### 7.6.2 根目录CLAUDE.md写什么
根目录的CLAUDE.md只写跨服务都需要知道的内容:
```markdown
# 电商平台(ecommerce-platform)全局说明
## 项目结构
这是一个微服务项目,包含以下服务:
- order-service:订单全生命周期
- user-service:用户和认证
- payment-service:支付和对账
- gateway:API网关
各服务有自己的 CLAUDE.md,进入对应目录查看详细规范。
## 跨服务通用规范
### API规范
- 所有服务间调用走内部域名(不走网关)
- 服务间鉴权用 JWT(在 common-auth 模块里)
- 响应格式统一用 Result<T>(在 common-core 模块里)
### 数据库规范
- 每个服务独立数据库,禁止跨服务直接查对方的数据库
- 服务间数据同步走消息队列,不走同步接口(异步化原则)
### 日志规范
- 所有日志用 SLF4J,不直接用 System.out.println
- 错误日志必须包含 traceId(MDC注入)
- 不要在日志里打印密码、手机号等敏感字段
## 公共模块
- `common-core`:统一响应、异常处理、常量
- `common-auth`:JWT工具、权限注解
- `common-redis`:Redis工具封装
- `common-mq`:MQ消息定义
**修改公共模块前必须评估影响范围**,所有服务都依赖它。
7.6.3 子服务CLAUDE.md写什么
子服务的CLAUDE.md专注于本服务的特殊知识:
# 支付服务(payment-service)说明
## 这个服务的核心职责
处理所有支付相关操作:创建支付单、接收第三方回调、对账、退款。
## 最重要的规范:幂等性
**支付相关的所有操作必须幂等**。原因:网络重试、用户重复点击、第三方回调重复推送都是正常现象。
幂等实现:
- 支付单有唯一的 `payment_no`(业务幂等键)
- 所有写操作先查幂等键是否存在
- `payment_callback` 接口用Redis分布式锁 + 状态机控制,已处理的回调直接返回成功
## 第三方渠道的特殊性
- 微信支付:回调可能延迟30分钟,不要以为没收到就是失败
- 支付宝:沙箱环境和生产的密钥不同,注意配置文件
- 银行卡直连:有严格的报文格式要求,修改报文组装逻辑前先看文档
## 不要做的事
- 不要修改 `PaymentStatus` 枚举值对应的整数(和数据库直接映射)
- 不要在回调接口里做耗时操作(微信要求3秒内响应)
- 退款逻辑涉及资金,任何修改前必须CR
7.7 用#前缀实时更新记忆
在对话过程中,你可以用#前缀向Claude说话,它会把这条信息记录下来而不是作为普通对话处理:
# 我们刚讨论决定,分页查询统一用游标分页而不是offset分页
# 前缀会将内容写入 CLAUDE.local.md(个人本地记忆,不提交git)。如需更新团队共享的规范,请手动编辑 CLAUDE.md。这对于记录"正在进行中的决策"很有用,不用手动去编辑文件。
7.8 CLAUDE.local.md详解
CLAUDE.local.md是你的个人本地记忆文件,和CLAUDE.md的区别:
| 对比项 | CLAUDE.md | CLAUDE.local.md |
|---|---|---|
| 提交到Git | 是 | 否(加入.gitignore) |
| 团队可见 | 所有人 | 只有你 |
| 内容类型 | 团队共享规范 | 个人偏好、临时决策 |
| 更新方式 | 手动编辑 | 手动或 # 前缀 |
适合写进CLAUDE.local.md的内容:
# 个人开发偏好
## 我的工作环境
- IDE:IDEA 2024
- 终端:iTerm2
- 本地数据库密码:(这里可以写,因为不会提交git)
## 当前正在做的任务
正在重构订单模块的缓存逻辑,预计本周完成。
进度:Service层已完成,Mapper层还没改。
待讨论:Redis的key设计还没确定。
## 个人偏好
- 给我的代码示例都用Java 17语法(record、text block等)
- 错误信息用中文,代码注释用英文
- 生成测试时优先用JUnit 5 + Mockito
## 暂定的决策(还没写入CLAUDE.md)
- 决定用Caffeine本地缓存替代Redis,原因是减少运维复杂度
待确认:和团队对齐后写入CLAUDE.md
CLAUDE.local.md的维护建议:把它当做一个"工作日志",记录当前任务的状态、临时决策、个人配置。不需要永久保留,定期清理过时的内容。
7.9 CLAUDE.md的反模式
有些东西看起来适合写进CLAUDE.md,但实际上会让它变得臃肿、低效。
7.9.1 不应该写进去的东西
不应该写进去:完整的代码规范文档
# 错误示范:把所有规范都堆进去
## 命名规范
- 类名用大驼峰
- 方法名用小驼峰
- 变量名用小驼峰
- 常量用全大写
- 包名用全小写
- 接口名以I开头
...(继续列了30条)
这些规范Claude本来就知道(Java编程规范),写进去只是浪费Token。CLAUDE.md里只写那些"不说Claude会猜错的"内容。
不应该写进去:Claude能从代码里自己推断的东西
# 错误示范
这是一个Spring Boot项目,使用MyBatis做持久层,MySQL是数据库。
Controller层返回JSON格式的响应...
Claude看到你的代码就知道了,不需要在CLAUDE.md里重复。
不应该写进去:过期的信息
# 错误示范:已经废弃的说明还留着
## 旧版API(已废弃,2023年10月下线)
- /api/v1/order:旧版订单接口,不要再用
过期信息比没有信息更危险——Claude可能认真遵守了一条早就不适用的规范。
不应该写进去:应该用自动化工具保证的规范
# 错误示范:用CLAUDE.md替代linter
- 所有方法必须有JavaDoc注释
- 代码行长度不超过120字符
- import不能有通配符(import java.util.*)
这些规范应该用Checkstyle或SonarQube来强制检查,不应该依赖Claude记住。Claude的任务是帮你思考业务逻辑,不是充当linter。
7.9.2 反模式的本质
CLAUDE.md过于冗长会带来两个问题:
- 消耗大量Token(每次会话都要读入)
- 重要信息被淹没在大量普通信息里,Claude实际执行时关注度下降
一个经验法则:如果CLAUDE.md超过了500行,就需要审视了——要么是写了太多Claude本来就知道的东西,要么是需要拆分到子目录的CLAUDE.md里。
7.10 CLAUDE.md的维护策略
7.10.1 建立"更新触发点"
不要等到CLAUDE.md完全过时才更新。在以下情况下,把更新CLAUDE.md作为任务的一部分:
- 做了重要的架构决策:完成了缓存方案的讨论,把结论写进去
- 发现了一个新的坑:踩到了一个Claude会犯的错,把规避方法写进去
- 修改了关键接口:接口契约变了,更新相关说明
- 增加了新的约束:比如新增了"金额处理必须四舍五入"的规范
7.10.2 季度性清理
每个季度做一次CLAUDE.md的"清理":
- 检查"重要背景"里的信息是否还准确
- 删除已经废弃的功能说明
- 把已经用自动化工具覆盖的规范从CLAUDE.md里移出
- 更新命令(比如构建命令里的版本号)
7.10.3 用Claude来帮你维护CLAUDE.md
一个有意思的用法:让Claude帮你检查CLAUDE.md的质量:
帮我审查我们的CLAUDE.md:
1. 有哪些内容已经可以从代码里直接推断出来,可以删掉?
2. 有哪些重要的背景知识是从代码里看不出来的,需要补充?
3. 有没有过时的信息需要更新?
7.11 团队协作中的CLAUDE.md
7.11.1 CLAUDE.md是团队的共同财产
CLAUDE.md应该提交到Git,像对待代码一样对待它。这意味着:
- Code Review:修改CLAUDE.md也需要PR和CR
- Changelog:重要的修改在commit message里说明原因
- 冲突解决:多人同时修改时需要合并,和代码一样处理
7.11.2 建立团队共识
引入CLAUDE.md的时候,团队需要达成几个共识:
谁来维护:不是一个人的活。每个人在发现"Claude犯了某个可预防的错误"时,都有责任更新CLAUDE.md。
更新的质量标准:写进CLAUDE.md的内容要满足"Claude不说会犯错"这个标准,不是什么都往里堆。
避免规范冲突:不同人写的规范之间可能矛盾。设立一个人(比如技术负责人)来做最终的一致性审查。
7.11.3 新人入职场景
CLAUDE.md对新人入职也有价值。一个新人加入项目,在第一天让Claude Code读CLAUDE.md,然后问:
我刚加入这个项目,还不熟悉代码库。
基于项目说明书,给我讲讲最需要注意的5件事,
特别是那些容易犯错的地方。
Claude会帮他整理出关键的背景知识,比任何新人文档都更精准——因为CLAUDE.md里写的就是"容易犯错的地方"。
7.12 CLAUDE.md的测试:如何验证规则被遵守
写了一堆规范,怎么知道Claude真的在遵守?
7.12.1 用反向验证
写完某条规则后,立即用一个会触发它的场景测试:
# 你在CLAUDE.md里写了:
不要用 double 存金额,必须用 BigDecimal
然后在会话里测试:
帮我写一个计算订单折扣的方法,
输入:原价(元)、折扣率(0.0到1.0),
输出:折后价(元)
看Claude生成的代码里,金额字段用的是BigDecimal还是double。如果用了double,说明你的规范写得不够清晰或者权重不够,需要调整表达方式。
7.12.2 问Claude是否理解了规则
在会话开始时,直接问:
你已经读了我们的CLAUDE.md。
请告诉我,你认为这个项目里最关键的3条规范是什么,
以及违反它们会有什么后果。
Claude的回答能反映它对CLAUDE.md的理解程度。如果它说出来的不是你认为最重要的,可能需要在CLAUDE.md里加重点标注。
7.12.3 持续观察和反馈
日常使用中,每当发现Claude做了CLAUDE.md里明确禁止的事,记录下来:
- 规则写得不够清晰?重新表述
- 规则太多,Claude没注意到这条?把重要的放在更显眼的位置
- Claude理解了规则但判断该场景不适用?补充更明确的约束条件
7.13 最佳实践:保持精简
CLAUDE.md不是什么都往里堆的地方。几个原则:
不要把CLAUDE.md当代码规范文档用。规范太多,Claude会忘记执行;而且规范执行应该靠linter和checkstyle来保证,不是靠Claude记住。
只写Claude容易犯错的地方。那些"在代码里一眼就能看出来"的东西不用写,Claude自己能推断。只写那些"没有背景知识会搞错"的东西。
定期清理过期信息。项目迭代了,CLAUDE.md里写的信息可能已经不准确了,过期信息比没有信息更有害。
一个检验标准:对着CLAUDE.md里的每一条,问自己"如果没有这条,Claude会在什么场景下犯什么错?"如果答不上来,这条可能就不需要。