Agent skill
refactoring
Martin Fowlerのリファクタリング方法論に基づいた体系的なコード改善スキル。 コードスメルの検出から安全なリファクタリングの実施まで、テストを安全網として 段階的に品質を向上させます。「2つの帽子」を意識し、機能追加と分離して実施。
Stars
163
Forks
31
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/refactoring
SKILL.md
Refactoring Skill
外部から見た振る舞いを変えずに、内部の構造を改善するスキル。
対応領域
| 領域 | フォーカス | 詳細ガイド |
|---|---|---|
| Code Smell Detection | コードスメルの特定と優先順位付け | ./agents/code-smell-detector.md |
| Backend | Python/FastAPIコードのリファクタリング | ./agents/backend-refactorer.md |
| Frontend | TypeScript/SvelteKitコードのリファクタリング | ./agents/frontend-refactorer.md |
判断フロー
リファクタリングが必要な状況は?
│
├─ 大規模コードベースの改善 ────────→ ✅ サブエージェント並列実行
│
├─ Backend/Frontend両方の改善 ────→ ✅ サブエージェント並列実行
│
├─ 単一の関数/クラスの改善 ────────→ ✅ 直接リファクタリング
│
├─ 変数名/メソッド名の変更のみ ───→ ✅ 直接リファクタリング(軽量)
│
├─ テストがない/不十分 ────────────→ ❌ 先にテストを追加
│
├─ 本番リリース直前 ───────────────→ ❌ リファクタリング延期
│
└─ 機能追加と同時に行おうとする ──→ ❌ 別々に実施
サブエージェント並列リファクタリング
大規模なリファクタリングが必要な場合、専門サブエージェントを並列起動。
┌──────────────────────────────────────────────────────────────┐
│ 1. 分析フェーズ │
│ └── Code Smell Detector でコードスメルを特定 │
├──────────────────────────────────────────────────────────────┤
│ 2. 計画フェーズ │
│ └── 検出結果に基づいてリファクタリング計画を策定 │
├──────────────────────────────────────────────────────────────┤
│ 3. 実行フェーズ(並列可能) │
│ ├── Backend Refactorer → Pythonコードのリファクタリング │
│ └── Frontend Refactorer → TypeScriptコードのリファクタリング│
├──────────────────────────────────────────────────────────────┤
│ 4. 検証フェーズ │
│ └── テスト実行と動作確認 │
└──────────────────────────────────────────────────────────────┘
基本原則(Martin Fowler)
リファクタリングとは
「外部から見た振る舞いを変えずに、内部の構造を改善すること」
重要な特性:
- 小さなステップで進める
- 各ステップ後にテストが通ることを確認
- 新機能の追加とリファクタリングを混ぜない
2つの帽子
🎩 機能追加の帽子 🧢 リファクタリングの帽子
├── 新しい機能を追加 ├── コードの構造を改善
├── 既存コードは変更しない ├── 機能は追加しない
└── テストを追加 └── 既存のテストが通り続ける
⚠️ 両方の帽子を同時にかぶらない!
3回の法則
1回目: とにかく実装する
2回目: 似たようなことをする時、重複を感じつつも進める
3回目: 同じことを3回目にする時、リファクタリングする
リファクタリングワークフロー(5ステップ)
┌───────────────────────────────────────────────────────┐
│ │
▼ │
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│準備 │──→│テスト │──→│小さな │──→│繰り返し│ │
│ │ │確認 │ │ステップ│ │ │ │
└────────┘ └────────┘ └────────┘ └────────┘ │
│ │
▼ │
┌────────┐ │
│完了確認│─────────┘
└────────┘
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/
Didn't find tool you were looking for?