All posts
3 min readBy BetterCodePush

Run a release train, ship the fixes over the air

Ad-hoc mobile releases turn every deadline into a negotiation. A release train fixes the cadence; an OTA channel handles compatible JavaScript fixes without derailing it.

Release EngineeringProcessReact NativeOTA

Ad-hoc releasing — cut a release when the feature's ready, hold it when it's not — makes every release a negotiation. Delays cascade, exceptions become urgent, and the release manager ends up coordinating work that a predictable cadence could absorb.

The train, in one paragraph

A release train replaces "when it's ready" with "when it's scheduled": cut a release branch from main every week or two on a fixed day, stabilize briefly, submit. Anything merged by the cut rides; anything that misses waits for the next train — which is soon, so missing one is cheap and nobody argues. Features land behind flags so an unfinished feature is a dormant code path, not a blocked train. The store's review latency stops mattering to planning because submission happens on schedule regardless — the train absorbs review variance instead of your roadmap absorbing it.

Where trains break down without OTA

The classic failure is the exception. A crash ships in train 42. Now what? Your options are: hold train 43 and cram the fix in (the train stops being a train), or run a hotfix release outside the schedule (an unscheduled, high-stress, full store cycle — the exact thing the train existed to eliminate). Every team running trains without an update channel rediscovers this: the train handles the planned work beautifully and the exceptions violently. An update channel makes those compatible JavaScript exceptions less disruptive.

The train + OTA division of labor

  • The train carries: native changes, dependency and RN upgrades, new features (dark, behind flags), store-listing changes. Scheduled, reviewed, staged through the store's own rollout mechanisms.
  • The OTA channel carries: JavaScript bug fixes, copy and logic tweaks, iteration on flagged features already in the fleet. On demand, within hours, behind your own staged rollout with a real rollback.
  • The rule that keeps it clean: a compatible JavaScript emergency can justify an OTA update without stopping the train. If a fix needs native changes, it rides the next scheduled train.

Notice what this does to the emotional economy of releasing. The train removes deadline panic from features (miss it, catch the next). The OTA channel removes panic from bugs (fix ships today either way). What's left is a release process with no adrenaline in it — which is the actual goal, even if nobody puts "boring" on the OKR.

Starting from zero, pragmatically

Don't design the perfect process; install the skeleton and let it firm up. Biweekly cut on a fixed weekday. A one-page checklist for the cut-to-submit path — automate items as they prove annoying. Flags for anything unfinished. An update channel wired in before you need it, because the first emergency is the wrong time to do the integration. Two trains in, the calendar starts doing the project management; four trains in, you'll wonder how release dates were ever meetings. The train is old wisdom from the giants — the only modern part is that an OTA channel finally makes it survivable for a five-person team, because the exceptions no longer require the giant's staffing.

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