前端业务开发的通用经验 - 质量保障

8,051 阅读30分钟

什么是质量?

衡量质量的常用指标

  • 开发阶段:提测 bug 数(日均 bug 数、提测打回、千行 bug 率)
  • 上线阶段:终止次数、发布失败次数、回滚次数
  • 运维阶段:分等级线上故障(包括再犯型故障、已宣导过的常见低级故障)、各类监控指标

质量保障的基本要义,就是确保各项指标长期维持在合格线以上。一般由 QA 团队定期整理,公开发布(有的公司称之为“通晒”),不同团队横向比对,形成一种常态化约束。

线上故障需进行分级管理,定级标准主要看影响面和影响程度。比较重大的故障,通常有两种类型:一是影响了核心指标,如用户数、单量、交易额等;二是阻塞主流程,比如某个核心功能无法使用。

还有两类故障需要格外关注:一是低级错误,比如小数运算不考虑精度,这会严重影响技术团队的声誉和专业可信度;二是重复错误,同类问题再次发生,说明上次发生之后的复盘和后续保障动作没有做到位。

对于线上故障,技术团队应该定期复盘,研讨学习,管理者应该将其作为质量保障的长期管理手段,以提高团队的质量意识、规范故障处理的思路和动作、提升故障排查技能。


什么是故障?

技术故障 / 逻辑故障

技术故障是指类似按钮点不动、js 报错、接口报错、页面白屏、服务器宕机、死循环等纯因技术问题导致的故障;逻辑故障是指非技术的业务逻辑故障,即代码运行一切正常,但功能表现和执行结果不合逻辑或不符合预期,例如明明输入了手机号,却提示手机号不能为空。

技术异常通常能够被监控发现(如果监控覆盖比较全的话),而逻辑异常很难靠监控发现,技术手段上一般是靠自动化测试发现,或者靠人工深入到业务逻辑中主动埋点监控(例如监控订单总额,如果小于 0,就上报异常)。


内部故障 / 外部故障

内部故障是指团队负责范围内发生的故障,故障修复职责在己方。除了内部故障,其他的都是外部故障,也就是外部依赖发生的故障,需要由其他团队解决。

外部依赖,对业务方而言,不妨称之为供应链。对前端来说,供应链主要包括网络、客户端/容器、桥方法、接口、前端框架、各类第三方 sdk 等等。

任何外部依赖问题,有时候也是内部问题,反映出对外部依赖的不了解、技术选型不合理、对供应链的风险评估与监控有缺失。


质量保障的“三层四面”

质量保障是一个系统工程,从开发周期上,可划分为事前、事中、事后三个阶段,事前重【预防】,事中重【检测】,事后重【监控】;从保障手段上,可划分为四个方面:基建、测试、风控和管理。接下来本文将分阶段讲述其中的几个重点。


开发阶段

测试服务

质量保障最基本的方法是测试。测试环境、测试工具、测试服务是否给力,对于质量保障很重要。比如兼容性问题,如果有工具能一键驱动几十台机子渲染某个页面,截图比对,直接生成报告,标出问题机型,那这样兼容问题的发现基本就不用人操心了。再比如自动化测试,如果能方便的录制流程,无需人工写代码驱动,那么回归的覆盖面和效率都会大大提升(比如网易的 airtest)。


技术架构标准化

技术架构标准化是指有约束力的最佳实践。对最佳实践的衡量需要围绕两个核心利益点:开发效率、开发质量。也就是说一种经验能否算作最佳实践的基本标准,是看它能不能提高效率,以及是否有利于质量保障。

技术架构标准化之所以能保障质量,主要原理有两点,一是预防甚至消解导致故障的潜在隐患,通过设计上的硬约束剥夺人犯错的机会;二是控制复杂度,复杂度根本上是规模导致的,而规模是需求决定的,是技术不可控的,可控的是技术实现的复杂度。所以为什么用框架、组件化、mvvm,核心原因是降低复杂度。

约束开发模式,统一团队开发风格,也是为了控制复杂度,因为每个人的个性设计杂糅在一起会带来额外的复杂度。


供应链质量

