技术开发 频道

软件开发管理中的非常好的实践——日构建和持续集成

    根据经验,流程自动化会涉及到几个难点:

    第一, 如何解决编译的粒度和循环依赖问题

    对于一个小项目来说,整个项目参与构建的工件可以作为一个整体一起拉下来,然后编译。如果编译未过,则日构建整体不成功。典型的例子是建立一个src目录,不同开发人员共享,所有的原代码都放在该目录中。

    对于大项目而言(尤其是产品),由于有上百人参与开发,可能需要按照项目模块来组织开发,这个时候会存在多个src目录。在日构建中可以处理成把所有的src下拉后重定位到一个src目录进行编译。这种方案称之为大编译。但是这种方案有个问题,就是一旦某个模块的代码编译不能通过,整个大项目的编译也不能通过,除非这个编译问题能够立即解决,否则这将影响那些正常模块的日构建输出和功能验证。尽管大家会认为,编译问题自然要立即解决。但是这也就成为了影响日构建自动化的一个障碍因素。实际上,编译只是影响成功构建并达到可测试状态的其中一个影响因素,比如还可能存在元数据发布不成功,EJB部署不成功等。有些因素一旦发生,可能在某种特殊情形都无法短时间解决,这个时候就凸现了下面一种方案的价值。

    这种方案的基本原则是每个模块单独编译,元数据单独发布。但是这种方案要面临如何解决模块之间编译存在依赖链,并且这个链还可能成为环状的问题。这里与大家分享的一种做法是先执行前面提到的大编译,然后再把大编译的输出作为其它各项目编译的输入。这样可以解决编译次序和循环依赖问题,同时可以真实判断出哪些模块成功,那些模块存在问题,为针对单个模块的报告提供依据。

    实践中,曾经把一个三百号人同时开发的项目分成了60个模块。在项目的初期,一般情况都会存在个别模块在当日日构建无法编译通过,并且可能出现特殊因素无法立即解决。但是采用上述方案,这个有问题的模块并不会在编译期间过大制约其它多数模块的正常编译通过。

    第二, 对于大型项目如何控制个别子模块不成功对全局的影响

    一般来说,模块之间存在依赖关系,比如Module C依赖Module B,Module B又依赖Module A ,那么是否只要Module A 出现了编译问题,Module B和Module C在当日日构建就都不输出呢?这是个局部影响全局的问题。作者建议的做法是,Module A本次提交如果出现了问题,立即进行修复,特殊情况不能修复,由日构建系统自动根据编译结果判断启用Module A历史上最近一次成功版本。

    有人可能会问,这种做法是否存在潜在的问题呢?的确存在,但是这个问题并不会想象的那么可怕。只要不是接口变更,一般来说ModuleA的问题只会影响ModuleA的内部,而不会殃及依赖ModuleA的其它模块。如果ModuleA 在今日日构建做了一个接口的变更,而且这个变更也通知了依赖它的Module B,并让Module B做修改,那么启用Module A的最近历史版本的确会存在运行时接口对不上的错误。对于这样的情况,可以不予处理,等待这个问题下一次日构建再解决。最好的办法是管理上要求当日必须解决ModuleA的编译问题。

    上面提到的方法在几百人协同开发, 需求验证和测试并行的场景中经过检验,值得推广。

    第三, 复杂环境的部署

    可能很多人会认为解决了编译问题就解决了日构建的主要问题。其实不然,日构建的目的是产生一个最新的、可供测试的输出。为了达成可供测试的输出,首先日构建就要尽可能进行自测,以期在把工作产品传递到测试服务器、测试人员那里之前发现尽可能多问题。比如整个产品是否可以稳定的在应用服务器中加载?系统的登陆模块是否可以正常工作?菜单项是否可以正常读取?

    基于上述目的,日构建自身要配置出可以运行的实际环境以便运行验证测试程序。比如BVT(Build Verification Test)。对于J2EE的应用,大家都知道,视产品部署模型的复杂程度这项任务可以变得非常复杂。

    环境部署问题没有可供借鉴的统一模式。以笔者个人的经验,在日构建系统设计上如果能够做到与开发人员、测试服务器的部署模型和部署位置保持一致,就会大大减少环境的复杂性,这个时候可能需要人为制造一种统一环境。

    1.2.3报告机制

    日构建在某种程度相当于对项目的一次“体检”,体检的结果自然需要公而告之。公告的目的,是以实际数据来说明问题,并让项目中的所有成员对项目的现状达成一致的理解,并且这种理解是充满说服力的。日构建的报告可以做得很简单,也可以做得很复杂。形式可以采用web发布,也可以采用邮件通知。从项目管理的意义上,建议在这方面投入尽可能的精力和资源。

    以本人参与设计的一个日构建系统为例,其日构建报告为web形式,左右结构框架。左边显示日期,点击该日期后在右边显示具体的报告。其内容包括:

    按模块进行报告,报告中显示该模块的责任人(一般是负责该模块的项目经理)

    每个模块的编译情况。通过或者未通过。未通过则显示编译出错信息。

    每个模块的部署情况。成功或者失败。失败则显示失败的信息。

    每个模块的单元测试数目,单元测试的时间,成功率,代码测试覆盖统计。

    每个模块的代码行数,EJB数目,各种元数据的分类统计

    每个模块代码的风格审计,异常处理审计,命名规范审计,多语言处理审计等等

    1.2.4为日构建定制的管理制度

    专门的日构建设备

    必须为项目安排专门的日构建机器,而且要求机器的处理性能较高,内存容量较大。以目前PC机的处理性能,150人以下的项目,实践证明足以胜任。

    日构建的时间管理

    日构建并非每日仅构建一次。从持续构建的意义来看,日构建可以根据需要每日进行很多次。什么时候进行日构建?每日进行几次日构建非常好的?没有非常好的答案。但是根据经验,在项目的初期,由于功能大批量加入,测试还未全面开展,每日进行一次构建就可以了。随着、项目的推进,系统功能逐渐丰满、稳定,测试和需求验证也逐渐铺开,这时每日可以进行两次或者更多次构建。

    日构建的时机一般放在下午为佳,如果个别模块发生问题,相关人员可以利用晚上的时间留守解决。微软的日构建发生在凌晨,并且自动调度执行。开发人员次日来了关心的头一件事就是自己负责的模块是否在日构建中进行得顺利。

    午餐休息时间也是进行日构建的另一种选择。对于不大的项目,一个小时一般可以产生出日构建报告,这样开发人员中餐回来后可以直接对问题进行处理。而测试人员和需求人员也可以对上午的工作产品进行验证和测试。

    日构建轮值

    尽管完善的日构建是通过调度自动运行,但是对于应付一些突发的事件,比如产品临时检查,开发人员对发现的重大bug的立即修复等,可能需要人为调度日构建运行。原则上建议为日构建设立一位管理员。由于事物处理并非持续,这个管理员兼职即可。

    建议采取管理员在各部门或者项目组轮值的方式。这样有利于加强各方对日构建的重要性和处理过程形成共同的认识,同时也可以避免出现关键人物依赖。

    接口变更管理

    不同模块之间可能存在接口的依赖,这里的接口并非专指程序语言中的Interface,而是包括所有在编译和运行期间依赖的部件。接口的变更管理是开发管理过程中的一个难点,尽管从软件设计角度,我们可以采取一些方法来尽量避免这种依赖,比如接口倒置,接口分离;但是在现实中,这个问题难以回避。持续集成的一个主要目的就是解决这个问题。

    接口的变更原则上要求变更方主动通知接口依赖方,并且要求在日构建之前提前通知,以便接口依赖方有时间在集成编译之前能够调整原来调用老接口的代码。很多时候,这需要借助管理来保证。实际情况中,无法避免接口依赖方没有收到接口变更通知、或者来不及调整接口的情况,这种情况一旦发生,便导致接口依赖方的代码编译无法通过,问题就出来了。问题需要尽量避免,但是其产生并不可怕,这正体现了持续集成的作用——让问题尽早暴露。

    日构建中的行为规范

    日构建是一天中所有工作产品的一次集中验证,并且输出的组件要影响第二天的所有工作,为了保证其正确进行,需要规范参与者的行为。这种行为规范包括开发人员在日构建前的准备工作,日构建后的响应和处理。为了便于大家理解,这里以举例的形式介绍给大家。比如:

    1. 开发人员在日构建前15分钟必须提交代码,以保证处理流程可以下拉正确的代码。

0
相关文章