干了这些年工程我见过不少团队在 AI Coding 工具面前又爱又恨工具确实猛搭接口、写单测、改样板代码的速度比我十年前手写快了几十倍。可问题也恰好出在“快”上。AI 生成的代码总带着一种非常自信的气场——语法工整、缩进到位、命名像模像样看起来完全可以直接上生产。但等它合并进主干之后边界条件漏判、异常分支被吞、状态转换错位、安全校验被绕过这些问题才开始一个一个冒出来。越来越多的团队发现AI Coding 真正难的从来不是“能不能生成”而是“生成了之后怎么兜住质量”。这事靠人海战术补不完靠传统流程也压不住必须重新设计一套针对“机器产出”的质量治理方案。这篇文章我会把在几个项目里摸出来的完整路子捋一遍工程规范怎么约束 AI 产出、代码评审对着 AI 代码具体该审什么、风险驱动测试如何用有限的资源掐住最容易出事的环节。适合正在把 AI 编码工具接入生产项目的开发者和技术负责人也适合吃过亏之后想认真重构质量流程的同学。1. 先承认一个事实AI 生成的代码问题不在“语法”在“语义”1.1 为什么 AI 代码看起来总是“都对”先讲一个最容易被忽略的底层认知AI 编码工具本质上是大规模模式补全器不是带着全局意图去写程序的“虚拟工程师”。它生成的每一行代码都是基于大量历史样本推断出来的“高概率结果”所以语法概率最高、格式最工整、注释也最像人写的。可它并不真正理解这段代码在这个项目里承担什么责任、会被谁调用、在什么极端输入下会崩。这就好比一份满分试卷让誊写员抄了一遍。誊写员字迹漂亮、卷面整洁、每一题都有完整答案但他并不知道题目的逻辑是不是自洽更不知道某个填空题的“标准答案”放在另一个题目语境下是不是错的。AI 的“自信”不是来自推理而是来自概率拟合。正因如此它写出来的边界条件、异常分支、状态流转往往乍看合理、细看经不起推敲。我在实际 review 中最常碰到的情况是AI 补全了一个函数缺失了某个参数的 null 校验但恰好函数内部有另一个看似无关的判断把这个 null 情况“顺手”接住了于是单测全绿、功能正常。直到线上某个客户端传入了异常数据整个调用链才炸开而且报错位置离真正的漏洞点隔了三层调用。这种“错得很有逻辑”的缺陷恰恰是最难靠肉眼扫出来的。1.2 AI 代码的高发质量问题如果只分类不展开对治理没有意义。我把这些年收集到的问题按频率高低大致分了几类每一类都配一个常见场景边界与空值处理缺失AI 非常擅长写“正常路径”但对输入为空、长度为零、数值越界、日期跨年这类情况经常视而不见。常见于时间字符串解析、金额计算、列表截取。异常分支被静默吞掉生成代码里经常出现空 catch或者 catch 之后只记录日志但没有任何降级与兜底。更隐蔽的是AI 会把“捕获异常”和“处理异常”当成一件事。全局状态与共享资源被隐式修改AI 生成的重构代码容易破坏隐式约定比如改变了某一方法的执行顺序、把可变对象传给了多个调用方、动了一个共享缓存 key 的生成规则。安全校验把守位置错误权限判断被放在前端/入口之后服务端没有二次验证或者把用户输入直接拼进日志、SQL、文件路径。重复代码与随意抽象AI 倾向于把相似逻辑复制到新位置而不是复用原有实现导致同一个 Bug 会在三处同时存在修复时漏一处就出大事。这些问题的共同特征是静态检查工具大概率能抓到一部分空值、格式、未使用变量但语义层面的错误只有靠“对行为预期负责任的评审者”才能发现。所以后续所有的工程规范、评审流程、测试策略都应该围绕“把语义风险从差距中捞出来”这件事来设计。1.3 先建立可量化的基线没有度量就没有治理在引入 AI Coding 之前团队通常对“历史缺陷率”“单次变更修改文件数”“线上问题回滚率”这些指标是模糊的。不把基线做出来你没法判断 AI 投入后质量是变差还是变好。我建议至少先记录四个基础指标变更体积单次提交平均改动文件数、新增代码行数。AI 介入后这一项通常会暴涨如果涨了 3 倍以上评审压力会增加风险也会非线性放大。缺陷命中点Bug 是在代码评审阶段被发现还是测试阶段还是线上。它能直接反映评审和测试两个环节谁失效了。回归率某个模块在改动后出现旧功能被破坏的比例。AI 重构最容易踩这个坑。返工成本从提交到合入的平均耗时以及因为 review 不通过导致的修订轮数。 这四个指标不需要建数据平台先拿项目管理工具里的历史记录统计一遍就行。有了数字后面一切治理动作才有瞄准点。2. 工程规范让 AI 在栅栏里干活2.1 提示词里先写“宪法”别靠临场发挥很多人用 AI Coding 是直接甩一句话让它改代码改完拿去提交。这是把质量责任完全丢给概率。正确做法是在项目里维护一份“AI 协作规范”把它当成提示词里最靠前的固定上下文。规范不需要很长但必须是负面清单加正面约束的混合体。我见过比较好的模板大概长这样目标语言与框架版本全局固定不许擅自升级或替换推荐库。命名遵循项目既有风格禁止自创缩写。所有可能失败的外部调用必须有明确的异常处理禁止空 catch。输入参数必须进行 null、空值、类型、长度校验除非接口契约声明为不可空。不允许修改与本需求无关的模块代码。新增公共方法必须附带注释说明调用方与预期行为。禁止在业务代码中直接输出用户敏感信息。实际执行的时候可以把它精简成一段固定文本放在团队的 AI 工具配置里。这样做还有一个额外好处团队不同成员对 AI 的产出期望会收敛不会出现 A 让 AI 写代码、B 让 AI 写代码但风格完全两样的局面。2.2 工具链当警察格式化、静态检查、类型检查一条龙规范文件写得再好AI 也不会自觉遵守它只遵守“可验证的约束”。所以第二步就是把规范翻译成机器可以强制执行的检查项并且保证这些检查结果能够反馈回 AI 的生成过程。这一步我强烈推荐“本地检查优先、CI 拦截兜底”的双层结构。本地层包括统一的格式化配置、ESLint/静态分析规则、TypeScript 类型检查这些在开发环境里即时跑。关键技巧是把这些检查器的配置文件路径直接写进 AI 的上下文说明让 AI 在生成时就避开风格问题很多 AI 工具支持读取项目配置文件能明显降低生成代码在格式层面的噪音。CI 层的作用是兜底任何绕过本地检查的提交都会在合并前被拦截。不要在这件事上留白一旦允许“先合并后面再改格式”AI 生成的代码就会在本该聚焦逻辑风险的评审环节消耗掉大量注意力。2.3 接口与目录结构用物理边界锁住 AI 的扩散工程规范里最容易被忽视的是“物理边界”。AI 在修改一个需求时常常顺手扩大改动面——它会把相似函数的位置挪一挪、把公共方法重构一下、把数据结构顺手改成更方便的样子。从单次改动看可能没错但对团队来说这是不可控变异的源头。我们的解决方法是对仓库做分层约束。核心接口文件、数据库迁移脚本、公共配置目录标记为“只读区”AI 任务不授权不进入业务代码内部按模块划分一次改动只允许落在与需求相关的模块目录里。为了让这一点可执行我在评审里加了一条硬性规则凡是与需求无关的目录变更必须要开发者在提交说明里写清楚理由否则直接打回。实践证明这个规则几乎每周都能挡下两次“顺手改错文件”的事件。3. 代码评审人工仍然是质量最后的防线3.1 AI 时代的评审原则少看热闹多看语义传统代码评审依赖一个隐含前提作者能够解释自己的设计意图和潜在权衡。代码评审时开发者会主动说明“我为什么这么写、这里有哪些边界”。但 AI 生成的代码没有“意图”可解释它只会给出一个看起来自洽的结果。评审者面对的是一个充满语法自信但没有作者在场的产物这时候如果按传统从头到尾逐行扫效率会极低而且容易漏。我建议把评审重心从“这行代码对不对”转向“这段改动是否满足业务契约、是否引入了未声明的行为变化”。具体来说评审者先看 diff 范围和需求描述是否吻合再看关键逻辑分支有没有覆盖到边界最后从调用方的视角反向验证。特别是那种修改了公共函数或底层数据结构的改动必须确认所有受影响调用方都不会出现行为变化。3.2 三个必看维度语义正确性、风险边界、可维护性第一维度是语义正确性。AI 最容易在“类型对但语义错”的地方翻车。比如类型是字符串但内容格式与调用方预期不一致是数字但在算法上搞反了分子分母是集合但错误地按数组的索引访问。review 时要把重点放在“这个函数返回的值是否符合调用者未来会做的假设”。第二维度是风险边界。包括外部输入校验、资源释放、并发控制、安全权限。AI 生成的代码通常对异常路径的考虑非常单薄尤其是网络请求、文件读写、数据库事务这些边界。我的习惯是拿到一个 AI 改动先问三个问题这个函数被调用时最不可能的输入是什么外部服务返回失败时会发生什么这个改动是否影响其他并发的执行路径只要有一个问题答不上来这个改动就不该过。第三维度是可维护性。AI 很喜欢“一次性正确”的代码就是能跑但命名莫名其妙、抽象过度或无抽象、注释永远在解释“做了什么”而不是“为什么这么做”。这种情况最好当场要求重写因为 AI 代码一旦进入主干后续维护就是团队自己来扛没人愿意在三个月后面对一段写得像谜语的关键逻辑。3.3 一套可以直接抄的代码评审清单以下是我实际在用的一套 AI 代码评审清单按优先级排序不局限于任何具体技术栈检查项具体动作最高优先需求范围本次改动是否只涉及需求描述的文件存在无关文件变更时必须说明理由是输入校验所有外部输入是否有 null/空值/越界校验是异常路径外部调用失败是否有兜底禁止空 catch是状态变化是否修改了全局状态、共享对象、缓存 key 的逻辑是安全敏感信息是否进入日志/URL/响应体权限判定位置是否正确是返回值语义返回类型与调用方预期是否一致是否隐藏了错误状态是资源生命周期连接、流、锁、事务是否都有正确释放是并发是否存在竞态条件与重复操作中可测试性新增逻辑是否容易构造单测是否依赖过深 mock中命名与注释是否沿用项目风格注释是否解释“为什么”低这套清单不需要每一条都在评审时喊一遍真正的作用是让团队成员形成固定视角。评审者只要对照清单过一遍就不会被 AI 那种“整整齐齐”的观感带着走。我个人的建议是对 AI 生成的代码至少要留出比人工代码多 30% 的 review 时间。省下来的开发时间必须分一部分给质量把关环节要不然就是在预支后续的返工成本。4. 风险驱动测试把子弹打在故障概率最高的地方4.1 为什么“全覆盖”在 AI 编码时代反而是伪命题很多人对质量的第一反应是“提高测试覆盖率”。但在 AI 编码时代这招会失效因为测试用例本身也在大量用 AI 生成。覆盖率达到 90% 只能说明“每一行代码都被执行过”不能说明“所有关键行为都被验证过”。AI 生成的测试和 AI 生成的实现拥有同源缺陷它们会一起犯同一个认知盲区——比如都认为某个边界场景永远不可能发生。这种情况下覆盖率数字越漂亮虚假安全感越强。风险驱动的核心思路是把测试资源从“追求覆盖率”转移到“押注失败概率”。先评估改动中哪些部分的失效概率最高、失效影响最大然后把测试设计和执行资源集中在那里。它本质上和给自己买保险是一个道理你会为低概率高损失的房屋火灾买保险但不会为高概率低损失的铅笔丢失买保险。测试也是一样应该是风险优先而不是行数优先。4.2 怎样识别改动中的高风险区域在拿到一个 AI 生成改动的时候我习惯先给“改动面”做一次风险扫描不打分不下结论只标记高风险特征涉及外部输入解析的比如 HTTP 参数、文件上传、配置读取。涉及数据转换和单位换算的尤其是时间、金额、坐标、编号这类。修改了公共依赖或底层工具的影响面扩散到全部调用方。含并发或异步逻辑的比如锁、协程、消息队列消费。涉及身份认证、授权、敏感数据输出的。在原有代码上做“重构性改动”的AI 最容易在重构时悄悄改变行为。我给团队定的简易评估法是每个高风险特征记一分得分超过两分的改动必须补专门的边界测试和异常路径测试得分超过三分则额外要过一轮安全关注度高的评审。这样做不需要复杂权重模型也能让测试资源明显向风险倾斜。4.3 三条具体落地的测试策略策略一强制高风险区域的“边界用例优先”对识别出的高风险函数不先写普通成功路径而是直接写它最不可能被调用的场景。这个方法叫“先想最坏情况再动笔”。比如一个解析日期字符串的函数我会先写输入2024-02-30、输入空字符串、输入带时区偏移的格式、输入非 UTF 编码。这些用例在 AI 生成的测试里基本不会出现必须由人补。策略二人机协作的测试对拍我推荐的组合是“AI 生成正确路径测试 人工补边界与异常测试”。AI 很擅长把正常流程的测试写全省下大量重复劳动人的精力集中在 AI 不敢想象的输入上。两个来源的测试合并进同一个测试套件既能提速又能避开同源盲区。策略三属性测试与模糊测试常驻 CI对于数据处理类函数传统固定用例很难覆盖所有意外输入。这部分可以引入属性测试或模糊测试让测试框架自动生成海量随机输入并验证“输入数据类型合法时函数输出永远满足某个不变式”。比如金额计算函数不管输入怎么变结果都必须是非负、精度正确、不超过上限。这类测试对 AI 生成的实现特别有效因为它们能快速暴露出“某个分支压根没被思考过”的事实。落到流程上我会在 CI 里专门建一个“风险回归任务”每次 AI 相关的依赖升级或重构动作后自动跑一次全量模糊测试。跑完如果发现失败不要急着修先定位问题出在语义层还是实现层再决定是改代码还是补约束。5. 实操复盘用久了才会踩到的坑5.1 三个典型事故复盘第一个事故是“AI 的隐式状态变更”。某团队让 AI 优化一段缓存读取代码AI 把缓存过期时间计算从“调用时刻”改成了“创建时刻”导致一批缓存没能按预期刷新。单测全部通过直到线上数据出现明显延迟才被发现。复盘的原因是变更范围内没有涉及“缓存 key 生成规则”的明文约定AI 从代码里自己推断出了一个错误假设。教训是涉及缓存、时间、状态这类隐式约定时必须把约定写成显式注释放在关键代码附近并加入评审强制检查点。第二个事故是“重复请求风暴”。AI 在实现一个失败重试逻辑时没有加最大重试次数和退避间隔某次外部服务瞬时抖动直接引发了整批请求的重复排队把下游打满。代码层面看逻辑每一处都合理因为单次重试确实是正确行为但在分布式场景下90% 的失败同时触发重试就成了事故。从此以后凡是 AI 生成的循环、重试、定时任务代码我都会额外审查“是否有全局熔断和限流意识”。第三个事故是“异常被吞掉后问题迟到”。AI 在某个异步消息处理函数里 catch 了所有异常并直接 return结果导致失败的消息被静默丢弃业务数据缺失后两天才被发现。复盘时发现代码评审阶段就看到了空 catch但因为当时线上的“看起来没出问题”被打上了低优先级。这件事让我定下了一条死规矩空 catch 绝不接受任何形式的延迟修复评审阶段直接打回。5.2 复盘模板与排查纪律AI Coding 引入后的复盘和传统复盘有一个关键区别传统复盘问的是“谁在什么时间改了什么”AI 时代必须先回答“这段代码是 AI 生成的还是人写的、生成时用了什么上下文、经过了哪些检查环节”。我设计过一个简单复盘模板每次线上问题都按这个顺序问问题出现在哪段代码这段代码的生产路径是什么人写、AI 补全、AI 重构。如果来自 AI当时提示词里是否包含项目规范与边界约束。这行代码通过了哪些质量环节本地检查、CI 静态检查、评审、测试、模糊测试。失效的是规则本身还是执行规则的人。下一次应该在哪个环节加一道闸才能在本类问题进入主干之前拦住它。 按这个模板复盘几次后团队会自然发现真正的问题往往不是“AI 写错了”而是“我们没有一个环节在对语义负责”。静态检查和格式化拦住了风格问题评审环节因为惯性只看了逻辑主线测试环节因为覆盖率高产生了虚假安全感。这也是为什么我一直强调AI Coding 的质量治理不是某一个工具的升级而是一整套工程流程的重构。5.3 收网的小技巧把“人工例外”降到最低最后分享一个我在实操中特别受益的习惯给团队立了一套“人工例外机制”。允许开发者在特定场景下绕过某些 AI 相关的质量约束比如临时排查问题、快速原型验证、内部工具开发。这个机制必须配合两个条件一是绕过动作必须显式登记不能悄悄走侧门二是绕过代码进入主干前必须补上缺少的测试和评审。没有这个机制时团队会为了效率偷偷跳过规则最后反而比严格走流程更慢。有了它既保住了效率出口也把“例外”变成了可追踪、有限次的行为。我后来发现很多真正有效的质量改进就是在登记这些例外时发现的——因为它们把“规则哪里不合适”暴露得非常直接。AI Coding 的质量治理这条路每个团队都会走出一套自己的版本但核心逻辑是一样的让 AI 承担生成速度让人承担语义责任让规范与工具随时准备接管那些“看起来对但其实不对”的判断。我在实际项目中体会到这事的难点从来不是某一种工具或某一条规则而是能不能坚持在每个环节都问一句这段代码进入主干后我是否真的敢为它负责。如果敢AI 编码就是提速器如果不敢它就只是放大缺陷的加速器。