本文由 简悦SimpRead 转码,原文地址 shopify.engineering
在2020年,我们宣布React Native是Shopify移动领域的未来。作为这个旅程的一部分,......
在2020年,我们宣布,React Native是Shopify移动的未来,从那时起,我们一直在将我们所有的原生移动应用程序迁移到React Native。由于每个应用程序都是不同的,没有一个单一的方法可以适用于所有的应用程序。因此,我们为每个应用程序评估了所有可能的选项,并选择了最适合他们需求的选项。
例如,Shopify的销售点,自从它第一次在内部黑客马拉松中建立以来,已经走过了很长的路。它最初是为支持小型母婴店或周末战士而设计和建造的。然而,它的受欢迎程度激增,被我们一些最大的商家使用,每年处理价值数十亿美元的交易。代码库已经积累了大量的技术债务,应用程序的用户体验也不能满足拥有数百个地点和数万种产品的大型商家的需求。经过彻底的评估,很明显,我们无法通过渐进式的改变来解决这些问题。因此,我们决定进行全面重写,这对我们的商户来说是一个很大的打击。
另一方面,Shopify Mobile,我们的旗舰移动应用程序,是相当稳定的,并满足我们商家的需求。它也是我们最大的应用程序,每个平台有300个屏幕,并且花了六年多的时间来建立。从头开始重建它将是一个巨大的工程。即使我们假设我们使用RN会有两倍的生产力(不一定总是这样),我们至少需要三年时间来重建。那是一个非常长的时间。在这段时间里,我们将不得不停止所有的新功能开发,而最终拥有与我们开始时完全一样的应用程序。那么,重写显然是不可能的。
构建和迁移,一次完成
让所有的移动团队在整个公司使用一个单一的技术栈和工具,可以给你一个难以置信的杠杆。我们不希望Shopify Mobile错过所有的共享库、组件和工具,而其他的应用程序正受益于此。因此,我们决定逐渐开始在应用程序中采用React Native,而不是进行全面重写。
我们的Mobile Enablement团队开始打下基础,将React Native引入代码库中。然后,功能团队开始尽可能地在React Native中构建新功能。在这样做的同时,他们也根据需要将代码库的现有部分迁移到React Native。
这种方法在一段时间内效果很好。我们的团队对React Native非常陌生,这使我们能够学习和尝试不同的应用程序架构方法。这也让我们在构建大量功能的同时,避免产生技术债务,而我们在React Native方面的专业技术水平与原生技术不相上下。
然而,这种方法有几个缺点。
- 迁移的速度非常慢,我们需要四到五年的时间才能完成。
- 对设计系统的任何改变都要做三次--一次是在原生Android中,一次是在原生iOS中,一次是在React Native中。
- 我们必须维护三个不同的架构(Android、iOS、RN),直到迁移完成。
- 维护与现有原生代码的互操作性变得非常耗时。
在迁移的几个月后,很明显,这种策略已经不能很好地为我们服务了。它为提供商业价值进行了优化,但严重限制了迁移工作,使其变得缓慢和不确定。我们需要重新思考如何继续前进。
自从我们第一次选择这种方法以来,已经发生了很多变化。我们有一个坚实的基础,可以使用React Native构建复杂的功能,我们有来自工程、产品和用户体验领导层的全力支持来迁移应用程序,而且团队中的大多数开发人员都同意切换到React Native。
在评估了几种方案后,我们决定采用一种我们称之为 "迭代移植 "的方法。在这种方法中,我们开始在React Native中构建所有的新功能,并同时迁移现有功能。
教大家用React Native发货
为了实施这个计划,我们首先需要确保我们团队的所有工程师都知道如何编写React Native代码。他们都有使用Kotlin和Swift构建移动应用的丰富经验,但React Native对他们中的许多人来说是一个全新的技术栈。
为了让大家轻松过渡,我们与我们的学习和发展团队合作,创建了一个名为RN Accelerator的内部培训项目。
该计划旨在涵盖工程师需要知道的一切,以便能够在Shopify Mobile中运送React Native代码。它由五个里程碑组成。
- Javascript和Typescript
- React
- React原生
- 用于Shopify移动端的React Native
- 高级工具和技术
这样划分课程,使团队可以跳过他们已经知道的主题,花更多的时间学习其他内容。我们开始跟踪人们在这个项目中的进展情况,并在完成后颁发礼品。很快,所有的工程师都接受了完整的培训,并开始将React Native代码投入生产。
谁,什么,什么时候?识别并优先考虑要迁移的内容
我们想确定所有需要迁移到React Native的表面,这样我们就可以用它们来计划我们的工作和衡量我们的进度。为了做到这一点,我们写了一个自定义脚本,查看我们的iOS和Android代码,确定所有需要迁移的源文件。
然后,我们将这些文件填入电子表格,并按功能和子团队进行分类。我们还添加了一些列来表示复杂性和状态。每个子团队都有一个明确的、定义清晰的迁移范围。有一些表面不再属于任何团队,所以我们为它们确定了新的所有者。
迁移标准
在这一点上,我们有一个巨大的要迁移的表面清单,需要一种方法来确定其优先次序。我们使用了一个粗略的优先级框架(基于RICE评分模型),以确保我们对正确的事情进行优先排序,并在所有子团队中保持一致。对于清单上的每个项目,我们确定其。
- 覆盖面。使用该功能的每月活跃用户的数量
- 影响。 该功能在质量或数量上的影响(1-5,1为最低影响)。
- 信心。你对该功能的范围和影响的信心如何(百分比)
- 努力。移植这项功能所需的努力(多少人,多少周)。
然后,RICE分数被计算为。
(影响范围x影响x信心)/努力=RICE得分。
我们用这个分数对功能和组件进行堆叠排序,在每个开发周期中安排时间来移植它们,并在电子表格中更新它们的状态。
当我们迁移被认为是合格的表面时,我们确保没有遗漏的依赖关系,并通过迁移(有一个功能标志)、测试和运送到生产的过程。我们对每个迁移的功能进行两周的监控,以确保其稳定性,然后再删除本地版本并去除功能标记,然后将迁移标记为完成。
边走边做改进
当我们把屏幕移植到RN时,我们也会注意寻找机会来改善应用程序的用户体验。这样,我们就可以在不降低商家创新速度的情况下将应用移植到React Native。折扣列表就是一个很好的例子。我们想使搜索体验现代化,并使其与应用程序中的其他屏幕相似。由于创建了一个新的组件来渲染列表和过滤器,我们把React Native移植作为一个理由,从一开始就实施新的设计。
截图比较了以前的原生折扣列表和新的RN版本。
不过,并不是每个屏幕都有资格进行改进,我们已经建立了一个决策流程,帮助确定哪些改进将被优先考虑,哪些团队将拥有它们。这些决定是根据改进的类型(例如,错误修复、破损的窗口或修改RN的本地组件)和哪个团队拥有最多背景和带宽来拥有这个问题。如果在解决方案上没有明确的一致性,或者没有合适的团队来负责这个问题,则需要一个项目提案来进一步探索潜在的改进,然后再继续前进。
从哪里开始?在开始的时候
你无法改善你无法衡量的东西,这就是为什么第一步是绘制所有的屏幕并创建一个仪表板,在这里我们可以轻松地跟踪迁移。
我们的仪表盘用于跟踪迁移的进展。
现在我们只需要选择从哪里开始迁移。我们考虑从较小的屏幕开始,代码较少,影响较小。但经过讨论和原型设计后,我们决定应该全盘考虑,首先优先考虑影响大的屏幕,比如应用的根屏幕。
今天,Shopify Mobile中基于标签的主要导航屏幕是用Kotlin和Swift编写的。为了增加Shopify Mobile内的React Native开发,必须移植更多的表面。为了解除开发人员能够扩展和修改Shopify Mobile的障碍,应用程序需要更早地支持React Native--在登录后的根屏幕。
移植根目录屏幕将进一步增加应用程序中的React Native屏幕数量。这将把Shopify Mobile设置为完全的React Native,从应用开始到功能屏幕。它还使我们能够在应用启动时在React Native方面进行全局配置,而不是等待第一个React功能的启动。
Shopify移动应用程序的根屏幕。
此举的好处
我们有明确的目标进入该项目,但除了这些,我们还经历了一些额外的好处,值得在此提及。
减少iOS和Android之间的实施差异
我们一直知道一些业务逻辑在iOS和Android上是不同的,但在移植屏幕的时候,我们发现它比预期的要多。我们渲染产品字幕的方式,为显示某些组件而检查的权限,用于在GraphQL中查询数据的字段,等等。将所有这些统一起来,将使我们以后在其基础上实现新的功能变得更加容易。
更多的手使工作更轻松
iOS和安卓开发者走到一起,成为移动开发者,使我们从事功能开发、批准PR等工作的开发者总数增加了一倍。这提高了新功能开发的速度。另一个目标是让网络开发者更容易为Shopify Mobile做出贡献,这将更多地增加为应用做出贡献的开发者数量。
更简单的代码
从iOS和Android中的Imperative UI实现转向Declarative UI使我们的代码更加简单。另外,从头开始让我们能够更有意识地处理逻辑问题,删除从GraphQL查询的不必要的变量,清理旧的实验,并修复一些技术债务。
我们现在的位置
这里有一个剧透。今天,如果你打开Shopify移动应用,所有四个根屏幕都已经在React Native中了 所以,现在是挑战的时候了。 下面视频中的屏幕哪一个是原生的,哪一个是React Native?
Shopify移动应用的视频演示(左边是React Native,右边是native。你猜对了吗?)
这很难分辨吧?说实话,我甚至不记得哪一个是哪一个!这就是我们的主要目标之一。这也是项目的主要目标之一。一些用户可能会注意到变化,通常是更好的变化,因为我们也会在一些屏幕中引入改进,但大多数屏幕看起来和感觉都是一样的。
道路上的一些坎坷
移植根部屏幕的项目花了大约四个月。其中大部分是准备必要的基础设施,这是一项值得的投资,因为它最终会被所有其他屏幕的移植所使用。Shopify也在大力投资React Native的开源框架,我们内部也在使用这些框架,例如。
- FlashList - FlatList的性能变体
- React Native Performance - 一个性能监控框架
- React Native Skia - React Native的高性能2D图形
当然,有些挑战是我们已经预料到并准备好的,但有些挑战是在项目进行过程中出现的。以下是我们解决的一些挑战和学到的教训。
原生路线
在移植根屏幕时,我们需要一种方法来打开React Native的内部屏幕。通常情况下,这些屏幕会直接从视图控制器和原生的Fragment中打开。我们必须建立一种方法来从RN中打开它们,用一个自定义的名字,并在Android/iOS中实现它们。我们称这些为本地路由。通过自定义实现,我们现在在React Native中有一个导航器,允许我们导航到本地屏幕。这使我们能够完全删除支持的视图控制器和片段,尽可能多地删除每个屏幕的特定本地代码。
深度链接支持
深度链接在安卓系统中不是问题,在那里我们可以直接以模版的形式打开所需的屏幕。但是在iOS上,情况就不一样了--我们要建立整个导航栈来进入那个内屏。这意味着我们试图移植的所有根屏幕也是应用程序中所有深度链接的入口。起初,我们试图通过重新创建整个导航栈来遵循旧的行为。问题是,由于其他屏幕同时被移植,有一些非常复杂的情况,例如我们会有RN->Native->Native->RN。 因此,我们决定不采用这种解决方案,而是采用Android已经在做的iOS,也就是把深层链接作为一个模式打开。因为我们已经为每一个内部屏幕实现了Native Routes,所以这相当简单,我们能够很快地实现它。
安全区域
在开始的时候,我们发现另一件困难的事情是确定如何正确地支持安全区。我们的第一直觉是用SafeAreaView,但后来我们意识到,对于其中的一些组件,尤其是对水平安全区域的支持,我们需要包裹每个单元格的内容,而不是单元格本身。这是因为单元格的背景应该是不尊重安全区的,只是里面的内容。一旦我们意识到大部分安全区域的修复应该在我们的UI组件内完成,事情就容易多了。
不正确与正确的SafeArea配置(注意不正确版本中的灰色填充)。
现在,我们的根屏幕已经被移植了,大部分必要的基础设施也已经到位,我注意到,移植的速度正在加快 在这个项目之前,我们的大多数开发人员都不知道React Native,所以每天他们都在学习更多的东西,这进一步促进了速度的提高。在这样的项目中工作是很有挑战性的,因为你最终会与本地实现和React Native一起工作,但对于那些喜欢编码和愿意接受新挑战的人来说,这真的很有价值。还有很长的路要走,但根据我所看到的,未来的React Native Shopify比我们想象的要近得多。
Mauricio de Meirelles是Shopify核心移动团队的一名员工开发人员。
我们都能完成任务,快速出货,并且学习。我们的运作方式是低流程、高信任,并以影响力为交易条件。你必须非常关心你所做的事情,并致力于不断发展你的手艺,才能在这里跟上步伐。如果你正在寻求超速发展,能够解决复杂的问题,并能在变化(和一点混乱)中茁壮成长,你就找到了正确的地方。请访问我们的工程职业页面,找到你的角色。