这次迁移 AI 成本总估是 16.5 万美元。
这段披露不是客套,它决定了这个案例应该被如何解读,后面细说。
为什么迁移:不是性能,是稳定性
Bun 迁移到 Rust 的直接原因不是性能,而是稳定性。作者 Jarred Sumner 在文中列出了 v1.3.14 单个版本修复的 bug 样本:use-after-free、double-free、错误路径忘记释放、GC 对象与手动内存管理交叉引用导致的崩溃和泄漏。他的判断很坦率:不怪 Zig,是 Bun 这类项目的特殊性——JavaScript 引擎的垃圾回收内存和 Zig 的手动管理内存必须在同一套代码里交织,每一次分配都要人肉回答"这块内存谁负责释放、只释放一次吗、GC 扫描器看得见吗"。
Bun 原本已经做了不少防御:给 Zig 编译器打补丁加上 ASAN 并在每次提交时跑;用 Fuzzilli 全天候模糊测试;写了大量端到端内存泄漏测试。但有一个细节值得注意:安全检查完整的 ReleaseSafe 构建只在 Windows 上发布,macOS 和 Linux 上发布的是去掉了边界检查的 ReleaseFast。这是团队在性能和安全之间做的取舍,不是语言能力的缺失,Zig 提供了 ReleaseSafe,是 Bun 选择不在所有平台使用它。这个选择后面还会再出场一次。
问题在于所有这些反馈都来得太晚:fuzzing 发生在代码合并后,CI 发生在推送后,ASAN 发生在运行时。Rust 的价值在于把这一大类生命周期问题提前到编译期,用作者的原话概括就是,编译器错误是比风格指南更好的反馈回路。他也认真评估过替代方案:在 Zig 里自建智能指针(代码里确实加过 Rust 风格的 SharedPtr),或者迁到 C++。结论是自建智能指针"人体工学更差,且没有任何保证",而 C++ 依然依赖风格指南加 code review 的软约束。TigerBeetle 的 TigerStyle、Google 三万一千词的 C++ 风格指南,走的都是这条软约束路线,而软约束的困境永远是执行。
生产线是怎么搭起来的
更值得关注的是迁移方式。作者不是告诉 Claude 一句 Prompt "自由重写 Bun",而是设计了一套生产线,而且这条生产线的搭建顺序本身就是方法论。
写任何代码之前,他先花了约 3 个小时和 Claude 讨论如何把 Zig 模式贴近地映射到 Rust 模式,讨论结果固化成 PORTING.md。接着是最难的问题:如何给手动管理内存的代码标注 Rust 生命周期 ?他启动了一个专门的动态工作流,遍历每个文件里的每个结构体字段、追踪控制流、为复杂生命周期字段提出 Rust 表达方案,再由两个对抗评审 agent 审查该方案,最后固化成 LIFETIMES.tsv。然后对这两份文档本身再做一轮对抗评审,消除冲突建议,他自己也人工通读了一遍。
注意这个顺序:先冻结语义映射规则,再动手写代码。这两份文档就是这次迁移的 spec,不是需求文档意义上的 spec,而是"其他所有 Claude 必须遵循的语义契约"。
然后是试运行。在把 1,448 个 .zig 文件全部铺开之前,先只迁 3 个文件,跑通完整流水线:1 个实现者写 .rs 文件,2 个对抗评审者检查行为是否与 .zig 原文件一致、是否遵循两份文档,1 个修复者应用评审意见。流水线验证可行,才放量。
放量之后立刻翻车。全量启动约 2 分钟后,一个 Claude 跑了 git stash,另一个跑了 git stash pop,接着有 Claude 执行了 git reset HEAD --hard。它们在互相踩踏工作区。给每个 Claude 单独开 worktree 又会耗尽磁盘。解决方案是修改工作流规则:禁止 git stash、禁止 git reset、禁止任何不是"提交单个文件"的 git 命令,也禁止 cargo 等一切慢命令。之后拆成 4 个 worktree 分片,每个分片跑 16 个 Claude,峰值约 64 个 Claude 并行,峰值产出约每分钟 1,300 行代码。每一行都经过两个独立上下文的对抗评审和一轮修复才提交。
代码全部写完后,一切都还不能工作。接下来的每个阶段都是同一个循环结构的变体:把某类失败收集成文件形成工作队列,1 个实现者修、2 个评审者挑错、1 个修复者应用。编译错误按 crate 分组(拆解循环依赖后暴露出约 16,000 个编译错误,对一个人是天文数字,对 64 个 Claude 只是队列长度);子命令崩溃按 stacktrace 分组;测试失败按测试文件分组;CI 失败按平台分组。期间还有一次典型的目标劫持:Claude 把"让所有 crate 通过编译"理解成了"把报错的函数 stub 掉",并且开始写大段注释论证 workaround 的合理性。作者给对抗评审者加了一条规则:如果一个 workaround 需要一段话的注释来论证它没问题,那代码就是错的,去修代码。一次 prompt 修改,几个小时后这类行为消失。
连基础设施都在参与反馈。跑测试的机器上有耗尽 TCP socket 的压力测试、读写数 GB 磁盘的测试、spawn 上万进程的测试,光靠 prompt 约束不住,最后用 systemd-run 上了 cgroups 做内存、CPU 和 pid namespace 隔离。作者还忘了调大 EC2 实例的默认 IOPS,一条慢 grep 就能让磁盘读写冻结几分钟。commit 时间线上的空洞就是这么来的。这是个容易被忽略的教训:当执行速度不再是瓶颈,反馈基础设施的吞吐量就成了瓶颈。
数据
官方披露的数据很夸张:约 50 个动态 workflow,峰值 64 个 Claude 并行,11 天,6,778 个 commit(排除 merge 后 6,502 个,峰值一分钟 58 个),最终合并的 diff 超过 100 万行,0 个测试被跳过或删除。三大平台合计每轮跑约 6 万个测试、130 余万个断言,全绿后合并。合并前消耗约 5.9B uncached input tokens、690M output tokens、72B cached input token reads,按公开 API 价格约 16.5 万美元。
作者估计,传统方式需要 3 个对代码库有完整上下文的工程师干一年,期间几乎无法推进 Node.js 兼容性、bug 修复、安全修复和新功能。所以**现实中的替代方案不是"慢慢重写",而是"永远不重写,继续一个一个修文章开头那种 bug"**。
还有一个容易被忽略的细节:CI 全绿、作者人工确认测试确实在跑而非被跳过之后,他合并了,但没有发布。合并进 main 和发布版本之间隔着一段刻意保留的距离,v1.4.0 先进 canary 渠道。信心是分级建立的,这个分级由人掌握。
十九个回归:验证闭环的边界在哪里
这次迁移引入了 19 个已知回归,全部已修复。这个数字本身不重要,重要的是它们的共同来源:几乎全部来自"两种语言里语法看起来相同、语义却不同"的代码。
Zig 的 assert 是函数,参数在所有构建模式下都会求值;Rust 的 debug_assert! 是宏,release 构建下整个表达式被抹掉。一行把带副作用的调用放进断言的代码,机械翻译过去后语法几乎一样,但 release 构建下那个副作用(往热更新图里插入文件)静默消失了,HMR 在特定场景下坏掉。类似地:Zig 的字节切片重解释helper默默忽略末尾的奇数字节,Rust 的 bytemuck::cast_slice 遇到同样输入直接 panic;Zig 在 macOS/Linux 上用 ReleaseFast 去掉了边界检查,Rust 的 release 构建保留边界检查,于是一个从 Zig 原样移植过来的越界写,在 Zig 里静默通过,在 Rust 里变成 panic,前面提到的那个构建模式取舍,在这里第二次出场;Zig 的 comptime 格式串让颜色标记在参数替换前就被改写,Rust 没有 comptime 参数,同样的调用顺序颠倒了处理时机。
这组回归说明了两件事。
第一,即使有百万级断言的测试套件、双重对抗评审和全平台 CI,语义漂移依然有漏网之鱼。验证闭环有边界,它不是保险箱,是概率压缩器。
第二,漏过去的恰恰是测试覆盖之外的行为差异(release 构建路径、边缘输入、终端转义序列),这反过来定义了下一层防御该往哪加:Bun 合并后立刻上了 11 轮安全审查和 24/7 覆盖引导 fuzzing,后者对全部解析器累计执行了 1,000 亿次,产出约 15 个 PR。而且 fuzzer 发现 bug 后自动交给 Claude 提交复现和修复 PR,人类只审 PR。失败回流本身也被自动化了。
顺带一提一个和本系列此前判断相互印证的数据:迁移后的 Rust 代码约 4% 位于 unsafe 块内,其中 78% 是单行,来自 C++ 的指针、对 C 库的一次调用。unsafe 高度集中在 FFI 边界,这与 Rust 生态的普遍分布一致。因为 Bun 会继续使用 JavaScriptCore 和 BoringSSL 这些 C/C++ 库,这个比例不会归零,但作者预期随着从"忠实的 Zig 移植"重构为 idiomatic Rust,它会继续下降。
重新审视《人月神话》
这个案例迫使我们重新审视《人月神话》。
《人月神话》要解决的问题,不是"人多好不好",而是反对一种线性幻想:工作量等于人数乘以时间,所以增加人数就能压缩周期。Brooks 指出,软件不是可任意拆分的体力劳动。新人需要理解上下文,老人需要解释系统,模块之间需要协调接口,多人产出需要集成,最终还要保持概念完整性。这也是 Brooks 定律的来源:向已经延期的软件项目增加人手,只会让它更晚。
但 Bun 的案例说明,智能体时代确实改写了这个判断的一部分。64 个 Claude 不需要会议,不需要排期,不需要情绪协调,也不会因为缺少"组织归属感"而降低投入。它们可以 24 小时运行,可以并行修编译错误,可以同时做实现、评审和修复。传统人月模型里的某些人类协调成本,在这里被显著压低了。
所以不能再简单说:"多 agent 一定不行。"
真正的更新是:人月神话没有失效,但它的度量单位变了。
过去我们问:多少人,多少个月?
现在更应该问:这个任务能否被转化为可验证的工作流?有没有稳定语义参照?有没有机器可判定的反馈?能否拆分成互不踩踏的任务?每个输出是否经过独立评审?失败能否自动回流到修复队列?
Bun 迁移成功,不是因为"智能体数量多",而是因为它满足了几个罕见条件。
第一,原系统是语义源。Zig 版本就是 Rust 版本要保持的行为基准。迁移目标不是重新设计 Bun,而是尽量机械地把 Zig port 到 Rust。作者明确决定新代码要"看起来像把 Zig 转译成了 Rust",idiomatic 化留给 v1.4 发布之后。对比迁移前后的 canMergeSymbols 代码就能看出,读得懂 Zig 原文的人不需要重新学习就能维护 Rust 版本。这是为"团队迁移后还能维护它"服务的,不是审美选择。
第二,测试套件独立于实现语言。Bun 的测试主要用 TypeScript 编写,不依赖底层是 Zig 还是 Rust。这使得测试可以成为迁移后的行为仲裁者。这个条件比看起来苛刻得多,大多数项目的测试和实现深度耦合,换语言等于测试作废。
第三,反馈足够硬。编译器错误、测试失败、CI 失败、fuzzing 结果、stacktrace,都不是"感觉不对",而是可收集、可分组、可分发、可复现的工作项。整条生产线的每个阶段本质上都是"把某类硬反馈转换成工作队列"。
第四,评审被制度化。「实现智能体」不评审自己的代码。「评审智能体」在独立上下文中只拿到 diff,拿不到实现者的推理过程,并且被要求假设代码是错的。作者对此的观察很直白:写代码的 Claude 想让代码被合并,评审的 Claude 想找出问题,和人类一样,所以角色必须分离。博客展示了对抗评审在合并前实际抓住的 bug:libuv 异步 close 与 Rust Box 自动析构叠加导致的 use-after-free 加 double-free;负数时间戳用 trunc 拆秒和纳秒产生非法 timespec;unwrap_or 参数急切求值导致合法 CSS 输入直接 panic。三个都能通过编译,三个都看起来合理,三个都是靠"假设它是错的"这个立场找出来的。
第五,人类仍然掌握概念完整性。Jarred 没有把系统设计权交给 64 个 Claude。他决定全量迁移而非增量迁移,决定迁移风格是机械转译而非 idiomatic Rust,决定禁止哪些 git 命令,决定何时修改 workflow 的规则("段落长的注释即错误"就是他加的),决定合并,也决定合并不等于发布。整个过程中他的主要工作是监控工作流输出、人工抽读代码、以及在流程出问题时修改生成代码的流程而不是手修代码。
这正好回应《人月神话》的另一个核心概念:概念完整性。一个系统要像由一个头脑设计出来的。Bun 的案例表面上是 64 个智能体并行,实质上是一个强中心意图驱动的大规模执行系统。智能体放大了执行力,但没有替代系统意图。
这个案例的特殊性:它不是普通成本结构
在把这套方法论奉为模板之前,必须正视一个前提:Bun 作者做这件事时已经在 Anthropic 内部。文章开头明确披露,Bun 于 2025 年 12 月被 Anthropic 收购,团队在 Anthropic 工作,迁移使用的是预发布版本的 Claude Fable 5,一个当时外部拿不到的模型。
这意味着,这个案例不是一个普通团队按公开 API 价格调用公开模型完成迁移的案例。16.5 万美元是按 API 价格折算的参考数字,但真实约束与外部团队不同:他能使用预发布的更强模型;他处在模型厂商内部,遇到模型或工具问题时反馈链路更短;内部的 token 成本、额度和试错空间大概率与外部用户不同(这一点原文未披露,属于合理推断);Claude Code 的动态工作流能同时维持 64 个 Claude 运行 11 天,作者自己都说,没有这个能力他得手写一套 harness;而 Anthropic 对 Bun 的稳定性有直接的战略利益:Claude Code 本身跑在 Bun 上,迁移完成后 Claude Code v2.1.181 就切换到了 Rust 版 Bun。
所以这个案例证明的是:在顶级模型、充足资源、极强的工程上下文和高质量测试套件同时存在时,大规模受约束迁移可以被智能体显著压缩周期。它没有证明"任何团队都能用当前公开的 coding agent 低成本重写大型系统"。它更像一场前沿实验室级别的软件工程展示:给出方向,但不直接给出普遍 ROI。
为什么不能粗暴推广
这也解释了**为什么这个案例不能被推广为"以后大型重写都应该交给 agent"**。
Bun 做的是受约束的机械迁移,不是自由发挥式重写。它不是"顺便重新设计架构",不是"趁迁移重做产品语义",不是"边写边想要什么"。它保留架构、功能、性能目标和测试语义,只把语言和内存安全反馈机制换掉。真正危险的重写,通常是语义源不清、目标不稳、测试薄弱、架构顺手重画。智能体只会让这种混乱更快发生。
所以,Bun 案例对"agent 时代人月神话"的修正应该是:
执行力更接近可扩展资源,但验证闭环仍然不可省略,而且有边界。
在传统软件工程里,大型重写昂贵,是因为人类执行慢、沟通慢、集成慢。现在,在足够强的模型、足够大的 token 预算、足够好的测试套件和足够清晰的迁移目标下,原本经济上不可行的方案开始出现可行窗口。但新的瓶颈马上浮现:上下文如何封装(PORTING.md、LIFETIMES.tsv),任务如何分片(按 crate、按测试文件、按平台),智能体如何避免互相踩踏(git 命令白名单、cgroups 隔离),编译和测试如何承载反馈(错误分组成工作队列),生成代码如何被审查(双对抗评审加独立上下文),系统语义如何保持一致(人类总控加分级合并)。连磁盘 IOPS 都可能成为瓶颈。
这就是智能体软件工程真正的主题:**不是"让 AI 写更多代码",而是"把软件工作改造成可被智能体稳定消费、执行、验证和回流的生产系统"**。
从这个角度看,Bun 的 Rust 迁移是一次完整的软件工厂实验。编译器是仪表盘,测试套件是质检线,CI 是跨平台验收,fuzzing 是持续压力测试(现在连 bug 修复 PR 都自动生成),对抗评审是质量门,人工监控者是工艺工程师。智能体不是魔法程序员,而是生产线上的高吞吐执行单元。
《人月神话》提醒我们,不要把软件误解成线性劳动力问题。Bun 的案例提醒我们,也不要把智能体误解成线性数量问题。
未来真正重要的不是有多少工程师,也不是开多少 agent,而是组织能否建设这种能力:把需求变成规格, 把规格变成任务, 把任务变成可并行工作流, 把工作流接入编译器、测试、评审和运行时反馈, 再把失败反馈回流程本身。
Bun 的迁移给出的最终启示不是"重写变便宜了",而是:当模型能力、上下文工程、验证体系和资源预算同时到位时,软件工程的经济边界会发生变化。普通团队真正该学习的,不是 64 个 Claude 并行,而是 PORTING.md、LIFETIMES.tsv、对抗评审、测试闭环、CI 闭环、失败自动回流和人类总控这些工程结构,这些结构不依赖模型厂商的预发布模型,今天就能搭。
人月不再是主要度量单位。
新的单位是:可验证工作流的吞吐量。
感谢阅读。
Rewriting Bun in Rust: https://bun.com/blog/bun-in-rust/
夜雨聆风