三问助你Debug

译者按: Debug 也要三省吾身!

原文: Three Questions About Each Bug You Find

译者: Fundebug

本文采用意译,版权归原作者所有

你是否发现:有时候,当某个 BUG 被我们修复之后,却又发现一个由该 BUG 引发的另一个 BUG,或则由于修复算法的缺陷引入新的 BUG?因此,每一次修复 BUG,我都会问自己三个问题来确保我考虑周全。你也可以使用同样的方法来提高代码的质量。

这些精心设计的问题的核心思想是:每一个 BUG 都是某个隐藏的核心问题的表象。你需要解决这些表面症状,但如果只是治标,那么终究会在其它地方复发,没有治本;你需要发现导致这个 BUG 的核心问题,并且纠正它。导致出现 BUG 的核心问题一般不会随机而无法控制,只要你理解了它为什么会出现以及什么原因导致它出现。

在你对自己提出这三个问题之前,你需要克服自己的惰性,仔细地去分析产生 BUG 的原因。通过从出现 BUG 的代码位置开始,一步一步问自己为什么会错,往回倒着查看程序执行步骤,直到你找到出现这个 BUG 的模式。往往和同事一起 Debug 会有助于你证实你的假设。

程序异常是因为下标变量 J 越界了
为什么呢?
数组的长度为 10,下标最大为 9,但是下标 J 已经是 10 了
为什么呢?
J 是整个数组的长度,但是可索引的下标为 9。

在寻找 BUG 原因的过程中,同时检查一下关键变量的值,看看能否解释在此情况下,变量值为何如此。

为什么 name 是 null?
为什么会打印出一条错误信息?

你需要知道程序到底发生了什么,也就是说要将这些信息度都记录下来方便分析。

现在我们来看是看这三个问题。

1. 这个失误在其它地方有犯过么?

看看代码中其它地方有没有使用过类似的编程方法,通过适当的发散思维也有助于寻找类似的 BUG。

  1. 其它地方有没有使用数组的长度作为下标?
  2. 所有的数组都是源自同一个原始数组吗?
  3. 如果数组长度为 0,是否会出问题?

尝试描述这段代码应当遵循的逻辑,有 BUG 的代码会违反该逻辑。

数组起点的初始值加上数组的长度并减去 1 就是最后一个数组元素的下标。如果数组的长度为 0,则不满足。

如果每次修改一个 BUG 的同时修复了几个其它潜在 BUG,将大大提高你的工作效率。尝试将问题用更加抽象的角度去描述将有助于你理解整个程序,以防止引入新的 BUG。

2. 在这个 BUG 后面是否可能隐藏着另一个 BUG?

当你已经弄清楚如何修复这个 BUG,可以预想 BUG 修复后的程序行为。BUG 代码行之后的语句也可能隐藏着 BUG,只是程序以前因为 BUG 崩溃而没有执行到这一步;或则由于修复可能返回其它值,而以前没有考虑。可以试试向自己提如下问题:

接下来的语句可以成功执行吗?

当你在查看程序的控制流的时候,你可以弄清楚有哪些代码还没有执行过。

我是否测试过这些属性的组合

检查各种属性可能性的组合并不会花费太多工作精力,而且往往会发现很多情况开发者都没有考虑到!

我可以测试出所有错误信息吗?

要注意一个地方的改动可能导致其它地方出现 BUG。在局部对一个变量的更改也许会违背之前的一些假设。

如果我只是将 J 减去 1,如果数组的长度为 0,那么下一行代码会尝试操作数组中位于-1 位置的元素。

如果你已经对程序做了很多修改,每一次都要仔细考虑做法是否正确,甚至需要重新设计和实现这部分代码。

3. 我应该如何做来避免类似的 BUG?

你需要尝试寻找方法从根源上解决问题。使用新的方法和工具往往可以直接消除该类型的所有 BUG,而不是一个一个去发现和解决。

要找到 BUG 是什么时候引入的,是否可以在开发阶段避免?

设计没有问题;我在写代码的时候引入了 BUG

仔细检查 BUG 发生的原因,理清 BUG 发生的代码逻辑,然后看看如何纠正。

定义新的不同的类型来区分数组的索引和长度可以在编译时发现这个错误。(索引类型可以限定索引的最大长度)

每一个数组元素输出的时候都输出对应的下标的计算方法,这样我就可以很快找到问题。

假设你对产生某一个 BUG 的理由是“变量太多,我只是忘记了”,那么你需要做的是如何改进来保证你不需要记住很多变量。

关于Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用

您的用户遇到BUG了吗?

体验Demo 免费使用