Anthropic、Claude Code で大規模コード移行を解説
本文の状態
日本語全文を表示中
詳細モードで約18分の本文を読めます。
Anthropic は、Claude Code を活用した大規模なコード移行プロジェクトの成功確度を高めるための具体的な 6 つのステップと、その初期段階におけるルールブック作成や依存関係マッピングの方法論を公開した。
AI深層分析を開く2026年7月30日 06:18
AI深層分析
キーポイント
移行プロセスの前提条件としての「ジャッジ」構築
移行プロジェクト開始前に、元コードと移行先コードを同等に評価できる検証用テスト(ジャッジ)を整備することが必須であり、これは成功基準や出口条件の定義に直結する。
テストの分類と移植可能性の確保
既存のテストを外部呼び出し可能なものと内部依存のものに分類し、前者を両環境で実行可能なアサートに変換して、移行後の挙動変化を検出する仕組みを作る。
ルールブックと依存関係マッピングの作成
コード翻訳のためのルールブックを作成し、リファクタリングが必要な箇所のインベントリや実装順序を決定する依存関係マップを構築して移行の基礎固めを行う。
反復的な検証とワークフローの改善
Jarred や Mike の事例のように、一度の実行で終わらせず結果に基づいてルールやワークフローを見直し、出力を破棄して再実行を繰り返すことで品質を高める。
ルールブックの設計方針
新コードが既存構造を踏襲するか再設計されるかでルールブックの形状が変わり、前者は言語間の翻訳テーブル、後者は設計ドキュメントとなる。
重要な引用
The process below has been generalized to be relevant to multiple languages and scenarios.
A prerequisite before starting on your migration project is to have a strong judge in place, otherwise you won't have an exit condition or measure of success.
"The gap inventory is defined by what the rulebook's defaults won't cover, and the two are tested together in a joint audit."
"For Zig to Rust the difference was manual memory management (C and C++ work the same way)."
編集コメントを表示
編集コメント
このブログ記事は、AI を活用したコード移行が単なる「翻訳」の域を超え、厳密な検証プロセスを伴う本格的なエンジニアリング活動であることを示唆している。開発現場では、ツールへの依存度が高まる中で、人間の判断と AI の実行力を組み合わせた新しいワークフローの確立が求められている。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
コードの大規模移行のための 6 つのステップ
以下のプロセスは、複数の言語やシナリオに適用できるよう一般化されています。詳細については、Jarred のブログをご覧ください。
事前準備
移行プロジェクトを開始する前に最も重要なのは、「合格・不合格を判定する厳格な審査基準(ジャッジ)」を用意することです。これがないと、いつ完了したと言えるかの判断基準や、成功の指標が定義できません。
この審査基準は、元のコードと移行後のコードを公平に比較評価できるものでなければなりません。元の言語で書かれたテストスイートには、移行先のコードには存在しない内部関数への依存が含まれていることが多いためです。
このジャッジを作成するには、以下の手順を踏みます。
- 既存のテストを分類する: Claude を使って、外部呼び出しとして表現可能なテストと、移植できない内部機能に依存するテストを識別します。
- 移植性を高めるよう書き直す: 外部からアクセスされるテストを、元のコードと移行後のコードの両方で実行できるアサーション(検証)に変換します。さらに、敵対的なエージェントを用いて、書き換えによってアサーションが緩んでいないかを確認します。
- ジャッジを検証する: まず元のコードに対して実行し、合格することを確認します。次に、意図的に壊したコードを実行して不合格になることを確認します。バグを見逃さないジャッジでなければ意味がありません。
Jarred のケースでは、TypeScript といった第三者の言語で書かれた大規模なテストスイートが存在しましたが、これは例外です。多くのプロジェクトではそうではありません。Mike は Python から TypeScript への移行において、7 つの実世界シナリオからなる同等性のハーン(検証枠組み)を作成し、動作の変化があればすべてバグとして扱う方針を採用しました。
各段階に入る前に、以下の図を参考にすると理解しやすくなります。このプロセスは主に Jarred の手法に基づいていますが、各段階にレビューと承認のゲートを設けています。Mike も同様の全体構造とループワークフローを採用しましたが、彼は移行を最初から最後まで実行し、その結果に基づいてルールやワークフローを見直して再実行しました。出力は毎回破棄され、3 回目の実行までこれを繰り返しています。

