我一直在'修复'自己并不理解的 Bug

过去,每当一个 bug 消失,我都会庆祝一番。某个问题一直折磨着我,我改动了一些东西,问题就消失了,于是我感受到一阵胜利的暖流,然后继续往前走。我花了很长时间、吃了很多苦头才明白:bug 消失和 bug 被修复,是两件完全不同的事——而把这两者混为一谈,正在悄悄地让我的系统堆满我根本没有解决的问题。我只是把它们从自己眼前藏了起来。

这是那种平铺直叙说出来显得理所当然、真正践行起来却难得要命的教训,因为那一刻的一切都在把你往错误的方向推。

症状消失的诱惑

当你改动了某个东西之后问题立刻消失,你的每一根神经都在尖叫:你修好了。那种解脱是真实的、即时的、化学层面的。继续去做下一件事的压力是巨大的。谁愿意继续去戳一个在你看来已经不再是问题的问题呢?

但在那份解脱之下,藏着一个令人不适的真相:症状消失,只能证明症状消失了。它什么都没告诉你——什么都没有——关于你究竟是真的处理了底层的原因,还是仅仅扰动了它,让它暂时安静下来。这两种结果在那一刻看起来一模一样。它们感觉起来一模一样。但它们根本不是一回事。

我无法解释的”修复”

就我的情况而言,这个模式普遍得令人尴尬。我会改动一些东西——调换几个操作的顺序,加一个防护判断,调整附近的某个地方——然后错误就不再出现了。而如果你在那一刻拦住我,让我精确地解释为什么我的改动解决了这个问题,我根本做不到。我其实并不知道。我只知道我碰了点什么,然后那个糟糕的东西就闭嘴了。

我把这叫作修复。但这根本算不上调试。它更接近于在祭坛前献上一份贡品,然后注意到神明此刻似乎被安抚了。我改动了某样东西,宇宙停止了惩罚我,于是我选择把这种相关性解读为理解。那不是理解。那是带着提交信息的迷信。

一个你不理解的 bug 没有死——它只是躲起来了

一个你无法解释的修复,最深层的问题在于:你根本不知道自己实际做了什么。你是消除了这个 bug,还是只是把它挪到了你当下看不见的地方?你确实不知道,而这种不确定性并非无害。因为如果原因还在那里,原封未动,它就会卷土重来。它会在以后重新浮现,披着不同的外衣,出现在系统的某个别的角落,很可能是更糟糕的角落——而当它出现时,你将完全不记得它就是几个月前你以为自己打败过的那个敌人。

这正是我最严重低估的一点。无法解释的修复,并不会真正减少系统中 bug 的数量。它们只是把 bug 搬了个地方,同时剥夺了那本可以让你再次认出它们的知识。你并没有赢得这场战斗。你只是把同一个对手打散成了一个又一个伪装。

你慢慢盖起的鬼屋

时间一长,一个以这种方式维护的系统,就会塞满连作者自己都不真正理解的改动。一个”因为不加它就出问题”而加上的防护判断。一个纯粹出于恐惧而保留下来的顺序。一些小小的仪式,其最初的用意早已被彻底遗忘,但现在所有人都太紧张,不敢去动它。

这份代码库逐渐变成一座由迷信构成的鬼屋——满是你不敢触碰的东西,因为你不确定它们撑着什么。而最残酷的地方,在于这对你改动任何东西的能力所造成的影响。当你分不清哪些是承重的、哪些只是装饰时,你根本无法自信地行动。每一次改动都变成一场针对你不理解的力量的赌博,发生在一个由你亲手搭建、却已不再读得懂的结构之中。

这门纪律:不要相信一个你无法解释的修复

我最终强加给自己的规则,说出来很简单,坚持起来却真的很难:一个问题在我能够同时解释为什么它会发生、以及为什么我的改动处理了那个原因之前,都不算修好。两半都要。如果症状消失了,但我讲不出这个完整的故事,那么我什么都没修好——我只是看不见这个问题了,而那是一种远比一个我仍能看见的问题更危险的状态。

这样更慢。它无时无刻不在诱惑你跳过它,而诱惑最强烈的时刻,恰恰是最糟糕的那一刻:当症状已经消失、一切看起来都很好、从所有可见的迹象判断工作似乎已经完成的时候。那恰恰是这门纪律最重要的时候,也恰恰是它感觉最多余的时候。对一份白送的修复挑三拣四是反直觉的。但这也正是这份工作的全部。

为什么这在一个充满噪声的领域里更加重要

这个陷阱在像这样的领域里之所以格外凶险,是有原因的。这个环境噪声极强,这意味着它会不断地递给你虚假的确认。你做了一个改动,下一次运行看起来更好了——但那究竟是因为这个改动,还是仅仅因为运气?在一个结果会自行剧烈波动的系统里,“我做了 X 之后它变好了”是关于 X 是否起了任何作用的、极其薄弱的证据。

迷信恰恰在结果充满噪声的地方滋生,因为噪声会以刚刚好的频率不断奖励那些随机的仪式,让它们感觉起来像是真的。同样的随机性,既让一切难以评估,也让人危险地容易相信那些什么都没做的修复——因为时不时地,骰子就会在你那毫无意义的动作之后正好掷出对你有利的点数,而你的大脑会急切地记下一个原因。

更深一层的教训:理解才是唯一真正的修复

我最终相信的是:唯一持久的修复,是一个你一路理解到根本原因的修复。其他一切都是在透支未来。那个你没弄懂就糊弄过去的 bug 并不会消失;它只是安静了下来,而这笔贷款会在以后连本带息地到期——在一个你已经彻底忘记自己曾经借过它的时刻。

所以现在,当某样东西开始正常工作而我不知道为什么时,我已经训练自己不再感到解脱。我试着转而感到怀疑。一次无法解释的成功不是胜利;它是一桩恰好披着胜利外衣的、尚未解开的谜题,而把前者当成后者,是我所知最可靠地让你被自己过去的某个版本伏击的方式之一。

— 不荐信号,不保收益,非投资建议。