- 浏览: 360869 次
- 性别:
- 来自: 新京
文章分类
最新评论
-
borners:
挺好,很恰当的举例
OOA/OOD/OOP的区别[转] -
Successful051:
不能用啊????用不了。
JD-GUI反编译代码中注释的批量替换 -
keren:
是linux环境,要dll干嘛呢?
Tomcat Native library was not found -
fdh_金色年华:
使用BufferedReader的readLine方法读取每一 ...
JD-GUI反编译代码中注释的批量替换 -
z582227655:
wangzi6hao 写道缓存问题.在firefox下,更为严 ...
日历控件jscalendar-1.0中文解决方法
Linux是颠覆性的。就是五年以前(1991),谁能想得到散布在全球各地的几千名开发者,仅靠细细的互联网连接,能够在业余时间魔术般地铸成一个世界级的操作系统呢?
反正我没想到。在1993年初Linux引起我的注意的时候,我已经在Unix和开放源代码开发领域做了十年了。我是80年代中期最早的GNU开发者之一,已经在网上发布了相当一部分软件,正在开发或协助开发好几个直到今天都在广泛使用的软件(nethack,Emacs的VC和GUD模式,xlife和其它)。我觉得我很懂行了。
Linux颠覆了许多我以为我懂的东西。当时,我已经宣扬小而专的工具、快速建模和演进式开发等Unix理念多年了。但我也相信项目到了一定复杂程度后就需要更集中地按事先计划管理。我相信最重要的软件(操作系统和Emacs之类的大型工具)需要像大教堂一样来搭建:遗世独立的圣人巨匠们牵尺引斤琢之磨之;时候不到beta版不出。
Linus Torvalds的开发风格(尽早尽多的发布,委托所有可以委托的事,对所有的改动和融合开放)令人惊奇地降临了。这里没有建造大教堂的安静和虔诚;Linux社区更像一个充满不同议程和方法的嘈杂的市集(Linux归档站点就是绝好的例子,任何人的作品都接收)。然而,一个统一稳定的系统就象奇迹一般从这个市集中产生了。
这种设计风格不但可行,而且工作得很好,对我无疑是一个巨大的震撼。在我的摸索过程中,我不仅效力与个别项目,而且努力去理解为什么Linux世界没有在混乱中分崩离析,而是以大教堂建造者们难以想像的速度茁壮成长。
到1996年中,我想我开始理解了。我有了一个测试我理论的完美机会,一个我可以有意识地用市集风格来运行的开源项目。我这样做了,结果非常成功。
这里讲述的就是这个项目的故事。我将借它来提出一些开源软件有效开发的精髓。它们并非全部源自Linux世界,但我们会看到它们如何在Linux世界中得到印证。如果我是正确的话,它们会帮助您准确理解是什么使Linux社区成为优秀软件的源泉——或许,它们还会帮助您变得更加高效。
邮件必须得通过
从1993年以来,我负责宾州西切斯特一家提供免费网络服务的小公司CCIL的技术工作。我协同创建了CCIL,并写了我们独家的多用户论坛软件——您可以用telnet连接locke.ccil.org来试一下。今天它在三十条线上支持近三千名用户。这份工作允许我通过CCIL的56K线路每天二十四小时上网——其实,这份工作事实上要求这一点!
那时,我已经习惯使用即时的互联网邮件。我发现不时地要telnet登录上公司服务器“locke”检查邮件很烦人。我想要的是把我的邮件传送到我家里的个人机器“snark”上,这样我可以在邮件到达的时候得到通知,并使用我自己的软件来处理它。
这里互联网的默认邮件输送协议SMTP不适用了。SMTP是为全时在线的机器设计的,而我的个人机器并不总在网上,也没有一个固定的IP地址。我需要一个程序在我拨号上网期间连到服务器上,把要下到本地的邮件取回来。我知道有这种工具存在,且多数使用一个简单的叫做POP的协议。现在常用的客户端邮件软件都支持POP,但那个时候,我的邮件阅读器不支持它。
我需要一个POP3的客户端软件,所以我就跑到网上找了一个。事实上,我找到了三四个。其中的一个我用了一段时间,但它少了一个看起来很明显的功能:修改到达邮件的来信地址以便正确回信。
问题是这样的:假设“locke”上一个叫“joe”的人给我发了信。如果我把信取到“snark”上,然后试图回复,我的邮件程序会高高兴兴地努力把回信发送给“snark”上一个并不存在的“joe”。通过手工修正邮件的回信地址很快就变成了一件很痛苦的事。
显然这该是电脑替我做的事。但是现有的POP客户端软件没有一个会做!这给我们带来了第一个教训:
每一个好软件的起因都是挠到了开发者本人的痒处。
这或许应该是很显然的(一直有箴言道“需要是发明之母”),但软件开发人员太过经常地在那些他们既不需要也不喜欢的程序上消磨时日、换取工资。但在Linux世界不是这样子的——这或许解释了为什么Linux社区中产生的软件平均质量这么高。
那么,我立马儿投入到了一轮疯狂的编程中来写一个和现有POP3客户端竞争的软件了吗?不是!相反,我仔细检查了我拿到手的那些POP程序,自问“哪一个离我要的最接近?”因为:
好的程序员知道写什么。伟大的程序员知道改写(和重复使用)什么。
虽然我不自封伟大,但我努力模仿伟大的程序员。伟大者的一个重要特点是建设性的懒惰。他们知道你需要的是结果而不是过程,而且从一个好的部分方案开始总比从零开始要容易得多。
以Linus Torvalds为例,他实际上没有试图从零开始写Linux。相反,他的代码和主意开始于PC机一个小小的类UNIX系统MINIX。最终所有Minix的代码都被拿掉或重写了——但在起步的阶段,Minix提供了那个最后成为Linux的新生儿成长的脚手架。
遵循同样的精神,我出发去寻找一个已有的、写得过得去的POP程序来作为开发的基础。
UNIX世界里的源代码共享传统一直对代码再用很友好(这也是为什么GNU项目尽管对
UNIX很有成见,还是选择了UNIX作为其基本操作系统)。Linux世界几乎把这种传统发挥到了技术上的极限;有上万亿字节的开放代码可供获取。所以花点时间在Linux世界里找个别人“差不多够好”的程序,比其它任何地方都更有可能找到。
我就找到了。加上我以前找到的,我的第二次搜索有了九个候选对象:fetchpop、PopTart、get-mail、gwpop、pimp、pop-perl、popc、popmail和upop。我第一个选用的是欧松宏(音,Seung-Hong Oh)的“fetchpop”。我把我改写邮件头的功能加了进去,并作了其它一些改进。松宏后来把这些加进了他的1.9版本。
然而几个星期以后,当我碰到了Carl Harris的“popclient”代码时,我发现我遇到了一个问题。尽管fetchpop有一些很好的新主意(例如它的后台daemon模式),它只能处理POP3协议,而且程序代码写得比较业余(松宏当时是个聪明但是缺少经验的程序员,这两个特点都有显示)。Carl的代码好一些,很专业和稳固,但他的程序缺几个重要的而且难实现的fetchpop里的功能(包括我自己写的那些)。
继续用fetchpop还是转换到popclient上来?如果转换的话,我得扔掉我已经写好的那些代码来换取一个好一些的开发基础。
一个实用的转换动机是对多种协议的支持。POP3是服务器端POP协议中最常用的,但不是唯一的。Fetchpop和那一个竞争对手都不支持POP2、RPOP或APOP,而且为了好玩,我那时已经有了添加IMAP(最新设计的、最强大的POP协议)的模糊想法。
但我还有一个更理论上的原因来认为转换也是个好主意。这是我远在Linux之前就学到的。
“计划扔掉一个;无论如何你都会扔掉一个的。”(Fred Brooks《人月神话》第11章)
或者换句话说,直到你第一次实现一个方案之前,你常常并没有真正理解你的问题。第二次呢,或许你已经学到了如何把它做对。所以,要想把事做对,你得准备至少重来一次[注:JB]。
[注:JB]在《编程珠玑(Programming Pearls)》中,知名的计算机科学大师JonBentley对Brooks的上述文字有如下评注:“如果你计划要扔掉一个,你也会扔掉第二个”。他说的完全正确,Brooks和Bently不仅仅指出第一次尝试往往会错,而且指出用新的方法重新开始往往比挽救一堆乱代码来得快捷。
好吧(我对自己说),对fetchpop做的修改算我的第一次吧。于是我转换了。
在1996年6月25日我给Carl Harris发送了我写的第一批popclient补丁后,我发现他一段时间之前就基本上对这个项目失掉兴趣了。项目的源代码有些陈旧了,小臭虫们流连不去。我有很多要修改的东西;我们很快同意:理所当然,我该把整个项目接手过来。
在我没有觉察的时候,这个项目升级了。我不再是试图给一个现有的POP客户端程序做些小补丁。我开始负责维护整个程序,而且我知道我脑子里冒着的新主意可能会导致一些主要的变动。
在一个鼓励代码共享的软件文化中,这是一个项目进化的自然方式。我在实践下面这个原理:
如果你有正确的态度,有意思的问题会自动找上门。
Carl Harris的态度甚至更重要。他懂得:
当你对一个项目失去兴趣时,你最后的职责是把它交给一个称职的继承者。
尽管Carl和我从来没有讨论过这一点,我们知道我们的共同目标是找出问题的一个最好解决方案。我们唯一的问题是我能否证明我的能力。一旦我作到了,他优雅而迅速地作了交接。我希望当这一天轮到我的时候,我也能做得同样出色。
用户的重要性
就这样,我继承了popclient。同样重要的是,我继承了popclient的用户群。拥有用户是件美好的事情。他们的存在不仅证实了你的工作正在满足一种需求,而且证实的你做的不错。通过适当的培养,他们还可能成为你的开发伙伴。
很多用户也是黑客,这是UNIX传统中的另一个强项,而Linux把它发展到了快乐极致。因为可以得到源代码,他们可以是有效的黑客。这一点对缩短调试时间会非常有帮助。给上一点点鼓励,他们会帮助诊断问题,提出建议和补丁,并以你一个人不可企及的速度帮助改进代码。
把用户看作合作者是通往快速改进代码和有效调试的最佳通道。
这一点所蕴藏的能量很容易被低估。事实上,直到Linus Torvalds给我们演示之前,开源世界里几乎所有人都严重低估了它如何随用户数目而增长潜能,如何随系统的复杂度增大而更显威力。
事实上,我认为Linus最聪明、最有影响的手笔不是Linux内核本身,而是发明了Linux的开发模式。有一次当我当面向他表达这个观点时,他微笑了,安静地重复了他经常说的一句话:“我基本上是一个很懒惰的人,喜欢在其实是别人做的事情上领取荣誉”。象狐狸一样懒惰,或者象Robert Heinlein[译注:著名科幻作家]笔下的一个著名角色:太懒惰而不会失败。
回头来看,Linux方法和成功的一个先例是GNU Emacs的Lisp库和Lisp代码档案。与Emacs C核心和大多数的其它GNU工具的大教堂建造风格相反,Lisp代码群的进化是活跃的、多由用户驱动的。点子和原型经常要重写三四次才能达到一个稳定的最终形式。象Linux那种通过互联网的松散协作也很频繁。
确实,我自己在fetchmail之前最成功的一次编程可能是Emacs的VC(版本控制)模式。那是类似Linux一样与其他三个合作者仅通过电子邮件交流的一次合作。三个人中我至今只见过一个(Richard Stallman,Emacs的作者、自由软件基金会的创始人)。VC是Emacs中SCCS,RCS和后来CVS模式的前台;Emacs借此提供“单击式”的版本控制操作。它是由一个别人写的小小的、粗糙的sccsl.el模式演进而来。VC开发的成功也是因为不象Emacs本身,Emacs Lisp代码可以快速地通过多轮“发行/测试/改进”的循环。
Emacs的故事不是唯一的。其它软件也有这种双层的构架和双层的用户群:核心用大教堂模式;工具箱用市集模式。其中的一个是MATLAB,一个用于数据分析和可视化的商业工具。MATLAB和其它类似架构产品的用户一致认为,产品的开放部分——有一个巨大多样的用户群可以对其推敲的地方——才是动力、热情和创新的所在。
早发布、常发布
早发布和频繁发布是Linux开发模式中关键的一个部分。以前多数开发者,包括我,都认为这对复杂项目来说是个坏办法,因为早期版本几乎是问题版本的同义词,而你不想这么早就消耗完用户的耐心。
这个观点也促使人们普遍采取建造大教堂式的开发。如果首要的目标是尽量让用户少遇到臭虫,那么你应该六个月甚至更久才发布一个版本,在两次发布之间象狗一样拼命去调试。Emacs的C核心就是这样开发的。Lisp库实际上不是——因为在自由软件基金会所辖之外还有其它活跃的Lisp存档,提供独立于Emacs发布周期的新版本和测试版本。
那时最重要的Lisp库——俄亥俄州立大学Emacs Lisp档案——已经超前具有了今天Linux大型档案的管理精髓和许多功能。但是我们中很少有人深考过我们在做什么,以及俄亥俄档案的存在本身说明了自由软件基金会大教堂开发模式的哪些问题。在1992前后,我曾努力要把一大批俄亥俄代码合并到Emacs Lisp的官方库,但遇上政治性的麻烦,结果非常不成功。
一年以后,Linux影响逐渐扩大。显然Linux的开发者拥有一些不同的但是远远更为健康的东西。Linus的开放性开发政策正与建造大教堂的方式相反。Linux的互联网档案枝蔓繁衍,多个发行版在坊间流传。而所有这些都是由Linux内核前所未闻的发布频率所驱动的。
Linus在以最有效的合作者方式来对待他的用户:
早发布、常发布,听取用户的意见。
快速发布、采纳大量用户反馈,并不怎么算Linus的创新(Unix世界历来就有这种传统)。他的创新之处是把这个办法升级到了与所开发系统复杂性相匹配的规模和强度。在早期(1991年左右),我们不是没听说过他一天发布不止一个新的内核版本!因为他比任何人都努力地培养合作开发群体、促进网上合作。他的办法生效了。
但它是怎样生效的呢?难道只有Linus Torvalds的独特天才才能实现?
我想不是的。Linus当然是个骨灰级黑客。有几个人能从零开始开发出一个企业级操作系统的内核?但是Linux并不代表任何概念上的重大突破。Linus不是(至少还没有成为)象Richard Stallman或James Gosling(Java之父)那种设计和创新的天才。在我看来,Linus更象是工程和开发的天才,有着避开臭虫和死胡同的第六感官和找到从一点到另一点最快捷径的本领。确实,整个Linux内核透露着这种特质,反映了Linus本质上保守和一切从简的设计方法。
如果快速发布和淋漓尽致的利用互联网媒介不是偶然,而是Linus对最快捷径的天才工程洞察力的有机部分,那么他试图最大化的是什么?他试图从这个机制释放出一种什么样的力量?
这样一问,答案一目了然。Linus在不断地激励和奖掖他的黑客用户们——激励来自于在参与中得到的自我实现,奖掖来自于看到他们自己工作的持续(甚至每天)进步。
Linus直接瞄准了调试和开发力量的最大化,即使牺牲程序的稳定性或因某个修正不了的严重问题导致丧失用户也在所不惜。似乎Linus相信:
足够多beta测试者和合作开发者会让任何问题快速显形并很快被解决。
或者通俗一点,“只要眼球足够多,所有臭虫都好捉”。我称之为“Linus法则”。
对上述法则,我最早的表述是每个问题“都会有某个人能解决”。Linus有异议:理解和解决问题的人不一定甚至一般不是第一个发现问题的人。“一个人发现问题”,他说,“另一个人把它搞明白,而且我会作证说发现问题这一步更困难一些。”这是个重要的纠正;在下一节具体研究实际调试时我们会看到为什么。但是关键一点是,发现和解决问题这两个步骤一般都会很快完成。
我认为Linus法则中包含有大教堂模式和市集模式的关键区别。在大教堂式的编程观念中,臭虫和开发上的问题是复杂、困难和深奥的,要几个人全心全力几个月的投入才有把握已经把它们清理干净的,所以需要很长的发布周期;一旦等候已久的版本不够完美,失望就在所难免。
恰恰相反,在市集式的观念中,你预设臭虫都是简单的问题——至少在上千个共同开发者热心地琢磨每一个新版本的情况下,它们会很快就变简单了。相应地,你频繁发布来得到更多的纠错。作为一个附加效应,偶尔出个大勺子的后果也没有那么严重了。
这就是了。这也就够了。如果“Linus法则”是错误的,那么象Linux内核这样复杂的系统,经过了那么多人的敲打,应该在某一时刻已经在不曾预见的恶性互动和深藏不露的问题的重压下崩溃了。如果它是正确的,它足以解释Linux相对较少的问题,和数月甚至数年以上的持续运行时间。
或许这不该是如此一个意外。社会学家们多年前就发现了一大群同样内行(或同样白痴)观察者的平均预测要比其中随机选择一个人的预测可靠得多。他们称之为“神庙效应”。看来Linus显示了这一点甚至适用于调试一个操作系统——甚至是内核级的复杂程度,“神庙效应”也可以简化开发。
Linux情形中对“神庙效应”有帮助的一点是,任何一个项目的参与者都是自我选择的。一个早期评论指出,对Linux的贡献不是来自于一个随机的人群;他们都有足够的兴趣来使用这些软件、研究其机理、并试图解决所遇到的问题,而且真正给出显然可行的解决办法。经过了这些筛选的人一般都会有可以贡献的真才实料。
Linus法则也可以表述为“调试是可并行的”。尽管调试者们需要一个人来通讯协调,调试者们之间并不需要多少的协调。添加开发人员所带来的平方级复杂度和管理成本在这里不适用。
理论上因为调试者重复做功而导致的效率损失在Linux世界中似乎从来都不是问题。“早发布、常发布”策略的一个后果就是通过快速公步反馈修补来把重复做功最小化。
Fred Brooks(《人月神话》作者)甚至作过一个相关的非正式评论:“一个广泛使用的程序的维护费用一般是它开发成本的40%以上。令人惊奇的是,这个费用受用户数强烈影响。用户越多,发现的问题也越多。”
用户越多、发现问题越多是因为检验程序的角度也越多。当用户同时是合作开发者时,这个效应放大了。在检测问题的过程中,每个人都有一些不同的观察方法和分析工具,从不同角度逼近同一问题。“神庙效应”似乎正是因为这种多样性而有效。在调试程序这个特定的环境下,这种多样性也利于减少重复做功。
所以从开发者的角度来讲,增加更多的beta测试者不见得会减少当前最大问题的复杂程度,但会增加某个人的工具箱正好适用于该问题的几率——这样对这个人来说,这个问题其实很简单。
Linus在此之外还留有一招。如果可能存在大的“臭虫”,Linux内核的版本编号允许潜在用户选用老一点的“稳定”版本,或冒“臭虫”之险以求“实验”版本的最新功能。多数Linux黑客还没有系统地模仿这一招;但他们或许应该去模仿。给出这个选择使得两种版本都更有吸引力。[注:HBS]
[注:HBS]Linux内核的稳定版和实验版之分除了分担风险目的之外,还可针对解决发布期限问题。事实表明,当开发人员同时面临死板的功能列表和发布期限时,软件的制作质量明显降低。哈彿商学院的Marco Iansiti和Alan MacCormack指出放松其中任何一个限制均可产生可行的计划,在此表示感谢。
稳定版内核采用的是放松功能列表,但限制发布期限的策略。稳定版内核的维护者Alan Cox定期发布新的稳定版,但是不承诺何时必须解决哪个问题或加入哪个开发版新功能。
实验版内核刚好相反,采用的是放松发布期限,但固定功能列表(所谓“做完了再叫醒我”)的策略。De Marco和Lister引用的研究结果表明该策略不仅导致高质量的软件,并且平均比“保守”或“激进”开发计划都可带来更短的发布时间。
2000年初我开始怀疑我在本文先前的版本中低估了这种“做完了再叫醒我”的策略对提高开源项目生产力和质量的重要性。1999年GNOME项目为赶1.0版导致的负面影响表明,即便是开源项目,为赶进度而发布尚未成熟的代码也会严重影响到软件质量。
很有可能我们会发现,开发过程的透明性,连同“做完了再叫醒我”以及开发者的自我选择,可能是推动开源项目质量的三个同等重要的作用力。
要多少个眼球来驯服复杂度
在整体上观察市集风格很大地加速了调试和代码进化是一回事,在微观、日常的开发者和测试者行为上来准确理解怎样和为什么是另一回事。在这一节(写在原始文章的三年以后,采纳了读了本文、又对照了自身的很多开发者们的意见),我们来实打实地看一下其背后真正的机制。不喜欢技术的读者可以安全地跳到下一节。
理解这个问题的一个关键在于认识到以下重要的事实,那就是对源码知之甚少的用户所递交的问题报告往往并没有多少用处。这些用户一般只会报告表面症状;他们往往忽略了软件的运行环境,所以这些提交的报告不但会漏掉关键的背景数据,而且极少包括重现问题的步骤。
这里更深层的问题是测试者和开发者对程序的不同理解。测试者从外往里看,而开发者从里往外看。在封闭源码的开发模式中,他们都被卡在各自的这种角色里了,往往各说各的话,同时又觉得和对方交流非常困难。
开源开发打破了这种束缚,测试者和开发者基于源代码很容易建立起统一的模型,就之进行有效地交流。仅仅描述外观症状的问题报告和直接联系到基于源码抽象模型的报告对开发者的帮助是截然不同的。
如果能够在代码层给出一个对出错条件哪怕是不完整的提示性的描述,大多数臭虫在大多数情况下都是容易捉到的。当某个beta测试员指出“在某某行有一个边界问题”,或者只是“在某某条件下,这个变量溢出”,对这些代码的一个快速扫描常常足以锁定出错的准确模式并搞定一个修补办法。
所以,如果beta测试者和核心开发者都对源代码心里有数,双方的交流和合作会得到大大增强。这意味着核心开发人员的时间会节约下来,即便合作者人数众多。
开源方法另一个节约开发者时间之处来自于开源项目的典型通讯结构。我在上面用到了“核心开发者”一词;这反映了项目核心(一般很小;一个核心开发者很平常,一到三个很典型)和beta测试人员、补丁提供人员组成的项目外沿(经常上百人)的区别。
传统软件开发结构需要解决的根本问题是Brooks法则:“在延期的项目添加程序员只会延期更久”。普遍来讲,Brooks法则认为,随着开发人员数目的增加,项目的复杂程度和通讯成本按平方增加,而业绩仅以直线增加。
经验表明,臭虫大多集中在不同人写的代码的界面上;而一个项目的通讯协调成本一般按照人与人之间的交流渠道数量增加。这是Brooks法则的基础。也就是,问题随开发者之间通讯路径数目的增加而增加,而后者与开发者数目是平方关系(更准确地说,遵从公式N*(N-1)/2,这里N是开发者的数目)。
Brooks法则的分析(以及它引起的开发团体中对人数过多的恐惧)基于一个潜在的前提:项目的通讯结构必须是一个完整的图,每个人都与其他所有人交接。但是在开源项目中,外沿开发者做的实际上是平行分离的子项目,彼此交接甚少;代码变动和臭虫报告都流经项目的核心,只有在小小的核心团体中,才需要完整的Brooks通讯开销。
由于一个错误常常可以产生多个不同的症状,在用户使用习惯和环境细节不同时有不同的表现,这使得源代码层次上的问题报告往往非常有效。这类错误一般正是那些复杂和微妙的臭虫——那些最难重现并难以用静态分析捕捉的,并在软件中制造长期问题的祸根(比如动态内存管理错误或随机的中断窗口错误)。
一个测试者提供的对此类错误尝试性的源码级诊断(例如:“看起来第1250行的信号处理部分存在一个窗口”或者“你在哪里把缓冲区清零了?”)可能会成为对这些部分代码熟视无睹的开发者同时解决七八个不同症状的关键线索。此时,往往无法判定症状和臭虫的对应关系,但是多发布使得我们无须去一一了解。其他用户很快就会发现问题是否不再出现。很多情况下,源码级的问题报告使得症状无须明确被哪个补丁解决而自然消失。
复杂的多症状错误也常常会有从表面症状联系到内在臭虫的多个跟踪途径。一个特定的开发者或测试者所能追寻的跟踪途径可能取决于这个人的具体环境细节,也很可能随着时间的改变发生不便预测的变化。实际上,每一个开发者和测试者在寻找一个症状的病原时都是在检查该程序的状态空间的一个“半随机”集合。臭虫越微妙越复杂,此人技能和所追寻空间相关的保证就越小。
对于简单的容易重现的臭虫,那么,重音要放在“半”上面而不是“随机”上面;此时,调试技能和对代码、框架的熟悉程度是最重要的。但对于复杂的臭虫,重音就要放在“随机”上面。在这种情况下许多人同时追踪要比少数人串行追踪有效的多——即使这少数人的技能水平高的多。
要是从不同的表面症状挖掘到臭虫的跟踪途径难度不一、难以从观察症状来预测的话,并发追踪的效果就会非常之明显了。一个持续追踪这些路径的开发者在开始可能会遇到一个简单的路径,也同样可能会遇到一个复杂的路径。另一方面,试想有许多人在快速发布下平行地来检查这些追踪路径,那么其中某人很可能会碰上最容易的路径,在短的多的时间里解决这个问题。维护项目的人会看到这个,发行一个新版本;其他在更困难的路径上追踪同一个臭虫的人们就可以在花费太多时间之前停下来。[注:RJ]
注:RJ]给我提供不同难度跟踪途径的读者推测此类多表症问题跟踪的难易程度呈“指数”分布(我把其理解为高斯或泊松分布,且同意他的看法)。如果有实验数据可以给出类似的分布曲线,将会非常有价值。如果此曲线与等概率分布的平行线差别甚远,那么即使在一个开发者的情况下,也应该模仿市集模式,采取限定在一个表症上跟踪时间的策略。坚持有时不一定是件好事……
玫瑰何时不再是玫瑰
研究了Linus的作法并形成了一个它何以成功的理论后,我决定在我的新项目(当然没有Linux那么复杂和宏伟)里有意识地测试这个理论。
但我做的第一件事是把popclient重组和简化了许多。Carl Harris的代码实现得很好,但是有一种在C程序员中常见的多余的复杂。他把代码放在了中心位置,数据结构作为辅助。结果代码很漂亮,但是数据结构设计得潦草甚至丑陋(至少按我这个LISP老手的标准来看)。
然而,除了改进代码和数据结构设计以外,我的重写还有另一层目的,那就是把它进化成一个我完全理解的东西。要是你不完全理解一个程序,维护起来可不是好玩的。
于是在最初的一个月左右,我只是在按照Carl的章程做事。我作的第一个重要改变是添加了IMAP支持。我实现这点的方法是:把协议的具体部分做成插件形式,用一个通用的驱动来根据不同协议调用不同插件的方法表(分别针对POP2、POP3和IMAP)。这些变动都示范了下面这个程序员们应该记住的通用原则(尤其像C这种本身不支持动态数据类型的语言):
聪明的数据结构和愚蠢的代码要比反过来好的多。
Brooks的第九章:“给我看你的流程图而隐藏你的数据结构,我会继续糊涂着。给我看你的数据结构,我一般就不需要你的流程图了;它们将显而易见”。经过三十年的文化和术语的变迁,这个观点仍然正确。
这时(1996年9月初,大约开工后六个星期),我开始想这个程序大概该换个名字了——它毕竟不再仅仅是一个POP客户端软件。但是我在犹豫,因为在设计上还没有什么真正的新东西。我的popclient版本还需要发展出它自己的特征。
当popclient学会了怎样把取到的邮件再通过本机SMTP端口转发的时候,这一点迅速改变了。我过一会儿再细谈这个。但是首先:我说过我决定用这个项目来测试我关于Linus的成功理论。(您也会问)我是怎样做的呢?在以下方面:
我早发布和常发布(几乎从未低于十天一次;高强度开发的时候,一天一次)。
我把每个和我交流有关fetchmail的人加进了我的beta测试名单。
每当我发布一个版本,我就给beta名单发送一个内容丰富的通告,鼓励大家参与。
我听取beta测试者的意见,在设计上征求他们的看法,当他们送交补丁和反馈时给予鼓励。
这些简单的办法立杆见影。从项目一开始,我就收到很多那种多数开发者梦寐以求的高质量的臭虫报告,经常还附带了好的补丁。我收到过深思熟虑的评论、粉丝的邮件,还有高明的功能性建议。这说明:
将你的beta测试者作为“最有价值资源”来对待,他们就会成为“最有价值资源”。
Fetchmail庞大的beta测试名单(fetchmail-friends)成为衡量其成功的一个有意义的指标。在我最近一次修订这篇文章的时候(2000年11月),它有287名成员,而且每个星期在增加两三名。
实际上,当我在1997年5月下旬改写的时候,我发现这个名单由于一个有意思的原因,从它近300的巅峰开始流失成员了。一些人要求我把他们从名单中去掉,因为fetchmail对他们来讲近乎完美,他们再也不需要阅读这个邮件列表了!或许这是一个成熟市集风格项目正常生命周期的一部分。
Popclient变成了Fetchmail
当Harry Hochheiser把他转发邮件到客户机SMTP端口的草稿程序发给我的时候,这个项目的真正转折点来临了。我几乎马上意识到,这个功能会让其它所有的邮件递送模式成为历史。
许多星期以来,我一直在按照原来的架构改进fetchmail,但总有些不舒服的感觉。我感觉目前的接口设计还可以用,但是不够干净漂亮,而且到处是微不足道的选项。把收取的邮件直接输出到一个邮箱文件或转到标准输出里的选项尤其让我心烦,但我想不出为什么。
(如果你对互联网邮件的技术细节不感兴趣,可以安全跳过下面的两个段落。)
受到SMTP转发的启发,我终于看清了。Popclient在试图做太多的事情。它被设计成了既是一个邮件传输工具(MTA),又是一个本地投递工具(MDA)。有了SMTP转发功能,它就可以摆脱MDA的负荷,像sendmail那样专心只作MTA,并把邮件的本地投递留给其它程序来完成。
几乎每一个支持TCP/IP的平台上都预留了25号端口,为什么还要去折腾MDA的复杂配置或邮箱的“锁定-添加”呢?更有甚者,通过SMTP转发意味着收到的邮件看起来几乎和正常的SMTP邮件一样。这正是我们想做到的。
(返回到高一层次上……)
即使读者没看懂上面的技术细节,这里也有几条重要的经验可以学习。首先,这个SMTP转发的点子是我有意模仿Linus方法的最大收获。一个用户给了我这个好点子——我需要做的仅仅是理解它的含义。
自己拥有好主意是好事,认识到来自用户的好主意也是好事。有时候后者会更好。
很有意思的是,如果你谦虚地坦白别人的贡献很多,你很快就会发现外面的世界不会这么看。他们会认为是你创造了这一切,只不过是你太谦虚而已。Linus就是最生动的例子。
(当我在1997年的第一次Perl大会上发言时,黑客大亨Larry Wall正坐在前排上。当我讲到上面的那句话时,他以激动的口吻喊了起来,“说出来,说出来,哥们!”全场都笑了,因为他们知道这一点对Perl的发明者[译注:即Larry Wall]也不例外。)
在我发扬这种精神把项目运行了几个星期以后,我开始得到类似的赞扬——不仅来自我的用户,而且来自于其他有所耳闻的人们。我把其中一些邮件收藏了起来;要是什么时候我开始疑惑我生命的意义时,我就拿出来再看看 :-)。
但是有两条基本的、非政治性的经验对各种设计都适用。
最有突破和创新的方案常常来自于意识到你把问题的模型弄错了。
我之前想继续把popclient做成一个支持五花八门的本地递送模式的MTA/MDA时,就是试图在解决错误的问题。Fetchmail应该重新被设计为一个纯粹的MTA,作为正常SMTP邮件传输路径的一部分。
当你在开发中碰到死胡同时——当你绞尽脑汁要超越下一个补丁的时候——一般来讲你这时该问的不是你的答案对不对,而是你的问题对不对。或许你的问题需要重新定义。
嗯……我重新定义了我的问题。显然,正确的路子是
加入SMTP转发支持,
把它设置为默认模式,
最终把其它的传递模式都去掉,尤其是传递到文件和标准输出这两种模式。
这第三步让我犹豫了一段时间,担心会影响那些依赖于此类模式的popclient老用户。理论上,他们可以马上用.forward文件或其它非sendmail方式的类似功能来获得相同的效果。但在实践中,这种转换可能会让人头大。
但事实证明,做完这三步后好处非常明显。驱动代码中毛病最多的地方消失了。配置选项也大大简化——不再需要围着系统MDA和用户信箱打转,也不再需要担心背后的操作系统是否支持文件锁定。
而且,唯一可能丢失邮件的情形不见了。以前,如果你指定递送到文件而磁盘满了的话,你的邮件就丢了,而这在SMTP转发中不会发生,因为除非邮件成功传送或至少缓存了,SMTP的聆听端不会给以确认。
同时,性能也提高了(尽管不是你运行一次就能感觉到的)。改变后另外一个不小的好处是说明手册简化了许多。
后来,我为了对付一些涉及到动态SLIP(Serial Line Internet Protocol,串行线互联网协议)的晦涩情形,不得不把递送到用户指定MDA的功能加回来,但做起来简单多了。
说明了什么道理?在不损失效率的情况下,不要犹豫把多余的功能扔掉。Antoine deSaint-Exupéry[译注:著名法国作家,著有儿童故事《小王子》等](他不在写作经典儿童图书的时候是个飞行员和飞机设计师)曾说过:
“设计达到完美的时候,不是增加得不能再增加了、而是减少得不能再减少了”。
代码变得既优良又简单的时候,标志着它上正轨了。在这个过程中,fetchmail有了它自己的设计特色,脱离了上一代的popclient。
到了该换名字的时候了。新的设计和老的popclient相比,更像是sendmail的对偶;二者都是MTA,但sendmail是往外投递,新的popclient是接过来再投递。所以在动工两个月后,我把它重命名为fetchmail。
在这个SMTP转发功能如何进入fetchmail的故事里,有一个更普遍的经验。那就是不仅调试是可并行的;开发和(在可能令人吃惊的程度上)搜索设计空间也是。当采用快速短周期的开发模式时,开发和添加新功能在某种意义上就成为调试改错的一个特例——改正软件原始设计中的“功能不足错误”。
即使在高一层次的设计上,有许多共同开发者在你产品的设计空间附近随机行走将会很有价值。想象一下:一滩水是怎样发现下水口的,或者更恰当一点,蚂蚁怎样发现食物的?本质上是分散、搜索,并通过一个可扩展的通讯机制来协调。这一点很管用,就像Herry和我一样,你们随行中的一个很可能会发现有一个宝藏就在附近,你只不过太专注了一点而没有看到。
(续)
Fetchmail长大了
现在我有了一个整洁新颖的设计;我知道代码工作良好因为我每天使用;beta测试名单繁荣热闹。我慢慢明白了我不再是在做一个可能只对几个人有用的琐碎的个人编程——我在主持一个所有使用SLIP/PPP邮件接口的Unix用户都需要的程序。
带有SMTP转发功能的fetchmail在竞争对手面前表现强劲,并很可能成为那种在它的功能领域里鹤立鸡群的经典程序,那种让对手们不仅被放弃而且几乎被遗忘了的“领域杀手”。
我觉得这种结果不是你想达到就能达到的。你需要某种超级的设计构想,自然而然地、甚至命中注定地把你推到那里,而找到这种构想前提无非就是要有很多想法可以去尝试——或者有工程眼光把其他人的点子发挥到其原作者根本想象不到的地步。
Andy Tanenbaum最先想到了做一个运行于IBM PC机的简单Unix作为教学工具(他称之为minix)。Linus把Minix的概念推进到了Andy可能想都想不到的地步,使其变成一个如此神奇的东西。与此类似(不过是规模小些),我从Carl Harris和Harry Hochheiser那里借来主意并努力推进它们。我们都不是“原创”——那种人们浪漫想象中的天才。但是话说回来,多数科学、技术和软件的进展都不是由“原创”或者黑客神秘主义中的超级天才完成的。
这两个项目的结果都一致是那种很眩目的东西——那种每一个黑客毕生追求的成功!而且这意味着我将不得不把我的标准设得更高。为了让fetchmail达到我预想的目标,我不仅要为自己,而且还要为别人编程,同时保持程序简洁、健壮。
意识到这点之后,我所写的第一个也是绝对最重要的一个功能是集体收发(multidrop)——从一群用户的集体信箱里把累积的所有邮件取来,然后把每一封分发给单独的收信人。
我决定添加集体收发功能,一部分是因为用户们吵着要,但主要还是因为我觉得它会迫使我更全面地处理地址问题,从而甩掉单信发送模式中的臭虫。结果如我所愿。我花了很长的时间才把RFC822的地址解析搞定,不是因为它的哪一部分很难,而是因为它涉及了一堆相互关联的烦人细节。
然而集体收发也成为了一项优秀的设计决定。我是这样知道的:
任何一个工具都应该达到预期的用处,但是一个真正优秀的工具会带来预期不到的用处。
集体收发一个不曾预期的功能是可以在网络连接的客户端建立邮件列表、管理列表成员和展开别名。这样,通过ISP上网的个人不必持续访问ISP方的别名扩展文件就可以管理一个邮件列表。
我的beta测试员们要求的另一个重要功能是支持8位的MIME操作。这个很容易,因为我已经很小心地保持了代码的8位兼容性(没有强迫ASCII字符集中没有使用的第8位比特去携带程序中的信息)。不是因为我预料到了这个功能要求,而是遵循了另一个原则:
在写任何接口软件的时候,花点功夫尽可能不要干扰数据流——除非用户强迫你,永远不要扔掉任何信息!
要是我没有遵守这个规则,8位MIME支持会很困难且毛病不断。事实上,现在我所需要做的仅仅是读一下MIME标准(RFC1652),然后添加一条小小的文件头生成规则。
在一些欧洲用户的要求下,我添加了一个选项来限制每次连接能下载的邮件数目(这样他们可以控制他们昂贵的电话费)。我对这件事抵制了很长一段时间,直到现在也不是完全满意。但是如果你是给外边的世界写程序,你就不得不聆听你的顾客——就算他们不付你钱也是这个道理。
Fetchmail带来的其它几条经验
在回到更广义的软件工程问题之前,还有几条fetchmail经历中的经验值得细想。非技术性的读者可以安全地跳开这一节。
Fetchmail用户配置文件(rc)语法中包括了一些完全不解析的、可选的“噪音”关键词。它们所带来的类似英语的配置文件语法比把它们全部梳理掉之后所剩下的传统“关键词-对应值”语法要可读得多。
这开始于一个深夜实验——那时我注意到rc文件的定义看起来多么像一个微型的命令式语言。(这也是我把popclient原有的“server”关键词换成“poll”的原因。[译注:server是名词“服务器”,poll是动词“检查”,后面跟随的是邮件服务器名。改成poll使配置文件读起来更象句子。])
在我看来,努力把这个微型指令语言做得更像英语可能会使它更容易使用。那时,尽管我支持“把它做成一门语言”的设计流派,比如Emacs、HTML和许多数据库引擎设计的那样,我还不是特别热衷于“类似英语的”语法。
传统上,程序员们倾向于选用简洁紧凑、完全没有冗余的控制语法。这是计算资源昂贵时期的文化遗留。那时,解析过程不得不尽可能的廉价和简单,大概有50%冗余的英语明显是一个非常不合适的模型。
这不是我一般避免英语式语法的原因;我在这儿提起它正是为了打破这个看法。有了便宜的主频和处理器,简洁不应该为了简洁而简洁。现在一门语言对于人的方便比对于计算机处理的方便更重要。
然而我们还有需要小心的原因。其中之一是解析过程的复杂性成本不能被提高到富产臭虫和困惑用户的程度。另外,试图把一门语言的语法做得像英语经常迫使它的“英语”严重扭曲变形,以至于对自然语言的表面模仿变得像传统语法一样令人困惑。(你可以在许多所谓的“第四代”和商业数据库查询语言中看到这个坏效果。)
Fetchmail配置文件的语法似乎避免了这些问题,因为它的语言空间极为有限。它离一个通用的编程语言还很远;它需要表达的内容也很简单。所以在这里,由语言扭曲引起困惑的可能性很小。我觉得这里可能有一个更普适的经验:
当你的语言离图灵完备还差得远的时候,不妨给语法加点“糖衣”包装。
另一条经验是关于隐藏和安全性的问题。一些fetchmail的用户要求我改一下软件来把储存在rc文件里的密码加密,这样入侵者就不会在无意中看到它们。
我没有照办,因为这实际上并不会添加保护。不管怎样,任何一个有权读你rc文件的人都可以像你一样来运行fetchmail打开你的信箱——如果他们真的来找你的密码,他们也可以从fetchmail的代码中剥离出必要的解码器来得手。
所以,在rc文件中使用加密的密码只不过是给那些不怎么用心思考的人一种虚假的安全感。这里的一般规则是:
一个安全系统的安全性取决于其秘密的安全性。小心伪秘密。
市集风格的必要前提
这篇文章的早期审阅者和试验听众们不断希望了解市集开发风格的成功前提,包括项目领导人的素质和他开放项目和开始建立合作者社区时的程序代码状态。
很显然,市集风格不能帮助你从零开始编程[注:IN]。你可以使用市集风格测试、调试和提高你的代码,但是直接以市集模式来孕育一个项目会是很困难的。Linus没有这样试过,我也没有。你新生的开发者社区至少需要一个能运行和测试的东西。
[注:IN]另外一个和市集风格能否帮助你从零开始一个项目的话题是市集风格中能否产生创新。一些人声称市集风格缺乏强有力的领导,因此只能模仿或改进已有的工 程方法,但无法孕育创新。该观点最有代表的作品要属“万圣节档案”了。这是微软关 于开源现象的两篇内部备忘录,其作者认为,Linux只不过是追着Unix系统的尾巴跑,“(一旦追上了后者开始领跑),庞大的管理开销便会让其前进得非常缓慢”。
这个观点和事实完全不符。甚至万圣节文档的作者自己后来又指出“往往新点子首先在Linux中被实现,然后再被搬到其它平台。”
其实这也不是什么新现象。把上述论断中的“Linux”换成“开源项目”,我们可以发现来自开源社区的Emacs、万维网甚至英特网本身,不是追谁的尾巴跑出来的,也没有看到任何庞大的管理开销。现在开源项目不断创新给我们带来的是前所未有的广阔选择空间。随便举个例子,GNOME项目中关于图型界面和对象技术的创新已经受到大量的商业关注。其它例子举不胜举,随便那一天看一下Freshmeat.net就可证明。
该论断还包含的一个更根本的错误,即隐含地假设大教堂模型(或市集模型,或任何其它管理结构)可以有效的产生创新。这一点根本站不住脚。群体并没有更创造性的眼光。即便是市集模式中自愿组成的架构师团体往往也缺乏新点子,更不用说那些需要在权力平衡中考虑自身利益的公司委员会了。相反,创新来自个体。其周围的社会机制能做得最好的就是给予响应——去培育、奖励并严格的测试它们,而不是排挤它们。
所以,不论是软件还是其它任何领域,创新的关键在于如何去激发更多人提供新点子,以及如何不去排挤它们。
显然,假定大教堂开发模式有利于创新,而门槛低、流程通畅的市集模式不利于创新,是很荒谬的。如果创新的起点是一个人加一个点子,那么一个可以使该主意吸引成百上千合作人的环境将不可避免地优于一个为继续工作于该点子而不至于被解职,其主人不得不通过政治性的手腕将其推销到上级管理层的环境。事实是,那些使用大教堂方式机构的创新史表明,来自其自身的创新极为少见。大公司依赖大学研究成果作为其新点子(这也是“万圣节文档”作者对Linux有力地加速和吸收学术研究的不安),或收购那些基于某项创新而成立的小公司。这两种情况,创新均非来源于大教堂文化。恰恰相反,很多以类似方式买来的创新被“万圣节文档”作者所鼓吹“庞大的管理开销”所扼杀了。
上面讲的是负面的例子。这里更应该把一个正面的例子奉献给读者。我建议大家试试下面的方法:
定下一个你可以严守的创新准则。比如“看到了我就知道”就满足这个测试的条件。
再定下任何一个和Linux竞争的商业操作系统,和一个其开发工作进展的最好的消息渠道。同时观察该消息渠道和Freshmeat一个月,每天各自记下两者中你认为是创新的新点子数。三十天后,统计这两个数字。
我写这些文字这天,Freshmeat有22条软件发布信息,其中有3条看起来部分推进了现有技术。这只是Freshmeat上较差的一天。然而,如果哪个读者能在一个月中在某个商业渠道上找出类似的发现,我都会非常吃惊。
当你开始社区建设的时候,你要能够给出一个可行的承诺。你的程序不一定要工作得非常好。它可以是粗糙的、问题多多的、不完整的、缺少文档的,但它不可或缺的是
能运行
能说服潜在的合作者,它可以在可预见的将来进化成真正漂亮的东西。
Linux和fetchmail开放的时候都带有强劲、吸引人的基本设计。许多像我描述的那样来思考市集模式的人正确地认为这一点很关键;于是进而断定在项目领导人身上,高度的设计灵感和聪明不可或缺。
但是Linus的设计来自于Unix,我的最初设计来自于先前的popclient(尽管它后来变化很大,按比例来说比Linux的大的多),那么一个市集风格项目的领导人/主持人真的一定要有杰出的设计天才,还是只需掌握四两拨千斤的方法来利用大众的设计才能呢?
我认为项目主持人能否想出杰出灿烂的设计不是很关键,但绝对关键的是,他必须能够慧眼识别出他人的优秀设计或想法。
Linux和fetchmail项目都显示了这方面的证据。Linus,(像前面讨论过的)尽管不是一个特别有原创性的设计者,却展现了识别优秀设计并把它集成到Linux内核里强大才能。我也已经描述了在fetchmail里的最有力的一个设计思想(SMTP转发)怎样来自于另一个人。
这篇文章的早期读者捧我的场说我容易低估市集项目里原创设计的价值,因为我自己不缺创造性,因而就想当然的习惯了。这话大概有一点点的真实性在里面;设计(而不是编码或调试)确实是我的强项。
但是在软件设计里表现聪明和创造力的问题在于它会形成一种坏习惯——当你应该保持代码稳固和简单的时候,你开始放任地把它们搞得好玩和复杂。我曾经因为犯了这个错误把项目搞砸过,但是我在fetchmail里做到了避开这个错误。
所以我相信fetchmail项目的成功有一部分是因为我克制住了自作聪明的习惯;这(至少)反驳了设计的原创性是市集项目的成功关键。再想一下Linux,假设Linus在开发中试图整出操作系统设计的根本性创新,做出来的内核还会像我们现在的这么稳定和成功吗?
当然一定的设计和编码水准还是必要的,但是我认为几乎每个认真考虑发起一个市集型项目的人已经超出了这个基本要求。开源社区内部的声望机制给人们一种微妙的压力,就是不要发起自己不能坚持下去并搞好的开发项目。迄今为止,这一点似乎工作得很有效。
另外有一个和编程能力无关的技能,我认为对于市集型项目来讲和设计才能一样的重要——甚至可能更重要,那就是一个市集项目的主持人或领导者必须有良好的人际交流技能。
这应该是显而易见的。要建设一个开发社区,你需要吸引人群,让他们对你所做的感兴趣,并且让他们对自己的工作量舒心。要做到这一点,高超的专业技能会起很大的作用,但远远不是故事的全部,你所展现的人格也很重要。
Linus是一个平易的人,让人们喜欢他、想帮助他——这不是巧合。我是个活泼外向的人,喜欢和人群打交道,有着一些现场喜剧演员的直觉和本事——这不是巧合。要使市集模式运行起来,你最好还能有些让别人喜欢你的本领。
开源软件的社会语境
这句话写到了实处:最好的程序因作者日常问题的个人解决方案而开始,因一大批人正好都有这个问题而流行。这把我们带回了第一条经验的内容,用一种或许更有用的方式来表达是:
要解决一个有意思的问题,首先找到一个你觉得有意思的问题。
Carl Harris和先前的popclient如此,我和fetchmail也是如此。但是这点大家已经明白很久了。Linux和fetchmail的历史看来要求我们关心的是另外一个有意义的话题,即在下一个阶段——在用户和合作者形成了庞大活跃的社区时的软件的进化。
在《人月神话》中,Fred Brooks表述了程序员的时间是不能简单进行叠加的;添加开发人员的做法只能使得已经延期的软件项目更为延期。像我们前面提到的,他论述了项目的复杂程度和通讯成本按开发人员数目的平方增加,而业绩仅以直线增加。Brooks法则被广泛的认同为真理。但在这篇文章里,我们已经探讨了开源开发过程在好几处破除了Brooks规则的前提假设——而且事实证明,如果Brooks法则统领一切,Linux就不可能发生。
以事后之明来看,Gerald Weinberg的经典《计算机编程的心理学》对Brooks法则作出了一个关键的修正。在他对“无私编程”的讨论里,Weinberg注意到一些地方的开发人员不对自己的源码画地为牢,而是鼓励他人在其中寻找错误和指出改进的余地——在这些地方,改进比别处进展得明显快很多。(最近,Kent Beck的“极度编程”技术——把编程者配对让他们互相监督——或许可以看作是强制这一效果的尝试。)
Weinberg的用词可能让该结论未获得应有的认可——把网络黑客说成毫无私心未免让人莞尔。但是我认为他的结论在今天看起来比以往任何时候都更让人信服。
市集模式,借助“无私编程”效果的极致动力,有力减弱了Brooks法则的效果。Brooks法则背后的原理没有被推翻,但是庞大的开发者群体和廉价的通讯所带来的前所未有的准线性可扩展性,开始将其效果淹没。这就像牛顿式的和爱因斯坦式的物理之间的关系——旧的系统在低能量下仍然有效,但当质量和速度变得足够大的时候,就得到了如同核爆炸或Linux那样的惊奇。
Unix的历史应该使得我们对研究Linux的结果(和我在小规模上有意拷贝Linus的方法所实验确认的结果)有了心理准备。这是说,虽然编程基本上仍是一种个人封闭的活动,真正高超的程序来自于借助整个社区的注意力和脑力。一个在封闭的项目中只使用自己脑力的开发者,将会输给一个知道怎样创造一个开放和进化式环境的开发者、一个知道从中吸收成千上万人的探索设计空间的反馈、编码贡献、臭虫检测和其它改进的开发者。
但是有几个因素阻止了传统的Unix世界把这个方法发挥到极致。一个是各种软件许可、贸易秘密和商业利益的法律限制。另一个(回头来看)是互联网还不够好。
在便宜的互联网普及之前有过一些地域性的紧密团体,在文化上鼓励Weinberg的“无私编程”,使得一个开发者可以容易地吸引到一批有水平的“军师”和合作者。贝尔实验室、麻省理工的人工智能和计算机实验室、伯克利加州大学——这些成为了传奇性的和依然强劲的发明家园。
Linux是第一个有意识地并成功地把全世界当作智囊库使用的项目。我不认为Linux的孕育期与互联网的诞生重叠是一个巧合。Linux在1993-1994之间网络服务业起步和对互联网的主流兴趣爆发的同期脱离了它的婴儿时代也不是巧合。Linus第一个学会了如何运作普及的网络连接所促成的新规则。
虽然便宜的互联网是Linux模式进化出来的必要条件,我觉得它还没有构成充分条件。另一个关键的因素是一种领导风格和一套合作制度的建立——使得开发者可以吸引合作者并在这个媒介中获取最大程度的收益。
但什么是这种领导风格、什么是这些制度呢?它们不可能是基于权力关系的——就算是可能,强制性的领导不会产生我们所看到的成果。在这个题目上,Weinberg很恰当地引用了19世纪俄罗斯无政府主义者Pyotr Alexeyvich Kropotkin的自传《一个革命者的回忆录》:
成长于一个农奴主的家庭,我进入社会后,像我那个时候所有的年轻人一样,很是相信领导、命令、训斥、惩罚等等的必要性。但是在早期我不得不管理重要的事业和对付[自由的]人们的时候,在每个错误都会立刻导致严重后果的时候,我开始领悟到按指令的原则行事与按共同理解的原则行事之间的区别。前者在阅兵式中运行得令人崇敬,然而就真实的生活而言,它却一文不值;而且目标只有通过许多共同意志的竭诚努力才能实现。
这个“许多共同意志的竭诚努力”正是Linux这种项目所要求的;在这个我们叫作互联网的无政府主义者天堂里,对志愿者们行使“指令的原则”事实上是不可能的。要有效地运作和竞争,想要领导协作性项目的黑客们不得不学会怎样按照Kropotkin的“共同理解的原则”里模糊地提出的模式,招募和激励活动中的相关社区。他们必须学会使用Linus法则。[注:SP]
[注:SP]除了Kropotkin对“指令的原则”的批判和Linus法则,关于社会组织的内在机制问题还存在更广泛的讨论。其中包括另一个众所周知的软件工程定理——Conway定理,其通俗地表述就是“如果分四个小组去开发一个编译器,得到的会是个四遍编译器”。其最初的描述更为一般化:“软件产品的体系结构必然是其开发机构内在通讯结构的缩影”。简单说来就是:“方法决定结果”,或者“过程演变为产品”。
同样有必要指出的是开源社区存在多层与其功能吻合的组织结构,网络无处不在。除了英特网本身,成员间还存在一个分布式、松耦合的点对点网络,具有多重冗余性和可降级。在这两个网络中,节点的重要性只取决于外界愿意和他合作的程度。
其中的点对点网络对于开源社区惊人的生产力至关重要。以下的SNAFU法则又进一步发展了Kropotkin解释权力关系的尝试:“真正的交流只能在平等的个体间存在;下级从上级得到更多回报的可靠方式是通过顺耳的谎言而非事实真相。”团体项目的创新最终依赖于真正平等的交流,因此,会受到权力关系的严重阻碍。开源群体不存在权力关系的事实从反面告诉了我们权力关系导致的修正错误的可怕代价、生产力的底下和机会的丧失。
更进一步,SNAFU法则预测在基于权力的机构中,随着流入决策层的顺耳谎言越来越多,决策者和现实之间的脱节将越来越严重。这个结果在传统软件开发中扮演的角色显而易见,就是下级极力掩饰、无视及淡化问题,做出的软件将成为灾难。
我早先引用了“神庙效应”作为Linus法则的一个可行的解释。但是不可避免自我呈现出的一个更有力的类比是生物学和经济学中的自适应系统。Linux世界在很多方面都接近自由市场或生态系统:其中的自私个体都试图将自身的利益最大化,但在这个过程中形成了一个自我纠正的自发秩序,其周密度和有效性不管多少中央计划都无法与之媲美。那么,这里就是寻找“共同理解的原则”的地方。
Linux黑客们最大化的这个“利益函数”不是经典经济学上的,而是他们自我的满足和在其他黑客中的声望这些摸不到的东西。(有人或许可以把他们的动机称为“利他的”,但这忽略了利他主义自身对利他主义者就是一种自我满足这一事实。)这样运作的自愿者文化其实也很常见;我长期参与的另一个就是科幻粉丝族。不像黑客族,他们早就明白地认识到了“自我彰显”(ego-boosting,或在其他粉丝中增强个人的声望)是志愿者活动背后的基本动力。
Linus,通过成功地把自己定位为一个主要由其他人来开发的项目的看门人,以及培养对这个项目的兴趣直到它可以自我维持,表现了对Kropotkin“共同理解的原则”的敏锐把握。上述对Linux世界的类经济学视角使得我们能够看到这种理解是如何应用的。
Linus方法其实就是利用“自我彰显”来创造有效的市场。是“自我彰显”把单个黑客的自我利益最大化行为紧密地和那些只有通过持续合作才能达到的困难目标绑在一起。通过fetchmail项目,我证明(尽管在小规模上)可以通过复制他的方法产生出好结果。或许我甚至做得比他更有意识和更系统一点。
很多人(尤其是在政治上不相信自由市场的那些)会以为自我驱动的个人主义者们形成的文化会是支离破碎、占山为王、低效浪费、秘密诡异和充满敌意的。但是这个设想显然被(只举一个例子)Linux文档的惊人的广度、深度和质量所证伪。既然程序员痛恨写文档是众所周知的,上述事实简直是奇迹。Linux黑客们是怎样生产出这么多文档的呢?显然,Linus自我彰显的自由市场在产出优良的、协同的行为上优于商业软件厂商的大笔资金驱动的文档工厂。
Fetchmail项目和Linux内核项目都表明,通过适当地表彰众多其他黑客的成就,一个有力的开发/协调者可以使用互联网来收获拥有许多合作者的好处,而不至于让项目陷入嘈杂的混乱。所以针对Brooks法则,我反过来建议下面的一条:
如果开发的协调者有一个至少和互联网一样好的通讯媒介,而且懂得如何不通过强迫来领导,那么多个头脑不可避免地优于一个头脑。
我认为开源软件的未来会更多地属于那些懂得如何运行Linus规则的人们,属于那些告别大教堂来拥抱市集的人们。这不是说个人的远见和才华不再重要;而是,就我看来开源软件的前沿会属于那些开始于个人的远见和才华,然后通过有效地建设相关志愿者社区来扩展放大的人们。
或许这不仅是开源软件的未来。要对付一个问题,没有闭源的开发者可以比得上Linux社区所能驱动的才能之众。极少有人甚至能雇得起那些对fetchmail作出了贡献的200(1999:600, 2000: 800)多人!
也许,开源文化最终胜利的原因不是因为合作在道德上正确或软件“劳役”在道德上错误(假设你相信后者,Linus和我都不),而只是因为开源社区可以在一个问题上投入多几个数量级的技术工时。闭源世界无法赢得这场进化式的军备竞争。
关于管理和马其诺防线
原始于1997年的《大教堂和市集》文章以上述预见结束——程序员/无政府主义者的幸福网络群体胜出并压倒了传统闭源软件的阶层化世界。
然而,很多怀疑者并不信服;他们提出的问题值得我在这里做出回应。多数对市集模式的反对归结到以下观点:市集模型的鼓吹者们低估了传统管理促进生产率的效果。
老脑筋
反正我没想到。在1993年初Linux引起我的注意的时候,我已经在Unix和开放源代码开发领域做了十年了。我是80年代中期最早的GNU开发者之一,已经在网上发布了相当一部分软件,正在开发或协助开发好几个直到今天都在广泛使用的软件(nethack,Emacs的VC和GUD模式,xlife和其它)。我觉得我很懂行了。
Linux颠覆了许多我以为我懂的东西。当时,我已经宣扬小而专的工具、快速建模和演进式开发等Unix理念多年了。但我也相信项目到了一定复杂程度后就需要更集中地按事先计划管理。我相信最重要的软件(操作系统和Emacs之类的大型工具)需要像大教堂一样来搭建:遗世独立的圣人巨匠们牵尺引斤琢之磨之;时候不到beta版不出。
Linus Torvalds的开发风格(尽早尽多的发布,委托所有可以委托的事,对所有的改动和融合开放)令人惊奇地降临了。这里没有建造大教堂的安静和虔诚;Linux社区更像一个充满不同议程和方法的嘈杂的市集(Linux归档站点就是绝好的例子,任何人的作品都接收)。然而,一个统一稳定的系统就象奇迹一般从这个市集中产生了。
这种设计风格不但可行,而且工作得很好,对我无疑是一个巨大的震撼。在我的摸索过程中,我不仅效力与个别项目,而且努力去理解为什么Linux世界没有在混乱中分崩离析,而是以大教堂建造者们难以想像的速度茁壮成长。
到1996年中,我想我开始理解了。我有了一个测试我理论的完美机会,一个我可以有意识地用市集风格来运行的开源项目。我这样做了,结果非常成功。
这里讲述的就是这个项目的故事。我将借它来提出一些开源软件有效开发的精髓。它们并非全部源自Linux世界,但我们会看到它们如何在Linux世界中得到印证。如果我是正确的话,它们会帮助您准确理解是什么使Linux社区成为优秀软件的源泉——或许,它们还会帮助您变得更加高效。
邮件必须得通过
从1993年以来,我负责宾州西切斯特一家提供免费网络服务的小公司CCIL的技术工作。我协同创建了CCIL,并写了我们独家的多用户论坛软件——您可以用telnet连接locke.ccil.org来试一下。今天它在三十条线上支持近三千名用户。这份工作允许我通过CCIL的56K线路每天二十四小时上网——其实,这份工作事实上要求这一点!
那时,我已经习惯使用即时的互联网邮件。我发现不时地要telnet登录上公司服务器“locke”检查邮件很烦人。我想要的是把我的邮件传送到我家里的个人机器“snark”上,这样我可以在邮件到达的时候得到通知,并使用我自己的软件来处理它。
这里互联网的默认邮件输送协议SMTP不适用了。SMTP是为全时在线的机器设计的,而我的个人机器并不总在网上,也没有一个固定的IP地址。我需要一个程序在我拨号上网期间连到服务器上,把要下到本地的邮件取回来。我知道有这种工具存在,且多数使用一个简单的叫做POP的协议。现在常用的客户端邮件软件都支持POP,但那个时候,我的邮件阅读器不支持它。
我需要一个POP3的客户端软件,所以我就跑到网上找了一个。事实上,我找到了三四个。其中的一个我用了一段时间,但它少了一个看起来很明显的功能:修改到达邮件的来信地址以便正确回信。
问题是这样的:假设“locke”上一个叫“joe”的人给我发了信。如果我把信取到“snark”上,然后试图回复,我的邮件程序会高高兴兴地努力把回信发送给“snark”上一个并不存在的“joe”。通过手工修正邮件的回信地址很快就变成了一件很痛苦的事。
显然这该是电脑替我做的事。但是现有的POP客户端软件没有一个会做!这给我们带来了第一个教训:
每一个好软件的起因都是挠到了开发者本人的痒处。
这或许应该是很显然的(一直有箴言道“需要是发明之母”),但软件开发人员太过经常地在那些他们既不需要也不喜欢的程序上消磨时日、换取工资。但在Linux世界不是这样子的——这或许解释了为什么Linux社区中产生的软件平均质量这么高。
那么,我立马儿投入到了一轮疯狂的编程中来写一个和现有POP3客户端竞争的软件了吗?不是!相反,我仔细检查了我拿到手的那些POP程序,自问“哪一个离我要的最接近?”因为:
好的程序员知道写什么。伟大的程序员知道改写(和重复使用)什么。
虽然我不自封伟大,但我努力模仿伟大的程序员。伟大者的一个重要特点是建设性的懒惰。他们知道你需要的是结果而不是过程,而且从一个好的部分方案开始总比从零开始要容易得多。
以Linus Torvalds为例,他实际上没有试图从零开始写Linux。相反,他的代码和主意开始于PC机一个小小的类UNIX系统MINIX。最终所有Minix的代码都被拿掉或重写了——但在起步的阶段,Minix提供了那个最后成为Linux的新生儿成长的脚手架。
遵循同样的精神,我出发去寻找一个已有的、写得过得去的POP程序来作为开发的基础。
UNIX世界里的源代码共享传统一直对代码再用很友好(这也是为什么GNU项目尽管对
UNIX很有成见,还是选择了UNIX作为其基本操作系统)。Linux世界几乎把这种传统发挥到了技术上的极限;有上万亿字节的开放代码可供获取。所以花点时间在Linux世界里找个别人“差不多够好”的程序,比其它任何地方都更有可能找到。
我就找到了。加上我以前找到的,我的第二次搜索有了九个候选对象:fetchpop、PopTart、get-mail、gwpop、pimp、pop-perl、popc、popmail和upop。我第一个选用的是欧松宏(音,Seung-Hong Oh)的“fetchpop”。我把我改写邮件头的功能加了进去,并作了其它一些改进。松宏后来把这些加进了他的1.9版本。
然而几个星期以后,当我碰到了Carl Harris的“popclient”代码时,我发现我遇到了一个问题。尽管fetchpop有一些很好的新主意(例如它的后台daemon模式),它只能处理POP3协议,而且程序代码写得比较业余(松宏当时是个聪明但是缺少经验的程序员,这两个特点都有显示)。Carl的代码好一些,很专业和稳固,但他的程序缺几个重要的而且难实现的fetchpop里的功能(包括我自己写的那些)。
继续用fetchpop还是转换到popclient上来?如果转换的话,我得扔掉我已经写好的那些代码来换取一个好一些的开发基础。
一个实用的转换动机是对多种协议的支持。POP3是服务器端POP协议中最常用的,但不是唯一的。Fetchpop和那一个竞争对手都不支持POP2、RPOP或APOP,而且为了好玩,我那时已经有了添加IMAP(最新设计的、最强大的POP协议)的模糊想法。
但我还有一个更理论上的原因来认为转换也是个好主意。这是我远在Linux之前就学到的。
“计划扔掉一个;无论如何你都会扔掉一个的。”(Fred Brooks《人月神话》第11章)
或者换句话说,直到你第一次实现一个方案之前,你常常并没有真正理解你的问题。第二次呢,或许你已经学到了如何把它做对。所以,要想把事做对,你得准备至少重来一次[注:JB]。
[注:JB]在《编程珠玑(Programming Pearls)》中,知名的计算机科学大师JonBentley对Brooks的上述文字有如下评注:“如果你计划要扔掉一个,你也会扔掉第二个”。他说的完全正确,Brooks和Bently不仅仅指出第一次尝试往往会错,而且指出用新的方法重新开始往往比挽救一堆乱代码来得快捷。
好吧(我对自己说),对fetchpop做的修改算我的第一次吧。于是我转换了。
在1996年6月25日我给Carl Harris发送了我写的第一批popclient补丁后,我发现他一段时间之前就基本上对这个项目失掉兴趣了。项目的源代码有些陈旧了,小臭虫们流连不去。我有很多要修改的东西;我们很快同意:理所当然,我该把整个项目接手过来。
在我没有觉察的时候,这个项目升级了。我不再是试图给一个现有的POP客户端程序做些小补丁。我开始负责维护整个程序,而且我知道我脑子里冒着的新主意可能会导致一些主要的变动。
在一个鼓励代码共享的软件文化中,这是一个项目进化的自然方式。我在实践下面这个原理:
如果你有正确的态度,有意思的问题会自动找上门。
Carl Harris的态度甚至更重要。他懂得:
当你对一个项目失去兴趣时,你最后的职责是把它交给一个称职的继承者。
尽管Carl和我从来没有讨论过这一点,我们知道我们的共同目标是找出问题的一个最好解决方案。我们唯一的问题是我能否证明我的能力。一旦我作到了,他优雅而迅速地作了交接。我希望当这一天轮到我的时候,我也能做得同样出色。
用户的重要性
就这样,我继承了popclient。同样重要的是,我继承了popclient的用户群。拥有用户是件美好的事情。他们的存在不仅证实了你的工作正在满足一种需求,而且证实的你做的不错。通过适当的培养,他们还可能成为你的开发伙伴。
很多用户也是黑客,这是UNIX传统中的另一个强项,而Linux把它发展到了快乐极致。因为可以得到源代码,他们可以是有效的黑客。这一点对缩短调试时间会非常有帮助。给上一点点鼓励,他们会帮助诊断问题,提出建议和补丁,并以你一个人不可企及的速度帮助改进代码。
把用户看作合作者是通往快速改进代码和有效调试的最佳通道。
这一点所蕴藏的能量很容易被低估。事实上,直到Linus Torvalds给我们演示之前,开源世界里几乎所有人都严重低估了它如何随用户数目而增长潜能,如何随系统的复杂度增大而更显威力。
事实上,我认为Linus最聪明、最有影响的手笔不是Linux内核本身,而是发明了Linux的开发模式。有一次当我当面向他表达这个观点时,他微笑了,安静地重复了他经常说的一句话:“我基本上是一个很懒惰的人,喜欢在其实是别人做的事情上领取荣誉”。象狐狸一样懒惰,或者象Robert Heinlein[译注:著名科幻作家]笔下的一个著名角色:太懒惰而不会失败。
回头来看,Linux方法和成功的一个先例是GNU Emacs的Lisp库和Lisp代码档案。与Emacs C核心和大多数的其它GNU工具的大教堂建造风格相反,Lisp代码群的进化是活跃的、多由用户驱动的。点子和原型经常要重写三四次才能达到一个稳定的最终形式。象Linux那种通过互联网的松散协作也很频繁。
确实,我自己在fetchmail之前最成功的一次编程可能是Emacs的VC(版本控制)模式。那是类似Linux一样与其他三个合作者仅通过电子邮件交流的一次合作。三个人中我至今只见过一个(Richard Stallman,Emacs的作者、自由软件基金会的创始人)。VC是Emacs中SCCS,RCS和后来CVS模式的前台;Emacs借此提供“单击式”的版本控制操作。它是由一个别人写的小小的、粗糙的sccsl.el模式演进而来。VC开发的成功也是因为不象Emacs本身,Emacs Lisp代码可以快速地通过多轮“发行/测试/改进”的循环。
Emacs的故事不是唯一的。其它软件也有这种双层的构架和双层的用户群:核心用大教堂模式;工具箱用市集模式。其中的一个是MATLAB,一个用于数据分析和可视化的商业工具。MATLAB和其它类似架构产品的用户一致认为,产品的开放部分——有一个巨大多样的用户群可以对其推敲的地方——才是动力、热情和创新的所在。
早发布、常发布
早发布和频繁发布是Linux开发模式中关键的一个部分。以前多数开发者,包括我,都认为这对复杂项目来说是个坏办法,因为早期版本几乎是问题版本的同义词,而你不想这么早就消耗完用户的耐心。
这个观点也促使人们普遍采取建造大教堂式的开发。如果首要的目标是尽量让用户少遇到臭虫,那么你应该六个月甚至更久才发布一个版本,在两次发布之间象狗一样拼命去调试。Emacs的C核心就是这样开发的。Lisp库实际上不是——因为在自由软件基金会所辖之外还有其它活跃的Lisp存档,提供独立于Emacs发布周期的新版本和测试版本。
那时最重要的Lisp库——俄亥俄州立大学Emacs Lisp档案——已经超前具有了今天Linux大型档案的管理精髓和许多功能。但是我们中很少有人深考过我们在做什么,以及俄亥俄档案的存在本身说明了自由软件基金会大教堂开发模式的哪些问题。在1992前后,我曾努力要把一大批俄亥俄代码合并到Emacs Lisp的官方库,但遇上政治性的麻烦,结果非常不成功。
一年以后,Linux影响逐渐扩大。显然Linux的开发者拥有一些不同的但是远远更为健康的东西。Linus的开放性开发政策正与建造大教堂的方式相反。Linux的互联网档案枝蔓繁衍,多个发行版在坊间流传。而所有这些都是由Linux内核前所未闻的发布频率所驱动的。
Linus在以最有效的合作者方式来对待他的用户:
早发布、常发布,听取用户的意见。
快速发布、采纳大量用户反馈,并不怎么算Linus的创新(Unix世界历来就有这种传统)。他的创新之处是把这个办法升级到了与所开发系统复杂性相匹配的规模和强度。在早期(1991年左右),我们不是没听说过他一天发布不止一个新的内核版本!因为他比任何人都努力地培养合作开发群体、促进网上合作。他的办法生效了。
但它是怎样生效的呢?难道只有Linus Torvalds的独特天才才能实现?
我想不是的。Linus当然是个骨灰级黑客。有几个人能从零开始开发出一个企业级操作系统的内核?但是Linux并不代表任何概念上的重大突破。Linus不是(至少还没有成为)象Richard Stallman或James Gosling(Java之父)那种设计和创新的天才。在我看来,Linus更象是工程和开发的天才,有着避开臭虫和死胡同的第六感官和找到从一点到另一点最快捷径的本领。确实,整个Linux内核透露着这种特质,反映了Linus本质上保守和一切从简的设计方法。
如果快速发布和淋漓尽致的利用互联网媒介不是偶然,而是Linus对最快捷径的天才工程洞察力的有机部分,那么他试图最大化的是什么?他试图从这个机制释放出一种什么样的力量?
这样一问,答案一目了然。Linus在不断地激励和奖掖他的黑客用户们——激励来自于在参与中得到的自我实现,奖掖来自于看到他们自己工作的持续(甚至每天)进步。
Linus直接瞄准了调试和开发力量的最大化,即使牺牲程序的稳定性或因某个修正不了的严重问题导致丧失用户也在所不惜。似乎Linus相信:
足够多beta测试者和合作开发者会让任何问题快速显形并很快被解决。
或者通俗一点,“只要眼球足够多,所有臭虫都好捉”。我称之为“Linus法则”。
对上述法则,我最早的表述是每个问题“都会有某个人能解决”。Linus有异议:理解和解决问题的人不一定甚至一般不是第一个发现问题的人。“一个人发现问题”,他说,“另一个人把它搞明白,而且我会作证说发现问题这一步更困难一些。”这是个重要的纠正;在下一节具体研究实际调试时我们会看到为什么。但是关键一点是,发现和解决问题这两个步骤一般都会很快完成。
我认为Linus法则中包含有大教堂模式和市集模式的关键区别。在大教堂式的编程观念中,臭虫和开发上的问题是复杂、困难和深奥的,要几个人全心全力几个月的投入才有把握已经把它们清理干净的,所以需要很长的发布周期;一旦等候已久的版本不够完美,失望就在所难免。
恰恰相反,在市集式的观念中,你预设臭虫都是简单的问题——至少在上千个共同开发者热心地琢磨每一个新版本的情况下,它们会很快就变简单了。相应地,你频繁发布来得到更多的纠错。作为一个附加效应,偶尔出个大勺子的后果也没有那么严重了。
这就是了。这也就够了。如果“Linus法则”是错误的,那么象Linux内核这样复杂的系统,经过了那么多人的敲打,应该在某一时刻已经在不曾预见的恶性互动和深藏不露的问题的重压下崩溃了。如果它是正确的,它足以解释Linux相对较少的问题,和数月甚至数年以上的持续运行时间。
或许这不该是如此一个意外。社会学家们多年前就发现了一大群同样内行(或同样白痴)观察者的平均预测要比其中随机选择一个人的预测可靠得多。他们称之为“神庙效应”。看来Linus显示了这一点甚至适用于调试一个操作系统——甚至是内核级的复杂程度,“神庙效应”也可以简化开发。
Linux情形中对“神庙效应”有帮助的一点是,任何一个项目的参与者都是自我选择的。一个早期评论指出,对Linux的贡献不是来自于一个随机的人群;他们都有足够的兴趣来使用这些软件、研究其机理、并试图解决所遇到的问题,而且真正给出显然可行的解决办法。经过了这些筛选的人一般都会有可以贡献的真才实料。
Linus法则也可以表述为“调试是可并行的”。尽管调试者们需要一个人来通讯协调,调试者们之间并不需要多少的协调。添加开发人员所带来的平方级复杂度和管理成本在这里不适用。
理论上因为调试者重复做功而导致的效率损失在Linux世界中似乎从来都不是问题。“早发布、常发布”策略的一个后果就是通过快速公步反馈修补来把重复做功最小化。
Fred Brooks(《人月神话》作者)甚至作过一个相关的非正式评论:“一个广泛使用的程序的维护费用一般是它开发成本的40%以上。令人惊奇的是,这个费用受用户数强烈影响。用户越多,发现的问题也越多。”
用户越多、发现问题越多是因为检验程序的角度也越多。当用户同时是合作开发者时,这个效应放大了。在检测问题的过程中,每个人都有一些不同的观察方法和分析工具,从不同角度逼近同一问题。“神庙效应”似乎正是因为这种多样性而有效。在调试程序这个特定的环境下,这种多样性也利于减少重复做功。
所以从开发者的角度来讲,增加更多的beta测试者不见得会减少当前最大问题的复杂程度,但会增加某个人的工具箱正好适用于该问题的几率——这样对这个人来说,这个问题其实很简单。
Linus在此之外还留有一招。如果可能存在大的“臭虫”,Linux内核的版本编号允许潜在用户选用老一点的“稳定”版本,或冒“臭虫”之险以求“实验”版本的最新功能。多数Linux黑客还没有系统地模仿这一招;但他们或许应该去模仿。给出这个选择使得两种版本都更有吸引力。[注:HBS]
[注:HBS]Linux内核的稳定版和实验版之分除了分担风险目的之外,还可针对解决发布期限问题。事实表明,当开发人员同时面临死板的功能列表和发布期限时,软件的制作质量明显降低。哈彿商学院的Marco Iansiti和Alan MacCormack指出放松其中任何一个限制均可产生可行的计划,在此表示感谢。
稳定版内核采用的是放松功能列表,但限制发布期限的策略。稳定版内核的维护者Alan Cox定期发布新的稳定版,但是不承诺何时必须解决哪个问题或加入哪个开发版新功能。
实验版内核刚好相反,采用的是放松发布期限,但固定功能列表(所谓“做完了再叫醒我”)的策略。De Marco和Lister引用的研究结果表明该策略不仅导致高质量的软件,并且平均比“保守”或“激进”开发计划都可带来更短的发布时间。
2000年初我开始怀疑我在本文先前的版本中低估了这种“做完了再叫醒我”的策略对提高开源项目生产力和质量的重要性。1999年GNOME项目为赶1.0版导致的负面影响表明,即便是开源项目,为赶进度而发布尚未成熟的代码也会严重影响到软件质量。
很有可能我们会发现,开发过程的透明性,连同“做完了再叫醒我”以及开发者的自我选择,可能是推动开源项目质量的三个同等重要的作用力。
要多少个眼球来驯服复杂度
在整体上观察市集风格很大地加速了调试和代码进化是一回事,在微观、日常的开发者和测试者行为上来准确理解怎样和为什么是另一回事。在这一节(写在原始文章的三年以后,采纳了读了本文、又对照了自身的很多开发者们的意见),我们来实打实地看一下其背后真正的机制。不喜欢技术的读者可以安全地跳到下一节。
理解这个问题的一个关键在于认识到以下重要的事实,那就是对源码知之甚少的用户所递交的问题报告往往并没有多少用处。这些用户一般只会报告表面症状;他们往往忽略了软件的运行环境,所以这些提交的报告不但会漏掉关键的背景数据,而且极少包括重现问题的步骤。
这里更深层的问题是测试者和开发者对程序的不同理解。测试者从外往里看,而开发者从里往外看。在封闭源码的开发模式中,他们都被卡在各自的这种角色里了,往往各说各的话,同时又觉得和对方交流非常困难。
开源开发打破了这种束缚,测试者和开发者基于源代码很容易建立起统一的模型,就之进行有效地交流。仅仅描述外观症状的问题报告和直接联系到基于源码抽象模型的报告对开发者的帮助是截然不同的。
如果能够在代码层给出一个对出错条件哪怕是不完整的提示性的描述,大多数臭虫在大多数情况下都是容易捉到的。当某个beta测试员指出“在某某行有一个边界问题”,或者只是“在某某条件下,这个变量溢出”,对这些代码的一个快速扫描常常足以锁定出错的准确模式并搞定一个修补办法。
所以,如果beta测试者和核心开发者都对源代码心里有数,双方的交流和合作会得到大大增强。这意味着核心开发人员的时间会节约下来,即便合作者人数众多。
开源方法另一个节约开发者时间之处来自于开源项目的典型通讯结构。我在上面用到了“核心开发者”一词;这反映了项目核心(一般很小;一个核心开发者很平常,一到三个很典型)和beta测试人员、补丁提供人员组成的项目外沿(经常上百人)的区别。
传统软件开发结构需要解决的根本问题是Brooks法则:“在延期的项目添加程序员只会延期更久”。普遍来讲,Brooks法则认为,随着开发人员数目的增加,项目的复杂程度和通讯成本按平方增加,而业绩仅以直线增加。
经验表明,臭虫大多集中在不同人写的代码的界面上;而一个项目的通讯协调成本一般按照人与人之间的交流渠道数量增加。这是Brooks法则的基础。也就是,问题随开发者之间通讯路径数目的增加而增加,而后者与开发者数目是平方关系(更准确地说,遵从公式N*(N-1)/2,这里N是开发者的数目)。
Brooks法则的分析(以及它引起的开发团体中对人数过多的恐惧)基于一个潜在的前提:项目的通讯结构必须是一个完整的图,每个人都与其他所有人交接。但是在开源项目中,外沿开发者做的实际上是平行分离的子项目,彼此交接甚少;代码变动和臭虫报告都流经项目的核心,只有在小小的核心团体中,才需要完整的Brooks通讯开销。
由于一个错误常常可以产生多个不同的症状,在用户使用习惯和环境细节不同时有不同的表现,这使得源代码层次上的问题报告往往非常有效。这类错误一般正是那些复杂和微妙的臭虫——那些最难重现并难以用静态分析捕捉的,并在软件中制造长期问题的祸根(比如动态内存管理错误或随机的中断窗口错误)。
一个测试者提供的对此类错误尝试性的源码级诊断(例如:“看起来第1250行的信号处理部分存在一个窗口”或者“你在哪里把缓冲区清零了?”)可能会成为对这些部分代码熟视无睹的开发者同时解决七八个不同症状的关键线索。此时,往往无法判定症状和臭虫的对应关系,但是多发布使得我们无须去一一了解。其他用户很快就会发现问题是否不再出现。很多情况下,源码级的问题报告使得症状无须明确被哪个补丁解决而自然消失。
复杂的多症状错误也常常会有从表面症状联系到内在臭虫的多个跟踪途径。一个特定的开发者或测试者所能追寻的跟踪途径可能取决于这个人的具体环境细节,也很可能随着时间的改变发生不便预测的变化。实际上,每一个开发者和测试者在寻找一个症状的病原时都是在检查该程序的状态空间的一个“半随机”集合。臭虫越微妙越复杂,此人技能和所追寻空间相关的保证就越小。
对于简单的容易重现的臭虫,那么,重音要放在“半”上面而不是“随机”上面;此时,调试技能和对代码、框架的熟悉程度是最重要的。但对于复杂的臭虫,重音就要放在“随机”上面。在这种情况下许多人同时追踪要比少数人串行追踪有效的多——即使这少数人的技能水平高的多。
要是从不同的表面症状挖掘到臭虫的跟踪途径难度不一、难以从观察症状来预测的话,并发追踪的效果就会非常之明显了。一个持续追踪这些路径的开发者在开始可能会遇到一个简单的路径,也同样可能会遇到一个复杂的路径。另一方面,试想有许多人在快速发布下平行地来检查这些追踪路径,那么其中某人很可能会碰上最容易的路径,在短的多的时间里解决这个问题。维护项目的人会看到这个,发行一个新版本;其他在更困难的路径上追踪同一个臭虫的人们就可以在花费太多时间之前停下来。[注:RJ]
注:RJ]给我提供不同难度跟踪途径的读者推测此类多表症问题跟踪的难易程度呈“指数”分布(我把其理解为高斯或泊松分布,且同意他的看法)。如果有实验数据可以给出类似的分布曲线,将会非常有价值。如果此曲线与等概率分布的平行线差别甚远,那么即使在一个开发者的情况下,也应该模仿市集模式,采取限定在一个表症上跟踪时间的策略。坚持有时不一定是件好事……
玫瑰何时不再是玫瑰
研究了Linus的作法并形成了一个它何以成功的理论后,我决定在我的新项目(当然没有Linux那么复杂和宏伟)里有意识地测试这个理论。
但我做的第一件事是把popclient重组和简化了许多。Carl Harris的代码实现得很好,但是有一种在C程序员中常见的多余的复杂。他把代码放在了中心位置,数据结构作为辅助。结果代码很漂亮,但是数据结构设计得潦草甚至丑陋(至少按我这个LISP老手的标准来看)。
然而,除了改进代码和数据结构设计以外,我的重写还有另一层目的,那就是把它进化成一个我完全理解的东西。要是你不完全理解一个程序,维护起来可不是好玩的。
于是在最初的一个月左右,我只是在按照Carl的章程做事。我作的第一个重要改变是添加了IMAP支持。我实现这点的方法是:把协议的具体部分做成插件形式,用一个通用的驱动来根据不同协议调用不同插件的方法表(分别针对POP2、POP3和IMAP)。这些变动都示范了下面这个程序员们应该记住的通用原则(尤其像C这种本身不支持动态数据类型的语言):
聪明的数据结构和愚蠢的代码要比反过来好的多。
Brooks的第九章:“给我看你的流程图而隐藏你的数据结构,我会继续糊涂着。给我看你的数据结构,我一般就不需要你的流程图了;它们将显而易见”。经过三十年的文化和术语的变迁,这个观点仍然正确。
这时(1996年9月初,大约开工后六个星期),我开始想这个程序大概该换个名字了——它毕竟不再仅仅是一个POP客户端软件。但是我在犹豫,因为在设计上还没有什么真正的新东西。我的popclient版本还需要发展出它自己的特征。
当popclient学会了怎样把取到的邮件再通过本机SMTP端口转发的时候,这一点迅速改变了。我过一会儿再细谈这个。但是首先:我说过我决定用这个项目来测试我关于Linus的成功理论。(您也会问)我是怎样做的呢?在以下方面:
我早发布和常发布(几乎从未低于十天一次;高强度开发的时候,一天一次)。
我把每个和我交流有关fetchmail的人加进了我的beta测试名单。
每当我发布一个版本,我就给beta名单发送一个内容丰富的通告,鼓励大家参与。
我听取beta测试者的意见,在设计上征求他们的看法,当他们送交补丁和反馈时给予鼓励。
这些简单的办法立杆见影。从项目一开始,我就收到很多那种多数开发者梦寐以求的高质量的臭虫报告,经常还附带了好的补丁。我收到过深思熟虑的评论、粉丝的邮件,还有高明的功能性建议。这说明:
将你的beta测试者作为“最有价值资源”来对待,他们就会成为“最有价值资源”。
Fetchmail庞大的beta测试名单(fetchmail-friends)成为衡量其成功的一个有意义的指标。在我最近一次修订这篇文章的时候(2000年11月),它有287名成员,而且每个星期在增加两三名。
实际上,当我在1997年5月下旬改写的时候,我发现这个名单由于一个有意思的原因,从它近300的巅峰开始流失成员了。一些人要求我把他们从名单中去掉,因为fetchmail对他们来讲近乎完美,他们再也不需要阅读这个邮件列表了!或许这是一个成熟市集风格项目正常生命周期的一部分。
Popclient变成了Fetchmail
当Harry Hochheiser把他转发邮件到客户机SMTP端口的草稿程序发给我的时候,这个项目的真正转折点来临了。我几乎马上意识到,这个功能会让其它所有的邮件递送模式成为历史。
许多星期以来,我一直在按照原来的架构改进fetchmail,但总有些不舒服的感觉。我感觉目前的接口设计还可以用,但是不够干净漂亮,而且到处是微不足道的选项。把收取的邮件直接输出到一个邮箱文件或转到标准输出里的选项尤其让我心烦,但我想不出为什么。
(如果你对互联网邮件的技术细节不感兴趣,可以安全跳过下面的两个段落。)
受到SMTP转发的启发,我终于看清了。Popclient在试图做太多的事情。它被设计成了既是一个邮件传输工具(MTA),又是一个本地投递工具(MDA)。有了SMTP转发功能,它就可以摆脱MDA的负荷,像sendmail那样专心只作MTA,并把邮件的本地投递留给其它程序来完成。
几乎每一个支持TCP/IP的平台上都预留了25号端口,为什么还要去折腾MDA的复杂配置或邮箱的“锁定-添加”呢?更有甚者,通过SMTP转发意味着收到的邮件看起来几乎和正常的SMTP邮件一样。这正是我们想做到的。
(返回到高一层次上……)
即使读者没看懂上面的技术细节,这里也有几条重要的经验可以学习。首先,这个SMTP转发的点子是我有意模仿Linus方法的最大收获。一个用户给了我这个好点子——我需要做的仅仅是理解它的含义。
自己拥有好主意是好事,认识到来自用户的好主意也是好事。有时候后者会更好。
很有意思的是,如果你谦虚地坦白别人的贡献很多,你很快就会发现外面的世界不会这么看。他们会认为是你创造了这一切,只不过是你太谦虚而已。Linus就是最生动的例子。
(当我在1997年的第一次Perl大会上发言时,黑客大亨Larry Wall正坐在前排上。当我讲到上面的那句话时,他以激动的口吻喊了起来,“说出来,说出来,哥们!”全场都笑了,因为他们知道这一点对Perl的发明者[译注:即Larry Wall]也不例外。)
在我发扬这种精神把项目运行了几个星期以后,我开始得到类似的赞扬——不仅来自我的用户,而且来自于其他有所耳闻的人们。我把其中一些邮件收藏了起来;要是什么时候我开始疑惑我生命的意义时,我就拿出来再看看 :-)。
但是有两条基本的、非政治性的经验对各种设计都适用。
最有突破和创新的方案常常来自于意识到你把问题的模型弄错了。
我之前想继续把popclient做成一个支持五花八门的本地递送模式的MTA/MDA时,就是试图在解决错误的问题。Fetchmail应该重新被设计为一个纯粹的MTA,作为正常SMTP邮件传输路径的一部分。
当你在开发中碰到死胡同时——当你绞尽脑汁要超越下一个补丁的时候——一般来讲你这时该问的不是你的答案对不对,而是你的问题对不对。或许你的问题需要重新定义。
嗯……我重新定义了我的问题。显然,正确的路子是
加入SMTP转发支持,
把它设置为默认模式,
最终把其它的传递模式都去掉,尤其是传递到文件和标准输出这两种模式。
这第三步让我犹豫了一段时间,担心会影响那些依赖于此类模式的popclient老用户。理论上,他们可以马上用.forward文件或其它非sendmail方式的类似功能来获得相同的效果。但在实践中,这种转换可能会让人头大。
但事实证明,做完这三步后好处非常明显。驱动代码中毛病最多的地方消失了。配置选项也大大简化——不再需要围着系统MDA和用户信箱打转,也不再需要担心背后的操作系统是否支持文件锁定。
而且,唯一可能丢失邮件的情形不见了。以前,如果你指定递送到文件而磁盘满了的话,你的邮件就丢了,而这在SMTP转发中不会发生,因为除非邮件成功传送或至少缓存了,SMTP的聆听端不会给以确认。
同时,性能也提高了(尽管不是你运行一次就能感觉到的)。改变后另外一个不小的好处是说明手册简化了许多。
后来,我为了对付一些涉及到动态SLIP(Serial Line Internet Protocol,串行线互联网协议)的晦涩情形,不得不把递送到用户指定MDA的功能加回来,但做起来简单多了。
说明了什么道理?在不损失效率的情况下,不要犹豫把多余的功能扔掉。Antoine deSaint-Exupéry[译注:著名法国作家,著有儿童故事《小王子》等](他不在写作经典儿童图书的时候是个飞行员和飞机设计师)曾说过:
“设计达到完美的时候,不是增加得不能再增加了、而是减少得不能再减少了”。
代码变得既优良又简单的时候,标志着它上正轨了。在这个过程中,fetchmail有了它自己的设计特色,脱离了上一代的popclient。
到了该换名字的时候了。新的设计和老的popclient相比,更像是sendmail的对偶;二者都是MTA,但sendmail是往外投递,新的popclient是接过来再投递。所以在动工两个月后,我把它重命名为fetchmail。
在这个SMTP转发功能如何进入fetchmail的故事里,有一个更普遍的经验。那就是不仅调试是可并行的;开发和(在可能令人吃惊的程度上)搜索设计空间也是。当采用快速短周期的开发模式时,开发和添加新功能在某种意义上就成为调试改错的一个特例——改正软件原始设计中的“功能不足错误”。
即使在高一层次的设计上,有许多共同开发者在你产品的设计空间附近随机行走将会很有价值。想象一下:一滩水是怎样发现下水口的,或者更恰当一点,蚂蚁怎样发现食物的?本质上是分散、搜索,并通过一个可扩展的通讯机制来协调。这一点很管用,就像Herry和我一样,你们随行中的一个很可能会发现有一个宝藏就在附近,你只不过太专注了一点而没有看到。
(续)
Fetchmail长大了
现在我有了一个整洁新颖的设计;我知道代码工作良好因为我每天使用;beta测试名单繁荣热闹。我慢慢明白了我不再是在做一个可能只对几个人有用的琐碎的个人编程——我在主持一个所有使用SLIP/PPP邮件接口的Unix用户都需要的程序。
带有SMTP转发功能的fetchmail在竞争对手面前表现强劲,并很可能成为那种在它的功能领域里鹤立鸡群的经典程序,那种让对手们不仅被放弃而且几乎被遗忘了的“领域杀手”。
我觉得这种结果不是你想达到就能达到的。你需要某种超级的设计构想,自然而然地、甚至命中注定地把你推到那里,而找到这种构想前提无非就是要有很多想法可以去尝试——或者有工程眼光把其他人的点子发挥到其原作者根本想象不到的地步。
Andy Tanenbaum最先想到了做一个运行于IBM PC机的简单Unix作为教学工具(他称之为minix)。Linus把Minix的概念推进到了Andy可能想都想不到的地步,使其变成一个如此神奇的东西。与此类似(不过是规模小些),我从Carl Harris和Harry Hochheiser那里借来主意并努力推进它们。我们都不是“原创”——那种人们浪漫想象中的天才。但是话说回来,多数科学、技术和软件的进展都不是由“原创”或者黑客神秘主义中的超级天才完成的。
这两个项目的结果都一致是那种很眩目的东西——那种每一个黑客毕生追求的成功!而且这意味着我将不得不把我的标准设得更高。为了让fetchmail达到我预想的目标,我不仅要为自己,而且还要为别人编程,同时保持程序简洁、健壮。
意识到这点之后,我所写的第一个也是绝对最重要的一个功能是集体收发(multidrop)——从一群用户的集体信箱里把累积的所有邮件取来,然后把每一封分发给单独的收信人。
我决定添加集体收发功能,一部分是因为用户们吵着要,但主要还是因为我觉得它会迫使我更全面地处理地址问题,从而甩掉单信发送模式中的臭虫。结果如我所愿。我花了很长的时间才把RFC822的地址解析搞定,不是因为它的哪一部分很难,而是因为它涉及了一堆相互关联的烦人细节。
然而集体收发也成为了一项优秀的设计决定。我是这样知道的:
任何一个工具都应该达到预期的用处,但是一个真正优秀的工具会带来预期不到的用处。
集体收发一个不曾预期的功能是可以在网络连接的客户端建立邮件列表、管理列表成员和展开别名。这样,通过ISP上网的个人不必持续访问ISP方的别名扩展文件就可以管理一个邮件列表。
我的beta测试员们要求的另一个重要功能是支持8位的MIME操作。这个很容易,因为我已经很小心地保持了代码的8位兼容性(没有强迫ASCII字符集中没有使用的第8位比特去携带程序中的信息)。不是因为我预料到了这个功能要求,而是遵循了另一个原则:
在写任何接口软件的时候,花点功夫尽可能不要干扰数据流——除非用户强迫你,永远不要扔掉任何信息!
要是我没有遵守这个规则,8位MIME支持会很困难且毛病不断。事实上,现在我所需要做的仅仅是读一下MIME标准(RFC1652),然后添加一条小小的文件头生成规则。
在一些欧洲用户的要求下,我添加了一个选项来限制每次连接能下载的邮件数目(这样他们可以控制他们昂贵的电话费)。我对这件事抵制了很长一段时间,直到现在也不是完全满意。但是如果你是给外边的世界写程序,你就不得不聆听你的顾客——就算他们不付你钱也是这个道理。
Fetchmail带来的其它几条经验
在回到更广义的软件工程问题之前,还有几条fetchmail经历中的经验值得细想。非技术性的读者可以安全地跳开这一节。
Fetchmail用户配置文件(rc)语法中包括了一些完全不解析的、可选的“噪音”关键词。它们所带来的类似英语的配置文件语法比把它们全部梳理掉之后所剩下的传统“关键词-对应值”语法要可读得多。
这开始于一个深夜实验——那时我注意到rc文件的定义看起来多么像一个微型的命令式语言。(这也是我把popclient原有的“server”关键词换成“poll”的原因。[译注:server是名词“服务器”,poll是动词“检查”,后面跟随的是邮件服务器名。改成poll使配置文件读起来更象句子。])
在我看来,努力把这个微型指令语言做得更像英语可能会使它更容易使用。那时,尽管我支持“把它做成一门语言”的设计流派,比如Emacs、HTML和许多数据库引擎设计的那样,我还不是特别热衷于“类似英语的”语法。
传统上,程序员们倾向于选用简洁紧凑、完全没有冗余的控制语法。这是计算资源昂贵时期的文化遗留。那时,解析过程不得不尽可能的廉价和简单,大概有50%冗余的英语明显是一个非常不合适的模型。
这不是我一般避免英语式语法的原因;我在这儿提起它正是为了打破这个看法。有了便宜的主频和处理器,简洁不应该为了简洁而简洁。现在一门语言对于人的方便比对于计算机处理的方便更重要。
然而我们还有需要小心的原因。其中之一是解析过程的复杂性成本不能被提高到富产臭虫和困惑用户的程度。另外,试图把一门语言的语法做得像英语经常迫使它的“英语”严重扭曲变形,以至于对自然语言的表面模仿变得像传统语法一样令人困惑。(你可以在许多所谓的“第四代”和商业数据库查询语言中看到这个坏效果。)
Fetchmail配置文件的语法似乎避免了这些问题,因为它的语言空间极为有限。它离一个通用的编程语言还很远;它需要表达的内容也很简单。所以在这里,由语言扭曲引起困惑的可能性很小。我觉得这里可能有一个更普适的经验:
当你的语言离图灵完备还差得远的时候,不妨给语法加点“糖衣”包装。
另一条经验是关于隐藏和安全性的问题。一些fetchmail的用户要求我改一下软件来把储存在rc文件里的密码加密,这样入侵者就不会在无意中看到它们。
我没有照办,因为这实际上并不会添加保护。不管怎样,任何一个有权读你rc文件的人都可以像你一样来运行fetchmail打开你的信箱——如果他们真的来找你的密码,他们也可以从fetchmail的代码中剥离出必要的解码器来得手。
所以,在rc文件中使用加密的密码只不过是给那些不怎么用心思考的人一种虚假的安全感。这里的一般规则是:
一个安全系统的安全性取决于其秘密的安全性。小心伪秘密。
市集风格的必要前提
这篇文章的早期审阅者和试验听众们不断希望了解市集开发风格的成功前提,包括项目领导人的素质和他开放项目和开始建立合作者社区时的程序代码状态。
很显然,市集风格不能帮助你从零开始编程[注:IN]。你可以使用市集风格测试、调试和提高你的代码,但是直接以市集模式来孕育一个项目会是很困难的。Linus没有这样试过,我也没有。你新生的开发者社区至少需要一个能运行和测试的东西。
[注:IN]另外一个和市集风格能否帮助你从零开始一个项目的话题是市集风格中能否产生创新。一些人声称市集风格缺乏强有力的领导,因此只能模仿或改进已有的工 程方法,但无法孕育创新。该观点最有代表的作品要属“万圣节档案”了。这是微软关 于开源现象的两篇内部备忘录,其作者认为,Linux只不过是追着Unix系统的尾巴跑,“(一旦追上了后者开始领跑),庞大的管理开销便会让其前进得非常缓慢”。
这个观点和事实完全不符。甚至万圣节文档的作者自己后来又指出“往往新点子首先在Linux中被实现,然后再被搬到其它平台。”
其实这也不是什么新现象。把上述论断中的“Linux”换成“开源项目”,我们可以发现来自开源社区的Emacs、万维网甚至英特网本身,不是追谁的尾巴跑出来的,也没有看到任何庞大的管理开销。现在开源项目不断创新给我们带来的是前所未有的广阔选择空间。随便举个例子,GNOME项目中关于图型界面和对象技术的创新已经受到大量的商业关注。其它例子举不胜举,随便那一天看一下Freshmeat.net就可证明。
该论断还包含的一个更根本的错误,即隐含地假设大教堂模型(或市集模型,或任何其它管理结构)可以有效的产生创新。这一点根本站不住脚。群体并没有更创造性的眼光。即便是市集模式中自愿组成的架构师团体往往也缺乏新点子,更不用说那些需要在权力平衡中考虑自身利益的公司委员会了。相反,创新来自个体。其周围的社会机制能做得最好的就是给予响应——去培育、奖励并严格的测试它们,而不是排挤它们。
所以,不论是软件还是其它任何领域,创新的关键在于如何去激发更多人提供新点子,以及如何不去排挤它们。
显然,假定大教堂开发模式有利于创新,而门槛低、流程通畅的市集模式不利于创新,是很荒谬的。如果创新的起点是一个人加一个点子,那么一个可以使该主意吸引成百上千合作人的环境将不可避免地优于一个为继续工作于该点子而不至于被解职,其主人不得不通过政治性的手腕将其推销到上级管理层的环境。事实是,那些使用大教堂方式机构的创新史表明,来自其自身的创新极为少见。大公司依赖大学研究成果作为其新点子(这也是“万圣节文档”作者对Linux有力地加速和吸收学术研究的不安),或收购那些基于某项创新而成立的小公司。这两种情况,创新均非来源于大教堂文化。恰恰相反,很多以类似方式买来的创新被“万圣节文档”作者所鼓吹“庞大的管理开销”所扼杀了。
上面讲的是负面的例子。这里更应该把一个正面的例子奉献给读者。我建议大家试试下面的方法:
定下一个你可以严守的创新准则。比如“看到了我就知道”就满足这个测试的条件。
再定下任何一个和Linux竞争的商业操作系统,和一个其开发工作进展的最好的消息渠道。同时观察该消息渠道和Freshmeat一个月,每天各自记下两者中你认为是创新的新点子数。三十天后,统计这两个数字。
我写这些文字这天,Freshmeat有22条软件发布信息,其中有3条看起来部分推进了现有技术。这只是Freshmeat上较差的一天。然而,如果哪个读者能在一个月中在某个商业渠道上找出类似的发现,我都会非常吃惊。
当你开始社区建设的时候,你要能够给出一个可行的承诺。你的程序不一定要工作得非常好。它可以是粗糙的、问题多多的、不完整的、缺少文档的,但它不可或缺的是
能运行
能说服潜在的合作者,它可以在可预见的将来进化成真正漂亮的东西。
Linux和fetchmail开放的时候都带有强劲、吸引人的基本设计。许多像我描述的那样来思考市集模式的人正确地认为这一点很关键;于是进而断定在项目领导人身上,高度的设计灵感和聪明不可或缺。
但是Linus的设计来自于Unix,我的最初设计来自于先前的popclient(尽管它后来变化很大,按比例来说比Linux的大的多),那么一个市集风格项目的领导人/主持人真的一定要有杰出的设计天才,还是只需掌握四两拨千斤的方法来利用大众的设计才能呢?
我认为项目主持人能否想出杰出灿烂的设计不是很关键,但绝对关键的是,他必须能够慧眼识别出他人的优秀设计或想法。
Linux和fetchmail项目都显示了这方面的证据。Linus,(像前面讨论过的)尽管不是一个特别有原创性的设计者,却展现了识别优秀设计并把它集成到Linux内核里强大才能。我也已经描述了在fetchmail里的最有力的一个设计思想(SMTP转发)怎样来自于另一个人。
这篇文章的早期读者捧我的场说我容易低估市集项目里原创设计的价值,因为我自己不缺创造性,因而就想当然的习惯了。这话大概有一点点的真实性在里面;设计(而不是编码或调试)确实是我的强项。
但是在软件设计里表现聪明和创造力的问题在于它会形成一种坏习惯——当你应该保持代码稳固和简单的时候,你开始放任地把它们搞得好玩和复杂。我曾经因为犯了这个错误把项目搞砸过,但是我在fetchmail里做到了避开这个错误。
所以我相信fetchmail项目的成功有一部分是因为我克制住了自作聪明的习惯;这(至少)反驳了设计的原创性是市集项目的成功关键。再想一下Linux,假设Linus在开发中试图整出操作系统设计的根本性创新,做出来的内核还会像我们现在的这么稳定和成功吗?
当然一定的设计和编码水准还是必要的,但是我认为几乎每个认真考虑发起一个市集型项目的人已经超出了这个基本要求。开源社区内部的声望机制给人们一种微妙的压力,就是不要发起自己不能坚持下去并搞好的开发项目。迄今为止,这一点似乎工作得很有效。
另外有一个和编程能力无关的技能,我认为对于市集型项目来讲和设计才能一样的重要——甚至可能更重要,那就是一个市集项目的主持人或领导者必须有良好的人际交流技能。
这应该是显而易见的。要建设一个开发社区,你需要吸引人群,让他们对你所做的感兴趣,并且让他们对自己的工作量舒心。要做到这一点,高超的专业技能会起很大的作用,但远远不是故事的全部,你所展现的人格也很重要。
Linus是一个平易的人,让人们喜欢他、想帮助他——这不是巧合。我是个活泼外向的人,喜欢和人群打交道,有着一些现场喜剧演员的直觉和本事——这不是巧合。要使市集模式运行起来,你最好还能有些让别人喜欢你的本领。
开源软件的社会语境
这句话写到了实处:最好的程序因作者日常问题的个人解决方案而开始,因一大批人正好都有这个问题而流行。这把我们带回了第一条经验的内容,用一种或许更有用的方式来表达是:
要解决一个有意思的问题,首先找到一个你觉得有意思的问题。
Carl Harris和先前的popclient如此,我和fetchmail也是如此。但是这点大家已经明白很久了。Linux和fetchmail的历史看来要求我们关心的是另外一个有意义的话题,即在下一个阶段——在用户和合作者形成了庞大活跃的社区时的软件的进化。
在《人月神话》中,Fred Brooks表述了程序员的时间是不能简单进行叠加的;添加开发人员的做法只能使得已经延期的软件项目更为延期。像我们前面提到的,他论述了项目的复杂程度和通讯成本按开发人员数目的平方增加,而业绩仅以直线增加。Brooks法则被广泛的认同为真理。但在这篇文章里,我们已经探讨了开源开发过程在好几处破除了Brooks规则的前提假设——而且事实证明,如果Brooks法则统领一切,Linux就不可能发生。
以事后之明来看,Gerald Weinberg的经典《计算机编程的心理学》对Brooks法则作出了一个关键的修正。在他对“无私编程”的讨论里,Weinberg注意到一些地方的开发人员不对自己的源码画地为牢,而是鼓励他人在其中寻找错误和指出改进的余地——在这些地方,改进比别处进展得明显快很多。(最近,Kent Beck的“极度编程”技术——把编程者配对让他们互相监督——或许可以看作是强制这一效果的尝试。)
Weinberg的用词可能让该结论未获得应有的认可——把网络黑客说成毫无私心未免让人莞尔。但是我认为他的结论在今天看起来比以往任何时候都更让人信服。
市集模式,借助“无私编程”效果的极致动力,有力减弱了Brooks法则的效果。Brooks法则背后的原理没有被推翻,但是庞大的开发者群体和廉价的通讯所带来的前所未有的准线性可扩展性,开始将其效果淹没。这就像牛顿式的和爱因斯坦式的物理之间的关系——旧的系统在低能量下仍然有效,但当质量和速度变得足够大的时候,就得到了如同核爆炸或Linux那样的惊奇。
Unix的历史应该使得我们对研究Linux的结果(和我在小规模上有意拷贝Linus的方法所实验确认的结果)有了心理准备。这是说,虽然编程基本上仍是一种个人封闭的活动,真正高超的程序来自于借助整个社区的注意力和脑力。一个在封闭的项目中只使用自己脑力的开发者,将会输给一个知道怎样创造一个开放和进化式环境的开发者、一个知道从中吸收成千上万人的探索设计空间的反馈、编码贡献、臭虫检测和其它改进的开发者。
但是有几个因素阻止了传统的Unix世界把这个方法发挥到极致。一个是各种软件许可、贸易秘密和商业利益的法律限制。另一个(回头来看)是互联网还不够好。
在便宜的互联网普及之前有过一些地域性的紧密团体,在文化上鼓励Weinberg的“无私编程”,使得一个开发者可以容易地吸引到一批有水平的“军师”和合作者。贝尔实验室、麻省理工的人工智能和计算机实验室、伯克利加州大学——这些成为了传奇性的和依然强劲的发明家园。
Linux是第一个有意识地并成功地把全世界当作智囊库使用的项目。我不认为Linux的孕育期与互联网的诞生重叠是一个巧合。Linux在1993-1994之间网络服务业起步和对互联网的主流兴趣爆发的同期脱离了它的婴儿时代也不是巧合。Linus第一个学会了如何运作普及的网络连接所促成的新规则。
虽然便宜的互联网是Linux模式进化出来的必要条件,我觉得它还没有构成充分条件。另一个关键的因素是一种领导风格和一套合作制度的建立——使得开发者可以吸引合作者并在这个媒介中获取最大程度的收益。
但什么是这种领导风格、什么是这些制度呢?它们不可能是基于权力关系的——就算是可能,强制性的领导不会产生我们所看到的成果。在这个题目上,Weinberg很恰当地引用了19世纪俄罗斯无政府主义者Pyotr Alexeyvich Kropotkin的自传《一个革命者的回忆录》:
成长于一个农奴主的家庭,我进入社会后,像我那个时候所有的年轻人一样,很是相信领导、命令、训斥、惩罚等等的必要性。但是在早期我不得不管理重要的事业和对付[自由的]人们的时候,在每个错误都会立刻导致严重后果的时候,我开始领悟到按指令的原则行事与按共同理解的原则行事之间的区别。前者在阅兵式中运行得令人崇敬,然而就真实的生活而言,它却一文不值;而且目标只有通过许多共同意志的竭诚努力才能实现。
这个“许多共同意志的竭诚努力”正是Linux这种项目所要求的;在这个我们叫作互联网的无政府主义者天堂里,对志愿者们行使“指令的原则”事实上是不可能的。要有效地运作和竞争,想要领导协作性项目的黑客们不得不学会怎样按照Kropotkin的“共同理解的原则”里模糊地提出的模式,招募和激励活动中的相关社区。他们必须学会使用Linus法则。[注:SP]
[注:SP]除了Kropotkin对“指令的原则”的批判和Linus法则,关于社会组织的内在机制问题还存在更广泛的讨论。其中包括另一个众所周知的软件工程定理——Conway定理,其通俗地表述就是“如果分四个小组去开发一个编译器,得到的会是个四遍编译器”。其最初的描述更为一般化:“软件产品的体系结构必然是其开发机构内在通讯结构的缩影”。简单说来就是:“方法决定结果”,或者“过程演变为产品”。
同样有必要指出的是开源社区存在多层与其功能吻合的组织结构,网络无处不在。除了英特网本身,成员间还存在一个分布式、松耦合的点对点网络,具有多重冗余性和可降级。在这两个网络中,节点的重要性只取决于外界愿意和他合作的程度。
其中的点对点网络对于开源社区惊人的生产力至关重要。以下的SNAFU法则又进一步发展了Kropotkin解释权力关系的尝试:“真正的交流只能在平等的个体间存在;下级从上级得到更多回报的可靠方式是通过顺耳的谎言而非事实真相。”团体项目的创新最终依赖于真正平等的交流,因此,会受到权力关系的严重阻碍。开源群体不存在权力关系的事实从反面告诉了我们权力关系导致的修正错误的可怕代价、生产力的底下和机会的丧失。
更进一步,SNAFU法则预测在基于权力的机构中,随着流入决策层的顺耳谎言越来越多,决策者和现实之间的脱节将越来越严重。这个结果在传统软件开发中扮演的角色显而易见,就是下级极力掩饰、无视及淡化问题,做出的软件将成为灾难。
我早先引用了“神庙效应”作为Linus法则的一个可行的解释。但是不可避免自我呈现出的一个更有力的类比是生物学和经济学中的自适应系统。Linux世界在很多方面都接近自由市场或生态系统:其中的自私个体都试图将自身的利益最大化,但在这个过程中形成了一个自我纠正的自发秩序,其周密度和有效性不管多少中央计划都无法与之媲美。那么,这里就是寻找“共同理解的原则”的地方。
Linux黑客们最大化的这个“利益函数”不是经典经济学上的,而是他们自我的满足和在其他黑客中的声望这些摸不到的东西。(有人或许可以把他们的动机称为“利他的”,但这忽略了利他主义自身对利他主义者就是一种自我满足这一事实。)这样运作的自愿者文化其实也很常见;我长期参与的另一个就是科幻粉丝族。不像黑客族,他们早就明白地认识到了“自我彰显”(ego-boosting,或在其他粉丝中增强个人的声望)是志愿者活动背后的基本动力。
Linus,通过成功地把自己定位为一个主要由其他人来开发的项目的看门人,以及培养对这个项目的兴趣直到它可以自我维持,表现了对Kropotkin“共同理解的原则”的敏锐把握。上述对Linux世界的类经济学视角使得我们能够看到这种理解是如何应用的。
Linus方法其实就是利用“自我彰显”来创造有效的市场。是“自我彰显”把单个黑客的自我利益最大化行为紧密地和那些只有通过持续合作才能达到的困难目标绑在一起。通过fetchmail项目,我证明(尽管在小规模上)可以通过复制他的方法产生出好结果。或许我甚至做得比他更有意识和更系统一点。
很多人(尤其是在政治上不相信自由市场的那些)会以为自我驱动的个人主义者们形成的文化会是支离破碎、占山为王、低效浪费、秘密诡异和充满敌意的。但是这个设想显然被(只举一个例子)Linux文档的惊人的广度、深度和质量所证伪。既然程序员痛恨写文档是众所周知的,上述事实简直是奇迹。Linux黑客们是怎样生产出这么多文档的呢?显然,Linus自我彰显的自由市场在产出优良的、协同的行为上优于商业软件厂商的大笔资金驱动的文档工厂。
Fetchmail项目和Linux内核项目都表明,通过适当地表彰众多其他黑客的成就,一个有力的开发/协调者可以使用互联网来收获拥有许多合作者的好处,而不至于让项目陷入嘈杂的混乱。所以针对Brooks法则,我反过来建议下面的一条:
如果开发的协调者有一个至少和互联网一样好的通讯媒介,而且懂得如何不通过强迫来领导,那么多个头脑不可避免地优于一个头脑。
我认为开源软件的未来会更多地属于那些懂得如何运行Linus规则的人们,属于那些告别大教堂来拥抱市集的人们。这不是说个人的远见和才华不再重要;而是,就我看来开源软件的前沿会属于那些开始于个人的远见和才华,然后通过有效地建设相关志愿者社区来扩展放大的人们。
或许这不仅是开源软件的未来。要对付一个问题,没有闭源的开发者可以比得上Linux社区所能驱动的才能之众。极少有人甚至能雇得起那些对fetchmail作出了贡献的200(1999:600, 2000: 800)多人!
也许,开源文化最终胜利的原因不是因为合作在道德上正确或软件“劳役”在道德上错误(假设你相信后者,Linus和我都不),而只是因为开源社区可以在一个问题上投入多几个数量级的技术工时。闭源世界无法赢得这场进化式的军备竞争。
关于管理和马其诺防线
原始于1997年的《大教堂和市集》文章以上述预见结束——程序员/无政府主义者的幸福网络群体胜出并压倒了传统闭源软件的阶层化世界。
然而,很多怀疑者并不信服;他们提出的问题值得我在这里做出回应。多数对市集模式的反对归结到以下观点:市集模型的鼓吹者们低估了传统管理促进生产率的效果。
老脑筋
发表评论
-
将博客搬至CSDN
2014-02-06 16:56 546新博客地址 http://blog.csdn.net/java ... -
猴子管理法则--每个人都应该管理好自己的猴子
2011-06-27 17:04 1560转自http://wiki.mbalib.com/wi ... -
国家标准气象灾害预警信号符号
2011-06-23 16:15 1329总听天气预报报道什么红色、橙色预警,但是一直不清楚代表什么意思 ... -
咽炎现象及注意事项
2008-10-22 15:39 1115似乎得咽炎了,痛苦啊 ... -
笔记本网络IP切换
2008-06-09 15:23 1691笔记本电脑的移动性使其在开发中很便捷,也是因此使其在不同网络环 ... -
总结win2003系统无法玩游戏的情况[转]
2008-04-30 15:44 4035为了编程,系统改成了0 ... -
教你怎么做到千杯不醉(男士必看)
2008-04-19 14:06 1372中国酒文化源源流长, ... -
教堂与集市(转载)魔法大熔炉
2008-04-14 08:23 2251第四章 魔法大锅炉 前言 ——————————————— ... -
教堂与集市(转载)开拓智域
2008-04-14 08:22 1181第三章 开拓智域 在观察由开放原始码版权所定义的“官方”意识形 ... -
解决:魔兽争霸3冰封王座跳出无法运行而且屏幕发白(非白屏,只是比正常白)
2008-03-31 18:11 9602由于编程需要,把系统改成了WIN2003。 但是问题随之而来, ... -
经典禅语
2008-01-05 12:00 10571 人之所以痛苦,在于追求错误的东西。 2 如果你不给自己烦 ... -
教堂与集市(转载) 第一章 黑客文化简史
2008-01-05 11:06 1080第一章 Hacker文化简史 序曲: Real Progra ... -
[转]向诸葛亮学十堂课(仅86字)
2008-01-03 21:59 980諸葛亮字孔明,琅琊(今 ... -
感慨“逝者如斯夫”啊
2007-08-24 22:06 1271经常来javaeye上来逛,今天看到招聘专栏找工作的帖子,很是 ...
相关推荐
### 大教堂与集市:开源与封闭软件的发展模式 #### 引言 《大教堂与集市》(The Cathedral and the Bazaar)是埃里克·斯蒂文·雷蒙德(Eric Steven Raymond)撰写的一篇关于开源软件开发的经典文章。这篇文章通过...
### 大教堂与市集——探索开源文化的精髓 #### 概述 《大教堂与市集》是由Eric Steven Raymond撰写的一篇著名文章,该文首次发表于1997年,后经多次修订完善,成为了开源运动的重要文献之一。本文通过对fetchmail...
### 大教堂与市集:理解Linux开发模式的双重视角 #### 标题解析: - **大教堂与市集**:这一标题形象地对比了两种截然不同的软件开发方式。"大教堂"通常指的是传统的、封闭式的软件开发模式,这种模式下软件的构建...
### 大教堂与市集——开源软件开发的经典之作 #### 概述 《大教堂与市集》是由美国计算机科学家艾里克·斯蒂芬·雷蒙德撰写的一部关于开源软件开发的经典著作。该书首次揭示并分析了两种截然不同的软件开发模式:...
《大教堂与市集》是Eric Raymond的一篇经典文章,主要探讨了两种不同的软件开发模式:集中式的“大教堂模式”和分布式协作的“市集模式”,并以Linux的开发为例,展示了市集模式的巨大成功。 Linux的成功在于其开放...
《大教堂与市集》是艾伦·凯(Eric S. Raymond)于1997年撰写的一篇论文,后来发展成为一本书。该书是开源运动的重要文献,对比了两种不同的软件开发模式:“大教堂模式”代表传统的封闭式开发,以集中式控制和秘密...
软件开发的圣经,很好的一本讲软件开发的书
描述Linux开源世界的开发方式,和集中式开发方式的对比
《大教堂和市集》(Eric Raymond著):开放源代码运动的《圣经》,《21世纪的书》。与微软商业化持相反的观点,主张自由,开源。体现了互联网时代的精神。
《大教堂与市集》这本书由Eric S. Raymond撰写,他通过自己对开源软件社区的观察和参与,特别是从他主持开源软件项目的经历出发,详细探讨了两种截然不同的开源社群模式:Emacs代表的“大教堂”模式和Linux代表的...
《大集市与教堂》是Eric S. Raymond的一部著作,该书深入探讨了开源软件运动的核心理念,特别是通过Linux项目的发展历程,揭示了开放源代码社区的运作模式及其对传统软件开发模式的挑战与革新。Raymond将两种不同的...
《图灵的大教堂》这本书由美国作家乔治·戴森撰写,深入探讨了计算机科学的先驱艾伦·图灵的生活及其对计算机科学发展的深远影响。接下来,我们将详细解析该书涉及的一些关键知识点。 ### 图灵与计算机科学 艾伦·...
作者Eric S. Raymond对他成功的开源项目fetchmail进行了剖析,以测试Linux历史上提出的一些有关软件工程的令人惊讶的理论。
《大教堂》是著名古典吉他作曲家安德烈斯·塞戈维亚(Andrés Segovia)改编自阿根廷作曲家曼努埃尔·巴里奥斯(Manuel Ponce)的一首作品。这首乐曲以其深情的旋律和丰富的和声展现了古典吉他的魅力。在六线谱中,...
”在《图灵的大教堂》一书中,作者乔治·戴森着重介绍了一小群人,为首的是供职于新泽西州普林斯顿高等研究院的约翰·冯·诺依曼,他参与建造了最早的一台计算机,以实现艾伦·图灵提出的通用机的愿景。他们的工作...
互联网程序员都每天刷题嘛大教堂和集市书 埃里克·史蒂文·雷蒙德 (Eric Steven Raymond) 参考文本和版权位于: 这本书属于公共领域,他的作者是 Eric Steven Raymond,这个存储库托管了这本书的原始版本,用 ...
圣索菲亚大教堂导游词.doc
它是世界五大教堂之一,也是世界第四大教堂。这座教堂在13世纪末由行会建造,象征着当时市民阶级对贵族权力的胜利,是共和政体的象征。 2. **建筑风格**:佛罗伦萨大教堂的建筑风格体现了拉丁十字式与集中式的融合...
教堂的钟楼,即著名的乔托钟楼(Giotto's Campanile),是由乔托设计,其简洁而优雅的线条与大教堂相得益彰。教堂内部同样令人震撼,巨大的空间、华丽的装饰和精美的壁画展现了文艺复兴时期的艺术精髓。 布鲁乃列...
梵蒂冈城与圣彼得大教堂.ppt