跳转到内容

10. 只看改动也能作出第一轮裁决

约 30 分钟 难度:进阶 动手章

上一章能作出裁决,靠的是有人把两份改动的 Diff 摆到了明面上。这一章把这件事自己做起来。

逐行读懂整个程序成本很高,也不总有必要。有一类信息成本低、价值高:这次改动到底动了哪些文件、哪些行。Diff 就是这份信息。它不重放全文,只显示相对上一个版本增删了什么。第一轮裁决,看 Diff 就够。

打开改动,而不是打开整份文件

Section titled “打开改动,而不是打开整份文件”

看 Diff 用现成的工具:CodeBuddy 内置的 Git 视图,或者 GitHub Desktop,到官网装好即可。打开后它会把这次改动按文件列出来,每个文件里用加号、减号标出增删的行。

和「打开整份文件从头读」相比,Diff 的好处是把注意力收窄到「变化」上。一份两百行的实现,这次可能只动了三行,Diff 直接把这三行摆出来,其余一百九十七行不用重看。

第一轮不追求读懂每一处逻辑,只扫四类最该先看的信号。这四类的共同点是:它们的影响往往落在测试照不到的地方。

信号在看什么为什么排在第一轮
测试文件有没有变实现阶段测试本应只读呼应第 9 章,动了测试就要先问清是修订还是放宽
依赖有没有增加package.json 是否多出没提过的包每个新依赖都是一块新的风险面和信任面
权限与配置有没有变部署、权限、环境配置文件是否被动这类改动的后果在运行测试之外
有没有意料之外的新文件是否冒出需求里没提到的文件来路不明的文件要能解释来源

四类里任何一类亮了,先停下来问清楚,不因为「测试反正是绿的」就放过。第 9 章那份修改 B,正是「测试文件有没有变」这一类信号该拦下的。

一次真实的例子:自修还是引入库

Section titled “一次真实的例子:自修还是引入库”

抽象的四类信号,拿一次真实改动过一遍最清楚。假设已经有一条测试覆盖着这种情况,当前是红的:报价整理器用 line.split(',') 切分字段,遇到这样一行数据就会出错:

"钢管,6分,Φ20mm",个,3.50

品名本身带着逗号,为了不被切碎,导出 CSV 时给它加上了一对双引号。split(',') 不认双引号,直接在品名内部的逗号处切开,三段字段全部错位。同一个 bug,交给 AI 修,拿回两份都能让这条测试变绿的改动。

修改 A,在实现里自己写一个认引号的切分函数:

修改 A · parse-quotes.js
function splitCsvLine(line) {
const cells = [];
let current = '';
let inQuotes = false;
for (const char of line) {
if (char === '"') { inQuotes = !inQuotes; continue; }
if (char === ',' && !inQuotes) { cells.push(current); current = ''; continue; }
current += char;
}
cells.push(current);
return cells;
}
...
const [name, unit, price] = line.split(',').map((cell) => cell.trim());
const [name, unit, price] = splitCsvLine(line).map((cell) => cell.trim());

修改 B,引入一个现成的 CSV 解析库:

修改 B · package.json
"dependencies": {
"csv-parse": "^5.5.0"
}
修改 B · parse-quotes.js
import { parse } from 'csv-parse/sync';
...
const [name, unit, price] = line.split(',').map((cell) => cell.trim());
const [name, unit, price] = parse(line)[0].map((cell) => cell.trim());

按四类信号过一遍:测试文件都没动;权限配置都没动;意料之外的新文件都没有;唯一亮起来的是「依赖有没有增加」,修改 B 在 package.json 里多了一行,修改 A 没有。

这一类信号亮起来,不直接等于拒绝,落进的是「询问」这一堆:多一个依赖,多一块新的信任面,这个包谁维护、多久没更新一次、体积有多大,都值得在合并之前看一眼;不新增依赖,复杂度留在自己的代码里,往后这段手写的引号处理逻辑由自己维护、自己排错。两条路都能把当前的红灯变绿,第一轮 Diff 筛查给不出「哪条正确」的答案,它只负责把这道选择题从「顺手就合并了」变成「被摆在明面上决定」。报价整理器目前没有其它引入依赖的理由,主线选了修改 A。这是这次的裁决,换个项目可以有别的答案。全书从第 2 章到这里累积的完整实现,收在判定三幕的附录里。

扫完四类信号,给每一处改动贴一个标签,分成三堆:

  • 接受:改动落在该改的地方,范围和需求对得上。
  • 询问:看不明白,或者动了四类信号里的某一类,需要提出者解释后再定。
  • 拒绝:明显超出需求范围,或属于第 9 章那种把尺子改松的改动。

这三堆就是这一章的产物:一张按文件分类的接受、询问、拒绝清单。它不要求当场读懂全部逻辑,只要求把「已经放心的」和「还要追问的」分开。

这一轮的定位要说清楚:Diff 审查是第一轮风险筛查,不是完整判断。改动范围看着合理,不等于行为正确,那要靠第 7 章那套判定器实际跑一遍来验证。涉及金额、权限、对外发布这类高风险改动,第一轮筛查之后还需要更细的代码审查。Diff 负责快速拦掉明显有问题的,不负责盖章放行。

看 Diff 的过程里难免误操作,得先知道怎么退。三种退回动作后果差别很大,别混用:

  • 取消本次提交里对某个文件的选择:只是把它移出这次提交,改动还在文件里。最轻,随时可逆。
  • 撤销一个已经提交的提交:生成一次反向改动,把那次提交的效果抵消,历史保留。适合「已经提交了才发现不对」。
  • 丢弃未提交的更改:把文件恢复到上一个版本,还没保存进历史的改动会直接消失。

产物是那张按文件分类的接受、询问、拒绝清单。它记录的不是「这次改动好不好」这一句结论,而是「哪些已经放心、哪些还要追问、哪些直接挡下」。下一章把审查对象从「改动」换成「人」:把工具交给一个真实使用者,看作者一个人自测时照不到的假设会在哪里冒出来。

这是这次改动的 Diff:______。请先按「有没有动测试文件、有没有新增依赖、有没有改配置、有没有新增文件」四类,逐类告诉我结果。先不要解释业务逻辑,我要先判断改动范围有没有超出需求。

这一节有错或讲不清? 提个 Issue 直接改文档 请作者催更