ステップ 1 — ルールブック、依存関係マップ、ギャップインベントリの作成

この段階では、移行の基盤となる 3 つの要素を整備します。具体的には、単なる翻訳ではなくリファクタリングが必要な箇所を特定する「インベントリ」、コード変換のためのルールを定めた「ルールブック」、そして移行の実施順序を決めるための「依存関係マップ」です。
これらを作成する順番は重要です。まずルールブックを整え、その後にギャップインベントリを作成する必要があります。なぜなら、ギャップインベントリとはルールブックのデフォルト設定ではカバーしきれない部分を定義したものであり、両者は共同監査を通じて互いに検証されるからです。
ルールブック
ルールブック の具体的な形式は、移行開始時に下す重要なアーキテクチャ上の判断によって決まります。特に重要なのは、新コードが既存の構造を踏襲するのか、それとも完全に再設計されるのかという点です。
前者(Jarred氏)の場合、ルールブックは主に型や言語間の慣習を翻訳するための参照テーブルとなり、難易度の高いコンポーネントについてはギャップインベントリへのリンクを含める形になります。後者(Mike氏)の場合は設計ドキュメントとなります。
Jarred氏は Claude とチャットしながらルールブックを作成し、曖昧さの生じる各領域ごとにポリシーを策定しました。また、自身の直感に基づき 8 つのカテゴリに分類された一般的な失敗モードを検証するための専用サブエージェント 8 体を設定して活用しています。
依存関係マップ
並列移行で作業を効果的に分割するためには、ファイル間の依存関係を把握しておく必要があります。どのファイルを最初に移行し、どのファイルを同じバッチに含めるべきかを明確にするためです。一部の言語やコードベースでは明示的なマニフェストが存在してこの作業が容易ですが、レガシーなコードベースや C/C++、Python といった多くの人気言語では、依存関係を発見してマップ化する必要があります。
Claude Code を使用すれば、エージェントをデプロイして決定論的なスクリプトを作成・実行し、このマップを生成させることが可能です。移行キットに含まれるプロンプト では、レビューと修正のループを行うワークフローを採用しています。*注:スターターキットは本記事で解説したプロセスを一般化したテンプレートであり、今回行った特定のポート変換にそのまま使用されたものではありません。*
移行ギャップの整理と懐疑的なレビュー
新しい言語には、旧言語とは異なる要件を満たす必要があります。例えば、Zig から Rust への移行では「手動メモリ管理」が大きな違いでした(C や C++ も同様に手動管理です)。具体的には以下のようになります。
Zig
fn readConfig(allocator: std.mem.Allocator) ![]u8 {
const buf = try allocator.alloc(u8, 1024);
// ...buf にデータを充填...
return buf; // コール側で解放する必要があるが、これはコメントにしか書かれていない
}
// 'defer allocator.free(buf)' を忘れた呼び出し元でもコンパイルは通る——メモリリークは実行時まで表面化しない。Rust
fn read_config() -> Vec<u8> {
let buf = vec![0u8; 1024];
// ...buf にデータを充填...
buf // 所有権が呼び出し元に移動し、メモリは自動的に解放される
}
// 移動後にこれを使う?二度解放する?どちらもコンパイルエラーになる。
// 解放を忘れる?forget を忘れるような free コール自体が存在しない——drop は自動的に行われる。一方、Python から TypeScript への移行では、「インターフェースと契約」の存在が大きなギャップとなりました。Python では、受け取るオブジェクトの形状や返却値を宣言する契約(型定義)は必須ではありません。しかし、TypeScript ではそれが求められます。具体例を見てみましょう。
Python
def register(handler):
handler.setup()
return handler.run({"retries": 3})
# .setup() と .run() メソッドさえあれば、どんなオブジェクトでもここで動作する。
# 実際にどのようなオブジェクトが渡されるのか?コードベース全体を読み込んで初めて分かる。TypeScript
interface RunResult { ok: boolean }
interface Handler
{ setup(): void;
run(opts: { retries: number }): Promise<RunResult>;
}
function register(handler: Handler): Promise<RunResult> {
handler.setup();
return handler.run({ retries: 3 });
}コンパイル前に契約書(ルール)を明文化しておく必要があります。Jarred と Mike は、この暗黙知を捉えた「ギャップインベントリファイル」を作成しました。Jarred は事前にこれらのギャップを棚卸しするアプローチを取りましたが、Mike はまず翻訳を実行した後に監査を行い、その結果としてギャップインベントリを作成するという方法を選びました。状況によっては、両方の手法を組み合わせる必要があるかもしれません。
ギャップインベントリファイル作成のための Claude Code プロンプトのサンプルは、こちらをご覧ください:Claude Code prompt to create a gap inventory file。
ステップ 2 — ルールの耐性テスト