一个页面到底依赖了啥?通常一个业务时间一久,维护的人换过一茬,基本就不容易搞清楚了。通用的依赖一般来说有:CDN、网络、容器、接口、基础框架/库、sdk(埋点、监控之类的)、打包工具等等。

每个项目都需要盘清依赖项,明确边界。关键要形成两点判断:第一,依赖是否可靠,比如出问题了能否找到维护方、核心的依赖如果崩溃有没有降级兜底方案等;第二,依赖是否彻底解耦,比如某些配置不应该跨项目或者跨团队存在,否则一定会相互中伤。

为了快速感知与定位供应链质量问题,针对每一种依赖,都需要做一些防范和监控。


CDN

CDN 的一个主要问题是节点故障,该问题的一种常见表现是:某个手机上请求静态资源耗时很长,但是切换下网络,发现就好了,再切回去,还是耗时很长,而其它手机一切正常。说明有可能是某个 CDN 节点发生了故障,同时又因为 DNS 缓存的缘故,出问题的手机可以稳定复现这个问题,清缓存后问题可能就不再复现。监控这类问题的基本策略是拿到所有资源的加载时长,如果超过阈值,则上报异常。


网络

网络的一个主要问题是劫持。比如这种场景:因为 js 静态资源是单独部署的,因此需要解决跨域问题,script 标签上要加 crossorigin=anonymous,响应头要带 Access-Control-Allow-Origin: *。资源被劫持后,响应没有带这个头,就会导致资源因跨域而被浏览器拒绝执行,页面肯定就挂了。监控劫持的一种思路是 IP 白名单,另一种思路是 hash 校验,参考 Subresource Integrity


容器

Webview 可能出各种问题,举个我遇到过的例子:h5 页面自适应设计依赖 rem,rem 的计算需要取页面宽度,这段 js 通常会放到 <header> 标签内执行。然后因为容器背后的预加载逻辑有问题,导致 html 解析到 rem 计算脚本的时候,取出的页面宽度是 0,相当于容器页面还没有初始化好,但 html 已经开始执行了。结果就是所有以 rem 为单位的尺寸,实际都等于 0px,页面就白屏了。那这怎么监控呢?基本思路是从 html 元素开始递归遍历每个元素,用 getBoundingClientRect 算出尺寸,如果发现所有元素都没有尺寸,就证明白屏了(细节上还要考虑 display、visibility、opacity 等问题),发现白屏,就把整个 dom 文档报上去(document.documentElement.innerHTML)。很暴力吧,不过上述问题就是通过这种方式发现的(发现 html 标签的 font-size 为 0)。

容器提供的桥方法,业务方最好做一次封装,避免在业务代码里直接使用桥方法,好处有很多:

  • 统一处理异常上报,避免遗漏
  • 统一处理桥方法升级适配,避免在整个项目里大范围搜索和改动
  • 统一处理监控逻辑,例如关键桥方法的成功率监控

接口

接口异常一般有三类:网络异常(5xx、超时);业务异常(一般用自定义状态码标识);数据结构问题(缺字段、数据不合法等)。

网络异常监控主要是通过网络库的能力,业务异常和数据结构异则需通过代码监控。举个数据异常的例子:后端某天发现一个接口的经纬度写反了(显然前端也是反的,所以功能没问题),于是单方面修改后,悄悄上线,上线后没实际在 app 上测,自以为问题修复了,那肯定就出问题了。这种情况,前端可以对经纬度的范围做下校验,有问题就报出来,这就是主动监控的一个例子。

除了异常监控,还需要考虑对接口的调用量进行监控,调用量的骤增或骤降也是一种异常。我遇到过因前端死循环导致接口调用量上涨几千倍的案例,就是因为缺调用量监控,故障持续了整整一天才被发现。不过后端只能对总量做监控,如果只是小样本用户遇到死循环不断调接口,不一定能触发后端的监控报警,因此前端应该主动监控接口短时间重复调用的情况。


基础框架/库

