Migrer de CodePush vers EAS Update : Le Guide OTA React Native (2026)

Guide de migration CodePush vers EAS Update en 2026 : désinstallation, runtime version fingerprint, canaux, rollback et retours d'expérience terrain sur React Native.

CodePush vers EAS Update : Guide 2026

Mis à jour : 8 septembre 2026

Migrer de CodePush vers EAS Update se fait en trois étapes : désinstaller react-native-code-push, installer expo-updates avec eas update:configure, puis publier vos correctifs JavaScript via eas update --branch production. Depuis la fermeture d'App Center le 31 mars 2025, EAS Update s'est imposé comme le successeur naturel pour livrer des mises à jour over-the-air (OTA) aux applications React Native, y compris pour les projets bare workflow qui n'utilisent pas le SDK Expo.

  • App Center CodePush a été officiellement arrêté le 31 mars 2025 ; le fork communautaire react-native-code-push existe mais n'offre plus d'infrastructure hébergée.
  • EAS Update fonctionne avec le workflow managé Expo et avec du React Native pur (bare workflow) via le package expo-updates.
  • La notion de runtime version remplace la vérification du binaire natif : deux bundles avec la même runtime version sont interchangeables.
  • Le plan gratuit d'EAS Update inclut 1 000 utilisateurs actifs par mois (MAU) ; le plan Production démarre à 99 $/mois avec 200 000 MAU inclus.
  • Les mises à jour ne peuvent pas modifier le code natif : un changement dans ios/ ou android/ exige toujours une soumission au store.
  • Un rollback se fait via eas update --republish sur une update antérieure, sans intervention côté client.

Pourquoi migrer de CodePush maintenant ?

La date butoir est passée. Microsoft a fermé App Center le 31 mars 2025, ce qui a entraîné l'arrêt du service CodePush hébergé sur lequel s'appuyaient des dizaines de milliers d'applications React Native. J'ai vécu cette migration chez Delivery Hero pour l'app rider déployée sur plus de 30 marchés, et je peux vous confirmer que le compte à rebours a été serré : notre backlog de dépendances natives n'était pas prêt, et une partie de nos correctifs de production passaient par CodePush plusieurs fois par semaine.

