题图

本文永久链接https://tonybai.com/2026/07/12/does-code-cleanliness-affect-coding-agents

大家好,我是Tony Bai。

过去两年,关于“要不要写干净代码”的争论,在AI编程助手崛起之后似乎有了一个新的、看似合理的反驳:代码是给人看的,AI 又不嫌丑,能跑通不就行了?

这话听起来没毛病。毕竟绝大多数针对Coding Agent的评测——无论是SWE-bench还是它的各种变体——都只盯着一件事:任务做完了没有,Pass rate高不高。至于Agent在这个过程中翻了多少代码、读了多少遍文件、烧了多少token,几乎没人系统地问过,更没人问过一个更根本的问题:同一个任务,扔给同一个Agent,如果代码库本身“整洁度”不同,结果会不一样吗?

SonarSource的两位研究者决定把这个问题搞清楚。他们的研究背景数据本身就值得记一下:据他们引用的一项2026年的调研,在12.8万个GitHub项目中,已经有22%~29%能找到Coding Agent活动的痕迹——而距离第一批真正实用的编程Agent上线,还不到一年时间。AI 写代码,已经不是“会不会发生”的问题,而是“发生得有多快”的问题。而与此同时,在SWE-bench Verified这样的评测集上,一个任务平均要消耗大约400万个token,其中大部分是输入token。

换句话说:Agent 已经在大规模吃你的代码库,而且吃得很贵。这篇论文想搞清楚的是:你喂给它的代码库的“干净度”,会不会决定“这顿饭吃得贵不贵”。

怎么才能做一次“干净”的对照实验

这个问题不好回答,原因很朴素:你几乎找不到两个“除了整洁度之外完全一样”的代码库。同一个项目的两个版本,往往架构、依赖、测试覆盖都对不上,你根本没法把行为差异归因到“整洁度”这一个变量上。

研究者的解法是自己造出这样的对照组——他们称之为“最小对照对”(minimal pair):两份代码库,架构相同、依赖相同、外部行为(测试能否通过)相同,唯一的区别是代码整洁度不同。这个“整洁度”用SonarQube的规则违规数和认知复杂度来量化。

具体是怎么造出来的呢?两条流水线,方向相反:

  • Slopify(做旧管线):拿一份原本干净(经过静态分析把关)的代码库,模拟“如果这个团队从来没做过代码审查和lint检查,它现在会长成什么样”——分三步:先让代码能跑起来、测试能过;再探索每个目录写摘要;最后针对性地引入违规,比如把小函数内联回调用处、把逻辑复制到多个分支里、往文件里塞死代码。每一步改完都要重新跑测试,破坏行为的改动会被拒绝。

  • Vibeclean(翻新管线):反过来,拿一份天然生长出来的、违规一堆的代码库,像正经做代码审查一样把这些违规逐条修掉——去重字符串、删掉注释掉的死代码、替换过时的写法,遇到真正的“上帝方法”(比如200多行的switch分发器),就拆成命名清晰的小helper。

最终他们凑出了6组这样的对照代码库(3个Java为主,3个Python为主),其中一半是公开仓库(比如Apache Commons BCEL、Netflix Genie、CKAN),一半是SonarSource内部的私有代码库——后者是故意留的一手,用来排除“模型可能训练时见过这份公开代码”这种记忆效应的干扰。

在这6组代码库之上,他们又手工设计了33个任务,并且给自己定了三条硬规矩,保证测的是"整洁度"而不是别的东西:

  1. 任务必须落在两个版本差异最大的“热区”;
  2. 任务描述只说外部可观察的输入输出,绝不点名要改哪个文件、哪个函数——探索过程必须留给Agent自己;
  3. 测试只在应用的公共接口(CLI、HTTP路由、库API)上跑,不检查内部实现细节。

每个任务在对照对的两侧各跑10次,用的Agent是Claude Code(Sonnet 4.6),总共660次试验

三个核心发现

发现一:能不能做完,和代码干不干净没关系

先说一个可能让“整洁代码党”略感意外的结论:Agent的任务通过率几乎不受代码整洁度影响。干净代码上通过率91.3%,脏代码上92.1%,差了不到一个百分点。

也就是说,如果你只看“这个功能做出来了吗”这一个指标,代码整不整洁根本无所谓——Agent照样能在“屎山”里摸索出正确答案。这也解释了开头那种“AI又不嫌丑”论调为什么听起来站得住脚:如果你的评价标准只是“完成没完成”,那这个论调是对的。

但问题恰恰出在这里——完成任务的“过程”,两边天差地别。

发现二:干净代码,实打实地更省token

