遗留系统不是让 AI 重写一遍:代码智能体驱动的行为保护式现代化

我参与过一次结算系统改造。代码库运行了十多年,核心模块混合了早期 Java、SQL 存储过程、定时脚本和少量后来补上的 Python 服务。团队最初给代码智能体的目标很直接:把一个 4200 行的账单模块拆成几个 FastAPI 服务,并补齐测试。模型在两天内生成了结构清晰的新工程,接口文档、类型提示和容器配置看起来都比旧系统规整。

问题出现在第一次历史账单回放。新服务按照文档计算的金额完全正确,却漏掉了旧系统中一个没有写进需求的规则:月底最后一个工作日生成的跨区订单,会把小数尾差归入主结算主体。这个行为隐藏在存储过程的一段 CASE 表达式里,只有财务对账脚本依赖它。重写后的代码通过了新编写的单元测试,却与生产运行十年的实际契约不一致。

这次经历改变了我对代码大模型参与遗留系统现代化的判断。真正稀缺的不是生成新代码的速度,而是识别旧系统已经形成的行为、数据和运维契约。没有这些契约,DeepSeek-Coder、Qwen2.5-Coder、Llama-3.3 或其他模型都可能把"难看但有意义"的逻辑整理掉。模型越擅长生成整洁实现,团队越容易忽略这些不显眼的边界。

后续方案不再以"重写模块"为单位,而是先建立行为地图,再把迁移拆成可验证的 Migration Slice。每个切片只改变一小段调用路径,带着输入样本、旧系统输出、允许差异和回退入口;代码智能体负责分析候选依赖、生成特征测试和提出补丁,确定性的编译、回放与对账系统负责决定能否进入下一阶段。

本文选择方向 A 中的遗留系统现代化作为主线。关键词采用代码大模型、AST 静态分析、FastAPI、Docker 沙箱、开源项目和 AIGC;不讨论 IDE 补全、自动写 Commit 或通用 CI/CD 效能。创源AIGC只作为可替换的模型中继节点参与代码分析,业务契约、回放证据和发布决定始终掌握在团队自己的工程系统中。

一、先停止"重写":迁移成功的对象是行为,不是代码行

遗留系统常被描述为技术债,但它也是多年业务决策的沉积层。重复判断、奇怪字段、无法解释的延迟任务和看似多余的 SQL,可能来自税务规则、历史兼容、客户合同或一次已经被遗忘的事故。删除这些代码也许能让静态扫描分数变好,却不一定让业务系统更正确。

因此,现代化工作的第一项指标不应是减少多少代码、引入多少微服务,而应是被保护的业务行为比例。行为包括接口响应、金额计算、状态变化、数据库写入、消息顺序、批处理时间、错误码、权限判断和运维操作。只有明确哪些行为必须保持、哪些允许改变、哪些本来就是缺陷,团队才知道模型生成的"优化"是否成立。

我把迁移目标分成四类:

目标类型 典型任务 验收重点
等价迁移 更换语言、框架或运行时 对外行为和数据结果保持一致
受控修复 修正已确认的历史缺陷 只改变批准的场景,其他行为不漂移
解耦拆分 从单体抽出服务 调用契约、事务边界和故障语义明确
能力替换 用新组件替代自研实现 性能、数据格式、回退路径同时验证

这四类目标不能混在同一个迁移切片中。若一边更换框架,一边修复金额逻辑,再顺手调整数据表,回放出现差异时很难定位原因。更稳妥的做法是先完成等价迁移,再用独立变更修复业务缺陷;每次只让一种差异拥有合法解释。

"行为保持"也不是要求复制所有旧问题。团队需要建立差异分类:expected 表示业务批准的新行为,tolerated 表示时间戳、随机标识等允许变化,unexpected 表示必须阻断的未知差异,unknown 表示证据不足。模型可以帮助解释差异,但不能自行把 unexpected 改成 expected。

项目开始时应明确不迁移的范围。无人使用的报表、已经冻结的客户接口和将在数月内下线的功能,可能只需维持现状;把它们全部重写会扩大成本和风险。遗留系统现代化不是一次全仓库清洁,而是根据业务寿命选择值得保护和迁移的路径。

