リファクタリングには二種類ある ― 構造を変えるものと、概念を変えるもの
私は最近の失敗から、大きな学びを得ました。
それは、
リファクタリングには二種類ある
ということです。
- 構造 を変えるリファクタリング
- 概念 を変えるリファクタリング
この区別を意識していなかったことが、バグの原因でした。
この記事では、自分なりに整理した実務的な優先順位と共にまとめます。
まず整理:構造と概念の違い
構造の変更
- 関数抽出
- 共通化
- 重複削除
- 名前変更
- ネスト解消
- 責務分離
振る舞いは変えない(意味は同じ)
これは「コードの形」を整える作業です。
概念の変更
- Boolean → sealed class
- nullable → 非null設計
- エラー型明確化
- 状態遷移の型化
- エンティティ再設計
- ドメインモデル再定義
- 責務の再解釈
プログラムが表現する「意味」が変わる
これは「コードが何を表現しているか」を変える作業です。
なぜこの区別が重要なのか?
私は「成功」という概念を Boolean で扱っていました。
しかし実際には:
- 作成成功
- 更新成功
- 失敗
という複数の意味がありました。
Boolean に押し込めたことで、 意味の差が見えなくなり、無意識に共通化してしまった のです。
これは単なる実装ミスではなく、
概念を粗く扱ったことによる設計ミス
でした。
なぜ通常は「構造 → 概念」なのか?
実務では、変更の順序がとても重要です。
基本原則は:
構造を整えてから、概念を強化する
理由はシンプルです。
① 変更の安全性
構造変更は:
- 振る舞いを変えないことが前提
- テストが通れば安全
概念変更は:
- 振る舞いの意味そのものを変える
- 影響範囲が広い
- リスクが高い
② デバッグのしやすさ
構造変更だけなら:
バグが出たら直前の変更が原因
と特定できます。
しかし構造と概念を同時に変えると:
どこで壊れたのかわからない
事故率が一気に上がります。
③ 複雑性の増幅
概念変更は波及範囲が広い:
- ViewModel
- UseCase
- Repository
- UI
- テスト
一気に影響します。
だから基本原則はこれです。
小さい意味変更を段階的に積み上げる
実務での使い分け(私の優先順位)
私が実務で使う順序はこうです。
第1段階:可視化
- ログ追加
- テスト追加
- 現在の挙動を完全に把握
ここを飛ばすと事故ります。
まず「今どう動いているか」を固定する。
第2段階:構造整理
- 重複除去
- 関数抽出
- ネスト削減
- 名前改善
意味は変えない
ここでは整理だけをする。
第3段階:責務の明確化
- UI責務とドメイン責務の分離
- 副作用の分離
- 純粋関数化
ここまで来ると、設計の輪郭が見えてきます。
第4段階:概念強化
- Boolean → sealed
- nullable排除
- エラー型明確化
- 状態遷移の型化
ここで初めて「概念」を変える。
まとめ
リファクタリングは一種類ではありません。
実務では次の二つに分類して考えると安全です。
| 種類 | 何を変えるか | リスク |
|---|---|---|
| 保存的リファクタリング | 構造 | 低い |
| 進化的リファクタリング | 概念 | 高い |
そして基本順序は:
- 保存的リファクタリングで構造を整える
- 意味を完全理解する
- 進化的リファクタリングで概念を強化する
この順番が事故を減らします。