DEV Community

George Fean
George Fean

Posted on Originally published at bundledrop.app

React Native OTA Is a Release Pipeline, Not a Download Feature

“Download a new JavaScript bundle and run it” describes transport. It does not describe a production release system.

A real React Native OTA implementation has to preserve compatibility with installed binaries, identify immutable artifacts, decide which release each device should receive, verify downloads, control activation, observe adoption, and recover when an update fails.

Those are separate boundaries. Treating them as one download call is how a convenient feature becomes an operational risk.

OTA has two layers

A React Native application always contains a native binary. That binary defines the native modules, platform configuration, permissions, build-time environment, and embedded JavaScript that shipped through the store.

An OTA update can replace compatible JavaScript and related assets. It cannot safely add a native capability that the installed binary does not contain.

That gives us the first rule:

An OTA artifact is valid only for a runtime that already provides everything the artifact expects.

Whether the change was written in JavaScript is not enough. What matters is whether the installed binary can execute it safely.

Model OTA as explicit stages

A production pipeline is easier to reason about when each stage has one job:

Stage Responsibility
Build Produce the JavaScript bundle, assets, and source map
Identify Assign an immutable artifact and runtime identity
Publish Make an artifact intentionally eligible for release
Resolve Select the right release for this device
Transfer Deliver a full bundle or compatible patch
Verify Check identity, integrity, and authenticity
Stage Store the candidate without replacing the active bundle
Activate Apply the candidate at a controlled lifecycle point
Observe Record checks, downloads, activations, health, and failures
Recover Return to a previous good or embedded bundle when necessary

The important part is not the labels. It is refusing to let one stage silently take responsibility for all the others.

Build once and keep the artifact identity

A useful release artifact is more than a bundle file. It normally includes:

  • the JavaScript bundle,
  • its source map,
  • referenced assets,
  • runtime compatibility metadata,
  • a cryptographic hash or equivalent immutable identity,
  • and enough build metadata to reproduce and diagnose the release.

If a beta rollout and a production rollout use different rebuilds, they are not promoting the same thing. Even when the source commit is identical, dependency resolution, environment values, build tooling, or generated assets may differ.

Build once, test that artifact, and promote that exact identity through release stages.

This also keeps crash traces useful. A source map only helps when it belongs to the precise bundle that produced the stack trace.

Release selection is not artifact delivery

These questions are often mixed together:

  1. Which release should this device receive?
  2. How should the bytes for that release be transferred?

The first is release resolution. It considers runtime compatibility, channel, targeting, rollout percentage, revocation state, and device history.

The second is transport. It may return the complete artifact or, when a compatible base is available, a smaller patch. If patch delivery is not possible or verification fails, a full-update fallback can remain available.

A patch is therefore a transfer optimization, not a release identity. The device should still end up with the exact immutable target artifact selected by the resolver.

Compatibility, integrity, and health are different checks

A strong OTA client evaluates at least three independent properties.

Compatibility

Can this installed native binary execute the candidate?

A runtime identifier might include a native build fingerprint, a declared runtime version, or another contract that changes whenever native capabilities change. A channel such as “production” or “beta” is not a substitute for that contract.

Integrity

Did the device receive exactly the bytes that were published?

Hash verification, signed manifests, and atomic writes protect the artifact from corruption and unintended mutation. A partially downloaded file should never become active.

Health

Did the candidate start successfully on this device?

A bundle can be compatible and intact but still contain a startup bug. Health must be established after activation, not inferred from a successful download.

Passing one check does not imply passing the others.

Download and activation should be separate decisions

Downloading an update in the background does not mean it should immediately replace the running bundle.

A safer client can:

  1. resolve an eligible release,
  2. download it to a staging location,
  3. verify the completed artifact,
  4. record it as a pending candidate,
  5. activate it at a deliberate restart or reload boundary,
  6. and mark it healthy only after the app reaches a meaningful commit point.

This separation makes application lifecycle behavior explicit. It also prevents an interrupted transfer from corrupting the current runtime.

