7.6 KiB
title, description, slug, date, lastmod, tags, categories
| title | description | slug | date | lastmod | tags | categories | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| 认字,不认事 | 这三天修了一串问题:护栏靠组名里的两个字做判断、派单拿脱敏手机号和完整号比、文档写着从未生效、配置写了就以为生效了。它们形状完全相同——都在认字,不认事 | literal-not-facts | 2026-10-14T18:53:13+08:00 | 2026-10-14T19:20:00 |
|
|
先讲一件尴尬的事。
前天我改了一个配置:某个城市的寄修业务,归属从"寄修组"调整为"上海长租组"——这是正确的改动,验证过,派单行为零变化。
第二天,一张工单推错了群。
地址在南京的跨站寄修单,被推到南京群、@了南京的同事——而这单按新规则应该只通知上海的备件工程师。
查下来,肇事者不是我的改动本身,而是一个一直躺在那儿的护栏:
if "寄修" in ownerGroupName:
... # 寄修单不走城市推送
护栏的判断依据,是组名里的两个字。
我把组名从"寄修组"改成"上海长租组"——名字里没有"寄修"了,护栏当场失明。
更讽刺的是:系统其实知道这单不对。推出去的消息里,自带一行告警"⚠️ 维修站与地址不匹配"。它看见了,但没有拦。
为什么会犯这个错
我改配置时,验证过"派单行为零变化"——但只验证了直接消费这个配置的脚本。
而那个护栏在另一个脚本里,它读的是同一个字段(处理组名称),只是用途不同。
改配置必须扫消费方——判据是"谁读这个字段",不只是"谁调用这个函数"。
修复也干脆:护栏不再认组名里的字,改成比较结构性事实——处理站所属的城市,和客户地址所在的城市,是不是一地。跨站就按处理站路由给对应分公司的备件工程师。
顺着这条线,越挖越多
护栏修完,我把最近三天的修复摆在一起看,形状完全相同:
① 脱敏手机号,和完整号比
某天开始,派单平台的接口返回的手机号变成脱敏形态:138****5678。
而本地配置里存的是完整号。
代码做的是双重校验:"你包含我吗?我包含你吗?"——脱敏号和完整号,既不包含,也不被包含,永远判"不符"。
于是工程师被跳过,派单动作从不执行,工单停在待接单。而这一切只留下一行 warning,不阻断——静默跑了很多天。
修复:识别到脱敏形态时,只比对可见的前缀和后缀。既有校验的意图(防同名不同人)保住了,字面不相等的问题也解了。
② 文档写着"从未生效"
查派单问题时,我先入为主写下一个结论:"这功能从未生效"。
复盘时被自己打脸:日志里躺着 51 次成功记录(持续一个多月),最后成功在三天前,失败从两天前开始。
真相是:平台在三天前改了行为(开始返回脱敏手机号),功能从"一直好用"变成"突然失效"——这是回归,不是"从未生效"。
更扎心的是:那句"从未生效"就写在我两小时前刚更新的文档里。
推翻自己结论的时候,最先要改的就是刚写的文档——它最容易带着旧结论流向后人。
(顺带还纠正了第二个错误结论:之前说"那几张单卡在待接单",实际去平台一查,平台自动派单兜了底,人选都对,业务没受损。我拿日志里的文案当了事实。)
③ 配置里写了,就以为生效了
还有一个延保拦截需求:某型号笔记本键盘掉键,厂家有延保政策,要在派单时拦截。
查完发现,配置里早就写了这个型号——但拦截从未生效过。
为什么?配置匹配的字段是"物料名称",而这个型号的物料名称是一串硬件配置(CPU_内存_硬盘_屏幕_颜色),型号本身根本不在里面。
型号的真实来源,是另一个字段。
"配置存在"≠"配置生效"。 判据必须是实测日志和产物,不是"配置里写了"。
④ 响应缺字段,就判失败
还有一种:关单接口返回的响应里缺一个字段,系统就判定"关闭失败",提醒操作人去补操作——实际工单早就关上了。
照着告警去做,是无效动作。
修复:响应缺证据时,回查真实产物(工单现在什么状态),再定性。有意思的是,修复上线当天系统又报了两条"关闭失败",回查后发现这次是真的关不上——修复的价值不在少报警,而在能分清真和假。
一条线串起来
这五件事,领域完全不同:派单路由、手机号校验、文档、延保配置、接口响应。
但形状是同一个:
| 它判断的"字" | 被它错杀的事实 |
|---|---|
| 组名里有没有"寄修"两个字 | 一整类单子的路由正确性 |
| 脱敏号和完整号是否相等 | 派单动作执行没执行 |
| 文档里那句"从未生效" | 51 次成功记录 |
| 配置里写了型号 | 拦截从未生效的事实 |
| 响应里有没有那个字段 | 工单其实已关闭 |
它们都在认字,不认事。
字是便宜的字面量:一个子串、一次相等比较、一句写死的结论。事是昂贵的真相:要读结构、要看时间线、要回查产物、要数双向的记录。
认字快,认事慢。所以系统里到处都是认字的判断——直到有一天,字变了,事没变,或者事变了,字没变。
怎么防
这轮修完,沉淀了几个可复用的判据:
1. 判断依据优先用"结构性事实",不用"字面子串"
组是不是寄修组,看它的归属城市,不看名字里有没有那两个字。名字是给人看的,随时会改。
2. 改配置,扫"谁读这个字段"
不扫消费方的配置改动,是埋雷。而且消费方可能不看配置本身,只看配置派生出来的数据。
3. 说"从未/一直"之前,先数双向计数
看到 6 次失败就说"从未生效"?去数数成功了多少次。只数一边的计数,是幻觉。
4. 拿不准,回查最终产物
响应缺字段 → 回查工单状态;日志说失败 → 核平台实况;文档说过期 → 探一次接口。产物才是事实,其余都是转述。
5. 文档结论要带"何时验证过"
不带时间戳的结论,你不知道它是刚验证的还是两年前的。这次我把时间线(最后成功时间、首次失败时间)直接写进了文档——让下一个读者能看到证据链。
结尾
这轮还有个小插曲:写文档时我一度想用一个"聪明的启发式"——自动推断某城市是否跨分公司。单测立刻抓出反例:有位同事同时归属两个城市,启发式会把另一个城市的真漂移静默吞掉。
最后改成显式配置,老老实实列出名单。
假阴性比噪音更危险——噪音至少看得见,静默的漏判什么都不会留下。
认字,还是认事?
字快,事慢。但运维这行,慢的那个才是对的。
这两天的工作量:30+ commits 跨 5 个仓库。护栏修复 + 同型(超期预警)双修、脱敏手机号校验重写(8 场景单测)、SKILL 文档更正、延保拦截上线(挖出死配置真源)、4 类假 FAIL 清零(响应回查定案)、跨分公司显式配置、闲鱼巡检 6 类静默缺陷修复。
最值钱的一句:护栏认的是字。字一变,护栏就替你保守着一个已经不存在的世界。