外部の動作を変えずにコードの内部構造を改善する作業。
リファクタリングとは、外部から見た動作(入力に対する出力)を変えずに、コードの内部構造を改善する作業のことです。新しい機能を足したりバグを直したりはしません。あくまで「中身の整理整頓」だけを行います。
身近な例で考えると、机の引き出しの中を整理するのに似ています。引き出しから取り出せる物(外部の動作)は前と同じですが、中の仕切りを整えたことで、次に物を探すのが楽になります。見た目の使い勝手は変わらず、内部だけ使いやすくするのがポイントです。
上のツールで▶ボタンを押すと、左の入力と右の出力(外部動作)が一定のまま、中央のコードだけが読みやすく変わっていく様子を確認できます。
リファクタリングは「小さく・頻繁に」行うのが基本です。まとめて一気にやるとリスクが大きくなるため、日々の開発の中でこまめに整理します。
良いタイミングには次のようなものがあります。
・機能を追加する前:これから手を入れる部分を先に整えておくと、追加作業が楽になる
・同じ処理の重複に気づいたとき:コピーされたコードを1つにまとめる
・コードレビューで指摘を受けたとき:読みにくいと言われた箇所を整理する
・TDDのRefactor段階:テストが緑になった直後に、すかさず整える
逆に避けたいのは「テストもなくリリース直前に大規模に書き換える」ことです。掃除に例えると、来客の直前に押し入れを全部ひっくり返すようなもので、片づかないまま本番を迎える危険があります。安全な時間帯に少しずつ進めましょう。
リファクタリングの大前提は「動作を変えない」ことです。しかし、人が手で直す以上、うっかり動作を壊してしまう危険は常にあります。これを防ぐのがテストです。
正しい進め方は次のとおりです。
・① 変更前にテストを用意:今の動作を記録し、緑(成功)であることを確認しておく
・② 小さく変更する:一度に大きく変えず、少しずつ整える
・③ 変更のたびにテスト:実行して緑のままなら、動作は変わっていない証拠
テストは「壊れたらすぐ知らせてくれる火災報知器」のようなものです。報知器がなければ、コードを直すたびにビクビクしなければなりません。テストがあるからこそ、安心して大胆に内部を整えられます。上のツールの最後で、変更後にテストを再実行して動作が同じだと確かめる流れを確認できます。
なぜリファクタリングが必要なのか。それは、コードは書き続けるうちに少しずつ複雑になっていき、放置すると「技術的負債」が積み上がるからです。
技術的負債(=テクニカルデット)とは、「今は動くが、後で直すのが大変になるコードの状態」のことです。お金の借金と同じで、放っておくほど利息が増えていくイメージです。
・読みにくいコードが増える → 機能を追加しようとしても、どこを直せばいいか分からない
・重複した処理が増える → 1か所直しても、コピーした場所を直し忘れてバグになる
・名前がわかりにくい → 別の開発者(または未来の自分)が意味を読み解けなくなる
リファクタリングはこの負債を少しずつ返済する作業です。動作(外部の見た目)は変えず、内部だけ整えることで、次の機能追加やバグ修正がしやすい状態を保ちます。家の掃除と同じで、汚れてからまとめて大掃除するより、こまめに片づける方が楽です。
| パターン名 | 改善前の問題 | 何をするか |
|---|---|---|
| 名前の変更 | 変数名が「x」や「tmp」など意味不明 | 役割が分かる名前に直す |
| 関数の抽出 | 同じ処理が複数箇所にコピーされている | 1つの関数にまとめて呼び出す形にする |
| 条件の簡略化 | if文が何重にも入れ子になっている | 読みやすいシンプルな条件に整理する |
| 定数化 | プログラム内に「3.14」などの数値が直書きされている | 名前付きの定数(例:PI)に置き換える |
上の表はどれも「外部から見た動作は変えず、コードの読みやすさ・保守のしやすさを上げる」改善です。リファクタリングには決まった手順(パターン)がたくさんあり、これらを積み重ねることで、コードが少しずつ整っていきます。
大切なのは、どのパターンを使うときもテストを実行して動作が変わっていないことを確認することです。テストが「緑のまま」であれば、改善が正しかったことの証拠になります。上のツールの「外部の動作:変わらない」という表示が、まさにリファクタリングの本質を示しています。