4.1 细粒度、低耦合、可复用的架构
4.1.1 软件架构
好的软件架构让软件能够比较容易地扩展新的功能,或者改进已有的功能,从而让软件具有较高的可维护性、可复用性。而从软件交付过程的视角来看,软件架构也很重要。
例如,无论是一个大型的单体应用,还是一组在运行时相互配合的微服务,对软件交付过程有什么影响呢?如果是大型的单体应用,则意味着构建慢,部署也慢,写几行代码想运行一下试试,半小时后见结果。此外,由于构建时就绑定在一起,所以总有很多人在修改,一起集成,一起测试,有很多协调工作要做,而且还相互拖累——一旦某一部分出现问题,整体进度就会被拖慢。
而如果是微服务架构,那么对每个微服务的构建和部署就快得多。如果各个微服务之间的耦合性较弱的话,则通常开发一个新功能只需要改动某一个微服务即可。于是各个微服务可以分头集成、测试和发布,而不会相互影响、相互等待,不用“等火车”“赶火车”“扒火车”,减少了等待时间。系统解耦得越好,各个子系统、微服务的交付过程就越独立自主。
软件不仅是由代码组成的,还包括软件部署运行的环境,以及各种配置参数、数据库表结构等。它们之间的耦合性最好也比较弱,可以分别单独进行修改变更。例如,不必因为不同环境中配置参数的不同而重新构建源代码,也不必因为调整业务配置参数而重新走一遍“开发—集成—测试—发布”流程。这也是软件架构要支持的内容。
构建慢、部署慢、测试慢,代码合并冲突多,集成频率低,发布频率低……遇到这些情况,先往上游看看,看架构是否合理,有没有改进的空间。如果架构是分层、分模块的,模块是细粒度的,模块间是低耦合的,各模块是可以分别构建和部署、在运行时才通过API相互配合的,那么集成、测试、发布就好做,就快;反之就不好做,就慢。
4.1.2 测试脚本和测试数据的架构
软件架构首先影响代码编写的效率,这发生在软件交付过程之前。而软件架构对软件交付过程本身的影响,倒像是“副产品”。相比之下,自动化测试脚本和测试数据的架构,对软件交付过程最重要的影响就是:测试脚本和测试数据的架构好,实现测试用例的速度就快,就好维护,就可以进行更多的自动化测试。于是,软件交付过程就更快了。
比如数据驱动是把测试脚本与测试数据分离,同一个测试脚本可以跑好几批数据,分别测试各种正常情况、边界情况、异常情况。这样,就不必为每种情况再写一遍高度类似的测试脚本了。
再比如页面对象模型,以Web页面或移动端原生应用页面为单位来封装页面上的控件,以及控件的部分操作。而测试脚本调用操作函数,基于页面封装对象来完成具体的页面操作。这样,可以清楚地看到在什么页面上执行了什么操作,也可以更容易地将具体的测试步骤转换成测试脚本,同时代码的可读性和可维护性也得到了大幅提升。
类似地,还有业务流程抽象、关键字驱动等。这些方法的本质都是通过合理的分层架构来提高复用性。
4.1.3 组织架构
软件系统的架构,以及测试脚本和测试数据的架构,要细粒度、低耦合、可复用,由人构成的组织的架构也是这样的。组织架构也要小团队、低耦合、可复用,为的是开发并上线一个新特性,最好是一个小团队甚至一个人就能完成,自主完成工作。不然就会出现很多人和人之间,以及团队和团队之间的配合协作、排优先级、工作交接等事情,这些都是很消耗时间和精力的。
在敏捷中经常提到的特性团队是这么定义的:“特性团队是指长期的、具备交付价值所需的各种角色的、可以协同完成完整用户价值交付的团队。”注意,“特性团队”中所说的特性,不是指几天就能开发完成的一个小特性,而是更像一个需要长期开发和维护的独立产品,或者一个完整功能。我们把特性团队的定义拆解来看看。
• 长期团队。团队应该长期负责某个系统或者系统的某部分,比如某个子系统、某个功能或者某个组件。也就是说,一方面,团队不能总是打一枪换一个地方;另一方面,某块内容总是有一个长期负责它的团队。为什么呢?因为随着时间的流逝,这个团队对其所负责的部分越来越了解,开发效率就会越来越高,交付效率也会越来越高。并且团队会有主人翁责任感,始终注意不让架构腐化或者别那么快地腐化。此外,在有后续需求或者发现缺陷时能迅速做出反应。总之,这个团队对这个部分越有自主性,效率就越高,也越让人踏实。
• 跨职能团队(或者叫全功能团队、全流程团队、Stream-aligned team等)。这个团队具备交付价值所需的各种角色——每个职能部门不能都是一个竖井,以至于设计、开发、集成、测试、发布时必须到各个职能部门转一圈儿。各种测试工作,最好是团队内部的开发人员和测试人员一起就能完成,而不是每次都跑到测试部门去申请本次测试资源。部署操作也一样,不论是往测试环境中部署还是往生产环境中部署,都应该由团队自己轻松完成,无须提交变更单给运维人员。敏捷运动兴起时,重点强调了需求、开发、测试最好由一个团队负责,而DevOps运动将其扩展到了部署运维以及安全。
• 独立完成开发。各个开发团队之间的划分也有讲究,本质上也是要自主、要解耦。一个新功能的开发,或者一个已有功能的改造,最好是一个团队就能单独完成,而不需要牵扯到若干团队,比如,必须由他们分别来改各自负责的源代码,否则在软件交付过程中出现跨团队的集成、测试、发布等一系列协作时,会很麻烦。如果软件架构做得好,细粒度、低耦合,那么就比较容易实现各个开发团队之间的解耦:不同开发团队和软件模块相互对应。由于通常改一个模块就能实现需求,这就意味着一个需求由一个开发团队就能独立实现。
除了特性团队定义中提到的上述三个要点,团队规模也很重要。你可能听说过亚马逊的“两个披萨原则”[1]。一个团队5~9人最佳,团队再大最好也别超过15人,更大的团队就要细分,让每一部分专注于自己的业务,并且拥有相当程度的自主性,可以对结果负责。它的核心是要提高运作效率。
有人是这么评论亚马逊的做法的:“你可以在不添加新的内部结构或直接报告的情况下添加新的产品线,你不用开会、不必经历一系列项目流程,就能在物流和电子商务平台上添加它们。你不需要(从理论上说)飞往西雅图,安排一场会议,让人支持你在意大利开展的项目,或者说服任何人将新业务加入他们的路线图。”
除了细粒度、低耦合,好的组织架构还有一个重要特点就是可复用。以软件交付为例,应该有组织级的工具平台团队,来评估、比选、引入、运维、集成、定制甚至开发与软件交付相关的工具平台。这比让每个开发团队都重复地做这些事情要划算得多。在推动软件交付过程的改进方面,最好也要有组织级的领导协调,根据本企业的业务特点,制定相对统一或至少比较收敛的规范和流程,让相关人员不用费心就能走上正确的道路,而组织结构调整、转岗之类的人员流动所带来的学习成本也会明显降低。
此外,要留意康威定律。康威定律说的是,你把组织结构做成什么样子,那么所开发的软件系统的架构就会长成什么样子。所以,为了让软件系统有一个好的架构,就需要对组织结构深思熟虑,让它符合我们想要的未来的系统架构。
总之,需要有合理的组织架构,其中的核心就是细粒度、低耦合、可复用,让每个团队都具备自主性,独立负责一个模块,而这个模块是细粒度、低耦合甚至是可复用的。于是,整个软件开发过程就会顺畅、高效得多,当然也包括其中的软件交付过程。
架构细粒度、低耦合、可复用,自己完成一件事情,不要总是动辄牵扯到别的人、别的事,这是软件交付的第1个策略。合理的组织架构、软件架构、测试脚本与测试数据架构,让软件交付过程顺畅、高效。