Skip to content

Laravel-Datenbankmigrationen sicher testen und deployen

Datenbankänderungen werden verlässlich, wenn Migration, Testdatenbank und Deployment denselben nachvollziehbaren Ablauf bilden. Dieser Leitfaden zeigt dir, was in Git gehört, wann ein Schema-Dump hilft und wie du riskante Produktionsänderungen kontrollierst.

Moritz Klaßen

Moritz Klaßen

Entwickler & Gründer von Klassen AI

8 Min. Lesezeit

Eine Laravel-Migration ist nicht erledigt, sobald php artisan migrate lokal ohne Fehlermeldung durchläuft. Der erfolgreiche Befehl belegt nur, dass die Änderung auf genau dieser Datenbank funktioniert hat. Er sagt noch nichts über eine leere Testdatenbank, abweichende Entwicklungsstände oder das Deployment mit bestehenden Produktionsdaten aus.

Ein sicherer Workflow behandelt die Migration deshalb als Teil einer durchgängigen Kette. Eine Datenbankänderung muss aus der Versionsverwaltung reproduzierbar sein, in einer getrennten Testdatenbank funktionieren und sich kontrolliert auf einen bestehenden Datenbestand anwenden lassen. Erst diese Kette macht aus einer lokalen Änderung einen deploybaren Stand.

Definiere zuerst das Zielbild der Datenbankänderung

Bevor du eine Migration erzeugst, sollte klar sein, welchen Übergang sie beschreibt. Eine neue Spalte kann technisch in einer Zeile angelegt werden. Für den Workflow sind jedoch weitere Fragen relevant: Darf die Spalte zunächst leer sein? Erwartet bereits veröffentlichter Anwendungscode ihren Inhalt? Müssen vorhandene Datensätze ergänzt werden? Kann während des Deployments noch eine ältere Anwendungsversion auf das Schema zugreifen?

Formuliere die Änderung daher als Übergang zwischen zwei gültigen Zuständen. Zum Beispiel: Die Tabelle orders erhält zunächst eine optionale Spalte für eine externe Referenz. Anschließend befüllt ein separater Prozess bestehende Datensätze. Erst eine spätere Anwendungsversion setzt die Referenz voraus. Dieses Vorgehen hält Schemaänderung, Datenmigration und fachliche Aktivierung auseinander.

Gerade bei kleinen Projekten wirkt diese Trennung zunächst aufwendiger als eine große Migration. Sie reduziert aber die Zahl der Annahmen, die während eines einzelnen Deployments gleichzeitig erfüllt sein müssen.

Behandle Migrationen als versionierte Quelle des Schemas

Laravel verwendet Migrationen als versionierbare Beschreibung des Datenbankschemas. Ein Schema-Dump kann einen kompakten Ausgangsstand ergänzen; bei einer Datenbank ohne ausgeführte Migrationen lädt Laravel zunächst diesen Dump und führt danach neuere Migrationen aus. Die Dokumentation empfiehlt außerdem, Schema-Dateien in die Versionsverwaltung aufzunehmen. Die Laravel-Dokumentation beschreibt Migrationen, Schema-Dumps und das Verhalten von schema:dump –prune.

Für dein Repository ergibt sich daraus eine klare Aufteilung. Folgende Bestandteile gehören üblicherweise in Git:

  • neue und weiterhin benötigte Dateien unter database/migrations,
  • ein bewusst erzeugter Schema-Dump unter database/schema,
  • Factories und Seeders, die für reproduzierbare Tests oder einen definierten Ausgangsbestand erforderlich sind,
  • Tests, die das Verhalten der Anwendung mit dem geänderten Schema absichern,
  • Testkonfigurationen ohne Zugangsdaten und andere Geheimnisse.

Nicht in Git gehören lokale Datenbankdateien mit Arbeitsdaten, Produktionskopien oder exportierte Dumps mit personenbezogenen und vertraulichen Inhalten. Ein Schema-Dump beschreibt die Struktur; er ist kein Anlass, echte Datensätze im Repository abzulegen.

Bereits veröffentlichte Migrationen solltest du als unveränderliche Historie behandeln. Wenn eine Migration nur auf deinem unveröffentlichten Branch existiert, kannst du sie noch korrigieren. Sobald andere Entwicklungsstände oder eine Umgebung sie ausgeführt haben könnten, ist eine zusätzliche Migration nachvollziehbarer. Andernfalls kann dieselbe Migrationsdatei je nach Ausführungszeitpunkt unterschiedliche Bedeutungen tragen.

