プログラミング言語を一括りに変更するという行為は、過去では企業にとって2年以上の軍令状を切らせるほどの出来事だった。エンジニアたちはその話を聞くと顔を引きつらせ、予算委員会はその話題を聞くと恐怖を感じていたが、コードベースは年々進化しなかった。アンソロピーが7月16日に記事を公開したことで、この古い負債は一旦清算された。過去1か月間、同社の内部開発者たちはClaude Fable5やOpus4.8、そしてダイナミックなワークフローを使用して、それぞれ数万から数十万行の規模を持つ10個のコードパッケージを、新しい言語にすっきりと移行させた。これはまだ未来形の想定ではなく、実際に彼ら自身で起きている現実である。

最も目立った2つのケースが、想像を現実に変えた。Bunの共同創設者Jarred Sumnerは、Claude Codeを用いてBun全体をZigからRustへと移行させ、わずか2週間で100万行のコードを生成し、メインブランチにマージする前にCIテストチェーンの100%が通過した。マージ後に19のバグが見つかったが、すべて修復済みであり、Rustバージョンは6月にClaude Codeとともにリリースされている。一方、Anthropic Labsの共同責任者Mike Kriegerは、1つの週末にPythonコードベースを16.5万行のTypeScriptに移行し、数百のプロキシ、8つの段階的なチェックポイント、3回の対抗審査を含んでおり、最後には奇偶検査によって各コマンドの出力をPython版と一字一句比較した。

image.png

アンソロピーはこの戦法を一文にまとめ、全文の中で最も鋭い刀となった。「あなたが修正しているのはコードそのものではなく、コードを生み出すプロセス(ループ)自体だ」というのがその言葉である。この点を理解すれば、これまで無期限に先延ばしにされてきた移行プロジェクトは、もはや触ってはいけない禁地ではなくなった。

まず、いつ動かすべきか、なぜ動かすべきかを明確にすることが必要だ。この行動の前提はすでに変わっている。チームが移行を開始するのは、プロジェクトが始まった頃の技術的状況と今が一致していないためである。あるいは、当初の妥協が限界に達したか、より良い道が出現したか、あるいは元のエコシステムが縮小したかのいずれかである。Jarredは当時Zigを選んだ理由は、C言語レベルの性能と極限の単純さを組み合わせたものであり、大規模なモデルが登場する前、オーリンの狭いアパートで1年かけてBunを作ったことに合致していた。その単純さには代償があり、彼は後になってそのことを明確に述べている。

2026年に進むと、Bunのコマンドラインツールの月間ダウンロード数は1,000万を突破し、Claude Code内でも広く使用されている。しかし、昨年の四半期においては、そのコストがそれほど大きくなく、これにより長期的な計画を停止したり、何ヶ月分のリソースを投入して大きなプロジェクトを行う必要性はなかった。言語の移行は確かにより小さい、速く、安全なシステムをもたらすが、誰もその費用を支払う意欲はなかった。エンジニアたちも自分のキャリアリスクを考えなければならない。複数のコードを並行して保守することになる可能性があるが、最終的には90%の同等性しか得られず、最初から動かずにいた方がよほどマシかもしれない。

現在は様子が異なる。最悪の場合でも、そのブランチを削除して再びやり直せばいい。もちろん、ビジネス上の計算は必要だ。100万行規模の移行は、4年前のようなプロジェクトのように何百万ドルも費やす必要はなくなったが、実装には数万から数十万ドルかかる。Bunの移行では、59億の未キャッシュ入力トークンと6.9億の出力トークンが消費され、API価格で約16.5万ドル相当。Mikeが主に担当した移行にも2,700万トークンが使われた。本当にMikeが決断を下したのは、コンパイルの壁だった。彼のチームの内部ツールは単一のバイナリファイルとしてユーザーに配布され、Pythonツールチェーンで各プラットフォームをコンパイルするには約8分かかり、全体のビルドマトリクスを完了するには30分待たなければならなかった。移行後には、同じコンパイルが2秒で完了し、バイナリの起動速度は6倍に改善し、独立したデプロイラインも排除できた。