行为目录还应包含非功能契约。旧系统可能在接口慢时返回缓存结果,在批处理高峰限制并发,在依赖不可用时把任务放入人工队列。这些不是响应 JSON 能表达的行为,却会影响新实现的容量和故障半径。契约中记录超时分层、最大查询数、内存上限、重试次数、降级结果和人工接管条件,防止新服务"功能正确但运行方式完全不同"。

权限与审计也是行为。旧系统可能通过数据库视图隐藏字段,通过批处理账号区分业务线,或在特定操作后生成审计票据。迁移后若只复制业务返回值,却让服务账号获得更大的表权限,等价性仍然失败。权限快照、数据访问字段和审计事件应和接口结果一起进入迁移基线。

行为目录需要 owner 和有效期。财务规则由结算负责人确认,接口兼容由调用方确认,运行约束由平台负责人确认。长期无人认领的行为不能默认为必须保留,应进入澄清队列;否则团队会花大量时间保护一个已经没有消费者的偶然实现。每季度复核高风险契约,确保它们仍与当前业务相符。

二、从生产事实建立行为地图:特征测试、流量样本与时间规则

需求文档通常只能描述系统希望如何工作,生产数据才记录它实际如何工作。行为地图需要同时收集接口、数据库、消息和调度四类证据。接口层记录规范化请求、响应、错误码和延迟区间;数据库层记录读写表、关键查询和事务结果;消息层记录 Topic、Schema、顺序与重复处理;调度层记录运行时间、时区、节假日和补偿任务。

对缺少测试的模块,先写 Characterization Test,也就是特征测试。它不判断旧行为是否合理,只把当前可观察结果固定下来。测试输入来自脱敏后的历史样本、边界值和事故案例,输出经过规范化后保存为快照。等业务负责人确认后,快照才升级为迁移契约。

下面是一份简化的行为契约,它将月底规则、金额精度和允许变化分开记录:

yaml 复制代码
contract_id: billing-month-end-v3
scenario: cross_region_order_on_last_business_day
input_fixture: fixtures/order_20318.json
clock:
  timezone: Asia/Shanghai
  instant: 2026-07-31T15:40:00+08:00
expected:
  currency: CNY
  rounding_scale: 2
  remainder_owner: primary_entity
tolerances:
  generated_at_ms: 2000
ignored_fields:
  - trace_id
  - worker_id

时间是遗留迁移中最容易漏掉的输入。代码可能读取系统时间、数据库时间、任务计划时间和消息产生时间,不同节点的时区又不相同。测试环境应注入可控时钟,覆盖月末、年末、闰日、夏令时、节假日和跨日重试。若只回放普通工作日数据,许多真正危险的规则永远不会出现。

外部依赖也要固定。旧系统调用税率、汇率、会员和库存服务时,测试不能继续请求当前线上数据,否则同一输入每天得到不同结果。回放环境为外部依赖建立录制响应或合成替身,并记录响应版本。需要验证真实集成时再进入独立的契约测试,不把网络波动混进代码等价性判断。

特征测试还要覆盖失败路径。超时后是否重试、数据库死锁时是否回滚、消息重复时是否幂等、下游返回空字段时使用什么默认值,这些行为往往没有写进正常测试,却直接决定生产稳定性。新实现若只复制成功路径,流量一旦遇到异常就会表现出完全不同的故障语义。

生产样本不能原样进入测试仓库。采集器先做字段投影、脱敏和一致性校验,保留关系结构但移除个人信息与密钥。样本带 source_version、schema_hash、captured_at 和 retention,过期后重新生成。模型只读取满足代码分析需要的最小片段,不接触完整生产数据。

样本集要避免只包含"最常见请求"。高频普通订单可以验证吞吐,却不一定覆盖历史包袱。采样器同时保留长尾租户、极端金额、空字段、重复消息、跨月订单和手工修复后的记录。每个样本关联选择原因,避免数据清理后只剩一批容易通过的正常案例。

批处理回放不能只比较最终文件。需要记录分片规则、游标推进、失败重入、并发度和完成标记。新任务可能更快生成相同结果,却在进程重启后重复处理最后一页;也可能因为并行顺序改变而违反下游消费约束。回放器应在随机中断点停止并恢复,验证任务能否从持久化检查点继续。

