关于 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 - 搜索源文件:
**/*.ts、**/*.py、**/*.swift、**/*.go、**/*.rs、**/*.java - 确定源代码根目录 — 即生产代码所在位置(例如
src/、lib/、Sources/) - 将代码库划分为多个分析单元,按顶层源代码目录划分(例如
src/auth/、src/api/、src/utils/、lib/models/)。每个目录将作为第 3 步中一个子 Agent 的工作范围。
如果用户指定了范围,则将分析限制在该范围内。
测试文件模式
确定哪些文件是测试文件。仅在以下位置引用的代码属于死代码。
| 语言 | 测试文件模式 |
|---|---|
| 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 步:快速筛查 — 命令行工具(可选)
如果已安装命令行工具,可运行它,对零引用死代码进行快速初筛。
| 语言 | 工具 | 检查 | 运行 |
|---|---|---|---|
| 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" |
重要限制:命令行工具会将测试导入视为真实使用。它们无法检测仅在测试中使用的代码,只能找到在任何位置都完全没有引用的符号。要检测仅在测试中使用的代码,必须执行第 3 步。
如果未安装命令行工具,或工具出错且未生成报告,则跳到第 3 步。不要要求用户安装任何工具。
第 3 步:仅测试分析 — 并行子 Agent(核心)
派发前,先阅读项目的测试配置和 CI 工作流,找出是否有任何测试层级会在测试之间重置共享的外部资源,例如数据库、固定端口或缓存。这类层级没有跨进程互斥机制,因此并发运行的子 Agent 会互相清除对方的状态,并返回看起来像真实缺陷的失败。将任何此类层级标记为所有子 Agent 都不得运行。
这是主要分析。在一条助手消息中,为第 1 步中的每个顶层源目录各发出一个 Agent 工具调用。每个 Agent 调用都使用 model: "opus",且不设置 name。等待每个 Agent 都报告后再继续。不得在报告不完整时开始下一步,也不得重新启动尚未报告的 Agent。根据 Rules 部分的规定,最多调用 8 个。发出调用时明确说明数量。每个子 Agent 的提示词都要求其将共享工作树及其 git 索引视为只读——任何实证检查都必须在 $TMPDIR 下创建的独立 git worktree 中运行,并在之后丢弃。HEAD 保持原位:使用 git show <ref>:<path> 读取其他引用,而不是运行 git checkout 或 git switch。每条命令都使用该工作树的绝对路径,并用 && 连接连续步骤,以免某个步骤失败后其余步骤仍在共享检出目录中运行。拆除和验证要分别作为独立命令运行。为该工作树单独安装依赖,绝不能通过任何方式使用共享树的依赖安装:删除工作树会沿符号链接删除内容,而重定向的测试套件会写入共享安装目录。如果无法为其单独安装依赖,则不运行该检查,并报告这一情况。子 Agent 启动的每个测试运行器,无论是在工作树还是共享检出目录中运行,都必须在独立的进程组中运行,并由运行器外部强制施加超时。拆除前,子 Agent 必须停止其启动的每个运行器的进程组,因为停止运行器后,它生成的进程可能仍在运行。之后,子 Agent 要验证 git worktree list 不再显示该工作树、git status --short 状态干净、HEAD 仍位于启动时所在的分支,并且共享树的依赖目录仍可解析(依赖安装被删除后,git status 仍会显示干净,因为该目录被 git 忽略)。还要确认这些进程组中的所有进程,以及任何命令行包含该工作树路径(如果有)的进程,都已不再运行;并按 PID 报告任何无法停止的进程。如果无法列出进程,则报告该检查未运行,并列出相关进程组及工作树路径(如果有)。对于子 Agent 无法修复的损坏,须以准确的修复命令代替发现结果进行报告。
子 Agent 策略
每个子 Agent 都会收到:
- 分配给它的目录,用于扫描导出的符号
- 第 1 步中的测试文件模式
- 完整的项目根路径,以便它能在整个代码库中使用 grep
子 Agent 任务
每个子 Agent 在分配的目录中执行以下步骤:
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(有生产引用)
合并结果
所有子 Agent 完成后,收集并合并其结果。对多个报告中重复出现的符号进行去重(例如重新导出的符号)。如果某个 Step 2 工具将某个符号报告为未使用,而子 Agent 将其分类为 alive,则在合并结果中保留该符号,并附上子 Agent 提供的生产引用。
步骤 4:筛选、分类与评估
对步骤 2 和步骤 3 合并后的结果应用以下筛选条件:
- 框架入口点:跳过按惯例使用的符号 — barrel 文件中的 React 组件、URL 配置中的 Django 视图、Go
init()和main()函数、Go 接口实现、Rustmain()、Rust trait 实现、#[derive(...)]生成的代码、在 main 中注册的 CLI 处理程序、魔术方法/生命周期方法(__init__、__repr__)、序列化方法(to_json、from_dict)、接口/协议实现 - 重新导出链:在判定符号已失效前,先追踪 barrel 文件(
index.ts、__init__.py)。通过 barrel 重新导出的符号可能有间接使用者。 - 动态用法:将可能通过反射(
getattr、importlib、Go 中的reflect包、Rust 中的proc_macro)、基于字符串的查找或装饰器/属性注册来使用的符号标记为“可能已失效”,而不是“确定已失效” - 跨包引用:在 monorepo 中,判定符号已失效前,请确认它未被同级包导入
- 设计文档 / 规范 / 路线图:如果项目包含规范文件、路线图或 TODO 文件(例如,
.turbo/plans/、ROADMAP.md、TODO.md),请将仅在测试中发现的结果与其交叉核对。仅用于测试的 API 可能是等待集成的计划功能 — 应标记为 investigate,而不是 delete。如果唯一的引用来自一个 frontmatter 中status:为done的计划,仍应标记为 investigate,并注明该计划声称工作已交付:要么状态有误,要么集成已经落地但后来出现回归
对每项发现进行分类:
- 确定已失效:定义文件之外的引用为零
- 仅测试中已失效:存在引用,但全部位于测试文件中
- 可能已失效:因动态用法、框架惯例、复杂的重新导出链,或步骤 2 的工具报告某个符号已失效而子 Agent 找到了该符号的生产环境引用,导致存在不确定性
评估发现
对分类后的结果运行 /evaluate-findings 技能,根据实际代码验证每项发现并剔除误报。阅读每项发现对应的完整定义文件 — 不要只看被标记的符号。周边代码可能表明该功能已通过其他方式实现(例如,公开的 ping() 方法可能仅用于测试,而 handleConnect() 中的私有 keepalive 循环才负责实际工作)。
在下一节中继续处理评估结果。
建议操作
为每项仍然成立的发现指定一项建议:
| 信号 | 建议 |
|---|---|
| 没有测试,也没有生产环境用法 | delete |
| 有测试,但没有生产环境用法,且没有规范/路线图引用 | delete(方法 + 测试断言) |
| 有测试,但没有生产环境用法,且在规范/路线图/TODO 中被引用 | investigate(计划功能,并非已失效) |
| 部分接线、意图不明确,或需要领域背景 | investigate |
对于标记为 investigate 的发现,运行 /investigate 技能,以确定代码是计划功能、尚未接线的集成,还是确实已失效。
常见的无用代码模式
留意以下高价值模式:工具和简单的 grep 往往会漏掉它们:
- 仅供测试使用的状态访问器:只为测试断言而公开内部状态的属性或方法(例如,
isEnabled、count、currentItems)。模块的生产环境调用方使用的是行为(事件、回调、副作用),只有测试会窥探内部状态。移除这些内容时,也必须删除相应的测试断言,或改为通过基于行为的验证来实现。 - 未使用的数据模型字段:可序列化类型(Codable 结构体、dataclass、POJO)中已解码、但生产代码从未读取的属性。移除可序列化类型中的字段时,还要更新所有编码了该字段的数据文件(JSON、YAML、XML、数据库架构、迁移文件)。
- 遗留的枚举项:已定义,但生产代码从未构造或匹配的枚举项。
- 孤立的便捷方法:公开的包装方法会以略有不同的参数调用另一个公开方法,但所有调用方都直接使用底层方法。
相邻发现
扫描无用代码时,记下以下相关问题并告知用户,但不要采取行动:
- 无用代码附近的 Bug:无用代码附近往往有 Bug,例如缺少调用、连接缺失或集成不完整
- 未接入的功能:代码处于“几乎运行”的状态——已定义、已测试,但未连接到系统的其他部分。要将其与真正的无用代码区分开来。
步骤 5:呈现发现结果
按置信度级别对结果分组:
确定无用(定义之外没有任何引用)
| 文件 | 符号 | 类型 | 行范围 | 建议 |
|---|
仅测试使用的无用代码(仅在测试中被引用)
| 文件 | 符号 | 类型 | 引用该符号的测试文件 | 建议 |
|---|
可能无用(需手动核实)
| 文件 | 符号 | 类型 | 不确定的原因 | 建议 |
|---|
请包含:
- 每个类别的总数
- 估算可移除的代码行数
- 建议的移除顺序(先处理叶子依赖)
规则
- 如果识别出的顶层源代码目录超过 8 个,请让用户缩小范围,或将相关目录分组到更少的子 Agent 批次中。
- 如果未发现无用代码,请明确报告,并说明范围限制或分析注意事项。