首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
代码卫生清扫实战:六步流程清理死代码与无用依赖
📅 2026/10/13 9:56:55
✍️ 爱科研究院
👁 阅读 3,247
1. 代码卫生这件事为什么值得单独拎出来做一次“清扫”代码仓库跑久了几乎都会变成一间堆满杂物的储藏室。刚建仓那会儿目录干净、依赖清晰、命名统一谁看了都舒服。可三个月后再打开utils里躺着七八个没人调用的函数package.json里挂着三四个当初为了试一下某个功能装进来的库注释里还留着“TODO: 临时方案回头改”这种永远等不到回头的话。这些东西单看都不致命但它们叠加在一起会让每一次改动都变得犹豫——你不敢删因为不确定有没有人用你不敢改因为不知道牵一发动全身会碰到哪里。我把它叫做代码卫生问题。它不是 bug不会让程序崩溃但它像慢性病一样消耗团队的判断力和维护成本。hygiene-sweep这个 skill 要解决的就是把这种“说不清哪里脏、但就是不舒服”的状态变成一条可以重复执行的六步清扫流程。所谓 skill在这里指的是一套封装好的、可被调用和复用的操作能力——你可以把它理解成一个专门负责打扫代码仓库的“清洁工”你告诉它扫哪里它按固定套路把死代码、无用依赖、冗余配置、过期注释这些垃圾清出去。这篇文章适合三类人看一是接手了历史项目、面对一坨祖传代码不知道从哪下手的同学二是团队里负责代码质量、想建立定期清理机制的人三是单纯对“skill 到底怎么落地”感兴趣、想自己搭一个类似工具的开发者。我会把六步清扫的每一步拆开讲清楚——为什么这么设计、具体怎么操作、实测中会遇到什么意外以及那些文档里不会写的坑。全程按我实际跑下来的经验来说不绕弯子。2. 清扫之前先想清楚代码卫生到底在扫什么2.1 死代码不是“没用的代码”而是“没人敢删的代码”很多人对死代码的理解停留在“没有被调用的函数”。实际项目里死代码的形态要复杂得多。我梳理过几类最常见的孤儿函数定义在某个模块里全仓库搜索没有任何调用点但作者当初写的时候可能留了导出导致静态分析工具也判断不准。僵尸分支if条件里引用了某个早已下线的配置项这个分支永远不会走到但代码还在。幽灵依赖package.json或requirements.txt里列着但代码里import都找不到的库。化石注释注释描述的逻辑和当前代码完全对不上比如注释说“这里做缓存”代码其实早就改成直连了。重复实现同一个功能在两个地方各写了一遍只是命名不同谁也不知道该留哪个。这些问题的共同点是它们不会报错但会让阅读者产生误判。你读代码时以为某个函数很重要结果发现根本没人调你以为某个依赖是核心结果它只是历史遗留。清扫的目的不是追求“零冗余”这种理想状态而是把那些明确可以安全移除的部分识别出来降低认知负担。2.2 为什么用 skill 的形式来做而不是写个脚本有人会问清理死代码写个脚本跑一下静态分析不就行了我一开始也这么想但实际做下来发现纯脚本的问题在于它只给结论不给判断依据。比如脚本告诉你“函数formatDateV2没有被调用”但你不知道它是真的废弃了还是通过动态引用、字符串拼接的方式被调用了。这时候你需要的是一个人来判断而不是脚本直接删。hygiene-sweep作为 skill 的价值在于它把扫描、分类、人工确认、执行清理、验证、记录这六个环节串成了一条标准流程。脚本负责扫描和初步分类人负责确认和决策最后再回到自动化验证。这样既保留了自动化的效率又避免了“脚本一跑删错东西”的风险。这也是我坚持把它做成 skill 而不是一次性脚本的原因——清扫是周期性动作不是一锤子买卖。2.3 六步清扫的整体节奏在展开每一步之前先给一个全局视角。六步的顺序不是随便排的它遵循一个原则先看清楚再动手先动低风险的再动高风险的先改代码再改依赖最后一定要验证和留痕。步骤名称核心动作风险等级第一步全量扫描静态分析 依赖树导出无风险第二步分类标记按类型和置信度分组无风险第三步人工确认逐项判断是否可删低风险第四步分批清理按风险从低到高执行中风险第五步回归验证测试 构建 冒烟无风险第六步记录归档生成清扫报告无风险这个顺序的关键在于第三步和第四步之间不能省。我见过有人扫描完直接批量删除结果把一个通过反射调用的函数删了线上直接报错。人工确认这一步看起来慢但它是在为后面的清理兜底。3. 第一步到第三步扫描、分类、确认的完整操作链路3.1 全量扫描工具选型和参数配置扫描阶段的目标是拿到两份清单代码层面的引用关系和依赖层面的使用情况。不同语言生态用的工具不一样我按常见场景列一下。对于 JavaScript/TypeScript 项目我用的是ts-prune配合depcheck。ts-prune负责找出没有被引用的导出depcheck负责找出package.json里没用到的依赖。命令大概是这样npx ts-prune --project tsconfig.json unused-exports.txt npx depcheck --json unused-deps.json对于 Python 项目vulture是比较好用的死代码检测工具依赖检查用pipreqs反向生成实际使用的依赖列表再和requirements.txt对比vulture . --min-confidence 80 dead-code.txt pipreqs . --print actual-deps.txt这里有个参数值得说一下vulture的--min-confidence默认是 60我建议第一次扫描时调到 80 以上。原因是置信度低的条目太多会淹没真正有价值的结果。等清理完高置信度的部分再回头用低阈值扫一遍。注意扫描前一定要确保工作区是干净的没有未提交的改动。否则扫描结果里会混入你正在写的临时代码造成误判。3.2 分类标记把“可能没用”和“确定没用”分开扫描出来的原始清单是一锅粥直接看会崩溃。我的做法是按两个维度分类类型和置信度。类型维度就是前面说的那几类孤儿函数、僵尸分支、幽灵依赖、化石注释、重复实现。置信度维度分三档高置信度全仓库无引用、无动态调用痕迹、无字符串匹配。这类基本可以直接进入待删列表。中置信度有间接引用可能比如通过eval、反射、配置文件映射调用。这类需要人工确认。低置信度工具误报或者引用关系复杂到工具判断不了。这类先放着不急着处理。我一般会生成一个 Markdown 表格来承载这些信息方便后续逐项打勾条目类型置信度所在文件备注formatDateV2孤儿函数高src/utils/date.ts全仓库无调用legacyConfig僵尸分支中src/config/index.ts可能被环境变量引用lodash幽灵依赖高package.json代码中无 import这个表格是整个清扫流程的核心工作台后面每一步都围绕它展开。3.3 人工确认怎么判断一个函数真的可以删这是最考验经验的一步。工具说“没引用”但你不能全信。我总结了一套确认动作按顺序执行全局搜索函数名不只是搜调用还要搜字符串。有些代码通过window[funcName]这种方式调用普通引用分析抓不到。检查配置文件有些函数名会出现在路由配置、插件注册表、定时任务列表里这些地方是字符串引用静态分析容易漏。检查测试文件如果某个函数只在测试里被引用而测试本身也是废弃的那这个函数大概率可以删。但要先确认测试是否还在 CI 里跑。看 git 历史用git log -S 函数名看看最后一次改动是什么时候。如果两年前就没人碰过删除的风险相对低。问作者如果作者还在团队里直接问一句比猜半天强。如果作者已经离职那就按前四步的判断来。提示对于中置信度的条目我建议先标记为“待观察”不要在这一轮删除。等下一轮清扫时如果它还是没人用再处理。这样给自己留一个缓冲期。4. 第四步到第六步清理、验证、归档的实操细节4.1 分批清理为什么不能一次性全删确认完清单后最诱人的做法是“全选删除提交收工”。我试过一次结果回滚了三个 commit 才恢复。教训是清理必须分批而且每批只做一类操作。我的分批策略是这样的第一批删幽灵依赖。这是风险最低的因为依赖删了不影响代码逻辑最多是构建时找不到包。而且package.json的改动很容易回滚。第二批删孤儿函数和化石注释。这两类都是纯代码层面的不涉及配置和依赖影响范围可控。第三批处理僵尸分支和重复实现。这类改动会触及逻辑需要更仔细的验证。第四批处理中置信度条目。这些是上一轮标记为“待观察”的如果确认还是没用再删。每一批做完都要单独提交commit message 写清楚删了什么、为什么删。这样万一出问题git revert就能精准回滚不用大海捞针。4.2 回归验证删完之后怎么确认没删坏清理完一批后验证动作不能省。我通常跑三层验证构建验证确保项目能正常编译打包。这一步能抓出依赖删除导致的缺失问题。测试验证跑全量单元测试和集成测试。如果测试覆盖率不高这一步的保障有限所以要结合第三层。冒烟验证手动跑一遍核心业务流程。比如 Web 项目就打开几个关键页面看看有没有报错后端服务就调几个核心接口确认返回正常。这三层里冒烟验证是最容易被跳过但最重要的一层。因为测试用例可能本身就没覆盖到被删的代码路径只有实际跑一遍才能发现“删完之后某个边缘功能挂了”这种问题。注意如果项目有 CI/CD 流水线清理分支推上去后先别合并等流水线全绿再操作。流水线里的构建和测试步骤就是自动化的回归验证。4.3 记录归档清扫报告应该包含什么最后一步是生成清扫报告。这份报告不是为了交差而是为了下一轮清扫时有参照。我一般会记录这些内容清扫时间和执行人。扫描工具及版本方便复现。删除条目清单包括类型、位置、删除原因。保留条目清单包括那些被标记为“待观察”的注明为什么这轮没删。验证结果构建、测试、冒烟各自的结论。遗留问题比如某个依赖不确定能不能删留给下一轮判断。这份报告我习惯放在仓库的docs/hygiene/目录下按日期命名。下次清扫时先翻上一次的报告看看“待观察”的条目现在是什么状态效率会高很多。5. 实测中踩过的坑和几条硬核经验5.1 动态引用是最大的误判来源我遇到过一次典型的误判一个函数handleLegacyCallback在全仓库搜索不到任何调用静态分析也确认无引用我把它删了。结果上线后某个老接口报错排查半天才发现这个函数是通过一个配置文件里的字符串映射被调用的——配置文件里写的是handler: handleLegacyCallback然后框架在运行时动态加载。静态分析根本抓不到这种引用。从那以后我在人工确认环节加了一条搜索函数名时必须同时搜索不带引号的纯字符串形式。而且对于框架类项目要特别检查路由配置、事件注册表、插件清单这类地方。5.2 依赖删除要看“间接依赖”这层关系删package.json里的依赖时有个坑是你删掉的那个库可能是另一个库的间接依赖。比如你删了lodash但axios内部依赖lodash的某个方法虽然axios自己会带上但版本可能和你项目里锁定的不一致导致行为变化。我的做法是删依赖前先跑一次npm ls 依赖名或pip show 依赖名看看有没有其他包依赖它。如果有要么保留要么确认间接依赖的版本兼容后再删。5.3 清扫频率别等代码烂透了才动手我建议的清扫频率是每个迭代周期做一次小清扫每个季度做一次大清扫。小清扫只处理高置信度的死代码和幽灵依赖半小时能搞定。大清扫走完整六步包括中置信度条目的确认大概需要半天。这个频率的好处是每次要处理的东西都不多不会积累成一座山。我见过有的团队一年不清扫最后打开仓库一看光package.json里就有二十多个不知道干嘛的依赖那时候再清理成本高到没人愿意接。5.4 团队协作时的沟通成本如果仓库是多人协作的清扫前最好在团队里同步一下。原因很简单你删掉的“死代码”可能是别人正在准备用的。我就遇到过这种情况——我删了一个工具函数结果同事第二天说他正准备在新功能里用。虽然可以恢复但来回沟通也是成本。我的做法是清扫前发一条消息说明这次要清理的范围让大家有异议的提前说。清扫报告出来后也同步一份让所有人知道删了什么。6. 把这套流程固化成可复用的 skill6.1 skill 的封装思路把判断逻辑和操作步骤分开回到hygiene-sweep这个 skill 本身。它的设计核心是把“判断逻辑”和“操作步骤”解耦。判断逻辑是什么算死代码、置信度怎么分、什么情况下不能删。操作步骤是用什么命令扫描、怎么生成报告、怎么分批提交。这样设计的好处是当你的项目技术栈变化时只需要替换操作步骤里的工具命令判断逻辑可以复用。比如从 JavaScript 项目换到 Python 项目扫描工具从ts-prune换成vulture但分类标准和确认流程基本不变。6.2 触发方式手动触发还是定时触发我目前是手动触发为主定时触发为辅。手动触发用在两种情况一是接手新项目时做一次全面清扫二是感觉代码库开始变乱时做一次针对性清扫。定时触发我设的是每月一次只跑扫描和分类生成报告但不自动清理等我有空时再处理。不建议做成全自动清理因为人工确认这一步没法省。全自动清理的风险前面说过了删错东西的代价远大于省下来的那点时间。6.3 后续可以扩展的方向这套流程跑顺之后我打算往两个方向扩展。一是把清扫报告和代码覆盖率结合看看被删的代码是不是本来就没被测试覆盖如果是说明测试本身也有盲区。二是把清扫和代码评审挂钩在 PR 里自动检测新增的死代码从源头减少积累。不过这些都是后话先把六步清扫跑稳再说。工具是为人服务的流程跑通了再考虑自动化程度的提升。我在实际使用这套流程大半年后最大的体会是代码卫生不是一次性的运动而是一种需要定期维护的习惯。每次清扫完看着干净的依赖列表和精简的代码结构那种舒适感是实实在在的。而且清扫过程中经常会有意外发现——比如某个以为很重要的模块其实早就没人用了或者某个依赖的版本已经落后了好几个大版本。这些信息对后续的技术决策都有帮助。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/13 9:56:55
Eclipse Windows安装配置避坑指南:JDK匹配、环境变量与workspace规范
2026/10/13 9:56:55
MyCat双主双从高可用读写分离架构实战:数据库中间件故障切换与复制配置详解
2026/10/13 9:56:54
MATLAB LSTM短期风速预测:从数据预处理到参数调优实战
2026/10/13 10:47:11
Flutter前台/后台服务插件对比与选型指南
2026/10/13 10:47:11
Flutter后台服务插件对比:前台服务与后台任务选型指南
2026/10/13 10:47:11
Kafka架构原理与线上故障排查实战指南
2026/10/13 10:47:11
2D PCA降维实战:从传统PCA痛点到大矩阵特征提取
2026/10/13 10:47:11
AIHOT:7千星开源AI热点日报框架,自己找热点自己写日报,12天冲上日榜
2026/10/13 10:42:10
HAProxy+Nginx Ingress如何实现真实IP透传?PROXY Protocol实战指南
2026/10/13 0:00:40
物联网系统集成避坑指南:从选型到验收的关键要点
2026/10/13 0:00:40
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查
2026/10/13 0:00:40
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析
2026/10/12 8:53:44
流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南
2026/10/12 8:53:42
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别
2026/10/13 8:14:22
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容
2026/10/12 19:53:23
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/11 21:41:11
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/11 23:43:10
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)