AIEngineeringBun

史上最高调的AI重写:Claude花11天搞定Bun,创始人花一个月才敢交底

Tina··原文链接
收录于 2026/7/10 23:09:26

2026 年 5 月,Bun 项目完成了一次在软件开发史上近乎罕见的大规模代码迁移。这次迁移从 5 月 3 日启动,到 5 月 14 日正式合并入主分支,只用了 11 天,写代码仅 6 天,整个过程公开。但创始人 Jarred Sumner 写博客总结却花了快一个月,比写代码的时间长多了。

这个 JavaScript 运行时原本拥有 535,496 行 Zig 代码(不含注释),约 20% 由 C++ 编写,并嵌入了多个 C/C++ 库。此次借助 AI 重写为 Rust,涉及超过 100 万行代码变更、6778 次提交,在 Claude Code 中运行了约 50 个动态工作流(dynamic workflows)。

根据 Sumner 披露的数据,这次重写消耗了 59 亿个未缓存输入 token、6.9 亿个输出 token、720 亿个缓存输入 token 读取,按 API 定价约花费 16.5 万美元。他估计,如果让 3 名完全熟悉 Bun 代码库的工程师手工完成这次迁移,大约需要一年时间,且这一年里团队几乎无法推进新功能开发、bug 修复和安全修复。

成果:从 6.7GB 内存泄漏到 609MB 稳定

Bun 最初是一个 Zig 项目,覆盖面极广:既是 JavaScript/TypeScript 转译器,也是打包器、包管理器、测试运行器、模块解析器、HTTP/WebSocket 客户端,还实现了 Node.js API 层。这种产品宽度让 Bun 的 CLI 月下载量超过 2200 万次,并获得 Vercel、Railway、DigitalOcean、Claude Code 和 OpenCode 等项目或公司的支持。

但同样因为这种宽度,Bun v1.3.14 中有一个让团队头疼已久的问题:连续执行 Bun.build() 调用时,内存不断累积、永不释放。每次构建约泄漏 3MB,对于每次请求都触发构建的开发服务器来说,内存会被一点点吞噬直至进程崩溃。

内存泄漏增长曲线

实测数据:500 次构建后内存占用 1.9GB,1000 次后 3.5GB,1500 次后 5.1GB,2000 次后飙升至 6.7GB。这只是诸多内存问题的冰山一角。v1.3.14 的 bug 修复清单还包括:zlib 模块 .reset() 与异步 .write() 竞争导致"堆释放后使用"崩溃;http2 模块嵌套 JavaScript 回调触发哈希表重哈希致内部流指针失效;UDPSocket.sendMany() 在 valueOf/toString 回调改变连接状态时发生越界写入;crypto.scrypt 缓冲区分配失败时回调和密码缓冲区永远得不到释放。

这些 bug 几乎都指向同一个根源:将 GC 与手动内存管理在同一个软件里混合使用。JavaScriptCore(以及 V8)对异常处理和 GC 有极其严格的规则,而 Zig 像 C 语言一样不会自动管理内存。当两种范式在同一个进程中,每个内存分配都需要逐行审查:这些字节在哪里被释放?怎么确保只释放一次?这个被 GC 管理的指针对保守栈扫描器可见吗?

团队并非没有努力——他们修改了 Zig 编译器加了 Address Sanitizer 支持,CI 中每次提交都跑 ASAN 测试,Windows 上用 ReleaseSafe 构建,用 Fuzzilli 做 24/7 模糊测试,还有大量端到端内存泄漏测试。即便如此,崩溃报告仍然源源不断。

"我们的 bug 修复列表让人感觉很糟糕,我厌倦了带着对 Bun 崩溃的担忧去睡觉。" —— Sumner

而 Rust 版本交出的答卷是:同样执行 2000 次 Bun.build(),内存稳定在 609MB

