Ohhnews

分类导航

$ cd ..
foojay原文

AI辅助的未使用与死代码移除

#ai辅助#死代码移除#java#生产环境分析#代码清理

为何你的代码库正导致AI表现不佳,以及如何应对

[LOADING...]

你的AI编码助手的表现,完全取决于它所工作的代码库。如果您的Java应用程序承载着多年的死代码和未使用代码(大多数确实如此),那么AI的推理预算就会被浪费在无人运行的代码上。结果就是:更多的幻觉、更差的建议、更高的Token成本。以下是对策。

本文将深入探讨这一问题,并提供一个实用工作流:结合Azul Intelligence Cloud的Code Inventory与AI编码代理,从生产环境Java应用中查找并移除死代码。

上下文窗口:AI的工作内存

要理解未使用代码如何损害AI性能,首先需要了解大型语言模型处理代码的方式。当你让AI助手添加功能、修复Bug或重构模块时,它不会只查看你正在指向的文件。它会加载你的提示、聊天记录、关联文件、导入的依赖以及推理你的请求所需的其他任何内容。所有这些文件都会被合并到上下文窗口中。

上下文窗口是AI针对给定任务的工作内存,以Token为单位衡量——大致对应单词碎片或代码片段。导入一个函数签名大约消耗20个Token,一个中等复杂的文件可能需要5000个Token。现代模型提供20万Token或更多的上下文窗口,听起来很充裕,但当你处理一个庞大、耦合的巨无霸系统时,这点容量根本不够。

上下文成本并不取决于你的仓库中有多少文件,而在于AI回答一个问题需要加载多少内容。在一个干净、模块化的代码库上,AI只需拉入需要的函数即可停手。但在一个混乱、遗留代码堆积的代码库上,同样的请求会引发连锁反应:深层导入链、上帝对象、动态分发器、以及多年无人触碰的代码。

AI助手的输出因此变得劣化,出现更多幻觉、错误的方法签名、错误的参数类型等问题。

除了糟糕的代码生成,过载的上下文窗口还会影响预算。使用LLM模型的成本基于消耗的Token数计算。每次请求拖入大量不必要的内容,处理请求的成本也随之显著增加。

未使用与死代码比你想象的更常见

2025年,Azul对Java工程师进行了代码库状况调查62%的受访者报告因死代码或未使用代码导致DevOps生产力损失。 对开发者来说,这种负担表现为认知负荷——他们花时间理解毫无用途的代码。对AI助手来说,则表现为浪费的Token和降级的推理能力。

但最让团队意外的是:大多数未使用代码看起来并不像死代码。

经典的死代码(例如单个类中从未被调用的私有方法)很容易被发现,你的IDE就能搞定。IntelliJ内置了专门的分析工具。静态分析工具也能很好地处理。

真正的问题是那些看起来活着、但在生产中从未真正运行的代码。例如:

  • 通过依赖注入连接的Spring Bean和服务,技术上可到达,但可能从未被调用。
  • 被特性开关关闭、从未在生产中启用、然后被悄然遗忘的模块
  • 静态分析器认为可到达(因为技术上确实可能)的反射驱动组件
  • 来自已弃用工作流的旧API控制器,仍然存在且完全连接,但从未接收过流量。

静态分析器回答的是“这段代码能被调用吗?”这个问题。它们不回答“这段代码在生产中实际被调用了吗?”对于Java应用而言,这是两个截然不同的问题。

运行时证据改变了一切

这时,Azul Intelligence Cloud的Code Inventory功能就派上了用场。部署在生产环境中JVM实例旁边的Intelligence Cloud代理,会将JVM已收集的数据转发到Intelligence Cloud系统。Code Inventory不问源代码的可达性,而是报告在生产运行时实际执行了哪些代码。它将其他所有代码标记为未使用,无论它们看起来多么可达。

输出是一个由Intelligence Cloud API生成的结构化数据文件,将整个代码库映射到实际的生产行为。不再是“这段代码可能被调用”,而是“这段代码在观察窗口内从未被调用”。

这一区别改变了你能够采取的行动。现在你可以区分:

  • 在开发环境运行但不在生产环境运行的代码(环境漂移)。
  • 在某些环境运行但不在其他环境运行的代码(特性开关分歧)。
  • 在所有观察环境中从未运行过的代码。

对于工程经理来说,这意味着基于生产数据而非直觉做出移除决策。对于Java开发者来说,这是一个IDE无法提供的清理起点。

AI辅助代码移除工作流

识别未使用代码只是第一步。安全地移除它们——不破坏测试或遗漏隐藏依赖——才是大多数团队卡住的地方。这正是AI编码代理发挥作用之处。

