はじめに

リファクタリングは、ソフトウェアの外部動作を変えずに内部構造を改善する作業です。従来は手作業で行う必要があり、時間がかかる上にミスのリスクも伴いました。Cursorを使えば、AIがコードを分析して改善点を提案し、安全にリファクタリングを実行できます。

本記事では、CursorのAI機能を活用したリファクタリング手法を解説します。この記事を読むことで、以下のことができるようになります。

  • 非推奨APIを検出し、最新のAPIに自動更新できる
  • 最新の言語仕様やベストプラクティスへの移行提案を受けられる
  • デザインパターンを適用してコード構造を改善できる
  • パフォーマンス問題を特定し、最適化を実施できる
  • Tab補完を活用して連鎖的な修正を効率的に適用できる

実行環境と前提条件

実行環境

項目 要件
オペレーティングシステム Windows 10以上、macOS 10.15以上、Ubuntu 20.04以上
Cursor バージョン 2.3以降(2026年1月時点の安定版)
インターネット接続 必須(AIモデル利用に必要)
料金プラン Hobby(無料)以上

前提条件

  • Cursorがインストールされ、アカウントでログイン済みであること
  • リファクタリング対象のコードベースが開かれていること
  • 基本的なデザインパターンの知識があること(推奨)
  • バージョン管理(Git)でコードが管理されていること

期待される結果

本記事の手順を完了すると、以下の状態になります。

  • 古いAPIや非推奨コードを安全に最新化できる
  • コードの可読性と保守性が向上する
  • パフォーマンスのボトルネックを特定し改善できる
  • 継続的なリファクタリングワークフローが確立される

非推奨APIの検出と更新

ライブラリやフレームワークのアップデートに伴い、使用しているAPIが非推奨になることがあります。Cursorは、これらを検出して最新のAPIに更新する支援をします。

Agentによる非推奨API検出

Agent(Ctrl+I)を使って、プロジェクト全体の非推奨APIをスキャンできます。

1
2
3
指示例:
@Codebase このプロジェクトで使用されている非推奨APIを検出し、
推奨される代替APIへの移行方法を提案してください。

Agentは以下のような分析結果を提供します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Agentの分析結果例:

## 検出された非推奨API

### 1. React関連
- `componentWillMount` (3箇所)
  → `useEffect` または `constructor` に移行推奨
- `componentWillReceiveProps` (5箇所)
  → `static getDerivedStateFromProps` または `useEffect` に移行推奨

### 2. Node.js関連
- `url.parse()` (8箇所)
  → `new URL()` に移行推奨
- `Buffer()` コンストラクタ (2箇所)
  → `Buffer.from()` または `Buffer.alloc()` に移行推奨

### 3. Express関連
- `res.sendfile()` (1箇所)
  → `res.sendFile()` に移行推奨

自動更新の実行

検出された非推奨APIを自動的に更新できます。

1
2
3
指示例:
検出された非推奨APIをすべて最新の推奨APIに更新してください。
各ファイルの変更内容を確認させてください。

Before: url.parse() の使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const url = require('url');

function parseUrl(urlString) {
  const parsed = url.parse(urlString, true);
  return {
    hostname: parsed.hostname,
    pathname: parsed.pathname,
    query: parsed.query,
  };
}

After: URL コンストラクタへの移行

1
2
3
4
5
6
7
8
function parseUrl(urlString) {
  const parsed = new URL(urlString);
  return {
    hostname: parsed.hostname,
    pathname: parsed.pathname,
    query: Object.fromEntries(parsed.searchParams),
  };
}

段階的な更新アプローチ

大規模なコードベースでは、段階的に更新することをお勧めします。

flowchart TD
    A[非推奨API検出] --> B[影響範囲の分析]
    B --> C{リスク評価}
    C -->|低リスク| D[一括更新]
    C -->|高リスク| E[段階的更新]
    E --> F[ファイル単位で更新]
    F --> G[テスト実行]
    G --> H{テスト成功?}
    H -->|はい| I[次のファイルへ]
    H -->|いいえ| J[ロールバック・調整]
    J --> F
    I --> K{全ファイル完了?}
    K -->|いいえ| F
    K -->|はい| L[完了]
    D --> G

最新言語仕様への移行提案

JavaScript/TypeScript、Python、Javaなど、言語自体も進化しています。Cursorは最新の言語仕様への移行を提案します。