我从来没有遇到过 vue 和 react 本身的 bug,大概是用得晚而浅吧。一般通用框架成熟期的质量还是很可靠的,毕竟有那么多人在用(当测试)。框架也提供了一些异常处理机制,需要利用起来,比如 vue 的全局配置方法 errorHandler 和 react 的 ErrorBoundry 组件。

引入第三方库,原则上需要锁定版本,不要完全指望开发者遵循 semver 版本语义,任何变动都可能产生故障。


sdk

我们引入的 sdk 也是可能出问题的。像监控类的 sdk 必须先加载,且必须是同步加载,可考虑内联,避免网络加载失败(网络成功率不会是百分之百,一般 99.9% 上下),然后一定要控制版本。

举个我见过的例子,某 sdk 内部有一个上报逻辑,为减小代码体积,自行封装了 ajax 方法,但忘了设置异步,搞成了同步,而容器不允许同步的网路请求,就崩了,这种业务方除了猛怼似乎也没什么辙。sdk 的质量只能由服务方保障,所以就需要明确:依赖的服务得找得到负责人。


打包工具

像 webpack 这样通用的工具也会出问题吗?一般工具本身的 bug 不容易遇到,常见的是用法导致的 bug。比如打包出的代码包含 es6 导致兼容问题(没错,2020 年安卓 5.x 仍有不小的用户量),那显然是某个环节出问题了(比如 import 了 node_module 里未经编译的 js),还有像我之前用 tapable.js 导致 es6 代码被引入,都莫法编译:

所以说选择外部依赖时,要么是一个通用依赖,有很多人在用,并且跟你是同一种用法(tapable 是一个通用依赖,但很少有人直接使用),要么非常清楚背后的代码实现,再不然就得靠全面的测试。否则贸然引入就会有风险。

针对打包环节,一般需要的监控手段有:


日志

哪些场景需要记日志?

  • 为了统计某个指标
  • 为了排查问题
  • 为了监控异常

哪些地方需要记日志?

  • 主动监控:接口、桥方法、业务异常
  • 被动监控:网络/资源时延与成功率、页面性能、js error

日志覆盖不全,意味着监控有漏洞。但也不是越多越好,增加流量消耗,浪费服务器资源。


怎么记日志?

首先是做分类和分级,主要目的是有助于针对性的配置报警,比如可以针对某个页面的 x 类问题的 x 级别配置一个报警策略。

  • 分类:网络 / js / 自定义
  • 分级:Error / Warn / Info
    • Error 是指明确对用户有严重影响的、不允许存在的异常,如果允许存在,就不能是 Error 级别。Error 级日志只要存在,就应该想办法清零,报警策略上,敏感度应该最高,只要一出现就报警
    • Warn 是指明确是个问题也明确一定有少量出现,但是对用户影响不算严重。这种级别在报警策略上,主要是看量级,少量 Warn 允许存在
    • Info 一般就是纯提供问题排查信息的日志,完全不用考虑监控

手动上报的日志名称,最好和自动上报的日志名有所区分(例如统一加个前缀),同时带上标记,例如页面名、接口路径、方法名,好处是一目了然。日志的参数内容,应该尽可能包含问题排查所需的必要信息(通用的如客户端型号、系统版本、网络类型、地理位置等等)。


日志存到哪里?

  • 本地:量较大但使用频率极低的日志,存用户本地,以避免过度消耗服务器资源,需要通过回捞才能查看,不能即时查看
  • 云端:使用频率较高,需要即时查看

技术方案

很多时候,技术方案都是后端主导,毕竟业务逻辑主要在后端,前端只是负责展示,而且后端涉及库表、缓存、接口、系统调用关系、逻辑流等,很容易画出一堆图来讲,前端要是没个明确指南,技术方案往往不知道写啥。

