T-CREATOR

GitHub Copilot でリファクタ促進プロンプト集:命名・抽象化・分割・削除の誘導文

GitHub Copilot でリファクタ促進プロンプト集:命名・抽象化・分割・削除の誘導文

コードの質を高めるには、適切なタイミングで適切なリファクタリングを実施することが重要です。 GitHub Copilot を活用すれば、リファクタリング作業を効率的に進められます。

リファクタリングプロンプト早見表

リファクタリングの種類ごとに効果的なプロンプトを整理しました。すぐに使える実践的な誘導文を集約しています。

#カテゴリ目的代表的なプロンプト例期待される効果
1命名改善変数・関数名の明確化"この変数名をより意図が明確になるようにリネームして"可読性向上、意図の明確化
2命名改善一貫性のある命名"この関数群の命名規則を統一して"コード全体の統一感向上
3抽象化共通ロジックの抽出"重複している処理を共通関数に抽出して"DRY 原則の実現、保守性向上
4抽象化インターフェース設計"この処理を抽象化してインターフェースを定義して"拡張性向上、依存関係の整理
5分割関数の分解"この長い関数を責任ごとに分割して"単一責任の原則、テスト容易性向上
6分割モジュール分離"この機能を独立したモジュールに分離して"凝集度向上、再利用性向上
7削除デッドコード除去"使われていないコードを特定して削除して"コードベースの簡潔化
8削除冗長性の排除"この条件分岐を簡潔にして"可読性向上、バグ混入リスク低減

背景

現代開発におけるリファクタリングの位置づけ

ソフトウェア開発において、リファクタリングは機能追加と同じくらい重要な作業となっています。 コードは書いた瞬間から「負債」になる可能性があり、継続的な改善が求められるでしょう。

しかし、多くの開発現場では以下のような状況が見られます。

  • 納期優先でリファクタリングが後回しになる
  • どこから手をつければよいか判断が難しい
  • リファクタリングの効果が見えにくい

GitHub Copilot のような AI アシスタントツールは、こうした課題を解決する強力な味方になります。 適切なプロンプトを使えば、リファクタリングの方向性を示唆してくれるだけでなく、実装まで支援してくれるのです。

以下の図は、リファクタリングが開発サイクルのどこに位置するかを示しています。

mermaidflowchart TB
    req["要件定義"] --> design["設計"]
    design --> impl["実装"]
    impl --> test["テスト"]
    test --> review["コードレビュー"]
    review --> refactor["リファクタリング"]
    refactor --> impl
    refactor --> release["リリース"]
    release --> maint["保守・運用"]
    maint --> req

    style refactor fill:#e1f5ff

リファクタリングは実装とテストの間で継続的に行われ、コード品質を高める役割を担っています。

GitHub Copilot がリファクタリングにもたらす変化

GitHub Copilot は、コード補完だけでなく、リファクタリングの提案やコード改善においても優れた能力を発揮します。 開発者が「どう改善したいか」を自然言語で伝えるだけで、具体的な実装案を提示してくれるのです。

特に以下の点で、従来のリファクタリング手法とは異なる価値を提供します。

  • 即座のフィードバック: IDE 上でリアルタイムに改善案を得られる
  • 学習効果: 提案されたコードから新しいパターンを学べる
  • 心理的ハードル低減: リファクタリングを始めるきっかけになる

この記事では、GitHub Copilot を使った効果的なリファクタリングプロンプトを、4 つの観点から整理してご紹介します。

課題

リファクタリングにおける典型的な困難

リファクタリングを実施する際、多くの開発者が直面する課題があります。 これらの課題を理解することで、GitHub Copilot の活用方法がより明確になるでしょう。

命名に関する課題

変数名や関数名は、コードの意図を伝える最も重要な要素の一つです。 しかし、以下のような問題がよく発生します。

  • 略語や短縮形が多用され、意味が不明瞭になる(flgtmpdata など)
  • プロジェクト内で命名規則が統一されていない
  • 処理の本質を表現できていない名前が使われている

