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

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

主旨

这本书讲的是关于持续集成的原则和实践。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等工具来检查代码的圈复杂度、耦合度等指标,有的还能提示代码中潜在的风险。


圈复杂度(Cyclomatic complexity)是一种代码复杂度的衡量标准,在1976年由Thomas J. McCabe, Sr. 提出。

在软件测试的概念里,圈复杂度用来衡量一个模块判定结构的复杂程度,数量上表现为线性无关的路径条数,即合理的预防错误所需测试的最少路径条数。圈复杂度大说明程序代码可能质量低且难于测试和维护,根据经验,程序的可能错误和高的圈复杂度有着很大关系。

 
   

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

持续部署

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

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

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

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

持续反馈

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

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

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

CI的未来

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

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

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

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

原文地址:https://www.cnblogs.com/cx2016/p/12078749.html

时间: 2024-11-10 13:58:28

《持续集成:软件质量改进和风险降低之道》的相关文章

CI框架源码阅读笔记3 全局函数Common.php

从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap引导文件都会最先引入全局函数,以便于之后的处理工作). 打开Common.php中,第一行代码就非常诡异: if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 上一篇(CI框架源码阅读笔记2 一切的入口 index

IOS测试框架之:athrun的InstrumentDriver源码阅读笔记

athrun的InstrumentDriver源码阅读笔记 作者:唯一 athrun是淘宝的开源测试项目,InstrumentDriver是ios端的实现,之前在公司项目中用过这个框架,没有深入了解,现在回来记录下. 官方介绍:http://code.taobao.org/p/athrun/wiki/instrumentDriver/ 优点:这个框架是对UIAutomation的java实现,在代码提示.用例维护方面比UIAutomation强多了,借junit4的光,我们可以通过junit4的

Yii源码阅读笔记 - 日志组件

?使用 Yii框架为开发者提供两个静态方法进行日志记录: Yii::log($message, $level, $category);Yii::trace($message, $category); 两者的区别在于后者依赖于应用开启调试模式,即定义常量YII_DEBUG: defined('YII_DEBUG') or define('YII_DEBUG', true); Yii::log方法的调用需要指定message的level和category.category是格式为“xxx.yyy.z

源码阅读笔记 - 1 MSVC2015中的std::sort

大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格式化,去掉或者展开用于条件编译或者debug检查的宏,依重要程度重新排序函数,但是不会改变命名方式(虽然MSVC的STL命名实在是我不能接受的那种),对于代码块的解释会在代码块前(上面)用注释标明. template<class _RanIt, class _Diff, class _Pr> in

CI框架源码阅读笔记5 基准测试 BenchMark.php

上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功能,各模块之间可以相互调用,共同构成了CI的核心骨架. 从本篇开始,将进一步去分析各组件的实现细节,深入CI核心的黑盒内部(研究之后,其实就应该是白盒了,仅仅对于应用来说,它应该算是黑盒),从而更好的去认识.把握这个框架. 按照惯例,在开始之前,我们贴上CI中不完全的核心组件图: 由于BenchMa

CI框架源码阅读笔记2 一切的入口 index.php

上一节(CI框架源码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程,这里这次贴出流程图,以备参考: 作为CI框架的入口文件,源码阅读,自然由此开始.在源码阅读的过程中,我们并不会逐行进行解释,而只解释核心的功能和实现. 1.       设置应用程序环境 define('ENVIRONMENT', 'development'); 这里的development可以是任何你喜欢的环境名称(比如dev,再如test),相对应的,你要在下面的switch case代码块中

Apache Storm源码阅读笔记

欢迎转载,转载请注明出处. 楔子 自从建了Spark交流的QQ群之后,热情加入的同学不少,大家不仅对Spark很热衷对于Storm也是充满好奇.大家都提到一个问题就是有关storm内部实现机理的资料比较少,理解起来非常费劲. 尽管自己也陆续对storm的源码走读发表了一些博文,当时写的时候比较匆忙,有时候衔接的不是太好,此番做了一些整理,主要是针对TridentTopology部分,修改过的内容采用pdf格式发布,方便打印. 文章中有些内容的理解得益于徐明明和fxjwind两位的指点,非常感谢.

CI框架源码阅读笔记4 引导文件CodeIgniter.php

到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.com/usr/reg 经过引导文件,实际上会交给Application中的UsrController控制器的reg方法去处理. 这之中,CodeIgniter.php做了哪些工作?我们一步步来看. 1.    导入预定义常量.框架环境初始化 之前的一篇博客(CI框架源码阅读笔记2 一切的入

jdk源码阅读笔记之java集合框架(二)(ArrayList)

关于ArrayList的分析,会从且仅从其添加(add)与删除(remove)方法入手. ArrayList类定义: p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Monaco } span.s1 { color: #931a68 } public class ArrayList<E> extends AbstractList<E> implements List<E> ArrayList基本属性: /** *

dubbo源码阅读笔记--服务调用时序

上接dubbo源码阅读笔记--暴露服务时序,继续梳理服务调用时序,下图右面红线流程. 整理了调用时序图 分为3步,connect,decode,invoke. 连接 AllChannelHandler.connected(Channel) line: 38 HeartbeatHandler.connected(Channel) line: 47 MultiMessageHandler(AbstractChannelHandlerDelegate).connected(Channel) line: