--- name: refactor description: Martin Fowler の方法論に基づく体系的なコードリファクタリング。ユーザーがコードのリファクタリング、コード構造の改善、技術的負債の削減、レガシーコードのクリーンアップ、コードスメルの解消、コード保守性の向上を求めた際に使用する。本スキルは、リサーチ・計画・安全な段階的実装からなる段階的アプローチを案内する。 --- # コードリファクタリングスキル Martin Fowler 著『Refactoring: Improving the Design of Existing Code』(第 2 版) に基づくコードリファクタリングへの体系的アプローチ。本スキルは、テストに支えられた安全で段階的な変更を重視する。 > "Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure." — Martin Fowler ## 中核となる原則 1. **振る舞いの保全**: 外部から見える振る舞いは変えてはならない 2. **小さなステップ**: テスト可能な微小な変更を積み重ねる 3. **テスト駆動**: テストはセーフティネットである 4. **継続的**: リファクタリングは一度きりの作業ではなく継続的に行う 5. **協調的**: 各フェーズでユーザーの承認が必要 ## ワークフロー概要 ``` Phase 1: Research & Analysis ↓ Phase 2: Test Coverage Assessment ↓ Phase 3: Code Smell Identification ↓ Phase 4: Refactoring Plan Creation ↓ Phase 5: Incremental Implementation ↓ Phase 6: Review & Iteration ``` --- ## フェーズ 1: リサーチと分析 ### 目的 - コードベースの構造と目的を把握する - リファクタリングの範囲を特定する - ビジネス要件に関するコンテキストを収集する ### ユーザーへの確認事項 開始前に次の点を明確化する。 1. **範囲**: どのファイル/モジュール/関数をリファクタリングするのか? 2. **目標**: 何を解決したいのか?(可読性、性能、保守性) 3. **制約**: 変更してはいけない領域はあるか? 4. **時間的圧力**: 他作業のブロッカーになっているか? 5. **テスト状況**: テストは存在するか?パスしているか? ### アクション - [ ] 対象コードを読み、理解する - [ ] 依存関係と統合箇所を洗い出す - [ ] 現行アーキテクチャを文書化する - [ ] 既存の技術的負債マーカー(TODO、FIXME)を控える ### 出力 ユーザーへの提示内容: - コード構造のサマリー - 検出された問題箇所 - 初期推奨事項 - **着手の承認を依頼する** --- ## フェーズ 2: テストカバレッジの評価 ### なぜテストが重要か > "Refactoring without tests is like driving without a seatbelt." — Martin Fowler テストは安全なリファクタリングを可能にする**鍵となる要素**である。テストがなければ、バグを混入させるリスクがある。 ### 評価ステップ 1. **既存テストの確認** ```bash # テストファイルを探す find . -name "*test*" -o -name "*spec*" | head -20 ``` 2. **既存テストの実行** ```bash # JavaScript/TypeScript npm test # Python pytest -v # Java mvn test ``` 3. **カバレッジの確認(可能な場合)** ```bash # JavaScript npm run test:coverage # Python pytest --cov=. ``` ### 判断ポイント: ユーザーへの確認 **テストが存在し、かつパスしている場合:** - フェーズ 3 へ進む **テストが不足、または不完全な場合:** 次の選択肢を提示する。 1. 先にテストを書く(推奨) 2. リファクタリング中に段階的にテストを追加する 3. テストなしで進める(リスクあり、ユーザーの明示的同意が必要) **テストが失敗している場合:** - STOP。リファクタリング前に失敗テストを修正する - ユーザーに確認: 先にテストを直すべきか? ### テスト作成のガイドライン(必要時) リファクタリング対象の各関数について、以下をテストでカバーする。 - ハッピーパス(通常動作) - エッジケース(空入力、null、境界値) - エラーシナリオ(無効入力、例外) 「red-green-refactor」サイクルを用いる。 1. 失敗するテストを書く(red) 2. パスさせる(green) 3. リファクタリングする --- ## フェーズ 3: コードスメルの特定 ### コードスメルとは? コードに潜むより深い問題の症状である。バグそのものではないが、コード改善の余地を示す指標となる。 ### よくチェックすべきコードスメル 完全なカタログは [references/code-smells.md](references/code-smells.md) を参照。 #### クイックリファレンス | スメル | 兆候 | 影響 | |-------|-------|--------| | **Long Method(長すぎる関数)** | メソッドが 30〜50 行を超える | 理解、テスト、保守が困難 | | **Duplicated Code(重複したコード)** | 同じロジックが複数箇所に存在 | バグ修正を複数箇所で行う必要がある | | **Large Class(巨大なクラス)** | 責務が多すぎるクラス | 単一責任原則に違反 | | **Feature Envy(機能の横恋慕)** | 他クラスのデータを多く使用するメソッド | カプセル化が不十分 | | **Primitive Obsession(プリミティブ型への執着)** | オブジェクト化すべき所をプリミティブで多用 | ドメイン概念が欠落 | | **Long Parameter List(長すぎるパラメータリスト)** | パラメータが 4 個以上 | 正しく呼び出すのが困難 | | **Data Clumps(データの群れ)** | 同じデータ項目が常に一緒に現れる | 抽象化が欠落 | | **Switch Statements(スイッチ文)** | 複雑な switch / if-else の連鎖 | 拡張が困難 | | **Speculative Generality(投機的一般化)** | 「念のため」のコード | 不要な複雑さ | | **Dead Code(デッドコード)** | 使われていないコード | 混乱と保守負担 | ### 分析ステップ 1. **自動分析**(スクリプトが利用可能な場合) ```bash python scripts/detect-smells.py ``` 2. **手動レビュー** - コードを体系的に読み進める - 各スメルを場所と深刻度とともに記録する - 影響度別に分類する(Critical / High / Medium / Low) 3. **優先順位付け** 次のようなスメルに注力する。 - 現在の開発をブロックしているもの - バグや混乱を引き起こしているもの - 変更頻度が高いコードパスに影響するもの ### 出力: スメルレポート ユーザーへの提示内容: - 検出されたスメルの一覧と場所 - 各スメルの深刻度評価 - 推奨される優先順位 - **優先順位の承認を依頼する** --- ## フェーズ 4: リファクタリング計画の作成 ### リファクタリング技法の選定 各スメルに対して、カタログから適切なリファクタリング技法を選ぶ。 完全な一覧は [references/refactoring-catalog.md](references/refactoring-catalog.md) を参照。 #### スメル別の推奨リファクタリング | コードスメル | 推奨リファクタリング | |------------|---------------------------| | Long Method(長すぎる関数) | メソッドの抽出、Replace Temp with Query | | Duplicated Code(重複したコード) | メソッドの抽出、Pull Up Method、Form Template Method | | Large Class(巨大なクラス) | クラスの抽出、Extract Subclass | | Feature Envy(機能の横恋慕) | Move Method、Move Field | | Primitive Obsession(プリミティブ型への執着) | Replace Primitive with Object、Replace Type Code with Class | | Long Parameter List(長すぎるパラメータリスト) | パラメータオブジェクトの導入、Preserve Whole Object | | Data Clumps(データの群れ) | クラスの抽出、パラメータオブジェクトの導入 | | Switch Statements(スイッチ文) | ポリモーフィズムによる条件記述の置き換え | | Speculative Generality(投機的一般化) | Collapse Hierarchy、Inline Class、デッドコードの削除 | | Dead Code(デッドコード) | デッドコードの削除 | ### 計画の構成 [templates/refactoring-plan.md](templates/refactoring-plan.md) のテンプレートを使用する。 各リファクタリングごとに次の項目を埋める。 1. **対象**: 何を変更するか 2. **スメル**: どの問題に対処するか 3. **リファクタリング**: どの技法を適用するか 4. **手順**: 細かいマイクロステップ 5. **リスク**: 何が失敗しうるか 6. **ロールバック**: 取り消す方法 ### 段階的アプローチ **重要**: リファクタリングはフェーズに分けて段階的に導入する。 **フェーズ A: クイックウィン**(低リスク、高価値) - 明確化のための変数名変更 - 明らかな重複コードの抽出 - デッドコードの削除 **フェーズ B: 構造改善**(中リスク) - 長すぎる関数からのメソッド抽出 - パラメータオブジェクトの導入 - メソッドを適切なクラスへ移動 **フェーズ C: アーキテクチャ変更**(高リスク) - 条件分岐をポリモーフィズムで置換 - クラスの抽出 - デザインパターンの導入 ### 判断ポイント: ユーザーへの計画提示 実装前に: - 完全なリファクタリング計画を提示する - 各フェーズの内容とリスクを説明する - 各フェーズについて明示的な承認を得る - **質問**: 「フェーズ A を進めてもよいか?」 --- ## フェーズ 5: 段階的な実装 ### ゴールデンルール > "Change → Test → Green? → Commit → Next step" ### 実装のリズム 各リファクタリングステップで: 1. **事前チェック** - テストがパスしている(green) - コードがコンパイルできる 2. **小さな変更を 1 つだけ行う** - カタログのメカニクスに従う - 変更は最小限に保つ 3. **検証** - 直ちにテストを実行 - コンパイルエラーがないか確認 4. **テストがパス(green)した場合** - 説明的なコミットメッセージでコミット - 次のステップへ進む 5. **テストが失敗(red)した場合** - 直ちに STOP - 変更を取り消す - 何が起きたかを分析する - 不明な場合はユーザーに確認する ### コミット戦略 各コミットは次の性質を満たすこと。 - **アトミック**: 論理的に 1 つの変更 - **可逆**: 容易に revert できる - **説明的**: 明確なコミットメッセージ コミットメッセージ例: ``` refactor: Extract calculateTotal() from processOrder() refactor: Rename 'x' to 'customerCount' for clarity refactor: Remove unused validateOldFormat() method ``` ### 進捗報告 サブフェーズごとに、ユーザーへ次を報告する。 - 行った変更 - テストはまだパスしているか - 発生した問題 - **質問**: 「次のバッチへ進めてもよいか?」 --- ## フェーズ 6: レビューと反復 ### リファクタリング後チェックリスト - [ ] 全テストがパスする - [ ] 新たな警告/エラーが出ていない - [ ] コードが正常にコンパイルできる - [ ] 振る舞いが変わっていない(手動検証) - [ ] 必要に応じてドキュメントが更新されている - [ ] コミット履歴が整理されている ### メトリクス比較 リファクタリング前後で複雑度解析を実施する。 ```bash python scripts/analyze-complexity.py ``` 改善点を提示する。 - コード行数の変化 - 循環的複雑度の変化 - 保守性指数の変化 ### ユーザーレビュー 最終結果を提示する。 - 全変更のサマリー - Before / After のコード比較 - メトリクスの改善 - 残る技術的負債 - **質問**: 「この変更内容で問題ないか?」 ### 次のステップ ユーザーと議論する。 - 追加で対処すべきスメルはあるか? - フォローアップのリファクタリングを計画するか? - 同様の変更を他箇所にも適用するか? --- ## 重要なガイドライン ### STOP してユーザーに相談すべきタイミング 次の場合、必ず一旦止まりユーザーに相談する。 - ビジネスロジックに不確実性がある - 外部 API に影響しうる変更 - テストカバレッジが不十分 - 重大なアーキテクチャ判断が必要 - リスクレベルが上昇する - 想定外の複雑さに遭遇した ### 安全のためのルール 1. **テストなしでリファクタリングしない**(ユーザーが明示的にリスクを受容しない限り) 2. **大きな変更を一度にしない** - 微小なステップに分割する 3. **各変更後のテスト実行を省略しない** 4. **テスト失敗時は続行しない** - 修正かロールバックを先に行う 5. **思い込みで進めない** - 疑問があれば確認する ### やってはいけないこと - リファクタリングと機能追加を同時に行わない - 本番障害対応中にリファクタリングしない - 理解していないコードをリファクタリングしない - 過剰設計をしない - シンプルに保つ - 全てを一度にリファクタリングしない --- ## クイックスタート例 ### シナリオ: 重複を含む長い関数 **Before:** ```javascript function processOrder(order) { // 150 行のコードに以下が混在: // - 重複したバリデーションロジック // - インラインの計算処理 // - 複数の責務 } ``` **リファクタリング手順:** 1. processOrder() に対する**テストの存在を確認** 2. バリデーションを validateOrder() に**抽出** 3. **テスト** - パスすること 4. 計算を calculateOrderTotal() に**抽出** 5. **テスト** - パスすること 6. 通知を notifyCustomer() に**抽出** 7. **テスト** - パスすること 8. **レビュー** - processOrder() は明確な 3 関数の調整役となる **After:** ```javascript function processOrder(order) { validateOrder(order); const total = calculateOrderTotal(order); notifyCustomer(order, total); return { order, total }; } ``` --- ## 参考文献 - [Code Smells Catalog](references/code-smells.md) - コードスメルの完全な一覧 - [Refactoring Catalog](references/refactoring-catalog.md) - リファクタリング技法 - [Refactoring Plan Template](templates/refactoring-plan.md) - 計画用テンプレート ## スクリプト - `scripts/analyze-complexity.py` - コード複雑度メトリクスの分析 - `scripts/detect-smells.py` - スメルの自動検出 ## バージョン履歴 - v1.0.0 (2025-01-15): Fowler の方法論、段階的アプローチ、ユーザー相談ポイントを備えた初版リリース