8 min

Claude Code PostgreSQL migrations: prompt per cambiamenti sicuri

Impara come scrivere prompt per Claude Code per migrazioni PostgreSQL sicure: pattern expand-contract, backfill, rollback e cosa verificare in staging prima del rilascio.

Claude Code PostgreSQL migrations: prompt per cambiamenti sicuri

Cosa rende rischiosa una modifica di schema PostgreSQL

Una modifica di schema PostgreSQL sembra semplice finché non incontra traffico reale e dati reali. La parte rischiosa di solito non è l'SQL in sé. È quando il codice dell'app, lo stato del database e la tempistica del deployment smettono di essere allineati.

La maggior parte dei guasti è pratica e dolorosa: un deploy si rompe perché codice vecchio tocca una nuova colonna, una migrazione prende un lock su una tabella calda e i timeout aumentano, oppure una modifica “veloce” elimina o riscrive dati senza farsi notare. Anche quando nulla va in crash, puoi spedire bug sottili come default sbagliati, vincoli rotti o indici che non hanno mai finito di essere creati.

Le migrazioni generate dall'AI aggiungono un altro livello di rischio. Gli strumenti possono produrre SQL valido che è comunque non sicuro per il tuo carico, per il volume dei dati o per il tuo processo di rilascio. Possono anche indovinare nomi di tabelle, non considerare lock di lunga durata o minimizzare il rollback perché le down migration sono difficili. Se usi Claude Code per le migrazioni, servono dei guardrail e un contesto concreto.

Quando in questo post si dice che una modifica è “sicura”, significa tre cose:

  • Retrocompatibile: vecchie e nuove versioni dell'app possono funzionare durante il rollout.
  • Osservabile: puoi misurare il progresso e individuare problemi rapidamente.
  • Reversibile: hai un piano di rollback che puoi eseguire sotto pressione.

L'obiettivo è che le migrazioni diventino lavoro di routine: prevedibili, testabili e noiose.

Regole di sicurezza da seguire prima di scrivere qualsiasi prompt

Inizia con poche regole non negoziabili. Mantengono il modello focalizzato e ti impediscono di spedire una modifica che funziona solo sul tuo laptop.

Suddividi il lavoro in piccoli passi. Una modifica di schema, un backfill dei dati, un cambiamento dell'app e un passaggio di pulizia sono rischi diversi. Raggrupparli rende più difficile vedere cosa si è rotto e più difficile rollbackare.

Preferisci cambi additivi prima di quelli distruttivi. Aggiungere una colonna, un indice o una tabella è di solito a basso rischio. Rinominare o droppare oggetti è dove avvengono gli outage. Fai prima la parte sicura, sposta l'app e poi rimuovi la cosa vecchia solo quando sei sicuro che non venga più usata.

Fai in modo che l'app tolleri entrambe le forme per un po'. Il codice dovrebbe saper leggere sia la colonna vecchia che quella nuova durante il rollout. Questo evita la corsa comune in cui alcuni server eseguono codice nuovo mentre il database è ancora vecchio (o viceversa).

Tratta le migrazioni come codice di produzione, non come uno script rapido. Anche se stai costruendo con una piattaforma come Koder.ai (backend in Go con PostgreSQL, più client React o Flutter), il database è condiviso da tutto. Gli errori costano.

Se vuoi un set compatto di regole da mettere all'inizio di ogni richiesta SQL, usa qualcosa come:

  • Una modifica per migrazione: expand, poi backfill, poi switch del codice, poi cleanup.
  • Evita lock lunghi: usa la creazione concorrente di indici e batch piccoli per gli update.
  • Richiedi un piano di rollback per ogni passo, incluso come fermare un backfill a metà.
  • Richiedi query di verifica e metriche di successo (conteggi righe, tassi di null, tempi).
  • Richiedi un runbook: come eseguirlo, cosa osservare e chi chiamare.