数据库模式也属于输入。特征测试固定 schema_hash、索引、约束和触发器,测试前自动创建临时实例并执行历史迁移脚本。只在最新空库上运行测试,会漏掉真实环境中由长期升级形成的数据状态。对于无法完整复制的数据库,至少保留脱敏模式、统计分布和关键约束。

行为地图并非一次性文档。生产出现新分支、人工补偿或事故修复后,相应样本和契约要进入回归集。新增样本先在旧实现与当前新实现上双跑,确认差异分类,再纳入基线。否则迁移项目持续数月后,早期行为地图会逐渐落后于仍在变化的单体系统。

三、依赖切片:用调用图、数据血缘和变更半径选择迁移单位

按目录或类名拆服务,往往会切断真实的业务边界。一个账单函数可能调用折扣规则、读取客户等级、写入结算表并发送通知;另一个看似相邻的函数却属于完全不同的批处理流程。迁移单位应由调用路径和数据所有权决定,而不是由文件结构决定。

依赖切片包含五张图:静态调用图、运行时调用图、数据库读写图、消息流图和配置依赖图。AST 静态分析擅长发现显式调用与导入,运行时 Trace 补充反射、动态代理和配置分支;数据库审计发现存储过程与触发器;消息目录说明异步消费者;配置图记录功能开关和租户覆盖。

图不是越大越好。团队需要从一个业务入口向下遍历,只保留与目标行为相关的节点,并标记 side_effect、state_owner、latency_budget 和 change_frequency。高频变化但无状态的转换逻辑适合先抽离;跨多表事务、强顺序消息和无法回放的外部副作用应留到后期。

每个候选切片计算变更半径:

变更半径 = 直接调用者数量 + 跨域数据表权重 + 外部副作用权重 + 未覆盖分支权重 + 回退难度。

这个公式不追求一个普遍正确的分数,而是迫使团队把风险来源写出来。两个代码量相近的模块,若一个只做格式转换,另一个负责扣款和消息发布,迁移顺序显然不同。模型可以从代码和 Trace 中生成候选值,但权重由系统负责人确认。

动态语言、反射和字符串拼接会让静态调用图不完整。对于无法解析的调用点,不应随意归到"无依赖",而应标记 unresolved,并通过运行时探针、日志或人工阅读补证据。未解析比例超过阈值的切片保持只分析状态,不能直接进入自动改造。

数据库依赖要区分读取者和所有者。多个模块读取同一张表,不代表它们都拥有表结构;迁移时先定义唯一数据 owner 和可用的读取契约。若新服务继续直接写旧表,同时旧单体也在写,很容易出现双写顺序、锁和回滚问题。早期切片优先采用单写者原则,新服务通过适配层读取或提交命令。

配置依赖常常比代码依赖更隐蔽。租户开关、环境变量、数据库参数表和运维脚本都可能改变同一调用路径。分析器将配置键映射到读取位置和生效范围,并在回放时固定配置快照。未登记的动态配置读取视为 unresolved,不能让模型根据默认值推断生产行为。

依赖图还要区分强依赖和观察依赖。同步调用、事务表和必须成功的消息属于强依赖;指标、审计副本和异步报表可能属于观察依赖。拆分时先保护强依赖,再验证观察依赖是否允许延迟或丢弃。将所有边都视为同一强度,会得到无法拆分的巨大组件;忽略观察依赖,又会让运营和审计在上线后才发现数据缺口。

切片排序可以采用"业务价值除以变更半径",但不应机械按分数执行。一个低风险切片如果依赖尚未准备好的数据 owner,仍要等待;一个高价值切片若没有回退入口,也不应作为首批试点。排序结果只是决策输入,最终计划要写明前置条件和阻塞项。

四、Migration Slice:给代码智能体一个可以完成且可以拒绝的任务

整仓库 Prompt 很难形成稳定结果。上下文太大时,模型会忽略局部规则;目标太宽时,它可能同时改接口、数据结构和测试。Migration Slice 将任务收窄为一个入口、一组行为契约、一个允许变更目录和一条回退路径。

一个切片至少包含:

字段 作用
entrypoint 本轮需要迁移的业务入口
behavior_contracts 必须通过的特征测试与契约测试
allowed_paths 模型允许提出修改的目录
forbidden_dependencies 禁止新增的跨域依赖
data_ownership 可读、可写与只经适配器访问的数据
risk_budget 最大文件数、最大接口差异和最大性能退化
rollback_adapter 新旧实现切换或回退方式

