和系统共舞:当 AI agent 走进软件这架活机器(Donella Meadows 风格)

和系统共舞:当 AI agent 走进软件这架活机器

我想说的其实很简单:

AI coding agent 没有让软件工程的基本功过时。它只是把系统里的水流拧大了。

水流变大以后,真正重要的问题不是水从哪里来,而是岸够不够清楚,闸门够不够灵,排水口够不够快。换成软件工程的话,就是:代码生成变快以后,测试、构建、评审、监控、架构边界、团队学习,能不能跟上。

这就是 Birgitta Böckeler 在 Martin Fowler 网站上那篇《Harness engineering for coding agent users》真正有价值的地方。文章署名是 Böckeler,不是 Fowler 本人。她属于 Thoughtworks,而 Fowler 长期在那里工作。所以我下面说 Fowler / Thoughtworks 这一脉,指的是一种工程思想生态,不是把文章误归到某一个人名下。

这篇文章用一个词概括 AI 时代的软件工程:harness

我愿意把它翻译成:让力量有形状的东西。

一、软件不是仓库,是池塘

我们平时说代码库,很容易把它想成一个仓库:文件放在里面,谁需要谁取出来,改完再放回去。

但这不是代码库真实的样子。

代码库更像一片池塘。它看起来安静,其实一直在流动:需求进来,代码进来,bug 进来;测试修掉一部分 bug,重构排掉一部分债务,部署把一部分变化送到用户手里;人来了又走,知识被写进文档,也会慢慢过期。

你只看一个 commit,会觉得那只是几行代码。你拉长时间看十年,会看到一个会生长、会老化、会积累记忆、也会遗忘的活系统。

AI agent 走进这片池塘以后,发生了什么?

它没有把池塘变成机器。它只是让一条水流突然变快了:写代码这件事,变得更便宜、更快、更容易批量发生。

问题也就从这里开始。

二、系统里最危险的事,是只放大一条流

系统思考里有三个很朴素的词:存量、流量、反馈。

存量,是某一刻可以数出来的东西。代码行数、模块数量、未解决的 bug、技术债、测试用例、文档、团队成员的脑容量,都是存量。

流量,是让存量增加或减少的动作。提交代码、修复 bug、删除依赖、补测试、写文档、做复盘,都是流量。

反馈,是系统发现自己偏了以后,把它拉回来,或者继续推着它往前走的力量。类型检查、单元测试、CI、code review、监控告警、事故复盘,都是反馈。

AI agent 主要改变了哪一个?

它主要放大了“提交代码”这条流。

这当然很有用。可是任何系统都怕一件事:只放大流量,不增强反馈。

水龙头变大,排水口没变,浴缸会溢出来。城市扩张很快,下水道没跟上,雨季就会内涝。软件也是一样。

如果写代码的速度提了十倍,但测试反馈、构建反馈、架构治理、依赖审计、生产监控、人类理解,都还停在原来的速度,你不会得到一个十倍高产的团队。

你会得到一个十倍速度制造混乱的团队。

这不是反 AI,也不是保守。这只是系统规律。

三、harness 是什么

Böckeler 说的 harness,不是给 agent 套一根绳子那么简单。

它是一整套工程环境:上下文、规范、工具、测试、检查、监控、信号。它的作用不是压住 agent,而是把 agent 的力量导向代码库真正需要的方向。

我更喜欢这样理解:

harness 是代码库周围一组活着的反馈环。

最快的一圈,是编译和类型检查。名字写错、参数不对,几秒钟就能知道。

再慢一点,是单元测试。一个分支被改坏,几十秒内被抓出来。

再慢一点,是集成测试和 CI。组件之间的契约坏了,几分钟到几十分钟内暴露。

更慢的是生产反馈。真实用户变慢了、报错了、流失了,监控和指标会说话。

还要更慢的,是架构和技术债反馈。模块膨胀、依赖混乱、review 越来越累,往往几周甚至几个月后才显形。

最慢的,是组织学习反馈。同一类问题反复出现,同一条规则总被绕过,同一个团队总在夜里救火,这些通常要更长时间才能看清。

这些反馈没有谁可以替代谁。

你不能指望生产监控替代单元测试。等用户替你发现 bug,已经太晚了。你也不能指望类型检查替代架构治理。类型系统能告诉你一个变量错了,不能告诉你一个模块边界已经变形。

真正好的 harness,是这些圈都还活着,而且它们的信号能被人看见。

四、AI 让延迟变得更贵

反馈有一个特别关键的属性:延迟。

