8 min

Migraciones PostgreSQL con Claude Code: instrucciones para cambios seguros

Aprende plantillas de Claude Code para migraciones PostgreSQL seguras: patrón expand-contract, backfills, planes de rollback y qué verificar en staging antes del release.

Migraciones PostgreSQL con Claude Code: instrucciones para cambios seguros

¿Qué hace que un cambio de esquema en PostgreSQL sea riesgoso?

Un cambio de esquema en PostgreSQL parece simple hasta que se encuentra con tráfico real y datos reales. La parte riesgosa normalmente no es el SQL en sí. Es cuando el código de la app, el estado de la base de datos y el tiempo de despliegue dejan de coincidir.

La mayoría de las fallas son prácticas y dolorosas: un deploy se rompe porque código antiguo toca una columna nueva, una migración bloquea una tabla caliente y aumentan los timeouts, o un cambio “rápido” borra o reescribe datos sin avisar. Incluso cuando nada se cae, puedes enviar bugs sutiles como defaults incorrectos, constraints rotas o índices que nunca terminaron de construirse.

Las migraciones generadas por IA añaden otra capa de riesgo. Las herramientas pueden producir SQL válido que sigue siendo inseguro para tu carga, volumen de datos o proceso de release. También pueden adivinar nombres de tablas, pasar por alto locks de larga duración o minimizar el rollback porque las migraciones DOWN son difíciles. Si usas Claude Code para migraciones, necesitas barreras y contexto concreto.

Cuando en este post decimos que un cambio es “seguro”, significa tres cosas:

  • Compatibilidad hacia atrás: las versiones antigua y nueva de la app pueden correr durante el despliegue.
  • Observable: puedes medir el progreso y detectar problemas rápidamente.
  • Reversible: tienes un plan de rollback que puedes ejecutar bajo presión.

El objetivo es que las migraciones sean trabajo rutinario: predecible, comprobable y aburrido.

Reglas de seguridad a seguir antes de escribir cualquier prompt

Empieza con unas cuantas reglas innegociables. Mantienen al modelo enfocado y te evitan enviar un cambio que solo funciona en tu laptop.

Divide el trabajo en pasos pequeños. Un cambio de esquema, un backfill de datos, un cambio de app y un paso de limpieza son riesgos distintos. Agruparlos complica ver qué se rompió y dificulta el rollback.

Prefiere cambios aditivos antes que destructivos. Añadir una columna, un índice o una tabla suele ser bajo riesgo. Renombrar o eliminar objetos es donde ocurren outages. Haz la parte segura primero, cambia la app y elimina lo antiguo solo cuando estés seguro de que no se usa.

Haz que la app tolere ambas formas durante un tiempo. El código debería poder leer la columna antigua o la nueva durante el despliegue. Esto evita la carrera común donde algunos servidores ejecutan código nuevo mientras la base de datos aún es vieja (o al revés).

Trata las migraciones como código de producción, no como un script rápido. Incluso si construyes con una plataforma como Koder.ai (backend en Go con PostgreSQL, más clientes en React o Flutter), la base de datos es compartida por todo. Los errores son costosos.

Si quieres un conjunto compacto de reglas para poner al inicio de cada solicitud de SQL, usa algo como:

  • Un cambio por migración: expand, luego backfill, luego cambiar la app, luego cleanup.
  • Evitar locks largos: usar builds de índices concurrentes y actualizaciones en pequeños lotes.
  • Requerir un plan de rollback para cada paso, incluyendo cómo detenerse a mitad de un backfill.
  • Requerir queries de verificación y métricas de éxito (conteos de filas, tasa de nulos, tiempos).
  • Requerir un runbook: cómo ejecutarlo, qué vigilar y a quién avisar.

Un ejemplo práctico: en lugar de renombrar una columna de la que depende tu app, añade la nueva columna, backfillea lentamente, despliega código que lea nueva y luego antigua, y solo más tarde elimina la columna vieja.

Qué incluir en tu prompt para que Claude Code se mantenga con los pies en la tierra

