100万行のコードが、2週間足らずで書き変わった。
書き換えた先はZigというシステムプログラミング言語から、Rustという別の言語への全面移植です。JavaScriptランタイムのBunを作るチームが、2026年にAnthropicと組んでやった仕事でした。普通に見積もれば、エンジニアを丸ごと1つの四半期は拘束する規模の仕事です。
この事例をAnthropicが公開したとき、核心をこう言い切りました。
“The core insight is that you don't fix the code. You fix the process (loop) that produced the code.”
筆者訳コードを直すのではなく、コードを生んだループ(プロセス)を直す。
この一文が、今回のレッスン全体の背骨です。前々回のマルチエージェント、前回のAgent TeamsとDynamic Workflowsで仕組みを見てきました。今回はその仕組みが、実際の現場で何を動かしたかを見ていきます。
なぜ今この話が重要か
大規模な移行やリファクタは、仕事の中でも重要度が高いのに、最も先送りされてきた仕事でもあります。
“Big migrations and refactors are some of a team's most important work, and the easiest to push off to a 'better time' since they'd tie up engineers for a quarter. With dynamic workflows, Claude can now land that kind of work in days or weeks.”
筆者訳大規模なマイグレーションやリファクタリングは、チームにとって最も重要な仕事の一つでありながら、エンジニアを1四半期拘束してしまうために「もっと良いタイミングで」と先送りされがちな仕事でもある。Dynamic workflowsを使えば、その種の仕事をClaudeが今や数日から数週間で仕上げられる。
始めるハードルが下がったからといって、途中で投げ出していい理由にはなりません。Borisは別の場面で、もう一つ釘を刺しています。
“always make sure that when you start a migration, you finish the migration.”
筆者訳マイグレーションを始めたら、必ず最後までやり切ること。
数日から数週間で終わるようになったことと、片手間で終わらせられることは、まったく別の話です。ここから先で見ていくのは、最後までやり切るための具体的な手順です。
前提: 先に審判を立てる
6段階の手順に入る前に、必ず済ませておくことがあります。合否を機械的に判定できる「審判」を、先に立てることです。
やることは実質2つです。既存のテストを、内部APIに依存するテストと、外部から見た挙動を確認するテストに仕分けます。外部向けのテストは、新旧どちらの実装でも動くように、移植可能なアサーションへ書き直します。
Instagramの共同創業者であるMike Kriegerは、Python(パイソン)からTypeScript(タイプスクリプト)への移行でこれを徹底しました。実世界の7つのシナリオでパリティハーネス(新旧の挙動を突き合わせる検証装置)を組み、挙動が少しでも変われば、理由を問わずすべてバグとして扱ったといいます。
テストスイート自体が心もとない場合はどうするか。Anthropicの答えは明快です。
“If you can't inherit a referee, have Claude build one. Your original codebase is the ground truth either way.”
筆者訳審判を継承できないなら、Claudeに作らせればいい。元のコードベースが、いずれにせよ真実の基準になる。
審判が先、翻訳は後。この順番を逆にすると、大量に生成された「もっともらしいコード」を、人間が1つずつ目で確認する羽目になります。
6段階の手順
審判が立ったら、ようやく本体の作業に入ります。手法は6段階に分かれています。
大規模移行の6段階
ルールブックを作る
言語間の型・慣用句の対応表、依存関係マップ、ギャップ一覧を用意する
代表3ファイルで実地テスト
3種のエージェントを走らせてルールを検証する。翻訳結果は全部捨てる
全体翻訳
実装・レビュー・修正のループを多エージェントで回す
コンパイル
ワークスペース全体をビルドし、エラーを分類して潰す
スモークテスト
クラッシュを根本原因ごとに分類し、修正エージェントが対応する
挙動一致
テストスイートを並列シャードで実行し、審判と突き合わせる
ステップ1では3つのものを作ります。言語間の型・慣用句の対応表(ルールブック)、並列作業を可能にする依存関係マップ、新しい言語が強制する要件をまとめたギャップ一覧です。Zig→Rustの場合、いちばん大きなギャップは手動メモリ管理が所有権モデルに変わる点でした。
ステップ2が、この手法でいちばん独特なところです。代表的な3ファイルを選び、3種類のエージェントを走らせます。ルールブック通りに訳すエージェント、ターゲット言語のシニアエンジニアのように訳すエージェント、そして2つの差分から新しい翻訳ルールを作るエージェントです。
Bunの移行を率いたJarred Sumnerは、このステップで重大な問題を2つ発見しました。1,448ファイル全体に先に展開していたら、大量の問題を生んでいたはずの問題です。そして見つけたあと、この3ファイル分の翻訳結果は全部捨てます。この段階の目的は進捗を出すことではなく、ルールを精緻化することだからです。
ステップ3以降は、精緻化したルールを使った量産です。実装、レビュー、修正のループを多エージェントで回します。進捗の判定も機械的です。バッチスクリプトが、翻訳済みファイルがディスク上に存在するかどうかだけを見て、次に進むかを決めます。
ステップ4のコンパイルとステップ5のスモークテストは、どちらも「エラーを分類して、修正エージェントに割り振る」作業の繰り返しです。Jarredのケースでは、循環importを直したことで数千件のRustモジュールエラーが表面化しましたが、依存関係を削除・移動・境界を再構築するかを分類するロジックを組んで、ループを解消しました。
最後のステップ6、挙動一致では、テストスイートを並列シャードで実行し、審判と突き合わせます。Mikeのケースでは、エンドツーエンドテストが自律的に夜通し走り、壊れた箇所を直しては再実行することを4晩連続で繰り返したといいます。
効いている設計原則
ここまでの6段階は、いわば骨格です。効いているのはむしろ、その骨格を支えている個別の設計判断のほうです。
モデル配分
大きいモデルと小さいモデルを、役割で明確に分けます。
“Smaller models handle the high-volume implementation fan-out well; save your largest model for reviewers and for anything that writes rules other agents will follow.”
筆者訳実装の大量処理は小さいモデルに任せてうまくいく。最大のモデルは、レビュアーと、他のエージェントが従うルールを書く仕事のために取っておく。
Mike Kriegerは主要な移行フェーズで12体のサブエージェントを展開し、実装にはClaude Sonnetを使いました。トークン消費はループのどこかに必ず集中します。だからこそ、どこに小さいモデルを置き、どこに一番大きいモデルを残すかを、最初に設計しておく必要があります。
完了は機械が判定する
「終わった」を、人間の感覚ではなく、機械が確認できる条件で定義します。
“Done should mean the output file exists on disk.”
筆者訳完了とは、出力ファイルがディスク上に存在することを意味するべきだ。
バッチスクリプトが、翻訳済みファイルの存在だけを見て次に進むかどうかを決めます。人間が「たぶん終わっている」を確認して回る作業が、ここでは丸ごと要らなくなります。
ルールブックに戻す
レビュアーが同じ間違いを繰り返し見つけているとき、直す場所を間違えないようにします。
“When a reviewer keeps catching the same mistake across files, the fix isn't per-file. You add one sentence to the rulebook and regenerate the affected batch.”
筆者訳レビュアーが複数のファイルで同じ間違いを捕まえ続けているとき、直すべきはファイル単位ではない。ルールブックに1文を足して、影響を受けたバッチを再生成する。
ファイルを1つずつ直して回るのは、対症療法です。原因はたいてい、ルールブックの記述が1文足りないことにあります。
敵対的レビュー
作った本人とは別の文脈を持つレビュアーが、成果物を検証します。
“Adversarial review allows for longer running tasks and is often worth the token consumption. Let scripts — a compiler, a diff, a test suite — be the referee.”
筆者訳敵対的レビューは、より長時間のタスクを可能にする。多くの場合、そのトークン消費に見合うだけの価値がある。コンパイラや差分、テストスイートといったスクリプトに、審判の役をさせよ。
意見が割れたときは、第三のエージェントにエスカレーションします。判定するのは人の勘ではなく、スクリプトです。
未解決はフラグで明示する
判断がつかない箇所を、隠さずに残します。
実装エージェントは、自信を持てない移植判断に出会うと、TODO(port): 理由 という形式でコード中に明示的なフラグを立てます。あいまいなまま次の工程に進めるより、後工程の人間かエージェントが拾える場所に、迷いをそのまま残しておくほうが安全だからです。
ビルドデーモン
修正の権限を、1箇所に集約します。
修正エージェントはパッチを書くだけで、ビルドそのものを実行する権限は持ちません。ビルド権限を持つのは専用のデーモンだけです。デーモンがパッチをまとめて受け取り、まとめて再ビルドし、影響を受けたテストだけを再実行して、結果をフィードバックします。複数のエージェントが同時にビルドを取り合う事故を、権限を1箇所に絞ることで防いでいます。
数字で見る
2つの実例を、数字だけ並べて比べます。
2つの大規模移行の実績
- 期間: 2週間未満
- 既存テスト: 100%がマージ前CIを通過
- マージ後の回帰: 19件(すべて修正済み)
- トークン: 入力59億・出力6.9億(API価格換算で約16.5万ドル)
- 性能: HTTPサービングと実ワークロードで2〜5%向上
- バイナリサイズ: Linux/Windowsで19%削減
- ビルド反復のメモリ: 2,000回の反復で6,745MBから609MBへ
- unsafeコード: Rustコード全体の約4%
- 期間: 週末で完了
- プロセス: 8段階のフェーズゲート・3回の敵対的レビュー
- コンパイル時間: プラットフォームあたり8分から2秒へ
- バイナリ起動: 6倍高速化
- トークン消費: 27万
- サブエージェント: 主要翻訳フェーズで12体を展開
数字を見ると、興味深いのは速度だけではありません。Bunのケースでは、移行後にパフォーマンスがむしろ2〜5%向上し、バイナリサイズも19%小さくなっています。移行は「元の動きを保つだけの作業」ではなく、コードを整理し直す機会にもなっています。
判断の基準が変わった
期間もコストも1桁変わったことで、これまで見送っていた仕事の採算ラインが動きました。
“A year of memory-bug patches in the changelog, or one chronic bottleneck, can now justify it.”
筆者訳変更履歴に並ぶ1年分のメモリバグ修正、あるいは1つの慢性的なボトルネックが、今なら移行を正当化しうる。
以前は「言語を変えるほどの価値があるか」を判断するハードル自体が高すぎて、検討の土俵にすら乗らなかった仕事です。それが今は、日々のバグ修正コストと比較して判断できる規模になっています。
ただし、安くなったのであって、無料になったわけではありません。
“While million line migrations no longer cost $3 to $4 million in engineering resources over the course of a four year project, they still cost tens to hundreds of thousands of dollars or more to execute.”
筆者訳100万行規模の移行は、もう4年がかりで300万〜400万ドルというプロジェクトではなくなった。それでも実行には、数万から数十万ドル以上のコストがかかる。
視点
安くなった、でも無料ではない
判断の基準が動いたのは「検討する価値があるかどうか」のラインです。実行にあたっては、審判を立て、ルールブックを育て、6段階を回すだけの準備がやはり要ります。数字が1桁変わったことを、準備なしでいいことだと勘違いしないでください。
自分の仕事に持ち帰る
100万行の移行をする機会は、多くの人にはめったに来ません。ですが、ここまでの原則は規模を問わず効きます。
大規模移行という極端な例で際立つのは、この4つが特別な技術ではなく、規律だということです。1人の仕事の進め方にも、そのまま持ち込めます。
まとめ
- 大規模移行の核心は「コードを直す」ことではなく「コードを生んだループを直す」ことにある
- 手を動かす前に、合否を機械的に判定できる審判を立てる。テストが無ければClaudeに作らせる
- 手順は6段階。特にステップ2の実地テストは、進捗でなくルールの精緻化が目的で、翻訳結果は捨てる
- 効いているのはモデル配分、機械的な完了判定、ルールブックへの反映、敵対的レビュー、フラグ、ビルドデーモンという個別の設計判断
- Bunは100万行を2週間未満、Kriegerは16.5万行を週末で終えている。どちらも人手の見積もりより1桁短い
- 判断の基準は動いたが、無料にはなっていない。数万から数十万ドル以上の投資は依然として要る
次は毎朝のルーティンを自動で回す。今日見た「完了を機械が判定する」という考え方を、日々のルーティンに落とし込みます。
明日のアクション
100万行の移行をする機会は、今日は来ないかもしれません。ですが次の3つは、今の仕事のどれか1つに、今日から適用できます。
- 今取り組んでいる修正や移行を1つ選び、「何をもって壊れていないと判断するか」を1行で書き出す
- 同じ指摘を2回した経験を思い出し、個別修正ではなくルール・基準そのものに反映できないか考える
- 次に何かのレビューを頼むとき、書いた本人ではなく別のセッションか別の人に見てもらう
この3つが、100万行の話から今日持ち帰れる分量です。