リファクタリングには二種類ある ― 構造を変えるものと、概念を変えるもの

私は最近の失敗から、大きな学びを得ました。

それは、

リファクタリングには二種類ある

ということです。

  • 構造 を変えるリファクタリング
  • 概念 を変えるリファクタリング

この区別を意識していなかったことが、バグの原因でした。

この記事では、自分なりに整理した実務的な優先順位と共にまとめます。


まず整理:構造と概念の違い

構造の変更

  • 関数抽出
  • 共通化
  • 重複削除
  • 名前変更
  • ネスト解消
  • 責務分離

振る舞いは変えない(意味は同じ)

これは「コードの形」を整える作業です。


概念の変更

  • Boolean → sealed class
  • nullable → 非null設計
  • エラー型明確化
  • 状態遷移の型化
  • エンティティ再設計
  • ドメインモデル再定義
  • 責務の再解釈

プログラムが表現する「意味」が変わる

これは「コードが何を表現しているか」を変える作業です。


なぜこの区別が重要なのか?

私は「成功」という概念を Boolean で扱っていました。

しかし実際には:

  • 作成成功
  • 更新成功
  • 失敗

という複数の意味がありました。

Boolean に押し込めたことで、 意味の差が見えなくなり、無意識に共通化してしまった のです。

これは単なる実装ミスではなく、

概念を粗く扱ったことによる設計ミス

でした。


なぜ通常は「構造 → 概念」なのか?

実務では、変更の順序がとても重要です。

基本原則は:

構造を整えてから、概念を強化する

理由はシンプルです。


① 変更の安全性

構造変更は:

  • 振る舞いを変えないことが前提
  • テストが通れば安全

概念変更は:

  • 振る舞いの意味そのものを変える
  • 影響範囲が広い
  • リスクが高い

② デバッグのしやすさ

構造変更だけなら:

バグが出たら直前の変更が原因

と特定できます。

しかし構造と概念を同時に変えると:

どこで壊れたのかわからない

事故率が一気に上がります。


③ 複雑性の増幅

概念変更は波及範囲が広い:

  • ViewModel
  • UseCase
  • Repository
  • UI
  • テスト

一気に影響します。

だから基本原則はこれです。

小さい意味変更を段階的に積み上げる


実務での使い分け(私の優先順位)

私が実務で使う順序はこうです。


第1段階:可視化

  • ログ追加
  • テスト追加
  • 現在の挙動を完全に把握

ここを飛ばすと事故ります。

まず「今どう動いているか」を固定する。


第2段階:構造整理

  • 重複除去
  • 関数抽出
  • ネスト削減
  • 名前改善

意味は変えない

ここでは整理だけをする。


第3段階:責務の明確化

  • UI責務とドメイン責務の分離
  • 副作用の分離
  • 純粋関数化

ここまで来ると、設計の輪郭が見えてきます。


第4段階:概念強化

  • Boolean → sealed
  • nullable排除
  • エラー型明確化
  • 状態遷移の型化

ここで初めて「概念」を変える。


まとめ

リファクタリングは一種類ではありません。

実務では次の二つに分類して考えると安全です。

種類何を変えるかリスク
保存的リファクタリング構造低い
進化的リファクタリング概念高い

そして基本順序は:

  1. 保存的リファクタリングで構造を整える
  2. 意味を完全理解する
  3. 進化的リファクタリングで概念を強化する

この順番が事故を減らします。