05 ago 2025·8 min de lectura

Reconstruir una app sin cambiar la base de datos: plan de corte que funciona

Aprende a reconstruir una app sin cambiar la base de datos con un plan de corte seguro que preserva cuentas e historial, usando despliegue por etapas y rollback.

Reconstruir una app sin cambiar la base de datos: plan de corte que funciona

¿Qué puede salir mal cuando reconstruyes pero mantienes la misma base de datos?

Mantener tu base de datos actual puede parecer más seguro que una migración completa. Pero una reconstrucción aún puede fallar de formas que los usuarios perciben como “datos faltantes”. La mayoría de los problemas vienen de desajustes pequeños entre lo que la app antigua asumía y lo que la nueva realmente hace.

Los fallos más dolorosos aparecen como pérdida de cuentas o historiales “vacíos”. Las filas pueden seguir ahí, pero la nueva app las lee de forma distinta. Causas comunes incluyen cambios en el hash de contraseñas, diferente normalización de correos (sensibilidad a mayúsculas y espacios), nuevos formatos de ID de usuario o un proveedor de auth nuevo que no mapea bien las identidades existentes.

Reconstruir sin cambiar la base de datos también corre el riesgo de una corrupción sutil durante el corte. Si la app antigua y la nueva escriben al mismo tiempo, puedes bifurcar la realidad: registros duplicados, actualizaciones sobrescritas o eventos registrados en un sistema pero no en el otro.

Un plan de corte debe proteger ante todo: los inicios de sesión y el mapeo de identidades, el orden de las escrituras (sin doble escritura ni actualizaciones perdidas), las trazas de auditoría e historial (timestamps, cambios de estado, facturas, mensajes) y los jobs en segundo plano (emails, webhooks, procesos de facturación) que podrían reprocesar datos antiguos.

A veces mantener la base de datos es la decisión equivocada. Si el esquema es inseguro (secretos expuestos, evidencia de inyección SQL, constraints faltantes) o el modelo de datos es tan inconsistente que cada pantalla necesita lógica especial, puede que gastes más tiempo parchando que reconstruyendo.

Un ejemplo simple: tu reconstrucción cambia las reglas de “membresía de organización”. Los datos antiguos permiten múltiples roles por usuario, pero la nueva app espera uno solo. Los usuarios de repente “pierden acceso” porque el código nuevo elige el rol incorrecto. Detectar esto temprano es para lo que sirven las comprobaciones de compatibilidad.

Define las metas y elige un enfoque de corte

Empieza escribiendo las promesas que te niegas a romper. Esto evita que la reconstrucción se convierta en un rewrite arriesgado donde la gente pierde acceso, historial o confianza.

La mayoría de equipos acaban con promesas como: las cuentas existentes siguen funcionando, los datos históricos se mantienen intactos y el downtime es casi nulo o programado y corto. Si alguna promesa puede ceder (por ejemplo, una ventana de mantenimiento de 10 minutos está bien), decídelo ahora, no durante la noche de lanzamiento.

Convierte “éxito” en comprobaciones claras que todo el equipo acepte. Por ejemplo: los usuarios pueden iniciar sesión y ver el mismo rol y permisos; las páginas clave muestran los mismos totales (pedidos, facturas, mensajes); las nuevas acciones crean exactamente una escritura en la base de datos; los jobs críticos siguen ejecutándose; y el soporte tiene un guion claro para los problemas más probables.

Dos estilos de corte comunes

Un cambio programado (big-bang) redirige todo el tráfico a la nueva app en un momento planificado. Es más sencillo de razonar, pero la apuesta es mayor si algo falla.

Un cambio gradual desplaza el tráfico por pasos (por porcentaje, por grupo de usuarios o por funcionalidad). Toma más tiempo, pero reduce el riesgo porque puedes aprender del uso real antes de ir a todo.

Define el rollback como algo que puedas decidir rápido y ejecutar sin adivinanzas. Sé explícito sobre lo que verán los usuarios (un periodo de solo lectura vs. volver completamente a la app antigua) y qué ocurre con los datos creados durante el intento.

Mapea los datos actuales y los recorridos de usuario asociados

Obtén una imagen clara de lo que realmente hay en producción hoy, no solo lo que dice el ERD. Las reconstrucciones suelen romperse porque una tabla “pequeña” o un job en background estaba haciendo mucho trabajo en silencio.