Un esempio pratico: invece di rinominare una colonna da cui dipende l'app, aggiungi la nuova colonna, esegui lentamente il backfill, distribuisci codice che legge la nuova e poi la vecchia, e solo più tardi rimuovi la colonna vecchia.

Cosa includere nel tuo prompt per tenere Claude Code ancorato

Claude può scrivere SQL decente da una richiesta vaga, ma le migrazioni sicure hanno bisogno di contesto. Tratta il tuo prompt come un mini brief di progettazione: mostra ciò che esiste, spiega cosa non deve rompersi e definisci cosa significa “sicuro” per il tuo rollout.

Inizia incollando solo i fatti del database che contano. Includi la definizione della tabella più gli indici e i vincoli rilevanti (primary key, unique, foreign key, check, trigger). Se sono coinvolte tabelle correlate, includi anche quegli estratti. Un piccolo estratto accurato impedisce al modello di inventare nomi o di perdere un vincolo importante.

Aggiungi la scala reale. Conteggi di righe, dimensione delle tabelle, tasso di scrittura e traffico di picco dovrebbero cambiare il piano. “200M righe e 1k scritture/sec” è una migrazione diversa da “20k righe e soprattutto letture.” Includi anche la versione di Postgres e come le migrazioni girano nel tuo sistema (singola transazione vs passi multipli).

Descrivi come l'app usa i dati: le letture importanti, le scritture e i job in background. Esempi: “API legge per email”, “i worker aggiornano lo status”, o “i report scansionano per created_at”. Questo determina se ti serve expand/contract, feature flag e quanto è sicuro un backfill.

Infine, sii esplicito su vincoli e deliverable. Una struttura semplice funziona bene:

  • Estratti di schema attuale e obiettivo
  • Assunzioni di scala (righe, scritture/sec, finestra di manutenzione se esiste)
  • Dipendenze dell'app (query/endpoint/job)
  • Vincoli duri (no downtime, evitare lock lunghi, evitare rewrite completo della tabella)
  • Deliverable: SQL più un piano in linguaggio semplice, verifica e rollback

Chiedere sia l'SQL che un piano d'esecuzione forza il modello a pensare a ordine, rischi e cosa controllare prima del deploy.

Expand/contract in parole semplici (e quando usarlo)

Il pattern expand/contract cambia un database PostgreSQL senza rompere l'app mentre la modifica è in corso. Invece di uno switch rischioso singolo, fai in modo che il database supporti sia la forma vecchia che quella nuova per un periodo.

Pensalo come: aggiungi nuove cose in sicurezza (expand), sposta traffico e dati gradualmente, e solo dopo rimuovi i pezzi vecchi (contract). Questo è particolarmente utile per lavori assistiti dall'AI perché ti costringe a pianificare il mezzo caotico.

Le quattro fasi

Un flusso pratico è:

  • Expand: aggiungi una nuova colonna nullable o una tabella, aggiungi un indice se serve, e aggiungi vincoli in modo da evitare blocchi (per esempio, aggiungere i vincoli come NOT VALID quando appropriato).
  • Compatibility: aggiorna l'app per gestire sia i campi vecchi che quelli nuovi. Questo può significare dual-write (scrivere in entrambi) o read-fallback (leggere nuovo, ripiegare su vecchio).
  • Backfill: copia i dati vecchi nel nuovo a piccoli batch, con checkpoint e modo di riprendere.
  • Contract: quando sei sicuro che tutto usa il nuovo percorso, stringi le regole (imposta NOT NULL, valida i vincoli), poi droppa la colonna o tabella vecchia.

Usa questo pattern ogni volta che gli utenti potrebbero essere ancora su codice vecchio mentre il database cambia. Include deployment multi-instance, app mobile che aggiornano lentamente o qualsiasi rilascio in cui una migrazione può durare minuti o ore.

Una tattica utile è pianificare due release. La Release 1 fa expand più compatibility così nulla si rompe se il backfill è incompleto. La Release 2 fa il contract solo dopo aver confermato nuovo codice e nuovi dati in posizione.

