研发质量保障与工程效率
上QQ阅读APP看书,第一时间看更新

06 快速交付团队的内功修炼心法

作者介绍

范钢 航天信息首席架构师,规模化敏捷SPC,软件架构及重构的客座讲师。先后参与了数十个国内大型软件项目,涉及国家财政、军工、税收、医疗等领域的大数据建设、风险防控与人工智能研究。长期关注大型业务系统的品质保证、防止腐化以及技术改造等困扰软件企业的问题,微服务及大数据转型的实践者与倡导者。

案例综述

现如今,我们处在一个快速变化的年代。这种快速变化首先体现在近10年的互联网高速发展。2009年,淘宝在“双11”的交易额只有0.5亿元,而2018年已经达到2100亿元。从0.5亿元到2100亿元,市场在变,技术架构也在不停地演变。然而这种演变不仅是互联网的专利,传统行业也在经历着互联网转型的过程,这种转型带给我们太多的痛苦与挑战。

经过了这一轮互联网转型,我们是不是可以休息一段时间呢?答案是不能。2016年,Alpha Go战胜了围棋大师李世石,引发了整个社会对人工智能的关注。人工智能并没有媒体炒作得那么神奇,它的神奇在于背后海量数据的支撑。然而,当我们收集了海量的数据,应该把它放到哪里呢?如果放到关系型数据库,数据越多,查询越慢。因此,可以想见,在未来三到五年时间里,我们又将经历一轮大数据转型,这一轮转型虽然又会给我们带来很多挑战,但是也会带来很多全新的机遇、全新的市场,与全新的发展机会。

当我们看到机会的时候,我们的竞争对手也会看到这些机会。我们站在同一起跑线上起跑,谁将获得最后的胜利取决于速度。

软件的价值在用户刚刚提出需求的时候价值最高。用户刚刚提出需求时,市场上还没有相应产品,如果我们适时地推出我们的产品,那么用户将很有意愿去购买。但是,如果我们的开发耗时几个月,当我们把所有的功能都完善了再推出产品时,可能市场已经发生变化,或者竞争对手先于我们进入,我们的产品价值就降低了。因此,我们需要用更快的速度交付用户最急切需要的功能,快速占领市场,获得竞争优势。

为了获得快速交付的能力,“临时抱佛脚”是必然不行的,团队需要在平时修炼“内功”。而这种“内功”的修炼体现在软件研发的方方面面,包括高效的团队组织、精简的代码维护、强有力的平台支撑、自动化的运维平台。

案例实施

1.高效的团队组织

许多团队都有这样一个经历,在项目初期,由于业务简单,参与人员少,我们往往可以获得一个较好的交付速度。但是,随着项目的不断推进,业务变得越来越复杂,参与的人也越来越多,交付速度就变得越来越慢,使得团队越来越不能适应市场的快速变化,一直处于竞争的劣势。然而,软件规模化发展是所有软件发展的必然趋势,因此,解决规模化团队与软件快速交付的矛盾就成了我们不得不面对的难题。

为什么团队越大交付越慢呢?从需求到交付的过程中,我们要经历多个部门的交互才能完成,大量的时间被耗费在了部门间的沟通协调中了。这样的团队被称为“烟囱式”的开发团队。

烟囱式的开发团队会导致烟囱式的软件开发。在大多数软件项目中,每个功能都要有每个功能的页面、Controller、Service、DAO,需要编写大量的代码,其中很多都是重复的。代码写得越多Bug就越多,日后越难以维护和变更。

此外,统一的发布也制约了交付的速度。当业务负责人将需求分配给多个团队开发时,A团队的工作可能只需要1周就可以完成。但是,当A团队完成了他们的工作以后,并不能立即交付给客户,因为B团队需要开发2周,因此A团队只能等B团队开发完成以后才能统一交付。如果不能立即交付用户,A团队的开发速度再快也不能产生用户价值。随着团队的规模不断扩大,这种状况就会越来越常见,经常会出现五六个团队等待发布时,某个团队突然出现了重大Bug,其他所有团队只能继续等待,交付速度就变得越来越慢。

为了解决以上问题,我们需要调整组织架构,将架构调整为跨功能团队或特性团队。在特性团队中,每个团队都直接面对终端客户,比如购物团队,所有与购物相关的功能都是由他们负责完成的,包括从需求到研发,从UI到应用再到数据库。最后,经过测试后,也是由这个团队负责上线部署。这样,整个交付过程就都是由一个团队负责的,减少了沟通协调的成本,交付速度自然获得了提升。

