FDE岗位实务指南 · 第16篇
上一篇讲了怎样建立判断标准。标准一立起来,"不对"就会被源源不断地发现——这本来是好事,但紧接着会出现一个更考验功力的场景:发现了不对,怎么办?
太多团队的答案是条件反射式的:换模型、改提示词、加规则,三件套轮着上,哪个碰巧让症状消失就用哪个。症状确实可能消失——但原因没消失。下一次换个输入,问题换张脸回来,再换一轮模型。这不是排障,这是抽奖。
这一篇讲FDE 的排障方法:把"出错了"变成一份可以复查的定位记录。核心动作只有三个——先写清预期与实际,保留能复现问题的最少材料,沿链路找到第一处与预期不一致的位置。之后的修复和验证,都建立在这份定位之上。
业务、数据、模型、接口、流程五类问题听起来都很像"系统不好用",但它们的修复路径完全不同。分开它们的不是经验直觉,是证据。
先感受下这五类问题的差别。
业务问题:判据本身就错了,系统再准也是在忠实执行错误指令,改法是和业务重新对齐判据。
数据问题:输入是脏的——字段缺失、口径混乱、版本过期,改法在数据治理,碰模型没用。
模型问题:输入干净、判据正确,但模型提取或推理就是错了,这时才轮到换模型、改提示词。
接口问题:请求根本没到该到的地方,或者到了但被拒绝、被截断,改法在集成层。
流程问题:系统每一环都对,但环节之间的衔接断了——该通知的没通知、该等待的没等待,改法在工作流设计。
五类问题,五种修法,五种责任人。混在一起"大力出奇迹",唯一确定的结果是没人对最终结果负责。
一、面对错误,先写清预期与实际
排障的第一个坑,是从"感觉不对"直接跳到"动手改"。中间的记录环节被跳过,后面的所有工作都失去基准。
所以第一张表永远只有两列:预期是什么,实际是什么。写得越具体越好——不是"核验功能不正常",而是"输入一份用途栏为空的申请单,预期进入'待补充'分支并退回填写人,实际进入了'待确认'分支"。这两行字写完,排障范围就收缩了一大半:你不用再怀疑整个系统,只需要解释这一次具体的不一致。
预期这一列有个隐蔽的难点:它必须来自判据,而不是来自"我觉得应该"。第 15 篇建立的判据在这里开始还债——有判据的团队,预期列照抄判据;没判据的团队,排障第一步就得先补判据,否则"修复"完成与否都无从判定。
这就是为什么排障和评测是同一条纪律的两端:评测时建立的判据,就是排障时的预期基准;排障时发现的首次偏离,积累下来就是评测要新增覆盖的样本类型。两件事用同一套记录语言,团队的知识才真正开始复利。
澄川制造的核验工作流在这一篇要出场三次,处理三张互相独立的故障卡(全部教学合成数据)。先说清楚:三个故障的症状有点像——都是"申请单的处理结果不对"——但定位出来分属三个不同的层。这正是这一篇要演示的:症状归症状,原因归原因。
二、保留能复现问题的最少材料
定位的前提是复现,复现的前提是材料。故障发生时的现场如果不保留,后面所有的分析都建立在转述上——而转述会丢东西,丢的往往正是关键的那部分。
最少材料清单不复杂:输入原文(那份申请单本身,一个字不改);请求时间(精确到秒,用来对日志);身份与范围(谁发起的、以什么角色、对哪个对象);目标对象的状态(申请单当时处于哪个版本、什么状态);日志片段(相关的几段,不是全部日志打包)。
材料保留有一个残酷的时间窗口:故障发生后的几分钟。过了这个窗口,内存里的现场散了,日志被滚掉了,当事人开始凭记忆描述——而记忆会自发地把"我看到的"补全成"我觉得合理的"。排障老手都知道一个规律:转述每经过一个人,关键技术细节衰减一截,业务情绪增加一截。到第三手的时候,"保存返回 403"已经变成"系统完全不让用了"。所以材料清单要在故障响应的第一时间执行,最好变成系统能力——一次请求的关键节点证据自动落盘,而不是靠人想起来去截屏。
"最少"两个字是纪律。材料太多,分析者会在无关细节里迷路;材料太少,复现不出来。检验标准很朴素:把这份材料交给一个不了解情况的同事,他能照着把问题重现出来。复现不了,说明材料不全,回去补,不要往下走。
还有一个更朴素的纪律:先复现,再动手。复现之前不改任何东西——不改配置、不改代码、不改数据,尤其不同时改多个位置。很多故障就是这样被"修丢"的:当事人一顿操作,症状没了,但没人知道是哪一步起的作用,更没人知道是不是只是暂时掩盖。故障被修丢,比故障本身更可怕,因为它把一个可学习的事件变成了一颗定时炸弹。
还有一个进阶做法值得提:在隔离环境里预置故障做演练。课程里管这叫"可复位的教学故障开关"——人为制造一个已知问题,让团队按清单走一遍材料保留和复现流程。演练的价值在于把"故障发生后的第一反应"练成肌肉记忆,否则真实故障来临时,清单只是贴在墙上的纸。
三张故障卡的现场材料(教学合成)都已按这个清单留存。第一张:缺用途的申请单进入了"待确认",输入原文、请求时间、提交人身份、工作流版本 v3.2 的日志都在。第二张:核验员提交一份内容正确的提案,保存时返回 403。第三张:同一份保存请求,客户端显示超时。三张卡彼此独立,分开定位。
三、沿链路找到首次偏离的位置
材料齐了,复现成了,真正的定位才开始。
这是排障的核心动作。一条业务请求在系统里走过的路径是一连串节点:原文输入 → 字段提取 → 候选生成 → 规则判断 → 动作执行 → 结果落库。定位的方法是从头到尾逐节点核对:每个节点收到的输入是什么,预期产出是什么,实际产出是什么。第一处"预期与实际不一致"的节点,就是首次偏离点——问题出在它身上,或它上游。
为什么强调"第一处"?因为下游的所有异常都可能是这一处的后果。在第三处看到奇怪的数据、在第五处看到错误的动作,都不说明第三处和第五处有毛病——它们可能只是在忠实地传递上游的错误。沿链路从头找,就是避免在错误的层上修错误的 bug。
执行这个核对有个顺序讲究:从输入端开始顺着走,而不是从报错处开始倒着猜。顺着走,每一步的"预期"都明确——因为上一节点的实际产出就是下一节点的输入;倒着猜,每一步都要先脑补上游状态,猜着猜着就把假设当事实了。速度上看似倒着快,算上返工,顺着走才是捷径。
故障卡一:缺用途进入待确认。逐节点核对:输入原文,用途栏确实为空,与预期一致。字段提取节点,预期提取出"用途:空",实际日志显示提取返回空——一致。规则节点,预期收到"空"后进入"待补充",实际进入了"待确认"——首次偏离出现在这里?慢着,再细看规则节点的输入:日志里它收到的用途值不是空,而是字符串"维修"。提取返回空,规则却收到"维修",中间还有一个映射配置:用途字段为空时,默认填"维修"。首次偏离修正为这个映射默认值——它在所有人都没注意的地方,把"缺用途"悄悄变成了"用途是维修"。规则节点没有判错,它判的是被篡改过的输入。
这个问题单的完整记录是:输入缺用途;提取返回空(正确);规则节点收到默认"维修"(首次偏离在映射配置);处置是移除该默认值,让"空"以本来面目到达规则节点;复测:缺用途的单子被拦下进入"待补充",用途完整的单子正常通过——正常路径和异常路径都要过。记录最后附上请求标识与工作流版本的对应关系,因为默认值是 v3.2 引入的,v3.1 没有——没有版本对应,下次升级时这个坑还会回来。
值得对照的是第 14 篇那个故障:同样是"缺用途进错分支",那次是字段绑定错位置(取到了有字的备注栏),这次是空值被默认值顶替。症状几乎一样,机制完全不同——这也是为什么不能只凭症状开药方。
故障卡二:提案正确但保存 403。症状是权限拒绝,直觉是"权限配置错了"。但 403 只是一个状态码,它能说明"服务端拒绝了这次请求",说明不了"为什么拒绝"。逐节点核对:请求里的身份是核验员甲、角色有效;目标对象是申请 A-017;再查对象状态——A-017 的提案已从 v1 更新到 v2,而请求里引用的提案版本仍是 v1。首次偏离不在权限层,在版本前置:保存动作要求"基于当前版本提交",请求基于的是旧版本。403 是系统按设计拒绝过期写入,不是权限配置事故。只凭状态码断言根因,就会把一次正常的版本保护当成权限故障去"修",越修越糟。
故障卡三:写入超时。症状是客户端转圈后报超时,直觉是"没保存上,再点一次"。且慢——超时只说明客户端没等到响应,不说明服务端没执行。这条线索按纪律留到第 18 篇展开,这里只记一条:超时是一个"未知"状态,不是"失败"状态,两者通往完全不同的处置。
三张卡定位完,回看它们分属的层:卡一在数据与配置的交界(映射默认值),卡二在接口与流程的交界(版本前置校验),卡三横跨执行与状态。没有一张的答案是"换模型"——这不是巧合,是生产环境里故障分布的常态:模型层的问题远没有人们本能以为的那么多。
四、用对照实验检验解释
找到了首次偏离点,就有了解释;但解释还只是假设,要靠对照实验检验。实验的原则是一条:一次只改变一个可控条件,看结果是否按解释预期变化。
故障卡一的对照实验:保持输入不变(同一张缺用途申请单),只移除映射默认值,重跑——进入"待补充",解释成立。再加一组反向对照:输入用途完整的申请单,重跑——正常通过,说明移除默认值没有误伤正常路径。两个方向都过,解释才算站住。
为什么不能一次改多处?假设你同时移除了默认值、又顺手改了规则节点的判断顺序,结果好了——请问是哪个改动起的作用?不知道。更麻烦的是,如果结果变坏了,你也不知道该回退哪一个。一次一个变量,是唯一能保证"改动与效果之间的因果关系可读"的做法。这里要警惕一个认知陷阱:看到两个现象先后出现或同时出现,不能直接当因果。移除默认值之后系统正常了,看似铁证,但如果恰好在同一时刻上游数据也变了,结论就可能是错的——所以对照实验要保留"只改一个条件"的克制,还要在记录里写明"还有哪些条件没控制住"。
相关与因果的混淆在排障里有个高发形态:故障总在周二出现,于是"周二"成了嫌疑犯。查下去才发现周二凌晨有批处理任务,真正相关的是批处理占满资源,"周二"只是它的影子。破解方法没有捷径:把怀疑的链条一环环拆开验证,而不是抓住第一个可疑的相关性就停手。
设计对照实验时,样本量小也没关系,关键是"区分度":这组实验的结果,能不能把候选解释区分开?故障卡一里,"移除默认值后缺用途单被拦"这一个结果,就把"映射默认值顶替"和"规则节点判断错误"两个候选解释分开了——如果是规则节点判错,移除默认值后它照样会把空值判去"待确认"。一个实验区分两个假设,比十个模棱两可的观察值钱。
故障卡二的对照实验思路相同:用同一身份、同一对象、基于 v2 重新提交——保存成功。再反向:基于 v1 再提交一次——仍是 403。解释成立:拒绝与版本挂钩,与身份无关。实验做完,修复方向根本不是"改权限",而是客户端提交前要取回当前版本号。
五、把修复变成可复查的记录
排障的终点不是"好了",是一张填完的排障卡:症状、预期、第一处偏离、证据、候选原因、下一检查、负责人。七个字段各有把关的环节:症状和预期决定定位是否对准了问题;第一处偏离是整份记录的骨架;证据是让别人能复核你;候选原因(注意是复数)逼你承认曾经的不确定性;下一检查把"还没查完的"显性化;负责人让记录有落点。澄川这三张卡(教学合成)归档后,团队多了一笔资产——
下次再有"缺用途进错分支"的报告,先查映射配置有没有被人加回默认值;再有保存 403,先查版本前置而不是权限表;再有超时,先查状态而不是先重试。排障卡的价值随复用次数增长,这就是它和"修好了事"的区别。
以卡一为例,七个字段落地后是这样的:症状——缺用途申请单进入待确认;预期——进入待补充并退回填写人;第一处偏离——映射配置节点将空值替换为默认"维修";证据——提取节点空值日志与规则节点入参日志对照;候选原因——映射默认值配置、规则节点判断错误(已被对照实验排除);下一检查——排查 v3.2 以来历史单据的假用途污染,负责人数据组,期限两周;负责人——工作流配置归系统负责人,历史数据清理归数据组。一张卡片,七个字段,排障从个人手艺变成了团队可交接的工作。
两条收尾纪律。其一,修复后复测的不只是出问题的路径,还有相关的正常与异常路径——移除默认值那次,如果把"用途为空"和"用途为空白字符"当成一回事,就会漏掉一类变体。其二,记录尚未确认的影响:默认值的移除影响了 v3.2 之后的所有申请单,历史单据里有没有已经被"维修"这个假用途污染的数据?这个问题没有当场答案,如实写进记录,指定负责人和清理期限。最不能做的是清空日志、删掉失败样本,然后声称"问题不存在了"——那不是排障,是销赃。
迁移练习留给你:从这三张卡里任选一张,隐去"首次偏离"那一行,交给同事,请他只看记录材料重现问题、指出他认为的首次偏离点。他指得出来,说明你的记录合格;指不出来,说明记录里缺了关键材料。排障能力考核的从来不是背错误码清单,而是按证据定位、按记录复核。
下一篇换个方向:前面几篇都在讲"做错了怎么办",第 17 篇讲"怎么让错的发不出手"——会调用工具,还要懂权限、状态和执行边界。