3个案例,详解如何选择合适的研发模式 | 研发效能提升36计


image.png

策划&编辑|雅纯

上一讲,我们详细介绍了4种常见的分支模式及其优劣对比。本文我们将根据不同的团队场景,分析如何选择适合团队的研发模式。

研发模式选择看什么

image.png

研发模式的选择与产品形态、发布方式、团队规模、协作成熟度密切相关。比如团队规模很小,协作成熟度很高,就直接用主干开发。类似于Web服务端的开发,可以做到持续部署,可以选择GitHub—Flow,或者是TBD。

如果你的团队规模比较大,需要开发的时候做相应的隔离,再看协作的成熟度。如果一般就用GitHub—Flow,成熟度很高就用TBD。

另外,有一些研发场景有固定的发布窗口,它是按版本的方式去发布,我们建议有相应的release分支去做隔离。实际过程中我们应该尽可能地根据自己实际的产品和团队情况,来去制定合适的分支规则。

image.png

举一个例子,这张图的上半部分是需求价值流。可以看到他们开发的时候是一个需求一个需求地做,做完一个需求再做另外一个。因此可以看出这是一种持续交付需求的方式,针对一个需求会做相应的代码变更。

image.png

与之相应的分支模式为GitHub—Flow。

在做特性开发的时候,只要做特性之间的隔离,但是可以做到持续发布,所以直接在Master上发布就好,因为是在Master上去发布,所以不会同时有两个发布存在。

可以看到分支模式和发布流水线之间是强相关的。发布流水线会基于不同的分支来去做相应地事情。比如特性分支发生变化后,针对特性分支做相应的集成和验证,通过后再合到Master上去做集成,完成集成后做相应的SIT(系统级别的验证),然后再做部署。因此分支模式和发布流水线之间是强相关的。

这是持续发布的方式。

image.png

版本制发布模式常见于客户端的发布。例如iOS或安卓,因为有一定的发布节奏。和上面的持续发布模式相比多了一个release分支,用来做版本发布用。从整体来看分支模式比较简单。不建议用Git—Flow,可以对其适当进行裁剪。

研发模式的目的是减少代码协作当中的冲突,减少等待。代码之间的协作冲突有两种,一是开发过程中的冲突和隔离,另一个是发布过程中的隔离,所以组合方式无非是:分支开发和主干发布、分支开发和分支发布、或是主干开发和分支发布等几种。

image.png

这个例子初看和Git—Flow一样,但是相对于Git—Flow,它有两个变化。首先,它没有release分支,它的发布表现为在主干上打Tag。第二,它的hotfix不合回主干,而是直接在hotfix上打Tag进行发布。这样它就少了release分支,少了hotfix和master之间的同步。

整个分支模式有这样一个特点,它有四种分支:feature分支、develop分支、master分支和hotfix分支,其中develop和master是长期分支,feature和hotfix是短期分支。开始开发的时候会拉一个feature分支,合并完成后消亡掉,如果是热修复,会拉一个hotfix分支,hotfix分支永远是从tag上创建的,之后创建tag,分支消亡。

所以长期分支就两个,大部分的情况下hotfix就是feature分支,整个流程比Git—flow简化很多。

分支模式实践案例分析

分支模式是和产品的形态和团队是强相关的,以下是几个实践案例。

1、P2P直播CDN产品

image.png

第一个案例是P2P直播CDN产品,左边是它的架构图,分为一个客户端和一个服务端。客户端是有多端的,比如手机、路由器、机顶盒等,每一种端的发布形式是不一样的。终端,客户端和服务端之间有两条通信链路,一条是视频数据的链路,另外一条是控制数据的链路。

服务端包括了三部分,控制面、用户面,和数据、运营、监控等服务。每一块都包含多个具体的应用。团队成员物理上在一起,协作紧密,工程能力还可以,有单元测试和功能自动化保证,基本上可以做到比较快的测试反馈。

它有两种应用:一个是服务端应用。一般golang、C++都是通过源码级构建依赖,运行时依赖配置中心,共30个左右应用,一次发布一个应用,每个应用是独立发布,所以不存在发布的依赖性和编排问题。

另外一个是客户端,一个代码多端构建,无运行依赖,有的可以热更新,有的需要通过应用市场发布,比如说iOS,所以发布频率不太一样,会导致长期有多个版本存在。那么,怎样针对服务端和客户端去做研发模式的设计?

image.png

首先看服务端,服务端是一个看上去比TBD还简单的模式,因为人很少,服务拆得足够小,几乎每个服务同时只有1—2个人在修改。这样的情况下就没必要再用release分支,直接在主干上开发。基本上一个服务一个库,而且这个服务拆得粒度很小,平均一个人大概是3、4个应用,这个服务是很小的。

这样的情况下,它会有一些自己的纪律,比如因为要保证多端和客户端多版本,代码需要保证向前兼容,同时代码是直接Push在Master分支上的,不存在合并等问题。在Master上一旦代码提交会有对应的测试,如果测试失败,提交者需要在一小时内修复。在Master上创建Tag即会视为一次发布。

如果出现问题,在最新代码上修复,永远发布最新的版本。这就是服务端的流水线,所以如果有类似的团队建议可以尝试一下,基本上来说如果做好纪律,可以做到很高效地发布。

image.png

客户端基本上就是TBD的模式。平时还是主干开发,代码在主干上集成,但是要发布的时候会拉一个release分支,因为客户端的发布和升级比较困难,需要做足够多的发布前验证,这个情况下就需要release分支去保护。同时因为它会同时存在多个版本,所以需要在release分支上做bugfix。

