リファクタリング
リファクタリング
外部から見た振る舞いを変えずに内部構造を改善すること。テストが安全網になる。
概要
リファクタリングは、「外部から見た振る舞いを変えずに、コードの内部構造を改善する」作業です。この定義は Martin Fowler の著書『リファクタリング』によって広まったもので、2つの条件が両方そろって初めてリファクタリングと呼べます。機能を追加したりバグを直したりするのは(良いことですが)リファクタリングではなく、逆に「きれいにしたつもりが挙動が変わってしまった」なら、それはリファクタリングの失敗です。
日々の開発では、変数名を分かりやすく変える、長すぎる関数を分割する、重複したコードを1か所にまとめる、といった小さな改善として現れます。派手さはありませんが、コードは書かれる時間より読まれる時間のほうが圧倒的に長いため、「読みやすく変更しやすい構造を保つ」ことは開発速度そのものに直結します。リファクタリングを怠ったまま劣化が積み重なった状態が、技術的負債と呼ばれるものの正体の一部です。
なぜ生まれたか
ソフトウェアは一度書いて終わりではなく、仕様変更や機能追加で書き換えられ続けます。ところがかつての開発現場では「動いているコードには触るな」が鉄則でした。構造を直したい箇所があっても、触れば壊れるかもしれず、壊れたかどうかを確かめる手段もない。結果としてコードはつぎはぎで劣化し続け、やがて誰も手を出せなくなって「全部作り直すしかない」という最悪の結末に至る — これが典型的な失敗パターンでした。
この状況を変えたのが、自動化されたテストという安全網と、「変更を意味の壊れない極小のステップに分解する」という技法の体系化です。1990年代の Smalltalk コミュニティで実践されていた手法を Kent Beck らの XP(エクストリーム・プログラミング)が開発プロセスに組み込み、1999年の Fowler の著書が「関数の抽出」「変数のインライン化」のように名前と手順のカタログとして整理しました。これによりコード改善は「勇者の賭け」から「誰でも安全に実行できる日常作業」に変わったのです。
詳細
何が引き金になるか — コードの臭い
リファクタリングは思いつきで始めるものではなく、「コードの臭い(code smell)」と呼ばれる兆候が引き金になります。代表的な臭いには、同じロジックがコピーされている「重複コード」、1つの関数が何画面にもわたる「長すぎる関数」、1つの修正のたびに複数のクラスを書き換える羽目になる「変更の分散」、あるオブジェクトが他のオブジェクトの内部データを触りすぎる「不適切な親密さ」などがあります。臭いの多くは結合度と凝集度の問題として説明でき、リファクタリングの多くは「結合を弱め、凝集を高める」方向への構造変換です。設計の定石であるデザインパターンは、しばしばリファクタリングの「到達点」として使われます — 最初からパターンを当てはめるのではなく、臭いに導かれて少しずつパターンの形に近づけていくのです。
安全網と小さなステップ
リファクタリングの成否を分けるのはテストの有無です。「振る舞いを変えない」ことを保証するには、変更の前後で同じテストが通ることを機械的に確認するしかありません。テストのないコードの変更は計器なしの飛行のようなもので、Michael Feathers はテストのないコードをそれだけで「レガシーコード」と定義したほどです。テストがない場合は、まず現状の振る舞いをそのまま写し取るテスト(特性テスト)を書いてからリファクタリングに入ります。
もう1つの鍵が、ステップを極小に保つことです。「関数を1つ抽出する → テストを実行 → Git にコミット」のように、数分単位でコードが常に動く状態を保ちながら進めます。ステップが小さければ、テストが落ちた瞬間に「直前の1手」が原因だと分かり、即座に巻き戻せます。大きな設計変更にも、この小さな一手の積み重ねとして到達するのがリファクタリングの流儀です。テストを先に書く TDD では、この改善ステップが「レッド→グリーン→リファクタ」のサイクルの一部として最初から組み込まれています。
リファクタリングとリライトの違い
「このコードはひどいから全部書き直そう」というリライト(作り直し)は、リファクタリングとは別物です。リライトは長期間「動かない・出荷できない」状態を経由し、その間も既存システムには修正が入り続けるため、追いかけるゴールが動き続けます。さらに、汚いコードに埋め込まれた無数のバグ修正や例外対応 — ドキュメントに残っていない暗黙知 — を作り直しで落としてしまう危険もあります。リファクタリングは同じ目的地へ「常に動く状態を保ったまま分割払いで向かう」アプローチであり、多くの場合こちらが安全です。リライトが正当化されるのは、技術基盤そのものが寿命を迎えたなど、小さなステップでは越えられない断絶があるときに限られます。
実務での位置づけ
リファクタリングは「いつかまとめてやる特別なイベント」ではなく、機能追加の一部として日常的に行うのが理想です。Fowler はこれを「まず変更を簡単にし、それから簡単になった変更をせよ」と表現しました。新機能を今の構造に無理やり押し込むのではなく、先に構造を新機能が自然に収まる形へ直してから実装する、という順序です。キャンプ場を来たときより少しきれいにして帰る「ボーイスカウト・ルール」も同じ精神です。一方で、リファクタリングと機能変更を1つのコミットに混ぜると、コードレビューで「どこが振る舞いの変更か」を判別できなくなるため、コミットやプルリクエストを分けるのが定石です。CI/CD で全テストが自動実行される環境が整っているほど、リファクタリングの心理的なハードルは下がります。