代码智能体先输出分析报告,不立即修改文件。报告说明调用路径、未解析依赖、建议目标结构、可能漂移的行为和需要补充的测试。若未知依赖过多,它应该返回 blocked_by_evidence,而不是用推测补齐。能够明确拒绝证据不足的任务,比生成一份看似完整的重构更有价值。

第二步才是补特征测试。测试必须先在旧实现上通过,再冻结为基线;如果测试本身无法通过,说明对旧行为的理解有误。第三步生成最小补丁,限制文件数量和公共 API 变化。第四步在 Docker 沙箱中构建、执行测试并记录差异。第五步由规则和人工决定是否进入双跑。

任务上下文按证据分层:核心代码与契约直接提供,调用图和数据血缘通过检索引用,历史讨论只提供经过确认的结论。模型不能读取整个聊天历史来决定业务规则,因为旧讨论可能已经失效。每条关键约束关联 owner、版本和来源,便于发现冲突。

不同模型可以承担不同子任务。Qwen2.5-Coder 或 DeepSeek-Coder 用于代码解释与候选补丁,Llama-3.3 可以在内部环境中处理较通用的文本归纳,本地规则引擎负责路径、Schema 和风险预算校验。模型名称只是可替换的执行选项,任务契约不应绑定特定模型的输出习惯。

切片进入执行队列前需要冻结 manifest。代码提交、契约版本、样本摘要、依赖图、工具版本和模型路由都写入 manifest;执行过程中仓库发生新提交时,本轮结果不自动合并。智能体可以基于新版本重新分析,但必须生成新的 slice_version。这样能避免测试针对旧代码、补丁却落到新分支的隐性漂移。

并行切片要检查冲突。两个任务即使修改不同文件,也可能共同改变同一接口、数据库表或消息 Schema。调度器根据依赖图建立资源锁,冲突切片串行执行;低风险只读分析可以并行。模型无法通过把改动移动到另一个文件绕过资源冲突,因为锁绑定业务资源而不是路径。

人工评审界面应围绕证据组织。先展示行为契约和未解析依赖,再展示语义差异、测试结果和补丁;大段模型解释放在最后。评审者可以批准、拒绝或要求补证据,但不能直接在结果页放宽风险预算。需要扩大范围时,回到任务定义生成新版本。

切片完成后不立即删除旧适配器。将契约、回放样本、差异记录和回退入口打包为 migration_record,至少保留到观察期结束。后续若同一调用路径出现问题,可以从记录还原当时的输入、工具和判断,而不是只看到一个已经合并的提交。

五、双跑不是复制请求:需要比较状态、副作用与时间窗口

新旧实现同时处理同一输入,是验证等价性的核心方法,但不能简单把生产请求发两遍。查询类请求可以直接双跑,写操作则可能造成重复扣款、重复消息和库存变化。更安全的方式是主路径执行真实副作用,影子路径使用副作用捕获器,把数据库写入、消息发布和外部调用转成可比较的事件。

比较器先规范化输出。时间戳按容忍窗口比较,随机 ID 映射为占位符,列表按业务键排序,金额使用 Decimal 和明确舍入规则,错误码映射到内部语义。文本相似度只适合说明性字段,不能用于金额、权限和状态判断。模型可以解释复杂文本差异,但确定性字段必须精确比较。

副作用事件包含 operation、resource、before_hash、after_hash、business_key 和 sequence。旧系统真实写入后,采集器生成事件;新系统在影子存储中执行同样事务,生成另一组事件。比较器检查事件数量、顺序、资源和状态变化。只比较 HTTP 响应,会漏掉响应相同但数据库或消息不同的情况。

批处理需要按窗口比较。旧系统可能逐条处理,新实现可能并行聚合,单条事件顺序不同但最终账本一致。契约应声明 order_sensitive 或 window_equivalent:前者逐事件比较,后者在固定时间窗内比较集合与总量。不能由比较器在发现差异后临时放宽规则。

