- はじめに
- 1. まず定義から — 何がリファクタリングでないか
- 2. 安全網なしに始めない — 特性化テスト
- 3. 継ぎ目を作る — 触れないコードに手を入れる
- 4. 小さな刻みの規律 — 常に緑を保つ
- 5. 並行変更でインターフェースを変える
- 6. 大きな変更を戻せる断片へ切る
- 7. リファクタリングと機能開発を混ぜるか
- 8. 自動リファクタリングツールと大規模変更
- 9. 止めどきを決める
- クイズ: 理解度を確認しましょう
- おわりに
- 参考資料
- 関連記事
はじめに
このブログにはすでに リファクタリングの経済学、いつコストが回収されるか があります。あの記事は変更頻度と修正コストから出発し、いつリファクタリングが利益になるかを計算します。つまり投資判断の記事です。
この記事は残りの半分です。やると決めた後に どう安全に進めるか を扱います。振る舞いの保存を主張する根拠をどう確保するか、触れないコードにどう最初の手を入れるか、インターフェースを変えながらどう常にデプロイ可能な状態を保つか、そして大きな変更をどの軸で切れば各断片が戻せる単位になるかが主題です。二つの記事は順に読めば、一つの決定と一つの実行になります。
1. まず定義から — 何がリファクタリングでないか
1-1. 名詞と動詞
Martin Fowler はリファクタリングを二つの品詞で定義します。名詞としてのリファクタリングは「ソフトウェアを理解しやすく修正コストを下げるために、観察可能な振る舞いを変えずに内部構造へ加える変更」です。動詞としてのリファクタリングは「一連のリファクタリングを適用し、観察可能な振る舞いを変えずにソフトウェアを再構成すること」です。
二つの定義に共通する条件は一つだけです。観察可能な振る舞いの保存です。この条件がリファクタリングを再構成や書き直しから区別します。
1-2. 「観察可能な振る舞い」の境界はチームが決める
定義は明快ですが境界はそうではありません。次は観察可能な振る舞いでしょうか。
- 応答時間: ユーザーには観察されます。性能が二倍遅くなったリファクタリングは振る舞いを保存したとは言いにくいです。
- ログ形式: 人には見えにくいものの、ログを解析するアラート規則にとっては契約です。
- エラーメッセージの文言と、ソートされていない一覧の実際の順序: 仕様には無くても、消費者が文字列で分岐したり順序に依存していることがあります。
したがって実務での第一歩は 今回のリファクタリングで何を保存するかを一行で書くことです。「公開 HTTP レスポンス本文とステータスコードは保存する、ログ形式は保存しない」と書いておけばレビュアーと争わずに済みます。
1-3. リファクタリングでないもの
- バグ修正: 定義上、振る舞いを変えます。リファクタリングのコミットに混ぜると差分から区別が消えます。
- 性能最適化: 観察可能な特性を意図的に変える作業です。構造改善を伴っても別作業として扱います。
- 書き直し: 振る舞いの保存を保証せず最初から作り直すことです。
- 依存の大規模アップグレード: ライブラリ側の振る舞い変化も一緒に吸収するため純粋なリファクタリングではありません。
区別が重要なのは好みの問題ではありません。レビューの仕方とロールバックのリスクが違うからです。リファクタリング PR を読むレビュアーは「構造は良くなったか」を見ます。その中に振る舞いの変更が隠れていれば、誰もその部分を検討しないまま承認されます。
実務規則を一つに縮めるとこうです。一つのコミットは構造変更か振る舞い変更のどちらか一方です。二つの帽子を同時にかぶらないという古い助言がこの話です。
2. 安全網なしに始めない — 特性化テスト
2-1. 振る舞いの保存は主張ではなく観測です
「振る舞いは変えていません」は検証可能な文でなければなりません。そのためには変更前後の振る舞いを比較する手段が要ります。レガシーコードを扱う文献で長く使われてきた名前が 特性化テスト (characterization test) です。Michael Feathers のレガシーコード関連の著作で広く知られる用語で、要点は単純です。
特性化テストはコードが 何をすべきかではなく、今何をしているかを固定します。だからバグも一緒に固定します。これは誤りではなく意図です。リファクタリングの途中でバグを直すと、失敗したテストがリファクタリングのミスなのかバグ修正なのか区別できなくなります。
# 例 — 特性化テストを作る手順
# 1) 入力を集める。本番ログ、サンプルリクエスト、境界値を集める。
# 2) 現在の実装に通して出力を記録する。期待値を手で書かない。
# 3) 記録した出力をゴールデンファイルとして固定する。
# 4) おかしく見える結果もそのまま固定し、コメントでのみ印を付ける。
def test_pricing_characterization(golden):
for case in load_cases("fixtures/pricing_inputs.jsonl"):
actual = calculate_price(**case)
# この値は「正しい」値ではなく「現在の」値という意味である。
golden.assert_match(case["id"], actual)
# 既知の異常: 割引率が 100% を超えると価格が負になる。
# リファクタリング中はこの振る舞いも保存する。修正は別コミットで。
2-2. カバレッジの数字は安全網ではありません
全体の行カバレッジ 90% は、今から触るファイルがテストされているという意味ではありません。必要なのは全体指標ではなく 変更対象のコードが実際に実行されるテストです。開始前に対象ファイルだけカバレッジを測り、実行されない分岐から特性化テストを埋めてください。
もう一つの罠は テストが通るという事実と、テストが何かを検証しているという事実は違うという点です。一度は故意にコードを壊し、テストが失敗するか確認してください。
2-3. どの層に安全網を置くか — ここは意見が割れます
リファクタリングの安全網を単体テストに置くか、より粗い境界テストに置くかは合意された答えがありません。問題は 内部構造に結合した単体テストはリファクタリングと一緒に死ぬという点です。クラスを分割すればそのクラスを対象にしていたテストも書き直しになり、結果として安全網が無い状態で作業することになります。
Kent C. Dodds はテスティングトロフィーを説明する中で、テストがソフトウェアの実際の使われ方に似るほど多くの確信を与えられる、とまとめています。この観点ではリファクタリングの安全網を結合層に置くほうが自然です。
ただしこの論争の底には用語の問題もあります。Fowler は実践的テストピラミッドの記事で、「単体」が何を意味するか三人に尋ねれば微妙に異なる四つの答えが返ってくるだろうと書き、Dodds も単体テストの定義が二十四種類ほど存在すると認めたうえで Justin Searls の言葉を引用します。要旨は、テストの種類の比率を巡って議論すること自体が注意を逸らす、というものです。
- 単体テストの安全網: 速いです。小さな刻みを保つにはフィードバックループが秒単位である必要があります。ただし構造に結合していればリファクタリング対象と一緒に崩れます。
- 境界・結合テストの安全網: 内部構造を変えても生き残ります。ただし遅いです。Fowler は端から端までのテストが「悪名高く不安定で、予期しない理由でしばしば失敗する」、そして「保守コストが大きく、かなり遅く動く」と書いています。
実務上の折衷はたいていこうです。変える構造の外側の境界に安全網を置いてください。クラス三つを再配置する計画なら、その三つを包むモジュール境界にテストを置くということです。この判断は上記の資料が教えてくれるものではなく、この記事が提案する規則です。
2-4. テストなしで振る舞いの保存は可能か
これも割れる主題です。可能だとする側は、静的型システムと検証済みの自動リファクタリングだけを使えば機械的に安全だと考えます。不可能だとする側は、どの言語でも動的参照と副作用が残っており「安全な変換」という信念こそ最も危険だと考えます。軸は三つです。言語の静的検査の強さ、変換が純粋に機械的か判断を含むか、そしてコードの副作用の密度です。
3. 継ぎ目を作る — 触れないコードに手を入れる
3-1. 鶏と卵
レガシーコードで最もよくある膠着はこうです。テストを書くには依存を切る必要があり、依存を切るにはコードを変える必要があり、コードを変えるにはテストが必要です。この循環を破る概念が 継ぎ目 (seam) です。その地点のコードを編集せずに振る舞いを差し替えられる場所を指し、この用語も Michael Feathers のレガシーコード関連の著作で広く知られています。
抜け道は一つだけです。最もリスクの低い変更だけを先に行うことです。継ぎ目を作る変更は、それ自体ではほとんど何もしないべきです。
// 例 — リスクの低い順に継ぎ目を作る三段階
// 段階0: 触れない状態。時計とネットワークが関数の中に埋まっている。
async function expireSessions() {
const now = new Date()
const rows = await db.query('SELECT * FROM sessions')
return rows.filter((r) => r.expires_at < now)
}
// 段階1: 引数を足す。既定値を与えればすべての呼び出し元はそのまま動く。
async function expireSessions({ now = new Date(), query = db.query } = {}) {
const rows = await query('SELECT * FROM sessions')
return rows.filter((r) => r.expires_at < now)
}
// 段階2: これでテスト可能になった。本当のリファクタリングはここから始まる。
// expireSessions({ now: new Date('2026-01-01'), query: fakeQuery })
3-2. 非決定性の三つが最初の継ぎ目になりがちです
レガシーコードをテスト可能にするとき最初に引っかかるのは、たいてい 時計、乱数、ネットワークです。この三つを注入可能にすれば、その後の作業が急激に楽になります。ファイルシステムと環境変数がその次です。
3-3. 継ぎ目の種類とコスト
- 引数の継ぎ目: 引数を足して既定値を与えます。最も安価で安全です。
- コンストラクタの継ぎ目: 依存をコンストラクタへ移します。呼び出し元が多ければ並行変更が要ります。
- サブクラスの継ぎ目: メソッドを抽出しテスト用サブクラスで再定義します。素早い応急処置です。
- モジュールの継ぎ目: 読み込み時点で実装を差し替えます。強力ですがテスト間の隔離が壊れやすいです。
- プロセスの継ぎ目: スタブサーバーでプロセスの外から横取りします。最も現実的で最も遅いです。
3-4. 継ぎ目と呼んではいけないもの
本番コードにテストかどうかを問う分岐を入れるのは継ぎ目ではありません。if (isTest) のような条件は テストされる経路と実際に走る経路を別物にするため、安全網の目的そのものを壊します。継ぎ目は同じコードを別の協力者と一緒に走らせる装置であって、別のコードを走らせる装置ではありません。
4. 小さな刻みの規律 — 常に緑を保つ
4-1. 規律の定義
小さな刻みの規律は「こまめにコミットする」ことではありません。どの時点で止めてもデプロイ可能な状態を保つことです。この条件を守れば、リファクタリングを中断するコストがほぼ 0 になり、中断コストが 0 なら始める敷居も下がります。
[リファクタリングのループ — 一周が数分を超えないように]
1. 緑の確認 テストを走らせ、今が緑かをまず見る
2. 一段階の変更 改名一つ、抽出一つ、移動一つ。二つを束ねない
3. テスト 赤なら即座に戻す。直そうとしない
4. コミット 何をなぜ変えたかを一行で
5. 繰り返し
[中断規則]
- 赤の状態が 10 分を超えたら戻し、刻みをさらに小さくして再開する
- 戻すコストがやり直すコストを超えたなら、その刻みはすでに大きすぎた
4-2. 刻みの大きさを決める基準
刻みが適切かを判断する実用的な基準は一つです。失敗したとき捨ててやり直せるかです。捨てるのが惜しいと感じるなら、その刻みはすでに大きすぎます。初心者によくある失敗は 30 分間赤のまま「あと少し」と言う状況で、そこが戻すコストの最大点です。
4-3. フィードバックループの速度が規律を決めます
テストが 15 分かかれば、誰も一段階ごとには走らせません。すると複数の変更が一塊になり、失敗したとき原因を絞れなくなります。つまり 遅いテストはリファクタリングの手順全体を壊します。始める前に対象範囲だけ速く走らせる方法を確保し、全体スイートはコミットの後に走らせてください。
4-4. コミット履歴も安全網です
機械的な変更と判断を含む変更を同じコミットに入れないでください。改名 3,000 行とロジック修正 8 行が一つのコミットにあれば、レビュアーは 8 行を見つけられません。コミットを分けて初めて「このコミットだけ読めば済みます」と言えます。
5. 並行変更でインターフェースを変える
5-1. 拡張・移行・縮小
インターフェースを変えながら常に緑を保つ標準手法が、Martin Fowler のまとめた Parallel Change です。Fowler は拡張フェーズを「インターフェースを広げて旧版と新版の両方を支えるようにすること」、移行フェーズを「旧版を使っているすべてのクライアントを新版へ移すことであり、段階的に行える」と説明し、すべての利用箇所が移った後に縮小フェーズで旧版を取り除くとしています。このパターンは Joshua Kerievsky に帰属します。
// 例 — 関数シグネチャを並行変更で差し替える
// 拡張(expand): 新しい形を追加しつつ、古い形はそのまま残す。
export function createOrder(userId, items, options) {
return createOrderV2({ userId, items, ...options })
}
export function createOrderV2(input) {
/* 新しい実装 */
}
// 移行(migrate): 呼び出し元を一つずつ移す。各移動が独立したコミットになる。
// 旧関数には利用を検知できる信号を残す。
export function createOrder(userId, items, options) {
logger.warn('createOrder is deprecated', { caller: new Error().stack })
return createOrderV2({ userId, items, ...options })
}
// 縮小(contract): 呼び出しが 0 になったことをログで確認してから取り除く。
5-2. 非推奨の信号はコードに残す
ドキュメントにだけ書いた非推奨予告は誰も読みません。実際に機能する信号は 呼び出しを検知するか、ビルドを騒がしくするものです。
- 実行時ログ: 旧経路の呼び出し回数を数えます。縮小の時期をデータで決める唯一の方法です。
- 型レベルの印とコンパイラ警告: 新しく書かれるコードが旧経路を使えないようにします。
- リント規則: 新規利用を禁止し既存箇所だけ例外リストに置き、そのリストが減ることを指標にします。
5-3. 消費者を把握しているかが分かれ目です
呼び出し元をすべて把握し一つのコミットで直せるなら、並行変更は過剰です。並行変更が必要なのは、消費者が別リポジトリにある、デプロイ時期が違う、あるいは外部に公開されている場合です。判別の問いは一つです。「今変えたらコンパイルが壊れるコードを、私はすべて見られるか」。見られないなら並行変更です。
5-4. 縮小フェーズを忘れるとそれが負債になります
拡張と移行だけ行い縮小を飛ばせば、コードベースには永久に二つの経路が残ります。これが積み上がったものが、いわゆる技術的負債の大きな部分です。移行の開始と同時に縮小作業をチケット化しておくことが、唯一機能する予防策です。管理方法は 技術的負債 完全ガイド で扱います。
6. 大きな変更を戻せる断片へ切る
6-1. 切る四つの軸
軸A. 層で切る リポジトリ層だけ → サービス層だけ → コントローラだけ
軸B. 呼び出し元で切る 一度に一つの呼び出し元だけ新インターフェースへ移す
軸C. データ方向で切る 読み取り経路が先 → 書き込み経路が後 (またはその逆)
軸D. 実行時分岐で切る フラグで新実装を一部トラフィックにだけ露出する
各断片が満たすべき条件
- 独立してデプロイできる
- 独立して戻せる
- それ自体で有害でない (価値が無くてもよいが、損であってはならない)
6-2. 長いブランチがリファクタリングを殺す仕組み
リファクタリング専用ブランチを 3 週間維持すると、二つのことが同時に起きます。他の人がその間ずっと旧構造の上にコードを書き、マージ衝突が時間に比例するより速く増えます。結局マージ自体が安全網の無い大規模変更になります。
だから大きなリファクタリングほど、ブランチではなく主ブランチの上で断片として進めるべきです。システム規模の段階的置換が必要なら Strangler Fig パターン完全ガイド の構造がそのまま当てはまります。
6-3. 断片の順序を決める
- 最も多くの情報をくれる断片から。設計の仮定が誤っていたなら早く知るべきです。
- 最も戻しにくい断片は最後に。データ形式の変更がたいていここに当たります。
- 他人を止める断片は素早く通過。広範囲の改名が代表例です。
- 断片の間には実際のデプロイを挟みます。デプロイせずに積んだ断片は一つの大きな変更と同じです。
デプロイ単位の可逆性を判別する基準は デプロイ戦略 完全ガイド のチェックリストと同じです。とくにデータ形式に触れる断片はそれ自体で条件付き可逆なので、拡張・移行・縮小を別デプロイに分ける必要があります。
7. リファクタリングと機能開発を混ぜるか
7-1. ここは意見が割れます
リファクタリングを別チケットで管理するか機能作業に含めるかは古い論争です。
- 別チケット側: レビューが楽になり、ロールバック単位が分離され、投資量を追跡できます。
- 機能作業に含める側: 別チケットは優先順位争いで必ず後回しになり、文脈を把握した状態でやるほうがはるかに安上がりです。
- ボーイスカウト規則側: 触った場所を少しずつ良くすれば負債は自然に減ります。
- 計画的リファクタリング側: 少しずつ直す方法では構造的問題は解決できず、大きな変更には合意が要ります。
軸は三つです。コード所有権のモデル、レビューの処理速度、そして変更頻度の分布です。所有者が明確でレビューが速い組織では混ぜるほうがうまく機能します。レビューが遅い組織では混ぜるほど PR が大きくなり、さらに遅くなる悪循環が生まれます。
7-2. レビュー速度がリファクタリングの量を決めます
Google のエンジニアリング慣行の文書はこの因果を明示しています。遅いレビューは「コードの整理、リファクタリング、既存の変更へのさらなる改善を萎縮させる」というものです。同じ文書は、コードレビュー要請への応答にかかってよい最大時間を営業日一日と定めています。
レビュー基準についても同じ文書はこうまとめます。レビュアーは変更が完璧でなくとも 対象システムの全体的なコードの健全性を確実に改善する状態なら承認する側を選ぶべきだ、というものです。さらに必須でない仕上げの提案には「Nit: 」を付けて作者が無視できるようにすることを勧めます。好みの論争が起きればリファクタリングはそこで止まるので、この慣行がとくに重要です。
7-3. 実務上の折衷
- コミットは分け、PR は合わせてもよい。「このコミットは純粋なリファクタリング、次が機能」と分ければレビュアーは読み方を選べます。
- 順序はリファクタリングが先。機能を先に入れて後で整理すると、整理のコミットはたいてい永遠に来ません。
- 大きさの上限を決める。リファクタリング PR が大きくなるほどレビュアーは読まずに承認するようになります。この傾向は上記資料が測定したものではなくこの記事の経験則なので、上限値はチームが決めるべきです。
- 機械的な変更は別 PR に分離。8 節のコードモッドがここに当たります。
レビューの会話については コードレビューの話し方 に詳しくあります。
8. 自動リファクタリングツールと大規模変更
8-1. IDE の自動リファクタリングが安全な条件
IDE の改名やメソッド抽出は構文木を理解して動くため、文字列置換よりはるかに安全です。しかし 静的に追跡できない参照の前では黙って失敗します。
- リフレクションと動的ディスパッチ: 文字列から作られた名前はツールに見えません。
- 直列化された識別子: DB やキューに保存されたクラス名やイベント型名はコードではなくデータです。
- 設定ファイル、テンプレート、他リポジトリの消費者: いずれもツールの検索範囲外です。
だから自動リファクタリングを走らせた後にリポジトリ全体の文字列検索を一度行うことが 安価な保険になります。
8-2. コードモッドの手順
[コードモッドの進め方]
1. 変換規則を構文木ベースで書く (正規表現置換はコメントや文字列まで書き換える)
2. 20 ファイルほどの標本に適用し、結果を人が直接読む
3. 規則を直す。2 と 3 を結果が退屈になるまで繰り返す
4. 全体に適用し、全テストを走らせる
5. レビュー依頼時に「何をレビューすべきか」を併記する
[レビュー対象 — 結果の差分全体ではない]
- 変換スクリプトそのもの
- ツールが扱えなかった例外リスト
- 無作為に選んだ 10 ファイルの変換結果
- テスト結果とカバレッジの変化
8-3. 大規模変更は分割するほど危険なことがあります
一般には小さく分けるほうが安全ですが、機械的変換は例外になる場合があります。改名を複数の PR に分けると 中間状態でコードがコンパイルできない、あるいは二つの名前が共存する期間が長くなります。選択肢は、全体を一度にマージするか、5 節の並行変更で中間状態を合法にするかです。分けられない変更を無理に分ければ、安全ではなく不安定な中間状態が手に入ります。
8-4. 結果ではなく振る舞いを比較してください
大規模変更の最終検証は差分を読むことではありません。同じ入力に対して変更前後の出力が一致するかを比べるほうがはるかに強いです。ゴールデンファイル比較、そして本番トラフィックの複製を両実装に流して結果を突き合わせる方式が代表的です。後者はデプロイ戦略のシャドー手法と同じ構造なので、副作用の隔離が前提になります。
9. 止めどきを決める
9-1. 終了条件を先に書く
リファクタリングは本質的に終わりがありません。だから始める前に 終了条件を一文で書くべきです。良い終了条件は構造指標ではなく、次の変更のコストで書きます。
- 弱い目標: 「循環的複雑度を 15 以下にする」
- 強い目標: 「決済手段を一つ追加するとき直すファイルが三つ以下になる」
強い目標は次の機能を実際に入れてみれば達成の可否が即座に分かるので、検証できます。
9-2. 止めるべき合図
- 差分に本来の目的と無関係なファイルが現れ始める
- マージ衝突の解決時間がリファクタリング自体より長くなる
- 他人の作業を止めているという話が二度以上出る
- 「これさえ終われば」が三回繰り返される
- 安全網が赤のままで、その原因がリファクタリングかどうか分からない
- そもそもこのリファクタリングがどの次の変更を安くするためだったか答えられない
9-3. 中断も一つの結果です
途中で止めること自体は失敗ではありません。失敗は 止めた状態を記録せずに立ち去ることです。止めるときは、ここまでの部分をデプロイ可能な状態で締め、残りを負債リストに載せ、何を知ったかを一段落で残します。
9-4. 効果を測る
リファクタリングが実際に役立ったかは、コード指標より変更コストで確認するほうが正直です。DORA は変更のリードタイムを「変更がバージョン管理にコミットされてから本番にデプロイされるまでの時間」、変更失敗率を「デプロイ後に即時の介入を要したデプロイの割合」と定義します。リファクタリングした領域の作業でこの二つが改善しないなら、構造は綺麗になっても目的は達成されていません。指標が悪化したなら、戻すことも選択肢に残してください。
クイズ: 理解度を確認しましょう
クイズ 1: 同僚が「リファクタリング中に見つけたバグも一緒に直した」と PR を出しました。何を依頼しますか?
回答: バグ修正を別コミットまたは別 PR に分けるよう依頼します。
解説: リファクタリングの定義は観察可能な振る舞いの保存であり、バグ修正は定義上、振る舞いを変えます。一つのコミットに混ざると、テストが失敗したときリファクタリングのミスか意図した振る舞いの変更か区別できず、ロールバックでも望んだ半分だけを戻せません。特性化テストが今の誤った振る舞いまで固定する理由も同じです。
クイズ 2: リファクタリング対象のクラスに単体テストが 40 個あります。そのクラスを三つに分割する計画です。安全網に何を用意しますか?
回答: 分割するクラス群の外側の境界、つまりそれらを使うモジュール水準に特性化テストをまず作ります。
解説: クラス内部の構造に結合した単体テストは、分割した瞬間に書き直しになるため、まさにリファクタリング中に安全網が無い状態になります。変える構造を包む境界にテストを置けば、内部をどう再配置しても生き残ります。
クイズ 3: 自動改名ツールでクラス名を変え、すべてのテストが通りました。それでも確認すべき場所は?
回答: 文字列でシンボルを参照するすべての地点です。リフレクション、保存済みの型名、設定ファイルとテンプレート、そして他リポジトリの消費者です。
解説: 構文木ベースのツールは静的に追跡できる参照しか見ません。DB やメッセージキューにすでに保存された名前はコードではなくデータなのでツールの視野の外にあり、その場合の改名は即座に不可逆な変更になります。リポジトリ全体の文字列検索はほとんどコストのかからない保険です。
クイズ 4: 3 週間のリファクタリングブランチがマージ時に 300 件の衝突を出しました。次は何を変えますか?
回答: 長いブランチを維持する代わりに、主ブランチの上で独立してデプロイできる断片として進めます。必要なら並行変更で中間状態を合法化します。
解説: 長いブランチでは他の人が旧構造の上にコードを足し続け、衝突は時間に対して線形より速く増えます。層別、呼び出し元別、データ方向別に切り、断片の間に実際のデプロイを挟めば、各断片が戻せる単位になります。
クイズ 5: リファクタリングを終えましたが「良くなった」根拠がコード指標だけです。他に何を見ますか?
回答: その領域で行われる実際の変更のコスト、すなわち変更のリードタイムと変更失敗率を見ます。
解説: 循環的複雑度や結合度は代理指標にすぎません。リファクタリングの目的は次の変更を安くすることなので、検証も次の変更で行われるべきです。指標が改善しなかったなら、戻すことも正当な選択です。
おわりに
リファクタリングの難しさは、どのパターンを知っているかではありません。振る舞いを保存したと言える根拠を確保すること、そしてどの時点で止めてもデプロイ可能な状態を保つことです。この二つが揃えば残りは機械的な反復であり、揃わなければどれほど良い設計感覚も賭けになります。
手順を一行に縮めるとこうです。保存するものを書き、安全網を作り、継ぎ目を入れ、小さな刻みで変え、インターフェースは並行変更で移し、大きな変更は戻せる断片に切り、終了条件に達したら止めます。そして止めた場所を記録に残します。
参考資料
- Definition of Refactoring — Martin Fowler — 名詞と動詞としてのリファクタリングの定義、そして観察可能な振る舞いの保存がリファクタリングと再構成を分ける条件であることを引用しました。2026-08-15 確認。
- Parallel Change — Martin Fowler — 拡張、移行、縮小の三段階の定義と Joshua Kerievsky への帰属を引用しました。2026-08-15 確認。
- The Practical Test Pyramid — Martin Fowler — 「単体」の定義に合意が無いという記述と、端から端までのテストが不安定で保守コストが大きく遅いという記述を引用しました。2026-08-15 確認。
- The Testing Trophy and Testing Classifications — Kent C. Dodds — テストが実際の使われ方に似るほど確信を与えるという主張と、単体テストの定義が多数あり比率の議論が注意を逸らすという指摘を引用しました。2026-08-15 確認。
- Code Review Developer Guide, The Standard of Code Review — Google — 全体的なコードの健全性を確実に改善するなら完璧でなくとも承認するという基準と、必須でない提案に「Nit: 」を付ける慣行を引用しました。2026-08-15 確認。
- Code Review Developer Guide, Speed of Code Reviews — Google — 遅いレビューがコード整理とリファクタリングを萎縮させるという記述と、レビュー応答の最大時間が営業日一日という基準を引用しました。2026-08-15 確認。
- DORA metrics: the four keys — DORA — 変更のリードタイムと変更失敗率の定義を引用しました。2026-08-15 確認。
- 特性化テストと継ぎ目という用語は Michael Feathers のレガシーコード関連の著作で広く知られたもので、原文は引用せず概念のみ使用しました。2 節の安全網の配置規則、4 節のループと中断規則、6 節の切断軸、8 節のコードモッドのレビュー対象、9 節の中断合図は、上記資料ではなくこの記事で整理した手順です。
関連記事
- このブログの関連記事: リファクタリングの経済学、いつコストが回収されるか
- このブログの関連記事: ソフトウェアテスト戦略 完全ガイド
- このブログの関連記事: 検証の設計 — テストを信頼の根拠として見る
- このブログの関連記事: Strangler Fig パターン完全ガイド
- このブログの関連記事: コードレビューの話し方
- 関連ツール: 協業RPG
完全ガイドシリーズ
- 前の記事: デプロイ戦略 完全ガイド: 戻せるものと戻せないもの
- 次の記事: 技術的負債 完全ガイド: 特定し、測り、返す
현재 단락 (1/175)
このブログにはすでに [リファクタリングの経済学、いつコストが回収されるか](/blog/2026-07-31-refactoring-economics-when-it-pays) があります。あの...