Anthropic、Claude Code で大規模コード移行を解説
本文の状態
日本語全文を表示中
詳細モードで約18分の本文を読めます。
Anthropic は Claude Code を活用した大規模コード移行の具体的な手法を公開し、テストスイートの再構築からルール策定に至るまでの6段階プロセスを提示している。
AI深層分析を開く2026年7月29日 18:52
AI深層分析
キーポイント
評価者(Judge)の構築と検証
移行プロジェクトの成否を測るための強力な評価者を事前に用意する必要があり、既存テストの分類や外部呼び出しへの書き換え、破損コード検知による検証が必須となる。
ルールブックと依存関係マップの作成
コード翻訳のためのルールブックを作成し、リファクタリングが必要な箇所を特定するギャップインベントリと、実装順序を決める依存関係マップを整備する。
反復的な実行と改善のサイクル
Jarred や Mike の事例のように、一度目の実行結果に基づいてルールやワークフローを修正し、出力を破棄して再度実行するプロセスを繰り返すことで品質を高める。
多言語・多シナリオへの一般化
特定の言語に依存せず、TypeScript や Python など複数の言語やシナリオに適用可能な汎用的なフレームワークとしてこのプロセスが設計されている。
ルールブックの設計方針
新コードが既存構造を踏襲するか再設計するかによって、ルールブックは言語間の翻訳テーブルまたは設計ドキュメントとして形状を変える。
重要な引用
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 order matters: the rulebook must come before the gap inventory.
The exact shape of the rulebook depends on key architectural decisions you must make at the start.
You need to understand file dependencies to effectively break up workstreams for a parallel migration so you know which files to migrate first and which files to contain in the same batch.
編集コメントを表示
編集コメント
大規模なコード移行は通常、人的コストとリスクが非常に大きいが、この記事はそれを 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 // 所有権が呼び出し元に移動し、メモリは自動的に解放される
}
// 一度移動した後にこれを使う?二度解放する?どちらもコンパイルエラーになる。
// 解放を忘れる?解放するコード自体が存在しない——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 プロンプトでギャップインベントリを作成する。
ステップ 2 — ルールの耐性テスト

このステップは、大規模な移行に向けた「試運転」のようなミニ移行です。
ここでは Jarred が 3 つの異なるアプローチを試しました。1 つ目はルールブックに従って 3 ファイルを翻訳するエージェント、2 つ目は「シニア Rust エンジニアのように振る舞う」エージェントで 3 ファイルを翻訳するもの、そして 3 つ目は差分(diff)を分析して新しい翻訳ルールを作成するエージェントです。この段階で、もし全 1,448 ファイルに拡大していたら深刻な問題を引き起こしたであろう 2 つの重大な課題を発見できました。
プロンプトは以下のような内容になります:こちら。
この種の負荷テストは、2 つの翻訳結果をファイル単位で一行ずつ比較できる「構造維持型」の移行にのみ有効です。マイクのようにルールブック自体を再設計する場合は、代わりに設計ドキュメントに対して敵対的なレビューアーによる攻撃を行い、その後、使い捨ての完全な実行(エンドツーエンド・ラン)で検証するのが等価なテストとなります。
いずれにせよ、翻訳済みのファイルはすべて破棄してください。今回の目的は漸進的な改善ではなく、ルール自体を洗練させることです。
ステップ 3 — 全ファイルを翻訳する

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

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