„Spalte kundennummer heißt jetzt customer_id, ist aber sonst gleich.“ Die Nachricht kam am Freitag um 16:05 Uhr in den Kanal, in dem sonst nur deployt wird. Ich habe sie dreimal gelesen. Sonst gleich hieß in diesem Fall: 14 Views, zwei Dashboards und ein Modell, das seit acht Monaten dieselbe Spalte liest.

Der Nightly-Job ist um 03:12 Uhr gescheitert. Am Montag haben wir fünf Stunden damit verbracht, herauszufinden, welche Abfragen betroffen waren – nicht weil die Änderung schwer war, sondern weil niemand wusste, wer diese Spalte benutzt. Seitdem behandle ich Schemas als das, was sie sind: ein Vertrag zwischen Menschen, die nicht miteinander reden.

Was ein Schema verspricht

Ein Schema sagt nicht nur, welche Spalten es gibt. Es sagt: Diese Spalte ist immer gefüllt. Diese Werte kommen aus einer festen Menge. Diese ID ist eindeutig. Diese Beziehung gilt. Jede dieser Aussagen ist ein Versprechen an alle, die darauf aufbauen – und jedes Versprechen kann gebrochen werden, ohne dass die Datenbank sich beschwert.

Das Problem ist die Asymmetrie: Die abgebende Abteilung sieht eine Änderung, die abnehmende sieht einen Fehler. Und weil niemand die Abhängigkeiten aufgeschrieben hat, wird aus einer Umbenennung ein Vorfall. In sechs Monaten habe ich 38 Änderungen gezählt, die ein solches Versprechen gebrochen haben.

Die 38 Brüche

Whiteboard mit Schema-Diagramm und einer Liste von Änderungen mit farbigen Markierungen
Die Liste der Änderungen an unserem Whiteboard, nach Art sortiert. Die acht gelöschten Spalten sind die, die am meisten Ärger gemacht haben – nicht weil sie wichtig waren, sondern weil niemand sie angekündigt hat.

Ich habe die Änderungen aus den Migrationsdateien gezogen und nach Art sortiert. Wichtig: 38 ist die Zahl der Änderungen, die ich als brechend eingestuft habe, nicht die Zahl aller Migrationen. Insgesamt waren es 214, und die meisten davon waren neue Spalten, die niemanden stören.

Art der Änderungndavon mit FolgefehlerAnteil mit Folgefehler
Spalte umbenannt14642,9 %
Typ geändert9333,3 %
Spalte gelöscht8225,0 %
Constraint verschärft400 %
Tabelle umbenannt300 %
gesamt381128,9 %

Umbenennungen sind der häufigste Fall und der teuerste. Ein SELECT auf eine Spalte, die es nicht gibt, schlägt laut fehl – unangenehm, aber ehrlich. Gefährlich wird es an zwei anderen Stellen: bei SELECT *, das stillschweigend eine andere Spaltenmenge liefert, und bei Abfragen, die auf eine ähnlich benannte Spalte zeigen und danach so aussehen, als hätten sie Daten. Der Fehler, der wirklich weh tut, ist selten der laute.

Wie ich Verträge prüfbar gemacht habe

Ein Vertrag, den niemand prüft, ist eine Absichtserklärung. Also habe ich aus dem Information-Schema einen Schnappschuss pro Woche gezogen und die Wochen verglichen. Das ist keine große Infrastruktur, es sind zwei Exporte und ein Merge.

import pandas as pd

alt = pd.read_csv("schema_kw47.csv")   # spalten: tabelle, spalte, typ, nullable
neu = pd.read_csv("schema_kw48.csv")

vergleich = alt.merge(neu, on=["tabelle", "spalte"], how="outer",
                      suffixes=("_alt", "_neu"), indicator=True)

weg   = vergleich[vergleich._merge == "left_only"]
neu_  = vergleich[vergleich._merge == "right_only"]
typ   = vergleich[(vergleich._merge == "both") & (vergleich.typ_alt != vergleich.typ_neu)]

print(f"entfernt: {len(weg)}, hinzugefügt: {len(neu_)}, typwechsel: {len(typ)}")
print(weg[["tabelle", "spalte"]].to_string(index=False))

# Verdächtig: Spalte weg und ähnlich benannte Spalte neu
for _, r in weg.iterrows():
    kandidaten = neu_[neu_.tabelle == r.tabelle]
    print(r.spalte, "->", list(kandidaten.spalte))

Der letzte Block ist der nützlichste. Er findet Umbenennungen, die der Merge als „eine Spalte weg, eine neu“ sieht, und schlägt Kandidaten vor. Damit konnte ich in der nächsten Woche drei Umbenennungen vor dem Deploy erkennen und weitergeben, statt sie hinterher zu reparieren.

Wo ein Vertrag nicht reicht

Ein Schema sagt nichts über Bedeutung. Zwei Spalten können denselben Typ haben und unterschiedliche Dinge zählen: umsatz mit und ohne Mehrwertsteuer, datum als Buchungs- oder als Leistungsdatum. Diese Fehler findest du nicht im Information-Schema, sondern nur im Gespräch mit der Person, die die Spalte füllt.

Ein zweiter blinder Fleck: Der Vertrag gilt für die Tabelle, nicht für die Abfrage. Wenn drei Abteilungen dieselben Daten unterschiedlich aggregieren, entstehen Zahlen, die sich widersprechen, obwohl das Schema stimmt. Das findet kein Diff, dafür müssen zwei Menschen ihre Definitionen nebeneinanderlegen und merken, dass der eine Stornos mitzählt und der andere nicht.

Deshalb steht in unserem Vertrag inzwischen eine Zeile, die kein Werkzeug prüfen kann: „Diese Spalte bedeutet X, gefüllt von Team Y, Änderungen bitte vier Wochen vorher ankündigen.“ Der letzte Teil ist der einzige, der wirklich funktioniert hat – nicht wegen der Frist, sondern weil jetzt jemand namentlich verantwortlich ist.

Fazit

38 Brüche in sechs Monaten sind keine Ausnahme, das ist der Normalfall in jedem gewachsenen Datenhaushalt. Du wirst sie nicht verhindern, aber du kannst sie sichtbar machen und die teuren unter ihnen ankündigen. Ein wöchentlicher Schema-Diff kostet eine Stunde und findet genau die Fälle, die sonst um drei Uhr nachts auffallen.

Meine Frage an dich: Kennst du in deinem Datenbestand die Spalte, deren Umbenennung morgen die meisten Abfragen kaputt machen würde – und weißt du, wer sie alles liest?