双跑还要控制性能影响。影子任务使用独立队列和资源上限,不能抢占生产线程池;敏感请求先脱敏或只传引用;外部依赖通过录制替身;影子结果按数据策略过期。若旧系统本身接近容量上限,先离线回放历史流量,不要直接复制线上压力。

差异进入四级处理:P0 为金额、权限、状态和不可逆副作用差异,立即阻断;P1 为关键错误语义和消息顺序差异,需要负责人确认;P2 为性能或非关键字段差异,可进入设定期限的观察;P3 为已声明的随机值和格式差异,自动容忍。所有容忍规则有截止时间,避免临时例外永久留在系统里。

比较器本身也要有测试。给它输入已知金额误差、字段顺序变化、重复消息和容忍时间戳,确认分类符合预期。若比较器错误地忽略字段,所有双跑都会显示绿色;若它把随机标识当成关键差异,团队又会被大量噪声淹没。每个 comparator 绑定契约版本,规则变更先回放历史差异。

影子存储应与生产隔离,但保持约束一致。数据库使用相同字段类型、唯一键和触发规则,消息捕获器保留分区键与顺序,外部调用模拟器校验 Schema 和额度。只把副作用记录成一行文本,无法发现事务、幂等和顺序问题。影子环境缺少的能力要写入 fidelity_report,审批者据此决定是否还需人工测试。

双跑比例从离线样本、录制流量、内部请求逐步扩大到真实流量。每个阶段设置最大资源消耗和退出条件,影子队列积压或比较延迟超过阈值时自动降级,不影响主路径。双跑的目标是发现差异,不应成为新的单点故障。

六、模型接入层只提供分析能力,不拥有迁移事实

代码分析可能需要在本地模型、云端模型和统一中继之间切换。自建开源项目能控制日志与版本,但要维护推理资源和适配器;外部模型降低启动成本,却需要数据边界和合同审查。创源AIGC可以作为一个兼容中继节点参与脱敏代码片段的联调,不直接接触生产样本、数据库凭据或发布权限。

业务侧只声明任务能力、数据等级和成本上限,不在 Migration Slice 中写供应商名称:

yaml 复制代码
code_analysis:
  protocol: openai-compatible
  base_url: https://178.nz/yinc/v1
  api_key_env: MODERNIZATION_MODEL_TOKEN
  model_alias: legacy-code-analysis-route
  data_class: redacted_source
  tool_calls: disabled
  max_context_files: 24
  timeout_seconds: 50

发送到模型前,适配器移除密钥、个人数据、内部域名和无关文件,只保留目标切片、接口定义与契约摘要。文件路径映射为仓库内相对路径,Prompt 中不包含可写凭据。模型返回的是分析或补丁候选,写入工作区前还要经过路径白名单、补丁大小和语法检查。

模型调用记录保存 request_fingerprint、model_alias、adapter_version、policy_version 和 usage,不保存完整源码到普通日志。需要复盘时,从受限证据仓按任务 ID 获取原始片段。供应商或中继日志策略不满足数据等级时,任务自动路由到本地模型或转人工,而不是降低数据标签。

接入验收用同一批脱敏切片比较:依赖识别召回率、契约遗漏数、补丁语法通过率、首次测试通过率、错误自信率和单位有效切片成本。错误自信率指模型给出确定结论但证据证明错误的比例,它比回答长度或代码行数更能反映遗留分析风险。

更换模型时不重写任务模板。先回放冻结的分析与补丁样本,比较结构契约和行为测试,再给少量低风险切片使用新路由。若新模型产生更大补丁或更频繁修改公共接口,即使测试通过,也应先调整风险预算,而不是直接提高权重。

模型输出还要检查引用完整性。分析报告中的每个关键结论关联文件、符号、Trace 或契约编号;没有引用的业务推断标记为 hypothesis。后续补丁不能把 hypothesis 当成确定规则。这样的约束会让报告更短,却能区分代码事实和模型猜测。

长上下文并不等于应该发送更多文件。适配器先根据依赖切片选择核心代码,再按模型请求补充相邻符号;每轮增加文件都记录理由和数据等级。超过上下文预算时,优先保留契约、接口与副作用路径,删除重复实现和无关测试。把整个仓库压缩后发送,通常会降低关键规则的可见度。

