让问题永不复发 » 代码简洁之道
2013年11月19日 作者:Max Kanat-Alexander
在代码库中解决问题时,症状停止并不代表工作完成。只有当问题彻底消失且永不复发时,你的工作才算真正完成。
当问题不再出现明显症状时就停止解决,这种诱惑很大。你修复了bug,没人抱怨,而且似乎还有其他更紧迫的问题。那么为什么还要继续处理它呢?现在这样不就好了吗?
不。请记住,在软件领域我们最关心的是未来。软件公司陷入代码库难以管理境地的根本原因,就是没有彻底解决问题。
这也解释了为什么有些组织无法将混乱的代码库恢复到良好状态。他们看到代码中的一个问题,处理到没人抱怨就停止,然后转向处理下一个表面症状。他们没有建立确保问题永不复发的框架,没有追踪问题的根源并彻底消除它。因此他们的代码库永远无法真正变得"健康"。
这种未能完全处理问题的模式非常普遍。结果,许多开发者认为大型软件项目不可能保持良好设计——他们说:“所有软件最终都必须被抛弃并重写。”
事实并非如此。我的大部分职业生涯都在从头设计可持续的代码库,或将糟糕的代码库重构为良好的代码库。无论代码库有多糟糕,你都可以解决其问题。但你必须理解软件设计,需要足够的人力资源,并且必须处理问题直到它们永不复发。
一般来说,判断问题解决程度的一个良好准则是:问题解决到不再需要任何人关注它的程度。
绝对意义上实现这一点是不可能的——你无法预测整个未来,等等——但这更多是哲学上的反对而非实际反对。在大多数实际情况下,你可以有效地解决问题,使得现在没人需要关注它,并且也没有明显理由未来需要关注它。
示例
假设你有一个网页,并为网站编写了一个"点击计数器"来跟踪访问人数。你发现点击计数器存在一个bug——它统计的访问量是实际值的1.5倍。你有几种解决方式:
忽略问题 理由可能是你的网站不太受欢迎,所以点击计数器是否准确并不重要。而且,它让你的网站看起来比实际更成功,这可能对你有帮助。
这是一个糟糕的解决方案,因为在许多未来场景中,这可能再次成为问题——特别是当你的网站变得非常成功时。例如,一家主要新闻出版物发布了你的点击数据——但数据是虚假的。这会导致丑闻,用户对你失去信任(毕竟你知道问题却没有解决),你的网站再次变得不受欢迎。很容易想象这个问题可能以其他方式回来困扰你。
快速 Hack 解决方案 显示点击次数时,直接除以1.5,数字就准确了。然而,你没有调查根本原因,结果发现是因为在早上8:00到11:00期间,它统计的点击次数是实际的3倍。后来你的流量模式发生变化,计数器再次完全错误。你可能一段时间内甚至不会注意到,因为hack方案会使调试更加困难。
调查并解决根本原因 你发现它在8:00到11:00期间统计了3倍点击量。你发现这是因为你的Web服务器在那段时间从磁盘删除许多旧文件,这 somehow 干扰了点击计数器。
此时你又有机会采用hack解决方案——你可以简单地禁用删除过程或减少其运行频率。但这并不是真正追踪根本原因。你想知道的是:“为什么机器上发生其他事情会导致计数错误?”
进一步调查后,你发现如果中断程序然后重新启动,它会重新计数最后一次访问。删除过程占用了机器太多资源,导致在8:00到11:00期间每次访问都会中断计数器两次。因此在那段时间内,每次访问都被计数三次。但实际上,根据机器负载情况,这个bug可能添加无限(或至少不可预测)的计数。
你重新设计计数器,使其即使在中断时也能可靠计数,问题就消失了。
显然,从列表中选择的正确方法是调查根本原因并解决它。这使问题消失,大多数开发者会认为工作到此完成。然而,如果你真的想确保问题不再需要人工关注,还有更多工作要做。
首先,有人可能会来修改点击计数器的代码,在未来将其恢复为损坏状态。显然,正确的解决方案是添加自动化测试,确保点击计数器即使在中断时也能正确运行。然后你确保该测试持续运行,并在失败时提醒开发者。现在你完成了吗?
不。即使到这个时候,还有一些未来风险需要处理。
下一个问题是,你编写的测试必须易于维护。如果测试难以维护——开发者在修改代码时测试需要频繁更改,测试代码本身难以理解,代码变更时容易产生误报等等——那么测试很可能在未来被破坏或有人禁用它。然后问题可能再次需要人工关注。所以你必须确保编写了可维护的测试,如果不可维护就重构测试。这可能会引导你进入测试框架或被测试系统的另一条调查路径,以找出使测试代码更简单的重构方法。
此后,你还需要关注持续集成系统(测试运行器)——它可靠吗?它会以某种方式失败导致你的测试需要人工关注吗?这可能是另一条调查路径。
所有这些调查路径可能会发现其他问题,然后必须追踪其根源,这可能发现更多需要追踪的问题,依此类推。你可能会发现,只需从一些症状开始,并非常坚定地追踪根本原因,就能发现(并可能解决)代码库的所有主要问题。
有人真的这样做吗?是的。起初可能看起来很困难,但随着你解决越来越多的根本问题,事情确实开始变得更容易,你可以越来越快地前进,问题越来越少。
深入兔子洞
除此之外,如果你真的想冒险,还可以问一个问题:为什么开发者一开始会编写有bug的代码?为什么bug有可能存在?是开发者的教育问题吗?是他们的流程问题吗?他们应该边写代码边写测试吗?系统中是否存在某些设计问题使其难以修改?编程语言太复杂?他们使用的库写得不好?操作系统运行不正常?文档不清晰?
一旦得到答案,你可以问那个问题的根本原因是什么,并继续问这个问题直到满意。但要小心:这可能会带你进入兔子洞,到达一个改变你对软件开发整体看法的地方。事实上,理论上这个系统是无限的,最终会解决整个软件行业的根本问题。你想走多远取决于你自己。