Claude puede escribir SQL decente a partir de una petición vaga, pero las migraciones seguras necesitan contexto. Trata tu prompt como un mini brief de diseño: muestra lo que existe, explica qué no debe romperse y define qué significa “seguro” para tu despliegue.

Empieza pegando solo los hechos de la base de datos que importan. Incluye la definición de la tabla más los índices y constraints relevantes (primary keys, unique constraints, foreign keys, check constraints, triggers). Si hay tablas relacionadas, incluye esos fragmentos también. Un extracto pequeño y exacto evita que el modelo adivine nombres o pase por alto una constraint importante.

Añade la escala del mundo real. Conteos de filas, tamaño de tabla, tasa de escrituras y tráfico pico deberían cambiar el plan. “200M rows y 1k writes/sec” es una migración diferente a “20k rows y mayormente lecturas.” También incluye tu versión de Postgres y cómo se ejecutan las migraciones en tu sistema (transacción única vs varios pasos).

Describe cómo la aplicación usa los datos: las lecturas, escrituras y jobs importantes. Ejemplos: “API lee por email”, “workers actualizan estado”, o “reportes escanean por created_at”. Esto determina si necesitas expand/contract, feature flags y cuán seguro será un backfill.

Finalmente, sé explícito sobre restricciones y entregables. Una estructura simple funciona bien:

  • Fragmentos del esquema actual y el objetivo
  • Supuestos de escala (filas, writes/sec, ventana de mantenimiento si hay)
  • Dependencias de la app (queries/endpoints/jobs)
  • Restricciones duras (sin downtime, evitar locks largos, evitar reescrituras completas)
  • Entregables: SQL más un plan en lenguaje sencillo, verificación y rollback

Pedir tanto SQL como un plan de ejecución fuerza al modelo a pensar en orden, riesgo y qué revisar antes de enviar.

Expand/contract en lenguaje sencillo (y cuándo usarlo)

El patrón expand/contract cambia una base de datos PostgreSQL sin romper la app mientras el cambio está en progreso. En lugar de un switch arriesgado, haces que la base de datos soporte ambas formas durante un periodo.

Piénsalo así: añade cosas nuevas con seguridad (expand), mueve tráfico y datos gradualmente, y solo entonces elimina las partes antiguas (contract). Esto es especialmente útil para trabajo asistido por IA porque te obliga a planear el “medio desordenado”.

Las cuatro fases

Un flujo práctico se ve así:

  • Expand: añade una nueva columna nullable o tabla, añade un índice si hace falta, y añade constraints de forma que eviten bloqueo (por ejemplo, añadir constraints como NOT VALID cuando sea apropiado).
  • Compatibility: actualiza la app para manejar ambos campos. Esto puede significar dual-write (escribir en ambos) o read-fallback (leer nuevo, caer al antiguo).
  • Backfill: copia los datos antiguos al nuevo en pequeños lotes, con checkpoints y manera de reanudar.
  • Contract: una vez seguro de que todo usa la nueva vía, endurece reglas (poner columna NOT NULL, validar constraints), luego elimina la columna o tabla antigua.

Usa este patrón siempre que usuarios puedan seguir en versiones antiguas de la app mientras la base cambia. Eso incluye despliegues con múltiples instancias, apps móviles que actualizan lentamente, o cualquier release donde una migración pueda durar minutos u horas.

Una táctica útil es planear dos releases. El Release 1 hace expand más compatibility para que nada rompa si el backfill está incompleto. El Release 2 hace el contract solo después de confirmar que el código y los datos nuevos están en su lugar.

Una plantilla segura de prompt para migraciones expand/contract

Copia esta plantilla y rellena los corchetes. Empuja a Claude Code a producir SQL que puedas ejecutar, checks para probar que funcionó y un plan de rollback que puedas seguir.

You are helping me plan a PostgreSQL expand-contract migration.

Context
- App: [what the feature does, who uses it]
- Database: PostgreSQL [version if known]
- Table sizes: [rough row counts], write rate: [low/medium/high]
- Zero/near-zero downtime required: [yes/no]

