BUG(学名:缺陷),喜阴,容易在人类关注不到的角落繁殖,喜欢自由任性的开发流程,在规范、流程严格的环境中较难生存。
BUG初生期
BUG随着系统诞生而诞生。
系统MVP版本(最小化可行产品)后,BUG也开始苏醒,此时整个开发团队的规范和流程相对薄弱,是BUG喜欢的环境。
但是这个时候整个系统的业务逻辑简单清晰,相对应的业务代码也整洁清晰,开发和业务人员都少,每个人都对整个系统的逻辑了如指掌,缺少阴暗角落,BUG的增殖不易。
同时,这个时候系统业务量较少,存量的BUG也不多,程序、业务、测试们相对来说有更多的时间关注开发的质量以及生产的运行结果,BUG也更容易被消灭。
BUG的潜伏期
BUG随着系统复杂度变大而增殖。
MVP版本的效果得到验证后,业务和开发们的信心得到树立,决定大干快干,要往系统里加入更多的业务功能。在这过程中代码可能会由于时间紧迫、惰性等原因 引入下面的问题,而开始腐化:
补丁代码
//补丁形式举例
if(ProductA == currentProduct
|| ProductB == currentProduct
|| ProductC == currentProduct //最后一个判断为补丁判断逻辑) {
SendMsg();
......
}
//抽象封装形式举例
if(currentProduct.isMsgRequired()) {
SendMsg();
......
}
补丁形式代码的一个最大缺点就是缺少了封装性,或者说缺少了设计,所有复杂度都平铺于一个层级。这样的代码在业务简单、人员数量不多这类代码依然处于可控状态。
但当业务逻辑变复杂,产品增多时,将会变成一个灾难——复杂度失控。程序员再也无法理清这里的业务逻辑,而码农思维不能触达之处,是BUG最喜欢的场所。
数据不一致
//新增列,没有做初始化
ALTER TABLE trade
ADD COLUMN `product_type` VARCHAR(45) NULL AFTER `create_time`;
没有做存量数据的初始化的话,会导致在代码中增加一些没有业务含义的数据处理逻辑 ,增加了系统复杂度,增加了程序员的思维负担。
但同样,人少业务简单,该类坑能被很好的COVER住,但BUG们正心怀期待地等待着这个逻辑被遗忘。
重复的代码
一般来说,开发们都是经过专业训练的人,极度厌恶重复的代码,除非条件不允许。
经常会有类似“这个需求很简单,怎么实现我不管,明天上线”的场景出现,迫使开发们以当前最简单最快速的方式——“复制并修改”来完成代码的编写。
复制修改形式能应付本次任务,但却将复杂度进行了翻倍。所有人后续要修改对应的方法时,都需要仔细观察被复制的代码之间的异同,并在每一个复制点加上新逻辑。
复制代码是增加系统复杂度卓有成效的手段,如果我是BUG的话,我看到这些重复的代码,我会笑出来。
表里不一的方法
List<Order> getOrder(int userId) {
result = dao.findOrderByUserId(userId);
result.fileter(u->u.productId == 1);
return result;
}
名字上说着是获取用户的Order,实际上却是获取用户下产品ID为1的Orde。
这类方法完全失去了封装的意义,封装是为了屏蔽复杂度,而这类方法,除非你看实现,否则不能知道它是干什么的。而看了实现则暴露了更多的复杂度,失去了封装的意义。
其他各类增加复杂度的操作
不再使用的业务代码没有清理,不能清理
没有经过系统思考的业务的污染
......
BUG的爆发期
业务进一步成长,原有的开发人员已经Carry不住开发量,开始大量招聘相关人员。
新招聘的人员对历史的坑不太熟悉,系统也过于复杂,原有开发人员、业务人员也难以获得系统全貌时,BUG们开始增殖爆发。
开发一个新功能时:
在某个地方遗漏了需要加上的补丁代码,导致BUG
不知道历史数据与当前数据不一致,导致上生产出BUG
大量重复代码,差异很小,理解错误导致BUG
......
同时上述BUG爆发时,大家会耗费大量的时间去排查处理问题,相对来说减少了很多的开发时间,开发时间的减少又导致了更多的BUG,由此而形成了一个恶性的循环。
BUG的增殖总结
控制复杂度以减少BUG的诞生
在业务系统中,除了大意问题造成的BUG,大多数的BUG是由复杂度失控导致。
如果系统的复杂度过高,那么:
业务设计时考虑的细节过多
会导致设计的遗漏
导致大量特性在开发后段才发现,压缩了开发的时间
开发产品时,需要掌握的细节过多时
会导致无法看到全貌
代码质量会降低,产生大量BUG,压缩了测试的时间
进行测试时,业务过于复杂
导致需要进行大量测试
同时开发的质量不过关,反复测试又大量占用时间
最后导致整体质量失控
所以
要提高代码质量的根本是控制复杂度
系统整体的复杂度可以很高,但是每个业设计者、开发人员所直接面对的复杂度必须是 个人范围内可控的。这样在开发过程中,开发人员就能关注到每一个细节,减少BUG的诞生。
(系统复杂度控制并不简单,其关乎设计与监督,本文不展开,可以参考 之前的一篇文章 《从分治的思想到架构的设计》)
自动化测试以保证BUG的发现
在系统越变越复杂时,发布的频率也越来越快的今天,我们已经不可能每次上线都人肉全量回归一遍逻辑,只能增量测试修改部分。
然而,实际上每次修改都有可能引起一些存量逻辑的变更,如一些切面层级的修改甚至会影响系统的每一个方面。
因而在复杂度控制的基础上,我们需要
引入自动化测试的代码以发现BUG
虽然自动化测试的成本极高,代码量可能数倍于实际业务代码,当业务逻辑不稳定,经常会变化时,其代价则更高。
但当系统成长到一定程度,那么其必然会有沉淀出一些不变的主干逻辑,至少这些逻辑我们可以对其进行自动化测试。
写在最后
关于BUG的产生原因还有其他很多,本文不成系统地写了自己的一点看法,欢迎大家补充其他繁衍BUG的手段。
下一篇文章将跟大家分享我对一个套餐系统设计的思考,欢迎大家关注。