Cómo desplegar una actualización de app sin romper producción

Un backend roto se arregla en minutos: haces revert del deploy y la versión anterior vuelve a estar en línea. Una app publicada no funciona así. El binario vive en el teléfono del usuario, la store no te deja republicar la build anterior con el mismo número y quien ya actualizó se queda donde está. Por eso el despliegue de apps se gana antes de pulsar "publicar", no después. Tienes tres palancas: el rollout escalonado en la store, los feature flags dentro del binario y una estrategia de compatibilidad en el backend. Aquí va cuándo usar cada una.

Una app no es un servidor

Cuando despliegas una API y algo falla, el rollback es real: vuelves al commit anterior y en cinco minutos nadie recuerda el incidente. Con una app móvil no existe ese botón.

Publicas la versión 2, aparece un crash en el flujo de pago, subes corriendo la 3... y los usuarios que ya instalaron la 2 siguen con el bug hasta que actualicen. Muchos tienen la actualización automática desactivada. Y la store no te permite volver a publicar el binario de la 1 con el mismo versionCode o build number: para la tienda esa versión ya existió y no vuelve.

El control, por tanto, tiene que estar en cómo entregas, no en cómo deshaces. Un despliegue de app bien montado asume que el rollback instantáneo no existe y reparte el riesgo por otras vías.

Capa 1: rollout escalonado en la store

La primera palanca es no entregar la actualización a todos de golpe. Las dos tiendas lo permiten, con mecánicas distintas.

Google Play

En Play eliges un porcentaje de rollout entre el 1% y el 100%. Empiezas por un 5% o 10%, vigilas los crashes y las reseñas, y subes el dial cuando los números aguantan. Si algo va mal, puedes hacer halt: la publicación se congela y ningún usuario nuevo recibe esa versión. Play también permite frenar una release que ya está al 100%, así que aunque hayas completado el despliegue tienes un freno.

Cuidado con lo que el halt no hace: no revierte nada. Los usuarios que ya recibieron la versión con el fallo la conservan. Frenas la hemorragia, no la curas.

App Store

Apple funciona con phased release: una rampa fija de siete días que reparte la actualización en 1%, 2%, 5%, 10%, 20%, 50% y 100%. No mueves tú el dial, lo mueve el calendario. Lo que sí controlas es la pausa: puedes parar la rampa hasta 30 días, tantas veces como necesites, o soltar la actualización a todos cuando quieras.

Google PlayApp Store
ControlPorcentaje manual (1–100%)Rampa fija de 7 días
FrenarHalt, incluso tras el 100%Pausa de hasta 30 días
AcelerarSubes el dialRelease a todos ya
Rollback del binarioNoNo

Cuándo no usar el despliegue por fases: un hotfix de seguridad crítico. Si estás tapando una vulnerabilidad activa no quieres que tarde una semana en llegar al 100%; suéltalo a todos y asume el riesgo de la prisa. El escalonado es para releases normales, no para incendios.

Capa 2: feature flags y el interruptor de emergencia

El rollout escalonado decide quién recibe el binario. El feature flag decide qué se enciende dentro del binario que ya está instalado. Son cosas distintas y necesitas las dos.

La utilidad grande es el kill switch: apagar una funcionalidad rota para todos los usuarios sin pasar por revisión de la store. El checkout nuevo peta en producción, apagas el flag, la app vuelve al flujo estable anterior y ganas los dos o tres días que tardaría un hotfix en pasar revisión.

Para que esto funcione hay que montarlo con cuidado:

  • Evalúa el flag en el punto de uso, no una sola vez al arrancar. Si lees el valor solo en el splash, el usuario que ya tiene la app abierta no verá el apagado hasta que la reinicie, y en una emergencia eso es justo lo que no quieres.
  • Para los kill switches críticos ten un endpoint ligero que devuelva solo el estado de los interruptores y consúltalo cada minuto, fuera de la CDN que cachea el resto de la config. Un kill switch que tarda una hora en propagarse no es un kill switch.
  • No bloquees la interfaz esperando la red. Lee de la caché local de forma síncrona y refresca en segundo plano para el siguiente arranque.
  • Pasa siempre por una abstracción propia, no llames al SDK del proveedor desde media app. El día que cambies de herramienta lo agradecerás, y mientras tanto es lo único que te deja testear.

Cuándo no: no metas cada línea detrás de un flag. Cada flag es una rama de código que alguien tiene que mantener y limpiar. Un flag sin dueño ni fecha de retirada es deuda técnica con otro nombre. La regla sana es que todo flag nace con responsable y con fecha de caducidad.

Capa 3: el rollback que sí puedes hacer

No hay rollback de binario, pero sí hay maneras de deshacer daño. En orden de preferencia:

  1. Apagar el feature flag. Es lo más rápido y lo primero que deberías intentar. Si la funcionalidad rota estaba detrás de un flag, esto es tu rollback.
  2. Mantener el backend compatible hacia atrás. Aquí es donde se rompen la mayoría de las apps. Si cambias el contrato de la API y despliegas, revientas a todos los que aún no han actualizado el cliente, que son casi todos el primer día. La API tiene que seguir hablando con la versión anterior de la app durante bastante más tiempo del que crees.
  3. Code push para la lógica Dart. En Flutter puedes empujar cambios de código Dart sin pasar por la store con herramientas como Shorebird, con límites importantes: no toca código nativo y hay que respetar las políticas de las tiendas. Lo explicamos a fondo en nuestro artículo sobre code push con Shorebird.
  4. Forzar la actualización. Como último recurso, obligar a actualizar a una versión mínima. Es la peor experiencia para el usuario y solo se justifica cuando la versión antigua es un riesgo real, no por comodidad tuya.

Cómo lo montamos en Dribba

En quince años construyendo apps, algunas con millones de usuarios como la de CCOO, lo que más incidentes nos ha evitado no es una herramienta concreta, es el orden.

Primero, observabilidad. No puedes decidir si frenar un rollout si no ves el crash-free rate y los errores en tiempo real; a ciegas, el despliegue por fases solo retrasa el momento en que te enteras. Lo tratamos en detalle en observabilidad en apps móviles.

Segundo, un pipeline que haga siempre lo mismo. Un release manual es un release donde alguien se olvida de subir el dial o de mirar los crashes. Lo automatizamos con CI/CD sobre GitHub Actions.

Y tercero, un acuerdo previo de cuándo se frena. Definir antes del release qué crash-free rate o qué tasa de error dispara un halt evita la discusión a las tres de la mañana con la release ya al 50%.

En resumen

Desplegar una app sin sustos no va de reaccionar rápido, va de repartir el riesgo antes de publicar: sube el dial poco a poco, ten un flag que apague lo que se rompa y no cambies el contrato del backend por debajo de los usuarios que aún no han actualizado. El rollback instantáneo del backend no existe en móvil; lo que existe es un despliegue que asume que algo va a fallar.

Si estás montando el proceso de release de tu app o quieres una segunda opinión sobre el que ya tienes, hablamos.