开发中的垃圾代码 — 是什么、junk-code 的危害以及如何清除

作者: IT Sectr 发布日期: 2026-07-27 阅读时间: 10 分钟

垃圾代码(junk code)是指对项目没有益处,但增加项目体积、构建时间和团队认知负荷的代码和依赖关系。与永远不会执行的死代码不同,垃圾代码可能可以运行,但效率低下或冗余:重复的库、未使用的导入、注释掉的代码块、过时的 polyfill 和装饰性抽象。根据 CodeScene Code Health Report (2025),移动项目中平均 15% 的依赖关系未被直接使用,仅拖拽传递性包。垃圾代码是项目的额外负担:它使代码库变得更臃肿,但没有变得更强。定期审核依赖关系和删除多余的抽象直接影响构建速度和代码质量。

要点

  • 垃圾代码 — 无用或多余的代码和依赖关系,增加了项目规模却没有带来好处。
  • 垃圾代码的类型:死依赖、重复的库、注释掉的代码、空抽象。
  • 垃圾依赖增加了攻击面并减慢了 CI 流水线。
  • 审核工具:Gradle dependencies(Android)、SwiftPM audit(iOS)、depcheck(Node.js)。
  • 定期清理垃圾代码与编写新代码一样,是项目技术维护的重要组成部分。

什么是垃圾代码?

垃圾代码(junk code)是项目中存在但没有功能价值的代码、配置和依赖关系的统称。垃圾代码不一定是损坏或未使用的 — 问题在于它的存在在没有充分理由的情况下恶化了项目的指标。

垃圾代码分为四类。第一 — 多余的依赖关系:为了一个可以用标准工具实现的功能而连接的库。第二 — 死负荷:注释掉的代码块、没有工单的 TODO、空方法和存根类。第三 — 重复的解决方案:两个做同样事情的库(例如,同一个项目中的 Gson 和 Kotlin Serialization)。第四 — 过度工程:未被使用但为未来维护的架构层。

根据 Stripe Engineering Productivity (2025) 的研究,从典型项目中删除 10% 的垃圾代码可将完整构建时间平均缩短 22%。原因:每个额外的依赖关系都会增加构建图,每个空抽象都需要时间来理解,每个注释掉的代码块都会分散注意力。

与垃圾代码作斗争的主要困难是缺乏立竿见影的后果。带有垃圾代码的项目可以编译和运行。问题是逐渐积累的:构建变慢,传递性依赖关系的数量增加,一年后添加新功能所需的时间比应有的多一倍。

垃圾依赖及其识别方法

垃圾依赖是指连接到项目但未在代码中直接使用的库和包,或者仅在一个可以用标准 API 更轻松实现的功能中使用。

典型示例:当项目已使用 Kotlin Serialization 时,用于处理 JSON 的库(两个解析器 — 这就是垃圾代码);为单个 StringUtils.isEmpty 方法使用 Apache Commons Lang 库,而该方法可以用 Kotlin 扩展 isNullOrBlank 替代;在十个模块中的一个模块使用的 DI 库,而其他模块通过构造函数手动获取依赖关系。

每个额外的依赖关系不仅仅是二进制文件中的额外代码。它还会增加漏洞的攻击面:根据 GitHub Advisory Database(2025),移动项目中 40% 的关键 CVE 来自开发人员无法控制的传递性依赖关系。依赖关系越少 — 攻击面越小。

Android 项目依赖关系分析

groovy
// 查看 Gradle 依赖树
./gradlew app:dependencies --configuration releaseRuntimeClasspath

// 查找未使用的依赖(Gradle 插件)
plugins {
    id "com.autonomousapps.dependency-analysis" version "2.0.0"
}

// 生成未使用库报告
./gradlew buildHealth

对于 iOS,使用命令 swift package show-dependencies,它显示完整的依赖关系树。Xcode Build Timeline 工具显示每个库为构建增加了多少时间。如果一个库占用了 30% 的编译时间但只在一个屏幕上使用 — 它就是需要删除或替换的候选者。

对于 Node.js(React Native),使用 depcheck — 一个查找 package.json 中未使用依赖关系的工具,以及 npm-check,它还会显示过时的版本。实施规则:每个新的依赖关系都必须经过 code review,并说明为什么不能使用标准工具。

死导入和注释掉的代码

死导入是最常见的垃圾代码类型。它们不影响运行时,但会增加编译时间:编译器处理每个导入,即使它没有被使用。在大型项目中,删除未使用的导入可将构建时间缩短 5–10%。

现代 IDE 会自动以灰色突出显示未使用的导入。在保存文件时配置自动清理:在 IntelliJ IDEA 中 — Optimize Imports on the fly,在 Xcode 中 — Editor > Remove Unused Imports。在 CI 中添加检查:linter 应阻止带有未使用导入的提交。

注释掉的代码是另一种垃圾代码。开发人员注释掉代码块以在重构时保留功能。然而,git 存储了完整的更改历史:任何删除的代码都可以通过一个 git revert 或 git log -S 命令恢复。主分支中的注释代码是对团队的不尊重:每个开发人员都要花费脑力去思考为什么这被注释掉了以及何时应该取消注释。

规则:仓库中没有注释掉的代码。如果代码不需要 — 永久删除它。如果代码需要但暂时禁用 — 使用带有工单和截止期限的功能开关。// TODO: remove after migration 类型的注释 — 不要没有截止期限。设置日期并在日历中设置提醒。

多余的抽象和过度工程

过度工程是创建不能解决当前问题但需要维护的架构层。这是最难处理的垃圾代码类型之一,因为形式上代码是正确的:遵循 SOLID,有测试覆盖,符合架构。问题在于它不需要。