在干净代码上跑任务,Agent的操作footprint全面收缩:

  • 输入token减少7.1% —— 说明Agent往上下文里塞的代码变少了,不用东翻西找;
  • 输出token减少8.5% —— 涵盖了模型输出的一切:工具调用、代码、推理过程;
  • 推理字符减少11.1% —— Agent “想”得更少了。

结合通过率没变这一点,一个直接的推论就是:Agent在干净代码上,是用更少的token做了同样的活。效率提升,不是靠少干活,而是靠少绕路。

发现三:最大的差异,是“反复回头看”

如果说前面的token数字还只是"modest but consistent"(不算大但很稳定),真正炸裂的一个指标是文件重复访问率(file revisitation)——干净代码上,Agent回头重新读自己已经改过的文件的次数,减少了34%,而且这个方向在每一个代码库上都成立,跨度从-7%(CKAN)到-69%(Commons BCEL)。

研究者对这个现象的解读很值得玩味:“重复访问”更像是Agent对自己刚才那次修改“心里没底”的信号,而不是探索广度的体现。换句话说,在乱糟糟的代码里,Agent改完一处,心里犯嘀咕,又跑回去确认一遍;在干净的代码里,它改完就走,不带犹豫。

一个佐证是“读过的文件数”这个指标,在干净代码上反而略微上升(+3.2%),但per-repo的方向并不一致。一种合理的解读是:干净代码上Agent第一遍读得更广,但读完就下决心;脏代码上Agent一开始读的文件更少,但改完总要回去反复核实。

一个反直觉的细节:重构的“位置”很重要

如果这篇论文只讲到这里,那结论会显得过于干净——“写干净代码就是省钱”,一个AI 时代版本的老生常谈而已。但作者们进一步把33个任务拆成了两条赛道,发现了一个真正有意思的细节。

  • 跨模块任务(需要跨越两个以上模块边界的改动,14个任务):这里干净代码的优势最明显——输入token少10.7%,文件重复访问直接砍半(-50.8%)。整个数据集层面观察到的省token效应,大部分都来自这条赛道。
  • 认知热点任务(集中在单个巨型方法/类里的改动,13个任务):这里token footprint基本没变化。变化的是“活儿怎么干的”——Agent在干净代码上打开了更多文件,但每个文件改的行数变少了。

为什么会这样?因为翻新管线处理“上帝方法”的方式,是把逻辑拆分重新分布到更多、更小的文件里,而不是把复杂度真正消灭掉。所以“干净版本”的热点代码,逻辑被拆得更细、分布更广,Agent省下了“理解一个巨大switch语句”的脑力,却得多跑几个文件去逐一定位和修改。

论文里给了两个对比案例特别能说明这个细节:

  • 案例1(干净版明显更省):Commons BCEL里一个给字节码反汇编输出加栈效应标注的任务。脏代码版本里,操作码分发逻辑塞在两个几百行的巨型switch里;干净版本把它们换成了几十行的轻量分发器,委托给约十个命名清晰的helper函数。结果:干净版本上Agent少用35%输入token,少开25%文件,少改31%代码行,少花32%的对话轮次。
  • 案例2(反而是脏版本更省):Netflix Genie里一个给集群任务加“最大并发任务数上限”的任务。翻新管线在这里只是给核心逻辑周围抽了些helper,核心逻辑本身没动,文件大小两边基本一致。结果:干净版本反而多用了8%的输入token——因为逻辑被摊开到了更多方法里,Agent得多处跑动才能拼出完整画面。

这个发现对开发者的启示其实很实在:“拆函数”这件事本身不是免费的。如果你把一个巨型函数拆成十个小函数,却没有解决真正的耦合和复杂度问题,只是把认知负担摊薄、挪了个地方,那么对Agent(以及对人)来说,导航成本未必会降低,甚至可能上升。真正有效的重构,是消灭复杂度,而不是把它切成更多份。

排除一个容易被忽视的干扰项:注释

这里有个很容易被忽略但研究者非常严谨地处理了的细节:造对照代码库的过程中,注释和代码是一起被搬动的。比如某个内部代码库的“干净版”,比“脏版”多出了整整8000行注释——因为做旧管线在制造混乱的同时,顺手删掉了大量文档字符串和说明性注释块。

这就带来一个问题:如果不控制这个变量,前面所有关于“干净代码更省token”的结论,可能其实是在说“注释少的代码更省token”,而跟整洁度本身没什么关系——毕竟注释也是要被塞进上下文的。

