# 第 11 章 · 未雨绸缪

> 为舍弃而计划：第一个系统本来就不合用；交付后软件进入"前进两步、后退一步"的维护期，混乱度只会上升。

---

LLMS 索引： [llms.txt](/llms.txt)

---

两段题记正好互补：斯威夫特说"不变只是愿望，变化才是永恒"，罗斯福说"选择一种方法，试试看；如果失败了，没关系，再试试别的方法。不管怎么样，重要的是先去尝试。"

## 这一章讲了什么 {#summary}

本章分四段，从"要不要抛弃第一版"一路推到"系统为什么会腐烂"。

**试验性工厂和增大规模。** 作者借化学工业打头：实验室里能实现的反应过程，不可能在工厂里一步做成，中间必须有"试验性工厂"（pilot plant）来提供放大经验——海水淡化要先在日产 10000 加仑的试验场测试，再上 2000000 加仑的系统。软件行业面对同样的问题，却没有吸取教训：一个接一个的项目都是先设计算法、直接应用到待发布的软件里，再按进度把第一次开发的产品交给顾客。

他的判断很直接：**对大多数项目，第一个开发的系统并不合用**——太慢、太大、难以使用，或者三者兼有；要解决，除了重新开始别无他法。而且这条经验有普遍性："所有大型系统的经验都显示，这是必须完成的步骤。"原因之一是知识和新概念会不断出现，即使最优秀的项目经理也不可能一开始就无所不知。

所以管理上的问题不是"要不要做原型"，而是选择题的两面：**为舍弃而计划，无论如何一定要这样做**。把原型直接发布给用户看似省了时间，代价却很高——用户用起来极度痛苦，重新开发的人被分散精力，产品的声誉受损，而"即使最好的再设计也难以挽回名声"。

**唯一不变的就是变化本身。** 第一步是接受事实：变化与生俱来，不是不合时宜的异常情况。他引 Cosgrove 的观察：开发人员交付的是**用户满意度**，而不仅仅是有形产品；用户的实际需要和感觉会随程序的构建、测试与使用而变化。硬件产品也要求满足需求，但**物体的客观存在把用户对变更的要求阶段化了**；软件易于掌握又不可见，于是它的构建者面对的是永恒的需求变更。

作者在这里划了一条界线：他并不主张所有目标和需求的变更都应该、能够或必须整合进设计，因为项目开始时建立的基准会随着开发不断抬高，抬到最后可能什么都做不出来。但目标、设计策略、技术上的变化都不可避免，**事先为它们做准备，总比假设它们不出现要好**。抛弃原型这个概念本身，就是对"随着学习更改设计"这一事实的接受。

**为变更设计系统。** 这一节讲的是常规做法：细致的模块化、可扩展的函数、精确完整的模块间接口设计、完备的文档，此外还可以采用调用队列与表驱动的技术。他特别点出最重要的措施——**使用高级语言和自文档技术以减少变更引入的错误**，以及用编译时的操作整合标准声明。再加上一条纪律：**变更的阶段化**——每个产品都要有数字版本号，每个版本都要有自己的日程表与冻结日期，此后的变更归入下一个版本。

**为变更计划组织架构。** Cosgrove 主张把一切计划、里程碑和日程都当作尝试性的，作者认为这走得太远：当前小组失败的主因是**管理控制得太少，而不是太多**。但 Cosgrove 有一个卓越的观察值得留下——人们不愿为设计写文档，原因不只是惰性或时间压力：

> 通过设计文档化，设计人员将自己暴露在每个人的批评之下，他必须能够为他书写的一切进行辩护。如果团队架构因此受到任何形式的威胁，则没有任何东西会被文档化，除非架构是完全受到保护的。

也就是说，**文档之所以写不出来，往往是因为写它的人不安全**。接着他谈怎么为变更组建团队：分配的工作必须多样、富有延展性，团队可以灵活调动；大型项目需要两三个顶级程序员充当"技术轻骑兵"，在开工最密集时扑向问题。系统变化时管理结构也要跟着调整，于是老板必须重视能力培养，让管理人员和技术人才具备**互换性**。

障碍是社会性的，他点得很直白：管理人员常觉得高级人员太"有价值"，舍不得让他们去做实际编程；而管理职位又拥有更高的威信。业界给出过两种对策：贝尔实验室干脆废除所有职位头衔，每个专业人士都是"技术人员中的一员"；IBM 则保留管理与技术两条晋升线，级别在概念上对等。他还要求管理人员参加技术课程、高级技术人员接受管理培训，高层人员要做好亲自参与开发的技术与情感准备——工作量很大，但很值得。而**外科手术队伍式的团队，其整体观念是对这些社会障碍的彻底冲击**：高级人才编程时不会觉得自降身份，组织架构又最小化了成员间接口，于是当架构必须变化时，重新安排整支队伍相对容易。

**前进两步，后退一步。** 发布之后的变化叫"程序维护"，但作者强调它与硬件维护根本不是一回事。硬件维护包含三件事：替换损坏器件、清洁润滑、修改设计缺陷，而且多数修的是实现层缺陷，对用户常常不可见。软件维护不含清洁润滑与器件更换，**主要内容就是修复设计缺陷，而且通常还附带用户能察觉到的新增功能**。

他给了一组让人清醒的数字：广泛使用的程序，其维护总成本通常是开发成本的 **40% 或更多**；而且这个成本受用户数量影响很大——用户越多，发现的错误越多。麻省理工学院核科学实验室的 Betty Campbell 描出了版本生命期里的一个循环：新版本中先重现上一版本已修复的 bug，新功能带来新 bug，解决之后平稳几个月，接着错误率重新攀升——因为用户达到了新的熟练水平，开始使用那些新功能，高强度使用把不易察觉的问题挖了出来。

