21/8/23 读书笔记 调试
21/8/23 读书笔记
Code Complete 调试
调试不同于测试。测试偏重于从功能的角度中找出问题,重点回答“什么情况下这个程序会挂掉”;调试偏重于从程序的角度定位问题,重点回答“程序的哪里导致了错误,以及如何修改”。
时刻注意,开发高质量软件的最佳途径永远是用高质量的代码改进代码质量,而不是靠调试做修修补补。最佳的调试能够在不降低整体软件质量的基础上修复业已发现的缺陷,同时也应该尽可能地快。一个优秀的程序员能够在最短的时间内找到并修复最多的bug,并尽可能少地引入新的bug,但是最根本的还是在编写代码的时候就尽可能避免引入缺陷。
调试是一个折磨人的过程,但也有助于我们审视自己:
- 可读性越高,我们发现错误的速度越快。发现错误的过程可以让我们更加客观地审视代码的可读性。
- 如果我们在一开始就很清楚自己在干什么,那么就不会引入错误。错误的出现,证明了我们一定没有透彻地理解我们的程序,此时我们需要重新审视自己的程序,它要做什么?它怎么做的?
- 总结经验,形成记录,以免下次再犯,或者给下次调试提供启发。
- 调试的折磨促使我们思考如何才能更高效率地进行调试,以及如何一开始就写出高质量的程序。
- 促使我们思考自己的编程风格。平时就应该养成良好的编程习惯,让缺陷本身变得与我们良好的编程习惯格格不入。
调试分为寻找缺陷和修复缺陷两个阶段:
- 寻找缺陷:我们通过测试找到了一组使得程序出错的数据,此时我们需要去在程序中找到哪里造成了这种错误。
- 发现错误不应该是一个随机的过程,虽然我们可以随机地去猜测问题的来源,以求靠运气在尽可能短的时间里解决问题。但是这种“快速的肮脏的调试”需要有一定的限度,当我们短时间的随机尝试没有解决问题,那么就应该回到理性分析的轨道上来。
- 科学的调试方法需要我们:
- 稳定我们的错误状态。让错误可复现,同时简化我们用于触发bug的数据。
- 基于我们的数据,对错误的原因提出假设。这种假设应当将所有的数据都考虑进来,如果数据A和数据B都能触发错误,我们就应该尽可能让我们的假设覆盖A和B,而不是仅仅覆盖其中一者。
- 构建新的数据来验证我们的假设。可以证明也可以证伪。
- 尝试多种不同的方法来重现错误而不是局限于测试给出的数据,它们的交集可以帮助我们定位问题的所在。
- 对曾经修改过的部分保持警惕。
- 找不到错误时,抛下问题,暂时放弃思考。
- 修复缺陷:找出缺陷的位置后,我们需要去给出修复的方案。
- 修复一个缺陷前,一定确保自己已经理解了这个缺陷的本质,尽可能保证一次修改就能解决问题。
- 修复缺陷前,确保自己已经理解了这个缺陷所在的程序,至少是附近的代码,保证自己的修改不会对周围的程序引入新的缺陷。不负责任的修改可能能够解决问题,但是更可能会引入新的问题。
- 一定注意备份原始代码。
- 修复不应该是一个匆忙的工作,我们一定要致力于解决问题的本质而不是解决问题的表象。通过解决特例来修复问题,总有一天会让程序质量恶心透顶。
- 修复一个问题后,休息一段时间,再来回头思考自己的修改方案,往往能让我们更加肯定自己的修改方案的正确性。
- 一次只做一个改动。多次改动再进行测试会让我们再次陷入查找缺陷的泥潭。

浙公网安备 33010602011771号