1.3 如何实现目标
正如我们所说,作为软件从业者,我们的目标是尽快地向用户交付有用的可工作的软件。
速度是至关重要的,因为未交付的软件就意味着机会成本。软件发布之时就是投资得到回报之时。因此,本书有两个目标,其中之一就是找到减少周期时间(cycle time)的方法。周期时间是从决定进行变更的时刻开始,包括修正缺陷或增加特性,直至用户可以使用本次变更后的结果。
快速交付也是非常重要的,因为这使你能够验证那些新开发的特性或者修复的缺陷是否真的有用。决定开发这个应用程序的人(我们称为客户)会猜测哪些特性或缺陷修复对用户是有用的。然而,直到使用者真正使用之前,这些全是未经过验证的假设。这也是为什么减少周期时间并建立有效反馈环如此重要的原因。
有用性的一个重要部分是质量。我们的软件应该满足它的业务目的。质量并不等于完美,正如伏尔泰所说“追求完美是把事情做好的大敌”,但我们的目标应该一直是交付质量足够高的软件,给客户带来价值。因此,尽快地交付软件很重要,保证一定的质量是基础。
因此,我们来调整一下目标,即找到可以以一种高效、快速、可靠的方式交付高质量且有价值的软件的方法。
我们及我们的同修发现,为了达到这些目标(短周期、高质量),我们需要频繁且自动化地发布软件。为什么呢?
❑ 自动化。如果构建、部署、测试和发布流程不是自动化的,那它就是不可重复的。由于软件本身、系统配置、环境以及发布过程的不同,每次做完这些活动以后,其结果可能都会有所不同。由于每个步骤都是手工操作,所以出错的机会很大,而且无法确切地知道具体都做了什么。这意味着整个发布过程无法得到应有的控制来确保高质量。常常说软件发布像是一种艺术,但事实上,它应该是一种工程学科。
❑ 频繁做。如果能够做到频繁发布,每个发布版本之间的差异会很小。这会大大减少与发布相关的风险,且更容易回滚。频繁发布也会加快反馈速度,而客户也需要它。本书很多内容都聚焦于如何尽快得到对软件及其相关配置所做变化的反馈,这包括其环境、部署过程及数据等。
对于频繁地自动化发布来说,反馈是至关重要的。下面关于反馈的三个标准是很有用的:
❑ 无论什么样的修改都应该触发反馈流程;
❑ 反馈应该尽快发出;
❑ 交付团队必须接收反馈,并依据它作出相应的行动。
让我们逐一审视一下这三个标准,考虑如何能达到这样的标准。
1.3.1 每次修改都应该触发反馈流程
一个可工作的软件可分成以下几个部分:可执行的代码、配置信息、运行环境和数据。如果其中任何一部分发生了变化,都可能导致软件的行为发生变化。所以我们要能够控制这四部分,并确保任何修改都会被验证。
当修改了源代码后,可执行代码当然也就会随之发生变化。因此每当修改源代码后,都要进行构建和测试。为了能够控制这个流程,构建可执行代码并对其进行测试都应该是自动化的。每次提交都对应用程序进行构建并测试,这称作持续集成。我们会在第3章详细描述它。
之后的部署活动中都应该使用这个构建并测试后的可执行代码,无论是部署至测试环境,还是生产环境。如果你的应用软件需要编译,你应该确保在所有需要可执行代码的地方都使用在构建流程中已生成的这个,而不是再重新编译一次生成一个新的。
对环境的任何修改都应该作为配置信息来管理。无论在什么环境下,对于应用程序配置的变更都应该被测试。如果用户自己安装软件的话,任何可能的配置项都应该在各种具有代表性的环境上测试。配置管理将在第2章中讨论。
如果需要修改该应用程序所要被部署的运行环境,那么整个系统都应该在修改后的环境中进行测试。这包括对操作系统配置、该应用程序所依赖的软件集、网络配置,以及任何基础设施和外部系统的修改。第11章会讲基础设施和环境的管理,包括自动化地创建及维护测试环境和生产环境。
如果是数据结构发生了变化,这些变化也同样要经过测试。我们在第12章讨论数据管理。
什么是反馈流程?它是指完全以自动化方式尽可能地测试每一次变更。根据系统的不同,测试会有所不同,但通常至少包括下面的检测。
❑ 创建可执行代码的流程必须是能奏效的。这用于验证源代码是否符合语法。
❑ 软件的单元测试必须是成功的。这可以检查应用程序的行为是否与期望相同。
❑ 软件应该满足一定的质量标准,比如测试覆盖率以及其他与技术相关的度量项。
❑ 软件的功能验收测试必须是成功的。这可以检查应用是否满足业务验收条件,交付了所期望的业务价值。
❑ 软件的非功能测试必须是成功的。这可以检查应用程序是否满足用户对性能、有效性、安全性等方面的要求。
❑ 软件必须通过了探索性测试,并给客户以及部分用户做过演示。这些通常在一个手工测试环境上完成。此时,产品负责人可能认为软件功能还有缺失,我们自己也可能发现需要修复的缺陷,还要为其写自动化测试来避免回归测试。
运行测试的这些环境应该尽可能与生产环境相似,从而验证对于环境的任何修改都不会影响应用程序的正常运行。
1.3.2 必须尽快接收反馈
快速反馈的关键是自动化。对于实现完全自动化过程来说,唯一的约束条件就是你能够使用的硬件数量。如果是手工过程,我们可以通过人力来完成这个工作。然而,手工操作会花更长的时间,可能引入更多的错误,并且无法审计。另外,持续做手工构建、测试和部署非常枯燥而且有重复劳动,与人力资源利用率的准则相悖。人力资源是昂贵且非常有价值的,所以我们应该集中人力来生产用户所需要的新功能,尽可能快速地交付这些新功能,而不是做枯燥且易出错的工作。像回归测试、虚拟机的创建和部署这类工作最好都由机器来完成。
当然,实现这样的部署流水线是需要大量资源的,尤其是当有了全面的自动化测试套件之后。部署流水线的关键目的之一就是对人力资源利用率的优化:我们希望将人力释放出来做更有价值的工作,将那些重复性的体力活交给机器来做。
对于整个流水线中的提交(commit)阶段,其测试应具有如下特征。
❑ 运行速度快。
❑ 尽可能全面,即75%左右的代码库覆盖率。只有这样,这些测试通过以后,我们才对自己写的软件比较有信心。
❑ 如果有测试失败的话,就表明应用程序有严重问题,无论如何都不能发布。也就是说,像检查界面元素的颜色是否正确这类测试不应该包含在这个测试集合当中。
❑ 尽可能做到环境中立。这个环境没必要和生产环境一模一样,可以相对简单廉价一些。
相对而言,提交阶段之后的测试一般有如下这些特点。
❑ 它们通常运行更慢一些,所以适合于并行执行。
❑ 即使某些测试有可能失败,但在某种场合下,我们还是会发布应用程序。比如某个即将发布的版本有一个不稳定的修复,会导致其性能低于预先定义的标准,但有时我们还是会决定发布这个版本。
❑ 它们的运行环境应该尽可能与生产环境相同。除了测试功能以外,它同时还会对部署过程以及对生产环境的任何修改进行测试。
先经过一轮测试(在便宜的硬件上运行最快的那些测试)之后,再经过这种测试过程,会让我们对软件更有信心。如果这些测试失败了,这个构建版本就不会再进入后续阶段,这样就可以更好地利用资源。第5章中会详细介绍流水线技术,而第7、8、9章中会分别讲述提交测试阶段、自动化验收测试,以及非功能需求的测试。
这种方法的基础之一就是快速的反馈。为了确保对变更的快速反馈,我们就要注意开发软件的流程,特别是如何使用版本控制系统和如何组织代码。开发人员应该频繁提交代码到版本控制系统中,像管理大规模团队或分布式团队那样,将代码分成多个组件。在大多数情况下,应该避免使用分支。我们将在第13章讨论增量式交付以及组件的使用,在第14章中讨论分支与合并。
1.3.3 交付团队必须接收反馈并作出反应
参与软件交付过程的所有人(包括开发人员、测试人员和运维人员、数据库管理员、基础设施的专家以及管理者)都应该参与到这个反馈流程中,这是至关重要的。如果这些人无法做到每天都在一起工作(尽管我们认为团队应该是全功能团队),就一定要常常碰头并一起探讨如何改进软件交付的流程。对于快速交付高质量的软件来说,基于持续改进的过程是非常关键的。迭代过程有助于为这类活动建立规律性,例如每个迭代至少开一次回顾会议,在会上每个人都应参与讨论如何在下一个迭代中改进交付过程。
想要能够根据反馈来调整行动,就要对信息进行广播。使用一个大且可视的仪表盘(并非一定要电子的),或者其他通知机制对于确保反馈送达到每一个人是极为重要的。这个仪表盘应该随处可见,而且至少每个团队的屋中都应放置一个。
当然,如果最后没有引发什么改进行动,反馈也就没有什么用了。因此,这就要求纪律性和计划性。当需要采取行动时,整个团队有责任停下他们手中的事情,来决定接下来采取哪些行动。在完成此事之后,团队才能继续自己的工作。
1.3.4 这个流程可以推广吗
很多人认为我们所描述的过程太理想化了。他们认为,这样的事情在小团队中可能行得通,但在大型分布式项目中是不行的!
我们在过去的几年中,在不同的行业里做过很多大型项目。我们非常幸运,曾与有各种不同经验的同事在一起工作。本书中描写的所有技术与实践,在各种组织(无论大型组织还是小型组织)各种情况下的真实项目中都被证明是有效的。也正是在此类项目中一次又一次经历同样的问题,才促使我们写了这本书。
你会注意到,书中的很多东西都来自于精益思想和哲学。精益制造的目标是确保快速地交付高质量的产品,它聚焦于消除浪费,减少成本。多个行业的实践已经证明,精益制造可以节省大量成本和资源,带来高质量的产品,缩短产品上市时间。这一哲学在软件开发领域也渐成主流,而且影响着本书中的很多内容。精益并不仅仅局限于应用在小系统上,它已在大型组织甚至整个经济体系中得到应用。
根据我们的经验,这一理论与实践既可以应用在大型组织中,也可以应用于小团队,但我们并不要求你马上相信我们所说的。自己试一试,就能找到答案。保留那些对你的团队有效的实践,放弃那些无效的实践,将你的经验写下来,以便别人可以借鉴。