简介:AI参与编写的代码占比已接近半数,但语法规整并不等于工程质量过关。本文剖析AI生成代码制造模块边界混乱、隐式依赖、异常被吞等深层技术债的机制,完整还原一个订单状态机从“测试全绿”到线上资损的并发缺陷案例,并给出评审、测试、架构约束、意图留痕四层治理方案,帮助团队在效率与可维护性之间取得平衡。
“代码大部分是AI生成的,以后会变成没人看得懂的屎山吗?”这是近几年技术团队复盘会上出现频率越来越高的问题。直接给出结论:会,而且它正在发生;但这种“新时代屎山”和我们过去十年熟悉的屎山长得完全不一样——表面更干净,内核更难拆,排查成本甚至可能比纯手写的旧代码高出数倍。下文先校准对行业现状的认知,再拆解AI制造结构性技术债的具体机制,最后给出一套可落地的治理方案。
一、先校准认知:AI编码已从“尝鲜”变成“默认配置”
讨论这个问题的前提,是承认行业已经跨过了“要不要用”的阶段。几个可交叉印证的信号:某头部科技公司近期在公开业绩交流中披露,其内部约一半新代码由智能体生成、工程师负责审查;主流代码托管平台的开发者生态报告显示,大量新入行开发者注册第一周就在使用智能补全;多家代码质量与研发效能机构的独立调研也指向同一结论——全行业AI参与编写的代码占比即便尚未绝对过半,也已非常接近一半。与此同时,包括百度智能云在内的一些云厂商,也已将智能代码补全、生成式编程辅助、流水线质量门禁等能力作为通用产品类目集成进研发平台,使这类能力成为团队的“默认配置”而非“特殊工具”。
这意味着讨论焦点必须转移:不再纠结采用率,而是认真回答“如何为这种开发模式配套治理体系”。否则生成速度每提升一倍,技术债的堆积速度就同步放大一倍。
二、纠偏:屎山的本质从来不是“长得难看”
很多人对屎山的理解非常表层:缩进混乱、变量名叫a1/b2、单个函数上万行。按这个标准,AI生成的代码简直像“艺术品”:语法规整、缩进完美、命名规范,甚至贴心地配上完整注释,单看一个文件挑不出任何毛病,甚至赏心悦目。
但工程史上真正的系统灾难,没有一起是“缩进难看”导致的。灾难的根源始终是技术债的无序积累,典型表现为四类结构性问题:
模块边界混乱:本应互相隔离的业务域彼此渗漏,改一处、崩一片;
重复实现业务逻辑:同一条规则在多处各写一遍,口径随时间漂移;
隐式依赖全局状态:函数签名看不出真实输入,行为取决于看不见的上下文;
错误处理被强行吞掉:异常被静默丢弃,故障延迟暴露,现场信息永久丢失。
糟糕的是,AI恰恰在制造这四类深层问题上效率惊人。这是由技术特性决定的:大多数代码生成以单次会话、单文件为粒度,模型缺乏跨模块的全局一致性视图,更不掌握企业内部那些“不成文”的架构约定。它能保证每个局部都正确、体面,却无法保证文件与文件之间不互相矛盾。旧屎山是“局部烂、整体凑合”,新屎山是“局部完美、整体失序”——后者破坏力更大,因为它让人的防御心理完全松懈。
三、案例:一次“测试全绿”的合并,三周后的线上资损
场景:某核心业务线的订单状态流转重构。承接人是一位毕业不满两年的年轻工程师,他熟练地把需求文档投喂给一个高级智能体,几分钟后拿到几百行状态机流转代码:命名规范、注释齐全、单元测试全绿;自测主流程正常,审查导师粗看主逻辑后批准合并。
三周后,线上开始零星出现订单状态错乱的客诉:用户明明支付成功,但在特定并发场景下,订单状态被重置回“待支付”,系统据此自动触发了库存释放——资损风险直接拉满。
问题代码的骨架(已脱敏简化)大致如下:
// 表面:命名规范、注释齐全、逻辑清晰public void handlePaymentCallback(String orderId) {Order order = orderRepository.findByOrderId(orderId);// 仅处理待支付订单if (order.getStatus() != OrderStatus.WAIT_PAY) {return; // 缺陷1:失败被静默吞掉,无日志、无告警、无现场}order.setStatus(OrderStatus.PAID);orderRepository.save(order); // 缺陷2:读取-校验-写入非原子,并发下丢失更新}
两个缺陷相互叠加:
缺陷1摧毁了可观测性。冲突或重复的回调被无声丢弃,排查时连“它到底来过没有”都查不到,只能靠海量日志反推。缺陷2摧毁了并发正确性。“读取→校验→写入”三步之间没有原子性保护:支付回调与超时取消的补偿任务并发执行时,双方都读到“待支付”、各自写入目标状态,后写入者直接覆盖先写入者——于是“已支付”订单被补偿逻辑改回“待支付”,库存随之被错误释放。
修复的关键,是把状态转移从“分散的if”变成显式、原子、可证伪的操作(示意代码):
// 把"校验当前状态 + 变更目标状态"收敛为一次条件更新boolean ok = orderRepository.casUpdateStatus(orderId,EnumSet.of(OrderStatus.WAIT_PAY), // 期望的当前状态集合OrderStatus.PAID); // 目标状态if (!ok) {log.warn("状态转移冲突, orderId={}", orderId); // 冲突必须可见throw new StatusConflictException(orderId); // 让矛盾显性暴露}// 锁库存等联动动作与状态转移收敛到同一事务边界内
顺带的教训是:状态转移规则必须收敛到一张显式的转移表里集中管理,而不是散落在若干个service方法中各写各的if。转移表既给人看,也给监控看,自动化系统才能对非法状态组合发出告警。
四、为什么AI屎山比人工屎山更难排查
如果这是人工手写的旧代码,有经验的维护者大概率能“闻出味道”:新人处理并发忘了加锁、事务边界划错,这些都是老朋友。但AI生成代码的缺陷没有典型气味,排查成本被以下四个机制显著放大:
没有“决策考古层”。人工屎山虽然丑,但提交历史、命名挣扎、注释里的吐槽,无意间记录了作者意图与妥协点,老手可以顺着痕迹定位。AI代码是“无来历”的:它呈现结论、隐藏推理,而且往往是训练数据里最“平均”的写法,并不针对你的业务约束做过任何特化。
复制式扩散。同一套有缺陷的写法,会被AI换着变量名在多个文件重复生成。旧屎山的问题至少集中在一处,新屎山的问题呈分布式均匀分布——修一处漏九处,回归测试疲于奔命。
表面一致掩盖语义不一致。全仓库风格高度统一,会制造“质量可靠”的错觉,让评审防线天然松懈;但每个文件对隐含前提的假设可能互相矛盾,而这类矛盾在单文件评审中永远不可见。
测试幻觉。AI能让生成的代码通过它自己生成的测试,甚至生成迁就实现细节的“配合型”测试。实现与测试同出一源,失去了互相证伪的能力。“单测全绿”只能证明代码符合AI对需求的理解,不能证明它符合业务的真实约束——尤其证明不了并发与时序。
五、治理:给AI编码配上四道防线
AI本身不是屎山的成因,治理缺位才是。参考多数团队的实践,防线可以分四层建设:
第一道:评审标准从“读得顺”升级为“问得出”。表面规整度不再作为通过依据。对涉及状态、资金、库存的代码,新增必答清单:并发模型是什么,哪些操作必须幂等?事务边界在哪,失败回滚后状态是否一致?状态转移是否集中、显式、可枚举?异常路径是否留痕,被吞掉的失败去了哪里?答不上来就退回,而不是默认“应该没问题”。
第二道:测试策略超越“单测全绿”。对强一致场景补充多线程、多实例的并发测试,用真实竞争压出时序缺陷;引入不变量对账——定时扫描“已支付但库存已释放”这类非法状态组合,让隐性矛盾显性化;测试用例的设计与实现解耦,由独立视角定义“什么算对”,避免实现与测试同源。
第三道:架构约束显式化。把团队的隐性约定变成机器可执行的规则:状态机集中定义、转移表驱动、数据库层统一使用乐观锁或条件更新、异常统一收口。静态扫描、重复代码检测、依赖检查等通用门禁能力可以作为兜底嵌入流水线,但要对它们的边界保持清醒:它们查得出表面异常,查不出“逻辑冗余但表面正确”的结构性问题。
第四道:意图留痕。要求提交AI生成代码的工程师补充三类信息:需求与实现之间的映射关系、关键设计取舍(为什么选A不选B)、生成时使用的关键提示词与人工修正点,必要时沉淀为架构决策记录。未来的维护者先读意图、再读代码,排查成本完全不同。这也是“看不懂吗”这个问题最直接的答案——代码看不懂不可怕,可怕的是没有任何东西能解释它为什么长这样。
六、结语:需要更新的是“屎山”的定义
回到标题的问题:AI生成的代码大概率不会变成传统意义上“难看”的屎山——它会长成另一种东西:语法光鲜、意图缺失、缺陷均匀分布、无法凭经验直觉定位的新型技术债。它对组织的真正考验,是治理能力能否跟上生成速度:代码生产的效率已经提升数倍,而评审深度、测试强度、架构约束这三件事如果还维持旧节奏,债务的堆积只是时间问题。未来区分平庸团队与优秀团队的标准,将不再是“谁在用AI写代码”,而是“谁能为AI写的代码建立起可解释、可演进的工程秩序”。