先不要回退,也不要继续加码。把这次改动拆成“谁先执行、谁依赖谁、谁被谁观测”三层,找出异常是发生在被改动的环节,还是发生在依赖它的下游环节。多数“修好A却坏了B”的情况,是因为B本来就在等A的旧行为,A一变,B的假设就失效了。
判断依据不在于异常出现的时间先后,而在于它是否可被上游状态解释。可区分的原因大致有三组:
验证方式很简单:把改动单独停用一次,看异常是否同步消失。如果消失,说明存在依赖关系;如果不消失,先排查第三方和缓存,不要继续在依赖链上做假设。
如果异常只是告警、降级或非关键路径的延迟,且下游有兜底逻辑,可以保留改动,转而修正下游的假设。前提是你能明确指出下游依赖了哪个旧行为,并能在不改动上游的前提下替换它。代价是修复周期变长,期间异常会持续存在。
改写是拆依赖链的核心动作。常见做法是把“隐式依赖执行顺序”改成“显式依赖状态”。例如下游脚本原本假设某个资源已经就绪,现在改为监听就绪事件或轮询状态,而不是靠加载顺序碰运气。前提是你能定位到具体的依赖点;如果定位不到,改写只会把异常推到更隐蔽的位置。
如果下游依赖遍布多个模块、缺少测试覆盖,或改动影响的是支付、登录等关键路径,回退是合理选择。退出的代价是放弃这次优化收益,但换回了可预测的状态。回退后应记录“哪条依赖导致退出”,作为下一次拆链的起点,而不是简单搁置。
假设某页面为提速把首屏图片改为延迟加载,上线后埋点脚本报错,因为该脚本在初始化时读取图片尺寸,而图片此时尚未请求,尺寸为0。这里的依赖链是:埋点脚本依赖图片已加载,图片加载被延迟加载改动切断。
可选动作有三种。其一,保留延迟加载,把埋点脚本改为在图片加载完成事件后读取尺寸,结果是首屏收益保留,但埋点数据会晚于原先时机上报。其二,只对首屏第一张图取消延迟加载,结果是冲突消失,但首屏收益缩小。其三,整体回退,结果是埋点恢复,优化暂停。
这个例子说明:决定保留还是退出的关键,不是改动本身好不好,而是下游能否在不牺牲其核心职责的前提下接受新的时序。若埋点晚报会导致数据不可用,就应选第二种;若晚报可接受,选第一种更划算。
建议按顺序做三件事,每件事的结果都直接决定下一步:
需要提醒的是,抓取量或请求量下降、某项统计归零,都不能单独证明改动正确或错误。它们也可能是缓存命中、爬虫调度变化或统计口径调整的结果。把这类现象与依赖链判断分开处理,避免用单一指标替代因果分析。
拆依赖链的终点是一句可执行的判断:保留并改写下游、缩小改动范围,还是整体退出。只要这句话能对应到具体的依赖点和可验证的观察结果,这次异常就不算白遇到。下一次上线前,把这条依赖记录进改动清单,比事后争论“该不该优化”更有用。