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.apkAgora 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 testTestes instrumentados (Android)
- name: Run Instrumented Tests run: ./gradlew connectedAndroidTestO 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")endPipeline 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.




