Salta al contenuto
Supastack
IT
Tema
AccediOttieni Supastack
Tutti gli articoli

Migrare un SaaS esistente su Supastack

Un piano a fasi per migrare un SaaS funzionante — auth, tenant, fatturazione, job — sulla fondazione supastack, senza riscrittura e senza un weekend di downtime.

Noah Zixin Zhang4 min di lettura

Come si migra un SaaS esistente su supastack?

Dipende dalla codebase, ma la sequenza non cambia: migra a fasi, mai in un'unica riscrittura. Fai l'inventario del repo dividendolo in codice di prodotto (route, schema, logica di dominio — resta tuo) e infrastruttura (auth, tenancy, fatturazione, job, email — la sostituisce il kit). Adotta i moduli in ordine di dipendenza: struttura, auth, workspace, fatturazione, poi lavoro in background. Sposta i dati con uno script rieseguibile, conserva i customer ID Stripe e fai il cutover dopo che i controlli di accettazione passano. La maggior parte delle web app richiede giorni di sere, non settimane.

Fai l'inventario prima di toccare il codice

Ogni migrazione che finisce male è iniziata allo stesso modo: qualcuno ha aperto il repo del kit e ha cominciato a cancellare file. Il kit è una fondazione, non un tema — sostituisce la tua infrastruttura e non ha opinioni sul tuo prodotto. Prima di spostare qualsiasi cosa, dividi onestamente il tuo repository in due pile.

Il codice di prodotto è tutto ciò che un cliente potrebbe ridescrivi: le tue route, il tuo schema, la tua logica di dominio, i tuoi testi, la tua pagina dei prezzi. L'infrastruttura è tutto ciò che non vede mai: sessioni, controlli di tenancy, webhook di pagamento, job in coda, email transazionali. La prima pila migra intatta. La seconda viene sostituita — ed è esattamente questo il punto.

  • Elenca ogni gruppo di route ed etichettalo come prodotto o plumbing — sii spietato; una pagina di impostazioni che cambia solo l'URL di un webhook è plumbing.
  • Elenca le tue env var e segna quali configurano l'infrastruttura (segreti di auth, chiavi dei provider) rispetto al comportamento del prodotto.
  • Elenca le tue tabelle e segna quali saranno di proprietà del kit (users, sessions, workspaces, stato di fatturazione) rispetto a quali restano tue.
  • Scrivi l'unico user journey che ti paga — quel flusso è il test di accettazione per ogni fase qui sotto.

Mappa ogni giuntura su un modulo del kit

L'infrastruttura del kit è un insieme di moduli con contratti tra loro — il tuo lavoro è puntare ogni pezzo del tuo vecchio plumbing sul modulo che ora lo possiede. La maggior parte del mapping è uno a uno; i punti in cui non lo è sono quelli in cui prendi decisioni vere.

Due scelte di mapping meritano un pensiero vero invece di un porting meccanico. Se vieni da Prisma, il kit standardizza su Drizzle — puoi tenere Prisma per le tabelle di prodotto durante la transizione, ma pianifica di convergere; due ORM in una sola codebase sono una tassa di manutenzione che si accumula. Il confronto Prisma vs Drizzle spiega cosa cambia.

E se vieni da un vendor di auth gestita, resisti all'impulso di replicare ogni funzionalità del vendor prima del cutover. L'auth gestita include silenziosamente cose che il tuo prodotto potrebbe non usare — elenchi di sessioni, UI delle organizzazioni, sync via webhook. Mappa ciò che il tuo prodotto chiama davvero; il confronto sulle auth copre le differenze di capacità che contano.

Il tuo plumbing attualeModulo supastackCosa si porta dietro
Sessioni NextAuth.js / Auth.jsAutenticazione (Better Auth)Users e account migrano; le sessioni vengono re-emesse al cutover
Clerk o un vendor di auth gestitaAutenticazione (Better Auth)Gli users migrano via email; le sessioni di terze parti sono abbandonate, non ripetute
Tabelle org/team personalizzateSpazi di lavoroLe tue righe org diventano righe workspace + membership
Webhook Stripe fatti a manoFatturazioneI customer ID Stripe sopravvivono; lo stato dell'abbonamento viene ricalcolato
Job CRON + setTimeoutJob (Trigger.dev)Il lavoro pianificato passa dietro il contratto JobRunner condiviso
Nodemailer / email ad hocE-mailI template migrano; l'invio passa attraverso un solo adapter di provider

Adotta i moduli in ordine di dipendenza

L'ordine di migrazione non è una preferenza — è un grafo di dipendenze. Ogni fase qui sotto è rilasciabile separatamente, e ognuna produce un sistema funzionante che puoi verificare prima che inizi la successiva. La tentazione è fare tutto in un weekend e debuggare l'intersezione; resistile — una migrazione a fasi significa che ogni bug che incontri appartiene esattamente a un modulo, un superpotere di debugging che non apprezzerai finché non lo salti.

  • Fase 1 — scheletro: fai lo scaffold del kit, configura le env, lascia cadere le route di prodotto nella struttura del progetto. Nulla è ancora cablato; l'app si avvia e le tue pagine renderizzano.
  • Fase 2 — auth: sposta il sign-in su Better Auth, migra le tabelle user/account, verifica sign-in + sign-out da un capo all'altro. Ogni fase successiva dipende dall'esistenza di una sessione.
  • Fase 3 — tenancy: adotta il modello workspace, fai il backfill di workspaceId sulle tue tabelle di prodotto, applica lo scoping in lettura e scrittura. È qui che il post sui confini del workspace si ripaga.
  • Fase 4 — fatturazione: punta checkout e webhook sul modulo di fatturazione, rimappa gli abbonamenti esistenti, verifica i controlli di entitlement contro dati Stripe reali in modalità test.
  • Fase 5 — lavoro in background ed email: porta i job sul contratto JobRunner e sposta le email transazionali sull'adapter email. Arrivano per ultime perché dipendono da users e workspaces già esistenti.