Un template di prompt sicuro per migrazioni expand/contract

Copia questo template e riempi le parentesi. Spinge Claude Code a produrre SQL eseguibile, controlli per provarne il successo e un piano di rollback che puoi davvero seguire.

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

Due righe extra che aiutano nella pratica:

  • Chiedi di etichettare qualsiasi passo che blocca scritture come RISK: blocks writes, più quando eseguirlo (fuori picco vs in qualsiasi momento).
  • Forza onestà sui lock: "Se non sei sicuro se una statement prende un ACCESS EXCLUSIVE lock, dillo e offri un'opzione più sicura."

Operazioni comuni di schema e come chiedere SQL più sicuro

Porta un compagno
Invita altri a provare Koder.ai e guadagna crediti tramite il tuo referral.

Piccole modifiche di schema possono comunque ferire se prendono lock lunghi, riscrivono grandi tabelle o falliscono a metà. Quando usi Claude Code per le migrazioni, chiedi SQL che eviti riscritture e mantenga l'app funzionante mentre il database recupera.

Aggiungere colonne e default (senza lock lunghi)

Aggiungere una colonna nullable è di solito sicuro. Aggiungere una colonna con default non-null può essere rischioso su versioni più vecchie di Postgres perché può riscrivere l'intera tabella.

Un approccio più sicuro è una modifica in due passi: aggiungi la colonna come NULL senza default, backfilla a batch, poi imposta il default per le nuove righe e aggiungi NOT NULL una volta che i dati sono puliti.

Se devi imporre un default immediatamente, richiedi una spiegazione sul comportamento dei lock per la tua versione di Postgres e un piano di fallback se l'esecuzione dura più del previsto.

Indici, FK, vincoli, drop

Per indici su tabelle grandi, richiedi CREATE INDEX CONCURRENTLY così letture e scritture continuano. Richiedi anche la nota che non può essere eseguito dentro una transaction block, il che significa che il tuo strumento di migrazione deve supportare un passo non transazionale.

Per le foreign key, la strada più sicura è di solito aggiungere il vincolo come NOT VALID prima, poi validarlo più tardi. Questo rende il cambiamento iniziale più veloce pur imponendo la FK per le nuove scritture.

Quando rendi i vincoli più severi (NOT NULL, UNIQUE, CHECK), chiedi “clean first, enforce second.” La migrazione dovrebbe rilevare le righe bad, correggerle e solo dopo abilitare la regola più severa.

Se vuoi una checklist corta da incollare nei prompt, tienila stretta:

  • Includi note su lock e runtime atteso.
  • Usa CONCURRENTLY per indici grandi e segnala i limiti di transazione.
  • Preferisci NOT VALID poi VALIDATE per nuove foreign key.
  • Separa backfill e enforcement di NOT NULL/UNIQUE.
  • Droppa gli oggetti solo dopo un ciclo di release completo e aver confermato che nessuno li legge.

Prompts per backfill di dati lenti, costanti e recuperabili

I backfill sono dove si vede la maggior parte dei dolori di migrazione, non l'ALTER TABLE. I prompt più sicuri trattano i backfill come job controllati: misurabili, riavviabili e gentili con la produzione.

Inizia con controlli di accettazione facili da eseguire e difficili da discutere: conteggi righe attesi, un tasso di null target e alcuni spot check (per esempio confrontare vecchio vs nuovo valore per 20 ID casuali).

Poi chiedi un piano di batching. I batch mantengono i lock brevi e riducono le sorprese. Una buona richiesta specifica:

  • Come batchare (range di primary key o finestre temporali come created_at)
  • Una dimensione batch target (per esempio, 5.000 a 50.000 righe)
  • Se dormire tra i batch su tabelle calde
  • Che ogni batch sia una singola transazione chiara (non una transazione enorme)

