软件工程发展到现在,想想这个场景熟悉不熟悉。在一个狭隘的空间里,一个个年轻人,面前摆着一台电脑。这一批人中有一小批的键盘敲击速度很快,但是头顶有点凉。而另外一小批人,鼠标点击速度很快,头顶稍微没那么凉。
没有错了,前者就是我们的开发,后者就是我们的测试。
这是软件质量的第三篇,其实在日常的工作中,开发人员开发新功能的时间可能只占用到工作时间的 30% ,大部分人连 30% 都达不到。那这是不是意味着老板可以直接降薪到现有待遇的 30% 呢?那倒不行。他们不开发新需求,他们得去开会撕逼呀,他们得改 bug 呀,一刻也停留不下来的呀。
这时候他们就会开始骂测试,你们怎么搞的,测出那么多 bug,我们还上不上线了?测试就会骂开发,你们怎么搞的,写一点破功能那么多 bug。如此如此的对话,天天不绝。
那么我们能不能少些 bug 呢?答案是肯定的,可以少些。那我们能不能不那么忙呢?答案也是肯定的。
开源社区发展到现在,已经有成熟的方法论支撑软件质量这件事情了。今天只拆其中两个出来讲,分别是 测试左移 和 测试右移。
测试左移
我们都知道,最优秀的没有任何 bug 的系统是怎样的?那就是一行代码都没有的系统,连代码都没有,怎么能算是软件 bug 呢?那什么样的时间是解决 bug 的最佳时机呢?答案是在 bug 还没有被写出来或者刚刚写出来的时候就扼杀在摇篮里,成本最低。基于这个假设,我们提倡,将测试能力进行左移,左移到需求阶段,左移到开发阶段。关于左移到需求阶段,上一篇已经讲过了。本篇主要聊聊左移到开发阶段的事情。
其实要在开发阶段减少 bug 的产生,无非就一个方案,就是对已经写出来的代码进行审查,那具体要怎么审查呢?方法大概有三种。
1. 单元测试
开发人员对于模块实现的功能进行自检查,根据需求对于模块所期待的输入输出,进行编码实现,并在写完之后进行验证。
Java 中比较好的实践是这么一个简单的组合,Junit 进行测试驱动,Mockito 进行依赖的模拟。其他语言也有它自己的 XUnit 和 Mock 组合,这里不细说。如果要贯彻比较好,没有单元测试的代码不允许提交,这是最基础的。
2. 代码扫描
同样是汉字,李白可以写出 "举头望明月,低头思故乡",杜甫可以写出 "但愿人长久,千里共婵娟",而我,只能写出 "卧槽"。编程语言作为一门语言,不同的人写出来的东西也是千差万别。在历史的经验中,我们其实已经总结出了很多"美"的代码,表现优秀的代码。以及丑的代码,非常可能产生缺陷的代码。
比如:
① 潜在的bug:空的try/catch/finally/switch语句
② 未使用的代码:未使用的局部变量、参数、私有方法等
③ 可选的代码:String/StringBuffer的滥用
④ 复杂的表达式:不必须的if语句、可以使用while循环完成的for循环
⑤ 重复的代码:拷贝/粘贴代码意味着拷贝/粘贴bugs
⑥ 循环体创建新对象:尽量不要再for或while循环体内实例化一个新对象
⑦ 资源关闭:Connect,Result,Statement等使用之后确保关闭掉
代码扫描工具也不少,有 Sonor、Jacoco、PMD 等,在每次提交前都自己跑一次。也有能够实时提醒的最简单的方案,就是安装阿里巴巴的 IDEA 插件。名字叫 Alibaba Java Coding Guidelines。
随便装一样,bug 数量都能降低不少。
3. 代码评审
有些 bug 是一些逻辑问题或者边界问题或者违规问题,一般是不能通过单元测试或者代码扫描检查出来的,还是需要人的介入,毕竟人才是软件质量最大的负责人。那又有人问了,我自己看不行吗?为什么一定要别人帮忙一起看呢?好烦啊。说实话,一个逻辑你写出来后,你有非常大的可能都是咋按照你自己开发的思路在检查代码,有很多明显的错误,你自己发现不了的。多一双眼睛,这是减少缺陷非常优秀又互相学习的好机会。这里只看两个原则,一个是没有代码评审的代码不允许发布,第二个是尽量参照优秀的代码评审经验,比如 Google 的代码评审。《Google's Code Review Guidelines》
测试右移
测试右移这个基础的方法论呢,也不困难。对于已经上线的代码,我们能做的事情其实只有两件。快速发现故障,快速降低故障影响。
毕竟已经上线了,那么故障本身不可能再次进行测试,那只能做到快速发现故障,以及快速降低故障影响了。
1.快速发现故障
基本的实现手段就是 监控 + 告警。基本的实现路径就是 collectd + InfluxDB + Grafana。
collectd 采集数据,InfuxDB 进行数据存储,Grafana 进行数据展示。
当然你要是不缺乏资金,可以尝试一下 ELK 栈或者阿里云的 SLog,也是比较成熟的方案。
监控说完了,告警其实无非就那么几个方式。邮件、短信、电话、webhook基础上的机器人,这些方式在 Grafana 也是支持的。
2.快速降低故障影响
好了,终于到了已经凉的时间了,线上故障已经发生了,我们已经凉了,怎么样才可以不凉那么透呢?其实也只需要两个办法,快速切到稳定的版本和快速执行预案。
怎么快速切到稳定的版本呢?如果不想凉那么透,最好进行灰度发布,从正式环境引流一部分到灰度环境,如果有问题立刻把流量调度回正式环境。如果已经全部发布完成了,假设你有蓝绿环境,那么把环境直接切回到原来的环境就好了。一旦不巧,全部发布完成了,那你只能进行代码回滚了。
快速执行预案又是什么东西呢?想象这么一个场景,我们上线了一个发放优惠券的功能,然后发现线上发放的逻辑有一些问题,我们最快速的止损方案是什么?当然是停止发放并停止新发放优惠券的使用了。这一般可以用配置的形式实现。
假设在开发阶段已经考虑了故障的情况,已经开发了发放逻辑的开关,以及使用逻辑的开关。在故障发生后,打开配置平台,将发放逻辑和使用逻辑的配置都设置为不可用,那么故障影响面自然就降下来了。
当然如果你觉得人为操作可能会出问题(当然历史经验告诉我们,人为操作是最可能出问题的),你可以开发一个平台,专门管理这类在紧急时刻进行配置切换的事情,甚至在接收到告警的地方附上所需切换配置的地址,方便值班人员进行一键切换。
软件质量系列:
以上,国庆还在写,你不来个素质三连吗?感恩