你绝对不应该做的事之一

作者:周思博(Joel Spolsky)
译:Paul May 梅普华
Thursday, April 06, 2000
属于Joel on Software, http://www.joelonsoftware.com

Netscape 6.0第一个公开的beta版终于出来了。5.0版从来没出现过,最后一版重大改版是大约三年前发行的4.0版。在Internet世界里三年的时间长得_可怕_ 。就在这段时间中,Netscape只能无望地坐看市场占有率直线下降。

我这样批评他们在两个版本间等了很久,似乎有点惹人厌。他们并不是_故意的_ 这样的吧。真有人会故意这样做吗?

呃,没错,他们是故意的。他们做了一个每家软体公司都可能犯的一个最糟的策略错误

Upper_West_Side_Brownstones_2.jpg

他们决定把程式从头重写过。

Netscape并不是第一家犯这种错的公司。Borland买下Arago,想把它变成Windows版的dBase时就犯了同样的错。这个注定失败的专案用了很长的时间开发,长到让它被微软的Access彻底打败。Borland后来又重蹈覆辙整个重写Quattro Pro,还想拿重写的新功能来吓人。微软几乎也做了相同的蠢事,想要在一个叫Pyramid的专案里重写Windows版的Word,不过这个案子已经结束,并且消失在尘土之间。微软很幸运,他们从未停止使用原有的程式码库,所以一直有东西可以推出。因此这个错误只成为财务上的问题,而不是策略上的灾难。

我们都是程式师。而程式师在内心其实是个设计者,他们每到一个地方第一件事,就是先把一切铲平然后创造伟大的事物。我们对修补改进和种种花床的渐进式革新不会感觉兴奋。

程式师总想把旧程式丢掉重新开始,其中的原因很微妙。他们会认为旧的程式是一团乱,不过下面这有趣的观察指出_他们可能是错的_ 。他们会认为旧程式一团乱的直正原因是一个很基本的程式设计原理:

读程式比写程式困难。

这就是程式再使用如此困难的原因。这也说明为何团队中每个人都喜欢用不同的函数把字串切成字串阵列。他们自己写自己的函数,因为这样比去弄懂旧程式更简单而有趣。

Columbus_Ave_Barber_Shop.jpg

依据这个原则推论,你可以询问任何一个现役程式师正在用的程式码。几乎每一个人都会告诉你:「这真是一团乱,我真想把它丢掉重新开始。」

为什么会说是一团乱呢?

「嗯,」他们说:「看看这个函数。有两页那么长耶!这些东西都不属于这里的!我真不知道里头一半的API是叫来做啥的。」

在Borland新的Windows版试算表软体推出前,该公司有意思的创办人Philippe Kahnwas在媒体上到处吹嘘,说些Quattro Pro是整个重写的,所以比微软Excel好上多少倍等等的鬼话。全新的原始码哦!好像原始码会_生锈_ 一样。

新程式码比旧程式码好的想法显然是很荒唐的。旧程式码已经被_用过_ 也被_测试过_ 。_很多_ 问题都已被找出来并被_修好_ 。它并没有什么问题,不会因为在你的硬碟里放久了就生出新的问题。你错得离谱了,宝贝!软体应该要像旧宾士一样,光在放在车库里就会生锈吗?软体应该要像泰迪熊那样,不用 _全新的材料_ 做就不好吗?

回到那个两页长的函数吧。是的,我了解这只是个显示某个视窗的简单函数,不过却多了很多怪东西而且没有人知道为什么。嗯,我会告诉你原因:它们是问题的修正。其中一个是南西在没有Internet Explorer的电脑上安装时写的。另一个是对付记忆体不足时的问题。还有一个是要应付档案放在软磁上,使用者却中途抽掉磁片的状况。LoadLibrary呼叫很难看,不过却能让程式在旧版Windows 95上也可以执行。

