NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

单体代码仓库:Uber 的 Android 代码仓库演化史

  • 2017-05-21
  • 本文字数:3272 字

    阅读完需:约 11 分钟

Uber 技术日开幕式上,软件工程师 Aimee Lucido 呈现了一个有关 Uber Android 代码库历史的演讲。在这篇文章里,她继续展开说明 Uber 为什么要构建一个单体仓库来支持 Uber 的 Android 开发。

今天,你们要开始构建一个全新的 Android 应用,对于你们来说,开头部分总是最困难的。那么要如何开始呢?

如果你们跟我一样,那你们一定也是先在 Android Studio 里创建一个新的项目,然后创建一个主 Activity,配置 Gradle ,或者再创建一个 git 仓库,这样其他人就可以与你一起合作开发这个应用。那么恭喜!你的代码结构就组成了第一个版本的 Uber 搭车应用。

我们使用一个大纸箱表示 Uber 的第一个 Android 代码基库:一个装着代码的大箱子。

我们在 2010 年启动了我们的第一个 Android 打车应用,那时我们还是一个小公司。Uber 的工程团队只有寥寥数人。我们有一个合同工负责 Android 平台的开发,而且(如果你能够想象得到)我们甚至还没有 Android 的司机应用。我们的光杆 Android 工程师基于一个单独的代码库开发了打车应用的第一个版本:一个装着代码的大箱子。

在早期,使用单独的代码仓库来创建一个新的应用有几个好处:

  • 开箱即用的 Android:Eclipse(开发第一个版本打车应用所使用的 IDE)提供的代码结构就是一个单独的代码库。在 2017 年,构建一个单独的代码仓库比在 2010 年要容易得多,因为我们现在有 Android Studio,可以使用更多的 Android 类库。不过,不管你使用的是哪一种 IDE,这些 IDE 都会为你提供一个默认的仓库结构和一些基本工具,比如构建脚本和 git 集成。
  • 加快小型团队的开发:因为所有的依赖都集中在一个地方,对于一个小型的开发团队来说,使用单独的代码仓库可以提升他们的效率,共享代码和重构也会变得更简单。

几年之后,Uber 的 Android 开发有了小规模的扩张。到了 2013 年,我们聘请了第一个全职 Android 工程师,在这一时期,我们的工程团队已经比之前翻了一翻。也就是从那个时候,我们才开始开发第一个司机应用。

开发司机应用让我们有机会对我们的代码基库结构进行改进。打车应用仍然放在单独的代码基库里,不过我们将一些可重用的组件抽取出来,因为我们已经拥有了必要的资源和工具来做这件事情。核心的代码仍然放在它自己的仓库里,不过我们构建了一个类库,包含了两个应用都要用到的公共组件。

到了 2014 年,我们的增长规模要求我们去寻找其他不同的解决方案。Uber 的工程师已经超过一百个。Android 工程团队的工程师也从一个变成了八个。随着工程师数量的增长,我们的代码基库规模也在增长。

我们看着前行的方向,我们意识到,如果我们不做出一些改变,我们将会碰到如下几个问题:

  • 长时间的构建:我们最初的 Eclipse 项目使用 Ant 作为默认的构建工具,而使用 Ant 构建大型的代码库会越来越慢。
  • 功能耦合:共享代码很容易导致过度共享。随着 Android 应用功能不断的增加,我们担心功能会无机地耦合在一起。
  • 破坏 Master 分支:将变更 rebase 到最新的 Master 分支上之后发生构建错误,然后需要花很多时间进行调试,这种事情经常发生。而你会发现,构建错误与你的代码并没有关系,而是之前有人在没有运行测试的情况下将代码 rebase 到 Master 分支上。如果你跟我一样,那么也已经处在一个两难的境地。多个工程师在同一个代码基库上贡献代码,却不使用持续集成工具(见后面的 Submit Queue ),那么就存在产生冲突的风险。这会对 Master 分支造成破坏,而且会浪费时间。