このステップでは、大規模な移行前の「試運転」として、小規模なマイグレーションを実施します。
ここでは Jarred が 3 つのファイルについて、ルールブックに従って翻訳するエージェント、シニア Rust エンジニアのように振る舞うエージェント、そして差分(diff)を分析して新しい翻訳ルールを作成するエージェントという、3 つの異なるアプローチでテストを行いました。その結果、1,448 ファイルすべてに展開された場合、深刻な問題を引き起こす可能性があった 2 つの重大な課題を発見することができました。
プロンプトは、以下のような内容になる可能性があります:this。
この種の負荷テストは、2 つの翻訳結果をファイルごとに比較できる「構造維持型」の変換にのみ有効です。マイクのようにルールブック自体を再設計する場合は、代替案として設計ドキュメントに対して敵対的なレビューアーを投入し、その後、使い捨ての完全な実行環境で検証を行うテストが必要です。
いずれにせよ、翻訳済みのファイルはすべて破棄してください。今回の目的は漸進的な改善ではなく、ルールそのものを洗練させることです。
ステップ 3 — 全ファイルを翻訳する

残りのステップでも、実装・レビュー・修正を行うマルチエージェントループアーキテクチャを同じように適用します。
実作業は小規模なモデルに任せ、レビュー担当には大規模モデルを使用するのが効率的です。例えばマイクは、主要な移行タスクで 12 のサブエージェントを展開する際に Claude Sonnet を活用しました。
作業キューは機械的に管理すべきです。バッチスクリプトがディスク上に翻訳済みファイルが存在するか確認して完了判定を行い、未処理のファイルをバッチ単位で実装エージェントに割り当てます。キューは毎回ディスクの状態から再構築されるため、移行プロセスは本質的に再開可能になっています。
この段階では、エージェントが作業範囲を必要以上に慎重に設定してしまう傾向があります。その対策として、コンパイラが次のステップでミスを検出できるという文脈を含めた、明確かつ強い指示をプロンプトに追加します。
翻訳ツールが確信を持って実行できない箇所は、// TODO(port): <理由> という形式でフラグを立てておき、ステップ 4 で対応します。ここからはToDo リストが自動的に生成されます。コンパイラーがエラーを列挙し、スモークテストがクラッシュを検出し、テストスイートが失敗を報告するのです。
実装者の作業は、2 人の異なる文脈を持つ審査員が評価します。もし両者の意見が食い違った場合は、3 番目のエージェントに判断を委ねます。あるファイル群で同じミスを繰り返し見つけた場合、その都度個別の修正を行うのではなく、ルールブックに 1 行追加して該当バッチを再生成します。このステップを通じてルールブックは成長し続けますが、コードに対して手動でパッチを当てることは決してありません。
このステップで特に重要な設計判断の一つが、コンパイラーの配置場所です。マイクは TypeScript コンパイラーをループ内(各単位ごとに)で実行しました。なぜなら、1 単位のチェックには数秒しかかからないからです。一方、ジャレッドは Cargo の実行に数分かかるため、コンパイラーをループから完全に排除し、次のステップへ延期する方針をとりました。
この段階では、多くの重労働が完了しており、プロンプトも短くなり始めています。
ステップ 4, 5, 6 — コンパイル、実行、動作の一致確認

