Skip to content
Published on

BunのZigからRustへの書き換え11日間 — AI大規模マイグレーションで実際に持ち帰れるもの

シェア
Authors

はじめに — 書き換えより記事を書くほうに時間がかかったプロジェクト

2026年7月8日、Bunが Rewriting Bun in Rust を公開しました。作業自体は5月3日に始まり5月14日にメインブランチへマージされているので、Jarred Sumnerは11日で終えた仕事をまとめるのに2か月近くを使ったことになります。Simon Willisonが 自身のブログで 「5月9日から約束されてきた記事」だと指摘しているのも同じ話です。

数字はもう十分に出回りました — コメントを除いた53万5,496行のZig、1,448個の .zig ファイル、6,502個のコミット、最大64個の同時Claudeインスタンス、API定価換算で約16万5千ドル。これらの数字は引用しやすいのですが、持ち帰れるものではありません。あなたのチームが来年レガシーサービスを移すときに使えるのは「11日」ではなく、11日が成立するために事前に揃っていなければならなかった条件のほうです。

そしてその条件は思ったより厳しいものです。この記事はBunの発表、Anthropicの方法論のまとめ、Zig作者 Andrew Kelleyの反論、そしてマージ6週間後の実際のリポジトリの状態を並べて置き、このアプローチがどこで成立しどこで崩れるのかを見ます。結論から言うと、オラクルとなるテストスイートがある書き換えは、マイグレーションの中でいちばん簡単なケースです。その事実を認めるところから始めて、はじめて残りが役に立ちます。

数字で見る11日間

Bunの発表文とAnthropicのまとめに載った数値を一つの表に集めました。二つの文書が同じ値を述べている項目だけを載せ、食い違う項目は下の本文で別に取り上げます。

項目
期間2026-05-03 〜 05-14(11日間)、全プラットフォームのグリーンはビルド #54202
元の規模Zig 535,496行(コメント除く)、1,448個のファイル、このほか約20パーセントがC++
最終ディフ1,009,272行の追加、コミット6,502個(マージ込みで6,778個)
並列度ピーク64インスタンス(ワークフロー4個 × 16)、全体で約50個の動的ワークフロー
モデルプレリリース版のClaude Fable 5、レビューとルール生成にOpus 4.8
トークン非キャッシュ入力59億、出力6.9億、キャッシュ入力の読み取り720億
コストAPI定価換算で約16万5千ドル
テスト削除・スキップされたテスト0個、6プラットフォーム、1プラットフォームあたり5.7万〜6万個のテスト
unsafeの割合約78万行のうちunsafeブロックが約2万7千行(約4パーセント)、うち78パーセントが1行
マージ後の回帰判明しているもの19件、すべて修正済み
性能HTTPスループット2.8〜4.8パーセント向上、バイナリ約20パーセント縮小

性能の項目がこの表でいちばん静かな行です。16万5千ドルを使って得たものがスループット数パーセントなら、それ自体は投資の根拠になりません。Bunが実際に買ったのは速度ではなくメモリ安全性でした — Anthropicのまとめに引用されたリーク計測では、2,000回ビルドしたあとの累積メモリが6,745MBから609MBへ下がっています(Bunの発表文は同じ出来事を、ビルドあたり3.3MBのリークが消えたという別の書き方で説明しています。二つの文書の表現が食い違うため、この数値は単一ソースとして扱うほうが安全です)。ユーザーが体感した変化はLinuxのp50で起動時間が517msから464msへ10パーセント速くなった程度で、Willisonの表現どおり「退屈なのが良い」結果でした。

このコードベースがとりわけ移しやすかった三つの条件

第一に、言語に依存しないオラクルがすでにありました。BunのテストスイートはTypeScriptで書かれています。ランタイムをZigで書こうとRustで書こうと、テストコードは一文字も変わりません。1プラットフォームあたり5万7千個を超えるテストと100万個以上のアサーションが、移植が終わったかどうかを人の判断なしで判定してくれます。削除もスキップもされたテストが0個だという事実が重要な理由はここにあります — 審判に手を入れていないという意味だからです。

これがどれほど珍しい条件かは、逆の例を思い浮かべれば分かります。テストが内部実装に貼り付いているコードベース(モジュール内部の関数を直接呼ぶテストや、プライベートな状態を検査するテスト)は、言語を変えた瞬間にテストも一緒に捨てることになります。審判が消えたマイグレーションは、ただの大規模な無検証コミットです。Anthropicの文書が六段階のうち0段階目として「まず審判(the judge)を立てよ」と釘を刺し、わざと壊したコードで審判を回して、まず落ちることを確認せよ、と書いているのはこのためです。

