代码之后 书架

第 09 章 / 共 12 章

第九章 · 调试是一场侦探游戏

先说一个反直觉的事实:写代码是调试里最简单、最不值钱的一步。真正难的、真正花时间的、真正决定谁是高手的,是动手改之前那段没人看得见的推理。就像破案,凶手落网只是最后一秒的动作,之前那些天的勘查、走访、推理、排除,才是侦探的本事。

这一章讲的就是这段推理。调试——找出程序为什么没按你预期那样工作、然后修好它——本质上不是「改代码」这个体力活,而是一场侦探游戏。

bug 是一具尸体,你得倒推它怎么死的

侦探到现场,看到的是结果:一具尸体、一扇撬开的窗、地上的脚印。他要倒着推回去:什么样的过程,才会留下这一组痕迹?

调试一模一样。你看到的是现象:页面白屏、数字算错了、每天凌晨三点服务就崩一次。你要倒推的是:这套系统到底经历了什么,才会吐出这个结果?

能不能倒推成功,取决于你脑子里有没有一张图——这套系统到底怎么运作的那张图。我们给它个名字:因果模型,就是你心里对「谁调用谁、数据从哪流到哪、这里改一下那里会怎样」的那套理解。

因果模型不是文档,也不是架构图。它是你脑子里的一台模拟器:你想象「如果用户点了这个按钮」,脑子里就能跑一遍,预测出会发生什么。调试,就是拿这台模拟器跑出来的预测,去对照现实里真实发生的事——哪里对不上,bug 就藏在那个缝里。

这就解释了一件很多人想不通的事:为什么越诡异的 bug 越难,而且专坑高手?因为诡异的 bug,恰恰发生在你因果模型的盲区里。如果你的模型是对的、是全的,这个 bug 根本不会「诡异」——你一眼就想到了。它之所以让你百思不得其解,正是因为你的模型在那个地方错了或缺了一块。你想不到那个原因,是因为在你脑子那张图上,那条路根本不存在。

难的从来不是 bug 本身,是你和真相之间那块你没意识到的认知盲区。

破案的方法:假设,然后想办法证明它是错的

好侦探不猜凶手,他建假设、做验证。调试的核心动作,是一个不停转的闭环:

  1. 观察现象——现在到底哪里不对,越具体越好。「慢」不算,「点提交后转圈 8 秒」才算。
  2. 提假设——基于你的因果模型,猜一个原因:「大概是那个数据库查询没走索引」。
  3. 设计实验——想一个办法,能把这个假设证伪。注意是证伪不是证实。
  4. 看新证据,修正模型——实验结果不管支持还是推翻假设,你脑子那张图都更新了一点,然后回到第一步。

为什么强调「证伪」?因为人有个坏毛病:认定一个原因后,会专挑支持它的线索看,忽略打脸的。侦探最怕先入为主锁定一个嫌疑人,然后所有证据都往他身上凑。破案的纪律是反过来的——你要主动去找那个能洗清嫌疑的证据。一个假设扛过了你拼命想推翻它的所有实验,它才可信。

一个真实感的例子:那个「只在周一早上出错」的 bug

假设你在查一个报表功能。现象很具体:用户反馈,一份「上周销售汇总」的报表,数字每周一早上打开是错的,到了下午自己就对了。代码看起来平平无奇,谁写的都挑不出毛病。

菜鸟的反应是盯着报表那段代码一行行读,读一天也读不出花来——因为bug 不在那段代码里,它在你没看的地方。

侦探怎么查:

你看这个 bug:它不在报表代码里,在一个没人会盯着看的时区假设里。它之所以诡异,正因为写代码的人脑子里那张图默认「服务器时间就是用户时间」——盲区就在这。而破案靠的不是读代码,是那条「周一早上」的线索,加上一个能在现场把时钟拨过去、亲眼看结果的实验。

为什么 AI 能当助手,当不了侦探

AI 在这场游戏里是个好帮手,别小看它:它能飞快读完几千行日志、能列出「报表算错通常有哪几类原因」、能给你解释一段你没见过的陌生代码、能提出常规怀疑对象——「查查时区吧」它没准也说得出。这些相当于一个博学的、不知疲倦的助手,帮你把常规活儿包了。

但它当不了那个拍板的侦探,就卡在三件事上:

落点:AI 帮你写得越多,会查的人越稀缺

这里有个容易被忽略的转折。AI 越能帮你写代码,你手上就有越多不是你亲手写、你并不真懂的代码在跑。写的时候一路顺畅,出问题的那天,你面对的是一片自己脑子里没有对应因果模型的陌生地带。

能自动补全的代码在贬值,这没错。但正因为代码是补全出来的、是你没在脑子里过一遍的,读懂它、给它建因果模型、在它出事时把真凶揪出来的能力,反而更值钱了。调试考的从来不是打字,是判断力和因果推理——面对一个诡异现象,能不能问出那条对的线索、设计出那个能证伪的实验、并且认得出自己模型的盲区在哪。

这正是这本书的主线:凡是能被自动补全的都在贬值,凡是需要判断、需要在现场推理、需要担责的都在升值。调试,是这句话最集中的一次体现。AI 可以是你身边那个博学的助手,但站在现场、盯着那具「尸体」把因果链倒推出来的侦探,还得是你。

代码之后 · holly
面向对世界有好奇心的人 · 费曼式写法 · 由多个 AI 代理撰写与互相审校