一般估时超过一定天数的大中型需求,需要书面的技术方案和评审环节,尤其是涉及多人分工开发的场景。做技术方案主要目的有三个:

  • 提前想清楚关键问题,比如:
    • 影响范围:本次需求波及的改动范围,漏掉的话排期肯定就不准,QA 也特别关心改动范围,因为这决定了 QA 的测试范围
    • 依赖资源:要完成本次需求,哪些必备功能需要外部提供。需要明确这些功能是否可靠(可能需提前调研)、如果没有能否及时提供、无法及时提供又怎么办
    • 数据来源:展示层所需的所有数据都来自哪里,放到哪里或由谁提供才合适
    • 核心逻辑:通过“再叙述”确保理解无误
    • 接口方案:有时候接口前端定比较好,毕竟作为需求方最知道自己需要啥
    • 兜底方案:假设这个功能出问题了,有没有备份方案,例如开关、降级等手段
    • 监控方案:通过什么样的方法能知道我这个功能上线后是有问题还是没问题。这个其实很考验水平,一定程度也决定了自动化测试的设计用例,主动监控和用例设计某种程度上是一回事
    • 测试方案:需要准备哪些条件通过哪些过程达到哪些预期才能证明我的功能是没问题的。如果在需求开始前,能在脑子里模拟出整个功能的逻辑实现过程,发现其中的关键点和风险点,乃至倒推出需求设计的不合理处,其实很能体现做事的水平。有时候经验不足的开发,可能开发完后,才发现好些功能很难测甚至没法测。如果能尽早预见,就可以调整技术方案,或者增加估时。免得提测后,还手忙脚乱
    • 上线方案:主要关心谁先谁后,出问题了回滚顺序如何。还有,注意避开封禁期,我就见过灰度发布到一半结果刚好抵达封禁时间,导致剩下一半发不了的情况(虽说应算作发布系统的问题)
  • 排期的依据:大型需求只有经过拆解才可能给出准确排期
  • 同行把关:从团队管理角度,一般在各个细分领域有至少主备两人(或多人)专门负责。评审时视情况将相关的负责人拉进来,协助扫雷

如果是多人协作开发的需求,需要仔细划分,避免相互产生依赖,不能避免则要明确依赖关系。各自负责的范围也最好有明确边界,否则代码容易出现冲突。解冲突其实很容易出问题,凡是依赖人的谨慎与仔细之处,都容易出问题。

技术方案完成后,需要组织评审。评审的一个重要功能是需求再确认,同时也为后续测试方案提供依据,所以产品和 QA 都要参加。技术评审需要注意哪些是内部问题,哪些是公共问题。内部问题内部消化,避免在多方参与的会议上讨论内部问题。比如我多次遇到过会上几个后端在争论内部的技术细节问题,前端、产品、QA 都不知道他们在说啥,这样的会议效率极低。

啰嗦一句开会这事儿,原则上如果一个人只需听两分钟的内容,那他就不应该参加会议,最好由会议主持私下单独沟通。像著名产品经理纯银就倾向于开小会,哪怕多同步两次。这个比较依赖人的素质,多数人不懂怎么开会但又不得不组织各种评审会议,如果你被迫加入这样的会议,那听完两分钟就应该走人,别不好意思。


SOP

SOP(标准操作程序)是一种流程标准化的行动指南,SOP 应该达到的标准是任何新人看着文档就能一步步完成整个操作。每个团队 wiki 里,都应该有个专门的目录,记录各类 SOP。SOP 其实是贯穿所有环节的,比如上线 SOP、下线 SOP、线上故障处理 SOP、值班 SOP、运营活动配置 SOP 等等。


需求管理

有人可能觉得这咋是开发需要关注的事情呢?而且这事儿跟质量保障有毛关系?

通常做开发的人都习惯于执行,而不善于质疑。可能是因为公司未建立起相应的制度或共识,不过【对需求有判断力】的确是个比较高的要求。个人认为作为研发至少得主动争取表达意见,不表达就不可能有话语权,否则产品一句话,你加班仨小时,能忍?有些大厂就很明确,讲不清楚需求的收益,需求评审未通过,研发有权利拒绝执行。这就倒逼产品必须想清楚,说明白。理论上,不做需求就没有 bug 对不?做经过深思的需求,中途不要瞎改,相比起需求频繁变更,出 bug 的几率更小对不?

此外,需求的规模也是一个问题,这里要分情况,有时是客观现实决定一个需求就是很大,有时是因为需求拆分不合理,把多个需求揉到一起导致一个需求显得很大,有时是因为解决方案超出了“问题和目标”所定义的范围,夹带了太多“私货”。