里面每个问题都是在真实世界使用好几个星期之后才发现的。而程式师可能也花了好几天才在实验室重现并予以修正。问题看起来很多,可是解决方法可能只有一行程式,甚至可能只有几个字元,可是光两个字元的背后有着许多的时间和心血。

当你把程式码丢掉从头重写时,其实是把这所有的知识都丢掉了。这所有已修正的问题,好几年的写程式的成果。

你会让出市场领导地位。你会把两三年的时间当礼物送给竞争者。相信我,软体的一年可是很_长_ 的。

你会让自己陷入一个极端危险的位置,到时候会有很多年都只能发行旧版程式,完全无法因应市场需求的新功能而改变策略,因为你没有可以发行的程式。这段时期最好还是把公司收起来算了。

你会浪费大量的资金去撰写已有的程式。

Columbus_Ave.jpg

有代替的方案吗?大家都同意Netscape的旧程式似乎真的_非常_ 糟。嗯,或许它真的很糟,不过你知道吗?它还是能在世界上数不清的电脑系统上运作的还不错。

当程式师照他们一贯的作风,说他们的程式是团神圣的垃圾时,其实里头会包含三种错误的理由。

首先是架构上的问题。程式码并没有被正确地安排。网路连线的程式码会在莫名奇妙的地方自己显示对话盒;而这应该是由使用介面来处理才对。你可以小心的搬动程式码,重构并修改介面,一次一个地解决这些问题。你可以找一个程式师小心处理好,再把所有修改一次放回版本管理系统,就不会影响到其他人。即使很重大的架构修改,都不必 丢掉程式码 就可完成。我们花了好几个月重整Juno专案的架构:只是把东西搬来搬去,把程式码清理干净,建立合理的基础类别,并且在模组间建立明确的介面。不过我们非常小心的处理现有的程式码库,结果没有产生新问题也不必把还能用的程式丢掉。

程式师认为程式码一团糟的第二个原因是效率不好。谣传Netscape的成像程式很慢。不过这只牵涉整个专案的一小部份,你可以最佳化甚至重写这一小部份,并不需要把所有程式都重写。在最佳化速度时,抓到1%的工作重点可以得到99%的效果。

第三个理由是说程式码他X的丑。某个我做过的案子真的有一个叫FuckedString的资料类型。另一个案子则是在开始时在成员变数名字前加底线’',后来却改用比较标准的'm'。结果一半的函数是用’‘开头而另一半是用'm‘开头,看起来很丑。坦白地说,这种问题用Emacs的巨集功能五分钟就解决了,不需要从头开始。

你一定要记住,在要从头重新开始时, 完全没有理由 相信这次会做得比第一次好。首先你的程式团队根本不可能和当初相同,所以并不会真的有「更多的经验」。你其实只会把大部份的旧错重新再犯一次,另外再多加一些旧版本没有的新问题。

Lincoln_Center_Trees.jpg

在面对大型商业应用程式时, 做了就丢掉 是很危险的旧咒语。如果你只是写程式做做实验,想到更好的演算法时大可把上星期写的函数丢掉,这是很正常的。想借重构让某个类别更容易使用,这也不会有任何问题,不过把整个程式都丢掉是个危险的愚行。如果Netscape真的运用软体业经验成熟地管理,就不会把自己害得这么惨了。

一些回馈意见 ,包括某位很资深的前Netscape工程师的回应 。另外Seth Gordon写了一封电邮给我,针对阅读他人的原始码提供一些[很好的技巧](http://local.joelonsoftware.com/mediawiki/index.php/%E8%AE%80%E7%A8 %8B%E5%BC%8F%E7%A2%BC%E5%B0%B1%E5%83%8F%E8%AE%80%E7%8C%B6%E5%A4%AA%E6%B3%95 %E5%85%B8)。

这些网页的内容为表达个人意见。
All contents Copyright © 1999-2006 by Joel Spolsky. All Rights Reserved.