ChatGPT、Codex架构机制:Agent每次只改一个小功能,为什么项目依赖关系却越来越乱?
一个开发团队最近开始全面使用Codex。
刚开始,大家对效果都比较满意。
开发人员只需要描述需求,Agent就能读取项目代码,找到对应模块,完成接口修改、业务逻辑调整和测试补充。
团队还专门规定,每次任务尽量控制修改范围,不要一次性重构整个项目。
从代码审查结果看,这个策略确实有效。
大部分PR只修改几个文件,测试能够通过,功能也符合预期。开发人员甚至觉得,过去需要跨多个模块才能完成的需求,现在交给Agent很快就能处理。
但几个月之后,技术负责人发现了一些不太正常的变化。
原本相互独立的业务模块,开始出现越来越多的交叉引用。
订单模块需要引用用户模块的内部DTO,用户模块又开始调用订单模块的公共方法。几个公共工具包不断增加业务逻辑,逐渐变成什么功能都能往里面放的地方。
更麻烦的是,修改一个看起来很简单的功能,经常需要同时调整多个模块。
以前只需要修改一个Service,现在可能需要调整DTO、公共工具类、业务接口和多个模块的实现。
每次修改单独看都没有明显问题,单元测试也基本通过,但整个项目却越来越难维护。
负责人开始怀疑:是不是Codex生成的代码质量不够好?
但检查之后发现,很多代码本身并没有明显错误。
真正的问题是,每次修改都在解决眼前的需求,却没有持续维护整个系统的架构边界。
这就是AI编程时代一个值得重视的变化:代码生产成本下降之后,架构漂移可能以更快的速度积累。
一、为什么每次只改一个小功能,最后却会改变整个架构?
传统开发中,开发人员修改代码时,通常需要自己查找模块、理解调用关系,再决定应该把功能放在哪里。
这个过程虽然比较慢,却有一个特点:开发人员往往能够积累整个项目的结构认知。
例如,一个订单功能应该属于订单模块,用户信息应该通过用户模块提供的接口获取。
如果需要跨模块调用,就必须考虑依赖方向和职责边界。
但Agent的工作方式有所不同。
它通常从当前任务出发,读取相关文件,寻找已有实现,然后选择一条能够完成任务的修改路径。
例如,订单模块需要读取用户等级。
Agent发现用户模块已经有一个查询方法,于是直接引入用户模块的内部DTO。
这次修改可能完全正确。
过几天,用户模块需要展示最近订单,Agent发现订单模块有一个方便调用的Service,于是又增加了一条反向依赖。
两次修改都能正常运行。
但原本应该保持单向依赖的两个模块,现在已经开始互相引用。
问题就在这里。
架构漂移通常不是一次错误的重构造成的,而是大量局部合理的修改逐渐改变了系统原有的依赖关系。
每次修改只增加一点复杂度,但这些变化叠加之后,就可能形成新的架构问题。
二、第一种机制:Agent优化的是当前任务,不一定是整个系统
这是理解架构漂移最重要的一点。
假设一个项目有三个模块:
-
Order:订单业务。
-
User:用户业务。
-
Payment:支付业务。
正常情况下,三个模块应该围绕自己的业务职责组织代码。
订单模块需要用户信息,可以通过用户模块提供的接口获取。
支付模块需要订单数据,也应该通过明确的业务契约进行交互。
但当Agent接到一个具体任务时,它首先考虑的是怎样快速完成当前功能。
例如,订单页面需要显示用户等级。
如果User模块内部已经有一个DTO,Agent可能直接引用它。
从当前任务看,这样做很方便。
不需要重新定义对象,也不需要增加额外的转换逻辑。
但从架构角度看,订单模块已经开始依赖用户模块的内部实现。
以后用户模块修改DTO结构,订单模块也可能被迫调整。
这就是局部优化和全局架构之间的冲突。
Agent选择的方案可能减少了当前任务的开发成本,却增加了未来修改的成本。
如果这样的决策持续发生,项目会逐渐形成越来越多的隐性依赖。
Agent可以把一个功能实现得很快,但它不会天然知道哪种实现方式最有利于系统未来几年的维护。
三、第二种机制:公共模块逐渐变成依赖垃圾场
还有一个非常典型的现象:公共模块越来越大。
项目最初可能只有一个common模块,里面放一些真正通用的工具:
日期处理、字符串转换、基础异常、公共常量等。
随着Agent不断参与开发,这个模块开始出现更多内容。
订单状态转换放进去了,用户权限判断放进去了,支付金额计算也放进去了。
原因并不复杂。
当Agent发现某个方法可以被多个模块使用时,很容易倾向于把它抽取到公共位置。
从代码复用角度看,这似乎是合理的。
但问题在于:
能够被多个模块调用,不代表它就应该成为公共能力。
例如,订单状态转换和支付状态转换可能都存在状态判断。
但两者的业务含义、生命周期和约束可能完全不同。
如果为了减少重复代码,把它们强行放进一个公共工具类,后续修改就会让不同业务模块受到影响。
更严重的是,common模块可能开始反向依赖订单、用户和支付模块。
原本应该被所有模块依赖的基础组件,反而开始了解具体业务。
这时候公共模块就不再是稳定的基础层,而变成了整个项目的依赖集中地。
四、第三种机制:接口看起来解耦,实际依赖仍然存在
很多团队认为,只要使用接口,就能够避免模块之间的直接依赖。
但接口并不一定能够真正隔离模块。
例如,订单模块需要调用用户模块。
开发人员抽取了一个UserService接口,看起来已经完成了解耦。
但接口返回的仍然是User模块内部的实体对象。
订单模块还需要依赖用户模块的枚举、异常类型和数据结构。
从代码表面看,调用经过了接口。
但从实际依赖关系看,订单模块仍然了解用户模块的内部实现。
这种情况可以称为抽象边界泄漏。
真正有效的模块隔离,不只是增加一个接口,而是控制模块之间能够交换什么信息。
例如:
订单模块需要的只是用户ID、用户等级和必要的业务状态。
它不一定需要知道用户模块内部使用什么Entity、Repository或者数据库表结构。
如果跨模块接口直接暴露内部实体,未来内部实现调整时,其他模块就可能被迫跟着修改。
因此,模块边界应该由稳定的业务契约定义,而不是由接口数量决定。
五、为什么单元测试全部通过,也发现不了架构漂移?
这是很多团队最容易产生误判的地方。
Agent修改完代码后,能够自动补充单元测试。
CI也能正常运行。
所有测试通过,似乎说明这次修改没有问题。
但这里存在一个重要区别:
单元测试主要验证代码行为,架构测试验证模块之间是否遵守预定的结构约束。
例如,订单模块引用了用户模块的内部DTO。
只要测试数据和调用结果正确,单元测试完全可能通过。
但这并不代表这种依赖符合项目架构设计。
再比如,两个模块形成了循环依赖。
如果项目仍然允许这种依赖存在,部分功能可能照样能够正常运行。
但随着业务继续发展,循环依赖会增加模块拆分、独立测试和后续重构的难度。
这说明测试覆盖率和架构健康度不是同一个指标。
一个项目可以拥有很高的单元测试覆盖率,同时仍然存在严重的架构耦合。
因此,AI编程时代需要增加另一类验证:
不仅要验证Agent修改的代码能不能正常运行,还要验证它有没有破坏系统原来的依赖边界。
六、为什么Agent越多,架构漂移可能越明显?
如果只有一个Agent,团队至少还能通过人工审查控制部分架构变化。
但当多个Agent同时参与开发,问题可能进一步放大。
假设:
Agent A负责订单功能。
Agent B负责用户功能。
Agent C负责支付功能。
三个Agent分别处理不同任务,都能完成自己的功能测试。
但它们可能对同一个公共模块作出不同判断。
Agent A希望把订单查询方法放进common。
Agent B希望把用户状态转换也放进common。
Agent C又希望在公共模块中增加支付状态处理。
每个修改都有自己的理由。
但三个Agent之间不一定能够共享完整的架构决策过程。
结果就是,公共模块的职责不断扩大,模块之间的依赖逐渐增加。
更麻烦的是,Agent可能把项目中已经存在的错误依赖当成合理范例。
例如,历史代码已经存在订单模块直接调用用户Repository的情况。
新的Agent读取到这段代码后,可能认为这是项目惯用写法,于是在新功能中继续复制。
这会形成一种累积效应:
旧代码中的架构问题成为新代码的参考,新代码又进一步扩大原来的问题。
所以,多Agent协作不能只解决任务分配和代码冲突,还需要共享一致的架构约束。
并行开发提高的是局部任务完成速度,但架构一致性仍然需要统一管理。
七、真正的问题:项目缺少持续执行的架构约束
如果团队只依靠开发规范和AGENTS.md约束Agent,仍然存在一个问题。
文字规则需要Agent理解和执行。
即使在提示词中写明:
-
不允许跨模块直接访问Repository。
-
不允许形成循环依赖。
-
公共模块不能依赖业务模块。
-
业务模块之间通过明确接口交互。
Agent也可能因为上下文不足、历史代码冲突或任务压力而选择其他实现。
所以,架构规则不能只停留在文档里。
更可靠的方式是把部分架构约束转化为可以自动执行的检查。
例如,使用ArchUnit检查Java包之间的依赖关系。
可以规定订单模块不能直接访问用户模块的Repository:
@ArchTest
static final ArchRule orderShouldNotAccessUserRepository =
noClasses()
.that().resideInAPackage("..order..")
.should().dependOnClassesThat()
.resideInAPackage("..user.repository..");
这段规则表达的是一个明确约束:
订单模块不能直接依赖用户模块的Repository。
如果Agent修改代码时违反了规则,架构测试就可以在CI阶段发现问题。
当然,实际项目需要根据包结构和模块设计调整规则。
ArchUnit也不是完整的架构治理方案。
它更适合把已经确定的架构边界转化成自动化检查,而不是替团队决定业务模块应该怎样划分。
真正重要的是:
先确定架构规则,再让工具持续检查,而不是等到模块已经混乱后再依靠人工重构。
八、怎样让Codex修改代码之前,先理解项目架构?
我更建议团队给Agent增加一个明确的前置分析过程。
以前的任务可能是:
读取订单模块,修改订单查询功能,补充测试。
现在可以调整为:
首先识别当前功能属于哪个业务模块,分析它需要调用哪些模块,以及现有依赖方向是否符合架构约束。
然后再决定修改位置。
这个过程可以拆成四个动作。
第一步:读取项目架构地图。
让Agent先了解模块职责、模块之间允许的依赖方向,以及公共组件的使用边界。
不要只提供当前文件,还要提供必要的模块关系信息。
第二步:检查已有依赖路径。
如果订单模块需要用户信息,先检查项目是否已经提供稳定的用户查询接口。
不要因为某个Repository容易访问,就直接使用内部实现。
第三步:先输出修改方案。
让Agent说明:
-
需要修改哪些模块。
-
是否新增跨模块依赖。
-
是否改变现有接口。
-
是否涉及公共组件。
-
是否可能形成循环依赖。
对于高风险修改,先让开发人员确认方案,再开始编码。
第四步:修改后重新检查依赖关系。
除了运行单元测试,还要运行架构测试,并比较修改前后的依赖图。
如果新增了跨模块依赖,就必须说明为什么需要它。
这样做的意义不是让Agent每次写代码都经过复杂审批,而是把架构判断放到代码修改之前。
九、架构漂移不能只靠提示词,还需要CI门禁
除了让Agent提前理解架构,团队还应该把架构检查纳入正常开发流程。
例如:
普通单元测试负责检查业务行为。
集成测试负责检查模块协作。
ArchUnit负责检查包和类之间的依赖规则。
构建工具负责检查模块依赖关系和循环引用。
对于大型项目,还可以通过依赖分析工具生成模块依赖图,比较每次变更前后的结构差异。
这里需要注意一个问题:
并不是所有新增依赖都不合理。
例如,业务发展确实需要引入新的基础能力,或者原有模块需要拆分,这些都可能带来合理的架构变化。
因此,架构门禁不应该简单地禁止所有依赖增加。
更好的方式是:
对于明确禁止的依赖,直接阻止合并。
对于需要新增的依赖,要求提供变更理由和架构评审。
对于历史遗留问题,可以逐步建立基线,避免每次CI都因为旧问题失败。
这样才能让架构约束真正融入日常开发,而不是变成一套无法执行的规范。
十、用一个指标判断架构是否正在漂移
这篇只突出一个核心指标:
每次迭代新增的跨模块依赖边数。
这里统计的不是代码中新增了多少import,而是模块之间新增了多少条实际依赖关系。
例如,原本订单模块只依赖用户模块提供的公开接口。
某次迭代之后,订单模块又开始依赖用户模块的Repository。
从模块依赖图来看,就出现了新的依赖边。
如果连续几个迭代都在增加类似关系,说明模块边界可能正在逐渐扩大。
团队可以通过静态依赖分析、构建依赖图和架构规则检查,记录每次迭代新增的跨模块依赖。
需要注意,依赖数量本身并不能直接说明架构一定变差。
合理的业务演进也可能增加依赖。
因此,重点是观察新增依赖是否符合原有架构方向,以及是否出现未经设计的反向依赖和循环依赖。
真正值得警惕的不是依赖数量增加,而是系统开始依赖那些本来不应该依赖的模块。
十一、Plus和Pro怎么判断?
如果主要使用ChatGPT、Codex分析单个Java项目的模块边界、检查依赖关系、编写ArchUnit规则和辅助局部重构,Plus通常够用。
如果需要同时分析多个大型仓库、复杂模块依赖图、跨团队架构约束,并持续进行多轮架构治理和代码改造,Pro更适合高频、长任务的工程工作流。
但套餐升级不会自动让Agent理解整个系统的架构,也不能代替团队制定模块边界和架构规则。
真正重要的是,是否建立了能够持续约束代码变化的工程机制。
最后
Agent每次只改一个小功能,为什么项目依赖关系却越来越乱?
因为每次修改可能只考虑当前任务是否完成,却没有充分考虑新依赖对整个系统的长期影响。
公共模块不断膨胀、业务模块互相引用、接口暴露内部实现,以及多个Agent复制历史代码中的错误依赖,都可能让架构在一次次局部修改中逐渐漂移。
真正可靠的工程路径是:
明确模块职责 → 建立依赖边界 → 修改前分析影响 → 通过架构测试约束 → CI持续检查 → 观察新增跨模块依赖。
ChatGPT、Codex提高了代码生产效率,但系统架构不会因为代码生成得更快就自动变得更合理。
未来AI编程真正需要解决的,不只是让Agent能够正确修改一个方法,而是让它在持续修改整个项目时,始终遵守系统的结构约束。
代码可以由Agent快速生成,但架构边界必须由团队明确设计,并通过工程机制持续守住。
持续分享 Codex、大模型开发与 AI 编程实战内容
也整理了稳定的 Plus/Pro 会员订阅渠道,有需要可自取。
更多推荐

所有评论(0)