EAS Update em 2026: Guia de OTA no React Native e Expo

Guia prático de EAS Update em 2026: fingerprinting nativo, canais e branches, rollouts progressivos, rollback seguro, conformidade com a diretriz 4.5.4 da App Store e integração com GitHub Actions e Sentry.

EAS Update 2026: Guia OTA React Native

Atualizado: 13 de setembro de 2026

O EAS Update é o serviço da Expo para entregar atualizações Over-the-Air (OTA) do bundle JavaScript e dos assets de um app React Native sem passar por uma nova submissão à App Store ou Google Play, desde que a mudança não toque em código nativo. Em 2026, com o SDK 55 e o fingerprinting nativo estável, o EAS Update virou a forma padrão de corrigir bugs em minutos, rodar experimentos com canais e fazer rollback controlado (tudo isso dentro das regras da diretriz 4.5.4 da App Store, se você estruturar os canais e as runtime versions corretamente).

  • EAS Update entrega apenas mudanças em JavaScript, TypeScript e assets. Qualquer alteração em código nativo exige um novo build enviado para as lojas.
  • A partir do Expo SDK 53, o runtimeVersion baseado em fingerprint automático (policy "fingerprint") detecta mudanças nativas e evita crashes por incompatibilidade.
  • Canais (channels) desacoplam a distribuição da branch do build: um mesmo binário pode receber updates de production, staging ou preview sem rebuild.
  • Rollouts progressivos com --rollout-percentage permitem liberar um update para 5%, 25% ou 50% dos usuários antes do fanout total, com rollback em segundos via eas update:republish.
  • A diretriz 4.5.4 da App Store aceita OTA quando o update não altera propósito, comportamento essencial ou funcionalidades declaradas. Mudar um app de receitas para carteira cripto via OTA continua sendo motivo de banimento.
  • O EAS Update inclui um dashboard com telemetria de instalações, erros e adoção que integra com Sentry via source maps enviados no publish.

O que é o EAS Update em 2026

Então, indo direto ao ponto: o EAS Update é o CDN gerenciado da Expo para distribuir bundles JavaScript e assets diretamente para clientes em produção, ignorando o ciclo de revisão das lojas. Ele substitui o antigo expo publish (removido no SDK 50) e trabalha em conjunto com a biblioteca expo-updates, que roda embarcada no binário e faz o download, verificação de assinatura e swap atômico do bundle. Em projetos com bare workflow a mesma biblioteca funciona; não é preciso adotar o managed workflow completo para usar EAS Update.

A grande mudança de 2025 para 2026 foi a estabilização do fingerprinting: o CLI calcula um hash determinístico de tudo que compõe o "contrato" entre JS e nativo (pods do iOS, dependências Gradle, config plugins ativos, arquivos em ios/ e android/). Se um update tiver fingerprint diferente do binário instalado, o cliente simplesmente ignora, o que evita o crash clássico de "tentei atualizar um app que espera uma TurboModule que não existe". Honestamente, isso reduziu incidentes de fingerprint mismatch em quase todos os times com quem trabalho, principalmente aqueles que já rodavam múltiplas variantes.

Como configurar o EAS Update em um projeto novo

A configuração assume que você já tem o eas-cli instalado globalmente e um projeto Expo com eas.json. Se ainda não tem, comece com npm install -g eas-cli e eas init. Depois, adicione a biblioteca expo-updates:

npx expo install expo-updates
eas update:configure

O comando eas update:configure faz três coisas: cria a URL única do seu projeto no CDN, adiciona os campos updates.url e runtimeVersion ao app.json, e injeta os plugins nativos necessários no iOS e Android. Depois disso, o app.json mínimo fica assim:

{
  "expo": {
    "name": "MyApp",
    "slug": "my-app",
    "runtimeVersion": {
      "policy": "fingerprint"
    },
    "updates": {
      "url": "https://u.expo.dev/00000000-0000-0000-0000-000000000000",
      "fallbackToCacheTimeout": 0
    }
  }
}

