言語を切り替える
テーマを切り替える

Cursor でコードリファクタリング?効率を倍増させるテクニック

こんな経験はありませんか?

プロジェクトを引き継いでコードファイルを開くと、200行を超える関数があり、if-else が7、8層もネストされ、変数名は data1temp2 のような意味不明なものばかり。「これ誰が書いたんだ?」と心の中で毒づきながら git blame を確認すると、3ヶ月前の自分だった…という経験です。

リファクタリングしたいと思っても、バグを作り込むのが怖くて手が出せない。かといって放置すれば、仕様変更のたびに地雷原でダンスするような気分になります。

正直に言うと、以前の私もそうでした。しかし Cursor のコードリファクタリング機能を見つけてからは、AI は単にコードを書くだけでなく、リファクタリングに関しては人間以上に頼りになることに気づきました。

今日は、Cursor を使って「スパゲッティコード」を人間が読める形にリファクタリングする方法についてお話しします。

Cursor のリファクタリングは結局どこが凄いのか?

具体的な使い方の前に、なぜ私が Cursor をリファクタリングに最適だと思うのかをお話しします。

コードベース全体を本当に理解している

従来の IDE のリファクタリングツールは構文解析に基づくものが多いですが、Cursor はあなたのコードを「読んで」理解します。関数名を変更するとき、単に呼び出し元を変えるだけでなく、その関数がシステム全体でどんな役割を果たしているかを理解し、より合理的なリファクタリング提案をしてくれます。

Agent モードでファイルを跨いでリファクタリングできる

私が最も気に入っている機能です。例えば、あるユーティリティ関数をファイル A からファイル B に移動したい場合、従来の手順は「コードをコピー → 古いコードを削除 → 全ての import を更新 → 何も壊れていないことを祈る」でした。

Cursor の Agent モードを使えば、「この関数を utils.js に移動して」と伝えるだけで、全ての参照更新を自動的に処理してくれます。初めて使ったときは本当に驚きました。

Plan モード:実行前に計画を立てる

複雑なリファクタリングには、Cursor の Plan モード(Shift+Tab で有効化)が役立ちます。コードベースを分析し、いくつかの確認質問をした後、どのファイルをどう変更するかという詳細な実行計画を提示してくれます。あなたが OK を出して初めて実行に移ります。

まるでベテランプログラマーがコードレビューをしてくれているようで、多くの落とし穴を事前に回避できます。

実戦1:関数を抽出して責務を明確にする

実際のシナリオを見てみましょう。

以前メンテナンスしていた注文処理システムに、こんな関数がありました(簡略版):

function processOrder(order) {
  // 注文の検証
  if (!order.items || order.items.length === 0) {
    throw new Error('注文が空です');
  }
  if (!order.userId) {
    throw new Error('ユーザー情報が不足しています');
  }

  // 価格計算
  let total = 0;
  for (let item of order.items) {
    let price = item.price;
    if (item.discount) {
      price = price * (1 - item.discount);
    }
    total += price * item.quantity;
  }

  // 在庫チェック
  for (let item of order.items) {
    const stock = db.getStock(item.productId);
    if (stock < item.quantity) {
      throw new Error(`${item.name} の在庫が不足しています`);
    }
  }

  // 注文レコード作成
  const orderRecord = {
    id: generateId(),
    userId: order.userId,
    items: order.items,
    total: total,
    status: 'pending',
    createdAt: new Date()
  };

  db.saveOrder(orderRecord);
  return orderRecord;
}

この関数は40行以上あり、検証、計算、在庫チェック、保存という4つのことを行っています。ロジックは複雑ではありませんが、読むのは疲れ、テストもしにくい状態です。

Cursor で関数を抽出する

私は以下のように操作しました:

  1. 検証ロジック部分を選択(冒頭のいくつかの if 文)
  2. ショートカットキーを押す Cmd/Ctrl + K(Cursor 編集パネルを開く)
  3. 指示を入力validateOrder 関数として抽出

Cursor は自動的に以下を生成します:

function validateOrder(order) {
  if (!order.items || order.items.length === 0) {
    throw new Error('注文が空です');
  }
  if (!order.userId) {
    throw new Error('ユーザー情報が不足しています');
  }
}

そして元のコードを validateOrder(order); に置き換えます。

同様の方法で、calculateTotalcheckStockcreateOrderRecord の3つの関数も抽出しました。

最終的なコードはこうなりました:

function processOrder(order) {
  validateOrder(order);
  const total = calculateTotal(order.items);
  checkStock(order.items);
  const orderRecord = createOrderRecord(order, total);
  db.saveOrder(orderRecord);
  return orderRecord;
}