Goal
- Change: [describe the schema change]
- Current schema (relevant parts):
  [paste CREATE TABLE or \d output]
- How the app will change (expand phase and contract phase):
  - Expand: [new columns/indexes/triggers, dual-write, read preference]
  - Contract: [when/how we stop writing old fields and remove them]

Hard safety requirements
- Prefer lock-safe operations. Avoid full table rewrites on large tables when possible.
- If any step can block writes, call it out explicitly and suggest alternatives.
- Use small, reversible steps. No “big bang” changes.

Deliverables
1) UP migration SQL (expand)
   - Use clear comments.
   - If you propose indexes, tell me if they should be created CONCURRENTLY.
   - If you propose constraints, tell me whether to add them NOT VALID then VALIDATE.

2) Verification queries
   - Queries to confirm the new schema exists.
   - Queries to confirm data is being written to both old and new structures (if dual-write).
   - Queries to estimate whether the change caused bloat/slow queries/locks.

3) Rollback plan (realistic)
   - DOWN migration SQL (only if it is truly safe).
   - If down is not safe, write a rollback runbook:
     - how to stop the app change
     - how to switch reads back
     - what data might be lost or need re-backfill

4) Runbook notes
   - Exact order of operations (including app deploy steps).
   - What to monitor during the run (errors, latency, deadlocks, lock waits).
   - “Stop/continue” checkpoints.

Output format
- Separate sections titled: UP.sql, VERIFY.sql, DOWN.sql (or ROLLBACK.md), RUNBOOK.md

Dos líneas extra que ayudan en la práctica:

  • Pídele que etiquete cualquier paso que bloquee escrituras como RISK: blocks writes, y cuándo ejecutarlo (off-peak vs anytime).
  • Oblígalo a ser honesto sobre locks: "Si no estás seguro de si una sentencia toma un ACCESS EXCLUSIVE lock, dilo y ofrece una opción más segura."

Operaciones comunes de esquema y cómo pedir SQL más seguro

Deploy in the right order
Deploy your app after schema expand steps, then backfill gradually with checks.

Cambios pequeños de esquema aún pueden hacer daño si toman locks largos, reescriben tablas grandes o fallan a mitad. Cuando usas Claude Code para migraciones, pide SQL que evite reescrituras y mantenga la app funcionando mientras la base de datos se pone al día.

Añadir columnas y defaults (sin locks largos)

Añadir una columna nullable suele ser seguro. Añadir una columna con default no nulo puede ser riesgoso en versiones antiguas de Postgres porque puede reescribir toda la tabla.

Un enfoque más seguro es un cambio en dos pasos: añade la columna como NULL sin default, backfillea en lotes, luego establece el default para filas nuevas y añade NOT NULL una vez los datos estén limpios.

Si debes imponer un default inmediatamente, exige una explicación del comportamiento de locks para tu versión de Postgres y un plan de respaldo si el tiempo de ejecución es mayor al esperado.

Índices, FKs, constraints, drops

Para índices en tablas grandes, pide CREATE INDEX CONCURRENTLY para que lecturas y escrituras sigan fluyendo. También exige una nota de que no puede ejecutarse dentro de una transacción, lo que implica que tu herramienta de migración necesita un paso no transaccional.

Para claves foráneas, la ruta más segura suele ser añadir la constraint como NOT VALID primero y validarla más tarde. Esto hace que el cambio inicial sea más rápido mientras se aplica la FK para escrituras nuevas.

Al endurecer reglas (NOT NULL, UNIQUE, CHECK), pide “limpiar primero, aplicar después.” La migración debería detectar filas malas, corregirlas y solo entonces habilitar la regla más estricta.

Si quieres una checklist corta para pegar en prompts, mantenla ajustada:

  • Incluir notas sobre locks y tiempo de ejecución esperado.
  • Usar CONCURRENTLY para índices grandes y señalar límites transaccionales.
  • Preferir NOT VALID y luego VALIDATE para nuevas FKs.
  • Separar backfill de imponer NOT NULL/UNIQUE.
  • Eliminar objetos solo después de un ciclo completo de release y confirmar que nadie los lee.