成本按"有效迁移切片"核算,而不是按调用次数。模型费用、检索、沙箱、回放、人工评审和失败返工都进入同一账本。便宜模型如果产生大量无证据补丁,最终成本可能更高;强模型若只用于高风险分析,整体成本反而可控。路由策略依据任务阶段调整,而不是所有步骤固定使用同一模型。

七、AST 变换与确定性工具:让模型提出意图,让编译器执行机械修改

模型适合理解上下文和提出重构意图,不适合逐字符完成所有机械修改。改包名、替换废弃 API、增加参数、迁移注解和更新导入,可以交给 AST 或语言服务器完成。确定性工具保留语法结构,能生成可预测差异,也更容易在大量文件中重复执行。

例如,把 Python 中旧的同步适配器调用改成统一包装器时,可以先用 AST 查找目标调用,拒绝参数形态不符合预期的节点,再生成修改清单。下面的检查器只发现候选,不直接写文件:

python 复制代码
import ast
from dataclasses import dataclass

@dataclass(frozen=True)
class Candidate:
    line: int
    function: str
    arg_count: int

class LegacyCallVisitor(ast.NodeVisitor):
    def __init__(self) -> None:
        self.candidates: list[Candidate] = []

    def visit_Call(self, node: ast.Call) -> None:
        if (
            isinstance(node.func, ast.Attribute)
            and node.func.attr == "calculate_legacy"
        ):
            self.candidates.append(
                Candidate(node.lineno, node.func.attr, len(node.args))
            )
        self.generic_visit(node)

def find_candidates(source: str) -> list[Candidate]:
    tree = ast.parse(source)
    visitor = LegacyCallVisitor()
    visitor.visit(tree)
    return visitor.candidates

模型读取候选列表和附近语义,判断哪些属于本次切片;确定性变换器再执行批准的修改。遇到动态调用、反射或参数不完整时,工具生成 unresolved 清单,不尝试猜测。这个分工将"理解业务"和"修改语法"分开,减少模型因为格式或遗漏造成的大范围噪声。

AST 检查之外还要验证 API Surface。迁移前后导出公共函数、类、路由、事件 Schema 和数据库迁移,计算差异。未在契约中声明的公共接口变化直接阻断。对 Java 使用编译器符号与字节码兼容检查,对 Python 结合类型检查和运行时导入测试,不能只依赖文本 diff。

依赖升级也应独立处理。代码智能体可能为了使用新语法顺手升级框架,导致行为变化来自依赖而不是补丁。Migration Slice 锁定依赖摘要;如确需升级,先建立独立切片,回放安全公告、兼容性测试和启动性能。框架现代化与业务迁移同时发生,会让故障定位成倍复杂。

格式化、生成代码和锁文件属于可再生工件。它们可以在补丁通过后统一生成,不要让模型直接编辑。评审界面将语义变更与机械变更分开,负责人优先查看业务路径、数据访问和契约差异,避免几千行格式变化掩盖一处关键判断。

八、评测代码智能体:不要用生成速度替代迁移质量

为了评估这种流程,我会从历史缺陷和已完成迁移中构建脱敏切片集,覆盖金额、状态机、批处理、数据库访问、消息消费和外部适配六类任务。样本按时间切分,模型调试集与最终验收集分开,防止为已知答案优化 Prompt。

比较三种流程:人工阅读后直接重构、代码智能体一次性生成新模块、行为保护式 Migration Slice。下面的数值用于说明评测结构,不能视为对具体模型的普遍结论:

流程 契约通过率 未解释差异/切片 中位完成时间 平均补丁文件数 回退成功率
人工直接重构 82% 1.4 3.8 天 12.6 91%
智能体一次生成 61% 3.7 0.9 天 28.4 68%
Migration Slice 94% 0.5 2.1 天 8.3 98%

一次生成最快,却更容易扩大文件范围并产生未解释差异;行为保护式流程增加了契约采集和双跑成本,但把风险集中在小切片中。人工流程在熟悉模块时仍有优势,尤其是样本稀少或业务规则无法机器表达的场景。评测的目标不是证明模型替代工程师,而是找出在哪些步骤能减少重复分析。

质量指标至少包括依赖召回率、特征测试有效率、契约通过率、未解释差异、补丁半径、构建稳定性、性能退化、人工复核时间和回退成功率。单看测试通过率会被薄弱测试误导;单看代码量会奖励过度修改;单看完成时间会忽略后续事故与返工。