Empieza inventariando los datos detrás de tus flujos más usados. Centra la atención en las tablas que deciden quién es un usuario, a qué puede acceder y por qué te pagan. En la práctica, eso suele significar tablas de identidad y acceso, tablas de facturación/suscripción, tus registros centrales de dominio y ajustes, y las tablas de logs e integraciones que explican qué pasó (y por qué).

Luego, mapea las relaciones que no puedes romper. Busca claves foráneas (o “enlaces suaves” que solo se aplican en código), constraints de unicidad y registros que “deben existir” (como una org por defecto o un rol owner). Escribe lo que debe permanecer verdadero después del corte, por ejemplo: cada suscripción se mapea a exactamente una cuenta activa.

Finalmente, documenta dónde se crean o editan los datos. Hazlo simple: formularios UI, herramientas de admin, jobs en background, importaciones y webhooks. Captura las particularidades de las que depende producción, aunque sean desordenadas.

Ejemplo: si tu app actual escribe org_id de dos maneras distintas (registro web vs onboarding asistido por ventas), tu nueva app debe manejar ambas.

Haz compatibles la autenticación y la identidad de usuario

La autenticación suele ser lo primero que falla en producción. Desajustes pequeños en IDs de usuario, membresía de org o roles pueden dejar a la gente fuera o, peor, iniciar sesión en la cuenta equivocada.

Confirma qué trata tu sistema como “la” identidad. ¿Es un user_id numérico, un UUID, un email o un ID de proveedor externo (Google, GitHub)? Elige la clave estable y úsala de la misma manera en todas partes: membresía de org, permisos, propiedad de facturación y logs de auditoría.

Ejecuta una comprobación de compatibilidad que capture la mayoría de los problemas:

  • El mismo ID de usuario mapea al mismo email y org en ambas apps.
  • Las tablas de org y roles significan lo mismo (no solo comparten nombres).
  • Las reglas de unicidad coinciden con la vida real (especialmente unicidad de email).
  • Los usuarios eliminados o deshabilitados se comportan igual.
  • Campos de auditoría como created_at y last_login se preservan.

El manejo de contraseñas necesita cuidado extra. No rehasees contraseñas a menos que sea realmente necesario. Si la app antigua usa bcrypt y la nueva espera argon2, sigue verificando con el hash antiguo y migra en el próximo login (almacena el nuevo hash después de un inicio de sesión exitoso). Eso evita reinicios forzados de contraseña, tickets de soporte y churn.

Sesiones y tokens son la siguiente trampa. Durante un despliegue por etapas, pueden existir sesiones antiguas. Decide si las respetas, las expirás o ejecutas ambos validadores de tokens por una ventana corta. Si rotas claves de firma, plánificalo como un release, no como una sorpresa.

Siempre aparecen casos límite: emails duplicados de importaciones antiguas, usuarios en múltiples orgs y nombres de roles antiguos como "owner" vs "admin". Arregla estos casos con una capa de mapeo, no con ediciones ad hoc en la base de datos.

Diseña tus cambios de esquema y API para compatibilidad hacia atrás

La compatibilidad hacia atrás mantiene el corte tranquilo. Empieza por acordar qué no debe cambiar porque la app antigua aún depende de ello.

Trata la base de datos como un contrato: nombres de tablas, columnas clave, claves primarias y el significado de los valores existentes. Si el código antiguo espera que users.id sea un UUID y users.email sea único, mantén eso estable durante la transición.

Luego separa cambios seguros de los arriesgados.

Los cambios de solo añadir suelen ser seguros: nuevas columnas nullable, nuevas tablas y nuevos índices.

Los cambios que rompen (renombrar columnas, cambiar tipos de datos, endurecer constraints) deberían esperar hasta que la nueva app sirva todo el tráfico.

Un patrón práctico es expandir primero, cambiar después y contraer al final. Ejemplo: añade users.timezone como nullable, lanza la nueva app para que lo escriba, rellena filas antiguas y solo entonces considera ponerlo NOT NULL.

Lista de verificación práctica de compatibilidad

