APS 与排程

每一次升级都要能证明:排程系统的发布前检查与发布后验证

排程系统改一处,可能牵动计划、物料分配和已下发的生产。本文以一批真实改动为例,写我们让排程系统迭代升级时要过的关口:发布前查什么,发布后怎样逐项验证,哪些操作不可逆、要先点名,以及为什么有时宁可暂停上线。

发布于 更新于 青岛艾普仕

先说结论

  • 排程系统改一处,可能牵动计划、物料分配、齐套判断和已下发的生产。我们要求 APSer-Aps 的每一次升级都拿得出证据:发布前证明这次没有改坏,发布后证明在真实环境里确实是好的。
  • 发布前查的是:业务上不可能的状态必须为 0,排产结果核查没有阻断级的新发现,客户看得到的文字不出现内部编号和用语,数据迁移先在副本上跑两次;不可逆的改动在任务开始时就点名,先备份、先写好恢复步骤。
  • 发布后,「服务在线」不算:健康检查要看到新版本号,每批用真实浏览器逐项验证,同一个数字在不同页面要对得上,线上验收通过后打上线标记。
  • 上线的节奏由质量决定:验证没做完,就停在本机。9 月 26 日起,我们暂停了这套系统向生产环境的推送。

排程系统的一次升级,改动可能只有几十行,影响面却很宽:一处口径变了,排出来的计划、物料分配、齐套判断、预警数字都可能跟着变;而计划一旦下发,车间就按它开工。

我们开发自研排程系统 APSer-Aps 时,写代码、写测试、部署的大部分执行工作由 AI 编程工具完成,速度很快;组织这些工作的,是我们自研的工作平台 APSer-Atelier,它作为主控登记每一批改动、派出执行、安排复核、按关口放行或退回,并留下每一关的验证记录。本栏目另一篇《AI 写代码快了,交付怎么才靠得住》写过我们踩过的坑和几项长期做法。本文换一个角度,只讲发布这条线:一批改动从「写完」到「上线」要过哪些关口,每一关查什么,上线之后又怎样证明它确实是好的。

这里说的 APSer-Atelier 主控包括两部分:面对我们的主控会话(一段 Claude Code 或 Codex 对话),负责组织和派出;Atelier 平台,负责登记每一件事、挂起待拍板、记下要求、按规矩放行并留下记录。

本文反复用到两个词,先说清楚。发布前检查,是一次升级推送到生产环境之前,用事先写定、结果只有「通过 / 不通过」的检查,证明这次改动没有破坏已有的业务规则和数据。发布后验证,是升级部署到真实环境之后,用真实浏览器和只读复核证明新版本确实在运行、确实按预期工作;服务进程在线、接口返回正常,都只是其中一步。

一、排程系统的升级,难在哪里?

各模块自己的测试都通过,合起来仍可能出错。 物料分配、下发冻结、排程三个模块各有自己的测试。有一次验收时出现了两种情况:已下发的生产订单,某个组件的供给链断了;一张生产订单的开工时间,早于它所等待的物料的到货时间。每个模块单独看都没错,只有拿全量数据交叉核对才看得出来。

写回之后,错误会一路传下去。 排程结果会写回数据库、下发给车间、驱动采购建议。一份错误的计划,影响的是后面一连串的业务单据。

部署没有「一键回到上一版」。 这套系统推送即部署,新代码直接覆盖生产目录;要恢复,只能撤销提交后再推一次。代码能撤销,被覆盖的数据不一定能。

所以我们对它的每一次升级,都要求能拿出证据:发布前证明这次没有改坏,发布后证明在真实环境里确实是好的。

这不只是排程系统的问题

AI 让写代码变快之后,交付的稳定性并不会自动跟上。Google Cloud 的 DORA 研究团队在 2024 年 10 月发布的年度报告中估计:团队对 AI 的采用程度每提高 25%,交付吞吐会有幅度较小的下降(约 1.5%),交付稳定性约下降 7.2% [1][3];报告认为,改进开发过程不会自动改进软件交付,小批量、健全的测试机制这些基本功仍要坚持 [1]。2025 年 9 月的报告显示,AI 采用已经与更高的交付吞吐相关,但仍伴随交付不稳定性上升;报告指出,缺少健全的自动化测试、成熟的版本管理和快速反馈这类控制手段时,改动量增加会带来不稳定 [2]。

几种验证做法的对照