なぜAIがこの古い負債を清算できるのか?それは、大規模なコード移行がこれらの高級モデルにとって理想的なシナリオだからである。作業は天然的に並列処理可能で、ファイルやcrateといった数百から数千の独立したユニットが同時に処理でき、誰も待つ必要がない。コンテキストは明確かつ完全で、旧コードがモデルにとって最高の仕様書であり、翻訳代理が従うガイドブックとなる。さらに嬉しいことに、自動判定機能もある。大型コードベースにはテストセットが含まれており、代理が自己評価をできる。検証が十分に客観的であれば、モデルは真実に向かって数日間取り組むことができ、人間が品質を判断する必要はない。キューは自動的に生成される:コンパイラやテストが停止すると、次の処理が自動的に追加される。プロセス自体が偏差を防ぎ、審査員が発見した各問題には背後のルールを引用する必要があるため、一度の違反が待機リスト項目になるだけで、静かに分岐しない。ある代理が端末ケースに遭遇すると、修正方法はその後のすべての代理が遵守するルールになる。以下の2つのケースでは、MikeとJarredは重要なステップでFableを使用し、特にアドバイザーモードを活用し、複数のモデルを別々の段階で使い分け、トークン消費を最小限に抑えている。

本当に再利用可能なのは、彼らがまとめ出した6つのステップであり、この2つの移行から生まれ、汎用性を持ち、多くの言語やシナリオに適用できる。

作業を始める前に、まずは完璧な審判者を確保する必要がある。そうしないと、いつ終わるのか、成功かどうかを測れることがない。この審判者は、元のコードと目標コードの両方を同じ基準で評価できる必要がある。元の言語で書かれたテストケースは、目標コードに存在しない内部関数に依存していることが多い。これを構築するために、Claudeで既存のテストを分類する。外部呼び出しで表現できるものはどれか、内部実装に依存しているものはどれかを確認し、外部向けのテストを元のコードと移植版で動作するアサーションに書き直す。次に、対抗代理を配置して、改訂がアサーションの強度を低下させていないかを検証する。最後に、元のコードで走らせてそれがパスすることを確認し、意図的に破損させたコードで走らせてそれが失敗することを確認する。損害を検出できない審判者は、審判者とは言えない。MikeがPythonからTypeScriptへの移行で構築した奇偶校正台は、7つの本物のシナリオをカバーしており、あらゆる行動の変化は修復するべきバグとみなされる。

第1ステップは、ルールを立て、依存関係図を描き、欠陥リストを作成し、全体の移行に基礎を築くことである。順序には注意が必要である:ルールマニュアルは欠陥リストの前に置くべきであり、欠陥リストはルールマニュアルのデフォルト項目がカバーできない部分によって定義されるからである。両者は共に統合監査で検証される。ルールマニュアルの内容は、最初に行う主要なアーキテクチャ決定に依存する:新コードは古い構造を引き継ぐのか、それとも完全に再設計するのか。古い構造を引き継ぐ(Jarredのケース)場合は、マニュアルは型や慣習を二つの言語間で翻訳する一連の表であり、難しいコンポーネントについては欠陥リストに参照する。完全に再設計する(Mikeのケース)場合は、マニュアルは設計文書となる。JarredはClaudeと話し合いながら作ったが、各曖昧な領域に1つのポリシーを設定し、自分で直感的に列挙した一般的な失敗パターンごとに8つのサブエージェントを割り当てた。依存関係図は、ファイル間の依存関係を把握することで、作業を効率的に並列化し、どのファイルを先に移行し、どのファイルをまとめて移行するかを知ることができる。一部の言語やコードベースにはリストファイルが含まれているため、これを行うことは簡単だが、C/C++やPythonなどの古いコードでは依存関係を手動で探して描く必要がある。Claude Codeは、確定的なスクリプトを実行して図を生成するエージェントを配置することができる。欠陥リストには、新旧言語間の硬いギャップを記録する。ZigからRustへの移行では、手動メモリ管理の違いが問題になる。一つのバッファを解放し忘れていてもコンパイルは通るが、漏れは実行時にのみわかる。PythonからTypeScriptへの移行では、インターフェースと契約の違いが問題になる。Pythonではオブジェクトの構造を宣言する必要がなく、TypeScriptでは白紙の文書に記述する必要がある。JarredとMikeはこの隠れた知識を欠陥リストファイルに記録し、Jarredは初期段階で整理し、Mikeは移行後に補填した。どちらの方法も必要かもしれない。

第2ステップでは、ルールを圧縮し、ミニ移行を試航として実施する。Jarredは、マニュアルに従って3つのファイルを翻訳するエージェントを配置し、熟練したRustエンジニアのように3つのファイルを翻訳するエージェントを配置し、2つの差異を取得して新しいルールを抽出するエージェントを配置した。この段階で、彼は2つの致命的な問題を捕まえた。これらが全部の1448個のファイルに展開された後に爆発すれば、大きな問題になるだろう。この圧力テストは構造保持型移行に対してのみ有効であり、同じファイルの両方の翻訳が1行ずつ比較できるからである。もしマニュアルが再設計であれば、Mikeのように、設計文書を攻撃する対抗審査員を使って検証し、一時的なエンドツーエンドで検証する。いずれにしても、翻訳されたファイルはすべて破棄される。このステップの目的はルールを磨くことであり、進捗を積み重ねることではない。

