分裂的代价
舍弃旧代码是程序员经常面对的一种诱惑。不必说维护旧系统的人经常要面对,就是开发新功能新产品的人也时常如此 —— 因为你经常被建议要借鉴一个老产品的 code base ,而你本能地想拒绝而重新打造一个干净的系统。幸运的是,很早以前我已经开始相信舍弃这些东西是不对的,不应该重写任何不必重写的东西。当几次看到那些险些被舍弃的旧东西节省了大量工作之后,我更是坚信对那种看起来又老又旧的东西,应该留出一个星期的时间来仔细研究它是否有用。这一个星期可能会节省一个月的工作。
这个原则后来读到的很多文章里得到印证。但是我记得开始编程的时候完全不是这么想的。最近一个星期我仔细回顾了一下这个转变过程。
2005 年开始看 Linux kernel 的代码。我当时有一种稍稍自卑的感觉,感慨 Linux kernel 的代码为什么能把一切情况都考虑周全。比如一个 40 行左右的函数,让我来写最多只能写出十几行,而差的那 30 行处理的是很重要又不容易考虑到的情况。
当时看代码是用《 Understand the Linux Kernel, 3rd Edition 》做主线。书上讲的是 2.6.x 版本( x 是一个远小于 11 的数字)。而我参照的源代码是从网上下载的最新稳定版。从 2.6.1x 一直到 2.6.2x 更新了很多次。其间越来越多的看到书上的代码和最新代码的差异。跟随这些差异我开始阅读不同版本的 release notes ,以及针对特定修改的 mail list 讨论和 patch 注释。最后我意识到,书上的版本和最新版本之间的差异,往往就类似于上面提到的我能写出的十几行和实际的 40 多行的差异。为什么能把一切情况都考虑到?答案就是持之不懈地 Fix Bug 。
回想起来,尽管不够清晰,但这应该就是『绝不重写无必要重写的代码』这个想法产生的过程。没有人能把事情一次做对。旧的代码有很多错误,但是抛弃它们只是让已经犯过的错误有机会被重犯一次,加上新的错误。
另一方面,那些最后形成的代码,如果不比对《 Understanding the Linux Kernel, 3rd Edition 》上的旧版代码,似乎像是一次写就的。这是我最初的自卑的来源,也是很多初学者对编写代码的错觉。人们其实并不像最后结果体现的那样能一次就把事情做对,但是我们对错误的修正也没有必要直接体现错误本身。在循序渐进的过程中要把结果造就成一次就做对的面貌。如果你真的想回忆每个与之战斗过的错误,version control 才是怀念历史的地方。
已经有三年多没有碰过 Linux kernel 代码了。回顾起来,阅读它的代码让很多正确的想法第一次被植入到我头脑中。对于程序员来说,更多正确的有用的东西还是来自代码。