使用此方法的典型工作流如下:

  1. 检测与观察。 将Code Inventory部署到你的应用程序旁,让它运行。为了获得有意义的画面,你需要覆盖一个代表性的时间窗口:一个完整的发布周期、一个财年末或一次重大流量事件。让应用程序正常运转,数据自行积累。

  2. 导出Code Inventory报告。 当你准备行动时,导出Code Inventory报告。结果是一个Parquet文件,包含应用中的每个类(可选到每个方法),并标记了它们在观察期间是否运行过。

  3. 将数据交给AI代理。 Code Inventory数据天然适合AI编码代理。这类代理能很好地处理结构化输入数据,并且开箱即用能理解任何代码库。将Code Inventory报告加载到AI编码代理(如Claude Code)或自主代理(如Devin AI)中,提示它:

    • 根据你的仓库分析报告。
    • 识别哪些未使用类没有外部依赖(安全的移除候选)。
    • 为在别处被引用的未使用代码绘制依赖图。
    • 创建一个安全、可增量执行的移除计划。
  4. 处理单元测试问题。 移除遗留代码时最常见的担忧之一就是测试套件。移除一个未使用的类会破坏所有针对它编写的单元测试,进而破坏构建。

    Claude Code也可以处理这个问题,因为只验证已删除行为的测试不再有效。这些测试只是让死代码人为地活着。将代码和测试一起移除,既消除了构建失败,也真正缩短了构建时间——因为你不再需要验证本不需要存在的行为了。

  5. 迭代与规模化。 不必一次移除所有内容。先从最明显的候选开始:自包含的包、明显被开关的特性、你知道已废弃的旧API端点。分批移除少量代码,验证构建,在此过程中逐步建立构建信心。随着观察窗口扩大和对数据的信任增加,你可以处理更大的块。

清理未使用与死代码的商业理由

清理未使用与死代码带来的回报远不止优化AI性能:

  • 开发者入职。 每位新加入团队的开发者都需要在你的应用中建立心智模型。死代码污染了这个模型。干净的模块边界意味着更快的上手速度,更少花在考古式探索无人拥有或理解的代码上的时间。
  • PR审查速度。 更小、更干净的代码库意味着更短的diff、更集中的审查,以及更少关于某段代码是否仍然相关的讨论。
  • 构建和测试周期时间。 每次针对未使用代码运行的测试,都是在每次CI运行中浪费的时间。移除代码也移除了测试,永久性地缩短了反馈循环。
  • 未来的AI可扩展性。 使用AI时,你的团队会产生越来越多的代码。如果AI不需要在未使用和死代码的遗留混乱中挣扎,那么新引入的代码将更干净、更模块化。

实用考量

并非每一段未使用的代码都同样安全移除。团队在首次清理中会遇到同样的问题。以下是最常见的问题及处理方法。

  • 运行频率低的代码。 年终报告、财务结算流程和灾难恢复路径等用例需要更长的观察窗口才能显示为“已使用”。不要急于移除任何季节性代码。利用你的直觉,先处理你一直怀疑的代码区域。
  • 错误处理代码。 处理罕见故障模式的防御性代码值得保留,即使它在观察窗口内从未运行过。为其添加日志,以便将来知道它何时被触发,并在做出移除决定前延长观察期。
  • 误移除的代码。 代码在Git历史中。如果你需要恢复某个已移除的功能,一次git revert就能找回代码。移除的成本很低,而杂乱无章的持续成本很高。

更宏观的视角

最便宜的代码行就是不存在的代码行。你无需阅读它、维护它、测试它,也不必每次在提问时让AI在其中苦苦挣扎。

从遗留巨系统过渡到AI完成大部分工作的代码库,并不需要大爆炸式的重写。从移除不再需要的内容开始——基于生产实例实际运行了什么——并创建清晰的模块边界,让开发者和AI助手都能清晰地理解剩下的部分。

Azul Intelligence Cloud的Code Inventory功能提供了你所需的运行时证据,让你能自信地执行这一操作。将其与AI编码代理结合,你就拥有了一个能处理多年累积复杂性的工作流。

首先对你的应用程序进行检测,观察数据流入,然后让AI帮助你找到出路。

下一步

如果你想亲眼看看这篇博客的实际演示,请报名参加6月30日的AI4J领导力峰会。Azul Intelligence Cloud产品管理高级总监Erik Costlow将进行现场演示,展示如何结合使用Azul Code Inventory和AI编码工具,在Java应用中查找并删除未使用代码。

这篇文章《AI-Assisted Unused & Dead Code Removal》首发于foojay