第 19 章 · 20 年后的《人月神话》
题记两句针锋相对:帕特里克·亨利说"只能根据过去判断将来",埃德蒙·伯克说"然而永远无法根据过去规划将来"。这一章正站在这两句之间。
这一章讲了什么
为什么要出版纪念版
作者用一个场景开场:飞机上,邻座的陌生人正在读《人月神话》,他一路等着看对方会不会有反应,最后在机舱门口忍不住问"这本书如何?“对方回答——“噢!这里面的东西我早就知道。“他决定不介绍自己。
这个段子后面接着一个真问题:一本 20 年前写的、讲 30 年前经验的书,为什么还相关?他反驳了两种常见解释。第一种说"软件开发学科没有正确发展”,靠软硬件生产率对比来支持;他指出反常的不是软件慢,而是硬件快得不符合人类历史的常理——那是制造业从装配转向流水线、从劳动密集转入资金密集的结果。第二种解释是这本书其实在讲"团队中的成员如何创建事物”,软件只是顺带提及。他认为这个说法有些道理:“人类历史是一个舞台,总是上演着相同的故事……剧本变化非常缓慢,而舞台的布局却在随时改变。“所以这本书是关于人与团队的,它的淘汰过程会是缓慢的。
依然成立:概念完整性与结构师
他重申:整洁优雅的编程产品必须给每位用户提供条理分明的概念模型——描述应用、实现方法以及用户界面的使用策略;而用户感受到的概念完整性是易用性中最重要的因素。他用 Macintosh 应用界面的统一作为正面例子,也承认 MS-DOS 那种粗糙但统一的接口同样有效。
接着重申第 3、4 章的角色安排,并且语气比 1975 年更确定:委派一名产品结构师是最重要的行动。结构师负责用户能实际感受到的概念完整性,是概念模型的所有者、用户的代理,在功能、性能、规模、成本与进度的权衡中代表用户的利益。这个角色是全职工作,只有在最小的团队里才能与经理合并——他给了一个精确的比喻:”结构师就像电影的导演,而经理类似于制片人。“对大型系统,还要由主结构师把系统分解为子系统,让边界落在接口最小、最易严格定义的地方,并递归下去。
他甚至说,在软件工程实验课上坚持让每四名学生的小组选出不同的经理与结构师——“在如此小的队伍中定义截然不同的角色可能有点极端,但我仍然发现这种方法即使对小型团队也运作良好”。
通用工具的新难题:盲目的功能与频率猜测
个人计算机革命带来一个新处境:结构师要为大型、不确定的用户群设计,而不是为某个公司的单一应用设计。他随即指出一个悖论:“设计通用工具比设计专用工具更加困难,这是因为必须为不同用户的各种需要分配权重。”
由此产生"盲目的功能”:以性能甚至易用性为代价,不断增加边界实用功能。功能建议在初期总是很吸引人,性能代价要到系统测试才显现,而"随着功能一点一点地增加,手册慢慢地变厚,易用性损失以不易察觉的方式蔓延”。这种诱惑对存活多代的大众产品尤其强烈——数百万用户提出的上千个功能需求,每一个看起来都是"市场需要它"的证明。而常见的情况是,原结构师已经被嘉奖、调去别的岗位,接手的结构师在均衡表达用户整体利益方面往往经验不足。他引了对 Word 6.0 的批评作例子:功能被打包进去,产品变得又大又慢。
对策是定义用户群。用户群越大越不确定,就越需要明确地定义它,因为每个设计者心里的用户图像都不同,而结构师的图像会有意无意地影响每个决策。要记录的属性有四条:他们是谁;他们需要什么;他们认为自己需要什么;他们想要什么。
然后是方法上最有意思的一步:这些属性实际上是概率分布,而对着尚未清晰定义的对象做调查,既不确定又昂贵。所以他给出的建议是——结构师应该"猜测"或"假设"一整套完整的属性和频率值。理由有三:仔细猜测频率的过程会迫使结构师非常具体地考虑用户群;把它写下来会引发讨论,澄清不同设计者认识上的差异;明确列举频率能让人看出哪些决策依赖哪些用户群属性,这本身就是一种非正式的敏感性分析。他的总结是一句可以直接抄下来的话:“为用户群的属性明确地记载各种猜测。清晰和错误都比模糊不清好得多。”
他还顺手拆掉一个常见的误读。一位敏锐的学生说,《人月神话》自相矛盾地推荐了灾难处方——第 11 章让计划发布新系统的第二个版本,第 5 章却说第二个系统最危险。作者的回答是”他上当了":第 5 章的"第二个"是第二个实际系统,是引入大量新增功能与修饰的后续系统;第 11 章的"第二个"指开发第一个实际系统所进行的第二次尝试,它同样受进度、人员和范围约束。这是语言造成的差异。
WIMP 界面的成功
他把窗口、图标、菜单、指针选取称为几十年中最令人印象深刻的进步,并梳理了它的传播链:1968 年 Doug Engelbart 与斯坦福研究院团队公开提出,Xerox PARC 的 Bob Taylor 团队把它用在 Alto 上,Steve Jobs 先在跑得太慢的 Lisa 上尝试,1985 年在 Macintosh 上商业成功,随后被 IBM PC 兼容机上的 Windows 采用。
他从"人月"的视角解读这次成功:WIMP 是通过类比获得的概念完整性。桌面比喻加一致而细致的扩展——窗口用覆盖而非排列(直接来自桌面);拖放直接模仿用手拾起东西;图标与嵌套文件来自桌面文档;回收站、剪切复制粘贴,甚至把磁盘图标拖进回收站来弹出磁盘。“象征手法是如此的贴切,扩展是如此的连贯一致,新用户常常会被它所体现出的理念打动。”
他接着指出 WIMP 在哪里超越了桌面比喻:菜单与单手操作。真实桌面上人是直接操作文档的,而要求别人做事要发出指令,现有的处理能力无法可靠解释自由形式的命令;界面设计者于是从常用的文档操作里挑出命令,形成类似公文的"便条",让用户在语义标准的菜单里选择。他还分析了"命令表达与双光标问题":命令是祈使句,需要同时指定动词和名词,而自然的方式是两只手各控一个光标——但没有任何商业系统能容纳双鼠标,于是句法被迫改成"先选名词再选动词",光标在数据区与菜单区之间来回奔波,“每一次都丢弃了一些有用的位置信息”。他提出一个更优雅的方案:一只手在键盘上指定动词,另一只手用鼠标指定名词,光标便始终留在数据区。Mac 上把最常用的操作编码到键盘下方的 Z、X、C、V、S,正是这个思路的延伸——快捷键显示在菜单动词旁边,新手可以查,熟练用户直接盲打,且撤销让试错成本极低,于是"从新手向熟练用户的逐渐过渡"得以实现。
最后他点出 Mac 界面真正的高明之处:它用把界面固化进只读内存的方式,让第三方开发者使用标准界面比自造界面更容易、更快——这正是第 6 章推荐的"直接整合"技术的杰出例子。至于 WIMP 的命运,他预言它会在一代之内成为历史:指针选取仍是表达名词的方式,而语音无疑会成为表达动词的方法。
瀑布模型错了,增量开发更佳
这一节是全书最重要的自我否定。他直接写道:"现在我认为这是错误的,并不是因为它太过极端,而是因为它太过简单。“错在哪里?——“未雨绸缪"隐含地假设了传统的顺序/瀑布模型。
他指出瀑布模型的两个基本谬误:其一,它假设项目只经历一次过程,而且体系结构出色易用、设计合理可靠、编码实现能随测试顺畅修改——“换句话说,瀑布模型假设所有错误发生在编码实现阶段”。这导致系统测试甚至用户测试被放在构件过程的末尾,只有投入了全部开发投资之后,才会发现无法接受的性能问题、笨拙的功能和用户意图的错判。其二,它假设整个系统一次性被构建。他还补了一段历史后果:这个模型被奉为军用标准 DOD-STD-2167,以致在多数有见地的从业者已经放弃它之后仍然存活了很久。
因此他强调"必须存在逆向移动”:下游的经验和想法必须跃行而上,有时要跨过一个阶段去影响上游的活动——设计实现发现某些功能定义削弱性能,体系结构就得调整;编码实现发现功能让空间剧增,体系结构与设计实现都得改。在把任何东西变成代码之前,可能已经往复迭代了两轮以上的"体系结构—设计"循环。
替代方案就是增量开发。他引 Harlan Mills 的实时系统做法:先构建一个闭环的框架系统——主循环加一堆空的功能子函数,编译、测试、让它跑起来;然后加上基本的输入输出模块,“瞧,一个可运行的系统出现了”;再一个功能一个功能地开发与增加模块,每个阶段都拥有一个可运行、经过调试和测试的系统,并同样方式做回归测试。在功能都基本可用之后,再逐个精化或重写模块——“增量地开发(growing)整个系统”。
它的收益被他列得很清楚:可以很早就开始用户测试;可以采用按预算开发的策略,彻底保证不会出现进度或预算超支——代价是牺牲允许的功能。他教了 22 年软件工程实验课,四名学生一学期做一套真正的实时系统,后期改用增量开发,“常常因为屏幕上第一幅图案、第一个可运行的系统对团队士气产生的鼓舞效果而感到震惊”。
他还补了两个具体的实践形态。其一是 Parnas 的"产品族”:把软件作为一系列相关产品来设计,预测后续版本的差异,搭一棵家族树,把不易变化的设计决策放在树的根部,使模块的作用最大化;这条策略还能延伸到增量开发过程中间的各个版本,让产品以最低限度的回溯代价生长。其二是微软的"每晚重建":Jim McCarthy 描述说,既然发布后要继续往可运行系统上加功能,那为什么最初的构建要不一样?于是从第一个里程碑开始每晚重建并跑测试,让构建周期成为项目的"心跳"——重建失败就停下整个流程直到问题解决,“在任何时间,团队中的每个人都了解项目的状态”。作者评论说这非常困难、需要大量资源、是规范化且开诚布公的流程,而它给团队的可信度决定了士气和情绪状态。他也记录了一个对照:Bell 北方研究所是每周重建 1200 万行的系统。
至于增量开发与快速原型的关系,他引 Harel 对原型的定义——“仅仅反映了概念模型准备过程中所做的设计决策的一个程序版本,它并未反映受实现考虑所驱使的设计决策”,并指出两者既可独立存在又可相互关联;而"从第一个里程碑开始构建"与快速原型的差别在于功能:里程碑版本可能还不值得任何人使用,而可发布版本在完整性上配备了实用功能集,在质量上能健壮运行。
Parnas 是正确的,我是错误的
这一节的标题直接就是结论。他回顾第 7 章的争论:OS/360 当时的决定是所有人都了解所有材料,每人一份约一万页的工作手册,Harlan Mills 主张"编程是一个开放性的公共过程";Parnas 则相反,认为模块应通过定义良好的接口封装,内部结构是程序员的私有财产。1975 年他站在 Mills 一边。
二十年后他写道:"**但是,Parnas 是正确的,我是错误的。**现在,我确信信息隐藏——现在常常内建于面向对象的编程中——是唯一提高软件设计水平的途径。"
不过他没有一边倒:任何技术用错都会变成灾难。Mills 的技术是让人理解接口另一侧的详细语义,而接口语义的曲解正是系统 bug 的来源;Parnas 的技术则在面对变更时健壮得多,更适合作为"为变更设计"的理念。
顺着第 16 章的判断(过去的进展多来自消除非内在困难,而彻底的进展必须处理根本困难),他认为路径是:承认程序由比语句更大的概念结构要素组成,并尽量只做参数化与组装。他梳理了面向对象的三步源流:Parnas 的模块信息隐藏是第一步,是面向对象的鼻祖;第二步是把模块提升为抽象数据类型;第三步才是继承。而他要强调的关键判断是——"我们希望从面向对象编程中得到的最大收获实际上来自第一步,模块封装,以及预先建成的、为了重用而设计和测试的模块或类库"。他并提醒:这些模块本质上就是第 1 章讨论过的编程产品,很多人想要大规模重用,却不愿付出构建产品级质量(通用、健壮、经过测试和文档化)模块的初始代价——“这种期望是徒劳的”。
Boehm 的数据:人月到底有多少神话色彩
他引 Barry Boehm 对 63 个软件项目的调查(多数为航空项目加 25 个 TRW 项目)与《软件工程经济学》,结论与《人月神话》充分吻合:人力与时间之间的平衡远非线性,用人月作为生产率的衡量标准实际上是一个神话。其中几条值得单独记住:
- 第一次发布的最优进度时间与估计工作量的立方根成正比(T = 2.5 × MM^(1/3));
- 计划进度长于最优进度时,成本缓慢攀升——时间越充裕,所花费的时间就越长;
- 计划进度短于最优进度时,成本急剧升高;
- 无论安排多少人手,几乎没有项目能在少于 3/4 最优时间的条件下获得成功——他说这条可以充分作为项目经理拒绝不可能的进度担保时的理论依据。
他还引了两项关于 Brooks 法则的研究。Abdel-Hamid 与 Madnick 的结论是:向进度落后项目增加人手总会增加成本,但并不一定总会使项目更加落后;而且由于新成员立刻带来需要数周弥补的负面效应,在项目早期加人比在后期更安全。Stutzke 的模型则在真实项目上做过验证:他在一次中期偏移后成功增加了一倍人手并保住原进度;他最值得注意的一条建议是——后期加入的开发人员必须愿意作为团队成员投入工作,而不是企图改变或改进流程本身。作者也指出这两个模型都没考虑"开发人员必须重新安排"这件事,而实际中这常常是很重要的一步。最后他的态度很稳:“这些细致的研究使’异常简化’的 Brooks 准则更加实用”——但他仍保留那句最简单的陈述,作为最接近真相的经验法则,用来警告经理不要对进度落后的项目采取本能的修补措施。
人就是一切,以及放弃权力的力量
他解释这本书为什么偏重管理而非技术:一半因为他自己在 OS/360 的角色,更根本的是他相信——对项目成功而言,人员的素质、组织和管理比工具或技术方法更重要。Boehm 的 COCOMO 模型支持这一点:团队质量是项目成功最大的决定因素,权重是下一个次重要因素的四倍。他因此说自己欣赏强大工具,但更鼓励对"人的关注、激励与培养"的持续研究,并推荐了 DeMarco 与 Lister 的《人件》——那本书主张"我们行业的主要问题实质上更侧重于社会学而不是科学技术",并给出了工作空间与生产率、缺陷水平之间的关联数据。
他还提醒一件容易被忽略的事:团队融合这种无形的特性极其关键。他观察过六七个项目在不同实验室之间的转移,没有一个是成功的——任务可以转移,但项目转移即使带着良好文档、先进设计和部分原班人员,新队伍实际上仍是重新开始。他判断,正是破坏原有团队的整体性导致了产品雏形的夭折。
接着是本章篇幅不多但语气最热烈的一节:放弃权力。他引 Schumacher 的"附属职能行使"原理——如果较低级别组织的自由和责任得以保留,中心权威实际上是得到了加强,整体反而更融洽繁荣。他记录了两个实践案例:微软的 Jim McCarthy 说每个队伍(30–40 人)拥有自己的任务、进度甚至构建发布流程,争论的仲裁者是团队而不是老板,“我简直无法形容授权和由团队对项目自行负责成功与否的重要性”;IBM 软件业务退休主管 Earl Wheeler 说,把长期集权的管理权力下放之后,“改进的质量、提高的生产率和高涨的士气,这就像是魔术”——团队是流程的所有者,他们有不同的流程,也是进度计划的所有者。作者自己也补了一句谨慎:与成员谈话显示,真正的下放"显得多少有些保守",但授权确实是朝正确方向迈出的一大步。
最令人惊讶的新事物、新产业与元编程
他说每位他交谈过的计算机带头人都承认,对微型计算机革命和它引发的塑料薄膜包装软件产业感到惊讶,这是《人月神话》之后三十多年里最重要的改变。他引 Schumacher 概括的三个目标——价格足够低廉使几乎所有人都能用、适用于小规模应用、满足人们对创造的渴望——指出个人计算机恰好同时满足了这三条。由此他还讲了一个更广的判断:写作、绘画、制图、音乐、摄影等领域进步的原因与软件创造相近,都是消除了次要困难——以前改一稿要重新打一遍,现在改变与评估之间不再丢失思维的连贯性,就像分时之于软件开发。
对软件产业本身,他做了细致的两次划分。传统软件产业仍是四类参与者(计算机提供商、应用程序用户、定制程序开发者、商业包开发者),而 Tom DeMarco 观察到的是整个行业被分解成各个特殊领域:“Ada 是最后一个通用语言,并且它已经慢慢变成了一门专业语言。“与此并行,是成品软件产业——产品以千百万的规模销售,整套软件包的成本可以低于一个支持程序员一天的费用。他注意到这个新产业的文化差异:创业公司行事自由、更关注结果而非流程,天才的个人程序员更容易获得认可,因此"很多新一代的明星人物被吸引到薄膜包装的软件产业”。
由此他给出一个可能比预想更重要的方向:买来开发。提高健壮性与生产率的唯一途径是提升抽象级别,使用模块或对象组合来开发;而特别有希望的趋势是把大众市场软件包当作平台,在上面搭建更定制化的产品(用塑料包装的数据库与通讯软件包做货运跟踪系统或学生信息系统)。他把这种为软件包用户做功能定制的行为称为元编程,并指出它并非新概念(1960 年代就有用汇编宏装备应用语言的专家小组),只是规模大了若干倍——“当我们期待 C++ 类开发的高效市场时,可重用元程序的市场正在悄无声息地崛起”。他判断它处理的确实是根本问题:成品软件包提供了大型功能模块与精心定制的接口,其内部的概念结构根本无需再设计。
他还把这类用户分成四个层次:直接使用用户、在单个应用上做模板或函数的元程序员、向应用添加新功能的外部功能作者,以及把若干应用当作更大型系统构件的元程序员——最后一类需求最没有被满足,也正是收获最大的一类。为此他提出需要一个额外的接口,即元编程接口(MPI):元程序要能在整个应用集合之上控制;软件集必须掌握用户界面;必须能像命令行传参那样调用任何应用的功能;还必须接收应用输出并解析成有类型的逻辑实体。他指出 Unix 的管道与标准 ASCII 文本格式是最早的形态,AppleScript 是优秀的当代例子。
状态与未来
他最后用一个类比收束全书:向化学工程系系主任 Jim Ferrell 请教化学工程与化学的区别,得到的答案是 Arthur D. Little 如何于 1918 年在 MIT 建立第一个工业化学系,专门发现、发展并讲授所有过程共享的技术基础——先是经验法则,接着是经验图表,然后是设计公式,最后是热传导、质量转移与动量转移的数学模型。作者说,近五十年后他仍被化学工程与软件工程发展之间的相似性震动。Parnas 曾说把软件称为"工程"只是一厢情愿,他承认这可能对;但化学工程的历程让他觉得二十七岁的软件工程并非没有希望,只是不够成熟,就像 1945 年的化学工程。
于是第 1 章那三个问题依然悬在最后:如何把一系列程序设计和构建成系统;如何把程序或系统构建成健壮的、经过测试和文档化的产品;以及如何维持对大量复杂性的控制。他的结论毫不轻松——软件工程的焦油坑在将来很长一段时间里会继续使人举步维艰,因为软件系统可能是人类创造中最错综复杂的事物,我们只能在自己力所能及或刚刚超越力所能及的范围内探索。这个行业需要的是持续发展、学会用更大的要素开发、善用新工具、采用经论证的管理方法,以及最后一条:良好的自我判断,和"能够使我们认识到自己不足——上帝所赐予的谦卑”。
我的判断
- 这一章是全书最有价值的一章,因为它是作者对自己二十年判断的清算,而不是补充论证。它示范了一件罕事:一个人可以既坚持核心主张,又明确宣布自己当年错了——他坚持概念完整性,同时否定了"为舍弃而计划"和"信息隐藏是灾难处方"。
- 那条否定瀑布模型的自我批评击中了要点:老版本的问题不是结论错,而是隐含假设错(假设项目只走一次、假设所有错误都发生在编码阶段)。这与第 5 章的提醒闭环了——评估任何建议之前先问它的前提是否成立。
- “结构师像导演、经理像制片人"这个类比是我读到的最好的角色定义之一。它解释了为什么技术决策权与资源决策权必须分开:制片人管预算与档期,导演管作品的一致性;两者都不能替对方做决定。
- “为用户群属性明确记载各种猜测,清晰和错误都比模糊不清好得多”——这是对这一章"频率猜测"一节最好的概括,也是我读到的最可操作的方法论。注意它的立论不是"猜准”,而是让分歧显性化。
- WIMP 那一节的分析密度出人意料:他从双光标问题一路推到"键盘管动词、鼠标管名词",再推到快捷键与撤销如何构成新手向熟练用户的过渡通道。这是一段真正的设计推理,比结论本身更有教育价值。
- 我对他的 WIMP 命运预言持保留:语音输入在今天已普及,但"语音成为表达动词的方法"并没有发生——命名与操作仍然以指针加键盘为主。这说明对交互范式的预测比对工程管理的预测要难得多,因为前者取决于人的社会习惯,而不只是技术能力。
- Boehm 那几条数据里,我认为"时间越充裕,所花费的时间就越长"最值得单独记住。它意味着排期过宽与排期过紧都是有代价的,而代价的形式不同:前者是成本缓慢膨胀,后者是成本急剧上升。
- “项目转移没有一个成功"这一条,在我的经验里也成立,只是尺度更小:同一件事在不同环境里重做,即使资料齐全,也会退化成重新开始。它提醒我,能复制的只有产物,不能复制的是共同体。
- 最后那个化学工程的类比,是全书最诚实的收尾姿态:不宣称方法论已经成熟,也不否认它的前途,只是把软件工程放在一条更长的发展曲线上——“不是没有希望,只是不够成熟”。这比任何豪言壮语都更让人信服。
和我手上的工作有什么关系
- 关于瀑布模型的那段自我否定,对我的价值是具体的:我做个人项目时最常犯的错,正是把用户测试放在最后——先埋头写完,再拿给别人或自己用,然后发现要的不是这个。增量开发那几条收益里,“可以很早就开始用户测试"和"可以采用按预算开发的策略(代价是牺牲功能)“是我马上可以用上的两条规则。
- “每晚重建"这个做法我已经有一个弱化版本:每写完一篇笔记就构建、推送、上线。按这一节的说法,它的价值不只在产物,而在于给团队提供可信度——对我而言就是给"进展"提供可信度,它决定了士气与情绪状态。
- “结构师像导演、经理像制片人"给了我一个判断分工的尺子:在单人项目里我同时扮演两个角色,所以要刻意分清"这次我在做哪个决定”。做概念决策时不该被进度焦虑主导,做进度决策时也不该为了概念完美无限期推迟发布——第 11 章讲的"为舍弃而计划”,恰恰要求这两者在同一张桌子上谈判。
- “信息隐藏是唯一提高软件设计水平的途径"这句我认同,并且它对我的实际含义很直接:好的接口应该让人不必了解另一侧的实现。我的
bin/build.sh、ocr-pdf.py正是这样被设计出来的——调用时只需知道它接受什么、产出什么。判断一个抽象是否成功,就看使用它的人是否需要知道内部。 - “元编程"那一段让我重新看待自己最近做的一件事:这个站点本质上就是以软件包为平台搭建定制产品(Hugo 与 OINK 是平台,我的内容与脚本是元程序)。他也提醒了代价——不能修改平台的内部,且平台不会为你的用法做设计。按这条,我给自己留的自由度应该放在平台的接口边界上(配置、约定、脚本),而不是指望改动平台内部。
- 最后,“清晰和错误都比模糊不清好得多"这句我打算直接当作这个系列的写作标准:宁可写下可能被推翻的判断,也不要写一段谁都挑不出错但也什么都没说的话。
一句话记住
二十年后作者的清算给出了三条:概念完整性与结构师依然成立;瀑布模型与"为舍弃而计划"错了,因为前提错;而 Parnas 对了——信息隐藏与产品级模块,是通往根本进展的路。软件工程不是没有希望,只是还不够成熟。