知

模块六 · AI 协作与表达

第 10 / 11 节19 分钟阅读型

AI 协作与代码评审:把后端风险挡在合并之前

后端 AI 代码的错误代价不对称——生成一分钟,出错赔数据。这一节给出接口契约先行、事务与并发审查、SQL 与幂等的检查清单。

读完这一节,你能做到

  1. 说清后端场景里 AI 协作的风险为什么和前端不同。
  2. 用带版本与中间件现状的上下文,一次要出能落地的实现。
  3. 按「接口契约 → 单测 → 实现 → 事务并发审查」的顺序推进。
  4. 用五个后端检查点审出事务失效、线程池耗尽、N+1、重复扣款。
  5. 用编译、单测、集成测试与 SQL 日志证明改动没有变坏。

一、风险不对称

前端写错,用户看到白屏;后端写错,可能是一批数据错、一笔钱多扣、一个事务没回滚。AI 生成一段能通过编译的代码只要几十秒,而它引发的事故排查常常以小时计。

它很擅长它经常错
CRUD 骨架、DTO 与校验注解事务边界(自调用、事务里做 RPC)
常见设计模式与工具类并发安全(共享可变状态、线程池配置)
单元测试骨架查询性能(N+1、索引失效)
配置项与脚手架幂等与重试(重复提交、重复消费)

判断原则:后端的"能跑"是最低标准。 通过编译、跑通一次请求,只能说明语法与主流程对;真正决定成败的是事务、并发、性能与幂等。

二、上下文:版本与事务边界必写

后端上下文比前端多两项硬约束:框架版本和事务管理器。

  1. 目标:哪个接口、什么业务动作、验收看什么。
  2. 现状:JDK 与 Spring Boot 版本、ORM 选型、事务管理器配置、现有 DTO 与异常体系、分库分表与否。
  3. 约束:不新增依赖、必须走现有 Result 包装、事务只包 DB 操作、不许在事务里发 MQ。
  4. 验收:行为级——重复提交只生效一次、下游超时能补偿、单次请求 SQL 条数上限。
反例:帮我写一个下单接口。
正例:目标:下单接口,扣库存 + 建订单 + 发履约消息。
      现状:JDK 17 / Spring Boot 3.2,MyBatis-Plus,事务管理器是 DataSourceTransactionManager,
            异常统一走 BizException,消息用 RocketMQ,已有 outbox 表。
      约束:不新增依赖;事务内只做 DB;消息走 outbox 异步投递;不许用 @Transactional 包 RPC。
      验收:并发下库存不超卖;同一幂等键重复请求只生成一单;单次请求 SQL ≤ 6 条。

第二段的价值不在"写得长",而在它把事务边界和幂等键这两件最容易出事的事提前钉死了。

三、拆任务:契约先行,实现在后

后端推荐的顺序与前端不同——接口契约先定,单测跟着契约走:

① 接口契约(入参/出参/异常码/幂等键)
② 失败路径的单测(先红)
③ 实现(事务、DAO、消息)
④ 事务与并发审查(单独一轮,只审不写)

第一步定契约,AI 后面写的实现就被约束住了;第二步先写失败路径的测试,能逼出"异常怎么处理"这一层它最常偷懒的地方;第四步单独审事务与并发,避免和功能改动混在同一个 diff 里。

粒度标准:一次 diff 能在 10 分钟内逐行看完。 一个带事务、消息、缓存的接口,通常要拆成 2-3 轮。

四、五个后端检查点

第一点:事务边界

  • @Transactional 是否发生在同类自调用上?那样代理不生效,事务根本没开。
  • 事务里是否做了 HTTP 调用、消息发送、文件上传?长事务会拖死连接池。
  • 异常是否被吞掉?捕获后不抛,事务就不会回滚。
  • rollbackFor 的默认值只回滚运行时异常,受检异常要显式声明。

第二点:并发与线程池

  • 线程池是否用了无界队列?那等于放弃背压,任务堆到 OOM。
  • 共享可变状态有没有保护?SimpleDateFormat、非线程安全集合是最常见的产物。
  • ThreadLocal 用完 remove() 了吗?线程池复用会串数据。
  • CompletableFuture 用的是默认线程池吗?IO 任务和计算任务混在一起会互相饿死。

第三点:SQL 与索引

  • 循环里逐条查(N+1)?批量查询或 IN 一次拿回来。
  • 索引最左前缀被破坏了吗?函数、隐式类型转换、like '%x' 都会失效。
  • 分页 count 是不是每条都跑?大表上这是隐形炸弹。

第四点:幂等与重试

  • 幂等键存在哪、多久过期?没有幂等键的重试就是重复下单。
  • 消息消费端做了去重吗?MQ 至少一次投递意味着一定会重复。
  • 重试有没有退避?无间隔重试会把下游打挂。

