refactor-loop
Refactor Loop
unilyze メトリクスの CodeHealth スコアを収束条件として、リファクタリングを反復実行する。
Quick Reference
unilyze metrics # メトリクス定義、CodeSmell 閾値一覧
unilyze schema # JSON 出力の全フィールドリファレンス
Usage
/refactor-loop [path] [--target <score>] [--max-rounds N]
path: プロジェクトルート (省略時: カレントディレクトリ)--target: 目標 CodeHealth (省略時: 8.0)--max-rounds: 最大ラウンド数 (省略時: 5)
Workflow
snapshot = get_or_create_baseline(path)
hotspots = unilyze_hotspot(path) # git 履歴があれば churn x complexity で優先順位付け
targets = identify_worst_types(snapshot, hotspots, threshold=target)
# hotspots が取得できない場合 (非 git / 履歴不足) は CodeHealth 順にフォールバック
for round in range(1, max_rounds + 1):
type_to_fix = pick_worst(targets)
refactor(type_to_fix) # コード修正
run_tests() # テスト通過を確認
diff = unilyze_diff(snapshot) # 定量比較
report_round(round, diff)
if all_above_target(diff):
break
if has_degradation(diff):
fix_degradation() # 悪化を修正してから次へ
snapshot = update_snapshot()
print_final_summary()
Step 1: ベースライン取得 & hotspot 分析
スナップショットはリポジトリルートの .unilyze/ に保存する。
UNILYZE_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.unilyze"
mkdir -p "$UNILYZE_DIR"
# /quality-audit で作成済みなら再利用
if [ -f "$UNILYZE_DIR/quality-audit.json" ]; then
cp "$UNILYZE_DIR/quality-audit.json" "$UNILYZE_DIR/refactor-before.json"
else
# --prefix または -a で自前コードに絞る (サードパーティ除外)
unilyze -p <path> --prefix "App." -f json -o "$UNILYZE_DIR/refactor-before.json"
fi
hotspot を取得して優先順位を決定する (ループ開始時に1回だけ実行)。 git 履歴が十分にある場合のみ有効。非 git リポジトリや作りたてのリポジトリではスキップし、CodeHealth 順で進める。
# git 履歴があれば hotspot を取得 (失敗しても続行)
unilyze hotspot -p <path> 2>&1 || echo "hotspot unavailable, using CodeHealth order"
hotspot が取得できた場合、「変更頻度が高い かつ CodeHealth が低い」型を優先的にリファクタリング対象とする。 CodeHealth だけで順位付けすると、滅多に変更しないコードに労力を使ってしまう。
ワースト型を抽出:
jq --argjson t 8.0 '[.typeMetrics[] | select(.codeHealth != null and .codeHealth < $t)] | sort_by(.codeHealth) | .[0]' "$UNILYZE_DIR/refactor-before.json"
partial class や static 拡張メソッドクラスが GodClass 判定されている場合、 計測特性によるものであり改善対象から除外してよい (詳細: quality-audit/references/blind-spots.md)。
Step 2: リファクタリング実施
ワースト型のソースを読み、CodeSmell と メトリクスに基づいてリファクタリングする。
改善戦略の選択基準:
| CodeSmell / Metric | Strategy |
|---|---|
| GodClass (lines > 500) | 責務ごとにクラス分割 |
| LongMethod (lines > 60) | メソッド抽出 |
| HighComplexity (CogCC > 25) | 条件分岐の整理、早期 return、ストラテジーパターン |
| DeepNesting (depth > 4) | ガード節、メソッド抽出 |
| HighCoupling (CBO > 14) | インターフェース導入、依存逆転 |
| ExcessiveParameters (> 5) | パラメータオブジェクト導入 |
| LowCohesion (LCOM > 0.8) | 関連メソッド+フィールドを別クラスへ |
| BoxingAllocation | struct に override (ToString, GetHashCode)、ジェネリック制約、Span 活用 |
| ClosureCapture | static ラムダ化、ローカル変数をパラメータ渡し、closureless overload |
| ParamsArrayAllocation | 配列を事前作成して渡す、Span-based overload |
| CatchAllException | 具象例外型で catch、必要なら rethrow |
| MissingInnerException | throw new X("msg", e) で inner exception を渡す |
| ThrowingSystemException | ArgumentNullException 等の具象例外に変更 |
1つのラウンドで1つの型に集中する。複数の型を同時に変更しない。
Goodhart's Law に注意: メトリクス値を下げるためだけの変更(関数の過度な分割、boxing回避のために可読性を犠牲にする等)は行わない。変更後に「全体の可読性・保守性が改善したか」を定性的にも確認する。
CycCC が高い箇所はテスタビリティの問題。CogCC が高い箇所は可読性の問題。改善戦略が異なるので区別する。
Step 3: テスト実行
リファクタリング後、テストを実行して既存動作を壊していないことを確認する。
dotnet test # or project-specific test command
テストが失敗した場合、修正してからStep 4へ進む。
Step 4: 定量比較
unilyze -p <path> -f json -o "$UNILYZE_DIR/refactor-after.json"
unilyze diff "$UNILYZE_DIR/refactor-before.json" "$UNILYZE_DIR/refactor-after.json" 2>&1
判定ロジック:
- Degraded = 0 かつ対象型の CodeHealth >= target → 成功、次の型へ
- Degraded = 0 かつ CodeHealth < target → 改善不十分、同じ型で続行
- Degraded > 0 → 悪化を修正してから再計測
Step 5: ラウンドレポート
## Round N
| Type | Before | After | Delta |
|------|--------|-------|-------|
| Namespace.TypeName | 5.2 | 7.8 | +2.6 |
Changes: {変更内容の要約}
Status: Improved / Degraded / Insufficient
Step 6: 収束判定
以下のいずれかで終了:
- 全対象型が目標 CodeHealth に到達
- 最大ラウンド数に到達
- ユーザーが終了を指示
Step 7: 最終サマリー
## Refactor Loop Summary
| Round | Target Type | Before | After | Status |
|-------|-------------|--------|-------|--------|
| 1 | TypeAnalyzer | 5.2 | 7.8 | Improved |
| 2 | CodeSmellDetector | 6.1 | 8.5 | Target reached |
| 3 | DiffCalculator | 6.5 | 8.2 | Target reached |
Overall: N types improved, M reached target, K remaining
スナップショットを更新:
cp "$UNILYZE_DIR/refactor-after.json" "$UNILYZE_DIR/quality-audit.json"
Notes
- 1ラウンド1型に集中し、変更のスコープを限定する
- テスト通過を必ず確認してから次のラウンドへ
/quality-auditのスナップショットをベースラインとして再利用可能- 悪化が発生した場合は次のラウンドに進まず、まず悪化を修正する
- hotspot はループ開始時に1回取得すれば十分。git churn はループ中に大きく変わらない
- hotspot は git 履歴が必要。非 git リポジトリや履歴が少ない場合は CodeHealth 順にフォールバックする