Setze Schema-Dumps als Ausgangspunkt ein

Mit einem Schema-Dump fasst du den aktuellen Strukturstand als Ausgangsbasis zusammen. Das kann sinnvoll sein, wenn sich über längere Zeit viele Migrationen angesammelt haben und neue Datenbanken nicht jede historische Änderung einzeln durchlaufen sollen.

php artisan schema:dump

Nach seiner Erstellung kommen neue Änderungen weiterhin als reguläre Migrationen hinzu. Auf einer leeren Datenbank wird erst der Dump geladen, anschließend folgen die darin noch nicht enthaltenen Migrationen.

Die Variante mit –prune entfernt die bisherigen Migrationen aus dem Ausgangsbestand:

php artisan schema:dump --prune

Dieser Befehl sollte keine beiläufige Aufräumaktion sein. Er verändert die im Repository verfügbare Historie und kann parallele Branches betreffen, die noch auf älteren Migrationen aufbauen. Erzeuge einen solchen Dump daher in einer eigenen, gut sichtbaren Änderung. Prüfe anschließend mindestens, ob eine vollständig leere Datenbank aus dem Dump plus verbleibenden Migrationen aufgebaut werden kann.

Ein Schema-Dump passt vor allem zu einem Projekt mit stabiler Ausgangsbasis. Wenig sinnvoll ist er, wenn er eine fehlerhafte Migration verdecken soll oder gerade mehrere langlebige Branches tief in der Migrationshistorie arbeiten. Das Löschen alter Dateien repariert keinen unklaren Übergang.

Trenne Testdatenbank und Entwicklungsdatenbank konsequent

Die Test-Suite darf nicht von deiner lokalen Arbeitsdatenbank abhängen. Dort liegen oft manuell angelegte Datensätze, ältere Strukturen oder Daten, die einen Fehler zufällig verdecken. Verwende für Tests eine eigene Datenbank mit eigenen Zugangsdaten und einem eindeutig erkennbaren Namen. In CI sollte sie ausschließlich für den jeweiligen Lauf vorgesehen sein.

Besondere Vorsicht gilt für Befehle, die Tabellen leeren oder vollständig neu aufbauen. Sichere nicht nur die Umgebungsvariable APP_ENV ab. Prüfe bei eigenen Skripten zusätzlich, ob Host und Datenbankname den erwarteten Testwerten entsprechen.

Laravel stellt für Datenbanktests mehrere Reset-Strategien bereit. RefreshDatabase verwendet bei aktuellem Schema-Stand Transaktionen. DatabaseMigrations und DatabaseTruncation stehen für vollständige beziehungsweise alternative Zurücksetzungen zur Verfügung. Die Unterschiede sind in der Laravel-Dokumentation zu Datenbanktests beschrieben.

RefreshDatabase als üblicher Ausgangspunkt

RefreshDatabase passt für viele Feature-Tests, deren Daten nach jedem Test wieder verschwinden sollen. Die Testfälle erhalten einen kontrollierten Datenzustand, ohne dass jeder einzelne Test zwangsläufig das komplette Schema neu aufbauen muss.

DatabaseMigrations für den vollständigen Migrationsweg

DatabaseMigrations ist interessant, wenn der Migrationszyklus selbst Bestandteil der Prüfung sein soll. Das ist gezielter einzusetzen als ein pauschaler Standard für jede Testklasse. Anwendungstests und Migrationstests beantworten unterschiedliche Fragen.

DatabaseTruncation für einen geleerten Tabellenbestand

DatabaseTruncation leert Tabellen zwischen Tests und kann hilfreich sein, wenn Transaktionen nicht zum getesteten Verhalten passen. Welche Strategie geeignet ist, hängt unter anderem davon ab, ob dein Test mehrere Verbindungen, Prozesse oder Transaktionsgrenzen berührt.

Keine dieser Strategien ersetzt automatisch einen Test des Upgrade-Pfads. Eine frische CI-Datenbank beweist, dass sich das Projekt von null auf den aktuellen Stand bringen lässt. Sie beweist nicht, dass eine bereits vorhandene Datenbank der letzten veröffentlichten Version sicher aktualisiert werden kann.

Baue zwei Prüfpfade statt eines einzigen Tests auf

