半年来的工作感受

2013-06-17 23:00

半年来的工作感受

by Yin Wang

at 2013-06-17 15:00:00

original http://www.yinwang.org/blog-cn/2013/06/17/work

好久没有写博客,一方面因为工作太忙,另一方面是因为没有发现什么好写的。可是后来发现没什么好写的原因其实也是因为工作太忙了。忙得不正常,所以没有很多时间和精力来研究和欣赏自己喜欢的东西了。

我在一家叫做 Coverity 的公司工作,我住在三藩市(San Francisco)。Coverity 是一个奇怪的公司,三藩市是一个奇怪的城市。

Coverity 制造一种叫做“静态分析”(static analysis)的软件。这种软件可以在不运行程序的情况下,经过对代码的分析,自动的找到程序里面可能出现的问题。这有点像我之前给 Google 做的那个 Python 分析器,只不过针对另外的语言(C,C++ 和 Java 等),分析的侧重点不同,能处理代码的规模也貌似大一些。还有就是这么多年了,久经沙场考验了。

Coverity 具有世界上最先进的一些技术,所以麻雀虽小,却让很多人离不开它。恐怕很少有人知道,这小小的公司有一系列的大客户:美国宇航局, 波音, 洛克希德马丁雷神(Raytheon),BAE Systems丰田欧洲原子能中心(CERN)…… 貌似几乎所有对代码质量不敢有丝毫差错,又不得不用像 C++ 这样毛病众多的语言的公司,都购买了 Coverity 的产品。比如最近的火星好奇者号上的所有200多万行代码,都经过了 Coverity 的静态分析。当然,如此精密的设备不可能光靠 Coverity 查一下错就能确保万无一失,它必须依靠很多其它的技术,但 Coverity 确实是这些东西的开发过程里面比较重要的部分。

我必须承认,Coverity 给了我足够的启发,甚至间接的让我发现了自己之前做的 Python 静态分析里面存在的一些问题。Coverity 的产品在大规模的代码上面的成功,也让我意识到了自己在 Python 分析器里的一些突发奇想的设计的正确性和价值。如果我现在做一个新的 Python 分析器,它将比原来的精确和高效(也可以推广到其它语言比如 JavaScript)。我也清楚的看到,Coverity 自发研制的一些“不大严谨”的做法,其实比程序语言领域里面一些看似高深的“逻辑”还要“正确”。这些微妙的“提示信息”,让我把多个领域的知识串通了起来。所以我觉得跟这公司还有点臭味相投,加入 Coverity 也是不枉此行的。

然而我也发现,Coverity 缺少我所拥有的程序语言理论知识。绝大部分的 Coverity 工程师没有系统的学习过 lambda calculus 和函数式编程,从而无法利用这些东西里面的简单而精髓的思想。所以除了 C 和 C++ 以外,Coverity 在其它语言上面几乎没有什么建树,更没法做出 Python 的静态分析。在我的 Python 分析器中,其实包含了 Coverity 还没有的技术。Python 的静态分析本来就比 C++ 和 Java 之类的难,然而我的实现却异常的简单。这些微妙的技术,貌似很多人都可以说他“会做”,但是他们却很难把它做对。这就像“CPS 转换”一样,很多人都说他会做,可是真正做对的只有极少数人(我是其中之一)。这些技术源自于我对程序语言本质的理解,源自于 Dan Friedman, Kent Dybvig 和 Amr Sabry 等老师的教诲,也源自于我自己辛勤的实验,实验,再实验…… 在我简短而优雅的代码中,包含了许多人需要花费好几倍的代码长度才能达到的目标。所以虽然 Coverity 的工程师们技术实力很强,但在代码的简单程度和对程序语言语义的理解上,真的很难达到我的程度。

