用结构化思维解一切BUG(3):实际案例
背景
本文是系列文章《用结构化思维解一切BUG》的第 3 篇,也是最高潮篇!本系列文章主要介绍一种「无需掌握技术细节,只需结构化思维和常识即可解一切BUG的方法」。
在前序文章《用结构化思维解一切BUG(1):核心思路》中,我介绍了本方法的核心思路,即,基于结构化的「假设树」,通过重复多次执行「做试验→造现象→缩范围」动作序列,逐级下钻,缩小问题范围,直到找到问题根因。
在前序文章《用结构化思维解一切BUG(2):实践原则》中,我介绍了本方法的实践原则,「程序断案三字经」,总结为 5 条 30 个字:
- 先诊断,后开药。
- 信机器,慎信人。
- 做试验,缩范围。
- 找不同,看变化。
- 先脆弱,后稳定。
本文我将带大家进入真实BUG场景,在场景中介绍「假设树」如何生长、如何执行「做试验→造现象→缩范围」小闭环、如何运用实践原则。同时,您也从中感受到该方法的强大!
_提醒:_本文中,假设树的生长图和试验记录表格是重点,建议点击图片全屏预览,如用手机建议横屏观看。
案例背景
这个客户是创立了 20 年的国际贸易和海外工业制造公司,目前在筹备上市。为应对上市要求,需要搭建自己的质量管理系统。
这个应用由一个「导航应用」和多个「子应用」构成。导航应用中有多个子应用的链接。当用户登录导航应用后,点击任何一个子应用的链接,即可快速进入这些子应用,无需再次登录。请记住这里的「导航应用」和「子应用」,后文会多次提到。
有一天,客户反馈,部分用户有「跳转到子应用后无法自动登录」的问题。

显然,这仅有的信息,我无法做出任何有效判断。
大多数业务用户,甚至是程序员,在反馈问题时都缺乏必要细节。作为问题解决者,我的第一个任务就是,问对问题,得到我想要的信息。我一般会用 5W2H 的方法来问问题(请注意后续问题的中括号标注),以全面了解信息。
提醒:如果您是程序员,强烈建议刻意培养自己「提出一个好技术问题」的能力,因为好的问题才有更大几率被快速回答。具体请参考我的另一篇文章《如何提出一个好的技术问题?》。
明确问题
首先,我需要明确问题[What]。即,问 3 个问题:
- 希望的现象是什么?[To-Be]
- 实际的现象是什么?[As-Is]
- 哪个点不满足希望?[Gap]
在本场景中,三个问题的答案为:
- 希望用户可以免登录地进入子应用。
- 实际现象是,用点击子应用链接后,应用自动在新标签页打开一个登录页面。且这个登录界面就算输入正确密码和账号,也无法登录。
- 无法正常访问子应用。
缩小范围
根据上一篇文章的「假设树」方法,我们第一层需判断此问题是前端问题还是后端问题。
为了方便引用,我们把发生 BUG 和不发生 BUG 的用户分为两组,发生 BUG 的用户成为 N (Negative)组,未发生 BUG 的用户成为 P (Positive)组。
于是,我问了如下问题:

这个诊断过程,我践行了如下原则:
- 「先诊断,后开药」。在没有弄清楚问题根因之前,不做任何正式的修改建议。
- 「找不同,看变化」。尽量从现有现象中,找到大家的不同点,看有什么变化,变化就是线索。
有了这些信息,我靠直觉猜测,大概率是前端问题,且是浏览器问题。因为,从后端来看,我很难想到这两组用户在代码和数据上有显著的不同特征。具体来说:
- 后端代码,两组用户肯定是一样的。
- 后端数据,发生了 BUG 的用户没有任何明显相似点,发生 BUG 的用户与没发生 BUG 的用户也没有任何明显不同点。
直觉很有价值,可以更快找到正确路径。但直觉未经过严格证明,不能把它直接当做结论,还需要做试验证实。
另外,不用担心自己直觉不准,它并不会影响最终结果,因为它只是猜测。只要你按照「假设树」的路径逐个遍历,总会到达终点,只是要多做几次试验罢了。
回顾假设树-Step1
本阶段「假设树」没有任何确定性的进展,只能依靠直觉先做假设。具体进展如下(本文所有假设树示意图中,蓝色字体表示是假设,红色字体表示是已证实的结论):

