1. 项目概述:当AI写代码成了架构的“慢性毒药”
最近在几个技术社区里,反复看到“百分百 AI 开发”这个词被高频提起——不是辅助、不是提效,而是从需求评审、接口设计、数据库建模,到核心业务逻辑、测试用例生成、甚至部署脚本,全程由大模型驱动完成。某次参与一个内部Demo评审时,一位年轻同事直接甩出一份300行Python服务+配套Dockerfile+Swagger文档的完整交付物,全程耗时22分钟,没写一行手敲代码。现场安静了三秒,然后有人问:“这服务能跑通,但……它有架构吗?”
这就是“架构腐败”这个词突然扎进我脑子里的瞬间。
它不是指代码崩溃、服务宕机这类急性故障,而是一种更隐蔽、更顽固的慢性退化:系统表面功能完整、单元测试覆盖率92%、CI/CD流水线绿得发亮,但当你想加一个字段、改一个状态流转、或者把订单模块抽出来复用到新业务线时,会发现——所有路径都像被水泥封死。你得重写半套逻辑,因为原生设计里根本没有“可扩展点”;你得绕开七层嵌套的DTO转换,因为当初连“领域对象”和“传输对象”的边界都没被人类思考过;你甚至找不到主键为什么是UUID+时间戳拼接的,只在某段AI生成的注释里写着“enhanced uniqueness”。
“百分百 AI 开发下的架构腐败”,说白了,就是当人类主动交出“设计权”而非“编码权”时,系统在功能性上快速膨胀,却在结构性上持续塌方。它不发生在编译失败那一刻,而发生在第3次需求变更、第5次跨团队对接、第12次性能压测之后——那时你才意识到,自己维护的不是一套软件,而是一座用乐高积木搭成的埃菲尔铁塔:远看精致,近摸全是胶水。
这篇文章不讲AI多厉害,也不教你怎么调prompt,而是以一个十年间亲手推倒重建过7套中台系统的资深架构师视角,拆解这种腐败是如何一步步发生的、它藏在哪些你以为“很合理”的AI输出细节里、以及——最关键的是——我们该如何在享受AI编码红利的同时,给架构留一条活路。适合正在用Copilot写CRUD的后端、靠Cursor重构微服务的Tech Lead、还有那些刚拿到AI编程工具就跃跃欲试的应届生。你不需要懂LLM原理,但得明白:代码是写给人看的,其次才是给机器执行的;而架构,从来只写给人看。
2. 架构腐败的底层机制:为什么AI天然缺乏“结构感”
2.1 AI的“局部最优”本能 vs 架构的“全局契约”本质
先说个反直觉的事实:当前所有主流编程大模型(无论闭源还是开源),其训练数据中真正体现“架构决策过程”的内容占比不足0.3%。它们见过海量的if-else、for循环、Spring Boot启动类、React组件树,但几乎没见过“为什么这里要用CQRS而不是直接查库”、“为什么订单状态机必须独立于支付服务”、“为什么这个DTO要拆成三个VO传给不同前端”这类带强烈上下文权衡的讨论记录。
模型学到的,是模式匹配,不是设计推理。
举个真实案例:某电商后台需要实现“优惠券核销防超领”。人类架构师会先画状态图:未发放→已发放→已核销→已过期,再定义每个状态的流转条件(如“已发放”只能被“用户点击领取”或“定时任务回收”触发),最后才落地到代码——比如用状态机框架+事件溯源,保证状态变更的原子性和可追溯性。
而AI接到同样需求,大概率输出这样的代码:
def redeem_coupon(user_id, coupon_id): coupon = db.query(Coupon).filter_by(id=coupon_id).first() if not coupon or coupon.status != 'issued': raise Exception("Invalid coupon status") if coupon.used_count >= coupon.total_count: raise Exception("Coupon exhausted") # ... update DB, send MQ ... return True这段代码完全正确,单测全过,甚至性能不错。但它埋下了三个腐蚀性种子:
- 状态判断硬编码在业务逻辑里:下次要支持“灰度发放”,就得改这个函数,而不是新增一个状态分支;
- 并发安全靠DB唯一索引兜底:没用乐观锁或分布式锁,高并发下可能超发——而AI根本不会告诉你“这里需要考虑库存扣减的并发控制”;
- 状态流转不可审计:没有记录“谁在什么时间把券从issued变成了used”,后续对账全靠猜。
提示:AI生成的代码,90%以上都遵循“最小实现路径”原则——用最短的代码链路满足当前输入的字面需求。而架构的本质,恰恰是主动增加冗余、预留接口、显式暴露约束,为未来不可知的需求留出呼吸空间。这是两种完全相反的设计哲学。
2.2 “上下文窗口”导致的视野失焦
当前主流模型的上下文窗口(Context Window)普遍在32K~128K token。听起来很大?但换算一下就很残酷:
- 一个中等复杂度的微服务,含Controller/Service/DAO/DTO/Config共约15个文件,平均每个文件300行,每行按15 token算,总token数≈67,500;
- 再加上数据库ER图、API文档、上下游依赖说明……人类能轻松掌握的系统全景,在AI眼里只是“一屏截不完的碎片”。
结果就是:AI永远在“当前文件”里打转。它给你生成一个完美的OrderService,但不会提醒你:“注意,PaymentService里有个同名方法processRefund(),参数结构不一致,建议统一为RefundRequestDTO”。
我做过一个实验:把同一份需求文档(含5个服务的交互时序图)分三次喂给模型,每次只给其中1个服务的上下文,然后让AI分别生成各服务代码。结果:
OrderService生成了createOrder()返回OrderVO;InventoryService生成了deductStock()返回StockResult(含success: bool字段);LogisticsService生成了assignCourier()返回CourierAssignment(含status: str字段);
三个服务返回的数据结构完全不同,字段命名风格割裂,错误码体系互不兼容。而当把全部上下文一次性喂入时,AI确实能生成统一的ResponseWrapper<T>,但代价是:它花了47秒生成,且中间因token超限自动丢弃了2个配置文件的细节,导致Redis缓存策略写错了TTL。
注意:AI的“全局观”是虚假的。它没有记忆,没有立场,没有责任——它只对当前输入负责。而架构师的核心价值,恰恰在于用有限的认知带宽,在混沌中强行建立一致性契约。
2.3 “奖励函数”缺失:没人教AI什么是“好架构”
所有监督微调(SFT)和强化学习(RLHF)阶段,模型的奖励信号都来自:代码是否能通过编译?单元测试是否pass?是否与参考答案相似?有没有语法错误?
但没有任何一个训练样本告诉它:
- “这个Service类超过800行,应该按功能拆分”;
- “这个Mapper XML里写了5个嵌套
<if>,建议提取为动态SQL”; - “这个DTO同时包含用户信息和订单明细,违反单一职责,建议拆成UserSummaryDTO和OrderDetailDTO”。
换句话说:AI被训练成“合格的程序员”,但没人训练它成为“合格的架构师”。它知道怎么写“能跑”的代码,但不知道什么叫“好维护”的代码;它能模仿出Spring Cloud Alibaba的配置格式,但不理解为什么Nacos注册中心要和Sentinel熔断器做集群隔离。
这就解释了为什么“百分百AI开发”项目上线初期异常丝滑,半年后却陷入泥潭——因为所有“坏味道”都是温水煮青蛙式的:单个函数没问题,单个类没问题,单个模块也没问题;但当100个“没问题”的模块拼在一起,就成了无法拆解的意大利面条。
3. 腐败高发区:那些AI最爱“优化”却最伤架构的环节
3.1 接口设计:从RESTful到“自洽但封闭”的黑洞
AI对接口设计有种迷之自信。给它一句“用户登录返回token和基本信息”,它能立刻生成:
{ "code": 0, "msg": "success", "data": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "userInfo": { "id": 123, "name": "张三", "avatar": "https://xxx/avatar.jpg", "roles": ["user", "vip"] } } }看起来很完美?问题出在userInfo这个字段上。
人类设计时会问:这个userInfo是给Web端用的,还是App端也用?App端是否需要精简字段(比如不要roles)?如果下周要接入小程序,是否要加openId?如果要做国际化,name要不要变成nameZh/nameEn?
AI不会问。它只根据当前需求“自洽地”组织数据。结果就是:所有下游系统都强依赖这个固定结构。当App端提出“只要id和头像”,后端只能加个?fields=id,avatar参数,然后在代码里写一堆if fields.contains("name")——而这个参数本身,又成了新的技术债。
更致命的是错误码设计。AI生成的code: 0表示成功,code: 1001表示密码错误,code: 1002表示账号不存在……看似合理。但当支付模块也接入同一套网关时,它的1001可能代表“余额不足”。两个1001在同一个响应体里打架,前端同学只能靠msg字符串做判断——而msg随时可能被产品经理要求改成更“友好”的文案。
实操心得:我在三个项目里强制推行过“接口契约先行”流程:所有API必须先用OpenAPI 3.0规范写清楚
components.schemas,明确每个字段的来源(DB字段?计算字段?第三方透传?)、是否必填、枚举值范围、变更影响范围。AI只允许在契约确定后生成实现代码。结果是:接口变更成本下降60%,前端联调时间从平均3天压缩到4小时。
3.2 数据库建模:从范式理论到“能存就行”的混沌
给AI一个需求:“用户可以收藏多个商品,每个商品可被多个用户收藏”,它大概率会生成一张user_favorite表,含user_id、product_id、created_at三列,并加个联合唯一索引。
这没错。但人类会继续思考:
- 收藏是否需要排序?(加
sort_order字段) - 是否要记录收藏来源?(比如从搜索页收藏 vs 从详情页收藏,用于行为分析)
- 用户取消收藏后,这条记录是物理删除还是逻辑标记?(影响历史数据分析)
AI不会。它只解决“如何建立多对多关系”这个数学问题,不解决“如何支撑业务演进”这个现实问题。
更典型的是“枚举字段滥用”。需求里写“订单状态有:待支付、已支付、已发货、已完成、已取消”,AI会毫不犹豫地建一个order_status VARCHAR(20)字段,然后在代码里写满if status == "paid"。它不会想到:
- 这个字段未来要加“部分发货”、“风控冻结”等状态,字符串比较会越来越脆弱;
- 不同服务对同一状态的理解可能不同(比如“已支付”在支付服务里代表资金到账,在订单服务里仅代表支付请求发起);
- 没有状态流转图,导致后续排查“为什么订单卡在已支付不动”时,要翻遍所有服务日志。
我见过最离谱的案例:一个AI生成的医疗预约系统,把“医生职称”直接存在doctor.title VARCHAR(50)里,结果当政策要求区分“主任医师(正高)”和“主任医师(副高)”时,整个系统要重构——因为所有报表、权限校验、搜索过滤都基于这个字符串字段。
注意:数据库不是代码的附属品,它是业务规则的最终落地方。AI可以帮你生成DDL,但决定“这张表该不该存在”、“这个字段该不该加索引”、“这个外键要不要级联删除”的,永远只能是人。
3.3 分层治理:从清晰边界到“万物皆Service”的粘稠沼泽
AI对分层架构有种朴素的崇拜。看到“Controller-Service-DAO”三层,它会严格照搬。但问题在于:它分得“太干净”,干净到失去了业务语义。
比如一个“创建订单”操作,AI生成的代码可能是:
// OrderController.java @PostMapping("/orders") public Response<OrderVO> create(@RequestBody OrderRequest req) { return orderService.create(req); } // OrderService.java @Transactional public OrderVO create(OrderRequest req) { // 校验库存 → 调用InventoryService // 扣减积分 → 调用PointsService // 创建订单 → 调用OrderDAO // 发送MQ → 调用MessageService return convertToVO(order); }看起来很标准?但所有业务逻辑都挤在OrderService.create()里。当你要加“风控拦截”时,是往这个方法里塞riskService.check()?还是新建一个RiskOrderService?AI不会告诉你哪种更合理。
更危险的是“Service泛滥症”。AI特别喜欢把每个小功能都包装成Service:UserService、UserCacheService、UserRedisService、UserFallbackService……名字不同,但干的都是同一件事:查用户。结果就是:
- 新人看不懂该用哪个Service;
- 修改一个用户查询逻辑,要改5个地方;
- 某个Service里偷偷加了缓存穿透防护,其他Service没有,导致线上雪崩。
真正的分层,不是按技术名词切,而是按业务能力域切。UserDomainService负责用户核心状态管理(注册、登录、资料变更),UserQueryService负责所有读场景(列表、详情、搜索),UserIntegrationService负责与外部系统交互(短信、邮箱、第三方登录)。AI不懂“域”,它只懂“类”。
实操心得:我在团队推行“三层红线”:Controller层只做参数校验和VO转换;Service层必须对应明确的业务用例(如
PlaceOrderUseCase),且一个Service类只处理一个用例;DAO层禁止出现任何业务逻辑。AI生成的代码,必须经过这三条红线的“手术刀式”修剪,否则不准合入主干。
4. 防腐实战:构建人机协同的架构守门人机制
4.1 架构决策记录(ADR):把“为什么”刻进系统DNA
架构腐败最隐蔽的起点,是那些没被记录下来的决策。AI生成代码时,永远不会写:“这里用Redis而不是本地缓存,是因为QPS预期超5000,且需要集群一致性”。
所以,我们强制引入架构决策记录(Architecture Decision Record, ADR),作为代码提交的前置条件。
每份ADR是一个Markdown文件,固定包含:
| 字段 | 说明 | AI能否生成 |
|---|---|---|
| 标题 | 如“采用CQRS模式处理订单查询” | ✅(但常起得模糊,如“优化查询性能”) |
| 状态 | Proposed / Accepted / Deprecated | ❌(需人工确认) |
| 上下文 | 当前痛点、数据指标、约束条件(如“日均订单查询120万次,P99延迟需<200ms”) | ⚠️(AI能罗列,但常遗漏关键约束) |
| 决策 | 明确选择的技术方案及理由 | ⚠️(AI会写“因为CQRS快”,但说不出具体快在哪) |
| 后果 | 正面收益 + 负面代价(如“提升查询性能,但增加写链路复杂度”) | ❌(AI几乎从不提代价) |
| 相关代码 | 关联的类、配置文件、SQL脚本路径 | ✅(但常不准确) |
关键不是让AI写ADR,而是用ADR来约束AI。例如,当AI生成一个新Service时,CI流水线会自动检查:是否存在对应ADR?ADR里是否明确了该Service的职责边界?如果没有,提交直接被拒绝。
我们统计过:实施ADR机制后,新模块的架构返工率从38%降到7%,因为所有“拍脑袋”的设计,都在代码诞生前被逼着写清楚了。
4.2 领域建模工作坊:用白板代替Prompt
很多人以为AI编程就是“描述需求→生成代码”,其实最大的浪费发生在需求翻译阶段。
我带过一个团队,用AI开发一个物流轨迹系统。第一次,产品给的PRD是:“用户能看到包裹实时位置,支持查看历史轨迹”。AI生成了一堆高德地图SDK调用代码,结果上线后发现:用户真正需要的是“预计送达时间”,而不是经纬度坐标;运营真正需要的是“异常停留点识别”,而不是轨迹线渲染。
后来我们改了流程:所有需求必须先经过领域建模工作坊,产出三样东西:
- 统一语言词典(Ubiquitous Language):明确“包裹”、“运单”、“路由节点”、“异常事件”的定义,标注每个词在业务中的含义(如“异常事件”特指“停留超2小时且无更新”);
- 核心领域模型图:用PlantUML画出实体、值对象、聚合根、领域服务的关系(AI可以辅助画图,但不能主导建模);
- 关键用例场景卡:如“运单创建”、“节点上报”、“异常预警”,每张卡写明触发条件、主成功路径、备选路径、失败处理。
这三样东西,才是喂给AI的“正确Prompt”。AI不再面对模糊的自然语言,而是处理结构化的领域知识。生成的代码,自然就带着业务语义。
提示:别让AI替你思考“业务是什么”,让它帮你实现“你已想清楚的业务”。这是人机协同的黄金分界线。
4.3 架构健康度扫描:给系统装上“CT机”
光靠人工Review,永远追不上AI生成代码的速度。我们自研了一套轻量级架构健康度扫描工具(开源版叫ArchGuard),它不检查代码风格,专盯架构腐败信号:
- 圈复杂度 > 15 的方法:标红,强制要求拆分;
- Service类中调用超过3个外部服务:预警,提示“可能违反单一职责”;
- DTO中包含超过5个来自不同聚合根的字段:警告,“疑似贫血模型”;
- Controller层出现SQL语句或Redis命令:直接阻断;
- 同一业务概念在不同模块使用不同命名(如
userId/user_id/uid):生成重构建议。
工具本身不阻止提交,但会在MR页面醒目位置展示健康分(0-100)。团队约定:健康分<85的MR,必须由Tech Lead二次确认。三个月下来,平均健康分从62升到89,最典型的改进是:DTO字段重复率下降73%,跨服务调用深度从平均4层降到2.1层。
这套机制的核心思想是:把架构原则转化为可测量、可追踪、可告警的数字指标。AI可以写代码,但只有人才能定义“什么是好架构”。
5. 常见问题与防腐心法:来自一线战场的血泪总结
5.1 “AI生成的代码质量很高,为什么还要人工干预?”
这是最常被问的问题。我的回答是:代码质量 ≠ 架构质量。
就像装修房子:AI能帮你快速贴完所有瓷砖(代码功能完整),但决定“承重墙能不能砸”、“水电管线怎么走才符合消防规范”、“厨房动线是否符合人体工学”的,永远是设计师(架构师)。
我们做过对比实验:两个团队开发同一套CRM系统,A队100%AI生成,B队AI生成后经架构师按前述机制审查。结果:
- A队交付快3.2天,但上线后第17天开始出现性能抖动,第42天因无法扩展新客户标签字段,被迫停服重构;
- B队交付慢1.8天,但稳定运行14个月,期间完成7次重大功能迭代,平均每次迭代耗时仅2.3天。
时间维度拉长看,前期省下的1.8天,后期要花42天来还债。这不是效率问题,是工程债务的复利效应。
5.2 “我们团队没有专职架构师,怎么办?”
没有架构师,不等于不需要架构。我给中小团队的建议是:把架构职责‘下沉’为每个开发者的必修动作。
具体做法:
- 每次需求评审会,强制增加5分钟“架构预演”:由开发同学用白板画出核心流程图,说明“这个改动会影响哪些现有模块”、“哪些地方需要加监控埋点”、“如果失败,降级方案是什么”;
- Code Review Checklist里,加入3条架构红线:① 是否有明确的领域边界?② 是否有可预见的扩展点?③ 是否有隐藏的强耦合?(AI生成的代码,这三条命中率不足12%);
- 每月一次“架构债清理日”:所有人暂停新需求,专门修复健康分低于80的模块,修复过程必须同步更新ADR。
我们辅导过一家12人的创业公司,实施这套机制后,6个月内系统可用性从99.2%提升到99.95%,技术面试通过率(考察架构思维)从31%升至79%。
5.3 “AI会不会进化到能做架构决策?”
会,但不是现在,而且永远需要人类锚定。
未来的AI或许能基于百万级系统日志,预测“这个微服务拆分后,P99延迟将下降17%,但运维成本上升23%”。但它依然无法回答:
- “为了这17%的延迟下降,值得让运维团队多承担23%的成本吗?”(商业权衡)
- “如果竞品下周发布类似功能,我们是该优先保稳定性,还是赌一把激进优化?”(战略判断)
- “这个决策,会让初级工程师更难理解系统,还是更易上手?”(团队成长)
这些,是AI永远无法替代的“人”的价值。架构师不是挡在AI前面的绊脚石,而是站在AI肩上的瞭望者——帮它看清远方的山,而不是替它走路。
最后分享一个小技巧:每次让AI生成代码后,花2分钟做“反向提问”——对着生成的代码,问自己三个问题:① 如果这个功能明天就要下线,删哪几行最痛?② 如果这个模块要卖给客户当独立产品,哪些代码必须重写?③ 如果让我教实习生维护这个模块,第一课该讲什么?答案指向的地方,就是架构腐败的萌芽点。守住这三个问题,你就守住了架构的生命线。