记一次重大线上事故
背景
情况是这样的,我们的一个业务线,其中有一个功能,默认会展示一个按钮,默认情况下应该是不展示(false);但我们的默认值设置成了 true,在 mounted 的时候通过发请求的方式去获取信息,把这个值设置再成 false,即不展示按钮;但当网络非常卡的情况下会出现,用户快速点击,点到这个按钮。从而影响到客户的真实利益。
因为这个问题,我们这个业务组件的历史所有版本都要出补丁包,发出去的所有产品都需要让技术支持去联系客户去升级我们的版本。
因为这件事情,我和后端,测试的同事通宵了1天半发包。其他涉及到技服的同事,销售,品牌,以及已经卖出去的用户可能都要有响应的工作。还是整体上给海康与团队造成了不小的影响。
反思
因为代码还是很久很久以前的同事写的,已经很难回溯到当时的场景了,但是我还是想深入分析下,以及提下如何避免的方案。
这里先提到 DFX 设计中的一点:DFR:Design for Reliability 可靠性设计。在产品运行期间确保全面满足用户的运行要求,包括减少故障发生,降低故障发生的影响,故障发生后能尽快恢复。
还有一个就是,防御性设计(Defense Design)在设计和开发过程中,考虑到系统可能会出现的故障、错误、安全漏洞等问题,通过有效的防御手段来减少这些问题对系统和用户带来的影响。防御性设计的目的是为了提高系统的可靠性和安全性,减少系统出现故障的可能性,并能够有效地应对可能出现的问题。
默认值的设定
在需求文档以及设计稿上面,我们一般都是看到有数据的情况,而大家都不会关注没有没有数据的情况;那其实在设计初期我们就要有这样的概念,当接口返回都是空的情况下,我们的页面应该如何展示,肯定是一些数据都是空、0、true/false、null、undefined、[]、{};
在看板中,我们后端可能会返回这样一个统计数据的对象 const count = ref({a:''}) 但如果我们因为偷懒,这么写 const count = ref({});那在页面返回的时候可能会存在端在的 ¥ undefined 的情况,在网速快的情况下可能并不会暴露出问题。但当我们的服务跑了很久以后可能会存现接口返回较慢或者卡住的情况,这样很有可能就会导致出现那 0.0 几秒的问题了。
同样的问题也会出现在 computed 中,我们开篇项目其实就是 computed 的值没有设置对。
异常情况的处理
除了默认值的设定,还要考虑到异常情况的处理。在编写代码的时候,需要想到所有可能出现的异常情况,并给出对应的处理方式,以便在问题出现时能够快速响应并排除故障。例如,在接口请求过程中,应该预留足够的时间给后端返回数据,当请求超时或请求失败时,应该对用户进行相应的提示,告知用户该请求失败,以及如何重新尝试请求等信息。
举个简单的例子,大家在请求前后都会加 loading,但也有同学在请求失败的时候没有去掉 loading,这样就会导致 loading 一直存在,用户无法进行其他操作。
vue 生命周期
mounted 其实最好用,因为怎么写都不会出错,但坏处就是,dom 会同时被渲染就会出现上面的情况出现,所以有些初始请求我们可以考虑下 created 或者 beforeMount,甚至加上 async await。
其他要思考的点
其实本次故障最重要的还是说,我们直接影响到了客户的利益;针对这类可能会影响到业务的问题,我们可能需要多考虑一些防御性的设计与可靠性设计。
再举个简单的例子,交互稿对于删除可能不会单独画个框做删除。但如果我们不弹出 confirm 框,直接删除,那很有可能就会影响到用户的自身利益。
所以最终还是要站在用户的交互去思考可能会存在的风险和问题。仅仅实现功能是不够的。
总结
除了以上以外,其实还可能出现很多种情况;那尽可能用一些工具去避免这些问题的出现,比如 eslint、typescript
还有最重要的一点就是,我们是做好业务,而不是做完业务。