Richiedi idempotenza perché i backfill falliscono a metà. L'SQL dovrebbe essere sicuro da rieseguire senza duplicare o corrompere i dati. Pattern tipici sono “update solo dove la nuova colonna è NULL” o una regola deterministica dove lo stesso input produce sempre lo stesso output.

Specificare come l'app resta corretta mentre il backfill gira. Se le nuove scritture continuano ad arrivare, ti serve un ponte: dual-write nel codice dell'app, un trigger temporaneo o logica di read-fallback (leggi nuovo quando presente, altrimenti vecchio). Dì quale approccio puoi distribuire in sicurezza.

Infine, costruisci pausa e ripresa nel design. Chiedi tracking del progresso e checkpoint, come una piccola tabella che memorizza l'ultima ID processata e una query che riporta progresso (righe aggiornate, ultima ID, ora di inizio).

Esempio: aggiungi users.full_name derivato da first_name e last_name. Un backfill sicuro aggiorna solo righe dove full_name IS NULL, gira per range di ID, registra l'ultima ID aggiornata e mantiene le nuove iscrizioni corrette tramite dual-write fino al completamento dello switch-over.

Come chiedere piani di rollback che funzionano nella realtà

Spedisci più velocemente con Go e Postgres
Crea un backend in Go con PostgreSQL da chat, poi fai evolvere lo schema in sicurezza.

Un piano di rollback non è solo “scrivi una down migration.” Sono due problemi: annullare il cambiamento di schema e gestire i dati che sono cambiati mentre la nuova versione era live. Il rollback dello schema è spesso possibile. Il rollback dei dati spesso no, a meno che tu non lo abbia pianificato.

Sii esplicito su cosa significa rollback per la tua modifica. Se droppi una colonna o riscrivi valori in-place, richiedi una risposta realistica come: “Il rollback ripristina la compatibilità dell'app, ma i dati originali non possono essere recuperati senza uno snapshot.” Quell'onestà è ciò che ti salva.

Chiedi trigger di rollback chiari così nessuno discute durante un incidente. Esempi:

  • Tasso di errori o latenza oltre una soglia definita per 10 minuti
  • Un piano di query critico regredisce (per esempio, seq scan su una tabella calda)
  • Job di backfill indietreggia di più di N ore
  • Controlli dati falliscono (null dove non permesso, duplicati, righe mancanti)
  • Un passo di migrazione blocca scritture più di X secondi

Richiedi l'intero pacchetto di rollback, non solo SQL: DOWN SQL (solo se sicuro), passi di app/config per restare compatibili e come fermare i job in background.

Questo pattern di prompt è di solito sufficiente:

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.

Prima di spedire, cattura un leggero “safety snapshot” per poter confrontare prima/dopo:

  • Conteggi righe per tabelle interessate (e sottoinsiemi chiave)
  • Un piccolo set di query campione con risultati attesi
  • Aggregati semplici (sum, min/max) per colonne toccate
  • Una breve lista di ID da controllare prima e dopo

Sii anche chiaro su quando non rollbackare. Se hai solo aggiunto una colonna nullable e l'app fa dual-write, una correzione forward (hotfix codice, pausa del backfill, poi riprendi) è spesso più sicura che revertare e creare più drift.

Errori comuni da evitare con migrazioni assistite da AI

L'AI può scrivere SQL velocemente, ma non vede il tuo database di produzione. La maggior parte dei fallimenti avviene quando il prompt è vago e il modello riempie i vuoti.

Una trappola comune è saltare lo schema corrente. Se non incolli la definizione di tabella, indici e vincoli, l'SQL può mirare a colonne inesistenti o ignorare una regola di unicità che trasforma un backfill in un'operazione lenta e che prende lock.

Un altro errore è spedire expand, backfill e contract in un unico deploy. Questo toglie il tuo zoccolo di sicurezza. Se il backfill dura a lungo o fallisce a metà, ti ritrovi con un'app che si aspetta lo stato finale.

