5000 万行以上大型代码仓库工程实践

2024-03-23 20:18

本文主要是介绍5000 万行以上大型代码仓库工程实践,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者:marinewu,腾讯 PCG 工程效能平台部专家

腾讯 PCG 工程效能平台部自 2020 年开始进行大仓基本能力建设,并在 2021 年与工蜂合作成立了代码大仓研效联合项目组。在此, 我们想分享大仓/单仓踩过的坑。我们认为这些坑是真实存在且很难避免的,不是小马过河。

单仓并不简单。成功的单仓所带来的效果绝不止简单的代码聚合,但成本是大量的工具支持以及工程实践。单仓像放大镜,可以将优秀的工程实践以极低的成本推广,但同时也会将错误迅速放大。同时,向单仓迁移的过程也有相当程度的风险。本文会详细讨论单仓的益处、挑战,以及我们对挑战的应对之道,以供参考。

单仓/大仓是什么?

  • 单仓:指Monorepo,或单一仓库,是指将多个项目放在同个仓库中的版本控制策略。单仓在实践中可能有不同规模,如中心级、部门级、BG 级、公司级单仓。业界最著名的单仓是Google 的 google3,除一些开源项目,Google 内其它所有代码都放置于其中,有超过 20 亿行代码和超过 86T 的容量。目前公司内部成功应用的单仓大多在部门级及以下。

  • 大仓:指大型规模的仓库。“大型规模”并没有严格的定义 -- 在本文中,我们将大仓定义为“单机已无法承受其容量的仓库”。一个粗略的估计:大于五千万行代码 或 容量超过 100G 的仓库即为普遍认可的大仓。

单仓是我们的目的,而大仓则是(终极)单仓的必然结果。

为什么要发明单仓?

单仓的设计核心起源于"One Version"的哲学,并内化了规模化的思想。

Scalability 可规模化

在软件工程中,我们希望“解决一次,解决全部”。即,任何的一个良好的工程实践,都以自动化、规模化的手段几乎零成本地推广到所有的团队及代码。这样可以减轻开发人员的心智负担,使之将更多的精力放在创造性的工作上。典型的例子包括代码静态检查、自动化测试及持续集成、开发流程标准化、工具统一化等等。

单仓极大地简化了规模化:当所有代码都放置在一处,并且高度统一,则所有的工具都可以规模化地在单仓上作业。

Single Source of Truth ->One Version 从单可信源到单一版本

Single source of truth(SSoT)原则,是指开发人员在任意时间可以确定代码仓库内的哪个分支是唯一可信依赖源(SSoT)。在 CVS 中,单一来源是核心原则;在 DVS 中,如 git,在现代的业界实践中也采取了该原则,即要求永远都只有一条主干,且所有的分支(除了发布分支)最终都会被收拢回主干里。

单一版本(One Version)则更进一步,是指在任意时间,代码库内的每一份组件、每一个依赖只有一个版本。

  • 对内部库而言,这意味着使用主干开发(见下),并且必须在主干 HEAD 上依赖。这是一个非常强的约束——这意味着除了终端制品,任何一个内部被依赖的库都不能通过分支发布,而必须保持自己在单仓的主干上一直是发布状态。

  • 对外部依赖而言,同一个第三方库在单仓中永远只会引入一个版本。

不强制 SSoT/One Version 的版本控制策略往往通过制品/版本分支发布。这意味着在整个依赖关系图之上还有一个版本的维度。这也是在开源社区/SDK 发布商通常采用的策略,其根本原因在于并非完全掌握下游用户的情况。这样除了导致较高的维护成本,还会导致依赖关系难以满足(Dependency hell, 依赖地狱)。

主干开发 Trunk-based development

基于版本控制的协作模式一般分两种:

  • 分支开发:每个分支对应一个功能,开发者在这个分支上开发,直到最后完成功能后合并回主干。这是现在小仓下的主流模式。

  • 主干开发:每个分支只对应一个简单的修改。每个开发者在分支上完成修改后经过 CR 尽快合入主干。这是在单仓下推荐的开发模式。

两者的主要区别在于分支存活时间:保持主干始终健康,将所有的 commits 尽快小批量合入的是主干开发;以 feature 为单位,当 feature 完成之后再重新合入的是分支开发。

