All posts
6 min readBy BetterCodePush

Migrating from RevoPush to BetterCodePush without stranding old binaries

A careful React Native migration from RevoPush to BetterCodePush: native cutover, old-binary support, OTA rollout, and rollback.

MigrationRevoPushReact NativeOTA

RevoPush is not App Center wearing a new logo. Its current SDK is a maintained, CodePush-compatible client, with documented support for the New Architecture and current React Native ranges. If it is serving you well, there is no emergency reason to move. But if you want BetterCodePush's deliberately narrow, hosted update channel for a bare React Native or Expo prebuild project, this is how to change providers without leaving existing installs behind.

First: decide whether a migration is actually worth it

Stay on RevoPush if its CodePush-compatible API, deployment-key model, and current SDK support fit your release process. Its current guide documents separate SDK lines for React Native and Expo versions, so the fact that Microsoft archived react-native-code-push does not by itself mean a RevoPush app is unsupported.

Move when you want a different operational model: platform-specific BetterCodePush apps, explicit app-version targeting, a project-local CLI, and a hosted CDN update channel without adopting a wider build platform. This is a native-integration change, not a server URL swap. RevoPush configures its SDK and deployment key in the native app; BetterCodePush installs its own native bundle-loading path. That is why the cutover needs a store build.

What does not migrate automatically

RevoPush applications, deployments, deployment keys, release history, and installed updates remain in RevoPush. Do not try to point an existing RevoPush binary at BetterCodePush, and do not reuse a RevoPush deployment key as a BetterCodePush credential. This follows from the two products' separate native setup and release identities; treat it as a clean cutover, not a database import.

The good news is that the old and new fleets can coexist. Binaries already in users' hands continue checking RevoPush. The new store binary checks BetterCodePush. Keep the RevoPush project and its release path available until the old binary has aged out of your active install base.

Prerequisites

  • A bare React Native project, or Expo using prebuild/CNG (Expo SDK 52 or newer).
  • Access to the iOS and Android native projects and the ability to ship a new store build.
  • A BetterCodePush organization, one app per platform, and a Live API key.
  • A release branch or tag that matches the native binary you are about to build.

For Expo, BetterCodePush supports EAS Build but not Expo Go or expo-updates; follow the BetterCodePush quickstart before changing a prebuild project. Do not put the publishing API key in the app — it belongs in your CI or local secret environment.

Step 1 — inventory the RevoPush integration

Before editing dependencies, record the RevoPush app and deployment used by each platform, its target binary versions, update-check behaviour, rollout state, and the last known-good release. RevoPush's SDK reads its server URL and deployment key from native configuration, and its JavaScript wrapper controls when checks happen. Capture those choices now so you can test the BetterCodePush build against the behaviour your users actually have.

Step 2 — replace the client in the new native build

Make this change on the branch that will produce the new store binary. Removing the dependency here does not alter an already-distributed binary, so it is safe to stop the new build from linking RevoPush while old installs continue to use it.

npm uninstall @revopush/react-native-code-push
npm install --save-dev @bettercodepush/cli

# Bare React Native
npx bettercodepush init --build bare
npx pod-install # iOS

# Expo prebuild/CNG: use this instead of the bare initializer
# npx bettercodepush init --build expo
# npx expo prebuild --clean

The package name being removed is the one in RevoPush's current SDK installation guide. Removing the package is not all the cleanup: remove any RevoPush CodePushimports or bundle-URL override, RevoPush deployment-key and server-URL settings, and its Expo config plugin from the new source branch. The BetterCodePush initializer then prompts for new platform app IDs and writes its own configuration. If your app uses a dynamic Expo config, add the plugin tuple printed by the initializer, then rerun it; do not hand-edit generated native folders.

Replace the RevoPush codePush(...) wrapper or manual sync calls with the BetterCodePush root wrapper. The initializer handles native wiring; this is the JavaScript side that enables update checks:

import { BetterCodePush } from "@bettercodepush/react-native";

export default BetterCodePush.wrap({
  updateStrategy: "appVersion",
})(App);

Step 3 — ship the cutover binary before any BetterCodePush release

Build, test, and distribute this as a normal App Store and Play Store release. It is the first binary that knows how to load BetterCodePush updates. Pick a real new app version — for example, 1.4.0 — and record the React Native, Hermes, and native-module versions used to build it. Compatible JavaScript fixes can follow over the air; any native code, configuration, or runtime change still needs another store build.

Step 4 — release narrowly, then expand by release ID

Only after the new binary is installed on a test device should you publish its first BetterCodePush update. This iOS example targets the 1.4.0 binary; run the equivalent deployment for Android with its own app version when applicable.

npx bettercodepush doctor
npx bettercodepush deploy --platform ios --target-app-version 1.4.0 \
  --channel production --rollout 5

# Save the release ID printed by deploy, then expand only after the soak.
RELEASE_ID="paste-the-release-id"
npx bettercodepush bundle update "$RELEASE_ID" \
  --rollout-cohort-count 250 --yes

Watch update-apply success, crashes, JavaScript errors, and the product path the change touches. The rollout cohort count is out of 1,000, so 250 is 25%. The fullstaged rollout playbookcovers the metrics and soak windows worth using.

Concept mapping and rollback

  • RevoPush app + deployment: BetterCodePush app + channel. Create separate BetterCodePush apps for iOS and Android.
  • Deployment key: BetterCodePush app ID in native configuration. The publishing API key remains only in the CLI or CI environment.
  • release-react: bettercodepush deploy, targeted at an explicit app version.
  • patch --rollout: bettercodepush bundle update RELEASE_ID --rollout-cohort-count COUNT --yes.
  • rollback: bettercodepush rollback production --platform ios, or disable one known release by ID.

A rollback affects subsequent update checks, so keep the release ID and a known-good build in your release record. It protects the BetterCodePush fleet only; the RevoPush fleet remains on its existing service until its old binaries disappear.

Step 5 — finish the cleanup later

Once the BetterCodePush binary has replaced the active RevoPush install base and you have shipped a few clean updates, retire the old RevoPush release process and credentials. If any RevoPush native references remain after your first cutover, remove them in a later native store build — that cleanup itself cannot be an OTA update. Keep the migration notes and last-known-good releases for both systems until support for the old binary is over.

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