但是,release分支还是要保持活跃数尽量地少,所以一般只关注最新的活跃的release分支。这样TBD是一个非常合适的模式,针对发布它会做隔离,另外,因为一个版本需要保持一定时间的维护,所以需要一个相对长期的release分支。

2、基础网络产品

image.png

它是在软件层面做的虚拟化网络产品,很多外部做一些底层产品的公司会遇到这样类似的产品。整个产品研发50人左右,分为5个团队,每个团队大概10个人。团队间协作需求很高,一般都是一起发布、一起集成,但开发的时候是很多人一起开发的。

整个团队工程能力中等,有单元测试但是没有其他测试的保护,后面的测试主要是靠具体的环境去测,开发的语言是C+和C++为主,部署到物理机或者虚拟机上。应用是一份代码,多端构建,需要应对多种的硬件和操作系统,底层依赖Hypervisor和硬件。部署时可能需要停机,因为网络问题不是总能做到热更新,一次部署一个应用,发布顺序有要求。

如果有多个应用,应用间的发布有编排顺序,它的发布周期很长,通常几个月发布一次,同时会存在两个都在发布的版本,比如一个版本发布了80%,另外一个版本发布了10%。

image.png

这个产品的release分支会更长,它的版本需要固定下来,要有明确的Tag。所以Master不能直接提交,永远指向最后一个已发布的版本,但是整个开发其实是拉release去做,这个release可能会比较久。

在这边做完以后,在release做完整的测试和评审然后发布,完成后合进Master。这个类似于项目制,一个release相当于一个项目,从Master上创建出来以后,所有的开发和发布的工作都在这个release分支上,这个release分支就相当于项目的版本。发布完后release分支进入维护阶段。Master在这里是作为一个稳定基线来管理的。

3、金融安全产品

image.png

这个产品一份代码提供两种交付形态,包括SaaS和私有化交付形态。整个应用架构比较简单,包含一些后台服务和API入口,以及一个管理和配置用的控制台。后台服务里面API会调很多其他的服务,比如设备指纹、指标计算、数据服务等。
这是典型的大数据场景,包括很多人工智能的产品都是类似的架构。整个团队在150人左右,它的特点是前端、算法、后端、测试都有专门的职能团队,但是没有运维。

团队之间通常需要协作才能完成一个要求,一般来说不会有一个需求落在某一个团队,工程能力一般,没有单元测试和自动化功能测试的守护,基本上是靠后续的人工测试来去保证质量。

整个技术栈是以Java为主,K8s部署方式。另一个特点是二方包依赖较多,snapshot和release版本都有。运行时应用间有较强依赖,比如在API依赖了设备指纹,API依赖了指标计算,类似这样的依赖其实很多。

整个应用数大概是20个,一个应用很多人协作,一次发布往往是一组应用或者是一个应用,SaaS版本落后私有化版本较多。

image.png

它和Git—Flow有点类似,区别是没有Develop分支,release分支用来做了临时的集成分支,Master是发布分支,永远指向最新可发布版本。

作为私有化产品,有固定的版本节奏,一般一个月发布一个版本,于是每个月会拉一个release分支来做这个月的Feature分支的集成。集成完以后会合回Master去做发布,发布完打一个Tag。

所以在这里的release分支相当于一个迭代分支。整个测试是比较长的周期,同时也要维护多个版本,因此会有多个并行的release分支存在。

通过这几个例子可以发现,我们需要根据团队和产品的特征来确定它的分支模式。在这些分支模式里面,我们都尽可能地减少分支,让分支的维护成本低一点,因为每多一个分支意味着多一份维护成本。

除此以外,还有一些其他的场景,比如集成过程中,集成进去以后发现集成分支出现问题,需要把相应地代码摘出来。很多的Feature分支合在一起,合并进去以后想再摘出来就很难。这种情况其实也可以用分支,比如临时的集成分支解决。阿里内部的研发工具Aone,有一个分支模式叫Aoneflow,就可以解决相应的问题。

很多时候分支是可以很灵活地去使用的,但是灵活使用也会给程序员带来特别多理解和维护成本。我们的建议是分支越简单越好,另外尽可能地减少程序员的关注度,只关注在自己开发的分支上就好。这里给出几点建议:

  • 单主干:一个代码仓库应该保证有且仅有一个主干分支。像Gitflow里面Develop和Master就比较迷惑。
  • 最少长期分支:避免冲突的前提下,尽量减少长期分支的数量
  • Promotion(晋级):代码的提交应该是逐级合并,如Feature–Develop-Master,是逐步地Promotion的过程。
  • 发布不可变:发布的版本是不可变且可回溯的,可以根据Commit来追溯到你最早的源头。
  • 自动化事件触发:分支的持续集成过程应该是自动化的,且通过代码提交事件或制品变更事件自动触发。

总结

  • 团队研发本质上是一个异步的、延迟协作的过程,随着产品复杂度和团队复杂度的增加,协作成本快速上升。
  • 研发模式的本质是为高效交付需求,研发团队围绕代码库的一系列行为约束。
  • 通过分支进行隔离,避免冲突;通过小批量频繁提交,减少等待。
  • 控制分支需要考虑最大化生产力及最小化风险。
  • 分支的选择需要综合团队规模、协作成熟度、产品交付形态几个要素。

下一讲,我们将进入可信发布篇,敬请期待。

image.png

点击下方链接,免费体验云效流水线Flow。

https://www.aliyun.com/product/yunxiao/flow?channel=yy_rccb_36

image.png