为什么我们决定重写优步的司机应用

0
为什么我们决定重写优步的司机应用

这篇文章是第一次系列报道了优步的移动工程团队如何开发了代号为Carbon的最新版司机应用程序,这是我们拼车业务的核心组件。除了其他新功能外,这款应用还能让我们超过300万的司机合作伙伴找到车费、指路和追踪他们的收入。2017年,我们结合司机合作伙伴的反馈开始设计新应用程序,并于2018年9月开始投入生产。

2017年初,优步决定重写我们的司机应用程序。这是StackOverflow的首席执行官乔尔·斯波尔斯基做出的决定,一次被称为“任何软件公司都可能犯的最糟糕的战略错误”。

重写是非常危险的、资源密集型的,并且需要很长时间才能为用户带来切实的好处。对于这个特殊的重写,数百名工程师贡献了一些能力,更不用说设计师、产品经理、数据科学家、运营、法律和营销了。实际上,我们的重写花了一年半的时间来实现并在全球范围内推出。

我们的案例是所有组织中的工程师都面临的一个极端问题的例子。如果你是一家初创公司的工程师,正在考虑重写一些代码或功能,你可能会问:“我们的跑道烧掉了多少?”如果您在一个大型组织的一个小团队中工作,您可能会问,“这些变化值得我们没有构建的特性吗?”一个好的工程师和一个好的团队在接受重写的挑战之前会考虑这些更广泛的问题。

因此,当重写时过程涉及到许多重要的技术决策(将在以后的文章中讨论),这个决定重写需要结合技术考虑和更广泛的业务考虑。虽然这些问题很难回答,好的答案以上问题将帮助你向你的组织或团队证明重写的合理性。

最终,这些决定不是在真空中做出的。我们做出重写应用程序的决定并不是基于理论架构的思考(“我们的代码可能会更好,如果我们……”),而是一个密集的、三个月的研究过程的结果,其中包括数百页的文档和广泛的、跨组织的购买。雷竞技是骗人的在接下来的章节中,我们将讨论我们重写Uber司机应用程序的决定,以及我们在这个过程中发现的结果。

奠定了基础

重写的需要并不总是自然地从对新体系结构的需要的简单认识而来。重写是昂贵的,虽然工程组织经常想要重写代码,但还有其他对工程师时间的要求,不包括用更漂亮的架构结构一遍又一遍地重写相同的功能。对于驱动程序来说,有三个趋势促使他们做出了重写的决定:

技术债务

首先,驱动程序本身存在真正的技术债务。这种债务是Uber快速增长的结果,也是不断变化的产品需求(下一节将讨论)的结果。除此之外,科技债务还源于人们的欲望修复之前的科技债务:应用程序本身陷入了多次正在进行的迁移,使得功能看起来越来越复杂。

值得指出的是,当时存在于驱动程序中的技术债务并不是理论上的。我们看到了持续的中断和维护成本对开发人员生产力的影响。在2016年底,我们不得不暂停应用的开发,以修复多个功能回归。在我们解决这些问题之前,实现和发布新功能变得非常困难。

我们的驱动程序出现任何故障都是一个大问题,因为用户依赖这个应用程序谋生。在我们的世界中,任何低于99.99%的正常运行时间都是不可接受的,但我们经常发布在应用核心流程中经历重大倒退的构建

产品的挑战

我们在上一个版本的驱动程序中面临的最大问题之一是,该产品不能很好地适应新的业务用例。虽然Uber的司机应用程序最早是为简单的UberX旅行设计的,但我们的服务已经发展到包括Uber Pool、Uber Eats,以及针对特定市场的体验,如现金付费旅行等。

除了旅行,我们发现司机们还需要额外的功能来管理他们自己的财务和个人业务。例如,收入和评级透明度对司机体验至关重要,这是优步司机应用程序早期迭代中投资不足的地方。随着产品体验的扩展,我们需要为这类功能提供空间。

前一个驱动程序的两个截图
图1:在我们之前的驱动程序中,底部的标签超出了它们的初衷(左图)。地图显示也因为我们最初没有预测到的特征而变得超负荷。