特性团队并不是各自独立工作,相互之间不需要协调的。在这个案例中,无论是购物团队、交易团队、物流团队,都需要读取商品信息。如果他们都直接从商品信息表中读取,那么一旦商品信息表发生变更,多个团队都需要变更,还全都需要重新测试与发布,那么这个变更的成本就非常高了。因此,商品信息统一交给商品团队去维护,其他团队则去调用商品团队提供的接口,这样一旦商品信息表发生变更,只需要商品团队去维护,这样维护成本就得到了降低,这才是高内聚的组织形式。

有了特性团队的组织形式,如果还是统一发布,那么交付速度依然无法提升。因此,在特性团队的基础上,我们采用了微服务的架构,即每个特性团队各自维护自己的微服务。这样,只要该团队完成了一次开发,就自己独立打包、独立发布,不再需要等待其他团队。这样,交付速度就可以得到大幅度提升。

然而,构建一个特性团队的成本是很高的。团队中的每个成员都必须既要懂业务,也要懂开发,既要懂UI、应用,还要懂数据库,甚至大数据,要做全栈工程师。按照这个标准构建每个特性团队,是没有办法真正落地的。这个问题的关键在于底层的架构团队。架构团队通过构建技术中台,将软件开发中诸如UI、应用、数据库、大数据等许多技术进行了封装,然后以接口的形式开放给业务,降低业务开发的技术门槛。这样,特性团队的主要职责就是业务开发,他们可以从软件技术中解脱出来,将更多的精力放到对需求的理解,对业务的实现上,我们称之为大前端。所谓大前端,就是一种职能的转变,即业务开发人员不再关注技术,而是更加关注业务本身,深刻地理解业务,并快速应对市场变化调整业务需求。

采用“大前端+技术中台”的战略,需要架构团队的支撑,更需要微服务架构的支持。因此,随着传统行业向互联网转型,又要经历一轮新的微服务的转型。如图6-1所示,当用户以不同的渠道接入到系统中以后,通过服务网关就会调用到各个微服务中去了。

图6-1 “大前端+技术中台”战略

每个微服务都是一个软件项目,麻雀虽小五脏俱全,每个微服务都有各自的基础设施、服务、实体、数据访问与数据库。此时有必要引出两个重要的概念:去中心化的技术治理、去中心化的数据管理。

•去中心化的技术治理,就是指每个微服务都有自己的基础设施。因此,每个团队可以根据各自业务的不同,选用不同的技术架构,甚至不同的语言,这样可以为日后快速的技术变革创造条件。现如今,技术变革越来越快,但在老项目中进行技术架构调整,对每个团队来说都是一种挑战。在传统的单体应用中,一旦技术架构有调整,那么所有的功能都要调整,其成本就会非常高昂。但是,如果原有的功能可以正常运行,我们为什么要进行调整呢?因此,采用了微服务架构,老功能可以继续运行在老的技术架构上,新功能则放在新的微服务中,采用新的技术架构。这样,我们采用新技术的成本就会大幅降低,可以更加频繁地采用新技术,从而让我们在市场中获得技术竞争的优势。

•去中心化的数据管理,就是指每个微服务除了有自己的基础设施,通过数据库拆分,还可以有自己的数据库。这样,每个微服务都可以根据自己的需求,采用不同的数据库,这对于即将到来的大数据转型尤为重要。客户服务与商品服务,对应的客户表与商品表的特点就是数据量小但需要反复读取,因此我们可以采用一个小型的MySQL数据库,在前面架设一个Redis缓存。但交易服务对应的交易数据的特点是高并发与大数据量写入,一个数据库肯定无法支撑,因此我们可以选用分布式的NewSQL数据库(如TiDB)。而经营分析与业务查询,是对海量数据的数据分析与秒级查询,因此我们可以通过读写分离,将数据抽取到NoSQL数据库或大数据平台中以应付这一需求,比较典型的应用有Kylin和ElasticSearch。

2.精简的代码维护

软件开发就像一个管道,软件开发中的每个环节的速度都会影响整个团队的交付速度,而在整个软件开发的流程中,代码的编写无疑是占比最大的一个环节。同时,随着软件业的不断发展,每个软件的生命周期都会越来越长,这就意味着,今后我们会将更多的时间用在更新维护软件上,而不是开发新的软件。如何快速高效地去更新维护一套软件,成了所有软件团队的梦魇。

