书评:《人月神话》
原文:Book Review
作者:John Baldwin
《人月神话》及其对 FreeBSD 项目的现实意义
书名:The Mythical Man-Month
作者:Frederick P. Brooks Jr.
出版社:Addison-Wesley Professional;周年纪念版(1995)
纸质版定价:$42.99
电子版定价:$34.99
ISBN-10:0201835959
ISBN-13:978-0201835953
页数:336
《人月神话》是软件开发领域的经典著作,由一系列短文构成,以轶事证据为依据给出实用建议。这些文章不讨论具体技术细节,而关注人如何开发软件。
我初次阅读 TMM(即本书另一简称)是在大学毕业不到一年时,由 FreeBSD 开发者推荐。尽管自 1975 年初版以来,部分编程实践已发生变化(例如高级编程语言的普及与交互式调试),但作者 Fred Brooks 指出的许多问题至今仍具现实意义。
Brooks 首先讨论的议题之一,是区分简单计算机程序与产品。开发者为自己完成某项任务而草草写就的程序只是计算机程序;而产品必须能被他人使用,必须正确处理任意输入(包括错误输入),必须有文档说明如何使用、程序适合或不适合哪些任务。开发产品所需的工作量远超实现初始计算机程序的时间。在 FreeBSD 中,程序至少应有一份手册页;但 Handbook 中的补充文档往往也是必需的(虽然并不总是齐全)。
同样,将计算机程序设计为计算机系统的一部分也需要相当的工作量。属于系统的程序应当为用户提供一致的接口,也应当提供一致的协作方式。类 UNIX 系统通过管道连接进程的输入输出流来达成一致性。但还有其他方式可实现一致性,例如对常用命令行标志复用相同的选项字母,或在手册页中使用一致的措辞。
第 2 章讨论软件项目的进度安排,特别要破除“为软件项目增加开发者能线性提升生产力(并缩短工期)”的观念。Brooks 强调了增加开发者带来的沟通成本与培训所需的额外时间。他还指出,许多软件项目存在必须串行(而非并行)完成的关键路径任务。这些事实提醒人们,不要对进度延误做出过于乐观的回应,特别是不要把“为进度落后的项目追加开发者”作为条件反射式的反应。
FreeBSD 的发布并不像 Brooks 例子设想的那样,将硬性截止日期与一组必备特性绑定在一起。但 Brooks 的若干观察仍适用。即便可以放弃特性、专注于在指定日期发布,FreeBSD 的发布(尤其是新分支上的 X.0 发布)仍经常延期。延期通常源于关键路径上的任务,例如等待一份计划在发布前后公布的待发安全公告。FreeBSD 拥有庞大的开发者群体,沟通占用大量时间。
第 3 章提出一种团队组织模型,类似于外科手术团队的组织方式。每个团队由一名负责为最终产品编写代码的“外科医生”以及若干辅助成员组成。FreeBSD 开发者通常并未按 Brooks 设想的方式组织为团队。但 Brooks 的模型确实突出了除编写代码之外对开发至关重要的任务,其中两位团队成员专门负责工具与测试。
过去十年,FreeBSD 社区在改进 FreeBSD 上的可用工具方面投入了大量精力,包括为 Clang 等工具链组件采用更现代的替代品,引入 DTrace 等全新工具。改进调试器与性能分析工具的工作仍在继续。
FreeBSD 开发者还扩大了测试覆盖。多年来,FreeBSD 源码树中混杂着一些并不定期运行的测试,部分测试使用通用框架,但许多测试是独立脚本。经多位开发者努力,FreeBSD 引入 kyua 测试框架,并于 10.1-RELEASE 首次随带一组基于 kyua 的测试。过程中从 NetBSD 引入了许多既有测试,并将既有测试转换到新框架之下。这些测试如今可作为自动化任务轻松运行。这一领域仍有大量工作要做,不仅需要补充既有测试套件的空缺,还需要新增诸如安装程序测试之类的新类别测试。
第 4 章强调概念完整性的重要性,并提出在大型系统中实现概念完整性的方案。Brooks 断言:“概念完整性是系统设计中最重要的考量。”他指出,实现概念完整性的唯一办法是限制参与设计的人数。Brooks 建议将架构与实现分离:设计集中于架构师头脑中,而实现的工作量分摊给更广的开发者群体。FreeBSD 并未在这些角色之间划出明确界线,也没有任何正式的、系统范围的架构师。非正式情况下,FreeBSD 开发者会请求同行评审,对于系统特定部分的修改,也存在若干其评审被广为寻求的个人。在某些情况下,这些个人事实上就是相应部分的架构师,但并未正式任命。FreeBSD 过去曾尝试使这一过程更为正式,但都未成功。
第 5 章讨论第二系统效应。Brooks 记录了开发者的倾向:在系统第一版中精简特性,到了第二系统又走向另一极端。开发者完成初始版本后,便给 2.0 版本添加所有可想到的特性。Brooks 称这一系统“是人类设计过的最危险的系统”。结果是臃肿、概念完整性差的系统。FreeBSD 并不完美,系统中的某些部分确实深受这一倾向之苦。我们的开发者彼此学习,也从自身过往失误中学习。同行评审有时能让我们更早而非更晚地修正失误,但并非总能如此。然而,FreeBSD 的开发者社区提供了一处场所,开发者在此可受到指导并被提醒诸如第二系统效应之类的趋势。
第 6 章和第 7 章强调有效沟通与组织对于多人协作开发的重要性。FreeBSD 开发者与社区成员通过邮件、Web 论坛、IRC 等实时聊天工具交流。但社区也认识到面对面交流的价值,并高度重视会议、开发者峰会与用户组会议中的“面对面时光”。社区不仅作为参会者,也作为组织者、演讲者与志愿者积极参与这些会议。组织仍是 FreeBSD 面临的挑战。FreeBSD 项目的正式成员以开发者为主,但项目需要编码之外的多种技能。这并非说项目完全无组织,但项目为达到最高效能所需的许多非编码任务确实存在覆盖缺口。
第 8 章讨论系统项目所需时间与资源的估算。Brooks 引用了若干研究的结果,这些研究分析了影响系统项目进度的若干因素。其中一项研究首先指出:程序员工作日中相当一部分时间会被会议、为其他项目做的工作(如高优先级 bug 修复)或硬件故障等因素消耗。其他研究则指出,随时间产出的代码行数会随程序复杂性而变化。所调研的项目中,操作系统最为复杂(因而开发最慢)。
第 9 章讨论大型程序各部分之间的内存分配管理。从技术角度看,本章大部分内容已被虚拟内存淘汰。但本章仍有几处亮点。首先,Brooks 讲述了 OS/360 开发中的一段轶事:某些做法导致系统性能极差。每个模块都被分配了大小限制,各模块开发者采取覆盖、向邻近模块“借用”空间等变通手段,未顾及对系统整体的影响。每个模块在自身分配的空间内单独运行良好,但整体系统因过度磁盘 I/O 满足覆盖请求而受累。这个故事带来的若干教训之一是:必须顾及系统整体效应,不能只盯着某一模块。本章的第二个要点是数据表示对性能的重要性。数据表示往往决定了约束性能范围的算法。这种情况下,大幅性能改进来自改变数据表示,而非优化既有代码流程。
第 10 章中,Brooks 讨论正式文档的重要性及其在设计中的作用。具体而言,推导规格说明迫使人考虑大量设计决策,否则这些决策在编码会话深处才会被发现。这恰恰是 FreeBSD 项目不太擅长的领域。作为开源项目,FreeBSD 的开发者不会拿到外部客户给定的规格说明据以设计。许多情况下,FreeBSD 的开发者自身就是 FreeBSD 的用户,将自己的设计需求套用到 FreeBSD 上。此外,项目积极寻求客户反馈以确定其需求。我们虽不能总是满足每一项需求,但确实会据其设定项目方向。供应商峰会是项目与客户接洽的工具之一,常与既有会议或开发者峰会同期举办。如果你是希望与项目接洽的 FreeBSD 客户,请联系作者或其他 FreeBSD 开发者。我们希望听到你的声音。
第 11 章聚焦变更。软件产品的需求会随时间变化,软件所运行的硬件亦然。Brooks 鼓励开发者拥抱变更、为变更做计划,而非与之对抗。对于新软件项目(乃至新子系统),常需要构建初始原型以更好地理解所求解的问题。正如推导规格说明会迫使人考虑若干设计决策,实现原型也会暴露大量意料之外的问题。麻烦出在人们假定这一初始版本必须作为最终产品发布,而不是有自由去重新架构实际产品的设计。正如在软件设计中要为变更做计划,构建软件的组织也要为设计做计划。对 FreeBSD 而言,这意味着构建并维护一个社区,而不仅是源代码。FreeBSD 项目在其整个生命周期中得益于许多人的贡献。个人在为 FreeBSD 工作一段时间后,常转向其他兴趣或任务。FreeBSD 在社区的这些波动中存活下来,并持续成长。
然而,我们必须继续积极欢迎并吸纳新人加入社区。工具的重要性是第 12 章的主题。虽然提及了第 3 章中的工具匠,但本章关注的是编程工具之外的话题。具体而言,Brooks 讨论了在多名开发者之间共享新硬件访问权的权衡、模拟器相对真实硬件的重要性、交互式调试的益处。交互式调试如今被开发者视为理所当然,但另两个话题至今仍具前瞻性。在 arm64 平台上启动 FreeBSD 时,因可用机器数量有限,遇到了目标机调度方面的约束。QEMU 等模拟器也允许开发者坐在桌面之前,在更广的平台上测试 FreeBSD。
Brooks 接下来在第 13 章讨论系统调试。本章开篇强调架构与设计。在规划与设计阶段偷工减料,会埋下系统不稳定、漏洞百出的种子。Brooks 倾向于自顶向下的设计方法,通过迭代细化丰富设计细节。本章大量篇幅警示系统测试(如今称为集成测试)中的各种捷径。第一,应单独测试和调试各组件,而非尝试同时测试多个组件。前者需要更多“脚手架”,但后者中组件间的 bug 可能以令人意外的方式相互影响,导致更难调试的故障。有时跨组件的多个 bug 在更大规模的测试中相互抵消,造成正确性的假象。第二,不要吝啬为测试所需而最终产品中用不到的额外代码与工具。这些代码与工具能实现更详细的组件测试,从而在后续集成测试中节省时间与挫败。第三,维护一份当前系统的标准副本,新组件与在制组件都对照该副本测试。对该系统的变更应按计划周期性发布,为在制组件提供相对稳定的测试期。最后,新组件应一次一个地加入系统,每加入一个组件都运行完整的回归测试套件。
第 8 章讨论的是软件项目的规划与估算,第 14 章则关注项目启动后的管理。Brooks 的第一个建议是制定包含具体里程碑的进度表。含糊的里程碑会导致管理层之间(或管理者与开发者之间)沟通含糊。Brooks 还警告不要微观管理。管理者跳出来纠正被管理者本有能力解决的问题时,被管理者会以隐瞒问题报告作为自卫。为促进清晰沟通,管理者必须愿意接受坏消息,而非立即发号施令。
第 15 章讨论程序文档这一主题。Brooks 首先指出程序文档所需的三个层次:如何使用程序、如何测试程序、如何修改程序。Brooks 指出,成书时的根本问题之一,是文档与源代码分离存放,不可避免会过时。他提出自文档化源代码作为这一问题最可行的解决方案。自文档化源代码如今被广泛采用,高级编程语言的使用更使其趋于合理。Javadoc、Doxygen 等源码内文档系统被许多软件项目用于将文档与源代码一并存放,并可方便地转换为更易读的格式。虽然 FreeBSD 内核源代码的某些部分确实使用了 Doxygen 注释,但 FreeBSD 前两个层次的文档大多与源代码分离存放。有些文档确实不适合存放在源代码中,但 API 描述可能更适合用 Doxygen 之类的系统,而非独立的手册页。
Brooks 在描述最后一个文档层次时提到的文档话题之一,是解释设计决策的“为什么”。虽然这些决策有时会在源代码注释中讨论,但在现代软件项目中,这些决策往往在源代码仓库日志中解释。FreeBSD(和 BSD)有使用源代码控制的悠久历史,FreeBSD 项目文化期望并鼓励深思熟虑的日志信息,解释变更的“为什么”,而不仅是“是什么”。
TMM 的 20 周年纪念版收入了题为《没有银弹》的文章,作为第 16 章。该文最初发表于 1987 年,讨论软件生产力。Brooks 特别断言,在接下来的十年(1987-1996)中,不会出现任何“银弹”将软件开发者的生产力提升一个数量级。Brooks 首先将软件开发的困难分为两类:本质(essence)与偶然(accidents)。软件的本质是其抽象设计,包括所使用的数据表示和算法。偶然则涉及表达这一抽象设计时的困难,包括硬件的限制、用于表达思想的语言和形式。
Brooks 认为,1987 年之前提升软件生产力的三大进步都作用于偶然困难。高级编程语言允许程序员更简洁地表达概念,同时将许多琐碎细节交给编译器处理。分时系统缩短了开发期间的响应时间,使开发者能够长时间保持专注。统一的编程环境提供了将既有程序连接起来以解决更大型任务的标准方式,如在 UNIX 中使用 I/O 管道。
软件开发的本质依然困难。软件是复杂的。Brooks 指出,在人创造的许多其他物件中,重复元素很常见,但在软件中,重复元素会被合并为子程序。软件项目的新版本并非仅仅通过复制既有模块而形成,而是包含必须与既有模块交互的全新组件。同时,软件是不可见的,其结构难以详细可视化。即便使用可视化,也被迫对信息加以约束,例如聚焦于控制流而非数据流。
Brooks 断言,源自这一本质的困难是软件固有的。1987 年时,他未见任何提出或实践中的技术,能在随后十年中以实质性方式攻克这些困难。
第 17 章中,Brooks 回应了对《没有银弹》的部分批评,并在原论文发表九年后审视了更多的银弹候选方案。
为鼓励对 TMM 原著中各种主张展开更激烈的讨论,第 18 章提供了要点列表。
最后,第 19 章中,Brooks 在原著首次写成 20 年后重新审视了前 15 章的内容。在某些方面,这是全书最有趣的一章。Brooks 指出哪些主题因技术进步而过时,同时肯定了他认为仍然成立的主张。《人月神话》的力量无疑源自其写成数十年后仍具现实意义。这部分归功于《没有银弹》中描述的软件本质。技术进步并未改变软件的根本构造。其次,TMM 很大程度上讨论的是人,以及与软件打交道的人之间的互动。虽然新技术确实影响我们的生活方式和任务中的“偶然”部分,但人终究是人。
JOHN BALDWIN(<jhb@freebsd.org>)于 1999 年以提交者身份加入 FreeBSD 项目。他参与过系统的多个领域,包括 SMP 基础设施、网络协议栈、虚拟内存和设备驱动支持。John 曾任职于核心团队和发布工程团队,并每年春季组织一次 FreeBSD 开发者峰会。
最后更新于