I problemi che emergono più spesso:

  • Backfill non idempotenti e senza tracking del progresso
  • Aggiungere NOT NULL, UNIQUE o FK prima di pulire e validare i dati
  • Transazioni di lunga durata senza lock timeouts o statement timeouts
  • Nessuna query di verifica, così i problemi restano nascosti fino a che gli utenti non li colpiscono

Un esempio concreto: “rinomina una colonna e aggiorna l'app.” Se il piano generato rinomina e backfilla in una sola transazione, un backfill lento può tenere lock e rompere il traffico live. Un prompt più sicuro forza batch piccoli, timeouts espliciti e query di verifica prima di rimuovere il percorso vecchio.

Cosa verificare in staging prima del rilascio

Staging è dove trovi problemi che non compaiono su un piccolo DB dev: lock lunghi, null inaspettati, indici mancanti e code path dimenticate.

Per prima cosa, controlla che lo schema corrisponda al piano dopo la migrazione: colonne, tipi, default, vincoli e indici. Uno sguardo veloce non basta. Un indice mancante può trasformare un backfill sicuro in un disastro.

Poi esegui la migrazione su un dataset realistico. Idealmente è una copia recente di produzione con campi sensibili mascherati. Se non puoi farlo, almeno imita il volume di produzione e i punti caldi (tabelle grandi, righe larghe, tabelle molto indicizzate). Registra i tempi di ogni passo così sai cosa aspettarti in produzione.

Una checklist breve per staging:

  • Schema corrisponde al piano (colonne, tipi, vincoli, indici)
  • Tempi registrati su volumi realistici
  • Compatibilità testata: app vecchia con schema nuovo e app nuova con schema vecchio (quando il piano dice che deve funzionare)
  • Query di verifica eseguite: tassi di null, conteggi righe, controlli di orphan per nuove FK, letture campione
  • Segnali operativi osservati durante l'esecuzione: lock, deadlock, timeout, query lente

Infine, testa flussi utente reali, non solo SQL. Crea, aggiorna e leggi record toccati dalla modifica. Se expand/contract è il piano, conferma che entrambe le forme funzionano fino al cleanup finale.

Un esempio realistico: cambiare una colonna senza rompere gli utenti

Possiedi il tuo codebase
Tieni il controllo completo esportando il codice sorgente una volta solido il flusso di migrazione.

Immagina di avere una colonna users.name che contiene nomi completi come “Ada Lovelace.” Vuoi first_name e last_name, ma non puoi rompere le iscrizioni, i profili o le schermate admin durante il rollout.

Inizia con un passo di expand che sia sicuro anche se non viene distribuito codice: aggiungi colonne nullable, conserva la colonna vecchia ed evita lock lunghi.

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

Poi aggiorna il comportamento dell'app per supportare entrambi gli schemi. Nella Release 1, l'app dovrebbe leggere dalle nuove colonne quando presenti, ricadere su name quando sono null e scrivere in entrambi così i nuovi dati rimangono consistenti.

Dopo viene il backfill. Esegui un job a batch che aggiorna piccoli blocchi di righe per esecuzione, registra progresso e può essere messo in pausa in sicurezza. Ad esempio: aggiorna gli users dove first_name è null in ordine crescente di ID, 1.000 alla volta, e registra quante righe sono cambiate.

Prima di stringere le regole, valida in staging:

  • Le nuove iscrizioni popolano first_name e last_name e continuano a impostare name
  • Gli utenti esistenti vengono mostrati correttamente anche se è presente solo name
  • Il backfill può fermarsi e riprendere senza duplicare il lavoro
  • Non restano null inaspettati dopo il completamento del backfill
  • Le query base su users non risultano sensibilmente più lente

La Release 2 sposta le letture sulle nuove colonne solo. Solo dopo dovresti aggiungere vincoli (come SET NOT NULL) e droppare name, idealmente in un deploy separato più tardi.

