"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil ." ------ Donald Knuth, Structured Programming with go to Statements, ACM Computing Surveys, 1974
这句话已经被引用了 50 年。
它依然有效,恰恰是因为它描述的那个陷阱,从来没有真正被解决过------开发者总是在不该优化的时候优化、不该抽象的时候抽象、不该重写的时候重写。
这篇文章不打算讲故事,也不堆砌名词。我们用软件工程史上几个被反复验证、有据可查的论断和案例,来客观地说清楚一件事:
为什么"过度优化"是一种系统性的反模式,以及业界沉淀出的应对方法。
一、它在工程界有名字
很多人陷在"优化怪圈"里却不自知,部分原因是它没有一个统一的中文叫法。但在英文工程语境里,它的几种典型形态早已被命名、归类、写进经典著作。
1. Premature Optimization(过早优化)
来源:Knuth (1974)。
需要澄清的是 Knuth 的原意------他不反对优化 ,他反对的是在测量之前的优化。他完整的论断是:97% 的代码不值得被优化,但有 3% 的关键路径必须被认真优化。判断该不该优化,要靠 profiling 数据,而不是直觉。
2. Speculative Generality(投机性通用化)
来源:Martin Fowler, Refactoring: Improving the Design of Existing Code (1999, 2nd ed. 2018,中文版《重构(第2版)》)。
这是 Fowler 在书中明确列出的"代码坏味"之一。典型表现:
- 只有一个实现的 interface
- 永远不会被传非默认值的"可配置参数"
- "为了未来扩展"预留的钩子函数
Fowler 给的判断标准很直接:"If you need it, do it; if you don't, don't."
3. Gold Plating(镀金)
来源:早期出现在 IEEE 软件工程术语和 PMBOK(Project Management Body of Knowledge,《项目管理知识体系指南》中文版)中。
指的是在已满足需求后继续添加用户未要求的功能或细节。在项目管理领域,它被明确列为范围管理风险------因为它会消耗预算、延长周期,且不带来对应的客户价值。
4. Bikeshedding(自行车棚效应)
来源:C. Northcote Parkinson, Parkinson's Law: The Pursuit of Progress (1957,中文版《帕金森法则》)。
软件工程界的使用,源自 Poul-Henning Kamp 1999 年在 FreeBSD-Hackers 邮件列表的著名邮件 "A bike shed (any colour will do) on greener grass..."。
它描述的是一种注意力错配------团队倾向于在小事上花掉远超核心议题的讨论时间。Code Review 中常见的形态:
- 命名风格争论 30 分钟
- 缩进/格式占据 80% 的评论
- 真正的架构问题反而没人讨论
二、为什么过度优化是有害的
如果只用一句话概括,那就是:
它让你用工程上看起来正确的行为,回避了工程上真正困难的问题。
展开看,至少有四个客观成本。
1. 机会成本
每一小时投入到没有人要求的优化,都是从真正有价值的工作那里偷走的时间。
DeMarco 和 Lister 在 Peopleware (1987,中文版《人件(第3版)》) 中给出的核心观察是:知识工作者真正的产能差异,不在编码能力,而在注意力如何分配。同样的 8 小时,决定产出的是"你在做什么"而不是"你做得多努力"。
2. 边际效益递减
Pareto 原则(80/20 法则)在软件工程领域被反复印证。Knuth 那句"97%"也是它的同义表达------性能瓶颈通常集中在很小一部分代码里。对非热点路径做优化,投入和产出严重不成比例。
这个判断不是直觉,是过去半个世纪 profiling 工具反复验证过的事实。
3. 复杂度成本
John Ousterhout 在 A Philosophy of Software Design (2018,中文版《软件设计的哲学(第 2 版)》,人民邮电出版社 2024) 中提出一个核心论断:
复杂度(complexity)是软件系统最重要、也是最持久的成本。
每一次"为未来准备的抽象"、每一层"为优雅而引入的间接",都在增加:
- 新人理解系统的时间
- 修改时需要考虑的连带影响
- bug 藏身的角落
Fred Brooks 在 The Mythical Man-Month (1975,中文版《人月神话(40 周年中文纪念版)》) 里也说过类似的话:conceptual integrity(概念一致性)是系统设计中最重要的考量。过度抽象的代价,常常是牺牲掉这种一致性。
4. 反馈延迟成本(最致命的一个)
这是最容易被忽略的一类成本。
Eric Ries 在 The Lean Startup (2011,中文版《精益创业》) 里把它表达为 Build-Measure-Learn 循环:未经市场验证的假设没有价值,在反馈到来之前所做的所有"完美准备",都可能因为方向本身错了而整体归零。
一个被反复引用的反例,来自 Joel Spolsky 2000 年的文章 Things You Should Never Do, Part I。文章详细记录了 Netscape 的决策:因为认定老代码"不够好",团队决定从零重写浏览器。重写持续了将近 3 年,期间几乎没有产品演进。等到新版本发布时,市场已经被 IE 拿走了。
Spolsky 的结论是:
"It's harder to read code than to write it. (...) Throwing away the code means throwing away your knowledge."
那不是"代码不够优雅"的代价,而是整个公司命运的代价。
三、为什么我们如此容易陷进去
不是因为懒,恰恰相反,陷进去的往往是最在乎质量的那批人。
业界对这个现象有几个互相印证的解释:
优化是少数完全可控的事。 业务会变、需求会变、老板想法会变。但代码不会。在不确定性扎堆的工作中,"把这段代码改得更漂亮"是少数几件结果完全由自己决定的事。
优化有即时反馈。 重构之后 IDE 立刻变绿、测试立刻通过、Lint 立刻消停。而业务价值要数周乃至数月才能验证。
优化是一种"完美主义防御"。 只要还在打磨,就不用面对评判。一旦交付,就要接受真实世界的反馈------而那个反馈可能是负面的。"还差一点点"是一种心理上的避风港。
优化容易和职业身份绑定。 "我是一个写优雅代码的工程师"成为一种自我认同,而非可被取舍的工程权衡。这一点比技术原因更难以察觉。
四、行业沉淀的应对原则
过去半个世纪的软件工程实践,沉淀出几条被广泛接受、有出处的判断框架。它们不是个人经验,而是经过大规模工程验证的原则。
1. YAGNI(You Aren't Gonna Need It)
来源:Ron Jeffries 在 XP(Extreme Programming)实践中提出,Kent Beck 在 Extreme Programming Explained (1999,中文版《解析极限编程》) 中将其确立为 XP 核心原则。
它的逻辑是:
- 你预测未来需求的准确率,远低于你以为的
- 即使预测对了,未来真正实现时所知信息远多于现在,方案会更好
- 提前实现的代码会成为未来变更的阻力
结论:只为已知的、明确的需求写代码。
2. Rule of Three(三次法则)
来源:Don Roberts 提出,被 Martin Fowler 收录在 Refactoring (1999) 中。
判断什么时候该抽象的规则非常简单:
- 第一次出现 → 就这么写
- 第二次出现 → 容忍重复
- 第三次出现 → 此时你才真正知道"什么是共同的、什么是变化的"
Sandi Metz 在博客文章 The Wrong Abstraction (2016) 中进一步阐述了这条法则的反面:
"Prefer duplication over the wrong abstraction."
她的观点是:错误的抽象比重复的代码更难修复,因为它会绑定所有调用方。
3. KISS(Keep It Simple, Stupid)
来源:洛克希德首席工程师 Kelly Johnson 在 1960 年代提出,后被软件工程界广泛采纳。
它和 Ousterhout 的"复杂度是核心成本"互为印证:简单方案的总成本,通常低于复杂方案------即使后者在某些维度看起来更先进。
4. Worse is Better(劣即是优)
来源:Richard P. Gabriel 在 Lisp: Good News, Bad News, How to Win Big (1991) 中提出,后续整理为独立论文 The Rise of "Worse is Better"。
Gabriel 用这个概念解释一个看似反直觉的现象:简单、能用、能演进的设计,往往在长期竞争中击败完美但难落地的设计。
C 击败 Lisp 是他原始论文的例子。后来的从业者用它解释了从 JavaScript 到 React 到无数"不完美但获胜"的技术路径。
5. MVP(最小可行产品 / Minimum Viable Product)
来源:Eric Ries, The Lean Startup (2011),源头可追溯到 Steve Blank 的客户开发方法论。
核心承认是:没有人能在交付之前知道什么是"对的" 。最高效的路径不是把方案做完美再交付,而是用最小成本交付,用真实反馈指导后续投入。
五、怎么判断自己正陷在里面
理论之后,回到实操。判断"是否陷入优化怪圈",可以用几个客观信号:
信号 1:你说不清"做完"长什么样
如果一个任务没有明确的 Definition of Done (Scrum 术语),那它一定会被无限延伸。Definition of Done 的价值,不在于定义结果,而在于定义边界。
动手前用一句话写下"做完了"是什么样,贴在显眼的位置。当你想"再加一个抽象"时------它在这句话里吗?不在,就停。
信号 2:你的优化没有 profiling 数据支撑
按 Knuth 的判断,任何未经测量的性能优化都属于过早。
问自己三个问题:
- 当前性能是不是业务/用户感知的瓶颈?
- Profiling 数据有没有定位到具体热点?
- 优化的预期收益是不是大于它引入的复杂度?
三个都答不上来,就先别动。
信号 3:抽象只有一个实现
这是 Fowler 的 Speculative Generality 最直接的检测方法。
打开 IDE,查一下这个 interface 有几个 implementation。如果只有一个,你大概率不需要它。 等到第三次重复出现时再抽象(Rule of Three),那时你才真正知道什么该抽出来。
信号 4:你在重构无关的代码
Boy Scout Rule(Robert C. Martin, Clean Code, 2008,中文版《代码整洁之道》)经常被错误使用------"离开营地时让它比来时更干净"被解读为"借机重构整个模块"。
它的本意是小幅、本地的清理 。判断边界是:清理能不能在当前任务的 commit 里独立提交,且不影响主任务的可审查性?
如果你的 PR 标题是"修一个 bug"但 diff 是 +800 / -600,那不是 Boy Scout,那是借机镀金。
六、一个必须澄清的边界
反对过度优化,不等于鼓励技术债务的累积。这是常见的误读。
两者的区别在于决策依据:
| 维度 | 过度优化 | 合理工程 | 粗制滥造 |
|---|---|---|---|
| 关注点 | 想象中的未来需求 | 当前明确需求 + 已知风险 | 仅交付,不管后续 |
| 抽象时机 | 提前预设 | 出现重复后 | 完全无抽象 |
| 性能优化依据 | 直觉、洁癖 | profiling 数据 | 不考虑 |
| 测试覆盖 | 追求 100% | 覆盖关键路径与边界 | 缺失或形式化 |
| 决策方式 | 自我驱动 | 价值驱动 | 工期驱动 |
合理的工程实践,是在价值、成本、风险 三者之间持续寻找平衡点。过度优化和粗制滥造,是同一根坐标轴的两个极端,都背离了工程的本质。
七、AI 时代的新变量:当过度工程从陋习变成默认产出
文章到这里讨论的全部是「人类」的过度工程倾向。但 2023 年以来,这个老问题出现了一个全新的变量------AI 写的代码默认就是过度工程。
Andrej Karpathy 在 X 上的一段公开观察被反复引用:
"它们真的很喜欢把代码和 API 搞复杂,堆砌抽象概念,不清理死代码......明明 100 行能搞定的事情,非要实现成 1000 行的臃肿架构。"
"模型会代你做错误假设,然后不假思索地执行。它们不管理自身的困惑,不寻求澄清,不呈现矛盾,不展示权衡,在应该提出异议时也不反驳。"
把这两段话对照本文前面讨论过的反模式,几乎是逐条对应:
- 堆砌抽象概念 → Speculative Generality
- 100 行的事情写成 1000 行 → YAGNI 反面
- 不清理死代码、改动无关代码 → Boy Scout Rule 的错误使用
- 不假思索地执行 → Premature Optimization
- 不呈现权衡、不提出异议 → Bikeshedding 的反面(避难就易)
也就是说,本文列举的全部反模式,正在被 LLM 工业化、规模化地生产。这带来三个新的工程现实:
1. 过度工程从「慢性病」变成了「急性病」
人类工程师做过度设计,一周也许产出 200 行多余代码;LLM 一次会话就能产出 2000 行。代码库被淹没的速度被压缩到原来的 1/10。技术债的积累不再是按月计算,而是按 PR 计算。
2. 工程判断从「奢侈品」变成「必需品」
不熟悉 YAGNI、KISS、Rule of Three 的开发者,过去只是「写得慢一点」;今天意味着他们的代码库会以远超想象的速度堆积技术债。缺少工程纪律的人加上 AI,等于屎山生成器------AI 不会替你做价值判断,它只负责把你说的话最大化执行。
3. 工程纪律正在被「外置」给 LLM
应对方式正在被工程社区自发地探索------核心思路可以概括为:在 LLM 写代码之前,先把人类积累的工程纪律以可执行的规则形式喂给它,让模型在生成时受到这些规则的筛检。这类规则集常见的几条,翻译成本文的语言其实是:
| 注入的规则 | 对应本文原则 |
|---|---|
| 简洁优先 | KISS + YAGNI + Worse is Better |
| 精准修改 | Boy Scout Rule 正确用法 + 避免镀金 |
| 编码前思考 | Definition of Done + 测量优先 |
| 目标驱动执行 | MVP + Build-Measure-Learn |
不是巧合。LLM 的默认行为正把过去 50 年我们用来抵抗自身缺陷的原则,逼着所有人重新用一遍------只不过这次不是用在自己身上,而是用在 AI 身上。
在 AI 时代,工程师的核心价值不再是写代码,而是为 AI 定义边界。
而那条边界,正是这篇文章前面六节讨论的所有原则。
八、结语
软件工程作为一门学科,从来不是关于"如何写出最完美的代码"------它是关于如何在不确定性、有限资源和不断变化的需求下,持续交付价值。
这是 Knuth、Brooks、Fowler、Ousterhout、Ries 这几代人,用著作和案例反复在说的同一件事。
"优化怪圈"之所以隐蔽,是因为它产出的代码在局部看上去常常非常漂亮。它的真正危险在于:
它让我们用工程上看起来正确 的行为,回避了工程上真正困难的问题------价值判断、取舍、按时交付。
如果有一条贯穿始终的判断标准,或许是这一句:
优秀的工程不是把每一行代码做到极致,而是清楚地知道哪些代码值得被做到极致,哪些不值得,并且在两者之间保持冷静。
九、推荐阅读
如果文章里某个概念让你想进一步深入,下面是按类型组织的延伸资料。每一条都标注了它"能帮你解决什么问题",按需取用即可,不必通读。
起点:理解问题本身
-
Donald Knuth, Structured Programming with go to Statements(1974) 「过早优化是万恶之源」的原始出处。读完你会发现 Knuth 真正的论点比口号微妙得多------他要的是测量驱动的优化,而不是不优化。
-
Fred Brooks, The Mythical Man-Month(1975,中文版《人月神话(40 周年中文纪念版)》) 软件工程的奠基性作品。「没有银弹」和「概念一致性」两章,直接对应本文讨论的过度抽象问题。
方法论:怎么做才对
-
Martin Fowler, Refactoring(1999 / 2018,中文版《重构(第 2 版)》) Speculative Generality 的命名出处,也是 Rule of Three 的权威说法。配合实战可立刻识别项目里的过度抽象。
-
John Ousterhout, A Philosophy of Software Design(2018,中文版《软件设计的哲学(第 2 版)》,人民邮电出版社 2024) 300 页、两个晚上能读完。核心论点「复杂度是软件系统最持久的成本」是本文第二节的理论基础。
-
Kent Beck, Extreme Programming Explained(1999,中文版《解析极限编程》) YAGNI 第一次被确立为工程原则的现场。今天敏捷实践里一半的基础概念都来自这本。
-
Eric Ries, The Lean Startup(2011,中文版《精益创业》) MVP / Build-Measure-Learn 的源头。技术人的视角看会有点反直觉------但反直觉处往往是真相。
-
Robert C. Martin, Clean Code(2008,中文版《代码整洁之道》) Boy Scout Rule 的出处。提醒:这本书的某些具体建议(如"函数尽量短")今天已经被滥用并广受批评,但 Boy Scout Rule 这条本身仍然有效------只要你按本文第五节的方式正确使用它。
短读物:一两个小时能读完
-
Joel Spolsky, Things You Should Never Do, Part I(2000) Netscape 因重写浏览器而被 IE 击败的详细复盘。读完后你会本能地拒绝「我们从零开始重写吧」这种建议。
-
Sandi Metz, The Wrong Abstraction(2016) 500 字的短文,定义了一条工程界的硬约束:Prefer duplication over the wrong abstraction。
-
Martin Fowler, Yagni Fowler 把 YAGNI 整理成一篇 5 分钟能读完的实战定义,比《XP Explained》整本更直接。
-
Richard P. Gabriel, The Rise of "Worse is Better"(1991) 如果你对「为什么不完美的方案最终赢了」感到困惑,这是工程史上最经典的回答。
现象与心理学背景
-
C. Northcote Parkinson, Parkinson's Law(1957,中文版《帕金森法则》) Bikeshedding 的概念发源地。读完会理解为什么大组织开会总把最多时间花在最不重要的事情上。
-
Tom DeMarco & Timothy Lister, Peopleware(1987,中文版《人件(第 3 版)》) 解释「为什么完美主义会偷走你的注意力」。1987 年写的,今天读依然准确------这本身就是一种警示。