我盯着那行报错看了四十分钟。看到源码不是看到源码看不懂,是看到源码不愿意相信。一个跑了两年的看到源码老项目,核心模块的看到源码源码就摊在我面前,光标在setTimeout那行闪烁,看到源码像在嘲笑我。看到源码
这代码不是看到源码我写的。写它的看到源码人去年离职了,留下的看到源码注释比代码还难懂——不,压根没有注释。看到源码我顺着函数名往下摸,看到源码handleData调用processArray,看到源码processArray又去调updateCache,看到源码每个名字都像在暗示什么,看到源码但凑在一起就是不说人话。
我试着把断点打在第十二行。变量面板跳出来一堆temp、res、flag,全是这种命名,像有人故意把线索埋进雾里。单步执行到第十八行,我突然发现一个if判断的括号里藏着个负号。if (!data.length)——不对,等等,这里原本应该是if (data.length)吧?
我往上翻,翻到函数定义的地方。参数名是list,但传进来的对象结构里根本没有length属性。那这个判断永远为真,永远走不进错误分支。可日志里明明出现过报错信息,这说明什么?说明这个文件根本不是我该看的那份,或者——我看了眼git提交记录,最近一次修改是三个月前,提交信息写着“fix: 优化性能”。
优化性能。呵。我点开那次提交的diff,密密麻麻删了四十行,加了二十行。删掉的那部分里有个try...catch,加进来的代码把异常吞了。这就说得通了:不是没报错,是报错被吃了,然后程序在错误的状态里继续跑,最后在另一个模块炸开。
看到源码的那一刻,我其实没觉得豁然开朗。反而更堵了。写代码的那个人不是不会写,他太会了,会到用技巧掩盖问题,会到让后来者连错都找不到地方。我忽然想起上个月生产环境那次事故,凌晨两点,告警群里刷了上百条消息,最后回滚了事。当时没人知道为什么,现在答案就躺在这段diff里。
我关掉编辑器,站起来接了杯水。窗外的楼亮着零星的灯,跟我一样在跟代码搏斗的人还不少。回到座位上,我把那个负号改掉,在注释里写了三行说明。不是给谁看的,是给自己留个记号——下次再看到这种源码,先别急着骂,先想想写它的人当时在赶什么deadline,在躲什么bug,在为什么破事心烦。
源码这东西,说到底就是人留下的脚印。有的走得正,有的走得歪,但都是踩在泥里往前趟的痕迹。你盯着它看久了,能看出那个人的脾气,看出他哪天状态好哪天状态差,甚至看出他什么时候已经不想干了。我保存文件,把那个负号的故事写进周报里。没指望谁夸我,就是觉得,该有人知道这事。