博狗
国际娱乐城官网
Portraits
Journal
Contact
不幸的是,历史再次重演了 ;) 队友们听说我花一下午重写了一个换名器,非常紧张,咋呼地跟我说:“你知道我们的换名器是花了多少个月的时间做出来的吗?你知道我们写了多少测试来保证它的正确性吗?你现在一下午做出来一个新的,你如何能保证它的正确!” 我不知道他们怎么好意思说出这样的话来,因为事实是,他们花了这么多个月,耗费这么多人力,写了这么多的测试,做出来的换名器却仍然有 bug,没法用。当我把我写的测试和几个大点的 open source 项目(AngularJS, Backbone 等)放进他们的换名器之后,就发现有些地方出问题了,而所有的测试和 open source 项目通过我的换名器,却得到完全正确的代码。另外经过性能测试,我的换名器速度要快四倍的样子。所以就像
Dijkstra
所说:“最优雅的程序往往也是最高效的。”
结束这个项目之后,我换了一个团队(cluster团队),这个团队的人要好很多,低调而且幽默。Shape Security 的产品(Shape Shifter)里面包含一个高可靠(HA)集群管理系统,它可以通过网络,选举 leader,构建一个高容错的并行处理集群。这个集群管理系统一直以来都是公司里很复杂,却是可靠性要求最高的一个部件,一旦出问题就可能有灾难性的后果。确实,它当时可靠性非常高,从来没出过问题。但由于历史原因,它的代码过度复杂而缺乏模块化,以至于很难扩展来应付新的客户需求。我进入这个新团队的任务,就是对它进行大规模的简化,模块化和扩展,让它满足新的需求。
在这个项目中,由于代码的改动幅度很大,在同事和部门领导的理解,信任和支持下,我们决定直接抛弃已有的测试,完全靠严格而及时的 code review,逻辑推理,推敲讨论,手工试验来保证代码的正确。在我修改代码的同时,一位更熟悉已有代码的队友一直通过 git 默默监视着我的每一次改动,根据他自己的经验来判断我的改动是否偏离了原来的语义,及时与我交流和讨论。由于这种灵活而严格的方式,工程不到两个月就完成了。改进后的代码不但更加模块化,更可扩展,适应了新的需求,而且仍然非常可靠。假设部门领导是“测试教条主义者”,不允许抛弃已有的测试,这样的项目是绝对不可能如期完成的。然而在当今世界遇到这样领导的机会,恐怕十个人里面不到一个吧。
Coverity最后,我举一个由于测试方式不当而非常失败的案例,那就是 Coverity 的 Java 静态分析产品。我承认 Coverity 的 C 和 C++ 分析器也许是非常好的,然而 Java 的分析器,很难说。当我进入 Coverity 的时候,同事们已经忍受了整整一年的管理层的威逼和高压,超时过劳工作,写出了基本的新产品和很多的测试。可是由于技术债太多,再多的测试也没能保证产品的可靠性。
我的任务就是利用我深入的 PL 知识,不停的修补前人留下来的各种蹊跷 bug。有些 bug 需要运行20多分钟之后才出现,一次还看不出是怎么回事,所以修起来非常耗时。有时候我只好趴在电脑前面养神,时不时的睁眼看看结果。Coverity 是如此的在乎测试,他们要求每修复一个 bug 你就必须写出新的测试。测试必须能够如实的重现 bug 的现象,修复之后测试必须能够通过。这看似一个很在乎代码质量的做法,然而它不但没能保证产品的稳定可靠,而且大幅度的减慢了工程进度,并且造成员工的疲惫和不满。
有一次他们分配给我一个 bug:在分析一个中型项目的时候,分析器似乎进入了死循环,好几个小时都不能完成。因为 Coverity 的全局静态分析,其实就是某种图遍历算法。当这个图里面有回路的时候,你就必须小心,如果不问青红皂白就递归进去,就可能进入死循环。避免死循环的办法很简单,你构造一个图节点的集合(Set),然后把它传递到函数里面作为参数。 每当访问一个节点,你先检查这个节点是否已经在这个集合里,如果在你就直接返回,否则你就把这个节点加入到集合里,然后递归处理这个节点的子节点。它的 C++ 代码大概就像这个样子:
国际娱乐城官网
Portraits
Journal
Contact