一个分页 Bug,AI 改了 17 个文件
AI 修 Bug 最危险的地方,不一定是写错代码
真正危险的是:
它会把一次局部修复,悄悄变成一次系统变更
AI 可以帮团队更快生成代码,但测试真正要判断的是:
这次修改到底还是一个 Bug Fix,还是一次隐藏的系统重构
前几天,一个开发同事在群里说:
“这个分页问题我已经让 Claude 修了,应该问题不大”
半小时后,PR 提交上来了
我点开一看:
17 files changed
+623 -218
我当时第一反应不是:
修好了没?
而是:
为什么一个分页 Bug,会改 17 个文件?

一、问题本身,其实非常普通
线上有个 Bug
用户反馈:
订单列表翻到第二页后,偶尔会出现重复数据
开发排查后发现,问题和分页 SQL 有关
理论上,大家都会觉得:
改一下 SQL,这事就结束了。
但 AI 接手后,事情开始逐渐不对劲。
二、第一层修改:SQL
这一层其实是正常的。
AI 修改了排序逻辑:
ORDER BY create_time DESC, id DESC
这个修改理由也很合理:
- 避免相同时间的数据排序不稳定
- 让分页结果更可预测
- 减少翻页过程中重复或遗漏数据的概率
这一层没问题,甚至可以说改得还不错。

三、第二层修改:DTO 开始变化
继续往下看,我发现参数结构变了:
- pageNo
- pageSize
+ PageRequest request
看到这里,风险其实已经开始扩大了。
因为这已经不是单纯修 Bug,而是:
接口结构调整。
AI 开始“顺手优化结构”了。
这个动作在代码层面看起来很合理,但在真实项目里,它的影响往往不止当前接口。
可能影响的范围
- 前端参数传递方式
- 网关参数解析逻辑
- 老客户端兼容性
- 自动化接口脚本
- Mock 数据与联调工具
- 文档与测试基线
为什么这里要警觉?
因为分页 Bug 的核心问题在 SQL。
如果连 DTO 都一起改了,那这次修改的性质已经发生变化:
| 原本预期 | 实际变化 |
|---|---|
| 修复一个分页查询 Bug | 同时调整了接口参数结构 |
| 局部代码修复 | 开始影响上下游调用关系 |
| 可控回归范围 | 回归范围明显扩大 |

四、第三层修改:公共工具类
继续看,我发现 PaginationUtil.java 也被改了。
这一瞬间我就警觉了。
因为:
公共方法一旦变化,影响范围通常会指数级放大。
改动大概是这样:
- subList()
+ stream().skip().limit()
从代码写法上看,这可能只是实现方式优化。
但对于测试来说,重点不是“写法优不优雅”,而是:
这个公共方法整个系统有多少地方在用?
后来一查,这个工具类整个系统有 40 多个接口都在使用。
这时候,这个问题就已经不是:
“订单分页 Bug”
而是:
“全局分页行为变化”
这里可能带来的风险
- 边界分页行为变化
- 空集合处理差异
- 性能特征变化
- List 实现差异导致的隐含问题
- 其他接口无感知受影响
风险提示
如果一个改动碰到了下面这些文件,测试要立刻提高警觉:
UtilBaseServiceCommonConfigConverterAbstractXXX- 公共分页 / 缓存 / 校验 / 序列化工具
因为这些地方的改动,通常都不是“只影响当前需求”。

五、第四层修改:缓存 Key 变了
然后,我看到最危险的一段:
- order:list:${userId}
+ order:list:${userId}:${pageNo}
AI 自动把分页参数拼进缓存 Key。
从逻辑上看,这似乎也没错:
- 不同分页有不同结果
- 缓存按页区分更精确
但真正的问题不在“语法对不对”,而在:
缓存维度变了。
以前是:
用户维度缓存
现在变成了:
用户 + 分页维度缓存
这意味着:
缓存数量可能直接膨胀。
缓存变更的隐藏风险
- 原来的缓存清理逻辑可能删不干净
- 分页维度增加后,Key 数量可能暴增
- 新旧 Key 并存时,可能出现脏数据
- 缓存命中率模型被改变
- 测试环境通常很难暴露 Redis 容量问题
- 线上热点用户可能放大问题
为什么这里特别危险?
因为这种改动往往具备 3 个特征:
- 代码看起来是对的
- 本地和测试环境未必能测出问题
- 真正出问题时,往往已经在线上了
也就是说,它不是那种“功能一跑就报错”的问题。
它更像是一种:
隐藏得很深、但上线后代价很高的系统性风险

