Refactoring
構造と意図の整合をとり、変更容易性(changeability)を高める。 リファクタリングの目的は、将来の変更コストを下げること。それ以外にない。
詳細なコーディングルールではなく、「まともなエンジニアなら理解している良いコードとは何か」にフォーカスする。
前提: ローカル開発状態
このスキルの対象は、開発完了後でまだステージング・プロダクションに反映されていないコードである。 外部への影響を気にせず、構造をあるべき姿に大胆に合わせられる。
使用時期
- コードが理解しにくい、または保守しにくい場合
- 関数・クラスが大きすぎる場合
- Code Smellに対処が必要な場合
- コード構造のせいで機能追加が困難な場合
- ユーザーが「コードをきれいにして」「リファクタリングして」「改善して」と求めた場合
リファクタリングの2つの次元
リファクタリングは以下の2つの次元で行われる。両方を意識して行う。
1. 内部の衛生
コードの読みやすさ・書きやすさを改善する行為。外部から観測できる振る舞いは変えない。
- 意図を伝える命名
- 重複の排除
- 関心の分離
- 不要なものの削除
2. 構造の再設計
責務・境界・契約・依存関係を見直し、変更容易性を高める行為。 旧来はリファクタリングと区別されていたが、AIの認知範囲と実行能力を前提に本スキルに含める。
目的は、目の前のタスクに局所最適化された構造を壊し、将来の変更を見据えた構造へ再設計することにある。
AIは現在のタスクに引っ張られて局所最適なコードを作りやすい。 構造・契約・境界・データ設計を見直し、将来の変更コストを下げる方向に補正することを重視する。
AI向けの原則
以下の原則は「人間」が主体で設計・コーディングしていた旧来の考え方である。 人間を上回る認知範囲と実行手数を持つAIが自律的にコードを改善する上では、これらは適用しない。
適用しない従来の原則
- リファクタリングは日常の開発フローの中で少しずつ行うもの
- 「間違えようがないほど小さい変更」を1ステップとし、それを繰り返す
AIが従うべき原則
構造を観察し、不適切なら大きく変える
些細なロジック修正や見た目の変更の際は適用不要。 変更の関連範囲を十分に調査した上で、構造・状態・境界に問題があれば根本から正す。
中途半端を避ける
ファイル分割や、責務移譲、分離、そのほかリファクタリングに伴いルール・規約が変更されうる場合は、 必ずこのプロジェクト全体を一度精査すること。 新しいルールや規約変更が統一を持って全体最適されることを目指す。
中途半端な変更によって例外が増える変更は避ける。 逆にやる時は一気にやる。AI前提では大規模な変更であっても一貫性が保たれるのであれば広範囲に実施すべき。
関連変更を漏らさない
変更に関連する部分を十分に調査し、網羅的に扱う。 一部だけ改善して他を放置すると、かえって一貫性が損なわれる。
実施フロー
Phase 1: 内部の衛生
1. 理解
コードの内容を読み、何をしているかを把握する
2. 観測
「観測シグナル」セクションの各観点でコードを評価する
3. 仮説立案
観測した兆候に対して「何が問題か」の仮説を立てる
兆候の検知 → 即修正は禁止
4. 検証
呼び出し元・依存関係で仮説を検証する
5. 処方
仮説が正しければ適切な変更を選択するPhase 2: 構造の再設計
1. 目的の理解
変更の目的・意図・範囲を理解する
2. 構造の観察
責務、境界、依存方向、契約の形状、データ構造、命名を洗い出す
3. 境界デザイン
機能境界・責務分離・モジュール分割を判断する
→ module-boundary-design スキルを必ず参照すること
4. 再構築
観察結果に基づき構造を再整理・構築する
中途半端にせず、関連範囲を一貫して変更する判断原理
個別具体の規約ではなく、良い構造とは何かという普遍的なルール。
結合の配置
結合は無くせない。どこに置くかが問題。
- 変わるもの同士は一緒に、変わらないものと分離する
- 依存は安定側(変化が少ない側)へ向ける
- 循環依存は設計の誤りを示す。必ず解消する
意図の明確さ
コードは読み手のために書かれる。
- 命名は実装ではなく責務を表す
- コメントが必要な箇所は、抽出+意図を伝える命名で表現できないか考える
- 命名に迷うのは設計に迷いがある兆候
抽象化はコストである
抽象化は正しければ利益だが、間違えば重複以上の負債になる。
- 間違った抽象を共有するより、重複を許容する方がコストが低い
- 抽象化は「広げること」だけでなく「受け入れる可変性を制限すること」でもある
複雑性の管理
複雑性はゼロにできない。どこに押し込むかが問題。
- 本質的な複雑性(ドメイン固有)は受け入れる
- 偶有的な複雑性(設計のまずさ由来)は排除する
転用可能性の判断
その処理に多数の主語を置いたとき、転用の可能性が高いか:
- 特定のドメイン知識に依存しない汎用的な処理 → 転用可能性が高い
- 特定のビジネスルールに密結合 → 転用可能性は低い
- 転用可能性が高いと判断した場合のみ抽象化を検討する
観測シグナル
判断原理の具体的な現れ方。兆候を検知したら、まず対応する原理に照らして仮説を立てる。 検知 → 即修正は禁止。
結合・境界の兆候
| シグナル | 何を兆しているか | 対応原理 | 確認すべき質問 |
|---|---|---|---|
| 1つの関数・クラスが複数の理由で変更される | 責務の混在。関心が分離されていない | 結合の配置 | このモジュールが変わる理由は何か。複数あるなら分離すべき |
| ある変更が複数のファイルに波及する | 結合の誤配置。変更軸がまたがっている | 結合の配置 | この変更は1箇所に閉じられるか |
| 他モジュールの内部構造に直接依存している | 境界の侵害。カプセル化が崩れている | 結合の配置 | 公開インターフェース経由でアクセスできるか |
| 循環参照がある | 依存方向の誤り。安定性が逆転している | 結合の配置 | 依存を一方通行にできるか |
抽象化の兆候
| シグナル | 何を兆しているか | 対応原理 | 確認すべき質問 |
|---|---|---|---|
| 似た処理が複数箇所にある | 抽象化の未発見、または意図的な分岐 | 抽象化はコストである | 転用可能性は高いか。2箇所以上で実際に使われているか |
| 抽象の中に条件分岐が増殖している | 間違った抽象の共有 | 抽象化はコストである | 抽象を解体して重複に戻した方が単純にならないか |
| 将来必要になるかもしれない抽象化 | 予測による過剰設計 | 抽象化はコストである | 現在の具体例だけで十分か |
複雑性の兆候
| シグナル | 何を兆しているか | 対応原理 | 確認すべき質問 |
|---|---|---|---|
| 起こり得ない状態への防御コード | 偶有的複雑性の増殖 | 複雑性の管理 | このエラーは実際に起こりうるか |
| 正常系の流れを妨げるtry-catch | 防御的コーディングの過剰 | 複雑性の管理 | 正常系の可読性を損なっていないか |
| 同じ失敗を複数層で処理している | 責務の不明瞭さ | 複雑性の管理 | どの層でこの失敗を扱うべきか |
| エラー型や意味が潰れている | 情報の喪失。呼び出し元が判断できない | 複雑性の管理 | エラーの原因と回復手段が伝わるか |
命名の兆候
| シグナル | 何を兆しているか | 対応原理 | 確認すべき質問 |
|---|---|---|---|
| 名前から責務が読み取れない | 意図の欠落 | 意図の明確さ | この名前を見て何をするか分かるか |
| Manager / Handler / Processor などの汎用名 | 複数の責務が混在している可能性 | 意図の明確さ | このモジュールの責務は一言で言えるか |
| コメントで補足が必要な名前 | 命名の失敗。抽出で表現できないか | 意図の明確さ | 抽出+適切な命名でコメントを不要にできるか |
| 同じ名詞が文脈によって違う意味で使われている | 境界の未設定。Bounded Contextの混線 | 結合の配置 | この名詞の意味は文脈ごとに異なるか |
アンチパターン
AIがリファクタリングで犯しやすい誤り。
- Smellの全消し — 兆候を欠陥と誤解し、文脈なしで修正する
- 予測抽象化 — 将来のユースケースを予想して抽象化する
- 過剰防御 — 起こり得ないシナリオにエラーハンドリングを追加する
- 部分改善 — 関連範囲の一部だけ直し、一貫性を崩す
- 命名だけの修正 — 構造問題を放置し、名前だけ変えて完了とする
- 規約の機械的適用 — 判断原理を飛ばしてパターンを当てる
完了条件
以下の条件を満たせば完了:
- 関連範囲の一貫性が保たれている
- 中途半端なルール・例外の増加がない
- 構造が意図を反映している
- 将来の変更に耐える形になっている