让ChatGPT、Codex改造老项目时,最值得警惕的是单测全绿、性能更好,却可能改错业务结果的核心重构。
例如一个用了六年的计费模块,代码里混着会员折扣、阶梯价、优惠券、税费和特殊合同。Codex把几千行判断拆成规则对象,接口也变快了。
开发环境跑完测试,全部通过。新旧接口对几组样例算出来的金额也一致。团队准备切换时,却在一批真实历史订单上发现:旧逻辑算出99.90元,新逻辑算出99.89元;还有一些订单相差几元,集中在叠加优惠和跨月结算场景。
两边都没报错。代码正确运行,不等于账单金额正确。
所以核心计费、结算、权限计量这类重构,我更建议在正式替换前增加一道验证:新旧逻辑双跑对账(Dual Run / Shadow Run)。但双跑不是简单把两套代码都执行一次,而是要保证不会因此多扣一次钱、重复入账或者发两次通知。
一、测试全部通过,为什么仍然不能直接替换?
单元测试主要验证开发者已经写出的规则。问题是,老系统里可能还有没人写进测试的隐含行为。
某个大客户有历史协议价;春节期间的优惠叠加顺序与平时不同;老订单要沿用下单时的税率;金额分摊后的尾差落在最后一项;某个早已下线的活动仍影响存量退款。
这些规则不一定在需求文档里,却已经反映在真实业务结果中。Agent重构时按"更合理、更清晰"的方式重新实现,可能反而改变原有语义。
但也不能倒过来认定旧代码永远正确。旧实现也可能有Bug。双跑不是盲目复制旧行为,而是把差异找出来,决定保留、修正还是迁移。
二、最容易出现差异的三类计费细节
第一类是规则执行顺序。比如订单先打会员九折,再减10元优惠券,与先减券再打九折,结果可能不同。重构时把条件整理得更漂亮,不代表顺序可以互换。
第二类是金额精度与舍入。先对每个商品行计算税费并四舍五入,再求和;与先求全单税额,最后统一舍入,可能相差一分钱。BigDecimal虽然能避免一部分浮点误差,但计算精度、scale、舍入方式和分摊规则仍然需要明确。
第三类是外部状态。商品价格、汇率、税率、优惠券剩余额度和客户等级可能随时间变化。旧代码算价时读取的是昨天的配置,新代码隔了几秒读取到更新后的配置,即使两套公式完全相同,也可能得到不同金额。
所以遇到差异先问:两套实现拿到的输入、规则版本和业务时点是否一致?
三、双跑到底怎么做,才能避免重复扣款?
正式业务请求进入系统后,旧计费实现先继续作为主路径,负责生成用户实际看到的报价和最终结算结果。
同一份输入快照,再交给新实现计算一份影子结果。新实现只计算,不提交账务、不扣费、不调用支付、不发消息,也不触发优惠券核销。两份结果进入独立的对比流程,记录差异供分析。
可以把关键关系理解为:
真实请求 → 旧逻辑正式结果 → 正常返回与入账;相同输入快照 → 新逻辑只读计算 → 差异记录。
对于简单、无副作用的计算器,可以在同一请求内短暂双算;但对于耗时复杂的计费链路,更适合用有界异步任务或抽样离线回放,避免影子计算拖慢主请求。
特别要注意:不是把新系统接到"测试支付账户"就万无一失。有些副作用发生在库存、账务流水、MQ、通知甚至外部风控中。安全双跑要求从执行能力上隔离副作用,而不是只约定开发人员不要写入。
四、真正决定对账结果的,是同一份输入快照
很多团队已经做了双跑,却发现差异数量异常多,原因是两套逻辑根本没有计算同一个业务状态。
例如旧系统计算订单时读取客户等级为VIP;新系统异步计算时,客户等级刚好调整为普通会员。又或者旧路径使用10月的税率,新路径查到了11月的新配置。
这类差异不是算法Bug,而是输入漂移。为了让对账有意义,至少需要固定订单明细、优惠券状态、客户等级、价格版本、税率版本、汇率以及计费时区和业务时间点。
用不可变请求快照记录规则与配置版本。敏感数据只保留对账必需字段。
如果新旧实现都要查外部系统,还应该评估是否能够把查到的有效业务参数先归一化成一份快照;否则"同时调用"也不保证数据相同。
双跑首先要证明输入相同,然后比较输出。
五、不要只对比最终金额,要能解释差异来自哪里
假设两套逻辑都返回总价99.90元,但一个把优惠分摊到商品A,另一个分摊到商品B。当前总额一致,后续按单品退款时却可能算出不同结果。
因此差异记录最好同时包含:订单或事件标识、规则版本、币种、应付总额、税费、折扣、分摊金额和关键的计费明细。比如:
bill=ORDER-1048 rules=old-v3/new-v4
subtotal=120.00/120.00
coupon=20.00/20.00
fee=0.00/0.01
payable=100.00/100.01
reason=pending_review
记录差异以后,不要直接按"金额只差0.01元"全部忽略。少一分钱是否允许,取决于业务规则、合同要求、会计处理及后续退款影响。
我更建议把差异分成三类:
实现缺陷:新逻辑遗漏老规则或计算错误,需要修复。
预期变化:经过业务确认,要主动修正旧规则,必须有明确迁移方案。
不可比差异:输入版本不同、快照缺失或数据质量问题,需要先修复对账条件。
差异没有归因,跑再多订单也无法提高上线把握。
六、双跑不应该一次性覆盖全量生产流量
计费属于核心链路,验证也要考虑对生产的影响。影子计算可能消耗数据库连接、CPU和额外缓存,某些复杂报价还会放大下游查询。
我的建议是分阶段推进。
第一阶段:历史样本回放。 用脱敏且覆盖主要业务类型的历史订单测试,先处理已知差异。
第二阶段:低比例影子流量。 旧结果继续对外生效,新结果只用于对账。设置抽样比例、任务队列上限和失败丢弃或延后策略,绝不能让影子任务拖死正式计费。
第三阶段:分场景扩大验证。 重点覆盖阶梯计费、组合优惠、跨月结算、退款和特殊合同,而不只是扩大订单数量。
第四阶段:灰度切换。 对已验证的客户或业务范围采用新实现,保留快速回退开关,继续核对账务和售后结果。
这样才能逐步建立上线证据。
七、怎么判断双跑已经达到上线条件?
如果只用一个指标,我建议采用 Dual-run Result Diff Rate(双跑结果差异率)。
计算方式是:可比较样本中,新旧结果不符合既定一致性规则的订单数 ÷ 可比较订单总数。
比如10000笔有效样本中,有37笔出现需要解释的差异,那么差异率为0.37%。这只是团队自定义的工程指标,不存在所有业务通用的安全阈值。
计费系统尤其不能只看一个全局平均值。某个特殊合同客户的差异率很高,可能被普通订单的大流量掩盖。必须按计费规则、币种、合同类型、促销方式及退款场景分组查看。
另外,差异率降低并不自动说明可以切换:未归因的差异、尚未覆盖的关键业务,以及影子执行带来的延迟与资源压力,都需要一起验收。
我的上线条件会明确到:关键业务场景已覆盖;高风险差异已解释或修复;影子路径没有业务副作用;灰度具备监控、回退和账务复核手段。
八、ChatGPT、Codex怎样参与这次工程改造?
不建议直接向Codex下达"重写计费模块,保证和旧代码一致"的模糊任务。
更好的顺序是:先扫描旧实现,整理规则表和特殊分支;再生成历史订单回放工具;随后实现独立、无副作用的新计费入口;最后构造字段级差异报告和回归测试。
ChatGPT可以帮助解释为何某些金额差异不能直接忽略,设计差异分类与灰度标准。Codex则适合追踪Repository、计费Service、配置版本和测试用例,批量完成代码改造及对比验证。
不过,Agent不能自行决定把0.01元当成允许误差,更不能在未经批准时替换正式扣款逻辑。金额与合同规则必须由业务和财务确认,Agent负责提供完整、可复核的工程证据。
九、Plus和Pro怎么判断?
如果你主要让ChatGPT、Codex分析一个计费类、重构少量公式、生成单元测试,或者针对几百笔历史订单完成一次对账,Plus通常已经够用。
如果日常涉及大型SaaS计费系统,多个仓库共享规则,包含优惠、税务、账务、退款和历史合同,还需要Codex长时间执行跨模块重构、回放大量样本、分析差异和多轮回归,那么Pro更适合高频、长任务的工程工作流。
判断标准不是计费金额有多大,而是实际任务的持续时间、代码范围、验证轮次和使用额度。套餐也不会替代业务确认与生产发布审批。
最后
Agent重写核心计费逻辑,测试全部通过,为什么上线前还要双跑对账?
因为测试能证明已覆盖场景符合预期,却不能自动证明多年沉淀的业务规则、金额精度和特殊合同全部得到保留。
双跑的价值不只是找到不同,而是建立一条可追溯的验证链:
冻结相同输入 → 旧逻辑正式执行 → 新逻辑隔离计算 → 字段级差异对账 → 归因修复 → 灰度切换。
当ChatGPT、Codex越来越容易把老代码重写得更简洁、更快时,真正需要守住的,不只是单测通过率,而是:
新系统上线后,每一笔实际收费都符合经过确认的业务规则。
持续分享 Codex、大模型开发与 AI 编程实战内容
也整理了稳定的 Plus/Pro 会员订阅渠道,有需要可自取。