Blue-green vs canary: despliegues más seguros para cambios en API y BD
Comparación de blue-green y canary para cambios en API y base de datos, con pasos prácticos para reducir el riesgo de downtime durante migraciones de esquema y actualizaciones móviles lentas.

Por qué los despliegues se vuelven arriesgados con cambios en el esquema y actualizaciones móviles lentas
Un despliegue puede parecer perfecto en pruebas y fallar en cuanto llega tráfico real. La razón habitual es que no solo cambia tu código: también cambian el contrato de la API y el esquema de la base de datos, y rara vez avanzan al mismo ritmo.
Las cosas se rompen cuando distintas partes del sistema no coinciden en qué es "correcto". Un backend nuevo espera una columna que aún no existe. Un backend antiguo escribe datos en un formato que el código nuevo ya no entiende. Incluso cambios pequeños como renombrar un campo, endurecer validaciones o cambiar un valor de enum pueden provocar errores en producción.
Las apps móviles suben la apuesta porque las versiones antiguas permanecen. Algunos usuarios actualizan en minutos y otros en semanas. Eso significa que tu backend debe atender varias generaciones de clientes a la vez. Si lanzas un cambio de API que solo funciona con la app más reciente, puedes romper pagos, onboarding o sincronizaciones en segundo plano para un grupo de usuarios sin notarlo de inmediato.
"Riesgo de downtime" no es solo que el sitio esté caído. En sistemas reales suele manifestarse como fallos parciales:
- un pico de errores 4xx/5xx en endpoints específicos mientras el resto parece bien
- inicios de sesión fallando porque tokens, roles o registros de usuario ya no coinciden con lo esperado
- problemas silenciosos de datos (valores por defecto equivocados, texto truncado, relaciones faltantes) que aparecen días después
- jobs en segundo plano atascados que generan una cola que tarda horas en vaciarse
Por eso los equipos comparan blue-green vs canary: buscas reducir el radio de impacto cuando los cambios no son perfectamente compatibles.
Blue-green y canary en lenguaje sencillo
Cuando la gente compara blue-green vs canary, suele responder a una pregunta: ¿prefieres un cambio grande y controlado o una prueba pequeña y cautelosa?
Blue-green: dos versiones completas y un cambio de tráfico
Blue-green significa mantener dos entornos completos al mismo tiempo. "Blue" es la versión actual que atiende a los usuarios. "Green" es la nueva versión, desplegada y probada en paralelo. Cuando estés listo, cambias el tráfico de blue a green.
Este enfoque es excelente para la previsibilidad. Puedes validar la versión nueva con ajustes parecidos a producción antes de que atienda usuarios reales y luego hacer un único corte limpio.
El rollback también es directo: si algo va mal tras el corte, rediriges el tráfico de vuelta a blue. Es casi un cambio instantáneo, pero caches, jobs en segundo plano y cambios en datos aún pueden complicar la recuperación.
Canary: enviar primero un pequeño porcentaje de tráfico
Canary implica que la nueva versión se expone primero a una porción pequeña de usuarios o peticiones. Si parece saludable, aumentas ese porcentaje paso a paso hasta que atienda a todos.
Canary es ideal cuando te preocupa comportamiento desconocido bajo tráfico real. Puedes detectar problemas temprano, antes de que la mayoría de usuarios los sientan.
El rollback consiste en reducir el porcentaje del canario a cero (o parar el ruteo a la nueva versión). Normalmente es rápido, pero no siempre limpio, porque algunos usuarios habrán creado datos o estado que ambas versiones deben manejar.
Una forma simple de recordar las compensaciones:
- Blue-green favorece cortes limpios y reversiones rápidas.
- Canary favorece aprender del tráfico real con un radio de impacto limitado.
- Ninguno arregla automáticamente el riesgo de la base de datos. Si los cambios de esquema no son compatibles, ambos pueden fallar.
- Canary depende de monitorización porque decides según señales en vivo.
- Blue-green suele requerir capacidad extra porque ejecutas dos stacks completos.
Ejemplo: si lanzas una API que a veces devuelve un campo nuevo, un canary te ayuda a ver si clientes antiguos fallan al recibir datos inesperados. Si el cambio exige renombrar una columna que el código antiguo no soporta, blue-green no te salvará a menos que la migración soporte ambas versiones.
Qué hace diferente a una migración de base de datos frente a un deploy de código
Un deploy de código suele ser fácil de revertir. Si la nueva versión se comporta mal, redeployas la build anterior y vuelves casi al estado previo.
Un cambio en la base de datos es distinto porque altera la forma de tus datos. Cuando filas se reescriben, columnas se eliminan o restricciones se endurecen, volver atrás rara vez es instantáneo. Incluso si reviertes el código, puede que no entienda el nuevo esquema.
Por eso el riesgo en las migraciones suele depender menos del método de despliegue y más de cómo está diseñada la migración.
Fundamentos de migraciones en línea
Las migraciones más seguras son las que funcionan mientras versiones antiguas y nuevas de la app corren al mismo tiempo. El patrón es simple: haz un cambio que sea seguro de ignorar, actualiza el código para usarlo y limpia después.
Una secuencia común expandir-then-contract es:
- Cambios aditivos primero: añade una columna nullable, una tabla nueva, un índice que no bloquee escrituras.
- Comportamiento dual: escribe en antiguo y nuevo, o lee del nuevo con fallback al antiguo.
- Backfill por separado: migra datos existentes en pequeños lotes.
- Corte: mueve la mayor parte del tráfico al comportamiento nuevo.
- Cambios destructivos al final: elimina columnas antiguas, quita caminos de código viejos, endurece restricciones.
Las migraciones "big bang" combinan los pasos más riesgosos en una sola release: locks largos, backfills pesados y código que asume que el nuevo esquema existe en todas partes.
Por qué las actualizaciones móviles lentas elevan la exigencia
Los clientes móviles pueden quedarse en versiones antiguas semanas. Tu backend debe aceptar solicitudes antiguas y devolver respuestas antiguas mientras la base de datos evoluciona.
Si una app antigua no envía un campo nuevo, tu servidor no puede de pronto exigir ese campo en la base de datos. Necesitas un periodo donde ambos comportamientos funcionen.
Qué estrategia reduce el riesgo de downtime en migraciones de esquema
La elección segura depende menos de la herramienta de despliegue y más de una pregunta: ¿pueden la versión antigua y la nueva de la app funcionar correctamente con el mismo esquema de base de datos durante un tiempo?
Si la respuesta es sí, blue-green suele ofrecer el menor downtime. Puedes preparar el cambio de base de datos primero, mantener el tráfico en el stack antiguo y luego cambiar de golpe al stack nuevo. Si algo falla, vuelves rápido.
Blue-green falla cuando la app nueva exige el esquema nuevo de inmediato. Ejemplos comunes incluyen borrar o renombrar una columna que la versión antigua aún lee, o añadir una restricción NOT NULL antes de que la app escriba el valor. En esos casos, revertir puede no ser seguro porque la base de datos ya es incompatible.
Canary es mejor cuando necesitas aprendizaje controlado. Un pequeño porcentaje de tráfico golpea la versión nueva primero, ayudándote a detectar casos límite como índices faltantes, patrones de consulta inesperados o jobs que se comportan distinto bajo carga. La contrapartida es que debes mantener ambas versiones funcionando, lo que normalmente exige cambios de base de datos compatibles hacia atrás.
Una regla práctica para decidir
Al sopesar blue-green vs canary para el riesgo de migración de esquema:
- Elige blue-green cuando puedas mantener el cambio de esquema aditivo y compatible, y quieras un corte rápido y limpio.
- Elige canary cuando no estés seguro de cómo se comportará el cambio en producción o esperes que datos raros importen.
- Si la migración obliga a un cambio rompedor inmediato, no elijas entre blue-green y canary: cambia el plan a expandir-then-contract.
Cómo se ve "compatible" en la práctica
Si añades un campo nuevo a la tabla orders, un camino seguro es: añadir la columna como nullable, desplegar la app que la escribe, backfill de filas antiguas y luego aplicar las restricciones. En ese escenario, blue-green te da un corte limpio y canary te da advertencias tempranas si algún camino de código aún asume la forma antigua.
Cómo las actualizaciones móviles lentas cambian la elección de despliegue
Los usuarios web suelen refrescar. Los móviles no.
En iOS y Android la gente puede quedarse en versiones antiguas semanas o meses. Algunos nunca actualizan hasta que la app lo fuerza. Eso convierte al cliente "antiguo" en una prueba permanente de tu compatibilidad hacia atrás.
Esto cambia el objetivo de "desplegar sin downtime" a "mantener varias generaciones de clientes funcionando a la vez." En la práctica, móvil suele empujarte hacia un pensamiento parecido al canary para APIs, aunque uses blue-green para infraestructura.
Cambios compatibles hacia atrás vs versionado de API
La mayoría de las veces quieres cambios compatibles hacia atrás porque permiten que clientes antiguos y nuevos compartan los mismos endpoints.
Ejemplos compatibles: añadir campos nuevos, aceptar formas de payload antiguas y nuevas, conservar los campos de respuesta existentes y evitar cambios de significado. El versionado de API para móviles es útil cuando el comportamiento debe cambiar (no solo añadir datos) o cuando hay que eliminar o renombrar campos.
Ejemplo: añadir un campo opcional marketing_opt_in suele ser seguro. Cambiar cómo se calcula price normalmente no lo es.
Planificar una ventana de deprecación
Si necesitas un cambio rompedor, trata el fin del soporte como una decisión de producto. Una ventana de deprecación útil se mide por "usuarios activos todavía en versiones antiguas", no por días en el calendario.
Secuencia práctica:
- Publica el backend que soporte clientes antiguos y nuevos.
- Publica la app nueva y vigila la adopción por versión.
- Advierte o restringe solo cuando las versiones antiguas caigan por debajo de un umbral seguro.
- Elimina el comportamiento antiguo al final, con un plan de rollback.
Paso a paso: un patrón de rollout seguro para cambios en API + BD
Cuando cambias API y base de datos a la vez, el plan más seguro suele tener dos o tres etapas. Cada paso debe ser seguro de desplegar por sí mismo, incluso si los usuarios mantienen una app antigua semanas.
Un patrón de rollout que evita romper clientes antiguos
Empieza con un cambio aditivo en la base de datos. Añade columnas o tablas nuevas, evita renombrar o eliminar, permite nulls donde haga falta y usa defaults para que el código antiguo no choque con restricciones.
Luego lanza código que tolere ambas formas de datos. Las lecturas deben aceptar "campo antiguo ausente" y "campo nuevo presente". Las escrituras deben seguir escribiendo el campo antiguo por ahora y opcionalmente escribir el nuevo también.
Secuencia típica:
- Añade piezas nuevas al esquema (columnas, tablas, índices) sin eliminar las antiguas.
- Despliega código que lea tanto lo antiguo como lo nuevo y no falle con nulls.
- Backfill de filas existentes en pequeños lotes, luego verifica conteos, tasas de nulos y rendimiento de consultas.
- Cambia la ruta de escritura al campo nuevo, manteniendo lecturas con fallback.
- Cuando las versiones antiguas desaparezcan, elimina el campo viejo y limpia el código.
Backfill y verificación: donde se esconden los outages
Los backfills fallan con frecuencia porque se tratan como un script rápido. Ejecútalos gradualmente, vigila la carga y verifica resultados. Si el comportamiento nuevo necesita un índice, añádelo antes de cambiar lecturas o escrituras, no después.
Ejemplo: añades phone_country_code para mejorar el formateo. Primero añade la columna nullable, actualiza la API para aceptarla pero seguir funcionando si falta, backfill desde números existentes y luego empieza a escribirla en nuevos registros. Semanas después, cuando las apps antiguas hayan desaparecido en su mayoría, quita la ruta de parsing heredada.
Herramientas que hacen más seguras ambas estrategias (sin complicar demasiado)
No necesitas una configuración complicada para reducir riesgos. Unas cuantas prácticas evitan sorpresas cuando APIs y esquemas se mueven a ritmos distintos.
Lectura y escritura dual (temporal)
Dual-write significa que la app escribe datos en el lugar antiguo y en el nuevo durante la transición (por ejemplo, users.full_name y un nuevo users.display_name). Dual-read significa que puede leer de cualquiera, prefiriendo el campo nuevo pero haciendo fallback al antiguo.
Esto te da tiempo para actualizaciones lentas de clientes, pero debe ser un puente temporal. Decide cómo lo limpiarás, registra qué ruta se usa (nuevo vs fallback) y añade comprobaciones básicas para asegurar consistencia.
Feature flags para cambios de comportamiento
Las feature flags permiten desplegar código sin activar el comportamiento arriesgado. Son útiles para blue-green y canary porque separan "desplegar" de "activar".
Ejemplo: despliega soporte para un campo de respuesta nuevo pero mantiene la respuesta antigua hasta estar listo. Luego habilítalo para un grupo pequeño, observa errores y sube la exposición. Si algo falla, desactiva la flag sin redeployar.
Mentalidad de testing de contratos (la API es una promesa)
Muchas incidencias de migración no son realmente problemas de base de datos: son expectativas rotas del cliente.
Trata la API como una promesa. Evita quitar campos o cambiar significados. Haz que campos desconocidos sean opcionales. Los cambios aditivos son normalmente seguros; los rompientes deben esperar a una nueva versión de API.
Jobs de migración de datos confiables
Los backfills deben ser aburridos y repetibles: seguros para ejecutar dos veces, con reintentos, fáciles de rastrear y limitados para no provocar picos de carga.
Errores comunes que causan outages durante migraciones
La mayoría de los outages ocurren cuando una release asume que todo se mueve a la vez: todos los servicios se despliegan, los datos están limpios y todos los clientes se actualizan rápido. Los sistemas reales no funcionan así, especialmente con móvil.
Patrones de fallo frecuentes:
- Eliminar o renombrar una columna demasiado pronto. Código antiguo, jobs o apps móviles viejas aún la usan.
- Suponer que los clientes actualizan rápido. Las releases móviles tardan en llegar y muchos usuarios no actualizan enseguida.
- Ejecutar migraciones que bloquean tablas en horas pico. Un índice o cambio simple puede bloquear escrituras.
- Probar solo con datos limpios. Producción tiene nulos, formatos raros, duplicados y valores heredados.
- No tener un plan real de rollback para código y datos. "Volvemos a desplegar la versión anterior" no sirve si el esquema cambió.
Ejemplo: renombras status a order_status y despliegas la nueva API. La web funciona. Clientes móviles antiguos siguen enviando status y la API rechaza las peticiones, provocando fallos en checkouts. Si eliminaste la columna, restaurar el comportamiento no es un cambio rápido.
Un mejor enfoque por defecto es: hacer cambios en pasos pequeños y reversibles, mantener rutas antiguas y nuevas funcionando juntas y documentar qué harás si los métricas suben (cómo redirigir tráfico, qué feature flag apaga el comportamiento nuevo y cómo validar y reparar datos si un backfill va mal).
Checklist rápido antes de desplegar
Justo antes de una release, una lista corta evita problemas que llevan a rollbacks nocturnos. Esto importa sobre todo cuando cambias API y base de datos a la vez, especialmente con actualizaciones móviles lentas.
Cinco comprobaciones que evitan la mayoría de outages
- Compatibilidad: confirma que versiones antiguas y nuevas de la app funcionan contra el mismo esquema. Una prueba práctica es ejecutar la build de producción actual contra una base de staging con la migración aplicada.
- Orden de migración: asegúrate de que la primera migración sea aditiva y programa cambios destructivos (eliminar columnas, endurecer restricciones) para más tarde.
- Rollback: define la forma más rápida de deshacer. Para blue-green es cambiar el tráfico de vuelta. Para canary es enviar 100% al sistema estable. Si el rollback requiere otra migración, no es simple.
- Rendimiento: mide latencia de consultas tras el cambio de esquema, no solo la corrección. Un índice faltante puede hacer que un endpoint parezca caído.
- Realidad del cliente: identifica la versión móvil más antigua activa que aún llama a tu API. Si un porcentaje significativo está en ella, planifica una ventana de compatibilidad más larga.
Escenario de sanity rápido
Si añades preferred_language, despliega el cambio en la base de datos primero como nullable. Luego publica el servidor que lo lee cuando existe pero no lo exige. Solo cuando la mayor parte del tráfico use apps actualizadas hazlo requerido o elimina el comportamiento antiguo.
Ejemplo: añadir un campo nuevo sin romper apps móviles antiguas
Imagina que añades country al perfil y el negocio quiere que sea obligatorio. Puede romper en dos frentes: clientes antiguos no lo envían y la base de datos puede rechazar escrituras si aplicas NOT NULL demasiado pronto.
Una vía más segura son dos cambios separados: primero añade el campo de forma compatible hacia atrás y luego exige "requerido" cuando los clientes estén actualizados.
Cómo se ve con blue-green
Con blue-green despliegas la versión nueva junto a la antigua. Aun así necesitas que el cambio de la base de datos sea compatible con ambas versiones.
Flujo seguro:
- desplegar la migración (añadir
countrycomo nullable) - desplegar la versión green que acepta
countryausente y usa un fallback - probar flujos clave contra green (signup, editar perfil, checkout)
- cambiar el tráfico
Si algo va mal, vuelves a blue. La clave es que volver solo funciona si el esquema sigue soportando la versión antigua.
Cómo se ve con canary
Con canary expones el comportamiento nuevo a una pequeña porción (1%-5%) y vigilas errores de validación por campo faltante, cambios de latencia y fallos inesperados en la base de datos.
Una sorpresa común son clientes antiguos que envían actualizaciones de perfil sin country. Si la API lo trata como obligatorio de inmediato verás 400s. Si la BD aplica NOT NULL, podrías ver 500s.
Secuencia segura:
- añadir
countrycomo nullable (o con un default seguro como "unknown") - aceptar la ausencia de
countryde clientes antiguos - backfill de
countrypara usuarios existentes en background jobs - exigir "requerido" más tarde (primero en la API, luego en la base de datos)
Después del release, documenta qué pueden enviar los clientes antiguos y qué garantiza el servidor. Ese contrato escrito evita la misma rotura en la siguiente migración.
Si construyes con AppMaster (appmaster.io), la misma disciplina de rollout aplica aunque puedas generar backend, web y apps móviles nativas desde un único modelo. Usa la plataforma para publicar cambios aditivos y lógica tolerante primero, y endurece las restricciones solo cuando la adopción lo justifique.
FAQ
Blue-green ejecuta dos entornos completos y cambia todo el tráfico de golpe. Canary publica la nueva versión a un pequeño porcentaje primero y va ampliando según lo que veas en tráfico real.
Usa blue-green cuando quieras un corte limpio y rápido y estés seguro de que la nueva versión es compatible con el esquema de base de datos actual. Es especialmente útil cuando el riesgo principal está en el código de la aplicación y no en comportamientos inesperados en producción.
Usa canary cuando necesites aprender del tráfico real antes de desplegar a todo el mundo, por ejemplo si patrones de consulta, datos en casos límite o jobs en segundo plano pueden comportarse distinto en producción. Reduce el radio de impacto, pero exige supervisión estrecha y estar listo para frenar el despliegue.
No. Si el cambio de esquema rompe la compatibilidad (por ejemplo renombrar o eliminar una columna que el código antiguo aún usa), tanto blue-green como canary pueden fallar. La solución más segura es diseñar una migración en línea que soporte versiones antiguas y nuevas al mismo tiempo.
Los usuarios móviles pueden quedarse en versiones antiguas semanas o meses, así que tu backend debe soportar varias generaciones de clientes a la vez. Eso normalmente implica mantener compatibilidad hacia atrás durante más tiempo y evitar cambios que exijan que todos los clientes se actualicen inmediatamente.
Empieza con cambios aditivos que el código antiguo pueda ignorar, como columnas nuevas nullable o tablas nuevas. Luego despliega código que acepte ambas formas de datos y escriba de manera compatible, realiza backfills graduales, cambia el comportamiento y solo entonces elimina campos antiguos o endurece restricciones.
Haz una lista de lo que los clientes antiguos envían y esperan recibir, y evita quitar campos o cambiar significados. Prefiere añadir campos opcionales, acepta ambas formas de petición y retrasa la validación “requerida” hasta que la adopción sea alta.
Dual-write escribe en ambos lugares (antiguo y nuevo) durante la transición; dual-read lee preferentemente el nuevo campo y hace fallback al antiguo. Úsalo temporalmente, controla qué ruta se usa y planifica claramente cuándo limpiarlo.
Las feature flags permiten desplegar código sin activar el comportamiento arriesgado. Así puedes separar "desplegar" de "activar": habilitas para un grupo pequeño, observas y, si hay picos de errores, apagas la flag sin redeployar.
Eliminar o renombrar columnas demasiado pronto, forzar NOT NULL antes de que los clientes envíen el valor y ejecutar migraciones que bloquean tablas en horas pico son causas frecuentes. Otra causa es asumir que los datos de prueba son iguales a producción; los backfills fallan por nulos o formatos raros.


