BunのRustへの書き直しについての私の考え
(en.liujiacai.net)- BunのRustへの書き直しは、Zigで作られた既存のアーキテクチャとデータ構造を維持したまま、より主流な技術スタックへ移す判断に近い
- 書き直しPRは6,755コミット規模で、5月8日に公開されてから5月14日にマージされており、本番級ランタイム変更のレビューの深さには疑問が残る
- テスト通過は既知の経路だけを検証するものであり、エラーパス・並行性・極端なメモリ条件といったグローバル不変条件まで保証するのは難しい
- 核心はZigの失敗ではなく、迅速なリリースを重視するBunチームの文化と手動メモリ管理のコストとの構造的不一致にある
- 実際の賭けはZig対Rustではなく、AIが生成し、十分にレビューされていないコードを長期保守できるかどうかにある
Zigが築いたBunの土台
- BunのRust書き直しPRを見る前に、Bunが現在の位置に至るうえでZigの役割が大きかったことは押さえておくべき
- JarredがZigを選んだ理由は「かっこいいから」ではなく、ガベージコレクションなしの高性能JSランタイムを小さなチームが素早くプロトタイプ化できたからである
- Zigの低い摩擦、直接的なメモリ操作、単純なC相互運用性が、Bun初期の性能と小規模チーム体制を可能にした
- Jarredが「アーキテクチャは変わらず、データ構造も変わらない」と述べたように、Rustへの書き直しはZigで作られた骨格を引き継ぐ構造に近い
- Zigで基盤を作り、製品を出し、資金調達まで行い、その後会社が買収されて規模が大きくなってからより主流の技術スタックへ移るのは、通常のビジネス判断と見なせる
- ただし、これをZig自体の不適合として語るのは難しい
6日でマージされた大規模書き直しの危険
- 書き直しPRは6,755コミット、ブランチ名
claude/phase-a-port、5月8日に公開されて5月14日にマージという記録を残した - 本番級JSランタイム全体の書き直しが6日でマージされた点こそが核心的な問題である
- ソフトウェア工学の基本原則は「理解していないコードを本番で動かしてはならない」というものだ
- この原則は、コードに必ずバグがあるからではなく、バグが起きたときにどこから見ればよいのか分からなくなるため、保守性の最低線となる
- PRのレビュアー一覧には
coderabbitai[bot]、claude[bot]があり、唯一の人間レビュアーaliiは「Awaiting requested review」状態だった - Claudeが書いてClaudeがレビューした閉ループは論理的に不可能ではないとしても、結果的にはコードベース全体を人間が実際に最後まで読んでいないことを意味する
テスト通過では保証できないこと
- すべてのプラットフォームでテストスイートが通っても、それが検証するのは既知の動作と既知の経路の正しさに限られる
- テストスイートでは、次の領域を十分に保証するのは難しい
- エラーパスが正しく処理されるか
- ストレス状況の境界条件でどのように振る舞うか
- 並行実行時に状態の整合性が保たれるか
- 極端な条件でメモリモデルが意図どおりか
- Jarredも、JS境界へ再入する際のメモリ問題はRustコンパイラでは処理できず、依然として人間に依存すると認めていたと整理されている
- 問題は、人間に依存しなければならないその部分を人間がレビューしていないことである
- AIによるコード翻訳は、各関数が元コードと孤立した状態で同じように動作するよう合わせる局所的な意味等価性に近い
- 関数間のグローバル不変条件や、テストには書かれておらず原著者の頭の中にしかない設計制約までは、AI翻訳だけで保証するのは難しい
- こうした制約は現在のテストでは表面化せず、6カ月後に特定の本番負荷で説明しづらいクラッシュとして現れるかもしれない
- これはClaude固有の問題ではなく、十分なレビューなしに翻訳するあらゆるツールや人間のプログラマにも当てはまる
- 6,755コミット規模では、この種のリスクが大きく増幅される
買収後に変わったリスク負担者
- 初期のBunはJarredが自分自身に賭けたプロジェクトであり、Zigの採用、素早い反復、技術的負債の受容は、自らリスクを引き受けるスタートアップの論理として理解できる
- しかしBunが大企業に買収され、実際の本番システムで使われるユーザーベースを持つようになった今、書き直しのリスクを負うのはJarredではなく、本番でBunを動かすエンジニアとその先のユーザーたちへと変わった
- Jarredは、そのバージョンはまだcanaryであり、正式リリース前に最適化と整理作業が残っていると述べている
- canaryは防御線ではあるが、人間によるレビューの代替ではない
- 最適化と整理はコード品質の問題であり、保守担当者がコードを理解しているかという問題は解決しない
- チームの誰も全体を完全には読んでいないコードベースは、テストが包括的でもcanary期間が長くても、保守担当者にとって内部状態がブラックボックスのまま残る
- この問題は、将来深刻なバグをデバッグする現場で現実の苦痛として表れる可能性がある
Zig問題の診断ミス
- Jarredが挙げた以前の理由は、Zigコードベースにuse-after-free、double-free、エラーパスでのメモリリークが多かったというものだった
- この診断自体は正しいが、ここから「Zigはうまく機能しない」という結論を導くのは難しい
- より正確な診断は、迅速な反復を優先する商用プロジェクトにおいて、手動メモリ管理の認知コストがチームの予算を超えたということだ
- これはZigのバグではなく、Zigの設計目標とBunのビジネスモデルのあいだにある構造的不一致と見るべきである
- Zigの想定ユーザーは、自分が何をしているかを理解し、最終的な制御のためにコストを払う意思のあるシステムプログラマである
- TigerBeetleはZigで事実上メモリバグのほとんどないデータベースを書いたが、それはチーム文化とプロジェクトの性格がZigの哲学と合っていたからだと整理されている
- Bunチームの文化は、素早い反復、素早いリリース、素早いバグ修正に近く、これはZigが要求する厳格なメモリ規律と根本的に緊張関係にある
- 「自分たちのチームはこの道具で頻繁に失敗する」を「この道具は不適切だ」と解釈するのは、帰属の誤りに近い
- ハンマーが合わなかったからといって、ハンマー自体が悪いわけではないという比喩が使われている
短期見通しと長期リスク
- 短期的には、書き直し版はおおむね問題なく動く可能性が高い
- 主要経路はテストでカバーされ、canary段階で明白な問題が表面化し、Rustコンパイラの保証によってメモリバグの一種は除去されるからだ
- 表面的には、すべてが正常に見えるかもしれない
- 長期的には、人間が完全には読んでいない6,755コミットが構造的リスクとして残る
- 6カ月後に奇妙な並行性バグが現れたり、特定の負荷で境界条件が異常動作を引き起こしたりすれば、デバッグするエンジニアは、誰一人として本当に理解したことのないシステムに向き合うことになる
- 理解されたことのないシステムというのは、バグがないという意味ではなく、バグが起きたときになぜそうなるのか誰にも分からないという意味である
- 今回の書き直しにおける実際の技術的な賭けはZig対Rustではなく、AIが生成し、レビューされていないコードを本番環境で長期保守できるかどうかにある
- この問いは「テストがすべて通る」よりも複雑であり、「Rustのメモリ安全性」よりも深い問題である
- 結論は、「Zigが土台を作り、Claudeが建物を建て、人間のレビュアーはまだ向かっている途中だ」という比喩に要約される
- この建物にどれだけ長く住めるかは、最初の雨漏りが起きたときに誰かが設計図を読めるかどうかにかかっている
1件のコメント
Lobste.rs のコメント
「Bun を Rust に書き直そう」を論じる前に、Bun が今の位置まで来たのは Zig のおかげだという点は言っておくべき
Bun の作者 Jarred もそう言っている。Zig が Bun を可能にし、Oakland の臭い部屋で 1 年間ひとりでコードを書いていたプロジェクトが、JavaScript エコシステムで最も広く使われるツールの 1 つに成長するうえで、Zig の功績は大きいとのこと
また Zig は素晴らしい言語であり、Bun の成功はそこに大きく負っているが、Ghostty や Tigerbeetle のような他のプロジェクトは Bun が経験した安定性の問題を経験していないとも述べている
普通は データベース のほうが言語ランタイムより、安定性の課題がはるかに大きいからだ
この記事は LLM が書いた文章っぽさがかなりある
なので、その印象は 機械翻訳 の痕跡かもしれない
Zig の想定ユーザーが「自分が何をしているかを理解し、究極の制御権のために代償を払う覚悟のあるシステムプログラマ」だというなら、Rust や Swift 以後に出てきた言語という文脈では、再登場した C/C++ 的な過信 のように見える
Jarred が移行理由として「Zig のコードベースには use-after-free、double free、エラーパスでのメモリリークが多すぎた」と言ったのなら、LLM にひたすらメモリ安全性バグを探させるだけで、メモリ安全でない言語を実質的に安全にできるのかという問いに対する一つのデータポイントになる
ここでは LLM 企業自身ですらその道を選ばなかった
「Claude が書いたコードで、Claude がレビューしたコードなので、閉じたループは論理的に不可能ではないが、人間がこのコードベース全体を実際に読んだことはない、という意味だ」という話は、明らかに正しくなさそうだ
人間が書いた Zig のコードベースを Rust に機械的に翻訳したのなら、元のコードを理解している人は新しいコードも理解できる。APL に移したわけでもなく、どちらも C 構文の伝統にある手続き型言語なのだから
今後 LLM を監督なしで回せば、コードが次第に人間の感覚から離れていくかもしれない、という論点は開かれているが、現時点のコードベースは、100 万行規模の JavaScript ランタイムかつ単一バイナリの万能ツールとしては、まだ十分理解可能に見える
「AI は関数単位の局所的な意味等価性しか合わせず、関数間の大域的不変条件は理解しない」という文は、LLM に好意的な方向にも批判的な方向にも奇妙だ
LLM は各関数が元と同じように動くことを保証できるほど決定的ではない。そうした保証には
c2rustのようなツールが必要だ。LLM は元らしく見えるコードを翻訳することはできても、((abc & 45) << 3) == 360を((abc & 45) << 30) == 360に変えないよう防ぐのは、コンパイラ、テストスイート、そしておそらく LLM ベースのコードレビューによる確率的検出だけだ逆に、
c2rustのように各関数の同一性を保証しつつ、LLM のようにコメントや構造も保てる翻訳器があるなら、それは 完璧な翻訳器 であり、100 万行のコードベースも自動移植できる。コンパイラもこうした特殊ケースの一種と見なせるし、Clang はバグが皆無ではないにせよ、人々が信頼するには十分近い。LLM が Clang 並みの信頼性で Zig や C++ を Rust に翻訳できるなら、Chrome は今月末までに純 Rust になっていただろうさらに、関数間の不変条件をエンコードすることこそ 型システム の核心だ。Bun を Rust に書き直す理由の 1 つも、Rust の型システムのほうが複雑な不変条件をよりうまく表現できる点にある。Anthropic が全体をアセンブリにコンパイルしてからソースコードを焼き捨てたわけでもない
Bun は Rust への移植よりずっと前から バイブコーディング されてきた。AI をかなり早く受け入れており、Anthropic の買収以降はほぼすべてのコミットがボット作成だ。Jarred も、週末のあいだ AI に機能を書かせて複数の機能を追加したとツイートしていた
HTML Parsers in Portland は Python の HTML パーサの複数の移植を分析し、中核アルゴリズムの 1 つが各移植で非常に異なる実装になっていることを示している。どの場合も機械的移植は可能だったのに、実際にはそうならなかった
もちろんこれは今年初めの事例で、プロセスも違ったが、移植後の Bun コードベースで見た断片のいくつかも似た問題を抱えているように見える
しかし「LLM は各関数が元と同じように動くことを保証できるほど決定的ではない」という点は、結局のところ 人間がコードベース全体を実際に読んでいない という結論につながる
「すべてのテストが通る」の一例: https://github.com/oven-sh/bun/…
他のフォーラムでも複数人が同じコミットをリンクしていたのを見るに、どこかでそのリンクを見て、自分の期待に合っていると感じた後、実際に関連があるかは確認しなかったのだろう。自分たち自身にもっと高い基準を課すべきだと思う
最初のコミット: 「await process exit / JSON-parse-retry instead of fixed sleeps」
差し戻し: 「test: revert proc.exited change in spawn.test.ts, keep isDebug iteration count」
「理解していないコードは本番で動かすべきではない」という原則を、最近よく考えている
キャリアはかなり進んだが、意図的に管理職には行かず、プログラミングの経歴もむしろスタックの下のほうへ向かってきた。実際のプログラムそのものを深く理解するのが好きだからだ。機能を出してユーザーの生活を良くすることも非常に重要だが、プログラミングにおける大きな内的報酬の 1 つは、実際のシステムについて真である事実を学んでいるという感覚だ
私のような人間には、人間がプログラムのすべての行を理解すべきだというのは公理のように感じられる。だが、組織図の上では私のマネージャが、私が保守しているプログラムに責任を負っている。彼は素晴らしいマネージャで、マイクロマネジメントもしないので、チームが出荷するソフトウェアのすべての行を当然ながら理解していない。というか、コードをほとんど読んだことすらないかもしれない。だとすれば、彼はこの原則に反していることになる
「部下が書いて、自分は理解していないコード」と「自分の AI が書いて、自分は理解していないコード」のあいだに、意味のある違いがあるのかを考えている
前者は受け入れられるのに後者は気持ち悪い。違いは、そのコードについて 責任を負う人間 がいるかどうかだけなのだろうか。責められる誰かがいるかどうかなのだろうか。それが、自分が感じるこの強い道徳的感覚を正当化するのに十分かどうかは、まだ分からない
この強い感情は、良いエンジニアリングに必須というより、個人的な美意識に近いのではないかと心配している。好みは人それぞれで構わないが、それがすべてだとしたら、この仕事のこの側面は今後あまり楽しくなくなると覚悟すべきなのかもしれない。結局、管理職へ押しやられるようなもので、しかも自分の部下はボットというわけだ
「Zig は小さなチームでも、ガベージコレクタや重いランタイムなしに高性能な JS ランタイムを素早くプロトタイピングできるようにした」と言うが、他の主要ランタイムである Node と Deno も同じことをしている
どちらもガベージコレクタのない言語である C++ と Rust で JS エンジンをラップしている。V8 や JSC 自体が実際にはガベージコレクタを同梱しているのかどうかはよく知らないが、それは本質ではない
Bun リポジトリ全体がディストピアのように感じられる。ボット同士が会話しながら、意味不明な PR を作っている
例: https://github.com/oven-sh/bun/issues/30766
これが「完了」と見なされた速さはかなり衝撃的だが、記事がしっかりした証拠もなく描いているような、走行中の列車事故だとは思わない
「6 か月後に奇妙な並行性バグが現れ、特定の負荷で何らかの境界条件が異常動作を引き起こしたとき、デバッグするエンジニアは誰も本当に理解したことのないシステムに直面することになる」というのは、純然たる推測であり、事実は示されていない
言語移植 は LLM に向いている領域であり、元のコードも多くの人間に理解されていた。単純な言語移植が、なぜ突然診断可能性を取り返しのつかないほど壊すのかは分からない