所以,在 2013 年到 2014 年期间,为了解决这些问题,我们做出了一系列改变,我们迁移到了多仓库的代码基库。2013 年,我们使用 IntelliJ 和 Maven 代替了 Eclipse 和 Ant,这样我们就可以从服务器上拉取依赖包,并将我们的包仓库拆分成 20 多个更小的独立仓库。(例如,网络相关的包被移到自己的仓库里,然后应用程序在编译时通过 Maven 将它们拉取到本地。)同时,我们改用 Gradle 构建脚本,迈出了向多仓库侵袭的第一步。

我们使用几个纸箱表示多库代码仓库,Uber 在 2013 年将代码基库转成这种结构,以便满足不断增长的用户需求。

Uber 的多库代码基库由一些小代码基库组成,每个小代码基库代表了一个单独的离散想法,包含了一些类库,打车应用和司机应用在编译时拉取这些类库。每个代码库就像是一小箱子的代码,包含了自己的 IDE 项目文件、git 仓库和构建脚本。多库代码仓库是一种稳固的具有前瞻性的架构,解决了构建时间长、功能特性耦合和 Master 分支遭到破坏的问题。

现在的问题变成:为什么我们不是一开始就使用多库代码仓库?一句话:成本。将功能特性拆分到独立的仓库需要大量的时间,而且需要很多专家来完成。它需要很多领域的知识,比如 Maven、Gradle、VPN 和类库管理。这种知识的投入也只有在公司的规模增长到一定程度的时候才是值得的。

在将近三年的时间里,我们伴随着多库代码仓库结构一起成长。但到了 2016 年,我们的多库代码仓库开始出现瓶颈,我们的开发人员开始面临新的问题:

  • 架构孤岛:因为功能特性的强去耦,开始出现架构孤岛。我们早期构建了一个统一的 lint 系统,但它并不能防止不同的团队使用各种各样的 Activity Fragment ,以及 MVC 架构和我们自己的架构。从某种程度上说,架构孤岛是被允许的,甚至还有好处:工程师可以选择适合他们的架构。但随着规模的增长,我们的团队需要使用越来越多的类库。这意味着开发人员需要经常性地学习新的架构,而且学习曲线非常陡峭。因此,一个类库的架构如果没有被正确实现,将导致与消费者应用或其他功能特性的集成变得很困难,或者需要重度的代码重构。
  • ** 依赖地狱( Dependency Hell ):** 这个方案确实能够减小依赖地狱问题所带来的影响,不过这也取决于你的变更涉及到多少类库,即使是在代码基库上运行这个工具就需要很长的时间。另外,修复问题可能需要好几天的时间:识别问题的依赖情况,修复代码,然后在受影响的代码库上拉出新的版本。
  • 长时间的构建:我们的代码基库规模已经达到 Gradle 能够进行快速构建的上限。一个新应用需要超过 15 分钟的时间来构建,对一些 XML 小文件的调整可能会占用数个小时的开发时间。

那么我们是怎么解决这些问题的?

我们的解决方案:使用单体仓库,一个包含了多个独立项目的代码仓库。一个代码基库包含一个单体仓库,就像我们最初的打车应用那样。不过与打车应用不同的是,这个箱子里包含了多个逻辑组件,它们是相互独立的。现在,我们可以在工具和架构上投入时间和资源,来修正大型单体仓库的缺点:

  • IDE 支持: Android Studio 被作为默认的轻量级的 Android 开发者平台,不过并不是唯一的选择。我们发现,在一个大型的单体仓库里,IntelliJ 更适合我们目前的规模。
  • 长时间的构建: Uber 目前已经从 Gradle 转向 Buck ,Buck 是一个模块化的构建系统。我们的代码已经被拆分成离散的组件,所以很容易与 Buck 集成起来。我们自己开发的 Gradle 插件 OkBuck 将 15 分钟多的构建时间降到了 5 分钟以下,而对于增量构建,只需要不到一分钟的时间。
  • 破坏 Master 分支:我们最近引入了一个叫作 Submit Queue 的系统,用于将变更 rebase 到 Master 分支上,并且在合并之前运行一系列测试。这样可以防止工程师在提交代码时破坏构建过程,保持 Master 分支的整洁。

这看起来花费了不少功夫,事实确实如此。目前还没有开箱即用的单体仓库解决方案,因为很少有公司可以达到需要使用单体仓库的规模。我们现在有了时间、专家和资源来构建工具,防止那些在 2013 年出现过的痛点再次对我们的生产力造成破坏。

