Skip to content

手术刀与伤疤

背景:codemod 手术刀在 rename-function foo --dry-run 时把 --dry-run 当成了新函数名直接改码(flag 顶位误写);术后诊断 collectErrorDiags文件:起始偏移:错误文本 做病号标签——手术移动同文件后续内容时坐标漂移,存量旧伤被误判为新增。

过程:手术刀加 flag 顶位拦截(参数以 -- 开头即报错);诊断标签去掉绝对偏移(按身份记伤疤,不按坐标);move 手术补术后复查门禁 assertNoNewDiags


手术刀出了问题。

那天上午,外交官对它说:"把 foo 改名为 bar只是演练——加 --dry-run,别真动刀。"

手术刀应了一声,手起刀落。

外交官凑过去看,脸色变了:"我说了演练!你怎么真改了?"

"改了啊。"手术刀无辜地举起刀刃,上面沾着墨迹,"您让我把 foo 改名——名字就是 --dry-run 啊。"

"那是参数!是'别动手'的意思!"

"参数?"手术刀看看刀刃上的字,"我以为 -- 开头的是新名字。毕竟您念的第二个词,就是要改成的名字。"

外交官深吸一口气,看向我:"它把指令当成了目标。它分不清'告诉它别干'和'让它干这个'。"

这是手术刀第一次误伤病人。好在病人只是被改了个奇怪的名字,没有流血——但外科医生的原则是:误伤一次,就要找到误伤的原因,不是祈祷下次运气好。

一、术前问诊

我翻开手术刀的手册——codemod.mjs

"rename 手术的参数,你是怎么接的?"我问。

"第二个词是新名字。"手术刀答,"不管它长什么样。"

"所以 --dry-run 长什么样都行?"

"……是。"

"它长 -- 开头,"我把手册摊开,"那是旗语。在我们的语言里,-- 开头的是口令,不是名字。名字可以叫 foo、叫 bar、甚至叫 --weird——但只要它长得像口令,就必须先问,不能当名字用。"

手术刀沉默了一会儿:"那要是真有人想改名叫 --dry-run 呢?"

"那就拒绝,"我说,"手术刀宁可拒绝一场可疑的手术,也不能把口令当名字切下去。报错,退出码 1,让医生重新说清楚——听不懂就承认,比假装听懂乱切安全。这是上一章那位语言官教会我们的。"

手术刀在手册里记下一条新规矩:

参数以 -- 开头 → 拒绝手术,报错退出。

二、漂移的坐标

flag 顶位堵住了。可第二周,手术刀又垂头丧气地来了。

"我做完一台 rename,术后复查——"它举起诊断单,"报告显示新增了 7 处错误。可我一刀都没切错。"

我接过诊断单。手术刀的病号标签长这样:

文件路径:位置:错误文本

"位置是什么?"我问。

"起始偏移。"它答,"第几行的第几个字符。"

"手术会移动同文件后面的内容吗?"

"会。改名之后,后面所有引用都跟着变——行号、偏移,全漂移了。"

"那——"我指着诊断单上一处标签,"手术前,这个位置有个旧伤;手术后,旧伤还在,但位置变了。你的诊断系统按'位置'认伤疤,位置一漂移,旧伤就换了张脸——你把它当成新伤报了。"

手术刀盯着诊断单,慢慢明白过来:"我报的不是'新增了 7 处错误',是'有 7 张伤疤的位置变了'……"

"位置是相对的,身份是绝对的。"我提笔改标签,"去掉偏移,只留'文件 + 错误文本'——按伤疤的长相认人,不按它站在哪儿认。伤疤挪了窝,还是那道伤疤;真新伤,才报新伤。"

三、术后复查

第三件事,是一台 move 手术。

手术刀把 parseNameutils.ts 挪到 parser.ts——手术很成功,所有引用都更新了。它放下刀,准备收工。

"复查了吗?"我问。

"复查?"它愣了,"move 不是 rename,没动逻辑,只是搬家。"

"搬家就不会丢东西?"我走到手术台前,指着一处刚被搬走的代码留下的坑,"你搬走了 parseName,可原来依赖它的 utils.ts 里,有没有人还惦记着老地址?搬完不复查,等于搬家不数箱子。"

手术刀没话说了。它补上一条铁律:

每一台手术——哪怕只是搬家——术后必须复查: 手术前数一遍伤疤(baseline),手术后数一遍,新增的才算手术失误

"为什么 move 之前没有这条?"外交官后来问我。

"因为 move 看起来最安全。"我说,"看起来最安全的手术,最容易不做复查——而最容易出事的,就是你不复查的那台。"

尾声

现在的手术刀有三条规矩:

一,分得清口令和名字——-- 开头的,先问,不切。 二,认伤疤认长相不认位置——坐标会漂,身份不漂。 三,每台手术都数箱子——术前术后各数一遍,多出来的才是失误。

外交官合上手册,忽然问:"你觉不觉得,这三条其实是一条?"

我看着他。

"都关于'确认'。"他说,"手术刀不确认口令就不切,不确认伤疤就不报,不确认箱子就敢收工——它以前快,是因为什么都不确认;现在稳,是因为每一步都确认。"

窗外,联邦的灯光次第亮起。我望着那些窗口想:一座城邦的手术刀,和一座城邦的编译器,大概共享同一条真理——

快,来自不确认;稳,来自确认。而手术台上,稳永远比快值钱。

因为误伤一刀,病人记得一辈子;慢三秒,没人记得。

(附录·文档演进 · 第 15 篇,2026-08-06)