除了内存泄漏根本性解决,Rust 重写还带来了其他改善:

  • 稳定性:v1.4.0 修复了 v1.3.14 中可复现的 128 个 bug
  • 体积:结合 Rust 重写、ICU 更改和代码折叠,Linux 和 Windows 二进制文件减少约 20%
  • 性能:普遍提升 2% 到 5%

二进制体积对比

具体性能数据:Bun.serve 从 16.96 万 req/s 提升到 17.77 万 req/s;node:http 从 10.38 万提升到 10.85 万;next build 从 13.62 秒降至 13.03 秒;tsc 批量编译从 0.94 秒降至 0.89 秒。Claude Code 在基于 Rust Bun 发布后,Linux 启动时间从 517ms 降至 464ms,快了约 10%。

性能对比数据

方法:64 个 Claude,11 天,50 个工作流

Sumner 的方法与传统"让 AI 写代码"不同。他把整个重写过程拆成约 50 个动态工作流,每个工作流都是一个循环:

工作流伪代码模式

每个任务都有一个上下文(如 Jira ticket 或 GitHub issue),Claude 基于上下文写出代码,然后两个审查者(也是 Claude)审查代码,最后应用反馈。完成之后取下一条任务。每个工作流负责一个特定目标:生成 porting guide、机械式移植 .zig 到 .rs、修复 crate 编译错误、让 subcommand 跑起来、让整个测试套件通过。

峰值时期,Sumner 同时运行了 4 个工作流,每个工作流里 16 个 Claude,总共 64 个 Claude 在 4 个工作树中并行工作,各自提交和推送文件。最高峰时 Claude 每分钟写约 1300 行代码。

这种"实现者/审查者"分离设计是关键。写代码的 Claude 想让代码被接受,和人类工程师一样有偏见。所以审查者和实现者完全分开——审查者只看代码差异,不看推理过程,且被明确告知"假设代码是错的"。每个实现者对应两个以上对抗性审查者,审查者的唯一工作就是找 bug。

实现者/审查者分离设计

代码写完只是第一步。Zig 代码是单一编译单元,而 Rust 要拆分成约 100 个 crate 来加快编译速度,循环依赖导致 cargo check 一次性输出约 16000 个编译错误。对一个人来说是灾难,但对 64 个并行 Claude 来说,这是可以处理的工作队列——按 crate 分组,每个 crate 跑一遍 cargo check,一个 Claude 修复,两个审查,一个应用修改。

接下来是让 bun --version 跑起来,然后是 bun test。测试工作流每次随机跑 100 个测试文件,分片到 4 个工作树。测试套件包含多种类型:有些运行超过一分钟,有些耗尽系统 TCP 连接数,有些 fork 约 10000 个进程。Sumner 用 systemd-run 创建 cgroup 限制资源,但机器还是因磁盘空间不足崩溃了好几次。

两天后,Linux 平台失败测试从 972 个降到 23 个。一天半后 Linux 全绿。五天后,全部六个平台(Linux x64/arm64、macOS x64/arm64、Windows x64/arm64)全部通过。5 月 14 日,PR #30412 正式合并,测试套件全部通过,没有跳过或删除任何测试。

PR #30412 合并

隐忧:13,000 个 unsafe 和无法逐行审查的代码

Sumner 承认这项工作还没有真正结束。Bun 的 Rust 代码中约 4% 位于 unsafe block 内,约 13,000 个 unsafe 关键字,分布在约 27,000 行代码中(Rust 总代码量约 780,000 行)。其中 78% 的 unsafe block 只有一行,通常是一个来自 C++ 的指针或对 C 库的调用。

unsafe 统计

对比参考:uv 约 35 万行代码只有 73 次 unsafe 调用,而 Bun 的 unsafe 数量是 uv 的 178 倍。随后还在安全 Rust 代码中暴露了未定义行为——这比 C++ 还难调试,因为你会以为安全代码不可能出问题。

PathString::init 改为 unsafe fn

