Migraciones de Base de Datos para Cambios Fluidos

¡Hola, amigo!

Hoy, hablemos sobre qué son las migraciones de bases de datos y por qué son tan importantes. En el mundo de hoy, no es sorpresa que cualquier cambio en una base de datos deba hacerse con cuidado y siguiendo un proceso específico. Idealmente, estos pasos deberían integrarse en nuestra pipeline de CI/CD para que todo funcione automáticamente.

Aquí está nuestra agenda:

  1. ¿Cuál es el problema?
  2. ¿Cómo lo solucionamos?
  3. Un ejemplo simple
  4. Un ejemplo más complejo
  5. Recomendaciones
  6. Resultados
  7. Conclusión

¿Cuál es el problema?

Si tu equipo nunca ha tratado con migraciones de bases de datos y no estás del todo seguro de por qué son necesarias, aclaremos eso. Si ya conoces lo básico, siéntete libre de avanzar.

Desafío principal

Cuando hacemos cambios “planificados” y “suaves” en la base de datos, necesitamos mantener la disponibilidad del servicio y cumplir con los requisitos de SLA (para que los usuarios no sufran por tiempo de inactividad o retrasos). Imagina que quieres cambiar el tipo de una columna en una tabla con 5 millones de usuarios. Si haces esto “de frente” (por ejemplo, simplemente ejecutando ALTER TABLE sin preparación), la tabla podría bloquearse por un tiempo significativo — y tus usuarios se quedarían sin servicio.

Para evitar tales dolores de cabeza, sigue dos reglas:

  1. Aplica migraciones de una manera que no bloquee la tabla (o al menos minimice los bloqueos).
  2. Si necesitas cambiar un tipo de columna, a menudo es más fácil crear primero una nueva columna con el tipo correcto y luego eliminar la antigua después.

Otro problema: Control de versiones y retrocesos

A veces necesitas revertir una migración.

Hacer esto manualmente —entrar en la base de datos de producción y manipular los datos— no solo es arriesgado, sino también probablemente imposible si no tienes acceso directo. Ahí es donde entran en juego las herramientas de migración dedicadas. Te permiten aplicar cambios de manera limpia y revertirlos si es necesario.

¿Cómo lo solucionamos? Usa las herramientas adecuadas

Cada lenguaje y ecosistema tiene sus propias herramientas de migración:

  • Para Java, Liquibase o Flyway son comunes.
  • Para Go, una opción popular es goose (la que veremos aquí).
  • Y así sucesivamente.

Goose: Qué es y por qué es útil

Goose es una utilidad Go ligera que te ayuda a gestionar migraciones automáticamente. Ofrece:

  • Simplicidad. Dependencias mínimas y una estructura de archivos transparente para las migraciones.
  • Versatilidad. Admite varios controladores de bases de datos (PostgreSQL, MySQL, SQLite, etc.).
  • Flexibilidad. Escribe migraciones en SQL o código Go.

Instalación de Goose

Shell

 

Cómo funciona: Estructura de migración

Por defecto, Goose busca archivos de migración en db/migrations. Cada migración sigue este formato:

Shell

 

  • NNN es el número de migración (por ejemplo, 001, 002, etc.).
  • Después de eso, puedes tener cualquier nombre descriptivo, por ejemplo init_schema.
  • La extensión puede ser .sql o .go.

Ejemplo de una migración SQL

Archivo: 001_init_schema.sql:

SQL

 

Nuestro primer ejemplo

Cambiando un tipo de columna (String → Int)

Supongamos que tenemos una tabla users con una columna age de tipo VARCHAR(255). Ahora queremos cambiarlo a INTEGER. Así es como podría verse la migración (archivo 005_change_column_type.sql):

SQL

 

Lo que está sucediendo aquí:

  1. Migración Up

    • Cambiamos la columna age a INTEGER. La cláusula USING (age::INTEGER) le dice a PostgreSQL cómo convertir los datos existentes al nuevo tipo.
    • Nota que esta migración fallará si hay algún dato en age que no sea numérico. En ese caso, necesitarás una estrategia más compleja (ver abajo).
  2. Migración hacia abajo

    • Si retrocedemos, devolvemos age a VARCHAR(255).
    • De nuevo usamos USING (age::TEXT) para convertir de INTEGER a texto.

Los Casos Segundos y Complejos: Migraciones de Varios Pasos

Si la columna age podría contener datos desordenados (no solo números), es más seguro hacerlo en varios pasos:

  1. Agregar una nueva columna (age_int) de tipo INTEGER.
  2. Copiar los datos válidos en la nueva columna, tratando o eliminando entradas inválidas.
  3. Eliminar la columna antigua.
SQL

 

Para permitir un rollback adecuado, la sección Down simplemente refleja las acciones al revés.

La Automatización es Clave

Para ahorrar tiempo, es realmente conveniente agregar comandos de migración a un Makefile (o cualquier otro sistema de construcción). A continuación se muestra un ejemplo de Makefile con los principales comandos Goose para PostgreSQL.

Supongamos:

  • El DSN para la base de datos es postgres://usuario:contraseña@localhost:5432/nombredb?sslmode=disable.
  • Los archivos de migración están en db/migrations.
Shell

 

¿Cómo se usa?

1. Crear una nueva migración (archivo SQL). Esto generará un archivo db/migrations/002_add_orders_table.sql.

Shell

 

2. Aplicar todas las migraciones. Goose creará una tabla schema_migrations en tu base de datos (si aún no existe) y aplicará cualquier nueva migración en orden ascendente.

Shell

 

3. Deshacer la última migración. Solo deshacer la última.

Shell

 

4. Deshacer todas las migraciones (usar con precaución en producción). Reinicio completo.

Shell

 

5. Verificar el estado de la migración.

Shell

 

Ejemplo de salida:

Shell

 

Resumen

Al utilizar herramientas de migración y un Makefile, podemos:

  1. Restringir el acceso directo a la base de datos de producción, realizando cambios solo a través de migraciones.
  2. Seguir fácilmente las versiones de la base de datos y revertirlas si algo sale mal.
  3. Mantener un historial único y consistente de cambios en la base de datos.
  4. Realizar migraciones “suaves” que no rompan un entorno de producción en ejecución en un mundo de microservicios.
  5. Obtener validación adicional: cada cambio pasará por un proceso de PR y revisión de código (suponiendo que tienes esa configuración en su lugar).

Otra ventaja es que es fácil integrar todos estos comandos en tu canal de CI/CD. Y recuerda: la seguridad por encima de todo. 

Por ejemplo:

YAML

 

Conclusión y Consejos

Las ideas principales son muy simples:

  • Mantén tus migraciones pequeñas y frecuentes. Son más fáciles de revisar, probar y revertir si es necesario.
  • Utiliza la misma herramienta en todos los entornos para que desarrollo, pruebas y producción estén sincronizados.
  • Integra las migraciones en CI/CD para no depender de que una persona las ejecute manualmente.

De esta manera, tendrás un proceso confiable y controlado para cambiar la estructura de tu base de datos, uno que no rompa la producción y te permita responder rápidamente si algo sale mal.

¡Buena suerte con tus migraciones!

¡Gracias por leer!

Source:
https://dzone.com/articles/goose-as-crucial-tool-for-your-service