最后一周都在修它造的 Bug

发布时间:2026/9/11 8:42:58

最后一周都在修它造的 Bug 这是我接手的一个运行了 6 年的 Spring Boot 单体服务核心模块 5000 行最臃肿的那个类 1800 行47 个方法堆在一起没有注释单测覆盖率 8%。前任的前任离职的时候留了一句话“这块别乱动动了必翻车。”我用 Claude Code 开干了。第一周重构进展顺利AI 拆类、提方法、补测试速度确实快。第二周我开始合并代码准备上灰度然后——连续 3 天每天下午收到告警凌晨定位问题根因全是 AI 重构时悄悄改了的东西。AI 代码不会说我不确定它只会自信地告诉你这样完全等价。等你发现不等价可能已经在生产了。这篇文章记录我踩的 4 个真实坑每个坑都有具体的翻车经过和防坑方法。AI重构遗留代码踩坑复盘四大坑与防坑心法图4个核心踩坑的翻车现场与根因分析坑一AI 删掉的死代码其实是 Spring 运行时注入的 Bean这是最隐蔽的坑也是第一个让我上线后翻车的坑。我让 Claude Code 扫描整个服务找出没有被任何地方引用的类删掉它们。AI 给出了一份 37 个候选类的列表自信满满地说这些都是死代码删掉可以减少代码量、降低维护复杂度。我看了几个确实在项目里搜不到任何直接引用。批准删除。上线之后某个异步消息处理功能直接 500。定位了两个小时发现是 OrderNotificationEventListener 这个类被删了。这个类长这样Componentpublic class OrderNotificationEventListener {Autowired private NotificationService notificationService; EventListener public void handleOrderCreated(OrderCreatedEvent event) { notificationService.sendConfirmation(event.getOrderId()); }}项目里没有任何地方写 new OrderNotificationEventListener()也没有 Autowired OrderNotificationEventListener。但它是活的。Spring 的组件扫描会自动把 Component 标注的类注册到容器里。它监听 OrderCreatedEvent只要 Spring 的 ApplicationContext 发布这个事件它就会被调用。没有显式引用只有运行时反射注入。AI 的 grep 分析找不到调用链——因为调用链在运行时才存在。这不是个例。我们那个服务里被误判的类一共 11 个分属以下注解Component、Service、Repository、EventListener、Scheduled、ControllerAdvice。防坑心法给 AI 加一条硬性规则在 prompt 里明确写清楚。规则带有以下注解的类永远不标记为死代码即使找不到显式引用Component, Service, Repository, Controller, RestController,Configuration, Bean, Entity, MappedSuperclass,Scheduled, EventListener, Async, Aspect,ControllerAdvice, RestControllerAdvice,Converter, JsonComponent,以及任何以 *Initializer 结尾的类名。原因Spring 依赖注入是运行时反射机制静态分析无法追踪这类依赖。还有一类更难发现通过字符串动态加载的 Bean。Class.forName(“com.example.OrderService”)、BeanFactory.getBean(“orderService”)这些都是 AI 的死角。凡是字符串拼接类名的地方AI 永远判不准。过去用静态分析工具找死代码的逻辑在这里完全失效——不是工具不够好是 Java 的反射机制本来就是为了绕开静态分析而设计的。坑二上下文窗口到底AI 的函数语义悄悄漂移了第二个坑更难发现因为代码看起来完全正确测试也过了只有特定场景下才会出问题。我在重构的第 8 天让 Claude Code 整理一个 800 行的订单处理核心类把分散的逻辑提取成独立方法。任务很顺利AI 改了 40 多处代码变得整洁多了。三天后上线收到告警某类订单的优惠计算出现偏差。金额差了几块钱不是每笔都有大约每 200 笔里有 1-2 笔。排查到最后发现问题出在这段逻辑AI 在提取某个方法时把一个本来是局部变量的 BigDecimal discountBase 改成了读取 this.orderContext.getDiscountBase()。AI 注释里写的是“等价重构将局部变量替换为 context 字段读取逻辑不变。”逻辑确实一样——在大多数场景下。但问题在于orderContext.getDiscountBase() 在某些分支下返回的是已经应用了会员折扣之后的值而原来的局部变量是在折扣应用之前算的。两者在 95% 的情况下相等只有当订单同时触发多个折扣叠加的时候值才不同。为什么 AI 会犯这种错误这和上下文窗口的工作原理直接相关。会话开始├── 第 1-15 个文件 ← AI 能完整理解上下文├── 第 16-35 个文件 ← 早期细节开始被压缩└── 第 36-45 个文件 ← AI 对前面文件的记忆已经模糊这里的重构开始出现语义漂移当 AI 在处理第 40 个文件里的 OrderProcessor 时它对第 8 个文件里 discountBase 计算时机的记忆已经非常模糊了。它只知道这里用的是 discount basecontext 里也有一个 discount base就做了等价替换。根据 Augment Code 的实测数据在跨 50 个文件总计 200K token 的重构任务中上下文漂移导致约 40% 的任务出现不一致。这不是模型的问题是架构限制。任何当前的 LLM 都面临同样的问题。防坑心法一分批次重构每批控制在 10-15 个文件。每完成一批要求 AI 输出一份语义变更摘要哪些函数的行为发生了变化变化在哪里。这个摘要不需要人工审读每一行但如果某个函数出现在摘要里那就重点 Review。防坑心法二在业务计算函数旁边写注释标明时序依赖。// 注意此处 discountBase 必须在 applyMemberDiscount() 调用之前计算// 如果读取 context.getDiscountBase()在多折扣场景下值会不同// 修改此函数时请检查 DiscountCalculationFlowTest 的时序测试用例BigDecimal discountBase order.getOriginalAmount();这类注释对人类工程师是废话但对 AI 是关键信号。有了它AI 在重构这个函数时会主动保留时序约束而不是等价替换。防坑心法三对金融计算、状态机转换、时序依赖的代码重构后必须跑专项测试。不是测试通过就 OK是要有覆盖多折扣叠加、边界金额、状态切换顺序的专项用例。这类 case 通常不在普通单测里需要专门写。坑三AI 说仅移动代码不改逻辑但它改了这个坑比上面两个更难防因为 AI 主动打破了你给它的限制而且改的是你没让它改的地方。有一次我明确告诉 AI“只重新组织这个文件的包结构不要修改任何实现逻辑不要做任何优化。”AI 回答“明白我只会移动代码不做任何功能变更。”然后它把代码移好了同时删掉了 3 个冗余的局部变量声明把一个三目表达式改成了 Optional 链式调用还把某个日志格式从字符串拼接改成了占位符格式。每一处单独看都是合理的优化。但合在一起有一处改坏了原来的代码是String errorMsg Order orderId failed: reason;log.error(errorMsg);// 注意errorMsg 这个变量后面还被用到了notifySupport(errorMsg);AI 改成了log.error(“Order {} failed: {}”, orderId, reason);notifySupport(Order orderId failed: reason);两处行为一致但 notifySupport 接收的消息和 log 打印的消息现在是两次字符串拼接变成了两个对象。在原来的代码里两者是同一个字符串引用这在某个地方被 比较过是的这是更早的坑所以行为不同了。这类问题的根源在于AI 不理解不要优化这个指令的真实含义。 对 AI 而言把字符串拼接改成占位符格式不叫改逻辑那叫最佳实践。它没有办法区分你的不改逻辑和它理解的不改逻辑。防坑心法用 git diff 做机械检查不要用眼睛。重构完成后用这个命令过滤掉纯格式变化看实质性的代码变更查看所有改动排除纯空白行变化git diff --ignore-all-space HEAD~1重点检查这些类型的变化git diff HEAD~1 | grep “^” | grep -E “(Optional|stream()|lambda|-)”任何你没有明确要求的变更都需要仔细审查。如果 AI 在只移动代码的任务里修改了表达式不管改得对不对都要 Review。另一个方法是在 prompt 里添加行为验证要求任务完成后列出所有你修改的代码行不包括格式调整。如果你修改了任何我没有明确要求修改的逻辑请在报告里标注原因。大多数时候 AI 会老实报告。偶尔它会遗漏但这比完全不报要好得多。坑四AI 生成的数据库迁移脚本开发 2 秒生产锁表 8 分钟这个坑跟 AI 重构的关系不是直接的但是 AI 辅助重构过程中非常容易触发。我们在重构中新增了一个字段 processed_atAI 生成了对应的 Flyway 迁移脚本ALTER TABLE orders ADD COLUMN processed_at DATETIME;UPDATE orders SET processed_at created_at WHERE status ‘COMPLETED’;CREATE INDEX idx_orders_processed_at ON orders(processed_at);在开发环境1000 条数据2 秒跑完完美。到了生产环境orders 表 5200 万行。ALTER TABLE 在 MySQL 5.7 默认加表级锁8 分钟。这 8 分钟里所有涉及 orders 表的写操作全部阻塞。我们的下单服务直接打满P99 超过 30 秒用户侧开始出现超时错误。数据库 DBA 问了一句话我现在还记得“这个脚本你们 review 过大表影响吗”我当时不知道怎么回答。AI 生成的脚本在逻辑上完全正确。它没有做错任何事。但它不知道生产环境的数据量不知道 ALTER TABLE 在大表上的行为也不知道需要用 pt-online-schema-change 或者 MySQL 8.0 的 INSTANT 算法来避免锁表。这类问题在小数据集开发环境里根本不会暴露AI 又无法感知生产数据规模。防坑心法建立数据库迁移的 checklistAI 生成脚本后必须走 checklist。数据库迁移 Review Checklist□ 涉及的表当前行数是多少生产数据□ ALTER TABLE 操作在 MySQL 版本下是否使用行锁还是表锁□ 行数超过 100 万时是否使用 pt-online-schema-change 或 gh-ost□ UPDATE 操作是否有 WHERE 条件批量更新是否分批执行□ 新建索引是否在低峰时段执行□ 这个迁移是否可回滚如果不可回滚已知晓风险对于大表 DDL推荐的命令是使用 pt-online-schema-change100万行以上pt-online-schema-change–alter “ADD COLUMN processed_at DATETIME”–executeDyour_db,torders或者 MySQL 8.0 的 INSTANT 算法仅支持部分 DDLALTER TABLE ordersADD COLUMN processed_at DATETIME,ALGORITHMINSTANT;可以把这个 checklist 直接加到 AI 的 system prompt 里让它每次生成迁移脚本时自动过一遍输出自检结果。不是所有问题都能被它发现但会好很多。这些坑背后的共同原因四个坑表面看原因各不相同但往深里追有一个共同点AI 没有你的系统上下文但它的
延伸阅读