第二に、構造を保存する翻訳でした。1,448個の .zig ファイルが同じ数の .rs ファイルになりました。アーキテクチャを設計し直していないので作業単位がファイル一つに落ち、ファイル間の依存グラフさえ分かれば並列化の順序が決まります。そして「終わったかどうか」を判定する方法が人の判断ではなくディスクの状態です — 出力ファイルが存在すれば完了です。Anthropicが公開した マイグレーションキット がキューをこう定義したおかげで、中断と再開がただになります。

第三に、対象言語のコンパイラが二人目の審判でした。Rustは所有権とライフタイムをコンパイル時に強制するので、翻訳が雑だとテストまで行く前にビルドが壊れます。Pragmatic Engineerの まとめ によれば約1,600個のコンパイルエラーを12時間で片付けたとのことですが、このエラー一覧そのものが次の作業キューを自動的に書いてくれる役割を果たします。強い型の言語から強い型の言語への移植には、このフィードバックがただで付いてきます。PythonからPythonへのリファクタリングには、この審判がありません。

三つの条件を一文に縮めるとこうなります — 機械が判定できる正解が二重にすでに存在していました。AIが優秀だから11日になったのではなく、11日のあいだ機械が自分で採点できる問題だったから11日になったのです。

人がそれでもやらなければならなかったこと — コードではなくループを直す

発表文でいちばん実務的な箇所は、コーディング以前の三時間です。SumnerはZigの慣用句をRustへどう移すかをClaudeと約3時間議論し、その結論が約600行の PORTING.md へ直列化されました。Anthropicのキットがこの文書に付けたメタルールが正確です — 二つのエージェントが違う答えを出しうる判断なら、ルールブックに入れるべきです

その次が3ファイルの予行演習です。ファイル一つごとに実装者を一人と敵対的レビュアーを二人つけて回し、出てきた翻訳物はすべて捨てて規則だけを直します。この段階で二件の問題が見つかりましたが、そのまま進んでいれば1,448個のファイル全部に広がっていたはずのものでした。三時間と三ファイルに人の時間を集中させるこの配置が、この方法論の核心です。

本作業で人が介入した箇所も、個別のファイルではなく系統でした。代表的なのが循環依存です。Zigは遅延コンパイルのおかげで循環インポートに耐えますが、Rustのモジュールシステムはそうではないので、数千個のモジュールエラーが一度に噴き出しました。このとき人がやったのはファイルを直すことではなく、依存関係を削除・移動・境界の再構成のどれに分類するかをロジックとして符号化することでした。Anthropicの文書が繰り返し使う表現がこの態度を要約します — コードを直すのではなく、そのコードを生み出したループを直します。

キットが勧めるキュー構造を最小限に移すと、こういう形になります。完了判定に人もモデルも入らないという点がすべてです。

# manifest.tsv: <元のパス>\t<対象パス>  (依存グラフの順に並べる)
# 「完了」の定義 = 対象ファイルがディスク上に存在する。それ以外の判定基準は置かない。
while IFS=$'\t' read -r src dst; do
  [ -f "$dst" ] && continue
  echo "$src -> $dst"
done < migration/manifest.tsv > migration/queue.txt

wc -l < migration/queue.txt   # 残りの作業量。中断は無料、再開は再実行するだけだ。

この構造がなぜ重要かは、逆の場合を考えれば明確です。「このファイルは全部移しましたか」とエージェントに尋ねた瞬間、進捗そのものがモデルの出力になり、進行状況がハルシネーションの対象になります。

反論 — 「レビューされていないスロップ」とマージ6週間後のリポジトリ

発表の翌日である7月9日、Zig作者のAndrew Kelleyが 反論 を出しました。The Registerが抜き出した「レビューされていないスロップ(unreviewed slop)」という表現が見出しを持っていきましたが、彼が投げた論証そのものはずっと鋭いものです。

核心は テストスイートの自己矛盾 です。100万行の未レビューコードを入れてよい根拠がテストスイートだというなら、そのテストスイートはなぜ既存のZigコードのバグを捕まえられなかったのか。Bun自身が古いコードにメモリバグが多かったと言っているのですから、同じ審判が新しいコードは十分に検証すると主張するのは成り立ちにくい、ということです。そこにKelleyは、問題の原因は言語ではなくプロジェクトの価値体系にあると付け加えます — 核心的な争点はZigとRustの言語機能とはまったく関係がなく、二つのプロジェクトの価値体系が分かれたところにある、というのがその要旨です。

この指摘は正しく、同時に方向が一つ抜けています。オラクルが捕まえられなかった欠陥群を、対象言語の型システムが構造的に取り除いてしまう場合が実際にあります — use-after-freeと二重解放は、Rustでは unsafe の外に出ると文法として表現できません。だから「テストが捕まえられなかったバグ」のかなりの部分は、テストではなくコンパイラが捕まえます。ただしその論理はunsafeの割合の分だけ弱くなり、Bunの場合は約4パーセント、約2万7千行です。HNの議論で出た反論がまさにこの点を突いています — 5万行のRustプロジェクトでunsafeが9行という事例と比べると、2万7千行は「安全なRust」と呼べる規模ではありません。