Teams can then choose an activation policy appropriate to the release: next cold start, next controlled restart, or a user-visible reload. The policy is product behavior, not merely a networking detail.

Rollback needs a server side and a device side

“Rollback” is frequently used for two different mechanisms.

Server-side rollback

The release service pauses or revokes a candidate so future update checks resolve to an older safe release.

This limits additional exposure, but it requires the device to run long enough to perform another check.

Device-side recovery

The native runtime detects that a newly activated candidate repeatedly failed before reaching the health commit point. It quarantines that artifact and returns to the previous good or embedded bundle without depending on JavaScript or the network.

This protects devices that fail before server-side instructions can reach them.

A production system benefits from both. Server control manages the fleet; local recovery protects the individual installation.

Old binaries do not disappear when a new app version ships

Mobile fleets are heterogeneous. Some users update quickly. Others stay on older store builds for weeks or months.

That means the release service must support multiple runtime lines at the same time. A new production release should not become the universal answer just because it is the newest.

A resolver can evaluate releases newest-first and fall through:

  1. ignore revoked releases and locally rejected hashes,
  2. find a compatible runtime,
  3. apply targeting rules,
  4. compare the installation's stable rollout bucket with the configured percentage,
  5. return the first release that passes every gate,
  6. or return no update when none qualifies.

Channels describe release intent. Runtime identities describe native compatibility. Keeping them separate lets multiple store versions coexist safely in the same production channel.

CI should preserve evidence, not just upload files

An OTA publishing job should retain the evidence needed to explain what happened later:

  • commit SHA and build identifier,
  • runtime and channel,
  • bundle hash and source-map hash,
  • artifact size,
  • publishing actor and timestamp,
  • release targeting and rollout policy,
  • and the immutable release identifier returned by the service.

This information makes release questions answerable:

  • Which source produced this bundle?
  • Which binaries were eligible?
  • Was a patch or full artifact delivered?
  • Which source map belongs to a crash?
  • When was the release paused or revoked?

Observability begins before the first device downloads anything.

Test the failure paths

A demo proves the happy path. Production confidence comes from deliberate failure tests.

At minimum, verify that:

  • an incompatible update is rejected or falls through,
  • a corrupted download fails integrity verification,
  • an interrupted download leaves the active bundle untouched,
  • an unsigned or invalid manifest is rejected,
  • a revoked release stops resolving for new checks,
  • a startup-crashing candidate is quarantined locally,
  • the previous good bundle starts without a network connection,
  • a missing patch base falls back to the full artifact,
  • and the exact source map for each artifact remains retrievable.

If the system cannot explain and survive these branches, it is still a downloader with optimistic assumptions around it.

Evaluate providers by pipeline semantics

Feature checklists tend to flatten meaningful differences. When comparing an OTA service or self-hosted stack, ask questions about the boundaries:

  • How is native compatibility represented?
  • Are uploaded and published states separate?
  • Does release resolution fall through to older eligible releases?
  • Are rollout cohorts stable per installation?
  • Can artifacts be verified independently of transport?
  • When are candidates activated and marked healthy?
  • Can a device recover before JavaScript starts?
  • Are source maps tied to immutable artifact identities?
  • Can the provider show resolved exposure separately from actual adoption?

The answers reveal more than a claim that a platform “supports OTA.”

The useful mental model

React Native OTA is a release pipeline that happens to transfer JavaScript.

The download is necessary, but it is not the difficult part. The difficult part is preserving the relationships between a native runtime, an immutable artifact, a release policy, an individual installation, and a known-good recovery state.

Once those boundaries are explicit, staged rollouts, targeting, patches, observability, and rollback become parts of one coherent system rather than a collection of toggles.


Disclosure: I am the founder of Bundle Drop. Bundle Drop supports React Native and Expo OTA workflows, including compatibility-aware release resolution, staged rollouts, patch delivery when a compatible patch is available, verified full-update fallback, and device-side recovery controls.

Originally published as React Native OTA Update Production Guide.

Top comments (0)