证明一次升级「没改坏」,常见的做法有下面几种。它们可以叠加使用,各自能证明的范围不同。

做法能证明什么证明不了什么投入适合的系统
上线后人工抽查几个页面主要页面能打开,常用操作走得通跨模块的数据是否自洽;抽查没走到的路径前期几乎为零,每次上线都要占人改动小、影响面窄的系统
只跑自动化测试写测试时想到的情况没有退化几个模块合起来的业务矛盾;真实部署环境里的配置差异前期写测试,之后每批自动跑模块边界清楚、彼此影响少的系统
分批放量(先让一小部分用户用新版本)新版本在一部分真实使用下不出错排程这类系统要先解决如何切分,才能用;写回的数据照样进库需要新旧版本并行运行的部署能力用户多、请求彼此独立的线上服务
发布前检查 + 发布后验证(本文的做法)业务上不可能的状态为 0、没有阻断级新发现;新版本在真实环境里版本号对、数字对得上检查清单之外的问题;部分检查目前靠纪律执行(见第六节)前期把业务规则写成检查,每批写一个验证脚本计划下发后直接驱动生产的系统,如排程、生产执行
表格可左右滑动

二、一批改动从写完到上线,要过哪些关口?

以 9 月 28 日的一批改动为例。这批改了四件事:超负荷时的加班方案、甘特图拖动后写回的时间基准、订单的预计发货日期、物料缺口的统计口径。在 Atelier 主控的组织下,开发由一个执行器完成,复核、落地和推送由主控会话完成,两者都是 AI;什么算通过,由我们事先定下的检查决定。

关口谁做查什么这批的实际结果
交付自检执行器全量自动化测试;三类业态的基准排程校验值;逐项浏览器验证;业务不变量;排产结果核查测试 801 项全部通过、0 项跳过,12 路并行约 327 秒;三类业态校验值不变;本批浏览器验证 34 项全部通过;不变量 0 违例;核查无阻断级新发现
独立复核主控会话读改动;独立重跑三类业态校验值和全部静态检查三类业态校验值与交付时一致;静态检查全部通过
本机落地主控会话落地前备份 → 重启到新版本 → 启动时全量核查 → 不变量与文案 → 页面数字与预警一致 → 真实浏览器走一遍关键页面 → 落地后再备份核查阻断 0、高 0、新发现 0;不变量 0;文案 0 命中;预警里「物料缺口」5 条与物料页 5 条一致;甘特页深浅两种主题均无页面错误、无意外写请求、页面不整体滚动,底栏显示新版本号
推送与线上验收主控会话推送前备份;按复核过的提交号推送;线上健康检查与版本号;只读复核线上数据;真实打开排程页面;通过后打上线标记这批按计划只落地本机,未推送(见第五节)
表格可左右滑动

有两处安排值得说明。

全量测试同一批只跑一次。 全量测试要跑五分多钟。复核时不再重跑,前提是测试结果可以复现、可以比对:我们把求解方式和「今天」的日期都固定了下来,每次运行会给出一个基线校验值,12 个并行进程各自算出的值必须一致。复核看的是这些值和通过数、跳过数,而不是再等五分钟。

复核者只读、只重跑,不动手修。 复核发现问题就打回原执行器,在原任务里修,修完再交;复核者不在复核过程中顺手改代码。这样每一次改动的来源都是清楚的。

三、发布前查什么?

业务上不可能的状态

我们把四条「无论如何都不该出现」的状态写成了代码检查,称为跨模块不变量:

  1. 生产订单的首道工序开工,不早于它所等待的在途物料或采购建议的到货日期;
  2. 已下发、尚未开工的生产订单,物料清单里每一种组件和原料都有分配覆盖,供给链不断;
  3. 分配两端数量守恒:一份供给分出去的总量不超过供给量,一个需求分到的总量不超过需求量;
  4. 锁定的分配,凡涉及生产订单的,这张生产订单都已下发或在制。

发布前对全量数据跑一遍,必须为 0。系统运行时,定时自动任务和人工写操作也要过同一道检查,违例数变多就整次回滚或拒绝。

排产结果核查:只看新发现

不变量只有四条,覆盖不了所有问题。另有一套排产结果核查,目前 89 条规则,分排程硬约束、供需、数量链、状态一致、页面与接口统计、业务参数、引用完整性七类,按阻断、高、中、提示四级区分严重程度。规则全部由程序判定,不调用大模型。其中 43 条是快速级,每次写操作后都跑;46 条是全量级,在定时、手动和服务启动时跑。

