【 tulaoshi.com - 编程语言 】
一. 大教堂和市集
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客户上加几个次要的补丁而已了,我得维护整个的工程,而且我脑袋里涌动着一些念头要引起一个大的变化。
在一个鼓励代码共享的软件文化里,这是一个工程进化的自然道路,我要指出:
4. 假如你有正确的态度,有趣的问题会找上你的,但是Carl Harris的态度甚至更加重要,他理解:
5.当你对一个程序失去爱好时,你最后的责任就是把它传给一个能干的后继者。
甚至没有商量,Carl和我知道我们有一个共同目标就是找到最好的解决方案,对我们来说唯一的问题是我能否证实我有一双坚强的手,他优雅而快速的写出了程序,我希望轮到我时我也能做到。
三. 拥有用户的重要性
于是我继续了popclient,同样重要的是,我继续了popclient的用户基础,用户是你所拥有的极好的东西,不仅仅是因为他们显示了你正在满足需要,你做了正确的事情,假如加以适当的培养,他们可以成为合作开发者。
Unix传统另一有力之处是许多用户都是黑客,因为源优码是公开的,他们可以成为高效的黑客,这一点在Linux世界中也被推向了令人兴奋的极致,这对缩短调试时间是极端重要的,在一点鼓励之下,你的用户会诊断问题,提出修订建议,帮你以远比你期望快得多的速度的改进代码。
6. 把用户当做协作开发者是快速改进代码和高效调试的无可争辩的方式。
这种效果的力量很轻易被低估,实际上,几乎所有我们自由软件世界中的人都强烈低估了用户可以多么有效地对付系统复杂性,直到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以所有实际可能的方式把它的用户作为协作开发人员。
7. 早发布、常发布、听取客户的建议
Linus的创新并不是这个(这在Unix世界中是一个长期传统),而是把它扩展到和他所开发的东西的复杂程度相匹配的地步,在早期一天一次发布对他来说都不是罕见的!而且因为他培育了他的协作开发者基础,比其他任何人更努力地充分利用了Internet进行合作,所以这确实能行。
但是它是怎样进行的呢?它是我能模拟的吗?还是这依靠于Linus的独特天才?
我不这样想,我承认Linus是一个极好的黑客(我们有多少人能够做出一个完整的高质量的操作系统内核?),但是Linux并不是一个令人敬畏的概念上的飞跃,Linus不是(至少还不曾是)象Richard stallman或James Gosling一样的创新天才,在我看来,Linus更象一个工程天才,具有避免错误和开发失败的第六感觉,把握了发现从A点到B点代价最小的路径的决窍,确实,Linux的整个设计受益于这个特质,并反映出Linus的本质上保守和简化设计的方法。
假如快速的发布和充分利用Internet不是偶然而是Linus的对代价最小的路径的洞察力的工程天才的内在部分,那么他极大增强了什么?他创建了什么样的方法?
问题回答了它自己,Linus保持他的黑客用户经常受到激励和奖赏:被行动的自我满足的希望所激励,而奖赏则是经常(甚至天天)都看到工作在进步。
Linus直接瞄准了争取最多的投入调试和开发的人时,甚至冒代码不稳定和一旦有非常棘手的错误而失去用户基础的险,Linus似乎相信下面这个:
8. 假如有一个足够大的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这种没有自然的动态类型支持的语言)记在脑中的一般原理:
9. 聪明的数据结构和笨拙的代码要比相反的搭配工作的更好
Fred Brooks也在他第11章中讲道:“让我看你的[代码],把你的[数据结构]隐藏起来,我还是会迷惑;让我看看你的[数据结构],那我就不需要你的[代码]了,它是显而易见的”。
实际上,他说的是“流程图”和“表”,但是在三十年的术语/文化演进之后,事情还是一样的。
此时(1996年9月初,在从零开始六个月后),我开始想接下来修改名字——究竟,它已不仅仅是一个POP客户,但我犹豫了,因为还没有什么新的漂亮设计呢,我的popclient版本需要有自己的特色。
当fetehmail学会怎样把取到的邮件转送到SMTP端口时,事情就完全改变了,但是首先:上面我说过我决定使用这个工程来测试我关于Linus Torualds所做的行为的理论,(你可能会问)我怎样做到这点呢? 以下面的方式:
1. 我尽早尽量频繁的发布(几乎从未少于每十天发布一次;在密集开发的时候是天天一次)。
2. 我把每一个和我讨论fetchmail的人加入一个beta表中。
3. 每当我发布我都向beta表中的人发出通告,鼓励人们参与。
4. 我听取beta测试员的意见,向他们询问设计决策,对他们寄来的补丁和反馈表示感谢。
这些简单的手段立即收到的回报,在工程的开始,我收到了一些错误报告,其质量足以使开发者因此被杀掉,而且经常还附有补丁、我得到了理智的批评,有趣的邮件,和聪明的特征建议,这导致了:
10. 假如你象对待最宝贵的资源一样对待你的beta测试员,他们就会成为你最宝贵的资源。
六. popclient变成了Fetchmail
这个工程的真正转折点是Harry Hochleiser寄给我他写的代码草稿,他把邮件转发到客户端机器的SMTP端口,我立即意识到这个特征的可靠实现将淘汰所有其他的递送模式。
几个星期以来我一直在修改而不是改进fetchmail,因为我觉得界面设计虽然有用但是太笨拙琐碎了,到处布满了太多的粗陋的细小选项。
当我思考SMTP转发时我发现popclient试图做的事太多了,它被设计成既是一个邮件传输代理(MTA)也是一个本地递送代理(MDA)。使用 SMTP转发,它就可以从MDA的事务中解脱出来而成为一个纯MTA,而象sendmail一样把邮件交给本地递送程序来处理。
既然端口25在所有支撑TCP/IP的平台上早已被预留,为什么还要为一个邮件传输代理的配置或为一个邮箱设置加锁的附加功能而操心呢?尤其是当这意味着抽取的邮件就象一个正常的发送者发出的SMTP邮件一样,而这就是我们需要的。
这里有几个教益:第一,SMTP转发的想法是我有意识地模拟Linus的方法以来的最大的单个回报,一个用户告诉我这个非同平常的想法——我所需做的只是理解它的含义。
11. 想出好主意是好事,从你的用户那里发现好主意也是好事,有时候后者更好。
很有趣的是,你很快将发现,假如你完全承认你从其他人那里得到多少教益的话,整个世界将会认为所有的发明都是你做出的,而你会对你的天才变得谦虚。我们可以看到这在Linus身上体现得多明显!(当我在1997年8月的Perl会议上发表这个论文时,Larry Wall坐在前排,当我讲到上面的观点时,他激动的叫了出来:“对了!说对了!哥们!”所有的听众都哄堂大笑起来,因为他们知道同样的事情也发生在Perl的发明者身上)。
于是在同样精神指导下工程进行了几个星期,我开始不光从我的用户那儿也从听说我的系统的人那儿得到类似的赞扬,我把一些这种邮件收藏起来,我将在我开始怀疑自己的生命是否有价值时重新读读这些信。
但是有两个更基本的,非政治性的对所有设计都有普遍意义的教益。
12. 最重要和最有创新的解决方案经常来自于你熟悉到你对问题的概念是错误的。
一个衡量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(在他成为经典儿童书籍作家之前是一个飞行员和飞机设计师)曾说过:
13. “最好的设计不是再也没有什么东西可以添加了,而是再也没有什么东西可以去掉。”
当你的代码变得更好和更简单时,这就是你知道它是正确的时候了,而且在这个过程中,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花了我相当长的时间,不仅因为它的每个单独部分都很难,而且因为它有一大堆相互依靠的苛刻的细节。
但是多投寻址也成为一个极好的设计决策,由此我知道:
14. 任何工具都应该能以预想的方式使用,但是一个伟大的工具提供你没料到的功能。
Fetchmant多投功能的一个没有料到的用途是在SLIP/PPP的客户端提供邮件列表、别名扩展。这意味着一个使用个人机器的人不必持续访问 ISP的别名文件就能通过一个ISP帐户治理一个邮件列表。我的beta测试员提出的另一个重要的改变是支持8位MIME操作,这很轻易做,因为我已经仔细的保证了8位代码的清楚,不仅因为我预见到了这个特性的需求,而且因为我忠实于另一准则:
15. 当写任何种类的网关型程序时,多费点力,尽量少干扰数据流,永远不要抛弃信息,除非接收方强迫这么作!
假如我不遵从这个准则,那么8位MIME支持将会变得困难和笨拙,现在我所需要做的,是只读一下RFC 1652,在产生信头的逻辑加上一点而已。
一些欧洲用户要求我加上一个选项来限制每次会话取得消息数(这样他们就可以从昂贵的电话网中控制花费了),我很长一段时间拒绝这样做,而且我仍然对它不很兴奋,但是假如你是为了世界而写代码,你必须听取顾客的意见——这并不随他们不付给你钱而改变。
八. 从Fetchmail得来的另一些教益
在他们回到一般的软件工程问题以前,还有几个从fetchmail得到的教益需要思考。
rc文件语法包括可选的“noise”要害字,它被扫描器完全忽略了,当你把它们全抽取出的时候,要害字/值对更具可读性。
当我注重到rc文件的声明在多大程度上开始象一个微型命令语言时,这是一个Late-night的体验(这也是我为什么把popclient原来的“server”要害字改成了“poll”)。
对我来说似乎把这个微型命令语言变得更象英语可能会使它更轻易使用。现在,虽然我对经过Emacs和Html及许多数据库引擎所证实的“把它做成一个语言”的设计方式确信不疑,但是我并不是一个通常的“类英语”语法的狂热拥护者。
传统程序员轻易控制语法使它尽量精确和紧凑,完全没有冗余,这是计算机资源还很昂贵时遗留下的一种文化传统,所以扫描策略需要尽可能的廉价和简单,而具有50%冗余度的英语,看来好象是一个非常不合适的模型。
这并不是我不用类英语语法的原因,我提到这一点是为了推翻它,在更廉价的时钟周期与核心的时代,简洁并没有走到尽头,今天对一个语言来说,对人更方便比对机器更廉价来的更加重要。
然而,有几个原因提醒我们小心一点,一个是扫描策略的复杂度开销——你并不想把它变成一个巨大的错误