修复上线之后:拿什么证明它真的生效
改完代码、跑通测试、部署上线——到这一步很多人就收工了。
但这个月有四次,“看起来修好了”和”真的修好了”之间差着一次取证:一次是日志里还有旧报错、一次是打包显示成功但关键步骤没执行、一次是审计报告把数据问题写成了代码 bug、还有一次是构建失败其实是本机网络的问题。
这篇记一下取证的具体做法。
一、先区分「我的网络」和「构建产物」
最省时间的一条原则:出问题先分清是环境还是产物。
- 发布时本机
dnf5 --refresh repoquery访问packages.redhat.com一直超时,看起来像构建挂了。但dnf repolist秒回、Copr API 那边构建状态是SUCCESS——是本机到那个镜像的网络问题,不是产物问题。判据要以构建侧(CI 状态、Copr API)为准。 - 联调环境里首页一直跳到升级页,一开始怀疑代码;核对后发现是数据库停在旧版本、代码是新版本。版本不一致本身就是”现象”,不是 bug。
这两件事的共同点:现象发生在你面前,原因不一定在你面前。先确认”我这边看到的”代表不代表”真实状态”。
二、A/B 对照:回退一次,让日志说话
线上刷了 7 条 stripslashes(): Passing null to parameter #1 ($string) of type string is deprecated ... on line 492。
改之前先做对照实验:
1 | git stash # 回退到修复前 |
回退能复现、恢复能消失,这才是修复生效的证据。只看”我改完访问了一下是好的”,说服不了任何人——包括三个月后的自己。
顺带一个判断:那 7 条报错正好对应列表页里 7 条没有对应管理员的封禁记录。数字能对上,根因才算落地:管理员被删 → LEFT JOIN sb_admins 返回 admin_name = NULL → PHP 8.4+ 把 null 传给 string 参数就报 Deprecated。
三、复现条件要跟线上对齐
同一个报错,本地死活复现不出来,有两个原因,都值得记:
① 数据条件不具备。 本地备份库里没有”孤儿记录”(线上是后来删管理员才产生的)。做法是临时把一条封禁记录的 aid 指向一个不存在的 999999,请求后立刻还原——不是造一套假数据,而是只改触发条件那一个字段。
② 分支条件不一致。 第一次测试压根没走到第 492 行:本地 banlist.hideadminname=1,匿名请求走的是 admin=false 分支,根本不会调用 stripslashes。测试前先把触发条件对齐线上配置,否则你会得到一个”没问题”的假结论。
测完必须还原:数据改回原值、模板缓存清空、临时容器删掉。否则下次有人在这个环境上测试,会拿到你留下的脏数据。
四、日志要能证明「步骤真的执行了」
这一条最容易忽略:“打包成功”不等于”关键步骤执行了”。
一个跨平台打包项目里有个关键步骤——把第三方预编译库绑定的 glib 符号隔离掉,否则应用一加载就崩。但这步如果没执行,打包依然是绿的。
所以验收方式是回看日志确认证据:
- Linux x64 与 arm64 各自出现
isolated sharp's bundled glib in 2 file(s)(提升副本与.pnpm副本各一),patchelf 0.19.2 安装成功; - FreeBSD 侧日志里没有这一步 —— 这是正常的:它走独立构建脚本、按平台直接跳过该修补,而且用的库是 WASM 实现、根本不链接 glib。
同一个判据在两个平台上结论相反,是因为平台分支不同。要求”两边日志一致”反而会误判。验收要按平台分别定判据,而不是套一个模板。
五、审计报告不能照单全收
有一份审计报告说:”12 条 supersede 边对应 6 条仍然 active,是写边时没回写 status”。
听起来是个明确的代码 bug。但核对之后发现两件事:
- 更可能是手工 SQL、导入、或旧版本写入留下的数据,当前正常写入路径没有这个逻辑;
- 而且审计快照是 308 条,手上库是 306 条 —— 两边根本不是同一份数据。
结论:不能照着审计的 SQL 批量执行。先备份,先查记录来源,只做”能确定来源”的那部分修复。
同一份报告里的另一条(提示词预算不足导致关键内容被裁)成立且更严重,值得优先修。
审计报告是一份线索,不是一份判决书。 它的价值在于指出”哪里可疑”,验证和定级还是要自己来。
六、发布前确认「在可安装产物里」
有一个功能做完验收,才发现改动只在本地分支:已经发布的二进制不包含新页面,装了新版的人根本拿不到这个能力。
修法是把分支合并、补文档、发下一个版本号,而不是覆盖已发布的版本。教训是:“我测过了”要加一句”在哪个产物里测的”。
另一个同类:部署完代码,页面还是旧的——因为 php-fpm 的 OPcache 设了 validate_timestamps=0,必须重启才加载新文件。这类”代码更新了但运行的是旧的”在 PHP/OPcache、Java 容器、Node 常驻进程里都会出现,验收要看运行时加载的版本,不是文件时间戳。
总结
“修好了”要有证据,证据要满足三条:
- 能复现也能消失——A/B 对照,回退一次让日志说话;
- 条件跟生产对齐——数据条件、分支条件、运行时版本;
- 证据指向那个步骤本身——不是”整体成功”,而是”这一步确实执行了”。
再加两条心态:
- 审计报告是线索不是判决书,先看两边数据是不是同一份;
- 现象在你面前,原因不一定在你面前——先分清是本机环境还是真实产物。
最后一句:上线不是终点,是验收的开始。