Migrating from Stallion to BetterCodePush without breaking OTA
A careful React Native migration from Stallion to BetterCodePush: native cutover, signing, rollout mapping, testing, rollback, and cleanup.
Moving an OTA provider is not a JavaScript-only refactor. Stallion's SDK owns the native bundle lookup on iOS and Android, and its installation guidehas you wire that lookup into AppDelegate and MainApplication. BetterCodePush needs the same native seam. That means one deliberate store build is the cutover point; it also means you can make the change without guessing which provider an installed binary will ask for.
Who should — and should not — migrate
Migrate if you want BetterCodePush's project-local CLI workflow, platform-specific apps, or a separate release pipeline. Do not migrate just to chase a feature that Stallion already provides and your team relies on: Stallion documents bundle signing, project tokens, bundles, releases, and phased rollout. A migration creates a new native dependency and a new operational surface. It is worth doing when you have a concrete reason and capacity to test a store build, not in the middle of an incident.
What maps cleanly, and what does not
| Stallion | BetterCodePush | Migration note |
|---|---|---|
| Project | Organization plus one app per platform | Create separate iOS and Android app IDs; do not reuse one ID for both. |
| Bucket and published bundle | Channel and deployed release | Rebuild and deploy the source; existing Stallion bundle hashes are not release IDs in BetterCodePush. |
| Release targeting an app version | --target-app-version | Target the version of the new native build, not the preceding Stallion build. |
| Rollout percentage | --rollout and stable cohorts | Start small again; do not copy a percentage without re-validating the bundle. |
| Stallion signing key | BetterCodePush signing key | Generate and embed a new BetterCodePush public key; do not assume key or signature compatibility. |
Stallion's own docs describe a bundle as first being published into a bucket and then released against a target app version; the default release rollout is 0%. See its publish command, release command, and distribution guide. BetterCodePush has a different data model, so treat the old service as release history, not as an artifact you can import.
Prerequisites
- Access to the bare React Native native projects, or an Expo prebuild/CNG project on SDK 52 or later.
- A BetterCodePush organization, a Live API key, and one BetterCodePush app for each platform you ship.
- A normal path to submit and test one new iOS and/or Android binary.
- A saved record of your active Stallion releases, target app versions, rollout state, and signing-key custody before changing anything.
Step 1 — Freeze Stallion releases and remove its client integration
Stop publishing new Stallion releases while you prepare the migration build. Then removewithStallion from the root component and remove the Stallion native bundle provider plus its project ID, app token, and public-signing-key configuration. Stallion documents those exact native hooks and values in its SDK setup and signing guide.
npm uninstall react-native-stallion
# If your release environment installed it globally:
npm uninstall -g stallion-cliDo not leave both SDKs attempting to supply the launch bundle. You can keep the Stallion console and its global CLI available for audit or emergency rollback of old Stallion binaries, but the new binary should have one bundle-loading owner: BetterCodePush.
Step 2 — Add BetterCodePush and create the cutover build
Install the local CLI, create iOS and Android apps in the BetterCodePush console, and make a Live API key available only to the CLI or CI. Generate a new signing key before the first production build if you plan to sign releases.
npm install --save-dev @bettercodepush/cli
npx bettercodepush keys generate
npx bettercodepush init --build bare
npx pod-install # omit for Android-only projectsThe initializer prompts for the BetterCodePush platform app IDs. Add the generated API key to the gitignored .env.bettercodepush, configure signing inbettercodepush.config.ts, then run npx bettercodepush keys export-public --yesfor a bare project. For Expo prebuild/CNG, use npx bettercodepush init --build expofollowed by npx expo prebuild --clean instead; Expo Go and expo-updatesare not compatible with BetterCodePush.
Wrap the root component, verify the native wiring, then build and distribute this version through your normal store process:
import { BetterCodePush } from "@bettercodepush/react-native";
export default BetterCodePush.wrap({
updateStrategy: "appVersion",
})(App);
# Before the store build
npx bettercodepush doctorStep 3 — Verify the cutover before publishing to everyone
Install the new binary on a real device. Confirm it starts from the embedded bundle, then make a visible JavaScript-only change and deploy it to a small rollout. The first BetterCodePush release must target the version in the new store build — for example, 1.0.0 below.
npx bettercodepush deploy \
--platform ios \
--target-app-version 1.0.0 \
--channel production \
--rollout 10 \
--message "Verify Stallion migration"Repeat for Android if applicable. Check the device on a fresh launch, inspect the release, and only then expand the rollout. BetterCodePush uses 1,000 stable cohorts; after retaining the release ID printed by deploy, 500 is a 50% rollout.
npx bettercodepush bundle show RELEASE_ID
npx bettercodepush bundle update RELEASE_ID --rollout-cohort-count 500 --yesRollback and the two-population reality
A BetterCodePush rollback affects subsequent checks by binaries that contain BetterCodePush:
npx bettercodepush rollback production --platform ios
npx bettercodepush bundle disable RELEASE_ID --yesIt cannot roll back an older Store build that still contains Stallion; that population must be managed from Stallion until it upgrades or ages out. Likewise, a Stallion rollback cannot reach the new BetterCodePush binary. Keep the old project, credentials, and runbook long enough to support those installed versions, then schedule a later native cleanup to remove any remaining Stallion packages or CI secrets.
The important constraint
There is no OTA-only migration between native bundle loaders. Ship one store build that removes Stallion and embeds BetterCodePush, test its first release at a small percentage, and keep both operational runbooks during the overlap. That is slower than a config swap, but it leaves every installed binary with a clear, testable update authority.