-- Pasul 1 & 2: Adăugare coloană și pregătire
ALTER TABLE orders ADD COLUMN status_v2 VARCHAR(50) DEFAULT NULL;
-- Exemplu de backfill în batch-uri (pseudo-cod/SQL)
-- Se rulează extern pentru a nu bloca tranzacțiile lungi
UPDATE orders
SET status_v2 = status
WHERE id BETWEEN 1 AND 10000
AND status_v2 IS NULL;
-- Pasul 4: După ce codul citește doar din status_v2
ALTER TABLE orders DROP COLUMN status;Dacă lucrezi pe un proiect care a trecut de faza de MVP și ai deja câteva milioane de rânduri într-o tabelă critică, știi groaza aia care te ia când trebuie să redenumești o coloană sau să schimbi un tip de date. Am pățit-o acum vreo 6 ani la un sistem de plăți: un simplu ALTER TABLE a blocat tabela timp de 12 minute. Rezultatul? Toate request-urile de scriere s-au stivuit în coadă, pool-ul de conexiuni s-a umplut și tot backend-ul a crăpat spectaculos.
De atunci, nu mai fac migrări "la noroc". Folosesc un pattern în 4 pași care e plictisitor, consumă timp de dezvoltare, dar îmi permite să dorm liniștit noaptea. E vorba de Add, Backfill, Flip și Remove.
Pasul 1: Adăugarea coloanei noi (Add)
Primul pas e cel mai simplu. Adaugi coloana nouă, dar o lași nullable sau cu o valoare default care să nu strice logica actuală. Ideea e că aplicația ta habar n-are de coloana asta încă. Codul vechi rulează bine mersi, baza de date doar are un câmp în plus care stă degeaba.
La Postgres, de la versiunea 11 încoace, adăugarea unei coloane cu default e instantanee (doar update de catalog), dar pe versiuni mai vechi sau pe alte motoare, trebuia să fii foarte atent la lock-uri. Am lucrat pe un proiect unde aveam 8k useri activi concurenți și orice lock mai lung de 2 secunde însemna timeout-uri în cascadă pe toate serviciile din aval.
Pasul 2: Dual Write și Backfill
Aici începe distracția. Modifici codul aplicației să scrie în AMBELE coloane (cea veche și cea nouă). În etapa asta, aplicația citește tot din coloana veche, dar menține consistența pentru datele noi. Practic, te asiguri că tot ce intră în sistem de acum încolo e salvat în ambele formate.
După ce ai deploy-at dual writing-ul, pornești un script de backfill pentru datele istorice. Eu prefer să fac asta în batch-uri mici, controlate. De exemplu, la un tabel de 5 milioane de rânduri, am rulat un worker care procesa câte 1000 de rânduri la fiecare 500ms. Am reușit să migrăm datele în aproximativ 3 ore, fără să creștem latența bazei de date cu mai mult de 5%. Dacă încerci să faci un singur UPDATE masiv, o să omori IOPS-ul și o să blochezi tabela pentru toți ceilalți.
Pasul 3: Schimbarea sursei de adevăr (Flip)
Odată ce backfill-ul e gata și ești sigur că datele din coloana nouă sunt identice cu cele din coloana veche, faci switch-ul. Modifici aplicația să citească din coloana nouă. Ideal, păstrezi dual writing-ul încă o perioadă scurtă, în caz că descoperi un bug critic și trebuie să faci rollback rapid la codul vechi.
Trade-off-ul aici e evident: complexitatea codului crește temporar. Ai if-uri, ai logică dublă în entități sau repository-uri, poate ai și niște teste unitare care arată destul de urât. Dar e un preț mic pentru a evita un downtime total care te-ar costa mii de euro în pierderi de conversii.
Pasul 4: Curățenia (Remove)
După ce ai rulat în producție câteva zile și ești 100% convins că totul e ok, elimini complet referințele către coloana veche din cod. Ultimul pas este un DROP COLUMN. Abia acum migrarea e considerată finalizată.
Am văzut multe echipe care sar peste pasul ăsta de lene sau de teama de a nu șterge ceva greșit. Rezultatul? Baze de date pline de coloane numite status_old, email_v2_final sau temp_age. Nu fi colegul ăla. Curăță schema bazei de date imediat ce nu mai ai nevoie de vechea coloană, altfel datoria tehnică se va aduna până când nimeni nu va mai înțelege care e sursa de adevăr.
E un proces care necesită cel puțin 3 sau 4 deploy-uri diferite pentru o singură schimbare de schemă. Merită efortul? Dacă ai un SLA de 99.9% și utilizatori care depind de disponibilitatea sistemului tău, categoric da. Pe un proiect mic, personal, probabil că e overkill. Voi cum gestionați migrările astea grele? Folosiți tool-uri externe sau mergeți pe varianta asta manuală?