对于程序员而言,Debug 是必不可少的能力,也是编程中的一大乐趣。大多数时候,我觉得 Debug 比写程序本身要好玩得多。但是我发现,很多人遇到了 Bug 就变得特别烦躁,完全不想理睬。很大的一个原因在于他们可能会花很长时间去 Debug,而且还有很大的可能是解不出来,只能求助于更有经验的人。
在这篇文章中,我会分享我最近的一次 Debug 经历,分析我在 debug 过程中用到的解决方法,希望可以给读者一点帮助。
问题现象
在 IOS 客户端上,有一个显示订单列表及一个显示订单数量的文本框。订单可以根据状态进行条件筛选,每次切换不同的筛选条件,会重新向后端发请求获取列表数据。
假设 A 状态下有十条数据,B 状态下有 0 条数据,问题表现是:从 A 状态切换到 B 状态之后,订单列表正常显示为空,而本应变为 0 的订单数量却没有变化,显示的还是 A 状态下的订单数量 10。
出错分析
一拿到这个问题,我的第一念头是,bug 可能出在前端,也可能出在后台,作为消费者,前端可能错误地显示了订单数量这个字段的值;作为提供者,后台可能提供了错误的订单数量。当然极端情况下,问题由双方共同造成。总而言之,范围比较广,比较棘手。
诊断过程
下面是具体的解决过程:
跟着直觉走:从前端到后台依次遍历可能出问题的代码
打开前端代码库,找到显示订单数量的文本框,开始查看它的数据来源以及显示的代码,不断往上查找。
可是我发现,由于我对相关代码的上下文了解太少了,查找起来比较困难;另一方面,也由于我这两天刚好生病了,精神状态不好,很快我就失去了耐性,不想再看。
同时我也觉得这样找下去不是办法,相关的代码很多,如果运气不好,问题不在前端,我还得到后端查找原因,点背的话,可能一天下来都还找不到原因,更别说解决问题。
刹车,觉得不对劲的时候要停下来
从纷繁的代码浏览停下来,起来走了走,又喝点水,待思维从刚刚的代码中冷却下来后,我开始从系统的角度考虑如何查找问题。
系统性思考
系统由两部分组成,我大可使用二分法,先排除掉是前端还是后端的问题。系统由两部分组成,而与错误有关的代码非常多,如果一点点看过去,效率会非常慢。
不容易的方法
首先,我的目的在于找到出错的地方,然后改掉错误;
所以,关键点在于找到出错的地方;
从代码相关的角度出发,所有与 bug 有关的代码都是怀疑目标;
在这个角度上,我必须看过所有的代码才能确定哪里出了问题;
显然,用这个方法,需要耗费比较多的时间精力。我隐约感觉,这个方法,从一开始就错了,因为它太麻烦了,非常不自然。
新的切入点
所以,我开始思考,从什么其他的角度切入,可以更好地定位问题。这个时候,我想到了《如何解题》中说的 当没有思路的时候,回到问题本身
。我再次打开 issue,查看上面是否有遗漏的条件。
- 看了看图片,没有什么收获,跟之前一样,只是显示了错误的计数情况
- 看了看 issue 的描述,也没有什么收获,说的是这个 issue 在什么情况下会出现
- 看了看 issue 的提交时间,是三天之前
三天之前
,欸,那三天之前的计数情况是正确的吗?跟测试人员沟通了一下,他告诉我,这个 bug 是三天之前第一次出现的
。
OK,感觉找到突破点了。
缩小范围
既然 bug 是在三天之前引入的,那么就极有可能是三天之前的某一次提交导致的。如果我能找到这次提交,将它与正常的代码做个对比,就可以极大地缩小错误的发生范围了。
前端 OR 后端
接下来的第一件事,我需要确定,是前端还是后端导致的错误。
为了方便排查,我将三天之前第一个提交的前端代码 (X) 签出运行,保持后端为当前最新的代码,此时 Bug 现象消失,哈,那就可以确定错误是发生在前端了。
持续缩小范围,精确到提交历史的哪些提交出现问题
将前端代码切换到当前代码库的最后一个提交,Bug 出现。很好,确定 bug 就是在三天之前到两天前这个时间范围内引入的。接下来,只要从 X 开始,一个提交一个提交地往前排除,找到第一次出现问题时的提交,查看下它与前一个提交的差异,就可以确定问题根源了。
问题在于,在这段时间之内,代码库的提交数量有 20 个,一个一个地往过看,运气不好的话,得找 20 次,费劲,不予采用。
无敌的二分法
看着提交历史,灵光一现,想到了 二分法
,采用这个方法的话,在 20 个提交中,最多只需要 5 次就可以找到目标了。
哈,脑子兴奋起来之后,又一下子想到了 git
的 bisect
命令,这就是用来在提交历史中作二分查找的呀。
果不其然,没有几下子,第一次出现问题的提交就被我用 git bisect
揪了出来。
修正错误
使用 Intellij IDEA
的文件历史对比功能,发现在这次提交中,修改的主要是分页器的代码。
于是,我找到提交这次代码的同事,给他把现象一描述,他一下子就想到了大概出问题的地方,三两下,改好了代码。
总结
在 debug 的过程中,有时候找不到思路,可能饶很久都找不到问题根源,就像走进了岔路就走不出来一样,时间久了,可能都忘了自己在干嘛了。遇到这种情况,千万不要死磕硬撑,此路不通,就换条路,觉得难走,也换条路。我在这次debug 的过程中,也遇到了这样的情况,一开始就从纷杂的前端代码入手,看了很多都看不出个所以然,自然而然就变得烦躁了。重要的是,遇到障碍后,及时让思维慢下来,不要沉浸在 debug 的状态里出不来。冷静下来之后,试一试从
新的角度去观察问题,并且持续问自己,有没有更简单的方法
。
至于我在这次 debug 中用到的技术和方法,全都是优秀的书籍上学来,类似的书籍有很多,建议多看。
拓展阅读
《突破思维的障碍》
《如何解题》
《编程珠玑》
《软件调试实战》