AI搬走COBOL,也搬走Bug

最新研究提醒开发者:AI可以把COBOL快速改写成Java,却未必理解隐藏在数据布局、批处理流程和数据库约束中的业务语义。迁移成功的标准不是代码能编译,而是系统行为经过独立验证。
AI迁移遗留系统,暴露了一个比幻觉更棘手的问题
**一篇于2026年7月底公开的预印本指出,AI在把遗留COBOL程序迁移到Java时,不仅会复制业务逻辑,也可能把原系统中的缺陷原样带进新系统。**截至2026年8月3日,这项题为《AI migrated legacy COBOL programs to Java, bugs included》的研究正在开发者社区引发讨论:生成代码能够编译、结构看起来更现代,并不意味着迁移已经正确完成。
**遗留系统迁移是把旧系统的业务能力、数据语义和运行约束转移到新技术栈的过程,而不是逐行更换编程语言。**COBOL到Java尤其容易让人误判,因为两种语言都能清楚表达条件、循环、计算和文件操作,表面上很像一次规模较大的“代码翻译”。真正困难的部分却通常不在代码文本里,而在COPYBOOK、JCL、数据库表、文件布局、作业调度、人工操作流程以及几十年积累下来的异常处理规则中。
**这项新研究最值得重视的结论,不是AI又写出了错误代码,而是AI可能非常忠实地复现错误行为。**如果迁移目标被定义为“让Java版本与COBOL版本输出一致”,那么源程序中存在了20年的边界条件错误,也可能被当成应当保留的行为。AI完成的是行为模仿,不是业务审计;它可以忠实,却不一定正确。

