【AI Native开发-09】测试全绿之后,代码评审还应该拦什么?
30 秒看懂这篇
测试全绿只说明已执行的断言通过了。它不会自动告诉你:是否改了 Plan 禁止触碰的文件、是否引入了新依赖、是否把关键逻辑 mock 成了空转。
flowchart TD
A["一份待合入 Diff"] --> B["第一层:范围<br/>文件与 Plan 授权一致?"]
B --> C["第二层:基本真实性<br/>依赖、导入、构建、调试残留"]
C --> D["第三层:高风险逻辑<br/>权限、数据、并发、不可逆操作"]
D --> E["第四层:测试有效性<br/>行为与副作用真的被断言了吗?"]
E --> F{"风险分级放行"}
F --> G["低风险:自动检查 + 快速评审"]
F --> H["高风险:人工逐行审 + 明确批准"]本篇给出 Diff 四层评审法、变更风险分级和 CI 控制规则。核心不是放弃代码质量,而是先确保改动忠于契约,再把有限的人眼投入高风险实现。
一个典型场景
删除功能的分支有 6 个文件、约 300 行改动,定向测试通过。合入前执行生产构建却失败:一处是 Plan 禁止修改的 article-loader.ts 被改了函数签名,另一处是删除接口 import 了不存在的模块。定向测试因 mock 没有覆盖真实导入与生产构建,没能发现。
这个案例的合理交付顺序是:定向测试通过 → 全量静态检查与生产构建失败 → 阻止合入 → 退回 Implement。不应写成“已经上线后才发现构建失败”;CI 的价值正是让这类问题无法越过合入闸门。
核心问题:AI 时代的 Code Review 变了什么
传统 Code Review 的隐含假设是:改动量小、作者理解每一行、评审者也大致理解。AI 打破了这三个假设:
- 改动量暴涨:AI 可以一次产出几百行,评审者逐行看的负担急剧上升;
- “作者”不完全理解改动:人是 AI 的协作者,但人未必逐行读过 AI 写的每一行——这意味着评审不能依赖”作者自己把关过了”;
- 越界动机更强:AI 倾向于产出”完整”的方案,会主动补齐它认为缺失的部分(造函数、加 import、顺手改别处),这些补齐全无 Plan 授权。
所以评审的重心必须转移:
传统评审重心:代码对不对、优不优雅、有没有 bug
AI 时代评审重心:
1. 这份 Diff 是否 = Plan 授权的改动?(一致性 / 越界)
2. 高风险逻辑是否正确、是否有人眼确认?(权限、不可逆操作)
3. 有没有 AI 特有的痕迹?(幻觉 import、编造 API、空实现、吞错误)
4. 测试是否真的验证了行为,而不是陪跑?(断言有效性)顺序很重要:先查”该不该有这些改动”,再查”改动对不对”。 越界的改动再优雅也是事故,因为它绕过了 Plan 评审。
Diff 评审清单
评审一份 AI 产出的分支,按这个顺序过:
【第一层:范围一致性】(先看,最重要)
□ git status / diff --stat 里的文件,是否都在 Plan 的
"新增 / 修改"清单内?
□ Plan 的"不碰清单"里的文件,是否一个都没动?
□ 有没有 Plan 之外的新文件、新依赖(package.json)?
□ 改动行数和这个步骤的规模匹配吗?
(一个"加删除按钮"的步骤改了 800 行,必有夹带)
【第二层:AI 特有痕迹】
□ 有没有 import / require 一个项目里不存在的模块?
□ 有没有调用一个不存在的函数 / API?
(grep 确认被调用的函数真的有定义)
□ 有没有空 try/catch、catch 后什么都不做的错误吞掉?
□ 有没有写死的测试数据、mock 值残留在生产代码里?
□ 有没有返回假数据 / 空数组 / "TODO 实现"的占位逻辑?
□ 注释里有没有"假设""大概应该是"这类不确定措辞?
【第三层:高风险逻辑】(必须逐行人眼)
□ 权限校验:服务端是否真的校验了,还是只靠前端隐藏按钮?
□ 不可逆操作:删除 / 覆盖 / 外发,有没有确认 + 缓解?
□ 状态判断:用的是真实字段(frontmatter 的 hide 布尔)还是幻觉字段
(status === 'published',本项目根本没有 status 字段)?
□ 并发 / 幂等:重复调用、并发调用安全吗?
□ 输入校验:外部传入的 id、参数有没有校验和边界处理?
【第四层:测试有效性】
□ 测试断言的是行为 / 副作用,还是只断言"没报错"?
□ 权限失败的用例,有没有断言"副作用没发生"
(文件还在 / 数据没变),而不只是状态码?
□ 用空实现测试法推演过吗?
(把被测逻辑换成空实现,测试应该变红——不变红说明测试没在测)
□ 有没有被改宽松 / 删掉的断言?(对比 Plan 阶段预期)第一层和第二层是 AI 时代新增的重点,也是传统评审最容易跳过的。养成一个动作:合入前跑一次 git diff main --stat,对着 Plan 的文件清单逐行核对。 这一步两分钟,能拦住绝大多数越界事故。
变更风险分级
不是所有改动都值得逐行审。按风险分级,把有限的人眼注意力花在刀刃上。四个维度(第 00 篇风险分级的细化应用):
| 维度 | 低风险 | 高风险 |
|---|---|---|
| 可逆性 | 可随时回滚(纯前端文案、样式) | 不可逆(删除、支付、外发、数据覆盖) |
| 影响面 | 单页面、单函数 | 全局、多模块、被多处依赖 |
| 数据敏感性 | 不碰用户数据 | 触及权限、隐私、资金 |
| 检测延迟 | 错了立刻能看到 | 错了要到生产 / 特定条件才暴露 |
据此把变更分成三级:
P0(强制逐行人眼评审 + 人工确认合入):
- 权限 / 认证 / 授权逻辑
- 不可逆操作(删除、支付、发消息、数据删除)
- 全局共享模块、被多处依赖的核心函数
- 数据迁移、schema 变更
→ 不允许 AI 自动合入,必须人逐行看 Diff 并签字
P1(人评审重点逻辑,测试充分可较快放行):
- 新业务功能、新接口
- 修改现有业务逻辑
- 涉及状态流转、并发
→ 人审范围一致性 + 高风险逻辑,测试必须覆盖异常路径
P2(可较宽松,AI 自检 + CI 把关;仅在预先批准时可自动合入):
- 纯展示、文案、样式调整
- 文档、注释、类型补充
- 不涉及逻辑和数据的重构
→ 测试 / 构建通过 + Diff 范围核对;自动合入不得触碰受保护路径关键原则:风险等级由”改动本身最危险的那部分”决定,不由改动大小决定。 一个三行的删除权限逻辑改动是 P0,一个三百行的纯样式页面是 P2。AI 特别需要这个分级——它倾向于认为”改得少就简单”,但权限判断里一行错就是越权。
越界检测:把”不许越界”变成硬规则
靠人每次记得核对清单不可靠,要把变更控制落到机制上:
1. 文件所有权 / 路径保护(CODEOWNERS 或 CI 脚本)
# .github/CODEOWNERS 示例;替换为真实且有写权限的用户或团队
/src/lib/auth/** @org/auth-maintainers
/src/lib/article-loader.* @org/content-maintainers
/keystatic.config.ts @org/content-maintainers
/package.json @org/platform-maintainersCODEOWNERS 本身负责分配所有者,不会单独形成强制闸门。GitHub 上还需要在分支保护或 ruleset 中启用 Require review from Code Owners;其他平台可用相应的保护分支与 CI 审批规则实现同等约束。
2. Diff 白名单校验
把 Plan 授权的文件清单作为输入,CI 校验实际改动文件是否超出:
# 思路:Plan 里声明允许改的文件,CI 比对实际 diff
allowed_files = plan["files_to_add"] + plan["files_to_change"]
actual_changed = git diff --name-only main...HEAD
unauthorized = actual_changed - allowed_files - 自动生成文件
if unauthorized:
fail(f"发现 Plan 未授权的改动: {unauthorized}")这道检查把”越界”从”评审时靠人眼发现”变成”CI 自动拦截”。AI 改了”不碰清单”里的文件,CI 直接红,根本到不了人评审那一步。
3. 新增依赖告警
package.json / pnpm-lock.yaml 的任何变更都应触发人工确认。AI 有时为了一个小功能引入一个重依赖,甚至 import 一个不存在的包(幻觉依赖)。
4. 生产代码里的调试残留扫描
CI 扫描:console.log / debugger / 写死的 localhost /
TODO / FIXME / 注释掉的代码块
出现在生产代码里则警告或拦截。变更可追溯
每个合入的变更都应该能回答三个问题:
这个改动是为什么? → 关联的 Spec / Plan / issue
它改了什么? → Diff(在授权范围内)
它被验证过吗? → 关联的测试 + Verify 报告落到实践:
- 分支名 / commit 关联需求编号(如
feature/article-delete-REQ001); - PR 描述里贴 Plan 链接和测试结果;
- 高风险变更在 PR 描述里显式列出”风险点和缓解措施”。
这样三个月后回头看任何一处代码,都能追溯到当初的意图和验证证据——这在 AI 高频产出的代码库里尤其重要,否则代码库会迅速堆积”没人知道为什么在这”的 AI 生成逻辑。
代码评审反模式
反模式一:全绿就合。 测试通过 ≠ 改动安全。测试可能是陪跑(断言无效)、可能漏掉越界文件、可能没覆盖高风险路径。测试是必要条件不是充分条件。
反模式二:只看主逻辑。 人的注意力天然集中在”这个功能怎么实现的”,于是跳过 import 区、跳过错误处理、跳过”顺手改的别的文件”。AI 的幻觉和越界恰恰藏在这些角落。对策:用清单强制覆盖第一层(范围)和第二层(AI 痕迹)。
反模式三:信任”作者已检查”。 传统评审里作者是人、会自我把关;AI 时代”作者”是 AI + 人,人可能根本没逐行看过。不能假设改动已经被理解过——评审者可能是第一个完整读这份 Diff 的人类。
反模式四:对 AI 产出过度宽容或过度苛刻。 两个极端都有害:宽容到”AI 写的大概对”就合,会放进幻觉代码;苛刻到要求每行都像资深工程师手写、反复让 AI 重写,会拖慢速度还不如自己写。正确姿态:对范围和高风险逻辑零容忍,对风格和非关键实现保持宽容。
回退与放行
越界、幻觉导入和高风险实现错误退回 Implement;陪跑测试回测试设计或自动化补强。P0 必须人逐行评审,P0 / P1 由人放行;P2 只有在预先批准的自动化策略、受保护路径和 CI 条件全部满足时才可自动合入。反复出现的越界应沉淀为 CI 与 pitfalls 规则。
结语
AI 让”写代码”变便宜了,于是”判断哪些改动该存在”变成了最贵的动作。代码评审的主角不再是代码本身,而是变更与契约的一致性——这份 Diff 是不是我们在 Plan 里授权的那一份。
到这里,“怎么把代码做出来”这条线(Spec → Plan → 评审 → Implement → 代码评审)就完整了。但软件正确与否,最终要靠验证说话。从下一篇开始进入测试与验证篇:首先是测试的源头——怎么把一份 Spec 系统性地变成结构化的文本测试用例,让”测什么”不再凭经验,而是每条需求都有对应的用例追踪。
