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.