一套系统跑久了,中低级别的发现不会是零:有些是已知、已接受的业务状态,有些已排进后续计划。如果每次都把全部发现列出来,一条真正的新问题很容易被旧问题淹没。所以我们把已知的发现逐条登记,发布前的判据只有一条:没有阻断级的新发现。

客户看得到的文字

开发过程里用的诉求编号、任务编号、内部过程用语,很容易随着代码进到页面上。发布前扫一遍前后端所有会显示给用户的文字,再对运行中的接口返回值扫一遍,两遍都必须 0 命中。

数据迁移:先在副本上跑两次

需要改客户数据的升级,迁移脚本必须满足几条:

  • 默认只演练,不写;
  • 写前、写后逐表比对行数和内容校验值,只允许清单里列出的表发生变化,其他租户的数据逐表不变;
  • 先在一份在线备份出来的副本上执行两次,第二次必须零变化,证明它可以重复执行;
  • 执行后跑不变量,变差就回滚。

不可逆的点,先点名

大多数改动可以撤销;少数改动一旦上线,撤销提交也恢复不了。我们把这类改动列成清单,这套系统目前有三处:

  • 数据版本号变化:会触发生产环境重新灌入数据,覆盖租户数据,也会擦掉已提交的计划,没有自动备份;
  • 反向代理配置:同一台机器上还有别的服务共用它;
  • 部署脚本自身:写错了,之后每一次推送的部署都会失败;它和代理配置要同时改时,必须分两次推,先推部署脚本。

凡是提交里含这三类改动,必须在任务开始时就点名,先备份、先写好恢复步骤。其余改动按正常节奏连续完成。

备份与恢复

每次推送前做一次完整备份:代码、数据库、运行配置,以及全部表的行数与内容校验值,本机和另一台设备各存一份。恢复默认只恢复到临时目录,并自动比对和跑不变量;要覆盖真实环境必须显式声明,覆盖前再自动备份一次。

四、发布后怎样验证?

「服务在线」不算

健康检查要同时满足三条:状态正常、数据库可达、返回的版本号是新版本。然后用浏览器真实打开一个排程页面。进程在线、接口返回 200,都只证明了其中一小段。

每批一个逐项验证脚本

每一批改动,都按这批的诉求写一个真实浏览器的验证脚本,到目前一共 34 个。以 9 月 28 日这批为例,脚本做了三件事:

  1. 订单页:前置评审中和被驳回的订单明细,「预计发货」显示为「待定」,并与状态列一致;
  2. 评审中心:超负荷评审的重排结果、加班产能的加开日期表和实际排进的小时数;
  3. 公司级甘特图:真实拖动一条任务向后 2 小时,校验通过后写回,再把写回前后的全部任务逐条比对:没拖的任务,编号、设备、精确起止时间、换型、子批全部不变;被拖的那条整段后移 2 小时。

前两项不发任何写请求,第三项只发校验和写回两个请求;每一页都在深浅两种主题下各看一遍,并检查页面没有整体滚动。

这些脚本只允许对「彩排环境」运行:用在线备份出来的数据副本、另起的独立端口。脚本启动时会检查地址,指向本机正式端口就直接拒绝,验证过程不会碰到真实数据。

同一个数字,在不同页面对得上

预警里说物料缺口 5 条,物料页就必须是 5 条。同一个业务数字在不同页面、不同接口里出现时,要逐一核对是否来自同一个口径。口径不一致,往往比单个数字算错更难发现。

线上只读复核

推送之后,不变量检查可以直接对线上跑:它通过线上接口读出数据,在本地组装后检查,只发读请求,不写任何东西。排产结果核查也只读取服务端最近一次的核查结果。

上线标记

线上验收通过后,给这个版本打一个带时间的上线标记,写明验收要点。需要回退时,最近一个标记就是「上一个验证过的好版本」。9 月 24 日到 26 日这三天,这套系统一共打了 32 个上线标记。

真实走一遍,才发现的问题

有一次本机验收,我们模拟取消了一张订单,释放了一张已下发的生产订单给它的分配;再点一键排产,系统把整张生产订单又挂回了那张已经取消的订单明细上。各模块的测试都没覆盖到这条路径,是按真实业务动作走一遍才发现的。修复后补了对应的测试:取消后不再挂回,减量后分配合计不超过剩余需求。

