「登不进去」三个字,比任何监控面板都更早告诉你哪条路径坏了。
一、周五晚上的一句「登不进去」
2026 年 8 月 21 日 21:40,一位客户发来邮件,正文只有五个字:登不进去。他先查服务状态,正常;再查数据库连接,正常;最后用客户邮箱复现,发现邀请链接在第七天过期,而客户是第九天点开的。
以下情境为教学假设,人物、引语和数字不代表经核实的真实案例。
修这条问题只花了 20 分钟:把邀请有效期从 7 天改成 30 天,并在过期页加一个「重新发送」按钮。真正让他不安的是另一件事——他没有任何一份清单能回答「哪些路径坏了算事故」。
清单写完后,他删掉了 8 条候选:改头像、切换语言、搜索过滤、主题切换、快捷键、帮助中心、账单地址、通知偏好。这些路径坏了,用户会刷新或换个方式,不会发邮件;候选 20 条,留下来 12 条。
过去一年,他修了 47 个线上缺陷,其中 31 个落在后来列出的这些关键路径上。这些路径他每天都用,却从没逐条写下来,更没有为每条路径定义「坏了是什么样」。
那天夜里他打开空白文档,先写下第一条:登录与会话续期。写到第十二条时,天已经亮了。
他把这 12 条按每天的使用顺序排,而不是按模块排。登录排第一,是因为它坏了之后后面 11 条都无法验证;删除账号排最后,是因为一年只发生几次。顺序会影响下一节里「一轮 35 分钟」的跑法。
二、十二条路径与四列判定
筛选标准只有一条:如果这条路径坏了,用户会发邮件来问,而不是自己再试一次。按这个标准,任务板里能进清单的有 12 条。
| 路径 | 失败后果 | 可逆 | 能否自救 | 验证方式 |
|---|---|---|---|---|
| 注册与首次登录 | 新用户卡在验证页,进不了产品 | 是 | 能,重发验证邮件 | 用未验证邮箱走一遍 |
| 登录与会话续期 | 老用户被登出且无法重登 | 是 | 能,重新登录 | 用过期令牌请求一次 |
| 邀请成员并接受 | 受邀者打不开工作区 | 是 | 部分,需要重发邀请 | 用第七天的旧链接接受 |
| 创建与编辑任务 | 保存丢失或写入别的租户 | 是 | 不能,用户不知道丢了 | 两个租户同时编辑同一条 |
| 删除项目 | 任务与附件连带消失 | 是,有软删除 | 不能,需要你恢复 | 删除后从回收站恢复 |
| 附件上传与下载 | 文件损坏或被越权读取 | 是 | 部分,可以重传 | 换账号直接请求文件地址 |
| 结账与支付回调 | 付了钱没开通,或重复开通 | 否 | 不能 | 重复与乱序回调各一次 |
| 订阅升级与降级 | 金额或权益算错 | 否 | 不能 | 用测试时钟跨一个周期 |
| 数据导出 | 导出残缺或混入他人数据 | 否 | 不能 | 导出后逐表核对行数 |
| 删除账号 | 删不干净,或误删他人数据 | 否 | 不能 | 删除后查残留与再次登录 |
| 权限变更 | 移除成员后旧会话仍可写 | 否 | 不能 | 改权限后立刻用旧会话写入 |
| 通知邮件与退订 | 该发的没发,退订不生效 | 是 | 部分,可以补发 | 退订后触发一次通知 |
12 条里有 6 条属于「内容」:任务、项目、附件、导出、删除账号、通知。它们的特点是失败不报错,用户过几天才发现,因此验证不能只看接口返回 200。
另外 2 条属于「钱」:结账与订阅,涉及金额、币种、周期和权益。这两条都不可逆,因为钱一旦收错,退款本身又是一次新的资金操作。
剩下 4 条属于「门」:注册、登录、邀请、权限。它们坏的时候最吵,用户会立刻发邮件,但也最容易被误判为「网络问题」。
「可逆」和「能否自救」是两列,不能合并。导出不可逆,但用户可以再导一次,算部分自救;删除项目可逆,但用户自己找不到回收站,仍然不能自救。
表格第五列是整张清单最有价值的部分。前四列描述风险,第五列才告诉你明天早上要敲什么命令、点哪个页面;没有第五列,清单只是一份焦虑清单。
路径之间会互相影响。登出会打断任务编辑,权限变更会改变导出范围;验证时要按依赖顺序跑,先门、再内容、最后钱,否则前面的失败会污染后面的结果。
三、五条不可逆路径的保护
12 条里有 5 条不可逆:结账、订阅变更、导出、删除账号、权限变更。它们的共同点是动作一旦发出,就没有「撤销」按钮,只能靠事前保护。
| 路径 | 预演 | 快照 | 冷却 | 回滚动作 |
|---|---|---|---|---|
| 结账 | 用测试密钥跑一次完整支付 | 订单与权益快照 | 回调后 10 分钟内不发货 | 关闭订单并退回权益 |
| 订阅变更 | 打印变更前后的金额与周期 | 订阅状态快照 | 变更后 24 小时内可撤回 | 恢复上一版订阅记录 |
| 导出 | 先跑一次计数查询 | 导出文件保留 7 天 | 大导出延迟 5 分钟执行 | 删除文件并通知接收方 |
| 删除账号 | 先列出将被删除的行数 | 全量逻辑备份 | 软删除保留 30 天 | 从备份恢复并重放事件 |
| 权限变更 | 先查该角色当前能访问的对象 | 权限矩阵快照 | 变更后 1 小时内可回退 | 恢复上一版权限矩阵 |
五条路径的保护动作不一样,但结构相同:先知道会影响多少行,再留一份能回去的快照,最后给一个短窗口反悔。
预演的输出必须是数字,不是「看起来没问题」。结账预演要打印金额与币种,导出预演要打印行数,删除账号预演要打印将删除的 11 张表各多少行;没有数字的预演等于没做。
快照的成本被高估了。一次逻辑备份只覆盖相关表,几秒钟就能完成;真正贵的是「想起来要备份」,所以它必须写进操作步骤,而不是靠临场记忆。
冷却时间不是越长越好。结账 10 分钟足够覆盖回调乱序,订阅 24 小时足够覆盖用户反悔,删除账号 30 天是给误删留的余量;再长,用户会以为你没执行。
快照放在哪里也要写清楚。放在同一块磁盘上的快照,遇到磁盘故障会一起消失;他的做法是导出到对象存储的独立前缀,保留 7 天,并在操作记录里写下路径。
五条路径里,导出最容易被当成只读操作。它有副作用:生成文件、占用存储、可能把数据发给错误的人;所以导出同样要预演、要冷却、要能撤回。
保护动作要写进操作记录,而不是记在脑子里。凌晨两点做删除账号时,人不会想起「先备份」,只会想起「快点做完」。
四、四十八项检查与三十五分钟
每条路径固定检查四项:谁能做(权限)、边界输入(空值、超长、特殊字符)、依赖失败(支付、邮件、存储挂了会怎样)、做完之后数据对不对(后果)。
12 条路径乘以 4 项,是 48 项检查。一轮全跑完 35 分钟,平均每项 44 秒;这个时长刚好能塞进一次发布前的晚上,而不至于让人产生「不如不跑」的念头。
四项里最容易被跳过的是「依赖失败」。大家习惯测成功路径,但支付超时、邮件退信、对象存储 503 才是线上缺陷的主要来源;每项检查至少要模拟一次依赖返回错误。
第二容易被跳过的是「后果」。接口返回 200 只说明请求被接受,不说明数据写对了;删除项目后要去任务表确认级联生效,权限变更后要去审计日志确认旧会话被拒。
44 秒一项是实测均值,不是目标。权限项通常 20 秒,依赖失败项要 90 秒以上;把长的项排在前面,时间窗口被压缩时也能跑完最贵的部分。
48 项不必每次全跑。只碰前端时,跑门与内容两类共 40 项;碰计费时,钱那 8 项一项不能少;删减规则写在清单顶部,而不是临场决定。
把这一轮的账算清楚,才知道它值不值得每月挤进发布流程。
复算:12 条路径 × 4 项 = 48 项检查
单轮耗时:48 × 44 秒 = 2,112 秒 = 35.2 分钟
单条路径:4 × 44 秒 = 176 秒 = 2 分 56 秒
不可逆覆盖:5 × 4 = 20 项,占 48 项的 41.7%
一年 12 轮:35.2 × 12 = 422.4 分钟 = 7.04 h
一年 7 小时,不到验证预算 192 小时的 4%。这笔投入买到的不是「没有缺陷」,而是「坏的时候知道先看哪一条」。
清单要能执行,每条路径的验证方式必须写到「点哪里、输入什么、看哪个字段」。写「测试登录」没有用,写「用过期令牌请求 /api/me,期望 401 且前端跳登录页」才能被下一次的自己执行。
五、行动清单与复算验收
- 用「用户会发邮件而不是再试一次」筛出 12 条路径,写进一页纸。
- 给每条路径补四列:失败后果、可逆、能否自救、验证方式。
- 标出其中的不可逆路径,本文基线是 5 条。
- 为每条不可逆路径写预演命令与要打印的数字。
- 为每条路径写四项检查,凑齐 48 项。
- 用秒表跑一轮,记录实际耗时与卡住的项。
- 把清单放进发布模板,发布前勾选,而不是靠记忆。
- 每季度删掉一条不再关键的路径,再加一条新出现的。
验收标准:任取一条路径,都能在 3 分钟内完成四项检查,并说出失败时用户会看到什么。
前四条是「把路径写下来」,后四条是「让清单活过三个月」。如果只有一晚,先写不可逆的五条;它们坏一次的代价,超过其余七条之和。
清单的版本要跟着产品走。每次新增一条外部依赖,就问一句它会不会让某条路径失败;会,就把对应的检查项加进去,而不是等下一次事故。
使用边界与实操验收
清单不是监控。 清单回答「发布前该看什么」,监控回答「上线后正在发生什么」。两者不能互相替代,清单也不能当作告警规则使用。
12 条是这款产品的数量。 换一个产品,路径数量会变;判断标准不变:坏了之后用户无法自己绕过,就必须进清单。
不可逆的判定会变。 软删除、快照、冷却都能把一条路径从不可逆改成可逆;改完之后要重跑一次验证,确认恢复真的可用。
耗时不构成质量承诺。 35.2 分钟是一轮检查的实测口径,不是对任何产品发布速度或事故率的预测,也不构成对第三方服务可用性的承诺。
关键参考与核验范围
- Playwright: Getting Started :用于说明关键路径如何用浏览器自动化复现,本文的 12 条不依赖该工具。
- Stripe Webhooks :用于核对支付回调的重复与乱序处理,实际行为以官方文档为准。
- PostgreSQL: BEGIN :用于说明不可逆操作如何包在事务里预演与回滚。
- MDN: HTTP Status :用于定义依赖失败时各路径应返回的状态码。
以上链接于 2026-10-26 实测可公开访问;未核验版本、价格与配额。核验日期:2026-10-26。
上述文档只用于核对概念与操作入口;12 条路径、48 项检查、35.2 分钟与全部比例均取自本组教学基线,与文档内容无对应关系。本文只讨论一个人的关键路径清单与不可逆保护,不讨论团队值班、监控选型与服务水平协议。
系列导航
- 上一篇:质量是一笔预算:四十七个缺陷、八十六小时与三条红线
- 下一篇:三层验证的一个人配比:单元、集成与六条端到端
- 回到目录:一个人的质量守门:验证预算、回归清单与发布门禁
- 相关阅读:一个人的计费系统:订阅状态机、按比例计费与失败重试
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。