这就是为什么我经常能够一眼就看出 Coverity 产品里存在的问题,并且很快的修正错误。举一个简单的例子,有一天我修改了一行代码,使得产品在某些 benchmark 上的内存使用量减少了一半。我为什么可以做到这一点呢?因为在我的 Python 分析器里,这个问题是从一开头就不存在的。它源自于一种幼稚的解释器写法,有点像 GoF 的《Design Patterns》里的那种。Coverity 的代码里面有好些类似的问题,都是我自己根本不可能犯的错误。我不是想贬低同事们的水平,他们都是 Stanford, Berkeley 等学校毕业的高材生,我们相处得也很好。可是我也很清楚,其实 Stanford 和 Berkeley 除了已故的 John McCarthy (我非常尊敬的人)以外,近年来在程序语言上面几乎没有什么作为,他们的教学队伍其实缺少真正世界级的程序语言专家,所以 Stanford 和 Berkeley 的人犯下那样的错误也就不足为奇了。

然后我就经常发现这样的麻烦事:我顺手改掉了一个自认为很显然的问题,或者一个我根本不会犯的错误,然后就发现有大批的测试需要被修改,我也会被要求写出“regression test”,用以防止同样的错误再次发生。某些同事对于测试的战战兢兢的态度,其实跟我当年在 Google 实习的时候没有什么两样。然而这次我假装比较“听话”,照他们说的做。看到这里的问题了吗?这些我“根本”不会犯的错误,几分钟时间顺手就改掉了,但是我却要花成天的工夫去修改和创建测试,防止它“再次”发生。我不得不说,在这些测试上所花费的工夫,占用了比我修改代码多好几倍,甚至几十倍的工夫!

想想这六个月以来我干了些什么,再比较一下在 Google 实习的那六个月独自从头做出来的东西,我发现自己简直什么也没有干。这就是我不喜欢“测试驱动开发”(TDD)的原因。在 Google 的六个月里,我忽略了同事对于测试的要求,从无到有的做出了如此精密的系统,一个测试都没有写照样做得好,为什么呢?因为我看得见事物的本质,我的代码非常的简单清晰。我随时都可以闭上眼,把它们完整的呈现在头脑里面,从而让“心灵之眼”可以看到可能出现的错误。也许这就是所谓的“逻辑思维”。

对测试过分依赖的人,往往不具有这样的思维能力。他们不能够看到代码最简单的本质,所以需要做很多试探,以求达到“近似解”。为了不至于偏差很多,就写很多测试,用以捕捉和防止每一次的错误。这就像一个初学画画的人,一点一点的描,用橡皮反复的擦,可总也抓不住事物的精髓。这些人对“错误”的记忆能力特别强,往往深入的追究一块代码是“如何”错的,“为什么”是错的,下次如何才能不犯同样的错误。

然而我却没法记住之前的代码是如何错的,我也不想知道为什么它是错的,我只记得“正确”的代码是什么样子。错误的方式有千万种,可是正确的却往往只有一个。把脑力浪费在记忆错误的东西为什么错,这就是为什么很多人不能写出优美而正确的代码。曾经有人问米开朗基罗,你是怎么做出“大卫”那样的杰作的。米开朗基罗说,我走到一块大石头前面,然后我就在它里面看到了大卫,剩下的工作就只是把多余的石头敲掉。试问如果米开朗基罗一天到晚都在研究分析丑陋的人如何的丑,为什么丑,他的眼睛还能看得见大卫吗?

我受到的训练让我可以直接看到简单而正确的结果,就像米开朗基罗看见大卫一样,所以测试对于我来说分量没有那么重。当我的代码需要大量的测试才能确保正确的时候,那就是它该被推翻重写的时候。所以我的代码往往没有任何补丁和变通,可以说是无懈可击。当然,优美的代码并不是一蹴而就的,有的代码被我推翻重来几十次才最后成功,但最后的结果肯定不会留下丝毫错误的痕迹。所以我觉得,看一个程序员的水平,不要看他留下来多少行代码,而要看他删掉了多少行。

我觉得做 Coverity 的工程师真累。这种累不止在于技术层面的繁琐,而且在于管理层对工程师的缺乏尊重以及不必要的压力。这让我在受到了足够的“启发”之后,开始怀疑是否还有继续为它工作的价值。对于公司管理,以及对于 IT 行业总体的看法,我还是以后再讲吧。