Comment migrer un SaaS existant vers supastack ?
Cela dépend de votre base de code, mais la séquence ne change pas : migrez par étapes, jamais en une seule réécriture. Triez le dépôt entre code produit (routes, schéma, logique métier — il reste à vous) et infrastructure (auth, tenants, facturation, jobs, e-mail — le kit la remplace). Adoptez les modules dans l'ordre des dépendances : structure, auth, workspaces, facturation, puis travail de fond. Déplacez les données avec un script réexécutable, gardez les customer ID Stripe et basculez une fois le test d'acceptation validé. La plupart des apps web prennent des jours de soirées, pas des semaines.
Faites l'inventaire avant de toucher au code
Toutes les migrations qui tournent mal ont commencé de la même façon : quelqu'un a ouvert le dépôt du kit et s'est mis à supprimer des fichiers. Le kit est une fondation, pas un thème — il remplace votre infrastructure, et il n'a aucune opinion sur votre produit. Avant de déplacer quoi que ce soit, séparez honnêtement votre dépôt en deux tas.
Le code produit, c'est tout ce qu'un client pourrait vous redécrire : vos routes, votre schéma, votre logique métier, vos textes, votre page de tarifs. L'infrastructure, c'est tout ce qu'il ne voit jamais : sessions, contrôles de tenant, webhooks de paiement, jobs en file, e-mails transactionnels. Le premier tas migre intact. Le second est remplacé — et c'est précisément le but.
- Listez chaque groupe de routes et étiquetez-le produit ou plomberie — soyez impitoyable ; une page de réglages qui ne fait que basculer une URL de webhook est de la plomberie.
- Listez vos variables d'environnement et marquez celles qui configurent l'infrastructure (secrets d'auth, clés de prestataires) par rapport à celles du comportement produit.
- Listez vos tables et marquez celles que le kit possèdera (users, sessions, workspaces, état de facturation) par rapport à celles qui restent vôtres.
- Écrivez le parcours utilisateur qui vous fait payer — ce flux est le test d'acceptation de chaque étape ci-dessous.
Affectez chaque raccord à un module du kit
L'infrastructure du kit est un ensemble de modules reliés par des contrats — votre travail consiste à pointer chaque morceau de votre ancienne plomberie vers le module qui en est désormais propriétaire. L'essentiel du mapping est un pour un ; les endroits où il ne l'est pas sont ceux où vous prenez de vraies décisions.
Deux choix de mapping méritent une vraie réflexion plutôt qu'un portage mécanique. Si vous venez de Prisma, le kit standardise sur Drizzle — vous pouvez garder Prisma pour les tables produit pendant la transition, mais prévoyez de converger ; deux ORM dans une même base de code sont une taxe de maintenance qui se cumule. La comparaison Prisma vs Drizzle détaille ce qui change.
Et si vous venez d'un prestataire d'auth managé, résistez à l'envie de répliquer chaque fonctionnalité du prestataire avant de basculer. L'auth managée regroupe discrètement des choses que votre produit n'utilise peut-être pas — listes de sessions, UI d'organisations, synchro webhook. Mappez ce que votre produit appelle réellement ; la comparaison des auth couvre les différences de capacités qui comptent.
| Votre plomberie actuelle | Module supastack | Ce qui est repris |
|---|---|---|
| Sessions NextAuth.js / Auth.js | Authentification (Better Auth) | Users et comptes migrent ; les sessions sont réémises à la bascule |
| Clerk ou un prestataire d'auth managé | Authentification (Better Auth) | Les users migrent par e-mail ; les sessions tierces sont abandonnées, pas rejouées |
| Tables org/équipe maison | Espaces de travail | Vos lignes d'org deviennent des lignes workspace + membership |
| Webhooks Stripe faits main | Facturation | Les customer ID Stripe survivent ; l'état des abonnements est redérivé |
| Jobs CRON + setTimeout | Tâches (Trigger.dev) | Le travail planifié passe derrière le contrat JobRunner partagé |
| Nodemailer / e-mails ad hoc | Les templates migrent ; l'envoi passe par un seul adaptateur de prestataire |
Adoptez les modules dans l'ordre des dépendances
L'ordre de migration n'est pas une préférence — c'est un graphe de dépendances. Chaque étape ci-dessous est livrable séparément, et chacune produit un système fonctionnel que vous pouvez vérifier avant que la suivante ne commence. La tentation est de tout faire en un week-end et de déboguer l'intersection ; résistez-y — une migration par étapes signifie que chaque bug rencontré appartient à exactement un module, un super-pouvoir de débogage dont vous n'apprécierez la valeur qu'en le sautant.
- Étape 1 — squelette : scaffoldez le kit, configurez l'env, déposez vos routes produit dans la structure du projet. Rien n'est encore câblé ; l'app démarre et vos pages s'affichent.
- Étape 2 — auth : faites passer la connexion sur Better Auth, migrez les tables user/account, vérifiez connexion + déconnexion de bout en bout. Chaque étape suivante suppose qu'une session existe.
- Étape 3 — multi-tenancy : adoptez le modèle workspace, remplissez
workspaceIdsur vos tables produit, appliquez le scoping en lecture et en écriture. C'est ici que l'article sur les frontières de workspace prouve sa valeur. - Étape 4 — facturation : pointez le checkout et les webhooks vers le module de facturation, remappez les abonnements existants, vérifiez les contrôles de droits contre de vraies données Stripe en mode test.
- Étape 5 — travail de fond et e-mail : portez les jobs sur le contrat JobRunner et faites passer les e-mails transactionnels sur l'adaptateur e-mail. Ils viennent en dernier parce qu'ils supposent des users et des workspaces déjà existants.
Déplacez les données honnêtement
La migration des données est l'endroit où les migrations gagnent leur réputation. Les règles qui la gardent ennuyeuse : écrivez un script que vous pouvez exécuter deux fois, ne modifiez jamais la source avant la bascule, et vérifiez les comptages au lieu de faire confiance au script.
Users et comptes atterrissent dans le schéma Better Auth — l'e-mail est la clé de jointure, et les comptes par mot de passe exigent une décision : soit votre format de hash existant est vérifiable par Better Auth, soit vous livrez un flux de réinitialisation de mot de passe à la bascule et le traitez comme un forcing. Les comptes OAuth tiers sont le cas facile — le prestataire les réémet à la prochaine connexion.
Pour la facturation, le chemin honnête est la re-dérivation plutôt que la copie de lignes : gardez le customer ID Stripe, laissez la gestion des webhooks du kit reconstruire l'état des droits depuis la vérité du prestataire, et comparez ce qu'elle calcule à vos enregistrements d'abonnements existants. Le registre de webhooks idempotent du kit existe précisément pour que les événements historiques rejoués ne provisionnent rien en double.
// 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}`);
}Ce qui reste à vous
Cela vaut la peine de le dire clairement, parce que c'est la partie que les gens ratent dans les deux sens : vos routes produit, votre schéma, votre logique métier et votre design ne changent pas. Vous ne reconstruisez pas votre app dans le kit — vous posez votre app dessus.
L'erreur inverse consiste à garder l'ancienne plomberie en vie « temporairement » à côté du nouveau module. Un second contrôle de session, un second gestionnaire de webhooks, une seconde table de droits — tout système parallèle « temporaire » est permanent tant que personne ne le supprime, et chacun est un incident futur. Remplacez, vérifiez, supprimez. Si une étape ne peut pas encore supprimer l'ancien chemin, elle n'est pas terminée.
La checklist de bascule
La bascule est une checklist, pas un événement. Exécutez-la au moins une fois dans un environnement de préproduction avec des données de forme production avant la vraie date.
- Réexécutez le script de données sur un snapshot de production frais ; les comptages et les sondages passent.
- Connectez-vous avec chaque prestataire d'auth que votre produit supporte — y compris ceux dont vous aviez oublié l'existence.
- Achetez votre propre produit en mode test Stripe et suivez le webhook jusqu'au droit d'accès.
- Envoyez chaque e-mail transactionnel une fois ; vérifiez que les liens atterrissent sur le nouveau déploiement.
- Vérifiez le parcours payant de bout en bout, puis basculez DNS/env et observez la première vraie session.
- Gardez l'ancien déploiement en marche pendant une semaine — le rollback est un changement de DNS, pas une migration.
Questions fréquentes sur la migration vers un starter kit
La même poignée de questions revient chaque fois que quelqu'un s'assoit pour le faire.
- Combien de temps prend réellement une migration ? — Pour une seule app web avec auth standard + Stripe : des jours de soirées, pas des semaines. Le B2B multi-tenant avec facturation maison ajoute du temps aux étapes trois et quatre — c'est le travail sur les données qui s'étire, jamais les déplacements de fichiers.
- Faut-il migrer ou repartir de zéro et porter les fonctionnalités ? — Si votre code produit est plus petit que votre dette d'infrastructure, repartir de zéro sur le kit et porter les routes produit est souvent plus rapide. Si votre schéma et votre logique métier sont la partie difficile, migrez sur place.
- Puis-je garder mon ORM / ma bibliothèque d'auth pendant la transition ? — Brièvement, oui — une frontière de module à la fois. Définitivement, non : deux de quoi que ce soit dans la couche infrastructure est une taxe qui se cumule.
- Et mes webhooks Stripe déjà en vol ? — Pointez l'endpoint webhook existant vers le gestionnaire du kit une fois l'étape quatre vérifiée ; le registre idempotent rend les événements rejoués ou dupliqués sûrs au lieu de dangereux.
- Dois-je adopter tous les modules ? — Non. Mobile, bureau, jobs et IA sont des surfaces à adopter ou à ignorer — la licence est la fondation, pas l'obligation de livrer une app par plateforme.