第五点:资源与可观测性

  • 流、连接、锁是否在 finally 或 try-with-resources 里释放。
  • 关键路径有日志与耗时打点吗?出问题时能不能定位到是哪一段慢。
  • 日志里有没有手机号、身份证、token?后端的日志合规风险比前端高。

五、验证:三道闸

  1. 静态闸:mvn -q compile / gradle compileJava + 静态检查。拦掉类型与 API 误用。
  2. 行为闸:单测覆盖失败路径(异常、超时、重复提交),集成测试跑真实数据库与事务。
  3. 数据闸:打开 SQL 日志数一数条数;并发测试跑一次库存扣减;看连接池与线程池指标。

第三闸是后端特有的一闸:逻辑正确但 SQL 爆炸、或并发下超卖,都会在前两闸全绿的情况下发生。

六、算例:一个下单接口的两种做法

任务:下单(扣库存 + 建单 + 发消息),要求幂等。时长示意。

做法 A:一次要完

项耗时
写提示词、等生成3 分钟
review 260 行25 分钟
发现事务里发了 MQ,改 outbox返工 40 分钟
压测发现超卖,加乐观锁返工 30 分钟
合计98 分钟

做法 B:拆四轮

轮次交给 AI 的自己做的耗时
① 契约DTO 与异常码定幂等键与错误语义10 分钟
② 失败路径测试用例骨架补重复提交、超时断言15 分钟
③ 实现DAO 与事务骨架加乐观锁、改 outbox20 分钟
④ 事务并发审查无(只审)逐条过五个检查点10 分钟
合计55 分钟
净收益 = 生成节省的时间 − (审查 + 返工 + 事故成本)

后端还有一项容易被忽略的成本:事故成本不是线性的。 超卖 3 件可能只是赔钱,重复扣款上了热搜就是另一个量级。

七、常见坑

  1. 以为编译通过就是对的。 事务、并发、索引都不在编译器的检查范围内。
  2. 让 AI 决定事务边界。 它默认把整个方法包进事务,包括 RPC。
  3. 不看 SQL 日志。 N+1 在开发数据量下完全无感,上线就炸。
  4. 幂等只在"想法"里。 没有落地成幂等键,就等于没有。
  5. 把生产数据贴进提示词。 订单号、用户信息、内部表结构都属于敏感信息。
  6. 一次让它改十个文件。 后端改动的影响面比前端大,diff 越大越不敢合。

八、面试怎么讲

后端岗位问 AI,多半会追问"你怎么保证不出事故"。

怎么用:契约先行,先写失败路径的单测,再让它填实现,最后单独一轮审事务与并发
怎么验:编译 + 单测 + 集成测试,外加 SQL 条数与并发压测
踩过什么坑:它把 MQ 发送写在事务里,回滚后消息已经出去了;后来统一走 outbox
换来什么:一个接口从 98 分钟降到 55 分钟,返工集中在"幂等与事务"这类结构问题上

一句话加分项:"AI 能写对 happy path,事故基本都出在失败路径和并发上,所以我把审查预算全放在这两块。"

九、动手任务:自写 → AI 写 → 对比 → 取舍

四步不跳:① 自己写(限时,关掉补全)→ ② 让 AI 写同一题 → ③ 逐条比对 → ④ 说出保留谁的写法。

① 自己写

  1. 写一个幂等的下单接口:扣库存用乐观锁,消息走 outbox,重复提交只生成一单。
  2. 给这个接口写三条失败路径单测:库存不足、重复提交、下游超时。

② 让 AI 写同一题

把第 1 题交给 AI,补一句 Spring Boot 3.2 + JDK 17,事务内只做 DB,MQ 走 outbox,必须幂等。

③ 对比要盯的点

检查面我的版本AI 版本差异说明
事务边界
幂等键
并发扣减
SQL 条数

④ 说取舍

  1. 它把消息发送放在事务里了吗?回滚之后消息会怎样?
  2. 它的幂等键存在哪、多久过期?
  3. 两个人同时下单同一件库存 1 的商品,它那版会超卖吗?

十、自测

  1. 后端 AI 协作的风险为什么和前端不对称?
  2. 上下文里哪两项是后端必写、前端可以不写的?
  3. 为什么推荐"契约先行 + 失败路径单测先行"?
  4. 五个后端检查点分别是什么?
  5. @Transactional 在什么情况下会静默失效?
  6. 三道闸里的"数据闸"具体看什么?

参考答案要点:错误代价是数据与资金,且不可回滚;框架版本与事务管理器;契约约束实现、失败路径是 AI 最常偷懒处;事务边界、并发线程池、SQL 索引、幂等重试、资源可观测;同类自调用、异常被吞、受检异常未声明 rollbackFor;SQL 条数、并发压测、连接池与线程池指标。

配套题目

★ 越多越该先背;点题目可看完整答案与深入版。

提示:读完可以顺手做几道对应题目,检验是否真的理解