软件设计是一件需要创造力的事情,天才式的软件必然来自天才式的设计,很多优秀软件的最初版本都是由顶尖黑客独自设计和编码的(正如KenThompson之于UNIX,Linus Torvalds之于Linux)。
但软件膨胀到一定程度,由一个人或几个人继续开发维护不太现实,对大型软件来说,多人合作似乎是一种必然,但到底多少人合适,如何分工和组织,如何调动程序员的积极性,如何让软件不会因规模和复杂性而失控有不同的方法和认识。
最经典的两种方式是:大教堂模式和集市模式:
传统大型软件公司的开发模式就像是艰难而缓慢的大教堂建造工程,它有着严密的管理和封闭的集中式结构,但在创新上、生产力上和Bug控制上却落后于集市模式。
集市模式是一种并行的、对等的扁平化开发结构,其参与者大多来自于互联网上的志愿者,结构松散,来去自由,就像是一个乱糟糟的集市,但就是这样的组织形式,却取得了像Linux这样令人惊叹的成功。
1.好的软件作品,往往源自于开发者的个人需要
按说这是显而易见的(正如老话说“需要是发明之母”),但太多的软件开发人员并不需要也不热爱他们正在开发的软件,他们把编程当差事,为的只是拿薪酬。Linux世界里可不是这样——也许这可以解释为什么Linux社区里原创软件的平均质量是如此之高。
2.优秀的程序员知道写什么,卓越的程序员知道改写(和重用)什么
卓越程序员们有个很重要的特征是“建设性懒惰”,他们知道人们要的是结果而不是勤奋,而从一个部分可行的方案开始,明显要比从零开始容易得多。
以Linus Torvalds为例,他并没有尝试从零开始写Linux,而是以重用Minix(一个用于PC机的迷你型UNIX类操作系统)的代码和理念作为开始,虽然Linux中所有Minix代码最终都被移除或重写,但它在Linux成长初期确实起到了类似脚手架的作用。
Linux世界则把这个传统发挥到了技术上的极限。相比其他地方,从Linux世界多达数T字节的开放源码中,找到一些他人写的“足够好”的代码要可行得多。
3.“计划好扔掉一个吧,迟早你会这么做的。”(Fred Brooks,《人月神话》第11章)
或者可以这么说:在你第一次把问题解决的时候,你往往并不了解这个问题,第二次你才可能知道怎么把事情做好。所以,如果你想做对事情,至少要再做一次。
4.如果你有正确的态度,有趣的事情自然会找到你
5.当你对一个程序不再感兴趣时,你最后的责任就是把它交给一个可以胜任的接棒者
6.把你的用户当成开发合作者对待,如果想让代码质量快速提升并有效排错,这是最省心的途径
这种做法的效力很容易被低估,事实上,连我们这些在开源世界里的人,都极大低估了这种做法的效力,也就是用户越多就越能有效对抗系统的复杂性,直到Linus Torvalds向我们明白地展示这一点。
我想,Linus最聪明和最有价值的成就其实不是构建出一个Linux内核,而是他发明的这种Linux开发模式。有一次我当面向他表达了这个看法,他笑了,平静地重复了他常说的话:“我基本上是个很懒的人,别人做事,我得名誉。”像狐狸那样懒,或者像Robert Heinlein曾经描绘的一个很有名的角色,太懒以至于无所不能。
Linux越来越广为人知,它明显与众不同而且要健康得多。Linus开放式的开发策略简直就是和大教堂模式对着干,Linux的Internet资源库生机勃勃,多个不同版本同时流传,而这完全是由Linux内核那前所未闻的频繁发布所驱动的。
UNIX另一个传统强项也被Linux发挥到美妙的极致:很多用户本身就是黑客。因为可以拿到源代码,这些黑客能极为有效地缩短排错时间,只要给他们一点点鼓励,他们就会帮你查找问题、给出建议并帮助改善代码,这些比你自己做要快得多得多。
7.早发布,常发布,倾听用户的反馈。
Linus的创新之处,并不完全在于大量采纳用户反馈并快速发布系统版本(这也是UNIX世界多年来的传统),而更多在于将这种做法强化到一种能和系统复杂度相匹配的强度。他在早期(1991年左右)发布内核的频率会超过一天一次!这要归功于他在发展合作开发群体方面的努力,Linus比其他任何人都更在意如何利用Internet杠杆促进合作,而且他真的做到了。
关于Linus成功的理论,有这么几个办法:
尽早发布并频繁发布(几乎从来没有低于10天一次的频率,在高强度开发阶段会一天一次)。
我把每一个因fetchmail联系我的人都加到beta列表(是指beta测试人员邮件列表——译者注)中。
每次发布新版本时,我都向beta列表发送朋友对话般的通知,鼓励他们参与。
我听取beta测试者们的意见,征求他们关于设计决策的看法,当他们发来补丁和反馈时给他们以热情回应。
这些简单措施立刻收到了回报。从项目一开始,我就收到一些质量高到让程序员们垂涎欲滴的那种bug报告,而且常常还附带了很不错的修复方法;我还收到经过深思熟虑的批评;收到粉丝来信;收到很有智慧的软件特性建议。
8.如果有足够多的beta测试者和合作开发者,几乎所有问题都会很快显现,然后自然有人会把它解决
或者说得更通俗一些:“只要眼睛多,bug容易捉。”也被称为“Linus定律”。
Linus定律道出了大教堂模式和集市模式最关键的区别:在大教堂建筑者看来,bug是棘手的、难以发现的、隐藏在深处的,要经过几个人数月的全心投入和仔细检查,才能有点信心说已经剔除了所有错误。而发布间隔越长,倘若等待已久的发布版本并不完美,人们的失望就越发不可避免。
如果Linus定律是错的,那么任何一个像Linux内核这么复杂的系统,经过如此多黑客的改动,在无法预见的不良交互影响以及难以发现的“深度隐藏”bug的重压下,应该已然在某个时刻轰然倒塌了。如果Linus定律是对的,它可以很好解释Linux为什么bug相对较少,且连续运行时间能够超过数月甚至数年。
这里隐含的问题是开发者和测试者对程序有着不匹配的思维模式,测试者是从外往内看,程序员是从内往外看。对于不开放源码的软件开发,开发者与测试者往往局限于自己的角色,各说各话,都对对方倍感沮丧。
事实上,一个仅描述外部可见症状的bug报告,和一个直接关联到源码的分析型bug报告,对开发者而言简直是天壤之别。只要能有一个对出错条件在源码级别上的提示性描述(即便不完整),大多数bug在大多数时间里就很容易被发现。如果你的beta测试人员中有人指出“在第n行有一个边界问题”,或者仅仅指出“在条件X、Y和Z下,这个变量会溢出”,你扫一眼那部分代码,往往很快就能准确找到出错模式并得出修正办法。
传统软件开发在组织结构上的根本问题由Brooks定律一语道破:“在一个已经延期的项目上增加人手,只会让项目更加延期。”更为一般地讲,Brooks定律指出,随着开发人员数目的增长,项目复杂度和沟通成本按照人数的平方增加,而工作成果只会呈线性增长。
Brooks定律是建立在经验基础上的,人们发现,bug很容易集中在不同人写的代码的交互接口上,沟通/协调的开销会随开发者间接口数的增加而增多,也就是说,问题规模和开发人员间的沟通路径数相关,即和人数的平方相关(更精确地讲,应该是N(N-1)/2,N代表开发者数目)。
Brooks定律(以及随之而来对开发团队规模的恐惧)建立在这样的假设上:项目的沟通结构是一个完全图 ,即人人之间都沟通。但在开源项目中,外围开发者实际工作在分散而并行的子任务上,他们之间几乎不交流;代码修改和bug报告都会流向核心团队,只有在那个小的核心团队里才会有Brooks开销
源码级bug报告非常有用的理由还有很多,但都围绕着这个事实:一个错误可能会导致多种症状,因用户使用方式和环境不同而有不同表现。这种错误往往正是那种复杂而狡猾的bug(比如动态内存管理错误或者非确定中断窗口的产物),这些bug很难重现,也很难通过静态分析找到根源,导致软件中长期存在一些难以解决的问题。
如果测试者能给出一个源码级的bug报告(这个bug可能会引起多种症状),尽管只是一个不确定的试探性描述(比如“看上去第1250行有一个信号处理窗口”或者“你在什么地方把这个buffer清零的”),也会帮助开发人员找到解决那些多症状(症状看上去也许并不相关)问题的关键线索,要知道开发人员往往离代码太近而不能发现问题。这种情况下,要想精确说出是哪个bug导致了哪个外部可见问题,通常很难甚至不可能,但如果经常发布的话,就没有必要去知道了,其他合作者会很快去查看他们发现的bug是否被解决了。很多情况下,人们会关心导致问题消失的源码级bug报告,但很少关心是哪次补丁修复了它。
对于复杂的多症状错误,要想从表面症状追踪到实际bug,往往有多种路径,开发者或测试者追踪时经由何种途径,取决于千差万别的个人运行环境,并且该环境会随时间产生不确定的变化。实际上,每个开发者和测试者在寻找病状症结的时候,都是在“半随机”(semi-random)的变量集合上对程序状态空间进行采样。越是隐蔽和复杂的bug,就越难从技能上保证采样的对症性。
对于简单和容易重现的bug,重点要放在“半”而非“随机”上,此时,调试技能以及对代码和架构的熟悉程度会大显身手。对于复杂的bug,重点就要放在“随机”上了,这种情况下多人共同追踪bug远比少数几个人循序追踪要有效得多——即便这几个人的平均技能要高很多。
当从表面症状追踪到bug的难度不一且难以预测时,并行纠错的效果会更加显著。一个开发人员会循序选取追踪路径,可能一开始就选了一条困难的路径,当然也可能是容易的路径。而在快速发布模式下,如果多人同时尝试对bug进行追踪,很可能某人立刻就发现了最简单路径,然后在比别人短得多的时间内逮住bug。项目维护人员看到后会发布一个新版本,这样在其他更难路径上追踪该bug的人就可以停下来,以免浪费更多的时间
9.聪明的数据结构配上愚笨的代码,远比反过来要好得多。
10.如果你把beta测试者当做最珍贵的资源对待,他们就会成为你最珍贵的资源。
衡量fetchmail有多成功的一个有趣指标是beta列表(fetchmail-friends列表)的规模,在本文最新一版时(2000年11月),列表成员达到了287名之多,并且每周还增加2到3名。
实际上,列表成员最多时接近300名,1997年5月底我修订这篇文章时发现,成员开始流失。一些人要求我把他们从邮件列表中去掉,而原因很有趣:他们觉得fetchmail已经足够好了,他们不想再看到关于这个项目的邮件往来!对于一个成熟的集市模式项目,也许这是其正常生命周期的一部分
11.仅次于拥有好主意的是,识别来自用户的好主意,有时后者会更好
很有趣的是,如果你发自内心地谦逊,并承认你欠别人很多,你将很快发现世界会这样对待你:他们认为是你发明了整个软件,而且你对自己的天赋有着得体的谦虚。我们可以看到这一点在Linus身上体现得有多好!
12.通常,那些最有突破性和最有创新力的解决方案来自于你认识到你对问题的基本观念是错的
13.设计上的完美不是没有东西可以再加,而是没有东西可以再减
14.任何工具都应具备预期内的功能,但一个伟大的工具能给你带来预期外的功能
15.写网关类软件时,尽可能不要干扰数据流,而且绝不要扔掉信息,除非接收方强迫你这么做。16.当你的语言还远不是图灵完备(Turing-complete)的时候,语法糖(Syntactic sugar)会让你受益良多
17.系统的安全性只取决于它所拥有的秘密
18.想要解决一个有趣的问题,先去找一个让你感兴趣的问题
很多人(尤其是那些在意识形态上不相信自由市场的人)把个人导向为主的利己主义文化看成是碎片化的、本位主义的、浪费资源的、不共享和不友善的。这里仅给出一例,就能很有力地证伪这个认识,那就是Linux文档有着令人震惊的广度、深度和质量。程序员痛恨写文档似乎已经成为一个不争的事实,那为什么Linux黑客们还要写出这么多文档?很明显,Linux自由的“egoboo”市场比那些有重金投资的商业软件公司,能够产生更有道德、更利他的行为。
19.如果开发协调者有一个至少像Internet这样好的沟通媒介,并且知道如何不靠强制来领导,那么多人合作必然强于单兵作战。
本文是《大教堂与集市》的一些摘录。