Warum Datenbank-Deployments anders sind: Das versteckte Netz von Abhängigkeiten

Du hast eine Produktionsdatenbank, die seit Jahren läuft. Eines Tages musst du eine Spalte zur orders-Tabelle hinzufügen. Sieht einfach aus. Die Hauptanwendung liest nur Spalten, die sie kennt, also sollte eine neue Spalte harmlos sein.

Doch dann schlägt der nächtliche Batch-Job fehl. Der Wochenbericht produziert plötzlich Müllzahlen. Ein Service eines anderen Teams wirft Fehler, die du noch nie gesehen hast.

Was ist passiert? Du hast eine Tabelle geändert, und alles ist kaputtgegangen.

Die Datenbank hat mehr Konsumenten, als du denkst

In vielen Organisationen bedient eine einzelne Datenbank selten nur eine Anwendung. Mit der Zeit sammeln sich Konsumenten an. Einige kennst du. Einige hast du vergessen. Einige wurden von Leuten gebaut, die das Unternehmen vor Jahren verlassen haben.

Hier ist, was eine typische Produktionsdatenbank bedient:

Das folgende Diagramm zeigt, wie eine einzelne Produktionsdatenbank mit vielen verschiedenen Konsumenten verbunden ist, jeder mit eigenem Zugriffsmuster:

flowchart TD DB[Production Database] DB -->|reads/writes| Web[Main Web Application] DB -->|reads/writes| API[Internal API Services] DB -->|writes| Batch[Nightly Batch Jobs] DB -->|reads| Report[Weekly Reporting Scripts] DB -->|reads| AdHoc[Ad-hoc Queries - Data Team] DB -->|reads/writes| Legacy[Legacy Services]
  • Die Haupt-Webanwendung, die Kunden nutzen
  • Interne API-Services, die andere Teams warten
  • Nächtliche Batch-Jobs, die Tausende von Zeilen verarbeiten
  • Wöchentliche Reporting-Skripte, die Dashboards füttern
  • Ad-hoc-Queries des Data-Teams oder von Business-Analysten
  • Legacy-Services, die niemand anfassen will, aber die noch laufen

Jeder dieser Konsumenten greift anders auf die Datenbank zu. Die Webanwendung liest vielleicht die status-Spalte, um eine Seite anzuzeigen. Der Batch-Job schreibt Tausende von Zeilen mit INSERT ... SELECT *. Das Reporting-Skript verlässt sich auf eine bestimmte View, die mehrere Tabellen joint. Der Analyst hat eine gespeicherte Query, die Spalten in einer bestimmten Reihenfolge erwartet.

Wenn du das Schema änderst, änderst du es nicht nur für deine Anwendung. Du änderst es für jeden einzelnen dieser Konsumenten.

Das eigentliche Problem: Du weißt nicht, wer von dieser Datenbank abhängt

Der schwierigste Teil von Schema-Änderungen ist nicht die technische Arbeit. Es ist das Discovery-Problem.

Dokumentation ist oft unvollständig oder veraltet. Code für Anwendungen, die nicht mehr aktiv sind, wird vielleicht noch als Konsument geführt. Batch-Jobs, die einmal im Jahr laufen, übersieht man leicht. Services, die von anderen Teams verwaltet werden, kommunizieren vielleicht nie mit dem Team, das die Änderung vornimmt.

Du kannst nicht sicher ändern, was du nicht vollständig verstehst.

Warum Rückwärtskompatibilität wichtig ist

Der sicherste Ansatz für Schema-Änderungen ist die Gewährleistung von Rückwärtskompatibilität. Das bedeutet, dass die Änderung keinen bestehenden Code brechen darf, der noch nicht aktualisiert wurde.

Hier sind praktische Muster, die das Risiko reduzieren:

Neue Spalten sollten nullable sein oder einen Default-Wert haben. Eine Pflichtspalte ohne Default bricht jedes INSERT-Statement, das sie nicht enthält. Wenn du eine Pflichtspalte hinzufügen musst, füge sie zuerst als nullable hinzu, befülle die Daten nachträglich und mache sie dann in einer separaten Änderung zur Pflicht.

Vermeide es, Spaltentypen direkt zu ändern. Füge stattdessen eine neue Spalte mit dem neuen Typ hinzu, migriere die Daten schrittweise, aktualisiere alle Konsumenten auf die neue Spalte und lösche erst dann die alte Spalte. Dieser Prozess kann Wochen oder Monate dauern, verhindert aber Ausfallzeiten.

