1. 为什么缺陷总是反复出现:从现象处理到根因治理
做软件研发这些年,我最怕听到的一句话不是"线上出故障了",而是"这个问题之前不是修过吗,怎么又出现了"。
这句话背后藏着一个团队长期没有解决好的问题:大家一直在修"现象",而不是在治"根因"。所谓缺陷根因分析,就是在问题发生之后,不满足于把表面症状处理掉,而是顺着链条一路追下去,找到让缺陷得以产生的那个最底层原因,再针对这个原因设计改进动作,保证同样的问题不会以相同或者类似的方式再次出现。
1.1 现象修复与根因修复的差别
我见过太多这样的场景:线上接口报错,排查了半天发现是缓存键冲突,代码里加了一个前缀,问题消失,大家都松了口气。结果过了一个月,另一个服务因为同样的缓存键冲突又挂了一次。这就是典型的现象修复。
现象修复的特点是"头痛医头,脚痛医脚",处理的是直接触发问题的那个动作。它的优势是快,特别适合正在进行的故障处理场景。但它的劣势也很明显——没有改变产生问题的土壤。缓存键冲突的背后,很可能是缺少统一的命名规范,或者是缓存工具封装层没有内置隔离机制。只要这个土壤还在,换一个时间、换一个人、换一段代码,问题就会再次冒头。
根因修复则完全不同。它会继续追问:为什么这里会写缓存键冲突?为什么评审的时候没人发现?为什么测试场景没有覆盖到?顺着这些问题往上走,最后落到一个或多个可以改变的系统性因素上,比如补充代码规范、在CI流水线里增加静态检查、修改缓存组件的封装逻辑。改的是这一层,收益的是整个体系。
1.2 深度不够的三种典型表现
根据我的观察,根因分析做得不到位的团队,通常会呈现三种典型表现。
第一种表现是归因到人。分析会开到最后,结论落到了"某某同学责任心不强""某某同事粗心大意"这种层面。把原因归结到人,等于什么都没分析。人是会犯错的,任何系统只要依赖于某个人的"细心",就一定存在隐患。正确的做法是把人当做一个普通环节来看待——如果一个人容易犯错,那就设计机制让错误在发生之前被拦截,或者让错误的代价足够小。
第二种表现是停留在最浅层的技术原因。比如数据库连接池被打满,根因写的是"连接数配置太小"。这个结论没错,但不完整。为什么配置会偏小?是容量评估缺失,还是压测场景不真实?接单服务上线的决策过程里有没有评审环节?评审的检查清单里有没有连接数这一项?如果这些问题没有被回答,那么下一次换一个服务上线,大概率还会栽在同一个坑里。
第三种表现是没有把分析结果转化成动作。根因分析做得轰轰烈烈,鱼骨图画了一黑板,5 Whys追了五层,最后的产出却是一份漂亮的会议纪要,然后就没了下文。任何不能转化为改进动作的分析,本质上都是自我安慰。
判断一次根因分析是否有效的标准只有一个:在未来的三个月里,是否有一项新的制度、工具或流程,因为这次分析而真实地落地了。
1.3 根因分析缺失的隐性成本
很多人低估了缺陷重复发生的代价。表面上看,每次修复bug花的可能就是几个小时的排查时间。但放到一个更长的时间轴上,这笔账非常惊人。
首先是信任损耗。同一个故障反复出现,客户会怀疑你的专业能力,内部业务方会对研发团队失去耐心,团队自己也会因为长期在同一个地方栽跟头而产生挫败感。
其次是隐性工时占用。每次重复修复,都要重新拉群、重新排查、重新上线、重新发公告。这些时间成本是分散的,散落在各个团队的日历里,很难被量化,但累积起来相当可观。
最后是机会成本。当团队的大部分精力都用来对付那些本来就不该再次发生的老问题时,就没有时间去优化架构、建设工具、探索新业务。这个损失,比前两者都更大。
这也是为什么我越来越觉得,缺陷根因分析这件事,不是质量团队一个部门的事,它本质上是一个研发组织的治理能力问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 根因分析的完整流程:从问题发现到结论收敛
很多团队做根因分析,流程是混乱的。通常是故障处理完了,负责人被要求写个报告,于是凭着记忆把时间线整理一遍,挑几个说得过去的原因写上,提交,结束。
这种流程做一百次,也攒不下任何组织能力。一套合格的根因分析流程,应该是标准化的、有节奏感的、每一步都有明确产出的。
2.1 先冻结现场,把信息收集做扎实
根因分析最大的敌人是信息失真。故障刚发生的时候,大家都很紧张,一边翻日志一边改配置,现场非常混乱。等到故障恢复,再想复盘的时候,很多人已经记不清当时的操作顺序了。所以流程的第一步,是在问题发生的当下,就把"现场"冻结下来。
具体要做三件事。第一,保留关键证据,包括异常日志、核心接口的请求记录、应用在故障时间段内的监控曲线、相关配置项的变更记录。能导出的都导出来,按时间线归档。第二,记录关键操作的时间点,谁在什么时间做了什么操作,比如谁在几点几分改了哪台服务器的配置,谁在几点几分执行了限流。第三,锁定范围,明确这次故障涉及哪些服务、哪些功能、哪些用户群体,把影响边界画清楚。
这一步看起来基础,但绝大多数分析失败,都是因为信息收集阶段偷了懒。等到复盘会开起来,大家各说各话,场面会非常被动。
2.2 组织一场高质量的分析会
信息收齐之后,需要一场专门的根因分析会议来收敛结论。这个会和普通的周会、项目会有很大区别,想开好需要提前做功课。
会议时间要放在故障恢复后的24到48小时之内。间隔太短,情绪还没平复,很容易开成追责会;间隔太长,细节会遗忘,关键信息会被记忆美化。我倾向于在24小时以后开,给当事人一点缓冲,也给自己留出整理时间线的时间。
会上最重要的规则是先讲事实,后讲观点。所有参与分析的人,只能陈述自己确认过的信息,不能凭印象猜测。谁要是说"我觉得可能是……",当场就要被要求提供证据支撑。这个规则执行得越严格,分析会的质量就越高。
会议的主持人非常关键。原则上,主持人不应该由故障相关的直接负责人担任,否则很容易陷入"自己分析自己"的立场困境。最好是由测试负责人、架构师或者质量管理专员这类第三方角色来担任。主持人的核心任务是持续提问,不断追问"为什么",不放过任何一个含糊的结论。
2.3 结论收敛的标准:真因的三个特征
分析会不能无限期地开下去,任何一个问题都要有收敛的时候。我的经验是,当讨论找到的那个原因同时满足三个特征时,就可以判定为真正的根因,进入措施制定阶段。
第一个特征是可解释性——它能够完整解释故障发生的整个因果链,中间没有断点,不需要用"巧合""运气不好"这类词来填补逻辑缺口。
第二个特征是可验证性——能通过日志、数据、实验来验证它的存在。如果一个根因没办法被验证,那它就是一个猜测,不是结论。
第三个特征是可干预性——我们能够针对它设计出明确的改进措施。如果找到一个原因,但完全想不出怎么干预,那它对实践的指导意义就很有限。
三个特征全部满足,就可以把根因锁定,并开始设计改进措施。如果讨论了半天,发现根因还是模糊的,那说明信息收集环节还有遗漏,需要返回去补数据,而不是在会上硬憋结论。
3. 根因分析方法选型与实战要点
方法不在多,在适用。我用过的根因分析方法有好几种,各有各的适用场景,也各有各的坑。挑团队用得上的几种展开聊聊。
3.1 5 Whys:够用,但不等于机械地问五次
5 Whys是最常用的方法,操作起来门槛最低,效果却非常依赖提问质量。
我记得有个团队分析过一次电商订单金额对不上的问题。第一层问,为什么订单金额不对?答:因为优惠券重复使用了。第二层问,为什么优惠券能重复使用?答:因为校验逻辑只校验了前端,没有校验后端。第三层问,为什么后端没有校验?答:因为后端接口的开发认为前端已经校验过了,自己这边不需要再校一遍。第四层问,为什么开发会这么认为?答:因为接口文档里没有明确写清楚"优惠券是否允许重复使用"这个规则。第五层问,为什么接口文档会遗漏?答:因为需求评审时,业务方、产品、开发都没把优惠券的使用边界当做一个独立的验收标准来讨论。
到第五层,根因就浮出来了。它不在代码层,而在需求评审的粒度上。这个结论,直接催生了一条新的团队规则:凡是涉及金额、库存、优惠券这类敏感字段的改动,必须把字段边界和校验责任写进接口文档和测试用例。
5 Whys执行中有两个常见的坑。一个是问的次数不够,问了两三层就停在"代码写错了""配置配错了"这种直接原因上,没有继续往下走。另一个是问偏方向,追着某个技术细节一直问下去,忽略了流程、管理、协作层面的因素。我的建议是,追到第五层的时候,停下来看一眼:这个原因是不是已经涉及了制度、流程、规范、工具链这类组织层面的因素?如果还没有,大概率是追的方向有偏差。
3.2 鱼骨图:适合多因素交叉的问题
5 Whys适合链条式的单线问题,但如果遇到那种"多个因素同时出问题才导致故障"的场景,鱼骨图比5 Whys更合适。
鱼骨图的经典分类是6M法,也就是从人(Man)、机器(Machine)、材料(Material)、方法(Method)、测量(Measurement)、环境(Mother Nature)六个维度去拆解。在软件领域,我一般会把这六个维度映射成更贴合研发场景的分类,比如人员、代码与架构、环境与基础设施、流程与方法、测试与质量、外部依赖。
画图的时候,先把故障现象写在右侧鱼头的位置,然后从六个维度分别头脑风暴,把可能的原因都填进去。填完之后,再对每个原因做进一步拆分,找出它们之间的相互影响关系。
鱼骨图最关键的技巧是先发散、后收敛。发散阶段不要做任何判断,所有想法都先记下来,很多看似不靠谱的思路,到后面反而是关键线索。收敛阶段再逐一排查、验证、排除,把真正的根因找出来。一旦进入收敛环节,就必须用数据和事实说话,不能用投票的方式决定哪个是根因——投票决定的根因,本质上还是主观判断。
3.3 FMEA与因果图:面向预防的进阶工具
5 Whys和鱼骨图都是事后分析,FMEA则偏重事前预防。FMEA的全称是失效模式与影响分析,核心思路是针对一个流程或设计,提前列出所有可能失效的方式,评估每种失效模式发生的频率、影响程度和当前被检测出来的概率,然后计算风险优先数,优先处理风险最高的项。
这个方法在系统设计评审阶段特别实用。比如设计一个下单接口,可以用FMEA的方式把可能的失效模式全部列出来:库存超卖怎么办、支付回调丢失怎么办、消息队列积压怎么办、接口幂等性丢失怎么办。每项都给出严重度、发生频率和检测度的评分,然后针对高风险项提前做好预案。这样做下来,很多问题在开发阶段就被拦住了,根本不会流入线上。
因果图则是把所有可能的原因和它们导致的结果用图的方式表达出来,用来梳理复杂系统中的因果关系。它比5 Whys更立体,可以同时展示多条因果链,适合用来分析系统级、跨模块的问题。但因果图的绘制难度也更高,对分析者的系统理解能力要求比较高,不建议团队刚上手根因分析就上因果图。
3.4 方法选型对照
方法选型不需要纠结太久,我整理了一个简单的对照逻辑:
| 方法 | 适用场景 | 主要优势 | 主要局限 |
|---|---|---|---|
| 5 Whys | 单一线条、因果链清晰的问题 | 简单易用,能快速深入 | 追问方向不对时容易带偏 |
| 鱼骨图 | 多因素交叉、故障链路长的复杂问题 | 覆盖面广,能系统展示因素关系 | 收敛耗时,需要层层验证 |
| FMEA | 设计评审、发布前的风险评估 | 事前预防,避免问题发生 | 依赖评审者的经验,经验不足时会漏项 |
| 因果图 | 跨模块、系统性问题 | 表达立体,逻辑清晰 | 绘制难度高,对分析能力要求高 |
对于刚起步的团队,我建议先从5 Whys开始,用熟练之后再引入鱼骨图。FMEA和因果图可以等团队的分析能力上来之后,再逐步尝试。
4. 从根因到改进措施:落地闭环的实操细节
找根因只是前半程,后半程是制定措施并保证落地。很多团队的根因分析结果不错,但最后流于形式,问题就出在这个环节。
4.1 制定措施的三条原则
第一条原则是措施必须指向根因,不能只指向现象。如果根因是"缓存键缺少隔离机制",那措施就应该是在缓存组件层做修改,或者在代码规范里增加强制要求。如果措施写的是"把冲突的缓存键改个名",那本质上还是在修现象,问题还会再犯。
第二条原则是措施必须具体到可执行。要避免"加强代码评审""提高测试覆盖率"这类大而空的表述。合格的措施应该包含明确的改动对象、执行人和完成时间,比如"在缓存工具封装层增加namespace维度(开发者:张工,完成时间:下周三)"或者"在CI流水线中集成统一命名检查脚本(执行人:王工,完成时间:两周内)"。
第三条原则是措施必须有验证机制。每一条措施落地之后,都要有一个明确的方式来证明它确实起到了作用。比如,规范改了之后,新产生的缓存键是否都符合新规范?检查脚本上了之后,能不能在代码提交阶段就拦截掉不合规的写法?没有验证机制的措施,落地之后有没有效果,谁也说不清楚,那就等于白做了。
4.2 如何防止措施走过场
我观察到一种很常见的现象:措施制定阶段大家热情很高,散会之后各忙各的,两周之后一检查,一半的措施没有动静。
要解决这个问题,靠自觉是不行的,必须把措施纳入项目管理体系。我建议给每条措施都指定唯一的owner,同时设定明确的截止时间,定期检查进度。对于根因分析中提出的措施,可以建立一个专门的待办清单,并在团队的迭代会上同步进展,直到全部关闭。
我有一个执行得很严格的习惯:措施不落地,根因分析就不算关闭。在缺陷管理工具里,给根因分析单独建立一个任务类型,关联到具体的问题记录上。问题可以关闭,但分析任务要一直保持打开状态,直到所有改进措施都完成并且验证通过。
另外还要关注措施落地之后的"回头看"。半年之后,通过代码检索、监控数据来确认一下,当初的问题是否真的没有再次出现。这个回头看的意义,不只是验证某一次分析的效果,更重要的是向整个组织传递一个信号——根因分析不是走过场,每一份分析报告背后都连着真实的改进。
4.3 把根因分析沉淀为组织能力
根因分析的最终目的,不是解决一个具体问题,而是建立一套机制,让组织整体的缺陷预防能力持续提升。
我的做法是,把每一次根因分析的结论归档到一个知识库中,按系统、按模块、按问题类型分类整理。每个结论都包含问题现象、根因、分析过程、改进措施、验证结果这几个要素。下次遇到类似问题的时候,先查一遍知识库,大概率能直接找到参考案例,节省大量排查时间。
其次,对高频根因做统计分析。一个季度结束的时候,把这段时间所有的根因分析汇总起来,看看主要集中在哪些类别。如果发现在"接口文档不完整"这类根因上出现了四五次,那就可以考虑在下个季度的质量规划里,专门针对文档规范做一次专项改进。
最后,把典型根因案例做成培训材料,在新员工入职、团队分享、技术周会的时候讲一讲。越是具体的案例分析,越容易让团队成员产生共鸣。长期坚持下来,团队整体的风险意识会有明显提升,很多问题在出现苗头的阶段就会被发现和处理掉。
好的根因分析机制,是那种"一次分析,长期受益"的机制。每次分析都是给整个组织的质量体系打一个补丁,补丁打多了,体系的漏洞就越来越少。
5. 常见问题与排查经验:那些年踩过的坑
做根因分析这几年,我踩过不少坑,也看着不少团队在同样的坑里反复折腾。挑几个典型的问题,结合实操经验聊聊。
5.1 分析会开成追责会,怎么办
这是最常见、也最致命的问题。一旦会议氛围变成"谁的锅",所有人都会进入防御模式,信息的分享就不再真实,分析也就失去了意义。
我的经验是这样处理的。第一,会议开场就需要立规矩,明确说清楚"我们是来研究为什么系统会出问题,不是来判定谁负责任"。第二,主持人在讨论过程中持续引导,把话题往流程、机制、工具这些方向引导,一旦有人说"这个失误是某某导致的",就要及时拉回来,问一句"那到底是流程中哪个环节允许了这个失误发生"。第三,如果需要追责,单独安排环节处理,而不是放在根因分析会上。
我以前遇过一个特别好的引导者,他在会上说了一句话让我印象很深:"今天到这里的人,都是来帮忙的,不是来被审判的。系统出了问题,是系统设计不够健壮,没有把人的错误兜住,所以我们今天的任务,是把系统的漏洞补上。"这句话一说,整个会议的氛围立刻不一样了。
5.2 根因明明找到了,但问题还是复发
这种情况遇到一次就够让人崩溃的。排查了半天,根因分析也到位了,措施也出了,结果过了几个月,同样的问题又冒出来了。
事后分析,原因通常出在两类。第一类是当初找的"根因"其实还不够底层,只是停在了次表层。比如把根因落在了"代码里没有做参数校验",但更深层的问题是"架构上缺少统一的入参校验框架,每个开发都自己写校验,写得对不对全看个人水平"。前者修的是单点,后者改的是体系。单点修完只能保证这一个地方不出事,体系不改变,换个入口照样出事。
第二类问题是措施虽然定了,但执行的时候打了折扣。比如措施写的是"所有新增接口必须经过代码评审",但实际执行中,因为排期紧张,不少接口跳过了评审直接上线。这个问题的本质是措施没有配套的强制执行机制,建议在落地措施时,把"流程自动化"作为优先选择。比如,把规范压进自动化检查工具,让不符合规范的东西根本合不进主干,而不是依赖人来遵守规则。
5.3 小问题到底要不要做根因分析
这是一个很有争议的问题,也是我被问过最多的问题之一。团队日常会有大量的小bug——文案错了、按钮样式不对、某个小功能偶发异常。如果每个都要做根因分析,显然不现实,资源不允许,也容易让大家陷入流程疲劳。
我的判断标准有三个:第一个标准是看影响面。这个问题的发生范围是单个用户、某类用户,还是所有用户?影响面越大,越值得深入分析。第二个标准是看潜在后果。虽然现在是个小问题,但如果放在特殊条件下放大,会不会演变成严重事故?比如一个小功能偶发异常,现在只是报错,但如果遇到秒杀时刻变大流量,会不会把整个服务拖垮?第三个标准是看发生频率。同一个问题反复出现,哪怕每次影响都不大,也值得做一次完整的根因分析,因为重复本身就是一个信号,说明背后有系统性的漏洞。
三个标准都过不了的问题,就不需要做完整的根因分析,记录一下现象,顺手改掉就好。但一定要记住一个前提——不做根因分析的意思是"判定为低风险,不需要投入",而不是"它不值得被记录"。所有的问题记录都要保留,当同一个小问题积累到第三四次的时候,就要把它升级为根因分析的候选对象。
5.4 分析结论过于发散,无法收敛
做根因分析的时候,还有一种情况是大家越聊越开,从缓存冲突聊到技术债,从技术债聊到项目排期,从排期聊到组织管理,最后结论写了一堆,但对解决当下问题一点帮助都没有。
遇到这种情况,我一般会启用一个简单的收束策略。把所有推导出来的原因分成两个维度:可控和不可控。不可控的那些,比如外部环境变化、历史遗留包袱,暂时不作为行动项,只做记录。可控的里面再分两类:短期能解决的,直接列为行动项;短期解决不了的,列入改进计划,给出未来的规划方向。这样一来,发散的讨论就有了出口,不会再绕圈。
我个人还有一个习惯,就是在分析会的最后,快速过一遍结论:"好,我们今天确定下来的根因是这几项,对应的措施是这几条,owner和时间节点已经定了,下周我们在迭代会上同步进展。另外有几项不可控的,今天的会议上不做展开,后续单独讨论。" 这样说清楚之后,会议的产出立刻变得明确,所有人也知道下一步要去做什么。
5.5 工具选型:根因分析该用什么工具记录
工具从来不是根因分析的核心,但没有合适的记录工具,分析的结果很容易散落各处,找不回来。
我的建议是尽量和现有的研发管理系统打通,直接在缺陷管理平台里做根因分析记录。比如在Jira、Tapd、禅道或者自研的工单系统里,给bug增加"根因分类""根本原因""改进措施""验证结果"这几个自定义字段。每个bug关闭之前,必须把这些字段填完整,否则不允许关闭。这样,每一次根因分析的结果都跟着缺陷记录走,既不会丢失,也方便后续的统计分析。
对于跨系统的重大故障,可以单独建立一份故障报告文档,把分析过程、时间线、根因推导、改进措施都写进去,归档到团队的Wiki或者知识库。文档的格式可以标准模板化,减少每次整理的精力消耗。但要注意,模板不能太复杂,否则会变成负担,最后大家就不愿意填了。我见过一个很好的模板,核心就四个部分:发生了什么、为什么发生、怎么防止再发生、怎么验证防住了。简洁明了,人人都愿意写。
6. 根因分析在日常工作中的扩展应用
根因分析的方法,不止适用于线上故障和缺陷。在很多其他场景里,这套思路同样能发挥价值。
6.1 从线上缺陷扩展到需求评审
需求阶段的问题是成本最低、影响却最大的。很多缺陷的根子,在需求评审阶段就已经埋下了。比如需求描述模糊,开发理解偏差,做出来的功能和业务方的期待不一致。等到上线后才发现,再回头改,成本翻了好几倍。
把根因分析的思路应用到需求评审中,就是在需求评审的时候,不只看这个需求本身,还要追问几个"为什么":为什么需要这个功能?用户的核心诉求是什么?这个改动会影响到哪些现有逻辑?有没有类似的改动之前出过问题?这些问题在评审阶段被回答得越充分,开发阶段和测试阶段出现的返工就越少。
6.2 从缺陷复盘扩展到项目复盘
项目复盘的时候,大家通常会回顾进度、成本、质量、人力等几个维度,但很多时候复盘停留在"进度延期了""需求变更太多"这样的表面结论上,没有往深处挖。
如果能把根因分析的思路带入项目复盘,追问"为什么进度会延期""是哪一类的需求变更影响了进度,为什么没有更早地识别出来""排期的时候有哪些信息是被遗漏的",项目的复盘质量会有质的提升。做一次深度的项目根因复盘,收获往往比做好几个浅层复盘要大得多。
6.3 从研发团队扩展到个人工作习惯
根因分析其实也是一种思维方式。我自己在个人日常工作中也会使用这个框架。比如,连续两周在同一个时间段觉得效率低下,我不会简单地归因于"状态不好",而是会记录一下这两周每天的工作内容、作息时间和精力状态,看看有什么共性。有时候会发现,其实是某个固定的会议安排打断了自己的深度工作时间,调整一下日程结构,问题就解决了。
这种思维习惯的好处是,它把"问题"从一种需要忍受的烦恼,变成了一个可以分析和解决的对象。遇到问题的时候,第一反应不再是谁的错,而是"现在的信息还缺什么""哪些环节可以优化"。心态转变之后,解决问题就顺畅多了。
最后再分享一点个人经验
在做根因分析这件事上,我最大的体会是:别把它想得太玄,也别把它做得太重。它本质上就是一群靠谱的人,坐下来花几个小时,认认真真地把"为什么出了事"这个问题回答透,再老老实实地把改进动作做到底。
一个团队如果能坚持把每一次分析做完、做透,哪怕中间会犯错、会返工,长期下来积累的改进成果,会远远超过那些频繁救火的高效团队。因为前者在消灭问题的源头,后者只是在一遍又一遍地给同一个问题打补丁。
如果你所在的团队还在被"问题重复发生"困扰,我建议你从下一次故障复盘开始,把根因分析做扎实。不要追求一次就完美,先跑起来,在过程中不断调整。毕竟,避免问题重复发生,靠的不是运气,而是体系。