Ein belastbarer Workflow prüft zwei unterschiedliche Situationen. Der erste Pfad startet mit einer leeren Datenbank. Der zweite startet bei Bedarf mit dem Schema oder einem bereinigten Datenbestand der zuletzt veröffentlichten Version.

Der leere Aufbau gehört in jeden regulären CI-Lauf. Ein kompakter Ablauf kann so aussehen:

php artisan migrate
php artisan test

Der zweite Prüfpfad ist besonders für riskante Änderungen relevant: entfernte oder umbenannte Spalten, geänderte Datentypen, neue Eindeutigkeitsregeln und Migrationen bestehender Inhalte. Als Ausgangspunkt kann ein strukturell repräsentativer, anonymisierter Stand der vorherigen Veröffentlichung dienen. Darauf führst du ausschließlich die neuen Migrationen und die betroffenen Anwendungstests aus.

Damit beantwortest du beide entscheidenden Fragen:

  • Kann das Projekt aus der Versionsverwaltung vollständig neu aufgebaut werden?
  • Kann eine bereits existierende Installation vom letzten veröffentlichten Stand auf den neuen Stand wechseln?

Bei einer kleinen internen Anwendung muss der zweite Pfad nicht für jede harmlose Indexergänzung automatisiert werden. Er sollte aber bewusst Teil der Prüfung werden, sobald vorhandene Daten oder parallele Anwendungsversionen betroffen sind.

Nutze einen wiederholbaren Ablauf für jede Änderung

Für Solo-Entwickler und kleine Agenturteams reicht ein kompakter Prozess. Entscheidend ist, dass er bei jeder Datenbankänderung in derselben Reihenfolge abläuft.

  1. Übergang beschreiben: Halte fest, welcher alte Zustand existiert, welcher neue Zustand entstehen soll und ob beide während des Deployments gleichzeitig unterstützt werden müssen.
  2. Migration erzeugen: Lege eine neue Migrationsdatei an, statt bereits ausgeführte Historie nachträglich umzuschreiben.
  3. Auf Arbeitsdaten prüfen: Führe die Migration auf deiner lokalen Entwicklungsdatenbank aus und teste die betroffene Funktion.
  4. Leeren Aufbau prüfen: Verwende eine separate, entbehrliche Datenbank und baue das Schema vollständig aus Dump und Migrationen auf.
  5. Anwendungstests ausführen: Prüfe neben der Tabellenstruktur auch Schreibvorgänge, Validierung, Abfragen und fachliche Randfälle.
  6. Upgrade-Pfad bewerten: Entscheide ausdrücklich, ob bestehende Daten oder ältere Anwendungsversionen eine zusätzliche Prüfung erfordern.
  7. Gemeinsam committen: Migration, zugehöriger Anwendungscode und relevante Tests gehören in eine nachvollziehbare Änderung. Ein Schema-Dump kommt nur hinzu, wenn er bewusst erneuert wurde.
  8. CI auf frischer Datenbank ausführen: Die Pipeline baut das Schema selbst auf. Eine dauerhaft wiederverwendete CI-Datenbank kann fehlende Migrationen verdecken.

Für einen manuellen Test eines vollständigen Neuaufbaus auf einer garantiert entbehrlichen lokalen Datenbank kann migrate:fresh sinnvoll sein:

php artisan migrate:fresh --seed

Prüfe vor solchen Befehlen immer Datenbankname und Zugangsdaten. Auf gemeinsam genutzten Entwicklungs-, Staging- oder Produktionsdatenbanken gehören sie nicht in einen unüberprüften Standardablauf.

Plane das Deployment als Datenübergang

Vor dem Deployment reicht die Frage „Läuft die Migration?“ nicht aus. Du musst wissen, was während und nach ihrer Ausführung mit Daten und Anwendungscode passiert.

Bei Änderungen, die ältere Anwendungsversionen brechen könnten, hilft ein gestuftes Vorgehen:

  1. Erweitere das Schema zunächst abwärtskompatibel, etwa durch eine neue optionale Spalte oder Tabelle.
  2. Veröffentliche Anwendungscode, der mit altem und erweitertem Schema umgehen kann.
  3. Übertrage vorhandene Daten bei Bedarf in einem kontrollierten, separat beobachtbaren Prozess.
  4. Stelle Lese- und Schreibzugriffe auf die neue Struktur um.
  5. Entferne alte Spalten oder Tabellen erst in einer späteren Veröffentlichung, wenn keine alte Anwendungsversion mehr darauf zugreift.