O fallbackToCacheTimeout: 0 faz o app abrir imediatamente com o bundle em cache e baixar o novo update em segundo plano, que é o comportamento que a maioria dos usuários espera. Se seu app é usado offline com frequência (POS, logística, saúde), considere aumentar esse valor para dar chance de baixar o update antes de renderizar. Para publicar um update para o canal preview:

eas update --branch preview --message "Fix crash na tela de checkout"

Runtime version e fingerprint nativo

O runtimeVersion é o mecanismo que garante que um update JavaScript só é aplicado em binários compatíveis. Historicamente havia três políticas: "sdkVersion" (deprecada), "nativeVersion" (usa a versão do app) e "appVersion". Em 2026, a recomendação da Expo é usar "fingerprint", que analisa o projeto inteiro e produz um hash como a7f3b2c9d1e5. Toda vez que você adiciona um config plugin, atualiza uma dependência com código nativo (por exemplo, react-native-mmkv) ou muda algo em ios/ ou android/, o hash muda automaticamente.

Você pode inspecionar o fingerprint do projeto atual e comparar contra um build específico:

npx expo-doctor
npx @expo/fingerprint fingerprint:generate
eas build:list --platform ios --limit 5 --json | jq '.[].fingerprint'

Se o hash local não bate com o do último build de produção, você está prestes a fazer um update que ninguém vai receber. Nesse cenário, o correto é abrir um build novo (eas build --profile production) e só publicar o update contra o novo runtime version. Vou compartilhar um erro que vi em duas empresas diferentes: alguém instalou react-native-firebase em uma feature branch, publicou o OTA para produção "porque era só JS", e o app crashou em 100% dos devices. O fingerprint teria evitado o incidente. Se você está migrando de appVersion, publique primeiro um binário com "policy": "fingerprint" e só depois comece a publicar updates com a nova política.

Canais, branches e ambientes

Canais e branches são os dois eixos que organizam a distribuição. Branches são coleções versionadas de updates (parecidas com branches git). Canais são apontamentos configurados no binário no momento do build: cada binário lê de exatamente um canal, e o canal aponta para uma branch por vez. Essa indireção é o que permite mover a branch hotfix-checkout para o canal production sem gerar um novo binário.

Um setup típico de três ambientes fica assim no eas.json:

{
  "build": {
    "development": {
      "developmentClient": true,
      "channel": "development"
    },
    "preview": {
      "distribution": "internal",
      "channel": "preview"
    },
    "production": {
      "channel": "production",
      "autoIncrement": true
    }
  },
  "submit": {
    "production": {}
  }
}

Para promover um update de preview para produção sem republicar o código, use eas channel:edit:

eas update --branch hotfix-checkout --message "Fix carrinho vazio"
eas channel:edit preview --branch hotfix-checkout
# Testado, aprovado, agora produção:
eas channel:edit production --branch hotfix-checkout

Um padrão que eu uso em clientes Series B: manter uma branch production-YYYYMMDD por semana e apontar o canal production para ela. Quando algo dá errado, eas channel:edit production --branch production-YYYYMMDD-anterior reverte em segundos. Para uma visão mais profunda do processo de deploy incremental de features, veja o guia sobre rotas protegidas no Expo Router v7, que combina bem com feature flags por canal.

Rollout progressivo e rollback seguro

Rollouts progressivos foram adicionados como GA no CLI em 2025 e, na minha experiência, são a diferença entre "OTA como ferramenta profissional" e "OTA como roleta russa". Ao invés de liberar um update para 100% dos devices no canal, você começa com 5% e monitora antes de escalar:

eas update --branch production \
  --message "Refactor animação da splash" \
  --rollout-percentage 5

# Depois de 30min sem picos de crash:
eas update:edit --rollout-percentage 25
eas update:edit --rollout-percentage 50
eas update:edit --rollout-percentage 100

