Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2.4 早发布,常发布

“早发布、常发布”是 Linux 开发模式的关键组成部分。在过去,大多数开发者(包括我)都认为,对于比玩具项目规模更大的项目而言,“早发布、常发布”听起来并不靠谱,因为早期版本按定义几乎就是有很多 bug 的版本,而你不想把用户的耐心耗光。

这种信念让人们更加倾向于大教堂式开发模式。如果首要目标是让用户尽量少遇到 bug,那你就会每隔六个月(或更久)才发布一个版本,并在发布间隔期间拼命 debug。Emacs 的 C 语言核心就是按照这种方式开发的,而 Lisp 库实际上并不这样——因为除了 FSF 控制的版本,还有活跃的 Lisp 代码仓库(archives),你可以不受 Emacs 发布周期的限制,自己去那里找到新代码和开发中的版本。2

其中最重要的一个是俄亥俄州立大学的 Emacs Lisp 代码仓库,它预示了当今大型 Linux 代码仓库的精神和许多特性。但我们中很少有人真正深入思考过我们在做什么,或者这个代码仓库的存在本身,是如何揭示 FSF 大教堂式开发模式的问题的。大约在 1992 年,我曾认真尝试将大量俄亥俄州大学的代码正式合并到官方的 Emacs Lisp 库中。我遇到了人事上的阻力,这件事最后基本没成。

但一年后,Linux 变得广为人知。很明显,那里正在发生一些不一样的事,而且健康得多。林纳斯的开放式开发政策与大教堂式开发截然相反。网上的 Linux 代码仓库越来越多,多个发行版不断涌现。而所有这一切,都是由前所未有的核心系统频繁发布所驱动的。

林纳斯用一种最有效的方式,把他的用户当成协作开发者:

早发布,常发布。倾听你的客户。

林纳斯的创新,与其说是快速迭代发布、吸纳大量用户反馈(这种做法在 Unix 社区已有很长时间的传统),不如说在于将这种模式扩展到与所开发系统的复杂性相匹配的强度。在早期(大约 1991 年),也不是没听过他一天发布好几次新内核!正是因为他比任何人都积极地培养他的协作开发者,并用互联网进行协作,才取得了成功。

但这究竟是怎么运作的呢?这是我可以复制的吗?还是说它依赖于林纳斯·托瓦兹某种独特的才华?

我不这么认为。当然,林纳斯是一位非常优秀的黑客。我们这些黑客当中有多少人能从头构建出一个完整、产品级的操作系统内核?但 Linux 并不代表任何令人敬畏的概念性飞跃。林纳斯并不是(或者至少目前还不是)像理查德·斯托曼或詹姆斯·高斯林(James Gosling,开发了 NeWS 和 Java)那样的设计创新天才。在我看来,林纳斯更像是一位工程和实现方面的天才,他对避免 bug 和开发死胡同有着第六感,并且真正擅长找到从 A 点到 B 付出最少努力的路径。事实上,Linux 的整个设计都散发着这种品质,并反映了林纳斯本质上保守和简化的设计方法。

那么,如果说快速发布和充分利用互联网媒介并不是偶然,而是林纳斯的工程天赋洞察到“付出最少努力路径”的组成部分,那么他当时到底在最大化什么?他从这套机制里榨出了什么?

这样提问,答案就很明显了。林纳斯是在不断地刺激和奖励他的黑客兼用户——通过让他们参与其中、获得自我满足感的前景来刺激,通过看到他们的工作不断(甚至每天)改进来奖励。

林纳斯的直接目标是最大化投入到 debug 和开发上的总工时,即使这可能以代码不稳定、遇到棘手的严重 bug 而损失用户基础为代价。林纳斯的行为表明他相信类似这样的道理:

  1. 只要有足够多的测试者和协作开发者,几乎所有问题都能被快速定性,并且对有些人来说,修复方案显而易见。

或者换个更不正式的说法:“眼睛够多,bug 不躲。” 我管它叫“林纳斯定律”。

我最初的表述是每个问题“对某人来说是一目了然的”。林纳斯不同意,认为理解和修复问题的人不一定是(甚至通常不是)最先发现问题的人。“有人发现问题,”他说,“然后其他人理解问题。而且我敢这么说,发现问题才更难。” 这个修正很重要;我们将在下一节更详细地考察 debug 实践时看到这一点。但关键点是,过程的两个部分(发现和修复)都倾向于很快发生。

我认为,林纳斯定律正是大教堂模式和集市模式之间核心差异的基础。大教堂模式认为,bug 和开发过程中的问题是棘手的、难以察觉的、深层次的现象。需要少数专注的人花好几个月审查,才能有信心说已经搞定了所有问题。因此,发布的间隔很漫长,而当大家期待已久的版本发布后并不完美时,就难免让人失望。