JavaScript/TypeScript のモダン化

古いJavaScript構文を最新のES仕様に移行します。

1
2
3
4
5
6
指示例:
@src/utils このディレクトリのコードをES2023+の最新構文に更新してください。
- var を const/let に
- function を arrow function に(適切な場合)
- Promise チェーンを async/await に
- オプショナルチェーンとNullish coalescingを活用

Before: 従来の構文

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var self = this;
var users = [];

function fetchUsers() {
  return fetch('/api/users')
    .then(function(response) {
      return response.json();
    })
    .then(function(data) {
      if (data && data.users) {
        users = data.users;
      }
      return users;
    })
    .catch(function(error) {
      console.error(error);
      return [];
    });
}

function getUserName(user) {
  if (user && user.profile && user.profile.name) {
    return user.profile.name;
  }
  return 'Unknown';
}

After: モダンな構文

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const users = [];

const fetchUsers = async () => {
  try {
    const response = await fetch('/api/users');
    const data = await response.json();
    return data?.users ?? [];
  } catch (error) {
    console.error(error);
    return [];
  }
};

const getUserName = (user) => user?.profile?.name ?? 'Unknown';

TypeScript の型安全性向上

型定義を強化してコードの安全性を向上させます。

1
2
3
4
5
指示例:
このファイルの型定義を改善してください:
- any型を具体的な型に置き換え
- undefined/nullの可能性がある箇所に適切な型ガードを追加
- ジェネリクスを活用して再利用性を向上

Before: 弱い型定義

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function processData(data: any): any {
  const result = [];
  for (const item of data) {
    result.push({
      id: item.id,
      name: item.name,
      value: item.value * 2,
    });
  }
  return result;
}

After: 強い型定義

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
interface InputItem {
  id: string;
  name: string;
  value: number;
}

interface OutputItem {
  id: string;
  name: string;
  value: number;
}

function processData(data: InputItem[]): OutputItem[] {
  return data.map((item) => ({
    id: item.id,
    name: item.name,
    value: item.value * 2,
  }));
}

デザインパターンの適用

Cursorは、コードの構造を分析してデザインパターンの適用を提案します。

コード構造の分析

まず、現在のコード構造を分析します。

1
2
3
4
指示例:
@src/services/payment.service.ts このファイルを分析して、
適用できるデザインパターンを提案してください。
現在の問題点と改善後のメリットも説明してください。

Strategy パターンの適用例

条件分岐が多いコードにStrategyパターンを適用します。

Before: 条件分岐による実装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class PaymentService {
  processPayment(type: string, amount: number): PaymentResult {
    if (type === 'credit_card') {
      // クレジットカード処理(50行)
      const fee = amount * 0.03;
      // ...多数の処理
      return { success: true, fee };
    } else if (type === 'bank_transfer') {
      // 銀行振込処理(40行)
      const fee = 300;
      // ...多数の処理
      return { success: true, fee };
    } else if (type === 'e_wallet') {
      // 電子マネー処理(45行)
      const fee = amount * 0.01;
      // ...多数の処理
      return { success: true, fee };
    }
    throw new Error('Unknown payment type');
  }
}

After: Strategy パターン適用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// 支払い戦略インターフェース
interface PaymentStrategy {
  process(amount: number): PaymentResult;
  calculateFee(amount: number): number;
}

// クレジットカード戦略
class CreditCardStrategy implements PaymentStrategy {
  process(amount: number): PaymentResult {
    const fee = this.calculateFee(amount);
    // クレジットカード固有の処理
    return { success: true, fee };
  }

  calculateFee(amount: number): number {
    return amount * 0.03;
  }
}

// 銀行振込戦略
class BankTransferStrategy implements PaymentStrategy {
  process(amount: number): PaymentResult {
    const fee = this.calculateFee(amount);
    // 銀行振込固有の処理
    return { success: true, fee };
  }

  calculateFee(amount: number): number {
    return 300;
  }
}

// 電子マネー戦略
class EWalletStrategy implements PaymentStrategy {
  process(amount: number): PaymentResult {
    const fee = this.calculateFee(amount);
    // 電子マネー固有の処理
    return { success: true, fee };
  }

  calculateFee(amount: number): number {
    return amount * 0.01;
  }
}