これら 3 つのステップは同じループ構造を共有しており、人間の判断に依存する部分が段階的に減っていくため、まとめて解説します。
ステップ 4 は、移行対象の言語や規模によっては、実際にはステップ 3 に統合されることがよくあります。
コンパイラ処理の規模や難易度によっては、エージェントがこれを実行しないケースもあります。Jarred は、ワークスペース全体で一度だけコンパイラを呼び出すオーケストレーションスクリプトを使ってこの工程を遂行しました。その後、「修正用エージェント」がエラーリストを並列処理しながら、敵対的なレビュー(アドバーサリアル・レビュー)を行いました。ビルドを再度実行し、これを繰り返します。
エラーリストを確認することは、調整が必要なシステム全体の課題を発見する上で役立ちます。例えば、Jarred は Zig の遅延コンパイルが許容していた循環参照のインポートを修正した結果、数千件の Rust モジュールエラーに直面しました。彼は依存関係を削除するか、移動させるか、境界を再構築すべきかを分類するロジックを実装することで、このループ問題を解決しました。
ステップ 5 もまた、コンパイラのエラーリストと同様に、機械的な真実の根拠を持っています。それが「スモークテスト」でのクラッシュです。先ほどのループ修正のように、ここでは根本原因に基づいて課題をカテゴリ化し、敵対的なサブエージェントがその原因を検証する仕組みを採用しました。
ステップ 6 は、物語の結末となる部分で、2 つのコードベース間でプログラムの挙動を比較します。
ファイルはすでに翻訳され、コンパイル済み、スモークテストも完了しています。次は、これらを分割(シャーディング)して、事前準備段階から用意したテストスイートを実行する時です。失敗したテストに対して「修正用エージェント」が両方のコードベースを比較検討し、対応します。その修正内容については、敵対的なレビュアーがチェックを行います。
このループの次の段階は、ビルドデーモン です。これはバイナリを再構築できる唯一のプロセスです。修正担当者はパッチを作成しますが、デーモンがそれらをまとめて一度に再構築し、影響を受けたテストを再実行して結果をフィードバックします。これにより、最もコストのかかる処理を複数のエージェントが独立してトリガーするのを防ぎ、シリアル化して効率化しています。
同じ失敗が多数のテストで繰り返される場合、修正はより上位の段階で行われます。バグの原因となったルール自体を修正し、そのルールが影響したファイルだけを再生成します。
マイクのこのアプローチが重要なのは、多くの開発者が十分に整備されたテストスイートや移植済みの環境を持っていないからです。マイクは Claude に、新しい移植版と元の Python コードベースの両方で 7 つの実世界シナリオを実行する小さなスクリプトを作成させ、その結果を比較しました。各失敗したシナリオには専用の修正エージェントが割り当てられ、すべてが成功するまでこのループが回されました。
さらに一歩踏み出し、Claude は自らエンドツーエンドのテストスイートを設計し、夜間に自律的に実行しました。壊れた箇所を修正して 4 晩連続で再実行した結果、事前にリストアップされたシナリオでは予測できなかった些細な不具合(ペーパーカット)まで見つけることができました。
ここでの教訓は、テストスイートが欠けていてもこのステップが阻害されるわけではないということです。既存の審査役を引き継げない場合は、Claude に作成させればよいのです。どちらにせよ、元のコードベースこそが絶対的な真実(グランドトゥルース)です。
コードマイグレーションのベストプラクティス
これまでの実行を通じて、前回とは異なる教訓をいくつも得ました。あなたの次のマイグレーションでも、このガイドではカバーしきれない新たな課題が現れる可能性が高いでしょう。しかし、どのプロジェクトでも通用するいくつかの原則は存在します。
- このガイドを盲目的に鵜呑みにしないでください。各マイグレーションは状況が異なります。まずはこれを出発点として捉え、具体的なマイグレーション計画を立てる前に Claude へ相談することをお勧めします。
- 個々の失敗に一喜一憂する必要はありません。個々のエラー処理はループの役割です。修正エージェント(Fixer agents)がそれらを解消してくれます。あなたが注力すべきは、パターンや傾向の把握にあります。
- レビューは対立的に、検証は機械的に行ってください。対立的なレビューを採用すれば、実行時間の長いタスクも許容され、トークン消費に見合う価値があります。審判役にはスクリプト(コンパイラ、差分ツール、テストスイートなど)を任せてください。
- すべてで最大規模のモデルを使う必要はありません。トークンコストはループ内で集中するため、ここは慎重に設計する必要があります。小規模なモデルでも、大量の実装タスクを並列処理する「ファンアウト」には十分対応できます。最大規模のモデルは、レビュー役や、他のエージェントが従うべきルールを記述する役割に限定しましょう。
- 人的リソースは初期段階で集中投入してください。ルールブックの作成とストレステストが最も時間を要します。その後の工程は、主にキュー(待機列)が解消される作業になります。
- ワークキューは機械的かつ再開可能に設計してください。「完了」の定義は「出力ファイルがディスク上に存在すること」と明確に定めておきましょう。
コードそのものより、レビューループの結果を確認する
Jarred による Bun への移行はすでに本番環境で稼働していますが、すべての移行にはトレードオフが伴います。例えば、Rust コードの約 4% は「unsafe」ブロック内に存在しており、これらは主に C/C++ の境界線で行われる単一行のポインタ操作です。
しかし、新しいコードベースは明らかに改善されています。チームのツールで検出可能なメモリリークはすべて修正済みです。2,000 回のビルドを繰り返すベンチマークでは、使用メモリの最大値が 6,745 MB から 609 MB に劇的に減少しました。また、バイナリサイズも Linux と Windows で 19% 縮小されています。さらに、異言語間の最適化により、HTTP サービングや次世代のビルド(next build)、TypeScript コンパイラ(tsc)といった実世界のワークロードにおいて、2〜5% の速度向上が実現しました。
長年先延ばしにしてきた移行を見直す時期ではないでしょうか。これまで我慢して使ってきたコードベースを選び、Claude にその移行プロセスをシミュレーションさせてみてください。
*関連記事*
- 移行用スターターキット:注釈として、このスターターキットは上記の工程を一般化したテンプレートであり、今回の特定のポートで実際に使用されたものとは異なります。
- コード近代化プラグイン:言語間の移植ではなく、レガシーシステムの近代化やフレームワークのアップグレード向けに設計されています。
- Claude Code における動的ワークフロー
AI算出
技術分析ainew評価標準
AI エージェントによる開発ワークフローの実践事例として、単なる製品紹介ではなく再現可能な工程と戦略を提供しているため技術分析とし、新規性は既存の発表を深掘りした追報・解説レベルと判断した。
6つの評価軸を見る
- AI関連度
- 75
- 情報源の信頼性
- 100
- 新規性
- 50
- 調べる価値
- 75
- 重複の少なさ
- 100
- 日本での有用性
- 25
同じ出来事を2媒体で確認
同じ出来事を扱う別媒体の記事です。見出しと公開時刻を比較できます。
関連記事
News to Guide
ニュースの次に確認する
発表内容を、現在の料金や仕様と照らし合わせられる関連ガイドです。
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み