Antes de aplicar cualquier migración, asegúrate de:

  • Las nuevas columnas empiezan como nullable o con un default.
  • Los nuevos enums aceptan valores antiguos (o los mapas de forma segura).
  • Las restricciones se endurecen gradualmente (validar después, aplicar después).
  • Las APIs antiguas siguen funcionando aunque la app nueva añada campos.
  • Tienes un plan para backfill y verificación de filas existentes.

Las filas antiguas son la trampa. Si las nuevas reglas de validación rechazan datos legacy (teléfonos faltantes, estados inválidos, nombres en blanco), no “arregles” bloqueando logins. Usa lecturas tolerantes, scripts de migración y avisos dirigidos (pide a los usuarios que completen campos faltantes después de iniciar sesión).

Plan de despliegue por etapas paso a paso (solo lectura a tráfico completo)

Protege historiales y totales
Revisamos facturas, marcas de tiempo y trazas de auditoría para que los usuarios no vean páginas vacías.

Trata el corte como una serie de pequeñas apuestas. Cada etapa debe ser reversible y tener una prueba clara de que la app nueva se comporta como la antigua.

Etapas (de más seguro a más arriesgado)

Empieza apuntando la app nueva a datos de producción, pero limita lo que puede hacer:

  • Ejecuta la app nueva en modo solo lectura. Permite que los usuarios naveguen, busquen y vean historial, pero bloquea acciones que escriban.
  • Añade tráfico sombra para algunos endpoints clave. La app antigua sirve las respuestas, mientras la nueva ejecuta las mismas peticiones en segundo plano y comparas salidas.
  • Habilita un pequeño conjunto de acciones de escritura para un grupo muy reducido usando feature flags (primero usuarios internos, luego un pequeño porcentaje).
  • Aumenta el tráfico gradualmente (10 %, 25 %, 50 % y luego 100 %), con puertas de monitorización en cada paso.

Entre etapas, pausa y revisa lo aprendido. Si algo falla, detén el despliegue y arregla antes de aumentar el riesgo.

Criterios para avanzar

Escribe checks de “go/no-go” antes para que las decisiones no se tomen bajo presión:

  • La tasa de errores y la latencia se mantienen dentro de un umbral pequeño comparado con la app antigua.
  • Las respuestas sombra coinciden en campos clave (permisos, totales, precios, estados).
  • No hay escrituras inesperadas en modo solo lectura (confírmalo con logs DB).
  • Los tickets de soporte y las quejas de usuarios no aumentan tras cada subida.
  • El runbook de rollback está probado y puede restaurar la app antigua en minutos.

Planifica las escrituras durante la transición (evita bifurcaciones de datos)

La forma más rápida de romper un corte es permitir que ambas versiones escriban los mismos registros de maneras distintas. Necesitas una regla clara: en cada paso de rollout, quién puede escribir qué.

Elige una única fuente de verdad para escrituras. A menudo, la app antigua sigue escribiendo mientras la nueva corre en modo solo lectura. Luego haces el cambio: la app nueva pasa a ser la escritora y la antigua queda en solo lectura o con endpoints de escritura bloqueados. Esto previene un “split brain” silencioso.

Las escrituras duales (ambas apps escribiendo) suenan seguras, pero suelen crear bifurcaciones que solo notas días después, como estados de suscripción desajustados o facturas duplicadas. Solo haz escrituras duales si puedes probar que es seguro.

Si debes soportar escrituras duales por una ventana corta, haz que los conflictos sean previsibles:

  • Haz cada escritura idempotente usando un request ID estable.
  • Define reglas de conflicto por adelantado (last-write-wins para campos de perfil, pero nunca para pagos).
  • Añade una tabla pequeña de write-log (request ID, user ID, timestamp, entidad principal tocada).
  • Rechaza campos desconocidos o mapeos por “mejor suposición”.
  • Pon feature flags estrictos alrededor de rutas nuevas de escritura para poder apagarlas rápido.

Un enfoque práctico: mientras migras registros de sign-up, permite que la app nueva cree cuentas, pero dirige resets de contraseña y cambios de facturación por la app antigua hasta verificar cada camino de escritura.

Monitorización y validación de datos durante el corte

El corte no es solo un deploy. Es un experimento en vivo, especialmente al mover tráfico por etapas.