こうした命名の問題は、コードレビューで指摘されることが多いものの、適切な代替案を考えるのに時間がかかります。

抽象化に関する課題

適切な抽象化レベルを見極めることは、経験を要する難しい判断です。

  • 抽象化しすぎると理解が困難になる
  • 抽象化が不足すると重複コードが増える
  • どのタイミングで抽象化すべきか判断が難しい

特に、将来の拡張性を考慮した抽象化は、現時点では過剰に見えることもあり、チーム内で合意を得るのが難しいでしょう。

分割に関する課題

大きな関数やクラスを適切に分割することは、リファクタリングの基本ですが、実践は容易ではありません。

  • どこで分割すればよいか境界が見えにくい
  • 分割後の関数間の依存関係が複雑になる懸念がある
  • 分割することでかえって可読性が下がる場合がある

100 行を超える関数を前にして、「どこから手をつければ…」と悩んだ経験は、多くの開発者にあるはずです。

削除に関する課題

不要なコードの削除は、一見簡単に思えますが、実は慎重さが求められます。

  • 本当に使われていないか確認が難しい
  • 削除することで影響範囲が予測できない
  • 「念のため残しておこう」という心理が働く

結果として、デッドコードがそのまま残り続け、コードベースが肥大化していくのです。

以下の図は、リファクタリングにおける課題とその影響を示しています。

mermaidflowchart LR
    subgraph challenges["リファクタリングの課題"]
        naming["命名の不明瞭さ"]
        abstract["抽象化の判断難"]
        split["分割の境界不明"]
        delete["削除の躊躇"]
    end

    subgraph impacts["コードへの影響"]
        read["可読性低下"]
        maint["保守性低下"]
        dup["重複コード増加"]
        bloat["コードベース肥大化"]
    end

    naming --> read
    abstract --> dup
    split --> maint
    delete --> bloat

    read --> cost["開発コスト増大"]
    maint --> cost
    dup --> cost
    bloat --> cost

    style challenges fill:#ffe6e6
    style impacts fill:#fff3e6
    style cost fill:#ffcccc

これらの課題に対して、GitHub Copilot を活用した具体的なアプローチが求められています。

解決策

GitHub Copilot を活用したリファクタリング戦略

GitHub Copilot に適切なプロンプトを与えることで、リファクタリングの課題を効率的に解決できます。 ここでは、4 つの観点(命名・抽象化・分割・削除)それぞれについて、実践的なプロンプト戦略をご紹介しましょう。

命名改善のためのプロンプト戦略

命名の改善は、リファクタリングの中で最も即効性のある施策です。 GitHub Copilot は、コンテキストを理解した上で、より明確で意図が伝わる命名を提案してくれます。

基本的な命名改善プロンプト

変数や関数の名前を改善したい場合、以下のようなプロンプトが有効です。

パターン 1: 意図を明確にする命名

textこの変数名をビジネスロジックの意図が明確になるようにリネームして

このプロンプトは、単なる技術的な命名ではなく、ビジネスの文脈を反映した命名を求めています。

パターン 2: 命名規則の統一

textこのファイル内の関数名をキャメルケースに統一して

プロジェクト全体で命名規則を統一することで、可読性が大幅に向上します。

パターン 3: 型情報を反映した命名

textこの変数名を TypeScript の型情報が明確になるようにして

型安全性を重視する場合、型情報が名前から推測できることが重要です。

具体的なシナリオ別プロンプト

より具体的な状況に応じたプロンプトをご紹介します。

Boolean 値の命名

textこの真偽値の変数名を is/has/should などの接頭辞を使って明確にして

Boolean 値は、質問形式の命名にすることで可読性が向上します。

配列・コレクションの命名

textこの配列変数名を複数形にして、要素の種類が明確になるようにして

配列やリストは、複数形を使うことで直感的に理解できるようになります。

関数の命名(動詞の選択)

