OTA 更新的生产环境实战手册
了解如何在生产环境中安全执行 OTA 更新:通过分阶段发布、实时监控和回滚策略,让每次上线都更有把握。
中文
复制

Expo 的 OTA 更新服务(EAS Update)能在几分钟内把更新推送到用户手上。这当然很好,但有时候慢一点反而更有用,尤其是当你想观察更新在真实环境中的表现时。
最强的信号来自真实用户、真实设备和真实流量。EAS Update 的设计目标是快速分发,同时仍然让你掌控更新如何进入生产环境,以及这些信号究竟在哪里出现。
你用来测试一次更新的时间和覆盖面越多,它往往就越安全。但生产环境很少给你无限的时间,慢下来也有实实在在的代价。EAS Update 并不能消除这种取舍,它让你掌控曝光范围、时机和恢复手段,从而针对每次更新自行决定落在天平上的哪个位置。
本文会介绍一套在生产环境中运行 EAS Update 的实用做法,从分阶段发布、监控,到中止和回滚,重点放在这些机制在真实生产环境中的实际表现。
Expo 文档里列出了团队使用 EAS Update 的几种部署模式示例——从直接发布到生产环境的两条命令流程,到带 staging channel 和版本化分支、支持发布前更结构化测试的流程。
你选择的具体模式会决定你如何构建和验证更新,但本文中的运维原则*(逐步放量、观察真实信号、根据所见做出响应)*在更新进入生产环境后都适用,与工作流无关。
有意小步发布
在生产环境中,小步发布通常意味着限制曝光范围。
与其让一次更新立刻对全部用户可见,你可以先只向一小部分用户推送。这样能限制曝光范围,也让你有机会在进一步扩大之前,观察更新在真实环境中的表现。
rollout 会限制应用检查更新时能够收到该更新的用户比例。其余用户继续使用上一个更新。
在这篇文章中,我们会用基于 per-update rollouts 的示例来说明,这类发布针对单个已发布的更新生效。
例如,你可以先以较低的发布比例发布一个更新:
eas update --rollout-percentage=10
这样既能控制成本,也能控制影响范围,同时留出时间观察该更新在生产环境中与真实用户交互时的表现。
EAS Update 也支持基于分支的发布,即逐步把用户切换到另一个分支(一串更新流)。如果你想了解这两种发布模型在实际使用中的区别,部署指南中对可用的发布类型做了简要说明。
当你准备好评估实际表现时,下一步就是弄清楚该关注哪些信号,以及如何解读它们。
关于更新体积与带宽的说明
用户每下载一次更新都会消耗带宽。举例来说,如果你每周发布一次更新,而用户经常打开你的应用,那么一个月下来他们可能会下载多次更新。
EAS Update 提供的带宽相当充裕,所以对大多数应用来说,正常使用中并不会触及上限。减小更新体积主要是为了提升可靠性,并让你有更多余量来频繁发布更新。
从 SDK 55 版本开始,你可以选择启用 Hermes 字节码差分(目前处于 beta 阶段),此时更新会以二进制补丁的形式分发,而不是完整的 JavaScript bundle。客户端不再下载整个新的字节码文件,而是对已安装的内容应用差分,从而同时减小下载体积和带宽占用。

字节码差分大幅提升了可用的带宽余量,让用户能够下载更多更新,同时消耗的数据量比以前更少。
在生产环境中观察发布过程
当更新推送给一小部分用户后,EAS 仪表盘会近乎实时地展示该更新在生产环境中的表现。
更新采用情况与数量
首先要看的事情之一,是有多少用户真正接收到了这次更新。
例如,10% 的灰度发布并不意味着 10% 的用户会立即应用更新。默认情况下,应用会在冷启动时检查更新,如果有可用更新就下载。更新会在应用下次重启时生效,这意味着用户不会同时全部采用新版本。
有时团队会把 fallbackToCacheTimeout 调快几秒,让应用在启动时短暂等待更新下载完成,以便立即应用更新。这能加快采用速度,但也会影响启动行为,因为更新下载期间启动画面可能会一直显示。
更常见的做法是,团队通过 updates JS API 观察并响应更新生命周期。像 useUpdates 这样的 hook 会暴露更新系统的状态(例如应用当前是否在检查更新、是否有可用更新、是否已经下载了更新),从而可以在应用渲染完成后协调更新的采用。
无论配置如何,灰度发布期间你通常要看到的是采用率随时间逐渐上升,而不一定是立刻出现尖峰。
如果采用率低得出乎意料,这本身就是一个信号:用户可能没有频繁启动应用,不足以触发更新,或者更新可能不适用于你预期的那些构建版本——例如,它面向的运行时版本与这些构建版本支持的版本不同。
崩溃与致命错误
在早期灰度发布阶段,崩溃数据往往是最清晰的信号。启动崩溃和致命运行时错误通常会很快暴露出来,因为受影响的用户在更新运行的那一刻就会遇到。
你通常最先注意到这一点的地方是 Expo dashboard 中的部署和更新视图,在那里你可以看到每次更新的崩溃次数与启动数据并列显示。

