第 16 章 · 没有银弹
这一章是 1986 年的同名论文,收进纪念版后成了全书流传最广的部分。题记就是那句著名的判断:在未来的十年内,无论是在技术还是管理方法上,都看不出有任何突破性的进步,能够保证在十年内大幅度地提高软件的生产率、可靠性和简洁性。
这一章讲了什么
摘要与比喻
作者把软件活动分成两部分:根本任务是打造构成抽象软件实体的复杂概念结构;次要任务是用编程语言表达这些抽象实体,并在时空限制下把它们映射成机器语言。近年生产率的巨大进步都来自对次要障碍的突破——硬件限制、笨拙的语言、机器时间匮乏。于是问题变成一个算术题:除非次要任务占了全部工作的 9/10,否则即使把它压缩到零,也换不来数量级的提升。
所以他主张把注意力转向根本任务,并提出四条方向:做好市场调研、避免重复开发已上市的产品;把快速原型作为需求迭代的一部分;以"有机地生长"的方式增量开发;以及不断挑选和培养杰出的概念设计人员。
人狼的比喻是这一章的入口:民间传说里最可怕的妖怪是人狼,因为它会毫无预兆地从熟悉的面孔变成可怕的怪物;而软件项目也常常如此——看起来简单明了的东西,转眼就成了落后进度、超出预算、满是缺陷的怪物。为了对付人狼,我们在寻找银弹。作者说,找了十年,没找到。
但他特意补了一句:怀疑论者不是悲观主义者。虽然看不到单一突破,但已有的革新经过规范化、持续发展和传播,确实可能在将来带来数量级的提高——“虽然没有通天大道,但是路就在脚下。”
根本困难
这一节是全文的理论核心。他先纠正一个视角:软件看起来发展慢,其实是因为计算机硬件发展得太快——三十年里性价比提升六个数量级,人类文明史上没有第二个产业做到过。
然后借用亚里士多德的划分:把困难分成根本的(essence,软件特性中固有的)和次要的(accident,当前生产方式造成的、并非与生俱来)。根本困难在于软件实体必然是一个相互牵制关联的概念结构——数据集合、数据之间的关系、算法和功能调用。这些要素是抽象的,但内容极其丰富、要求高度精确。他的判断是:软件开发中真正困难的是规格说明、设计和测试这些概念结构的构造,而不是表达概念、验证实现的逼真程度。语法错误会有,但和概念错误相比微不足道。
由此他给出软件四个无法规避的内在特性:
| 特性 | 含义 |
|---|---|
| 复杂度 | 软件实体可能比任何人造物都复杂,因为没有哪两部分是相同的——如果相同,我们早就把它抽成子函数了;规模增加不是重复添加同类元素,而是加入不同元素,且它们之间的交互往往非线性递增 |
| 一致性 | 物理学家相信自然界背后有简化原理,软件工程师没有这种安慰;他必须应付的很多复杂度来自人为惯例与系统接口,随心所欲、无规则、随时间变化,而且仅仅因为是不同的人设计的 |
| 可变性 | 工业品出厂后很少改动,而软件持续承受变更压力:功能最容易感受变更压力,而且软件纯属思维产物、修改成本极低;成功软件的生命期又常长于它当初所依赖的硬件平台 |
| 不可见性 | 软件没有空间形体,因此没有建筑平面图、机械制图或电路图那样的几何表达;用图形表达软件结构时,得到的是相互重叠的多张图(控制流、数据流、依赖、时序、名字空间),而且通常不是层次扁平的 |
他的结论是:软件的复杂度是根本属性,不是次要因素。数学与物理之所以能靠简化模型取得进展,是因为被忽略的复杂度不是研究对象的根本属性;当复杂度本身就是本质特性时,这条路走不通。这些特性还直接衍生出管理问题:复杂度让全面理解变得困难,妨碍概念完整性,让所有离散出口难以寻找和控制,最终把开发变成一场灾难。
三次真正的突破,都是次要困难
作者回顾了软件领域最有成效的三次进步,并逐一指出它们的边界:
- 高级语言:最有力的突破,生产率至少提高 5 倍。它消除了整层不属于程序本身的复杂度(位、寄存器、通道这些机器语言关心的东西)。但它的作用是让表达更容易,并没有触及设计的复杂度;而且对不擅长使用复杂语言要素的人来说,它反而可能增加脑力负担。
- 分时:价值在于及时性,让人能维持对复杂度的总体把握——批处理的漫长周转会让人遗忘细节,重新进入思考的代价极高。但作者指出,这同样是次要困难:它的效果随响应时间趋近人类可辨识的约 100 毫秒而趋于零。
- 统一编程环境:Unix 与 Interlisp 通过集成库、统一文件格式、管道与过滤器,解决了"程序共同使用"这一次要困难;每个新工具都能通过标准格式被任意程序调用,于是又带动了整个工具库的发展。
逐一否决候选银弹
这一节是全文最容易被引用、也最容易招恨的部分。他对当时最被看好的技术逐条给出判断:
- 面向对象编程:他自己就是看好者之一,并仔细区分了抽象数据类型与层次化类型这两个不同的进步来源。但他的结论是——它们消除的是设计表达上的次要困难,“除非不必要的低层次类型说明占据了软件产品设计的 90%,面向对象编程才能带来数量级上的提高”,因此对这颗银弹"深表怀疑"。
- 人工智能:他借 Parnas 对两种 AI 定义的辨析,指出语音识别、图像识别与专家系统之间几乎没有共同技术,很难想象它们能给编程实践带来什么差异——“软件开发上的困难是决定说什么,而不是如何说”。
- 专家系统:他给出全书对这类技术最公允的评价。它的真正贡献是把具体应用的复杂度与程序本身相分离,并把诊断规则以统一风格编码;而且产生诊断规则本身也是编制测试用例时必须完成的工作,如果做得好,可以减少测试设计的总工作量。但前提是"构建专家系统的必要前提条件是拥有专家",而知识获取的工作量是知识获取本身的两倍。他最终认为它最大的价值是传播优秀实践——让缺乏经验的人用上最优秀者的经验,因为软件工程中最好与一般实践之间的差距比其他工程领域都大。
- “自动编程”:引 Parnas 的断言作结——“自动编程总是成为一种热情,使用现在并不可用的更高级语言编程的热情。“他指出多数所谓技术说明本质上是问题的解决方案,而不是问题本身。数据发生器、方程系统这类能做自动生成的应用有共同的简洁属性(参数少、已知解法多、选择规则清晰),因此难以推广到寻常系统。
- 图形化编程:他认为至今没有令人信服的进展,而且"确信将来也不会出现”,理由是流程图本身就是很差的结构表达工具——“流程图已经被证明是完全不必要的设计工具,程序员是在开发之后,而不是之前绘制描述程序的流程图”。
- 程序验证:它不能省略程序测试;更严肃的是,即使完美的程序验证,也只能证明程序满足技术说明,而这时软件工作中最困难的部分已经接近完成——验证实际上变成了对规格说明的测试。
- 环境和工具、工作站:都会是有价值的改进,但按同样的算术,回报必然有限。他尤其点明:当机器速度再提高十倍,“程序开发人员的思考活动将成为日常工作的主要活动——实际上,这已经是现在的情况了”。
真正有希望的四条路
作者把生产率写成一条公式:任务时间 = ∑(频率)×(时间)。既然最耗时的是创造性工作,那么任何只加速"表达概念"的手段,都无法真正改变结果。因此他把希望寄托在能对付根本困难的做法上:
- 购买而非自行开发。他开玩笑说,构建软件最彻底的方案是不开发任何软件。软件成本一直是"开发的成本而非复制的成本”,所以共享能大幅降低每个用户的成本。他分析了为什么软件包在 1980 年代才被广泛接受:不是软件包变了,也不是需求变了,而是计算机硬件与软件的成本比率变了——花两百万买机器的人愿意再花二十五万做定制薪资系统,而花五万美元买办公机器的人不愿意。他还举了电子表格与简单数据库这些"戏剧性的例外":它们让大量不写代码的人用计算机解决了新问题。他的建议很具体:在生产一线配备个人计算机、装好通用软件、并培训会熟练使用它们的人。
- 需求精炼与快速原型。他认为开发中最困难的部分是确切决定要搭建什么系统,而"客户不知道自己需要什么",甚至不知道哪些问题必须回答。于是他下了一个明确的定论:在使用和开发客户定制系统之前,想要完整、精确、正确地抽取需求,“实际上也是不可能的”。因此最有希望的技术,是把快速原型化作为迭代需求过程的一部分——原型模拟关键界面、演示主要功能,不受真机速度与成本的约束,也不处理异常分支,目的是让客户能测试概念结构的一致性与可用性。他批评当时"先写清系统、再竞标、再开发、最后安装"的流程建立在根本错误的假设上。
- 增量开发——生长,而非搭建。他回忆 1958 年听到朋友说"搭建(building)系统"而非"编写(writing)系统"时的震动,随后又建议我们转向自然界:生物大脑的复杂程度远远超过任何对它的描述与仿真,其秘密是逐步发育成长,而不是一次性搭建。Harlan Mills 也曾建议所有软件系统都应以增量方式开发:先让系统能运行,哪怕只是正确调用一系列伪子系统;然后一点一点充实。作者说这种方法"在过去几十年中,没有任何方法和技术能如此彻底地改变我自己的实践"——它天然要求自上而下设计,让逆向跟踪变得方便,也天然利于原型开发。他还特别提到士气效应:当一个可运行系统出现时(哪怕极其简单),开发人员的热情会迸发出来;屏幕上映出第一个图案(哪怕只是个矩形),动力就成倍增长。他给出的经验是:团队可以在四个月内"培育"出比"搭建"复杂得多的系统。
- 卓越的设计人员。他把这一条放在最后,也放在最重的位置。作者承认方法论的价值——好的实践可以传授,所以颁布规范、编写教材、设立机构都是对的;但他断言良好设计与卓越设计之间的区别肯定不在于方法的完善性:“卓越设计来自卓越的设计人员。“软件开发是创造性过程,完备的方法学可以培养和释放创造性思维,却无法孕育或激发它。他因此提出最尖锐的一条批评:多数机构花大量资源寻找和培养管理人员,却没有任何一家在寻求和培育杰出设计人员上投入相同资源,而产品的技术特色最终依赖这些人。他的建议是:机构必须公开表明杰出设计者与卓越管理者同样重要,并在薪资、办公室、家具、设备、差旅与人员支持上完全对等;然后尽早、有系统地识别顶级设计人员(“最好的通常不是最有经验的”)、指派职业导师、制定并维护职业发展计划、提供相互交流与激励的机会。
我的判断
- 这一章的真正贡献不是"没有银弹"这个结论,而是它给出的判断标准:任何新技术,只要它加速的是"表达概念"而非"构造概念”,那么它能带来的提升就有上限。这条标准让我能快速判断一个流行技术的天花板在哪——包括今天热得发烫的东西。
- 那个 9/10 的算术值得单独记住。它把争论从"这个技术好不好"转成"它作用在哪一层”,而这正是大多数技术讨论缺失的维度。
- 复杂性那四条里,我认为一致性最容易被低估。复杂度还能靠抽象与分层缓解,而"人为惯例、无规则、随时间变化、而且仅仅因为是不同人设计的"这一条几乎无法通过设计消除——它本质上源于软件必须与人和其它系统共存。
- 我对作者几处"确信不会出现"的断言持保留:图形化编程在他身后的几十年里确实没有取代文本编程,但他对程序验证的处理略显轻率——今天的形式化方法与类型系统在很多关键系统里承担了实实在在的工作,即便它们确实不能省略测试。这类"我确信将来也不会出现"的句式,提醒我注意作者也有时代局限。
- 专家系统那一节的公允程度出乎我的意料:他没有简单否定,而是承认"把具体应用的复杂度与程序本身相分离"是真实进步,并指出它最大价值在传播优秀实践。这个评价框架今天完全可以平移到对 AI 编码工具的判断上——它们的力量来自把经验编码并规模化传播,而不是来自解决了概念复杂度的根本问题。
- “增量开发"那一段带着明显的个人情感,而且它构成了本系列最有意思的一处回响:作者说没有任何方法能像它一样彻底改变他自己的实践,理由之一是士气——有个能跑的东西,动力就成倍增长。这与我做这个站点的体验完全一致:先让站点跑起来再长大,比规划好一切再动手现实得多。
- 最后那条"卓越设计来自卓越的设计人员”,是这一章最不技术、也最难接受的一段。它在一定程度上否定了方法论的作用,但我认为它是对的:方法能提高下限,决定上限的是人。它对个人项目的含义是——与其追求更多流程,不如把有限的时间投在判断力与品味的磨练上。
和我手上的工作有什么关系
- 用 9/10 这个标准衡量我手上的工具:构建脚本、模板、OCR 流水线都在加速"表达与机械操作",它们的价值有上限;而我花在"想清楚要做什么"上的时间,才是真正决定产出的部分。不要指望工具解决设计问题——这条我在这个站点上已经验证过:省下的是打字时间,省不下的仍是决定写什么。
- “购买而非自行开发"这条和我现在做的事在表面上相悖(我在从零搭站点),但逻辑一致:我购买的是 OINK 主题与 Hugo,而不是自己写文档框架。真正的自研只花在别人无法提供的那部分——我的内容与工作流。
- 需求精炼那条对我有直接的减负作用:在动手之前不可能把需求想全,这不是我能力不足,而是这类工作的固有属性。所以正确做法是尽早做出可运行的东西(哪怕只是骨架),让"客户”(也就是未来的我)能对着实物说出真实需求。这个站点的系列骨架先上线、内容后填,正是同一路数。
- 增量开发与士气那一段,解释了我为什么坚持"每完成一篇就构建、推送、上线":每一步都留下一个可运行的、可见的状态,比攒一大块再发更可持续。作者说团队能在四个月内培育出比搭建复杂得多的系统,我在这个系列上体验到的正是这个效应。
- 设计人员那条给了我一个具体的行动项:把"个人项目的技术负责人"当成一个需要培养的角色来看待,而不只是一个身份。具体做法是保留决策记录(
docs/sessions/与这个笔记系列),因为判断力的提升来自复盘自己的判断,而不是来自积累更多工具。
一句话记住
没有任何单项技术能让软件生产率提升一个数量级,因为根本困难在于构造复杂的概念结构本身;能真正改变结果的只有四件事——买而不是造、把需求做成迭代、让系统生长而不是一次搭成,以及培养卓越的设计者。