需求规模过大,会带来一系列问题,导致出问题的概率增大:

  • 吸收率。信息量太大,一次评审是消化不完的,评一次不够,评多次又浪费时间。合理的粒度,是保障评审效率和效果的基本前提,一个会议超过一小时,有效性就非常可疑(对普通干活的人而言)
  • 信息量太大,导致细节问题太多,很容易遗漏,难以提前发现
  • 粒度太大,导致工期较长,可能出现交叉需求、并发需求,可能因人力长期无法释放阻塞其他事情的进展
  • 粒度失控,引入过多信息,会增大信息同步成本。如果项目工期较长,还会因记忆问题,中途需要不断沟通对齐,进一步降低效率
  • 容易出现变更。需求变更,经验不足的开发很容易改旧引新,改旧代码引入新故障

对研发而言,需求管理可以做的事情包括:

  • 质疑必要性、合理性,凡事先想为什么。无明确收益或收益不可衡量的拍脑袋式的需求,拒绝执行或优先级往后排(有些事拖一拖往往有转变)
  • 大需求、探索性需求可考虑分期开展
  • 明确要做的事情,符合问题与目标所定义的范畴
  • 需求变更不能轻易接受,哪怕是需要接受的合理变更,也应该给需求方施加一点压力(比如记下本次需求一共变了几次,结项后来个复盘,或者明确告知,需求变更需要单独走流程,重新评审)
  • 对产品本人需要有判断,比如刚加入团队,又提比较大的需求,大概率会出现较多的需求变更

涉及多个需求的整体管理问题,一般需要 TL 关注,比如需求间是否存在耦合关系,时间安排是否合理,优先级谁先谁后等。比如这样一种场景:关联需求分两拨人同时做,其实是有风险的,假设 a 依赖 b 的某功能,b 也依赖 a 的某功能,必然只能一起测试、一起上线,否则就死锁了对不。但这么做中间沟通协调的成本就比较高,出问题的概率也比较大。

如果需求间出现了重复,甚至冲突,那就是产品内部管理的事故了,TL 之间就需要友好沟通一番。


集成阶段

分支管理

发错分支,或者本地 master 分支合入了一些测试代码然后不小心推到远程,这些绝对出现过不止一次。分支管理必须通过代码管理平台从技术上限制:

  • 禁止本地直接 push master,禁止远程直接修改 master
  • 合并 master 强制走 pr,必须有 approve 才能合并
  • 锁定线上环境发布的分支,避免其他分支因误操作被发上线

代码集成最好和上线流程强行绑定,避免合了 master 但不上线的情况,导致故障无法及时暴露,也影响到其他需求的上线


封禁期 / 灰度 / 渐进式

封禁期是一种风控手段,避免节假日上线,除了可能因用户量增加放大故障的后果,还有就是避免因休假无人修 bug,也算是对员工的一种保护吧。不过有时会出现为了赶时间多个上线堆积到封禁期前一天的情况,这也是风险,需管理者注意。

灰度发布主要是避免一次性上线,导致潜在故障也跟着瞬间全量。在逐步开量的过程中如果发现问题,可以及时中止回退,从而降低影响和损失。灰度发布需要注意比例控制,比如单纯因为谨慎,第一阶段只开很小的量,考虑到监控阈值都是按全量配置的,往往因量太小而无法及时暴露问题。

渐进式是一种基础理念,不仅体现在发布过程,也体现在日常做事中。像我入行一年多终于进了大厂,刚进去就干了件非常愣的事:把 webpack 1 升到 3,以解决编译速度等问题。因为项目有一定规模,吭哧干了一星期,一个 pr 改动文件好几百个,甚至都超出了代码管理平台的 diff 上限。这么大的改动,没有 QA 测试,自己测了几遍,就直接上线了。结果就出了一个偶现问题(特定条件下发生),这就懵了吧,几百个文件 diff,是哪个改动导致的呢?如果分成几批渐进式改造,不仅降低风险,还有助于事后定位问题。


Code Review

