让Bug永不复发
2013年11月19日 by Max Kanat-Alexander
在代码库中解决问题时,症状停止并不意味着工作完成。只有当问题彻底消失且永不复发时,你的工作才算真正完成。
问题的本质
当问题不再有任何可见症状时,很容易停止解决工作。你已经修复了bug,没有人抱怨,而且似乎还有其他紧迫问题。那么为什么要继续在这上面花时间呢?现在不是挺好的吗?
不,记住我们在软件中最关心的是未来。软件公司陷入代码库无法管理境地的原因,就是没有真正彻底地解决问题。
这也解释了为什么有些组织无法将混乱的代码库恢复到良好状态。他们看到代码中的一个问题,处理到没有人抱怨为止,然后转向处理下一个可见症状。他们没有建立框架来确保问题永不复发,没有追溯问题的根源并使其消失。因此,他们的代码库永远不会真正变得"健康"。
常见误区与解决方案
这种未能完全处理问题的模式非常普遍。结果,许多开发人员认为大型软件项目不可能保持良好设计——他们说:“所有软件最终都必须被扔掉重写。”
这不是真的。我的大部分职业生涯都在设计可持续的代码库,或者将糟糕的代码库重构为良好的代码库。无论代码库有多糟糕,你都可以解决它的问题。但是,你必须理解软件设计,需要足够的人力,并且必须处理问题直到它们永不复发。
一般来说,判断问题解决程度的一个好准则是:问题解决到不再需要任何人关注它的程度。
实际案例:网页点击计数器
假设你有一个网页,为网站写了一个"点击计数器"来跟踪访问人数。你发现计数器有个bug——它计数的访问量是实际访问量的1.5倍。你有几种解决方案:
忽略问题
理由是网站不太受欢迎,计数器是否准确无关紧要。而且,它让网站看起来比实际更成功,这可能对你有帮助。
这种解决方案的坏处是,在未来许多情况下这可能再次成为问题——特别是当你的网站变得非常成功时。
快速修复方案
在显示点击量时,只需将其除以1.5,数字就准确了。但是,你没有调查根本原因,结果发现它在上午8:00到11:00期间计数了3倍的点击量。后来你的流量模式发生变化,计数器又完全错误了。
调查并解决根本原因
你发现它在8:00到11:00期间计数了3倍点击量。你发现这是因为你的Web服务器在那段时间从磁盘删除许多旧文件,这以某种方式干扰了点击计数器。
进一步调查,你发现如果中断程序然后重新启动,它会重新计数最后一次访问。删除进程占用了机器上太多资源,导致在8:00到11:00期间每次访问都会中断计数器两次。所以在那段时间它每次访问会计数三次。
你重新设计计数器,使其在中断时也能可靠计数,问题就消失了。
确保问题永不复发
显然,正确的选择是调查根本原因并解决它。这使问题消失,大多数开发人员会认为工作到此结束。但是,如果你真的想确保问题不再需要人工关注,还有更多工作要做。
首先,有人可能会修改点击计数器的代码,在未来将其恢复到损坏状态。显然,正确的解决方案是添加自动化测试,确保点击计数器在中断时也能正常运行。然后你确保测试持续运行,并在失败时提醒开发人员。
但即使到了这一步,仍然有一些未来风险需要处理。
下一个问题是,你编写的测试必须易于维护。如果测试难以维护——当开发人员更改代码时它经常变化,测试代码本身很晦涩,如果代码更改很容易返回误报等等——那么测试很可能会中断,或者有人会在未来禁用它。
之后你还要关注持续集成系统(测试运行器)——它可靠吗?它是否会以某种方式失败,导致你的测试需要人工关注?这可能是另一条调查路径。
所有这些调查路径可能会发现其他问题,然后必须追溯到它们的源头,这可能会发现更多需要追溯的问题,等等。你可能会发现,只需从几个症状开始,并非常坚定地追溯根本原因,就可以发现(并可能解决)代码库的所有主要问题。
深入探索
除此之外,如果你真的想冒险,还可以问一个问题:为什么开发人员一开始就编写了有bug的代码?为什么bug有可能存在?是开发人员的教育问题吗?是他们的流程问题吗?他们应该边写代码边写测试吗?系统中是否存在某些设计问题使其难以修改?编程语言太复杂?他们使用的库写得不好?操作系统行为不正常?文档不清楚?
一旦你得到答案,你可以问那个问题的根本原因是什么,并继续问这个问题,直到你满意为止。但要小心:这可能会把你带进兔子洞,进入一个改变你对软件开发整体看法的地方。