Per progetti SaaS attivi 24/7, "fermiamo 10 minuti per fare la migration" non è un'opzione. Vediamo le quattro tecniche che usiamo davvero per cambiare schema senza fermare il servizio.
1. Expand-contract
Il pattern più importante. Per ogni cambio di schema:
- Expand: aggiungi la nuova colonna/tabella senza toccare la vecchia.
- Deploya il codice che scrive in entrambe.
- Backfill dei dati storici.
- Deploya il codice che legge dalla nuova.
- Contract: rimuovi la vecchia.
Sono 5 deploy. Sono anche zero downtime.
2. Lock-aware ALTER TABLE
In Postgres alcuni ALTER bloccano l'intera tabella. Le regole d'oro:
ADD COLUMN ... NOT NULLcon DEFAULT su Postgres < 11: lock pesante. Su Postgres ≥ 11: lock leggero.ALTER COLUMN TYPE: spesso richiede una full table rewrite. Da evitare in produzione, meglio fare expand-contract.CREATE INDEX: usa sempreCONCURRENTLY.ALTER TABLEcon timeout: impostastatement_timeoutelock_timeoutper non bloccare il sistema se il lock non arriva subito.
3. Backfill a batch
Mai un singolo UPDATE ... WHERE ... su una tabella grande. Serve uno script Node che processa 1.000-10.000 righe per volta, con pause:
while (rows = await fetchBatch()) {
await updateBatch(rows);
await sleep(200); // respirare
}
4. Feature flag
Il deploy del codice nuovo va dietro feature flag. Se il backfill non è completo o c'è un bug, lo si disattiva senza rollback Git.
Quando NON usare zero-downtime
Per progetti interni con utenti aziendali a orari precisi, una manutenzione notturna di 5 minuti costa molto meno tempo che il setup completo zero-downtime. Bisogna sapere quando ne vale la pena.