Refactoring Skill
外部から見た振る舞いを変えずに、内部の構造を改善するスキル。
対応領域
| 領域 | フォーカス | 詳細ガイド |
|---|---|---|
| Code Smell Detection | コードスメルの特定と優先順位付け | ./agents/code-smell-detector.md |
| Backend | Python/FastAPIコードのリファクタリング | ./agents/backend-refactorer.md |
| Frontend | TypeScript/SvelteKitコードのリファクタリング | ./agents/frontend-refactorer.md |
判断フロー
code
リファクタリングが必要な状況は? │ ├─ 大規模コードベースの改善 ────────→ ✅ サブエージェント並列実行 │ ├─ Backend/Frontend両方の改善 ────→ ✅ サブエージェント並列実行 │ ├─ 単一の関数/クラスの改善 ────────→ ✅ 直接リファクタリング │ ├─ 変数名/メソッド名の変更のみ ───→ ✅ 直接リファクタリング(軽量) │ ├─ テストがない/不十分 ────────────→ ❌ 先にテストを追加 │ ├─ 本番リリース直前 ───────────────→ ❌ リファクタリング延期 │ └─ 機能追加と同時に行おうとする ──→ ❌ 別々に実施
サブエージェント並列リファクタリング
大規模なリファクタリングが必要な場合、専門サブエージェントを並列起動。
code
┌──────────────────────────────────────────────────────────────┐ │ 1. 分析フェーズ │ │ └── Code Smell Detector でコードスメルを特定 │ ├──────────────────────────────────────────────────────────────┤ │ 2. 計画フェーズ │ │ └── 検出結果に基づいてリファクタリング計画を策定 │ ├──────────────────────────────────────────────────────────────┤ │ 3. 実行フェーズ(並列可能) │ │ ├── Backend Refactorer → Pythonコードのリファクタリング │ │ └── Frontend Refactorer → TypeScriptコードのリファクタリング│ ├──────────────────────────────────────────────────────────────┤ │ 4. 検証フェーズ │ │ └── テスト実行と動作確認 │ └──────────────────────────────────────────────────────────────┘
基本原則(Martin Fowler)
リファクタリングとは
「外部から見た振る舞いを変えずに、内部の構造を改善すること」
重要な特性:
- •小さなステップで進める
- •各ステップ後にテストが通ることを確認
- •新機能の追加とリファクタリングを混ぜない
2つの帽子
code
🎩 機能追加の帽子 🧢 リファクタリングの帽子 ├── 新しい機能を追加 ├── コードの構造を改善 ├── 既存コードは変更しない ├── 機能は追加しない └── テストを追加 └── 既存のテストが通り続ける ⚠️ 両方の帽子を同時にかぶらない!
3回の法則
code
1回目: とにかく実装する 2回目: 似たようなことをする時、重複を感じつつも進める 3回目: 同じことを3回目にする時、リファクタリングする
リファクタリングワークフロー(5ステップ)
code
┌───────────────────────────────────────────────────────┐
│ │
▼ │
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│準備 │──→│テスト │──→│小さな │──→│繰り返し│ │
│ │ │確認 │ │ステップ│ │ │ │
└────────┘ └────────┘ └────────┘ └────────┘ │
│ │
▼ │
┌────────┐ │
│完了確認│─────────┘
└────────┘
Step 1: 準備
| 項目 | 内容 |
|---|---|
| テストカバレッジ確認 | 対象コードのテストが存在するか確認 |
| コードスメル特定 | 問題のあるコードパターンを識別 |
| 範囲決定 | 影響範囲を特定、小さな単位に分割 |
Step 2: テストの確認
bash
# Backend poetry run pytest poetry run pytest --cov=src --cov-report=term-missing # Frontend npm run test
- •すべてのテストがグリーンであること
- •カバレッジが十分であること(目標: 80%以上)
Step 3: 小さなステップで変更
- •1つのリファクタリングを選択 → 最小単位に分解
- •変更を適用 → コンパイル/構文チェック
- •テストを実行 →
poetry run pytest -x - •コミット → 小さな単位でコミット
Step 4: 繰り返し
- •Step 3を繰り返す
- •各ステップ後にテストを実行
- •問題があれば即座にロールバック
Step 5: 完了確認
- •全テストを実行
- •コードレビュー(変更前後の比較)
- •動作確認(開発サーバーでの確認)
コードスメル一覧
Bloaters(肥大化)
| コードスメル | 症状 | 対処法 |
|---|---|---|
| Long Method | 長すぎるメソッド | Extract Method |
| Large Class | 責務が多すぎるクラス | Extract Class |
| Primitive Obsession | プリミティブ型の多用 | Replace Primitive with Object |
| Long Parameter List | パラメータが多すぎる | Introduce Parameter Object |
Object-Orientation Abusers(OOP違反)
| コードスメル | 症状 | 対処法 |
|---|---|---|
| Switch Statements | 条件分岐の多用 | Replace Conditional with Polymorphism |
| Temporary Field | 時々しか使わないフィールド | Extract Class |
| Refused Bequest | 継承の誤用 | Replace Inheritance with Delegation |
Change Preventers(変更困難)
| コードスメル | 症状 | 対処法 |
|---|---|---|
| Divergent Change | 1つのクラスが複数の理由で変更 | Extract Class |
| Shotgun Surgery | 1つの変更が複数クラスに影響 | Move Method, Move Field |
Dispensables(不要なもの)
| コードスメル | 症状 | 対処法 |
|---|---|---|
| Duplicate Code | 重複コード | Extract Method, Pull Up Method |
| Dead Code | 使われていないコード | 削除 |
| Speculative Generality | 過剰な汎用化 | 不要な抽象化を削除 |
Couplers(結合度の問題)
| コードスメル | 症状 | 対処法 |
|---|---|---|
| Feature Envy | 他クラスのデータを多用 | Move Method |
| Inappropriate Intimacy | クラス間の過度な結合 | Move Method, Extract Class |
| Message Chains | 長いメソッドチェーン | Hide Delegate |
主要リファクタリングカタログ
Extract Method(メソッドの抽出)
python
# Before: 長いメソッド
def print_owing(self):
print("***********************")
print("*** Customer Owes ***")
print("***********************")
outstanding = sum(order.amount for order in self._orders)
print(f"name: {self._name}")
print(f"amount: {outstanding}")
# After: メソッドを抽出
def print_owing(self):
self._print_banner()
outstanding = self._calculate_outstanding()
self._print_details(outstanding)
def _print_banner(self): ...
def _calculate_outstanding(self) -> float: ...
def _print_details(self, outstanding: float): ...
Extract Class(クラスの抽出)
python
# Before: 責務が多すぎるクラス
class Person:
def __init__(self, name: str, office_area_code: str, office_number: str):
self._name = name
self._office_area_code = office_area_code
self._office_number = office_number
# After: 電話番号を別クラスに抽出
@dataclass
class TelephoneNumber:
area_code: str
number: str
class Person:
def __init__(self, name: str, telephone: TelephoneNumber):
self._name = name
self._telephone = telephone
Replace Conditional with Polymorphism
python
# Before: switch文による条件分岐
def calculate_pay(employee_type: str, hours: int) -> float:
if employee_type == "engineer":
return hours * 50
elif employee_type == "manager":
return hours * 75 + bonus()
# ...
# After: ポリモーフィズムを使用
class Employee(ABC):
@abstractmethod
def calculate_pay(self, hours: int) -> float: pass
class Engineer(Employee):
def calculate_pay(self, hours: int) -> float:
return hours * 50
安全なリファクタリングルール
DO ✅
- •テストファースト: リファクタリング前にテストがグリーンであることを確認
- •小さなステップ: 1つのリファクタリングを完了 → テスト → コミット → 次へ
- •機能追加との分離: リファクタリングを完了してから機能追加
- •ロールバック準備:
git reset --hard HEADで即座に戻れる状態を維持
DON'T ❌
- •複数のリファクタリングを同時に実施
- •テストを実行せずに進める
- •「ついでに機能も追加しよう」
- •「このバグも一緒に直そう」
レイヤー別リファクタリング
Domain層
python
# Before: プリミティブ型の多用
class Order:
def __init__(self, amount: float, currency: str): ...
# After: 値オブジェクトの抽出
@dataclass(frozen=True)
class Money:
amount: Decimal
currency: str
class Order:
def __init__(self, total: Money): ...
UseCase層
python
# Before: 複数の責務を持つユースケース
class UserUseCase:
def create_user(self, data): ...
def update_user(self, id, data): ...
def delete_user(self, id): ...
# After: 単一責務に分割
class CreateUserUseCase:
def execute(self, input: CreateUserInput) -> CreateUserOutput: ...
API層
python
# Before: ルーターにロジックが混在
@router.post("/users")
async def create_user(data: dict):
if not data.get("email"):
raise HTTPException(400, "Email required")
# ビジネスロジックがここに...
# After: ルーターは薄く
@router.post("/users", response_model=UserResponse)
async def create_user(
data: UserCreateRequest,
usecase: CreateUserUseCase = Depends(get_create_user_usecase)
):
return await usecase.execute(data)
リファクタリングチェックリスト
🔴 開始前(必須)
- • テストが存在し、すべてグリーン
- • コードスメルを特定した
- • リファクタリングの範囲を決めた
- • 影響範囲を把握した
🟡 各ステップ(推奨)
- • 1つのリファクタリングのみを実施
- • テストを実行した
- • テストがグリーン
- • 変更をコミットした
🟢 完了時
- • すべてのテストがグリーン
- • コードの可読性が向上した
- • 新たなコードスメルが発生していない
ベストプラクティス
DO ✅
- •テストを安全網として活用
- •小さなステップで進める
- •各ステップ後にコミット
- •80%の改善で良しとする
- •残りは次のイテレーションで
DON'T ❌
- •テストなしでリファクタリング
- •大きな変更を一度に実施
- •機能追加と同時に実施
- •完璧主義に陥る
- •範囲を広げすぎる
参照ファイル
| ファイル | 説明 |
|---|---|
./agents/code-smell-detector.md | コードスメル検出詳細 |
./agents/backend-refactorer.md | Backendリファクタリング詳細 |
./agents/frontend-refactorer.md | Frontendリファクタリング詳細 |
.claude/rules/code-style.md | コードスタイル規約 |
.claude/rules/backend/layer-rules.md | Backendレイヤールール |
参考資料
- •Martin Fowler『リファクタリング(第2版)』
- •Refactoring Guru: https://refactoring.guru/