估计好多团队都不搞 Code Review,从实践来看,通过 Code Review 发现问题的比例,的确很少,因为太依赖人的素质了。不过 Code Review 仍有必要性,首先低阶新人的代码必须 Review;其次,Code Review 是代码质量保障的一种手段,每个人都在闹,业务代码是坨翔,那 Code Review 的时候干嘛去了呢;第三,可作为团队内部技术交流的一种方式,互相学习编码经验(主要是新人学老鸟)。所以从机制建设上看,Code Review 也许某阶段做不好,但不能没有。不好可以改进,没有,那整个技术管理机制就太弱了。

为了提高 Code Review 的可行性和有效性,贺师俊在这篇文章里提到一点:限制单个 pr 的粒度。也有一定道理,不过操作成本略高,视团队情况而定吧。

一般需要 Review 的内容:

  • 代码质量(前置校验、边界条件、复用、技术实现、加日志...)
  • 代码可读性(指出看不懂的地方、改命名、加注释...)
  • 发现问题(技术实现的问题、业务逻辑的问题...)

重点强调:第一版代码(新增页面、新增业务组件)必须严格 review,一个变量都不能放过。因为第一版代码奠定了某种基础,如果设计上存在问题,后续迭代大概率会在错误的方向上越走越远,到后面每加点新东西都举步维艰。想重构优化,改动范围、回归成本都成倍放大,极其痛苦。第一版代码一定要找最靠谱的人写,然后重点 review。初期看可改可不改的问题,只要有改的理由(哪怕只是潜在风险)都应该改,不要小看破窗效应

代码管理平台可以提供一种约束性保障:每个 comment 必须得到回复或解决,才允许代码合并主分支

Review 有两种形式,一种是提 PR 时 review,一种是定期复盘型 review。提 PR 一般是提测的同时就要提出来,等上线前提就晚了,一是审核人不好提意见,会因顾虑阻塞上线而被迫 approve,二是就算提了意见,改还是不改,改了要不要测?所以 PR 提太晚,Code Review 就是无效的。

Code Review 在操作上一般还会面临一些问题:

  • 责任心:“他做事一般比较靠谱,pr 不用仔细看,肯定没问题”;“何苦互相为难,礼尚往来嘛,直接 approve”
  • 逆向选择:“嗯,已经有两个人看过 pr 了,那我也不用仔细检查,肯定没问题,上线”

解决责任心问题,主要机制是 reviewer 一起担责,如果事后出问题,复盘时判定这个问题应该可以在 review 时发现却因没有仔细 review 而被忽略,那么 reviewer 是需要承担一部分责任的。当然,有责任就有权益,approve 的数量和 comment 的数量都可以作为研发过程度量的指标,以反映投入度和贡献。


参考:Google Code Review Developer Guide


运维阶段

监控

监控系统的责任,是尽快发现问题。要做好这点,有两个基本前提:

    • 监控的覆盖面全,能尽可能多的捕获到问题
    • 记录的信息全,对问题的描述足够充分,能为后续定位问题的原因提供足够的信息
  • 准:报警准确,误报过多会降低感知问题的敏锐性

监控系统做得比较深入,可以做很多功能,比如页面回放,直接复现用户的操作过程。


参考:浅谈前端监控


备份、降级、开关

前端习惯了完全动态化,随时上线,无版本概念的开发模式,有时无法体会兜底措施的重要性。

像 native 这种缺热修复能力,也没办法回滚,一发版就没后悔药的,各个重点功能都必须考虑备份方案,出问题了可自动降级,或通过开关控制,关闭入口。通常大厂 APP 都有专门的配置下发方案。前端可以利用 json 配置平台,或者接口,或者最粗陋的,直接扔个 json 文件到服务器,借此实现远程开关控制。

事实上前端也会遇到版本问题,参考

image.png


故障管理

核心理念

拆解事故,不为追责;看清根因,逐步提升。


故障处理

如何在最短时间内止损、定位原因、修复上限,非常考验水平和经验。总结下来,有一系列标准化动作供参考:

Step 0 明确问题

