「いつかやらなきゃ」と思いながら手をつけられていないjQueryコード、型のないJavaScript、Create React Appのビルド——レガシーコードの移行は、動いているものを壊すリスクが常につきまとうため先延ばしにされがちです。
Claude Codeを使うと、この移行作業が劇的に変わります。Plan Modeで移行計画を自動策定し、worktreeで安全に並行作業し、移行前のテストを自動生成してから変換し、問題があれば/rewindで即座にロールバック。この記事では、jQuery→React、JS→TypeScript、CRA→Vite、Express→Honoの4つの移行パターンを具体的なコード付きで解説します。
Claude Codeが備える移行支援機能
Claude Codeには、レガシーコード移行を安全に進めるための機能が揃っています。
Plan Modeで移行計画を自動策定する
Shift+Tab 2回でPlan Modeに入ると、Claude Codeがコードを読み取り専用モードで分析し、移行計画を策定します。
# Plan Modeで起動(Shift+Tab 2回) > このプロジェクトをjQueryからReactに移行する計画を立ててください。 > 以下を含めて: > - 依存関係マップと移行優先順位 > - 各フェーズの作業内容とリスク > - 並行テスト戦略 > - 推定作業量(ファイル数・複雑度)
worktreeで安全に並行作業する
# 独立したワークツリーで移行作業を行う claude --worktree migration-react # ワークツリー内では独自のブランチ・ファイルで作業 # メインブランチを汚さず移行を試行できる # → 問題があればワークツリーを削除するだけ
/rewindで安全にロールバックする
Claude Codeは各ツール実行時に自動でチェックポイントを作成しています。/rewindを使うと、過去のチェックポイント一覧から選んで、Claude Codeが行ったファイル変更も含めて巻き戻せます。
# jQuery→Reactの変換を実行 > src/components/Modal.jsxをjQueryからReactコンポーネントに変換してください # 変換結果に問題があれば過去のチェックポイントに巻き戻し /rewind # → チェックポイント一覧が表示される # → 変換前の時点を選択 # → 「restore code + conversation」を選択して復元
/rewindはClaude Codeが行ったファイル変更を復元できますが、手動での編集やDB操作など外部への変更は戻せません。リスクの高い移行は小さなバッチで進め、各ステップで/rewindを挟みましょう。移行プロジェクト用のCLAUDE.md設定
# レガシーコード移行ルール ## 移行元 → 移行先 - jQuery → React 18 + Next.js 16 - JavaScript → TypeScript strict - CRA → Vite 8 - Express → Hono 4 ## 段階的移行の方針 - 新旧コードの共存を許容する(ビッグバン移行は禁止) - 変換前に必ずCharacterization Test(現在の動作を記録するテスト)を生成 - 1PRあたりの変更は1コンポーネントまたは1ファイルに限定 - 移行後に既存テストがすべてパスすることを確認してから次へ ## TypeScript - strict: true(anyは禁止、unknownとtype guardを使う) - as型アサーション禁止(type guard関数を使う) - @ts-ignore, @ts-expect-error禁止 ## 変換パターン - $.ajax → fetch + TanStack Query - DOM操作 → useState/useRef - jQuery plugin → React Hook or npm package - process.env.REACT_APP_ → import.meta.env.VITE_ - Express middleware → Hono built-in middleware
jQuery → React 移行パターン
jQueryからReactへの移行は「DOM操作中心」から「状態管理中心」へのパラダイムシフトです。Claude CodeにjQueryのコードを渡すと、このパラダイム変換を自動で行います。
DOM操作 → useState/useRef 変換
// jQuery: DOMが真実の源泉
$(document).ready(function() {
let count = 0;
$("#increment").on("click", function() {
count++;
$("#counter").text(count);
if (count >= 10) {
$("#counter").addClass("highlight");
}
});
$("#reset").on("click", function() {
count = 0;
$("#counter").text(count).removeClass("highlight");
});
});
import { useState } from "react";
export function Counter() {
// React: stateが真実の源泉(DOMは自動で同期される)
const [count, setCount] = useState(0);
return (
<div>
<span className={count >= 10 ? "highlight" : ""}>
{count}
</span>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
<button onClick={() => setCount(0)}>リセット</button>
</div>
);
}
$.ajax → TanStack Query 変換
// jQuery: コールバックベースのデータ取得
function loadUsers() {
$("#loading").show();
$.ajax({
url: "/api/users",
method: "GET",
success: function(data) {
data.forEach(function(user) {
$("#user-list").append(
"<li>" + user.name + " (" + user.email + ")</li>"
);
});
},
error: function() {
$("#error").text("読み込みに失敗しました").show();
},
complete: function() {
$("#loading").hide();
}
});
}
import { useQuery } from "@tanstack/react-query";
interface User {
id: string;
name: string;
email: string;
}
function useUsers() {
return useQuery<User[]>({
queryKey: ["users"],
queryFn: () => fetch("/api/users").then((r) => r.json()),
});
}
export function UserList() {
const { data: users, isPending, error } = useUsers();
if (isPending) return <div>読み込み中...</div>;
if (error) return <div>読み込みに失敗しました</div>;
return (
<ul>
{users?.map((user) => (
<li key={user.id}>
{user.name} ({user.email})
</li>
))}
</ul>
);
}
このjQueryコンポーネントをReact関数コンポーネントに変換してください。 変換ルール: - DOM操作はuseState/useRefに置換 - $.ajaxはfetch + TanStack Queryに置換 - イベントバインディングはJSXのonClick等に置換 - jQueryプラグインは同等のnpmパッケージまたはカスタムHookに置換 - 変換前の動作を検証するテストも生成 ファイル: /add src/legacy/UserModal.js
JavaScript → TypeScript 段階的移行
大規模なJavaScriptプロジェクトをTypeScriptに移行する場合、一度に全ファイルを変換するのは現実的ではありません。Claude Codeを使って段階的に進めます。
Phase 1: TypeScript導入(既存JSと共存)
このJavaScriptプロジェクトにTypeScriptを導入してください。 要件: - tsconfig.jsonを生成(allowJs: true, strict: false で開始) - package.jsonにTypeScript関連の依存を追加 - 既存の.jsファイルはそのまま維持 - 新規ファイルのみ.tsで作成するルールを設定 - eslintのTypeScript設定も追加
Phase 2: データ層から型付け開始
src/services/ 配下のJavaScriptファイルをTypeScriptに変換してください。 優先順位: 1. API型定義(src/types/ に作成) 2. APIクライアント(src/services/api.js → api.ts) 3. ユーティリティ(src/utils/*.js → *.ts) 変換ルール: - anyは使わない。不明な型はunknown + type guardで処理 - APIレスポンスはzodスキーマでバリデーション - 変換後にtsc --noEmitで型チェックが通ることを確認
Phase 3: strict有効化とany撲滅
tsconfig.jsonのstrictをtrueに変更し、 発生する型エラーをすべて修正してください。 修正ルール: - any → unknown + type guard関数 - as型アサーション → type guard関数に置換 - @ts-ignore/@ts-expect-error → 根本原因を修正 - 暗黙のanyパラメータ → 明示的な型注釈 修正後にtsc --noEmitがエラーなしで通ることを確認してください。
Create React App → Vite 8 移行
Create React App(CRA)は2025年に公式に非推奨となりました。Vite 8(2026年3月安定版)はRustベースのRolldownバンドラーを搭載し、ビルド速度が大幅に改善されています。
| 項目 | CRA | Vite 8 |
|---|---|---|
| 設定ファイル | なし(ejectが必要) | vite.config.ts |
| 環境変数プレフィックス | REACT_APP_ |
VITE_ |
| 環境変数アクセス | process.env.REACT_APP_X |
import.meta.env.VITE_X |
| index.html | public/index.html |
プロジェクトルート |
| テスト | Jest | Vitest推奨 |
| バンドラー | webpack | Rolldown(Rustベース) |
このCreate React AppプロジェクトをVite 8に移行してください。 作業内容: 1. vite.config.tsを生成(React plugin + tsconfig paths) 2. index.htmlをpublic/からルートに移動し、scriptタグを追加 3. 環境変数: REACT_APP_ → VITE_ にすべて置換 4. process.env.REACT_APP_ → import.meta.env.VITE_ にすべて置換 5. package.jsonのscriptsを更新(react-scripts → vite) 6. react-scripts をアンインストール 7. Viteの依存をインストール 8. vite buildが成功することを確認
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
build: {
outDir: "build", // CRAと同じ出力先を維持
},
resolve: {
tsconfigPaths: true, // Vite 8: tsconfig.jsonのpathsを自動解決
},
server: {
port: 3000, // CRAと同じポート番号を維持
open: true,
},
});
Express → Hono 移行
Hono 4のコアバンドルはわずか14KB(Expressのインストールサイズは約572KB)で、ベンチマークではレスポンス速度が2〜3倍高速です。TypeScriptネイティブで型推論がルートからバリデータまで自動伝搬します。
ルーティング変換
// Express
import express from "express";
const app = express();
app.use(express.json());
app.get("/api/users/:id", (req, res) => {
const id = req.params.id;
const page = req.query.page;
res.json({ id, page });
});
app.post("/api/users", (req, res) => {
const { name, email } = req.body;
// ... DB操作
res.status(201).json({ name, email });
});
// ↓ Hono(Claude Codeが変換)
import { Hono } from "hono";
const app = new Hono();
app.get("/api/users/:id", (c) => {
const id = c.req.param("id");
const page = c.req.query("page");
return c.json({ id, page });
});
app.post("/api/users", async (c) => {
const { name, email } = await c.req.json();
// ... DB操作
return c.json({ name, email }, 201);
});
ミドルウェア変換
// Express: 外部パッケージが必要
import cors from "cors";
import morgan from "morgan";
import helmet from "helmet";
app.use(cors());
app.use(morgan("dev"));
app.use(helmet());
// ↓ Hono: 組み込みミドルウェア(外部依存なし)
import { cors } from "hono/cors";
import { logger } from "hono/logger";
import { secureHeaders } from "hono/secure-headers";
app.use("*", cors());
app.use("*", logger());
app.use("*", secureHeaders());
このExpressアプリケーションをHono 4に移行してください。 変換ルール: - req.params → c.req.param() - req.query → c.req.query() - req.body → await c.req.json() - res.json() → return c.json() - res.status(201).json() → return c.json(data, 201) - express.json()ミドルウェア → 不要(Honoは自動パース) - cors, morgan, helmet → hono/cors, hono/logger, hono/secure-headers - ルーターモジュール: express.Router() → new Hono() 変換後にすべてのエンドポイントが正しく動作するかテストも生成してください。
Honoの詳しい使い方はTypeScript × Hono完全ガイドをご覧ください。
安全な段階的移行を支えるテスト戦略
移行で最も重要なのは「動いているものを壊さない」ことです。Claude CodeにCharacterization Test(既存動作を記録するテスト)を自動生成させてから変換を行います。
src/components/UserForm.jsxの現在の動作を記録するテストを生成してください。 テスト内容: - 初期表示で何が表示されるか - 各入力フィールドに値を入力したときの挙動 - 送信ボタンを押したときのAPI呼び出し - バリデーションエラー時の表示 - 成功時のリダイレクト 目的: このテストは移行後にも同じ動作をすることを検証するために使います。 テスト結果が変わったら移行に問題があるということです。
# 移行の進め方(全体フロー) # Step 1: Characterization Testを生成(移行前の動作を記録) > src/legacy/Modal.jsxの動作を記録するテストを生成して # Step 2: gitでコミット(安全なロールバック地点を作成) > テストをコミットして # Step 3: React版コンポーネントに変換 > src/legacy/Modal.jsxをReactコンポーネントに変換して > src/components/Modal.tsxに出力 # Step 4: テスト実行(移行前と同じ動作か確認) > テストを実行して全パスすることを確認して # Step 5: テスト失敗時は/rewindで変換前に戻る /rewind # → 変換前のチェックポイントを選んで復元 # Step 6: テスト成功なら次のコンポーネントへ
テスト戦略全般はClaude Codeテスト完全ガイドをご覧ください。
よくある質問
claude --worktree migrate-servicesでサービス層を、claude --worktree migrate-componentsでUI層を同時に移行できます。各ワークツリーは独立したブランチで作業するため、互いに干渉しません。ReactDOM.createRoot(document.getElementById("react-section"))でReactコンポーネントを特定のDOMノードにマウントし、残りのページはjQueryが管理する構成です。Claude Codeに「段階的移行用のブリッジコンポーネントを作って」と依頼すると、この共存コードも生成できます。allowJs: true, strict: falseで共存を開始し、新規ファイルは.tsで作成します。次にデータ層(API型定義→ユーティリティ→フック)から順に型を付け、最後にstrict: trueを有効にして残りのanyを撲滅します。CLAUDE.mdに「any禁止」を書いておけば、Claude Codeが新規コードでanyを使うことはありません。jest.fn()をvi.fn()に置換して」と依頼すると一括変換できます。まとめ
- Plan Modeで計画→worktreeで安全に実行→/rewindでロールバック: この3ステップが移行の安全網
- jQuery → React: DOM操作中心→状態管理中心へのパラダイムシフト。「葉」のコンポーネントから段階的に移行
- JS → TypeScript: allowJs共存→データ層から型付け→strict有効化の3フェーズ。CLAUDE.mdでany禁止を強制
- CRA → Vite 8: 環境変数プレフィックス置換・index.html移動・vite.config.ts生成の3ステップ
- Express → Hono: ルーティング/ミドルウェアの1:1変換。サイズ572KB→14KB、速度2〜4倍改善
- Characterization Test: 移行前に既存動作をテストで記録し、移行後に同じテストで検証する
リファクタリング全般はClaude Codeリファクタリング完全ガイド、ワークフロー設計はClaude Codeワークフロー完全ガイドもあわせてご覧ください。