Cómo pedir backfills que sean lentos, constantes y recuperables

Los backfills son donde aparece la mayor parte del dolor de migraciones, no el ALTER TABLE. Los prompts más seguros tratan los backfills como jobs controlados: medibles, reanudables y suaves con producción.

Empieza con checks de aceptación fáciles de ejecutar y difíciles de discutir: conteos de filas esperados, una tasa objetivo de nulos y algunas comprobaciones puntuales (por ejemplo, comparar viejo vs nuevo para 20 IDs aleatorios).

Luego pide un plan por lotes. Los lotes mantienen los locks cortos y reducen sorpresas. Una buena petición especifica:

  • Cómo hacer batch (rangos de PK o ventanas de tiempo como created_at)
  • Tamaño objetivo por lote (por ejemplo, 5,000 a 50,000 filas)
  • Si dormir entre lotes en tablas calientes
  • Que cada lote sea una transacción clara y pequeña (no una enorme)

Requiere idempotencia porque los backfills fallan a mitad. El SQL debe ser seguro para re-ejecutar sin duplicar ni corromper datos. Patrones típicos: “update solo donde la columna nueva sea NULL” o reglas deterministas donde la misma entrada produce siempre la misma salida.

También explica cómo la app se mantiene correcta mientras corre el backfill. Si entran escrituras nuevas, necesitas un puente: dual-write en el código, un trigger temporal o lógica read-fallback (leer nuevo si existe, sino el viejo). Di qué enfoque puedes desplegar con seguridad.

Finalmente, integra pausa y reanudación en el diseño. Pide seguimiento de progreso y checkpoints, como una tabla pequeña que guarde el último ID procesado y una query que reporte progreso (filas actualizadas, último ID, tiempo de inicio).

Ejemplo: añades users.full_name derivado de first_name y last_name. Un backfill seguro actualiza solo filas donde full_name IS NULL, corre por rangos de ID, registra el último ID actualizado y mantiene nuevos registros correctos vía dual-write hasta que el switchover esté completo.

Cómo pedir planes de rollback que funcionen en la vida real

Own your codebase
Keep full control by exporting the source code once the migration workflow is solid.

Un plan de rollback no es solo “escribe una migración down.” Son dos problemas: deshacer el cambio de esquema y manejar datos que cambiaron mientras la versión nueva estuvo activa. El rollback de esquema suele ser posible. El rollback de datos a menudo no, a menos que lo hayas planificado.

Sé explícito sobre qué significa rollback para tu cambio. Si borras una columna o reescribes valores en sitio, exige una respuesta realista como: “El rollback restaura compatibilidad de la app, pero los datos originales no se recuperan sin un snapshot.” Esa honestidad te mantiene seguro.

Pide triggers de rollback claros para que nadie discuta en un incidente. Ejemplos:

  • La tasa de errores o latencia supera un umbral definido durante 10 minutos
  • Una consulta crítica degrada su plan (p. ej. seq scan en una tabla caliente)
  • El job de backfill se atrasa más de N horas
  • Fallan las comprobaciones de datos (nulos donde no se permiten, duplicados, filas faltantes)
  • Un paso de migración bloquea escrituras más de X segundos

Requiere el paquete de rollback completo, no solo SQL: DOWN migration SQL (solo si es seguro), pasos de app para mantener compatibilidad y cómo parar jobs en background.

Este patrón de prompt suele ser suficiente:

Produce a rollback plan for this migration.
Include: down migration SQL, app config/code switches needed for compatibility, and the exact order of steps.
State what can be rolled back (schema) vs what cannot (data) and what evidence we need before deciding.
Include rollback triggers with thresholds.

Antes de enviar, captura una “instantánea de seguridad” ligera para poder comparar antes y después:

  • Conteos de filas para tablas afectadas (y subconjuntos clave)
  • Un pequeño set de queries de muestra con resultados esperados
  • Agregados simples (sum, min/max) para columnas tocadas
  • Una lista corta de IDs para comprobaciones puntuales antes y después