textこの関数名を処理内容に応じた適切な動詞(get/fetch/calculate/validate など)で始めて

関数名の動詞は、処理の性質を正確に表現する必要があります。

抽象化のためのプロンプト戦略

抽象化は、コードの再利用性と拡張性を高める重要な手法です。 GitHub Copilot は、重複パターンを見つけて適切な抽象化レベルを提案してくれます。

共通処理の抽出プロンプト

重複したコードを発見し、共通化する際に使えるプロンプトです。

パターン 1: 重複ロジックの関数化

textこの2つの関数に共通する処理を抽出して、新しい関数を作成して

類似した処理を見つけたら、すぐに共通関数として抽出するよう促します。

パターン 2: ユーティリティ関数の作成

textこのコードブロックを汎用的なユーティリティ関数として抽出して

特定の処理を汎用化することで、プロジェクト全体で再利用できるようになります。

インターフェース・抽象クラスの設計プロンプト

より高度な抽象化として、インターフェースや抽象クラスを定義する際のプロンプトです。

パターン 1: インターフェースの定義

textこの実装からインターフェースを抽出して、依存関係を逆転させて

具体的な実装から抽象を導き出し、依存性逆転の原則(DIP)を適用します。

パターン 2: 戦略パターンの適用

textこの条件分岐をストラテジーパターンで抽象化して

複雑な条件分岐は、戦略パターンを使うことで拡張性が高まります。

パターン 3: ジェネリクスの導入

textこの関数を型パラメータを使ったジェネリック関数に変換して

TypeScript のジェネリクスを活用することで、型安全性を保ちながら汎用化できます。

分割のためのプロンプト戦略

大きな関数やクラスを適切に分割することで、テスト容易性と保守性が向上します。 GitHub Copilot は、責任の境界を見つけて分割案を提案してくれます。

関数の分割プロンプト

長大な関数を小さな関数に分解する際のプロンプトです。

パターン 1: 責任による分割

textこの関数を単一責任の原則に従って複数の小さな関数に分割して

1 つの関数が複数の責任を持っている場合、それぞれを独立した関数にします。

パターン 2: 処理ステップごとの分割

textこの長い処理を、前処理・メイン処理・後処理の3つの関数に分割して

処理の流れに沿って段階的に分割することで、可読性が向上します。

パターン 3: 抽出と構成

textこの関数内の複雑な計算ロジックを別関数に抽出して

複雑な部分だけを切り出すことで、メイン関数の見通しが良くなります。

モジュール・ファイルの分割プロンプト

ファイルレベルでの分割を行う際のプロンプトです。

パターン 1: 機能別の分離

textこのファイルの機能を、ドメインロジックとUI ロジックに分離して

関心の分離(Separation of Concerns)を実現するための基本的な分割です。

パターン 2: レイヤーの分離

textこの処理をプレゼンテーション層・ビジネスロジック層・データアクセス層に分けて

アーキテクチャのレイヤーに従って分割することで、構造が明確になります。

パターン 3: コンポーネントの分割(React の場合)

textこのReact コンポーネントを、表示ロジックとビジネスロジックを分けたカスタムフックと表示コンポーネントに分割して

React では、カスタムフックを使った分割が推奨されます。

削除のためのプロンプト戦略

不要なコードを見つけて削除することで、コードベースを健全に保てます。 GitHub Copilot は、使われていないコードや冗長な処理を特定するのに役立ちます。

デッドコードの特定と削除プロンプト

実際には使われていないコードを見つけるプロンプトです。

パターン 1: 未使用コードの検出

textこのファイル内で使われていない関数や変数を特定して削除して

プロジェクト内で参照されていないコードを見つけ出します。

パターン 2: 到達不可能なコードの削除

textこの条件分岐で実行されることがないコードパスを削除して

論理的に到達できないコードは、混乱の元になるため削除すべきです。

冗長性の排除プロンプト

同じことを表現している重複や、不要な複雑さを削除するプロンプトです。