更多相关文章

2026/9/8 15:28:38

嵌入式系统EMIFA SDRAM配置与初始化实战指南

1. 项目概述与核心价值在基于TI C6000系列DSP或类似高性能嵌入式处理器的项目里,外部存储器接口(EMIFA)的配置往往是硬件工程师和底层驱动开发者必须啃下的硬骨头。尤其是当你的系统需要挂载SDRAM(同步动态随机存取存储器&#xf…

2026/9/10 23:22:03

Citrix虚拟化技术核心架构与部署实践

1. Citrix虚拟化技术概述Citrix Virtual Apps and Desktops 7 1912 LTSR是当前企业级虚拟化解决方案中的标杆产品。作为长期服务版本(Long Term Service Release),它为企业提供了稳定可靠的应用程序和桌面虚拟化平台。我在金融行业实施这个方…

2026/9/6 6:08:45

07-使用案例集

Dify 使用案例集(可复刻实战) 适用版本:Dify Community Edition v1.16.0 面向读者:零基础,已完成前 6 篇 阅读时长:约 40 分钟 前置:04、05、06 目标:照着做,独立复刻 6 …

2026/9/11 14:12:07

GPT-6 Astra提示词指南:如何用slop词黑名单消除AI味

这周圈子里最热闹的事,莫过于OpenAI把GPT-6 Astra带到了台前。我更新模型后的第一件事,就是拿它把我去年攒的那堆旧提示词全部跑了一遍。结果很分裂:文章框架、逻辑、信息密度都比以前好太多,但读起来还是那副熟悉的味道——"…

2026/9/11 14:12:07

Python+Pygame复刻《燃烧的蔬菜》游戏开发全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/11 14:12:07

从神经元到世界模型:大模型全栈构建操作手册

1. 这不是一本“讲大模型”的书,而是一本“造大模型”的操作手册“从神经元写到世界模型”——光看标题,很多人第一反应是:又一本讲Transformer、讲LLaMA、讲RLHF的科普读物?不。这本书的底层逻辑根本不在“解释”,而在…

2026/9/11 14:07:06

QTabBar拖入拖出:实现可分离标签窗口的完整状态机与索引算法

简介:针对Qt开发者的QTabBar增强功能示例代码包,重点解决选项卡拖出为独立窗口、拖回主窗口以及拖回后重新排序标签页的交互实现。工程适用于需要自定义标签页拖放行为的桌面应用开发场景,适合具备一定Qt基础的读者参考。压缩包共82个文件&am…

2026/9/10 16:39:38

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/10 11:16:38

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 12:32:02

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/10 15:49:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码