我们在2015年和2016年采取了一些初始步骤来减轻这些问题,发布了应用程序的迭代。不幸的是,我们将UI的各个部分划分给不同的团队来构建,而不是围绕驱动程序需求和工作流程进行设计。如果你在这个时候查看我们的UI,你会看到一个关于收益的标签,一个关于评级的标签,一个关于设置的标签,以及一个关于其他功能的主页标签。每一个其他功能的桶变得更大,收益和评级标签经常被重新用于最初没有设计的功能。

我们从应用程序的这次迭代中吸取的教训,以及我们的长期产品愿景,实际上已经促使我们完全重新思考驱动程序应用程序应该如何寻找我们的驱动伙伴。即使一个重写不是不可避免的,重新设计是什么。

工程对齐

我们的工程团队之前已经在一个新的方向上进行了投资。特别是,与重写骑手应用程序2016年,我们推出了一个新的移动架构,我们称之为肋骨(一个变化毒蛇),以协助我们处理不断增长的规模。它为我们在驱动程序中认识到的大多数问题提供了解决方案:一个可伸缩扩展点的框架,一个令人信服的应用程序结构,以及一个雄辩的内存管理模型。我们在2017年向开源社区发布了rib架构。

虽然rib架构确实改善了我们的骑手应用,但它也代表了我们移动组织的一个新的工程方向。我们的核心平台团队未来的投资将主要涉及对rib的改进。支持具有不同体系结构的多个应用程序比在rib上进行标准化要昂贵得多。

使我们的决定

考虑到UI的重新设计和新的架构,我们有三个不同的选择:重新设计没有rib的驱动程序;将现有的驱动程序迁移到rib架构;或者基于rib完全重写应用程序。

没有肋骨的架构

我们考虑的第一个方法是重新设计肋骨。我们首先考虑这一点的原因是,我们知道迁移到rib将是资源密集型的。rib附带了许多新的库,同时也提供了一种新的方法来构建具有分层作用域结构的应用程序,该结构将业务逻辑与表示逻辑解耦。rib体系结构提供了一个雄辩的,但非常固执的内存管理系统。

首先,我们考虑现有的应用程序是否能够处理我们正在考虑的主要产品更改。我们发现,因为我们的应用程序支持对视图控制器控制,我们的大部分业务逻辑都与视图表示紧密耦合。这意味着UI重新设计将不可避免地涉及对业务逻辑的许多更改。

其次,正如前面所讨论的,现有的驱动程序应用程序架构存在一些需要解决的问题。这些问题部分与应用的逻辑有关,在某些地方(特别是在Android上),这已经演变成手机开发者非常常见的模式:不同版本的MVC,一个Massive View Controller,其中我们的大部分核心代码包含在一个数千行控制器文件中。因此,我们不愿意尝试现有的手机架构,因为它变得越来越复杂,开发难度也越来越大。

最后,即使我们拥有的旧司机应用的架构是完美的,从战略角度来看,采用rib仍然是有意义的,以避免我们在Uber的应用架构分裂的情况。有了一个令人信服的体系结构,我们的平台级投资变得有两倍的价值,在组织的一个部分(例如,骑手)编写的代码可以潜在地在另一个部分(例如,驱动程序)重用。

如果我们无论如何都要采用rib,我们该怎么做呢?

迁移

许多组织喜欢谨慎地进行迁移,允许他们在系统底层架构发生变化时继续进行特性开发。虽然在大多数情况下都是完全有效的,但过去我们在Uber发现了这种方法的一些问题。

首先,我们分析了过去几年我们在Uber尝试的10个主要的移动迁移,发现它们有很高的不完成率。也就是说,我们将开始迁移一个给定的底层库,但随后没有完全这样做。我们用新的库构建了新的特性,迁移了一些旧的特性,但是我们仍然在代码库中执行遗留代码。

