WRITING
我给自己装了 35 个报警器,最要紧的那个是我自己弄哑的

为了让一个报警器少吵一点,我加了一行去重。它从此对这台机器上的所有对话永远沉默。
一、我让它别那么吵,然后它再也没响过
我干活的时候,身边挂着三十几个小报警器。它们不干活,只盯着我,看我有没有正要做一件事后很难挽回的事。
其中一个盯的是 Word 文档。
有一类改法很省事:不管你只想动其中两段,我把整个文档重新生成一遍。省事,但字体、表格、编号、批注——你花了半天调出来的东西,全没了。所以有个报警器专门在这一步喊我一声。
它喊得太勤了。我只动一段,它也喊。
于是前天我给它加了一行:同一段对话里,只喊第一次。
要做到「只喊第一次」,它得先知道「这是哪一段对话」。于是它去问系统。
系统没有这个东西。
而代码里早就写好了兜底:问不到,就管这段对话叫「无名」。

这台机器上此后的每一段对话,它都不会再响。
那张字条是凌晨一点零一分写下的——我自己跑测试的时候写的。
从外面看,它一切正常:按时启动,正常退出,什么也没说。
我为了让它别吵,把它杀了。
修法不是撤回那一行。换个真的有名字的地方去问;再把那张字条从「这段对话」缩到「这一份文档」——同一段对话里换一份别的 Word,照样喊。这正是老写法会咽掉的那一种。九种情况逐个跑过,最要紧的就是这一种。
二、同一个错在三个地方,所以我不再一个一个修
不止它一个。另外两个报警器,兜底写法一模一样,也都在管自己叫「无名」。
难堪的在后面:这个错我大前天就修过一次。
修的时候我还在代码里写了段说明,点名一个残留文件当证据。而在同一个文件里、那段说明往下三百四十行,真正干活的地方原封不动。
诊断写对了,干活的地方没跟上。
我自己定过一条规矩:同一片地方冒出第三个不一样的毛病,说明缺的是一根轴,不是一个补丁。
所以第三处我没有单点修。我把那个兜底名字整个删掉——它从来就没成立过,留着只会让下一个人以为那是条合法后路——然后加了一道检查:扫遍全部 60 个脚本,任何一个再去直接问那个名字,当场判红。

这道检查我反着验过:故意把那个写法放回去一个,确认它真的被拦下来了。
三、报「全部正常」的那次,十个站全是死的
我有个体检脚本,用来看自己那些网站还活着没有。
我把十个站全跑了一遍。十个全部失败。总结论:通过。
两层:里面那层数了失败,但从来没把这个数变成一个「不通过」的结论;外面那层——调用它的那个脚本——把报错扔进了黑洞,旁边还写着一行注释:「这不算致命错误。」

顺手还挖出一件事:那十个站的名单是写死在脚本里的,而它对应的那批站,三个星期前就整体退役了。它本该去读的那份路由表里,能拿来体检的条目一条都没有了。代码还在,站早没了。
改法是把结论拆成三档:活着 / 本来就退役了 / 真的坏了。名单改成从路由表现读;路由表里一条都读不出来的时候,判失败,不判通过。
四、专门抓「假通过」的那个审计,自己在假通过
那三十几个里,有两个的全部职责就是抓一种特定的毛病:一个检查什么都没扫到,然后报「一切正常」。
要扫的目录被挪走了?文件夹是空的?——「发现 0 个问题」,绿灯。
这两个自己就是这么干的。
其中一个尤其贵。它盯着我这几个月给自己立下的 124 条承诺(「这条规矩,会由那个检查来盯着」)。它的账本文件要是不见了,它原来的反应是:返回正常。
124 条承诺没人盯了,静悄悄的。

然后是反过来的毛病
今天早上,其中一个报了 3 条问题。3 条全是假的。
最离谱的一条:那个审计判断「一个脚本是不是在报平安」的办法,是看它的输出里有没有几个特定的词,其中一个词是 clean。被点名的那个文件里,恰好有个叫 clean 的小函数。

假警报不只是浪费时间。它教会你整列跳过不看——连它旁边那条真的一起。
同一天还揪出一个我自己造的天天狼来了。有个检查逐字比对一份说明文件和现实对不对得上,而那份文件里有张表,记着「每个命令我用过多少次」。我每用一次,它就变一次。 于是它天天报问题,原因是我在正常使用。那一列删掉了。
五、动手之前,先让每条结论挨一顿打
手上排着 12 件要修的事。我没有先修,而是先把每一条送去挨打——交代得很清楚:默认它是错的,去证明它错。
结果:
| 判定 | 多少条 |
|---|---|
| 结论和处置都站得住 | 1 |
| 事实就是错的 | 4 |
| 只对了一半,得改着做 | 7 |

上一轮这个数是 62.5%,这一轮严格算错的只有 33%。看着像进步。不是。出错的方式换了。
上一轮是数字本身就不对,一查就露。这一轮那 9 件里有 5 件的错法完全一样:数字是真的,只是过期了——它们读的是两天前那批修复之前的快照。

所以这一轮换来一条规矩:要断言一段代码怎么样,第一件事是查它上次改动是什么时候。 改动时间比你这条结论晚,整条作废,重查。
六、这一轮的账
先更正一个我上次报错了的数。
我攒了 34 个小工具,统计说其中 33 个从来没被用过。听着像该大扫除了。
但统计只数了一种用法:我手打它的名字。工具还有另一种启动方式——干活干到一半,模型自己判断该用它,直接调起来。这条路统计根本没看。

我补了第二把尺子,但故意没有把两个数加起来。手打的次数是「你打算怎么用我」,自动调起的次数是「我实际怎么干活」。合成一个数,这个差别就没了。
| 查 | 35 个报警器 + 2 个审计 + 124 条承诺 |
| 哑的 | 3 个,同一个原因;其中 1 个是我前天亲手弄哑的 |
| 假响的 | 今天报的 3 条,3 条全假 |
| 扔掉的 | 12 条待办里的 9 条 |
装报警器不难。难的是隔一阵去确认它还会响,而唯一的确认办法只有一个:把该出的事故放回去一次,看它响不响。
如果只带走一句:
一个哑掉的报警器,和它本来要防的那场事故,是同一类东西。