让 Cursor 加个功能,它手一快,一口气动了七八个文件、甩给你几百行改动。你从第一行往下翻,看着都挺像那么回事,测试也绿了,于是点了全部接受。过两天,一个跟这次需求八竿子打不着的角落突然炸了------回头一查,正是它那天"顺手"改掉的一行。
这种事我踩过不止一次。一开始我以为是自己看得不够仔细,后来才想明白:AI 生成的大段改动,问题从来不是均匀分布在每一行里的,而是高度集中在固定几个地方。 你逐行平均用力,几百行看下来又累又抓不到重点;真正的坑,往往就在它"顺手多做的那一点"和"看着等价其实改了行为的那一处"。
这篇专门聊一件事:面对 AI 一次改出来的大段代码,怎么用一套固定的 review 顺序,把有限的注意力压到真正有风险的地方,再让它自己把改动交代清楚。熟练之后,一次几百行的改动,几分钟就能扫干净,不用再硬着头皮逐行啃。
一、先转个观念:review AI 的 diff,不是逐行读代码,而是找它偏离你意图的地方
人 review 同事写的代码,默认前提是对方理解了需求,你主要看他的实现稳不稳、好不好。review AI 的代码,这个前提不能有。它有个很要命的倾向:只要它觉得"这样更完整",就会自信地把你没让它做的事一起补上,而且不打招呼。
所以看它的 diff,目标不是"读懂每一行",而是带着一个明确的问题去翻:哪些地方偏离了我这次交代给它的意图? 偏离分两种,一种是明着的改错了,一种是它擅自扩大了改动范围。后者隐蔽得多,也是大段 diff 翻车的主因。
| 看法 | 注意力怎么分配 | 结果 |
|---|---|---|
| 从第一行逐行读到最后 | 平均铺在几百行上,看到后面忘了前面 | 又累又容易漏,真正的坑一扫而过 |
| 先看范围、再按风险点扫 | 八成精力压在夹带改动和行为变化上 | 快,而且专打它最爱出问题的地方 |
还有个心理关要过:测试是绿的,不等于这次改动是安全的。 测试只能证明"被测试覆盖到的路径没变",而 AI 夹带的私货,十有八九落在你压根没写测试的角落里。绿勾是底线,不是放行条。
二、最高风险区:它"顺手"改了和本次任务无关的东西
这是 AI 大段改动翻车的第一来源,也是我 review 时最先看的部分。具体盯四类。
第一类,任务范围外的文件被改了。 你让它改下单逻辑,结果文件清单里混进了公共工具、配置文件、依赖清单甚至锁文件。这些东西一处变动影响全局,必须逐个问一句:这次为什么要动它?它答不出和需求的直接关系,就退回去。
第二类,和需求无关的函数被顺手调整。 你让它修 A 页面,它路过 B 函数觉得"这里也能优化",就一起改了。这种改动在 diff 里最不起眼,却可能在别的调用方那里埋雷。
第三类,它删了代码。 删比加危险得多。加一段代码,顶多新逻辑有问题;删掉一段,它可能删的是某个很偏的边界处理、某个历史兼容分支------它判断"这看起来没用",恰恰是因为它不了解这段当年为什么存在。凡是删除行,我都要确认它说得出"这东西为什么可以安全删"。
第四类,改了公共函数的签名或默认行为。 一个被多处调用的函数,参数顺序、默认值、返回结构动一点,所有调用方都受影响。这种改动它常常只在当前用到的地方帮你适配好,其它调用点根本没碰。
我现在的固定动作是:打开 diff 先不看代码,先看"本次改了哪些文件"这个清单。 范围外的文件、莫名出现的删除、公共函数的变动,先挑出来,带着疑问再往下看。
| 改动类型 | 危险程度 | 必须问清楚的一句话 |
|---|---|---|
| 任务范围外的文件被改 | 高 | 这个文件跟这次需求有什么关系? |
| 无关函数被顺手调整 | 高 | 为什么要动它?能不能这次先不碰? |
| 出现删除行 | 高 | 删掉的这段当年是兜什么情况的? |
| 公共函数签名/默认值变化 | 很高 | 所有调用方都检查过了吗? |
| 只在你点名的文件里加逻辑 | 偏低 | 正常确认实现即可 |
三、第二高风险区:它"看着等价"、其实偷偷改了行为的地方
第二类坑,是它在你要求的范围内,把一段代码换了个写法,看起来只是换了个姿势,实际行为已经偏了。这一节和我前面讲"不改变行为的重构"是连着的,这里专门说 review 时怎么一眼识破。
高频的有这么几种:
- 条件判断取反或合并。 它把"满足 A 且满足 B"改成另一种写法,逻辑上本该等价,但德摩根那套取反稍不留神就错,尤其是带空值判断的时候。凡是 if 条件被动过,我会手动代一两个边界值进脑子里跑一遍。
- 错误被"温和地吞掉"。 这是它特别爱干的事:原来遇到异常会往外抛、会让调用方知道出错了,它包一层异常处理,悄悄返回一个空值或默认值。表面上程序不报错、不崩了,实际上问题被藏起来,后面更难查。看到新增的兜底返回、空的异常处理,都要停下来问:这个错原本该不该被这样压下去?
- 默认值、空值处理变了。 原来是传空就怎样,它改成了另一个默认;或者把"为空才走这分支"的判断写反。这类差异在 happy path 上完全看不出来,专挑极端数据的时候爆。
- 循环边界和遍历顺序。 多一次少一次、是否包含端点、遍历顺序变了,依赖顺序的逻辑就会出问题。
| 看着无害的写法 | 实际可能藏的坑 |
|---|---|
| 把异常包住、返回空列表或默认值 | 错误被静默吞掉,调用方以为成功了 |
| 重写了 if 的判断条件 | 取反/合并时漏了某个边界,分支走偏 |
| 顺手换了个默认参数 | 没传该参数的旧调用方行为全变了 |
| 把循环改写成更"简洁"的写法 | 端点、顺序、空集合处理被改动 |
| 把几个判断合并成一行 | 短路求值顺序变了,空指针类问题冒出来 |
经验是:凡是 diff 里出现"这块我只是换了个写法、逻辑没动"的改动,恰恰要多看一眼。 真正直白的新增逻辑反而不容易错,因为它没东西可以"等价替换"。
四、别单方面硬啃,让它先把改动"自证"一遍
几百行代码让人单方面啃,既慢又被动。我现在会让 AI 自己先交代,它交代得越清楚,你 review 要补的盲点就越少。下面这几个动作成本很低,效果很直接。
动手前先要计划。 不管是加功能还是改 bug,让它先列出"打算改哪几个文件、每个文件动什么、为什么要动",你确认范围没问题再放行。它计划里要是已经列了范围外的文件,当场就砍掉,别等改完再退。
改完要一份改动说明。 让它按文件列清楚:这个文件动了什么、属于本次需求的哪一部分、有没有顺手改动范围外的东西。这份说明是你对照 diff 的地图------说明里没提、diff 里却出现的改动,就是重点怀疑对象。
让它自己挑风险点。 我经常直接问一句:这次改动里,你认为最可能影响现有功能、最需要我重点看的三处是哪里?它对自己改了什么心里有数,往往真会把最危险的地方交代出来,比你自己从头摸快得多。它要是说"没有风险",我反而更警惕------大改动不可能没有风险。
看不懂的地方,让它用大白话讲。 某段改得绕,别硬猜,让它解释这段为什么这么写、和原来的区别在哪。它解释得含糊、前后矛盾,或者解释出来的意思和你要的不一样,那一段就标红细看。
下面这段是我在它改完之后、正式 review 之前固定发的一段,可以直接抄(具体菜单入口和操作以你当期 Cursor 界面为准):
text
我要 review 你这次的改动,在我看之前,请先按下面几点如实交代:
1. 逐个列出本次改动的文件,每个文件分别动了什么、对应我需求的哪一部分;
2. 明确列出:有没有改动本次需求范围之外的文件、函数或配置?
如果有,逐个说明为什么必须动,我没确认前这些都要撤掉;
3. 列出所有删除的代码,说明每一处删掉的是做什么的、为什么可以安全删;
4. 列出所有改动了公共函数、默认值、异常处理、条件判断的地方,
说明改前改后的行为是否完全等价,不等价的地方单独标出;
5. 你自己判断:这次最可能影响现有功能、最该让我重点看的三处是哪里;
6. 这次改动里,哪些路径是现有测试覆盖到的,哪些没有被覆盖。
先回答这些,不要急着让我接受改动。
这一份交代拿回来,再配合下面两节的工具兜底,大段 diff 基本就被你攥在手里了。
五、用工具兜底:能自动化的检查别只靠眼睛,同时清楚它们挡不住什么
人眼负责抓"意图偏离",机器负责扫"机械性错误",两边配合才稳。该跑的检查一道别省:
- 先把测试完整跑一遍,全绿是底线。 但别满足于绿,要追问一句:它这次新写的逻辑,有没有对应的测试?没有的话,让它为新分支补上测试,尤其补那些异常路径、空值、边界数据------这些正是它最容易夹带私货、又最没被覆盖的地方。
- 静态检查、类型检查、构建都过一遍。 改名漏改引用、类型对不上、import 多余,这类机械错误机器一扫就出来,具体可用的检查项和命令以你项目当期的工具链为准。
- 看 diff 时用编辑器的折叠和忽略空白功能。 纯重命名、整体缩进造成的大片"假改动",折叠掉或者按"忽略空白"看,屏幕上就只剩真正变化的行,注意力不被噪音稀释。
- 对被改动的公共函数,用"查找所有调用方"跳进去扫一遍。 别信它说"我都检查过了",亲自确认每一个调用点和新签名对得上。
| 检查手段 | 能挡住什么 | 挡不住什么 |
|---|---|---|
| 跑测试 | 被覆盖路径上的行为变化、编译错误 | 没写测试的路径、范围外的夹带改动 |
| 让它补新逻辑的测试 | 新分支、异常、边界没被验证 | 测试本身写错、断言方向写反 |
| 静态/类型检查、构建 | 漏改引用、类型不符、语法问题 | 业务意图对不对、错误处理该不该吞 |
| 折叠/忽略空白看 diff | 重命名、缩进造成的视觉噪音 | 隐藏在真实改动里的逻辑变化 |
| 查所有调用方 | 公共函数改动牵连到别处 | 调用方传参语义本身用错 |
一句话:工具负责把"明显不对"的东西清零,"看起来对但其实不是你要的"那部分,还得靠前面那套按风险点扫的人工顺序。
六、一张 review 落地清单,照着勾就不容易漏
把整套动作收成可以直接照做的清单,旁边附上我以前的坏习惯:
- 打开 diff 先看"改了哪些文件",不先读代码
- 圈出范围外的文件、无关函数、删除行、公共函数变动,逐个问"为什么动"
- 它答不出和需求关系的夹带改动,直接退回,不抱着"改都改了"的心态收下
- 重点扫条件取反、异常被吞、默认值/空值、循环边界这几类"看着等价"的改动
- 让它先交改动说明(按文件、含范围外、含删除、含风险点、含测试覆盖)
- 让它自己挑出最该重点看的三处,说"没有风险"就提高警惕
- 测试全绿之外,确认新逻辑有测试、异常和边界路径被覆盖
- 静态检查、类型检查、构建都过一遍
- 折叠掉重命名/缩进的假改动,只看真实变化行
- 公共函数变动,亲自查所有调用方,不只信它的"已检查"
- 确认没问题后,再决定接受;改动太大没把握,让它按目的拆成几块,分批 review
| 以前的坏习惯 | 容易出的问题 |
|---|---|
| 从第一行逐行读到最后 | 注意力平均摊,夹带改动和行为变化全漏 |
| 测试绿了就点全部接受 | 没覆盖到的路径被改坏,绿勾给了假安全感 |
| 看到范围外文件改动也懒得问 | 配置/公共工具被悄悄改动,影响一大片 |
| 看到它删代码默认"没用才删" | 兜边界的历史逻辑被删,极端情况崩 |
| 看到"换了个写法逻辑没动"就跳过 | 条件取反、异常被吞这类等价陷阱最隐蔽 |
| 一个大 diff 一次性接受 | 出问题没法定位是哪一块,只能整体回退 |
| 只靠自己眼睛,不跑检查不看调用方 | 机械错误漏网,公共函数牵连别处没发现 |
这套东西核心就一句话:review AI 的大段改动,别当"读代码",要当"抓偏离"------先看它动了哪些不该动的,再看它在该动的地方有没有偷换行为,然后让它自己交代、让工具兜底,把注意力花在风险最高的那一小撮地方,而不是平均铺在几百行上。 养成这套顺序,你会发现真正需要逐字抠的,往往也就十几行。
写在最后
到这儿,"怎么让 AI 改得对、别出事"这条链算是彻底闭环了:项目规则、多文件任务拆解、补测试安全网、提交闸门、会话上下文、不改变行为的重构,再到今天这篇怎么 review 大段 diff------前面是在它动手前后设卡,这篇是你亲自把关的最后一道。它们拼起来就是一句话:让 AI 干机械的快活,你守意图和边界的慢活。
评论区留个数字,看看你现在 review AI 的大段改动是哪种状态:1、基本不怎么看,测试绿了就接受;2、会逐行看,但几百行看到头大、还总漏;3、知道要挑重点,但还没有固定顺序;4、已经有一套"先看范围、再扫风险、让它自证、工具兜底"的习惯了。 票数最高的那档,我下一篇往细里拆。
后面还排着两个选题:一个是接入渠道的避坑,只讲怎么分辨好坏、不推任何东西;另一个是让 AI 给 AI 做交叉 review(一个写、另一个专门挑刺)的实操。你更想看哪个,评论区说一声。
觉得有用,点个赞、收藏起来,下次面对它甩来的几百行改动前照着走一遍;顺手关注一下,这个系列我一篇篇往下更。