Für kleine Anwendungen mit geplantem Wartungsfenster kann ein gemeinsamer Schritt angemessen sein. Auch dann solltest du die Reihenfolge ausdrücklich festlegen. Anwendungscode und Migration gleichzeitig hochzuladen, ohne ihre Ausführungsreihenfolge zu definieren, ist kein kontrollierter Deployment-Ablauf.

Vor der Produktionsausführung brauchst du außerdem eine passende Wiederherstellungsstrategie. Ein down()-Abschnitt ist kein allgemeiner Ersatz dafür. Er kann eine Spalte wieder anlegen, aber gelöschte Inhalte nicht automatisch rekonstruieren. Je nach Änderung besteht der Rückweg aus einem Anwendungs-Rollback, einer korrigierenden Vorwärtsmigration, einer Datenwiederherstellung oder einer Kombination daraus.

Behandle risikoreiche Artisan-Befehle mit Absicht

In Produktion sollten nur die für den geplanten Übergang notwendigen Migrationen ausgeführt werden. Lege für Befehle mit Rücksetz-, Neuaufbau- oder Rollback-Wirkung eine klare Regel fest:

  • migrate –force: Verwende den Befehl nur als bewusst festgelegten Teil eines kontrollierten Deployments.
  • migrate:rollback: Setze ihn erst ein, wenn die betroffenen Migrationen, ihr down()-Pfad und die Folgen für vorhandene Daten geprüft sind.
  • migrate:refresh und migrate:fresh: Nimm sie nicht als regulären Schritt in Produktionsabläufe auf.
  • schema:dump –prune: Gehört in eine bewusste Repository-Wartung, nicht als spontaner Teil des Produktionsdeployments.

Stelle außerdem sicher, dass bei parallelen Deployments nur ein Prozess die Migrationen ausführt. Protokolliere den ausgeführten Stand und prüfe danach die betroffenen Schreib- und Lesewege.

Die entscheidende Prüfung ist Reproduzierbarkeit

Ein sicherer Laravel-Migrationsworkflow beginnt nicht beim Produktionsbefehl. Er beginnt bei einer versionierten Änderung, die sich auf einer leeren Datenbank reproduzieren lässt, in einer getrennten Testumgebung geprüft wird und einen geplanten Übergang für bestehende Daten besitzt.

Schema-Dumps können diesen Ablauf vereinfachen, ersetzen ihn aber nicht. Test-Traits halten Testdaten kontrollierbar, beweisen allein jedoch keinen sicheren Upgrade-Pfad. Und –force ersetzt keine Prüfung der Daten- und Anwendungsauswirkungen.

Wenn du jede Datenbankänderung durch dieselbe Kette führst – Migration, leerer Aufbau, Anwendungstest, Upgrade-Bewertung und kontrolliertes Deployment –, bleibt ihr Weg bis in die Produktion nachvollziehbar.

Häufige Fragen

Kurz beantwortet, damit du schneller einschätzen kannst, was für dein Projekt wichtig ist.

Sollte ich alte Laravel-Migrationen nachträglich ändern?

Nur solange die Migration sicher noch auf keiner geteilten oder veröffentlichten Umgebung ausgeführt wurde. Danach ist eine zusätzliche Migration nachvollziehbarer, weil die vorhandene Historie unverändert bleibt.

Wann lohnt sich schema:dump in Laravel?

Ein Schema-Dump lohnt sich, wenn eine lange Migrationshistorie für neue Datenbanken durch einen kompakten Ausgangsstand ersetzt werden soll. Danach legst du weitere Schemaänderungen weiterhin als reguläre Migrationen an.

Reicht RefreshDatabase zum Testen von Migrationen?

RefreshDatabase eignet sich für viele Datenbanktests, ersetzt aber keinen gezielten Test des Upgrade-Pfads. Prüfe riskante Änderungen zusätzlich gegen den Schema- oder Datenstand der letzten veröffentlichten Version.

Darf migrate:fresh in der CI-Pipeline verwendet werden?

Ja, wenn die CI-Datenbank ausschließlich für den aktuellen Lauf vorgesehen und vollständig entbehrlich ist. Auf gemeinsam genutzten oder produktiven Datenbanken darfst du den Befehl nicht einsetzen.