コード品質 レッスン3

リファクタリングの進め方

外部から見た振る舞いを変えずに内部構造を改善するリファクタリングの進め方と安全に行うコツを学ぶ

リファクタリングとは何か

リファクタリングとは、外部から見た振る舞いを変えずに、 内部の構造だけを改善することです。マーティン・ファウラーの定義では 「観察可能な振る舞いを保ったままソフトウェアの内部構造を改善する規律だった手法」とされます。

重要な区別

バグ修正や機能追加を同時に行うことはリファクタリングではありません。 振る舞いが変わる変更と、振る舞いを変えない構造改善は、コミットとして分けるのが原則です。

安全に進めるための原則

1. テストという安全網を先に用意する

振る舞いが変わっていないことを確認する手段がなければ、リファクタリングは博打になります。 対象コードにテストがなければ、まずテストを追加してから着手します。

2. 小さなステップを積み重ねる

一度に大きく書き換えるのではなく、「名前を変える」「関数を抽出する」など 小さな変換を1つずつ行い、都度テストを実行して確認します。

3. リファクタリングと機能追加を混在させない

「構造を整理するコミット」と「機能を追加するコミット」を分けることで、 レビューしやすく、問題が起きた際の切り分けも容易になります。

代表的なリファクタリング手法

「関数の抽出(Extract Function)」は最も基本的で頻繁に使う手法の1つです。 長い処理の一部に名前をつけて独立させることで、可読性と再利用性が向上します。

// before: 1つの関数に責任が混在している
function printInvoice(order) {
  let total = 0;
  for (const item of order.items) {
    total += item.price * item.quantity;
  }
  const tax = total * 0.1;
  const finalAmount = total + tax;

  console.log(`小計: ${total}円`);
  console.log(`税額: ${tax}円`);
  console.log(`合計: ${finalAmount}円`);
}

// after: 計算ロジックを関数として抽出する
function calculateSubtotal(items) {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

function calculateTax(subtotal, rate = 0.1) {
  return subtotal * rate;
}

function printInvoice(order) {
  const subtotal = calculateSubtotal(order.items);
  const tax = calculateTax(subtotal);
  const total = subtotal + tax;

  console.log(`小計: ${subtotal}円`);
  console.log(`税額: ${tax}円`);
  console.log(`合計: ${total}円`);
}

抽出したcalculateSubtotalcalculateTaxは 単体テストの対象にしやすく、他の場所でも再利用できます。

いつリファクタリングするか

リファクタリングは単独のタスクとして予定するよりも、「ボーイスカウトルール」のように、 触れたコードを少しだけきれいにしてから離れる習慣として実践するのが効果的です。

  • • 新機能を追加する前に、その周辺コードを整理してから着手する
  • • コードレビューで指摘された構造上の問題を、次の変更のタイミングで解消する
  • • 大規模な書き直しは別途計画を立て、段階的に進める

ポイント

  • • リファクタリングは振る舞いを変えずに内部構造だけを改善すること
  • • 着手前にテストという安全網を用意する
  • • 小さなステップで進め、都度テストで確認する
  • • 機能追加とリファクタリングはコミットを分ける

確認クイズ

1 / 3

リファクタリングの定義として正しいものはどれか?