個人開発でアプリを数年運用していると、ある日突然「このコード、もう自分でも読めない」と感じる瞬間があります。新機能を追加しようとしてファイルを開いたら、コメントアウトが積み重なったコードと、どこから呼ばれているかわからない関数が並んでいた——そういう経験をされた方は少なくないと思います。
私もそういう状況に陥っていました。2つのアプリを掛け持ちで運用しながら新機能を追加し続けた結果、コードベースは「動いているが触れない」状態になっていました。テストを書こうにも、副作用だらけで手が出せません。リファクタリングしようにも、どこを変えると何が壊れるかわかりません。
そこで2026年の初めから、Claude Code を本格的に使って技術的負債の削減に取り組みました。3ヶ月後、同じコードベースはずいぶん扱いやすくなりました。このエントリでは、具体的に何をどの順序でやったか、どの部分でClaudeが役に立ち、どこで限界を感じたかを記録として残しておきます。
技術的負債の「地図」を作る:まず現状を数値で把握する
リファクタリングで最初につまずくのは「どこから手をつければいいかわからない」という問題です。感覚では「全部悪い」と感じていても、優先順位なく始めると途中で疲弊します。
まず私がやったのは、Claude Code に「技術的負債の地図」を作らせることでした。具体的には次のプロンプトをプロジェクトルートで実行します。
claude "このリポジトリ全体を分析して、技術的負債を以下の観点で棚卸しし、Markdown形式のレポートを出力してください:
1. TODO/HACK/FIXME/XXX コメントの一覧と場所
2. 重複しているコードのパターン(50行以上の類似コード)
3. 関数の複雑度が高い箇所(条件分岐が5段以上のもの)
4. 依存関係が循環しているモジュール
5. テストカバレッジが0%のファイル(package.jsonやtest設定がある場合)
各項目に「影響範囲」と「修正難易度」の2軸で★1〜5の評価をつけてください"このコマンドを実行すると、Claude は数分かけてリポジトリ全体を走査し、かなり具体的なレポートを出力してくれます。私のケースでは:
- TODO/HACKコメント:47箇所(そのうち2年以上前のもの:23箇所)
- 明らかな重複コード:8パターン(約1,200行)
- 循環依存:3組のモジュール
- テストカバレッジ0%のファイル:全体の67%
という数字が出てきました。「感覚」では「かなり悪い」と思っていましたが、数字になると「循環依存が3つしかない」「重複は8パターンに集約されている」と認識が変わります。この「地図」があることで、感情的にならずに優先順位をつけられるようになりました。
優先順位のつけ方:「緊急度 × 影響範囲」マトリクスで判断する
地図ができたら次は「どこから手をつけるか」の判断です。ここで私が失敗しがちだったのは「気になるところ」から手をつけてしまうこと。コードが醜くて目につく場所よりも、実際に問題を引き起こしている場所を先に直すべきでした。
Claude に優先順位の整理を依頼する際のポイントは、ビジネス上の制約を一緒に渡すことです。
claude "先ほどの技術的負債レポートを参照して、優先順位を決める手伝いをしてください。
制約条件:
- 毎週1〜2日しかリファクタリングに使えない
- 現行機能を壊してはいけない(本番環境で動いています)
- 3ヶ月後に新機能Xを追加する予定があり、その周辺モジュールは必ず整理したい
上記を踏まえ、『今週やること』『今月やること』『3ヶ月以内にやること』の3段階で分類してください"こうすることで、「循環依存の解消は影響範囲が大きいので今月はやらない、まず重複コードの8パターンを今週と来週で片付ける」という具体的なプランが出てきます。
特に「新機能Xを追加する予定」という情報を渡すのが重要でした。Claudeはその文脈を元に、新機能の周辺モジュールを「先に整理しておくべき負債」として特定してくれたからです。
重複コードの削減:サブエージェントを使った安全なリファクタリング
優先順位の上位に来た「重複コードの削減」から着手しました。私のケースでは、APIレスポンスのエラーハンドリングが8つのファイルに微妙に異なる形で書かれていました。
ここで Claude Code のサブエージェント(--headless + worktree)を使うと、「本番コードを一切触らずに」リファクタリング案を試せます。
# worktreeを使って安全な実験環境を作る
git worktree add /tmp/refactor-api-errors -b refactor/api-error-handling
claude --headless "
作業ディレクトリ: /tmp/refactor-api-errors
タスク: 以下の8ファイルに散らばっているエラーハンドリングロジックを統合してください
対象ファイル: src/api/users.ts, src/api/products.ts, src/api/orders.ts, ...(略)
手順:
1. まず8ファイルのエラーハンドリングパターンを分析する
2. 共通化できる部分とできない部分を整理する
3. src/utils/apiErrorHandler.ts として共通モジュールを作成する
4. 8ファイルを新モジュールを使うように書き換える
5. TypeScriptの型エラーがないことを確認する
6. 変更のdiffを最後に出力する
"このアプローチの良いところは、mainブランチには一切手を触れないことです。worktreeで作業させ、結果のdiffを確認してから git merge するかどうかを自分で判断できます。
実際に試してみると、8パターンあった重複コードは1つの共通ユーティリティと、各ファイルに必要な薄いラッパーに整理されました。コード量は約340行から95行になりました。
ただし、Claudeが出力したコードをそのままマージするのは危険です。私は必ずdiffを確認し、型定義が正確かどうか、エラーメッセージが変わっていないかをチェックしてからマージしていました。
循環依存の解消:「なぜこうなったか」を理解してから直す
重複コードの次に取り組んだのが循環依存の解消です。これが一番難しく、1ヶ月近くかかりました。
循環依存の問題は、コードの問題というより「設計の問題」です。A モジュールが B を import し、B が A を import する構造になっているということは、最初の設計段階で責務の分離が不明確だったことを意味します。
ここでClaude Code に直接「直して」と依頼するのは危険でした。なぜなら、循環依存を解消するには関係するモジュールの「責務の再設計」が必要で、それはAIより人間が判断すべき領域だからです。
代わりに私がやったのは、Claude に「なぜこの循環依存が生まれたかを分析させる」ことでした。
claude "src/store/userStore.ts と src/components/UserProfile.tsx の間に循環依存があります。
なぜこうなったかを分析し、設計上の問題点を説明してください。
解決策として考えられるパターンを3つ示し、それぞれのメリット・デメリットも教えてください"出力された3つのパターン(イベントバス導入、Context分離、依存性注入)を読んで、自分でどれを採用するか判断しました。この「分析→選択→実装」の流れが、循環依存解消には最も効果的でした。
テストのない領域にテストを追加する:段階的アプローチ
テストカバレッジが0%だったファイルへのテスト追加は、すべてを一気にやろうとすると挫折します。私はClaude を使って「まずこのファイルの重要な処理だけテストする」という段階的アプローチを取りました。
claude "src/utils/priceCalculator.ts のテストを作成してください。
ただし、全てのケースをテストしようとしなくていいです。
このファイルの処理の中で、バグが起きると最もユーザーに影響が大きい関数を3つ特定し、
その3つに絞ったテストを vitest で書いてください"「全部テストしろ」ではなく「最も重要な3つだけ」という指示が効果的でした。完璧なカバレッジを目指すより、リスクの高い部分だけでも網羅することで、実際の品質向上につながりました。
3ヶ月の作業を通じて、テストカバレッジは0%→38%になりました。完璧ではありませんが、コア機能の主要パスはテストで保護されています。
週次ヘルスチェックを自動化する:フックに組み込んだ実際の設定
3ヶ月の作業で一番効いたのは、派手なリファクタリングではなく「毎週、同じ物差しで測り直す」ことでした。人間の意志だけで続けるのは無理があったので、Claude Code のフックに組み込んでいます。
.claude/settings.json に SessionStart フックを1つ足すだけです。
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{ "type": "command", "command": ".claude/scripts/debt-healthcheck.sh" }
]
}
]
}
}呼び出されるスクリプトの中身は、前回実行から7日経っていなければ即座に抜ける、という条件つきです。毎回のセッション起動で走らせると邪魔になるためです。
#!/usr/bin/env bash
set -euo pipefail
STAMP=".claude/.debt-last-run"
NOW=$(date +%s)
# 前回から7日未満なら何もしない(604800秒 = 7日)
if [ -f "$STAMP" ] && [ $(( NOW - $(cat "$STAMP") )) -lt 604800 ]; then
exit 0
fi
OUT=".claude/debt/$(date +%Y-%m-%d).md"
mkdir -p "$(dirname "$OUT")"
claude -p "技術的負債の棚卸しを実行し、TODO/HACKコメント数・重複パターン数・循環依存の組数・最大関数複雑度の4項目だけを、
1行1項目の「指標: 数値」形式で出力してください。説明文・前置きは不要です。" > "$OUT"
# 前回のレポートとの差分だけを表示する
PREV=$(ls .claude/debt/*.md 2>/dev/null | tail -2 | head -1)
if [ -n "$PREV" ] && [ "$PREV" != "$OUT" ]; then
diff "$PREV" "$OUT" || true
fi
echo "$NOW" > "$STAMP"ここで意図的にそうしている点が2つあります。
1つは、Claude への指示で「4項目だけ・1行1項目・説明文不要」と出力形式を絞っていることです。SessionStart フックの標準出力はそのままセッションのコンテキストに乗ります。ここで数千字のレポートを吐かせると、本題に入る前にコンテキストを浪費します。数値4行なら差分の判断には十分です。
もう1つは、レポートを上書きせず日付つきファイルで積んでいることです。diff で前回との差分だけを見られるようにしておくと、「今週 TODO が3件増えた」という事実が視界に入ります。絶対値を眺めても人は動きませんが、増加分は気になるものです。
なお、フックのスクリプトが exit 2 を返すとその処理はブロック扱いになります。健康診断はあくまで通知が目的ですので、diff の戻り値を || true で握りつぶし、常に正常終了させています。ここを雑にすると、負債を測るための仕組みがセッション開始を妨げるという本末転倒に陥ります。
うまくいかなかった3つのこと
成功した手順だけを並べても再現の役には立ちませんので、私自身が明確に失敗した点も残しておきます。レビュワーのいない個人開発では、こうした失敗に気づくまでの時間がそのまま損失になります。
一度に渡すファイル数を増やしすぎた。 重複コードの統合で、最初は対象8ファイルを1回の指示にまとめて渡しました。結果、前半4ファイルは共通ユーティリティを使う形に整理されたのに、後半のファイルでは微妙に方針の異なる第2のヘルパーが生まれていました。長い作業の途中で「さっき決めた方針」が薄れていく感覚に近いものがあります。5ファイル程度に分け、前の作業で作った共通モジュールのパスを明示的に渡すようにしてから、この揺れは出なくなりました。
生成されたテストが実装を写していた。 テスト追加で最初につまずいたのがこれです。実装コードを読ませてテストを書かせると、実装のロジックをそのまま期待値に写したテストができあがります。これは実装が間違っていても通ってしまうため、テストとしての価値がありません。対策として、実装ファイルではなく仕様のメモを先に渡し、「期待値はこの仕様から書き起こし、実装は参照しないでください」と指示を分けました。手間は増えますが、こうして書いたテストは実際に2件のバグを検出しました。
複雑度の数値を目標にしてしまった。 最大関数複雑度18という数字が目についたので、一時期「複雑度を下げる」こと自体を指示していました。すると条件分岐が機械的に小関数へ切り出され、呼び出しを3階層たどらないと処理の全体像が見えないコードになりました。数字は改善したのに、読みやすさは悪化しています。指標は「悪化していないかを見るためのもの」であって、指標そのものを最適化の対象にすべきではありませんでした。
成果の測定:3ヶ月後に変わった数字
3ヶ月間の取り組みを終えて、改めて最初に作った「地図」と同じ分析をClaude に実行してもらいました。結果:
| 指標 | 開始時 | 3ヶ月後 | 変化 |
|---|---|---|---|
| TODO/HACKコメント数 | 47 | 19 | -60% |
| 重複コードパターン | 8パターン | 2パターン | -75% |
| 循環依存 | 3組 | 0組 | -100% |
| テストカバレッジ | 0% | 38% | +38pt |
| 最大関数複雑度 | 18 | 7 | -61% |
数字だけ見ると「半減以上できた」と言えます。ただし体感として最も大きかった変化は数字ではなく、「新機能を追加するのが怖くなくなった」という感覚です。以前は新しいコードを書くたびに「壊れるかもしれない」という不安がありましたが、今はずいぶん楽になりました。
Claude Code を使った技術的負債管理で学んだこと
3ヶ月やってみて感じた重要な点をいくつか挙げておきます。
Claudeに「全部やって」と言わない:技術的負債の解消には設計判断が伴います。Claudeは「選択肢を提示する」「具体的な実装を試す」ことは得意ですが、「どの設計を採用するか」は人間が決める必要があります。
worktreeを使って本番リスクをゼロにする:サブエージェントで自動リファクタリングを試す際は、必ずworktreeを使って分離した環境で実験します。mainブランチへの影響ゼロで試行錯誤できる点が、Claude Codeを使う最大のメリットの一つです。
週1回の「健康診断」を続ける:最初に作った「地図」と同じ分析を週1回実行することで、負債が再び積み重なっていないかを監視できます。自動化できる部分はClaude Code のフックに組み込みました。
全部は終わらない、終わらせなくていい:技術的負債はゼロにはなりません。「動いているが触れない」状態から「触れるが完璧ではない」状態になれば、開発速度は確実に上がります。完璧を目指さず、継続的に少しずつ改善する習慣を作ることが最終的なゴールです。
次にやるべきことは、今回整理したコードに対して「継続的な品質監視」の仕組みをClaude Code のフックで自動化することです。PRのたびにClaudeが負債指標をチェックし、悪化傾向があればコメントを残してくれる仕組みを作ろうと考えています。
同じように「動いているが触れない」コードの前で立ち止まっている方の、次の一手のヒントになれば嬉しく思います。