一句话,道破了改进难点所在。近在项目中围绕持续集成做改进的时候,对这一点感受颇深。跌跌撞撞的一路走来。我们的持续集成的过程已经变得有些“个性化”,反过头来看我们一路的变化,非常有意思。

  相关厂商内容

  百度开发者大会:Web App设计、移动互联网应用、个性化推荐、敏捷(3月23日 免费报名中!)

  Visual Studio 11 Beta 和 .NET Framework 4.5 Beta版免费下载中!

  更高效的SCRUM:微软认证项目管理培训课程

  软件研发,不仅仅是持续集成

  从项目的技术架构说起,我们的项目是采用的J2EE+Flex的方式进行开发的。在我进入项目组的时候,一个比较健壮的持续集成环境已经搭好了。工程分为两个,一个是Java后端的工程,一个是Flex前端的。我们的持续集成服务器是CC。整个开发工作是围绕着持续集成展开的。一周为一个迭代。

  那个时候,我们采用的是比较标准的方式:

  后台采取TDD的方式开发。

  每次提交代码之前更新所有代码,然后运行所有测试用例,全部为绿色的时候才提交。

  前台Flex比较麻烦,所以采取了用功能测试覆盖单元测试的方式。用基于Ruby的FunFx写单元测试。工作方式与后台差不多,每次前台功能测试全部通过了才提交。

  持续集成的流程是每隔5分钟检测一边代码库,有更新build。

  build的流程是先编译后台,跑单元测试,单元测试通过了,再编译Flex,将swf和html以及后台的文件打成war包,部署到tomcat上去,跑功能测试。

  build成功之后发布到特定的目录,形成发布列表。有war包供人下载。

  那个时候,build一次大概是15分钟,因为Check In Gate环节是按照标准流程走的,所以build出错的几率也小。CC大多数时候是绿的。哪怕偶尔出问题红了,也很快被修正了。

  随着项目的开发,代码规模越来越庞大。功能测试越来越慢,比起自动执行脚本那种速度,开发人员更乐意手动点两下,加之上面对工作进度的压力。更改了一些工作方式:

  后台的工作方式不变。

  前台,将功能测试脚本的工作交给几个测试人员编写。几个测试人员也坐在附近,基本可以看作团队成员(到后来编制也是我们团队的成员了)。 开发人员人肉测试一下保证没有问题了再提交。

  测试人员写脚本的流程是拿到上一次build成功的war包,在本地写脚本,本地测通过了再提交。

  持续集成服务器上功能测试不通过的时候,由测试人员提交BUG,开发人员修改。

  持续集成的流程依旧。

  这样,从后来收集的数据看,工作效率是提升了,因为参照以前的统计,开发人员的工作一下减轻了1/3。以进度来衡量的速度自然很轻易可以让上级满意了。

  新的解决方案总会产生新的问题,测试人员在测试方面的专业性,使得他们写出来的脚本测的更细。功能测试的时间占耗,增长的更快了,短短半个月,增长到了1个小时。每当出现问题,作出反应之后,要在1个多小时以后才能知道结果。而且,持续集成方面并没有做到,一旦出错,谁也不能提交代码这么严格。模块化的设计所带来的假想的安全感和进度的压力,使得开发人员修正问题的激励不高。于是修正问题的速度不如产生问题的速度快。持续集成服务器在那两个个礼拜里只有两头是绿的,周一早晨和周五下午。


  早,我们发觉,由开发人员重构造成的脚本失败占大多数,而测试人员每次拿到的上一个版本是没有错误的。所以会出现自动化脚本本地跑得过,服务器上跑不过的情况发生。于是我们修改了发布的逻辑,在后台单元测试通过、flash编译完成的情况下打的那个war包,复制一份,放到某特定目录指定为current build。供测试人员写测脚本使用。

  过程改进之后,测试人员可以快速的修正脚本了,虽然对于开发人员重构造成测试人员工作的返工无疑是一种浪费,但是毕竟自动化的测试省了回归测试的不少时间,还是可以接受。

  脚本的修正速度解决之后,工作似乎有了些起色,但很快,问题的本质暴露了出来--build的时间太长了,修得速度还是跟不上问题产生的速度。尤其是中间缺少当build失败时强制阻止代码提交的环节。这之后依然是周一和周五两头绿,中间都是红的。于是,我们觉得问题还是出在build速度上。我们人工的将功能测试脚本分到四个suite里去,然后以多线程的方式运行。速度被提高了4倍。于是又消停了两天。


  好景不长,多线程的测试似乎不太稳定。很多本地可以跑通的测试用例,到了服务器上失败。险些一个礼拜都没有build出一个版本。后不得不改回单线程。这时,build一次已经占到了100分钟。第一期的产品Backlog还没有完成1/3。

  持续集成走到这里已经进入一个困境,有必要做一些更深一步的改进。经过多次讨论,归纳出了几套方案:

  分冒烟测试和all test两套测试用例集是我们当中呼声高的一种方案,当我的代码提交之后在跑完所有单元测试和基本的冒烟测试之后发布beta版,由测试人员接到beta版,进行更细致的自动化测试并带一些人肉测试。但是反对的声音认为,不跑完全部的测试用例失去了持续集成的意义。而且会更降低开发人员修正 Bug的积极性。于是作为修正,支持的声音则提出,在Check-In Gate处把关,恢复每个人提交代码之前跑测试用例的实践。可这明显会给开发人员带来更大的工作负担,估计以此时的进度压力,开发人员的安全感肯定会大幅下降。很可能会推行不下去。

  另一个方案是从细节处调优,把WEB应用部署到另外一台机器上去,或许会稳定一些了。但是反对的声音认为,以测试用例的这个增长速度,他早晚会不稳定的,而且可能撑不过两周。作为修正,想考虑分布式,但是我们所有人的知识储备中,并没有一个人清楚CC有没有分布式能力。所以想的是购买Cruise,但是价格的障碍摆在眼前了,在项目前景还不是很明朗的情况下,估计很难申请到资金,但也不是不可能,只要我们敢于冒这个风险。

  第三,便是更为高级的分支式开发,将版本库划分一下分支,以分支来搭配持续集成,以分支合并来触发自动构建。这样做,开发的过程更加有板有眼,粒度可以划分的更细。可是分支的划分,一时想不清楚。但是假设想清楚了,似乎这也使得我们的工作流程更加复杂了,做如此之大的改变,风险有多大?效果有多大?成本有多大?到底是值不值得?一时也想不清楚。