开发日志6: 像治病一样修 Bug
从广义上来讲,修bug和治病救人其实属于同一个工种。前者针对的是代码,而后者针对的是病人。除了对象不一样、具体的操作手法不一样之外,二者遵循的目标其实是一致的,都是把一个部分有问题的整体,尽量恢复到整体功能完备的状态。换句话说就是,针对有毛病的代码或者人,尽量消除这个毛病的影响,使其对整个应用或者人体完全没有妨碍。 不
从广义上来讲,修bug和治病救人其实属于同一个工种。前者针对的是代码,而后者针对的是病人。除了对象不一样、具体的操作手法不一样之外,二者遵循的目标其实是一致的,都是把一个部分有问题的整体,尽量恢复到整体功能完备的状态。换句话说就是,针对有毛病的代码或者人,尽量消除这个毛病的影响,使其对整个应用或者人体完全没有妨碍。
不过这里又引出了修bug和治病救人的另一个区别:如果你的水平足够好,bug总是可以修复到接近完整的状态,完全达成修复的预期;但医术受限于科技发展和时代进步,在越来越多的领域,面对疑难杂症时往往束手无策。
我并非具备医学知识的医学生,也没有过医学从业经历,只是以一个普通人偶尔去医院的所见所闻,来类比这两个相仿的工种,看看二者在处理模式上有什么共通之处,从而借助这个精细化管理的纯科学工种的思路,指导我们写代码时,通过一套系统的思维方式,以最小的成本、最大的收益修复问题。
我们都知道,除非是挂急诊,或者遭遇车祸这类突如其来的情况需要紧急救治,其他的就诊过程都有固定的流程和次序。大体上可以归纳为这样的过程:首先要自查。比方说,你要准确知道自己到底是头疼脑热,还是四肢无力,得有能明确表达出来的不适感,或者能明确表现出来的异常。前者是症状轻微时的自我判断,后者是症状明显或者严重时的外在表现。但不管是哪一种,你在就医之前一定已经做出了异常判断——也就是说,你需要修复这个异常,起源是你觉得自己哪里不舒服,或者哪里不正常。
然后你会通过不同的方式,到医院挂号、选科室、完成分诊。之后大夫会根据你描述的症状,结合你在这家医院的长期就诊病历,以及你能提供的辅助数据,比如现在流行的腕上健康装置记录的日常心率、呼吸、血压等健康数据,来作为佐证。医生会根据你往期的健康数据建立基准,再结合你的描述、表现以及他的经验,对你的病因做出初步诊断。之后他会开一些化验检查单,比如让你去做心电图、验血、验尿等等。这些检查做完之后,你再回到这位医生处,医生会结合你之前的两类数据,加上当下的检查结果,对第一次问诊时的猜测进行验证或者修正。
打个比方,你第一次和大夫问诊的时候,他可能会告诉你,你的症状有点像流感,或者可能是某种病毒感染。等你把化验结果拿回来给他,他会对照结果验证自己之前的判断对不对,哪些地方需要修正,是不是从根本上就需要重新做出判断。之后再看新的判断和第一次拿到的化验单数据是否可以互证,也就是第一次的猜测,在拿到第一次的化验数据之后做了第二次修正,这个修正是否能和第一次的化验数据互相佐证。换句话说就是,化验数据能证明判断是对的,而大夫的第二次修正或者说第二次诊断,也能完美契合化验数据表现出来的、或者说应有的症状。至此,如果二者完美呼应,就诊会进入下一个阶段,大夫针对诊断出来的疾病,给你开一些药,或者说明一些康复方法,让你遵照流程完成整个治疗过程。但如果第二次诊断,也就是修正后的判断还不能得到充分论证,他会继续给你开第二次的化验流程,让你去补充他需要的其他数据,再重复之前的过程,看第二次的诊断经过第二次的化验数据修正之后,是否可以互相论证。如果答案是肯定的,就可以按照之前的流程进行下一阶段的治疗。
当你拿着处方,按照处方开的药方或者治疗手段完成治疗后,也有两种结果:疾病被治愈了,自然万事大吉;另一种是,问题没有像预期的那样得到完全根治,甚至完全没有解决,反而变得更糟了。这时候我们就需要重新评估自己的状态,重新判断疾病情况,再去医院重新走完上述的流程。
这一整个过程和我们修bug简直如出一辙。可能有时候我们觉得,修bug嘛,就是把报错的函数改对,或者把写错的值域改成正确的就行了,其实完全不是这样。一个应用是一个整体、一个系统,里面的某一个函数并不是单独存在的,它有自己的上下游消费者。就像我们人体里的某几个器官,比如胃属于消化系统,不是孤立存在的,它的好坏影响着整个消化系统,而整个消化系统又影响着整个人体的营养循环。所以说,修一个bug不只是简单改一处,不像铺装的路面上有个坑,你把它填平就完事了,完全不是这样。
基于复杂系统的类比,我们修bug也有类似医疗诊断的一套流程。我们先是发现一个现象,比如说执行到某一段代码有告警,或者业务到某一个时间段延迟持续异常,这就像我们自身发病的感受,有点头疼、有点发烧,或者行动迟缓等等。当你发现这个现象之后,会先根据现象,以及你对代码库的了解做出初步诊断。比如日志里报错了,它会列出来代码的哪一行出了问题,有大致的错误描述,你就去找到这一行,做一个简单的定位。当然,说到这里,我忘了一个前提:我这一套修复bug的流程,是和AI共同完成的过程。
所以我们根据应用的运行状态做出初步判断之后,会拿着这个现象以及初步判断去打开AI工具,比如Claude Code。然后你大概会这么跟它说:“我在日志里看到这样那样的报错,大致看了一下,可能是因为这个原因,你帮我浏览一下整个代码库,详读相关的代码和实现,帮我诊断一下这个问题到底是什么原因?”这时候AI就像是你去医院挂号看的一位大夫,它得知道你的问题症状,然后拿着这些症状,依托它的训练数据和能力,在代码库中定位问题的症结所在。当它做出初步诊断之后,不能匆忙或者盲目地开始着手修复问题,而是需要像我们在医院就诊的情况一样,先找依据、找证据来证明这个判断是对的。对于代码来说,就是需要一个复现问题的过程,以此判断是不是我们猜测的原因导致了这个问题。
按照医院的就诊流程,这时候需要患者拿着大夫开的化验要求,去做一系列检查。但现在AI的能力已经可以自主运行所有的操作和验证,所以这一步其实是它自己猜测了一个可能的原因,然后自己去跑一些它“开出来”的化验流程,拿到“化验结果”之后,以此论证它做出的初步诊断是不是能解决我们最初的问题。如果能解决,它就要开出处方,对应到编程领域,就是它需要写一个计划、一个流程,说明应该怎么修,也就是这个“病”应该怎么治,得有清晰的步骤。这个计划就是大夫给患者开的治疗处方,有了处方之后患者照着处方去抓药,或者进行其他物理治疗,对应到编程来讲,就是AI会拿着这个处方、这个计划,去做代码层面的修复。
按照我们治病的经历,拿着处方、拿到药之后,并不是说按顿数把药吃完,或者按流程做完康复操作之后就不管不顾了。我们其实会有一个判断:这些药吃了之后,大夫说7天一个疗程,7天过去了,我的症状到底轻了没有?有没有达到大夫描述的缓解状态?对应到软件开发,或者说bug修改这个环节就是,AI把自己制定的修复计划执行完之后,需要去验证:我把这些代码修改、这些修复应用之后,最初的那个问题是不是如我们预期的那样消失了?或者症状有没有得到缓解?因为有些问题不会立马消失,比如说延迟这种问题,你可以让它从几千毫秒降到几十毫秒,就像人的有些症状只能得到缓解,而不能根除一样。
做完这个判断之后,就出现了分叉:你的预期是百分百完全修复,而这一次诊断开的处方、这个计划执行完之后,修复到了什么程度?是完全修复还是部分修复?如果完全修复,那么到此为止。如果是部分修复,我们需要重新回到诊断、化验、处方这个循环上,再完整走一次流程,直到处方也就是计划的执行效果趋近于我们的预期。
这就是以我们平常去医院就诊为例,类比说明的我们借助AI修改bug时经历的完整循环。但有人可能会问:“我平常做的时候都是直接让AI把bug修了,也没有你这么长的流程,好像它修得也还挺完美。”那么我想请你回忆一下,你有没有过这样的经历:有些小感冒,或者偶感风寒的时候,你也会去医院挂号,让大夫诊断,走完整个会诊流程吗?不会的。你会去药店买点药,自己照着说明书吃完,缓解症状。并不是说一个流程开发出来,就要把所有相关的问题都往里套,而是说一个流程是对应某一复杂程度的问题定义的,比它更复杂的问题,需要把这个流程重复多次,或者做某种嵌入,把它放到更复杂、更完整的流程里,再添加一些更细分的流程;而对于比它更简单的问题,完全没必要生搬硬套这一整套完整的体系,简简单单去药店买对症的药,按时服用即可。
说回到AI,如果你明确知道问题出在哪一部分、哪个点,有非常清晰的修复脉络,那没必要照着这个路径完完整整走一遍。就像你看见靶子就在前面,朝着它放一枪就行,没必要执行复杂的军事计划。但假如你的目标在森林的另一边,那你可能就需要借助无人机、借助导弹来定位、锁定目标,然后摧毁目标。不同的问题体量要借助不同的框架,框架本身是解决问题的思路,但如果选得不对,也会成为解决问题的阻碍。
评论Comments
加载中…Loading…
留下评论Leave a comment