パターン 1: 条件分岐の簡素化

textこの if-else 文を三項演算子または論理演算子で簡潔にして

シンプルな条件は、より簡潔な形式で表現できます。

パターン 2: 早期リターンの活用

textこのネストした条件分岐を早期リターンを使ってフラットにして

ガード節を使うことで、ネストを減らし可読性を高められます。

パターン 3: 不要な中間変数の削除

textこの一度しか使われていない中間変数を削除して、直接代入して

不要な変数は認知負荷を高めるため、削除するのが望ましいでしょう。

パターン 4: デフォルト引数の活用

textこの関数の引数チェックとデフォルト値設定を、ES6 のデフォルト引数で置き換えて

現代的な言語機能を使うことで、コードが簡潔になります。

以下の図は、各リファクタリング戦略の関係性を示しています。

mermaidflowchart TB
    start["リファクタリング開始"] --> assess["コード品質評価"]

    assess --> naming["命名改善"]
    assess --> abstract["抽象化"]
    assess --> split["分割"]
    assess --> remove["削除"]

    naming --> n1["意図明確化"]
    naming --> n2["規則統一"]

    abstract --> a1["共通処理抽出"]
    abstract --> a2["インターフェース定義"]

    split --> s1["関数分割"]
    split --> s2["モジュール分離"]

    remove --> r1["デッドコード削除"]
    remove --> r2["冗長性排除"]

    n1 --> verify["検証・テスト"]
    n2 --> verify
    a1 --> verify
    a2 --> verify
    s1 --> verify
    s2 --> verify
    r1 --> verify
    r2 --> verify

    verify --> complete["リファクタリング完了"]

    style naming fill:#e1f5ff
    style abstract fill:#e8f5e9
    style split fill:#fff3e0
    style remove fill:#fce4ec

図から分かるように、4 つの戦略は独立しながらも、最終的には検証・テストという共通のゴールに向かっています。

具体例

ここからは、実際のコードを使って各リファクタリング手法を具体的に見ていきましょう。 GitHub Copilot へのプロンプトと、その結果得られるコード改善の実例をご紹介します。

命名改善の実践例

例 1: 不明瞭な変数名の改善

リファクタリング前の、意味が分かりにくいコードです。

typescript// リファクタリング前
function calc(d: number, r: number): number {
  const t = d * r;
  const x = t * 0.1;
  return t - x;
}

このコードに対して、以下のプロンプトを使います。

プロンプト:

textこの関数の変数名を、ビジネスロジックの意図が明確になるようにリネームして

GitHub Copilot が提案する改善後のコードです。

typescript// リファクタリング後
function calculateDiscountedPrice(
  basePrice: number,
  quantity: number
): number {
  // 小計を計算
  const subtotal = basePrice * quantity;

  // 10%の割引額を計算
  const discountAmount = subtotal * 0.1;

  // 割引後の価格を返却
  return subtotal - discountAmount;
}

