find-dead-code について
find-dead-codeは、リポジトリ内の未使用コードを調べる開発者向けのレビュー用スキルです。テストのみの参照と本番コードでの参照を区別し、ディレクトリ単位のシンボル分析、フレームワークの規約、プロジェクト計画をもとに削除か調査かを判断します。
主な機能
- 01言語、ソース範囲、テスト境界の特定
- 02言語別解析ツールの任意実行
- 03ソースディレクトリ単位の公開シンボル調査
- 04本番コードとテストの参照を区別
- 05規約やプロジェクト計画に基づく評価
活用シーン
- 01コードベースの未使用関数や型の特定
- 02テストでのみ参照されるシンボルの発見
- 03一部のみ接続された実装の確認
- 04削除前にロードマップや動的利用を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tobihagemann/ turbo --skill find-dead-codeインストール後、Claude Code で/find-dead-codeを入力して読み込まれたか確認します。
詳細
デッドコードの検出
コードベース内のデッドコードを特定します。**基本ルール:テストでしか使われていないコードもデッドコードです。**本番環境での使用のみが対象です。
未完成または放棄された実装では、宣言されているものの使われていないシンボルが、意図されているがまだ使われていない可能性があります。Git の履歴から、未完成のスタックの一部として意図的に追加されたことがわかる場合は、削除ではなく調査を推奨してください。
ステップ 1:言語、対象範囲、テスト境界を特定する
プロジェクトの構成を確認します。
- 設定ファイルを確認します:
package.json、tsconfig.json、pyproject.toml、setup.py、Package.swift、.xcodeproj、Cargo.toml、go.mod、pom.xml、build.gradle - ソースファイルを glob で検索します:
**/*.ts、**/*.py、**/*.swift、**/*.go、**/*.rs、**/*.java - ソースルート、つまり本番コードが配置されている場所を特定します(例:
src/、lib/、Sources/) - コードベースを分割し、トップレベルのソースディレクトリを単位として分析します(例:
src/auth/、src/api/、src/utils/、lib/models/)。各ディレクトリを、ステップ 3 で 1 つのサブエージェントが担当する範囲とします。
ユーザーが対象範囲を指定している場合は、その範囲に分析を限定します。
テストファイルのパターン
どのファイルがテストファイルかを確認します。これらの場所からのみ参照されているコードはデッドコードです。
| 言語 | テストファイルのパターン |
|---|---|
| TS/JS | *.test.{ts,tsx,js,jsx}、*.spec.{ts,tsx,js,jsx}、__tests__/**、__mocks__/**、*.stories.{ts,tsx,js,jsx} |
| Python | test_*.py、*_test.py、tests/**、test/**、conftest.py |
| Swift | *Tests.swift、*Test.swift、Tests/**、*UITests.swift、XCTestCase のサブクラス |
| Go | *_test.go、testdata/** |
| Rust | tests/**、benches/**、#[cfg(test)] モジュール(ソースファイル内のインラインテストモジュール) |
| Java/Kotlin | src/test/**、*Test.java、*Tests.java、*Spec.java、*Test.kt |
| 一般 | fixtures/**、__fixtures__/**、mocks/**、testutils/**、testhelpers/**、spec/** |
また、テストランナーの設定(jest.config.*、vitest.config.*、pytest.ini)、Storybook ファイル、ベンチマークファイルも除外します。
ステップ 2:手早く確認 — CLI ツール(任意)
CLI ツールがインストールされている場合は、ゼロ参照のデッドコードを最初に手早く調べるために実行します。
| 言語 | ツール | 確認コマンド | 実行コマンド |
|---|---|---|---|
| TS/JS | knip | npx knip --version | npx knip --no-exit-code |
| Python | vulture | vulture --version | vulture <src_dirs> --min-confidence 80 |
| Swift | periphery | which periphery | periphery scan --skip-build |
| Go | deadcode | which deadcode | deadcode ./... |
| Rust | コンパイラー警告 | — | cargo build 2>&1 | grep "dead_code" |
重要な制限事項: CLI ツールはテストでのインポートを実際の使用として数えます。そのため、テストでしか使われていないコードを検出できません。文字どおりどこからも参照されていないシンボルのみを検出します。テストのみでの使用を検出するには、ステップ 3 が必須です。
CLI ツールがインストールされていない場合、またはツールがレポートを出力せずにエラーになった場合は、ステップ 3 に進みます。ユーザーにインストールを依頼しないでください。
ステップ 3:テストのみの分析 — 並列サブエージェント(コア)
サブエージェントを起動する前に、プロジェクトのテスト設定と CI ワークフローを読み、テスト間でデータベース、固定ポート、キャッシュなどの共有外部リソースをリセットするテスト階層があるか確認します。このような階層にはプロセス間の排他制御がないため、サブエージェントが同時に実行すると互いの状態を消去し、実際の不具合に見える失敗を返します。そのような階層があれば、すべてのサブエージェントに禁止事項として伝えてください。
これが主要な分析です。ステップ 1 の最上位ソースディレクトリごとに Agent ツール呼び出しを 1 つずつ、すべて 1 つのアシスタントメッセージで出力します。各 Agent 呼び出しでは model: "opus" を使用し、name は指定しません。続行する前に、すべてのエージェントからの報告を待ちます。一部の報告しかそろっていない状態で次のステップを開始せず、まだ報告していないエージェントを再起動しないでください。Rules セクションに従い、最大 8 件までとします。呼び出しを出力する際は、件数を明示してください。各サブエージェントへのプロンプトでは、共有作業ツリーとその git インデックスを読み取り専用として扱うよう指示します。実証的な確認は $TMPDIR の下に作成し、後で破棄する隔離された git worktree で実施します。HEAD はそのままにします。他の参照を読むには、git checkout や git switch ではなく git show <ref>:<path> を使用します。すべてのコマンドでその worktree を絶対パスで指定し、連続した手順は && でつなぎ、手順が失敗した場合に共有チェックアウトで残りの処理が実行されないようにします。クリーンアップと検証は、それぞれ独立したコマンドとして実行します。worktree 自体に依存関係をインストールし、どのような方法でも共有ツリーのインストールを参照しないでください。worktree の削除時にシンボリックリンクをたどって削除され、リダイレクトされたテストスイートが共有インストールに書き込むためです。独自のインストールができない場合は、確認を未実行として報告します。サブエージェントが起動するすべてのテストランナーは、worktree 内でも共有チェックアウト内でも、ランナーの外部で適用されるタイムアウトを設定し、専用のプロセスグループで実行します。クリーンアップ前に、サブエージェントは起動した各ランナーのプロセスグループを停止します。ランナーを停止しても、起動したプロセスが残る場合があるためです。その後、git worktree list に worktree が表示されないこと、git status --short がクリーンであること、HEAD が開始時と同じブランチにあること、共有ツリーの依存関係ディレクトリが引き続き解決されることを検証します(インストールが破損しても gitignore 対象であるため、git status はクリーンのままです)。また、該当する場合は、各プロセスグループのプロセス、およびコマンドラインに worktree のパスを含むプロセスが残っていないことを確認し、停止できなかったプロセスは PID を報告します。プロセスを一覧表示できない場合は、その確認を未実行として報告し、該当するプロセスグループと worktree のパスを報告します。サブエージェントが修復できない損傷があれば、調査結果の代わりに正確な修復コマンドを報告します。
サブエージェント戦略
各サブエージェントには次の情報を渡します。
- エクスポートされたシンボルをスキャンする担当ディレクトリ
- ステップ 1 のテストファイルのパターン
- コードベース全体を grep できるようにする、プロジェクトルートの完全なパス
サブエージェントのタスク
各サブエージェントは、担当ディレクトリで次の手順を実行します。
a) エクスポートされた/公開シンボルを探します。
| 言語 | エクスポートされたシンボルのパターン |
|---|---|
| TS/JS | export function、export const、export let、export var、export class、export interface、export type、export enum、export default、module.exports |
| Python | _ で始まらないモジュール内のトップレベルの def と class、モジュールレベルの定数(FOO = ...)、__all__ 内のシンボル、公開関数(_ で始まらないもの) |
| Swift | public func、public var、public let、public class、public struct、public enum、public protocol、open class、open func、open var |
| Go | 大文字で始まる識別子:func FooBar、type FooBar struct、var FooBar、const FooBar(Go では大文字で始まる名前が公開範囲を示します) |
| Rust | pub fn、pub struct、pub enum、pub trait、pub const、pub static、pub type、pub mod |
| Java/Kotlin | public class、public static、public void、public フィールド、val/var プロパティ、fun (トップレベル)、@Bean、@Component、@Service が付与されたクラス |
b) 各シンボルについて、次の項目を除外して、コードベース全体を対象に参照を grep します。
- 定義ファイル自体
- 生成物/ベンダーディレクトリ(
node_modules/、dist/、build/、vendor/、__pycache__/、.tox/、.build/、DerivedData/、target/)
c) テストファイルのパターンに基づいて、各参照をテストまたは本番に分類します。
重要 — 同一モジュール内の参照も本番での使用として数えます。 同一モジュール/パッケージ内の別の本番ファイルから呼び出されているシンボルは使用中です。外部の呼び出し元がゼロでも、内部で使用されているシンボルを「未使用」として報告しないでください。いずれのファイルからも本番参照がゼロのシンボルだけを報告してください。「公開範囲が不必要に広い」(internal にできる、またはエクスポートを外せる)は可視性の問題であり、デッドコードではありません。含めないでください。
d) 各シンボルについて、次の構造化された結果を報告します。
- シンボル名、種類(関数/クラス/定数など)、定義ファイル、行範囲
- 本番参照の数(ファイルパスを含む)。同一モジュール内の参照も含めます
- テスト参照の数(ファイルパスを含む)
- 分類:
dead(本番参照がどこにもない)、test-only(テスト参照のみ)、alive(本番参照がある)
結果の統合
すべてのサブエージェントの処理が完了したら、結果を収集して統合します。複数のレポートに現れるシンボル(再エクスポートなど)は重複を除いてください。ステップ 2 のツールが、サブエージェントによって alive と分類されたシンボルを未使用と報告した場合は、そのシンボルをサブエージェントの本番参照とともに統合結果に残してください。
ステップ 4:フィルタリング、分類、評価
ステップ 2 と 3 の結果を統合し、以下のフィルターを適用します。
- フレームワークのエントリーポイント:慣例的に使用されるシンボルはスキップします。たとえば、バレルファイル内の React コンポーネント、URL 設定内の Django ビュー、Go の
init()関数とmain()関数、Go のインターフェース実装、Rust のmain()、Rust のトレイト実装、#[derive(...)]によって生成されるコード、main に登録された CLI ハンドラー、マジックメソッド/ライフサイクルメソッド(__init__、__repr__)、シリアライズメソッド(to_json、from_dict)、インターフェース/プロトコルの実装が該当します。 - 再エクスポートの連鎖:シンボルが使われていないと判断する前に、バレルファイル(
index.ts、__init__.py)をたどります。バレル経由で再エクスポートされるシンボルは、間接的に使用されている可能性があります。 - 動的な使用:リフレクション(
getattr、importlib、Go のreflectパッケージ、Rust のproc_macro)、文字列ベースの参照、デコレーター/属性による登録で使用されている可能性があるシンボルは、「確実に未使用」ではなく「おそらく未使用」として分類します。 - パッケージをまたぐ参照:モノレポでは、シンボルが使われていないと判断する前に、兄弟パッケージからインポートされていないことを確認します。
- 設計ドキュメント/仕様/ロードマップ:プロジェクトに仕様ファイル、ロードマップ、TODO ファイル(例:
.turbo/plans/、ROADMAP.md、TODO.md)がある場合は、テストでのみ見つかった項目と照合します。テストでのみ使われる API は、統合待ちの計画済み機能である可能性があります。その場合は「削除」ではなく調査として分類します。唯一の参照先がstatus:のフロントマターでdoneとされている計画の場合も、調査として分類し、その計画では実装済みとされていることを記します。ステータスが誤っているか、統合された後にリグレッションが発生した可能性があります。
各調査結果を分類します。
- 確実に未使用:定義ファイルの外に参照がない
- テストでのみ使用され未使用:参照はあるが、すべてテストファイル内にある
- おそらく未使用:動的な使用、フレームワークの慣例、複雑な再エクスポートの連鎖、または Step 2 のツールが、サブエージェントが本番コードでの参照を見つけたシンボルを未使用と報告したことにより、判断が不確かな場合
調査結果を評価する
分類済みの調査結果に対して /evaluate-findings スキルを実行し、実際のコードに照らして各調査結果を検証して、誤検出を除外します。各調査結果について、該当する定義ファイル全体を読んでください。フラグが付いたシンボルだけでなく、ファイル全体を確認します。周辺のコードから、機能が別の方法ですでに実装されていると分かることがあります(例:公開されている ping() メソッドはテストでしか使われていなくても、handleConnect() 内の非公開キープアライブ・ループが実際の処理を担っている場合があります)。
次のセクションでは、評価結果に従ってください。
推奨アクション
残った各調査結果に、推奨アクションを割り当てます。
| シグナル | 推奨 |
|---|---|
| テストがなく、本番環境での使用もない | 削除 |
| テストはあるが本番環境での使用はなく、仕様/ロードマップにも記載がない | 削除(メソッドとテストのアサーション) |
| テストはあるが本番環境での使用はなく、仕様/ロードマップ/TODO に記載がある | 調査(計画済みの機能であり、未使用ではない) |
| 一部しか接続されていない、意図が不明確、またはドメイン知識が必要 | 調査 |
調査とされた項目については、/investigate スキルを実行し、そのコードが計画済みの機能なのか、未接続の統合なのか、本当に未使用なのかを判断します。
よくあるデッドコードのパターン
ツールや単純な grep では見落とされがちな、検出効果の高いパターンに注意してください。
- テスト専用の状態アクセサー:テストのアサーションのためだけに内部状態を公開する、パブリックプロパティやメソッド(例:
isEnabled、count、currentItems)。本番環境の利用側が使用するのは、動作(イベント、コールバック、副作用)であり、内部状態を直接確認するのはテストだけです。これらを削除する場合は、対応するテストのアサーションも削除するか、動作に基づく検証に書き換える必要があります。 - 未使用のデータモデルフィールド:シリアライズ可能な型(Codable の構造体、dataclass、POJO)のプロパティのうち、デコードされるものの、本番コードでは一度も読み取られないもの。シリアライズ可能な型からフィールドを削除する場合、そのフィールドを含むすべてのデータファイル(JSON、YAML、XML、データベーススキーマ、マイグレーションファイル)も更新してください。
- 使われなくなった列挙型のケース:本番コードで一度も生成されず、照合もされない列挙型のケース。
- 孤立した利便メソッド:わずかに異なるパラメーターで別のパブリックメソッドを呼び出すパブリックなラッパーメソッドで、すべての呼び出し元が基になるメソッドを直接使用しているもの。
関連する所見
デッドコードをスキャンする際は、次の関連する問題にも気づいたら、記録してください(ただし、対応はしないでください)。
- デッドコード付近のバグ:デッドコードの近くには、呼び出し漏れ、接続の不備、統合の未完了など、バグのあるコードがあることがよくあります
- 接続されていない機能:「ほぼ動作している」コード(定義され、テストもされているものの、システムの他の部分につながっていないコード)。真にデッドなコードとは区別してください。
Step 5: 所見の提示
結果を確信度別に分類してください。
確実にデッド(定義箇所以外に参照がない)
| ファイル | シンボル | 種類 | 行の範囲 | 推奨事項 |
|---|
テスト専用のデッド(テストでのみ参照される)
| ファイル | シンボル | 種類 | 参照しているテストファイル | 推奨事項 |
|---|
デッドの可能性あり(手動で確認)
| ファイル | シンボル | 種類 | 不確実な理由 | 推奨事項 |
|---|
以下を含めてください。
- カテゴリごとの合計数
- 削除可能な行数の推定
- 推奨する削除順(依存関係の末端から)
ルール
- トップレベルのソースディレクトリが 8 個を超える場合は、スコープを絞るか、関連するディレクトリをより少ない数のサブエージェント用バッチにまとめるようユーザーに依頼してください。
- デッドコードが見つからない場合は、その旨を明記し、スコープ上の制約や分析上の注意点も記載してください。