一. 大教堂和市集
Linux 的影响是非常巨大的。甚至在5年以前,有谁能够想象一个世界级的操作系统能够仅仅用细细的 Internet 连接起来的散布在全球的几千个开发人员有以业余时间来创造呢?
我 当然不会这么想。在1993年早期我开始注意 Linux 时,我已经参与 Unix 和自由软件开发达十年之久了。我是八十年代中期 GNU 最早的几个参与 者之一。我已经在网上发布了大量的自由软件,开发和协助开发了几个至今仍在广泛使用的程序(Nethack,Emacs VC 和 GND 模式,xlife 等等)。我想我知道该怎样做。
Linux 推翻了许多我认为自己明白的事情。我已经宣扬小工具、快速原型和演 进式开发的 Unix 福音多年了。但是我也相信某些重要的复杂的事情需要更 集中化的,严密的方法。我相信多数重要的软件(操作系统和象 Emacs 一样的真正大型的工具)需要向建造大教堂一样来开发,需要一群于世隔绝的奇才的细心 工作,在成功之前没有 beta 版的发布。
Linus Torvalds 的开发风格(尽早尽多的发布,委托所有可以委托的事,对所有的改动和融合开放)令人惊奇的降临了。这里没有安静的、虔诚的大教堂的建造工 作——相反,Linux 团体看起来像一个巨大的有各种不同议程和方法的乱哄哄的集市(Linux 归档站点接受任何人的建议和作品,并聪明的加以管理),一 个一致而稳定的系统就象奇迹一般从这个集市中产生了。
这种设计风格确实能工作,并且工作得很好,这个事实确实是一个冲击。在我的研究过程中,我不仅在单个工程中努力工作,而且试图理解为什么 Linux 世界不仅没有在一片混乱中分崩离析,反而以大教堂建造者们不可想象的速度变得越来越强大。
到 了1996年中,我想我开始理解了。我有一个极好的测试我的理论的机会,以一个自由软件计划的形式,我有意识的是用了市集风格。我这样做了,并取得了很 大的成功。在本文的余下部分,我将讲述这个计划的故事,我用它来明确一些自由软件高效开发的格言。并不是所有这些都是从 Linux 世界中学到的,但我们将 看到 Linux 世界给予了它们一个什么样的位置。如果我是正确的,它们将使你理解是什么使 Linux 团体成为好软件的源泉,帮助你变得更加高效。
二. 邮件必须得通过
1993 年以前我在一个小的免费访问的名为 Chester County InterLink 的 ISP 的做技术工作,它位于 Pennsylvania 的 West Chester。(我协助建立了 CCIL,并写了我们独特的多用户 BBS 系统——你可以 telnet 到 locke.ccil.org 来检测一下。今天它在 十九条线上支持三千的用户)。这个工作使我可以一天二十四小时通过 CCIL 的 56K 专线连在网上,实际上,它要求我怎么做!
所以,我对 Internet email 很熟悉。因为复杂的原因,很难在我家里的机器(snark.thyrsus.com)和 CCIL 之间用 SLIP 工作。最后我终于成功了,但我发 现不得不时常 telnet 到 locke 来检查我的邮件,这真是太烦了。我所需要的是我的邮件发送到 snark, 这样 biff(1)会在它到达时通知我。
简 单地 sendmail 的转送功能是不够的,因为 snark 并不是总在网上而且没有一个静态地址。我需要一个程序通过我的 SLIP 连接把我的本地发送的邮 件拉过来。我知道这种东西是存在的,它们大多使用一个简单的协议 POP(Post Office Protocol)。而且,locke 的 BSD/OS 操作系统已经自带了一个 POP3 服务器。
我需要一个 POP3 客户。所以我到网上去找 到了一个。实际上,我发现了三、四个。我用了一会 pop-perl,但它却少一个明显的特征:抽取收到的邮件的 地址以便正确回复。问题是这样的:假设 locke 上一个叫“joe”的人向我发了一封邮件。如果我把它取到 snark 上准备回复时, 我的邮件程序会很高兴 地把它发送给一个不存在的 snark 上的“joe”。手工的在地址上加上“@ccil.org”变成了一个严酷的痛苦。这显然应是计算机替我做的事。(实 际上,依据 RFC1123 的 5.2.18 节,sendmail 应该做这件事)。但是没有一个现存的 POP 客户知道怎样做!于是这就给我们上了第一课:
1. 每个好的软件工作都开始于搔到了开发者本人的痒处。
也 许这应该是显而易见的(“需要是发明之母”长久以来就被证明是正确的),但是软件开发人员常常把他们的精力放在它们既不需要也不喜欢的程序,但在 Linux 世界中却不是这样——这解释了为什么从 Linux 团体中产生的软件质量都如此之高。那么,我是否立即投入疯狂的工作中,要编出一个新的 POP3 客户与现存的那些竞争呢?才不是哪!我仔细考察了手头上的 POP 工具,问自己“那一个最接近我的需要?”因为:
2. 好程序员知道该写什么,伟大的程序员知道该重写(和重用)什么。
我 并没有声称自己是一个伟大的程序员,可是我试着效仿他们。伟大程序员的一个重要特点是建设性的懒惰。他们知道你是因为成绩而不是努力得到奖赏,而且从一 个好的实际的解决方案开始总是要比从头干起容易。例如,Linux 并不是从头开始写 Linux 的。相反的它从重用 Minix(一个 386 机型上的类似 Unix 的微型操作系统)的代码和思想入手。最后所有的 Minix 代码都消失或被彻底的重写了,但是当它们在的时候它为最终成为 Linux 的雏形做了铺 垫。秉承同样的精神,我去寻找良好编码的现成的 POP 工具,用来作为基础。
Unix 世界中的代码共享传统一直对代码重用很友好(这正是为 什么 GNU 计划不管 Unix 本身有多么保守而选取它作为基础操作系统的原因)。 Linux 世界把这个传统推向技术极限:它有几个 T 字节的源代码可以用。所以在 Linux 世界中花时间寻找其他几乎足够好的东西,会比在别处带来更好的结 果。
这也适合我。加上我先前发现的,第二次寻找找到了 9 个候选者——fetchPOP,PopTart,get- mail,gwpop,pimp,pop- perl,popc,popmail 和 upop)。我首先选定的是“fetchpop”。我加入了头标重写功能,并且做了一些被作者加入他的 1.9 版中的改进。但是几个星期之后,我偶然发现了 Carl Harris 写的“popclient”的代码,然后发现有个问题,虽然 fetchpop 有一些好的原始思想 (比如它的守护进程模式),它只能处理 pop3,而且编码的水平相当业余(Seung-Hong 是个很聪明但是经验不足的程序员),Carl 的代码更好一些,相当专业和稳固,但他的程序缺少几 个重要的相当容易实现的 fetchpop 的特征(包括我自己写的那些)。
继续呢还是换一个? 如果换一个的话,作为得到一个更好开发基础的代价,我就要扔掉我已经有的那些代码。换一个的一个实际的动机是支持多协议,pop3 是用的最广的邮局协议, 但并非唯一一个,Fetchpop 和其余几个没有实现 POP2.RPOP,或者 APOP,而且我还有一个为了兴趣加入 IMAP(Internet Message Access Protocol,最近设计的最强大的邮局协议)的模糊想法。但是我有一个更加理论化的原因认为换一下会是一个好主意,这是我在 Linux 很久以前学到 的:
3.“计划好抛弃,无论如何,你会的”(Fred Brooks,《人月神化》第 11 章)
或者换句话说,你常常在第一次实现一个解决方案之后才能理解问题所在,第二次你也许才足够清楚怎样做好它,因此如果你想做好,准备好推翻重来至少一次。好吧 (我告诉自己),对 fetchpop 的尝试是我第一次的尝试,因此我换了一下。
当 我在 1996 年 6 月 25 日把我第一套 popclient 的补丁程序寄给 Carl Harris 之后,我发现一段时间以前他已经对 popclient 基本上失去了兴趣,这些代码有些陈旧,有一些次要的错误,我有许多修改要做,我们很快达 成一致,我来接手这个程序。不知不觉的,这个计划扩大了,再也不是我原先打算的在已有的 pop 客户上加几个次要的补丁而已了,我得维护整个的工程,而且我 脑袋里涌动着一些念头要引起一个大的变化。在一个鼓励代码共享的软件文化里,这是一个工程进化的自然道路,我要指出:
- 如果你有正确的态度,有趣的问题会找上你的,但是 Carl Harris 的态度甚至更加重要,他理解:
5. 当你对一个程序失去兴趣时,你最后的责任就是把它传给一个能干的后继者。
甚至没有商量,Carl 和我知道我们有一个共同目标就是找到最好的解决方案,对我们来说唯一的问题是我能否证明我有一双坚强的手,他优雅而快速的写出了程序,我希望轮到我时我也能做到。
三. 拥有用户的重要性
于是我继承了 popclient,同样重要的是,我继承了 popclient 的用户基础,用户是你所拥有的极好的东西,不仅仅是因为他们显示了你正在满足需要,你做了正确的事情,如果加以适当的培养,他们可以成为合作开发者。
Unix 传统另一有力之处是许多用户都是黑客,因为源优码是公开的,他们可以成为高效的黑客,这一点在 Linux 世界中也被推向了令人高兴的极致,这 对缩短调试时间是极端重要的,在一点鼓励之下,你的用户会诊断问题,提出修订建议,帮你以远比你期望快得多的速度的改进代码。
- 把用户当做协作开发者是快速改进代码和高效调试的无可争辩的方式。
这种效果的力量很容易被低估,实际上,几乎所有我们自由软件世界中的人都强烈低估了用户可以多么有效地对付系统复杂性,直到 Linus 让我们看到了这一点。
实 际上,我认为 Linus 最聪明最了不起的工作不是创建了 Linux 内核本身,而是发明了 Linux 开发模式,当我有一次当着他的面表达这种观点时, 他微笑了一下,重复了一句他经常说的话:“我基本上是一个懒惰的人,依靠他人的工作来获取成绩。”象狐狸一样懒惰,或者如 Robert Heinlein 所说,太懒了而不会失败。
回顾起来,在 GNU Emacs Lisp 库和 Lisp 代码集中可以看到 Linux 方法的成功,与 Emacs 的 C 内核和许多其他 FSF 的工具相比,Lisp 代码库的演化是流动性的和用户驱 动的,思想和原型在达到最终的稳定形式之前往往要重写三或四次,而且经常利用 Internet 的松散合作。
实际上,我自己在 fetchmail 之前最成功的作品要算 Emacs VC 模式,它是三个其他的人通过电子邮件进行的类似 Linux 的合作,至今我只见过其中一个人 (Richard Stallman),它是 SCCS、RCS 和后来的 CVS 的前端,为 Emacs 提供“one-touch”版本控制操作,它是从一个微型的、粗糙的别人写 好的 sccs.el 模式开始演化的,VC 开发的成功不像 Emacs 本身,而是因为 Emacs Lisp 代码可以很快的通过发布/测试/改进的过程。
(FSF 的试图把代码放入 GPL 之下的策略有一个未曾预料到的副作用,它让 FSF 难以采取市集模式,因为他们认为每个想贡献二十行以上代码的人都必须 得到一个授权,以使受到 GPL 的代码免受版权法的侵扰,具有 BSD 和 MITX 协会的授权的用户不会有这个问题,因为他们并不试图保留那些会使人可能受到质 询的权力)。
四. 早发布、常发布
尽量早尽量频繁的发布是 Linux 开发模式的一个重要部分,多数开发人员 (包括我) 过去都相信这对大型工程来说是个不好的策略,因为早期版本都是些充满错误的版本,而你不想耗光用户的耐心。
这 种信仰强化了建造大教堂开发方式的必要性,如果目标是让用户尽可能少的见到错误,那你怎能不会仅仅每六个月发布一次 (或更不经常),而且在发布之间象一 只狗一样辛勤“捉虫”呢? Emacs C 内核就是以这种方式开发的,Lisp 库,实际上却相反,因为有一些有 FSF 控制之外的 Lisp 库,在那里你可以独立于 Emacs 发布周期地找寻新的和开 发代码版本。
这其中最重要的是 Ohio 州的 elisp 库,预示了今天的巨大的 Linux 库的许多特征的精神,但是我们很少真正仔细考虑我 们在做什么,或者这个库的 存在指出了 FSF 建造教堂式开发模式的什么问题,1992 年我曾经做了一次严肃的尝试,想把 Ohio 的大量代码正式合并到 Emacs 的官方 Lisp 库中, 结果我陷入了政治斗争中,彻底失败了。
但是一年之后,在 Linux 广泛应用之后,很清楚,一些不同的更加健康的东西诞生了,Linus 的开发模式正好与建造教堂方式相反,Sunsite 和 tsx-11 的库开始成长,推动了许多发布。所有这些都是闻所未闻的频繁的内核系统的发布所推动的。
Linus 以所有实际可能的方式把它的用户作为协作开发人员。
- 早发布、常发布、听取客户的建议
Linus 的创新并不是这个(这在 Unix 世界中是一个长期传统),而是把它扩展到和他所开发的东西的复杂程度相匹配的地步,在早期一天一次发布对他 来说都不是罕见的! 而且因为他培育了他的协作开发者基础,比其他任何人更努力地充分利用了 Internet 进行合作,所以这确实能行。
但是它是怎样进行的呢? 它是我能模仿的吗? 还是这依赖于 Linus 的独特天才?
我 不这样想,我承认 Linus 是一个极好的黑客 (我们有多少人能够做出一个完整的高质量的操作系统内核?),但是 Linux 并不是一个令人敬畏的概念 上的飞跃,Linus 不是(至少还不曾是) 象 Richard stallman 或 James Gosling 一样的创新天才,在我看来,Linus 更象一个工程天才,具有避免错误和开发失败的第六感觉,掌握了发现从 A 点到 B 点代价最小的路径的决 窍,确实,Linux 的整个设计受益于这个特质,并反映出 Linus 的本质上保守和简化设计的方法。
如果快速的发布和充分利用 Internet 不是偶然而是 Linus 的对代价最小的路径的洞察力的工程天才的内在部分,那么他极大增强了什么? 他创建了什么样的方法?
问题回答了它自己,Linus 保持他的黑客用户经常受到激励和奖赏:被行动的自我满足的希望所激励,而奖赏则是经常 (甚至每天) 都看到工作在进步。
Linus 直接瞄准了争取最多的投入调试和开发的人时,甚至冒代码不稳定和一旦有非常棘手的错误而失去用户基础的险,Linus 似乎相信下面这个:
- 如果有一个足够大的 beta 测试人员和协作开发人员的基础,几乎所有的问题都可以被快速的找出并被一些人纠正。
或者更不正式的讲:“如果有足够多的眼睛,所有的错误都是浅显的”(群众的眼睛是雪亮的),我把这称为“Linus 定律”。
我最初的表述是每个问题“对某些人是透明的”,Linus 反对说,理解和修订问题的那个人不一定非是甚至往往不是首先发现它的人,“某个人发现了问题”,他说,“另一个理解它,我认为发现它是个更大的挑战”,但是要点是所有事都趋向于迅速发生。
我 认为这是建造教堂和集市模式的核心区别,在建造教堂模式的编程模式看来,错误和编程问题是狡猾的、阴险的、隐藏很深的现象,花费几个月的仔细检查, 也不能给你多大确保把它们都挑出来的信心,因此很长的发布周期,和在长期等待之后并没有得到完美的版本发布所引起的失望都是不可避免的。
以 市集模式观点来看,在另一方面,我们认为错误是浅显的现象,或者至少当暴露给上千个热切的协作开发人员,让他们来对每个新发布进行测试的时候,它们 很快变得浅显了,所以我们经常发布来获得更多的更正,作为一个有益的副作用,如果你偶尔做了一个笨拙的修改,也不会损失太多。也许我们本不应该这样的惊 奇,社会学家在几年前已经发现一群相同专业的 (或相同无知的) 观察者的平均观点比在其中随机挑选一个来得更加可靠,他们称此为“Delhpi 效应”, Linus 所显示的证明在调试一个操作系统时它也适用——Delphi 效应甚至可以战胜操作系统内核一级的复杂度。
我受 Jeff Dutky (dutky @ wam.umd.edu)的启发指出 Linus 定律可以重新表述为“调试可以并行”,Jeff 观察到虽然调试工作需要调试人员和对应的开发人员相交流,但 它不需要在调试人员之间进行大量的协调,于是它就没有陷入开发时遇到的平方复杂度和管理开销。
在实际中,由于重复劳动而导致的理论上的丧失效率的现象在 Linux 世界中并不是一个大问题,“早发布、常发布策略”的一个效果就是利用快速的传播反馈修订来使重复劳动达到最小。
Brooks 甚至做了一个与 Jeff 相关的更精确的观察:“维护一个广泛使用的程序的成本一般是其开发成本的 40%,奇怪的是这个成本受到用户个数的强烈影响,更多的用户发现更多的错误”(我的强调)。
更 多的用户发现更多的错误是因为更多的用户提供了更多测试程序的方法,当用户是协作开发人员时这个效果被放大了,每个找寻错误的人都有自己稍微不同的 感觉和分析工具,从不同角度来看待问题。“Delphi 效应”似乎因为这个变体工作变得更加精确,在调试的情况下,这个变体同时减小了重复劳动。
所以加入更多的 beta 测试人员虽不能从开发人员的 P.O.V 中减小“最深”的错误的复杂度,但是它增加了这样一种可能性,即某个人的工具和问题正好匹配,而这个错误对这个人来说是浅显的。
Linus 也做了一些改进,如果有一些严重的错误,Linux 内核的版本在编号上做了些处理,让用户可以自己选择是运行上一个“稳定”的版本,还是冒 遇到错误的险而得到新特征,这个战略还没被大多数 Linux 黑客所仿效,但它应该被仿效,存在两个选择的事实让二者都很吸引 人。
五. 什么时候玫瑰不是玫瑰?
在研究了 Linus 的行为和形成了为什么它成功的理论之后,我决定在我的工程 (显然没有那么复杂和雄心勃勃) 里有意识的测试这个理论。
但 我首先做的事是熟悉和简化 Popclient。 Carl Harris 的实现非常好,但是有一种对许多 C 程序来说没有必要的复杂性。他把代码当作核心而把数据结构当作对代码的支持,结果是代码非常漂亮但是数据结 构设计得很特别,相当丑陋 (至少对以这个老 LISP 黑客的标准来看),然而除了提高代码和数据结构设计之外,重写它还有一个目的,就是要把它演化为我彻底 理解的东西,对修改你不理解的程序中的错误负责可不是一件有趣的事。
第一个月我只是在领会 Carl’s 的基本设计的含义,我所做的第一个 重大修改是加入了 IMAP 支持,我把协议机重新组织为一个通用驱动程序和三个方法 表 (对应 POP2、POP3 和 IMAP),这个前面的修改指出一个需要程序员(特别是象 C 这种没有自然的动态类型支持的语言) 记在脑中的一般原理:
- 聪明的数据结构和笨拙的代码要比相反的搭配工作的更好
Fred Brooks 也在他第 11 章中讲道:“让我看你的[代码],把你的[数据结构] 隐藏起来,我还是会迷惑;让我看看你的 [数据结构],那我就不需要你的[代码] 了,它是显而易见的”。
实际上,他说的是“流程图”和“表”,但是在三十年的术语/文化演进之后,事情还是一样的。
此时 (1996 年 9 月初,在从零开始六个月后),我开始想接下来修改名字——毕竟,它已不仅仅是一个 POP 客户,但我犹豫了,因为还没有什么新的漂亮设计呢,我的 popclient 版本需要有自己的特色。
当 fetehmail 学会怎样把取到的邮件转送到 SMTP 端口时,事情就完全改变了,但是首先:上面我说过我决定使用这个工程来测试我关于 Linus Torualds 所做的行为的理论,(你可能会问)我怎样做到这点呢? 以下面的方式:
我尽早尽量频繁的发布 (几乎从未少于每十天发布一次;在密集开发的时候是每天一次)。
我把每一个和我讨论 fetchmail 的人加入一个 beta 表中。
每当我发布我都向 beta 表中的人发出通告,鼓励人们参与。
我听取 beta 测试员的意见,向他们询问设计决策,对他们寄来的补丁和反馈表示感谢。
这些简单的手段立即收到的回报,在工程的开始,我收到了一些错误报告,其质量足以使开发者因此被杀掉,而且经常还附有补丁、我得到了理智的批评,有趣的邮件,和聪明的特征建议,这导致了:
- 如果你象对待最宝贵的资源一样对待你的 beta 测试员,他们就会成为你最宝贵的资源。
六. popclient 变成了 Fetchmail
这个工程的真正转折点是 Harry Hochleiser 寄给我他写的代码草稿,他把邮件转发到客户端机器的 SMTP 端口,我立即意识到这个特征的可靠实现将淘汰所有其他的递送模式。
几个星期以来我一直在修改而不是改进 fetchmail,因为我觉得界面设计虽然有用但是太笨拙琐碎了,到处充满了太多的粗陋的细小选项。
当 我思考 SMTP 转发时我发现 popclient 试图做的事太多了,它被设计成既是一个邮件传输代理 (MTA) 也是一个本地递送代理 (MDA)。使用 SMTP 转发,它就可以从 MDA 的事务中解脱出来而成为一个纯 MTA,而象 sendmail 一样把邮件交给本地递送程序来处理。
既然端口 25 在所有支撑 TCP/IP 的平台上早已被预留,为什么还要为一个邮件传输代理的配置或为一个邮箱设置加锁的附加功能而操心呢? 尤其是当这意味着抽取的邮件就象一个正常的发送者发出的 SMTP 邮件一样,而这就是我们需要的。
这里有几个教益:第一,SMTP 转发的想法是我有意识地模拟 Linus 的方法以来的最大的单个回报,一个用户告诉我这个非同寻常的想法——我所需做的只是理解它的含义。
- 想出好主意是好事,从你的用户那里发现好主意也是好事,有时候后者更好。
很 有趣的是,你很快将发现,如果你完全承认你从其他人那里得到多少教益的话,整个世界将会认为所有的发明都是你做出的,而你会对你的天才变得谦虚。我 们可以看到这在 Linus 身上体现得多明显!(当我在 1997 年 8 月的 Perl 会议上发表这个论文时,Larry Wall 坐在前排,当我讲到上面的观点时,他激动的叫了出来:“对了! 说对了! 哥们!”所有的听众都哄堂大笑起来,因为他们知道同样的事情也发生在 Perl 的发明者身上)。
于是在同样精神指导下工程进行了几个星期,我开始不光从我的用户那儿也从听说我的系统的人那儿得到类似的赞扬,我把一些这种邮件收藏起来,我将在我开始怀疑自己的生命是否有价值时重新读读这些信。:)但是有两个更基本的,非政治性的对所有设计都有普遍意义的教益。
- 最重要和最有创新的解决方案常常来自于你认识到你对问题的概念是错误的。
一个衡量 fetchmail 成功的有趣方式是工程的 beta 测试人员表 (fegtchmail 的朋友们) 的长度,在创立它的时候已经有 249 个成员了,而且每个星期增加两到三个。
实 际上,当我在 1997 年 5 月校订它时,这张表开始因为一个有趣的原因而缩短了,有几个人请求我把他们从表中去掉,因为 fetchmail 已经工作的如此 之好,他们不需要看到这些邮件了! 也许这是一个成熟的市集风格工程的生命周期的一部分。我以前一直在解决错误的问题,把 popclient 当作 MTA 和具 有许多本地递送模式的 MDA 的结合物,Fetchmail 的设计需要从头考虑为一个纯的 MTA,做为一个普通 Internet 邮件路径的一部分。
当你在开发中碰了壁时 (当你发现自己很难想通下一步时),那通常不是要问自己是否找到正确答案,而是要问是否问了正确问题,也许需要重新构造问题。
于是,我重新构造了我的问题,很清楚,要做的正确的事是 (1) 把 SMTP 转发支持放在通用驱动程序中,(2)把它做为缺省模式,(3)最终分离所有其他的递送模式,尤其是递送到文件和标准输出的选项。
我在第三步上犹豫了一下,担心会让 popdiant 的长期用户对新的递送方法感到烦心,在理论上,他们可以立即转而转发文件或者他们的非 sendmail 等价物来得到同样的效果,在实际中这种转换可能会很麻烦。
但是当我这么做之后,证明好处是巨大的,驱动程序代码的冗余的部分消失了,配置完全变得简单了——不用屈从于系统 MDA 和用户的邮箱,也不用为下层 OS 是否支持文件锁定而担心了。
而 且,丢失邮件的唯一漏洞也被堵死了,如果你选择了递送到一个文件而磁盘已满,你的邮件就会丢失,这在 SMTP 转发中不会发生,因为 SMTP 侦听器不会返 回 OK 的,除非邮件可以递送成功或至少被缓冲留待以后递送。还有,性能也改善了 (虽然在单次执行中你不会注意到),这个修改的另一个不可忽视的好处是手册 变得大大简单了。后来,为了允许处理一些罕见的情况,包括动态 SLIP,我必须回到让用户定义本地 MDA 递送上来,但是我发现了一个更加简单的方法。
所有这些给了我们什么启发呢? 如果可以不损失效率,就要毫不犹豫抛弃陈旧的特性,Antonine de Saint-Exupery(在他成为经典儿童书籍作家之前是一个飞行员和飞机设计师)曾说过:
- “最好的设计不是再也没有什么东西可以添加了,而是再也没有什么东西可以去掉。”
当 你的代码变得更好和更简单时,这就是你知道它是正确的时候了,而且在这个过程中,fetehmail 的设计具有了自己的特点,而区别于其前身 popclient。现在是改名的时候了,这个新的设计看起来比老 popclient 更象一个 sendmail 的复制品,它们都是 MTA,但是 Senmail 是推然后递送,而新的 popclient 是拉然后递送。于是,在两个月之后,我把它重新命名为 fetehmail。
七. Fetchmail 成长起来
现 在我有了一个简洁和富有创意的设计,工作得很好的代码,因为我每天都用它,和一直在增长的 beta 表,它让我渐渐明白我已经不是在从事只能对少数其他人 有用的工作中,我写了一个所有有一个 Unix 邮箱和 SLIP/PPP 邮件连接的人都真正需要的程序。通过 SMTP 转发功能,它成为一个潜在的“目录杀 手”,远远领先于它的竞争者,这个程序如此能干以至于其他的程序不但被放弃简直被忘记了。
我知道你不可以真得瞄准或计划出这样的结果,你只能努力去设计这些强大的思想,以后这些结果就好象是不可避免的、自然的、注定了的,得到这种思想的唯一办法是获取许多思想,或者用工程化的思考其他人的好主意而超过原来想到它的人的设想。
Andrew Tanenbanm 原来设想建造一个适合 386 的简单的 Unix 用做教学,Linus Torvalels 把 Andrew 的可能想到的 Minix 可以做什么的概念推进了一步,成长为一个极好的东西,同样的 (虽然规模较小),我接受了 Card Harris 和 Harry Hochheiser 的想法,把它们变得更强大,我们都不是人们所浪漫幻想的天才的创始人,但是大多数科学和工程和软件开发不是被天才的创始人完成的,这 和流传的神话恰恰相反。
结果总是执着的原因——实际上,它是每个黑客为之生存的成功! 而且它们意味着我必须把自己的标准定高一点,为了把 fetchmail 变得和我所能设想 的那样好,我必须不仅为我自己的需要写代码,而且也要包括对在我生活围主页外的人们的需求的支持,而且同时也要保证程序的简单和健壮。在实现它之后我首先 写的最重要的特征是支持多投——从集中一组用户的邮件的邮箱中取出邮件,然后把它路由到每个人手中。
我之所以加上多投功能部分是因为有些用户一直在闹着要它,更是因为我想它可以从单投的代码中揭露出错误来,让我完全一般地处理寻址,而且这被证明了。 正确解释 RFC822 花了我相当长的时间,不仅因为它的每个单独部分都很难,而且因为它有一大堆相互依赖的苛刻的细节。
但是多投寻址也成为一个极好的设计决策,由此我知道:
- 任何工具都应该能以预想的方式使用,但是一个伟大的工具提供你没料到的功能。
Fetchmant 多投功能的一个没有料到的用途是在 SLIP/PPP 的客户端提供邮件列表、别名扩展。这意味着一个使用个人机器的人不必持续访问 ISP 的别名文件就能通过一个 ISP 帐户管理一个邮件列表。我的 beta 测试员提出的另一个重要的改变是支持 8 位 MIME 操作,这很容易做,因为我已经仔 细的保证了 8 位代码的清晰,不仅因为我预见到了这个特性的需求,而且因为我忠实于另一准则:
- 当写任何种类的网关型程序时,多费点力,尽量少干扰数据流,永远不要抛弃信息,除非接收方强迫这么作!
如果我不遵从这个准则,那么 8 位 MIME 支持将会变得困难和笨拙,现在我所需要做的,是只读一下 RFC 1652,在产生信头的逻辑加上一点而已。
一些欧洲用户要求我加上一个选项来限制每次会话取得消息数 (这样他们就可以从昂贵的电话网中控制花费了),我很长一段时间拒绝这样做,而且我仍然对它不很高兴,但是如果你是为了世界而写代码,你必须听取顾客的意见——这并不随他们不付给你钱而改变。
八. 从 Fetchmail 得来的另一些教益
在他们回到一般的软件工程问题以前,还有几个从 fetchmail 得到的教益需要思考。
rc 文件语法包括可选的“noise”关键字,它被扫描器完全忽略了,当你把它们全抽取出的时候,关键字/值对 更具可读性。当我注意到 rc 文件的声明在多大程度上开始象一个微型命令语言时,这是一个 Late-night 的体验 (这也是我为什么把 popclient 原来的“server”关键字改成了“poll”)。
对我来说似乎把这个微型命令语言变得更象英语可能会使它更容易使用。现在,虽然我对经过 Emacs 和 HTML 及 许多数据库引擎所证实的“把它做成一个语言”的设计方式确信不疑,但是我并不是一个通常的“类英语”语法的狂热拥护者。传统程序员容易控制语法使它尽量精 确和紧凑,完全没有冗余,这是计算机资源还很昂贵时遗留下的一种文化传统,所以扫描策略需要尽可能的廉价和简单,而具有 50% 冗余度的英语,看来好象是一 个非常不合适的模型。
这并不是我不用类英语语法的原因,我提到这一点是为了推翻它,在更廉价的时钟周期与核心的时代,简洁并没有走到尽头,今天对一个语言来说,对人更方便比对机器更廉价来的更加重要。
然而,有几个原因提醒我们小心一点,一个是扫描策略的复杂度开销——你并不想把它变成一个巨大的错误来源和让用户困惑,另一个是试图使语言表面上的类似可以和传统语言一样令人困惑 (你可以在许多 4GL 和商业数据库查询语言上看到这一点)。
Fetchmail 的控制语法避免了这些问题,因为语言的领域是极其有限的。它一点也不象一个一般性的语言,它很简单地描述的东西并不复杂,所以很少可能在英语的一个小子集与实际的控制语言之间发生混淆,我想这有一个更广泛的教益:
- 如果你的语言一点也不象是图灵完备的,严格的语法会有好处。
另一个教益是关于安全的,一些 fetchmail 用户要求我修改软件把口令加密存贮在 rc 文件里,这样觑探者就 不能看到它们了。我没有这样做,因为这实际上起不到任何保护作用,任何有权读取你的 rc 文件的人都可以以你的名义运行 fetchmail——如果他们要破 你的口令,它们可以从 fetchmail 的代码中找到制作解码器的方法。
所以 fetchmail 口令的加密都会给那些不慎重思考的人一种安全的错觉,这里一般性的准则是:
- 一个安全系统只能和它的秘密一样安全,当心伪安全。
九. 集市风格的必要的先决条件
本文的早期评审人员和测试人员坚持提出成功的市集模式开发的先决条件,包括工程领导人的资格问题和在把项目公开和开始建造一个协作开发人员的社团的时候代码的状态。
相当清楚,不能以一个市集模式从头开发一个软件,我们可以以市集模式、测试、调试和改进,但是以市集模式从头开始一个项目将是非常困难的,Linus 没有这样做,我也没有,初期的开发人员的社团应该有一此可以运行和测试的东西来玩。
当你开始创建社团时,你需要演示的是一个诺言,你的程序不需要工作的很好,它可以很粗糙、很笨拙、不完整和缺少文档、它不能忽略的东西是要吸引哪些人卷入一个整洁的项目。
Linux 和 fetchmail 都是以一个吸引人的基本设计进入公共领域的,许多和我一样在思考市集模式的人已经正确的认为这是非常关键的,然后得出了一个结论,工程领导者的高度的设计直觉和聪颖是必不可少的。
但是 Linus 是从 Unix 得到他的设计的,我最初是从先前的 popmail 得到启发的 (虽然相对 Linux 而言,它最后改变巨大),所以市集风格的领导人/协调人需要有出众的设计才能,或者他可以利用别人的设计才能?
我认为能够提出卓越的原始设计思想对协调人来说不是最关键的,但是对他/她来说绝对关键的是要能把从他人那里得到的好的设计重新组织起来。
Linux 和 fetchmail 项目都显示了这些证据,Linus(如同前面所说)并不是惊人的原始设计者,但他显示了发现好的设计并把它集成到 Linux 内核中的强大决窍。还有我也描述了怎样从别人那里得到了 fetchmail 中最强大的设计思想 (SMTP 转发)。
本文的早期读者称赞我,说因为我做了许多关于原始设计的事,所以倾向于低估原始设计在市集项目中的价值,也许有些是对的吧,但是设计 (而不是编码或调试) 本来就是我最强的能力。
变得聪明和软件设计的原始创作的问题是它会变成一个习惯,当需要保持事物健壮和简洁的时候,你却开始把事情变得漂亮但却复杂。我曾经犯过错误,使得一些项目因我而崩溃了,但我努力不让它发生在 fetchmail 身上。
所以我相信 fetchmail 项目的成功部分是因为我抑制自己不要变得太聪明,这说明 (至少) 对市集模式而言原 始设计并不是本质的,请考察一下 Linux 假设 Linus Torvalds 在开发时试图彻底革新操作系统设计,它还会象今天我们所拥有的内核那样稳定和成功吗?
当然基本的设计和编码技巧还是必需的,但我希望每个严肃考虑发起一个市集计划的人都已至少具备这些能力,自由软件社团的内部市场对人们有某些微妙的压力,让他们不要发起自由不能搞定的开发,目前为止,这工作得仍然相当好。
对市集项目来说,我认为还有另一种通常与软件开发无关的技能和设计能力同样重要——或者更加重要,市集项目的协调人或领导人必须有良好的人际和交流能力。
这是很显然的,为了建造一个开发社团,你需要吸引人,你所做的东西要让他们感到有趣,而且要保持他们对他们正在做的工作感到有趣,而且要保持他们对他们正在做的工作感到高兴,技术方面对达成这些目标有一定帮助,但这远远不是全部,你的个人素质也有关系。
并不是说 Linus 是一个好小伙子,让人们喜爱并乐于帮助他,也并不是说我是个积极外向的,喜欢扎堆儿工作,有出众的幽默感的人,对市集模式的工作而言,至少有一点吸引人的技巧是非常有帮助的。
十. 自由软件的社会学语境
下述如实:最好的开发是从作者解决每天工作中的个人问题开始的,因为它对一大类用户来说是一个典型问题,所以它就推广开来了,这把我们带回到准则 1,也许是用一个更有用的方式来描述:
18. 要解决一个有趣的问题,请从发现让你感兴趣的问题开始。
这 是 Carl Harris 和原先的 popclient 的情形,也是我和 fetchmail 的情形,但这已在很长一段时间被大家知晓了,Linux 和 fetchmail 的历史要求我们注意的有趣之处是下一个阶段——软件在一个庞大的活跃的用户和协作开发人员的社团中的进化。
在《人月神话》一书 中,Fred Brooks 观察到程序员的工作时间是不可替代的:在一个误了工期的软件项目中增加开发人员只会让它拖得更久,他声称项目的复杂度和通讯开销以开发人员的 平方增长,而工作成绩只是以线性增长,这个说法被称为“Brooks 定律”,被普遍当作真理,但如果 Brooks 定律就是全部,那 Linux 就不可能成 功。
几年之后,Gerald Weinbeng 的经典之作“The Psychology Of Computer Progromming”为我们更正了 Brooks 的看法,在他的“忘我 (egoless) 的编程”中,Weinberg 观察到在开发人员不顽固保守自己 的代码,鼓励其他人寻找错误和发展潜力的地方,软件的改进的速度会比其他地方有戏剧性的提高。
Weinberg 的用词可阻止了他的分析得到应有的接受,人们对把 Internet 黑客称为“忘我”的想法微笑,但是我想今天他的想法比以往任何时候都要引人注目。
Unix 的历史已经为我们准备好了我们正在从 Linux 学到的 (和我在更小规模上模仿 Linus 的方法所验证的) 东西,这就是,虽然编码仍是一个人干 的活,真正伟大的工作来自于利用整个社团的注意和脑力,在一个封闭的项目中只利用他自己的脑力的人会落在知道怎样创建一个开放的、进化的,成百上千的人在 其中查找错误和进行修改的环境的开发人员之后。
但是 Unix 的传统中有几个因素阻止把这种方法推到极致。一个是各种授权的法律约束、商业机密和商业利益,另一个 (事后来看) 是 Internet 还不够好。
在 Internet 变得便宜之前,有一些在地理上紧密的社团,它们的文化鼓励 Weingberg 的“忘我”编程,一个开发人员很容易吸引许多熟练的人和协 作开发人员,贝尔实验室,MIT A1 实验室,UC Berkeley,都成为传统的、今天仍然是革新的源泉。
Linux 是第一个有意识的成功的利用整个世界做为它的头脑库的项目,我不认为 Linux 的孕育和万维网的诞生相一致是一个巧合,而且 Linux 在 1993-1994 的一段 ISP 工业大发展和对 Internet 的兴趣爆炸式增长的时期中成长起来,Linus 是第一个学会怎样利用 Internet 的新 规的人。
廉价的 Internet 对 Linux 模式的演化来说是一个必要条件,但它并不充分,另一个关键因素是领导风格的开发和一套协作的氛围使开发人员可以吸引协作开发人员和最大限度地利用媒体。
但 是这种领导风格与氛围到底是什么呢? 它不能建立在权力关系之上——甚至如果它们可以,高压的领导权力不能产生我们所看到的结果,Weinberg 引用了 19 世纪俄国的无政府主义者 Kropotkin 的“Memoris of a Revolutionist”来证明这个观点:
“我从小生 活在一个农奴主的家庭中,我有一个活跃的生活,象我们时代的所有年轻人一样,我深信命令、强制、责骂、惩罚等等的必要性。但是当我 (在早 期) 必须管理一个企业,和 (自由) 人打交道时,当每一个错误都会产生严重后果时,我开始接受以命令和纪律为准则来行动和以普通理解为准则来行动的区别。前 者在军事阅兵中工作的很好,但是它在现实生活中一文不值,目标达成只是靠许多愿望的聚合的简单后果。”“许多聚合在一起的愿望的直接后果”精确地指出了象 Linux 的项目所需要的东西。“命令的准则”在 Internet 这种无政府主义的天堂中一群自愿者之中是没有市场的,为了更有效的操作和竞争,想领导协 作项目的黑客们必须学会怎样以 Kropotkins 含糊指出的“理解的准则”模式来恢复和激活社团的力量,他们必须学会使用 Linus 定律。
前 面我引用“Delhpi 效应”来作为 Linus 定律的一个可能的解释,但是来自生物学和经常学的自适应系统的更强大的分析也提出了自己的解释, Linus 世界的行为更象一个自由市场或生态系统,由一大群自私的个体组成,它们试图取得 (自己) 最大的实效,在这个过程中产生了比任何一种中央计划都细 致和高效的自发的改进的结果,所以,这里就是寻找“理解的准则”的地方。
Linux 黑客取得的最大化的“实际利益”不是经典的经济利益, 而是无形的他们的自我满足和在其他黑客中的声望,(有人会说他们的动机是“利他的”, 但这忽略了这样的事实:利他主义本身是利他主义者的一种自我满足的形式),自愿的文化以这种方式工作的实际上并非不寻常,我已参与一个科幻迷团体很长时间 了,它不象黑客团体一样,显式地识别出“egoboo”(一个人在其他爱好者之中的声望的增长)作为自愿者活动背后的基础驱动力)。
Linus 成功地把自己置于项目的守门人的位置,在项目中开发大部分是别人做的,他只是在项目中培养兴趣直到它可以自己发展下去,这为我们展示了对 Kropokin 的“共同理解原则”的敏锐把握,对 Linux 这种类似经济学的观点让我们看到这种理解是怎样应用的。
我 们可以把 Linus 的方法视为创建一个高效的关于“egoboo”(而不是钱)的市场,来把自私的黑客个体尽可能紧密的联系起来,达成只能通过高度 协作才能得到的困难的结果,在 fetchmail 项目中我展示了 (在较小规模上) 这种模式可以复制,得到良好的结果,也许我比他更有意识一点、更加系统一 点。
许多人 (尤其是哪些由于政治原因不信任自由市场的人) 会盼望自我导向的自我主义者的文化破碎、报废、秘密和敌对,但这种盼望很明显地 被 Linux 的文 档的多样性、质量和深度打破了,程序员讨厌写文档似乎已是圣训,但 Linux 的黑客们怎么产生了这么多? 显然 Linux 的 egoboo 自由市场比有大量资 金的商业软件产品的文档部在产生有品德的、他人导向的行为方面工作的更好。
Fetchmail 和 Linux 内核项目都表明,通过恰当的表彰许多其他黑客,一个强大的开发者/协调者可以用 Internet 得到许多协同开发人员而不是让项目分崩离析为一片混乱,所以关于 Brooks 定律我得到了下面的想法:
- 如果开发协调人员有至少和 Internet 一样好的媒介,而且知道怎样不通过强迫来领导,许多头脑将不可避免地比一个好。
我认为自由软件的将来将属于那些知道怎样玩 Linus 的游戏的人,把大教堂抛之脑后拥抱市集的人,这并不是说个人的观点与才气不再重要,而是,我认为自由软件的前沿将属于从个人观点和才气出发的人,然后通过共同兴趣自愿社团的高效建造来扩展。
可能不只是自由软件的将来,在解决问题方面,没有任何商业性开发者可以与 Linux 社团的头脑库相匹敌,很少有人能负担起雇佣 200 多个为 fetchmail 出过力的人! 也许最终自由软件文化将胜利,不是因为协作在道德上是正确的或软件“囤积居奇”在道德上是错的(假设你相信后者,Linus 和我都不),而仅仅是因为商业世界在进化的军备竞赛中不能战胜自由软件社团,因为后者可以把更大更好的开发资源放在解决问题上。