関数はわずか6行になり、業務フローが一目で分かるようになりました。

注意事項(避けるべき落とし穴)

1. AI に明確な抽出意図を伝える

単にコードを選択してショートカットキーを押すだけでは、Cursor は意図を正確に推測できないことがあります。「検証関数として抽出」や「この計算ロジックを独立した関数にする」と明確に伝えましょう。

2. 関数名の確認

AI が生成する関数名は、handleDataprocessItems のように一般的すぎることがあります。名前が不明瞭な場合は、calculateOrderTotalvalidateUserPermissions のように具体的な名前に手動で変更しましょう。

3. パラメータと戻り値の確認

抽出後、関数のパラメータが過不足なく渡されているか確認してください。AI が不要な変数まで渡してしまうことがあります。

実戦2:ネストされたロジックを最適化し、コードをフラットにする

深すぎるネストもよくある問題です。

例えばこの権限チェックコード:

function canUserEditPost(user, post) {
  if (user) {
    if (user.role === 'admin') {
      return true;
    } else {
      if (post.authorId === user.id) {
        if (post.status === 'draft') {
          return true;
        } else {
          return false;
        }
      } else {
        return false;
      }
    }
  } else {
    return false;
  }
}

この「アロー型」コードは読むのが非常に辛く、ロジックを一層ずつ追っていく必要があります。

Cursor に最適化させる

