> ## Documentation Index
> Fetch the complete documentation index at: https://supastack.dev/llms.txt
> Use this file to discover all available pages before exploring further.
> For the full documentation corpus in one file, fetch: https://supastack.dev/llms-full.txt

# Ein bestehendes SaaS zu supastack migrieren

Ein gestaffelter Spielplan, um ein laufendes SaaS — Auth, Mandanten, Billing, Jobs — auf das supastack-Fundament zu überführen, ohne Rewrite und ohne Downtime-Wochenende.

_Veröffentlicht 2026-10-29._
_Aktualisiert am 2026-10-29._

> **Wie migriere ich ein bestehendes SaaS zu supastack?**
> Das hängt von deiner Codebasis ab, aber die Reihenfolge ändert sich nicht: Migriere in Etappen, niemals in einem einzigen Rewrite. Sortiere das Repository in Produktcode (Routen, Schema, Fachlogik — der bleibt deiner) und Infrastruktur (Auth, Mandantenfähigkeit, Billing, Jobs, E-Mail — die ersetzt das Kit). Übernimm die Module in Abhängigkeitsreihenfolge: Struktur, Auth, Workspaces, Billing, dann Hintergrundarbeit. Verschiebe Daten mit einem wiederholbaren Skript, behalte die Stripe-Customer-IDs und schalte um, sobald die Abnahme besteht. Die meisten Web-Apps brauchen ein paar Abende, nicht Wochen.

## Mache Inventur, bevor du Code anfasst

Jede Migration, die schiefgeht, begann auf dieselbe Weise: Jemand hat das Kit-Repo geöffnet und angefangen, Dateien zu löschen. Das Kit ist ein Fundament, kein Theme — es ersetzt deine Infrastruktur, und es hat keine Meinung zu deinem Produkt. Bevor du irgendetwas verschiebst, teile dein Repository ehrlich in zwei Stapel.

Produktcode ist alles, was ein Kunde dir zurückbeschreiben könnte: deine Routen, dein Schema, deine Domänenlogik, deine Texte, deine Preisseite. Infrastruktur ist alles, was er nie sieht: Sessions, Mandantenprüfungen, Zahlungs-Webhooks, Jobs in der Queue, Transaktionsmails. Der erste Stapel migriert intakt. Der zweite wird ersetzt — und genau das ist der Punkt.

- Liste jede Routengruppe und markiere sie als Produkt oder Plumbing — sei kompromisslos; eine Einstellungsseite, die nur eine Webhook-URL umschaltet, ist Plumbing.
- Liste deine Env-Vars und markiere, welche Infrastruktur konfigurieren (Auth-Secrets, Provider-Keys) und welche Produktverhalten.
- Liste deine Tabellen und markiere, welche das Kit besitzen wird (Users, Sessions, Workspaces, Billing-Status) und welche deine bleiben.
- Schreib die eine User-Journey auf, die dich bezahlt — dieser Flow ist der Abnahmetest für jede Stufe unten.


## Ordne jede Nahtstelle einem Kit-Modul zu

Die Infrastruktur des Kits ist ein Satz von Modulen mit Verträgen dazwischen — deine Aufgabe ist es, jedes Stück deines alten Plumbings auf das Modul zu zeigen, dem es jetzt gehört. Das meiste des Mappings ist eins zu eins; die Stellen, an denen es das nicht ist, sind die echten Entscheidungen.

