发布时间: 2025年10月19日,星期日
在过去的三个月里,我和一群经验丰富、志同道合的工程师在 Amazon Bedrock 内部构建了一些非常酷的东西。虽然我对我们正在构建的东西感到非常兴奋,但我们的团队还有另一个独特之处------我们的大部分代码都是由 AI agents(如 Amazon Q 或 Kiro)编写的。先别翻白眼:不,我们不是在"氛围编程"。我不相信这是构建健壮软件的正确方式。
相反,我们使用一种人类和 AI agent 协作生成代码变更的方法。对于我们的团队,每次提交都有工程师的名字附在上面,而该工程师最终需要审查并为代码负责。我们使用指导规则来设置 AI agent 在我们的代码库中运行的约束,而使用 Rust 编程是一个巨大的优势。Rust 编译器以专注于正确性和安全性而闻名,在编译时捕获许多问题并提供有用的错误信息来帮助 agent 迭代。作为与"氛围编程"的对比,我更喜欢"代理编程"这个术语。虽然没那么激动人心,但在我们行业中,无聊通常意味着好。
对我来说,如今我提交的代码中大约 80% 是由 AI agent 编写的。我的个人工作流程是:将任务分解直到我自己头脑清晰(经常使用 AI 来探索方法),提示 AI agent,审查其输出,与它迭代直到我喜欢结果,偶尔接管变更集并自己完成。我关注 agent 产生的每一行代码,直到我对生成的质量完全满意才接受它们------这与我自己编写每一行代码没什么不同。
我一直是一个高效的程序员,即使在过去几年里我只能投入有限的时间编码,也能找到时间写代码。而过去的几个月是我职业生涯中编码产出最高的几个月。我的团队也不例外------我们的代码产出是典型高速度团队的 10 倍。这不是夸张------我们实际上收集并分析了这些指标。
以 200 英里的速度驾驶
有趣的地方就在这里。一个典型的软件团队,即使是有经验的团队,也不能总是把事情做对。即使有良好的测试和工程实践,错误偶尔也会溜过去。我们都听过"在生产环境中测试"这句话。这个现实是我一直认为仅仅专注于测试是不够的,投资于影响半径和恢复时间同样重要的主要原因。
AI 辅助的代码也不例外,即使经过人类彻底审查,它可能仍然包含错误,我怀疑概率并没有显著不同。然而,当团队以 10 倍的速度提交代码时,整体的数学计算发生了变化。过去一年一次或两次的生产影响错误,可能变成每周发生的事情。即使大多数错误在集成或测试环境中被捕获,它们仍然会影响共享的代码库,需要调查并减慢团队其他成员的速度。再次强调,这不仅仅是夸张------我们的团队看到了这些挑战随着产出量阶跃增长而出现的迹象。
我越来越相信,为了让代理开发将工程速度提高一个数量级,我们需要将有问题的提交概率减少一个数量级。可能甚至需要减少更多,因为在高速度下,单个提交也可能开始以意想不到的方式相互交互。
换句话说,以 200 英里的速度驾驶,你需要大量的下压力来保持汽车在赛道上!
成本效益重新平衡
减少错误机会的最好方法之一是改进测试。我是一个航空爱好者,一直钦佩飞机制造商使用的测试理念。从早期模拟,到组件测试,到风洞测试,到测试到破坏点,最终到完全组装飞机的测试飞行。甚至飞行模拟器也在提高整个行业安全性方面发挥作用。其中一些理念已经在软件行业中尝试过,但远未普及。
例如,我一直喜欢"风洞"式测试,在受控环境中测试完全组装的系统。为了实现这一点,我使用的一种模式是实现外部依赖的高保真"假"版本,可以在本地运行。如果你这样做,你就可以编写在本地运行的构建时测试,验证整个系统的端到端行为。你甚至可以向假依赖注入意外行为和故障,测试系统如何处理它们。这样的测试易于编写和执行,因为它们在本地运行,并且非常擅长捕获组件间接缝处的那些狡猾错误。
不幸的是,对于中等复杂度的服务,伪造所有外部依赖并不总是容易的。即使你这样做了,现在你必须随着真实依赖的演变而保持同步。由于这些原因,根据我的经验,大多数团队不会写这样的测试。
我认为我们看到了早期迹象,表明代理编程可以改变这里的计算方式。AI agents 擅长吐出大量代码,特别是当期望的行为众所周知且几乎没有歧义时。原则上合理但实施和维护成本太高的理念,其成本现在降低了一个数量级。我真的很喜欢在这样的行业转变中乘风破浪,因为它们打开了过去不实用的新方法的大门。
我们的项目(在 AI agent 的帮助下)维护外部依赖的假实现,如认证、存储、链复制和推理引擎,用于测试中。然后我们编写了一个测试框架,使用这些假实现在开发者机器上启动整个分布式系统,包括所有微服务。构建时测试然后在这个完全组装的堆栈上启动我们的金丝雀版本,验证整个系统工作正常。
我非常看好这种方法能够捕获一类过去只有在变更提交并到达测试环境后才能捕获的错误。几年前,这样的想法会因美好但太昂贵而遭到抵制。这一次,对于一个相对复杂的系统,只用了几天就实现了。
快速驾驶需要更紧密的反馈循环
归根结底,所有这些变更都需要构建、测试和部署,才能带来客户价值。软件团队拥有需要数小时来构建、打包然后测试软件变更的 CICD 流水线并不罕见。然后该流水线可能需要几天时间将一批变更滚动经过预生产阶段并最终到达生产阶段。通常,具有这种自动化水平的团队会被认为是一个健康的团队。
代理编程改变了这种动态。在构建、打包和测试一组提交所需的时间内,另一打提交可能正在等待推出。当一个变更集准备好部署到生产环境时,它可能包含 100 个或更多的提交。如果其中一个提交包含问题,部署需要回滚,使流水线停止。与此同时,更多的变更累积起来,增加了混乱和风险。
我是一级方程式的粉丝,这让我想起赛道上的事故如何导致黄旗升起。通常,汽车以巨大的速度和加速度在赛道上飞驰。但如果发生事故,赛道工作人员升起黄旗,要求所有汽车在安全车后面减速。一场激动人心的比赛变成了围绕赛道的悠闲驾驶,直到碎片清理干净,赛道再次安全。为了最小化这样的减速,比赛组织者不遗余力地为所有类型的事故做准备,并确保他们能在几分钟内清理赛道并重新开始比赛。
就像全系统本地测试有助于收紧捕获某些错误的反馈循环一样,我们可能需要类似地思考我们如何实施 CICD 流水线。当团队以每小时几十次提交的速度移动时,有问题的 issues 需要在几分钟而不是几小时或几天内被识别、隔离和回滚。这意味着典型的构建和测试基础设施需要比今天快一个数量级。就像在线视频游戏在玩家输入和游戏反应之间有高延迟时变得无法玩一样,如果每次提交仍然需要长时间的延迟才能看到反馈,那么移动速度快 10 倍真的很难。
沟通瓶颈
我喜欢观察运行良好的操作。如果你曾经偷看过繁忙餐厅的后台,乍一看你可能认为那是混乱。但如果你花点时间注意细节,你会发现所有成员都在不断地相互协调。厨师、厨师、服务员、清洁工和管理者来回传递信息流。通过保持持续同步,一个运行良好的餐厅即使在高峰时段也能成功地为顾客服务,而不会牺牲质量或延迟。
我相信,为软件团队实现类似的速度提升需要对团队沟通方式进行约束。当你的产出增加一个数量级时,你不仅仅是在写更多代码------你是在做更多决策。我们应该使用这种缓存策略还是那种?我们应该如何处理这个边缘情况?这里的正确抽象是什么?在正常速度下,一个团队可能每周做出一两个这样的决策。在 10 倍速度下,他们每天做出多个。
挑战在于许多这些决策会影响其他人正在做的工作。工程师 A 决定重构认证流程,这影响了工程师 B 即将扩展的 API。这些不仅仅是实现细节------它们是贯穿代码库的架构选择。
我发现传统的协调机制在这里引入了太多延迟。等待 Slack 响应或安排当天晚些时候的快速同步意味着要么创建瓶颈------决策阻塞进展------要么在意识到冲突之前冒险走错路。在高产出下,协调的成本可能占主导!
一种方法是消除协调------如果每个人都独立工作在组件上,他们不太可能需要协调。但我发现这种理想在大多数现实世界系统中不切实际。所以另一种选择是显著降低协调成本。我们的团队坐在同一层,我认为这对我们的速度至关重要。当有人需要做出可能影响他人的决策时,他们可以走过去,在白板前几分钟内解决问题。我们对方法达成一致,实时讨论权衡取舍,两个工程师都回到工作中。决策快速、正确地做出,而不会造成一堆阻塞的工作堆积。
我认识到这并没有解决分布式团队的问题------那仍然是一个开放的挑战。
前进的道路
我对代理开发的潜力感到非常兴奋。我认为它不仅有提高软件开发效率的能力,还能让我们解决以前太小众或太昂贵而无法解决的问题。收益是真实的------我们团队 10 倍的产出增长不是理论上的,它是可测量的。
但关键部分是:如果我们简单地将 AI agents 螺栓固定到现有的开发实践上,这些收益就不会实现。就像给狭窄轮胎和旧刹车的汽车添加涡轮增压器一样,结果不会是更快的圈速------而是撞车。在 10 倍代码速度下,我们当前的测试、部署和团队协调方法成为限制因素。瓶颈只是移动了。
这意味着我们需要从根本上重新思考我们如何构建软件。为每天 10 次提交设计的 CICD 流水线在 100 次提交下会崩溃。在正常速度下"足够好"的测试策略在高速度下会让太多错误溜过去。以前工作良好的沟通模式会造成持续的阻塞工作堆积。
好消息是,我们已经有了全面测试、快速部署和高效协调的伟大理念------这些理念显示出前景,但由于实施和维护成本太高而未被广泛采用。改变的是代理开发本身可以大幅降低那些成本。增加我们代码产出的相同 AI agents 也可以帮助我们构建维持该产出所需的基础设施。
这是真正的机会:不仅仅是更快地写更多代码,而是使用 AI 使以前不切实际的工程实践变得实用。成功的代理开发团队将是那些认识到整个软件开发生命周期需要协调演进的团队。