2016 年 3 月的一个凌晨,全球成千上万条构建流水线同时红了。起因是一个人从 npm 上删除了自己的一个包。那个包叫 left-pad,全部源码 11 行,功能是”在字符串左边补空格”。删除者的动机与任何使用者都无关,但包括 Babel、React 在内的数千个项目,构建瞬间失败–因为它们的依赖树的某条深层分支上,躺着这个 11 行的包。没有任何人写坏了一行代码,是”世界”变了:一个你从未听说过、埋在第十几层的依赖,消失了。
2020 年 12 月 31 日,Adobe 终止 Flash 支持,并在两周后主动屏蔽所有 Flash 内容。大量企业内网系统、政务与教育网站一夜之间变成考古现场。这些系统的开发者大多早已离场,代码一行未动,系统却死了。杀死它的不是 bug,是日历。
每个老程序员还见过第三种:一段没人敢动的祖传代码。”这个函数别碰”——老王说的。老王五年前就走了。它没有文档,没有测试,调用关系盘根错节,连加一行日志都要开三天的会评估风险。它还能跑,但没有人能解释它为什么能跑,也没有人能让它在任何方面变得更好。它不是一个活着的系统,它是一个系统标本。
三件事的共同点:从代码本身看,什么都没有发生。没有新的 bug 被写出来,没有攻击,没有硬件故障。系统自己变坏了。行业把这类现象统称为”技术债”,然后按照金融的逻辑去理解它:欠了债,算利息,找机会还本。
这篇文章要论证的是:“债”这个隐喻从根上就是错的。它对软件退化的预测,错得系统而深刻。真正对口的物理不是金融学,是热力学–技术债不是债,是熵。工程师在这个框架里的位置,也随之清晰:你不是代码的生产者,你是负熵的来源。
这个话题听起来像玄学,所以先把丑话说在前面:全文会像海森堡 bug 那篇一样守住一条线–类比必须是机制性的,不是装饰性的。第十节会专门诚实地交代这个类比在哪里成立、在哪里失效。借用物理不是为了显得深刻,是因为热力学恰好为三件金融隐喻解释不了的事提供了机制:为什么没人动的系统必然腐烂、为什么”以后再清理”永远不会发生、为什么工程师的持续投入本身就是系统的秩序来源。
一、校正误解:技术债不是金融问题
先用最诚实的态度给”技术债”一个公道:这个隐喻能流行三十年,是因为它极其成功地完成了一项沟通任务–向不懂软件的管理层解释”为什么要花时间修没有可见产出的东西”。利息=拖慢后续开发,本金=一次性偿还成本。 Ward Cunningham 在 1992 年发明这个词时,讲的正是这件事:为了赶发布,先写一段”知道不干净”的代码,换来的是未来每一次触碰它时多付一点摩擦。
但作为沟通工具的成功,掩盖不了它作为思维模型的系统性错误。金融债隐含三个承诺,而软件退化一个都不满足:
承诺一:利率可计算。债务有明确的利息,你可以算出拖欠的代价。而软件的退化成本无法计算,因为它通过耦合传播–一个草率的抽象,代价不体现在它自己身上,而体现在未来每一次踩过它的脚步上。你不知道有多少只脚、踩多少次。log4j 事件(2021 年 12 月)之后,无数团队的第一反应是”我们有在用 log4j 吗”–答案是”有,在你依赖的依赖的依赖里”。连损失的范围都测不出来,谈何利率。
承诺二:债务可清算。金融债有一个终点:还清,销账,翻篇。而软件的退化没有一个可以”结清”的账面。你可以重构掉某个模块,但重构本身又引入新的理解成本与新的风险。更关键的是下一节要讲的:即使你不欠任何”新债”,旧账也在自动增长–这一点金融债绝对不这么干(好吧,复利也这么干,但复利不会因为”你什么都不做”而凭空制造新的债主)。
承诺三:拖欠是线性的。债可以慢慢还,每次还一点,状况单调好转。而软件系统的退化是非线性、有相变的:系统会在很长一段时间内”看起来还行”,然后在某个点之后突然进入另一种状态–每个改动都会引发三个新的问题,渐进式改善从经济上不再成立。大泥球不是长出来的,是结晶出来的。跨过临界点之后,”每月多还 10%”不再能把你带回健康区。金融模型对相变一无所知。
Cunningham 本人数十年后专门出面解释过这个比喻的本意–它是关于”有意识地用未来的摩擦换今天的速度”这个决策的,不是给”先写烂代码以后再说”发许可证。但语言一旦离开创造者就会自己生长。”技术债”在大多数组织里的实际用法,是把一个热力学问题记成了一笔糊涂账:没有利率、没有账本、没有清算日,只有一个越来越重的体感。
所以换隐喻。软件系统真正对应的物理对象不是资产负债表,是热力学系统。代码库是一个不断与外界交换”能量”(工程投入)与”物质”(依赖、数据、人员)的开放系统,它自发的方向是退化,维持秩序需要持续的外部投入。下面把这个对应一层层建起来。
二、软件熵:无序是”偏离意图的方式数”
热力学里,玻尔兹曼给出的熵的定义极其朴素:
$$S = k \ln W$$
$W$ 是系统可能处于的微观状态数。熵不是”混乱”这种感觉,是一件可以数的事:这个系统有多少种你无法区分、但后果各不相同的存在方式。可能的态越多,熵越大;约束越强、可能态越少,熵越低。
信息论里,香农 1948 年给信息源的不确定性写下的公式,与玻尔兹曼熵在数学上是同一个对象。这里有一个流传很广的段子:香农为起名犹豫,冯·诺依曼建议他干脆就叫熵–“反正没人真的懂熵是什么,辩论的时候你永远占优”。段子归段子,数学是严肃的:“一个系统可能处于多少种状态”与”我对这个系统还有多少不确定性”,是同一枚硬币的两面。软件系统的”无序”,因此可以给一个不依赖感觉的定义:
软件熵 = 系统实际可能的行为状态数,相对”设计意图所允许的状态数”的超出部分。
你的程序”应该”是什么样,是规格书、类型、测试共同圈定的一小撮合法状态。而它实际可能是什么样,是由一切你约束不了的东西决定的:依赖树里每个包的每个版本、每条数据可能出现的形态、每个配置项的组合、每个并发交错的顺序、每个环境变量的值。约束不到的角落越多,实际状态数 $W$ 越大,系统偏离意图的方式越多–这就是熵。你没遇到 bug,不代表系统处于合法状态;只代表这一次的骰子还没掷出那些状态。
用一个真实事件给这个定义定性:log4j 事件之后,”我的系统里到底有哪些组件”成了全球工程组织的灵魂拷问。答案是绝大多数人答不上来。一个现代应用的依赖树动辄几百上千个传递依赖–每一层都可能变动、可能停更、可能带漏洞。系统的实际状态数,早已超出任何个人的理解范围。这不是道德问题,是结构问题:你在用一个线性缩小的头脑,去约束一个指数膨胀的状态空间。
有一个精确到可以和调试系列咬合的例子:未定义行为(UB)。C/C++ 标准把一类行为声明为”未定义”,等于在契约里写明:”这些状态,语言不约束。”于是系统的合法状态数被标准故意放大了,而优化器恰好在这片放大的状态空间里自由航行–它有权假设”无 UB”并据此变换代码。这就是为什么同一份源码,-O0 和 -O2 能编译出两个语义不同的程序。用熵的语言重述一遍:UB 就是把状态空间的开销从规范转移到运行时环境,谁也不约束的那些状态,最终由谁的编译器、谁的堆布局来掷骰子。
这一节只建立了一个定义。下一节用第二定律回答那个最让人不安的问题:为什么没人动,系统也会坏。
三、第二定律的软件表述:环境是热库,冻结的系统照样腐烂
热力学第二定律的最简表述:孤立系统的熵永不减少。日常版本更直白:东西会自己变坏。咖啡会凉,房间会乱,金属会锈–没有人给房间”添加”混乱,混乱是默认的走向,秩序才是需要解释的现象。
软件界早就观察到了这件事,甚至给它起了名字:bit rot。一段好好运行的程序,无人改动,几年后忽然就不能用了。民间解释常带着玄学色彩(”磁盘位翻转了””宇宙射线”),但真实的机制既不神秘也不罕见–只是很少有人把它写全。写全了你会发现,它就是第二定律的一个具体实现:
关键在于,软件永远不是孤立系统。你以为你冻结了代码,但你冻结不了代码的世界。
一个”冻结”的程序,实际浸泡在一个持续变化的热库里:
| 热库在变什么 | 真实案例 |
| 信任链 | 2020 年 5 月 AddTrust 根证书过期,大量旧设备与旧应用的 HTTPS 突然失败;2021 年 9 月 Let’s Encrypt 旧交叉签名到期,Android 7.1.1 以下的设备大面积报证书错误 |
| 协议 | 2020 年起各大浏览器禁用 TLS 1.0/1.1,一批只会老协议的客户端/服务端组合无声死亡 |
| 依赖生态 | left-pad(2016):热库主动删除了一个状态;npm 因此修改了 unpublish 政策 |
| 维护者的注意力 | event-stream(2018):原作者无心维护,转手给陌生人,恶意代码进入比特币钱包 Copay;faker.js/colors.js(2022):维护者亲手把自己的包改成死循环/删除,数千条流水线陪葬 |
| 时间本身 | Y2K;2038 年 1 月 19 日 32 位 time_t 溢出;时区规则每年都在改 |
| 平台政策 | 2014 年 YouTube API v2 下线,旧智能电视变砖;2021 年 8 月 GitHub 移除 git 的密码认证,所有硬编码密码的 CI 脚本当场失效;2020 年 Flash EOL(本文开头第二个故事) |
| 运行时演化 | Python 2 于 2020 年 1 月 1 日 EOL,整个生态用了十年也没迁完 |
逐条看,你会发现一个规律:每一条都不需要你的系统犯错。你的代码保持完美,热库自顾自地移动,你和环境的相对位置就变了。物理学的说法是:即使系统自身的熵不变,一个与环境耦合的系统,其相对于环境的熵也在增长。咖啡放在屋里会凉,不是因为咖啡分子变坏了,是因为屋子在持续”抽走”它的温度。
软件工程界其实早就经验性地发现了第二定律。1970 年代,Meir Lehman 和 Larry Belady 追踪 IBM OS/360 长达十年的演化,总结出”软件演化定律”,其中一条后来被称为”复杂性递增定律”:除非主动做功去降低它,一个持续使用的系统的复杂度必然随时间增长。注意这个句式–“除非主动做功”。这是热力学的句式,不是金融的句式。金融的句式是”除非还款”, Lehman 的句式是”除非做功”。差别在于:还款有终点,做功没有。第二定律没有”还清”这个概念,它只有”今天做了没有”。
《程序员修炼之道》引用犯罪学里的”破窗理论”来讲同一件事:一栋楼有一扇窗破了没人修,很快所有窗都会破。这个类比对了一半–它强调了秩序的传染性,但没说清为什么”没人修”是常态而非例外。热力学补上了这一块:不修才是自发方向,修是逆流。逆流需要能量,而能量永远紧张。这就引出全文最重要的一节。
四、耗散结构:秩序是流量,不是存量
到这里,第二定律描述的还是下坡路。但物理学家普里高津(Ilya Prigogine,1977 年诺贝尔化学奖)研究了一类奇怪的现象:有些系统偏偏活在远离平衡的地方,并且长期维持着高度的秩序。漩涡在水流中保持形状,火焰在燃烧中保持轮廓,生命体几十年如一日地对抗着自身的分解。它们的共同点:不是不服从第二定律,而是通过持续吸收能量、持续把熵”出口”到环境,在局部维持低熵。普里高津给这类系统起名:耗散结构(dissipative structure)。
耗散结构的要害是:它的秩序不是一种存量,而是一种流量。漩涡不是一个”东西”,是水流的一个过程;把水流掐断,漩涡不会”保存下来”,它直接消失。生命同理:有机体不是一件耐用品,是一条持续的新陈代谢之河。
现在看代码库。健康代码库的每一个特征,都是耗散结构的特征:
- CI 每天在跑(代谢);
- 依赖每周在升(与环境交换物质);
- 重构持续在发生(向外部出口熵);
- 文档与测试随代码更新(把”为什么”存储到系统外)。
而一个”冻结”的代码库–不升级、不动、只保证”能跑”–不是一个被保存下来的秩序,而是一个被掐断了能量输入的漩涡。它不会静止在原来的状态里,它会从”维持”滑入”分解”。第三节那张表就是它的分解时刻表。
最沉痛的真实注脚是 Heartbleed(2014 年)。OpenSSL 当时守护着全球过半网站的加密握手,而它的基金会一年收到的捐款大约只有两千美元量级,核心维护者屈指可数。系统的体积(被依赖的程度)与系统的代谢(持续投入的能量)之间是千倍量级的错配–漏洞不是偶然,是这个耗散结构长期处于能量饥渴的必然产物。事件之后,Linux 基金会成立核心基础设施计划(CII)为它输血,本质上是行业终于承认:这个结构需要的能量流,必须真实地存在,祈祷和感激替代不了。
把”秩序是流量”这把刀,架到工程世界最常见的一句谎话上:”先这样上线,以后再重构。”
金融隐喻下这句话勉强成立:欠债可以延后偿还。热力学框架下它直接违反能量守恒。局部降熵必须注入能量,而”以后”不是一个能量更充裕的时间点–以后的每个冲刺,同样要对抗以后产生的熵(新需求、新依赖、新环境变化),你今天的能量预算没有结余,以后也不会有。工时无法储蓄。每一个”以后”,都在用一个同样贫穷的未来给现在打白条。这不是纪律问题,不是态度问题,是能量守恒不提供赊账。
所以正确的问法从来不是”什么时候还债”,而是”这个系统的代谢率应该是多少“。维护预算不是一个可以从储蓄里支取的数字,它是基础代谢率–低于它,系统进入分解;想减肥可以,但不能停止呼吸。Google 的内部工程实践是一个极端但真实的参照:他们的单体仓库每周执行上百次自动化的大规模重构(改一个 API 签名,机器人同步修改所有调用点),秩序在那里是被工业化连续生产的。不必羡慕规模,但要看懂原理:他们不是欠债更少,是代谢更稳。
五、麦克斯韦妖与兰道尔原理:熵只能转移,不能消灭
第四节留了一个口子:耗散结构”向环境出口熵”。这一节讲清熵的去向问题,它会带出一个 surprising 的结论:重构不消灭复杂度,它只决定复杂度住在哪里。以及–工程师本人,在这个物理图景里到底是什么。
1867 年,麦克斯韦提出了物理学史上最著名的思想实验之一。想象一个妖,守在分隔冷热气体的暗门旁:它看清每个飞来的分子,快的放过去,慢的拦下来。久而久之,冷热分离,秩序出现,而妖看起来没有做任何功。这个妖折磨了物理学一百多年:如果它成立,第二定律就有漏洞。
谜底的解开用了三步,每一步都对软件有精确的对应:
- 西拉德(1929)指出:妖的魔法在于信息–它知道每个分子的速度,是这份知识在降熵。
- 兰道尔(1961)指出:信息是物理的,擦除信息有物理代价–抹掉一比特,至少耗散 $kT\ln 2$ 的能量。删除不是免费的。
- 贝内特(1982)补完最后一环:妖的内存有限,它必须不断擦除旧记录才能继续观察,而擦除的代价恰好抵消它创造的秩序。妖不免费。降熵的账单在”遗忘”处支付。
现在把工程师放进这个妖的位置。
工程师就是麦克斯韦妖。 你降熵的资本不是体力,是信息:你知道系统”应该”是什么样、每个模块存在的理由、哪个方案试过并且失败了。你评审代码时做的,正是妖的工作–看清每个飞来的变更,把会增加状态数的拦下来,把收敛状态数的放过去。一个有经验的工程师和一个新手的差别,本质上是”分子速度信息量”的差别。
但兰道尔原理紧接着给出警告,而这条警告恰恰是软件行业付了三十年学费才慢慢学会的:
删除不是免费的。 你删掉一段代码,删掉的不只是代码,还有”它为什么存在”的全部上下文。这个擦除动作有一张账单,要么现在付–把”为什么”转移到系统外部(测试、ADR、写得像人话的 commit message);要么未来付–某个后人(常常就是三个月后的你自己)在同一个地方重新踩一遍坑,重新发明一遍”为什么”。测试和架构决策记录(ADR)不是官僚主义的文档要求,它们是付给兰道尔原理的账单:把”系统必须如此”与”当年为何如此”存储在系统之外,让删除变便宜,让遗忘有备份。
把”熵只能转移、不能消灭”用在重构上,会得到一张任何架构师都该背下来的清单。重构产生的”减少的复杂度”,实际只有三个去处:
- 转移进更清晰的结构(模块化、分层、收敛 API):状态数被约束关进了更小的笼子。最优出口。
- 出口到外部存储(测试、文档、git 历史、ADR):复杂度以”可检索的说明”形式存在,代价是维护这些存储。次优,但必要。
- 留在人的头脑里:某几个人”就是知道”这摊东西怎么回事。这是最劣的出口,因为这个介质会蒸发–下班会清空缓存,离职就是格式化。
第三个出口,就是 bus factor 的热力学定义:系统运转所必需的负熵,有多大比例存储在会蒸发的介质里。 event-stream 和 faker.js 两起事件里,蒸发的是维护者本人;很多公司里,蒸发的是那个”什么都懂”的架构师。介质蒸发之后,那些没有来得及出口到结构或文档的秩序,就永远地变回了熵。
这一节还有一个值得尊敬的真实反例,它展示了”出口熵”的工程学最高水准。Windows 95 开发时,团队发现 SimCity 2000 在释放内存后仍然使用它–按今天的说法是教科书级 use-after-free,而它的代码显然不会改。Raymond Chen 团队的做法不是要求全世界的旧软件升级(把能量成本推给环境里最没有能量的人),而是让操作系统替它买单:检测到是 SimCity 在跑,就不真正释放那块内存。此后二十多年,Windows 的应用兼容层(appcompat shim)积累了成千上万条这样的受控规则。用热力学语言说:微软没有消灭旧软件的熵,他们把散落在千万用户处的无序,出口到了一个集中、可管理、可审计的结构里。这是一项被低估的负熵工程:承认熵的存在,给它修一个水库。
六、自由能与相变:从”渐进重构”到”只能重写”之间,存在临界点
前面五节讲的是渐进过程。这一节讲突变。
热力学里有个概念叫自由能:系统总能量中真正可提取、可使用的那部分。一杯沸水中和一桶温水中,可能蕴含同样的热量,但只有前者能推动机器–因为可用的不是总量,是与环境存在落差的这部分。代码库有一个精确的对应物:一个功能齐全的系统,可能已经是”温水”–所有能力都在,但你什么新功能都做不动。系统的规模还在,系统的自由能没了:每一分能力都锁死在与其它能力纠缠的结构里,提取不出来。
而自由能的耗尽,不是线性的,是有相变的。水在 99 度还是水,100 度突然成为汽。大泥球(big ball of mud,Foote 与 Yoder 1997 年的名文)的形成也一样:系统在很长一段时间里”还能改”,然后在某个点之后,进入另一种状态–改一处,崩三处。不是突然出现了新的坏代码,是纠缠度越过了临界值,任何改动传播的代价都从”局部”变成”全局”。
怎么知道系统接近临界点了?这些信号比任何指标都先到:
- 改动半径持续扩大:一个一行的需求,要动五个”不相关”的模块。
- 测试套件失去信号:长期全红,没人相信它,于是没人看它。
- 依赖口头地图:”这个先问老王”式的知识,成为变更的前置条件(第五节的第三出口已占主导)。
- 变更失败率上升、hotfix 周期拉长:系统对每一次触碰的”惊讶度”越来越高。
- 新人的上手时间以年计:外部化存储近乎归零,负熵全部存在人脑里。
过了临界点,选项就变了。渐进重构的负熵注入率,追不上熵产生率–这时候”每月还 10%”的金融逻辑彻底失效,需要的是一次性的、超过阈值的能量注入:隔离重写、绞杀者模式(strangler fig,用新系统逐步包绕旧系统,Martin Fowler 命名)、或整体重构。这是热力学的”必须先熔化,再结晶”。
但重写本身有一个教科书级的反面案例,值得每个动了”推倒重来”念头的人复习。Netscape 在 1990 年代末决定放弃旧代码,从零重写浏览器引擎,计划两年,实际拖了近四年–这期间旧产品停止演化,竞争对手拿走了整个市场。Joel Spolsky 那篇著名的檄文《Things You Should Never Do》(2000)正是为此而写,核心论断用本文的语言重述就是:重写扔掉的是代码,扔不掉的是隐性知识。旧代码里那些看似愚蠢的分支,很多是第 7 个真实用户的第 13 种真实环境逼出来的已经付过款的负熵–它们以最劣的形式(没有文档的代码)存储着,但它们确实存储着。推倒重写等于把这部分秩序连本带利还给熵,再用四年的时间重新挣回来。所以相变之路上,绞杀者模式优于推倒重来:逐块结晶,而不是整块熔化–每一步都保住已完成的知识,让系统在重写期间仍然活着。
还要记住相变的滞后效应:跨过临界点之后,即使你重新投入与从前相同的能量,也回不到从前的状态–恢复需要的是超过阈值的一次性投入。这就是为什么”我们只是比去年少招了两个人,怎么突然就乱成这样”的故事每年都在上演。不是突然,是早就过了线。
七、热寂:最稳定的系统,是最死的系统
热力学对宇宙的终局有一个假想:热寂。万物温度趋同,再无落差,熵达到最大。注意热寂的恐怖之处不是毁灭,而是无事可做–没有温差就没有功,没有功就没有变化。系统还在,宇宙还在,但再也不可能发生任何有意义的事了。
软件的热寂是什么样?不是崩溃。恰恰相反–是一个极其稳定的系统:
- 它在生产上稳定运行,一年不出一次事故;
- 没有任何人修改它,所以没有变更,所以没有风险;
- 它依赖的运行时已经 EOL 多年,锁死在某个永不升级的镜像里;
- 团队里没有一个人能完整说出它做什么,包括花钱养着它的业务方;
- 任何修改–包括安全补丁–的风险评估都高于收益,于是永远不做。
全球银行体系里数以千亿行计的 COBOL 是这个图景的经典版:不是没人想换,是它稳定到动不了的代价高过一切,同时它又死到不承载任何人的理解。它不是活的系统,它是系统形状的化石。
热寂给了一个反直觉的判词,值得写得大一点:
稳定与死亡,在极限处重合。一个”绝对不要动它”的系统,和死掉的系统,是同一个系统。
“别动它,它能跑”(if it works, don’t touch it)作为个人某次深夜 hotfix 后的喘息,是人之常情;作为组织策略,它是热寂的处方。因为第三节的热库不会因为你决定不动而停止移动–你不动,等于单方面宣布你的系统进入倒计时,倒计时的终点不是崩溃(崩溃反而会迫使你做功),而是连崩溃的资格都失去:一个不能被理解的系统,连”修好它”都无从定义。
2017 年的 Equifax 把这一节的学费替整个行业交了:Apache Struts 爆出高危漏洞后,Equifax 不是拒绝修补,而是在长达数月里无法定位自己哪些系统带着这个组件,最终 1.47 亿人的数据泄露。事后调查里最刺痛工程界的一条不是”他们没有补丁”,而是”他们的资产与依赖清单不完整”。依赖清单就是系统的”状态目录”–目录缺失的系统,处于热寂的早期:它连回答”我在哪里”都需要考古。四年后的 log4j,同样的剧情在几万家公司重演。
对照第六节的临界点信号你会发现它们是同一件事的两个刻度:相变是走向热寂的路上经过的那道门。跨过去之后仍持续供能的系统,会变成化石;跨过去之前持续供能的系统,还只是温水。两者之间,就是工程还有效的窗口。
八、AI 时代的熵账本
到这里,这个框架必须回答一个 2026 年的问题:AI 大规模写代码之后,熵的账变了没有?
变了,而且变得根本。用第二节的语言一句话说清:
生成放大 $W$(状态数),验证压缩 $W$(状态数)。AI 把生成变成了接近免费的东西,于是验证成了唯一的硬通货。
一次生成十个方案、一个下午堆出几千行能跑的代码–从熵的视角看,这不是在生产秩序,这是在批量制造未被约束的状态。每一行未经审查的 AI 代码,都是系统状态数的一次扩张:它可能正确,但”可能正确”的意思就是”还处于多种状态叠加”。谁负责把这些叠加态坍缩成确定的那个?验证能量:测试、评审、类型、运行时探针。第五节的结论在这里原样成立–熵只能转移,不能消灭。AI 没有消灭”把代码写对”这项工作,它只是把这项工作从”写”搬到了”证”。
经验数据已经在收账。GitClear 对两亿多行代码的逐年分析发现,AI 普及以来代码库的 churn(短期内被重写的代码比例)显著上升,复制粘贴式的重复代码块数年间涨了数倍,”被移动/重构的代码”历史上首次少于”被复制粘贴的代码”–用本文的语言说:行业平均的熵产生率,上升了。另一个黑色幽默的新词叫 slopsquatting:研究发现模型生成的代码里,相当比例引用了根本不存在的包,而攻击者开始抢注这些幻觉名字,等着复制粘贴的人上钩。幻觉包就是纯粹的状态数–连存在都不存在,就已经在扩大你的依赖树了。
但要中肯。同样的物理,反过来读就是机会:
- 生成便宜了,意味着试错的成本结构变了。第二节的搜索式开发(生成十个,验证后取其一)第一次在经济上成立–前提是验证基础设施跟得上。验证能量充足的团队,AI 是负熵泵:测试生成、依赖分析、大规模机械重构(第四节的 Google 式自动重构,正在变得人人可用)、旧代码的理解辅助(把第五节的第三出口–人脑里的隐性知识–部分搬运到文档)。
- 验证能量不足的团队,AI 是熵注入器:状态数暴涨,无人坍缩,churn 和重复率替你记账。
所以 AI 时代的热力学判据非常朴素:你的测试、评审、类型、CI 的吞吐,跟得上你生成代码的吞吐吗? 跟得上,你在用 AI 降熵;跟不上,你在用 AI 加速热寂。生成与验证的熵账,永远守恒。
九、负熵工程清单:把物理变成 SOP
物理讲完,落地为清单。这份清单的每一条,都对应前文的一条定律。
1. 把维护预算定义为”基础代谢率”,而不是”储蓄”。 每个周期固定比例(例如 10%~20%)的工程时间投入降熵活动:依赖升级、重构、测试补齐、文档。它的性质是呼吸,不是还债–低于基础代谢率,系统进入分解,且滞后效应(第六节)会让伤害在几个月后才显形。
2. 模块边界是熵屏障。 清晰的模块/API 边界,作用是把 disorder 圈在原地:一个模块内部腐烂,边界阻止它向全局传播。第五节 Windows shim 的启示反过来用:与其幻想没有熵,不如设计熵的隔离舱和排洪渠。微服务不是唯一形态,一个边界清晰的模块化单体完全等效–重点是所有权与契约,不是部署形态。
3. 测试是出口到系统外的熵。 每一条测试都是一句”系统必须如此”的外部存储。测试不是质量部门的仪式,是代码库的外置记忆–它把你脑中的约束坍缩成可执行的断言,让删除(第五节的兰道尔账单)变得便宜。测试覆盖差的 AI 生成代码,等于连账单都不打算付。
4. ADR 与 commit message 是付给兰道尔的账单。 决策记录(ADR)存”当年为何如此”,好的 commit message 存”这次为何如此”。它们让三个月后的你不必重新发明历史。写不出”为什么”的变更,本身就是一个危险信号。
5. CI 是代谢系统,不是门卫。 CI 的热力学角色是让系统持续做功:每一次提交都在验证全局状态、出口熵。它必须不可绕过–绕过 CI 的提交,就是给耗散结构断电。
6. 依赖治理是与热库的界面管理。 SBOM(软件物料清单)就是第三、七节里缺的那本”状态目录”–Equifax 缺的就是它。锁文件、定期自动升级(Dependabot/Renovate)、依赖的年龄与维护者健康度检查,都是对第三节那张表的制度化应对。主动升级是花小钱买秩序,被动腐烂是欠高利贷。
7. 每年做一次 bus factor 审计(第五节第三出口的盘点)。 对每个关键子系统问一句:必需的知识,有多少只存在于人脑里?超过一个人脑袋的,出口到结构(重构)或存储(文档、测试)。
8. 盯住熵指标的长期趋势,而不是绝对值。 绝对值毫无意义(任何复杂系统都有熵),趋势才是代谢健康的体检报告:
| 指标 | 近似的熵含义 | 观察方式 |
| 代码 churn 比例 | 短期重写率:方向未定/理解不足 | git 日志统计 |
| 重复代码块趋势 | 放弃结构化、直接复制状态 | jscpd / SonarQube |
| 变更失败率、回滚率 | 系统对变更的”惊讶度” | DORA 指标 |
| 改动半径(一个需求触达的模块数) | 纠缠度,接近第六节临界点的信号 | 代码考古/评审记录 |
| 新人上手指标 | 外部化存储缺口的代理 | 入职数据 |
| SBOM 完整度 | 对系统状态数的自知之明 | syft 等工具 |
十、诚实声明:这个类比在哪里失效
按照开头的承诺,这一节专门拆自己的台。物理学家读到这里大概已经眉头紧锁,他们的不满基本成立:
1. 软件没有温度,也算不出玻尔兹曼常数。 $S = k\ln W$ 里的 $k$ 有精确的物理值;软件的”状态”依赖你选取的抽象层级–从比特看、从函数看、从服务看,$W$ 是三个不同的数。所以本文从头到尾只做了定性推理,没有(也不可能)做任何定量计算。谁要是声称算出了”代码库的熵值”,那是在售卖伪科学。
2. “能量”不守恒。 热力学的能量严格守恒;工程里的”能量”是注意力与工时,它稀缺、有成本,但不能储蓄(第四节的论据恰恰建在这一条上–这是类比的有利偏差,但偏差就是偏差)。它也会疲劳、会离职、会被下一个季度的 OKR 挤掉–物理能量没有这些烦恼。
3. 不是所有复杂度都是熵。 Brooks 在《没有银弹》里的区分必须继承:本质复杂度是问题域自带的(税务规则就是复杂,与代码写得好坏无关),偶然复杂度才是我们自己制造的 Disorder。第九节的清单只应该作用于后者。把本质复杂度当熵去”重构”,是负熵工程里最常见的南辕北辙。
4. 物理的不可逆,软件里其实是”恢复成本高”。 分子不会后悔,代码可以被重新写出来–代价是时间与知识(第六节的 Netscape)。用”不可逆”描述软件,严格说是”不可逆经济上“。但正因为恢复要花钱,这条类比在决策上依然成立,只是成色打了折。
5. 最大的风险是这个框架本身被滥用。 “熵”这个词天然带三分深刻感,很容易退化成万能的说辞–拒绝任何新需求时说”这是熵增”,解释任何失败时说”代谢不足”。一个隐喻如果什么都能解释,它就什么都没解释。本篇用一万字建立这个框架,就是为了把它钉死在三个可检验的断言上:状态计数(偏离意图的方式数是真实的、可观察的)、不可逆性(有些丢失买不回来,或者说买回来的价格高到不可行)、开放系统(你冻结不了世界)。凡是用不进这三条的”熵”,都是修辞。
结语:工程师的物理学定义
1944 年,薛定谔在《生命是什么》里写下了那个著名的判断:生命以负熵为食。有机体维持自身秩序的方式,不是违背第二定律,而是持续从环境汲取秩序、把混乱排出体外–生命是宇宙里一小片逆流而上的漩涡。(值得诚实的是,薛定谔后来自己修正过:更严谨的说法是”生命以自由能为食”。这个修正不影响本文,反而提醒我们:连物理学家也在给自己的类比打补丁。)
把这篇长文收拢成一句话,就是给”工程师”这个职业一个热力学的定义:
工程师,是一个持续向系统注入负熵、以维持其远离热平衡态的信息结构。
你写下的每一行代码都会过期,你设计的每一个架构都会腐烂–这不是你的失败,这是第二定律,和咖啡会凉一样不值得沮丧。真正的分野在于:你是假装秩序是存量(”这次做完就一劳永逸了”),还是承认秩序是流量(”这个系统靠什么呼吸”)。
软件不是一个制品(artifact),而是一个过程(process)。它更像火焰,而不像雕塑。
你不是在建造它,你是在持续地点燃它。松手的那天,不是它完工的那天,是它开始熄灭的那天。
最后,一句给个人的话。第五节说工程师是麦克斯韦妖,但妖不是永动机–妖的降熵靠信息,而信息会遗忘;妖的做功靠能量,而能量会耗尽。所以可持续的负熵工程,永远不要建立在”某个英雄永不下班”之上,而要建立在第九节那些朴素机制之上:让结构替人记住,让测试替人约束,让流程替人呼吸。好的工程师写的不是不会腐烂的代码–那不存在–而是腐烂得最慢、最可预期、最容易修补的代码,以及一套自己会代谢的机制。
这不是宿命论。恰恰相反:看清水往低处流的人,才知道水泵应该装在哪。
附录 A:bit rot 触发源速查表
| 触发源 | 机制 | 代表案例 | 应对 |
| 证书与信任链 | 根证书过期/换签,旧端点失效 | AddTrust 过期(2020)、Let’s Encrypt 交叉签名到期(2021) | 证书监控、提前轮转测试 |
| 协议淘汰 | 平台方禁用旧协议 | TLS 1.0/1.1 禁用(2020) | 依赖扫描含协议项 |
| 依赖断供 | 上游包消失/改名 | left-pad(2016) | 锁文件、私有镜像/代理仓 |
| 维护者流失 | 能量流停止 | event-stream(2018)、faker.js(2022) | 依赖健康度检查(维护者数、发布频率) |
| 时间 | 纪元与字段溢出 | Y2K、Y2038(2038-01-19) | 64 位 time_t 审计 |
| 平台政策 | API 下线、认证方式变更 | YouTube API v2(2014)、GitHub 移除密码认证(2021)、Flash EOL(2020) | 订阅上游 deprecation 公告 |
| 运行时演化 | 语言/运行时 EOL | Python 2 EOL(2020) | 升级是代谢,不是项目 |
附录 B:系统的熵体检 SOP(每半年 30 分钟)
- 拉趋势(10 分钟):churn 率、重复块数、变更失败率、改动半径–只看趋势拐点,不看绝对值。
- 点库存(10 分钟):SBOM 完整吗?EOL 依赖几个?无维护者的直接依赖几个?
- 数人头(5 分钟):关键子系统的 bus factor 是几?哪些知识只在一个人脑里?
- 对信号(5 分钟):过一遍第六节的临界点信号清单。命中三条以上,渐进重构已不充分,启动绞杀者模式的评估–不要等到第六条。
附录 C:三条铁律
- 熵只能转移,不能消灭–每次”清理”前先问:复杂度搬去哪了?结构、存储、还是人脑?
- 秩序是流量,不是存量–维护预算是基础代谢率,低于它的每一分节省,都在预支相变。
- 你冻结不了世界–“先不动它”不是保存秩序,是把系统的存亡托付给一个不会停的热库。

发表回复