那个让我调了四个小时的Bug,原因竟然是...

上周五,我差点因为一个Bug提桶跑路。
事情是这样的。我们负责的一个后台管理系统,用户反馈说某个列表页偶尔会白屏,不是必现,大概十次能复现两三次。这还不是最糟心的——生产环境才出现,本地和测试环境都是好的。
我一开始以为是环境变量的问题,把dev、test、prod的配置翻来覆去对了一遍,没问题。又以为是打包的问题,webpack配置、代码压缩、sourcemap、tree shaking全部排查了一遍,还是没头绪。
然后我开始怀疑是数据问题。是不是某个接口返回了空数据,前端解构赋值的时候崩了?加了无数个可选链和默认值,问题依旧。
接着怀疑是浏览器兼容性,试了Chrome、Edge、Firefox,都能复现。甚至怀疑是某个第三方SDK搞的鬼,一个一个注释掉引入的库,还是没用。
整整四个小时,我看着屏幕,感觉它也在嘲笑我。
同事路过说:"要不你换个思路,看看Network面板?"
我深吸一口气,打开Network,刷新页面,一个一个请求看。突然注意到,接口响应里有一个字段的值是null,而其他地方这个字段都是数组。继续追下去发现,当这个字段为null时,页面某个组件里用了[].concat()的处理逻辑,看似没问题。但再往下走,这个值被传到了Array.prototype.sort()方法里——null没有sort方法,直接报错,但错误被某个全局的try-catch吞掉了,所以控制台什么都没显示。
问题的根源是什么?后端某个特定条件下返回了null而非空数组,前端没有做防御性判断。为什么本地不报?因为本地mock数据写死了空数组。
花了四个小时,最终改动就一行代码:
javascript
const list = (response.data.list || []).sort((a, b) => a.order - b.order);四个小时,一行代码。
这就是程序员的日常。你永远不知道一个看似简单的Bug背后藏着什么妖魔鬼怪。最折磨人的不是Bug本身有多难,而是你满世界找答案,结果答案就在眼皮底下,傻到不好意思跟人讲。
但我后来想明白了几点,写出来共勉:
永远不要假设数据是干净的——后端说"放心肯定不会为null",听听就好,该做的防御一个不能少
错误日志一定要打印完整——吞掉异常是灾难级的做法
先从网络请求看起——再花哨的问题,90%出在数据层面
调Bug的时候站起来走走——有时候换个姿势,思路就打开了
这条微博送给所有还在跟Bug斗智斗勇的兄弟姐妹们。你不是一个人在战斗。