Zwei Mapping-Entscheidungen verdienen echtes Nachdenken statt mechanischen Portierens. Wenn du von `Prisma` kommst: Das Kit standardisiert auf Drizzle — du kannst Prisma für Produkttabellen während des Übergangs behalten, solltest aber auf Konvergenz planen; zwei ORMs in einer Codebasis sind eine Wartungssteuer, die sich aufzinst. Der [Vergleich Prisma vs. Drizzle](https://supastack.dev/de/blog/prisma-vs-drizzle-for-saas.md) legt dar, was sich ändert.

Und wenn du von einem gemanagten Auth-Anbieter kommst, widerstehe dem Drang, jedes Vendor-Feature vor der Umstellung nachzubauen. Managed Auth bündelt still Dinge, die dein Produkt vielleicht gar nicht nutzt — Session-Listen, Organisations-UIs, Webhook-Sync. Mappe, was dein Produkt tatsächlich aufruft; der [Auth-Vergleich](https://supastack.dev/de/blog/better-auth-vs-nextauth-vs-clerk.md) behandelt die Fähigkeitsunterschiede, die zählen.

| Dein aktuelles Plumbing | supastack-Modul | Was übernommen wird |
|---|---|---|
| NextAuth.js- / Auth.js-Sessions | Authentifizierung (Better Auth) | Users und Accounts migrieren; Sessions werden beim Cutover neu ausgestellt |
| Clerk oder ein gemanagter Auth-Anbieter | Authentifizierung (Better Auth) | Users migrieren per E-Mail; Drittanbieter-Sessions werden aufgegeben, nicht nachgespielt |
| Eigene Org-/Team-Tabellen | Arbeitsbereiche | Deine Org-Zeilen werden zu Workspace- + Mitgliedschaftszeilen |
| Handgebaute Stripe-Webhooks | Abrechnung | Stripe-Customer-IDs bleiben; der Abo-Status wird neu abgeleitet |
| CRON- + setTimeout-Jobs | Aufgaben (Trigger.dev) | Geplante Arbeit wandert hinter den gemeinsamen JobRunner-Vertrag |
| Nodemailer / Ad-hoc-Mails | E-Mail | Templates wandern mit; der Versand läuft über einen Provider-Adapter |


## Übernimm Module in Abhängigkeitsreihenfolge

Die Migrationsreihenfolge ist keine Vorliebe — sie ist ein Abhängigkeitsgraph. Jede Stufe unten ist separat auslieferbar, und jede produziert ein funktionierendes System, das du verifizieren kannst, bevor die nächste beginnt. Die Versuchung ist, alles an einem Wochenende zu machen und die Schnittmenge zu debuggen; widerstehe ihr — eine gestaffelte Migration bedeutet, dass jeder Bug, dem du begegnest, zu genau einem Modul gehört, eine Debugging-Superkraft, deren Wert dir erst klar wird, wenn du sie überspringst.

- Stufe 1 — Skelett: Scaffold das Kit, konfiguriere die Env, lege deine Produktrouten in die Projektstruktur. Noch ist nichts verdrahtet; die App bootet und deine Seiten rendern.
- Stufe 2 — Auth: Ziehe den Sign-in auf Better Auth um, migriere die User-/Account-Tabellen, verifiziere Sign-in + Sign-out Ende zu Ende. Jede spätere Stufe hängt davon ab, dass eine Session existiert.
- Stufe 3 — Mandantenfähigkeit: Übernimm das Workspace-Modell, fülle `workspaceId` auf deinen Produkttabellen nach, setze Scoping bei Reads und Writes durch. Hier bewährt sich der [Beitrag zu Workspace-Grenzen](https://supastack.dev/de/blog/workspace-boundaries.md).
- Stufe 4 — Billing: Zeige Checkout und Webhooks auf das Billing-Modul, mappe bestehende Abos neu, verifiziere Entitlement-Prüfungen gegen echte Stripe-Daten im Testmodus.
- Stufe 5 — Hintergrundarbeit und E-Mail: Portiere Jobs auf den JobRunner-Vertrag und ziehe Transaktionsmails auf den E-Mail-Adapter um. Sie kommen zuletzt, weil sie voraussetzen, dass Users und Workspaces bereits existieren.


## Bewege die Daten ehrlich

Datenmigration ist die Stelle, an der Migrationen ihren Ruf verdienen. Die Regeln, die sie langweilig halten: Schreib ein Skript, das du zweimal laufen lassen kannst, mutiere die Quelle nie vor dem Cutover und verifiziere Zählungen, statt dem Skript zu vertrauen.

Users und Accounts landen im Better-Auth-Schema — die E-Mail ist der Join-Key, und passwortbasierte Accounts brauchen eine Entscheidung: Entweder ist dein bestehendes Hash-Format eines, das Better Auth verifizieren kann, oder du lieferst beim Cutover einen Passwort-Reset-Flow aus und behandelst ihn als Forcing Function. OAuth-Accounts von Drittanbietern sind der einfache Fall — der Provider stellt sie beim nächsten Sign-in neu aus.

Beim Billing ist der ehrliche Weg die Neu-Ableitung statt des Zeilenkopierens: Behalte die Stripe-Customer-ID, lass die Webhook-Verarbeitung des Kits den Entitlement-Status aus der Wahrheit des Providers neu aufbauen und diffe, was sie berechnet, gegen deine bestehenden Abo-Einträge. Das [idempotente Webhook-Ledger](https://supastack.dev/de/blog/idempotent-billing-webhooks.md) des Kits existiert genau dafür, dass nachgespielte historische Events nichts doppelt bereitstellen.

```
// 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}`);
}
```


## Was deins bleibt

Es lohnt sich, das klar zu sagen, weil es der Teil ist, den Leute in beide Richtungen falsch verstehen: Deine Produktrouten, dein Schema, deine Domänenlogik und dein Design ändern sich nicht. Du baust deine App nicht im Kit neu — du setzt deine App auf ihm ab.

Der umgekehrte Fehler ist, altes Plumbing „vorübergehend“ neben dem neuen Modul am Leben zu halten. Ein zweiter Session-Check, ein zweiter Webhook-Handler, eine zweite Entitlement-Tabelle — jedes „vorübergehende“ Parallelsystem ist permanent, bis es jemand löscht, und jedes ist ein zukünftiger Incident. Ersetzen, verifizieren, löschen. Wenn eine Stufe den alten Pfad noch nicht löschen kann, ist sie nicht fertig.


## Die Cutover-Checkliste

Der Cutover ist eine Checkliste, kein Ereignis. Lauf sie vor dem echten Termin mindestens einmal in einer Staging-Umgebung mit produktionsförmigen Daten.

- Lauf das Datenskript gegen einen frischen Produktions-Snapshot; Zählungen und Stichproben gehen durch.
- Melde dich mit jedem Auth-Provider an, den dein Produkt unterstützt — inklusive derer, von denen du vergessen hattest, dass sie existieren.
- Kauf dein eigenes Produkt im Stripe-Testmodus und verfolge den Webhook bis zum Entitlement.
- Sende jede Transaktionsmail einmal; prüfe, dass Links auf dem neuen Deployment landen.
- Verifiziere die zahlende Journey Ende zu Ende, dann flippe DNS/Env und beobachte die erste echte Session.
- Lass das alte Deployment eine Woche laufen — ein Rollback ist ein DNS-Wechsel, keine Migration.


## Häufige Fragen zur Migration in ein Starter-Kit

Dieselbe Handvoll Fragen taucht jedes Mal auf, wenn sich jemand an diese Arbeit setzt.

- Wie lange dauert eine Migration wirklich? — Für eine einzelne Web-App mit Standard-Auth + Stripe: Tage an Abenden, nicht Wochen. Multi-Tenant-B2B mit eigenem Billing fügt Zeit in Stufe drei und vier hinzu — die Datenarbeit ist es, die sich zieht, nie das Verschieben von Dateien.
- Sollte ich migrieren oder neu starten und Features portieren? — Wenn dein Produktcode kleiner ist als deine Infrastrukturschulden, ist ein Neustart auf dem Kit mit portierten Produktrouten oft schneller. Wenn dein Schema und deine Domänenlogik der schwere Teil sind, migriere am Ort.
- Kann ich mein bestehendes ORM / meine Auth-Library während des Übergangs behalten? — Kurzfristig ja — eine Modulgrenze nach der anderen. Dauerhaft nein: Zwei von allem in der Infrastrukturschicht sind eine sich aufzinsende Steuer.
- Was ist mit meinen bereits unterwegs befindlichen Stripe-Webhooks? — Zeige den bestehenden Webhook-Endpunkt auf den Handler des Kits, sobald Stufe vier verifiziert ist; das idempotente Ledger macht nachgespielte oder doppelte Events sicher statt gefährlich.
- Muss ich jedes Modul übernehmen? — Nein. Mobile, Desktop, Jobs und KI sind Oberflächen zum Übernehmen oder Ignorieren — die Lizenz ist das Fundament, kein Mandat, pro Plattform eine App auszuliefern.