Se o Sentry acender vermelho no meio, você tem duas opções. A rápida é eas update:republish --branch production --group <id-do-update-anterior>, que republica o update anterior com um novo ID e passa a servi-lo para todos os devices que estavam recebendo o problemático. A alternativa é eas update:edit --rollout-percentage 0, que congela o rollout sem republicar. Isso é útil quando você não tem certeza se o update é ou não a causa do erro. Nunca use eas update:delete como resposta a incidente: devices que já baixaram o bundle continuam com ele, e você perde a auditoria. Republicar é sempre mais seguro que deletar.

EAS Update e as diretrizes da App Store (4.5.4)

Essa é a pergunta que mais aparece em consultorias: "OTA é permitido pela Apple?". Sim, com limites. A guideline 4.5.4 autoriza atualizações OTA de código JavaScript executado através do JavaScriptCore ou de um framework aprovado como o React Native, desde que "o propósito principal, funcionalidades essenciais e comportamento essencial do app não mudem". Na prática, isso significa que você pode corrigir bugs, ajustar layout, mudar copy, adicionar telas dentro de fluxos existentes, atualizar assets, alterar validações de formulário. Você não pode introduzir um novo modelo de negócio, adicionar tipos de conteúdo que não passaram por revisão (jogos de azar, dating, conteúdo adulto), ou transformar o app em outro produto.

A regra prática que eu sigo: se a diferença entre a versão antiga e a nova exigiria mudança da descrição na App Store ou triggeraria uma nova checagem de idade/categoria, faça um build novo e submeta. Se é uma correção pontual dentro do escopo existente, OTA é permitido. O Google Play é mais permissivo, mas segue princípios parecidos, e o Play Console rejeita apps que usam código dinâmico para contornar revisões de segurança. Se seu app tem componentes regulados (bancos, saúde, seguros), consulte o time de compliance antes de qualquer rollout. Nesse contexto complementa bem o post sobre armazenamento com criptografia usando MMKV, que passa por questões similares de conformidade.

Integração com CI/CD e GitHub Actions

Automatizar publish em CI evita o clássico "esqueci de publicar o hotfix". O workflow abaixo, que uso como base em quase todos os clientes, publica automaticamente no canal preview em cada PR e no canal production quando um push chega em main:

name: EAS Update

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  update:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}

      - run: npm ci

      - name: Publish preview update
        if: github.event_name == 'pull_request'
        run: eas update --branch pr-${{ github.event.number }} --message "PR ${{ github.event.number }}"

      - name: Publish production update
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
        run: eas update --branch production --auto

A flag --auto usa o hash do commit git como mensagem e a branch git como nome da branch EAS. Combinada com a action oficial expo-github-action, você ganha comentários automáticos no PR com QR code para escanear no Expo Go interno, o que reduz drasticamente o tempo entre "abriu o PR" e "produto testou no dispositivo real".

Debug, source maps e observabilidade com Sentry

Um update publicado sem source maps é uma caixa preta: quando um crash chega no Sentry, você vê linhas como anonymous @ index.android.bundle:1:243982 ao invés de nomes de função. O EAS Update gera source maps automaticamente, e o Expo tem uma integração oficial com o Sentry via o pacote @sentry/react-native que envia esses maps no momento do publish:

npx expo install @sentry/react-native

# Configure via sentry-expo config plugin
# app.config.ts:
plugins: [
  [
    "@sentry/react-native/expo",
    {
      organization: "meu-org",
      project: "meu-app",
      url: "https://sentry.io/"
    }
  ]
]

# No CI, apenas exponha SENTRY_AUTH_TOKEN
eas update --branch production --auto

Com isso, cada crash em produção vira um stack trace legível no Sentry. Combine com breadcrumbs de navegação (funciona out-of-the-box com Expo Router) e você tem contexto suficiente para diagnosticar 90% dos problemas sem precisar reproduzir localmente. Para uma discussão mais profunda sobre monitoramento de startup, o artigo sobre redução de cold start com TTI mostra como correlacionar métricas de performance com deploys OTA para detectar regressões introduzidas por updates.

Erros comuns e como evitá-los