// コンテキストクラス
class PaymentService {
  private strategies: Map<string, PaymentStrategy>;

  constructor() {
    this.strategies = new Map([
      ['credit_card', new CreditCardStrategy()],
      ['bank_transfer', new BankTransferStrategy()],
      ['e_wallet', new EWalletStrategy()],
    ]);
  }

  processPayment(type: string, amount: number): PaymentResult {
    const strategy = this.strategies.get(type);
    if (!strategy) {
      throw new Error(`Unknown payment type: ${type}`);
    }
    return strategy.process(amount);
  }
}

その他のパターン適用例

Cursorは様々なデザインパターンの適用を支援します。

パターン 適用シーン 指示例
Factory オブジェクト生成の複雑化 「このswitch文をFactoryパターンでリファクタリングして」
Observer イベント通知の散在 「このコールバック地獄をObserverパターンに変換して」
Decorator 機能の動的追加 「ログ機能をDecoratorパターンで追加して」
Singleton グローバルな状態管理 「このクラスをSingletonパターンに変換して」
Repository データアクセスの抽象化 「直接DBアクセスをRepositoryパターンで抽象化して」

パフォーマンス改善提案

Cursorは、パフォーマンス問題を検出し、改善提案を行います。

パフォーマンス分析の依頼

1
2
3
4
5
6
7
指示例:
@src/services/data.service.ts このファイルのパフォーマンス問題を分析し、
改善提案をしてください。特に以下の観点で確認してください:
- N+1クエリ問題
- 不要なループ処理
- メモリリーク
- キャッシュの活用

N+1問題の解決

Before: N+1クエリ問題

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
async function getOrdersWithProducts(userId: string) {
  const orders = await orderRepository.find({ where: { userId } });
  
  // N+1問題:注文ごとにクエリが発行される
  for (const order of orders) {
    order.products = await productRepository.find({
      where: { orderId: order.id },
    });
  }
  
  return orders;
}

After: JOINによる一括取得

1
2
3
4
5
6
async function getOrdersWithProducts(userId: string) {
  return orderRepository.find({
    where: { userId },
    relations: ['products'],
  });
}

ループ処理の最適化

Before: 非効率なループ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function findDuplicates(items: Item[]): Item[] {
  const duplicates: Item[] = [];
  
  for (let i = 0; i < items.length; i++) {
    for (let j = i + 1; j < items.length; j++) {
      if (items[i].id === items[j].id) {
        if (!duplicates.find(d => d.id === items[i].id)) {
          duplicates.push(items[i]);
        }
      }
    }
  }
  
  return duplicates;
}

After: Setを活用した最適化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function findDuplicates(items: Item[]): Item[] {
  const seen = new Set<string>();
  const duplicateIds = new Set<string>();
  
  for (const item of items) {
    if (seen.has(item.id)) {
      duplicateIds.add(item.id);
    } else {
      seen.add(item.id);
    }
  }
  
  return items.filter(item => duplicateIds.has(item.id));
}

メモ化によるキャッシュ

1
2
3
指示例:
この計算処理が頻繁に呼び出されています。
適切なメモ化を追加して、パフォーマンスを改善してください。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// メモ化ユーティリティ
function memoize<T extends (...args: any[]) => any>(fn: T): T {
  const cache = new Map<string, ReturnType<T>>();
  
  return ((...args: Parameters<T>): ReturnType<T> => {
    const key = JSON.stringify(args);
    
    if (cache.has(key)) {
      return cache.get(key)!;
    }
    
    const result = fn(...args);
    cache.set(key, result);
    return result;
  }) as T;
}

// 使用例
const calculateComplexValue = memoize((input: number): number => {
  // 重い計算処理
  return expensiveCalculation(input);
});

Tab補完を活用した連鎖的な修正

リファクタリングでは、1つの変更が複数の箇所に波及することがあります。CursorのTab補完は、この連鎖的な修正を効率化します。

連鎖修正のワークフロー

flowchart LR
    A[1箇所を修正] --> B[Tab補完が<br/>関連修正を提案]
    B --> C[Tabで適用]
    C --> D[次の修正箇所へ<br/>自動ジャンプ]
    D --> E{修正完了?}
    E -->|いいえ| B
    E -->|はい| F[完了]

実践例: 関数名の変更