研究者专门做了一次注释消融实验:人工把两侧代码库的注释量和抑制标记(比如# noqa// NOSONAR# TODO)拉齐,该删的冗余注释删掉,该补的缺失文档补回来,执行逻辑本身一字不改。结果是:

  • 两个原本注释量差不多的代码库,消融前后Agent的表现几乎没有变化——说明抑制标记这类东西对Agent的行为影响可以忽略。
  • 而两个注释差距很大的代码库,消掉这个干扰项之后,干净代码的省token优势反而更明显了(其中一个从原本几乎持平的+1.2%,变成了-18.0%)。

结论是:“干净代码更省token”这个效应,不是注释多少造成的假象,它是真的。

这对正在用AI写代码的你意味着什么

把上面这些拼起来,这篇论文给出的画面其实很清楚:

代码整洁度这件事,在Agent时代没有过时,只是换了一个记账方式。它不再决定“AI能不能帮你干活”(几乎不影响),但它实打实地决定了“AI帮你干这活要花多少钱、绕多少路”。7%~8%的token节省听起来可能不算惊艳,但要考虑到:这是在单个任务上的效应,而现实中的代码库要被Agent持续、反复地读取和修改。放大到一个团队一年内成千上万次Agent调用,这个数字会变成实打实的账单差异。而34%的文件重复访问下降,某种意义上更值得关注——它反映的不是“省钱”这么表面的东西,而是Agent对代码库“有没有把握”这件更本质的事情。

几个更具体的启示:

  1. 代码规范和Lint检查,现在有了一个新的正当理由:它不只是给人类同事看之前的一种“专业行为体现”,也是给你正在使用的AI编程助手省钱、减少来回折腾的机制。
  2. “跨模块的改动”是干净代码收益最大的场景。如果你的代码库模块边界本身就很清晰、命名规范,那么需要跨越多个模块的功能开发,会是Agent发挥得最稳的地方。
  3. 重构一个巨型函数时,别只满足于“拆开”。如果只是把复杂度搬到别处而没有真正简化,Agent(以及未来读这段代码的人)未必会轻松多少。
  4. 一个可预测、命名清晰的方法名,胜过一堆注释。论文的注释消融实验的结论告诉我们:注释本身不是关键变量,真正起作用的还是代码结构本身的清晰程度。

这项研究的局限

作为一篇技术解读,也需要提一下这项研究本身的边界,免得被过度解读:

  • 样本量不大——6组代码库、33个任务,虽然做了660次试验,但覆盖的代码风格和任务类型仍然有限;
  • 实验只用了Claude Code + Claude Sonnet 4.6这一套组合,论文作者自己也提到,用Claude Haiku 4.5跑同一批任务时,通过率太低,没法干净地读出footprint差异,因此没有纳入正式结果——这意味着结论未必能直接推广到其他模型或其他Agent框架上;
  • 试验本身的方差很大:同一个任务、同一份代码,跑十次,最贵的一次和最便宜的一次能差2.5倍以上;不同任务之间,哪一侧更省token甚至会反过来(27个非校准任务里,16个任务干净版更省,11个任务反而是脏版更省)。数据集层面-7.1%的整体结论,是在几百次试验上“求平均”求出来的,不代表每一次、每一个任务都会呈现这个方向。

研究者自己也很诚实地把这一点写进了论文:这不是一个“代码越干净AI越便宜”的简单等式,而是一个在足够大样本上才稳定显现的统计趋势,具体到某一次重构、某一个任务上,结果可能完全相反。

小结

“写干净代码”这件事,曾经的理由都指向人:方便同事review、方便未来维护、方便新人上手。AI 编程助手的普及,给这个老话题加了一条新的、可以被量化的理由——代码的整洁度,现在也是AI在你的代码库里干活时要付出的一项计算成本。

它不会让不写测试的团队突然写测试,也不会让屎山项目突然被推翻重写。但至少,下次有人问“AI又看不出代码丑不丑,为什么还要写规范”,你可以把这篇论文甩给他:AI是真看不出“丑”,但它会为“丑”多绕路、多回头、多花token——而这笔账,最终是算在人类的信用卡上的。

资料链接:https://arxiv.org/html/2605.20049v1


还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 从0 开始构建 Agent Harness 将带你:

  • 抛弃臃肿框架,回归“驾驭工程 (Harness Engineering)”的第一性原理
  • 用 Go 语言手写 ReAct 循环、并发拦截与上下文压缩引擎等,复刻极简OpenClaw
  • 构建坚不可摧的 Safety Middleware 与飞书人工审批防线
  • 在底层实现 Token 成本审计、链路追踪与自动化跑分评估
  • 从“调包侠”进化为掌控大模型边界的“AI 操作系统架构师”

扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。