1995年,Niklaus Wirth在《Computer》杂志上发表了一篇名为《A Plea for Lean Software》的文章。他在文中感叹:软件已经膨胀到以兆字节计。他引以为傲的Oberon操作系统——包含编辑器和编译器——只有200千字节。
三十年过去了。今天,一个简单的"Hello World" Electron应用就需要244兆字节。Wirth在2024年1月1日去世,但他留下的定律依然在折磨着我们:软件变慢的速度,比硬件变快的速度更快。
这不是夸张。这是一场持续了半个世纪的工程权衡。
一个被忽视的定律
大多数人熟悉摩尔定律:集成电路上的晶体管数量每18到24个月翻一番。但很少有人知道它的"邪恶双胞胎"——Wirth定律。
Wirth定律的表述是:软件变慢的速度,比硬件变快的速度更快。换句话说,硬件性能的提升,几乎完全被软件膨胀所抵消。
这个定律有一个更直白的名字:“软件膨胀定律”。
1995年,当Wirth抱怨软件达到"兆字节"级别时,他的读者可能会觉得这是危言耸听。但数据证明他是对的——而且比他想象的更糟。
微信:从457KB到257MB的十一年
2011年1月,微信发布Android 1.0版本,安装包大小为457KB。2022年7月,微信Android 8.0.24版本的安装包达到了257MB。
膨胀了575倍。
这不是孤例。根据DT财经统计的50个用户规模过亿的手机App,初始版本的安装包加起来约1.14GB,但这些App现在的安装包加起来已经超过100GB。
操作系统:从50MB到64GB
Windows 95的标准安装需要50-55MB硬盘空间。Windows 11需要64GB——这是1000倍的差距。
内存需求增长同样惊人。Windows 95最低需要4MB内存,Windows 11最低需要4GB——1000倍。
| 操作系统 | 发布年份 | 最小内存 | 硬盘空间 |
|---|---|---|---|
| Windows 95 | 1995 | 4 MB | 50-55 MB |
| Windows XP | 2001 | 64 MB | 1.5 GB |
| Windows 7 | 2009 | 1 GB | 16 GB |
| Windows 10 | 2015 | 2 GB | 32 GB |
| Windows 11 | 2021 | 4 GB | 64 GB |
这不是微软一家的问题。macOS、Linux发行版都在沿着同样的轨迹膨胀。
膨胀的根源:不是懒惰,是选择
软件膨胀不是开发者懒惰的结果。它是无数个理性决策累积的后果。
抽象层的代价
现代软件开发建立在层层抽象之上。以一个典型的Web应用为例:
- 硬件层:CPU、内存、存储
- 操作系统层:进程管理、内存管理、文件系统
- 运行时层:Node.js、JVM、.NET
- 框架层:React、Angular、Vue
- 库层:成百上千的第三方依赖
- 应用层:业务逻辑代码
每一层都承诺"让开发者更高效",但每一层都带来开销。
Electron是这种抽象堆积的典型案例。一个Electron应用包含:
- 完整的Chromium浏览器引擎
- Node.js运行时
- 应用自身的JavaScript代码
结果是:一个简单的桌面应用就需要200MB+的安装包,运行时占用数百MB内存。
依赖爆炸
2016年3月,一个名为Azer Koçulu的开发者从npm删除了一个名为left-pad的包。这个包只有11行代码,功能是将字符串填充到指定长度。
结果:全球数以万计的项目构建失败。React、Babel、Node.js——整个JavaScript生态都受到了影响。
这揭示了一个令人不安的事实:现代软件的依赖树已经深到没有人能完全理解。
根据Sonatype 2023年的研究,平均每个JavaScript项目包含42个直接依赖和683个传递依赖。这意味着你安装一个包,可能不知不觉引入了数百个其他包。
一个典型的node_modules文件夹动辄几百MB。这不是异常,这是常态。
开发效率与运行效率的权衡
当代码要被机器执行时,效率是第一要务。当代码要被人阅读和维护时,可读性是第一要务。
但在商业项目中,还有第三个更重要的维度:开发速度。
一个功能,用C++从零实现需要两周,用React加现成组件库只需要两天。在市场竞争的压力下,选择哪个?
“快速上市"几乎总是击败"高效运行”。这不是开发者的错,这是商业逻辑的必然结果。
Wirth在1995年写道:时间压力可能是软件膨胀的首要原因。它阻碍了仔细规划,阻碍了对可接受方案的改进,鼓励快速构思的软件添加和修正。时间压力逐渐腐蚀了工程师的质量和完美标准。
安全:膨胀的隐性成本
软件膨胀不只是用户体验问题,它是安全问题的温床。
攻击面的指数增长
网络安全有一个基本原理:代码量越大,漏洞越多。
这不是理论推测。假设每千行代码平均有5-50个漏洞,那么一个100万行代码的项目可能有5000到50000个漏洞。
问题在于:现代软件的代码量已经膨胀到令人难以置信的程度。
IEEE Spectrum的一篇文章指出:一个基于Electron的应用,如果算上所有依赖,可能包含超过5000万行代码。而这一切,只是为了打开一个车库门。
Apple的iMessage攻击案例说明了问题的严重性。苹果决定让iMessage支持大量图片格式的预览——包括带有嵌入式字体的PDF。结果是:攻击者可以通过发送一条消息,利用PDF解析器中的漏洞远程控制iPhone。
这个漏洞不是核心功能,而是"功能膨胀"带来的副作用。
供应链攻击的温床
依赖越多,风险越大。每个第三方库都是潜在的攻击入口。
2024年的研究表明,npm生态系统中有大量被劫持、植入恶意代码的包。开发者无法逐一审查数百个依赖,只能祈祷不出问题。
2018年的event-stream事件是一个典型案例:一个流行的npm包被黑客接管,植入了窃取加密货币钱包的恶意代码。数百万下载量,无人察觉。
环境:被忽视的代价
软件膨胀的环境影响很少被讨论,但数据令人震惊。
电子垃圾的加速器
2022年,全球产生了6200万吨电子垃圾,比2010年增长82%。预计到2030年将达到8200万吨。
软件膨胀是电子垃圾增长的重要推手。当操作系统和应用程序不断膨胀,用户的硬件更快变得"过时"。一台运行Windows 95的电脑只需要4MB内存,而Windows 11需要4GB——旧硬件被软件膨胀强制淘汰。
根据联合国大学的数据,2022年全球电子垃圾的价值估计为910亿美元,但只有17.4%被妥善回收。
能源消耗的隐形增长
臃肿的软件消耗更多能源——不仅在终端设备上,更在数据中心。
全球数据中心消耗的电力占全球电力的约2-3%。虽然能效在提高,但需求增长更快。软件膨胀是需求增长的重要因素:每一个冗余的功能、每一个不必要的依赖,都在全球服务器上复制数百万次。
另一种可能:极简软件的证明
软件膨胀不是物理定律,它是工程选择。有证据表明,另一种方式是可行的。
Oberon:200KB的完整系统
Wirth在1995年展示的Oberon操作系统是一个完整的开发环境:操作系统、编辑器、编译器,总共200KB。
这不是玩具系统。Oberon被用于实际的教学和研究工作,证明了"小而完整"是可能的。
Trifecta:现代的证明
2024年,一位开发者为了证明观点,编写了一个名为Trifecta的图片分享服务:
- 1600行新源代码
- 约5个重要依赖
- 总大小:3MB
对比:另一个类似的图片分享服务打包为288MB的Docker镜像,还有一个基于Node.js的解决方案包含1600个依赖,超过400万行JavaScript代码。
Tauri vs Electron:架构选择的力量
Tauri框架展示了架构选择对软件大小的巨大影响。同样的应用:
| 指标 | Tauri | Electron |
|---|---|---|
| 安装包大小 | 8.6 MB | 244 MB |
| 内存占用(6个窗口) | 172 MB | 409 MB |
| 底层引擎 | 系统WebView | Chromium |
Tauri使用操作系统的原生WebView,而不是打包完整的Chromium。Rust后端编译为原生二进制,不需要JavaScript运行时。
结果是:安装包小了28倍,内存占用减少58%。
为什么膨胀无法停止
知道问题不等于能解决问题。软件膨胀持续增长,有其深刻的结构性原因。
市场机制不支持效率
在大多数软件市场,效率不是竞争优势。用户选择应用的标准是功能、价格、易用性,而不是内存占用或安装包大小。
当效率不产生收益时,投入资源优化效率就是不理性的商业决策。
正如Electron的维护者Felix Rieseberg所言:用户,无论是消费者还是企业,都不在乎二进制大小。一小时4K Netflix视频约7GB,一次Call of Duty更新超过300GB。在实践中,我们没看到终端用户比关心其他任何事情更关心二进制大小。
开发者的激励机制
开发者的激励机制也不支持效率。
- 功能越多,考核指标越好:完成的Story点数、新增的功能数量
- 依赖越多,开发越快:使用现成库比自己实现快得多
- 优化无产出:重构和优化代码需要时间,但不产生可衡量的"成果"
结果是:每个开发者都理性地选择"膨胀",集体结果却是灾难。
硬件的"纵容"
摩尔定律几十年的持续运作,让硬件性能提升成为软件膨胀的"安全网"。
“硬件会追上来的”——这句话成为了不优化代码的借口。
但摩尔定律正在放缓。2025年,PassMark首次记录到平均CPU性能的年度下降。硬件的免费午餐可能即将结束。
可能的出路
承认软件膨胀是理性决策的结果,不意味着接受它是不可改变的。
工程层面的策略
代码重构:定期清理冗余代码,移除未使用的依赖。这是一项需要持续投入的工作,但长期收益巨大。
模块化设计:将软件分为独立模块,每个模块可以独立更新、替换或移除。这减少了维护负担,也限制了问题的传播范围。
审慎使用依赖:不是每个功能都需要第三方库。有时候,几十行代码就能解决问题,而不需要引入一个可能带来安全风险的依赖。
选择高效框架:Tauri证明了跨平台开发不必以巨大开销为代价。在选择技术栈时,效率应该是一个考量因素。
组织层面的改变
建立效率指标:如果效率不被测量,它就不会被优化。安装包大小、内存占用、启动时间——这些指标应该被追踪和报告。
分配优化时间:在项目计划中,明确分配时间用于代码优化和依赖审计。将"技术债务偿还"作为正式的工作项。
建立依赖审查流程:引入新的依赖应该经过正式审查,评估其必要性、安全性和维护状态。
行业层面的推动
欧洲联盟的网络弹性法案(Cyber Resilience Act)要求软件供应商最小化攻击面。这可能是法规推动效率改进的先驱。
开源社区也在行动。越来越多的项目开始关注二进制大小、内存占用和启动时间。这种"效率意识"的传播是改变的基础。
结语
1995年,Wirth写道:软件膨胀的瘟疫不是自然法则。它是可以避免的,限制它是软件工程师的任务。
三十年过去了,软件膨胀的速度丝毫没有放缓。如果有什么变化,那就是它加速了。
但这不意味着我们应该放弃。Wirth的Oberon、现代的Tauri、Trifecta等项目证明了"小而完整"依然是可能的。关键在于:我们需要重新审视那些被视为理所当然的权衡。
硬件性能不是免费的。依赖不是无成本的。开发速度不应以运行效率为代价。
当摩尔定律最终触及物理极限时,当能源成本和电子垃圾成为不可回避的问题时,软件效率将从"可选优化"变成"生存必需"。
在此之前,我们能做的是:在每一个决策中,问自己一个问题——这段代码、这个依赖、这个功能,是否真的值得它带来的开销?
也许,这就是Wirth留给我们最宝贵的问题。
参考资料
- Wirth, N. (1995). “A Plea for Lean Software”. Computer, 28(2), 64-68.
- IEEE Spectrum. “Why Bloat Is Still Software’s Biggest Vulnerability”. February 2024.
- Pandaily. “WeChat Installation Package Expands 575 Times in 11 Years”. July 2022.
- Sonatype. “2024 Software Supply Chain Report”.
- Rieseberg, F. “Things people get wrong about Electron”. January 2025.
- Gethopp Blog. “Tauri vs. Electron: performance, bundle size, and the real trade-offs”. April 2025.
- Emsisoft. “The Hidden Danger: How Software Bloat Poses a Security Threat”. March 2024.
- Wikipedia. “npm left-pad incident”.
- Wikipedia. “Software bloat”.
- Wikipedia. “Feature creep”.
- Wikipedia. “Planned obsolescence”.
- WHO. “Electronic waste (e-waste) Fact Sheet”. October 2024.
- United Nations Institute for Training and Research. “Global E-Waste Monitor 2024”.
- GWS Media. “How system requirements for Microsoft Windows have changed”. December 2021.
- Bundlephobia. “Size of npm dependencies”.
- Medium. “The Hidden Costs of NPM Dependencies”. April 2025.
- Reddit. “Is Wirth’s law true?”. February 2017.