# Migrating from CodePush

This guide is for a React Native or Expo app that already publishes updates
with CodePush. It maps CodePush's commands onto their closest Rollbird
equivalent, covers the two places the underlying model differs, and hands off
to Rollbird's own setup and verification steps.

Read [OTA updates explained](https://rollbird.dev/docs/react-native-ota-updates/)
first for the general mechanism — channels, staged rollout, and rollback —
independent of either tool.

Still choosing a replacement? [CodePush alternatives in 2026](https://rollbird.dev/docs/codepush-alternatives/)
compares hosted and self-hosted options, including the ones that keep the
CodePush client API, with costs at three app sizes.

## CodePush retirement facts

Checked October 2026:

- **App Center retired on 31 March 2025.** Signing in and API calls stopped on that date, and CodePush was retired with it ([Microsoft: App Center retirement](https://learn.microsoft.com/en-us/appcenter/retirement)).
- **Analytics and Diagnostics are the only App Center features still running**, extended to the end of March 2027 in Microsoft's update of 15 April 2026. CodePush is not included.
- **The standalone CodePush server is archived.** Microsoft's [code-push-server](https://github.com/microsoft/code-push-server) repository is read-only; its archive notice was committed on 20 May 2025.
- **The `react-native-code-push` SDK is archived.** Its [repository](https://github.com/microsoft/react-native-code-push) was archived the same day, and its README says it won't support the New Architecture.

## What maps directly

Both tools sign in, link an app to a place updates are published from, ship a
release, and roll one back. CodePush was distributed both as the standalone
`code-push` CLI and as the `appcenter codepush ...` wrapper; the subcommands
below are the same either way. The table lists each CodePush command next to
its Rollbird equivalent. Every Rollbird command and flag below is documented
in the [CLI reference](https://rollbird.dev/docs/cli/).

| CodePush command | Rollbird equivalent |
| --- | --- |
| `code-push login` | `rollbird login` |
| `code-push app add <appName> <os> <platform>` and `code-push deployment add <appName> <deploymentName>` | `rollbird init` |
| `code-push release-react <appName> <platform>` | `rollbird release` |
| `code-push rollback <appName> <deploymentName>` | `rollbird rollback` |
| `code-push deployment ls <appName>` | `rollbird status` |
| `code-push release-react <appName> <platform> --rollout 25` | `rollbird release --rollout 25` |
| `code-push promote <appName> Staging Production` | No direct equivalent: run `rollbird release --production` from the same commit |

`rollbird init` does the combined job of CodePush's app and deployment setup:
it links the app to a Rollbird project and embeds a channel in the native
build in one step. Run it once per channel:

```bash
npx rollbird init --channel preview
```

The [Getting started](https://rollbird.dev/docs/getting-started/) guide walks
through this step and the ones that follow in full, including the wrapper
import and the native rebuild.

## What works differently

**Deployment keys become channels.** CodePush issues one deployment key per
deployment — typically `Staging` and `Production` — and a build embeds that
key to say which deployment it requests. Rollbird embeds a channel name in
the same place: `rollbird init --channel preview` or
`rollbird init --channel production` writes the channel into the native
build. A binary only requests the channel it was built with; publishing to a
different channel does not reach it. Treat each CodePush deployment your app
used as one Rollbird channel.

**Update eligibility compares a fingerprint, not a version range.** CodePush's
`release-react` command targets a binary version range through
`targetBinaryVersion`; any installed binary whose app version satisfies that
range is offered the update. Rollbird's default `fingerprint` strategy
instead compares a fingerprint of the native build: `rollbird init` embeds
that fingerprint, and a release only reaches a binary whose native code
produced a matching one. A native dependency change, a native configuration
change, or any other native-affecting edit changes the fingerprint, so
re-run `rollbird init` and rebuild after one. An `appVersion` strategy is
also available for apps that want to target by version instead; see the
[React Native SDK guide](https://rollbird.dev/docs/react-native/#compatibility-strategies).

## JavaScript API mapping

CodePush is wired into the app through the `react-native-code-push` module;
Rollbird through `@rollbird/react-native`. The calls map like this. Rollbird
names come from the [React Native SDK guide](https://rollbird.dev/docs/react-native/).

| CodePush | Rollbird | Notes |
| --- | --- | --- |
| `import codePush from "react-native-code-push"` | `import { Rollbird } from "@rollbird/react-native"` | |
| `export default codePush(App)` | `export default Rollbird.wrap({ projectKey })(App)` | `projectKey` is the public key in `rollbird.json`. |
| `codePush({ checkFrequency, installMode })(App)` | `Rollbird.wrap({ projectKey, strategy, fallbackComponent })(App)` | Rollbird checks on launch and installs on the next launch; there is no resume-time check option. |
| `deploymentKey` option or native key | Channel embedded by `rollbird init --channel <name>` | The binary carries its channel; there is no key in JavaScript. |
| `release-react --mandatory` with `mandatoryInstallMode: codePush.InstallMode.IMMEDIATE` | `rollbird release --force` | The app reloads after download unless `reloadOnForceUpdate` is `false`. |
| `codePush.sync()` | Handled by `Rollbird.wrap` | `Rollbird.runUpdateProcess` is exposed for explicit control. |
| `codePush.checkForUpdate()` | `Rollbird.checkForUpdate` | Prefer the wrapper-managed flow unless the app needs explicit update controls. |
| `codePush.restartApp()` | `Rollbird.reload()` | |
| `codePush.notifyAppReady()` | Not needed | The native client reports readiness and recovers to the previous bundle if a new one crashes first. |
| `codePush.getUpdateMetadata()` | `Rollbird.getBundleId()`, `Rollbird.getChannel()`, `Rollbird.getAppVersion()` | |
| `codePushStatusDidChange`, `codePushDownloadDidProgress`, or the `sync` callbacks | `onProgress`, `onUpdateProcessCompleted`, `onError` | Options on `Rollbird.wrap`. |

## Migration steps

Follow [Getting started](https://rollbird.dev/docs/getting-started/) from the
beginning: install the CLI, sign in, run `rollbird init` for each channel
your app used with CodePush, wrap the root component, and rebuild the native
binary. Keep only one update controller active in the app; remove the
CodePush package and its native wiring once the Rollbird-wrapped binary is
built.

## Verifying the migration

Complete steps 7 through 9 of
[Getting started](https://rollbird.dev/docs/getting-started/): publish a
preview release, confirm a rebuilt device receives it, and test a rollback.
One additional check is specific to a migration from CodePush: confirm the
rebuilt binary's native project no longer contains CodePush's deployment key
or native configuration.

If your team is deciding between Rollbird and Expo's service instead,
[Rollbird vs EAS Update](https://rollbird.dev/docs/rollbird-vs-eas-update/)
compares setup, concepts and cost. If you moved to Revopush after App Center,
[Rollbird vs Revopush](https://rollbird.dev/docs/rollbird-vs-revopush/) shows
the same move from Revopush's CodePush SDK, file by file.
