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

Cursor でコードをリファクタリング?効率を倍にする実践テクニック

引き継いだプロジェクトでコードを開くと、200 行超の関数、7〜8 階層にネストした if-else、変数名は data1temp2——「これ誰が書いたんだ?」と git blame を当てたら、3 ヶ月前の自分だった、なんてこともあります。

リファクタリングしたい。でもバグが怖い。放置すれば、要件変更のたびに地雷原を歩く気分。

Cursor のリファクタリング機能は、こうした「スパゲッティコード」を人間が読める形に戻すうえで、想像以上に頼りになります。本記事では、Cursor を使って安全にコードを整理する実践テクニックを紹介します。

Cursor リファクタリングは何が強いのか

具体的な手順の前に、なぜ Cursor がリファクタリング向きなのかを整理します。

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

従来の IDE リファクタリングは構文解析が中心です。Cursor はコードを「読んで」理解します。関数をリネームするとき、呼び出し元を書き換えるだけでなく、システム全体での役割を踏まえた提案が得られます。

Agent モードでファイルを跨げる

個人的に一番助かる機能です。ユーティリティ関数をファイル A から B へ移す場合、従来は「コピー → 削除 → import 更新 → 祈る」でした。Agent モードなら「この関数を utils.js に移して」と伝えるだけで、参照更新まで任せられます。初めて使ったときは本当に驚きました。

Plan モード:先に計画、後から実行

複雑なリファクタリングには 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 処理が 1 関数に詰まっています。ロジック自体は難しくないのに、読みにくくテストもしづらい状態です。

Cursor で関数を抽出する

手順は次のとおりです。

  1. 検証ロジック部分を選択(冒頭の if 群)
  2. Cmd/Ctrl + K で編集パネルを開く
  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 も抽出しました。結果はこうなります:

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. 抽出意図をはっきり伝える

コードを選んでショートカットだけ押すと、意図を外すことがあります。「検証関数として抽出」「この計算ロジックを独立関数に」など、具体的に指示しましょう。

2. 関数名を確認する

handleDataprocessItems のような汎用名は避け、calculateOrderTotalvalidateUserPermissions など意味のある名前へ手動修正を。

3. パラメータと戻り値をチェック

不要な変数まで引数に入ることがあるので、抽出後は必ず確認してください。

実戦 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. Cmd/Ctrl + L で Chat を開く
  3. 入力:このコードはネストが深すぎる。早期リターン(early return)でリファクタリングして

最適化結果:

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 型推論

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

型を付けたい場合:

  1. 関数を選択
  2. 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

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

型追加後:

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

型ヒントがあれば、補完が効き、リファクタリング時の型互換チェックも可能になります。

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

単一ファイルを超える変更——例えば UserServiceservices/user.js から services/user/UserService.js へ移し、補助関数も別ファイルへ分割する——は手作業だと漏れやすく、動かなくなるリスクも高いです。

Agent モードを起動

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

Plan モードで制御する

複雑なタスクは Agent 入力欄で Shift+Tab を押し、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 で実行。import 更新まで自動で進みます。

Agent モードが向く場面

  • クラス / 関数 / 変数のリネーム(複数ファイル)
  • モジュールの分割・統合
  • 新しいファイル構造への移行
  • パターンの一括置換(例:すべての varconst に)

リファクタリング後は必ず検証

AI がどれだけ優秀でも、最終確認は自分の仕事です。

チェックリスト

1. 全テストを実行

npm test

失敗したら、コードの問題かテスト更新かを切り分けます。

2. 型エラーを確認(TypeScript プロジェクト)

npm run type-check

3. AI の diff をレビュー
git diff で意図しない変更がないか確認します。

4. 重要パスを手動テスト
ビジネスロジックに触れる変更は、メインフローを必ず通します。

5. 修正漏れを検索
旧関数名・変数名で検索し、参照が残っていないか確認します。

AI リファクタリングでバグを防ぐコツ

小刻みに進める
一度に大量変更せず、1 関数ずつテストを通してから次へ。

Git 履歴をきれいに
リファクタリング単位で commit し、問題があればすぐ戻せるようにします。

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

変更理由を AI に聞く
理解できない変更があれば:

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

推理過程を説明してもらい、妥当性を判断できます。

ベストプラクティス:Cursor リファクタリングを速くする

1. 実行前に方針を話し合う

いきなりコードを書かせず、Ask モード(Chat デフォルト)で方針を議論します。

この 200 行の関数をリファクタリングしたい。提案はある?

合意できたら Agent モードへ切り替えて実行。

2. @ でコンテキストを渡す

複数ファイルに関わる場合は @ で関連コードを引用します。

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

意図が伝わりやすくなります。

3. タスク粒度を調整する

  • 一度で成功するなら、タスクをまとめても OK
  • 失敗が多いなら、細かく分割

例:

  • 簡単すぎ:この関数を抽出して
  • ちょうど良い:ユーザー認証モジュールをログイン・登録・パスワードリセットの 3 ファイルに分割
  • 複雑すぎ:バックエンド全体の権限モジュールをリファクタリング

4. 完了後に要約を生成

PR 説明文のたたき台として:

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

構造化された要約がそのまま Pull Request に使えます。

5. 複雑な計画を保存

大規模リファクタリングの Plan は .cursor/plans/ に保存できます。

  • チームで方針を共有できる
  • 中断しても再開しやすい
  • 次回の類似作業の参考になる

最後に

数ヶ月 Cursor でリファクタリングしてきて感じるのは、AI が「面倒な作業」を肩代わりし、設計やアーキテクチャに頭を使える時間を増やしてくれる、ということです。

ただし AI はアシスタントであって、全部任せる存在ではありません。何をどう分けるか、最終的なコードが期待通りか——判断するのはあなたです。

まずは小さく試しましょう。コアモジュールではなく、影響の小さいユーティリティ関数から。慣れてきたら範囲を広げていけば十分です。ツールは、使いこなして初めて価値が出ます。

Cursor でのリファクタリング経験があれば、ぜひコメントで教えてください。

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

単一ファイルの関数抽出から、Agent による複数ファイルリファクタリングまでの手順

⏱️ 目安時間: 30 分

  1. 1

    ステップ1: 単一ファイルで関数を抽出

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

    ステップ2: ネストロジックをフラット化

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

    ステップ3: 型注釈を追加

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

    ステップ4: Agent でクロスファイルリファクタリング

    Chat を Agent モードに切り替え → 目標(移動 / リネーム / モジュール分割)を記述 → 複雑なタスクは Shift+Tab で Plan モードに切り替え、計画確認後に実行。
  5. 5

    ステップ5: 検証とコミット

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

FAQ

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

6分で読めます · 公開日: 2026年1月22日 · 更新日: 2026年6月15日

関連記事

コメント

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