Uber 花了数年的时间才达到了目前的开发状态。当我们还是一个由几个工程师组成的团队时,我们没有时间和资源来创建 Submit Queue 或者搭建 Buck。不过,我们早期的洞见促进了架构决策,让我们可以快速地伸缩。现在,我们有了更进一步的发展,我们可以在开发上投入更多,确保未来的服务增长是无缝和高效的。

提升开发者的生产力是块难啃的骨头,但却非常重要。随着每一次小步跑的进步,我们不仅让 Uber 变得更好,也让整个 Android 社区变得更好。

查看英文原文: THE JOURNEY TO ANDROID MONOREPO: THE HISTORY OF UBER ENGINEERING’S ANDROID CODEBASE ORGANIZATION

2017-05-21 19:001634
用户头像

发布了 322 篇内容, 共 134.5 次阅读, 收获喜欢 144 次。

关注

评论

发布
暂无评论
发现更多内容

斟茶兵——远程进程管理

白粥

进程管理 运维开发 系统维护 离职交接 日常工作

「MySQL」数据库备份和还原

恒生LIGHT云社区

MySQL 数据库 MySQL 数据库

华为云应用构建技术实践精选集

华为云开发者联盟

云计算 华为云 内容合集 技术专题合集 应用构建

保险行业办理过等保选择哪家好?有成功案例吗?

行云管家

网络安全 等保 等级保护 等保2.0

EasyRecovery如何恢复ps的psd文件

淋雨

数据恢复 EasyRecovery

CSS之选择器

Augus

CSS 12月日更

零代码训练营第七期本月启动,现正开放报名!

明道云

产品经理进阶(一)Web APP UI一致性设计

No Silver Bullet

产品经理 12月日更

java序列化实现原理和深度分析

Java个体户

Java

大数据开发之Hadoop家族都有谁

@零度

大数据 hadoop

羊肉泡馍我们来了,尚硅谷西安分校设立首期特惠

编程江湖

编程开发

再添神器!Paddle.js 发布 OCR SDK

百度开发者中心

OCR paddle.js

The Data Way Vol.7|从故事里寻找开源的『内核』

SphereEx

Apache 开源 播客 Meetup SphereEx

🍃【Spring专题】「实战系列」spring注解@ConditionalOnExpression详细使用说明

洛神灬殇

spring Spring Framework Condition 12月日更 ConditionOnExpression

万字详解什么是生成对抗网络GAN

华为云开发者联盟

算法 推荐算法 GAN 强化学习 生成对抗网络

搞定react源码 惊艳面试官

buchila11

React

解析云原生2.0架构设计的8大关键趋势

华为云开发者联盟

云原生 架构设计 数据治理 存算分离 分布式云

结算中心全国集中化支撑解决之道

鲸品堂

Redis分布式锁的正确使用

编程江湖

redis java编程

热门盘点:企业该如何对待低代码?应不应该选择低代码?

优秀

低代码

一个渐进式微前端框架 - Fronts

RingCentral铃盛

架构 大前端 测试 经验分享

如何用GoldWave将音频添加生成机械化音效

懒得勤快

【混合云小知识】混合云应用场景包含哪些?

行云管家

云计算 混合云

【喜讯】尚硅谷西安分校成立啦

@零度

尚硅谷 西安分校成立

30个类手写Spring核心原理之环境准备(1)

Tom弹架构

Java spring 源码

2021年11月国产数据库大事记

墨天轮

数据库 opengauss TiDB oceanbase 国产数据库

uni-app技术分享| uniapp实现直播旁路推流

anyRTC开发者

uni-app 音视频 视频直播 视频通话 旁路推流

学习react源码 征服面试官

buchila11

React

怎么排查是哪里出现了数据倾斜

编程江湖

大数据 数据倾斜

埃文科技上榜CCSIP 2021中国网络安全产业全景图3大安全模块

郑州埃文科技

网络安全 ip技术 全景图

Linux一学就会之重定向和文件的查找(Linux下一切皆文件)

学神来啦

Linux 运维 linux云计算 linux一学就会

单体代码仓库:Uber的Android代码仓库演化史_语言 & 开发_薛命灯_InfoQ精选文章