经典示例 — 一个具有单一 invoke 方法的抽象 UseCase 类,它只是调用仓库。如果 UseCase 没有添加逻辑(缓存、重试、转换),只是传递调用 — 这就是一个多余的实体。它增加了项目导航:开发人员打开 UseCase,看到 invoke → repository — 然后关闭。时间浪费,收益为零。

另一个例子 — 过度参数化。一个具有六个类型参数的通用接口,只在一个地方使用。每个类型参数都是认知负荷:阅读代码时需要记住六个类型,而实际上只使用两个。如果抽象没有被复用 — 它就是多余的。

截断标准:如果一个抽象没有在三个不同的上下文中被复用 — 删除它。抽象只有在真正解决重复问题时才是合理的,而不是在预测假设性的未来场景时。YAGNI(You Ain't Gonna Need It)是防止过度工程的最佳原则。

垃圾代码审核工具

垃圾代码审核需要结合静态分析、依赖关系分析和手动检查。无法完全自动化搜索多余的抽象,但技术性垃圾代码(死导入、未使用的库、注释掉的代码)可以通过工具找到。

类别工具检查内容
未使用的依赖dependency-analysis(Gradle)代码中未使用的库
未使用的依赖depcheck(Node.js)package.json 中没有导入的包
未使用的依赖swift package --show-dependenciesSwiftPM 依赖树
死导入IDE(Optimize Imports)未使用的导入表达式
注释掉的代码grep -r "//" / rg "^\s*//"含有代码的注释块
空方法/类SonarQube / CodeClimate没有方法体或方法体为空的方法
重复的库Gradle lint(duplicate classes)来自不同库的类冲突

为了全面审核,每个 sprint 运行一次 buildHealth(Android)或 depcheck(Node.js)。在 CI 中创建一个仪表板,显示每个 sprint 的依赖数量变化趋势。如果数量增加,但功能没有成比例增加 — 团队正在积累垃圾代码。

注意 duplicate classes — 两个库包含同一个类的错误。这不仅是垃圾代码,也是构建冲突的直接来源。在 Gradle 中,这种冲突通过 force 或 exclude 解决,但每次这样的解决都表明其中一个库是多余的。

项目定期清理流程

清理垃圾代码不是一次性行动,而是一个定期流程。没有规则,垃圾代码在两到三个 sprint 内就会回来。最佳实践是分配每个 sprint 的 10–15% 容量用于技术清理,包括垃圾代码审核。

该流程包括四个步骤。第一 — 诊断:运行工具、获取报告、确定优先级。高优先级 — 具有已知 CVE 的依赖和重复的库。中优先级 — 死导入和注释掉的代码。低优先级 — 多余的抽象(需要手动分析)。

第二 — 清理:删除死依赖、将重复的库替换为一个、删除注释掉的代码。每个更改都使用单独的提交,并附带清晰的消息:remove unused dependency: gson(replaced by kotlinx.serialization)、delete commented code in LoginViewModel。

第三 — 验证:构建项目、运行测试、检查 UI。如果删除依赖后测试通过 — 该依赖确实不需要。如果测试失败 — 意味着某个地方留下了静态分析器未能检测到的隐藏引用。

第四 — 预防:更新 code review 清单、在完成定义中添加没有理由就不得添加新依赖的规则、在 CI 中设置自动检查。预防是防止垃圾代码再次积累的唯一方法。

常见问题

垃圾代码与技术债务有什么区别?

技术债务是有意识的折中决策(快速但质量低),计划将来修复。垃圾代码不是有意识的决定,而是积累的废物:多余的依赖、注释掉的代码、没有人计划也没有人想维护的空抽象。

应该多久清理一次垃圾代码?

最佳节奏 — 每个 sprint 分配 10% 的时间进行技术清理。这可以在不积累关键数量的情况下控制垃圾代码。如果项目中有很多垃圾代码 — 从一次大型清理 sprint 开始,然后过渡到定期节奏。

如何说服团队删除垃圾代码?

测量并展示数字:测量删除 3–5 个多余依赖前后的构建时间。每次构建节省 15–30 秒乘以每天的构建次数,就可以得到团队节省的小时数。数字比抽象的整洁号召更有说服力。

如果项目稳定,是否值得从依赖中删除垃圾代码?

是的,特别是当依赖存在 CVE 时。即使项目稳定,传递性依赖中的漏洞也是安全风险。此外,在更新 SDK 或语言时,旧依赖可能不再兼容,在升级前删除它可以节省数小时的迁移时间。

如何处理代码中的 TODO?

每个没有工单的 TODO 都是垃圾代码。制定规则:TODO 只能以 // TODO(PROJECT-1234):fix 格式编写,并链接到跟踪器中的任务。定期检查 TODO 并关闭那些已过时的。删除过期的 TODO — 如果问题在半年内没有出现,就不关键。

总结

  • 垃圾代码 — 无用的代码、未使用的依赖和多余的抽象,无益地增加项目规模。
  • 四类:多余的依赖、死负荷、重复的库和过度工程。
  • 每个额外的依赖都意味着构建时间、攻击面和认知负荷的增加。
  • 审核工具:dependency-analysis(Gradle)、depcheck(Node.js)、SonarQube、用于注释代码的 grep。
  • 定期清理:sprint 的 10–15% 用于技术工作,每个 sprint 进行一次依赖审核。
  • 预防:通过检查新依赖进行 code review、设计时遵循 YAGNI、自动清理导入。
  • 规则:没有新依赖未经论证,没有 TODO 没有工单,主分支中没有一行注释掉的代码。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读