怎样在复杂代码中找bug?

避免错误

1、缺少必要的注释

大段的if-else缺少注释,让维护者无法快速分辨分支逻辑。特定地方存在hack或复杂逻辑的代码,缺少注释会让后来者不明所以。为了你好,也为了后来者好,请务必加上代码。说不准以后还是由你来维护这段代码。

2、不变和变化的部分拆分

程序员中流传着一句话,此处不要写死,将来必改。有经验的程序员会将一些业务层的逻辑抽象出来,写成配置文件,好处就是若后续需求有改变,只需改配置文件即可,肯定不会引入bug。

3、忽视测试部分

程序员中又流传着一句话,没有测试的代码等于没写。虽不敢全部赞同,却也有几分道理。从测试用例驱动开发,持续集成,每次编译自动跑测试用例,能够保证系统的稳定同时也减轻测试成本。自己改的的部分做好自测,理解需求,做一个有责任心的工程师。

4、直接操作数据

你应该通过方法去操作数据,而不是直接操作数据,这样能够保证你总能操作数据正确。例如一个类中定义的属性发生变化了,代码中所有涉及到直接操作该属性的代码都需要修改。如果通过方法操作该属性,则仅需修改操作方法,对于外部调用者,类属性变化被屏蔽了,遵循了解耦的原则,代码稳定性大大提高。

5、缺乏文档或文档质量低下

前期文档很重要,不论是框架的API使用手册,还是需求或设计文档,以及各种既定流程的规范,不同种类的模板及核对表,等等这些文档,对于项目来说都是非常重要的资源。而往往有些项目,这类文档就是交由非软件行业的人员来编写,或者前期根本不打算在文档上浪费时间。

6、无尽的需求变更,永远追不上的进度

这是最常见也是最可怕的,因为无论怎样,我们都无法完成它。客户可能认为改个程序,就像改个Excel一样简单省事,甚至会使用可动用的一切权利和资源来推行变更。好吧,我承认这样的客户我遇到过很多。当我向客户解释过变更的代价并提供备选方案后,也就只能等待客户的选择了,这多少有些运数的成分,但也是无奈之举。

7、仅仅靠加班应对进度落后

进度落后并不可怕,可怕的是仅靠加班来追赶进度。这是问题的关键,长时间的赶工仍然无法赶上进度,这只意味着项目有某种更深层次的问题,已经不是单开赶工可以解决的了。留意那些长时间加班的项目,他们往往在管理上存在很大问题,发现这些问题,在你成为PM时,不要犯类似错误。

怎样在复杂代码中找bug?

  1. 放大现象,有些bug现象不太明显,那么就想办法增大它的破坏性,把现象放大。这只是个思路,具体怎么放大只能根据具体的代码来定。比如:美剧《豪斯医生》里有一集,怀疑病人心肺有问题,就让病人去跑步机上跑步,加重心肺负担,从而放大症状。
  2. 二分法定位,把程序逻辑一点点注释掉,看看还会不会出问题,类似二分查找的方法,逐步缩小问题范围。
  3. 模拟现场,有时候我会问自己,如果我要实现bug描述的现象我要怎么写代码才行?比如:我遇到一个死锁问题,但是检查代码发现所有的锁都是配对的,没有忘记解锁的地方,而且锁很简单就是一个普通的临界段,保护几行赋值语句而已。这样的代码怎么写才能让他死锁呢?我想如果让我故意制造这样一个现象,只有在上锁的时候强制杀掉线程了,既然这样就可以去看看有谁强杀线程了没有。
  4. 制作工具,针对某些bug编写一些调试辅助工具。比如,我那个系统没有完善的崩溃报告,虽然也有dump,但是分析出来的callstack经常不准。于是我为解决崩溃问题编写了个工具,会自动扫描代码,在每个函数入口和出口插入log,以此来定位崩溃点。
  5. 掩盖问题,虽然这样做有点不厚道,但是有时不得不这么做。有些bug找不到真正的root cause,但是又要在规定时间内解决,那么我们就可以治疗症状而不去找病因。比如用try catch掩盖一些奇怪的崩溃。不到万不得已不要这么干,未来可能会付出更大代价。

减少 bug 的第一步,是提升自己的程序员素养,努力不给自己和别人找麻烦。

程序员新人怎样在复杂代码中找bug?

另外,团队协作也很重要,前期的技术方案和设计评审、代码审查,对减少一些重大的错误和弱智的 bug 都非常有好处。

与几个有经验的程序员一起评审一个技术方案,常常会发现一些重大的问题,比如为什么用缓存,为什么做持久化,高并发下怎么应对,这部分设计支持线程重入吗,这个循环为什么设置成10分钟,这个超时设置为什么是60秒,传输协议加密了吗,等等。很多方案可能会仅限于解决当前的问题,但有经验的程序员却能透过时间的重重迷雾,发现这个方案在未来某个时间点可能爆发的问题。这就是评审的力量。

欢迎关注我的其它发布渠道