首先是明确问题的严重性。如果问题很严重,需要将周知对象上升到更高级别管理者。原因是:更高级别的管理者本身有更多的经验,同时可以调动更多的资源,有助于尽快解决问题。毕竟线上故障,往往优先级最高,快速解决问题是最高目标,上级有义务有责任停止工作立马投入到故障处理流程中。如果因为没有及时上报,而是自己硬抗,导致故障处理时机延宕、影响消除时间延后,那可就有责任了。

除了判断严重性,还有就是要搞清楚真正的问题究竟是什么,避免后续排查出现方向性错误。很多时候通过文字不见得能把问题描述清楚,尤其是来自非技术人员的反馈,这时就需要尽快电话或当面核对,也可以要求截图或视频佐证,最好是自己能复现

Step 1 及时止损

明确问题后的第一件事一定是止损,而不是埋头去找问题。当然也需要判断影响是否足够严重,不是很严重的问题,或者止损措施可能导致更大的损失,那么需要酌情处理。

Step 2 定位原因

原因定位有很多思路,很考验能力和水平,也比较有意思,像是在解谜。

定位过程需要遵循一定的科学方法,不能全靠瞎猜:

  • 新增问题看改动。没改动,就不会有新增问题
  • 看日志。找全日志,不全就加日志
  • 正向 / 逆向推导。从代码入手一步步梳理过程。最好是监控系统能够提供重现故障路径的信息
  • 对比法。比如只改某一个地方,分别发布后 AB 对照下
  • 排除法。一步步删除,逐步定位病灶,删除的原则可以考虑二分法

Step 3 修复上线,回归验证

故障修复后,需要周知通报。

Step 4 复盘总结

一般线上故障,都需要文字化的记录整个排查过程、分析根本原因、明确后续措施。


故障复盘

故障管理主要围绕复盘文档进行,从技术管理角度看,无论对团队还是个人,故障复盘文档其实是一种重要资产和财富,是拿千万流量和真金白银喂养出来的,可以沉淀和提炼出大量的实战经验,甚至可以结集出版。

复盘文档主要包括四个部分:

1、时间线

从产生问题到发现问题的分钟级过程分析,主要目的是看处理的过程和动作是否合理,是否走了弯路,能否更快发现问题,能否找到更好的解决思路,技术层面、管理层面怎么改进更有利于故障的排查和处理。从管理角度看,主要关注三个时长指标:

  • 从故障发生到故障被发现的时长,显然是越短越好
  • 从故障被发现到影响消除的时长,看止损是否及时
  • 从故障被发现到问题解决的时长,看解决问题效率

如果时间都太长,那就更有必要深度复盘。

2、影响范围、定级

影响范围是定级的依据,定级是一种管理措施。比如:

  • 团队质量目标:全年无 xx 级以上故障
  • 达到 xx 级别的故障,需要 xx 职级的老板参与复盘

3、根因

必须定位到最初的源头,找到根因,才算真正定位到了原因。找到根因的基本标识,一是能复现,二是无法再追问,换句话说,找到根因的基本方法就是复现 + 持续追问(5 why 原则)。

判断原因是否有效,主要看能否针对原因提出可执行的解决方案。按这个标准,以下原因都是无效的(通常都是主观意识类的):

  • 疏忽大意
  • 时间太紧
  • 测试回归不全,要多测试
  • 重视程度不够、缺乏敬畏之心、要加强重视
  • 不仔细
  • 考虑不全面
  • 对代码不熟悉

从故障复盘角度看,无效的原因,不一定无用。比如长期的疲劳工作肯定会增加出错的概率,管理者也应该关注,不能过于机械主义,没人情味,出了故障一顿犀利追问,把人搞成了人犯。

4、后续措施

后续措施主要是针对本次故障的原因给出的改进措施,主要目的是解决这样几个问题:如何保证下次不出错?如何保证下次出了错能及时发现?如何保证别人不出同样的错?如何保证同一类型的问题不再发生?后续措施也需要跟进结果,每次故障复盘会议第一步就是回顾上期布置的 TODO。


三大红线

事前:未经测试禁止上线

事中:无法回滚禁止上线

事后:无法回归禁止上线