另一方面,在集市模式看来,你假定 bug 通常是浅显的现象——或者至少,当成千上万个热切的协作开发者上手折腾每个新版本时,这些 bug 就会很快变得浅显。因此,你频繁发布,获得更多的修正,而好处是,即使偶尔有翻车的版本流出,你的损失也不大。

就这么简单。这就足够了。如果“林纳斯定律”是假的,那么像 Linux 内核这样复杂的系统,被这么多开发者修改过,应该在某个时刻因为无法预见的恶性交互和还没发现的深层 bug 的重压而崩溃。另一方面,如果它是真的,那它就足以解释 Linux 为什么 bug 相对较少,并且能持续运行数月甚至数年。

话说回来,这也许本不该如此令人惊讶。社会学家多年前就发现,一群同样专业(或同样外行)的观察者,他们的平均意见作为预测,可靠性远比随机挑选的单个观察者的意见要高,他们管这叫德尔菲效应(Delphi effect)a。看起来,林纳斯所展示的是,这甚至适用于 debug 操作系统——即德尔菲效应能够在操作系统内核的复杂度层面上驾驭开发复杂性。3

Linux 情况中一个明显有助于德尔菲效应的特殊之处在于,任何给定项目的贡献者都是自我筛选的。一位早期回应者指出,收到的贡献并非来自随机样本,而是来自那些对软件足够感兴趣的人,他们会使用软件,了解其工作原理,尝试找到自己遇到问题的解决方案,并实际产生一个看似合理的修复。任何通过这些筛选的人,极有可能贡献出有价值的东西。

林纳斯定律换句话说就是“Debug 是可并行化的”。尽管 debug 需要开发者与某位协调者沟通,但它并不要求 debug 的人之间进行大量协调。因此,它不会陷入增加开发复杂度的二次复杂度和管理成本困境。

在实践中,debug 的人重复工作所导致的效率损失——虽然理论上存在——在 Linux 社区中似乎从来都不是问题。“早发布、常发布”策略的一个效果就是通过快速传播反馈的修复来最小化这种重复。4

布鲁克斯(《人月神话》(The Mythical Man-Month)的作者)甚至对杰夫b的观点做了一个即兴评论:“维护一个广泛使用的程序的总成本通常是其开发成本的 40% 或更多。令人惊讶的是,这个成本受用户数量影响很大。更多的用户发现更多的 bug。”[强调为作者所加]

更多的用户会发现更多的 bug,因为增加用户,就增加了更多的方式对程序进行压力测试。当这些用户同时也是协作开发者时,这种效应会被放大。每个人都会以略微不同的感知方式、不同的分析工具集,从不同的角度来处理 bug 定位,德尔菲效应似乎正是由于这种差异性而生效。在 debug 的特定背景下,这种差异性也倾向于减少重复工作。

因此,增加更多测试 Beta 版本c的用户可能不会降低开发者眼中当前“深层” bug 的复杂性,但它增加了一些人恰好适配该问题的工具集,让这个 bug 对那个人来说变得浅显。

林纳斯也做好了风险对冲。为了防止出现严重 bug,Linux 的内核版本采用特定的编号方式,让潜在用户可以选择最新的稳定版本,或者冒引入 bug 的风险选择最新的实验版本。大多数 Linux 黑客还没有系统性地模仿这种策略,但也许他们应该效仿;因为两种选择都存在,这让两者都更有吸引力。5

