Mobile

8 abr, 2026

CI/CD Mobile: o caos invisível que separa times comuns de times de alta performance

Publicidade

Você não percebe que seu processo de entrega mobile está quebrado… Até o dia em que precisa subir uma versão urgente — e tudo dá errado.

Build que falha. Assinatura inválida. Versão rejeitada na loja. Pipeline que ninguém entende. E, claro, aquele clássico:

“Funciona na máquina do fulano”.

Se você já viveu isso, parabéns — seu CI/CD não é um pipeline. É um ritual místico.

O problema: mobile não é backend (e nem web)

No backend, você faz deploy várias vezes por dia. No mobile, cada release é quase uma cirurgia.

Porque envolve:

  • build nativo
  • assinatura criptográfica
  • distribuição via loja
  • validação manual
  • tempo de aprovação

Ou seja: erro custa caro e demora pra corrigir.

O básico que quase ninguém faz direito

Um pipeline mobile decente precisa ter:

  • build automatizado
  • testes automatizados
  • versionamento consistente
  • distribuição automatizada
  • rollback planejado

Se falta um desses, você já está operando no modo risco.

Pipeline Android com GitHub Actions (exemplo real)

Aqui começa a diferença entre teoria e prática.

Workflow básico

name: Android CI

on:
push:
branches: [ "main" ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Setup JDK
uses: actions/setup-java@v3
with:
java-version: '17'

- name: Build APK
run: ./gradlew assembleRelease

Isso aqui funciona.

Mas ainda não é CI/CD de verdade.

É só “build automático”.

Assinatura de APK: onde muita gente se perde

Sem assinatura correta, você não publica nada.

E pior: não consegue atualizar versões existentes.

Usando secrets no pipeline

- name: Decode Keystore
run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > keystore.jks- name: Build Signed APK
run: |
./gradlew assembleRelease \
-Pandroid.injected.signing.store.file=keystore.jks \
-Pandroid.injected.signing.store.password=${{ secrets.KEYSTORE_PASSWORD }} \
-Pandroid.injected.signing.key.alias=${{ secrets.KEY_ALIAS }} \
-Pandroid.injected.signing.key.password=${{ secrets.KEY_PASSWORD }}

Aqui já começa o nível sênior:

  • segredo nunca fica no repo
  • pipeline controla credenciais
  • build é reproduzível

Versionamento automático (adeus versionCode manual)

Se você ainda incrementa versão na mão, você já perdeu.

Exemplo com Gradle

def versionCode = System.getenv("GITHUB_RUN_NUMBER") as Integer ?: 1

android {
defaultConfig {
versionCode versionCode
versionName "1.0.${versionCode}"
}
}

Resultado:

  • cada build tem versão única
  • evita conflito na Play Store
  • elimina erro humano

Distribuição automatizada (o divisor de águas)

Aqui separa pipeline bonito de pipeline útil.

Firebase App Distribution

- name: Upload to Firebase
uses: wzieba/Firebase-Distribution-Github-Action@v1
with:
appId: ${{ secrets.FIREBASE_APP_ID }}
token: ${{ secrets.FIREBASE_TOKEN }}
groups: testers
file: app/build/outputs/apk/release/app-release.apk

Agora sim:

  • QA recebe build automaticamente
  • sem APK no Slack
  • sem “manda o link aí”

Testes no pipeline (e o problema dos flaky tests)

Testar mobile não é trivial.

Principalmente UI.

Testes unitários

- name: Run Unit Tests
run: ./gradlew test

Testes instrumentados (Android)

- name: Run Instrumented Tests
run: ./gradlew connectedAndroidTest

 O problema real:

  • testes quebram sem motivo
  • emuladores instáveis
  • tempo alto de execução

Sênior não tenta “testar tudo”.

Ele testa o que dá confiança de deploy.

E o iOS? (spoiler: é mais chato 😅)

Aqui entra o terror chamado certificados.

Exemplo com Fastlane

lane :build do
match(type: "appstore")
build_app(scheme: "MyApp")
end

Pipeline com GitHub Actions

- name: Install Fastlane
run: gem install fastlane- name: Build iOS
run: fastlane build

O caos aqui envolve:

  • provisioning profiles
  • certificados expirando
  • integração com App Store

Se isso não estiver automatizado, seu time sofre.

Rollout gradual (deploy sem pânico)

Subir para 100% dos usuários direto é pedir problema.

Estratégia:

  • 5% dos usuários
  • monitora crashes
  • sobe para 20%, 50%, 100%

Isso evita:

  • incidentes massivos
  • avaliações negativas
  • rollback desesperado

Feature flags: deploy ≠ release

Aqui é mentalidade avançada.

Exemplo simples

if (remoteConfig.enableNewCheckout) {
showNewCheckout()
} else {
showOldCheckout()
}

Com isso:

  • você deploya código incompleto
  • ativa feature sem nova versão
  • faz rollback sem store

O erro clássico dos times mobile

Eles tratam CI/CD como:

“automatizar build”

Quando na real é:

automatizar confiança

O mindset sênior aqui

Sênior não pergunta:

“o build passou?”

Ele pergunta:

  • consigo reproduzir esse build?
  • consigo distribuir sem esforço manual?
  • consigo voltar atrás rápido?
  • consigo confiar nesse deploy?

Conclusão

CI/CD mobile não é sobre YAML bonito.

É sobre eliminar:

  • erro humano
  • dependência de pessoas
  • deploy manual
  • medo de release

Se seu pipeline ainda depende de:

  • alguém rodar build local
  • alguém subir APK manualmente
  • alguém lembrar de versão

Então você não tem CI/CD. Você tem um gargalo.