动作发生以后,多久能知道后果?这决定了系统能不能被调节。

过去,一个人一天可能改一个文件,review 到第二天也还勉强说得过去。现在,一个 agent 一次可以改三十个文件,生成六十个测试,引入两个依赖,顺手改掉三条接口契约。

如果反馈还停在“明天再 review、下周再集成、上线后再看”的节奏,系统就会出问题。

因为动作太快,信号太慢。

这也是为什么 Fowler 那一脉长期强调持续集成、快速构建、自动测试。所谓“十分钟构建”不是程序员洁癖,而是系统控制的基本要求:

让信号比破坏来得早。

AI 把执行速度提上来了,反馈延迟就不能再被当成小问题。构建慢、测试慢、review 慢、监控慢,在 AI 时代都会变成更大的风险。

五、不是所有代码库都同样适合 agent

Böckeler 提到一个很好用的词:harnessability。

意思是:有些代码库天生更容易被 harness 驾驭,有些则不容易。

什么样的代码库更容易?

边界清楚。模块知道自己负责什么,也知道不该碰什么。

测试可信。跑过的测试真的能给人安全感,而不是一堆装饰。

构建稳定。失败能说明问题,成功也值得相信。

文档和规则可执行。不是写给人看的口号,而是能进入脚本、模板、AGENTS.md、CI 和 review 流程里的约束。

生产信号清楚。出了问题能知道哪里痛,而不是只知道“好像有点慢”。

这样的代码库,用 agent 会更安全。因为 agent 的动作虽然快,但它每迈一步,都能碰到边界、听到反馈、看见后果。

反过来,如果一个系统本来就没有测试、没有边界、没有监控、没有可信文档,AI 带来的第一件事很可能不是生产力,而是混乱的加速度。

所以“要不要上 agent”不是一个工具问题。

它首先是一个系统问题:你的代码库有没有足够清楚的岸?

六、不要只盯着最低层的指标

Donella Meadows 写过一篇文章,叫《Leverage Points: Places to Intervene in a System》。里面把干预系统的杠杆点分成十二级,从弱到强,大概可以理解为:参数、缓冲、结构、延迟、反馈、信息流、规则、自组织、目标、范式。

很多关于 AI 写代码的讨论,都卡在最低层。

一天写多少行代码?合多少 PR?节省多少工时?agent 跑了多少轮?

这些不是没用。但它们只是参数。参数好测,杠杆却很低。

更高一层,是结构。模块边界是不是清楚?依赖图是不是可理解?代码是不是能被局部修改?

再高一层,是反馈。测试、CI、review、监控、事故复盘,能不能把系统拉回健康状态?

再高一层,是信息流。谁能看见风险?谁知道某个 agent 总在绕过规则?谁知道同一种 review comment 已经重复了一百次?

再高一层,是规则。哪些代码可以合并?哪些检查不能跳过?什么情况下必须回滚?agent 可以改哪里,不能改哪里?

再高一层,是目标。团队到底想优化什么?是 PR 数量,还是长期可维护性?是看起来省了多少人力,还是让用户和工程师都更少受折磨?

最高处,是范式。我们到底把软件工程师看成什么?

如果工程师只是“把需求翻译成代码的人”,那 AI 看起来就在替代工程师。

如果工程师是“把意图变成可演化系统的人”,那 AI 只是多了一个执行层。

范式不同,所有指标都会变样。

七、目标会偷偷换掉

AI 进入组织以后,最危险的变化常常不是技术变化,而是目标变化。

一开始,团队可能说自己的目标是:做用户喜欢的产品,让系统稳定,让新人能读懂代码,让工程师不要长期疲惫。

后来,AI 来了,目标悄悄变成了:PR 更多、周期更短、工时更少、报表更好看。

这些新目标并不一定错。但它们和原来的目标不是一回事。

目标一变,系统里的激励、指标、会议、周报、review 标准都会跟着变。表面上吞吐提高了,背后可能是技术债变厚了,用户体验变差了,工程师更累了,事故更多了。

所以 Thoughtworks Technology Radar 提醒大家,不要只看 coding throughput。我也赞同。

更好的指标,应该一起看:first-pass acceptance、iteration cycles、post-merge rework、failed builds、review burden、DORA,以及生产事故和用户体验。

但还要再说深一点:

选择指标,本身就是选择目标。

你测什么,系统就会学着变成什么。

八、工程师的位置正在上移

过去,工程师大量时间花在写代码上。

