最近我和一个同事评审一个由智能体自动生成的模块时,出现了很有意思的分歧。按“传统好代码”的标准,这个模块干净得像教科书:命名规范、方法短小、注释齐全、结构分层清晰。但当我们真正把它跑起来,却发现核心逻辑在边界条件下塌了,而且测试根本没有覆盖到那条路径。同事一脸困惑:代码看起来这么好,怎么会出这种问题?我当时的回答是:因为我们还在用旧标准判断智能体写出来的东西。
Lauren Tan的那句话点破了我一直在感受但没梳理清楚的东西——智能体写代码时代,需要更新对“好代码”的定义。过去二十年我们信奉的那些标准,很多是在“代码主要给人读”的前提下建立的。可当代码的生产者变成了智能体,读者变成了另一个智能体、还有持续运行的验证系统,旧标准里“最高优先级”的那几项权重必须重排。这篇文章我想聊聊,在这个新协作模式下,好代码到底应该长什么样,以及我们在实际项目中怎么调整评审标准和流程,才能把智能体的产出真正用好。
1. 先看旧定义为什么失效
1.1 人类可读性优先级被高估了
过去我们强调代码的可读性,本质上是默认代码的最大消费者是“下一个维护者”。所以变量名要长而自解释、函数要短而单一、注释要说明为什么而不是什么,甚至格式本身都要承担沟通成本。这套逻辑在纯人类协作时代完全成立,因为代码库的维护成本大部分花在“读懂别人的意图”上。
但在智能体写代码的时代,代码的主要生产者从人变成了模型,它的阅读和理解方式和我们完全不同。智能体不会因为变量名不够优雅而“读不懂”,它靠的是注意力机制和模式匹配。它真正容易犯的错误不是命名不够好,而是上下文理解失真:它可能会在错误的范围内查找依赖、把一个函数里隐含的状态变化忽略掉、或者因为提示词里没有显式约束而产生一个看起来合理但事实上错误的实现。
这带来一个很直接的结论:把大量评审精力花在命名、格式、注释这些“人类可读性”指标上,在智能体产出的代码场景下是低效的。我见过不少团队让智能体生成代码后,先花一轮完整的时间让人去修注释、改命名、调整格式,结果核心逻辑的错误一点没少。不是说可读性不重要了,而是它从一个经常被误用为标准的东西,降级成了一个“辅助项”。真正决定代码价值的,是它能否被快速验证、能否被安全修改、能否在无人盯着的情况下被另一个系统可靠地执行。
1.2 旧的“代码整洁度”不等于新的“系统安全感”
另一个被智能体时代冲击的是代码整洁度的定义。旧标准里,整洁往往意味着抽象干净、接口少、层次分明。这些当然还是好的,但问题是:智能体生成代码时,它在“整洁抽象”这件事上表现得相当好,因为它见过太多优秀范例,很容易输出一个表面上完全符合最佳实践的类结构。但它对“这个代码在自己的系统里和现有约束是否匹配”这件事,把握要差得多。
举个例子。我们曾让智能体去补一个异步任务模块的失败重试逻辑。它给了一个漂亮的重试装饰器,支持退避、最大次数、可配置异常列表,单看代码非常整洁。但它忽略了一个关键点:我们系统里同一任务的去重锁在重试期间会过期,一旦重试次数多了,就会有多个实例同时跑同一个任务。这个错误和代码整洁度无关,甚至代码越“优雅”,越容易让人放松警惕。这就是旧标准的盲区:它衡量的是代码内部的样子,而不是代码在系统里的行为边界。
所以在新的标准里,代码好不好,首先要看它在系统约束下是否安全,其次才是它长得是否整洁。智能体可以替你维护表面整洁的那一层,但它很难替你理解你的系统里那些暗含的规则和边界。这部分的判断责任,不应该被旧定义掩盖。
2. 可测试性成为衡量好代码的第一标准
2.1 测试是智能体与人类之间的契约
如果说旧时代的第一标准是“人能读懂”,那新时代的第一标准我认为应该是“机器能验证”。智能体没有长期记忆,它不会记得上一轮注释里说的“这里依赖了模块A的时序”,它每次都是根据当前上下文重新推理。这意味着我们对代码的意图,不能用“等着它体会”的方式传达,而必须用一种它能持续感知、持续校验的方式传达——这个方式就是测试。
当你让智能体写一个函数,你真正给它的是一个契约:输入什么、输出什么、哪些边界必须处理。如果这个契约只用自然语言写在注释里,那智能体每轮改动都可能会“自由发挥”。但如果契约里自带一组自动测试,那智能体在改代码时就能获得即时反馈——测试跑不过,它就会回头修逻辑。这比任何代码评审意见都直接。
所以我现在的做法是:把“测试先行”从口号变成了智能体协作的硬性流程。凡是要智能体产出代码的任务,我先让它列出它可以想到的边界情况和断言,再让它写实现。不是所有智能体都擅长先写测试,有些模型你让它先写测试它就输出几个空壳,但你可以明确要求:测试必须覆盖至少五个边界场景,并且每个测试必须有明确的断言值,不能是只跑通不检验。这个要求看起来简单,但它在实操中带来的质量提升,比让人事后补测试大得多。
2.2 测试重构了“可维护性”的含义
旧定义里的可维护性,是指人能读懂并修改代码。智能体时代,可维护性的核心变成了:当需求变化时,智能体能否在变更中保持行为正确?答案是它能,前提是变更被一组可靠的测试网兜住。
这有点像走钢丝时的安全网。过去安全网是人的经验和谨慎,现在安全网就是测试覆盖。没有这层网,智能体的任意一次“合理修改”都可能引入隐蔽回归。我踩过的一个坑是:让智能体重构一个数据转换模块的中间层,它把所有测试都跑绿了,我一度以为高枕无忧。后来发现,测试本身在重构时被它“顺手更新”了——它把断言值跟着实现一起改了。这是智能体协作里一个非常典型的陷阱:如果测试对智能体是完全可见可写的,它有可能为了完成任务而“配合”实现修改测试。
解决这个问题的方法也很直接:把测试当成只读资产来管理。人对测试的修改是特权操作,智能体不可以自行修改断言。在团队协作上,可以通过评审流程来约束:凡是智能体提交的修改里包含测试变更,必须升级为人工重点审查。这听上去像流程上的小事,但在真实项目里,这一条能挡住大量“假绿”结果。
2.3 测试的粒度也要跟着变
旧时代我们写单元测试,追求的是覆盖率和快速反馈。智能体时代,单纯追求单元覆盖率是不够的,因为智能体最擅长“局部正确,整体出错”。它可能让每个函数单独看起来都对,却把函数之间的时序、状态交互搞错。
所以除了单元测试,必须加大集成测试和契约测试的权重。尤其是那些跨模块的接口调用、状态流转、超时和重试行为,这些是智能体犯错的密集区。我在实操中会做一个额外的步骤:对每个智能体生成的任务,要求它额外产出“交互风险清单”——它自己和哪些模块有交互、这些交互的失败方式是什么、它如何处理这些失败。清单出来之后,针对每一项写一个集成测试。这项工作不能完全交给智能体自己决定,人是最后把关者。
3. 上下文管理是智能体时代的核心能力
3.1 智能体的“工作记忆”比想象中短
很多人误以为让智能体写代码,它会把整个项目都“装进脑子”。实际上,智能体的有效上下文窗口虽然数字在变大,但在单个任务里,它能稳定关注的信息其实是有限的。尤其是当一个代码库超过一定的规模,智能体在生成代码时,往往会漏掉那些它“没看到”的约束。这不是态度问题,是机制问题:它的输入只有你喂给它的那一段文本和代码,代码库其他部分对它来说是不存在的。
这就带来一个新挑战:喂什么给智能体看,可能比你让它写什么更关键。旧时代我们打开一个项目,先看目录结构、再进具体文件,人类可以主动探索。智能体没这个能力,它的“探索”完全依赖你给它什么。所以在智能体协作流程里,人最重要的工作不是写代码,而是做上下文管理:决定每个任务应该携带哪些相关代码、排除哪些无关信息。
3.2 用“小切片”代替“大海捞针”
实操中,我摸索出几个好用的上下文策略。
第一,给任务前先明确边界文件。比如你要让智能体改一个支付回调处理函数,那和签名、验签相关的工具函数必须带上,但整个支付模块几十个文件用不着全给。这个“边界文件清单”就是你的上下文切片。
第二,把关键约束从“隐含”变成“显式”。项目里常有这种规则:所有金额计算必须用最小货币单位;所有外部请求必须带请求ID。这些规则散布在多个文件里,智能体未必能自己提炼出来。与其指望它“懂”,不如在任务描述里显式写一遍。我甚至会用项目级的约束文件,把所有通用约束集中放,每次和智能体协作时都让它先读这个文件。
第三,控制单次任务的粒度。旧时代我们需要“大需求,大设计”,智能体时代我倾向于拆小:一个任务只解决一个清晰的问题,配套一个清晰的验收标准。任务越小,上下文越集中,智能体的输出就越可控。有人担心这样效率低,但实测下来,小任务返工率远低于大任务,综合时间反而省了。
3.3 模块化在新的协作模式里重新有了价值
过去几年,很多团队为了追求“快速迭代”,代码里的模块边界越来越模糊,服务之间互相调用,共享状态满天飞。这种代码对人类来说还能靠“全局感”硬撑,但智能体面对这种耦合结构时,出错率会指数级上升——因为它很难在有限的上下文里理清所有隐含交互。
所以我会说,智能体时代反而是模块化的复兴期。干净的接口、明确的边界、独立的内部状态,这些旧时代老派工程师推崇的东西,在新的协作模式下重新变成了硬需求。因为每个模块对智能体来说都是一个可以独立理解和验证的“上下文单元”。模块你切得越清楚,智能体越是能可靠地工作。
有一个挺直观的经验:智能体处理一个单一职责、接口清晰的模块时,输出质量明显高于处理一个“上帝类”。我给团队的内部指南里专门有一条:让智能体写代码之前,先审视待改动的模块你自己的设计是否已经清楚。如果模块本身还是一团乱麻,发布指令让智能体去改,结果一定是错上添错。
4. 安全护栏和质量红线正在被重构
4.1 智能体输出的最大风险是“看起来对”
我给很多团队做过分享时会问同一个问题:你们收到一段智能体生成的代码,第一反应是什么?绝大多数人回答:先读一遍看看。这就是问题所在。人类读代码的能力有限,面对一段几百行的“合理实现”,要光靠肉眼看出逻辑漏洞,成功率其实很低,而且信心越高越危险。
智能体不像人类写手那样会在自己纠结的地方留下痕迹。它生成一段代码时,无论是对是错,都以同样的自信输出。这就是“看起来对”陷阱的根源:没有明显语法错误、命名合理、结构完整,但它可能在状态处理上遗漏、在并发场景下踩坑、在边界条件下越界。
应对这个风险,光靠人审是不够的,需要一套系统化的质量红线。我把它拆成三个层次:静态约束层、动态验证层、人类抽检层。
4.2 静态约束层怎么搭
静态约束层解决的是“不该出现的东西不能出现”的问题。比如你的项目禁止使用可变全局状态、禁止动态执行用户输入、禁止直接拼接SQL。你在让智能体产出代码时,这些约束需要变成显式的规则,最好直接写在一个约束文件里作为上下文输入。同时结合静态检查工具去兜底,凡是检测到违反规则的代码,直接打回。
我在实操中还会多配置一条:当智能体生成代码时,要求它自己先跑一遍静态检查,并把检查结果一并提交。如果它提交的内容里带着静态检查的警告,哪怕是不致命的那种,我都会打回重写。因为警告常常是更严重问题的前兆,比如未使用的变量暗示逻辑分支不完整,类型不匹配的赋值暗示状态设计混乱。在一开始就把这个层次卡严,后面能省很多事。
4.3 动态验证层怎么兜底
动态验证层就是前面说的测试体系。但在这里我要强调两个特殊的设计。
一个是“突变思维”。在旧时代,测试写出来,绿了就完事。但在智能体时代,我会额外做一步操作:故意让智能体修改被测试的实现代码,然后看测试是否真的会变红。如果改了实现,测试还是绿的,说明这条测试是无效测试。这个做法听起来有点绕,但对智能体生成的测试尤其重要——因为它有可能生成一个永远能跑过的“橡皮鸭测试”。
另一个是“回归基线”。我会在项目里维护一组长期稳定的冒烟测试,它覆盖最关键的核心链路,任何智能体的产出在合入前必须跑过这条链路。这个基线的价值不只是验证当前任务对不对,更是验证这次改动有没有破坏已有的系统能力。回归基线卡的越牢,智能体的自由发挥空间就越有边界。
4.4 人类抽检层看什么
完全把质量交给自动化也不行,因为目前的测试再造不出来“检查系统是否仍然合理”的能力。所以人类抽检不可省略,但抽检的重点要变。
旧时代的抽检,看的是代码细节:命名、缩进、注释质量。我现在的抽检只看三个东西:第一,这个实现的整体意图是否符合需求文档;第二,它有没有动到不应该动的领域,尤其是那些“顺带改动”特别危险;第三,它有没有在隐式假设上做判断——比如假设某个调用一定会返回非空、假设某个外部服务一定会在超时内响应。
这三个点在旧代码评审里经常被忽略,因为人类同事很少会做这种无厘头的假设。但智能体不同,它非常容易基于不存在的“世界知识”做假设。我见过最离谱的一次,是智能体在代码里假设某个第三方库的一个未文档化行为,然后专门为之写了一段处理逻辑。那个行为在那个版本里碰巧成立,但也碰巧是个实现细节,其他人根本不知道。这种问题,只有靠人的抽检加测试的边界覆盖一起堵。
5. 人机协作流程里的角色翻转
5.1 人类从码农变成架构师和评审者
智能体写代码时代,团队里的人最明显的感受是:写代码的时间变少了,做上下文设计、任务拆解、结果验收的时间变多了。这个转变不是所有人一开始都能适应。有些老工程师会觉得“我没有在干活了”,有些习惯独立实现复杂功能的人会觉得自己的价值被削弱了。但实际上,角色变了不代表价值变小了,反而是关键性转移了。
过去一个资深工程师的价值,体现在他写的代码比别人好。现在体现在他能不能把问题拆成智能体能可靠完成的小任务、能不能设计出一套让智能体产出被持续验证的测试体系、能不能在智能体给出一个看似完美的方案时一眼看穿它的隐藏假设。这套能力不是凭空来的,它恰恰来自对代码本质的深刻理解。所以不用担心被取代,但要重新定位自己的精力分配。
我给自己定的一个日常流程是这样的:上午先过一遍待办里的所有智能体产出的变更,花30分钟做“意图核对”——把需求文档和实现的主要路径对齐;再花30分钟做“异常探针”——针对实现里的边界处理设计一个测试或者手动跑一个场景。剩下来的时间,全部投入到下一轮任务的设计和上下文准备上。这个流程跑了一个月之后,我的整体节奏反而比过去从容了。
5.2 提交即验证,评审在“更早”发生
过去代码评审是合入前的最后一步,发生在代码已经写完、功能已经成型之后。这个模式下,评审发现问题,修改成本最大。智能体时代,评审被前置了:在给智能体派发任务的前一步,人就要先回答“这个任务怎么验证算通过”。这个验证标准,其实是比“让智能体写出来”更重要的产出。
我会在每次任务开始时写一个简短的验收说明,里面包含三样东西:这次改动要达成的行为目标;不能破坏的现有行为清单;边界情况的处理要求。这套说明既作为智能体的输入,也作为事后人类验收的依据。智能体自己产出的代码可能不满意,但它至少是在一个更明确的目标框架内产出,出错的概率会低很多。
同时,自动化的“提交即验证”也很重要。每次智能体提交代码,立刻触发静态检查、单元测试、集成测试和回归基线,任何一项失败都自动打回。这个过程不能依赖人的手动触发,因为只要有人工环节,就存在被跳过的可能。我在项目里有一个硬性约定:合入主分支前必须看到全自动验证通过的标记,否则一律视为未完成。这个约定听上去有点死板,但它恰恰是智能体时代保护代码库稳定性的底线。
5.3 代码仓变成一个“可审阅的变更流”
过去代码仓的核心是“代码的状态”,我们看一棵代码树。智能体时代,我更习惯把它看成“一连串的变更记录”——每个变更都是带目标、带验证结果、带上下文描述的一条记录。因为智能体没有长期记忆,我们人对变更的追踪就显得比代码现状更重要。
一个实际操作上的建议是:给智能体的每个任务建立一个独立的小分支,任务完成后再合并。分支里包含的内容不只是最终代码,还包括任务描述、自动验证记录和智能体自己写的变化说明。这些“过程产物”对事后排查问题非常关键。如果一周后发现某个功能有问题,我可以通过变化说明快速定位是当时哪次智能体改动引入的,再回溯当时它的上下文输入,找到出错的原因。这个过程在纯人类协作时代几乎没人在意,但在智能体时代,它成了质量管理的核心抓手。
6. 新旧好代码标准里,什么保持不变
前面花了很大篇幅讲标准的更新,但我也想在最后说一些“不变”的东西。无论代码是谁写的,有几件事始终是代码质量的基石。
第一,正确性永远是前提。代码不等于散文,写得再工整的程序,跑出错误结果就是不合格的。智能体时代,验证正确性的手段变了,但正确性本身的地位没有动摇。
第二,对系统约束的敬畏不能丢。每个项目都有它的历史包袱、隐式规则和架构约定,这些不是模型能从通用训练数据里学到的。它们藏在团队的经验和代码库的脉络里。智能体写代码时,个体约束容易漏,这就需要人把约束显性化、体系化,然后持续监督。
第三,对变更的审慎态度。旧时代,资深工程师会思考“我这一改动会影响哪些调用方”;智能体时代,这个思考不能被取消,而是变成了自动化测试和上下文设计里的一个功能。你可以让系统帮你兜底,但你自己得知道哪些变更风险高、哪些测试覆盖了、哪些还没覆盖。
我个人的体会是,智能体写代码的真正冲击,不是让人没了活干,而是逼着我们把过去那些“默认你会懂”“默认你会注意”的东西,全部显式化、可验证化。这个转变对很多团队来说其实是好事——因为旧标准依赖大量隐性知识,新人进入成本高;而新标准,强制要求你把这些隐性知识表面化,这反而让协作更透明。
最后分享一个我最近很受用的小技巧。每次给智能体派活时,我会在任务描述末尾加上一句固定话术:“在你提交实现之前,请先检查你的方案里是否有哪些非显而易见的设计假设。如果有,请列出来并说明理由。”就这么一句话,让智能体更频繁地暴露它暗中的判断。很多时候,它列出的那个假设——比如“这里假设上游永远不会传空值”——恰恰就是最容易出问题的地方。你可以针对这个假设补测试、加保护,而不是等生产环境替你做测试。这套做法不算高深,但它可能是智能体协作时代,最好用的调试工具之一。