Evoluzione dello schema sicura alla rigenerazione per migrazioni prevedibili
L'evoluzione dello schema sicura alla rigenerazione mantiene validi i dati di produzione quando il codice backend viene rigenerato. Scopri un approccio pratico per pianificare modifiche dello schema e migrazioni.

Perché le modifiche allo schema sembrano rischiose con backend rigenerati
Quando il backend viene rigenerato da un modello visuale, una modifica al database può sembrare come tirare un filo in un maglione. Modifichi un campo nel Data Designer, clicchi rigenera, e all'improvviso non stai solo cambiando una tabella. Stai anche cambiando l'API generata, le regole di validazione e le query che la tua app usa per leggere e scrivere dati.
Quello che di solito va storto non è che il nuovo codice non si compili. Molte piattaforme no-code (inclusa AppMaster, che genera vero codice Go per i backend) genereranno volentieri un progetto pulito ogni volta. Il rischio reale è che i dati di produzione esistono già e non si rimodellano automaticamente per adattarsi alle tue nuove idee.
I due guasti che le persone notano per primi sono semplici:
- Letture rotte: l'app non riesce più a caricare i record perché una colonna è stata spostata, un tipo è cambiato o una query si aspetta qualcosa che non c'è.
- Scritture rotte: nuovi o aggiornati record falliscono perché sono cambiate le constraint, campi richiesti o formati, e i client esistenti inviano ancora la forma vecchia.
Entrambi i fallimenti sono dolorosi perché possono rimanere nascosti fino a quando gli utenti reali li colpiscono. Un database di staging può essere vuoto o appena popolato, quindi tutto sembra a posto. La produzione ha casi limite: null dove avevi assunto valori, stringhe enum vecchie o righe create prima che esistesse una nuova regola.
Ecco perché l'evoluzione dello schema sicura alla rigenerazione è importante. L'obiettivo è rendere ogni modifica sicura anche quando il codice del backend viene rigenerato completamente, così i record vecchi restano validi e i record nuovi possono comunque essere creati.
“Migrazioni prevedibili” significa semplicemente che puoi rispondere a quattro domande prima del deploy: cosa cambierà nel database, cosa succederà alle righe esistenti, quale versione dell'app può ancora funzionare durante il rollout e come tornerai indietro se appare qualcosa di inaspettato.
Un modello semplice: schema, migrazioni e codice rigenerato
Quando la tua piattaforma può rigenerare il backend, aiuta separare tre cose nella testa: lo schema del database, i passi di migrazione che lo modificano e i dati live già in produzione. Confondere queste cose è il motivo per cui le modifiche sembrano imprevedibili.
Pensa alla rigenerazione come al “ricostruire il codice applicativo dall'ultimo modello”. In uno strumento come AppMaster, quella ricostruzione può avvenire molte volte durante il lavoro normale: sistemi un campo, aggiusti la logica di business, aggiungi un endpoint, rigeneri, testi, ripeti. La rigenerazione è frequente. Il tuo database di produzione non dovrebbe esserlo.
Ecco il modello semplice.
- Schema: la struttura delle tabelle, colonne, indici e vincoli del database. È ciò che il database si aspetta.
- Migrazioni: i passi ordinati e ripetibili che portano lo schema da una versione alla successiva (e talvolta muovono anche i dati). È ciò che esegui in ogni ambiente.
- Dati runtime: i record reali creati da utenti e processi. Devono rimanere validi prima, durante e dopo la modifica.
Il codice rigenerato dovrebbe essere trattato come “l'app corrente che parla con lo schema corrente”. Le migrazioni sono il ponte che mantiene schema e dati runtime allineati mentre il codice cambia.
Perché la rigenerazione cambia le regole del gioco
Se rigeneri spesso, apporterai naturalmente molte piccole modifiche allo schema. Questo è normale. Il rischio appare quando quelle modifiche implicano un cambiamento del database non retrocompatibile, o quando la tua migrazione non è deterministica.
Un modo pratico per gestire questo è pianificare l'evoluzione dello schema sicura alla rigenerazione come una serie di piccoli passi reversibili. Invece di un grande interruttore, fai mosse controllate che mantengano vecchi e nuovi percorsi di codice funzionanti insieme per un breve periodo.
Per esempio, se vuoi rinominare una colonna usata da un'API in produzione, non rinominarla immediatamente. Prima aggiungi la nuova colonna, scrivi su entrambe, backfilla le righe esistenti, poi cambi le letture sulla nuova colonna. Solo dopo rimuovi la colonna vecchia. Ogni passo è facile da testare e, se qualcosa va storto, puoi mettere in pausa senza corrompere i dati.
Questo modello mentale rende le migrazioni prevedibili, anche quando la rigenerazione del codice avviene quotidianamente.
Tipi di modifiche allo schema e quali rompono la produzione
Quando il backend viene rigenerato dallo schema più recente, il codice solitamente assume che il database corrisponda a quello schema in questo momento. Ecco perché l'evoluzione dello schema sicura alla rigenerazione riguarda meno il “Possiamo cambiare il database?” e più il “Possono i dati vecchi e le richieste vecchie sopravvivere mentre distribuamo la modifica?”.
Alcune modifiche sono naturalmente sicure perché non invalidano righe o query esistenti. Altre cambiano il significato dei dati o rimuovono qualcosa che l'app in esecuzione si aspetta ancora, ed è lì che avvengono gli incidenti in produzione.
Basso rischio, solitamente sicuro (additivo)
Le modifiche additive sono le più facili da mandare perché possono coesistere con i dati vecchi.
- Nuova tabella che dipende da nulla al momento.
- Nuova colonna nullable senza requisito di default.
- Nuovo campo API opzionale end-to-end.
Esempio: aggiungere una colonna nullable middle_name alla tabella users è tipicamente sicuro. Le righe esistenti restano valide, il codice rigenerato la leggerà quando presente e le righe più vecchie avranno semplicemente NULL.
Rischio medio (cambiamenti di significato)
Queste modifiche spesso “funzionano” tecnicamente ma rompono il comportamento. Richiedono coordinazione perché la rigenerazione aggiorna validazioni, modelli generati e assunzioni della logica di business.
Le rinomine sono la trappola classica: rinominare phone in mobile_phone potrebbe rigenerare codice che non legge più phone, mentre la produzione ha ancora dati lì. Allo stesso modo, cambiare unità (ad esempio prezzo in dollari vs centesimi) può corrompere silenziosamente i calcoli se aggiorni prima il codice o prima i dati.
Gli enum sono un altro punto critico. Stringere un enum (rimuovere valori) può rendere invalide righe esistenti. Allargare un enum (aggiungere valori) è in genere sicuro, ma solo se tutti i percorsi di codice possono gestire il nuovo valore.
Un approccio pratico è trattare i cambiamenti di significato come “aggiungi nuovo, backfilla, switch, poi rimuovi dopo”.
Alto rischio (distruttivo)
I cambiamenti distruttivi sono quelli che più spesso rompono la produzione immediatamente, specialmente quando la piattaforma rigenera codice che smette di aspettarsi la vecchia forma.
Eliminare una colonna, eliminare una tabella o cambiare una colonna da nullable a not-null può far fallire le scritture nel momento in cui una richiesta prova a inserire una riga senza quel valore. Anche se pensi “tutte le righe ce l'hanno già”, il prossimo caso limite o job in background può dimostrare il contrario.
Se devi fare un cambiamento not-null, fallo in fasi: aggiungi la colonna come nullable, backfilla, aggiorna la logica dell'app per impostarla sempre, poi imponi not-null.
Prestazioni e sicurezza (possono bloccare le scritture)
Indici e vincoli non sono “cambiamenti di forma dei dati”, ma possono comunque causare downtime. Creare un indice su una tabella grande o aggiungere una constraint UNIQUE può bloccare le scritture abbastanza a lungo da causare timeout. In PostgreSQL, alcune operazioni sono più sicure se fatte con metodi online-friendly, ma il punto chiave è il timing: esegui le operazioni pesanti durante traffico basso e misura quanto durano su una copia di staging.
Quando le modifiche richiedono più cura in produzione, pianifica per:
- Un rollout in due fasi (prima lo schema, poi il codice, o viceversa) che rimanga compatibile.
- Backfill che girano a batch.
- Un chiaro piano di rollback (cosa succede se il backend rigenerato va live prima del previsto).
- Query di verifica che dimostrino che i dati corrispondono alle nuove regole.
- Un ticket "rimuovi campo vecchio più tardi" così la pulizia non avviene nello stesso deploy.
Se usi una piattaforma come AppMaster che rigenera il codice backend dal Data Designer, la mentalità più sicura è: manda in produzione cambi che i dati vecchi possono sopportare oggi, poi stringi le regole solo dopo che il sistema si è adattato.
Principi per cambiamenti sicuri alla rigenerazione
I backend rigenerati sono fantastici finché una modifica di schema arriva in produzione e le righe vecchie non corrispondono alla nuova forma. L'obiettivo dell'evoluzione dello schema sicura alla rigenerazione è semplice: mantenere l'app funzionante mentre il database e il codice rigenerato si allineano a piccoli passi prevedibili.
Parti dal principio "espandi, migra, contrai"
Tratta ogni modifica significativa come tre mosse. Prima, espandi lo schema così sia il codice vecchio che quello nuovo possono funzionare. Poi migra i dati. Solo dopo, contrai rimuovendo colonne, default o vincoli vecchi.
Una regola pratica: non combinare mai "nuova struttura" e "pulizia distruttiva" nello stesso deploy.
Supporta la forma vecchia e nuova per un po'
Presumi che ci sarà un periodo in cui:
- alcune righe hanno i nuovi campi, altre no
- alcune istanze dell'app eseguono codice vecchio, altre il codice rigenerato
- job in background, importazioni o client mobile possono restare indietro
Progetta il database in modo che entrambe le forme siano valide durante quella sovrapposizione. Questo conta ancora di più quando una piattaforma rigenera il backend dal modello più recente (per esempio in AppMaster quando aggiorni il Data Designer e rigeneri il backend Go).
Rendi le letture compatibili prima delle scritture
Comincia facendo in modo che il codice nuovo sappia leggere i dati vecchi in sicurezza. Solo dopo cambia i percorsi di scrittura per produrre la nuova forma dei dati.
Per esempio, se dividi un singolo campo status in status + status_reason, distribuisci codice che può gestire l'assenza di status_reason prima di iniziare a scriverlo.
Decidi cosa fare con dati parziali o sconosciuti
Quando aggiungi enum, colonne non-null o vincoli più stretti, decidi in anticipo cosa succede quando i valori mancano o sono inattesi:
- permettere null temporaneamente, poi backfill
- impostare un default sicuro che non cambi il significato
- mantenere un valore "unknown" per evitare letture fallite
Questo evita corruzione silenziosa (default sbagliati) e errori gravi (nuove constraint che rifiutano righe vecchie).
Prevedi un piano di rollback per ogni passo
Il rollback è più facile durante la fase di espansione. Se devi tornare indietro, il codice vecchio dovrebbe ancora funzionare contro lo schema espanso. Scrivi cosa faresti per annullare (solo codice, o codice più migrazione), ed evita cambiamenti distruttivi finché non sei sicuro di non doverli annullare.
Passo dopo passo: pianifica una modifica che sopravvive alla rigenerazione
I backend rigenerati sono inflessibili: se lo schema e il codice generato non concordano, la produzione di solito lo scopre per prima. L'approccio più sicuro è trattare ogni cambiamento come un piccolo rollout reversibile, anche se costruisci con strumenti no-code.
Inizia scrivendo l'intento in linguaggio semplice e com'è fatto oggi il tuo dato. Scegli 3-5 righe reali dalla produzione (o da un dump recente) e annota le parti problematiche: valori vuoti, formati vecchi, default sorprendenti. Questo ti evita di progettare un campo nuovo perfetto che i dati reali non riescono a soddisfare.
Ecco una sequenza pratica che funziona bene quando la tua piattaforma rigenera il codice backend (ad esempio, quando AppMaster rigenera servizi backend Go dal modello del Data Designer):
-
Espandi prima, non sostituire. Aggiungi colonne o tabelle in modo additivo. Rendi i nuovi campi nullable all'inizio o dai loro default sicuri. Se introduci una nuova relazione, permetti alla FK di essere vuota finché non l'hai popolata.
-
Distribuisci lo schema espanso senza rimuovere nulla. Applica la modifica al database mentre il codice vecchio continua a funzionare. L'obiettivo è: il codice vecchio continua a scrivere le colonne vecchie e il database le accetta.
-
Backfill con un job controllato. Popola i nuovi campi con un processo batch che puoi monitorare e rieseguire. Rendilo idempotente (eseguirlo due volte non deve corrompere i dati). Fai il backfill gradualmente se la tabella è grande e logga quante righe sono state aggiornate.
-
Cambia prima le letture, con fallback. Aggiorna la logica rigenerata per preferire i nuovi campi, ma ricadere sui vecchi quando i nuovi mancano. Solo dopo che le letture sono stabili, cambia le scritture verso i nuovi campi.
-
Pulizia alla fine. Una volta che hai fiducia (e un piano di rollback), rimuovi i campi vecchi e stringi i vincoli: imposta NOT NULL, aggiungi constraint UNIQUE e applica le FK.
Esempio concreto: vuoi sostituire una colonna status free-text con status_id che punti a una tabella statuses. Aggiungi status_id come nullable, backfilla dai valori testuali esistenti, aggiorna l'app per leggere status_id ma ricadere su status quando è null, poi elimina status e rendi status_id obbligatorio. Questo mantiene ogni rigenerazione sicura perché il database resta compatibile in ogni fase.
Pattern pratici riutilizzabili
Quando il backend viene rigenerato, piccole modifiche allo schema possono riverberare nelle API, nelle regole di validazione e nelle form dell'UI. L'obiettivo è fare i cambiamenti in modo che i dati vecchi restino validi mentre il codice nuovo viene distribuito.
Pattern 1: Rinomina non distruttiva
Una rinomina diretta è rischiosa perché i record vecchi e il codice vecchio spesso si aspettano il nome originale. Un approccio più sicuro è trattare la rinomina come un periodo di migrazione breve.
- Aggiungi la nuova colonna (es.
customer_phone) e mantieni la vecchia (phone). - Aggiorna la logica per dual-write: quando salvi, scrivi su entrambi i campi.
- Backfilla le righe esistenti così
customer_phoneè pieno per i record correnti. - Cambia le letture sulla nuova colonna quando la copertura è alta.
- Elimina la colonna vecchia in una release successiva.
Questo funziona bene in strumenti come AppMaster dove la rigenerazione ricostruisce modelli e endpoint dallo schema corrente. Il dual-write mantiene felici entrambe le generazioni di codice durante la transizione.
Pattern 2: Dividere un campo in due
Dividere full_name in first_name e last_name è simile, ma il backfill è più complesso. Mantieni full_name finché non sei sicuro che la divisione sia completa.
Una regola pratica: non rimuovere il campo originale finché ogni record non è backfilled o non ha un fallback chiaro. Se il parsing fallisce, conserva l'intera stringa in last_name e segnala il record per una revisione.
Pattern 3: Rendere un campo obbligatorio
Trasformare un campo nullable in obbligatorio è un classico che rompe la produzione. L'ordine sicuro è: backfill prima, poi imporre.
Il backfill può essere meccanico (impostare un default) o guidato dal business (chiedere agli utenti di completare i dati). Solo quando i dati sono completi aggiungi NOT NULL e aggiorna le validazioni. Se il backend rigenerato aggiunge validazioni più severe automaticamente, questa sequenza evita fallimenti a sorpresa.
Pattern 4: Cambiare un enum in sicurezza
I cambi di enum rompono quando il codice vecchio invia valori vecchi. Durante la transizione, accetta entrambi. Se stai sostituendo "pending" con "queued", mantieni entrambi i valori validi e mappali nella logica. Quando sei sicuro che nessun client invia il valore vecchio, rimuovilo.
Se il cambiamento deve essere spedito in una release sola, riduci il raggio d'azione:
- Aggiungi nuovi campi ma mantieni i vecchi anche se inutilizzati.
- Usa un default DB così gli insert continuano a funzionare.
- Rendi il codice tollerante: leggi il nuovo, ricadi sul vecchio.
- Aggiungi uno strato di mapping temporaneo (valore vecchio in, valore nuovo salvato).
Questi pattern mantengono le migrazioni prevedibili anche quando il codice rigenerato cambia comportamento rapidamente.
Errori comuni che causano sorprese
Le sorprese più grandi accadono quando la gente tratta la rigenerazione del codice come un pulsante magico di reset. I backend rigenerati possono mantenere il codice pulito, ma il tuo database di produzione contiene ancora i dati di ieri, con la forma di ieri. L'evoluzione dello schema sicura alla rigenerazione significa pianificare per entrambi: il nuovo codice che verrà generato e i record vecchi che resteranno nelle tabelle.
Una trappola comune è presumere che la piattaforma "si occuperà delle migrazioni". Per esempio, in AppMaster puoi rigenerare un backend Go dal Data Designer aggiornato, ma la piattaforma non può indovinare come vuoi trasformare i dati reali dei clienti. Se aggiungi un campo richiesto, serve comunque un piano chiaro su come dare un valore alle righe esistenti.
Un'altra sorpresa è eliminare o rinominare campi troppo presto. Un campo può sembrare inutilizzato nell'UI principale, ma essere letto ancora da un report, un'esportazione schedulata, un webhook o una schermata admin che qualcuno apre raramente. Il cambiamento sembra sicuro nei test, poi fallisce in produzione perché un percorso di codice dimenticato si aspetta ancora il nome della colonna vecchia.
Ecco cinque errori che portano spesso a rollback notturni:
- Cambiare lo schema e rigenerare il codice, ma non scrivere né verificare la migrazione dei dati che rende valide le righe vecchie.
- Rinominare o cancellare una colonna prima che ogni reader e writer sia stato aggiornato e deployato.
- Backfill su una tabella grande senza verificare quanto tempo ci vorrà e se bloccherà altre scritture.
- Aggiungere una nuova constraint (NOT NULL, UNIQUE, FK) per prima e poi scoprire dati legacy che la violano.
- Dimenticare job in background, esportazioni e report che leggono ancora i campi vecchi.
Uno scenario semplice: rinomini phone in mobile_number, aggiungi NOT NULL e rigeneri. Le schermate dell'app possono funzionare, ma un'esportazione CSV vecchia seleziona phone, e migliaia di record esistenti hanno mobile_number nullo. La soluzione è di solito un cambiamento a fasi: aggiungi la nuova colonna, scrivi su entrambe per un po', backfilla in sicurezza, poi stringi i vincoli e rimuovi il campo vecchio solo dopo avere prove che nulla dipende più da esso.
Checklist rapida pre-deploy per migrazioni più sicure
Quando il backend viene rigenerato, il codice può cambiare rapidamente ma i tuoi dati di produzione non perdonano sorprese. Prima di mandare una modifica allo schema, fai un breve controllo "può fallire in sicurezza?". Rende l'evoluzione dello schema sicura alla rigenerazione noiosa (ed è quello che vuoi).
I 5 controlli che catturano la maggior parte dei problemi
- Dimensione e velocità del backfill: stima quante righe esistenti devono essere aggiornate e quanto ci metterà in produzione. Un backfill che va bene su un DB piccolo può richiedere ore sui dati reali e rendere l'app lenta.
- Rischio di lock e downtime: identifica se la modifica può bloccare scritture. Alcune operazioni (alter su tabelle grandi o cambi di tipo) possono tenere lock abbastanza a lungo da provocare timeout. Se c'è rischio di blocco, pianifica un rollout più sicuro (aggiungi la colonna, backfill dopo, switch del codice per ultimo).
- Compatibilità codice vecchio vs nuovo schema: presupponi che il backend vecchio possa girare per un breve periodo contro il nuovo schema durante deploy o rollback. Chiediti: la versione precedente leggerà e scriverà senza crashare? Se no, serve un rilascio in due fasi.
- Default e comportamento dei null: per le nuove colonne, decidi cosa succede per i record esistenti. Saranno NULL o hanno bisogno di un default? Assicurati che la logica tratti valori mancanti come normali, specialmente per flag, campi status e timestamp.
- Segnali di monitoraggio da osservare: scegli gli allarmi esatti che controllerai dopo il deploy: error rate (fallimenti API), query lente al DB, fallimenti di queue/job e qualsiasi azione utente chiave (checkout, login, submit form). Guarda anche bug "silenziosi" come un picco di errori di validazione.
Un esempio rapido
Se aggiungi un nuovo campo obbligatorio come status alla tabella orders, non spingerlo come "NOT NULL, senza default" in un colpo solo. Prima aggiungilo nullable con un default per le nuove righe, distribuisci codice rigenerato che gestisca l'assenza dello status, backfilla le righe vecchie e solo allora stringi i vincoli.
In AppMaster questo approccio è particolarmente utile perché il backend può essere rigenerato spesso. Tratta ogni modifica di schema come un piccolo rilascio con rollback facile e le tue migrazioni resteranno prevedibili.
Esempio: evolvere un'app live senza rompere i record esistenti
Immagina uno strumento interno di supporto dove gli agenti taggano i ticket con un campo testo libero chiamato priority (esempi: "high", "urgent", "HIGH", "p1"). Vuoi passare a un enum rigoroso così report e regole di routing non debbano più indovinare.
L'approccio sicuro è un cambiamento in due release che mantiene i record vecchi validi mentre il backend viene rigenerato.
Release 1: espandi, scrivi entrambi e backfilla
Inizia espandendo lo schema senza rimuovere nulla. Aggiungi un nuovo campo enum, per esempio priority_enum con valori come low, medium, high, urgent. Mantieni il campo originale priority_text.
Poi aggiorna la logica in modo che i ticket nuovi e modificati scrivano in entrambi i campi. In uno strumento no-code come AppMaster, questo di solito significa aggiornare il modello del Data Designer e il Business Process così che l'input venga mappato nell'enum e si salvi anche il testo originale.
Successivamente, backfilla i ticket esistenti in piccoli batch. Mappa i valori testuali comuni all'enum ("p1" e "urgent" -> urgent, "HIGH" -> high). Tutto ciò che è sconosciuto può temporaneamente mappare a medium mentre viene revisionato.
Quello che gli utenti vedono: idealmente nulla cambia ancora. L'UI può mostrare lo stesso controllo di priorità, ma dietro le quinte stai popolando il nuovo enum. I report possono iniziare a usare l'enum non appena il backfill è in corso.
Release 2: contrai e rimuovi il percorso vecchio
Quando sei fiducioso, cambia le letture per usare solo priority_enum, aggiorna filtri e dashboard e poi rimuovi priority_text in una migrazione successiva.
Prima della Release 2, valida con un piccolo campione per catturare i casi limite:
- Scegli 20-50 ticket tra team e età diverse.
- Confronta la priorità mostrata con il valore enum memorizzato.
- Controlla i conteggi per valore enum per individuare spike sospetti (ad es. troppi
medium).
Se emergono problemi, il rollback è semplice perché la Release 1 ha mantenuto il campo vecchio: ridistribuisci la logica di Release 1 e fai leggere l'UI da priority_text mentre sistemi il mapping e rilanci il backfill.
Prossimi passi: rendi l'evoluzione dello schema un'abitudine ripetibile
Se vuoi migrazioni prevedibili, tratta le modifiche allo schema come un piccolo progetto, non come una modifica veloce. L'obiettivo è semplice: ogni cambiamento deve essere facile da spiegare, facile da provare e difficile da rompere accidentalmente.
Un modello dati visuale aiuta perché rende l'impatto visibile prima del deploy. Quando puoi vedere tabelle, relazioni e tipi in un unico posto, noti cose che è facile perdere in uno script, come un campo richiesto senza default sicuro o una relazione che orfana record vecchi. Fai un rapido "chi dipende da questo?": API, schermate, report e job in background.
Quando devi cambiare un campo già in uso, preferisci un breve periodo di transizione con campi duplicati. Per esempio, aggiungi phone_e164 mantenendo phone_raw per una o due release. Aggiorna la logica per leggere dal nuovo campo quando presente e ricadere sul vecchio quando non lo è. Scrivi in entrambi i campi durante la transizione, poi rimuovi il campo vecchio solo dopo aver verificato che i dati sono stati interamente backfillati.
La disciplina degli ambienti è ciò che trasforma buone intenzioni in rilasci sicuri. Mantieni dev, staging e production allineati, ma non identici.
- Dev: prova che il backend rigenerato parte pulito e i flussi base funzionano dopo la rigenerazione.
- Staging: esegui il piano di migrazione completo su dati simili alla produzione e verifica query chiave, report e importazioni.
- Production: distribuisci quando hai un piano di rollback, monitoraggio chiaro e un piccolo set di controlli "must pass".
Rendi il tuo piano di migrazione un documento reale, anche breve. Includi: cosa cambia, l'ordine, come backfillare, come verificare e come tornare indietro. Poi eseguilo end-to-end in un ambiente di test prima che tocchi mai la produzione.
Se usi AppMaster, appoggiati al Data Designer per ragionare visivamente sul modello e lascia che la rigenerazione mantenga il codice backend coerente con lo schema aggiornato. L'abitudine che mantiene le cose prevedibili è rendere esplicite le migrazioni: puoi iterare rapidamente, ma ogni modifica ha comunque un percorso pianificato per i dati di produzione esistenti.


