“把这段逻辑抽成helper。”“这里加个缓存。”“给那个功能建个service。”“把这个模块拆一下。”“再加一层抽象。”“引入一个新依赖。”
AI编程助手做这些决定时,几乎不会出错。每一步都挑不出毛病,每一个pull request都能通过,每一行diff看起来都干净利落。
问题恰恰出在这里。
一个系统可以在每个局部决策都正确的情况下,整体变得越来越糟。这不是AI的问题,是一个架构问题——只是AI让它跑得更快了。
危险的地方在于:没有任何一处看起来明显是错的
把时间拉长到几周,事情是这样的:
- 任务1 → 加校验
- 任务2 → 抽helper
- 任务3 → 加缓存
- 任务4 → 加service
- 任务5 → 加重试逻辑
- 任务6 → 重构模块
- 任务7 → 再接一个集成
每一次改动都通过了。每一处看起来都很干净。每一个测试都是绿的。
但六周之后:
- 两个service同时持有同一条业务规则
- 校验逻辑出现在三个地方
- 缓存策略前后不一致
- 依赖关系变成双向的
- 没人说得清哪一层负责什么
- 一个“简单”的改动要碰八个文件
没有任何一次提交摧毁了架构。系统是自己漂过去的。
局部正确不等于系统正确
这是整件事的核心。
AI agent通常只处理眼前这个任务。你说“加上重试支持”,它找到最合理的那个位置把重试加进去。你说“复用这段逻辑”,它抽出一个helper。你说“把这个模块清理一下”,它引入一层新的抽象。
每一个回答,对那个prompt来说都可能是对的。
但架构不是一堆局部正确选择的集合。架构关心的是这些选择在时间维度上如何相互作用。
开发者和agent承担的责任不一样。agent问的是:我怎么把这个任务成功完成?开发者应该问的是:这个改动对整个系统做了什么?
这不是同一个问题。
agent可能优化的方向是:更少的代码行、更清晰的分隔、更少的重复、更快的实现、更容易复用。
而系统真正需要的可能是:更强的边界、更少的抽象、一个明确的归属方、有意的重复、更少的耦合、更简单的依赖。
一个局部优雅的改动,放到全局依然可能是有害的。
那个“乐于助人”的helper
假设你有一个 calculateInvoiceTotal(),一开始只有一个模块在用。
后来另一个功能需要类似的行为。AI建议:把它移到 shared/utils 里。听起来很合理。
再后来,又一个模块import了它。再后来,有人往里加了客户特定的逻辑。然后是税务规则。然后是折扣行为。
现在你这个“共享helper”里装的是贯穿整个系统的业务逻辑。
当时没有任何一步看起来不合理。但架构已经悄悄变了。它从“发票拥有发票逻辑”,变成了“所有人都依赖 shared/utils”。
这就是架构漂移。
AI让架构漂移变得更快
在AI之前,引入五个新抽象是要花力气的。现在几分钟就能发生。
这改变了成本结构。开发者生成新service、新接口、新层级、新适配器、新helper的速度,已经超过了他们评估这些东西该不该存在的速度。
风险不在于AI生成明显糟糕的架构。更有意思的风险是:它生成看似合理的架构,速度快过团队维持一致设计的能力。
测试全绿,保护不了架构
这一点很重要。
你的测试套件可能会说:一切正常。但这不等于:系统在变好。
测试能抓住的是:行为被破坏、回归、无效输出。
测试通常抓不住的是:归属不清、不必要的抽象、依赖蔓延、重复的业务规则、架构漂移。
你可以做到100%测试通过,同时代码库每周都变得更难改。
代码评审同样可能漏掉它。评审者看到的是一个pull request,也许80行,diff看起来合理。但架构问题往往要跨越很多次改动才显现出来。
PR 1加个helper。PR 2加个service。PR 3再加个依赖。PR 4复用那个helper。PR 5绕过那个service。
单独看:没问题。合起来看:一团乱麻。
这就是为什么只看当前diff的评审并不总是够用。
评审AI代码时,多问一个问题
在评审AI生成的代码时,问一句:如果我们把这个模式重复20次,系统会变成什么样?
这个问题出奇地有用。
如果每个功能都要加一个service、一份config、一个queue、一个共享helper,那么问题可能不在某一次改动,而在这个模式本身。
架构往往取决于一个决策被重复之后会发生什么。
减少漂移最简单的办法之一,是把归属关系写清楚。比如:支付拥有支付规则,订单拥有订单生命周期,用户拥有身份,通知只负责发消息。
然后告诉agent:除非明确要求,不要把业务规则搬过这些边界。
这比让模型每次都从零判断架构归属要好得多。
给AI约束,而不只是目标
差的prompt是:“重构一下,让它更干净。”
更好的写法是:重构这个模块。约束条件——保持现有架构边界、不引入新依赖、不创建新的共享抽象、业务逻辑留在现有领域内、实现前先提方案。
目的不是让AI变得没用,而是阻止每一个任务都变成一次小型架构重设计。
在动手写代码之前,可以先要求它回答:这个改动触及哪条架构边界?哪些模块会依赖这个改动?是否引入了新的抽象?是否产生了新的依赖?是否已经存在类似逻辑?它对未来的改动有什么影响?
这会把讨论逼到语法之上的一层。
还有一个值得盯的信号:依赖方向。健康的系统通常有清晰的关系,比如 Controller 指向 Service,Service 指向 Repository。当AI开始引入反向依赖时,架构的裂缝就已经出现了。
热门跟贴