第3ステップでは、すべてを翻訳し、残りのステップは同じ多エージェントループで行われる:実装、審査、修正。このような苦労の作業は小さなモデルに任せることができ、審査は大きなモデルに任せる。Mikeは12個のサブエージェントを配置した際にはClaude Sonnetを使用した。タスクキューは退屈なほど機械的でなければならない:バッチスクリプトが翻訳されたファイルがディスク上にあるかどうかを見ることで、どれが完了したかを判断し、待機リストを実装エージェントに送る。キューは常にディスクから再構築されるため、移行は断点から再開可能である。エージェントが過剰に慎重になり、あまりに少ない作業をすることは時折あるが、その場合、コンテキスト付きの簡潔な指示を提示し、次にコンパイラが間違いを検出するように促す。翻訳器が自信がない作業は、// TODO(port): 原因という一行を標示し、第4ステップに渡す。ここから先では、待機リストが自動的に作成される:コンパイラがエラーを数え、スモーキングテストがクラッシュを検出、テストケースが失敗を報告する。2つの対抗審査員はそれぞれのコンテキストに基づいて実装者の作業に点数をつけるが、両者が意見が食い違えば第三のエージェントに渡す。ある審査員が複数のファイルで同じ種類のエラーを繰り返し検出する場合、修正方法は個々のファイルにパッチを当てることではなく、ルールマニュアルに一文を追加し、影響を受けたバッチを再生成することである。このステップではマニュアルがどんどん厚くなり、コードは手動でそのマニュアルに合わせて修正されることはない。また、重要な設計決定の一つはコンパイラの配置場所である:MikeはTypeScriptコンパイラをすべてのループに組み込み、数秒で1つのセルを検出できるようにした。一方、Jarredはコンパイラをループ外に完全に遮断し、次に進めるためにcargoを数分かける。

第4、5、6ステップは一緒に説明する。これらは同じループを共有し、後に行くほど人の判断は不要になるからである。第4ステップのコンパイルは、言語と規模によっては第3ステップに統合されることがある。Jarredはワークスペース全体でコンパイラを実行するスクリプトを使用し、エラー修正エージェントがエラー一覧を並列に処理し、対抗審査が同時に進行し、ビルドが再実行され、このプロセスを繰り返す。エラー一覧を読み取ると、システム的な問題を検出できる。例えば、JarredがZigの緩いコンパイルが許容した循環依存を修正した後、数千のRustモジュールのエラーが湧き出た。彼は循環に論理を追加して、削除するべきもの、移動するべきもの、境界を再構築するべきものを分類し、ようやく抑制できた。第5ステップとコンパイラエラー一覧には同じ機械的な真実の源泉がある:スモーキングテストが吐き出すクラッシュ。修復方法は根本原因を分類し、対抗サブエージェントに提出することである。第6ステップ、つまり物語の終わりは、2つのコードベースの動作を個別に比較することである。ファイルが翻訳され、コンパイルされ、スモーキングテストを通過したら、それを砕き、準備段階のテストケースを適用して実行し、失敗したものを修正エージェントに送り、対抗審査員がその修正方法を検証する。ループの後には、構築ガーディアンプロセスがある。これは唯一、バイナリを再構築できる役割である。修正エージェントはパッチを書き込み、ガーディアンプロセスがそれらをまとめて再構築し、影響を受けるテストを再実行し、結果を戻す。最も高価な操作を直列化し、多数のエージェントがそれぞれの操作をトリガーしないようにする。同じ失敗が多数のテストで繰り返し発生した場合、修正方法は上流に戻る:そのバグを生じさせるルールを変更し、それに影響されるファイルだけを再生成する。Mikeのこの手法は特に注目に値する。多くの開発者は現成または移植されたテストケースを持っていないが、彼はClaudeに小さなスクリプトを書いて、7つの本物のシナリオで新規移植版とPythonオリジナル版を同時に実行し、出力の違いを比較させた。各失敗したシナリオに修正エージェントを割り当て、7つすべてを通るまでループを続ける。さらに進んで、Claudeにエンドツーエンドのテストケースを設計させ、一夜中自主的に実行させ、どこでクラッシュしたかを検出し、すべてのシナリオリストで予測できない細かい傷を捉えた。