用途
codebase-refactoring 是一个引导式工作流技能,用于对代码库进行整体健康度改善,而非修复某个具体 bug。它聚焦于代码库的三大维护成本:重复代码、死代码和复杂度,使用 jscpd 工具定位每一类问题,并给出对应的修复策略。当被要求“清理”“改进”“重构”或“偿还技术债”时使用本技能。
主要能力
- 健康度测量:通过
npx jscpd --health --reporters ai <path>得到健康分,该分数是重复、死代码、复杂度三个子分数的加权混合,每个子分数是代码行数的占比,分数越高越好,100 表示干净。三个子分数中最低的那个指示代码库真正最痛的地方,应优先处理该维度。若某维度显示 n/a(例如死代码 n/a),表示 jscpd 无法测量它——死代码检测需要 JavaScript、TypeScript 或 Python 在代码中占实际比例——应跳过该步骤,而不是当作干净。 - 仪表盘视图:
npx jscpd --dashboard可在同一屏展示最大重复格式、最复杂文件、最大死代码发现等完整信息。其控制台输出(默认)是唯一带有逐文件细节的报告器;在--dashboard上使用--reporters ai只会打印上述单行摘要,不会给出细分。若需要可解析的细节,可在--dashboard上使用--reporters json。 - 三个独立阶段:重复代码 → 死代码 → 复杂度是默认顺序,因为先修复重复可避免第二步浪费精力追踪调用点,先清理死代码可确保第三步排名的文件确实值得简化。三个阶段本身相互独立,可按任意顺序安全运行。
使用方式
第一步 — 重复代码
运行 npx jscpd --reporters ai --summary <path>,找出并阅读每个克隆,判断是否为值得合并的真实副本,并应用匹配的重构策略(提取函数、参数化、提取共享模块或常量,或针对结构性重复使用基类/模板)。完整的引导工作流、重命名/近似克隆的分诊规则以及每种克隆类型的示例见 dry-refactoring。其 --summary 热点排名与健康分的重复维度读取的是同一份数据,因此被标记为重复热点的文件正是拖低分数的文件。
第二步 — 死代码
运行 npx jscpd --dead-code --reporters ai <path>。这是从项目入口点(package.json 的 main/bin/exports/scripts、pyproject.toml 的 scripts、框架约定,或 --entry <glob>)出发的图遍历,而非引用计数——一个只被自身死代码调用的辅助函数也会被报告,因此删除一个发现可能使另一个原本未报告的发现变为真实。覆盖 JavaScript、TypeScript、JSX、TSX、Vue、Svelte、Astro 和 Python。
四类发现及常规修复:
| 类别 | 含义 | 常规修复 |
|---|---|---|
| unused-file | 从未被任何地方导入,也不是入口点 | 删除文件 |
| unused-export | 被导出,但没有其他文件导入它 | 删除它,或若同文件内仍有使用则去掉 export 关键字 |
| unused-symbol | 模块私有声明,其自身文件内无任何引用 | 删除它 |
| unused-import | 被导入但文件内从未引用 | 删除导入行 |
工作流:
- 使用
--reporters ai运行(可加--dead-code-categories聚焦某一类别,--include-tests使仅测试使用不算“已使用”,--include-entry-exports也标记入口文件自身未使用的导出,通常排除因为其整个表面就是公共 API)。 - 按置信度从高到低排序。所有发现为 0-100;低于 90 的发现会附带一个可能出错的理由——eval/getattr 调用、分析器不认识的装饰器、通配符重导出、名称仅出现在字符串字面量中,或别处有文件解析失败(见 JSON 报告中的 statistics.unparsedFiles)。删除前先阅读理由;
--min-confidence(默认 60)只设置报告下限,并不使低置信度发现变得安全。 - 对每个高于你舒适阈值的发现,手工确认——在仓库中 grep 符号名(动态访问、基于字符串的路由器、jscpd 不解析的模板文件都可能隐藏真实使用)——然后应用上表中的修复。
- 每批删除后运行项目自身的测试套件和构建,而不仅是重跑 jscpd:死代码分析证明的是从入口点无法到达该代码,而非删除它不会破坏构建步骤。
- 一批之后重跑
--dead-code:一个仅有的导入刚被你删除的文件现在可能自身已死——这种级联是预期的,持续进行直到干净通过,而非只跑一次。 - 单独报告你跳过了什么及原因(低置信度、无法排除的理由,或有意保留的死代码如公共库导出),与实际删除的内容分开。
第三步 — 大而复杂的文件
运行 npx jscpd --complexity --reporters ai --summary-top 10 <path>,或与重复和大小一起排名:npx jscpd --reporters ai --summary --summary-by complexity <path>。
cx 是语言感知的圈复杂度估算——大致每个函数一条路径加上每个分支一条(if、循环、&&/||、match/switch 分支、三元和 Elvis 运算符,按语言实际写法计数)——应作为排名信号阅读,而非精确指标。散文和数据文件(Markdown、JSON、YAML、锁文件等)始终得 0 分;大 README 不是重构目标。jscpd 自身的健康分将复杂度 50 或以上的文件视为“复杂”(其死代码式半衰期曲线校准的阈值)——若摘要未明确给出明显截止点,这是“值得阅读”的合理默认值。
工作流:
- 取复杂度最高的前 N 个文件(以及从
--summary形式取大小最大的——一个巨大但低复杂度的文件,如大数据表,与一个小而高复杂度的文件是不同的问题)。 - 阅读每个文件并指出实际驱动因素:深层嵌套、一个做多件不相关事情的长函数、针对类型或状态的大型 switch/if 链,或实际上是多个模块粘在一起的文件。
- 选择匹配策略:
- 深层嵌套 → 守卫子句/提前返回,展平快乐路径
- 长函数、多个职责 → 按职责提取函数,以其功能命名
- 针对固定用例集(类型、枚举、状态)的分支 → 替换为查找表/映射,或若每个用例已有自己的类型则用多态
- 文件做太多 → 沿实际接缝拆分(每个文件一个导出组),而非任意按行数切割
- 一次重构一个文件;保持行为不变——这是复杂度处理,不是重写。若阅读时发现重复克隆(第一步)或死分支(第二步),仍在其自己的步骤中处理,以便每次变更的原因可追溯。
- 对触及的文件重跑
--complexity确认 cx 确实下降——只移动代码而未减少每函数分支的拆分没有修复任何东西,只是给问题改了名。
第四步 — 确认改进
运行 npx jscpd --health --reporters ai <path>,将分数和等级与第 0 步测量比较。某个维度变差(例如在提取共享复杂度修复时重复又悄悄回来)是重访该步骤的信号,而不仅是记录——重跑相关阶段(--summary、--dead-code、--complexity),而非只信任一个聚合数字。
适用场景
- 被要求“清理”“改进”“重构”或“偿还技术债”的代码库
- 需要系统性降低重复代码、死代码和复杂度的项目
- 希望用可量化的健康分跟踪重构进展的团队
限制
- 死代码检测需要 JavaScript、TypeScript 或 Python 在代码中占实际比例,否则该维度显示 n/a,应跳过。
- 复杂度 cx 是估算值,应作为排名信号而非精确指标;散文和数据文件始终得 0 分。
- 死代码发现低于 90 置信度时可能出错,删除前必须人工验证。
- 两次运行的健康分只有在基于相同维度构建时才可比较;若某维度因文件构成变化从 n/a 变为已测量(或反之),应说明而非将原始分数差读作改进或回归。
- 每批删除后需运行项目自身测试和构建,死代码分析不保证删除不会破坏构建步骤。
- 建议每步单独提交(重复修复、死代码删除、复杂度简化),以便审查者验证“是否确实减少了 X”。
- 不要在没有先阅读
--dashboard的情况下对从未见过的代码库运行全部三个阶段——若只有一个维度确实糟糕,在三个维度上平均用力会浪费时间。 --health-input文件(覆盖率、安全扫描结果)可叠加到同一分数,若项目已跟踪这些;它不改变本工作流,只是为第 0 步增加更多维度。