Empieza con un conjunto reducido de señales que indiquen si los usuarios aún pueden iniciar sesión, navegar y guardar cambios:

  • Tasa de éxito de login y éxito de reset de contraseña
  • Tasa de error por endpoint (5xx, timeouts, errores de auth)
  • Fallos y reintentos de escrituras (creaciones, actualizaciones, pagos, invitaciones)
  • Latencia en p95/p99
  • Salud de colas y jobs en background (emails, webhooks, sync de facturación)

Los números no bastan. Valida los datos en sí durante cada paso de tráfico (5 % luego 25 % luego 100 %): compara conteos de filas y totales para las entidades que tocas, verifica la consistencia de actividad reciente (claves foráneas, timestamps, estados) y revisa a mano las últimas escrituras en busca de anomalías obvias.

Superpón señales de usuario. Un pico en tickets de soporte, una caída tras el login o capturas repetidas de “algo salió mal” suelen mostrar problemas antes que los dashboards.

Define umbrales de decisión antes de empezar. Ejemplo: si el éxito de logins baja 2 % durante 10 minutos, o los fallos de escritura superan 0.5 %, pausa el despliegue e investiga. Si la causa no es obvia pronto, haz rollback.

Plan de rollback que puedas ejecutar en minutos

Sprint de arreglo 48-72 horas
La mayoría de arreglos se entregan en 48-72 horas con verificación experta.

El rollback no es un fracaso. Es la válvula de seguridad que te permite proteger cuentas e historial.

Define disparadores claros de rollback

Elige un pequeño conjunto de alarmas que exijan acción, no debate:

  • Fallos de login o signup por encima de un umbral (por ejemplo, 2 % durante 5 minutos)
  • Escrituras sospechosas (campos obligatorios ausentes, nulls inesperados, registros duplicados)
  • Regresión de rendimiento mayor (timeouts, CPU de DB al 100 %, backlog de colas)
  • Señales de seguridad (bypass de auth, cheques de permiso que no se ejecutan)

Cuando suene un disparador, una sola persona debe tener la autoridad para llamar al rollback. El resto ejecuta.

Pasos de rollback que lleven minutos, no horas

Escribe esto como una checklist de emergencia y ensáyala una vez:

  • Cambiar el tráfico de vuelta a la app antigua (load balancer, feature flag o toggle de despliegue).
  • Congelar las escrituras en la app nueva (devolver un mensaje de mantenimiento claro).
  • Preservar evidencia: logs de requests, trazas de error y una snapshot de las tablas afectadas.
  • Drenar o pausar jobs en background para que no sigan escribiendo datos malos.
  • Verificar: una persona comprueba logins y flujos clave, otra revisa la salud de datos.

Para escrituras parciales, mantén una regla de recuperación simple. Etiqueta nuevas escrituras por origen (antiguo vs nuevo) para poder aislarlas, o encola escrituras y reprográmalas solo después de validarlas.

Errores y trampas comunes (y cómo evitarlos)

La mayoría de cortes fallan por razones aburridas: un pequeño desajuste que solo aparece con usuarios reales e historial real. Trata la compatibilidad como una característica de producto, no como una tarea de última hora.

Un error clásico es romper la identidad de usuario. Si cambias claves primarias, renumeras usuarios o intercambias formatos de UUID a mitad de camino, puedes desconectar silenciosamente cuentas de suscripciones, permisos y trazas de auditoría. Mantén los mismos user IDs de punta a punta, o añade una capa de mapeo estable y pruébala con un snapshot completo de producción.

Las contraseñas son otra trampa frecuente. Los equipos “migran” auth y reinician accidentalmente a todos porque el nuevo servicio no puede verificar el esquema de hash antiguo (o olvida la salt/pepper por usuario). Mantén la verificación antigua, re-hashes en logins exitosos y nunca registres ni expongas secretos durante la depuración.

Los datos de producción te sorprenderán. Los datos de prueba rara vez incluyen usuarios eliminados, emails duplicados por importaciones antiguas, problemas con zonas horarias, registros parciales o flags legacy que la UI ocultó hace años. Planifica pruebas alrededor de casos límite, no de caminos felices.

Cinco comprobaciones previenen la mayoría de incidentes:

  • Congela el formato de user ID y claves primarias durante el corte.
  • Confirma que los hashes de contraseña y las reglas de sesiones/tokens coinciden con el comportamiento antiguo.
  • Ensaya con un snapshot parecido a producción y scripts reales de migración.
  • Inventaría los escritores en background (jobs, webhooks, cron) y ponles puertas durante el rollout.
  • Retrasa cambios de esquema que rompan hasta que la app nueva tenga 100 % del tráfico de lectura y escritura.