要弄清某个更新为什么会崩溃,通常需要配合外部崩溃监控,比如集成 Sentry,这样堆栈跟踪和会话上下文就能与部署数据一起查看。Sentry 能捕获大多数运行时崩溃和异常,但非常早期的启动崩溃可能仍然只能通过平台崩溃报告看到,比如 App Store Connect 或 Play Console 的报告。
EAS Update 还内置了错误恢复机制。如果新应用的更新在启动早期就崩溃(应用还没来得及渲染出任何内容),应用可以将该更新标记为失败,并回退到之前可用的更新。这是一道防御机制,目的是避免应用陷入无法使用的状态——一启动就崩溃,根本撑不到下载修复程序。
观察趋势
早期发布阶段的数据可能很嘈杂。样本量小的时候,波动会被放大,尤其是在只有一小部分用户收到更新时。
不要对单个数据点做出反应,而要看趋势,比如:
-
随着收到更新的用户增多,崩溃率或错误率是否发生变化?
-
崩溃是否集中在更新发布的时间点附近?
用这些信号来判断:扩大覆盖范围后,你看到的行为是否会改变。如果随着更多用户拿到更新,崩溃率和错误率仍然可以接受,那么扩大发布范围通常是合理的。如果随着覆盖扩大而恶化,就先暂停,不要继续放量。
安全地扩大发布范围
当更新已经在有限范围内运行了一段时间,接下来要决定是否让更多用户收到它。
扩大发布范围并不会改变更新本身。你并没有发布任何新东西,只是让更多设备在下次检查更新时有资格收到同一个更新。
例如,要提高发布百分比:
eas update:edit
交互式指南会带你选择该更新并设置新的百分比。
如果在覆盖范围扩大的过程中,你监控的信号仍然可以接受,就可以继续扩大发布,直到更新覆盖全部用户。
回滚更新
如果发布过程中出了问题,你有两种方式停止或撤销更新,具体取决于它已经推进到什么程度。
如果发布仍在进行中,你可以将其撤回。如果更新已经覆盖了全部用户,你可以将其回滚。
回滚正在进行的发布
发布仍在进行时,你可以将其撤回。撤回会停止发布并重新发布控制更新,让客户端回到之前的状态——包括那些已经收到更新的用户。
当信号表明这次更新不应继续发布,而你想在它触达全部用户之前撤销其影响时,撤回就是正确的做法。
要撤回一个进行中的发布:
eas update:revert-update-rollout
发布完成后的回滚
一旦发布完成*(例如达到 100%)*,就没有可撤回的发布了。此时如果你认为这次更新不该继续运行,就要使用回滚。
回滚会发布一个新的更新,指示客户端运行之前的版本,或回退到内置更新。
回滚用于更新已经完整发布、而你需要把用户移回某个已知良好状态的情况。
要回滚到之前的更新:
eas update:rollback
交互式指南会帮助你选择回滚类型并完成回滚。
关于持久化状态兼容性的说明
撤回发布或回滚到之前的更新,前提是旧代码仍能正确处理较新更新所创建的持久化状态。
如果某次更新引入了不向后兼容的改动*(例如对本地存储或数据库 schema 的改动)*,把用户退回旧更新可能导致崩溃或异常行为。
在无法安全回滚的情况下,可能需要发布一个恢复兼容性的新更新。
调试更新问题
Expo 文档中有一份详尽的调试指南,逐一讲解常见的失败场景,例如构建收不到更新、runtime version 不匹配、更新在启动后不久崩溃,或资源加载失败。它还介绍了实用的调试策略,比如查看 expo-updates 日志、核对 runtime version、检查更新 manifest。
大多数更新问题归根结底是配置不匹配,或者构建本身不符合你所期望的那次更新的条件;只要知道该往哪里看,通常都不难诊断。
结语
如果你还没有在生产环境中使用过 EAS Updates,值得一试。直接把更新推送给用户,一开始可能看起来是很大的一步,但只要有了灰度发布、监控和回滚手段,更新就变成一件可以放心推进的事。
EAS Updates 让你既能快速迭代,又不失去掌控。你可以逐步引入改动,观察它们在真实用户身上的表现,并在需要时做出响应——这一切都在同一套工作流里完成。
EAS Update 提供了出色的开发体验,让团队可以专注于快速交付世界级的用户体验和改进。
有了合适的运维习惯,更新就会成为一种自然、可靠的方式,让你持续为每天使用这款应用的人改进它。