Claude Code × レガシーコード移行完全ガイド|jQuery→React・JS→TypeScript・CRA→Vite・Express→Hono【2026年最新】

Claude Code × レガシーコード移行完全ガイド|jQuery→React・JS→TypeScript・CRA→Vite・Express→Hono【2026年最新】 AI開発

「いつかやらなきゃ」と思いながら手をつけられていない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での移行計画策定プロンプト
# Plan Modeで起動(Shift+Tab 2回)
> このプロジェクトをjQueryからReactに移行する計画を立ててください。
> 以下を含めて:
> - 依存関係マップと移行優先順位
> - 各フェーズの作業内容とリスク
> - 並行テスト戦略
> - 推定作業量(ファイル数・複雑度)
Plan Modeではファイルの読み取りのみで変更は行いません。計画に納得してからExitして実行に移れるため、大規模な移行でも安心です。詳細はPlan Mode完全ガイドをご覧ください。

worktreeで安全に並行作業する

worktreeで移行ブランチを作成
# 独立したワークツリーで移行作業を行う
claude --worktree migration-react

# ワークツリー内では独自のブランチ・ファイルで作業
# メインブランチを汚さず移行を試行できる
# → 問題があればワークツリーを削除するだけ

/rewindで安全にロールバックする

Claude Codeは各ツール実行時に自動でチェックポイントを作成しています。/rewindを使うと、過去のチェックポイント一覧から選んで、Claude Codeが行ったファイル変更も含めて巻き戻せます。

移行作業中の/rewind活用
# jQuery→Reactの変換を実行
> src/components/Modal.jsxをjQueryからReactコンポーネントに変換してください

# 変換結果に問題があれば過去のチェックポイントに巻き戻し
/rewind
# → チェックポイント一覧が表示される
# → 変換前の時点を選択
# → 「restore code + conversation」を選択して復元
/rewindはClaude Codeが行ったファイル変更を復元できますが、手動での編集やDB操作など外部への変更は戻せません。リスクの高い移行は小さなバッチで進め、各ステップで/rewindを挟みましょう。

移行プロジェクト用のCLAUDE.md設定

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
// 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");
  });
});
移行後: React(Claude Codeが生成)
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 $.ajax
// 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();
    }
  });
}
移行後: TanStack Query(Claude Codeが生成)
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 変換プロンプト
このjQueryコンポーネントをReact関数コンポーネントに変換してください。

変換ルール:
- DOM操作はuseState/useRefに置換
- $.ajaxはfetch + TanStack Queryに置換
- イベントバインディングはJSXのonClick等に置換
- jQueryプラグインは同等のnpmパッケージまたはカスタムHookに置換
- 変換前の動作を検証するテストも生成

ファイル: /add src/legacy/UserModal.js
移行は「葉」のUIコンポーネント(モーダル、フォーム等の独立した部品)から始め、徐々にページ単位→ルーティング全体へと進めるのが安全です。

JavaScript → TypeScript 段階的移行

大規模なJavaScriptプロジェクトをTypeScriptに移行する場合、一度に全ファイルを変換するのは現実的ではありません。Claude Codeを使って段階的に進めます。

Phase 1: TypeScript導入(既存JSと共存)

TypeScript導入の初期設定プロンプト
この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撲滅

any撲滅プロンプト
tsconfig.jsonのstrictをtrueに変更し、
発生する型エラーをすべて修正してください。

修正ルール:
- any → unknown + type guard関数
- as型アサーション → type guard関数に置換
- @ts-ignore/@ts-expect-error → 根本原因を修正
- 暗黙のanyパラメータ → 明示的な型注釈

修正後にtsc --noEmitがエラーなしで通ることを確認してください。
TypeScriptの型システムについてはTypeScript移行ガイドで詳しく解説しています。

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ベース)
CRA → Vite 移行プロンプト
この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が成功することを確認
生成されるvite.config.ts
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,
  },
});
Vite 8ではRolldown(Rustベースの統一バンドラー)が採用され、esbuild+Rollupの二重構成が解消されました。Linear社の実績ではビルド時間が46秒→6秒に短縮されています。

Express → Hono 移行

Hono 4のコアバンドルはわずか14KB(Expressのインストールサイズは約572KB)で、ベンチマークではレスポンス速度が2〜3倍高速です。TypeScriptネイティブで型推論がルートからバリデータまで自動伝搬します。

ルーティング変換

Express → Hono ルーティング変換
// 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 → Hono ミドルウェア変換
// 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 変換プロンプト
この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(既存動作を記録するテスト)を自動生成させてから変換を行います。

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テスト完全ガイドをご覧ください。

よくある質問

Q移行は「葉」から始めるべきですか、それとも「根」から?
Aフロントエンドの移行は「葉」(末端のUIコンポーネント:モーダル、フォーム等)から始めるのが安全です。葉は他のコンポーネントへの依存が少ないため、変換しても影響範囲が限定されます。徐々にページ単位→ルーティング全体へと進めてください。バックエンドの移行(Express→Hono等)はルーティングモジュール単位で進めるのが一般的です。
Q大規模プロジェクト(100ファイル以上)の移行はどう進めますか?
AClaude Codeのworktree機能で複数の移行ブランチを並行に進めることをおすすめします。たとえばclaude --worktree migrate-servicesでサービス層を、claude --worktree migrate-componentsでUI層を同時に移行できます。各ワークツリーは独立したブランチで作業するため、互いに干渉しません。
QjQuery→Reactの移行期間中、jQueryとReactは共存できますか?
Aはい、Module Federationやiframe分離で新旧コードを共存させられます。ただし最も簡単な方法は、同じページ内でjQueryセクションとReactセクションを並べる方法です。ReactDOM.createRoot(document.getElementById("react-section"))でReactコンポーネントを特定のDOMノードにマウントし、残りのページはjQueryが管理する構成です。Claude Codeに「段階的移行用のブリッジコンポーネントを作って」と依頼すると、この共存コードも生成できます。
QTypeScript移行でanyを一気に排除すべきですか?
A一気に排除するのはリスクが高いです。まずallowJs: true, strict: falseで共存を開始し、新規ファイルは.tsで作成します。次にデータ層(API型定義→ユーティリティ→フック)から順に型を付け、最後にstrict: trueを有効にして残りのanyを撲滅します。CLAUDE.mdに「any禁止」を書いておけば、Claude Codeが新規コードでanyを使うことはありません。
QCRA→Vite移行後にテストが動かなくなりました
ACRAのJest設定はViteでは動きません。テストフレームワークをJest→Vitestに移行する必要があります。Claude Codeに「jest.config.jsをvitest.config.tsに変換して、テストファイルの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ワークフロー完全ガイドもあわせてご覧ください。