特征测试有效率可以用变异测试验证:主动修改金额舍入、边界日期和错误处理,观察测试能否失败。若模型生成了大量测试但无法捕获这些变化,覆盖率再高也没有保护作用。对关键切片,应从真实历史缺陷中抽取反例,而不是只测试模型自己生成的实现。

性能比较固定数据集、并发、缓存和运行时版本,分别记录吞吐、P95 延迟、数据库查询数、内存和批处理完成时间。新实现平均速度更快,但尾延迟或查询放大超过预算时不能上线。遗留现代化的目标是持续交付,不是用一个漂亮的平均值换取新的生产瓶颈。

九、绞杀式发布与停止条件:现代化要允许系统长期处于过渡态

大多数遗留系统无法在一个版本中全部替换。绞杀式迁移让新旧实现长期并存:入口根据租户、业务键或功能开关选择路径,旧系统继续承担未迁移部分,新服务逐步接管经过验证的切片。这个过渡态需要被当作正式架构维护,而不是临时桥梁。

路由必须保持会话和业务键一致,同一订单不能在流程中途切换实现。新路径上线前先离线回放,再做无副作用双跑,然后开放内部租户、低风险租户和小比例生产流量。每次扩大范围都要求契约差异、性能、错误预算和回退演练同时通过。

回退入口在发布前验证。配置切回旧路径不代表回退完成,还要确认新服务是否已经写入数据、发布消息或改变缓存。若存在新旧数据格式,需要双向兼容层或明确的不可回退点。到达不可回退点前必须有人工确认,不能让自动扩量跨过。

过渡期最容易产生"双重真相"。接口由新服务处理,报表仍读旧表;订单状态写入新库,客服工具却查询旧缓存。每个 Migration Slice 维护 source_of_truth 和 read_path,仪表盘显示新旧路径覆盖比例与数据延迟。没有明确所有者的同步任务,应被视为迁移风险而不是普通后台作业。

停止自动化的条件包括:未解析依赖超过预算、特征测试无法捕获已知缺陷、双跑出现 P0 差异、影子资源影响生产、模型持续扩大补丁范围、回退演练失败或业务负责人无法解释关键规则。停止不等于项目失败,而是说明当前切片缺少进入下一阶段的证据。

有些系统不值得用代码智能体现代化。即将退役、业务量极低、缺少可运行环境或法律上不能复制数据的系统,可能更适合封装接口和维持最低维护;实时控制、资金核心和硬件耦合模块若无法建立高保真回放,也应以人工审查和形式化验证为主。模型不是所有技术债的清理工具。

遗留系统现代化的关键,不是把旧代码快速翻译成新框架,而是将隐藏多年的行为变成可以验证、讨论和迁移的契约。代码智能体负责探索依赖、补充测试和提出局部实现,AST 工具负责机械修改,双跑系统负责暴露差异,工程师负责确认业务含义与风险。只要迁移单位足够小、证据足够完整、回退入口始终可用,旧系统就能在不中断业务的前提下逐步退出,而不是被一次充满不确定性的"重写"替代。

相关推荐
MomentYY2 小时前
RAG 图检索&多跳推理:有些答案需要“顺藤摸瓜”
人工智能·agent·ai编程
爬楼的猪3 小时前
跟着AI Agent学powershell
windows·ai编程
程序员黑豆3 小时前
Java变量详解:从入门到精通
java·前端·ai编程
用户3126874877203 小时前
用 Agent 帮我买房:小区数据采集、测评图文生成全流程
ai编程
爱吃的小肥羊3 小时前
用时1.5天,Claude突破了黎曼猜想的新纪录
aigc·openai·ai编程
月弦笙音3 小时前
为什么程序员大多都拥抱 AI,而音乐人却抗拒并隔离 AI 音乐池?
ai编程
朦胧之4 小时前
AI 开发
ai编程
倾听醉梦语4 小时前
React/Vite/Next.js 前端开发工具 SpotPatch:点击页面元素精准定位 JSX/TSX 源码
javascript·react·ai编程·vite·next.js·前端开发工具
zandy10115 小时前
8款主流编程软件,五个维度深度解析——2026年AI编程工具技术选型
agent·ai编程