团队规则不是一次写完的:从 Code Review 中持续演进
不少团队第一次建设 AI 编码规范时,会组织一次会议,把大家能想到的要求全部写进 .claude/rules/。
这种方式通常只能得到一份看起来很完整、实际很快过时的规范。
真正有效的规则并不是一次设计出来的,而是在项目实践中逐渐生长:发现问题、分析原因、选择治理机制、验证效果,再清理过期内容。
规则主要有两个来源
来源一:项目成立时确定的架构约定
这类规则通常来自技术选型或架构评审,例如:
- API 采用 REST 风格。
- Java 方法使用 camelCase。
- 数据库结构改动通过 Flyway migration。
- 日志使用 SLF4J,不使用标准输出。
- Controller 不直接访问数据库。
- 认证与授权使用公司统一组件。
它们在项目开始时就应该进入 CLAUDE.md、rules、架构测试或 CI。
但要注意,不是所有架构文档都应该复制给 Claude。只保留会影响日常实现、且不能从代码稳定推断的部分。
来源二:开发和 Review 中反复出现的问题
这一类往往更有价值,因为它来自真实成本。
例如项目最初只写了:
- API 使用统一响应格式
后来出现了多次线上或 Review 问题:
- 必填字符串没有校验空白字符。
- 数值参数没有范围限制。
- 分页大小没有上限。
- 错误响应泄露内部异常信息。
- 列表接口没有返回总数。
这时可以把规则演进为:
# API 输入校验
- 所有外部输入在进入业务层前完成校验
- 必填字符串同时拒绝 null、空字符串和纯空白
- 数值参数必须定义业务范围
- 分页 pageSize 最大为 100
- 枚举参数拒绝未知值,不得静默使用默认值
# API 错误响应
- 不向客户端返回堆栈、SQL 或内部类名
- 业务错误映射为项目定义的公开错误码
- 未知异常由全局处理器转为通用服务端错误
规则因此不再是一句口号,而是对真实故障模式的总结。
什么时候应该新增规则?
不要看到一次错误就马上增加永久规则。先判断它属于哪种情况:
- 一次性的疏忽:修复即可,不一定需要新增规则。
- AI 或团队成员反复犯同类错误:值得沉淀。
- 现有规则过于模糊:修改原规则,不要另加重复条目。
- 能由静态工具准确检查:增加 lint 或 CI,而不是扩充自然语言。
- 属于高风险底线:同时增加规则说明和强制门禁。
- 只影响一个模块:使用路径规则。
- 属于多步骤操作:转为 Skill。
一个实用门槛是“第二次发生”:Claude 或 Reviewer 第二次遇到同类问题时,就应该评估是否把它沉淀为项目资产。
从一次 Review 到一条规则的完整流程
假设 Reviewer 发现新接口没有校验 userId。
第一步:确认问题不是孤例
查看近期 PR,确认是否已有类似问题,以及现有规则是否提到外部输入校验。
第二步:找出根因
可能的原因包括:
- 规则完全缺失。
- 规则只写了“注意校验”,过于模糊。
- 路径 glob 没有匹配当前 Controller。
- 项目没有统一校验组件。
- CI 没有相关测试。
不同根因对应不同解决方案。
第三步:选择正确治理层
- 告诉 Claude 如何使用项目校验器:rules。
- 生成新接口的标准步骤:Skill。
- 禁止未经验证的 DTO 进入 Service:架构测试或 lint。
- 验证错误响应:集成测试。
- 判断某个业务参数的合理范围:人工评审。
第四步:加入正反示例
不要这样:
public User getUser(String userId) {
return userService.findById(userId);
}
应该使用项目约定的 DTO 与 Bean Validation:
public UserResponse getUser(@NotBlank String userId) {
return userService.findById(userId);
}
如果项目已经由 Spring 和全局异常处理器统一处理校验失败,就不必再手写一次 null 和空字符串判断。规则应该匹配项目实际架构,而不是堆叠重复防御。
第五步:验证是否改善
观察后续几次 PR 中同类问题是否减少。如果没有,需要检查规则是否加载、是否足够具体,或者是否应该升级为工具检查。
冲突规则必须主动解决
不同角色对规范的理解可能不同。例如:
- 前端希望列表响应为
{ data: [] }。 - 后端希望返回
{ data: { list: [], meta: {} } }。
两种方案各有理由,但不能同时作为全局最终规则交给 AI。
可以按规则性质决定:
- 架构和技术选型:由明确的技术负责人或架构决策流程拍板。
- 风格偏好:团队讨论后形成一致意见。
- 短期无法统一:先不写成强规则,维持现状并积累数据。
- 不同模块确实需要不同方案:用路径明确分区。
规则文件中最好记录简短理由或链接到 ADR,避免几个月后有人在不了解背景的情况下“修正”回旧方案。
规则需要负责人
可以按主题设置维护责任:
.claude/rules/
├── coding-style.md # 工程效率负责人
├── api-design.md # 后端负责人
├── security.md # 安全负责人
├── testing.md # 质量负责人
└── frontend.md # 前端负责人
负责人不意味着只有一个人能修改,而是确保:
- 有人处理冲突和过期内容。
- 修改能够找到合适 Reviewer。
- 高风险规则不会被随意弱化。
- 团队知道出现问题时找谁讨论。
可以使用 CODEOWNERS 或仓库评审规则,把规则变更纳入正式 PR 流程。
每个大版本都应该做一次清理
规则只增加不删除,最终一定会变成负担。定期清理至少包括四类工作。
删除过时规则
例如过去要求应用层包装 { code, data, message },后来公司网关统一完成包装。如果旧规则不删除,AI 可能再包装一次。
合并重复规则
同一个日志要求同时出现在 CLAUDE.md、coding-style.md 和 backend.md,不仅浪费上下文,还可能在后续修改时出现版本不一致。
消除冲突规则
例如“方法名使用 camelCase”和“数据库字段使用 snake_case”并不一定真正冲突,但如果没有明确对象,Claude 可能误解。应该改为:
- Java 方法和变量使用 camelCase。
- PostgreSQL 表名和字段使用 snake_case。
- ORM 映射遵循现有实体映射方式。
删除能够由工具完全替代的说明
当 formatter、lint 或架构测试已经稳定检查某项规则后,可以缩短自然语言,只保留 AI 生成正确代码所需的部分。
如何衡量规则有没有效果?
“感觉代码更统一了”不够可靠。可以建立几个简单指标。
Code Review 问题分类
每个迭代统计:
- 命名问题数量
- 日志问题数量
- 缺少错误处理或错误边界不当
- 测试遗漏
- API 校验遗漏
- 数据库查询问题
- 安全和敏感信息问题
如果某类问题持续出现,说明对应规则可能缺失、模糊、范围错误,或者应该升级成自动检查。
CI 违规趋势
统计每周或每个迭代的:
- lint 失败次数
- 静态分析问题数量
- 测试失败类型
- Secret Scan 命中
- 架构测试违规
重点看趋势,而不是只看总数。代码量上升时,最好同时观察每个 PR 或每千行变更的违规率。
随机抽查 AI 生成 PR
定期抽取若干 PR,检查:
- Claude 是否遵守路径规则。
- 生成代码是否模仿了项目现有模式。
- 是否存在“测试通过但设计明显不一致”的情况。
- 哪些规则经常被忽略。
Review 返工时间
记录从首次 Review 到满足合并标准所需的轮次和时间。如果低级规范问题减少,Review 应该逐渐转向业务和设计讨论。
规则成熟度的三个阶段
阶段一:建立基本边界
项目初期只写少量高价值规则:输入校验、统一错误模型、敏感数据要求、数据库变更方式。
阶段二:从真实问题补充细节
根据事故和 Review 加入分页上限、幂等性、批量操作、日志上下文和测试要求。
阶段三:重新组织与自动化
把规则按主题和路径重组,将确定性内容迁移到 lint/CI,清理重复和过时说明,并建立指标。
这比项目开始时试图写出一份“终极规范”可靠得多。
小结
规则治理是一个闭环:
发现重复问题 → 分析根因 → 选择治理层 → 编写或修改规则
→ 自动化验证 → 观察指标 → 清理冲突和过期内容
规则的价值不在于数量,而在于它是否减少了真实错误、返工和 Review 成本。
下一篇给出一套完整的 Java + Spring Boot 示例,把前面的分层、路径规则和工程约束组合在一起。