随着业务的不断开展,用户会对软件提出越来越多的需求,使得软件规划越来越大,代码越来越复杂,进而导致维护越来越困难,更新周期越来越长。如果不能保证软件的设计质量,不能有效降低软件的规模与复杂度,那么软件维护就会越来越难,周期越来越长,也就毫无快速交付可言了。怎样才能保证软件的设计质量呢?我认为,要真正教给开发人员一套切实可行的方法,让开发人员在开发过程中有迹可循,才是解决之道。这个方法就是领域驱动设计。

如图6-2所示是电商软件的付款功能,其最初的需求是比较简单明了的,因此我们最初的版本也是比较清晰的。接着,软件开始不断变更。首先,增加商品折扣,而商品折扣还分为限时折扣、限量折扣、某类商品折扣与某个商品折扣。为了实现这个需求,我们在这段代码中进行扩展,增加了一个if语句。此时,代码开始逐渐膨胀。接着,我们又增加了VIP与支付方式的新需求。每次需求变更,我们都在这个payoff方法中不断地塞代码,代码质量变得越来越差。随后,我们还陆续加入了秒杀、预订、闪购、众筹、返券等各种新需求,这段代码就越来越乱,变更也就越来越难。许多软件就是在这样的变更中不断轮回,这是我们不愿看到的。

图6-2 付款功能代码示例

那么,如何摆脱这样的轮回呢?我们的设计必须做出改变,那就是基于领域的分析设计。软件的本质就是对真实世界的模拟,因此领域驱动设计的基本思想就是将软件设计与真实世界对应起来,即真实世界是什么样子,软件就怎样设计。这样的对应体现在以下三个方面:真实世界有什么事物,软件世界就有什么对象;真实世界中这些事物有什么行为,软件世界中这些对象就有什么方法;真实世界中这些事物间有什么关系,软件世界中这些对象间就有什么关联。

我们将我们对业务的理解,按照这三个对应绘制成领域模型,然后再根据领域模型指导软件开发,如图6-3所示。当软件变更时,将变更还原到真实世界中,然后根据真实世界指导领域模型变更,最后将领域模型的变更映射成程序变更,我们的设计质量就提高了。

图6-3 真实世界是什么样子,软件就怎样设计

以商品折扣的需求为例,我们在变更时首先回到领域模型中进行分析设计。首先,我们要分析付款与折扣之间的关系。这时我们需要回答两个问题:当付款发生变更时折扣是否一定变?当折扣发生变更时付款是否一定变?如果回答都是否定,则说明付款与折扣是软件变化的两个不同的原因。

单一职责原则要求软件设计的每个元素都只完成自己职责范围内的事,而对这里“一个职责”,正确的理解就是“软件变化的一个原因”。正因为这样的理解,我们在设计软件时,就应当将使软件变化的同一个原因的代码放在一起,而将使软件变化的不同原因的代码放在不同模块或不同的类中。如果我们这样设计了代码,在日后变更时,只要是因为这个原因而需要修改的代码就都在这个模块或这个类中,与其他模块或类无关,这样代码维护的成本就降低了,质量也就上升了。基于这样的分析,付款与折扣是软件变化的两个不同的原因,因此,将折扣从付款中抽取出来单独编写一个类。

同样,不同类型的折扣,当限时折扣发生变更时限量折扣不一定变,限量折扣发生变更时某类商品折扣也不一定变,这说明不同类型的折扣各自也是不同的软件变化的原因,因此也应当放到不同的类中。最后的领域模型设计如图6-4所示。

图6-4 基于分析设计的业务模型

基于这样的设计,付款变更与折扣无关,折扣变更也与付款无关,那么变更范围就缩小了,设计质量就提高了。如果开发团队都能这样变更,那么日后的变更成本就会减小,快速交付将成为可能。同时,软件开发也可以进入一种良性循环,不断地维护下去。

3.强有力的平台支撑

软件开发团队只是有了高质量的代码也不能实现快速交付,因为还需要平台架构的支撑。开发团队要提高交付速度,就需要降低代码编写量,但是每次开发都既要考虑业务逻辑,又要考虑技术实现,如何快得起来?因此,快速交付团队的内功修炼中一个重要的方面就是平台架构的修炼。如果有一个强有力的架构团队,将开发中需要使用的各种技术与设计,下沉到平台中封装起来,那么开发人员在开发功能时,技术门槛就将得到降低,开发人员就可以将更多的精力去思考与开发业务代码。这样在开发速度提高的同时,业务代码减少了,维护代码的速度也就提高了。

