代码是负债,规格才是资产

去年重写一个跑了八年的老系统时,我们遇到一段谁也不敢动的代码:一个订单状态机里,某个分支对特定渠道的订单多做了一次看似多余的状态回写。没有注释,没有文档,git blame 指向一个六年前离职的同事,提交信息是「fix」。

删掉它?没人敢。留着它?重写后的新系统要不要复刻这个行为?我们最后花了两天,从客服的历史工单里考古出了答案:那是给某个老渠道的对账系统留的兼容行为,那个渠道——三年前就下线了。两天考古,换来「可以安全删除」五个字。

这件事当时只当是老系统的日常。直到最近读到 lencx 在《浅谈 AI 编程》里的一个说法,我才把它放进一个更大的框架里:代码是负债,规格约束才是资产。这个「暴论」值得认真对待,因为 AI 正在把它从哲学命题变成经济现实。

推导其实很平实

AI 把代码生产的边际成本压到趋近于零,这件事的直接后果是:软件工程的核心矛盾从「谁来写代码」变成「谁来定义正确性、约束生成、验证结果、承担长期演化责任」。在新矛盾下重新记账,代码就进了负债栏,理由有三:

代码会腐烂。框架升级、依赖漏洞、业务演进,每一项都在让存量代码贬值。持有代码要持续付维护利息——这就是负债的定义。

代码会隐藏意图。代码只说「现在怎么做」,不说「为什么必须这么做」。我们那段状态回写就是标本:行为还在,理由失传。而 AI 重构时最需要的恰恰是理由——没有意图信息,模型会满怀信心地把最重要的补丁当垃圾清理掉。它比人类更果断,这在这个场景下不是优点。

代码会制造审查负担。这是 AI 时代新增的属性。生成可以无限扩容,人类审查不能:机器分钟级产出几千行,人小时级验证。Stack Overflow 的调查数据把这个矛盾摆得很直白——84% 的开发者在用或计划用 AI 工具,只有 3.1% 高度信任 AI 的输出。采纳率和信任度在剧烈分叉,分叉的裂缝里堆的全是待审查的 diff。

「几乎对」比「错」危险

审查负担里最阴险的部分,是 AI 产出的错误画像变了。明显的错误不可怕,CI 会拦、测试会红。可怕的是几乎对的代码:局部完全合理,整体悄悄失稳。

我们真实遇到过的几例:新增的依赖版本与部署基线冲突(代码本身没毛病);复用了一个工具函数但破坏了它的隐性约定(那个函数所有调用方都默认输入已排序);为了让测试变绿写了一个恰好绕过断言的特判。每一例单独 review 都很难看出问题——它们「披着工程完整性的外衣」:有测试、有类型、有错误处理。有人给这类东西起名叫代码泥浆,我觉得很传神:看起来是坚实的地面,踩上去才知道。

泥浆的生产速度是人肉审查跟不上的。这就逼出下一个问题——

什么才是资产:一个思想实验

想清楚这个问题,一个思想实验就够:下一代模型发布时,你允许它重写什么?

实现代码——随便重写,写得更好我求之不得。但业务不变量(「余额不能为负」)、接口契约、合规要求、安全边界、兼容承诺、性能预算、历史决策记录——一个字都轮不到它自作主张。

这条线就是资产负债的分界线。没有这些约束,模型升级只会让混乱以更高速度发生;有了这些约束,模型升级是一次可控的再编译。代码是规格的一次编译产物,过期了重新编译就是;规格才是跨模型周期存续的东西。

按这个标准盘点我们的资产清单:spec 和验收标准、领域术语表、机器可读的接口和数据契约、测试集、ADR(架构决策记录)、风险清单。盘点的结果挺羞愧的——八年老系统,代码几十万行,够格叫「资产」的文档不到二十份。那两天的考古,本质上是在为六年前没写的一行 ADR 付利息。

应对:从逐行审查到分层治理

回到审查跟不上的问题。既然生成/审查的非对称是结构性的,用人海战术逐行 review 对抗批量生成就注定失败,出路是把审查从「看代码」升级成「按风险分层治理」:

低风险(文档、注释、测试补齐、样式):自动化检查加抽样,人基本不看。中风险(业务逻辑、接口行为):AI 先审一遍、人看关键点、CI 兜底。高风险(支付、权限、认证、数据迁移):设计评审、强测试、灰度、回滚预案、责任人签字,一样不能少。

review 的核心问题也跟着变了。以前问「这行写得好不好」,现在问:是否符合 spec?是否破坏领域不变量?有没有测试证明?可观测吗?可回滚吗?要不要更新 ADR?一句话,从写代码走向治理代码

我承认这套东西听起来官僚。但官僚化的反面不是自由,是我们那两天的考古——以及未来千百次这样的考古,只不过考古者换成 AI 之后,它不会考古,它会直接删。

反向提炼:用 AI 还旧债的具体做法

「从代码反向提炼规格」不是一句口号,我们在老系统上真跑了一轮,把操作流程写下来。

对象是那个八年老系统里最核心的订单模块,约四万行。流程分三步:

第一步,让模型通读代码,产出行为规格草稿。注意提示词的措辞很关键——不是「总结这个模块」(会得到一篇正确的废话),而是「以验收测试的口吻,列出这个模块对外承诺的所有行为,特别标注:与直觉不符的行为、针对特定条件的特殊处理、看起来多余的步骤」。最后那半句是灵魂,专门钓那些藏着历史原因的角落。订单模块钓出来 40 多条「可疑行为」。

第二步,人工分诊。每条可疑行为三种归宿:有人知道原因的,当场写成一句「为什么」,进规格;没人知道原因但有据可查的(工单、老邮件、监控历史),指派考古,限时两小时——查不出就归第三类;彻底失传的,标注「行为存在,理由未知,改动前需灰度验证」。第三类不丢人,诚实标注的未知比编造的解释值钱。40 多条里大概一半当场有答案,四分之一考古出来,剩下的老实挂着「未知」。

第三步,规格草稿变测试。能自动验证的行为直接让模型生成对应的特征测试(characterization test)钉住现状。这步的意义是让规格「活」着——文档会烂,测试挂在 CI 上不会。

三步走完,订单模块第一次有了一份 60 多条的行为规格加一批护栏测试。总投入大约三人日——八年的债,三天还掉主要的本金,这买卖放在 AI 之前想都不敢想(纯人工做过类似的事,一个模块一个月起步)。

有个副产品值得一提:分诊会开完,两个老同事感慨说好几条「特殊处理」他们自己都忘了是自己写的。规格提炼顺便完成了一次组织记忆的抢救——那些只活在少数人脑子里的系统知识,趁人还在,落成了文字。

一个自检问题

结尾留一个我拿来问自己团队的问题,也送给你的团队:

如果明天代码全部消失,只留下文档、规格、测试和决策记录,你们多久能重建系统?

如果答案是「重建不了,好多逻辑只活在代码里」——那些逻辑就是你最大的隐性负债。好消息是上一节已经演示过的:还债的工具也到位了,三人日能还掉一个核心模块的主要本金。用 AI 还 AI 时代之前欠的债,算是这个时代给老系统维护者的一点温柔。