Sumner 自己也承认,这次重写引入了 19 个已知回归问题,大多数源于语法相同但语义不同的代码。例如 Zig 的 assert 是一个函数,参数在每次构建中都会运行;而 Rust 的 debug_assert! 是一个宏,在发布版本中整个表达式(包括函数调用)都会被删除。

Zig assert vs Rust debug_assert 语义差异

代码审查也是绕不开的问题。100 万行的变更实际上没法由人类逐行看——就算一秒钟看一行,也要连续看 11.7 天;按实际代码审查速度(一小时 200 行),要两年多才能看完。这次 PR 的审查者主要是 claude[bot]coderabbitai[bot]。Sumner 承认他的审查方式是"检查对抗性审查 agent 是否正确捕获了差异,确保转换指南被遵守,同时自己也手动读了不少代码"——但"不少"是多少,他没说。

还有一个绕不开的问题:Bun 在 2025 年 12 月被 Anthropic 收购了,真正能有效维护这套代码库的工具基本只有 Claude 自己。社区里有人说,这已经算不上传统意义上的开源项目了——你想给 Bun 提 PR,得先订阅 Anthropic,或者指望那几个已经看懂了 AI 生成代码的核心成员。

16.5 万美元换一年工作量

16.5 万美元换一年工作量,值吗?

这次重写的 API 成本约 16.5 万美元,等于 3 名工程师一年的工作量。在 Hacker News 上引发激烈讨论。

有人认为这笔账很划算——16.5 万美元在硅谷请不了几个全职工程师,更不用说 Anthropic 这种级别公司的工程师。按 levels.fyi 数据,Anthropic 工程师总包很可能达到 50 万美元甚至更高。即便按 50 名工程师平均年薪 33.6 万美元粗略计算,折合到每天约 1292 美元,50 个人连续工作 11 天,光人力成本就接近 71 万美元,还不包括福利、办公场地、设备和管理开销。

但 Sumner 用的是"Claude Fable 5 的预发布版本",一个尚未对公众开放、可能受出口管制的高阶模型。API 定价只是最终用户看到的数字,背后是 Anthropic 投入的巨额研发费用。如果算上模型研发成本、训练成本、算力投入、工程人力等,相信最终总成本很可能超过 150 万美元。

成本分解

而且真正的成本不在这张账单上。这个代码库有 6778 次提交,没有一个人从头到尾完整读过。虽然眼下一切正常,可六个月后呢?当某个诡异的并发问题在凌晨三点突然冒出来,负责值班的工程师面对的是一个连他自己都说不清内部逻辑的系统。延伸到以后都得 AI 来维护,维护成本怎么算,其实挺难。

Anthropic 收购 Bun

工程启示

对于关注工程化实践和 AI 落地的工程师来说,这次重写有几个值得关注的点:

  1. 工作流编排而非单次生成:Sumner 没有让 AI 一次性重写,而是拆成 50 个动态工作流,每个工作流是"实现-审查-反馈"的闭环循环。这种模式将大规模重构变成了可管理的任务队列。

  2. 对抗性审查机制:实现者和审查者完全分离,审查者被明确告知"假设代码是错的"。这种设计利用了 LLM 在 code review 中寻找差异的能力,同时规避了实现者的自我确认偏见。

  3. 并行编译错误处理:16000 个编译错误对人类是灾难,但对 64 个并行 Claude 是工作队列。按 crate 分组、逐个修复的模式,是处理大规模迁移中编译错误的实用策略。

  4. 语义差异是最大风险:Zig 和 Rust 之间看似等价的语法(如 assert 函数 vs debug_assert! 宏)可能产生截然不同的行为。跨语言迁移中,语法相似性反而可能掩盖语义陷阱。

  5. 长期维护成本未知:百万行 AI 生成代码、1.3 万个 unsafe、Anthropic 收购后的工具锁定——这些都是技术决策之外的长期风险因子。

参考链接:

评分:
暂无0 人评分)