摘要:阿里的研发团队众多,不同业务部门使用的发布流程和分支策略并不统一,但总体上还是比较有规律的。其中有一个主流的发布模式和对应的分支使用方式,叫做“AoneFlow”。这套工作模式在思维上独树一帜,在阿里以外的地方并不多见。本文着重介绍这些实践,并就分支机构管理这一话题展开讨论。Introduction阿里内部流行很多有趣的工程实践。有些做法是通过工具和流程嵌入到群体的大环境中,不易被外界复制,而有些做法则是流露在每个人的日常习惯中,默默地遵循着。比如分支管理这件事,其实是一种工具,一种习惯,颇有阿里的特色,举个例子很合适。阿里有很多研发团队,不同业务部门使用的发布流程和分支策略不统一,但总体来说还是比较有规律的。其中有一个主流的发布模式和对应的分支使用方式,叫做“AoneFlow”。这套工作模式在思维上独树一帜,在阿里以外的地方并不多见。本文着重介绍这些实践,并就分支机构管理这一话题展开讨论。分支模式详解说到分支管理模式,我们最熟悉的就是TrunkBased和GitFlow。TrunkBased模式是持续集成倡导的工作??方式。它由一个主干分支和许多发布分支组成。每个发布分支都是在特定版本的提交点从主干创建的,用于在线部署和Hotfix。在TrunkBased模式下,没有明确的功能分支。当然,Git的分布式特性天生就是让每个人都有一个本地分支,TrunkBased也不排除短期特性分支的存在,但在谈到这种模式时,人们通常不会明确强调。虽然这几年有很多很好的例子,但是TrunkBased模型并没有一统天下。它的缺点是显而易见的。太多的团队同时在主干上工作,发布时可能会发生灾难(尤其是在多版本并行开发的情况下)。补救措施是FeatureToggle,频繁的集成和足够的测试覆盖,对开发团队的能力提出了比较高的要求。目前TrunkBased模式主要用于不需要同时维护多个历史版本的SaaS项目,尤其是经过微服务改造的各种小服务。TrunkBased模式有两种常见的演变。OneFlow模式参考了TrunkBased的很多思想,更加严格的定义了操作流程,增加了hotfix分支等内容。多主干模式(通常是双主干、固定开发分支和固定发布分支)是TrunkBased使用固定发布分支的特例。在提升团队微服务实施能力一文中有介绍,这里不再赘述。GitFlow模式是几种模式的集大成者,包括一个主分支,一个开发分支,很多特性分支,很多发布分支和hotfix分支,还有很多繁琐的合并规则。它有一个Git插件,但它早已不复存在了。由于每个阶段的每个操作都有明确的定义,曾经在很多看重流程的公司眼里是个香饽饽。但是使用起来不是很方便,大量的merge冲突和对集成测试不友好也是被诟病最多的地方。是的,还有GithubFlow模式,不过这个策略无非就是在TrunkBased的基础上增加了个人仓库和PullRequests之间合并代码的操作,类似于在同一个仓库中添加个人分支。从实用的角度来说,更适合分布式团队。GithubFlow也有演进,例如强调多环境部署和将存储库或分支与环境相关联的GitlabFlow模式。要么像TrunkBased那样简单粗暴,要么像GitFlow那样繁琐复杂。真的没有别的选择了吗?AoneFlow的另一种方式在AoneFlow上,你可以看到很多其他分支模式的影子。它基本上兼顾了TrunkBased“易于持续集成”和GitFlow“易于管理需求”的特点,同时避免了GitFlow的繁文缛节。看具体套路。AoneFlow只使用三种分支类型:trunk分支、feature分支、release分支,以及三种基本规则。第一条规则,在开始工作之前从主干创建功能分支。AoneFlow的feature分支基本上是借鉴了GitFlow,并没有什么特别之处。每当你开始一个新的工作项(比如一个新的特性或一个未解决的问题)时,从代表最新发布版本的主干创建一个特性分支,通常以feature/前缀命名,然后在这个分支上提交代码更改。也就是说,每个工作项(可以由一个人完成,也可以由多人协作完成)对应一个特性分支,所有的修改不允许直接提交到trunk。第二条规则是通过合并功能分支形成发布分支。AoneFlow的release分支设计的非常巧妙,可谓是整个系统的精华所在。GitFlow首先将完成的特性分支合并回公共主线(即开发分支),然后从公共主线上拉取发布分支。TrunkBased也是在trunk分支上等待所有需要的特性开发完成,然后从trunk分支上的特定位置拉取release分支。AoneFlow的思路是从trunk中拉出一个新的分支,合并所有需要集成或者发布的feature分支,从而得到release分支。发布分支通常以release/前缀命名。这个规则很简单,但实际玩法却相当丰富。首先,发布分支的目的可以是灵活的。基本玩法是将每个release分支对应到一个特定的环境,比如release/test分支对应部署测试环境,release/prod分支对应线上正式环境等,并结合pipeline工具来实现对接各环境中的代码质量扫描和自动化测试检查点,输出部署包直接发布到对应环境。进阶的玩法是将一个发布分支映射到多个环境,比如将灰度发布和正式发布串在一起,中间加入人工验收步骤。对于进阶玩法,如果按照迭代计划链接特性分支,创建一个迭代演化的固定发布分支,然后在这个发布分支的流水线上串起一系列环境,有点像经典的持续集成流水线.或者做一个release分支,把所有feature分支关联在一起,专门用于所有提交的集成测试,就可以起到TrunkBased的效果。当然,这些花哨的进阶玩法都是我的想象,阿里的发布分支总体上还是中规中矩的。其次,发布分支的特性构成是动态的,调整起来特别容易。在一些市场瞬息万变的互联网公司,采用“敏捷运营”的乙方公司经常会遇到这种情况,已经完成等待上线的需求可能是市场策略调整或甲方的临时决定随时。该功能突然需要延迟发布或根本不需要。或者在某个特性上线前发现存在严重的开发问题,需要淘汰。按照惯例,这时候需要手动“取回代码”,将已经合并到开发分支或主分支的相关提交一一移除。做过的同学都知道,很麻烦。在AoneFlow模式下,重建release分支只是分分钟的事。删除原来的release分支,从trunk中拉取一个新的同名release分支,然后合并需要保留的feature分支。这一系列动作可以在很大程度上实现自动化,不会在仓库里留下一堆拆码记录,干净无污染。另外,发布分支是松耦合的,这样可以有多个集成环境进行不同特性组合的集成测试,也方便管理每个特性部署到不同环境的时机。松耦合并不意味着没有相关性。由于测试环境、集成环境、预发布环境、灰度环境、上线正式环境的发布流程通常是依次进行的,所以流程中可以只需要上一个环境验证过的特性,可以传递到下一个环境进行部署,形成一个漏斗状的特性发布流程。阿里有一个统一的平台来自动化发布分支之间特性组合的迁移,这在下面的工具部分会介绍。规则三:发布到线上官方环境后,将对应的release分支合并到trunk中,在trunk中添加tag,删除release分支关联的feature分支。当一个发布分支上的流水线完成了线上正式环境的部署,就意味着相应的功能真正发布了。这时候应该将release分支合并到trunk中。为了避免在代码仓库中积累大量的历史特性分支,一些已经上线的特性分支也要进行清理。与GitFlow类似,主分支上的最新版本始终与在线版本保持一致。如果想回到历史版本,只需要在主分支上找到对应的版本标签即可。除了基本规则外,在实际操作中还有一些不成文的技巧。比如Hotfix上线后正常的处理方式应该是新建一个对应上线环境的release分支(相当于Hotfix分支),并为这个分支创建一个临时的pipeline,以保证必要的发布前检查和smoketest可以自动执行。但其实还有一个简单的方法,就是把线上官方环境对应的release分支关联的feature分支全部清空,直接在这个release分支上修改,修改后使用已有的pipeline自动发布。如果您必须修复历史版本中的错误怎么办?然后老老实实在主分支上找到版本标签的位置,然后从那个位置创建一个Hotfix分支。但是,由于阿里的大部分产品都是在线SaaS业务,这样的场景并不多见。正是这些简单的规则,构成了AoneFlow独有的核心套路。AoneFlow中每一个看似简单的步骤都不是凭空捏造的,而是经过大量产品团队反复磨练后积累的经验。接下来说说AoneFlow的技术门槛和阿里内部的应对。AoneFlow的体验优化熟悉武术的人都知道,想要掌握一门门派的看家武术,除了招式要懂之外,还必须要有深厚的内功和得心应手的兵器。不然拿到邪剑谱,也只能看着谱子叹气了。阿里团队的内功和武器,其实就是良好的编码习惯和完备的配套工具。这里所说的习惯,除了开发流程和代码分支管理之外,还包括日常开发中的一些约定。阿里的很多开发规定都记录在“文件”里,主要是《阿里巴巴 Java 开发手册》。它的内容现在是公开的,所以它不再是秘密。举一个具体的例子。在AoneFlow的过程中,每次release分支rebuild时,代码都会重新合并,然后编译生成新的部署包。但是,即使代码内容相同,如果项目依赖了一些可能发生变化的第三方软件包,那么打包后的产品行为仍然可能不一致。因此在阿里的代码规范中,明确指出线上发布的代码不能使用包含“SNAPSHOT版本”(即未正式发布的版本)的依赖包,以保证每一个构建的产品是一致的。是一致的。像这样的细节还有很多,良好的开发习惯是保证软件质量的必要前提。工具可以使团队协作更加顺畅。虽然只要理解了原理,AoneFlow中每个分支的创建、合并、变更步骤都可以用简单的Git命令玩转。但是,其中一些操作(例如为每个要合并的发布分支选择正确的功能分支组合)手动执行时极易出错,让团队中的个人重复这些日常琐事并不是一件令人愉快的事情。在阿里巴巴内部,使用AoneFlow流程的团队基本上不需要运行Git来处理分支,而是由阿里巴巴集团内部一个名为Aone的协同研发平台(以下简称平台)接管。这个平台承担了集团80%的产品开发流程,从需求和用户故事到部署上线,内置了很多以服务组件形式嵌入的研发效率提升工具,发布组件为用户增色不少AoneFlow的经验。比较显着的辅助“功效”包括以下几个方面。首先是整个过程的自动化。作为内部工具,该平台的功能具有高度凝聚力。对于项目,从提出原始需求,拆分需求到任务,根据任务在线创建特性分支,再聚合生成发布分支,根据模板自动创建测试环境,直到后期运维保证可以一站式搞定。这个过程远远超出了代码分支管理。但也正因如此,对于AoneFlow,平台将特征分支与需求项向前关联,保证了特征分支的命名规范;向后,将发布分支与部署行为相关联,确保每个环境版本源的可靠性。打通了Ren和Du的端到端交付。其次是发布分支的管道。作为流程自动化的一种手段,CI/CD管道是许多现代交付团队中的常见标准做法。AoneFlow的代码生命周期涉及到很多分支。当这些分支被创建或更新时,往往需要伴随着一系列的其他行为。管道将日常开发过程中的这些代码分支连接到它们表达的潜在意图(例如提交代码以运行集成测试)。特别是发布分支。AoneFlow的每个发布分支通常都与特定的部署环境相关联。当有新代码合并到分支时,要及时对代码进行检查和部署。理想情况下,每个不同的分支都应该有一个与其角色相匹配的服务管道。AoneFlow的release分支相对固定,因此比GitFlow更容易进行持续集成。理论上,任何管道工具都可以与AoneFlow一起使用。不过,阿里统一平台提供了代码审查、安全检查、在线部署等功能的流水线集成,这对内部团队使用AoneFlow的优化还是加分不少。另一个有用的帮助是分会的管理。特性分支和发布分支关系的维护是AoneFlow独有的问题。当您需要根据现有功能集进行更改时,记住每个发布分支来自哪个功能分支非常有用。例如,当某个特性需要退出某个特定的发布分支时,通常会将除该特性以外的其他特性的分支合并一次,以替换原来的发布分支。这些信息人工记录并不容易,如果通过平台展示辅助会方便很多。当某些功能组合在低级发布环境(如集成测试环境)进行验证时,我们希望将其内容直接迁移到高级别环境(如预发布环境)对应的发布分支。这样可以保证线上版本必须通过预发布验证,预发布版本必须通过集成验证等等,让各个发布分支形成一个系列。同样,这个操作可以使用普通的Git命令来实现,但是使用可视化工具会使这个过程更加直观。此外,平台提供代码仓库各分支状态的统一展示,包括分支对应的部署环境的机器信息和运行记录。正是这些“高附加值”的助力,让AoneFlow扬长避短,成为阿里团队支持复杂项目的首选利器。写在最后代码分支模式的选择没有绝对的对错。关键是要配合项目的规模和发布节奏。阿里协同研发平台经过大量的实践经验,总结出一套独创的分支管理方式,通过灵活、高效、简单、实用的流程,保障了阿里旗下众多产品的交付。当你还在为琳琅满目的分支模式犹豫不决,舍不得GitFlow的并行特性开发和TrunkBased的持续集成友好性时,AoneFlow或许是一个值得考虑的选择。笔者介绍的是阿里巴巴研发效率事业部的技术专家林凡(化名金鸡)。阅读原文