私は Chat モードを直接使います:

  1. このコードを選択
  2. Cursor Chat を開く(Cmd/Ctrl + L
  3. 入力:このコードはネストが深すぎる。早期リターン(early return)を使ってリファクタリングして

Cursor が提示した最適化バージョン:

function canUserEditPost(user, post) {
  if (!user) return false;
  if (user.role === 'admin') return true;
  if (post.authorId !== user.id) return false;
  return post.status === 'draft';
}

コードは17行から5行に短縮され、ロジックが一目瞭然になりました。

最適化テクニックのまとめ

早期リターン(Early Return)
条件を満たさない場合は即座に return し、深いネストを回避します。

ガード節(Guard Clauses)
例外的な状況や境界条件を関数の冒頭で処理してしまいます。

条件判定の抽出
条件が複雑な場合は、独立した関数に抽出します。例えば:

function isPostEditable(post) {
  return post.status === 'draft';
}

function isPostOwner(user, post) {
  return post.authorId === user.id;
}

これでメインロジックがさらに明確になります。

実戦3:型注釈を追加し、コードを安全にする

JavaScript や Python を使っていて型システムがない場合、リファクタリング時に問題が起きやすいです。関数の戻り値を変更しても、呼び出し元が気づかない可能性があります。

そんな時、Cursor に型注釈を追加してもらうのが非常に有効です。

TypeScript 型推論

JavaScript の関数があるとします:

function getUserInfo(userId) {
  const user = db.getUser(userId);
  return {
    name: user.name,
    email: user.email,
    age: calculateAge(user.birthDate)
  };
}

これに TypeScript の型を追加したい場合:

  1. 関数を選択
  2. Cursor Chat に入力:この関数に TypeScript の型注釈を追加して

Cursor はコードのコンテキストを分析し、以下を生成します:

interface UserInfo {
  name: string;
  email: string;
  age: number;
}

function getUserInfo(userId: string): UserInfo {
  const user = db.getUser(userId);
  return {
    name: user.name,
    email: user.email,
    age: calculateAge(user.birthDate)
  };
}

パラメータと戻り値の型を推論するだけでなく、UserInfo インターフェースも定義してくれました。

Python Type Hints

Python も同様です。以下のようなコードに対し:

def calculate_discount(price, user_level):
    if user_level == 'vip':
        return price * 0.8
    elif user_level == 'premium':
        return price * 0.9
    else:
        return price

Cursor に型を追加させると:

def calculate_discount(price: float, user_level: str) -> float:
    if user_level == 'vip':
        return price * 0.8
    elif user_level == 'premium':
        return price * 0.9
    else:
        return price

型ヒントがあれば、IDE はより良いコード補完を提供でき、リファクタリング時の型互換性チェックも可能になります。

実戦4:Agent モードによる大規模リファクタリング

ここまでは単一ファイル内のリファクタリングでしたが、複数ファイルにまたがる場合はどうでしょう?

例えば、UserService クラスを services/user.js から services/user/UserService.js に移動し、関連する補助関数も独立したファイルに分割したいとします。

このようなファイル定規のリファクタリングは、手動で行うと漏れが発生しやすく、変更後に動かなくなる可能性が高いです。

Agent モードの起動

ここで Cursor の Agent モードの出番です。

  1. Cursor Chat を開く
  2. “Agent” モードをクリック(または @agent と入力)
  3. リファクタリングの目標を記述:
UserService クラスを独立したモジュールにリファクタリングして:
- services/user/UserService.js に移動
- 補助関数 formatUserData, validateEmail を services/user/utils.js に移動
- これらの関数を参照している全ての箇所を更新

Plan モードでリファクタリングを制御下に置く

複雑なリファクタリングには、Plan モードをお勧めします。Agent 入力欄で Shift+Tab を押すと、Cursor は Plan モードに切り替わります。

コードベースを分析し、実行計画を出してくれます:

📋 リファクタリング計画

1. 新しいファイル構造の作成
   - services/user/UserService.js
   - services/user/utils.js

2. UserService クラスの移動
   - services/user.js から services/user/UserService.js へ移動
   - export default UserService を追加

3. 補助関数の移動
   - formatUserData → services/user/utils.js
   - validateEmail → services/user/utils.js

4. 参照の更新(5つのファイルで更新が必要)
   - controllers/userController.js
   - routes/userRoutes.js
   - tests/userService.test.js
   - ...

実行を確認しますか?(y/n)

この計画を確認し、直接編集することも可能です。問題なければ y を入力すると、Cursor は計画通りに実行します。

プロセス全体が完全に自動化されており、すべての import 文も同期して更新されます。

Agent モードの適用シーン

  • クラス/関数/変数の名前変更(複数ファイルにまたがる場合)
  • モジュールの分割または統合
  • 新しいファイル構造へのコード移行
  • 特定パターンの⼀括置換(例:すべての varconst に変更)

リファクタリング後の検証を忘れずに

AI がどれほど賢くても、リファクタリング後のコードは自分で検証する必要があります。

私のチェックリスト

1. 全テストの実行

npm test

テストが失敗した場合、コードの問題かテストの更新が必要かを優先的に確認します。

2. 型エラーのチェック(TypeScript プロジェクト)

npm run type-check

3. AI の変更内容をコードレビュー
git diff を使って AI が何を変更したか確認します。意図しない変更が含まれていることがあります。

4. 重要なパスの手動テスト
特にビジネスロジックに関わるリファクタリングの場合、メインフローは必ず手動で実行して確認します。

5. 修正漏れの確認
古い関数名や変数名を検索し、すべて更新されているか確認します。

AI リファクタリングでのバグ混入を防ぐために

小刻みなイテレーション
一度に大量のリファクタリングを行わないでください。1つの関数を変更し、テストが通ったら次へ進みます。

Git 履歴をクリーンに保つ
リファクタリングが完了するたびにコミットします。問題が発生してもすぐにロールバックできます。

git add .
git commit -m "refactor: 注文検証ロジックを独立関数として抽出"

AI に変更理由を説明させる
AI の変更理由が理解できない場合は質問しましょう:

なぜこのパラメータをオプショナルに変更したのですか?

AI は推理過程を説明してくれるので、その変更が合理的か判断する助けになります。

ベストプラクティス:Cursor リファクタリングをより効率的に

これまでの使用経験から、効率を高めるいくつかのコツをまとめました:

1. 実行前にまず議論する

いきなり AI にコードを書かせないでください。まず Ask モード(Cursor Chat のデフォルトモード)でリファクタリング案を議論します:

この200行の関数をリファクタリングしたいのですが、何か提案はありますか?

AI がいくつかのアイデアを出します。それに同意できたら、Agent モードに切り替えて実行します。

2. @ でコンテキストを引用する

リファクタリングが複数ファイルに関わる場合、@ で関連コードを引用します:

@services/user.js @controllers/userController.js
ユーザー認証ロジックを controller から service に移動したい。インターフェースは変更しないで。

これで AI は意図をより正確に理解できます。

3. タスクの難易度を調整する

  • AI が一度で処理できるなら、タスクを追加する
  • 頻繁に失敗するなら、タスクを細分化する

例えば:

  • 簡単すぎる:「この関数を抽出して」
  • 適切:「ユーザー認証モジュールをリファクタリングし、ログイン、登録、パスワードリセットの3ファイルに分割して」
  • 複雑すぎる:「バックエンドシステム全体の権限モジュールをリファクタリングして」

4. リファクタリング後に要約を生成させる

リファクタリング完了後、AI に PR の説明文を生成させることができます:

今回のリファクタリングで行った変更を要約して

AI は構造化された要約を出力し、そのまま Pull Request に使用できます。

5. 複雑なリファクタリング計画を保存する

大規模なリファクタリングの場合、Plan モードで生成された計画を .cursor/plans/ ディレクトリに保存できます。

メリット:

  • チームメンバーがリファクタリングの考え方を確認できる
  • 中断しても再開できる
  • 次回同様のリファクタリングをする際の参考になる

最後に

数ヶ月間 Cursor でコードリファクタリングを行ってきて感じるのは、AI は本当に多くの「汚れ仕事」を肩代わりしてくれ、アーキテクチャや設計を考えることに集中させてくれるということです。

しかし忘れてはならないのは、AI はアシスタントであり、ベビーシッターではないということです。リファクタリングの方針、コード分割の判断、最終的なコードが期待通りかの確認、これらの決定権はあなたの手にあります。

最後に1つアドバイスです:小さなところから始めてください。いきなりコアモジュールをリファクタリングするのではなく、重要度の低いユーティリティ関数で練習してみましょう。Cursor のリファクタリング能力に慣れてきたら、徐々に範囲を広げていってください。

結局のところ、どんなに良いツールでも、使いこなしてこそ価値があります。

Cursor を使ったリファクタリングであなたの開発ライフがどう変わったか、ぜひコメントで教えてください。

Cursor を使ったコードリファクタリングの標準フロー

単一ファイルの関数抽出から複数ファイルにまたがる Agent リファクタリングまでの完全な手順

⏱️ Estimated time: 30 min

  1. 1

    Step1: 単一ファイルでの関数抽出

    抽出したいコードブロックを選択 → Cmd/Ctrl+K で編集パネルを開く → 「xxx関数として抽出」と入力 → 関数名、パラメータ、戻り値をチェック。40行以上の長い関数に適しています。
  2. 2

    Step2: ネストロジックのフラット化

    ネストされたコードを選択 → Cmd/Ctrl+L で Chat を開く → 「早期リターン(Early Return)を使ってリファクタリング」と入力 → ガード節スタイルのコードを取得。複雑な条件はさらに独立した関数へ抽出。
  3. 3

    Step3: 型注釈の追加

    関数を選択 → Chat に「TypeScript 型注釈を追加」または「Python type hints を追加」と入力 → AI がパラメータと戻り値の型を推論し、必要に応じてインターフェースを定義。
  4. 4

    Step4: Agent によるクロスファイルリファクタリング

    Chat を開き Agent モードに切り替え → 目標(移動/名前変更/モジュール分割)を記述 → 複雑なタスクは Shift+Tab で Plan モードを使い、計画を確認してから実行。
  5. 5

    Step5: 検証とコミット

    npm test / type-check を実行 → git diff で変更をレビュー → 重要なパスを手動テスト → 小刻みに commit する(例:refactor: 注文検証ロジックを独立関数として抽出)。

FAQ

Cursor のリファクタリングと従来の IDE リファクタリングの違いは?
従来の IDE は主に構文解析による名前変更や移動を行いますが、Cursor はコード全体の意味とプロジェクト内での役割を理解し、より業務に適したリファクタリング提案を行います。さらに Agent はファイルを跨いだ参照変更が可能で、Plan モードで計画を立ててから実行できるため、大規模リファクタリングに適しています。
関数抽出時に AI の命名が不正確な場合は?
生成後、calculateOrderTotal や validateUserPermissions のように具体的な名前に手動で変更してください。同時にパラメータが過不足なく渡されているか確認し、必要なら AI に「使用されているパラメータのみ保持して」と指示します。
複数ファイルにまたがるリファクタリングの操作方法は?
Agent モードを使用し、Chat で目標を明確に記述します(例:UserService を services/user/UserService.js に移動し、補助関数を utils.js に移し、全ての参照を更新)。複雑なタスクは Shift+Tab で Plan モードに切り替え、計画を確認してから実行します。
リファクタリング後のバグ混入をどう防ぐ?
全テスト(npm test)と TypeScript の型チェック(type-check)を実行し、git diff で AI の変更内容を確認します。重要な業務パスは手動で実行し、小刻みにコミットして、明確なコミットメッセージを残すことで回帰しやすくします。
Plan モードで生成された計画は保存できますか?
はい、.cursor/plans/ ディレクトリに保存できます。チームで共有したり、中断後に再開したり、次回の同様のリファクタリング時の参考にしたりできます。

5 min read · 公開日: 2026年1月22日 · 更新日: 2026年2月4日

コメント

GitHubアカウントでログインしてコメントできます

関連記事