改善のポイントは以下の通りです。

  • 関数名が処理内容を明確に表現している(calccalculateDiscountedPrice
  • 変数名がビジネスの文脈を反映している(dbasePricerquantity
  • 計算の意図がコメントと変数名で明確になっている

例 2: Boolean 値の命名改善

条件判定に使われる変数の命名を改善します。

typescript// リファクタリング前
function checkUser(user: User): boolean {
  const flag = user.age >= 20;
  const status = user.verified;

  if (flag && status) {
    return true;
  }
  return false;
}

プロンプト:

textこの関数の真偽値変数を is/has などの接頭辞を使って明確にして、また条件判定を簡潔にして

改善後のコードです。

typescript// リファクタリング後
function isEligibleUser(user: User): boolean {
  // ユーザーが成人かどうか
  const isAdult = user.age >= 20;

  // ユーザーが認証済みかどうか
  const isVerified = user.verified;

  // 成人かつ認証済みのユーザーのみ適格
  return isAdult && isVerified;
}

Boolean 値に is 接頭辞を付けることで、質問形式になり意図が明確になりました。 また、不要な if-else 文を削除してシンプルな return 文にしています。

抽象化の実践例

例 3: 重複ロジックの共通関数化

複数の場所で同じようなパターンが繰り返されているコードです。

typescript// リファクタリング前
function processUserData(users: User[]): ProcessedUser[] {
  return users.map((user) => {
    const fullName = `${user.firstName} ${user.lastName}`;
    const age = new Date().getFullYear() - user.birthYear;
    return { ...user, fullName, age };
  });
}

function displayUserInfo(user: User): string {
  const fullName = `${user.firstName} ${user.lastName}`;
  const age = new Date().getFullYear() - user.birthYear;
  return `${fullName} (${age}歳)`;
}

プロンプト:

textこの2つの関数に共通する名前と年齢の計算処理を、再利用可能なユーティリティ関数として抽出して

GitHub Copilot による改善案です。

typescript// リファクタリング後:共通ユーティリティ関数
/**
 * ユーザーのフルネームを生成
 */
function buildFullName(
  firstName: string,
  lastName: string
): string {
  return `${firstName} ${lastName}`;
}

/**
 * 生年から現在の年齢を計算
 */
function calculateAge(birthYear: number): number {
  return new Date().getFullYear() - birthYear;
}

抽出したユーティリティ関数を使って、元の関数を書き直します。

typescript// リファクタリング後:ユーティリティを使用
function processUserData(users: User[]): ProcessedUser[] {
  return users.map((user) => ({
    ...user,
    fullName: buildFullName(user.firstName, user.lastName),
    age: calculateAge(user.birthYear),
  }));
}

function displayUserInfo(user: User): string {
  const fullName = buildFullName(
    user.firstName,
    user.lastName
  );
  const age = calculateAge(user.birthYear);
  return `${fullName} (${age}歳)`;
}

共通処理を関数として抽出することで、DRY 原則を実現し、テストもしやすくなりました。

例 4: 戦略パターンによる抽象化

複雑な条件分岐を抽象化して拡張性を高めます。

typescript// リファクタリング前
function calculateShippingFee(
  weight: number,
  type: string
): number {
  if (type === 'standard') {
    return weight * 100;
  } else if (type === 'express') {
    return weight * 200 + 500;
  } else if (type === 'overnight') {
    return weight * 300 + 1000;
  }
  return 0;
}

プロンプト:

textこの配送料計算の条件分岐を、ストラテジーパターンで抽象化して拡張性を高めて

改善後のコードでは、配送タイプごとの計算ロジックを分離します。

typescript// リファクタリング後:配送料計算戦略のインターフェース
interface ShippingStrategy {
  calculate(weight: number): number;
}

各配送タイプの具体的な計算ロジックを実装します。

typescript// リファクタリング後:標準配送の戦略
class StandardShipping implements ShippingStrategy {
  calculate(weight: number): number {
    return weight * 100;
  }
}

// 速達配送の戦略
class ExpressShipping implements ShippingStrategy {
  calculate(weight: number): number {
    return weight * 200 + 500;
  }
}

// 翌日配送の戦略
class OvernightShipping implements ShippingStrategy {
  calculate(weight: number): number {
    return weight * 300 + 1000;
  }
}

戦略を管理する仕組みを作ります。

typescript// リファクタリング後:戦略の選択と実行
const shippingStrategies: Record<string, ShippingStrategy> =
  {
    standard: new StandardShipping(),
    express: new ExpressShipping(),
    overnight: new OvernightShipping(),
  };

function calculateShippingFee(
  weight: number,
  type: string
): number {
  const strategy = shippingStrategies[type];

  if (!strategy) {
    throw new Error(`未対応の配送タイプ: ${type}`);
  }

  return strategy.calculate(weight);
}

この抽象化により、新しい配送タイプの追加が容易になり、各計算ロジックのテストも独立して行えます。

分割の実践例

例 5: 長大な関数の責任別分割

1 つの関数が複数の責任を持っているケースです。

typescript// リファクタリング前
async function registerUser(
  userData: UserInput
): Promise<void> {
  // バリデーション
  if (!userData.email || !userData.email.includes('@')) {
    throw new Error('無効なメールアドレス');
  }
  if (!userData.password || userData.password.length < 8) {
    throw new Error('パスワードは8文字以上必要');
  }

  // パスワードのハッシュ化
  const salt = await bcrypt.genSalt(10);
  const hashedPassword = await bcrypt.hash(
    userData.password,
    salt
  );

  // データベース保存
  const user = {
    email: userData.email,
    password: hashedPassword,
    createdAt: new Date(),
  };
  await db.users.insert(user);

  // ウェルカムメール送信
  const emailContent = `
    ${userData.email}様

    ご登録ありがとうございます。
  `;
  await emailService.send(
    userData.email,
    'ご登録完了',
    emailContent
  );
}

プロンプト:

textこのユーザー登録関数を、バリデーション・パスワード処理・DB保存・メール送信の責任ごとに分割して

まず、バリデーションを分離します。

typescript// リファクタリング後:バリデーション
function validateUserInput(userData: UserInput): void {
  // メールアドレスの検証
  if (!userData.email || !userData.email.includes('@')) {
    throw new Error('無効なメールアドレス');
  }

  // パスワードの検証
  if (!userData.password || userData.password.length < 8) {
    throw new Error('パスワードは8文字以上必要');
  }
}

次に、パスワードのハッシュ化を独立した関数にします。

typescript// リファクタリング後:パスワードハッシュ化
async function hashPassword(
  password: string
): Promise<string> {
  const salt = await bcrypt.genSalt(10);
  return await bcrypt.hash(password, salt);
}

ユーザーデータの作成と DB 保存を分離します。

typescript// リファクタリング後:ユーザーデータ作成とDB保存
async function createUserRecord(
  email: string,
  hashedPassword: string
): Promise<User> {
  const user = {
    email,
    password: hashedPassword,
    createdAt: new Date(),
  };

  return await db.users.insert(user);
}

ウェルカムメールの送信も独立させます。

typescript// リファクタリング後:ウェルカムメール送信
async function sendWelcomeEmail(
  email: string
): Promise<void> {
  const emailContent = `
    ${email}様

    ご登録ありがとうございます。
  `;

  await emailService.send(
    email,
    'ご登録完了',
    emailContent
  );
}

最後に、これらを組み合わせたメイン関数を作ります。

typescript// リファクタリング後:メイン関数(各処理を統合)
async function registerUser(
  userData: UserInput
): Promise<void> {
  // 入力値の検証
  validateUserInput(userData);

  // パスワードをハッシュ化
  const hashedPassword = await hashPassword(
    userData.password
  );

  // ユーザーレコードを作成
  await createUserRecord(userData.email, hashedPassword);

  // ウェルカムメールを送信
  await sendWelcomeEmail(userData.email);
}

各関数が単一の責任を持つようになり、テストや保守が格段に容易になりました。

例 6: React コンポーネントの分割

ロジックと表示が混在した React コンポーネントを分割します。

typescript// リファクタリング前
function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchUser() {
      try {
        const response = await fetch(
          `/api/users/${userId}`
        );
        const data = await response.json();
        setUser(data);
      } catch (error) {
        console.error('ユーザー情報の取得に失敗', error);
      } finally {
        setLoading(false);
      }
    }

    fetchUser();
  }, [userId]);

  if (loading) return <div>読み込み中...</div>;
  if (!user) return <div>ユーザーが見つかりません</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