虽然听上去差异微小,但从分支开发迁移到主干开发对研发模式的影响深远。请务必确保您的团队深入理解前置需求(挑战)和长期影响(益处)。下文将分别阐述。

Case: API Deprecation

考虑以下常见场景:一个公共库的作者如何 deprecate 旧的 API 并提供新的 API 作为替代品?

小仓场景:这个公共库按版本分支或按制品发布。API 可以在下一个版本直接更新,并强制 API 调用方对 API 进行更新。这样的好处是 API 提供方的责任较简单,坏处是每一个 API 调用方都需要自行更新代码,并且 API 提供方无法保证自己的新版 API 已经被使用。

单仓/主干场景:公共库只提供源码依赖,并不按版本发布。这样,我们需要保证公共库在主干上始终是正确的。API deprecation 时,API 提供方可以查找所有 API 调用方,并且发动大规模自动修改,原子性地将所有的旧 API 调用更新至新的 API。这样的好处是 API 调用方的责任更简单,但是这种修改只有在能够查找 API 的所有用户、并且整个持续集成水平较高时才有可能。

从上面的例子可见,在单仓/主干的场景下,很多代码的维护工作可以左移并规模化。减少重复劳动、大规模的工业化,是我们启用单仓的原动力。

益处

以下的益处基于开发者视角和代码维护者视角。

再次提醒, 单仓想要达成预期效果需要工具、流程和文化三方面的准备,以及长期大量持续维护。贸然迁移到大仓不但不会带来收益,反而会导致项目和代码管理彻底混乱。请确保您对挑战部分有所准备。

主干开发

主干开发是为了进行持续集成。频繁地、小批量地构建/测试是持续集成的关键。通过主干开发可以:

  • 避免较长的稳定期,使持续集成可以持续运行,均摊每次合入的风险

这篇关于5000 万行以上大型代码仓库工程实践的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/839411

相关文章

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

MySQL数据库函数之JSON_EXTRACT示例代码

《MySQL数据库函数之JSON_EXTRACT示例代码》:本文主要介绍MySQL数据库函数之JSON_EXTRACT的相关资料,JSON_EXTRACT()函数用于从JSON文档中提取值,支持对... 目录前言基本语法路径表达式示例示例 1: 提取简单值示例 2: 提取嵌套值示例 3: 提取数组中的值注意

CSS3中使用flex和grid实现等高元素布局的示例代码

《CSS3中使用flex和grid实现等高元素布局的示例代码》:本文主要介绍了使用CSS3中的Flexbox和Grid布局实现等高元素布局的方法,通过简单的两列实现、每行放置3列以及全部代码的展示,展示了这两种布局方式的实现细节和效果,详细内容请阅读本文,希望能对你有所帮助... 过往的实现方法是使用浮动加

JAVA调用Deepseek的api完成基本对话简单代码示例

《JAVA调用Deepseek的api完成基本对话简单代码示例》:本文主要介绍JAVA调用Deepseek的api完成基本对话的相关资料,文中详细讲解了如何获取DeepSeekAPI密钥、添加H... 获取API密钥首先,从DeepSeek平台获取API密钥,用于身份验证。添加HTTP客户端依赖使用Jav

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

python实现简易SSL的项目实践

《python实现简易SSL的项目实践》本文主要介绍了python实现简易SSL的项目实践,包括CA.py、server.py和client.py三个模块,文中通过示例代码介绍的非常详细,对大家的学习... 目录运行环境运行前准备程序实现与流程说明运行截图代码CA.pyclient.pyserver.py参

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

nginx-rtmp-module模块实现视频点播的示例代码

《nginx-rtmp-module模块实现视频点播的示例代码》本文主要介绍了nginx-rtmp-module模块实现视频点播,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录预置条件Nginx点播基本配置点播远程文件指定多个播放位置参考预置条件配置点播服务器 192.

CSS自定义浏览器滚动条样式完整代码

《CSS自定义浏览器滚动条样式完整代码》:本文主要介绍了如何使用CSS自定义浏览器滚动条的样式,包括隐藏滚动条的角落、设置滚动条的基本样式、轨道样式和滑块样式,并提供了完整的CSS代码示例,通过这些技巧,你可以为你的网站添加个性化的滚动条样式,从而提升用户体验,详细内容请阅读本文,希望能对你有所帮助...