方法论与洞察

沉默失败先装嘴律 · 够不到的环境,先给它一层能看见的报错 · v1

入档:2026-08-21 来源:zombie-world 的 iOS 触屏问题 —— 玩家反馈只有「走不动、开不了枪」, 连改两轮都没中,因为手机上一处 throw 就是整块功能静默消失,而看不到那行报错 验证状态:⭐⭐ 二次验证(同一项目两轮 + 同轮第三处,见下;跨项目未验证


一句话律

当一个失败不报告任何原因时,第一件该修的不是那个 bug,是「它不说话」这件事。

判据很好认:如果你能拿到的全部信息就是「就是不行」,那你接下来做的每一次修改都是猜。 猜中了你也不知道为什么中,猜不中你连排除了什么都不知道。


事故形态:连改两轮,都在盲改

玩家反馈 iOS 上「走不动、开不了枪」,而换枪、开菜单都正常。

两轮之间隔了一天,共同点是:我手上没有 iOS 设备,改完了验不了, 而那台设备回来的信息永远只有「还是不行」。

真正的问题不在这两个判断哪个对——而在于在装上观测手段之前,它们都只是候选。 手机、App 内嵌 WebView、投影仪、别人的电脑,这些环境的共同点是没有控制台: 一处 throw 会把后面所有代码一起带走,页面看起来只是「某块功能没了」, 没有任何东西告诉你发生过异常。


修法:把「不能观测」当成第一优先级的缺陷

第二轮的修改里,真正有长期价值的不是那几处 try/catch,而是这三层:

1. 在所有脚本之前,装一层会显示到屏幕上的错误捕获

<!-- 必须放在其它 script 之前,且自身不依赖任何东西 -->
<script>
window.addEventListener('error', function(e){
  showRedBar('脚本出错:' + e.message + ' @ ' + e.filename + ':' + e.lineno);
}, true);
window.addEventListener('unhandledrejection', function(e){ /* 同上 */ });
</script>

关键是显示到屏幕,不是 console——够不到的设备上,console 等于不存在。 红条只在真出错时出现,正常构建里永远看不到它。

2. 给关键链路做一份能截图的账目

触摸链路加了计数器(收到几次 / 认领几次 / preventDefault 执行几次 / 第一条报错是什么), 做成一个可开关的浮层。判读规则写在代码注释里,别人截一张图就能定位断在哪一环:

live 一直是 0        → 事件根本没进来
start 涨、claim 不涨  → 被判成「落在 UI 上」了
pd < claim           → preventDefault 没生效(事件不可取消)
顶上出现 ERR 那一行   → 直接就是答案

3. 报错要说「为什么」,不能只说「失败了」

联机连不上时原来只有一句「联机服务器连接中断」,等于没说。 浏览器不让脚本看到 WebSocket 握手的 HTTP 状态码——那就绕一下: 连不上时去探一次 /healthz,探得通说明服务器活着、是握手被拒(多半是来源不在白名单), 探不通才是网络问题。拿不到直接证据时,用一个能拿到的旁证把可能性分开。


判据:什么时候值得先装探针

装探针要花时间,不是每次都划算。触发条件是你够不到出问题的那个环境

情况做法
本机能复现别装,直接断点/日志更快
够不到设备,但对方能配合操作,做成「打开开关 → 截图发我」
够不到设备,对方也不会操作装成默认可见的(如出错才出现的红条)
一次都没成功观测过探针优先于修复,先别改业务代码

成本比较通常很悬殊:装一层错误显示大约十几行,而一次盲改要付出 「改 → 打包 → 提交审核 → 等过审 → 请人测 → 得到『还是不行』」的完整往返。 在这种项目里,一次盲改的代价远高于一层探针。


与相邻几条律的分工


反例 / 边界


关联文档

类型/协作工具链主题/协作主题/调试