プロンプト:

textこのReact コンポーネントを、データ取得ロジックをカスタムフックに分離して、表示コンポーネントをシンプルにして

まず、データ取得ロジックをカスタムフックとして抽出します。

typescript// リファクタリング後:カスタムフック(データ取得ロジック)
function useUser(userId: string) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    async function fetchUser() {
      try {
        setLoading(true);
        const response = await fetch(
          `/api/users/${userId}`
        );
        const data = await response.json();
        setUser(data);
      } catch (err) {
        setError(err as Error);
        console.error('ユーザー情報の取得に失敗', err);
      } finally {
        setLoading(false);
      }
    }

    fetchUser();
  }, [userId]);

  return { user, loading, error };
}

次に、表示部分を純粋なプレゼンテーショナルコンポーネントにします。

typescript// リファクタリング後:プレゼンテーショナルコンポーネント
function UserProfileView({ user }: { user: User }) {
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

最後に、カスタムフックを使うコンテナコンポーネントを作ります。

typescript// リファクタリング後:コンテナコンポーネント
function UserProfile({ userId }: { userId: string }) {
  const { user, loading, error } = useUser(userId);

  if (loading) return <div>読み込み中...</div>;
  if (error) return <div>エラーが発生しました</div>;
  if (!user) return <div>ユーザーが見つかりません</div>;

  return <UserProfileView user={user} />;
}

この分割により、ロジックと表示が明確に分離され、それぞれを独立してテストできるようになりました。

削除の実践例

例 7: デッドコードの削除

使われていない関数や変数を特定して削除します。

typescript// リファクタリング前
function processOrder(order: Order): ProcessedOrder {
  // 使われていない変数
  const tempValue = 100;
  const debugFlag = false;

  // 実際に使われている処理
  const total = order.items.reduce(
    (sum, item) => sum + item.price,
    0
  );

  // 到達不可能なコード
  if (false) {
    console.log('この処理は実行されません');
  }

  // 使われていない関数
  function calculateTax(amount: number): number {
    return amount * 0.1;
  }

  return {
    orderId: order.id,
    total,
  };
}

プロンプト:

textこの関数内で実際に使われていない変数、到達不可能なコード、未使用の関数を削除して

不要なコードを削除した結果です。

typescript// リファクタリング後
function processOrder(order: Order): ProcessedOrder {
  // 注文アイテムの合計金額を計算
  const total = order.items.reduce(
    (sum, item) => sum + item.price,
    0
  );

  return {
    orderId: order.id,
    total,
  };
}

デッドコードを削除することで、コードの見通しが良くなり、保守性が向上しました。

例 8: 冗長な条件分岐の簡素化

複雑な条件分岐を簡潔にします。

typescript// リファクタリング前
function getUserStatus(user: User): string {
  let status = '';

  if (user.isPremium === true) {
    status = 'プレミアム会員';
  } else {
    if (user.isActive === true) {
      status = '通常会員';
    } else {
      status = '無効';
    }
  }

  return status;
}

function canAccessFeature(user: User): boolean {
  if (user.isPremium === true) {
    return true;
  } else {
    return false;
  }
}

プロンプト:

textこのネストした条件分岐を早期リターンと三項演算子で簡潔にして、Boolean 値との明示的な比較も削除して

簡潔にリファクタリングした結果です。

typescript// リファクタリング後
function getUserStatus(user: User): string {
  // プレミアム会員の場合は早期リターン
  if (user.isPremium) {
    return 'プレミアム会員';
  }

  // アクティブかどうかで判定
  return user.isActive ? '通常会員' : '無効';
}

function canAccessFeature(user: User): boolean {
  // Boolean 値をそのまま返却
  return user.isPremium;
}

ネストを減らし、不要な変数や冗長な条件判定を削除することで、コードが読みやすくなりました。

例 9: デフォルト引数による簡素化

引数チェックとデフォルト値設定を、現代的な構文で置き換えます。

typescript// リファクタリング前
function createPagination(
  page: number | undefined,
  limit: number | undefined
): Pagination {
  // 引数の存在チェックとデフォルト値設定
  let currentPage = page;
  if (currentPage === undefined || currentPage === null) {
    currentPage = 1;
  }

  let itemsPerPage = limit;
  if (itemsPerPage === undefined || itemsPerPage === null) {
    itemsPerPage = 10;
  }

  // バリデーション
  if (currentPage < 1) {
    currentPage = 1;
  }

  return {
    page: currentPage,
    limit: itemsPerPage,
  };
}

プロンプト:

textこの関数の引数チェックとデフォルト値設定を、デフォルト引数と Math.max で簡潔にして

デフォルト引数を使った簡潔な形に書き直します。

typescript// リファクタリング後
function createPagination(
  page: number = 1,
  limit: number = 10
): Pagination {
  // ページ番号は最小1に制限
  const currentPage = Math.max(page, 1);

  return {
    page: currentPage,
    limit,
  };
}

デフォルト引数を使うことで、冗長な存在チェックが不要になり、コードが大幅に簡潔になりました。

以下は、リファクタリングのビフォー・アフターを比較する視点を示した図です。

mermaidflowchart LR
    subgraph before["リファクタリング前"]
        b1["不明瞭な命名"]
        b2["重複コード"]
        b3["長大な関数"]
        b4["デッドコード"]
    end

    subgraph process["リファクタリング"]
        p1["命名改善"]
        p2["抽象化"]
        p3["分割"]
        p4["削除"]
    end

    subgraph after["リファクタリング後"]
        a1["明確な意図"]
        a2["DRY原則"]
        a3["単一責任"]
        a4["簡潔性"]
    end

    b1 --> p1 --> a1
    b2 --> p2 --> a2
    b3 --> p3 --> a3
    b4 --> p4 --> a4

    style before fill:#ffe6e6
    style process fill:#fff9e6
    style after fill:#e6ffe6

実践例から分かるように、適切なプロンプトを使えば GitHub Copilot がコード改善を効率的に支援してくれます。

まとめ

GitHub Copilot を活用したリファクタリングは、コード品質を継続的に高める強力な手段です。 この記事でご紹介した 4 つの観点(命名・抽象化・分割・削除)を意識することで、効果的なリファクタリングが実現できるでしょう。

各戦略の要点

命名改善では、意図を明確にする命名や規則の統一が重要です。 Boolean 値には is​/​has 接頭辞を、配列には複数形を使い、関数名は適切な動詞で始めましょう。

抽象化では、重複ロジックの共通関数化やインターフェースの定義が中心となります。 戦略パターンなどのデザインパターンを活用すれば、拡張性の高い設計が可能です。

分割では、単一責任の原則に従った関数分割や、レイヤー別のモジュール分離を実践します。 React のようなフレームワークでは、カスタムフックによる分割が効果的ですね。

削除では、デッドコードの特定や冗長性の排除を行います。 早期リターンやデフォルト引数などの現代的な構文を使えば、コードが簡潔になります。

プロンプト活用のコツ

GitHub Copilot に対するプロンプトは、具体的で明確なものほど良い結果が得られます。 「何を改善したいか」を明確に伝えることが、成功の鍵となるでしょう。

また、一度に全てをリファクタリングしようとせず、小さな単位で段階的に進めることをお勧めします。 各変更後にテストを実行し、動作を確認しながら進めることで、安全にリファクタリングできます。

リファクタリングの継続的実践

リファクタリングは、一度やって終わりではありません。 コードを書くたびに、少しずつ改善していく習慣をつけることが大切です。

GitHub Copilot は、そうした日々の改善活動を支える心強いパートナーとなります。 プロンプトを活用して、より良いコードを書き続けていきましょう。

チーム全体での活用に向けて

この記事でご紹介したプロンプト集は、チーム内で共有することで、より大きな効果を発揮します。 コードレビューの際に「このプロンプトを試してみては」と提案し合うことで、チーム全体のコード品質が向上していくでしょう。

リファクタリングは、技術的な改善だけでなく、開発者の学習機会でもあります。 GitHub Copilot が提案するコードから新しいパターンを学び、自分のスキルとして吸収していってください。

関連リンク