Warum ein Datenbank-Rollback schwieriger ist als ein Anwendungs-Rollback

Sie deployen eine neue Version Ihrer Anwendung. Etwas läuft schief. Sie drücken den Rollback-Button, die alte Version läuft wieder, und innerhalb weniger Minuten ist alles beim Alten. Keine Daten verloren, keine bleibenden Nebeneffekte. Der Prozess wirkt sauber und umkehrbar.

Stellen Sie sich nun ein anderes Szenario vor. Sie führen eine Datenbank-Migration aus, die eine status-Spalte zur orders-Tabelle hinzufügt. Die Migration wird abgeschlossen, die neue Spalte mit Standardwerten befüllt, und Ihre aktualisierte Anwendung beginnt, echte Daten in diese Spalte zu schreiben. Einige Stunden später entdecken Sie einen Fehler in der Anwendungslogik, der die status-Werte unzuverlässig macht. Sie entscheiden sich, die Anwendung auf die vorherige Version zurückzusetzen. Der alte Code läuft wieder. Aber die status-Spalte ist immer noch da. Die darin geschriebenen Daten sind immer noch da. Und Ihr alter Anwendungscode weiß möglicherweise nicht, wie er mit dieser zusätzlichen Spalte umgehen soll – oder schlimmer noch, er bricht ab, weil er auf eine Spalte trifft, die er nie erwartet hat.

Das ist das Kernproblem: Ein Anwendungs-Rollback stellt nur den Code wieder her. Ein Datenbank-Rollback muss sowohl die Struktur als auch die Daten in den Zustand vor der Migration zurückversetzen. Und das passiert nicht automatisch.

Warum Anwendungs-Rollback einfach ist

Wenn Sie eine Anwendung zurücksetzen, tauschen Sie im Wesentlichen einen Satz ausführbaren Code gegen einen anderen aus. Die alte Version übernimmt, beginnt neue Anfragen zu bearbeiten, und das System läuft weiter. Während des Rollbacks selbst wird kein persistenter Zustand geändert. Die Datenbank bleibt genau so, wie sie vor dem Auslösen des Rollbacks war. Das Einzige, was sich ändert, ist, welche Version des Codes ausgeführt wird.

Diese Einfachheit ist der Grund, warum viele Teams Rollbacks als Sicherheitsnetz betrachten. Wenn etwas schiefgeht, einfach zurücksetzen und später erneut versuchen. Das funktioniert gut für zustandslose Dienste und für Anwendungen, bei denen sich das Datenbankschema zwischen den Versionen nicht ändert.

Warum Datenbank-Rollback anders ist

Ein Datenbank-Rollback bedeutet, strukturelle Änderungen an einem Live-Datenspeicher rückgängig zu machen. Das bedeutet, Spalten zu entfernen, gelöschte Tabellen wiederherzustellen oder geänderte Constraints zurückzusetzen. Und anders als Anwendungscode enthalten Datenbanken Daten, die seit der Ausführung der Migration geändert, hinzugefügt oder gelöscht worden sein können.

Betrachten Sie eine Migration, die eine Spalte namens legacy_flag aus der users-Tabelle entfernt. Wenn Sie ein Rollback durchführen müssen, müssen Sie diese Spalte wieder hinzufügen. Aber was ist mit den Daten, die in dieser Spalte waren? Wenn die Migration sie einfach gelöscht hat, sind diese Daten weg, es sei denn, Sie haben sie vorher gesichert. Wenn die Migration die Spalte umbenannt oder transformiert hat, müssen Sie diese Transformation exakt rückgängig machen, ohne neue Daten zu beschädigen, die in der Zwischenzeit geschrieben wurden.

Hier ist ein konkretes Beispiel, das das Problem verdeutlicht. Eine Vorwärts-Migration fügt eine Spalte hinzu, und das entsprechende Rollback versucht, sie zu entfernen:

-- Vorwärts-Migration: eine NOT NULL-Spalte mit Defaultwert hinzufügen
ALTER TABLE orders ADD COLUMN status VARCHAR(20) NOT NULL DEFAULT 'pending';

-- Stunden später schreibt der neue Anwendungscode echte Statuswerte
-- Einige Zeilen haben jetzt status = 'shipped', 'cancelled', etc.

-- Rollback-Migration: Spalte entfernen
ALTER TABLE orders DROP COLUMN status;
-- Dies gelingt, aber alle Statusdaten gehen dauerhaft verloren.

Wenn die Rollback-Migration stattdessen versuchen würde, die Daten durch Umbenennen oder Transformieren der Spalte zu erhalten, müsste sie Constraints, Indizes und alle neuen Daten berücksichtigen, die von der alten Anwendung nach dem Rollback geschrieben wurden – ein fragiler und oft ungetesteter Prozess.

Dies ist kein theoretisches Problem. Teams, die auf Down-Migrationen setzen – Skripte, die die Änderungen der Vorwärts-Migration rückgängig machen – stellen oft fest, dass diese Skripte selten getestet, manchmal fehlerhaft und fast immer riskant in der Produktion sind. Eine Down-Migration, die auf halbem Weg fehlschlägt, kann die Datenbank in einem inkonsistenten Zustand hinterlassen, mit teilweise rückgängig gemachten und teilweise nicht rückgängig gemachten Änderungen.

Der sicherere Ansatz: Rückwärtskompatible Migrationen