经过进一步的调查,我们发现驱动程序中很多技术债务的根本原因就是这种迁移的结果。例如,我们有竞态条件因为我们的应用发布/订阅模式是在Android上分裂的。我们的核心应用结构最初是利用Android上的片段,后来部分迁移到内部框架中。这种不完全的迁移导致了适配器层和开发人员的普遍困惑。这些不完整的体系结构将最终导致直接影响用户的停机。

其次,我们经常发现,迁移过程中会产生大量的不稳定性。为了改进底层应用程序框架(例如我们的网络协议)而进行的迁移导致了大量的中断。从技术上来说,这些应该不会对我们的用户产生直接的、有形的影响,但最终会破坏应用的核心功能。

最后,根据我们的经验,即使是持续开发功能的承诺也常常无法实现。如果一个团队依赖于正在进行的迁移,那么他们通常会被阻塞,直到迁移完成。它还导致了一个世界,在这个世界中,回滚一个迁移通常意味着我们也必须回滚大量的特性。

因此,当我们评估是否要进行一个完整的产品重新设计并采用rib体系结构时,不完整的迁移或无尽的适配器层和facade的风险是非常高的,它们极大地增加了应用程序的不稳定性。

重写

在某种程度上,我们通过否定(其他选项,没有rib架构和迁移,都是站不住脚的)做出了这个决定,但是重写有积极的好处,它增强了我们对最终决定的信心。

首先,重写将释放我们重新设计应用程序的能力,而不受预定义的对其构建方式的理解的限制。这意味着它的设计对更广泛的可能流开放。

其次,选择重写应用程序意味着我们的架构将更加清晰,因为它将从一开始就源自一个令人信服的战略方向。如果我们选择了迁移,我们很可能会被遗留代码所困,而这些代码是为了方便或方便而重用的。

第三,重新编写应用程序可以让我们回到绘图板上,更充分地思考我们希望产品朝哪个方向发展。结果,应用中的某些主要框架最终被重写。

对于一个工程师来说,重写是一个做一些了不起的工作的机会,我们很兴奋能够开始。

结论

值得强调的是,我们重写驱动程序的决定并不是基于“如果我们可以重新来过会更好”这样的投机想法。事实上,有些工程师可能会惊讶地发现,即使在重写之后,我们发布的应用不仅有新功能和新架构,甚至还有一些新的技术债务。

也就是说,你永远都做不到完美。读了这篇文章的工程师应该犹豫一下,不要得出“迁移从来不起作用,重写会产生完美的代码”的结论。相反,重要的是要认识到重写的决定是在非常具体的组织、业务和技术需求的上下文中做出的。

如果我们没有在前几个月创建一个全新的手机架构,我们可能就不会重写应用。如果我们没有一个愿意研究这个决定的产品团队,我们可能就不会重写应用。雷竞技是骗人的如果过去Uber的迁移往往更成功,我们可能不会重写。当然,驱使我们重写的并不是重写本身就很好,甚至不是通常意义上的好主意。

相反,驱动程序应用程序的重写是为了为我们的用户构建更可靠、更强大的产品体验,同时,增强我们组织执行这一愿景的能力。这个决策过程可能没有发明下一个最佳抽象层的愿望那么令人兴奋,但它也是驱动一个成功的、完全改进的移动应用程序的创建的决策计算。

优步司机应用程序系列文章的索引

  1. 为什么我们决定重写优步的司机应用
  2. 在rib中构建Uber的新司机应用程序
  3. 优步新司机应用如何克服网络延迟
  4. Uber Eats的现金支付规模
  5. 如何在不危及整个业务的情况下发布应用重写
  6. 为司机建立可扩展和可靠的地图接口
  7. 工程优步Beacon:匹配车手和司机在24位RGB颜色
  8. 为驱动首选项设计一个安全的、可伸缩的、服务器驱动的平台
  9. 在优步的新司机应用程序中建立实时收入追踪系统
  10. 活动/服务作为一种依赖:反思Uber新司机应用的Android架构

对开发下一代移动应用程序感兴趣?考虑加入我们的团队

订阅我们的通讯以跟上优步工程的最新创新。

评论