All posts
3 min readBy BetterCodePush

Staged rollouts and instant rollbacks: a safe OTA release playbook

Ship to 5% first, watch, then expand — or pull it back in seconds. A practical rollout discipline for React Native over-the-air updates, and exactly when to abort.

RolloutsReliabilityOTAReact Native

Over-the-air updates remove the days of latency that app review used to impose. That is the whole point — but review was also an accidental safety net. When you can ship to every device in minutes, you can also ship a bug to every device in minutes. Staged rollouts are the discipline that replaces review as your guardrail.

The pattern

Expand in steps, with a soak window at each one: 5% → 25% → 50% → 100%. At each step you are answering a single question — is this release healthier than or equal to the one it replaces? Only move up when the answer stays yes for the full soak window.

# Start narrow
npx bettercodepush deploy --platform ios --target-app-version 1.4.0 \
  --channel production --rollout 5

# The deploy output prints the release ID. Soak, watch metrics, then widen.
RELEASE_ID="paste-the-release-id"
npx bettercodepush bundle update "$RELEASE_ID" \
  --rollout-cohort-count 250 --yes

What to watch during the soak

  • Crash-free session rate, compared against the previous release, not an absolute number.
  • JavaScript error rate and unhandled promise rejections.
  • Your one or two most important product funnels (sign-in, checkout, whatever pays).
  • Update-apply success: are devices on the rollout actually landing on the new bundle?

Decide your abort criteria before you deploy

The worst time to define "bad" is while you are staring at a graph at 11pm. Write the thresholds down before the release: for example, abort if the crash-free rate drops more than half a point below the prior release, or if the checkout funnel falls by more than a few percent. Pre-committing turns a judgment call into a checklist.

Rollback mechanics

There are two distinct recovery paths, and it helps to keep them straight:

  • Server-side disable. You disable the rollout; the next update check on each device falls back to the last known-good release. History is preserved — nothing is deleted — so you keep a full audit trail of what shipped and when.
  • Client-side auto-fallback. If a new bundle crashes on launch before it is marked good, the client reverts to the previous working bundle on its own, so a bad release cannot brick the app into a crash loop.

Operational habits that pay off

  • Keep a separate channel per environment (staging, production) and always deploy to staging first.
  • Always target a specific app version so a JS bundle never lands on a native build that cannot run it.
  • Keep a known-good release identified so rollback is a decision, not an investigation.
  • Do not roll out to 100% right before a weekend or a holiday.

If you are still setting this up, the mechanics slot straight into the migration workflow: deploy narrow, inspect the active release, expand or disable.

Ship your first update

Create an organization and deploy an over-the-air update in minutes. No billing setup during the beta.

Start with BetterCodePush