Eine zuverlässigere Strategie ist es, jede Datenbank-Migration rückwärtskompatibel zu gestalten. Das bedeutet, dass die Schemaänderungen, die Sie vornehmen, die alte Version Ihrer Anwendung nicht beschädigen sollten. Wenn Sie eine neue Spalte hinzufügen müssen, fügen Sie sie hinzu, ohne vorhandene Spalten zu entfernen oder zu ändern. Die alte Anwendung funktioniert weiter, weil sie die neue Spalte einfach ignoriert. Die neue Anwendung beginnt, sie zu nutzen. Wenn sich die neue Version als fehlerhaft herausstellt, können Sie die Anwendung zurücksetzen, ohne die Datenbank überhaupt anzufassen. Die zusätzliche Spalte bleibt, aber der alte Code kümmert sich nicht darum.

Dieser Ansatz erfordert Disziplin. Jede Schemaänderung muss auf ihre Auswirkungen auf alle Anwendungsversionen bewertet werden, die noch laufen könnten. Aber es ist weitaus sicherer, als sich auf Down-Migrationen zu verlassen, die fehlschlagen oder Daten verlieren können.

So funktionieren rückwärtskompatible Migrationen in der Praxis für häufige Operationen:

  • Spalte hinzufügen: Einfach hinzufügen. Machen Sie sie nicht NOT NULL, es sei denn, Sie können einen Standardwert angeben, der sowohl für alten als auch für neuen Code funktioniert. Die alte Anwendung wird sie weder lesen noch beschreiben, daher wird sie nicht beeinträchtigt.

  • Spalte umbenennen: Benennen Sie sie nicht direkt um. Fügen Sie stattdessen die neue Spalte mit dem neuen Namen hinzu, aktualisieren Sie die Anwendung, um während einer Übergangsphase in beide Spalten zu schreiben, und entfernen Sie dann die alte Spalte in einer späteren Migration, nachdem bestätigt wurde, dass der alte Code nicht mehr läuft.

  • Spalte entfernen: Stellen Sie zuerst die Nutzung in der Anwendung ein. Deployen Sie diese Änderung. Dann entfernen Sie die Spalte in einer separaten Migration. Wenn Sie die Anwendung zurücksetzen müssen, ist die Spalte noch vorhanden.

  • Spaltentyp ändern: Fügen Sie eine neue Spalte mit dem neuen Typ hinzu, migrieren Sie die Daten schrittweise, aktualisieren Sie die Anwendung, um die neue Spalte zu verwenden, und entfernen Sie erst dann die alte Spalte.

Jedes dieser Muster fügt Schritte hinzu, aber jeder Schritt ist ohne Datenverlust umkehrbar.

Die wahren Kosten von Down-Migrationen

Einige Teams bevorzugen immer noch Down-Migrationen, weil sie einfacher zu schreiben scheinen. Ein einzelnes Skript, das die Änderung rückgängig macht, wirkt sauberer als ein mehrstufiger rückwärtskompatibler Ansatz. Aber die Kosten dieser Einfachheit zeigen sich unter Druck.

Wenn ein Produktionsvorfall eintritt und Sie schnell ein Rollback durchführen müssen, ist das Letzte, was Sie wollen, eine ungetestete Down-Migration auszuführen, die fehlschlagen, zu lange dauern oder stillschweigend Daten löschen könnte. Der Zeitdruck, der Stress und das Fehlen eines sauberen Fallbacks machen Down-Migrationen zu einem Glücksspiel.

Rückwärtskompatible Migrationen nehmen dieses Glücksspiel aus der Gleichung. Sie ermöglichen es Ihnen, die Anwendung unabhängig von der Datenbank zurückzusetzen. Sie geben Ihnen Zeit zu entscheiden, was mit der Schemaänderung zu tun ist, ohne eine sofortige, riskante Umkehrung zu erzwingen.

Eine praktische Checkliste für die Datenbank-Rollback-Planung

Wenn Sie schmerzhafte Rollback-Szenarien vermeiden möchten, hier eine kurze Checkliste, die Sie vor jeder Migration durchgehen sollten:

  • Kann die alte Anwendungsversion nach dieser Migration noch korrekt ausgeführt werden?
  • Wenn die Migration eine Spalte hinzufügt, ignoriert der alte Code sie?
  • Wenn die Migration eine Spalte entfernt, hat der alte Code bereits aufgehört, sie zu verwenden?
  • Wenn die Migration eine Spalte umbenennt oder ändert, gibt es eine Übergangsphase, in der beide Strukturen (alt und neu) koexistieren?
  • Gibt es einen getesteten, sicheren Weg, diese Migration ohne Datenverlust rückgängig zu machen?

Wenn Sie nicht alle diese Fragen mit Ja beantworten können, birgt Ihre Migration ein Rollback-Risiko, das Sie nicht adressiert haben.

Das Fazit

Ein Datenbank-Rollback bedeutet nicht nur, eine Version zurückzusetzen. Es geht darum, Daten intakt und konsistent zu halten und gleichzeitig sicherzustellen, dass die Anwendung ohne Nebeneffekte in ihren vorherigen Zustand zurückkehren kann. Der sicherste Weg ist, Migrationen so zu gestalten, dass Sie nicht gezwungen sind, zwischen einem Anwendungs-Rollback und Datenverlust zu wählen. Bauen Sie Rückwärtskompatibilität in jede Schemaänderung ein, und behandeln Sie Down-Migrationen als letzten Ausweg, nicht als Standardstrategie. Ihr zukünftiges Ich, das um 2 Uhr morgens einen Vorfall debuggt, wird es Ihnen danken.