Per il rollback, mantienilo semplice. L'app continua a leggere name durante la transizione e il backfill è fermabile. Se devi rollbackare la Release 2, torna a leggere name e lascia le nuove colonne finché non si è stabilizzato tutto.

Prossimi passi: rendi i tuoi prompt una routine ripetibile di migrazione

Tratta ogni modifica come un piccolo runbook. Lo scopo non è un prompt perfetto. È una routine che impone i dettagli giusti: schema, vincoli, piano di esecuzione e rollback.

Standardizza cosa deve includere ogni richiesta di migrazione:

  • Schema attuale e la modifica esatta (tabelle, colonne, indici)
  • Vincoli e fatti sul traffico (dimensione tabella, tasso di scrittura, downtime consentito)
  • Sequenza di rilascio (expand, deploy app, backfill, contract)
  • Come osserverai il progresso (query/metriche, runtime atteso)
  • Passi di rollback (cosa revertare prima, quali dati potrebbero rimanere)

Decidi chi possiede ogni passo prima che qualcuno esegua SQL. Una semplice divisione evita “tutti pensavano che qualcun altro lo facesse”: gli sviluppatori possiedono il prompt e il codice di migrazione, ops gestisce la tempistica e il monitoraggio in produzione, QA verifica il comportamento in staging e i casi limite, e una persona è il go/no-go finale.

Se costruisci app via chat, può aiutare delineare la sequenza prima di generare SQL. Per i team che usano Koder.ai, Planning Mode è un posto naturale per scrivere quella sequenza e snapshot più rollback possono ridurre l'impatto se qualcosa di inaspettato accade durante il rollout.

Dopo il deploy, pianifica il cleanup di contract subito mentre il contesto è fresco, così colonne vecchie e codice di compatibilità temporaneo non restano per mesi.

Domande frequenti

Perché le modifiche di schema PostgreSQL rompono la produzione anche se l'SQL sembra semplice?

Una modifica di schema è rischiosa quando codice dell'app, stato del database e tempistica di deployment smettono di essere allineati.

Modalità comuni di fallimento:

  • Il codice vecchio tocca una nuova colonna/constraint e va in crash
  • Una migrazione prende un lock pesante su una tabella occupata e le richieste scadono
  • Una modifica “piccola” riscrive o elimina dati silenziosamente
  • Lavoro su indici/constraint che impiega più tempo del previsto e causa query lente
Qual è il modo più sicuro di default per cambiare uno schema senza downtime?

Usa un approccio expand/contract:

  • Expand: aggiungi nuove colonne/tabelle/indici nullable in modo compatibile
  • Compatibility: distribuisci codice che può leggere/scrivere entrambe le forme
  • Backfill: copia i dati a piccoli batch con checkpoint
  • Contract: stringi i vincoli e rimuovi i campi vecchi solo dopo un ciclo di rilascio completo

Questo mantiene sia le versioni vecchie che quelle nuove dell'app funzionanti durante il rollout.

Quali rischi extra introducono le migrazioni generate da AI?

Perché il modello può generare SQL che è valido ma insicuro per il tuo carico di lavoro.

Rischi tipici legati all'AI:

  • Indovinare nomi di tabelle/colonne o non considerare un vincolo importante
  • Proporre una migrazione “big bang” che rimuove le opzioni di rollback
  • Ignorare il comportamento dei lock, i limiti di transazione e le lunghe costruzioni di indici
  • Minimizzare il rollback (soprattutto quando i dati vengono trasformati o rimossi)

Tratta l'output dell'AI come una bozza e richiedi piano di esecuzione, controlli e passi di rollback.

Cosa dovrei incollare nel prompt perché Claude Code non faccia supposizioni?