関数名を変更すると、Tab補完が呼び出し元の修正を提案します。

  1. 関数名を getUser から fetchUserById に変更
1
2
3
4
5
6
7
8
9
// 変更前
async function getUser(id: string): Promise<User> {
  return userRepository.findOne({ where: { id } });
}

// 変更後(手動で変更)
async function fetchUserById(id: string): Promise<User> {
  return userRepository.findOne({ where: { id } });
}
  1. Tab補完が関連箇所の修正を提案
1
2
3
// Tab補完による提案(ゴーストテキスト)
// 呼び出し元ファイルに自動ジャンプ
const user = await fetchUserById(userId); // getUser → fetchUserById
  1. Tabキーで適用し、次の修正箇所へジャンプ

型定義の変更と連鎖修正

インターフェースのプロパティを変更した場合も、Tab補完が活躍します。

1
2
3
4
5
6
7
8
// 型定義を変更
interface User {
  id: string;
  // name を firstName と lastName に分割
  firstName: string;
  lastName: string;
  email: string;
}

Tab補完により、user.name を使用している箇所に対して以下のような修正が提案されます。

1
2
3
// Tab補完の提案
// user.name → `${user.firstName} ${user.lastName}`
const displayName = `${user.firstName} ${user.lastName}`;

複数ファイルへの波及

Tab補完は、開いているファイルだけでなく、プロジェクト全体の関連箇所を認識します。

変更内容 Tab補完の提案範囲
関数名変更 すべての呼び出し元
プロパティ追加/削除 すべての使用箇所
インポートパス変更 すべてのインポート文
定数値変更 すべての参照箇所

リファクタリングワークフロー

安全かつ効率的にリファクタリングを行うためのワークフローを紹介します。

事前準備

リファクタリングを始める前に、以下を確認します。

  1. テストの存在確認: リファクタリング対象のコードにテストがあるか
  2. チェックポイント作成: 大きな変更前にチェックポイントを作成
  3. 影響範囲の把握: 変更が影響する範囲を確認
1
2
3
指示例:
リファクタリングを始める前に、チェックポイントを作成してください。
その後、@src/services/order.service.ts の影響範囲を分析してください。

段階的リファクタリング

一度に大きな変更を加えるのではなく、小さな単位で進めます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
段階的リファクタリングの指示例:

ステップ1:
まず extractMethod リファクタリングで、
calculateTotal 関数内の税計算ロジックを別関数に抽出してください。

ステップ2:
抽出した税計算関数のテストを追加してください。

ステップ3:
税計算関数を TaxCalculator クラスに移動してください。

変更の検証

各ステップ後にテストを実行して、動作を検証します。

1
2
3
指示例:
ターミナルでテストを実行してください。
失敗したテストがあれば原因を分析して修正してください。

よくあるトラブルと対処法

リファクタリング後にテストが失敗する

テスト失敗の原因を特定し、修正します。

1
2
3
4
5
6
指示例:
以下のテストが失敗しています:
「Expected: 100, Received: 103」

テストコードとリファクタリング後のコードを比較して、
原因を特定し修正してください。

循環参照が発生する

モジュール間の依存関係を整理します。

1
2
3
4
5
指示例:
リファクタリング後に循環参照エラーが発生しました:
"Circular dependency detected: A → B → C → A"

依存関係を分析し、循環を解消する方法を提案してください。

型エラーが大量に発生する

段階的に型エラーを解消します。

1
2
3
指示例:
リファクタリング後に型エラーが50件発生しています。
エラーをカテゴリ別に分類し、優先度の高いものから順に修正してください。

まとめ

Cursorを活用したリファクタリングは、コード品質を効率的に向上させるAI駆動開発の重要な手法です。本記事で解説した内容をまとめます。

  • 非推奨APIの検出と更新: Agentによる自動検出と最新APIへの移行支援
  • 最新言語仕様への移行: モダンな構文と型安全性の向上
  • デザインパターンの適用: コード構造の分析とパターン適用の提案
  • パフォーマンス改善: N+1問題の解決、ループ最適化、キャッシュ活用
  • Tab補完による連鎖修正: 関連箇所への自動ジャンプと修正提案

これらのテクニックを組み合わせることで、安全かつ効率的にコードベースの品質を向上させられます。リファクタリングは一度きりの作業ではなく、継続的に行うことでコードの健全性を維持できます。

参考リンク