Lista rápida antes, durante y después del corte

Evita bifurcaciones por doble escritura
Diagnosticamos conflictos entre escritores y añadimos puertas seguras para el corte.

Trata el corte como un ensayo, no como un simple switch.

Antes del corte (ensaya como si fuera real)

Verifica backups restaurándolos en un entorno de test y abriendo la app contra la copia restaurada. Ensaya migraciones end-to-end (aplicar, verificar, revertir) con volúmenes parecidos a producción. Confirma que monitorización y alertas cubren errores, latencia y locks/queries lentas en la DB. Ejecuta flujos de auth (login, signup, reset de contraseña, refresh de sesión, acceso por roles) para algunos roles reales. Muestra tablas clave (users, suscripciones/pedidos, auditoría/historial) y busca nulls inesperados, índices faltantes y registros recientes inconsistentes.

Haz un dry run cronometrado con un pequeño grupo interno. Si algo no está claro, conviértelo en un paso del runbook.

Durante y después del corte (confía, pero verifica)

Comienza con un porcentaje mínimo y aumenta en pasos. Confirma que el switch de rollback funciona sin un deploy de código (practícalo antes del día de lanzamiento). Valida escrituras para los flujos más importantes (perfiles, pagos, permisos). Revisa que el trabajo en background (colas, cron, emails, webhooks) se ejecute una vez, no dos. Prepara soporte con una nota de estado corta, lista de problemas conocidos y una forma rápida de encontrar usuarios afectados.

Escenario ejemplo: reconstruir una SaaS manteniendo todo el historial

Un pequeño equipo SaaS decide reconstruir su web app porque la actual es lenta y frágil, pero no pueden perder nada en la base de datos: cuentas de usuario, proyectos, facturas y estado de suscripción. El objetivo es reconstruir la app sin cambiar la base de datos y luego cambiar el tráfico de forma segura.

Primero, lanzan la app nueva en modo solo lectura. Los usuarios siguen usando la app antigua para editar, pero la nueva puede iniciar sesión, cargar proyectos, mostrar facturas pasadas y mostrar el plan actual. Internamente, el equipo compara conteos y totales (proyectos por usuario, importe de la última factura, fecha de próxima renovación) entre las pantallas antigua y nueva. Los clientes no ven cambios, pero el equipo encuentra rápido dónde difieren las suposiciones.

Después, la despliegan a una cohorte pequeña: 5 % de usuarios con bajo riesgo de soporte y una mezcla de tipos de plan. Observan el éxito de logins, tiempo de carga de proyectos, totales de facturas que coinciden con la app antigua y tickets nuevos que mencionen datos faltantes.

Al segundo día detectan una discrepancia de facturación para planes anuales. La app nueva muestra “paid” correctamente, pero escribe un next_billing_date erróneo cuando un usuario actualiza el perfil de la empresa. Eso es un momento de rollback. Cambian el tráfico de vuelta a la app antigua, deshabilitan escrituras en la app nueva y vuelven a aplicar el pequeño conjunto de actualizaciones afectadas usando un script y los logs de auditoría. Nadie pierde historial y la corrección se prueba otra vez en modo solo lectura antes de reactivar la cohorte.

Próximos pasos y cuándo puede intervenir FixMyMess

Trata el plan de corte como un proyecto por sí mismo. Empieza escribiendo qué no debe romperse: inicio de sesión, facturación, reportes clave y cualquier integración que escriba en la base de datos.

Trae ayuda temprano si observas patrones como problemas intermitentes de login/sesión entre entornos, un esquema que “creció orgánicamente” sin dueño claro, o brechas de seguridad como secretos expuestos y queries SQL sospechosas.

Si heredaste un prototipo generado por IA que ahora falla bajo comportamiento real de producción, FixMyMess (fixmymess.ai) se centra en diagnosticar y reparar esas brechas: especialmente problemas de auth, patrones de esquema inseguros y lógica frágil, para que tu plan de despliegue y rollback por etapas coincida con lo que el código realmente hace. Un punto de partida común es una auditoría de código gratuita para sacar a la luz los puntos reales de fallo antes de que cambies el tráfico.