Os padrões de falha que mais aparecem em auditorias de setups OTA:

  • Update publicado no canal errado. Um eas update --branch production rodado de laptop pessoal, sem revisão. Solução: bloqueie publish manual para production via política do EAS e force o fluxo via CI.
  • Fingerprint mismatch silencioso. O update sobe mas ninguém baixa porque o hash não bate. Solução: adicione uma etapa em CI que roda npx @expo/fingerprint fingerprint:compare contra o último build produtivo antes de publicar.
  • Assets grandes empurrados via OTA. Vídeos e imagens pesadas fazem o download demorar em redes lentas. Solução: mova assets grandes para um CDN próprio e referencie por URL, ou publique-os junto do binário se forem estáveis.
  • Ignorar telemetria de adoção. Um update com 60% de adoção após 7 dias indica bug de download ou fingerprint. Monitore eas update:view semanalmente.
  • Dependências com pós-instalação nativa. Adicionar algo como react-native-reanimated mesmo em versões que "não deveriam" tocar nativo pode invalidar o fingerprint. Sempre valide com fingerprint antes de publicar.

Se o app usa a Nova Arquitetura, o cuidado é ainda maior: updates que assumem uma TurboModule ausente ou renomeada crasham no boot. Já paguei esse pato em um projeto e não recomendo repetir. Recomendo ler o guia sobre migração para a Nova Arquitetura antes de configurar um pipeline OTA para um app que ainda está migrando.

Perguntas frequentes

EAS Update funciona em bare React Native ou só em apps Expo?

Funciona nos dois. O pacote expo-updates pode ser instalado em projetos bare que não usam o resto do ecossistema Expo; basta seguir o guia de instalação manual e adicionar os hooks nativos no AppDelegate.mm e MainApplication.kt. Nada obriga a adotar o managed workflow.

Qual a diferença entre EAS Update e CodePush?

O AppCenter CodePush da Microsoft foi oficialmente descontinuado em março de 2025. O EAS Update é o sucessor natural para projetos React Native e Expo, com vantagens em fingerprinting nativo, canais e integração com o resto do EAS (Build, Submit, Metadata). Migração direta é possível preservando a chave updates.url.

Quanto custa o EAS Update em 2026?

O plano Free inclui 1.000 updates ativos mensais e é suficiente para projetos pequenos. Planos pagos (Production, Enterprise) escalam por MAU (monthly active users) que efetivamente baixaram updates, começando em torno de US$ 99/mês. Consulte a página oficial de pricing da Expo para valores atualizados; grandes contas costumam ter negociação enterprise.

É possível fazer OTA de mudanças no código nativo?

Não. Qualquer coisa que compile para o binário (código Kotlin/Swift, dependências CocoaPods, config plugins que geram arquivos nativos) exige um novo build e submissão. O EAS Update só distribui o bundle JavaScript e assets. O fingerprint automático detecta essas mudanças e impede publish equivocado.

Como fazer rollback rápido de um update em produção?

Use eas update:republish --branch production --group <id-do-update-bom-anterior>. Isso republica o bundle antigo com um novo ID e passa a servi-lo imediatamente. Evite eas update:delete em incidentes: devices que já baixaram permanecem com o bundle problemático e a auditoria fica prejudicada.

Sobre o Autor Sofia Kowalczyk

Sofia is a mobile platform engineer with seven years of React Native experience, focused on developer tooling and CI/CD for mobile teams. She spent three years at Shopify on the Point of Sale app, where she helped move the team off Bitrise onto a custom EAS Build setup and wrote much of the internal testing harness for Detox flake reduction. Before Shopify she was at a Berlin-based scooter startup where she was the second mobile hire and shipped the first RN version of the rider app to roughly 40 cities. She runs a small consultancy now, mostly helping Series A and B startups untangle their Fastlane lanes and set up over-the-air update strategies that don't violate App Store guidelines. Her writing leans toward the unglamorous middle of the stack: provisioning profiles, monorepo setups with Nx, and why your bundle size keeps creeping up.