マージ6週間後の状態も一緒に見なければ公平ではありません。Tom Lockwoodが7月27日に リポジトリを直接確認した ところでは、最後のリリースタグは5月12日のv1.3.14で、Rustベースのv1.4.0はそのときまでカナリア状態でした。ボットアカウントが開いたオープンPRは7月9日の1,277個から7月27日には2,475個へ増えており、LockwoodはCIコストとそれに続くトークン使用を合わせると実際のコストは80万ドルに近づくと推定しています。この推定値は外部の観察者による計算であって、BunやAnthropicが確認した値ではないため、そのまま引用してよい数字ではありません。

反対側の事実もはっきりしています。RustベースのBunは6月17日にリリースされたClaude Code v2.1.181に載って、すでに数百万人のもとで動いていました。つまり「本番で動く」と「リリースが終わった」が6週間以上ずれていたということで、この隙間こそマイグレーション計画に入れるべき項目です。マージは終わりではなく、残作業のキューが始まる地点です。

この方法があなたのコードベースで崩れる地点

オラクルがなければここで終わりです。この方法論は、人のレビューを機械の判定で置き換えて速度を得ます。判定者がいなければ置き換えられたのではなく、ただ省略されただけです。HNのスレッドで出た例が良いものでした — ゲームエンジンを同じやり方で移すなら、何を審判にするのか。出力がピクセルと音であるシステムには、移植可能なユニットテストがそもそもうまく作れません。同じ問題はUIレイヤー、バッチレポーティング、ハードウェア制御にもそのまま当てはまります。

再設計が混ざると並列化が崩れます。Bunは構造をそのままにしたのでファイル単位のキューが成立しました。移しながらモジュール境界を引き直すことにすると、作業単位がファイルではなくモジュールになり、元と対象を行単位で突き合わせられなくなり、3ファイルの予行演習という検証装置そのものが無意味になります。Anthropicのキットが再設計型を「ルールブックが設計文書に変わり、予行演習が設計レビューに変わる」と別立てにしている理由です。移すことと引き直すことを一度にやろうとする誘惑が、この方法論のいちばんよくある失敗原因です。

レビューの経済が成り立ちません。100万行を人が読む方法はありません。Bunはそれを認めて、敵対的レビュアーとコンパイラとテストに判定を委ねました。この選択が正しかったかどうかは、実のところまだ結論が出ていません — 判明している回帰19件は「見つかったもの」であって、見つかっていないものの数は定義上わかりません。HNである開発者が残した反応がこの緊張をよく示しています。2週間で回帰を19件出したら、自分の会社なら顧客に謝罪文を書いているだろう、というものです。22万ダウンロードではなく2,200万ダウンロード規模のランタイムで、19という数字を大きいと見るか小さいと見るかはドメインが決めます。

そしてこの規模の書き換えは、いまだに例外的な選択です。Anthropicの文書が率直に書いているとおり、こうしたプロジェクトが以前は4年で300万〜400万ドルだったとすれば今は16万5千ドルであり、だから「存在を賭けた正当化」が要らなくなりました。敷居が下がったのは事実です。しかし敷居が下がったことが、そのまま良い判断だという意味ではありません。移す理由が慢性的なボトルネック一つ(ビルド時間、メモリバグの履歴)に要約できないなら、移さないほうが依然としてデフォルトです。

おわりに — 11日はAIの性能ではなくオラクルの関数でした

まとめるとこうなります。

  • 11日を作ったのはモデルではなく、三重の機械判定です — 言語に依存しないテストスイート + ファイル対ファイルの構造保存 + 強い型のコンパイラ。この三つのうち一つでも欠ければ、同じ方法が同じ速度を出しません。
  • 人が使った時間は前のほうに集中していました。3時間のルールブック議論と3ファイルの予行演習が、1,448個のファイルに広がるはずだった問題を先に断ち切りました。その後の人の介入は、個別のコードではなく系統的なエラーの分類規則でした。
  • 批判にも実体があります。テストが捕まえられなかったバグを根拠に書き換えを正当化しながら、同じテストで新しいコードを保証する論証には穴があり、unsafe約2万7千行は「安全になった」という記述をかなり弱くします。
  • マージとリリースのあいだに6週間以上の残作業がありました。マイグレーションの日程でマージ日を完了日として置くと、その区間が丸ごと消えます。

オラクルがある書き換えは、マイグレーションの中でいちばん簡単な問題です。あなたのプロジェクトでまず確認すべきなのはモデルでも予算でもなく、移す前と移した後で同じように回せるテストがあるかどうかです。なければ、この記事の11日は参考資料ではなく別世界の話です。

参考資料