软件交付的终态是提供一个稳定可预期的系统,可预期的系统要确保环境和软件制品的一致性。而在软件研发协作的过程中,无论是制品、环境,还是发布过程,往往都是定义在代码里的。
软件交付体现为发布,而提升交付能力的目标,是要发的容易,发的频繁,增量要多,每次发的时间要少。
软件交付的终态是提供一个稳定可预期的系统,可预期的系统要确保环境和软件制品的一致性。而在软件研发协作的过程中,无论是制品、环境,还是发布过程,往往都是定义在代码里的。
软件交付体现为发布,而提升交付能力的目标,是要发的容易,发的频繁,增量要多,每次发的时间要少。
以上图为例,横轴是时间,纵轴是每一次发布的耗时。一个红点表示一次失败的发布,一个绿点表示一次成功的发布。图中该应用的发布成功率只有30%左右。
在我们的实际工作中,很多问题都会导致发布成功率特别地低。例如:
约定的时间没有办法交付,可能是因为在软件的沟通和设计阶段耗时过多,导致开发很晚开始,或者软件质量特别差,缺陷特别多,导致花费大量的时间修复问题。随着时间的推移,团队花到维护上的时间越来越多,用于新功能开发的时间越来越少。同时为了发地更快,就会减少原来应该在进入维护阶段前做掉的事情,让维护的工作在后期堆得更多。
随着问题暴露得越来越晚,修复成本呈指数级上升,一旦问题留到客户那里,成本将会非常高的。举个例子,有一年在法国出现过一个电信故障,因为电信软件生产环境和开发环境是隔离的,一旦出现问题,意味着需要派工程师从中国飞过去,在现场进行问题调研,再给出相应的修复补丁。在这个过程中,电信设备是没有办法正常工作的,电信设备的停机将会带来极高的成本。在商业角度,电信运营商会向设备商提出赔偿要求,如果一个小时不能恢复,可能就需要赔付好几百万欧元。
在互联网行业里也是一样,问题发现的越晚修复成本越高。修复成本分为两类:我们把上线之后出现质量问题带来的修复成本,称为交付质量的成本,即交付出去之后由于缺陷导致的成本。另一方面,在交付的过程中产生的成本,也是越到后面越高,因为涉及到的研发团队的角色、职能越多。
所以,要提升发布的成功率,我们需要保证待发布内容的质量。只有发布内容的质量得到保证,才能做到发的容易,发的频繁。
上图把测试做了简单的分类,下半部分偏技术实现导向,上半部分偏业务结果导向,右侧面向整个产品来看质量,左侧面向团队功能实现层面看质量。
1象限(左下角),包括单元测试和组件测试,都是偏技术实现的,而且是团队内部要搞定的。功能测试、工作流、场景性的测试,包括用户体验测试和结对测试偏业务,但是也属于团队实现过程当中做的测试,所以放到2象限。3象限,在整个产品的角度是偏业务的,包括可用性探索测试、客户结对测试和验收测试。最后的4象限一方面偏技术实现,另一方面又是站在整个产品的完整性角度做的验证,如压测、性能测试、安全测试、数据迁移测试、扩展性测试、负荷测试等,要把这些测试都做好成本是特别高的。
有了完整的测试分类,在整个软件交付的生命周期里如何来合理安排它们呢?需要质量保障体系来定义。
上图是企业里比较常见的质量保障体系。从需求评审和架构设计开始,这时需求的质量和架构的质量是非常重要的,如果这时候质量出现问题,后面所有的工作都需要返工。需求和架构明确下来后就进入编码和开发环节,代码质量和安全质量就会成为核心关注点。接下来是编码的测试验证阶段,包括测试质量,数据质量,稳定性质量等。当功能测试告一段落之后,性能和用户体验成为主要关注对象,直到发布完成。发布之后需要综合线上用户的反馈,产生一个非常全面的质量评估。
往下一层是大量的实践,比如自动化测试的实践,稳定性测试的实践,性能测试的实践,安全测试的实践等。
再往下是整个基础平台和流程支撑,包括测试承载的环境和工具等。
整个软件交付周期中上下游所有的角色都应该参与在质量保障的工作中。
质量是团队所有人的事。有的团队说要加强业务需求评审,之所以质量问题多,是前序业务输入的需求问题多;有的团队说要加强开发自测,之所以质量不好是因为开发自测的质量不好,事实上,这些观点都是片面的,这些举措往往不但于事无补,还可能造成团队成员间的对立。解决质量问题的第一步是要有一个明确的标准,比如需求的质量标准,作为代码的质量标准,每个阶段有相应的标准定义。
另外,上游永远要考虑能否很好地帮助下游把工作做好。比如如果在做架构设计的时候没有考虑可测性,等到开始测试的时候就没有办法开展了。上游的人应该承担更重大的角色,上游做好下游可以省很多事情。
第三,要避免测试代码的“公地危机”。做测试自动化会产生相应的测试代码,可能会涉及到通用的一些部分,几个团队大家都要共用,这时如果没有很好的维护,没有一种代码集体所有制,没有建立好标准化,测试代码就会变成“公地危机”。
要做到持续的交付,一定要有持续的测试保证达到待发布状态。要做到这一点,以微服务为例。
上图是比较典型的微服务应用的例子,如果要给它做相应的质量保障,提供相应的测试来守护它,整体的测试策略如下:
红色虚线部分是单元测试的时候就可以保证质量的,将测试限定在被测试系统的某一部分,比如只测Service那一层某一个接口或是函数,或者是domain里面的某一个方法。在这个基础上,更大的范围是组件层面的,组件之间能否交互,接口是否正确等。整个组件里中,排除掉外部的依赖(如数据依赖和网络的消息依赖),更多的是跟网关之间的接口测试。再往外,才是与外部应用的集成测试,应用与应用之间会有协议交互,彼此交付所遵循的协议和规范即为契约。基于契约的测试,我们称之为契约测试。而再往外就是端到端的测试了,关注的就是不是一两个应用,而是多个应用组合在一起的完整的系统。
站在用户的角度来说,交付的系统是否提供了一个正常的业务服务,只要关注端到端测试就可以了。而站在组件的开发的角度来说,我只负责Service layer这一层的修改,我只要把这一层用单元测试保护一下。因此,对于不同的角色,选择的测试是不一样的,而且不同的时间阶段里面选择的测试也会不一样。在这个过程当中,选择什么样的测试主要考虑的是成本和收益的平衡。
如图,纵轴表示成本,横轴表示质量,红色的曲线表示质量成本的曲线,成本的曲线会随着质量的要求做相应的变化。
两条斜的虚线,向上的这条是指不做测试,认为质量投入会有成本,不投入相应的手段保证质量。这条虚线一开始的成本为0,因为完全不做跟质量相关的事情。但是随着后面缺陷越来越多,故障越来越多,管理质量的成本就不会不得不越来越高,呈指数级上升。
另一条往下的虚线,认为质量就应该做到极致,一开始就投入了非常多的成本,到质量变好的时候,投入将会快速地下降。我们给质量的成本做了个简单的分类:
一个是低质量的成本,包括内部缺陷成本,研发过程当中由于缺陷带来的种种制约。另外也包括外部缺陷成本,缺陷留给客户,可能会产生的业务上的损失。如果觉得这个缺陷成本很低,在质量这一方面就可以不用投入。
另一个是高质量的成本,包括预防的成本和验证的成本,验证成本也被叫做“评估成本”,用来评估质量好与不好。比如,如何知道这个制品的质量好还是不好?需要定义相应的一些验收的用例验证,然后执行它,看是符合预期还是有一些意想不到的问题。
选择哪些测试在哪个阶段执行,是取决于这些成本考虑的。有些测试,要投入很多测试机器,要雇很多人写测试用例,执行和分析很多测试自动化的脚本,有一些测试成本特别高,买一台测试设备需要几十上百万,如果在产品的早期就进行这样的投入,往往是不经济的。从这个角度来说选择什么样的测试策略,其实是从经济学的角度来考虑,测试是非常高成本的事情。
一方面,要提升应用的发布成功率,需要建立在全面的质量守护体系的基础上。研发团队的上下游各个角色都需要参与到质量守护的活动中,并为之负责,特别是上游要更多地为下游考虑质量问题,共同提升质量水平,避免测试的公地危机。
另一方面,质量是有成本的,守护质量也是有成本的,我们对质量的投入要平衡成本与收益,既要避免质量问题带来的过高管理成本,也要避免对质量保障的过度投入。简而言之,质量问题首先是一个经济问题,然后是管理问题,最后才是工程技术问题。