设计试验
为了验证上述的猜想,我陆续做了如下试验:

在这个过程中,我践行了如下原则:
「做试验,缩范围」。通过不同的对比实验,来验证自己的猜想,缩小问题范围。
「找不同,看变化」。在 1 和 2 两步,我尽量控制单一变量,排除掉其他因素干扰,来看有什么变化。
「信机器,慎信人」。我相信,BUG 没有真随机,任何 BUG 都有其底层规律,一定有某个地方的不同导致了 BUG。并且,我得到初步结论后,再次与所有观测到的现象做意义印证,看是否能解释观测到的全部现象。
回顾这组试验,「假设树」前进了两步:
回顾假设树-Step2
Step2 中,我们虽然判断大概率是浏览器原因,但不能 100% 确定,因为没有排除后端原因。所以,我们只能沿着假设的方向进一步探索。如下图所示:

回顾假设树-Step3
Step3 中,我们确定了此问题是 Chrome 浏览器版本所致,则整条路径的所有原因都可以确认了。即,可以确认是前端原因,且是浏览器原因。同时,可以排除后端原因。如下图所示:

初步结论
从上述一系列实验中,我们得到初步结论:本 BUG 跟浏览器版本密切相关。
接下来,我给了客户两个修改建议,并嘱咐,如果还未找到根因再来找我。
- 查阅 Chrome 的近期更新日志,试图从中找到跟本 BUG 相关的更新条目。
- 仔细阅读跟自动登录相关的「前端」代码(只有大约 30 行),检查哪些代码最有可能伴随浏览器版本升级而发生变化。
请注意,这两个建议,都是低成本修改建议,主要目的是做试验,参考「先诊断,后开药」的原则。
第二次设计试验
第二天,反馈来了:
- Chrome 每个版本的更新日志内容太多了,有上千条,一条条去查找不现实。(我错误地估计了 Chrome 的更新量)
- 看了登录相关的前端代码,尝试了所有可能伴随浏览器升级而发生不一样行为的地方,结果都一样。
这说明,我们虽然缩小范围到了那 30 行代码的范围,但还是达不到让执行者能修改的地步,还需要进一步缩小范围,找到根因。
于是,我让客户讲解这 30 行代码做了什么。通过讲解,我了解到这 30 行代码主要是发送异步请求完成子应用的自动登录,然后跳转到子应用的 home 页面。
根据 Web 应用的常识,我知道,登录不成功,要么是登录请求执行失败,要么是 Cookie 里 SessionID 未写入成功。为了验证上述猜想,我又设计了如下试验:

基本确定完根因后,我又拿这个根因,跟前面所有的现象作比对,发现全部能解释。
回顾假设树-Step4
回顾这组试验,「假设树」继续生长,最终找到根因是链接未加密。如下图所示:

最终结论
最终发现根因是:
- Chrome 新版本写入 Cookie 的规则发生变化,加密请求创建的 cookie 不能被非加密请求改写。
- 导航应用与子应用的 schema 不一致,一个是 http,一个是 https。
最终的修改建议是:
为每个子应用配置加密访问证书,并更改环境配置中子应用的域名的 schema 为 https。
客户更改完后,BUG 完美修复!
本案例总结
这个案例,我们精准还原了解决一个 BUG 的完整过程。过程中,我们不停地调整「假设树」,不停地「做实验→造现象→缩范围」,最终到达终点,找到根因。
除了前述 5 个原则以外,我还想说一点精神层面的事。任何一个 BUG 的解决过程都是崎岖坎坷的,特别是当你面对一组现象,完全找不到任何联系的时候,任何人都会心情低落。但越是这样,越要沉着冷静。不要想一步登天,直接找到根因;而是要从「假设树」根部逐步往下钻,一层一层抽丝剥茧。每一层下钻,每一次试验,你都会得到新反馈,用来排除其它假设。这些反馈是支撑你继续往下钻的动力。所谓「慢慢来,比较快」,就是这个意思。
全系列文章总结
至此,《用结构化思维解一切BUG》的方法的核心思想、实践原则和实践案例都已经讲完。后续,我会不定期更新更多实际案例,以帮助您加深理解。
如果您完整读完本系列的三篇文章(《用结构化思维解一切BUG(1):核心思路》、《用结构化思维解一切BUG(2):实践原则》和本文),并基本理解所有段落,那么恭喜您,您的解 BUG 功力已经显著提高!如果您还愿意在实际工作中践行那么几次,您一定会成为顶级解 BUG 高手!
最后,重温下本系列文章最开头的那句话:
面对万“卷”世界,有人选择拼命学习新技术,解决眼前的、点状问题;有人提升思维层级,解决未来的、面状问题。您选择什么?
关于作者
您好,朋友。我现就职于西门子工业软件,担任高级咨询顾问。成功领导 10 多个世界 500 强企业的数字化转型项目,跨越政府、零售、金融、汽车制造、生物医药等多个行业,创造巨大商业价值。
如有任何与「数字化转型」有关的问题,欢迎用以下方式与我交流:
- 添加我的个人微信「zjh1943」。添加时请注明姓名、行业、交流的问题。
- 关注我的微信公众号「知明所以」。
- 关注我的知乎专栏:https://www.zhihu.com/people/zhu-jin-heng 。
用结构化思维解一切BUG(3):实际案例的更多相关文章
- 工作中的Buff加成-结构化思考力:第一章:认识结构化思维及其作用
一:引言 为了更好的说明结构思考力,我们先来做几个小测试. PS:如果你能做到,请留言,因为我要和你交好友,因为你是人才啊,可以挖一挖,挖到我的公司中. 第一个测试:请在三秒内记住下列数字.数字顺序不 ...
- 有效的结构化思维训练,MECE分析法
MECE原则,表达精准分类与全面性的有效利器 结构化思维的本质就是逻辑,其目的在于对问题的思考更完整.更有条理,它帮助我们一个一个找到线头,理清思路,探求事物之间的相互联系.MECE分析法是一种结构化 ...
- 你真的了解字典(Dictionary)吗? C# Memory Cache 踩坑记录 .net 泛型 结构化CSS设计思维 WinForm POST上传与后台接收 高效实用的.NET开源项目 .net 笔试面试总结(3) .net 笔试面试总结(2) 依赖注入 C# RSA 加密 C#与Java AES 加密解密
你真的了解字典(Dictionary)吗? 从一道亲身经历的面试题说起 半年前,我参加我现在所在公司的面试,面试官给了一道题,说有一个Y形的链表,知道起始节点,找出交叉节点.为了便于描述,我把上面 ...
- 结构化CSS设计思维
LESS.SASS等预处理器给CSS开发带来了语法的灵活和便利,其本身却没有给我们带来结构化设计思维.很少有人讨论CSS的架构设计,而很多框架本身,如Bootstrap确实有架构设计思维作为根基. 要 ...
- 详解Google-ProtoBuf中结构化数据的编码
本文的主要内容是google protobuf中序列化数据时用到的编码规则,但是,介绍具体的编码规则之前,我觉得有必要先简单介绍一下google protobuf.因此,本文首先会介绍一些google ...
- NumPy之:结构化数组详解
目录 简介 结构化数组中的字段field 结构化数据类型 创建结构化数据类型 从元组创建 从逗号分割的dtype创建 从字典创建 操作结构化数据类型 Offsets 和Alignment Field ...
- Bigtable:一个分布式的结构化数据存储系统
Bigtable:一个分布式的结构化数据存储系统 摘要 Bigtable是一个管理结构化数据的分布式存储系统,它被设计用来处理海量数据:分布在数千台通用服务器上的PB级的数据.Google的很多项目将 ...
- 视频结构化 AI 推理流程
「视频结构化」是一种 AI 落地的工程化实现,目的是把 AI 模型推理流程能够一般化.它输入视频,输出结构化数据,将结果给到业务系统去形成某些行业的解决方案. 换个角度,如果你想用摄像头来实现某些智能 ...
- Shell 语法之结构化命令(流程控制)
许多程序在脚本命令之间需要某种逻辑流控制,允许脚本根据变量值的条件或者其他命令的结果路过一些命令或者循环执行这些命令.这些命令通常被称为结构化命令.和其他高级程序设计语言一样,shell提供了用来控制 ...
- 第25章 SEH结构化异常处理_未处理异常及向量化异常
25.1 UnhandledExceptionFilter函数详解 25.1.1 BaseProcessStart伪代码(Kernel32内部) void BaseProcessStart(PVOID ...
随机推荐
- C语言链表实现(郝斌数链表学习笔记)
#include "stdafx.h" #include<stdio.h> #include<stdlib.h> typedef struct Node { ...
- 批量获取FreeSWITCH所有分机号及其密码
前言 有次项目上需要获取所有FreeSWITCH注册分机的分机号和密码,就用python写了个小脚本来获取. 可以先把freeswitch/conf/directory/default/目录下的所有x ...
- 缓存面试解析:穿透、击穿、雪崩,一致性、分布式锁、Redis过期,海量数据查找
为什么使用缓存 在程序内部使用缓存,比如使用map等数据结构作为内部缓存,可以快速获取对象.通过将经常使用的数据存储在缓存中,可以减少对数据库的频繁访问,从而提高系统的响应速度和性能.缓存可以将数据保 ...
- 抢先体验!超强的 Anchor Positioning 锚点定位
本文,将向大家介绍 CSS 规范中,最新的 Anchor Positioning,翻译为锚点定位. Anchor Position 的出现,极大的丰富了 CSS 的能力,虽然语法稍显复杂,但是有了它, ...
- .NET周刊【8月第2期 2023-08-14】
本周由于Myuki大佬感染新冠,国际板块暂停更新一周,将在下周补齐,所以本周只有国内板块. 国内文章 解决 Blazor 中因标签换行导致的行内元素空隙问题 https://www.cnblogs.c ...
- [htmlayout] picture标签替代img, 解决更新图片数据后依然显示原图片数据
在hl中, 你可能遇到过这样的情况. 给img标签设置了一个图片路径. 在软件使用过程中对这个路径的数据进行过重写, 删除等等 但img依然还是显示最初载入的图片数据. 解决办法: 用&quo ...
- 线段树hdu-4027
Smiling & Weeping ---- 姑娘,倘若,我双手合十的愿望里有你呢 Problem Description A lot of battleships of evil are ...
- linux安装clickhouse
linux安装clickhouse 1. 系统要求 ClickHouse可以在任何具有x86_64,AArch64或PowerPC64LE CPU架构的Linux,FreeBSD或Mac OS X上运 ...
- 我封装的一个REPR轮子 Biwen.QuickApi
Biwen.QuickApi 项目介绍 [QuickApi("hello/world")] public class MyApi : BaseQuickApi<Req,Rsp ...
- cmake构建32位应用程序
1. 背景介绍 2. 工具介绍 3. 环境搭建 4. MinGW编译器版本 1. 背景介绍 最近需要使用第三方动态库文件G33DDCAPI.dll进行二次开发.由于这个动态库文件生成的时间比较早,且只 ...