También deja claro cuándo no hacer rollback. Si solo añadiste una columna nullable y la app hace dual-write, una corrección hacia adelante (hotfix, pausar el backfill y reanudar) suele ser más segura que revertir y crear más drift.

Errores comunes a vigilar con migraciones asistidas por IA

La IA puede escribir SQL rápido, pero no puede ver tu base de datos de producción. La mayoría de fallas ocurren cuando el prompt es vago y el modelo rellena huecos.

Una trampa común es omitir el esquema actual. Si no pegas la definición de la tabla, índices y constraints, el SQL puede apuntar a columnas que no existen o pasar por alto una regla de unicidad que convierte un backfill en una operación lenta y bloqueante.

Otro error es enviar expand, backfill y contract en un solo deploy. Eso elimina tu vía de escape. Si el backfill tarda o falla a mitad, te quedas con una app esperando el estado final.

Los problemas que más aparecen:

  • Backfills que no son idempotentes y no tienen seguimiento de progreso
  • Añadir NOT NULL, UNIQUE o FKs antes de limpiar y validar datos
  • Transacciones largas sin timeouts de lock o de sentencia
  • Sin queries de verificación, los problemas se ocultan hasta que los usuarios los encuentran

Un ejemplo concreto: “renombrar una columna y actualizar la app.” Si el plan generado renombra y backfillea en una sola transacción, un backfill lento puede sostener locks y romper tráfico en vivo. Un prompt más seguro obliga a lotes pequeños, timeouts explícitos y queries de verificación antes de quitar la ruta antigua.

Qué verificar en staging antes de enviar

Staging es donde encuentras problemas que nunca aparecen en una base de datos de dev pequeña: locks largos, nulos sorpresa, índices faltantes y rutas de código olvidadas.

Primero, verifica que el esquema coincida con el plan después de la migración: columnas, tipos, defaults, constraints e índices. Una mirada rápida no es suficiente. Un índice faltante puede convertir un backfill seguro en un desastre lento.

Luego ejecuta la migración contra un dataset realista. Idealmente una copia reciente de producción con campos sensibles enmascarados. Si no puedes, al menos iguala volumen y hotspots de producción (tablas grandes, filas anchas, tablas con muchos índices). Registra los tiempos de cada paso para saber qué esperar en prod.

Una checklist corta para staging:

  • El esquema coincide con el plan (columnas, tipos, constraints, índices)
  • Tiempos registrados en datos realistas
  • Compatibilidad probada: app antigua con esquema nuevo y app nueva con esquema antiguo (cuando el plan dice que debe funcionar)
  • Queries de verificación ejecutadas: tasas de nulos, conteos de filas, checks de orfandad para FKs nuevas, lecturas de muestra
  • Señales operativas vigiladas durante la ejecución: locks, deadlocks, timeouts, consultas lentas

Finalmente, prueba flujos reales de usuario, no solo SQL. Crear, actualizar y leer registros tocados por el cambio. Si el plan es expand/contract, confirma que ambas formas funcionan hasta la limpieza final.

Un ejemplo realista: cambiar una columna sin romper usuarios

Get rewarded for sharing
Share what you built with Koder.ai and earn credits for content you create.

Imagina que tienes users.name que guarda nombres completos como “Ada Lovelace.” Quieres first_name y last_name, pero no puedes romper registros nuevos, perfiles o pantallas admin mientras el cambio se despliega.

Empieza con un paso expand que sea seguro incluso si no se despliega código nuevo todavía: añade columnas nullable, conserva la columna antigua y evita locks largos.

ALTER TABLE users ADD COLUMN first_name text;
ALTER TABLE users ADD COLUMN last_name text;

Luego actualiza el comportamiento de la app para soportar ambos esquemas. En el Release 1, la app debe leer las columnas nuevas cuando existan, caer a name cuando estén a NULL, y escribir en ambas para que los datos nuevos permanezcan consistentes.

Después viene el backfill. Ejecuta un job por lotes que actualice pequeños bloques de filas por ejecución, registre el progreso y pueda pausarse con seguridad. Por ejemplo: actualizar users donde first_name es null en orden ascendente de ID, 1,000 a la vez, y loguear cuántas filas cambiaron.