Un fork communautaire, react-native-code-push, reste maintenu par Microsoft en open source, mais il ne fournit aucune infrastructure hébergée. Cela signifie qu'il faut auto-héberger un serveur, un stockage S3-compatible et une couche d'authentification, un travail conséquent quand une alternative clé en main existe (et franchement, personne n'a envie de porter cette dette). EAS Update, développé par Expo, coche toutes les cases : CDN mondial (Cloudflare), signature cryptographique, tableau de bord, historique d'updates et rollback en un clic.

L'autre raison est plus stratégique : la New Architecture avec Fabric et TurboModules exige un client d'update qui comprend le nouveau système de bridge JSI. Le SDK expo-updates a été mis à jour pour supporter Bridgeless dès la SDK 51 ; CodePush n'a jamais reçu ce support officiel.

CodePush vs EAS Update : tableau comparatif

Avant de plonger dans la migration technique, voici la comparaison pragmatique que j'utilise en revue d'architecture avec les équipes clientes. Les deux systèmes servent le même but (remplacer un bundle JavaScript sans passer par l'App Store ou Google Play), mais leurs modèles économiques, leur infrastructure et leur intégration au reste de l'outillage divergent nettement.

CritèreCodePush (communautaire)EAS Update
Statut du serviceFork open source, à auto-hébergerService géré par Expo, actif et supporté
PrixGratuit (mais infra à payer)Gratuit jusqu'à 1 000 MAU, 99 $/mois au-dessus
Support New ArchitectureNon officielOui, dès expo-updates 0.24 (SDK 51+)
Signature de bundleManuelAutomatique (RSA + certificats)
CDNÀ configurer soi-mêmeCloudflare global inclus
Compatibilité bare workflowOuiOui, via npx expo install expo-updates
RollbackRepublish manueleas update --republish en une commande
Interface d'adminCLI + serveur à écrireDashboard web expo.dev

Prérequis et compatibilité

Avant de démarrer, vérifiez ces cinq points, sinon vous perdrez des heures en debug obscur :

  • React Native 0.72 ou plus récent. EAS Update 4.x exige au minimum RN 0.72. Pour la New Architecture, poussez jusqu'à 0.76.
  • Un compte Expo avec un projet lié. Créez-en un sur expo.dev et récupérez le projectId.
  • EAS CLI installé. npm install -g eas-cli@latest. La version 15 introduit le flag --json stable pour l'automatisation CI.
  • Node.js 20 LTS. Node 18 est en fin de vie depuis avril 2025 et n'est plus testé par Expo.
  • iOS 13+ / Android 7 (API 24)+. Ce sont les mêmes minima que Expo SDK 55 ; en dessous, le module d'update ne se compile plus.

Si vous avez un projet bare (sans Expo), la migration reste possible mais requiert de suivre le guide Installing expo modules in existing native projects. J'ai fait cette bascule pour l'app SoundCloud Pulse en 2024 et le point le plus délicat a été le Podfile : il faut ajouter use_expo_modules! et régénérer les pods.

Étape 1 : désinstaller CodePush proprement

Ne vous contentez pas d'un npm uninstall. CodePush s'accroche à plusieurs endroits du code natif, et laisser des restes provoque des crashs au démarrage, en particulier sur Android où le ReactInstanceManager tente d'appeler une méthode absente.

# 1. Retirer la dépendance JS
npm uninstall react-native-code-push

# 2. iOS : nettoyer les pods
cd ios && pod deintegrate && pod install && cd ..

# 3. Android : retirer la référence dans MainApplication.kt

Dans android/app/src/main/java/<package>/MainApplication.kt, supprimez toute ligne comme import com.microsoft.codepush.react.CodePush et l'override getJSBundleFile(). Sur iOS, dans AppDelegate.mm (ou .swift selon votre setup), retirez la ligne :

#import <CodePush/CodePush.h>
// et
return [CodePush bundleURL];

Remplacez-la par la valeur par défaut :

#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif

Enfin, purgez la clé CodePushDeploymentKey de Info.plist et de strings.xml. Un build de vérification (eas build --profile preview) confirmera qu'il ne reste plus de référence.

Étape 2 : installer et configurer expo-updates

Une fois CodePush parti, l'installation d'EAS Update tient en trois commandes :

npx expo install expo-updates
eas update:configure
eas build:configure

La commande eas update:configure édite automatiquement app.json (ou app.config.js) pour y ajouter la section updates et un runtimeVersion. Voici la configuration minimale que je recommande pour la production :

{
  "expo": {
    "name": "MonApp",
    "slug": "mon-app",
    "runtimeVersion": {
      "policy": "fingerprint"
    },
    "updates": {
      "url": "https://u.expo.dev/<PROJECT_ID>",
      "checkAutomatically": "ON_LOAD",
      "fallbackToCacheTimeout": 3000
    }
  }
}

La politique fingerprint (stable depuis SDK 52) calcule un hash déterministe de tout le code natif et des dépendances. C'est mon choix par défaut : elle évite les incidents où un ingénieur oublie de bumper manuellement la version après avoir ajouté un module natif.

Comprendre le runtime version

Le concept clé d'EAS Update (et souvent le plus mal compris) est la runtime version. Chaque build de votre app embarque une chaîne de caractères ; chaque update publiée est étiquetée avec la sienne. Le client ne téléchargera que les updates dont la runtime version correspond exactement à la sienne. Honnêtement, c'est là que la plupart des équipes s'auto-sabotent lors des premiers déploiements.

Ce mécanisme remplace la vérification binaire de CodePush, qui reposait sur la version du bundle iOS/Android. La différence est fondamentale : avec CodePush, une même clé de déploiement pouvait servir des bundles incompatibles si vous aviez ajouté un module natif entre deux releases. Avec EAS Update, c'est impossible — un mismatch de runtime version signifie pas d'update, et l'app continue avec son bundle embarqué.

Trois politiques existent :

  • appVersion : utilise version de app.json. Simple mais fragile ; oublier de bumper avant d'ajouter un module natif publie une update cassée.
  • nativeVersion : combine version et buildNumber. Meilleur, mais toujours manuel.
  • fingerprint : hash automatique. Recommandé. Calculé par @expo/fingerprint, il change dès qu'un fichier natif ou une dépendance native bouge.

Pour inspecter le fingerprint avant un build :

npx expo-doctor
npx @expo/fingerprint . --debug

Étape 3 : publier votre première update

Après un build initial soumis aux stores avec eas build --profile production --platform all, la publication d'une correction JavaScript devient triviale :

# Modifiez votre code JS/TS
git commit -am "fix: correction du bug de tri sur la liste des commandes"

# Publiez sur le canal production
eas update --branch production --message "Fix tri commandes"

La CLI construit le bundle avec Metro, l'upload sur le CDN Expo et crée une entrée dans le dashboard. Au prochain lancement de l'app, chaque client compare son runtime version au manifeste servi par u.expo.dev, télécharge la nouvelle version en tâche de fond, et l'active au redémarrage suivant (comportement par défaut identique à CodePush).

Pour forcer un rechargement immédiat après téléchargement (utile pour un correctif critique), utilisez l'API impérative :

import * as Updates from 'expo-updates';

async function verifierMiseAJour() {
  try {
    const update = await Updates.checkForUpdateAsync();
    if (update.isAvailable) {
      await Updates.fetchUpdateAsync();
      await Updates.reloadAsync();
    }
  } catch (error) {
    console.warn('Erreur de vérification :', error);
  }
}

N'appelez pas reloadAsync en plein flow de checkout ou pendant une lecture audio, car cela tue le processus JS. Je l'ai appris à mes dépens sur SoundCloud, où un rechargement au milieu d'un stream a fait chuter notre note App Store pendant 48 heures. Sale semaine.

Stratégie de canaux et de branches

EAS Update sépare deux notions que CodePush confondait :

  • Canal (channel) : associé à un build. Défini dans eas.json sous build.profiles.*.channel. Un canal peut être production, staging ou preview.
  • Branche (branch) : ligne d'updates. Vous publiez sur une branche, et vous mappez une branche à un canal via eas channel:edit.

Ce découplage est puissant. Chez Delivery Hero, nous l'utilisions pour un rollout progressif : la branche production-canary était mappée au canal production pour 10 % des utilisateurs, la branche production-stable pour les 90 % restants. Configuration :

eas channel:edit production --branch production-canary --percent 10
eas channel:edit production --branch production-stable --percent 90

Si les métriques Sentry restent vertes pendant 24 heures, on promeut : eas update:republish --branch production-stable --group <canary-group-id>. Sinon, on retire la branche canary et zéro utilisateur supplémentaire ne reçoit le correctif défectueux.

Rollback et gestion des incidents

Le meilleur moyen de rollback n'est pas de republier l'ancien code source : c'est de republier une update antérieure déjà validée. La commande dédiée :

# Lister les updates récentes de la branche
eas update:list --branch production --limit 10

# Republier l'update ID xyz sur la branche production
eas update:republish --group <GROUP_ID> --branch production

L'avantage : le bundle est déjà signé, testé et déployé sur le CDN. Aucun rebuild Metro, aucune surprise. En cas d'incident critique, comptez 30 secondes entre la décision et la propagation sur le CDN. Les clients récupèrent la version restaurée au lancement suivant.

Pièges fréquents en production

Voici les cinq erreurs que je vois le plus souvent en audit post-migration :

1. Modifier du code natif sans bumper le runtime

Même avec la politique fingerprint, un ingénieur qui édite manuellement un .pbxproj sans passer par un config plugin risque de casser la détection. La règle : toute modification native passe par un plugin ou un rebuild EAS.

2. Oublier la signature de code sur iOS entreprise

Pour les distributions enterprise (in-house Apple), Apple exige que les bundles JS soient signés. EAS Update supporte la signature de code depuis expo-updates 0.19 ; activez-la avec expo-updates code-signing.

3. Confondre Updates.channel et Updates.releaseChannel

releaseChannel est déprécié depuis SDK 50. Utilisez Updates.channel pour lire le canal courant et adapter votre logique feature-flag.

4. Publier trop souvent sur un canal chargé

Chaque update télécharge ~2 MB en moyenne. Publier 20 fois par jour sur une base de 500 000 MAU coûte de la bande passante et vide les batteries. Regroupez en releases nommées.

5. Ne pas tester le rollback en staging

Le premier rollback fait toujours peur. Faites-en un en staging chaque mois, avec des utilisateurs internes, pour valider la procédure et former l'équipe on-call.

Pour approfondir le débogage de ces updates en production, je vous renvoie à mon guide sur React Native DevTools et le débogage moderne, qui détaille la lecture des logs Hermes remontés par Sentry avec l'ID d'update attaché.

Questions fréquentes

EAS Update est-il gratuit ?

Oui, jusqu'à 1 000 utilisateurs actifs mensuels (MAU) sur le plan Free. Le plan Production démarre à 99 $/mois et inclut 200 000 MAU ; au-delà, la facturation est de 0,005 $ par MAU supplémentaire. Consultez la page tarifs Expo à jour, car les seuils évoluent chaque année.

Peut-on utiliser EAS Update sans Expo SDK ?

Oui. Le package expo-updates s'installe dans un projet React Native bare via npx expo install expo-updates, à condition d'avoir initialisé expo-modules avec npx install-expo-modules. Vous n'êtes pas obligé d'adopter le workflow managé.

Que se passe-t-il si un utilisateur est hors ligne au moment de la mise à jour ?

L'application continue de fonctionner avec le bundle embarqué (ou la dernière update téléchargée). Au retour de la connexion, checkForUpdateAsync détectera la nouvelle version et la téléchargera. Aucun blocage utilisateur, contrairement à un check synchrone bloquant.

Combien de temps prend la migration depuis CodePush ?

Pour une app de taille moyenne, comptez 1 à 2 jours de travail d'un ingénieur : ½ journée pour la désinstallation propre de CodePush, ½ journée pour la configuration EAS Update, ½ journée pour l'intégration CI/CD, et ½ journée de tests de non-régression sur les deux plateformes.

Peut-on modifier le code natif via EAS Update ?

Non, jamais. Une modification dans ios/, android/, un Podfile ou un build.gradle exige un nouveau binaire soumis à l'App Store et à Google Play. C'est une contrainte des stores, pas d'EAS Update — CodePush avait la même limitation.

À propos de l'auteur Marcus Adeyemi

Marcus is a senior React Native engineer based in Berlin with twelve years in mobile, the last seven of them in JavaScript-driven cross-platform work. He spent three years at SoundCloud rewriting the listener app's playback queue and offline-download stack on top of TrackPlayer, and another two and a half years at Delivery Hero leading the rider app's reliability work across 30+ markets. He started out as a native iOS developer at a smaller agency shipping white-label banking apps, which still shows up in his writing whenever the topic turns to bridging Swift code or wrangling Xcode build settings. Most of his posts here cover Reanimated 3 and Gesture Handler internals, CodePush versus EAS Update tradeoffs, and the kind of Hermes crash reports that only show up in production. He occasionally speaks at React Native EU.