我目前正在从事软件开发工作,每天都会有各种各样的问题产生,每天也都需要不断地去解决这些问题。有些同学遇到某些软件bug的时候没有排查问题的思路,所以根据网络经验与个人经验总结一篇通用的软件bug排查思路,仅供参考,希望对新手程序员有一点点帮助
读后可能有的收获: 遇到大部分简单问题能够独立的、有理论支撑的排查并解决问题
写作逻辑: 本文通过软件开发过程来分析如果更一般性的修复bug,包括开发前如何引入更少的bug,开发过程中遇到问题,如何描述定位bug?定位bug后如何修复?修复完此bug后如何不引入新的问题?以及bug最终修复后,需要向上级汇报,如何描述此问题?
目录
怎么不写bug?
业务需求必须熟悉
核心思想-怀疑一切
需求更改如何应对
怎么不改bug?
怎么把bug定位并解决?
5WHY原则挖掘bug的是否真的解决?
解决方案是否满足最初需求
是否存在其它相关性问题?
解决bug的同时是否引入新的问题?
总结
怎么不写bug?
经常调侃咱们程序员“不是在写bug,就是在改bug呢?”这谁受得了,哪个大佬教我写的代码没有bug可以吗?写的代码没有bug,明白的告诉你不存在的。既然写代码总会有bug,那我少写点bug总可以了吧?这个可以有,下面引入少写bug的一般性思路,本文仅简要介绍,后续专门讨论。
业务需求必须熟悉
一般需求方提出需求都会提出很模糊,这个时候研发人员一般会按照自己的想法来实现需求方的需求,当把实现交到需求方时发现需求方要的不是此功能,程序员们就回去改程序。这种事情经常发生,也是大部分问题引入的原因。如果能够在需求提出后,我们研发人员能够核对清晰需求是什么?实现的目的是什么?清楚具体的页面,具体的交互逻辑,跟研发组长确定具体的实现形式,所有都清楚的条件下再进行开发,返工的情况就会大大降低。
核心思想-怀疑一切
函数调用需要怀疑:函数调用报错,返回空字符、空指针,奇怪的结果如何解决
调用外部接口:同上接口调用报错怎么办(重试还是熔断),接口的入参返回值改变怎么办?(面向接口编程)
外部接口调用: 对方传参空值,类型不正确,少传参怎么办?(函数必有校验逻辑)
需求更改如何应对
程序当前面对的问题通过怀疑一切的思想一般情况下能够规避大部分问题,往往程序有需求变化时,又会引入更多问题。很多时候考虑不到需求改变或者需求更新的时候,程序如何变化。我们经常听到,需求怎么老是变得抱怨。就像物理中提到的运动是绝对的,静止是相对的,需求的改变是绝对的,不变是相对的。需求铁定会变,这个时候就要求我们在设计代码的同时就考虑到未来需求改变的可能性。
应对需求变化所需技术较多不再一一介绍,这里简单提几个开发原则和需要之后加深学习的知识点
面向接口编程
对程序扩展开放,对修改关闭(OCP:Open-Closed Principle)。写好的代码我们就认为是最好的,一般就不允许修改了,要添加需求在新的地方写
单一功能原则,一个类只负责处理一件事情,一个函数只负责处理这件事情中的一件小事情
统一命名规范,程序员一般不愿意写文档,但是不写文档,咱们只要把命名写的规范能够让人看懂含义就可以使你后期查看代码更加易维护
这些概念不是太熟悉的可以学习设计模式、代码整洁之道,之后可以学习领域驱动设计思想
怎么不改bug?
bug产生了,我们是改还是不改?
查看是否影响核心业务,如果是核心业务就需要紧急修复。用户关注较多的功能就是核心业务
不修改这个bug,对其它业务是否有影响,如果对其它业务没有影响且用户也基本不关注,影响较小那我们可以先不改
有更重要的业务需求要进行开发,且此业务需求跟修改bug无关联,可暂时开发业务需求,之后进行修复bug
怎么把bug定位并解决?
此小结开始我们需要真正的开始解决bug了。解决bug的第一步,也是最重要的一步是发现问题。利用发现,定位,猜想,验证重复此过程,最终解决问题。此步骤顺序可变且可减少步骤。
发现bug: 发现问题第一步需验证此问题是不是问题,发现问题四要素,测试过程中此过程是需要记录的
问题复现的前提条件,经常是本地环境没问题,线上有问题,所以前置条件很重要
具体的操作步骤,操作步骤要具备复现性
预期结果是什么,正常情况下的结果一定要知道,正常结果不清楚的情况是无法进行修复的(问题所在位置的业务及程序逻辑必须清楚)
实际的结果是什么
缩小范围:在现有的现象下,尽可能地缩小问题产生的具体位置,从而减轻问题的排查难度
编译和运行时报错问题还好排查,基本根据前端现象就能基本定位报错模块,然后根据查看日志、ide环境debug就能轻松定位问题
逻辑型错误一般无法直接通过日志直接定位问题,需要进一步收集数据库、发生问题时的并发性事件等因素进行联合,通过假设与验证手段最终定位问题所在
搜索: 通过谷歌、百度搜索是否具有相应的问题,具备类似问题能够加快修复速度
猜想: 根据问题前端现象、错误的日志、debug信息,尽可能地想出可能的原因,并根据可能性进行排序;
验证: 根据猜想所得,一一验证是否猜想正确
若进行到此仍没能排查出问题,则需要增加日志、收集相关系统日志进一步进行猜想与验证。有时候在低纬度上无法解决的问题,在高纬度来看就不是问题,代码层面无法解决的问题可以从架构调整、业务逻辑调整的层面来解决问题。但是很多情况下因经验不足、相关技术不熟悉我们仍无法排查出问题,这时候最好的办法就是请教牛人,不是太紧急的问题也可以休息一下发散一下思维,也许回头看问题其实很简单。
案例: 通过一个不太通用性案例列举一个案例
发现bug: 大屏模块展示的数据时有时无
分析定位:业务程序逻辑梳理,大屏数据是从缓存中拿到数据,缓存数据是从A数据源拉取的;日志中无报错
猜测:
A数据源时有时无
数据库缓存可能存在定时清楚程序
验证:通过拉取A数据源数据发现确实存在随时间变化拉取数据时有时无现象,问题定位且得到验证
解决方案:当A数据源没能拉取新数据时,原缓存不做更新仍使用之前数据
5WHY原则挖掘bug的是否真的解决?
解决方案是否满足最初需求
问题解决后,回想问题是否满足最初的需求。对比以上案例,需求如果是大屏能够实时都有数据,换句话说是大屏只要不为空数据即可满足需求,则以上解决方案就可满足需求;如果需求是:实时获取最新5min内的数据,数据要求具有实时性那这种解决方案显然会存在大屏显示5min之前的数据,那这种解决方案是不可行的。这个时候需要解决从A数据源获取数据时有时无的问题;
是否存在其它相关性问题?
福无双至、祸不单行,一个问题的产生往往伴随着相关性其它问题的发生;一个问题发生的同时,需要查看其它相关性模块是否存在问题。目前的解决方案能否通用性的解决这些问题
解决bug的同时是否引入新的问题?
解决当前问题的同时很可能引入新的问题,这是我们需要花时间去考虑的问题。一个kafka中间件的问题场景。当前kafka的吞吐量增大,一般想到的方法是增加partition、增加消费者。仅仅增加partition和消费者是能够增加吞吐量,但是会引入另一个问题,就是不同partition间无法保证顺序性。如果我们的业务场景需要保证顺序性,那么此时就无形中引入了新的问题。
总结
本文主要写的如何规避bug、修复bug的一般见解,总结来说分为这几点。
1. 跟需求方尽可能确定具体的需求
2. 怀疑一切可能出错的地方
3. 为未来扩展功能提前做准备
4. 通过现象、猜想、验证等思路排查现有bug
5. 解决新bug同时要考虑需求是否满足,是否仍存在相关性问题,是否引入新的问题
感谢各位阅读完此文章,对文章内部中哪些有建议请留言,对相关模块想要更详细探讨解释的,也请留言。