六、最危险的地方,不是“代码错了”
看到这里,其实 AI 写的大部分代码都“有道理”
真正危险的地方在于:
它扩大了影响范围
这正是 AI Coding 很典型的一类风险:
一边修问题,一边“顺手优化代码”
比如:
- 重构代码结构
- 抽公共逻辑
- 统一参数对象
- 补全边界处理
- 调整缓存策略
- 修改通用工具实现
这些动作单独看都很合理。
但放在真实项目里,很多代码不是不能优化,而是:
不敢随便动
因为系统里真正决定风险的,往往不只是代码本身,还包括:
- 老接口兼容
- 历史数据
- 灰度逻辑
- 线上缓存
- 第三方依赖
- 老客户端行为
- 定时任务
- MQ 消费链路
- 团队长期积累的经验边界
这些东西,很多并不直接写在代码里。
AI 看不到这些“隐性上下文”,但测试必须想到。
七、后来我们怎么处理这个 PR?
最后,这个 PR 没有直接整体接受,而是拆开处理。
1)真正修 Bug 的修改:保留
保留 SQL 排序修复,这是核心问题。
也就是这部分:
ORDER BY create_time DESC, id DESC
因为这确实对应了分页重复数据的根因
2)结构优化类修改:回退
回退 DTO 重构和公共分页工具修改。
原因很简单:
这些改动不属于当前需求范围
当前目标是修复分页 Bug,不是顺手做结构治理。
3)缓存逻辑:单独设计方案
缓存相关修改没有直接合入,而是单独补方案,进一步评估:
- 缓存淘汰策略
- Key 生命周期
- Redis 容量影响
- 新旧逻辑切换方式
- 上线灰度方案
否则,这类修改上线风险太大
这次处理的核心原则
| 修改类型 | 处理方式 | 原因 |
|---|---|---|
| 直接对应根因的修复 | 保留 | 属于本次 Bug Fix 范围 |
| 顺手做的结构优化 | 回退 | 扩大影响范围,不符合当前目标 |
| 会影响系统行为的改动 | 单独评估 | 需要专项设计与验证 |
八、以后拿到 AI PR,先看这 4 类
现在我看 AI 提交的 PR,第一件事不是跑回归。
而是先判断:
这次修改的影响面,有没有被 AI 放大
我会优先扫下面 4 类内容
01 公共方法
重点关注:
UtilBaseServiceCommonConfig- 公共转换器
- 公共校验器
- 公共分页/缓存工具
为什么优先看?
因为这些地方的影响范围通常最大。
一个小改动,可能让几十个接口行为同时变化。
02 DTO / 返回结构
重点关注:
- 请求参数对象是否变化
- 返回字段是否变化
- 字段命名 / 类型是否变化
- 包装层级是否变化
为什么优先看?
这类修改最容易引发:
- 前端兼容问题
- 老客户端问题
- JSON 解析异常
- 自动化脚本失效
- 网关或序列化问题
03 缓存 / MQ / 配置
重点关注:
- 缓存 Key
- 失效逻辑
- MQ 发送 / 消费
- 配置项默认值
- 开关逻辑
- 限流与重试策略
为什么优先看?
因为很多问题测试环境根本测不出来
这类风险通常要到:
- 数据量上来
- 并发上来
- 线上链路更复杂
之后才会暴露。
04 顺手优化
重点关注:
- 顺手重构
- 统一命名
- 抽公共逻辑
- 改写实现方式
- 改造层级结构
为什么优先看?
因为这类修改最容易让“修 Bug”变成“系统重构”
而很多额外风险,就是从这些“看起来顺手的优化”里长出来的
AI PR 风险检查清单
拿到 AI 生成的 PR,可以先快速问自己这几个问题:
- 改动是否只围绕当前问题?
- 有没有碰到公共方法或基础层?
- DTO、返回结构、序列化对象有没有变化?
- 缓存、MQ、配置、定时任务有没有被改?
- 有没有“顺手优化”超出当前需求范围?
- 回归范围是否因此被放大?
- 当前改动还是 Bug Fix,还是已经接近一次系统变更?

最后
AI Coding 最大的变化,不是代码写得更快
而是:
- 系统变化速度越来越快
- 修改范围越来越大
- “局部修复”更容易演变成“全局影响”
所以测试的价值,也会越来越集中在这几件事上:
- 影响分析
- 风险识别
- 回归范围判断
- 发布决策支撑
AI 可以帮团队生成更多代码。
但最终决定哪些代码能安全上线,仍然需要人来判断
尤其需要:
测试和质量工程来做这道最后的风险判断
一句话总结
AI 最危险的地方,不一定是把代码写错
更危险的是:
它把你以为的“小修小补”,悄悄变成了一次系统级变更
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)