Skip to content

Staged OTA Rollout Procedure

Status: Active Last reviewed: 2026-06-28

Production-readiness #10. Documented procedure for shipping OTA updates to the production channel safely after R4 Launch Readiness is accepted. Preview channel still uses full 100% deploys because preview is the internal testing audience.

Before R4, do not use this procedure for normal work. Use yarn ota:preview instead.

Why staged

OTA deploys to 100% of users instantly. A bad bundle (Hermes parse failure, broken auth, missing assets) becomes a P0 incident in under 60 seconds. Staged rollout limits blast radius — a canary at 5% triggers the same Sentry crash alerts but only impacts a tiny audience while you decide to expand or roll back.

The procedure

1. Ship the canary (5%)

bash
TWOMORE_ALLOW_PRODUCTION_OTA=1 yarn ota:production:canary

This runs the production EAS update command with a 5% rollout percentage. Only about 5% of production users on the matching runtime version receive the update. The remaining users stay on the previous state.

Note the update group ID from the output — you'll need it for the next steps.

2. Watch Sentry for the next 60 minutes

Check the Sentry dashboard:

  • Filter by release = current app version (e.g., 0.4.0)
  • Filter by time window: since the canary publish
  • Look for:
    • New error types absent in the previous 24 hours
    • Crash rate spike (compare to the 95% on the old bundle)
    • perf.long-task breadcrumbs with severity=error (>= 1s)
    • nav.transition measurements > 2s

If the canary is healthy after 60 min, proceed to step 3. If problems, go to step 4 (rollback).

3. Expand the rollout

Edit the same update group to expand to 25%, then 100%:

bash
# Expand to 25%
eas update:edit <UPDATE_GROUP_ID> --rollout-percentage 25

# After another 60 min watching Sentry, expand to 100%
eas update:edit <UPDATE_GROUP_ID> --rollout-percentage 100

Replace <UPDATE_GROUP_ID> with the value from step 1.

Total time: ~2 hours to full rollout if both stages are clean.

4. Rollback (if canary or expansion fails)

First verify the current CLI surface:

bash
cd apps/mobile
eas --version
eas update:revert-update-rollout --help
eas update:republish --help
eas update:roll-back-to-embedded --help

For a failed canary or in-progress rollout, revert the rollout update:

bash
eas update:revert-update-rollout --group <UPDATE_GROUP_ID> --non-interactive

For a full production OTA where the previous known-good update group is known, republish the known-good group:

bash
eas update:republish --group <GOOD_GROUP_ID> \
  --destination-branch production \
  --message "rollback to <GOOD_GROUP_ID>" \
  --non-interactive

If no previous OTA is safe for the runtime, roll back to the embedded update in the installed production build:

bash
eas update:roll-back-to-embedded \
  --branch production \
  --runtime-version <RUNTIME_VERSION> \
  --message "rollback to embedded <RUNTIME_VERSION>" \
  --non-interactive

If you can quickly fix the issue, ship the fix through preview first, then start a new production canary. Do not publish a materially different production bundle without preview evidence unless it is an explicitly accepted emergency hotfix.

When NOT to use staged

Before R4, all normal OTA work goes to preview only.

After R4, trivial OTAs such as typo fixes or copy-only changes can ship directly with TWOMORE_ALLOW_PRODUCTION_OTA=1 yarn ota:production if the release owner records the source commit, runtime, update group, verification, smoke evidence, and rollback pointer.

Use staged for:

  • Any change touching auth, payment, or RSVP flows
  • Hex-arch refactors that span 5+ files
  • New Tamagui primitives or theme changes
  • Anything where a regression would lose user data or block usage

Cron-aware checklist

The daily Maestro run (03:00 KST) covers smoke tests but doesn't catch all regressions. For high-risk OTAs:

  1. Run yarn ota:production:canary BEFORE 03:00 KST
  2. Wait for the morning Maestro run (which targets preview, not production, but is a structural sanity check)
  3. Check Sentry around 04:00 KST when Korean users wake up — first real-traffic data lands here
  4. If clean by 05:00 KST: expand to 25%
  5. If clean by 07:00 KST: expand to 100%

This aligns the rollout with Korean user wake-up time so canary samples are real-traffic, not overnight noise.

Future: automated rollback

Tier 3 work — wire production monitoring to trigger an explicit rollback command if Sentry's event rate for the new release exceeds a threshold. Out of scope until production traffic exists to baseline against.

See:

Markdown remains the source of truth. Run yarn docs:check before handoff.