Why React Native Teams Use OTA Updates
Why OTA is really a second release path for compatible JavaScript and assets—not a shortcut around native releases.
React Native applications have an unusual release boundary.
A web application can usually deploy new JavaScript to infrastructure controlled by the team, and users receive that code on their next request.
A mobile application is different.
Users install a signed iOS or Android binary, and that binary remains on their device until another store version is installed.
That distinction can make the operational cost of a small JavaScript fix much larger than the fix itself.
A production bug may take a few minutes to correct, but distributing another native binary still involves building, signing, store processing, release management, and user adoption.
OTA updates add another release path.
They let an already-installed React Native application receive compatible JavaScript and bundled assets without requiring a new native binary for every eligible change.
The important word is compatible.
OTA does not replace native releases.
It gives teams a smaller release mechanism for changes the binary already installed on the device is capable of running.
The installed binary is still the authority
A React Native application contains two broad layers.
The first is native:
iOS and Android application code;
native modules;
permissions and entitlements;
platform configuration;
the JavaScript engine;
React Native’s native runtime.
Then there is the JavaScript-driven layer:
application logic;
UI behavior implemented with capabilities already present in the binary;
configuration represented in the bundle;
compatible bundled assets.
OTA operates inside the boundary created by the first layer.
A useful question before every OTA release is:
Can the exact production binary already installed on the device execute this JavaScript and load these assets without requiring a native capability introduced later?
If the answer is yes, the change may belong in the existing OTA runtime.
If the answer is no, the team needs another native build first.
That rule is much safer than judging compatibility by how small the source-code diff looks.
A one-line JavaScript change can still call a native API that older binaries do not contain.
A configuration change can still modify CocoaPods, Gradle, generated native projects, permissions, or other compiled behavior.
Source language is not the compatibility boundary.
The installed binary is.
What still requires a store release?
Some examples are straightforward:
adding or removing a native module;
incompatibly upgrading a native dependency;
changing iOS entitlements;
adding Android permissions or manifest behavior;
changing background modes or native extensions;
switching JavaScript engines;
changing React Native architecture;
making an Expo config-plugin change that alters generated native projects.
Those changes modify what the application binary is capable of doing.
No OTA runtime label can magically add those capabilities to binaries that users already installed.
The current Bundle Drop SDK follows that same model: its runtime value remains stable for compatible JavaScript/asset updates and must move when the native compatibility boundary changes.
This is why runtime identity is such an important concept in OTA systems.
A runtime version is not just another release number.
It is a declaration that:
these native binaries can safely execute updates published to this runtime line.
So why add OTA at all?
The simplest reason is production fixes.
Suppose a JavaScript conditional causes an important workflow to fail.
The native binary itself is fine.
The application already contains every native capability the corrected JavaScript needs.
Without OTA, the fix still has to wait for another complete native release process.
With OTA, the team can create a compatible JavaScript release and begin distributing it through the application’s update path.
The same applies to less dramatic changes:
fixing validation;
correcting copy;
repairing a layout;
changing JavaScript-only business logic;
updating compatible assets;
refining existing feature behavior.
OTA lets native releases focus on changes that actually require a new native binary.
Compatible application changes get their own release path.
That does not mean every user immediately runs the new code.
A device can be offline.
The app may not have launched.
The application’s update policy might wait until another restart.
A rollout may intentionally exclude the installation for now.
OTA gives the team more control over delivery; it does not remove the realities of distributed mobile devices.
Production OTA is much more than hosting a JavaScript file
A minimal OTA prototype can look deceptively simple:
generate a bundle;
upload it;
tell the client where to download it.
A production system needs to answer far more questions.
Is the release compatible?
An iOS installation and an Android installation may not share the same native compatibility history.
Runtime gating keeps bundles inside the native generation they were built to run on.
Which release track owns it?
Teams usually need several release tracks:
internal testing;
QA;
beta;
production.
These are often represented as channels.
A channel answers where the release belongs.
It should not be confused with runtime compatibility, and it should not necessarily be confused with application configuration such as which backend environment the app talks to.
Keeping those concerns separate makes the release model easier to explain.
Can the downloaded artifact be trusted?
An interrupted download should not replace a working application.
Neither should a corrupted local artifact or an unexpected server response.
OTA clients need an integrity boundary before activation.
The safe state is binary:
the intended artifact was verified, or the existing working application stays active.
Who should receive it first?
This is where staged rollout and targeting become useful.
They solve different problems.
A staged rollout asks:
How much of the eligible audience should receive this?
Targeting asks:
Which part of the audience is eligible?
You might first expose a candidate to internal testers, then a deliberate customer cohort, then 10% of the remaining production audience, then expand after observing the result.
That is a much more controlled model than making every release immediately eligible for every device.
Percentage rollout is not the same thing as adoption
This is an important operational distinction.
Setting a release to 20% exposure does not mean exactly 20% of your installed user base is currently running it.
Devices check at different times.
Some applications do not launch frequently.
Some users may download the update but not restart yet.
Some may still be excluded by runtime, channel, or targeting rules.
So rollout percentage describes eligibility control, not guaranteed adoption.
You still need observability to understand what is actually running.
That means release identity matters.
When something goes wrong, “the user has app version 4.3” may not be enough information.
You also want to know the JavaScript release, runtime line, channel, and update state involved.
Rollback has two different jobs
People often talk about rollback as one feature.
In an OTA system, there are at least two distinct recovery scenarios.
Server-side rollback
The team observes that a release is unhealthy and stops sending it.
Future update checks can then resolve toward an earlier compatible release.
On-device recovery
The harder case is a device that already installed a bad candidate and now cannot start reliably enough to receive the next server instruction.
That requires a known-good local state and recovery behavior that does not depend entirely on the broken JavaScript being healthy.
A mature OTA design needs both ideas.
The current Bundle Drop implementation exposes server-side release controls and local rollback safety; its public SDK describes verified immutable bundles, runtime gating, and rollback behavior.
Rollback does not replace crash reporting or startup diagnostics.
If a failure happens before the OTA layer initializes, native observability still matters.
Recovery is part of the safety model, not the whole monitoring system.
Patch delivery is useful—but it is not the release
Another practical problem appears once OTA is used frequently.
A small source change can still produce a large complete JavaScript bundle.
Downloading the whole artifact every time is simple but can transfer far more data than the actual change requires.
Patch delivery can reduce the network transfer when the device already has an appropriate starting state.
But patches have prerequisites.
Real device fleets contain different histories:
missed updates;
fresh installs;
staged-rollout branches;
recoveries;
users who have not launched recently.
So a patch should be treated as an optimized route to a release, not the definition of the release itself.
A dependable design keeps a complete verified update available when the optimized path cannot be used safely.
That is also the model currently documented by Bundle Drop: hybrid transport keeps the full verified release available while supported devices can use patch-sized delivery.
A practical OTA release flow
A provider-neutral production workflow can be surprisingly simple conceptually.
1. Classify the change
Confirm that the installed binary already contains every native capability the update needs.
If it does not, create another native build.
2. Produce one identifiable release
Do not rebuild “equivalent” bundles at every rollout stage.
Testing, observability, expansion, and rollback should refer to the same artifact identity.
3. Publish to a test track
Exercise the update using real release binaries.
A development server is not enough to prove the production startup path.
4. Verify the complete lifecycle
Test:
download;
validation;
activation;
cold startup;
critical application behavior;
recovery.
5. Begin with controlled exposure
Start with a deliberate cohort or percentage rather than the entire production population.
6. Observe
Watch update failures, startup behavior, crashes, and application-specific health indicators.
7. Expand deliberately
Increase eligibility when the evidence looks healthy.
8. Stop or roll back when it does not
Limit further exposure and return toward a known-good compatible release.
That process may sound conservative.
That is intentional.
OTA reduces the friction of distributing compatible code.
Lower distribution friction should make good release controls easier to use—not make them optional.
Where Bundle Drop fits
Disclosure: I built Bundle Drop.
Bundle Drop is one implementation of this release model for Expo and bare React Native applications.
The current public SDK includes runtime gating, channels, staged and targeted rollouts, rollback safety, hybrid patch/full-bundle transport, source-map metadata, and CI/CD publishing. Native changes still require a new App Store or Play Store build.
But those implementation details are not the important part of this article.
The useful model applies regardless of which OTA provider—or self-hosted system—you choose.
Before using OTA in production, a team should be able to answer:
What defines native compatibility?
Which changes still require a store release?
How are test and production release tracks separated?
How is an artifact verified before activation?
How is exposure limited?
How do we know which release a device is actually running?
What happens when a release is unhealthy?
Can an unusual device history still reach a valid release?
If those answers are explicit, OTA becomes a release-engineering tool.
If they are not, it is just remote file delivery.
OTA is not about eliminating native releases
React Native teams use OTA because many production changes belong to a JavaScript and asset layer that existing binaries already know how to execute.
For those changes, forcing every fix through a complete native release is often unnecessary.
OTA gives that work its own delivery path.
The value comes from respecting the native boundary rather than trying to work around it.
Compatibility determines whether a release can run.
Channels determine where it belongs.
Rollouts and targeting determine who receives it.
Verification determines whether it is safe to activate.
Observability tells you what actually happened.
Rollback and recovery give you a route back when evidence says the release is unhealthy.
That is why OTA is useful in React Native.
Not because native releases disappear.
Because every compatible JavaScript change does not have to become one.

