Bun の Rust 再構築から学ぶ AI の役割
Bun の創業者 Jarred Sumner が、メモリ安全性の欠如による安定性課題を克服するため、AI ツール Fable を活用して Zig から Rust への大規模リライティングを実施した背景とプロセスが明かされた。
キーポイント
Zig から Rust への移行理由
Bun は複雑な機能を持つプロジェクトだが、Zig のメモリ非安全性によりメモリリークやクラッシュが頻発し、GC と手動管理の混在によるライフタイム管理が課題となっていた。
AI ツール Fable による高速化
米国の輸出規制で一時的に利用制限があった AI 支援ツール「Fable」を活用し、Bun の大規模な言語移行を迅速かつ効率的に完了させた。
Bun の市場地位と重要性
月間ダウンロード数 2,200 万回を超え、Claude Code や Vercel などの主要プラットフォームやサービスで基盤として利用される重要なインフラとなっている。
技術的課題の克服と展望
メモリ安全性を担保しつつ高性能を維持できる Rust への移行により、安定性の大幅な向上が期待され、開発者の心理的負担も軽減された。
リファクタリングのリスクと従来の課題
完全な言語書き換えは通常、機能追加による遅延により見積もりより2倍以上の期間を要し、ユーザーへの影響がゼロになる1年間を費やす現実的な選択肢ではない。
AI を活用した効率的な書き換えアプローチ
Jarred はAnthropicのFableモデルを用いて、まず3時間にわたる詳細な対話でパターンマッピングを行い、600行に及ぶ「PORTING.md」という厳格なポートガイドを生成してから実装を開始した。
Rust 書き換えにおける技術的制約と対策
Bunの独自イベントループや非同期モデルを維持するため、tokioなどの外部ライブラリ禁止や非同期関数の不使用を定め、借用チェッカーの問題には生ポインタを使わずスカラー値へのキャプチャで対応するルールを設けた。
重要な引用
Our bugfix list felt bad and I was tired of going to sleep worrying about crashes in Bun.
For Bun, correctly handling the lifetimes of garbage-collected values and manually-managed values has been a major source of stability issues
A large percentage of bugs from memory-related issues occurred continuously.
Compiler errors are a better feedback loop than a style guide.
A year of zero user-facing impact is not a realistic option we could consider.
They were stepping on each other! And if I put each Claude into a separate worktree, I would run out of disk space because Bun's git repository is too big.
影響分析・編集コメントを表示
影響分析
このニュースは、AI ツールが単なるコード補完を超え、複雑な言語間の大規模リファクタリングやアーキテクチャ変更の主導権を握りつつあることを示す象徴的な事例です。特に、Zig のような高性能だがメモリ安全性に課題のある言語から Rust への移行を AI が加速させたことは、開発プロセスにおける AI の役割が「支援」から「共創・実行」へと進化していることを意味します。Bun という重要なインフラの安定化は、JavaScript/TypeScript エコシステム全体のパフォーマンスと信頼性向上に直結する重大な進展です。
編集コメント
Bun の創業者が AI ツール「Fable」を大規模な言語移行に活用した事例は、開発現場における AI の実用性が飛躍的に高まったことを示す画期的なケーススタディです。特に、輸出規制という政治的リスクさえも乗り越えて技術的課題を解決した点は、AI ツールの成熟度と信頼性を如実に物語っています。
こんにちは、ゲルゲーです。今回は『Pragmatic Engineer』ニュースレターの特別無料号をお届けします。
毎号、私はシニアエンジニアやエンジニアリングリーダーの視点から、ビッグテックとスタートアップの世界を解説しています。今回のテーマは、先週発行された「The Pulse」シリーズの 4 つトピックのうちの一つです。この記事を 7 日前に購読者向けに配信しました。もし転送されたメールをご覧になっている場合は、こちらから購読可能です。
先週サンフランシスコで、JavaScript ランタイム「Bun」の創設者である Jarred Sumner 氏にお会いし、Zig から Rust への書き換えについて詳しく伺いました。しかし当時、移行に使用されていたツール「Fable」が米国の輸出規制により利用できなくなっていたため、Jarred 氏はあまり詳細を明かしたがりませんでした。
image先週、Anthropic の本社で私と Jarred 氏
幸いにも現在は状況が解決し、Fable が世界中で使用可能になりました。また、Jarred 氏はプロジェクトの詳細を記した投稿も公開しています。
移行の議論に入る前に、背景をお伝えします。
Bun は非常に複雑なプロジェクトであり、多くのプロダクションソフトウェアがこの Bun に依存しています。Bun 自体が担っている機能は多岐にわたります:
- JavaScript、TypeScript、CSS のトランスパイル、ミニフィケーション、バンドリング
- テストランナー
- パッケージマネージャー(npm と互換性あり)
- その他、モジュール解決、WebSocket クライアント、Node.js 実装、多数の標準モジュールなど
現在、Bun の月間ダウンロード数は 2,200 万回に達しており、Claude Code や OpenCode といったソフトウェアがこれに依存しています。また、Vercel、Railway、DigitalOcean などのホスティングプロバイダーも、Bun を公式にサポートする体制を整えています。
なぜ書き換えが必要だったのか?
Zig はメモリエラーを完全に防げる言語ではありません。実際、メモリ関連のバグが頻発していました。Jarred氏が Bun の最新バージョンで報告したメモリ関連のバグには、メモリリークやメモリ問題によるクラッシュ、ヒープ外への書き込みなどが含まれています。
これは、Bun チームが Zig コンパイラをパッチ適用してメモリ関連の問題を減らし、エンドツーエンドのメモリリークテストを導入した後の話です。Jarred氏はこう述べています。
「バグ修正リストを見ていると気分が沈み、Bun のクラッシュを心配しながら眠る日々が続いていました。これを Zig のせいにするつもりはありません。Zig を使う他のユーザーには私たちが直面したようなバグは発生していないからです。GC(ガベージコレクション)と手動管理メモリを併用するのは非常に稀なケースであり、言語側がそのために設計されている例もありません。
Bun においては、ガベージコレクションされる値と手動管理される値のライフサイクルを正しく扱うことが、安定性の大きな課題となっていました。主な原因は小さなメモリリークで、まれにクラッシュを引き起こすこともあります。すべてのメモリアロケーションを慎重に見直す必要があります。これらのバイトはどこで解放されるのか?一度だけ確実に解放されるようにどう保証するか?JavaScript の例外処理は適切に行われたか?このガベージコレクションされたポインタは、保守的なスタックスキャナから見える状態にあるか?これはガベージコレクション対象のメモリなのか、手動管理のメモリなのか?」
メモリ安全性を確保しつつ高性能を発揮できる言語へ移行すれば、こうしたバグを排除できます。Rust はまさにその条件を満たす言語の一つです。Jarred 氏は次のように述べています。
「上記のバグリストの大部分は、使用後の解放(use-after-free)、二重解放(double-free)、エラーパスでの解放忘れが原因です。安全な Rust ではこれらはコンパイラのエラーとなり、Drop による RAII 風の自動クリーンアップが行われます。スタイルガイドよりも、コンパイラからのフィードバックループの方がはるかに優れています。」
しかし、Rust への完全書き換えは従来から最悪のアイデアとされてきました。少なくとも以前はそうでした。なぜなら、その所要時間が耐えられないほど長くなってしまうからです。
書き換えには二つの問題があります。一つ目は時間がかかりすぎる点、二つ目はそれ以上に時間がかかりすぎることです。書き換えを経験した開発者なら、事態の推移がどうなるかよく知っているはずです。
まず、どれくらいかかるか適当な推測を立てます。例えば「9 ヶ月」とします。
9 ヶ月後になっても、まだ約 6 ヶ月はかかりそうです。なぜなら、元のコードベースに新機能が追加され、それを新しい実装にも反映させる必要があるからです。
15 ヶ月が経過しても、同じ理由でまだ数ヶ月は残っています。
結局のところ、運が良ければ「機能凍結(feature freeze)」を 2 ヶ月間設けて、書き換えを約 18 ヶ月で完了させます。当初の 9 ヶ月の見積もりが、実際には 2 年以上かかることも珍しくありません。
Jarred 氏は、Bun を Zig で書き換えることをこれに例えています。
「歴史的に見て、書き換えは最悪のアイデアです。コメントを除くと、Bun の Zig 版は 535,496 ラインになります。
別の言語への書き換えには、小規模なエンジニアチームで丸 1 年かかります。」
ユーザーへの影響をゼロにするという選択肢は、現実的ではありません。そこで、安定性問題を解決するためにコードスタイルの強化で対応するのが最善策であり、Bun のコードベースに Rust 由来のス마트 포인터(スマートポインタ)を追加した際の計画でもありました。
しかし正直なところ、私はやりたくなかったのです。独自開発のスマートポインタは Rust よりも使い勝手が劣り、Rust が持つ保証さえ得られないからです。
「それなら、Anthropic の新モデル『Fable』を使って 1 週間かけて Bun を Rust で書き直せるか試してみよう」
Fable による Bun の書き換え
予想通り、Bun の書き換えは「Claude さん、Bun を Rust に書き換えて。間違いゼロで」とプロンプトを入力するだけでは実現しませんでした。Jarred は実際には以下のような手順で進めました。
ステップ 1:準備作業。Jarred によると、これは Claude との 3 時間にわたる集中した議論でした。
「コードを書く前に、Zig のコードベースにあるパターンを Rust にどうマッピングするかについて、Claude と約 3 時間話し合いました。Claude はその議論を『PORTING.md』というドキュメントにまとめ、これが Hacker News で話題になりました(Zig → Rust ポーティングガイド)」
このガイドは 600 行ほどのファイルで、以下のような指示が記載されています。
基本ルール:
- tokio、rayon、hyper、async-trait、futures は使用しない。std::fs、std::net、std::process も禁止。Bun のイベントループとシステムコールは Bun 自身が管理する。(Rust コア/標準ライブラリのスライス、イテレータ、メモリ操作、フォーマット、core::ffi は問題なし。I/O を扱うモジュールのみが禁止対象。)
- async fn(非同期関数)は使わない。すべてコールバックと状態機械で構成し、Zig と同じアプローチとする。
必ず JSON 形式で返してください。translation フィールドのみ。他のフィールドは一切追加しないこと — 余計なフィールドを書こうとして本文がトークン上限で打ち切られる事故を防ぐため:
{"translation": "リライト全文"}
借用チェッカーの制約を回避するためのリシェイプは許可されています。Zig のフローで重複する &mut 参照が発生した場合は、必要なスカラー値(.len() やインデックスなど)をローカル変数にキャプチャし、一旦借用を終了させた上で再借用してください。借用チェッカーの警告を黙らせるために安易に生ポインタ(raw pointers)を使うのは避けてください。
// PORT NOTE: 借用チェッカー対応のためにリシェイプ済みです。Phase B の差分リーダーが混乱しないように注意してください。
これは、Rust に精通した人にとっては意味が通じる一連の指示です。より深く学びたい場合は、Alice Ryhl氏との対談で Rust の基本や、なぜ Rust が他言語と異なるのかについて解説しています。
ステップ 2:試運転と敵対的レビューの実施
全ファイル数 1,448 件中、Claude に 3 ファイルの書き換えを依頼しました。書き換え後、Jarred 氏は Claude が変更を加えたセッションとは別に、Claude を用いた 2 つの独立した「敵対的レビュー」セッションを実施し、結果を検証・批判させました。
ステップ 3:作業を 64 個の AI エージェントに分散
Jarred 氏は、各エージェントが互いに干渉せず並列処理できるよう、作業ファイルを分割して割り当てました。
ステップ 4:実行時の問題解消(約 1 日)
実際にこれらを実行しようとした際、エージェント同士が互いの邪魔をしていました。
「Claude に全 1,448 個の .zig ファイルに対してワークフローをループさせようとしたのですが、開始から約 2 分後に、ある Claude がコミット前に git stash を実行し、別の Claude は git stash pop を実行。さらに別のプロセスでは git reset HEAD --hard が走っていました。まるで互いに踏みにじり合っている状態です!
各 Claude を別々のワークツリーに隔離すれば解決するかもしれませんが、Bun の Git リポジトリが巨大すぎるためディスク容量がすぐに不足してしまいます。また、最終的にはすべての変更をまとめてコンパイル・確認する必要があるので、完全に分けるわけにはいきません。」
そこで、Claude にワークフローを編集させました。具体的には、「特定のファイルを一度にコミットしない git コマンド(git stash や git reset など)や cargo コマンドは絶対に実行しない」という指示です。遅いコマンドも一切禁止しました。
すると Claude は再び作業を開始し、実際に機能するようになりました。ただし速度が遅すぎるため、ワークフローを 4 つのシャードに分割しました。それぞれが独立した worktree を持つ構成で、合計 4 つの worktree で、それぞれ 16 台の Claude が並列動作し、ファイルをコミット・プッシュする仕組みです。
ステップ 5:実行して約 2 日間待ちます。並列エージェントたちが動き出し、2 日間で Zig コード 535,496 ラインの書き換えを完了しました。各コミットは、2 つの敵対的なレビュー(adversarial review)を経た後でないと確定されません。
ステップ 7:約 1,600 のコンパイルエラーを修正(約 12 時間)。書き換え自体は完了しましたが、まだ何もコンパイルできません。Jarred は「crate」ごとに Claude にエラー修正を命じました。「crate」とは Rust におけるトップレベルのコンパイル単位のことです。この作業は人間エンジニアにとっては途方もないタスクですが、Claude には問題ありません。
「循環依存関係を解消した結果、約 16,000 のコンパイルエラーが見つかりました。人間 1 人にとっては膨大な数ですが、同時に 64 台の Claude を動かせば決して異常な数ではありません。
並列性を最大化するため、ワークフローは各 crate を順にループします。
- 各 crate に対して
cargo checkを実行し、出力をファイル単位でグループ化してエラーを保存する - その crate 内のすべてのコンパイルエラーを修正する
- 変更内容について 2 人の敵対的なレビューアーがチェックする
- 1 名の修正担当者が実際に修正を適用する」
image エージェントが一つずつエラーを修正していく様子を可視化したもの。出典:Anthropic
この移行フェーズで最も楽しかったのは、エージェントたちが深夜から午前 11 時 30 分まで稼働し、Jarred とチームが睡眠をとっている間に自らコンパイラーのバグを修正していた点です。
ステップ 8: ローカルでテストを実行する(約 2 日間)。Bun には大規模なテストスイートが存在します。次の目標は、これらのテストをコンパイルエラーなしで実行できるようにすることでした。
ステップ 9: CI でテストスイートをパスさせる(約 3 日間)。テストが実行可能になり(そして失敗し始めた)段階から、次はテストに合格するようコードを修正しました。これには 2 日かかりました。
ステップ 10: 完了!わずか 11 日で達成。すべてのテストがパスし、Jarred が期待通り動作することを確認した時点で変更をマージしました。計画から完了まで、全体で 11 日間でした。
image リライトの概要:約 55 万行のコードを、6,500 コミット、64 エージェントを使って 11 日間で移植
このプロセスは再現可能でしょうか?
API 利用料を含めると、この書き換えにはなんと 165,000 ドルもの費用がかかりました。Fable の API 価格体系によると、キャッシュ未ヒットの入力トークンが 59 億個、出力トークンが 6.9 億個、そしてキャッシュされた入力トークンの読み込みが 720 億回に達しています。Anthropic は API トークンを販売して収益を得ているため、同社にとっての書き換えコストはさらに低くなります。しかしそれでも膨大な金額です。これは米国のミドルティア企業におけるソフトウェアエンジニアの年収と同等の額になります。
では、もし人間がこれだけの作業を 1 年かけて行えたでしょうか?おそらく不可能でしょう。Mitchell Hashimoto も同様の見解を示しています。
「コストについてですが、Fable の API 利用料で 165,000 ドル(未確認)という価格は驚くほどお得だと思います。その程度の給与のエンジニアが、Claude が 11 日間で達成したマイルストーンを成し遂げることは絶対にあり得ません。たとえ N 人のエンジニアに合計 165,000 ドルの報酬を支払って 11 日間働かせたとしても、コスト対効果は合いません。
ただし、これは私の持論を再確認させる結果でもあります。特に Fable は、明確な報酬関数を持つ困難で集中度の高いタスクにおいて極めて優秀です。最近もその点についてツイートしています。」
もし AI によって、以前なら検討すらされなかったような書き換えや移行が可能になるならどうでしょうか?Jarred も認めるように、AI を使わずに Bun を Rust で書き換えるというアイデアは非現実的でした。
「手作業で行うなら、コードベースの全体像を理解したエンジニア 3 人が約 1 年を要するでしょう。その間、Node.js の互換性向上やバグ・セキュリティ対策の新機能実装は不可能です。決して実現しなかったはずです。現実的な選択肢は、何もせずこの投稿で挙げたバグと永遠に格闘し続けることでした。」
数ヶ月から数年を要する書き換えや移行が、こうしたプロジェクトが実現しない理由の多くです。コストの問題はひとまず脇に置き、こう考えてみてください。「AI が 1 年かかる書き換えを 1 週間に短縮できるなら、実行しますか?」
答えが「もちろんやる」という場合、Bun の移行事例がそのための青写真として既に存在します。ただし、記事には明記されていないいくつかの注意点があります。
- コードベースに精通し、非常に意欲的なエンジニアが必要であること
- 極めて堅牢なテストスイートが存在すること。これでテストが通れば、動作していることを確信できる状態であること
- 結果がどうなるか分からないまま、トークン消費への多額の投資をいとわない覚悟があること
公平に言えば、3 つ目の条件が最も脆弱な点です。なぜなら、コード移行のような「地味な作業」において LLM はすでに高い能力を発揮することが分かっているからです。堅牢なテストスイート(2 番目)と、細部まで手直しできる意欲的なエンジニア(1 番目)さえ揃っていれば、成功する可能性は極めて高いと言えます。
残るは、実際にどれほどのコストがかかるかという点です。おそらく 16.5 万ドルには達しないでしょう。プロジェクトをシンプルに設計したり、モデルの利用方法を工夫することで、さらにコストを抑えることも可能です。例えば、高レベルの計画策定には最も高価なモデルを使い、コーディングやレビューといったタスクには安価なモデルを活用するといった使い分けが有効です。
AI を活用した移行は確実に加速していますが、それは Bun のようにプロジェクト自体が十分に設計・構築されている場合にのみ成立します。
この抜粋が含まれる『The Pulse』の最新号をぜひご一読ください。あるいは、本日発行された最新の『The Pulse』もチェックしてみてください。本号では以下のトピックを取り上げています。
- Grok の CLI がローカルファイルをすべてクラウドへアップロードし、その挙動が露見した件
- 新たなトレンド:コードレビューの負荷が急増することへの懸念
- エンタープライズ開発者らは AI ラボの企業向け価格設定に不満を抱いているのか?そしてそれは重要なのか?
- Linux の生みの親であるリヌス・トーバルズ氏:AI は「明らかに有用」だと語る
最新号はこちらで読むことができます
原文を表示
Hi, this is Gergely with a bonus, free issue of the Pragmatic Engineer Newsletter. In every issue, I cover Big Tech and startups through the lens of senior engineers and engineering leaders. Today, we cover one out of four topics of last week's The Pulse issue. Full subscribers received the article below seven days ago. If you’ve been forwarded this email, you can subscribe here.
Last week in San Francisco, I met Jarred Sumner, creator of JavaScript runtime, Bun, and was keen to learn more about the rewrite of Bun from Zig to Rust. But at the time, Jarred didn’t want to say too much, as the tool used for the migration, Fable, was out of action due to the US government imposing export controls.
imageJarred and I at Anthropic’s HQ, last week
Fortunately, the situation is now resolved and Fable is available globally, and Jarred has published a detailed post about the project. Before we get into the migration, some context:
Bun is a complex project, with lots of production software depending on it. Bun itself does many things:
JavaScript, TypeScript and CSS transpiling, minifying and bundling
A test runner
A package manager (npm-compatible)
Other things: module resolution, a WebSocket client, Node.js implementations and many modules
Today, Bun has 22 million monthly downloads, and software like Claude Code and OpenCode depend on it, while hosting providers like Vercel, Railway and DigitalOcean do first-party support for Bun.
Why a rewrite?
Zig is not a memory safe language, and memory-related bugs occurred continuously. Jarred lists memory-related bugs in the latest version of Bun: memory leaks, crashes due to memory issues, heap-out-of-bounds writes, and so on. This was after the Bun team patched the Zig compiler to reduce memory-related issues, and put end-to-end memory leak tests in place. As Jarred says:
“Our bugfix list felt bad and I was tired of going to sleep worrying about crashes in Bun. I don't blame Zig for that - other users of Zig don't have the bugs we had, and mixing GC with manually-managed memory is an uncommon enough thing for software to need that no language really designs for it. (...)
For Bun, correctly handling the lifetimes of garbage-collected values and manually-managed values has been a major source of stability issues - most often small memory leaks and occasionally crashes. Every memory allocation has to be meticulously reviewed. Where do these bytes get freed? How do we ensure it only gets freed once? Did we check for JavaScript exceptions properly? Is this garbage-collected pointer visible to the conservative stack scanner? Is this garbage collected memory or manually managed memory?”
Moving to a memory-safe, yet performant language could eliminate such errors, and Rust is one such language that fitted the bill. Jarred:
“A large percentage of bugs from that list are use-after-free, double-free, and "forgot to free" in an error path. In safe Rust, these are compiler errors and RAII-like automatic cleanup with Drop. Compiler errors are a better feedback loop than a style guide.”
However, doing a full rewrite on Rust has always been a terrible idea. Or at least, it used to be, because of how unbearably long it would have taken:
There are two problems with rewrites: they take too long, and they take waaaay too long. A dev who has done rewrites probably knows how things tend to go:
Make an educated guess about how long it will take; say, nine months.
Nine months later, there’s still another ~6 months to go because new functionality is added to the original codebase, and now that new functionality needs to be added in!
By 15 months in, there’s still months left to go for the same reason!
In the end, you manage to mandate a “feature freeze” for two months and finish the rewrite in ~18 months, if lucky. The original nine-month estimate can end up taking 2+ years.
Jarred likened rewriting Bun in Zig to this:
“Historically, rewrites are a terrible idea. Excluding comments, Bun is 535,496 lines of Zig.
A rewrite in another language would take a small team of engineers a full year.
A year of zero user-facing impact is not a realistic option we could consider. So, enforcement through code-style to fix stability issues was our best bet, and was our plan when we added Rust-inspired smart pointers to Bun's codebase.
But honestly, I didn't want to do it. Homegrown smart pointers offer worse ergonomics than Rust, with none of the guarantees.
What if, instead, I spend a week testing if Anthropic's new model [Fable] can rewrite Bun in Rust?”
Rewriting Bun with Fable
Unsurprisingly, the rewrite was not as simple as typing a prompt like: “Claude, rewrite Bun in Rust. Make zero mistakes.” Instead, this is how Jarred did it:
Step #1: Prep work. Three hours of intense prep work with Claude, explained Jarred:
“Before writing any code, I spent about 3 hours talking to Claude about how to map patterns from our Zig codebase closely to Rust. Claude serialized this discussion into a PORTING.md document, which ended up on Hacker News [as the Zig → Rust porting guide]”
This guide is a 600-line file with instructions like:
Ground rules:
No tokio, rayon, hyper, async-trait, futures. No std::fs, std::net, std::process. Bun owns its event loop and syscalls. (Rust core/std slice, iter, mem, fmt, and core::ffi are fine — only the I/O-touching modules are banned.)
No async fn. Everything is callbacks + state machines, same as the Zig.
Borrow-checker reshaping is allowed. When matching Zig flow yields overlapping &mut, capture the needed scalar (.len(), index) into a local, drop the borrow, then re-borrow. Do NOT reach for raw pointers just to silence borrowck; leave // PORT NOTE: reshaped for borrowck so Phase B diff readers aren't confused.
It’s a series of instructions that makes sense to someone who’s expert in Rust. If you want to learn more, we cover Rust basics and why Rust is different, with Alice Ryhl.
Step #2: Trial run + adversarial review. Asking Claude to rewrite three files out of 1,448 total number of files. After the rewrite, Jarred ran two separate adversarial reviews with Claude to critique the result, in separate sessions than the one that Claude made the changes in.
Step #3: split up the work across 64 AI agents. Jarred split up the job so that agents worked on files independent from one another, in parallel.
Step #4: iron out issues with the run (~1 day). When Jarred attempted to run all this, agents kept getting in each other’s way:
“I asked Claude to loop the workflow on all 1,448 .zig files, and about 2 minutes in, one Claude ran git stash before committing. Another ran git stash pop. And then git reset HEAD --hard. They were stepping on each other! And if I put each Claude into a separate worktree, I would run out of disk space because Bun's git repository is too big and eventually the changes will need to be compiled and seen together.
So, I asked Claude to edit the workflow to instruct Claude to never run git stash or git reset or any git command that doesn't commit a specific file at once. No cargo either. No slow commands at all.
Then, Claude resumed the workflows. And it was working! Too slowly, so I split it into just 4 workflow shards each with their own worktree (4 worktrees total), each running 16 Claudes committing and pushing files.”
Step #5: have it run and wait ~2 days. The parallel agents went to work, and completed the rewrite of 535,496 lines of Zig code over the course of two days. Each commit was checked by two adversarial reviews, before being committed.
Step #7: fix ~1,600 compiler errors (~12 hours). The rewrite was completed, but nothing compiled. Going crate-by-crate (‘crate’ is Rust’s concept of a top-level compilation unit), Jarred had Claude fix compiler errors. This alone would be a herculean task for an engineer, but not for Claude:
“Fixing the cyclical dependencies revealed about 16,000 compiler errors. A massive number for 1 human, but not a crazy number for 64 Claude’s at once.
To maximize parallelism, the workflow looped over each crate.
For each crate, run cargo check, group the output by file and save the errors to a file
Fix all the compiler errors within that crate
2 adversarial reviewers for the crate's changes
1 fixer applies the fixes”
imageVisualizing fixing of errors, one by one, done by the agents. Source: Anthropic
The enjoyable thing about this phase of the migration was that the agents ran from midnight until 11:30am, fixing compiler bugs on their own – when Jarred and the team were getting some sleep.
Step #8: run tests locally (~2 days). Bun has a large test suite. The next step was to get these tests to run without compilation errors.
Step #9: get the test suite to pass CI (~3 days). Once the tests were running (and failing), the next step was to fix the code, so that the tests could pass. This took two days.
Step #10: Done in 11 days! After all the tests passed and Jarred verified that everything worked as expected, he merged the changes. The whole process took 11 days, from planning to the finish.
imageThe rewrite: porting ~550K lines of code, in 6,500 commits, over 11 days, with 64 agents
How repeatable is this process?
The rewrite cost a whopping $165,000 with API pricing. With Fable’s API prices, the rewrite consumed 5.9 billion uncached input tokens, 690 million output tokens, and 72 billion cached input token reads. Anthropic sells API tokens at a margin as its business, so the cost of the rewrite for it was lower. It’s a large amount: the equivalent of the annual base salary for a software engineer at a mid-tier company in the US!
But then again, could have an engineer done all this work in a year? Probably not, and Mitchell Hashimoto says the same:
“On the cost, I think $165,000 at API pricing for Fable (didn’t verify) is an incredible deal. There’s absolutely no way an engineer with that salary would’ve been able to achieve the milestones Claude did in 11 days. No way. (Even if you break it down to N engineers paid $165K total in 11 days it doesn’t math out)
This does, however, also reconfirm my own biases which is that Fable in particular is most excellent at hard, focused tasks with clear reward functions. I’ve been tweeting about this recently.”
What if AI enables rewrites and migrations that wouldn’t have been considered before? The idea of rewriting Bun in Rust without AI was impractical, admits Jarred:
“By hand, I think this would've taken three engineers with full context on the codebase about a year, during which time we wouldn't be able to improve Node.js compatibility, fix bugs, fix security issues or implement new features. We never would've done that. The realistic alternative was to do nothing and keep fixing the bugs at the top of this post forever.”
A rewrite or migration taking months or years is why so many of these projects never happen. Let’s take aside the cost for a minute and consider this question: if AI can shorten a one-year rewrite to a week: would you do it?
If the answer is “hell, yes:” a blueprint now exists in the form of the Bun migration on how to do it. There are some caveats not detailed in the post, though:
You need an engineer who is very motivated and knows the codebase very well
You need an extremely robust test suite, so when the test suite passes, you know it works
You need to be willing to invest a lot in tokens, not knowing how well it all will work
In fairness, #3 is the weakest point because we know LLMs are pretty good at “mundane” work like code migrations. With a good test suite (#2) and a motivated engineer to iron out things (#1), you’ll more likely than not succeed.
The remaining question is how much can be spent. It will likely not be $165K: and costs can be reduced with a simpler project, or by being thoughtful about model usage. For example, do high-level planning with the most expensive model, and cheaper ones for coding and review tasks.
Migrations with AI are surely speeding up, but only when projects are well-engineered like Bun’s has been.
Read the full issue of The Pulse this excerpt is from, or check out the latest The Pulse from today. Today’s issue covers:
Grok’s CLI uploaded all your local files to the cloud, then got caught.
New trend: concern about massive increase in code review load.
Are more devs at enterprises upset about enterprise pricing by AI labs – and does it matter?
Linux creator: AI “clearly useful.”
Read the full issue here
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み