维护的根本麻烦在于：**缺陷修复总会以 20%–50% 的几率引入新的缺陷**，所以整个过程是"前进两步，后退一步"。为什么不能修得更彻底？他给两条原因：看起来微小的错误往往其实是系统级问题，不容易看出来，局部修复的工作量清晰而小，更大范围的修复却常被忽视——除非软件结构简单或者文档写得非常详细；而维护人员常常不是原来的开发者，而是初级程序员或新手。后果是每条语句的维护需要的系统测试比正常编程更多，"理论上每次修复之后都要重新运行先前所有的测试用例"，现实中回归测试必须接近这个理想，于是成本极高。他的结论落到设计上：**采用能消除或至少能指明副作用的程序设计方法，会在维护成本上有很大回报；参与设计实现的人越少、接口越少，产生的错误也越少。**

**前进一步，后退一步。** 最后一节引 Lehman 和 Belady 对大型操作系统系列版本的研究：**模块总数量随版本号线性增长，但受影响的模块数量随版本号指数增长**。所有修改都倾向于破坏系统架构、增加混乱程度（熵）；用于修复原有设计瑕疵的工作量越来越少，用于修复"早期维护活动本身造成的漏洞"的工作量越来越多。系统越来越无序，修复迟早会失去根基——尽管理论上一直可用，实际上已面目全非，无法成为下一步进展的基础。再加上机器、配置、用户需求都在变，"现实系统不可能永远可用"，因此基于原有系统的重新设计是完全必要的。

全章收在一个近乎悲观的结论上：软件开发是**减少熵**的过程，所以系统本身处于亚稳态；而软件维护是**增加熵**的过程，"即使是最熟练的软件维护工作，也只是放缓了系统退化到非稳态的进程"。

## 我的判断 {#reading}

- "为舍弃而计划"是全书最容易被误读成浪费的一条。作者的意思不是"随便写个草稿扔掉"，而是**把第一次开发当作获取知识的必经步骤**：如果你不承认第一版会被抛弃，你就不会有意识地安排抛弃，最后就是把该抛弃的东西交付给用户。
- 我认同他对两类成本的排序：**把原型发给用户，省下的是时间，付出的是痛苦、精力和声誉**。后来流行的"最小可用产品"与这条并不相同——MVP 是刻意定义范围的首次交付，而这里说的是"明知不合适的系统"。区别在于是否诚实。
- "交付的是用户满意度，而不仅仅是有形产品"这句话，是这一章的枢纽。它把"需求变化"从道德问题（用户又改需求了）变成了物理问题（满意度是随使用变化的量），于是"为变更设计"就不再是妥协，而是设计目标的一部分。
- Cosgrove 关于文档的那段观察，我认为是本章最有社会学洞察力的一处：**文档缺失往往是安全感问题，不是纪律问题**。它和第 9 章"每个人都以为自己是争取小红花的学生"是同一种诊断——组织一旦让承认不确定变得危险，信息就会停止流动。
- 我怀疑"变更的阶段化"在今天的适用边界。版本号与冻结日期在交付型软件上仍然成立，但在持续部署的服务里，冻结与版本号的意义已经很弱；不过"变更要有阶段"这个思路可以换成别的形式（特性开关、灰度、发布批次），内核仍然有效。
- 维护成本 40% 以上、修复引入新缺陷 20%–50%、受影响模块指数增长——这三条构成了本章的量化骨架。它们合起来解释了一个反直觉的现象：**系统不是被一次大改动毁掉的，而是被一连串正确的修改拖垮的**。作者用热力学语言讲这件事（熵），我认为这是全书最恰当的比喻之一。

## 和我手上的工作有什么关系 {#relevance}

- 我的项目里，"第一个系统并不可用"这条已经是常态，只是我过去把它当成能力不足的证据，而不是过程的一部分。按这一章，正确的做法是**明确划出试验范围**：哪些代码注定要重写、哪些是要留下的骨架，事先说清楚，就不必在重写时产生内耗。
- "维护比开发更贵"这条对嵌入式尤其成立：出厂之后，固件升级的成本远高于写代码本身。这给了我一个具体的判据——**如果一个改动会引入必须回归验证的副作用，那么它在设计阶段就该被避免**，而不是留到维护期付 20%–50% 的额外成本。
- "参与设计实现的人越少、接口越少，产生的错误越少"这条，是我目前工作方式的直接辩护：单人加少量 Agent 的编队，接口数少，维护期的回归面也小。反过来说，如果我要引入并发改动，就得为指数增长的受影响模块留出测试预算。
- Cosgrove 关于文档与安全感的那段，我用得上：这个站点的会话记录之所以能写下去，是因为没有评审会借它追究我；如果哪天要给这些笔记加外部批评的环节，应先考虑清楚"承认不确定是否安全"，否则笔记会退化成为自己辩护的材料。
- "技术轻骑兵"这个角色我打算显式建立：给自己留两三个可以随时扑上去解决的通用能力（构建、检索、批量改写），在任务最密集时调用——这比每次重新造流程划算。实际上 `bin/build.sh` 与 `ocr-pdf.py` 就是这类轻骑兵的第一批成员。

## 一句话记住 {#takeaway}

第一个系统注定要丢掉，所以要为舍弃而计划；发布之后，维护会以"前进两步、后退一步"的方式不断增加系统的熵，减少熵的唯一办法是重新设计。