Antes de endurecer reglas, valida en staging:

  • Nuevas inscripciones rellenan first_name y last_name y aún ponen name
  • Usuarios existentes se muestran correctamente aunque solo exista name
  • El backfill puede parar y reanudar sin duplicar trabajo
  • No quedan nulos inesperados tras completar el backfill
  • Consultas básicas sobre users no se vuelven notablemente más lentas

El Release 2 cambia las lecturas a las columnas nuevas únicamente. Solo después deberías añadir constraints (como SET NOT NULL) y eliminar name, idealmente en un deploy separado y posterior.

Para rollback, mantenlo simple. La app sigue leyendo name durante la transición, y el backfill es pausables. Si necesitas revertir Release 2, vuelve las lecturas a name y deja las columnas nuevas hasta estabilizar.

Próximos pasos: convierte tus prompts en una rutina repetible de migración

Trata cada cambio como un pequeño runbook. El objetivo no es un prompt perfecto. Es una rutina que fuerza los detalles correctos: esquema, constraints, plan de ejecución y rollback.

Estandariza lo que cada solicitud de migración debe incluir:

  • Esquema actual y el cambio exacto (tablas, columnas, índices)
  • Restricciones y hechos de tráfico (tamaño de tablas, tasa de escrituras, downtime permitido)
  • Secuencia de release (expand, desplegar app, backfill, contract)
  • Cómo observar el progreso (queries/métricas, tiempo esperado)
  • Pasos de rollback (qué revertir primero, qué datos pueden quedar atrás)

Decide quién es responsable de cada paso antes de ejecutar SQL. Una separación simple evita “todos pensaron que otro lo haría”: desarrolladores a cargo del prompt y código de migración, ops a cargo del timing y monitoreo en prod, QA verifica staging y casos límite, y una persona es la responsable final del go/no-go.

Si construyes apps vía chat, ayuda esbozar la secuencia antes de generar cualquier SQL. Para equipos que usan Koder.ai, Planning Mode es un lugar natural para escribir esa secuencia, y snapshots más rollback reducen la zona de impacto si algo inesperado ocurre durante el despliegue.

Después de enviar, programa la limpieza de contract inmediatamente mientras el contexto sigue fresco, para que las columnas antiguas y el código de compatibilidad temporal no se queden meses.

Preguntas frecuentes

Why do PostgreSQL schema changes break production even when the SQL looks simple?

Un cambio de esquema es riesgoso cuando el código de la app, el estado de la base de datos y el tiempo de despliegue dejan de coincidir.

Modos de fallo comunes:

  • El código antiguo de la app accede a una nueva columna/constraint y falla
  • Una migración toma un lock fuerte en una tabla ocupada y las peticiones hacen timeout
  • Un cambio “pequeño” reescribe o borra datos silenciosamente
  • Trabajos de índices/constraints que duran más de lo esperado y causan consultas lentas
What’s the safest default way to change a schema without downtime?

Usa el enfoque expand/contract:

  • Expand: añade columnas/tablas/índices nuevos que sean compatibles
  • Compatibility: despliega código que pueda leer/escribir ambas formas
  • Backfill: copia datos en pequeños lotes con puntos de control
  • Contract: refuerza constraints y elimina campos antiguos solo tras un ciclo de release completo

Esto mantiene las versiones antigua y nueva de la app funcionando durante el despliegue.

What extra risks do AI-generated migrations introduce?

Porque el modelo puede generar SQL válido pero inseguro para tu carga.

Riesgos típicos relacionados con IA:

  • Adivinar nombres de tablas/columnas o pasar por alto una constraint importante
  • Proponer una migración “big bang” que elimina opciones de rollback
  • Ignorar comportamiento de locks, límites de transacción y largos builds de índices
  • Dar por resuelto el rollback (especialmente cuando los datos se transforman o eliminan)

Trata la salida de la IA como un borrador y exige plan de ejecución, verificaciones y pasos de rollback.

