项目管理和工程是自由源码开发中在很大程度上被忽略的问题。最近,Monty R. Manley 在 linuxprogramming.com 发表文章指出这一问题,但我对他的某些意见持保留态度。
项目管理和工程是自由源码开发中的被忽略的重大问题。Monty R. Manley 在一篇题为《开放源码道路上的项目管理》文章中指出这一问题。他建议在自由源码(以我的理解,是指自由软件和开放源码)项目中采用更加传统的软件开发模式。我绝对赞同更好的工程实践是需要的。然而,我认为我们需要做的是,理解自由源码的操作,尤其是其中成功的实践,并从中发见符合其文化的工程实践。
Manley 的文章是发人深省的,并刺激我进一步思考一些曾经考虑过的问题。我曾经领导过一个自由源码的项目(gimp-print),我曾经面对过很多这样的问题。在我的职业生涯中,我常常既是开发者也是发行工程师,也锻炼了一些这方面的观察能力。
本文中,我将探讨 Manley 提出的一些问题,分析在自由源码社区中如何应用这些方法,并提出一些建议,或至少指出一些在未来的工作中要注意的问题。
多年以来,在软件开发中采用的方法论是瀑布模型:首先是仔细的需求分析,开发组有步骤的制定一份功能(结构)说明,接着是概要设计,详细设计,然后才着手编码。编码结束后进行测试,然后才能发布软件。这看上去是很有逻辑的;只在理解后才开始构造。以这样严格的方式构造软件,工程师很明确每一步应该做什么。许多人提出了基本是基于这一模型的多种方法论;也有相当多的商业工具可以使这些步骤更机械化且不易出错。
据我理解Manley 的观点是,自由源码开发的一个弱点在于 (瀑布模型中的) 早期步骤 (需求和设计分析) 在很大程度上被忽略了。我不同意这一观点,理由如下:
首先讨论瀑布模型自身的缺陷。Manley 问道:
如果你不知道想做什么你怎么能写程序?
问得好。不知道做什么怎么去构造?没有一个正常人会考虑造一座桥而不知道要连接什么,交通流量有多大,地质条件如何以及一些类似的问题。软件会有什么不同吗?
通常不会。航天飞机项目的软件队伍一举成名,因为他们不用每周工作80小时就及时交付了bug-free 的软件。为医用监视器开发嵌入代码的程序员必须在同样严格的条件下按部就班的发布产品。在这些案例中,纪律严明的队伍的确是遵循了这样的模型:获得需求,经过反复的迭代设计和检查,然后才开始编码。在许多情况下,编码只是大规模的机械流程,早期构造中的 bug 受到相当的关注,因为这表明在前期有错误。但这一模型真的很高效吗?还是对自由源码软件或更商业化的软件更适当?我相信大多数时候它既不是高效的也不是适当的。
我刚才讲的两个例子都是任务苛刻的应用,软件只是关乎人命的大项目的一部分。这只是为了可靠性牺牲了创新的缓慢、小心翼翼的过程。比如这个医用监视器,程序员强调可靠性和正确性而不是新潮的图像,我不知道我是否和这样的项目有什么关系。大多数人直接接触到的大多数软件都不是这么苛刻的;它们只要有效的完成任务就行。
这不是表示我同意这种折衷,比如,像微软那样;他们过于极端,甚至在操作系统的底层,以至于基础不稳定。然而关键是,因为这种根本上的不完美,大多数计算机用户获得了更多的功能;完美与优秀是对立的,难点在于决定什么是“足够好”。比如说,简单的,在很多情况下并不需要完美的结构。在另一方面,前期工作的不足将留下许多隐患。依赖某个软件包的其它软件越多,这个包的可靠性越重要。
问题的另一方面是用户通常不知道程序要做什么。航天飞机项目的专家们很清楚他们需要什么,而且他们有多年的经验知道如何表达。比如说,字处理软件的用户知道他们需要编辑和排版功能,但只是在普通的层面上。在开始工作前几乎不可能获得全面的需求。如果编辑器不自动在行首缩进,用户可以很简单地说“哦,我真的不喜欢手工缩进”;但用户很难凭空想到这一点。没有经过适当训练的用户不会知道如何表达那些他 (她) 并不理解的需求。
这种情况就是所谓的 GIGO (错进错出)定理:如果初始需求是无用的,那么任何根据这些需求写的功能说明也是同样无用的。根据瀑布模型生产出的最终的产品将是一头白象 (white elephant)。
有时候我观察到这样的情况,为了遵守规则,程序员确实写了一份功能说明和设计,不过是在写了并调试了具有一定功能的代码之后。有些时候大家对这种情况视而不见;有些时候有人抱怨这么做耽误了事情或造成其他麻烦;有些时候这造成管理者不知道开发进展情况。有些时候——在我看来——是对合理要求和合理回应。
对需求、结构和设计文档的要求在管理上是相当理性的,即使实施的方式经常不这么理性,即使设计文档指明了下一阶段程序员的任务,而且通常比提前编写的、不能及时反映客观情况的文档更有效。需求文档至少展现了软件大致要解决的的问题。
为了低成本的明确需求,程序员必须接近最终用户。较之商业社会,这一点在自由源码世界中更容易实现。自由源码程序员不会因为担心竞争对手抢在自己前面而保密,也不会保留一些功能好在以后当作升级产品出售。
然而,为了更好的工作,也要求最终用户可以方便的找到和试用新的软件包。在这一方向已经有了一些实际的行动;比如 GNU 配置系统(GNU configure system)提供了构造软件包的通用方法。然而,下载和构造一个软件包并试用还是相当困难和耗时的,万一用不起来还要卸载(为了不丢失数据或系统的稳定性,可能还要恢复系统)。时常连反馈的渠道都很难找到。现有的软件包系统都不能真正的支持这一点。
常有人说开放源码更多的是复制和扩展而不是从头开始的创造。我完全不相信;一些最有创新精神的软件都出自开放式的开发环境:ARPAnet, UNIX (没错,UNIX 是在一个庞大的自由环境中开始的),Lisp Machine,以及窗口化系统的概念 (PARC)。而且,并不是所有的创新都是从头开始的,尤其是在软件世界中;无论商业或自由软件,大多数项目是在一定工作的基础上开始的。我同意,白手起家很可能更困难——在没有经验和资金支持的情况下,一支队伍为了一个宏伟的目标长期满负荷工作——但这样的项目很少。我刚才提到的真正具有创新性的自由项目都有相当的外部资金的支持。
这真的是对自由源码开发的打击吗?我不这么认为。最终对用户而言,只要做的一样好,是否是创新的并不重要。Linux 是从 UNIX 中派生出的,使用了新的也是更好的实现 (在很多方面,它比商业 UNIX 产品要轻)。GIMP 一开始只是 Adobe Photoshop 的替代品;它按照自己的方向发展,现在在许多方面比 Photoshop 更强大。
实际上,面向对象编程的主要目标之一,就是是用使用部件堆砌更大的东西。许多人没有意识到,这一基础依赖于 Aho,Weinberger 和 Kernighan (没错,Awk 的三个创始人) 那些人,以及其他 UNIX 早期工具的开发者们。使用简单通用的数据表示法 (使用空格分割的分行的 ASCII 文本),他们构造了一套可以集成使用(用 shell 脚本)的工具,能够形成更强大的工具。当然,这不是真正的面向对象编程,但这体现了重用的精神。
这种灵活的构造和重用工具的方法是软件区别于别的工程方向的特征。制造硬件部件的费用——甚至只是在已有基础上的微小的修改——都是非常高的。改造机器,浇铸新模具,设计新包装,不一而足。总之需要一条新的生产线。如果新的部件有什么缺陷,昂贵的资源就被浪费了,而宝贵的生产时间也白搭了。与之不同的是,软件的单位生产成本几乎是零;仅有的开销是设计和实现成本。这一点鼓励了自由的实验和创造性的使用已有的部件。
不管怎么说,作为自由源码本质特征,代码的共享极大的提高了这种软件开发形式的健壮性。实际上,如果有什么缺陷,那都是在很难找到的地方,而发现这种缺陷被看成是种挑战。为了能够让人们更容易的找到他们需要的自由源码,对于这些大量源码的整理和编目工作的需求十分强烈。
这也揭示了为什么专利法和过于苛刻的版权法对于软件是潜在的威胁,因为这些东西抑制了思想和方法的交流。专利法的本意是授予创造者在其创新领域有限垄断权以鼓励创新。但是软件更需要聪明和明智的使用已有方法,而不是巨大的创新,所以专利法实际上阻碍了软件所需的这种创新。
Manley 还质疑了“尽早和经常发布”的观点。他特别提出了 pre-alpha (功能或API不完备的)软件通常不应流传到开发组以外。他还声称“尽早和经常发布”与正规的发布方法是对立的。我不同意这些观点——首先,我相信软件开发应该是全面开放的(我将简短的举一些例子);其次,只要正确的施行,我认为这毫无疑问是一种好的发布方法。
首先,要理解“尽早和经常发布”的目的。这种方法的目的,是要让预期的用户有机会试用并报告问题,或者提出新的特性,甚至自己动手添加新功能并反馈。然而,简单的尽早和经常的发布并不能保证这一切;如果项目总体质量差,或者它做的事没人关心,这些都不会发生。如果发布过于频繁以至于使用户感到灰心丧气,而且他们也没有作出足够的贡献,这一目的也无法达到。
大公司经常周期性的做内部发布(可能是每天一次,也可能是每月一次),并鼓励有兴趣的雇员试用。这是“尽早和经常发布”的一种形式,虽然范围并不广。在这一点上他们与自由源码没什么不一样。
让我们从另一个角度看看尽早和经常发布的目的:让用户试用新东西。前面,我提到过需求分析的迭代模型:给用户一个原型,然后根据反馈修改。这两者是不是很接近?经常的发布使用户有机会反馈意见。
然而,要得到好的效果,这些必须做的得当。简单的每天晚上发布并通知每个人去升级没什么用;用户不知道会发生什么而且将花费大量时间升级,很快就对这种事情感到厌烦。真想这么做的人应该使用开发资料库。为了更有效,发布应该是这样的:
这并不是表示每个发布都要是精良的产品,但每个发布都应该具备优秀产品的特征:连贯,值得升级,和干净。注意,我没有说完备——如前所述,不了解需求,很难创造一个完备的产品。
这么做还有一个好处:它迫使开发者不停的测试,因为离发布的日子总是很近。这与好的发布方法没有什么矛盾的,都需要这么做。
使项目过于接近开发组——直到末期才允许外界使用——意味着丧失了发现潜在需求的能力。有两组形成了鲜明的对照的案例,相似的项目,一个公开的发布而另一个不是:
Emacs (文本编辑器) ——现有两个从 GNU Emacs 派生出的版本,GNU Emacs 和 XEmacs,分化在二十世纪九十年代出现。GNU Emacs 由自由软件基金会(Free Software Foundation)开发,而XEmacs 由一个分散的小组开发。分化是由于版权等问题引起的,而开发方法也各有千秋。
GNU Emacs 由自由软件基金的一个小规模的开发组开发。现在,21 版已经开发了几年;当前的发布是 20.7 版。
XEmacs 以公开的方式开发,有稳定和开发两个分支,以及一个公共的 web 站点。当前的稳定分支是 21.1.11,开发分支是 21.2.35。
XEmacs 有许多外观上的不同 (包括嵌入的图片和可缩放的字体),而且现在内部结构也大为不同;它做了很大程度的提炼,包括字符、事件以及键盘映射等一级对象。参考: http://www.xemacs.org/About/XEmacsVsGNUemacs.html。注意,这篇文章可能有些过时,不过其中指出的许多差异还是存在的。
GCC (C 编译器)——自由软件基金会以类似的方式开发了 GCC ,仅仅对外发布了最终版本。开发在1996年开始停滞不前,由于整个项目是不可见的,没人知道发生了什么。几年后,一支Cygnus Solutions 的队伍拿起了已有的代码,添加了一些外部补丁,重新开发了某些部分,并发布了 EGCS(Experimental GNU Compiler Suite)。这是以开放方式开发的,进展迅速。终于这一分化以自由软件基金将 GCC 转移到 EGCS 队伍告终。
综前所述,公开的开发模型有以下优点:
然而 Manley 还是正确的说明了开发(或 pre-alpha)、alpha、beta、候选发布和发布的区别。让我们进一步看看典型的用户基础是什么:
只要每个开发者和每个用户都清楚的理解了这些——每一阶段的预期情况,就以应该不会有什么麻烦。通常,困难——对于商业开发者也一样——在于如何在每一阶段进行适当的培训。经常出现的问题是,在 alpha 阶段以后做的培训太少;而在早期的培训却过长,忙于说明风险,致使培训冗长而且没有明显的成果。发布工程师需要了解这一流程。这一点是无可替代的。
根据我的经验,无论商业或自由项目,通常会在由 alpha 到 beta 的过渡阶段开始迷失方向,而 beta 阶段通常做的都很差。常常是过早的进入了 beta 阶段(项目还没有充分的完成),而且 beta 发布阶段的也不充分(没有足够时间),不能保证一个干净的产品。Beta 应该是特性完备的。如果测试出太多的问题(深层 bug 甚至是遗漏的需求),应当退出beta 阶段,并适当的回溯整个项目。如果不接受这一点,应当明白,这个发布是有缺陷的。
Linux 的发行版是一个有趣的开发过程。制造这些发行版的组织本质上是系统集成商,也扮演了监视开发和将软件包干净的集成到发行版的积极角色。这是一个有趣的模型,对自由软件开发也是种启示。发行版是正在开发的项目的有用的反馈来源,如果发行版差不多是开发者要遵循的统一的标准和惯例,而且能够提前公布集成(软件包)的时间,这对于开发者来说是有指导意义的。也许一个收费商业的 Linux 发行版,可以为那些不太愿意自己做这些麻烦工作的自由源码项目提供这些服务。
第一个主要发布总是容易的。没有什么已知的期望;刚开始,代码小,工作起来很容易,每个人都为自己的工作第一次公布而激动。第二个发布就困难了。人们的热情耗尽了;不知道接着该做什么(显而易见的事情在第一个发布都做了);代码建立在一个复杂的基础上;大家为做过这些东西而骄傲;而整个队伍还没有经验,不知道接下来会发生什么。
这不仅是自由源码项目的问题。我曾在封闭源码的项目中看到过同样的问题。我曾两次参与新软件产品的开发,每次我都看到了的第一个发布挺好,而第二个却混乱。这在商业的而不是技术的文章中曾提到,详见 Geoffrey A. Moore 和 Regis McKenna 的 《Crossing the Chasm》。我不是说这本书里有所有的答案,但它至少提出了问题。可能唯一的解决方法是坚持不懈和有组织的管理。我现在在 gimp-print 中就面对着这样的问题;迄今为止,我们的第一个发布(4.0)是很成功的,但下一步难得多。
为了激发更多的思考,我列出一些总结性的想法和建议。