迭代交付

iteration

迭浪

持续集成与交付(Continuous Integration & Continuous Delivery)已经不是什么新词汇了(以下简称 CICD), 然而在实践过程中, 我们很有可能遗忘了其中隐含的一个前提, 使得CICD落地的效果大打折扣.

正如团队里有人提出如下疑问:

  • 对于一个长流程如何做到持续集成?若一个长流程本身就有几天的开发工作量,那该怎么去做持续集成?
  • 先将流程的框架搭好再逐步去完善其中的细节?那框架应该达到什么程度,若中间涉及到数据存取的使用、依赖,因依赖部件未完成而导致无法集成怎么办?

软件开发长久以来受瀑布式开发模式的影响, 让我们惯性的认为 集成 是在所有组件功能完备以后才开始的. 然而, 迭代式开发打破了瀑布的模式, 强调以增量的方式, 快速交付-验证-改进-再交付, 如此迭代提升效率. CICD 正是在迭代交付的背景下发展出来的.

注意

迭代交付这个前提并非必要的, 即便没有, 实施 CICD 价值仍然是有的. 不过, 若能具备这个前提, 无疑能让 CICD 更早的产生收益.

讲到这里, 关于迭代交付这个概念还是太虚, 不容易理解. 那么就来个栗子🌰吧.

interface FullTextSearchService {
    List<String> search(String keyword);
}

假设, 要实现一个全文查询服务(FullTextSearchService) 如上接口代码. 根据设计其实现会非常复杂, 包括:

从零实现上述特性的话, 怕是要数周不止. 为了能够尽早集成, 我们可以提供一个最小可用交付版本:

class EmptyResultSearchService implements FullTextSearchService {
    @Override
    public List<String> search(String keyword) {
        return Collections.empty();
    }
}

你可能会认为这有什么好测的嘛? 是的, 只有一行代码, 显然不是期望中最终交付的样子, 但就是这样一个最小可用版本, 能够让你的代码编译-打包-部署-运行.

最小可用 版本就是要搭的流程框架的实现程度.

集成测试重点在于验证系统各组件之间的连通性, 即接口的实现是否满足契约. 换句话说, 这个版本的集成测试重点是, search 方法能否调通 .

那是不是说集成测试就不关心逻辑功能了?

不是, 能用集成测试覆盖所有场景那最好, 但那样的话自动化测试的研发投入很可能不亚于功能实现. 相较而言, 采用单元测试来覆盖接口的各种实现情况, 才是投入产出比更高的选择.

以此类推, 随着代码逻辑的叠加, 每次迭代的版本就离最终交付的目标更近, 同时每次的增量变动一旦出现偏差, 即刻在 CICD 中被发现并得以纠正. 比如为了实现分页, 接口需要调整:

interface FullTextSearchService {
    List<String> search(String keyword, int page, int, pageSize);
}

接口的调用方若是同一个工程, 那编译阶段就能发现问题, 这是最幸运的. 然而, 微服务架构下, 调用方很可能是其他人开发的另一个服务, 这样破坏契约的变动越晚发现, 代价就越惨痛.

知易行难啊, 如果你问我, 如何把握每次迭代的最小可用增量? 惭愧, 我没有标准答案, 唯有具体情况具体分析啦, 共勉!

The End.

cicdComment(0)