一次 Spring Boot 4 升级任务,暴露了 AI 在处理"简单配置修改"时的灾难性表现
背景
JeecgBoot 需要从 Spring Boot 3.5.5 升级到 4.1.0。任务本身不复杂——改几个弃用的 API、迁移几个配置项。本以为 AI 能轻松搞定,结果在 YML 配置文件的修改上翻车翻得彻彻底底。
需要改的 YML 配置(10 个 profile 文件)
就两件事:
| 改动 |
旧 |
新 |
| MongoDB 配置键 |
spring.data.mongodb.uri |
spring.mongodb.uri |
| 删除废弃属性 |
server.error.include-exception / include-message / include-stacktrace |
直接删除 |
正确的做法:把 mongodb 从 data: 子节点提升为 spring: 子节点(减 2 格缩进);删除三行 include-*。用 Edit 工具每个文件点两下,10 个文件也就 20 次操作,5 分钟的事。
AI 的实际操作过程
第一轮:直接用 Python 字符串操作 YAML
AI 写了一个 Python 脚本,试图"聪明地"批量处理所有 YAML 文件——不用 YAML 解析库,而是用字符串拼接操作结构化文件。
结果:
- mongodb 配置块被从文件中间移动到了文件末尾(脚本的
data_block_end 计算 bug)
- 文件中的
mybatis 被破坏成 ybatis(首字符被截断)
Mybatis 变成 Mmybatis(多了一个字符)
- 注释行
# uri: 变成裸的 uri:,导致重复配置(无效 YAML)
第二轮:试图修复自己的 bug
写第二个脚本"修复"第一个脚本的问题。但因为第一个脚本已经把 mongodb 缩进改了,第二个脚本的匹配条件失效,什么都没做。
第三轮:删除 include-* 属性
用 content.replace('include-exception: true\n', '') 删除——但留下了空的 error: 节点和错位的缩进。比如 server 下的 servlet: 缩进变成了 14 个空格。
第四、五、六轮:反复救火
- 修复
ybatis → mybatis
- 修复
mmybatis → mybatis
- 删除空
error: 节点
- 修复
servlet: 缩进
- 补回丢失的
# 注释前缀
- 修复重复的
uri: 行
*最终结果:修了七八轮,`include-` 属性还是留在文件里没删掉。**
问题出在哪
1. 不理解 YAML 的缩进敏感性
YAML 靠缩进表示层级。AI 把它当纯文本处理,用字符串 replace、切片、拼接——完全没意识到缩进就是 YAML 的"括号"。
2. 不会用工具
Python 生态有 ruamel.yaml、PyYAML 等成熟的 YAML 解析库,可以安全地读写 YAML 结构。AI 偏要用字符串拼接,结果越改越乱。
3. 没有测试习惯
每写一个脚本就跑一次,跑完不验证就跑下一个。错误层层叠加,最后文件被改了七八轮,谁也分不清哪些是原始内容、哪些是脚本污染的。
4. 没有备份意识
操作 10 个文件之前不做备份。出了问题没法回退,只能"写新脚本修复旧脚本的 bug",陷入死循环。
5. 理解不了任务本身的简单性
这个任务本质上就是:
spring: spring:
data: data:
mongodb: → redis:
uri: ... mongodb:
uri: ...
减两格缩进的事。用 Edit 工具选中 mongodb 块,改缩进——结束。AI 偏偏要绕一大圈,最后还没改对。
反思
这不是模型聪明不聪明的问题——DeepSeek V4 Pro 在复杂推理任务上表现不差。问题在于 AI 缺少"工程直觉":它不知道什么操作是危险的、什么时候该停手验证、什么时候该简单粗暴而不是"聪明"。
一个人类开发者在面对这个任务时会:
- 打开文件,看到 YAML 结构
- 意识到"这就是改缩进,别用脚本"
- 用 IDE 的 Tab/Shift+Tab 一键完成
- 10 个文件 2 分钟搞定
而 AI 的路径是:
- 写 Python 脚本
- 字符串 replace
- 出错
- 写更多脚本修复
- 更多错误
- 循环直到用户崩溃
不仅 YML——Java 代码编译也翻车
YML 只是冰山一角。整个 Spring Boot 4 升级涉及约 10 个 Java 文件的编译错误修复,DeepSeek 改了 10 次,9 次存在编译错误:
| 轮次 |
操作 |
结果 |
| 1 |
修复 getBody() → getRequestBody() |
编译通过 |
| 2 |
修复 getStatusCodeValue() |
替换成功,但没注意到同一文件还有 APPLICATION_JSON_UTF8_VALUE 也要改 |
| 3 |
修复 APPLICATION_JSON_UTF8_VALUE |
替换成功,但没发现 HttpHeaders 的 containsKey 也要改 |
| 4 |
修复 containsKey |
又漏了 setConnectTimeout |
| 5 |
修复 setConnectTimeout |
日志量更用错——先用 Duration 参数(不行),再换成 HttpComponents 工厂(不行),最后才换 SimpleClientHttpRequestFactory(对了) |
| 6 |
修复 YML |
字符串脚本破坏缩进,越改越烂 |
| 7-10 |
救火 |
反复修自己制造的问题,每一轮都引入新 bug |
核心问题:DeepSeek 没有"改完立刻编译验证"的习惯。 它改一处就自信满满地认为问题解决了,完全不知道自己漏了别的错误、或者自己的修改引入了新问题。
与 Claude Code 的直接对比
同一个人、同一台机器、同一个任务。之前在别的项目上用 Claude Code 做过类似的升级,体验完全不同:
| 维度 |
Claude Code |
DeepSeek (本次) |
| 改完就编译 |
✅ 每次修改后自动编译验证 |
❌ 改完就认为对了,从不主动验证 |
| 编译报错处理 |
✅ 看到错误立刻定位修复,不遗漏 |
❌ 修一个漏一个,要用户反复提醒 |
| YML 操作方式 |
用精确的 Edit 工具逐文件替换 |
写 Python 字符串脚本批量破坏 |
| 出错后策略 |
换一种方式重新尝试,不重复犯错 |
用同样的错误方式反复救火 |
| 自我纠错 |
意识到搞砸了就停手,承认问题 |
闷头越改越烂,让用户越来越崩溃 |
| 10 次修改通过率 |
~90% |
~10% |
差距不在模型推理能力,在工程素养——Claude 被训练出了"改代码→编译→看报错→修正"的闭环习惯,而 DeepSeek 没有。一个开发者如果改完代码不编译就提交,你会直接开了他。DeepSeek 就是那个不编译就交差的开发者。
结论
对于结构化配置文件(YAML/JSON/XML),永远不要用字符串操作。 要么用该格式的解析库,要么用编辑器的精确替换。这是每个初级开发者都该知道的常识——而 DeepSeek 在这个任务中的表现,连初级都不如。
对于任何代码修改,改完必须编译验证。 没有这个闭环,AI 就不是"助手"而是"破坏者"。DeepSeek 10 次修改 9 次编不过,不是因为它不会修,是因为它不知道自己没修好。
日期:2026-07-07
模型:DeepSeek V4 Pro + Claude Code(作为 IDE Agent 框架)
任务:Spring Boot 4 升级(Java + YML 配置迁移)
结论:改了 10 次,9 次存在编译错误。YML 改到最后一行没删掉,还把好的改坏了。
对比:同样的任务用 Claude 自己的模型(Opus/Sonnet),会每次改完编译验证,基本一次过。