为什么软件开发没有银弹

软件工程的每一次“革命
”都在解决同一类问题——而真正难的那部分,四十年来没人碰过。

1986 年,Fred Brooks 写了一篇论文,标题是《没有银弹——软件工程的本质与偶然》。这篇论文发表至今快四十年了,期间我们经历了面向对象、敏捷开发、微服务、低代码、AI 编程……每一波浪潮都被包装成“软件开发的终极答案”。

但如果你问任何一个在一线写了十年以上代码的工程师,他会告诉你:软件开发从来没有变容易过。

这篇文章想讲清楚一件事:为什么软件开发永远不会有银弹。理解了这件事,你就不会再被任何“革命性”工具或方法论忽悠。

本质复杂度与偶然复杂度

Brooks 把软件开发的困难分成两类。

本质复杂度 是问题本身固有的——你要开发的系统到底要做什么。一个工资核算系统必须理解税法规则,一个自动驾驶系统必须处理不可预测的道路场景。这些复杂性与你用不用 AI、换不换框架没有任何关系,它是问题自身的一部分。

偶然复杂度 是工具和实现方式带来的——内存管理、语法细节、编译配置、部署流程。这些不是问题本身要解决的,只是你在解决问题过程中被迫处理的额外负担。

过去四十年,我们消灭的基本都是偶然复杂度。高级语言消灭了汇编的内存管理,IDE 消灭了手动构建,云服务消灭了自建机房。每一次都像革命,但革命的成果只停留在“让实现变简单”,从未触及“让问题变简单”。

打个比方:从手动挡换到自动挡,换挡的负担消失了,但”如何从 A 到 B 避开拥堵”这个问题没有变简单。前者是偶然复杂度,后者是本质复杂度。

软件开发的两种复杂度 基于 Fred Brooks《没有银弹》的核心论点 本质复杂度 Essential Complexity 问题固有 · 不可消灭 系统到底要做什么——与工具无关 业务逻辑与领域规则 概念模型的构建与验证 设计决策与工程取舍 需求的理解与抽象 没有工具能替你理解问题 40 年来从未降低 偶然复杂度 Accidental Complexity 工具引入 · 可被消灭 解决问题过程中被迫处理的额外负担 语法细节 高级语言 手动构建 IDE 自建机房 云服务 样板代码 AI 编程 四十年的进步都在这里 持续被工具消灭

为什么本质复杂度消灭不掉

本质复杂度之所以“本质”,是因为它来自人类理解问题的认知极限。

任何一个非平凡的软件系统,都需要一个人或一个团队在脑中构建出完整的概念模型。这个模型必须自洽——每一处逻辑都不能和另一处冲突。随着系统规模增长,概念模型的状态空间指数级爆炸,没有人能在脑中同时容纳所有细节。

这不是智力问题,是信息论层面的限制。就像你不能在脑中完整模拟一盘围棋的所有落子顺序,你也没办法在脑中完整追踪一个百万行代码系统的所有执行路径。工具可以帮你——就像棋盘软件可以帮棋手复盘——但最终的判断、取舍、设计决策,还是得人来做出。

Brooks 的结论是:没有任何单一的技术或管理进步,能在十年内给软件生产力带来一个数量级的提升。他把这个称为“银弹不存在”的论断。注意,他不是说进步不可能,而是说没有银弹——没有一个东西能一次性地、成倍地解决问题。

历史一直在证明他

回顾过去四十年,每一波“银弹叙事”的结局都一样。

面向对象被包装成”用现实世界的方式建模软件”,结果大部分人用它来给数据库表套一层 getter/setter。敏捷说要”拥抱变化”,结果变成了站会和 Jira 看板的官僚主义。微服务承诺”独立部署、独立扩展”,结果把函数调用替换成了网络调用,debug 从看堆栈变成了翻分布式链路追踪。

低代码宣称”让业务人员自己写软件”,结果业务人员还是找 IT,而 IT 被困在一个表达能力只有 Excel 公式级别的平台上挣扎。

最近的银弹是 AI 编程。Cursor、Claude Code、GitHub Copilot 让写代码的速度快了数倍,于是有人宣布“软件工程已经被颠覆了”。但写代码只是实现,不是设计。AI 能帮你写一个排序函数,但 AI 不能替你决定这个系统要不要排序、排什么、排序结果影响哪个下游模块。

那些决策需要理解业务、理解用户、理解系统中每一项设计选择会引发的连锁反应。AI 没有这个理解,它只是在 token 空间中做概率推断。

不妨看看 Anthropic 自己的 Claude Code 源码——一个 3167 行的函数,486 个分支点,12 层嵌套。世界上最先进的 AI 公司,用最先进的 AI 工具,写出了教科书级别的意大利面条代码。银弹连发明它的人都救不了。

那还能做什么

承认没有银弹,不等于躺平。

Brooks 自己给出的答案是“买,不要造”——用现成的软件而不是自己开发。这在 1986 年是对的,在今天更是对的。能买到的就不要写,能复用的就不要重写。

第二个答案是 渐进式生长。好的软件不是一次性设计出来的,是长出来的。先做一个最小可用版本,用起来,发现问题,改,再用,再改。这个过程不可能被任何工具加速,因为真正需要时间的是你对问题域的理解逐渐加深——这跟打字速度无关,跟代码生成速度也无关。

第三个答案也是最反直觉的:砍需求。大部分软件项目之所以复杂,不是因为必须复杂,而是因为没人敢说不。“这个功能以后可能有用”——这句话造成的复杂度比任何技术债都多。每加一个功能,系统的状态空间就翻一倍。一个只有必要功能的系统和一个“可能要什么都有”的系统,开发难度差了一个数量级。

这就是二八原理在软件开发中最残酷的体现:20% 的功能承担了 80% 的使用量,剩下 80% 的功能不仅很少被使用,还贡献了绝大部分的复杂度和维护成本。

没有银弹。但如果你接受了这一点,你反而会成为一个更好的工程师——不再追逐下一个框架、下一个方法论、下一个 AI 工具来拯救你的项目,而是老老实实地理解问题、砍掉非必要的东西、让系统在可控的复杂度内生长。

这比任何银弹都管用。