跨团队的持续集成: 几个基本矛盾

 

单个团队内部的持续集成已经是成熟的实践. 跨团队的集成则碰到了很多问题, 包括全部测试运行时间过长, 合并成本高等问题. 针对这些问题有一些对应的解决方案, 如合理的分支策略, 分层的集成等.

这里想讨论一下几个基本的矛盾, 和理想中的解决方案

 

1. 并行开发 与 集成 之间的矛盾

这是本质问题, 如果所有功能都是由单一开发者循序渐进的完成, 则集成并不是大问题. 由于团队内部的集成已经有大量成熟的实践, 因此前面的假设可以修改为"如果所有功能都是由同一团队循序渐进的完成, 则集成并不是大问题". 这就为我们指出了一条思路: 如果把需要集成的部分, 都分配给一个团队去完成, 则会大大降低集成的难度

传统的大规模开发中, 往往按照模块划分开发团队, 比如做UI的, 做网络通信的, 做数据库访问的, 做协议的, 等等, 分别是不同的团队. 而最终为了完成某个用户可见的功能, 需要协调多个团队并行开发, 然后联调, 也就是集成. 也就是说, 是特性需要集成. 如果我们按照特性来划分团队, 则跨团队的集成将转化为团队内部的集成. 而团队内部的集成已经有大量成熟的实践

关于特性团队和模块团队的全面比较, 请参见<<Choose Feature Teams over Component Teams for Agility >>

降低集成成本的第一原则就是避免集成 . 减少跨团队的集成需求, 如果不能避免的话.

按照特性划分团队将带来一个明显的问题, 就是不同的团队的人可能会同时修改同一个文件, 同一个类, 同一个函数等, 因为不同特性都会涉及同样的功能模块, 如数据库/网络/UI等. 这就是我们想讨论的第二个问题

 

2. 基于文本的合并 与 基于语意的逻辑 之间的矛盾

现代的版本控制系统都能够智能的自动合并文本. 然而合并后的结果是不是我们所期待的, 则只能通过测试来验证. 并且对文本级别的冲突, 版本控制工具也无能为力, 还需要开发人员人工干预. 因此降低合并成本第一原则, 就是避免合并 . 这里有几种可能的方式来达到这一目标:

  1. 基于插件的架构: 这是开闭原则 的应用. 增加任何新的功能, 都不需要修改原来的文件或类, 而是增加文件或类. 一个大尺度的例子就是Eclipse生态系统. 如果把Eclipse的各个插件都看作某个完整系统的所需要的特性的话, 那么这些特性的开发者有的甚至从未谋面, 开发过程中也不需要跟其它插件进行代码级别的集成, 然而最后它们却能和谐的一起工作

  2. 基于小文件/小类/小函数的代码组织: 这是单一职责原则 的应用, "小"只是代码外在的表现, 真正的含义是职责单一. 以C语言为例, 考虑两种极端的情况. 一个极端是把整个系统都写在main函数里, 另一个极端是每个独立的功能都写成独立的函数, 并且每个文件只包含一个函数. 前一种情况下, 任何团队的任何改动, 都需要跟其它团队的改动做合并, 并通过全面测试验证合并后的行为. 后一种情况下, 合并的需求大大降低, 只有大家改同一个函数的时候才需要, 并且测试也可以以影响范围为边界进行测试. 当然我们不需要像后一种情况这样极端, 但至少指明了前进的方向.

这类方案也带来一个问题, 就是我们无法一开始就设计出如此良好的插件体系, 只能是一个演进的架构. 这期间, 插件和框架之间的接口变化将为集成带来挑战, 即旧客户与新服务如何和平相处. 这就是我们要讨论的第三个问题

 

3. 依赖的稳定性 与 依赖自身的演进升级 之间的矛盾

这是一个普遍问题, 尤其对分层的体系结构, 或者"平台 + 产品"模式的开发项目.

在理想情况下, 所谓的理想情况是指, 代码集体所有制/IDE完善的重构功能/全面的自动化测试用例/等, 公共API的变化所引起的客户代码的修改, 都可以由某个团队一次性的完成并提交. 然而在大量的大规模遗留项目中这是不可能的. 这种情况下, "第三方代码线"(参见<<Software Configuration Management Patterns >>)或者"变更控制修改组"(负责实现变更, 并提交)是可选的解决方案

这里想讨论一下另外一个大尺度上相对更通用的但会引入管理成本并有点风险的解决方案, 就是向后兼容, 或者Versioning . 几个例子:

  1. 我们知道COM技术就是为了解决DLL版本地狱的问题. COM组件的新版本与老客户能和平相处, 是因为COM组件的升级, 并不是直接修改老接口, 而是增加新接口, 保留老接口, 并提供能力查询接口, 这样新老客户都能各取所需. 这是"扩展对象"模式的一种应用. 这是API级别的向后兼容/Versioning

  2. Subversion. Subversion的客户端可以和服务器协商版本, 从而选择一种大家都理解的协议. 这是协议级别的向后兼容/Versioning

  3. XML. XML本身就是为扩展设计的, 可用于实现向后兼容的消息/协议等.

但实际的项目中, 我们并不希望老客户和老API/老协议长期存在, 而是希望把老API/老协议删掉, 所有老客户全部使用新的API和协议. 因此我们需要阶段性的应用上述方案, 并提供管理或者技术手段, 在过渡时期结束的时候, 确保新老交替已全部完成. 这里的风险就是, 混乱无力的管理一旦允许老接口存在一段时期, 它就会一直存在

 

以上三个问题在大规模遗留系统中很难得到彻底解决, 因此这几种解决方案或思路, 仅仅希望在开发新系统时能够提供一些考虑的因素

 

 

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页