五、验证没做完时,为什么宁可暂停上线?

9 月 26 日起,我们暂停了这套系统向生产环境的推送:新版本只在本机完整落地、验证,等质量达到要求后再恢复。为了从机制上防止误推,推送地址被设成了一个无效值,恢复要明确操作。

暂停期间,第二、三、四节的检查照常一批一批地跑,只是最后一关停在本机。

恢复推送之前,还有一类专门要查的前提:有些配置只存在于生产机上,不进代码仓库。缺了其中任何一项,一次部署覆盖配置之后,服务就可能整体不可用。这类「只存在于生产环境的前提」,我们逐项写进了恢复推送前的检查说明。

六、还有哪些做不到?

  • 部分检查靠纪律执行:不变量、排产结果核查、文案检查、全量测试是「每批必跑」的约定,还没有接进推送前的自动拦截,漏跑时没有机器兜底。本站已经接入了一道统一的发布门:推送前和上线打标记前,自动运行项目自己声明的检查清单,检查出错或没跑完都算不通过。排程系统计划照此接入,尚未完成。
  • 部署没有不可变的回滚:恢复靠撤销提交后再推送,数据层面的恢复靠备份。
  • 运行时间短:上面多数做法是 9 月下旬才建立的,长期效果还要时间检验。

七、给自己的系统建这套检查,按什么顺序做?

如果您也想让自己的业务系统每次升级都拿得出证据,可以参照我们给 APSer-Aps 建这套检查的顺序:

  1. 先列出业务上不可能出现的状态,写成可以计数的检查。 这类检查只看结果在业务上说不说得通,哪个模块改了什么都用得上,最值得先做。
  2. 把现有检查的发现逐条登记为已知。 之后只让新发现拦住发布,检查才不会被一长串旧问题淹没。
  3. 列出不可逆的操作清单。 数据覆盖、几个服务共用的配置、部署脚本自身这类改动,撤销提交也恢复不了,要在任务开始时点名、先备份、先写好恢复步骤。
  4. 准备一个和生产隔离的彩排环境。 用备份出来的数据副本、独立端口,逐项验证可以真实点击、真实写回,而不碰真实数据。
  5. 每批改动按这批的诉求写一个真实浏览器的验证脚本。 模块测试覆盖不到的路径,要按真实业务动作走一遍才发现。
  6. 定下发布后「算通过」的判据。 健康检查看到新版本号、同一个数字在不同页面一致、线上只读复核通过,三条都满足才算,服务在线只证明了其中一小段。
  7. 验收通过后给这个版本打上线标记。 需要回退时,能立刻找到上一个验证过的好版本。

以下是我们的看法

以下内容是我们的观点,供您参考。

第一,发布前检查和发布后验证回答的是两个问题。 发布前检查回答「这次改动有没有改坏什么」,发布后验证回答「在真实环境里是不是真的好」。前者再充分,也代替不了后者;我们最难发现的几个问题,都是真实走一遍才露出来的。

第二,把业务上不可能的状态写成代码,是排程系统最值得做的检查。 单元测试检查的是写代码的人想到的情况;不变量检查的是业务规则本身。它不关心哪个模块改了什么,只看结果在业务上说不说得通。

第三,只看新发现,检查才能长期开着。 一套检查如果每次都报出一长串旧问题,很快就没人看了。把已知问题登记在案,只让新问题拦住发布,检查才会一直被认真对待。

第四,先把不可逆的点列出来。 大多数改动可以撤销,少数不能。把这少数几处列清楚、每次点名,其余的改动就可以放心快跑;不列清楚,就只能每次都提心吊胆。

第五,宁可暂停上线。 迭代快是 AI 带来的好处,但上线的节奏应该由质量决定,而不是由写代码的速度决定。验证没做完,就停在本机。

如果您的企业正在用或准备用排程系统,也关心它每次升级是否可靠,欢迎预约交流。

参考来源

  1. Nathen Harvey, Derek DeBellis, "Announcing the 2024 DORA report", Google Cloud Blog, 2024-10-22
  2. Nathen Harvey, Derek DeBellis, "Announcing the 2025 DORA Report", Google Cloud Blog, 2025-09-23;报告全文 PDF
  3. DORA, "Accelerate State of DevOps Report 2024", Google Cloud, 2024;全文 PDF