作者注

  1. 在互联网大爆发之前,就已经存在成功的、集市风格的开源开发范例,并且与 Unix 和互联网传统无关。1990 年至1992 年间,主要为 DOS 机器开发的 info-Zip (http://www.cdrom.com/pub/infozip/) 压缩工具就是这样一个例子。另一个例子是 RBBS 电子公告板系统(同样用于 DOS),它始于 1983 年,并发展出了一个足够强大的社区,尽管互联网邮件和文件共享在技术上远优于本地 BBS,但直到现在(1999 年中)仍有相当规律的发布。虽然 info-Zip 社区在一定程度上依赖互联网邮件,但 RBBS 的开发者文化实际上能够在一个完全独立于 TCP/IP 基础设施的 RBBS 系统上,建立一个成规模的在线社区。

  2. 透明度和同行评审对于驾驭操作系统开发的复杂性是有价值的,说到底这并不是什么新概念。1965 年,在分时操作系统历史的早期,Multics 操作系统 (http://www.multicians.org/fjcc1.html) 的联合设计师科尔巴托(Corbató)和维索茨基(Vyssotsky)写道:

    预期在 Multics 系统持续运行时,将其发布……这样发布有两个可取的原因:首先,系统应经受住感兴趣的读者的公众审查和批评;其次,在一个复杂性日益增长的时代,我们有义务让现在和未来的系统设计者尽可能清晰地了解内部操作系统,以揭示基本的系统问题。

  3. 约翰·哈斯勒(John Hasler)提出了一个有趣的解释,说明为什么重复工作似乎并没有对开源开发造成净负担。他提出了我称之为“哈斯勒定律”的观点:重复工作的成本倾向于随团队规模的平方以下增长——即,其增长速度慢于为避免重复工作而需要的规划和管理开销。

    这个说法实际上并不与布鲁克斯定律矛盾。情况可能是,总体的复杂性开销和易受 bug 影响的程度随团队规模的平方增长,但重复工作带来的成本却是一个特殊案例,其增长较慢。这并不难找到合理的解释。首要原因是一个毋庸置疑的事实:让不同开发者的代码在功能边界上达成一致、避免重复工作,要比防止那些跨系统的、计划外的糟糕交互容易得多——而后者正是大多数错误的根源。

    林纳斯定律和哈斯勒定律的结合表明,软件项目实际上存在三个关键的规模区间。在小型项目上(我认为是1到最多3个开发者),除了指定一个首席程序员之外,不需要更复杂的管理结构。在此之上,存在一个中间范围。在这个范围内,传统管理的开销相对较低,在避免重复工作、bug 追踪和确保细节不被忽视方面,它的收益是正的。

    然而,再往上走,就进入了大型项目的区间。在这个区间里,传统管理的成本和问题会急剧上升,它的增速远比重复工作本身可能带来的成本要快。特别值得注意的是,传统管理在结构上难以利用“多眼效应”,甚至净收益会推向零——而我们前面已经看到,这种效应在发现 bug 和细节方面,远比传统管理有效。

  4. Linux 实验版与稳定版的区分,除了对冲风险之外,还有另一项功能,二者相关但并不相同。这种区分解决了另一个问题:截止日期的致命性。当程序员同时受制于不可更改的功能列表和固定的最终交付日期时,质量就会被抛到脑后,就很可能在酝酿一场巨大的混乱。得益于哈佛商学院(Harvard Business School)的马尔科·扬西蒂(Marco Iansiti)和艾伦·麦科马克(Alan MacCormack)向我展示了相关证据,表明放宽这两个约束中的任何一个,都能使日程安排变得可行。

    一种做法是固定截止日期,但保持功能列表的灵活性,允许未能在截止日期前完成的功能被移除。这基本上就是稳定版本内核分支的策略;艾伦·考克斯(Alan Cox,稳定版内核的维护者)以相当规律的间隔发布版本,但并不保证特定 bug 什么时候会被修复,或哪些功能会从实验分支回溯移植过来。

    另一种做法是设定预期功能列表,并在完成时才交付。这基本上就是“实验”内核分支的策略。德马科(De Marco)和 利斯特(Lister)引用的研究表明,这种调度策略(“完成后叫醒我”)不仅带来了最高的质量,而且平均而言,其交付时间也比“保守的”或“激进的”调度策略更短。

    我开始怀疑(大约在 2000 年初),在我早期版本的这篇文章中,我严重低估了这种“完成后叫醒我”的反截止日期策略对开源社区生产力和质量的重要性。1999 年匆忙推出 GNOME 1.0 的普遍经验表明,发布先行版本的压力会抵消许多开源通常能带来的质量优势。

    很有可能会发现,开源的流程透明性,连同“完成后叫醒我”的调度策略和开发者的自我选择,很可能会被证明是其质量的三个同等重要的驱动因素之一。

译者注

a. 德尔菲效应(Delphi effect) :社会心理学概念,指群体独立判断的综合往往优于个体判断的现象。该现象在思想史上可追溯至孔多塞(Marquis de Condorcet)的“陪审团定理”(Jury Theorem,1785)——若每个个体做出正确判断的概率略高于随机,则多数投票的准确率随群体规模增大而趋近于 1。20 世纪的社会学家将其归入“集体智慧”(Collective Intelligence)研究谱系。ESR 借此说明:在同时满足群体多样性、个体独立性,以及有效聚合机制的条件下,大规模协作开发才能够通过统计收敛的方式高效定位和修复 bug。

b. 此处的 Jeff 疑为杰夫·邓特曼(Jeffrey S. Duntemann)。他是一位知名的技术作家与编辑,90 年代末期在计算机书籍和杂志领域很活跃。