What should I paste into my prompt so Claude Code doesn’t guess?

Incluye solo los hechos de los que depende la migración:

  • Fragmentos relevantes de CREATE TABLE (más índices, FKs, UNIQUE/CHECK, triggers)
  • Versión de Postgres y cómo se ejecutan las migraciones (transacción única vs varios pasos)
  • Escala: conteo de filas, tamaño de tablas, tasa de escrituras, tráfico pico
  • Cómo usa la app esos datos (lecturas/escrituras/trabajos críticos)
  • Restricciones duras (sin downtime, evitar reescrituras completas, límites de locks)
  • Entregables: UP SQL + queries de verificación + plan de rollback + runbook

Esto evita suposiciones y fuerza el orden correcto.

Should I combine schema changes and backfills in one migration?

Regla por defecto: sepáralos.

Una división práctica:

  • Migración 1: expand schema (nuevas columnas/tablas, quizá constraints NOT VALID)
  • Despliegue de la app: código de compatibilidad (read-fallback o dual-write)
  • Job de backfill: actualizaciones por lotes con seguimiento de progreso
  • Migración 2: contract (validar constraints, poner NOT NULL, eliminar columnas antiguas)

Agrupar todo hace que las fallas sean más difíciles de diagnosticar y revertir.

How do I add a new column with a default without causing long locks?

Prefiere este patrón:

  1. ADD COLUMN ... NULL sin default (rápido)
  2. Backfill en lotes
  3. Establecer un default para filas nuevas
  4. Añadir NOT NULL solo después de verificar

Agregar un default no nulo puede ser riesgoso en algunas versiones porque podría reescribir la tabla completa. Si necesitas un default inmediato, pide notas sobre locking/runtime y un plan alternativo más seguro.

When should I use CREATE INDEX CONCURRENTLY, and what’s the catch?

Pide:

  • CREATE INDEX CONCURRENTLY para tablas grandes/activas
  • Una nota de que no puede ejecutarse dentro de un bloque transaccional (tu herramienta debe soportar eso)
  • Tiempo esperado y qué monitorear (esperas de locks, latencia de consultas)

Para verificar, incluye una comprobación rápida de que el índice existe y se usa (por ejemplo, comparar un EXPLAIN antes/después en staging).

What’s the safest way to add a foreign key on a large table?

Usa NOT VALID primero, luego valida:

  • Añade la FK como NOT VALID para que el paso inicial sea menos disruptivo
  • Ejecuta VALIDATE CONSTRAINT en un paso separado cuando puedas vigilarlo

Esto sigue aplicando la FK para escrituras nuevas, mientras controlas cuándo ocurre la validación costosa.

How do I prompt for a backfill that won’t melt production and can resume?

Un buen backfill es por lotes, idempotente y reanudable.

Requisitos prácticos:

  • Batch por rangos de PK o ventanas temporales
  • Actualizar solo filas que aún necesiten trabajo (ej., WHERE new_col IS NULL)
  • Mantener los lotes en transacciones cortas; opcionalmente pausar entre lotes
  • Registrar progreso (último ID procesado, filas actualizadas, tiempo de inicio)
  • Asegurar que la app siga correcta mientras corre el backfill (dual-write, trigger o read-fallback)

Esto hace que los backfills sobrevivan bajo carga real.

What does a realistic rollback plan look like for schema changes?

Objetivo de rollback por defecto: restaurar la compatibilidad de la app rápidamente, incluso si los datos no se revierten perfectamente.

Un plan de rollback práctico debe incluir:

  • Si el SQL DOWN es realmente seguro; si no, un runbook en su lugar
  • El orden exacto: parar/pausar jobs de backfill, desplegar el cambio de app, luego pasos de esquema
  • Triggers de rollback claros (tasa de errores, latencia, esperas de locks, chequeos de datos fallidos)
  • Una declaración de qué puede revertirse (esquema) vs qué no (datos)

A menudo el rollback más seguro es volver a leer desde el campo antiguo mientras se mantienen las columnas nuevas.

Related posts