Sei vorsichtig mit SELECT *. Queries, die alle Spalten selektieren und Ergebnisse auf eine feste Datenstruktur abbilden, brechen, wenn neue Spalten auftauchen. Wenn du den konsumierenden Code kontrollierst, ändere diese Queries so, dass sie nur die benötigten Spalten selektieren. Wenn du den Code nicht kontrollierst, musst du dich mit dem verantwortlichen Team abstimmen.

Lösche keine Spalten oder Tabellen ohne Verifikation. Eine Spalte oder Tabelle, die ungenutzt scheint, könnte von einem Skript referenziert werden, das quartalsweise läuft. Prüfe Query-Logs, Anwendungscode und sprich mit anderen Teams, bevor du etwas entfernst.

Der vergessene Konsument: Menschen

Anwendungen und Services sind nicht die einzigen Konsumenten deiner Datenbank. Auch Menschen führen manuelle Queries aus.

Das Data-Team hat vielleicht ein Skript, das den monatlichen Executive-Report erstellt. Der Business-Analyst hat eine gespeicherte Query in seinem Datenbank-Tool, die jeden Montagmorgen Kundendaten abruft. Das Operations-Team führt während der Incident-Response Ad-hoc-Queries aus.

Wenn du eine Spalte umbenennst oder eine Tabelle löschst, brechen diese manuellen Queries still. Niemand bemerkt es, bis der Report falsch rauskommt oder jemand während eines Ausfalls die benötigten Daten nicht findet.

Diese menschlichen Konsumenten sind schwerer zu verfolgen, weil sie oft in keinem Code-Repository auftauchen. Die beste Verteidigung ist Kommunikation. Bevor du eine Schema-Änderung vornimmst, sende eine Benachrichtigung an die Teams, die betroffen sein könnten. Gib ihnen eine Frist, um Bedenken zu äußern. Dokumentiere die Änderung an einem Ort, den Leute finden können.

Warum Datenbank-Rollbacks nicht wie Anwendungs-Rollbacks funktionieren

Wenn ein Anwendungs-Deployment schiefgeht, kannst du zur vorherigen Version zurückrollen. Der alte Code ersetzt den neuen Code, und das System erholt sich.

Datenbank-Änderungen funktionieren so nicht.

Wenn du eine Spalte hinzufügst und dann zurückrollen musst, verschwindet die Spalte nicht automatisch. Die in diese Spalte geschriebenen Daten bleiben erhalten. Wenn du einen Spaltentyp änderst und dann zurückrollst, passen die vorhandenen Daten möglicherweise nicht mehr in den alten Typ. Wenn du eine Spalte löschst, sind die Daten weg – es sei denn, du hast ein Backup.

Schlimmer noch: Eine problematische Datenbank-Änderung betrifft alle Konsumenten gleichzeitig. Während ein Anwendungs-Rollback nur die Benutzer dieser Anwendung betrifft, kaskadiert ein Datenbank-Rollback auf jeden Service, jedes Skript und jede Person, die die Datenbank berührt.

Deshalb erfordern Datenbank-Deployments mehr Planung, mehr Tests und mehr Koordination als Anwendungs-Deployments. Der Schadensradius ist größer, und die Wiederherstellungsoptionen sind begrenzter.

Eine praktische Checkliste vor deiner nächsten Schema-Änderung

Bevor du in der Produktion ein ALTER TABLE ausführst, gehe diese Liste durch:

  • Liste jede Anwendung, jeden Service und jedes Skript auf, das auf diese Tabelle zugreift
  • Prüfe auf SELECT *-Queries, die brechen könnten
  • Stelle sicher, dass neue Spalten Defaults haben oder nullable sind
  • Bestätige, dass keine zu entfernende Spalte oder Tabelle noch in Benutzung ist
  • Benachrichtige Teams, die manuelle Queries gegen diese Tabelle ausführen könnten
  • Habe einen Rollback-Plan, der die während der Änderung geschriebenen Daten berücksichtigt
  • Teste die Änderung in einer Staging-Umgebung, die die Produktions-Konsumenten abbildet

Das Fazit

Datenbank-Schema-Änderungen sind nicht nur Datenbank-Probleme. Sie sind Koordinationsprobleme, die jeden Konsumenten betreffen, der mit dieser Datenbank verbunden ist. Je mehr Konsumenten du hast, desto vorsichtiger musst du sein. Rückwärtskompatibilität, gründliches Discovery und klare Kommunikation sind keine Optionen. Sie sind der Unterschied zwischen einem reibungslosen Deployment und einem Produktionsvorfall, der mehrere Systeme gleichzeitig lahmlegt.