Como migro um SaaS existente para o supastack?
Depende do teu codebase, mas a sequência não muda: migra por fases, nunca numa só reescrita. Faz o inventário do repositório em código de produto (rotas, schema, lógica de domínio — fica teu) e infraestrutura (auth, tenancy, faturação, jobs, email — o kit substitui-a). Adota os módulos por ordem de dependência: estrutura, auth, workspaces, faturação e depois trabalho em segundo plano. Move os dados com um script que possas correr duas vezes, mantém os IDs de cliente Stripe e faz a transição depois de a aceitação passar. A maioria das apps web demora dias de serões, não semanas.
Faz o inventário antes de tocar no código
Todas as migrações que correm mal começaram da mesma maneira: alguém abriu o repositório do kit e começou a apagar ficheiros. O kit é uma fundação, não um tema — substitui a tua infraestrutura e não tem opinião sobre o teu produto. Antes de moveres o que quer que seja, divide o teu repositório nas duas pilhas com honestidade.
Código de produto é tudo o que um cliente conseguiria descrever-te de volta: as tuas rotas, o teu schema, a tua lógica de domínio, os teus textos, a tua página de preços. Infraestrutura é tudo o que eles nunca veem: sessões, verificações de tenancy, webhooks de pagamento, jobs em fila, email transacional. A primeira pilha migra intacta. A segunda é substituída — que é exatamente a ideia.
- Lista cada grupo de rotas e etiqueta-o como produto ou canalização — sê implacável; uma página de definições que só alterna um URL de webhook é canalização.
- Lista as tuas variáveis de ambiente e marca quais configuram infraestrutura (segredos de auth, chaves de fornecedores) versus comportamento do produto.
- Lista as tuas tabelas e marca quais ficam a cargo do kit (utilizadores, sessões, workspaces, estado de faturação) versus quais são tuas.
- Escreve a única jornada de utilizador que te paga — esse fluxo é o teste de aceitação de cada fase abaixo.
Mapeia cada junção a um módulo do kit
A infraestrutura do kit é um conjunto de módulos com contratos entre eles — o teu trabalho é apontar cada peça da tua canalização antiga ao módulo que agora a detém. A maior parte do mapeamento é um-para-um; os sítios onde não é são onde tomas decisões a sério.
Duas escolhas de mapeamento merecem reflexão real em vez de portagem mecânica. Se vens do Prisma, o kit padroniza no Drizzle — podes manter o Prisma nas tabelas do produto durante a transição, mas planeia convergir; dois ORMs num só codebase são um imposto de manutenção que se acumula. A comparação Prisma vs Drizzle expõe o que muda.
E se vens de um fornecedor de auth gerida, resiste à vontade de replicar cada funcionalidade do fornecedor antes da mudança. A auth gerida embrulha discretamente coisas que o teu produto pode não usar — listas de sessões, UIs de organizações, sincronização de webhooks. Mapeia o que o teu produto realmente chama; a comparação de auth cobre as diferenças de capacidades que importam.
| A tua canalização atual | Módulo supastack | O que atravessa |
|---|---|---|
| Sessões NextAuth.js / Auth.js | Autenticação (Better Auth) | Utilizadores e contas migram; as sessões são reemitidas na transição |
| Clerk ou um fornecedor de auth gerida | Autenticação (Better Auth) | Os utilizadores migram por email; as sessões de terceiros são abandonadas, não reproduzidas |
| Tabelas próprias de organização/equipa | Espaços de trabalho | As tuas linhas de organização tornam-se linhas de workspace + pertença |
| Webhooks da Stripe feitos à mão | Faturação | Os IDs de cliente Stripe sobrevivem; o estado da subscrição é rederivado |
| Jobs CRON + setTimeout | Tarefas (Trigger.dev) | O trabalho agendado passa para trás do contrato partilhado JobRunner |
| Nodemailer / email ad hoc | Os templates migram; o envio passa por um único adaptador de fornecedor |
Adota os módulos por ordem de dependência
A ordem de migração não é uma preferência — é um grafo de dependências. Cada fase abaixo é lançável separadamente e cada uma produz um sistema funcional que podes verificar antes de a próxima começar. A tentação é fazer tudo num fim de semana e depurar a interseção; resiste-lhe — uma migração faseada significa que cada bug que encontras pertence a exatamente um módulo, um superpoder de depuração que só vais apreciar quando o saltares.
- Fase 1 — esqueleto: faz o scaffold do kit, configura o env, larga as tuas rotas de produto na estrutura do projeto. Ainda nada está ligado; a app arranca e as tuas páginas renderizam.
- Fase 2 — auth: passa o início de sessão para o Better Auth, migra as tabelas de utilizadores/contas, verifica o sign-in + sign-out de ponta a ponta. Todas as fases seguintes dependem de existir uma sessão.
- Fase 3 — tenancy: adota o modelo de workspaces, preenche retroativamente o
workspaceIdnas tuas tabelas de produto, impõe o âmbito nas leituras e escritas. É aqui que o post sobre fronteiras do workspace se paga a si próprio. - Fase 4 — faturação: aponta o checkout e os webhooks ao módulo de faturação, remapeia as subscrições existentes, verifica os controlos de direitos contra dados reais da Stripe em modo de teste.
- Fase 5 — trabalho em segundo plano e email: porta os jobs para o contrato JobRunner e passa o email transacional para o adaptador de email. Ficam para o fim porque dependem de utilizadores e workspaces já existentes.
Move os dados com honestidade
A migração de dados é onde as migrações ganham a sua reputação. As regras que a mantêm aborrecida: escreve um script que possas correr duas vezes, nunca mutes a origem até à transição e verifica contagens em vez de confiar no script.
Utilizadores e contas aterram no schema do Better Auth — o email é a chave de junção, e as contas com palavra-passe exigem uma decisão: ou o teu formato de hash atual é um que o Better Auth consegue verificar, ou lanças um fluxo de reposição de palavra-passe na transição e tratas isso como uma função de força. As contas OAuth de terceiros são o caso fácil — o fornecedor reemite-as no próximo início de sessão.
Na faturação, o caminho honesto é a rederivação em vez de copiar linhas: mantém o ID de cliente Stripe, deixa o tratamento de webhooks do kit reconstruir o estado de direitos a partir da verdade do fornecedor e faz diff do que ele calcula contra os teus registos de subscrição existentes. O ledger idempotente de webhooks do kit existe precisamente para que eventos históricos reproduzidos não provisionem nada duas vezes.
// 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}`);
}O que fica teu
Vale a pena dizê-lo claramente, porque é a parte em que as pessoas erram nas duas direções: as tuas rotas de produto, o teu schema, a tua lógica de domínio e o teu design não mudam. Não estás a reconstruir a tua app dentro do kit — estás a pousá-la em cima dele.
O erro inverso é manter a canalização antiga viva «temporariamente» ao lado do módulo novo. Uma segunda verificação de sessão, um segundo handler de webhooks, uma segunda tabela de direitos — cada sistema paralelo «temporário» é permanente até alguém o apagar, e cada um é um incidente futuro. Substitui, verifica, apaga. Se uma fase ainda não consegue apagar o caminho antigo, não está terminada.
A checklist da transição
A transição é uma checklist, não um evento. Corre-a num ambiente de staging pelo menos uma vez com dados com formato de produção antes da data real.
- Corre novamente o script de dados contra um snapshot fresco de produção; as contagens e as verificações aleatórias passam.
- Inicia sessão com cada fornecedor de auth que o teu produto suporta — incluindo aqueles cuja existência esqueceste.
- Compra o teu próprio produto no modo de teste da Stripe e segue o webhook até ao direito.
- Envia cada email transacional uma vez; confirma que os links aterram no novo deploy.
- Verifica a jornada pagante de ponta a ponta, depois muda o DNS/env e observa a primeira sessão real.
- Mantém o deploy antigo a correr durante uma semana — o rollback é uma mudança de DNS, não uma migração.
Perguntas frequentes sobre migrar para um starter kit
O mesmo punhado de perguntas surge sempre que alguém se senta para fazer isto.
- Quanto tempo demora realmente uma migração? — Para uma única app web com auth padrão + Stripe: dias de serões, não semanas. B2B multi-tenant com faturação personalizada acrescenta tempo nas fases três e quatro — é o trabalho de dados que se estica, nunca a mudança de ficheiros.
- Devo migrar ou começar de raiz e portar funcionalidades? — Se o teu código de produto é menor que a tua dívida de infraestrutura, começar de raiz no kit e portar as rotas de produto é muitas vezes mais rápido. Se o teu schema e lógica de domínio são a parte difícil, migra no sítio.
- Posso manter o meu ORM / a minha biblioteca de auth durante a transição? — Por pouco tempo, sim — uma fronteira de módulo de cada vez. Para sempre, não: dois de qualquer coisa na camada de infraestrutura é um imposto que se acumula.
- E os meus webhooks da Stripe já em trânsito? — Aponta o endpoint de webhooks existente ao handler do kit assim que a fase quatro estiver verificada; o ledger idempotente torna eventos reproduzidos ou duplicados seguros em vez de perigosos.
- Tenho de adotar todos os módulos? — Não. Mobile, desktop, jobs e IA são superfícies para adotar ou ignorar — a licença é a fundação, não um mandato para lançar uma app por plataforma.