代码是存量。提交是流量。修 bug 是流量。我们天天在这些层面忙。

AI 让一部分执行工作变便宜以后,工程师并不是没事干了。恰恰相反,那些过去一直重要、但总被推迟的高杠杆工作,终于变成了主业。

写清楚 agent 如何在这套代码里工作,这是规则层。

设计 fitness function,保护架构边界,这是反馈层。

把反复出现的 review comment 变成文档、脚本或模板,这是信息流和规则层。

决定团队到底追求什么,不被 PR 数牵着走,这是目标层。

理解软件不是一次性生产出来的,而是长期长出来的,这是范式层。

Böckeler 借用了一个很好的说法:human in the loop 正在变成 human on the loop。

人不再只是站在循环里亲手搬每一块砖。人开始站在循环之外,看循环本身是否健康。

这不是退场。

这是工程师的工作重心上移。

九、怎么和系统共舞

她还写过一篇短文,《Dancing With Systems》。中心意思是:复杂系统不能被彻底控制,只能被持续倾听、引导和调谐。

放到 AI 时代的软件工程里,我会把它变成几条很具体的建议。

第一,先听系统说话。

不要急着加更多 agent、更多规则、更多指标。先看现有系统反复在哪里疼:事故复盘里重复出现的根因,review 里反复出现的问题,CI 里反复失败的步骤,线上指标里反复冒头的异常。

系统一直在说话。很多时候,是我们太急着下命令。

第二,把心智模型摆出来。

有人把 agent 当同事,有人把它当代码机器,有人把它当省人力工具,有人把它当加速器。这些理解不同,后面的规则就会完全不同。

最麻烦的不是大家意见不一致,而是大家以为自己说的是同一件事。

第三,不要为了速度关掉反馈。

为了让 agent 跑得顺,把测试跳过;为了让指标好看,把 acceptance 定得很松;为了减少摩擦,把 review 变成形式。这些动作短期都舒服,长期都会变成系统的账单。

第四,把失败沉淀成环境,而不是情绪。

如果 agent 总是犯同一种错,不要只骂它,也不要只换一个提示词。问一问:这件事能不能变成测试?能不能变成模板?能不能写进 AGENTS.md?能不能进入 CI?

一次失败如果只留下情绪,它会重复发生。一次失败如果变成环境,系统就学习了一次。

第五,把时间视野拉长。

AI 让今天变得很兴奋,但软件真正的账期通常在三年以后。三年后,谁维护今天生成的代码?谁解释今天定下的规则?新人打开这个模块时,是感谢你,还是被你困住?

能经得起三年追问的决定,才是好决定。

第六,承认复杂性不会消失。

AI 不会消灭复杂性。它只会改变复杂性的位置。

你把代码变简单,复杂性可能跑到提示词里。你把提示词变简单,复杂性可能跑到工具链里。你把工具链变简单,复杂性可能跑到组织流程里。

好的工程不是消灭复杂性,而是把复杂性放到最容易被看见、被讨论、被管理的地方。

十、harness 也需要被检查

还有一件事必须说清楚:harness 自己也会失效。

测试会变成走过场。lint 会变成噪声。架构规则会过期。AI review 可能看起来很聪明,却漏掉关键问题。指标可能驱动错误行为。AGENTS.md 可能越写越长,最后没人读。

调节器本身,也是系统的一部分。

所以成熟的 harness engineering,必须有一条反馈环专门用来检查 harness 自己:

测试是否还有效?

CI 是否真的能挡住风险?

review comment 是否应该沉淀成规则?

某条规则是不是已经过时?

agent 是否总在绕过同一类约束?

没有这条反馈环,harness 会从工程能力慢慢变成装饰。

十一、最后回到那句话

软件工程过去几十年的重要实践,持续集成、测试、重构、技术债治理、演进式架构、Technology Radar,本质上都在做同一件事:

让一个由人和代码组成的活系统,变得更容易被驾驭。

AI coding agent 没有改变这件事。

它只是把这件事变得更紧迫。

如果有人说,AI 来了,软件工程的旧规则都过时了,不必急着反驳。系统会用自己的方式给他反馈。

如果有人说,AI 改不了什么,我们照旧就行,也要保持警觉。水流已经变大,岸的形状就必须重新检查。

真正属于 AI 时代的工程师,不是被新工具迷住的人,也不是守着旧经验不动的人。

而是那些愿意每天看见系统、倾听系统、调整系统,并且耐心地和系统一起共舞的人。

岸不是为了禁锢水。

岸是为了让水抵达它该去的地方。