“Bug也一起搬”其实有三种情况
**COBOL迁移中的缺陷至少分为继承缺陷、翻译缺陷和环境缺陷三类,三者需要完全不同的排查方法。**把所有问题都归咎于“大模型幻觉”,反而会掩盖真正的工程风险。
| 缺陷类型 | 问题来源 | 典型表现 | 为什么普通单元测试抓不到 | |---|---|---|---| | 继承缺陷 | 原COBOL程序本来就有Bug | 特定日期、金额或账户类型下计算错误 | 测试用例来自旧系统输出,把错误结果当成标准答案 | | 翻译缺陷 | Java实现没有保持COBOL语义 | 小数精度变化、字段截断、分支路径遗漏 | 常规样本没有覆盖边界值和异常路径 | | 环境缺陷 | 数据库、编码、事务和调度环境变化 | 重复入账、乱码、批次状态不一致 | 方法级测试没有运行完整作业和事务链路 |
**继承缺陷是最容易被“高保真迁移”合法化的一类问题。**例如,一段COBOL程序在月末结息时对负数金额使用了错误的舍入规则,旧系统已经连续多年产生略有偏差的结果。如果团队把历史输入输出直接做成Golden Master测试,Java版本只要复现同一偏差就能全部通过。此时测试证明的是“新旧一致”,而不是“业务正确”。
**翻译缺陷通常藏在COBOL和Java不同的数值、内存与控制流模型中。**COBOL的PIC声明、COMP-3压缩十进制、隐式截断以及ROUNDED规则,与Java的int、long、double和BigDecimal并不存在天然的一一对应关系。一个金额字段在COBOL中可能拥有固定宽度和固定小数位,而Java的BigDecimal虽然适合金融计算,却需要显式处理scale、舍入模式和相等性;选错其中任何一个,代码仍然可以正常编译。
**环境缺陷往往比单个Java方法中的错误更危险。**一笔电汇是否正确完成,不只取决于金额计算,还取决于事务何时提交、失败后是否回滚、批处理能否重入、消息是否重复投递,以及数据库约束究竟由应用还是主机环境负责。把一个COBOL段落改写为干净的Java服务,只能说明段落被翻译了,不能说明整笔交易仍具备原来的原子性。
为什么代码干净、测试全绿,数据库仍可能出事
**编译成功只能证明Java语法和类型基本成立,不能证明业务语义成立。**大模型很擅长生成符合现代Java习惯的类、方法和异常处理,但“更像Java”有时恰恰意味着“更不像原系统”。原本依赖全局工作区、固定执行顺序和文件状态码的程序,被重构成对象、服务和异常后,隐藏的执行约束可能随之消失。
**单元测试全绿也可能只是因为测试边界画得太小。**迁移工具常以一个COBOL段落、程序或COPYBOOK为处理单位,测试则围绕生成的方法构造输入输出。现实中的主机程序却可能依赖前一个批处理任务写入的状态位、下一步JCL设置的返回码,或者某个操作员在凌晨手动重跑任务的惯例。这些依赖不在方法签名里,却属于系统真实语义的一部分。
**基于旧系统输出构造测试预言,会产生“Bug认证”问题。**测试预言是判断程序输出是否正确的规则;如果预言本身来自有缺陷的旧系统,新实现越忠实,反而越可能把缺陷固定下来。团队因此至少需要两套判断标准:一套验证新旧行为是否一致,另一套根据业务规则判断该行为是否正确。
**生产数据中的低频组合,是AI迁移最容易漏掉的区域。**一个交易模块可能在99.99%的正常输入下完全一致,只在闰年、负利率、超长姓名、冲正交易或跨日重跑时出错。对于每天处理1000万笔交易的系统,0.01%的错误仍然意味着每天1000笔异常;“准确率很高”在核心系统里未必是可接受指标。
百万Token上下文也装不下一个真实系统
**长上下文窗口是模型一次能够接收的文本范围,但它不等于模型已经建立了系统级理解。**即使模型能够输入数十万乃至上百万Token,一个运行数十年的主机系统仍可能包含数百万行源码、大量COPYBOOK、JCL、数据库定义、调度配置和外部接口文档。更重要的是,信息被放进上下文,不代表模型会在每一次决策中稳定调用它。
**“迷失于中间”效应会让模型对长上下文中部信息的利用能力下降。**关键字段定义如果位于提示词中间,模型可能在生成前几段代码时参考了它,却在后续重构中忽略其约束。迁移流程如果只是把仓库切块、检索若干相似文件再交给模型,检索系统还可能从一开始就没有找到真正相关的JCL或数据库脚本。
**COBOL系统的语义关系通常比源码文本更重要。**一个字段在哪里定义、被哪些程序修改、最终写入哪张表、由哪个批处理任务消费,构成的是一张依赖网络。普通文本检索擅长寻找相同词语,却不擅长回答“修改这个字段后,三个作业之后会影响哪一笔总账记录”。
知识图谱比“把整个仓库塞给模型”更靠谱,但不是银弹
**代码知识图谱是把程序、字段、数据库表、调用关系和数据流表示为节点与边的结构化系统地图。**在COBOL迁移场景中,它可以把某个Java方法追溯到原COBOL段落、COPYBOOK字段、JCL步骤和数据库列,使模型不必仅凭相似文本猜测依赖关系。
**图结构的价值在于把隐式上下文转成可以查询和验证的显式关系。**例如,团队准备修改客户余额字段时,系统可以先计算它被哪些程序读取、经过哪些批次聚合、是否参与对账,以及对应Java类中应当使用何种数值类型。这比向模型提问“请检查余额相关代码”更可控。
**知识图谱也不会自动识别一条业务规则究竟是需求还是历史Bug。**如果图谱只忠实记录了错误的数据流,它仍然只是一张更清晰的错误地图。图谱可以解决“依赖在哪里”的问题,却无法单独回答“这个依赖是否合理”。后者仍需要领域专家、会计规则、监管要求和真实业务案例参与判断。
| 迁移方式 | 上下文来源 | 主要优点 | 主要盲区 | 适合场景 | |---|---|---|---|---| | 逐段LLM翻译 | 当前代码片段 | 快、成本低、容易演示 | 丢失跨程序语义 | 原型、小型独立工具 | | 仓库检索增强 | 代码搜索与文档检索 | 能补充相关文件 | 检索不到就等于不存在 | 文档较完整的中型项目 | | 多Agent迭代 | 生成Agent与审查Agent互评 | 可反复修正结构和质量 | 多个Agent可能共享同一错误假设 | 半自动重构、人工审核流程 | | 知识图谱加约束 | 调用图、数据流、作业链路 | 影响分析更完整,可追溯 | 建图成本高,业务真值仍需人工确认 | 金融、保险、政府核心系统 | | 规则式转换器 | 语法树与固定映射 | 输出稳定、容易复现 | 难处理动态和隐式业务规则 | 语法规则明确的大批量转换 |
“93%准确率”并不能回答迁移是否安全
**迁移项目必须先定义准确率的分母和判定标准,否则跑分几乎没有决策价值。**另一篇2025年的COBOL到Java预印本曾报告,其方法使用包含5万份COBOL文件的语料,达到93%准确率,并将复杂度从18降至11.7、耦合度从8降至5.4。复杂度下降35%、耦合度下降32.5%,听起来很漂亮,但这些数字主要说明代码形态变得更现代,不能单独证明交易结果、数据状态和故障恢复仍然正确。
**结构质量和语义正确性是两个必须分别测量的维度。**Java代码可以拥有更低的圈复杂度、更清晰的模块边界和更少的依赖,同时把一个关键舍入规则改错。反过来,一段看起来仍然像“披着Java外衣的COBOL”的代码,也可能比彻底面向对象化的版本更容易保持行为一致。
**核心系统不应只报告总体通过率,还应报告高风险路径的零容忍指标。**金额守恒、账务平衡、幂等性、权限边界、事务回滚和审计日志不能被平均到普通分支里。1000个普通测试全部通过,也不能抵消1个重复扣款测试失败。
更可行的迁移流程,是让AI负责搬运,让证据负责验收
**安全的COBOL到Java迁移应当从系统考古开始,而不是从生成Java代码开始。**团队需要先建立程序清单、数据字典、调用图、批处理依赖和数据库读写关系,识别哪些规则来自正式需求,哪些只是历史行为。
**第一阶段应当冻结可观察行为并标记已知缺陷。**历史生产输入输出可以用来建立基线,但每个异常行为都要进入分类:必须保留、必须修复、需要业务确认,或者只为兼容过渡期暂时保留。没有这一层标记,Golden Master测试很容易变成Bug的长期许可证。
**第二阶段应当让AI生成带来源追踪的迁移结果。**每个Java方法都应能追溯到原始COBOL段落、字段定义和相关作业步骤;模型如果无法找到某项规则的来源,就应明确标记不确定性,而不是用一个看似合理的默认实现填空。
**第三阶段应当同时运行差分测试、性质测试和变形测试。**差分测试比较新旧系统在相同输入下的输出;性质测试检查金额守恒、借贷平衡等业务不变量;变形测试则通过系统性改变输入,验证输出是否满足预期关系。三种测试结合,才能区分“新旧不一致”和“新旧一起错”。
**第四阶段应当使用影子运行验证真实工作负载。**影子运行是在不影响正式结果的前提下,让Java系统接收与COBOL系统相同的生产流量,再比较输出、数据库变更和性能指标。对金融交易而言,比较对象不应只有最终返回值,还应包括事务边界、写入顺序、审计记录和失败恢复结果。
**第五阶段应当采用按业务能力切分的渐进替换,而不是一次性切换。**先迁移可观测、可回滚、依赖较少的模块,再逐步处理总账、结算等高风险路径。每次切换都必须有明确的回退方案,且回退不能依赖已经被新系统改写到不兼容状态的数据。
AI最有价值的角色,不是替代主机专家
**AI在遗留系统现代化中的现实价值,是缩短阅读、定位、解释和重复改写的时间。**它适合生成代码草稿、解释陌生段落、补充测试、整理数据字典、寻找相似逻辑和辅助影响分析,这些工作过去往往消耗大量人工时间。
**AI不适合独自决定哪些历史行为应当保留。**一个看起来像Bug的特殊分支,可能来自20年前的监管要求;一个长期没有触发的异常处理,也可能是为了应对极端灾备场景。模型能从代码中推测意图,却无法替代掌握制度背景的人给出最终裁决。
**多Agent审查能够提高覆盖率,却不能天然提供独立真值。**生成Agent和批评Agent如果使用相似模型、相同上下文和同一套测试,很可能对同一个错误假设达成一致。两个模型互相说“看起来没问题”,并不等于经过了独立验证。
**真正可靠的迁移交付物不只是Java仓库,还应包括一套可持续更新的系统地图。**调用图、数据流、业务规则、不变量、测试证据和迁移决策记录,都应随着Java系统继续演进。否则几年之后,新系统会再次变成没人敢动的遗留系统,只是语言从COBOL换成了Java。
结论:能编译只是起点,不是迁移完成
**这篇2026年7月的新研究给出的核心提醒很直接:AI可以提高COBOL到Java的代码转换速度,但不会自动判断被转换的行为是否值得保留。**代码搬过去、测试跑通、复杂度下降,都只是中间指标;数据没有被破坏、账务规则没有改变、异常路径可以恢复,才是迁移真正完成的证据。
**企业选择AI迁移工具时,最该追问的不是“每分钟能转换多少行”,而是“它如何证明跨文件、跨作业和跨数据库的语义没有丢失”。**如果答案仍然只是长上下文、向量检索和第二个Agent复查,那么它更像高效的代码生成器,而不是能够独立承担核心系统现代化的工程体系。
**COBOL的旧并不可怕,未经验证的现代化才可怕。**一段运行30年的代码至少已经暴露过大量问题;一段刚生成、结构优雅且所有人都过度信任的Java代码,反而可能把错误藏得更深。
参考来源
- 论文:《AI migrated legacy COBOL programs to Java, bugs included》,arXiv:2607.28271,2026年7月预印本;本文所述近期事件的主要来源。受链接域名要求限制,此处仅列论文编号。
- OpenHands GitHub仓库:用于了解软件开发Agent及多Agent迭代工作流的实现背景。
- Open Mainframe Project COBOL课程:用于核对COBOL、主机作业和企业遗留系统的基础技术语境。
- Reddit:开发者讨论COBOL到Java迁移经历:包含真实迁移项目中生成Java代码可读性与维护成本的社区经验。