那么,怎样设计一个强有力的平台架构呢?关键建设思路是将业务代码与技术框架解耦。在系统分层时,基于领域驱动的设计,将业务代码都整合在业务领域层中去实现。在此基础上,将业务领域层与其他各个层次的技术框架解耦,这就是整洁架构的设计思路。

整洁架构(The Clean Architecture)是罗伯特·马丁在《架构整洁之道》中提出来的架构设计思想。整洁架构设计的思想以圆环的形式把系统分成了几个不同的部分,因此又称为洋葱架构。在整洁架构的中心是业务代码(Entity和Application),最外层是各种技术框架,而中间通过适配器将业务代码与技术框架解耦。因此,整洁架构的核心就是这个适配层。

按照整洁架构的思想可以进行如图6-5所示的架构设计。在这个架构中,我们将适配层通过数据接入层、数据访问层与接口层来实现与业务的解耦。首先,用户可以以不同的前端多渠道接入。不同的渠道接入形式是不同的,然而通过数据接入层解耦,就能将前端的多渠道接入与后台的业务逻辑解耦。接着,通过数据访问层将业务逻辑与数据库解耦。未来,企业转型大数据之后,数据存储的设计可能不再是现在3NF的思路了,必然将发生一个巨大的转变。但归根结底都只是存储形式的转变,唯一不变的是业务逻辑层中的业务实体。因此,通过数据访问层的解耦,今后系统架构将更容易向大数据转型。

图6-5 基于整洁架构设计的架构

最后,就是底层技术架构。现在我们谈架构,越来越多地都是在谈架构演化,也就是底层技术架构要不断地随着市场和技术更迭。但实际上,很多系统在技术架构更迭时却非常痛苦。这主要是由于软件在设计时,将太多业务代码与底层框架耦合,使得底层框架一旦变更,就将变更大量业务代码。既然问题是耦合,那么解决的思路就是解耦。在平台建设的过程中,除了通过技术选型将各种技术整合到系统中以外,还应通过封装,在其上建立接口层。通过接口层的封装,封装许多技术的实现,以更加简便的接口开放给业务开发人员。这样,平台既可以降低业务开发的技术门槛,让他们更加专注于业务,提高开发速度,又让业务代码与技术框架解耦。有了这种解耦,未来可以用更低的成本进行技术更迭,跟上这个快速变化的时代。

4.自动化的运维平台

快速交付考验的是开发团队的每个环节,除了团队组织、软件开发、平台架构以外,发布与运维则是最后的“临门一脚”。前面的开发速度再快,没有最后的发布,软件不能送到用户手中,一切都是没有价值的。然而,在传统模式下,开发团队负责开发,运维团队负责发布,他们在交接时会耗费大量时间而不能快速发布出去。同时,传统的单体应用存在着集中发布的问题,转型微服务又存在着运维成本突增的问题。因此,为了更好地适应未来,面向互联网需要快速横向伸缩,以及业务快速迭代的特点,未来的运维将发生巨大的变革,朝着自动化运维与DevOps的方向发展。

DevOps就是将开发(Development)与运维(Operations)相结合,即“谁构建谁运维(who build who run it)”的思想。这种思想认为,开发人员是最了解要运行的软件的,因此应当让开发人员去运维自己的软件。运维人员的职能将发生巨大的转变,由安装部署系统变为去运维一套自动化运维平台,协助开发人员更加方便地去运维他们的软件。

有了这样一套自动化运维平台,开发人员可以将自己的微服务代码上传到各自的Git代码库中,通过Jenkins进行持续集成,就可以自动形成Docker镜像。然后,开发人员在运维平台中定义自己的微服务运行需要多少节点、每个节点多少资源,就可以自动化部署到云端平台,对外提供服务了。在这个过程中,开发人员不再需要编写安装手册与运维人员交接,而是自己制作类似Dockerfile这样的脚本,放到自动化运维平台发布出去。发布的环节减少了,速度将大大提高。有了这样的运维平台,甚至可以将软件从提出需求到待办事项,再到发布至云端平台,进行软件研发全生命周期管理,帮助我们优化研发过程、改进交付过程。

案例总结

只有将软件研发过程中的每个环节都进行了优化和改进,加强团队内部的内功修炼,才能让我们的交付速度真正快起来,从而在激烈的市场竞争中获得优势。