代码变便宜之后,设计押注如何改写
当智能体让写代码变得廉价,每一个设计决策背后的成本收益账都随之改变。本章说明到底什么变便宜了、什么依然昂贵,以及为什么一些曾被视为过度设计的做法如今可能转为正回报。
本文为中文版,根据英文原版改写,而非逐句直译。
今天最前沿的编程智能体,和几年前的代码补全几乎已经不是一类东西。交给它一个真实任务,它基本可以独立完成:探索陌生的代码库,跨多个文件修改,运行测试,阅读失败信息,然后反复迭代几个小时,直到测试通过。这些智能体已经强得惊人,而且还在快速进步。
结果是,工作发生的位置真的变了。默认的心智模型不再是”打开编辑器写代码”,而更接近”表达意图、委派出去、审查交回来的结果”。写代码这件曾经定义了这份职业的事,正越来越多地被交给别人。
由此很容易得出一个结论,也确实有很多人这样想:既然代码已经这么便宜,过去我们精打细算才肯投入的那些做法——架构设计、详尽的测试、严格的类型——现在还重要吗?既然随时可以重新生成,又何必花力气让代码易于修改?
但人们对这些智能体最普遍的抱怨,指向的恰恰是另一个方向。在各类行业调查中,最典型的挫败感并不是智能体写不出代码,而是代码差不多对:流畅、可信,却在细微处出错。它能编译,能通过主路径,读起来像一位合格工程师写的,却仍然会在某个智能体从未考虑过的边界上做错事。生成代码变便宜了,判断代码是否正确却没有——这正是大多数团队仍然逐一人工审查智能体提出的每一处改动的原因。
本章要讨论的就是这道落差。上一章把过度设计和设计不足看作上下文定价错误的两种形式:要么过早为过多的结构买单,要么把太多东西留着不命名、推给以后。能力强大的智能体并没有推翻这个框架,它改变的是输入这个框架的价格——而且改变得并不均匀:一些长期被嫌弃的做法突然变得值得,另一些则依旧和过去一样浪费。要分清哪个是哪个,我们必须准确说明到底什么变便宜了。
为什么不只保留规格说明?
把上面那个问题按字面推到底。它最激进的版本不只是说可以不写测试,而是说干脆不再维护代码:只保留一份规格说明(spec),需要时重新生成代码,把代码当作一次性的构建产物——就像我们早已习惯丢弃编译器生成的二进制文件一样。如果这成立,就不再有需要为之设计的长期构件,本章也就没有讨论对象了。
它并不成立,而编译器这个类比恰好就是它失效的地方。我们之所以能放心丢掉二进制文件,是因为编译具有确定性、保持行为、且是局部的:同样的源码总是产生同样的二进制;翻译过程不会改变源码已经确定的东西;改一行只会影响一处。从规格说明生成代码却不具备其中任何一条——把模型称作”编译器”,就是偷偷塞进了它根本无法提供的保证。原因可以追溯到一个顽固的事实:规格说明永远无法完全确定代码。凡是它没说的,模型都会补上——补得合理,但并不唯一——而重新生成是没有记忆的。每跑一次,规格说明没有固定下来的每个决策都会被重新掷一次骰子。上次你通过真实使用验证过的行为——来自生产环境的边界情况,后来证明很关键的默认值——都会被扔回帽子里重新抽样。修补规格说明来纠正出错的部分,再重新生成,上次正确的部分又可能悄悄漂移。想买到稳定性,只能写下更多内容,直到规格说明几乎确定了一切。
可是一份确定了一切的规格说明,本身就是代码——只不过用了一种更含糊的记法,由某个既不确定也不局部的东西来”编译”。
所以代码不会消失。我们已经钉死的每一个决策,那份精确而不断累积的记录,总得存放在某处,而最诚实的存放地仍然是代码。代码会长期存在,因此核心动作是修改这个长期存在的构件,而不是从零重新生成它。这正是 CMP 的基石:设计是为修改而存在的,而只有构件留存下来,才有东西可以修改。于是真正的问题不再是要不要保留代码,而是——既然打字的已经是智能体——到底什么真正变便宜了。
什么变便宜了
人们很容易说智能体让”写代码”变便宜了,但这条界线并不准确。很多与代码相关的事情依旧缓慢而痛苦,而另一些和敲字符毫无关系的事情却几乎变成了免费。真正的分界线在别处。
成本趋近于零的,是那些”正确”有可检查参照的任务——智能体可以拿自己的产出与之对照、自行评判,而不必等人来告诉它。
这个参照比测试套件更宽泛。有时它是智能体可以随时运行的机械裁判——编译器、类型检查器、一个失败的测试、一个 linter——这时它得到的是最强的形式:一个闭合的反馈回路。它动手、读取裁决、修正、再重复,中间不需要任何人。但同样常见的是,参照是一个可以对齐的具体目标:代码库中可以照着做的现有模式,一份要移植的参考实现,一个完整的示例,一份精确到足以确定答案的规格说明。只要存在一个已知正确的目标,智能体就会变成一台不知疲倦的”生成—检查”引擎,比人更快、也更有耐心地向目标收敛。这正是它在脚手架、代码约定和翻译类工作上显得那么强,而目标一旦模糊就立刻变得不那么可靠的原因。
所以,对任何一项成本,有用的问题都不是”这件事涉及代码吗?“,而是:
这项任务有没有一个判定器(oracle)——一个测试、一份参照、一个目标——能在不需要人来裁定的情况下,告诉你工作是否已经正确?
拿同一个问题去审视那些真正走红的演示——不是那些平平无奇的——形状完全一致:
- 智能体一次性写出一个可玩的游戏,先是贪吃蛇,再是横版射击游戏。判定器:它能跑起来;而且无数现成的实现早已讲清了这类游戏应该是什么样子,提供了源源不断可供模仿和对照的参照。
- 用 Rust 重写 Bun 这样规模的系统。判定器:原来的运行时本身,逐个行为做对比,再把它现有的测试套件整体搬过来。目标极其庞大,却被完全确定。
- 解魔方,或任何定义良好的谜题。判定器:还原后的状态,可以机械地判定。
这些看起来像是纯粹的智力展示,但它们的共同点并不在此。一个周末重写 Bun 和修一行编译错误,在规模和难度上毫无共同之处——唯一的共同点是:在每个例子里,“正确”都被确定在智能体能够触及、并能自行反复检查的地方。这里的惊艳是可验证性的症状,而不是它的反例。这些任务变便宜,都不是因为”写”变便宜了,也不是因为智能体单纯”懂得更多”。它们变便宜,是因为每一个都带着一个目标,智能体可以随意多次地拿自己去对照。共同的线索是可验证性——检查越严密、越自动化,任务就越便宜。这正是 AI 让其变得充裕的东西:一旦”正确”被确定得足够清楚、可以检查,走完通往它的那段距离就几乎免费——智能体会自己走到。
什么依然昂贵
同一条规律从另一面读,就能准确告诉你什么没有变便宜。
没有自动判定器的问题并没有变便宜。而且由于智能体现在产出的改动更多、更快,这类工作的绝对总量是在上升,而不是下降。
如果没有任何东西能机械地告诉智能体它错了,回路就永远不会自行闭合。智能体仍然会产出一些东西——流畅、可信,带着和正确版本一样的自信交付——但”看起来完成了”和”真的正确”已经分道扬镳,而只有人能把二者分开。昂贵的工作如今就集中在这里:
- 当没有测试覆盖某次改动触及的行为时,这次改动是否真的正确。
- 设计层面的错误:边界画错了位置,概念与领域不匹配,修改闭包只改了一半。
- 误解意图——智能体自信地解决了一个与真正重要的问题略有不同的问题。
- 只有在人工审查中才会浮现的语义判断和取舍。
即便完全离开代码,你也能感受到同一堵墙——本章本身就是这样写出来的。文字没有判定器:一段话终于写对时,不会有测试变绿;也没有什么能拿一个句子去和已知正确的答案做比对。所以初稿来得又快又流畅,但要把每一节写对,却需要一轮又一轮、一段又一段地打磨:提出一个看似合理的版本,阅读,判断它还差一点,再重写,有时要经过好几遍才有一版站得住。产出文字从来不是瓶颈;瓶颈在于判断哪个版本才真正正确,而这只能由人来定夺。这和智能体在代码中遇到微妙的正确性问题时撞上的是同一堵墙,只是从另一侧碰到。
昂贵的残余部分是:**在没有任何自动机制能告诉你错了的情况下,收敛到正确。**真正限制你能在多大程度上信任智能体产出的,是这一点,而不是打字速度——这也正是从设计角度看到的那个差不多对的问题。
针对昂贵部分的做法
退一步看,前面两节其实可以归结为一个古老的区分:做什么与怎么做。怎么做变便宜了,而且随着更强的模型和更完善的智能体框架(harness)出现,还在持续变便宜。每一次进步都让怎么做越来越不成问题。做什么这部分依然昂贵,模型和智能体框架都碰不到它:定义要构建什么、什么才算正确,这部分始终留在人手里。除了我们自己,它没有别的判定器。
这就指出了设计杠杆所在。判断什么算正确,是人的行为;杠杆在于,在每一个这样的决策做出的那一刻就把它捕获下来,写成智能体可以运行的形式——一个测试、一个类型、一份契约。判断只发生一次;此后,检查代码是否仍然遵守它,在智能体的每一次改动上都是廉价且自动的。所以一个代码库能做的最高杠杆的事情,就是把这些已经定下来的决策存成可执行的检查——把原本每次改动都要靠人手工复核的正确性,变成智能体可以自行验证的东西。
这并不是一类新技术。它精确地描述了我们早已拥有、并且争论了多年的那些做法:
- 测试与 TDD 把原本只能靠人仔细阅读才能检查的行为正确性,变成可执行的。被覆盖的行为从此有了判定器,于是它上面的回归就落入了廉价区域:智能体看到红色的测试,自己修好。
- 强类型接管了修改闭包中的一大块——也就是广度所关心的”这里改了,还有哪些地方必须跟着改”——让遗漏在机械层面可见。改了类型,编译器就会列出你忘掉的位置。一整类”忘了改的那个地方”从此不再无声无息。
- 架构是同一个动作在最大粒度上的体现。系统如何拆分成部分、每个部分负责什么、哪些部分可以依赖哪些部分,这是人对做什么的决定——结构层面的设计意图,本身不带判定器。一旦把它写成可强制执行的边界——依赖方向、模块可见性、每个接缝处的契约——这些意图就变得可以机械检查:一次越过不该越过的边界、或者把依赖指错方向的改动,会直接触发检查,而不是等着在审查中被发现。
- 工具箱里的其他东西——断言与契约、基于属性的测试、穷尽匹配、模式校验——是同一个动作的不同名字:把一个原本只存在于某人脑子里的假设变成可执行的,让违反它的行为产生信号,而不是意外。
用 CMP 的话说,这些做法做的是同一件事:把原本只能通过昂贵的人工审查来检查的上下文,转换成廉价的自动回路就能检查的上下文。它们把正确性从边界昂贵的那一侧拖到了廉价的那一侧。
这些都不是新东西——还是我们一直以来拥有的那些做法。智能体改变的是它们的价格。看清这一点最直接的方式,是把上一章里那笔旧账和新账并排摆出来,逐项对比。
在只有人参与的时代,这些做法的成本主要是人力:编写和维护测试,学习并满足类型系统,和编译器搏斗。收益——日后更少的无声错误——是真实的,但它是延后的、分散的。对很多团队来说,眼前看得见的成本胜过了分散的未来收益,诚实的结论就是 YAGNI。不做这些,往往才是正确的上下文押注。
这笔押注中的两项现在都变了:
- **成本一侧下降了。**编写测试、添加类型注解、满足检查器——这些都完全处在廉价区域,因为每一项都自带判定器。过去让这些做法显得”太麻烦”的大部分劳动,现在由智能体承担了。
- **收益一侧上升了。**这些做法的回报一直是”出错时有一个自动信号”。这个信号过去对能自己发现问题的人来说只是锦上添花,现在却成了稀缺资源——它决定了智能体那源源不断的廉价改动是否可信。收益不再是分散而延后的;它就是一次可以直接合并的改动与一次必须停下来人工审计的改动之间的差别。
当一种做法的成本下降,而它的收益又成为稀缺资源时,它的 ROI 不是小幅上升,而是直接翻转。对一个只有人的团队来说属于过度设计的东西,在主要修改者是智能体时,可能恰恰是正确的选择。品味没有变;变的是修改者,价格随之而变。这里还附带一个提醒:既然检查已经可以廉价地大量制造,稀缺的美德就不再是编写检查,而是删掉那些不带信号的检查——一个时好时坏或同义反复的检查比没有还糟,因为智能体会尽职尽责地对着一个谎言闭合回路。
预测
一个只能解释的框架只是一个故事。一个能够预测的框架,才是一笔可能输掉的押注。本章分离出的机制足够锐利,可以下这样的注:AI 让一切带有判定器的正确性成本崩塌,而其他一切的价格都原封未动。把这一条规律向前推,它就会对行业走向做出具体判断——下面每一条预测都由它推出,而且这组预测有意设计成可检验的,未来几年里有明确的方式可以证明它是错的。
**类型化语言重回中心。**TypeScript 取代 JavaScript,Rust 进入系统编程,Python 和 Ruby 快速补上渐进类型——采用率增长最快的地方,恰恰是智能体写代码最多的地方。因为类型既是最便宜就能造出来的判定器,又是最精确的判定器之一:它把一个决策确定得毫无歧义,不给模型留下重新掷骰子的余地,并让修改闭包在机械层面可见。动态语言生态的回应是加装静态层,而且方向是单向的。证伪条件很清楚:如果十年之后,主流的智能体技术栈是无类型的,并且照样运转良好,那么本章就错了。
**架构的回报转向分离做什么与怎么做。**在这个粒度上,设计最有价值的工作,是在带判定器的决策和永远不会有判定器的决策之间画一条干净的线——把做什么(只有人的判断才能决定什么算对)和怎么做(智能体可以自行检查并收敛)隔离开。沿着这条接缝切分的系统,会交给智能体一大片可以廉价工作、自行验证的领地,同时把昂贵的、没有判定器的判断集中到一个小而清晰、人真正能掌握的面上。这些边界是否也被机械强制,不如它们画在哪里重要:把做什么与怎么做的划分显式化的架构,会看到回报的跃升;而把两者混在每个模块里的架构,则会让人不得不同时到处审计。
**规格驱动设计会进入平台期,因为代码才是最精确的规格说明。**规格优先的正统做法把全部分量压在文档上,视其为最重要的构件,这是一笔押错的注。散文写成的规格说明只能松散地确定做什么;凡是它没说的,模型每次运行都会重新决定。我们能拥有的最精确、最完整的意图表达,就是代码本身。所以预测是:只在规格说明上投入的注意力,会恰好停滞在规格说明不再能确定答案的地方;能够持久的模式,是把意图以可执行的形式推进代码,把代码当作做什么的权威表达,而把规格说明当作它的一个有损视图。
**一个制造判定器的产业将会形成。**契约、基于属性的测试、运行时断言、模式校验、可执行的架构检查——这些工具的全部职责,就是把人的判断转换成自动信号。它们会成为一个独立的品类,有自己的预算科目和自己的投资逻辑。它卖的正是 AI 让其变得稀缺的那一种资源:不必逐行阅读就能信任的产出。
**代码库成为护城河。**给两个团队同样的模型、同样的智能体、同样的人数,它们会在唯一要紧的一点上分化:智能体的产出有多少可以不经人工审计就直接合并。区分它们的是代码库的可验证性,而更好的模型并不能抹平这个差距。可以预期这一属性会被度量:出现在 CI 仪表盘上的可验证性指标里,出现在技术尽职调查里,出现在对一次收购中的代码究竟值多少钱的诚实估值里。
**工程师的工作集中到没有判定器的那一半。**随着怎么做落到智能体手里,仍然属于人的工作是:决定构建什么,定义什么算正确,以及识别那些差不多对的东西。招聘、职级和资深程度会向规格定义、边界设计和审查倾斜。实现作为一个独立角色——照着别人的规格说明敲代码——会逐渐消失,因为那一半工作现在属于智能体。
在这一切之上,还有最后一笔押注。一旦检查可以廉价地批量生产,稀缺的技能就会从编写检查翻转为删除那些撒谎的检查。时好时坏和同义反复的检查会成为主要的失败模式,而管理检查套件的信噪比会成为一门有名字的学科。
所有这些预测都归结为同一个论断,只不过现在是以预测而非观察的形式表述:代码库的价值,正在向”它的正确性有多少能由智能体自行验证”迁移。可靠的编程智能体需要更好的代码库——未来几年会让这一点具体落地,一个团队接一个团队地,由谁能信任自己智能体交付的东西来见分晓。