Rules、Hooks、Lint 与 CI:怎样建立真正可靠的质量防线
把一条要求写进 CLAUDE.md 或 .claude/rules/,并不等于它会被百分之百执行。
Claude 会把这些内容当作上下文和行为指导。它通常会尽量遵守,但在指令模糊、相互冲突、上下文过长或用户临时要求不同做法时,仍然可能偏离。
因此,团队需要分清“指导”和“强制”。
rules 负责教 AI 怎么写
假设团队希望统一错误处理:
# 错误处理规范
- 仅在能够恢复、转换异常或补充关键上下文的边界捕获异常
- 错误日志使用项目 logger 的 error 方法
- 日志包含 requestId 和相关业务实体 ID
- 不得吞掉异常或只打印堆栈
- HTTP 错误响应由全局异常处理器统一生成
Claude 看到这些规则后,可以主动调整实现:
- 判断当前层是否是合适的异常边界。
- 使用项目现有 logger。
- 增加必要上下文。
- 避免在每一层重复记录同一个错误。
- 复用全局异常处理器。
这就是 rules 的优势:它不仅指出“结果不合格”,还提供正确写法的方向。
Hook 负责在生命周期节点执行动作
Claude Code 的 Hook 可以在特定生命周期事件发生时运行命令、调用 HTTP 端点或执行基于模型的判断。
常见事件包括:
UserPromptSubmit:用户提示提交后、Claude 处理前。PreToolUse:工具调用执行前,可以阻止操作。PostToolUse:工具执行完成后,可以检查结果。Stop:当前回复即将结束时。InstructionsLoaded:指令文件被加载时。
所以“Hook 只能事后检查”并不准确。它既可以事前阻止,也可以事后验证,还可以用于诊断规则加载。
适合 Hook 的场景包括:
- 阻止执行特定危险命令。
- 修改文件后自动运行快速检查。
- 阻止访问受保护路径。
- 在会话结束前确认关键验证是否完成。
- 记录实际加载了哪些规则。
- 为团队提供本地即时反馈。
但 Hook 并不是所有质量规则的最终保障。开发者可能不用 Claude Code,也可能在其他环境提交代码,因此真正不可绕过的要求仍然要放到 CI 或组织级策略中。
permissions 和 managed settings 负责技术约束
如果要求是“绝对不能调用某个工具、命令或路径”,单纯使用自然语言提示不够可靠。
Claude Code 设置具有不同作用域:
- 用户级:个人跨项目设置。
- 项目级:提交到仓库并与团队共享。
- 本地级:当前用户在当前项目的机器特有设置。
- Managed:由组织或 IT 部署,普通项目配置不能覆盖。
项目级适合共享 Hooks、权限和团队工具配置;Managed 适合组织安全政策、合规要求、沙箱和不可覆盖的限制。
这里有一个简单原则:
行为指导写进指令;工具权限写进设置;不可绕过的组织要求写进托管策略。
lint 负责确定性的代码检查
很多编码规范没有必要让 AI“记住”,因为机器可以直接检查。
例如:
- 缩进、引号、尾逗号和换行交给 formatter。
- 未使用变量、危险 API 和复杂度交给 lint。
- Java 命名、导入和代码结构交给 Checkstyle 或静态分析。
- 分层依赖交给 ArchUnit、dependency-cruiser 等架构测试。
如果 ESLint 已经会禁止 console.log,rules 中不需要反复解释全部检测细节;只需要告诉 Claude 使用哪个 logger、应该怎样记录结构化上下文。
这种分工很重要:
- 工具检查“有没有违反”。
- 规则解释“正确方案是什么”。
测试负责行为正确性
规则可以要求“修改行为时同步补测试”,但最终是否真的覆盖关键场景,需要运行测试验证。
团队应明确:
- 哪些改动需要单元测试。
- 哪些 API 需要集成测试。
- 哪些核心流程需要端到端测试。
- 测试文件放在哪里。
- 命名方式是什么。
- 最低必须运行哪些命令。
不要只写“补充必要测试”。更可执行的规则是:
- 修改 Service 分支逻辑时,在同模块 `src/test` 增加单元测试
- 修改 HTTP 状态码、请求校验或响应结构时,增加 Controller 集成测试
- 修复缺陷时先增加能复现问题的回归用例
- 提交前运行 `mvn verify`
CI 才是团队真正的合并门禁
本地规则和 Hook 都可以提高反馈速度,但 CI 才能为整个团队提供统一结果。
适合在 CI 强制执行的项目包括:
- 编译和类型检查
- formatter 与 lint
- 单元测试、集成测试
- Secret Scan
- 依赖漏洞检查
- 数据库迁移验证
- API schema 兼容性检查
- 架构边界测试
- 许可证与合规检查
并且要配合受保护分支:检查不通过就不能合并。否则 CI 只是一个可忽略的提醒。
对于密钥检查,不能只搜索 API_KEY= 或 password=。真实凭据可能有多种格式,也可能出现在 JSON、YAML、证书或历史提交中。应使用专业 Secret Scanner,并在提交前提供快速反馈、在 CI 中再次强制验证。
人工 Review 应该保留什么?
当格式、静态检查和测试能够自动完成后,人可以把精力放在真正需要判断的问题上:
- 业务逻辑是否正确。
- 接口设计是否符合未来演进方向。
- 权限模型是否存在越权路径。
- 事务边界是否合理。
- 是否真的需要增加缓存。
- 数据模型是否会造成长期维护成本。
- 失败模式、回滚方案和可观测性是否足够。
这些问题很难通过一条静态规则完全解决,也不能因为 AI 生成了代码就跳过人类判断。
一套实用的四层防线
可以把整个体系理解成四层:
第一层:生成前指导
CLAUDE.md.claude/rules/- Skills
- 现有代码范例
目标:让 AI 第一次就尽量写对。
第二层:操作时约束
- permissions
PreToolUse等 Hooks- 沙箱和受保护路径
目标:阻止高风险行为,快速反馈。
第三层:产物验证
- formatter
- lint
- 测试
- Secret Scan
- CI
目标:确定性地判断代码是否达到门槛。
第四层:人工判断
- Code Review
- 安全评审
- 架构评审
- 高风险业务审批
目标:处理无法完全自动化的决策。
不要用规则代替工具,也不要用工具代替规则
只写 rules 的问题是:Claude 可能不完全遵守。
只写 Hook 的问题是:它可以告诉 Claude“失败了”,却未必充分解释项目期望的正确设计。
只跑 lint 的问题是:它能检查代码形状,却很难判断业务设计是否合适。
只靠人工 Review 的问题是:成本无法随着 AI 代码产量线性扩张。
所以成熟方案不是四选一,而是让每种机制负责自己最擅长的部分。
小结
- rules 是指导层,不是强制执行层。
- Hooks 可以事前阻止、事后检查,也可以诊断加载过程。
- permissions 与 managed settings 适合工具和组织级约束。
- lint、测试和 CI 负责确定性验证。
- 人工 Review 负责业务、架构与风险判断。
下一篇将讨论规则本身的治理:规则从哪里来、怎样解决冲突、什么时候删除,以及如何用数据判断规则是否真的有效。