Sposta i dati onestamente

La migrazione dei dati è dove le migrazioni si guadagnano la loro reputazione. Le regole che la mantengono noiosa: scrivi uno script che puoi eseguire due volte, non mutare mai la sorgente prima del cutover e verifica i conteggi invece di fidarti dello script.

Users e account atterrano nello schema di Better Auth — l'email è la chiave di join, e gli account basati su password richiedono una decisione: o il tuo formato di hash esistente è verificabile da Better Auth, oppure rilasci un flusso di reset della password al cutover e lo tratti come un forcing function. Gli account OAuth di terze parti sono il caso facile — il provider li riemette al prossimo sign-in.

Per la fatturazione, il percorso onesto è la re-derivazione invece della copia delle righe: conserva il customer ID Stripe, lascia che la gestione webhook del kit ricostruisca lo stato degli entitlement dalla verità del provider e fai il diff di ciò che calcola contro i tuoi record di abbonamento esistenti. Il ledger idempotente dei webhook del kit esiste proprio perché gli eventi storici riproposti non provisionino nulla due volte.

// migration scripts are boring by design: upsert-shaped,
// re-runnable, and they verify counts instead of trusting themselves
for (const legacyUser of legacyUsers) {
  await upsertAuthUser({
    email: legacyUser.email.toLowerCase(),
    name: legacyUser.displayName,
    externalId: legacyUser.id, // preserved for one-shot joins
  });
}
const migrated = await countAuthUsers();
if (migrated !== legacyUsers.length) {
  throw new Error(`count mismatch: ${migrated} of ${legacyUsers.length}`);
}

Cosa resta tuo

Vale la pena dirlo chiaramente, perché è la parte che le persone sbagliano in entrambe le direzioni: le tue route di prodotto, il tuo schema, la tua logica di dominio e il tuo design non cambiano. Non stai ricostruendo la tua app dentro il kit — la stai appoggiando sopra.

L'errore inverso è tenere in vita il vecchio plumbing «temporaneamente» accanto al nuovo modulo. Un secondo controllo di sessione, un secondo handler di webhook, una seconda tabella di entitlement — ogni sistema parallelo «temporaneo» è permanente finché qualcuno non lo cancella, e ognuno è un incidente futuro. Sostituisci, verifica, cancella. Se una fase non può ancora eliminare il vecchio percorso, non è finita.

La checklist del cutover

Il cutover è una checklist, non un evento. Eseguila almeno una volta in un ambiente di staging con dati di forma produzione prima della data vera.

  • Riesegui lo script dei dati su uno snapshot di produzione fresco; conteggi e controlli a campione passano.
  • Accedi con ogni provider di auth che il tuo prodotto supporta — inclusi quelli di cui avevi dimenticato l'esistenza.
  • Compra il tuo stesso prodotto in modalità test di Stripe e segui il webhook fino all'entitlement.
  • Invia ogni email transazionale una volta; controlla che i link atterrino sul nuovo deploy.
  • Verifica il journey pagante da un capo all'altro, poi gira DNS/env e osserva la prima sessione reale.
  • Tieni in esecuzione il vecchio deploy per una settimana — il rollback è un cambio di DNS, non una migrazione.

Domande frequenti sulla migrazione in uno starter kit

La stessa manciata di domande emerge ogni volta che qualcuno si siede per farlo.

  • Quanto dura davvero una migrazione? — Per una singola web app con auth standard + Stripe: giorni di sere, non settimane. Il B2B multi-tenant con fatturazione personalizzata aggiunge tempo nelle fasi tre e quattro — è il lavoro sui dati che si allunga, mai lo spostamento dei file.
  • Devo migrare o ripartire da zero e portare le funzionalità? — Se il tuo codice di prodotto è più piccolo del tuo debito di infrastruttura, ripartire da zero sul kit e portare le route di prodotto è spesso più veloce. Se il tuo schema e la tua logica di dominio sono la parte difficile, migra sul posto.
  • Posso tenere il mio ORM / la mia libreria di auth durante la transizione? — Per poco, sì — un confine di modulo alla volta. Per sempre, no: due di qualsiasi cosa nel livello di infrastruttura è una tassa che si accumula.
  • E i miei webhook Stripe già in volo? — Punta l'endpoint webhook esistente sull'handler del kit una volta verificata la fase quattro; il ledger idempotente rende gli eventi riproposti o duplicati sicuri invece che pericolosi.
  • Devo adottare ogni modulo? — No. Mobile, desktop, job e AI sono superfici da adottare o ignorare — la licenza è la fondazione, non un mandato a rilasciare un'app per piattaforma.