Includi solo i fatti da cui dipende la migrazione:

  • Estratti CREATE TABLE rilevanti (più indici, FK, UNIQUE/CHECK, trigger)
  • Versione di Postgres e come girano le migrazioni (transazione singola vs passi multipli)
  • Scala: numero di righe, dimensione tabella, tasso di scrittura, traffico di picco
  • Come l'app usa i dati (letture/scritture critiche/job)
  • Vincoli duri (no downtime, evitare rewrite di tabelle, limiti di lock)
  • Deliverable: UP SQL + query di verifica + piano di rollback + runbook

Questo evita supposizioni e forza il giusto ordine.

Dovrei combinare cambi schema e backfill in un'unica migrazione?

Regola predefinita: separa.

Una divisione pratica:

  • Migrazione 1: expand dello schema (nuove colonne/tabelle, magari constraint NOT VALID)
  • Deploy app: codice di compatibilità (read-fallback o dual-write)
  • Job di backfill: aggiornamenti a batch con tracking del progresso
  • Migrazione 2: contract (validate constraint, impostare NOT NULL, rimuovere colonne vecchie)

Accorpare tutto rende i guasti più difficili da diagnosticare e rollbackare.

Come aggiungo una nuova colonna con default senza causare lock lunghi?

Preferisci questo schema:

  1. ADD COLUMN ... NULL senza default (veloce)
  2. Backfill a batch
  3. Imposta un default per le nuove righe
  4. Aggiungi NOT NULL solo dopo verifica

Aggiungere un default non-null immediato può essere rischioso su alcune versioni perché potrebbe riscrivere l'intera tabella. Se serve un default immediato, richiedi note sul comportamento dei lock e un fallback più sicuro.

Quando dovrei usare CREATE INDEX CONCURRENTLY e qual è la limitazione?

Chiedi:

  • CREATE INDEX CONCURRENTLY per tabelle grandi/calde
  • Una nota che non può essere eseguito dentro un blocco di transazione (il tuo tooling deve supportarlo)
  • Tempo di esecuzione stimato e cosa monitorare (attese di lock, latenza delle query)

Per la verifica, includi un controllo rapido che l'indice esista e venga usato (per esempio confrontare un EXPLAIN prima/dopo in staging).

Qual è il modo più sicuro per aggiungere una foreign key su una tabella grande?

Usa NOT VALID prima, poi valida:

  • Aggiungi la FK come NOT VALID così il passo iniziale è meno disruptivo
  • Esegui VALIDATE CONSTRAINT in un passo separato quando puoi monitorarlo

Questo continua a imporre la FK per le nuove scritture, lasciandoti controllare quando avviene la validazione costosa.

Come chiedo un backfill che non sovraccarichi la produzione e possa riprendere?

Un buon backfill è a batch, idempotente e riavviabile.

Requisiti pratici:

  • Batchare per range di PK o finestre temporali
  • Aggiornare solo le righe che hanno ancora bisogno (es. WHERE new_col IS NULL)
  • Tenere i batch in transazioni brevi; opzionalmente dormire tra i batch
  • Tracciare il progresso (ultima ID processata, righe aggiornate, ora di inizio)
  • Assicurarsi che l'app resti corretta mentre il backfill è in corso (dual-write, trigger oppure read-fallback)

Questo rende i backfill sopravvivibili sotto traffico reale.

Che aspetto ha un piano di rollback realistico per i cambi di schema?

Obiettivo di rollback predefinito: ripristinare rapidamente la compatibilità dell'app, anche se i dati non vengono perfettamente annullati.

Un piano di rollback pratico deve includere:

  • Se il DOWN SQL è veramente sicuro; se no, un runbook invece
  • L'ordine esatto: stop/pausa dei job di backfill, deploy del cambiamento di app, poi passi di schema
  • Trigger di rollback chiari (tasso di errore, latenza, attese di lock, controlli dati falliti)
  • Una dichiarazione di cosa si può rollbackare (schema) vs cosa no (dati)

Spesso il rollback più sicuro è tornare a leggere il campo vecchio lasciando le nuove colonne in posto.

Related posts