持续集成:软件质量改进和风险降低之道

主旨

这本书讲的是关于持续集成的原则和实践。Martin Fowler关于CI的热门文章发表于2006年,这本书作于2007年,虽然十年间CI的工具已经发生了不少变迁,但本书中提到的基本原则和实践仍然值得借鉴,而且书中提到的关于CI未来发展方向的论述也得到了验证。

本书分为两部分:

  • 第1部分:CI的背景知识,包括基本概念、基本原则与推荐的实践
  • 第2部分:如何创建全功能的CI系统,包括五个持续:
    • 持续数据库集成
    • 持续测试
    • 持续审查
    • 持续部署
    • 持续反馈

第1部分 CI的背景知识

什么是持续集成

Martin Fowler在其文章中将CI描述为:

一种软件开发实践,即团队的成员经常集成他们的工作,通常每个成员每天至少集成一次——这导致每天发生多次集成。每次集成都通过自动化的构建(包括测试)来验证,从而尽快地检测出集成错误。许多团队发现,这个过程会大大减少集成问题,让团队能够更快地开发出一致的软件。

这意味着持续集成其实是一系列实践的集合,例如:

  • 所有开发者均先执行个人构建,在将代码提交到代码库中,以保证集成构建不会失败
  • 开发者每天至少向代码库提交一次代码
  • 集成构建每天会在一台独立的机器上执行多次
  • 每次构建必须100%通过测试
  • 构建必须有产物,如可部署的包等
  • 修复失败的构建是最高优先级的事情
  • 构建中包含代码复查,生成如代码质量报告或依赖分析报告等,作为改进的参考

关于CI:

  • CI实践中提倡自动化,因为集成本身就是一个需要多次重复的过程,自动化有利于降低出错的可能性
  • 使用CI的一个明显好处是能够得到快速反馈,这与重构、TDD等实践的理念是一致的,即进行小的变更,而CI为这些变更提供了一张安全网
  • 关于“持续”一词的解读,其实更应理解为“经常”集成

引入持续集成

  • 越早引入CI越好。因为越到项目晚期实现CI会越困难,人们迫于压力有可能会拒绝改变。如果不得不在项目晚期开始实现CI,建议先从较小的工作开始,逐渐扩大范围
  • 应该“早集成,常集成”。因此应该经常提交代码,更多的提交通常意味着更小的变更,风险会更小
  • 项目文化中应该将修复失败的构建为最高优先级的事,否则团队中的人就无法继续自己的工作

利用CI减少风险

CI不仅仅能帮你节省时间,更能帮你减少项目中的以下风险:

没有可部署的软件

出现这种风险的原因可能有:

  • 没有使用独立的机器来负责软件集成
  • 没有将数据库变更通过版本化的脚本管理起来
  • 手工部署软件导致的错误

很晚才发现缺陷

出现这种风险的原因可能有:

  • 没有实现自动化的回归测试,仅凭手工没有时间回归所有测试
  • 较低的测试覆盖率

缺少项目可见性

出现这种风险的原因可能有:

  • 人工沟通导致信息的丢失与理解偏差
  • 缺乏对软件系统架构或类图的可视化呈现

低品质的软件

出现这种风险的原因可能有:

  • 没有遵守编码标准的代码
  • 没有遵守架构标准的设计
  • 重复的代码

第2部分 如何创建全功能的CI系统

持续数据库集成

持续数据库集成(Continuous Database Integration,CDBI)指的是将数据库的变更视为集成的一部分,在每次项目的版本库发生变更时,重建数据库和测试数据库。

为什么要进行数据库集成自动化?

因为团队没有赋予个人修改数据库的能力,许多项目中DBA经常成为瓶颈。

持续数据库集成的推荐实践

  • 将创建数据库、删除数据库、插入数据等任务使用脚本自动化,让每个开发者都能够使用简单如db:create式的命令执行
  • 使用不同的SQL文件来支持不同的环境,如开发、QA、生产环境等
  • 每个开发者应该有独享的数据库“沙盒”来隔离代码变更,比如独立的schema,或者独立的数据库
  • 将DBA解放出来研究和完成一些更高级的任务,比如优化数据库的性能、优化SQL的性能、数据规范化等
  • 数据库也有测试工具,如PL/Unit、QUnit、SQLUnit等

