例外情况要写成“条件—判断依据—动作—放弃动作”四段式,而不是只写一句“遇到异常时人工处理”。脚本执行者需要知道在什么信号出现时偏离主流程、偏离到什么程度、以及偏离后把结果交给谁。缺少这层描述,脚本会把本应跳过的页面当成正常页面处理,或者反过来把正常页面误判为异常而中断。
把人工经验转成脚本时,常见的做法是把主流程写得非常细:先取哪些字段、按什么顺序拼接、输出到哪一列。但脚本上线后,出错的地方往往不在主流程,而在主流程之外。人工操作时,人会在看到某个页面结构不对时顺手跳过,这个“顺手”没有被写进需求,脚本就照常执行。
这里有两条互相竞争的解释。第一种解释是规则写得不够多,只要继续补充更多判断分支就能覆盖。第二种解释是例外情况的描述方式错了:主流程写的是“做什么”,例外写的是“不做什么”,两者不在同一个判断层级上,脚本无法在运行时决定该走哪条路。
能区分这两种解释的证据是:把脚本在已知会触发例外的样本上跑一遍,看它是报错、静默产出错误结果,还是按预期跳过。如果是报错,说明缺的是分支判断;如果是静默产出错误结果,说明缺的是例外触发条件和跳过后的去向。前者补规则数量有用,后者补规则数量只会让判断越来越乱。
人工经验里“这个不对就跳过”之所以无法直接转成脚本需求,是因为它省略了判断依据。把例外写成可执行描述,至少要有四个字段:
这四个字段里,最容易被漏掉的是判断依据。只写触发条件和偏离动作,脚本会在信号出现时机械跳过,但跳过的是否真的是例外,无法在后续核对。补上判断依据后,才能把“跳过”和“误跳过”分开统计。
假设一个场景:需要从一批页面中提取某个字段,人工操作时遇到页面结构变化会跳过。写成脚本需求时,可以这样描述例外:当目标字段在页面中出现次数为零时,记录该条并跳过;当出现次数大于零但格式与主流程预期不一致时,记录该条并标记为待确认;当页面无法正常获取内容时,停止该条处理并报错。这三个分支对应三种不同的后续动作,不能合并成一句“异常时跳过”。
这个例子的关键不是字段本身,而是每个分支都有独立的后续动作。记录并跳过、记录并标记、停止并报错,分别对应不同的下一步:跳过的不需要再处理,标记的需要人工复核,报错的说明采集环节本身有问题,要先修采集再继续。
写完例外描述后,不要直接上线全量跑。先构造或找一批已知会触发例外的样本,单独跑一遍,观察脚本是否按描述进入对应分支。如果脚本没有进入例外分支,说明触发条件写得不够可观测;如果进入了但后续动作不对,说明偏离动作或放弃动作没有写清楚。
验证时要注意,一次改动前后的比较不能只看结果数量。搜索需求本身会随季节和热点变化,采集环境也可能不同,数量变化不一定来自例外处理。更可靠的做法是固定同一批样本,比较改动前后脚本在这批样本上的分支走向是否与预期一致。这一步的结果会直接决定下一步:分支走向一致,才适合扩大样本;不一致,先改描述再扩大。
例外描述不是把主流程再写一遍。主流程负责正常路径,例外负责正常路径之外的分支。两者共享同一套字段和输出格式,但判断层级不同。如果发现例外描述里出现了大量与主流程重复的步骤,说明边界没有划清,脚本会在两套逻辑之间来回跳。
划清边界的实际动作是:把主流程写成一条直线,把例外写成从直线上的某个点分出去的支线,并注明每条支线结束后是回到直线、进入另一条支线,还是终止。这个动作做完后,脚本需求里就不会再出现“遇到异常时人工处理”这类无法执行的句子,取而代之的是可以逐条核对的条件和动作。