Brownfield Agentic Engineering
推荐理由Addy Osmani 给遗留库上 Agent 的实操手册:绿黄红风险区、特征化测试先锁行为、只写下代码说不出的约束,再按完整单元迁移。附 Netflix、Bun 案例。要在真实旧系统里放 coding Agent,先读这本再动手。
读前速览
Addy Osmani 为使用 coding agents 安全地在 legacy brownfield codebases 中提供了一个实用的操作手册:按风险划分 zones,写下 code 不能说的内容,用 characterization tests 锁定当前行为,并以完整 units 迁移。
在棕地代码库中,仓库不再是系统行为的完整描述,因此 agents 需要无法从代码推断出的约束。
Institutional knowledge、duct tape、legacy services 和 cross-team expectations 都存在树之外。Agents 擅长推断系统地图,因此团队应仅提供业务细微差别、权衡理由、未强制执行的准则、领域规则和历史背景——"write down what the code can't say, and nothing else."
风险区域(green / yellow / red)将自治转化为操作程序,而非比喻。
Green 是安全、经过充分测试、隔离的代码,agents 可以进行 tight loop;yellow 是混合质量,必须先进行 characterization tests;red 是 auth、billing、permissions 和 payroll,每一步都需要人类配对。人绘制地图,区域仅在获得时移动,区域决定允许的 verbs。
研究必须产生持久产物,规划应从干净的上下文开始。
对于 yellow 和 red 工作,单独的只读通道应生成 comprehension memo,包含入口点、所有者、调用者、抽象、测试、生产信号、历史和未解问题,并引用文件、issue 或 dashboard。否则下一个 agent 将重复同样的考古,因为 chat history 在压缩后是糟糕的系统记录。
每一次重复的修正都是 harness 的缺失部分。
Instructions 记录不寻常的仓库事实,skills 包装可重用过程,plugins 提供对 ownership catalogs 或 dashboards 的受控访问。当同一条 review comment 再次出现时,应该转化为 lint 规则、hook、type、test 或 skill,使 harness 成为团队决定不再重复支付的失败记录。
先从零风险工作开始,用特征化测试锁定今天的行为,再让 agents 改进任何东西。
Characterization tests 确定模块今天的功能,包括丑陋的部分,因为一些丑陋行为是业务运行的基础,agent 会乐意在 green suite 后 "fix" 它。agent 会话不应是这些测试的唯一作者;Netflix 的 GraphQL cutover 使用 replay 和 shadow traffic 仅在 payloads 匹配时才推广。
其余 1 个要点
迁移应以完整单元完成,旧依赖必须可证明已消失。
半完成的迁移会让 agents 困惑,因为搜索返回矛盾的先例。Bun 的 Zig‑to‑Rust 端口(50 个工作流,11 天,adversarial reviewers,pre‑existing suite 作为 merge gate)、VB6‑to‑C# 研究(简单特征 92% 等价 vs 复杂特征 47%)、Stripe 的 3.7M‑line TypeScript 迁移、Asana 的 Enzyme backlog 等都指向相同结构:狭窄的机械范围、已有测试套件,以及人类审查每一次变更。
完整摘要
Addy Osmani 认为在旧代码库中进行 agentic engineering 本质上是让隐藏的约束可见,并使廉价的变更值得信赖。因为棕地仓库不再是系统行为的完整描述——institutional knowledge、legacy services 和 cross-team expectations 都存在树之外——未经监督的 agents 可能产生 "works" 但设计错误、测试脆弱的代码。他提出由人类而非 agent 绘制风险区域(green / yellow / red),并制定三条操作规则:区域仅在获得时移动,且区域决定允许的行为(tight loop、tests first 或 human pairing)。他建议仅记录代码无法说明的内容,生成持久的研究产物,使探索在会话后仍存活,并将重复的修正转化为 harness 元素(lint 规则、hooks、types、tests、skills)。他推荐先从零风险工作和特征化测试开始,锁定当前行为,再让 agents 改进,引用 Netflix 的 GraphQL shadow‑traffic cutover。迁移应以完整单元完成,删除旧路径,并以 Bun 的 Zig‑to‑Rust 端口、VB6‑to‑C# 研究(92% vs 47% 行为等价)、Stripe 的 3.7M‑line TypeScript 迁移、Asana 的 Enzyme backlog 和 Shopify 的 React Native 到 native 重写等证据为依据。
目录 · 10 节
在旧代码库中进行智能体工程,关键在于让隐藏的约束变得可见,并让低成本的变更值得信赖。让我们来谈谈在棕地代码库中应该怎么做。Agentic engineering in an old codebase is about making hidden constraints visible and cheap changes trustworthy. Let’s talk what to do in brownfield codebases.
在我的职业生涯中,我曾参与过一些代码库已经存在很久的团队。这些就是棕地系统:代码仓库已不再完整描述系统实际的行为。制度性知识、临时拼凑的方案、遗留服务,以及其他团队所依赖的预期,都存在于代码树之外。在编写新代码之前,你必须先了解这些约束,还必须证明改动没有破坏它们。我很喜欢与智能体一起编写代码,但如果不加监督地把它们投入一个较老的棕地代码库,你最终可能会得到一个“能运行”的东西,却采用了错误的系统设计,并配有脆弱的测试。During my career I’ve worked on teams whose codebases had been around a long time. Those are brownfield systems: the repository is no longer a complete description of how the thing actually behaves. Institutional knowledge, duct tape, legacy services, and expectations other teams depend on live outside the tree. You have to learn those constraints before you write new code, and you have to prove a change didn’t break them. I love coding with agents, but throw them at an older brownfield codebase unsupervised and you may end up with something that “works” but with the wrong system design and brittle tests.
即使在那些早于 AI 时代就希望开展现代化改造的团队中,你通常也必须非常、非常零碎地推进,并配备严格的测试以及强有力的信心保障,以确保不会破坏现有功能。你多少也知道,除了实际的用户旅程测试之外,你所进行的任何迁移都必须通过大量可重复的测试来确保系统继续按预期运行。如今,有些人可能会说,只要代理生成了不是由你逐步决策并编写的代码,你就已经身处棕地项目之中了。无论如何,你都希望以安全的方式优化低成本变更。Even in teams that wanted to do modernization efforts pre-AI, you often had to take things very, very piecemeal, with strong testing in place, a strong layer of confidence to make sure that you weren’t breaking things. You kind of knew that on top of actual user journey testing, any migrations you were making had to keep things working as intended via a barrage of repeatable tests. These days some folks may say that as soon as an agent drops code you didn’t author decision-by-decision, you’re already in a brownfield project. Regardless, you want to optimize for cheap changes being made safely.
而如今,尤其是在过去五到十年里,我觉得,人们越来越重视测试、验证,以及如何以不会破坏系统的方式进行更改。但这并不改变这样一个事实:如果你正在投入大量工作来引入智能体工程、软件工厂,以及所有这些用于自主处理大型代码库的其他模式,那么你必须额外投入相当多的谨慎和考虑;否则,你就有可能陷入技术债务的世界。And these days, especially in the last, I would say, maybe five to ten years, this idea of caring more about testing, caring more about verification, caring more about how you make changes in a way that is not going to break things, I feel has gotten more attention. But that doesn’t change the fact that if you’re doing a lot of work trying to introduce agentic engineering, and then software factories and all of these other kinds of patterns for autonomously working through these large codebases, you have to put quite a bit of additional mindfulness in place otherwise you risk signing up for a world of technical debt.
在我们深入讨论之前,先假设代码应当是事实来源。我们在其上添加的、用于帮助改造现有系统的任何内容,都是无法轻易推断出来的部分。我想从区域、影响范围以及其他一些我认为会有所帮助的模式来讨论这一点。Before we dive in, let’s assume that the code should be the source of truth. Anything we add on top to help brownfield is what can’t be easily inferred. I want to talk about this in terms of zones, blast radius and a few other patterns I think will help.
区域Zones
如果我要着手处理一个已经存在了一段时间的旧代码库,我可能会想先了解哪些代码不应该改动。你可以考虑划分以下区域。例如,绿色区域 = 安全/测试完善/相互隔离;黄色区域 = 质量参差不齐;红色区域 = 敏感功能/身份验证/计费/权限。If I’m going into an older codebase that’s been around for a while, I probably want to get a sense of what code shouldn’t I be touching. You can consider these zones. E.g. Green zone = safe/good tests/isolated, yellow = mixed quality, red = sensitive/auth/billing/permissions.
代码库中有哪些部分非常、非常敏感,或者并不是每个人都能很好地理解?也许你可以用不同的区域来表示这些部分。比如,你可以有一个绿色区域,这里的测试覆盖率非常高,采用当前的现代惯例,并且具有良好的隔离性。对于系统中的这些部分,代理可以放手在其中工作,并进行快速迭代。What are the parts of the codebase that are very, very sensitive, or that not everybody understands well? And maybe you would draw those with different zones. Maybe you have a green area that’s got very good test coverage, and is using modern conventions that are current, and has good isolation. And for those parts of the system, agents can go off and work on that in a tight loop.
有些网站,尤其是我合作过的电子商务网站,很容易出现五六个部门各自拥有独立微型网站的情况,但对最终用户而言,整个体验却会像是一个整体。而在表面之下,实际上隐藏着大量固有的复杂性。某个团队的项目可能拥有非常完善的测试覆盖率;也许这是在过去几年中构建的。其他团队则未必如此。因此,你会有这样一个绿色区域。There are sites, especially commerce sites that I’ve worked with, where you could easily have five or six departments all with their own microsites, when the entire experience to the end user is going to feel like a single thing. And there’s actually a lot of inherent complexity underneath the surface. One team might have really good test coverage for their stuff; maybe it was built in the last couple of years. Other teams may not. So you have this green zone.
也许你有黄色区域,其中的质量参差不齐,可能是各种内容的混合;在编写特性测试之后,代理可以在那里修改代码。Maybe you have yellow, which is mixed quality, maybe it’s a mix of things, and agents can change code there after characterization tests have been written.
然后你还可以划分出红色区域,其中包含身份验证、计费、权限、薪资等敏感内容,以及任何你通常不会随意接触并仓促修改的部分。比如,只有少数人了解其整体运作方式的系统。在这类系统中,你不希望有人在无人监督的情况下重写代码。And then you can have red areas, where you’ve got sensitive stuff like authentication, billing, permissions, payroll, anything that you wouldn’t normally touch and make some hasty changes to. For example, if only a small number of people understand how it all works. You don’t want unsupervised rewrites in that kind of system.
三个规则让这些区域成为操作流程,而不只是一个比喻。地图由人来绘制,而不是由智能体绘制;如果任由智能体选择,它会从最可怕的文件开始,因为最可怕的文件里有最有趣的名称。区域只有在满足条件后才会变更:一旦已有特征测试,且模块负责人审查过智能体的首次修改,黄色就会变成绿色。而区域决定行动方式:绿色区域采用紧密循环,黄色区域先写测试,红色区域则要求每一步都有人与智能体结对协作,否则就不开展这项工作。Three rules make the zones an operating procedure instead of a metaphor. A person draws the map, not the agent; left to choose, the agent starts in the scariest file, because the scariest file has the most interesting names. Zones only move when it’s earned: yellow becomes green once characterization tests exist and the module’s owner has reviewed the agent’s first changes. And the zone sets the verbs: green is a tight loop, yellow is tests first, red is a human pairing on every step or the work not happening.
写下代码无法表达的内容Write down what the code can’t say
自主程度应取决于影响范围、可观测性和可恢复性,而不应由模型的置信度决定。Autonomy should follow blast radius, observability, and recoverability. A model’s confidence is a poor guide.
所以我认为,至少对以下问题有一个基本的认识是有意义的:你如何看待整个世界的地图,以及智能体能够从代码库中自行推断出什么?智能体实际上可以从代码本身推断出相当多的信息。曾经有一段时间,人们试图为所有事情都写上 Markdown 文件,然后把这些文件一股脑塞进它们的上下文窗口。智能体其实很擅长理解系统的整体结构。你真正需要提供给它们的,是那些无法从代码本身显而易见地看出来的东西。是否存在某些约定?某些模式?某些其中未体现的细微之处?我认为这些很重要。So I think it makes sense to have at least a sense of, how do you think about the map of the world, and what can the agent infer itself from the codebase? Agents can actually infer quite a lot from the code itself. There was this period of time when people would try to include markdown files for absolutely everything, and then they’d stuff them in their context windows. Agents are actually pretty good at understanding the map of the system. What you want to give them is the stuff that is not obvious from the code itself. Are there conventions? Are there patterns? Are there nuances that are not in there? I think that’s important.
具体来说,这意味着:特定于业务或团队的细微差异,能够解释系统为何采用某种结构的权衡取舍,未通过静态分析或工具明确强制执行的指导原则,特定领域的业务规则,以及反直觉实现背后的外部约束和历史背景,等等。Concretely, that means: business or team specific nuance, trade-offs that explain why the system is structured a certain way, guidelines that aren’t explicitly enforced by static analysis or tooling, domain-specific domain rules, external constraints and historical context behind counter-intuitive implementations and so on.
写下代码无法表达的内容,仅此而已。Write down what the code can’t say, and nothing else.
让研究成果在本次会话结束后仍然保留Make the research survive the session
如果你的代理探索后没有产出任何持久化工件,下一个代理就得再次为同样的考古工作买单。If your agent’s exploration produces no durable artifact, the next agent pays for the same archaeology again.
我会在那张地图中再加入一项:一份持久化的研究产物。对于黄色和红色工作,我倾向于单独进行一次只读检查,并产出一份简短的理解备忘录,内容包括入口点、负责人、调用方、现有抽象、测试、生产信号、相关历史记录以及未决问题。所有结论都应引用文件、问题单、所有权记录或仪表板。One piece I would add to that map is a durable research artifact. For yellow and red work, I like a separate read-only pass that produces a short comprehension memo: entry points, owners, callers, existing abstractions, tests, production signals, relevant history, and open questions. Claims should cite a file, issue, ownership record, or dashboard.
否则,默认循环会白费它的研究成果。代理会弄清身份验证流程的运行方式,完成任务,却在会话结束时丢失这一模型。聊天历史并不是一个很好的记录系统,尤其是在压缩之后。The default loop otherwise wastes its research. The agent works out how the auth flow behaves, completes the task, and loses that model when the session ends. Chat history isn’t a great system of record, especially after compaction.
经过研究后,我会在一个干净的上下文中开始规划。询问各种可行方案会涉及哪些文件、会保留哪些不变量,以及应如何撤销这些方案。由人来选择路径。如果发现地图有误,实现就应停止。审查从头开始,并根据验收标准反向检查。干净的审查者更有可能注意到:某个测试证明了实现,却遗漏了需求。After research, I would start planning with a clean context. Ask which files the plausible approaches touch, which invariants they preserve, and how you would reverse them. A human picks the path. Implementation should stop if it discovers the map was wrong. Review starts fresh and works backward from the acceptance criteria. A clean reviewer is more likely to notice when a test proves the implementation while missing the requirement.
当指令变成测试框架When instructions become a harness
每一次重复的修正,都是测试工具链中缺失的一环。Every repeated correction is a missing piece of the harness.
准确说明各个部分如何配合是很有用的。Instructions 用于记录有关代码库的不寻常事实。Skills 用于封装可复用的流程,例如检查影响范围或验证架构变更。Plugins 则可以提供对所有权目录、事件档案或仪表板的受治理访问。It is useful to be precise about where the pieces fit. Instructions record unusual facts about a repository. Skills package reusable procedures such as checking blast radius or verifying a schema change. Plugins can provide governed access to the ownership catalog, incident archive, or dashboards.
Harness 是 agent 周围的工作环境:上下文、工具、权限、测试、日志和恢复机制。工厂会调度许多可靠的循环,维护持久状态,并将新颖的案例交还给人类处理。The harness is the working environment around the agent: context, tools, permissions, tests, logs, and recovery. A factory schedules many dependable loops, keeps durable state, and hands novel cases back to people.
实际检验在于代理出错时会发生什么。如果你悄悄修复差异,下一次会话可能会重蹈覆辙。当相同的审查意见再次出现时,就把它转化为 lint 规则、钩子、类型、测试或技能。对于无法通过机械方式强制执行的约束,才保留为文字说明。The practical test is what happens when the agent gets something wrong. If you quietly repair the diff, the next session can repeat it. When the same review comment appears again, move it into a lint rule, hook, type, test, or skill. Keep prose for constraints that cannot be enforced mechanically.
拒绝规则、限定范围的凭据或 CI 检查不必记忆。随着时间推移,测试框架会成为团队已经决定不再为之付出第二次代价的失败记录。A deny rule, scoped credential, or CI check doesn’t have to remember. Over time the harness becomes a record of failures the team has decided not to pay for twice.
从零风险的工作开始Start with zero-risk work
在让任何改进发生之前,先锁定今天的行为。Lock today’s behavior before you let anything improve it.
如果你要将代理引入现有代码库,这与其他类型的现代化工作非常相似。也许你可以从零风险的工作开始。不应该一上来就说:“嘿,我们把这个单体应用用 Rust 重写吧。”或许第一步应该是先解释这些东西是如何工作的。If you’re bringing agents into an existing codebase, it’s very similar to other kinds of modernization efforts. Maybe you begin with zero-risk work. It shouldn’t be like, hey, let’s rewrite this monolith in Rust or something like that. Maybe it’s, first explain how the things work.
生成能够锁定当前行为的特征测试。Generate characterization tests that can lock that current behavior.
特征化测试是用于记录系统当前实际行为的自动化测试,以便你能够安全地重构或修改遗留代码。Characterization tests are automated tests used to document a system’s actual current behavior so you can safely refactor or change legacy code
我所说的特征测试,是指那些明确固定模块当前行为的测试,包括其中不美观的部分,因为在旧系统中,有些不美观的行为正是业务赖以运行的基础,而智能代理会在测试套件全绿的情况下,毫不犹豫地“修复”它。之所以采用旧的机制,是因为要解决的问题本身就很老。Netflix 在 GraphQL 切换中也在生产规模上采用了同样的思路——针对新旧路径重放流量并进行影子流量测试,对比负载内容,只有在两者匹配时才予以升级。这就是当首页级界面没有可靠的单元测试套件时应采用的上线路径:不要猜测;让两者同时运行并进行比较。By characterization tests I mean tests that pin down what the module does today, ugly parts included, because in an old system some of that ugly behavior is what the business runs on, and an agent will happily “fix” it behind a green suite. The machinery is old because the problem is old. Netflix used the same idea at production scale in its GraphQL cutover - replay and shadow traffic against the old and new paths, diff the payloads, promote only when they match. That is the promotion path when a homepage-class surface has no honest unit suite: don’t guess; run both and compare.
如果是由代理让这些测试通过的,就不要让同一个会话成为测试的唯一编写者。先通过单独的一轮处理或由人工来固定行为,然后再让代理开始工作。否则,你得到的将是一套全绿的测试,而其中编码的是你刚刚臆造出来的实现。When an agent is the one making them pass, don’t let that same session be the only author of the tests. Pin the behavior first, in a separate pass or by a person; then let the agent work. Otherwise you get a green suite that encodes the implementation you just invented.
然后,你就开始着手进行机械式转换。你可以先做死代码和未使用导出的清查。不要一开始就处理系统中最棘手或最复杂的部分。最终,你希望对这些迁移中的任何一个都建立起信心。And then you start down the path of doing mechanical transforms. You can do dead code and unused export inventories. You don’t want to start with the trickiest or hairiest parts of the system. And ultimately you want to have that confidence with any of these migrations.
我记得,在参与大型代码库的过程中,我处理过许多不同类型的迁移,也见过人们非常谨慎地修复那些已经损坏的部分。I remember working on a number of different kinds of migrations over my time on large codebases, and people exercising a great deal of care, even when fixing things that were broken.
我曾经参与过的一批较老的代码库中,有一个是在 AOL。曾经有一天我本来应该休假,当时正在办公室附近的一家漫画书店里逛。碰巧我的老板给我发了条短信,问我是否能抽空过去一趟。AOL.com 的首页完全崩了,而我们身边没有足够多的 JavaScript 专家可以去查清楚问题所在。所以我说,好吧,没问题,我这就进来看看。 你可能会觉得,如今看来,一个首页能有多复杂?但当你有几十个甚至更多的部门,每个部门都可能负责许多不同的组件、各种不同的标准、各种不同的脚本和 A/B 测试,以及所有这些东西时,你就会希望避免因为自己的改动而让其他所有人的系统都出问题,因为你不一定能像期望的那样,为所有地方都配备完整的测试覆盖率。在那次事件中,我最终修好了问题,但我们基本上至少得对那些没有自己单元测试的部分进行用户测试,确认各项功能运行正常,同时不会影响所有其他人。这一点相当重要。One of the older codebases I worked on was at AOL. There was a day when I was supposed to be off, and I was visiting a comic book store near the office, and as it so happened, my boss dropped me a text and asked if there was any way I could swing by. The AOL.com homepage was completely broken, and we didn’t have enough JavaScript experts around to go and figure it out. So I said, okay, sure, I’ll come in and take a look. And you would think these days, oh, a homepage, how complicated can it be? But when you have dozens and dozens of departments of people that can own lots of different components, lots of different criteria, lots of different scripts, A/B tests, all of these things, you want to avoid breaking the world for everybody else, because you’re not necessarily going to have test coverage all over the place in the same way that you would like. In that case I was able to get it fixed, but we basically had to at least user-test the things that didn’t have their own unit tests. How well were things working, without breaking for everybody? So that was kind of important.
这仍然是那项工作。代理并不会消除几十个部门各自为政的问题;它们只是让针对这一问题尝试变更的成本更低。一个只有生产流量才能真正理解的界面,按定义就是红区;在你为这种流量搭建替身之前,我在休息日做的用户测试仍然是关卡。That’s still the job. Agents don’t remove the dozens-of-departments problem; they make it cheaper to attempt a change against it. A surface that only production traffic really understands is a red zone by definition, and until you’ve built a stand-in for that traffic, the user-testing I did on my day off is still the gate.
按完整单元迁移Migrate in complete units
当新路径能够正常工作,且旧依赖已被明确证明不再存在时,迁移才算完成。A migration is complete when the new path works and the old dependency is demonstrably gone.
未完成的迁移尤其容易让智能体感到困惑。搜索结果在四十个文件中返回旧方案,在十二个文件中返回替代方案,还有一个将两者都呈现为当前方案的垫片。智能体看到的是相互矛盾的先例。Half-finished migrations are particularly confusing to agents. Search returns the old approach in forty files, the replacement in twelve, and a shim that presents both as current. The agent sees contradictory precedent.
我宁愿端到端地完成一条路径,包括移除旧路径,也不愿转换三十个文件却让两种模式并存。如果删除工作要留到未来的清理工单中,那么这次迁移单元就还没有完成。I would rather finish one route end to end, including removing the old path, than convert thirty files and leave both patterns alive. If deletion is a future cleanup ticket, the migration unit is not complete.
即使替代实现仍在调用旧版实现,测试也可能继续通过。SWE Refactor Bench 将这种迁移称为“盲点”。在 520 次智能体运行中,只有 28 次通过了迁移审计、行为测试和独立验证。Tests can stay green while a replacement still calls the legacy implementation. SWE Refactor Bench calls this migration “Blindness.” Across 520 agent runs, only 28 passed its migration audit, behavioral tests, and independent verification.
如果 codemod 能完成常规变更,就使用代理来协助编写和检查。将例外队列交给代理。Stripe 的迁移之所以在这里很有参考价值,恰恰是因为没有代理参与:持久的产物是迁移机器。If a codemod can make the routine change, use the agent to help write and check it. Give agents the exception queue. Stripe’s migration is useful here precisely because no agents were involved: the durable artifact was the migration machine.
大型迁移的经验教训The lessons from bigger migrations
Bun 的 Zig-to-Rust 移植在一个包含 53.5 万行代码的代码库上运行了约 50 个工作流,历时 11 天;每个生成单元都由两名对抗性审查者审阅,并以完整的既有测试套件作为合并门禁。值得借鉴的是,在任何代理运行之前,团队先花了数小时编写移植指南,将 Zig 惯用写法映射到 Rust。Anthropic 自身的迁移流程则会在大规模运行开始前,先在一次可丢弃的小型迁移上对其规则手册进行压力测试,并舍弃试运行的输出。Bun’s Zig-to-Rust port ran about 50 workflows over 11 days from a 535,000-line codebase, with two adversarial reviewers on every generated unit and the entire pre-existing test suite as the merge gate; the part worth copying is that hours went into a porting guide mapping Zig idioms to Rust before any agent ran. Anthropic’s own migration process stress-tests its rulebook on a disposable mini-migration and throws the trial output away before the broad run begins.
一项受控的 VB6 到 C# 研究测得:简单功能的行为等价率为 92%,复杂功能则为 47%——单元大小才是关键杠杆。这种模式完全早于智能体的出现:Stripe 通过数月的 codemod 工作,在一个 PR 中将 370 万行代码迁移到了 TypeScript,整个过程没有使用智能体;而 Google 的大规模变更章节则解释了为什么随着代码库不断增长,原子变更会逐渐缩小。如今,Spotify 报告称,每月有 650 多个智能体 PR 在多年前构建的 Backstage 轨道上合并。A controlled VB6-to-C# study measured 92% behavioral equivalence on simple features and 47% on complex ones: unit size is the lever. The shape predates agents entirely: Stripe moved 3.7 million lines to TypeScript in one PR through months of codemod work, with no agents involved, and Google’s large-scale-changes chapter explains why atomic changes shrink as codebases grow. Spotify now reports 650-plus agent PRs merged monthly on rails Backstage built years earlier.
Asana 在两个自然周内清理了积压数年的 Enzyme 任务,模型和基础设施成本约为 12,000 美元。这 12,000 美元只是 token 账单,不能替代他们账面上原有的五年人员配置估算;应将其视为供应商报告的生成成本,而不是一项受控的节省研究。可迁移的部分与 Bun 相同:范围狭窄的机械式迁移、预先存在的测试套件,以及人类仍在审查每一项变更Asana cleared a multi-year Enzyme backlog in two calendar weeks for about $12,000 in model and infrastructure cost. That $12,000 is just a token bill but not a substitute for the five-year staffing estimate they had on the books; treat it as a vendor-reported cost of generation, not a controlled savings study. The transferable part is the same as Bun: a narrow mechanical migration, a pre-existing suite, humans still reviewing every change
公司之间转移的是围绕代理的结构。What transfers between companies is the structure around the agents.
实际发生了什么变化What’s actually changed
代理降低了尝试多个合理实现方案的成本,但并未改变选择其中一个方案所需的证据。Agents have changed the price of trying several plausible implementations. They haven’t changed the evidence required to choose one.
然后我想你可能也已经注意到了,今年我们开始越来越多地看到一些颇具规模、发展成熟的公司使用智能体进行大规模重写。我曾与一些 CTO 交谈过,他们允许团队让智能体使用不同的语言或框架尝试多种重写方案,因为如今这样做的成本已经更低,也切实可行,并且可以评估其中的权衡。And then I think you’ve probably seen, this year we’re beginning to read more and more cases of well-established companies who are using agents to do big rewrites. I’ve talked to CTOs who are allowing teams to have agents try multiple rewrites in different languages or frameworks because its now feasible to do so more cheaply and evaluate the trade-offs.
Shopify 用 12 周时间,由一个小团队通过由代理审核的、以屏幕大小为单位的检查点,将 Shop 消费者应用从 React Native 重构为原生 Swift 和 Kotlin。规模大得多的商家应用仍然是棕地问题:数百个屏幕、深度的平台集成、相同的审核关卡,以及更长的周期。Shopify rebuilt the Shop consumer app from React Native to native Swift and Kotlin in twelve weeks with a small team and agent-gated, screen-sized checkpoints. The much larger merchant app is still the brownfield problem: hundreds of screens, deep platform integration, same gates, longer clock.
你已经看过其他重写为 Rust 的示例,也看过人们进行框架级迁移。各种各样的迁移都已经有人完成过。而且在很多情况下,这些迁移原本可能需要人们用更长的时间来完成。如今,只要你有足够的 token,就可以让代理实际尝试在一系列不同的技术栈或语言之间完成迁移。You’ve seen other examples of rewrites to Rust. You’ve seen people do framework-level migrations. There have been all kinds of migrations that have been done. And in many cases, these are migrations people would have done on a much longer timeframe. These days, if you have enough tokens, you can just actually have agents go and attempt to complete a migration across a range of different stacks or languages.
你可以尝试让你的代理在多个相互竞争的方案中实际实现某些东西。与其让一个团队选择一个方案并全力投入,不如让他们把所有方案都实现出来。它们都可以通过你的单元测试进行检查。你还可以对所有方案进行性能分析,然后再做决定;在某些情况下,这样的成本会比原本低得多。我认为,这对如今的团队来说完全是另一种局面。You can try to have your agents actually implement something in a number of different competing options. Rather than having one team choose a single option that you go all in on, what you do is you have them implement all of them. They can all check against your unit tests. You can performance profile all of them, and then make a decision, which is much, much cheaper in some cases than it otherwise would have been. And that’s a completely different ball game, I think, for teams these days.
最后并行化Parallelize last
生成的代码越多,就越应该进行更有选择性的人为审查,而不是减少人类的主导权。在开始考虑循环、目标和并行化之前,请认真思考哪些因素能为你的棕地项目的成功做好铺垫。More generated code should lead to more selective human review, not less human ownership. Seriously consider what will setup your brownfield project for success before you go down the path of thinking about the loops/goals/parallelization.
软件工厂可以同时运行许多变更。只有在某个单元具备可靠的评判机制、恢复路径和人们能够理解的审查格式之后,我才会复制那一部分。Software factories can run many changes at once. I would copy that part only after one unit has a dependable judge, recovery path, and review format people can absorb.
并行会放大你已经存在的瓶颈。自动化验证可以处理五个经过检查的更改,而由一位资深人员逐行审阅则会导致排队、注意力碎片化,最终沦为走过场式的批准。Parallelism multiplies the bottleneck you already have. Automated verification can handle five checked changes. One senior reading every line gets a queue, fragmented attention, and eventually ceremonial approval.
我更倾向于让自动化审查首先关注意图、变更的不变量、测试结果、对等性差异以及回滚路径。完整差异仍然可用。人工注意力首先应放在影响范围最大且判定依据最薄弱的部分。I prefer automated review to lead with intent, changed invariants, test results, parity mismatches, and the rollback route. The complete diff remains available. Human attention goes first to the largest blast radius and weakest oracle.
工作树隔离的是更改,而不是行为。它们可能共享 Git 元数据、凭据、本地服务和网络访问权限。受信任的工作或许可以接受这种权衡。处理不受信任内容的无人值守代理则需要更强的沙箱和权限范围受限的凭据。Worktrees isolate changes, not behavior. They may share Git metadata, credentials, local services, and network access. Trusted work may accept that tradeoff. Unattended agents consuming untrusted content need stronger sandboxes and scoped credentials.
智能体会为歧义定价Agents put a price on ambiguity
生成的代码行数并不能告诉你代码库是否有所改进。我会跟踪交付前置时间、代码审查时长、人工干预次数、遗漏到生产环境的缺陷、回滚次数、预期结果不匹配次数,以及遗留下来的抑制项。Lines generated don’t tell you whether the codebase improved. I would track lead time, review minutes, human interventions, escaped defects, rollbacks, oracle mismatches, and suppressions left behind.
对于迁移,请跟踪剩余的旧导入、新路径承载的流量、对等性不匹配项,以及已移除的旧版依赖。如果所有流量仍然走旧路径,即使测试套件全绿,也只是白忙一场。For a migration, track remaining old imports, traffic served by the new path, parity mismatches, and legacy dependencies removed. A green suite with all traffic still taking the old path is busywork.
代理会明确标示歧义的代价。团队惯例会反复出现在审查意见中。Agents put a visible price on ambiguity. Tribal conventions become recurring review comments.
这项成本一直存在,只是在入职、审查和事故恢复期间支付而已。代理会让其中更多部分变得可量化。这让我们更有理由开展团队早已知晓其价值的维护工作。That cost was always there, paid during onboarding, review, and incident recovery. Agents make more of it countable. That gives us a stronger argument for maintenance work teams already knew was valuable.
下次代理处理主页等效页面时,我希望它留下的不只是修复结果:还要有一条合成用户旅程、一份责任归属记录,以及一个回归测试。The next time an agent works on the homepage equivalent, I would want it to leave behind more than the repair: a synthetic user journey, an ownership record, and a regression test.
下一位工程师和代理所继承的内容也很重要。What the next engineer and agent inherits matters too.
感谢你一直读到这里的《Elevate》!希望它对你有所帮助。我本月开始了一份新工作,并将对通讯的赞助和呈现方式等方面进行调整,这些改动很快就会推出。希望能继续在这里为你带来有关智能体工程和软件的文章,也感谢你一直以来的阅读。Thank you for reading Elevate so far! I hope it’s been of value. I’m starting a new job this month and am making changes to the newsletter around aspects such as sponsorship and presentation which will be rolled out shortly. I hope to continue bringing you write-ups on agentic engineering and software here and want to thank you for continuing to read.