持续测试

为什么要进行持续测试?

根据系统工程的可靠性定理,线性系统的可靠性是每个系统组件可靠性的乘积,这意味着系统整体的可靠性要低于任一个系统组件的可靠性。更何况考虑到系统组件之间的连接,软件系统应该是一个非线性系统,可靠性比线性系统更低,所以我们必须测试每一个系统组件的可靠性。

自动化测试的分类

  • 单元测试:对系统中最小的代码单元的测试,这个单元通常是一个类
  • 组件测试:验证系统的各个部分组合到一起能够产生预期的行为,通常通过API来执行
  • 系统测试:验证整个系统的行为是否正确,通常通过外部接口,如web界面或GUI等来进行,可以通过Selenium这样的框架来驱动浏览器执行
  • 功能测试:从客户的角度来验收整个应用程序是否满足所需功能,通过模拟用户行为来执行,通常也被认为是用户验收测试

持续测试的推荐实践

  • 将不同的开发者测试分类
  • 将自动化的开发者测试提交到代码库中
  • 先执行较快的测试
  • 使用适当的框架,让组件测试可重复

持续审查

持续审查是指对代码风格、代码是否符合编码标准的检查,需要通过自动化代码审查和人工审查相结合来进行。

自动化代码审查是人的智慧的增强,通过诸如PMD、Simian等工具来检查代码的圈复杂度、耦合度等指标,有的还能提示代码中潜在的风险。

但自动化代码审查并不能检出所偶问题,人工审查仍是必不可少的,人工审查更关注于自动化代码审查检查不出来的东西,比如代码的可读性、可维护性等。

持续部署

持续部署的目的是让团队随时随地都可以按需发布能工作的软件,同时使得发布工作量最小。

部署工作应该简化到只需一条类似ant deploy式的命令即可完成。

一个典型的部署工作由6大步骤组成:

  1. 为库中的资产打上标签:为版本库中的同一组文件打上标签,表示是与同一个包有关。相当于为包依赖的代码文件做一组快照。
  2. 得到干净的环境:在作者写这本书时docker还没有诞生,所以要得到一个干净的环境,通常需要删除所有已安装的软件甚至重新安装操作系统,所幸的是今天已有容器技术帮助实现应用程序环境之间的隔离,我们可以随时从一个干净的操作系统开始,层层构建所需的依赖。
  3. 为构建版打上标签:这里的构建版标签与第1步中的版本库标签是不同的。版本库标签用来说明一组文件是相关的,是同属于一个版本的;构建版标签用于表示构建出的二进制包是唯一的。
  4. 执行所有的测试:部署前的测试重点是在一个干净的类生产环境下执行所有的测试,包括所有的自动化测试,以及人工检查。尽管自动化测试能够覆盖许多检查项,但人工检查仍是必不可少的,因为软件仍然是由人来使用的产品,人工检查能够检查出一些自动化测试测不出来的问题,比如UI界面方面的问题等。
  5. 创建构建反馈报告:生成本次构建的报告,包括文件变化信息、修复了哪些缺陷、实现了哪些功能等,在团队中共享。
  6. 回滚的能力:如果部署后发现问题,通过构建版标签和版本库标签可以找到想要的版本,快速回滚到前一个构建版,“撤销”部署。

持续反馈

CI存在的意义就是为了能够快速构建并让构建快速失败,目的就是为了能够获得快速的反馈信息。

持续反馈就是在正确的时间,以正确的方式,将正确的信息发送给正确的人。

CI系统可以利用的反馈机制有邮件、SMS、可视设备、windows任务条、宽屏显示器等。时至今日,还有IM消息这样的方式可供选择。

CI的未来

在写作这本书时,人们在CI的实践中经常抱怨:

  • 怎样才能防止失败的构建频繁发生?
  • 怎样才能让构建执行得更快?

作者期待在将来看到更多的工具能够支持以下功能:

  • 开发者在合入代码前能够先在一台独立的计算机上执行集成构建,即个人构建,成功后才合入代码
  • 并行化方面提供更多功能,或者利用额外的硬件和软件资源来加速构建
powered by Gitbook最后更新于: 2018-06-03 15:18:35

results matching ""

    No results matching ""