12. Jan. 2026·7 Min. Lesezeit

Null‑Daten‑Abstürze verhindern: Datenbank‑Constraints, sinnvolle Defaults und Backfills

Verhindere Null‑Daten‑Abstürze mit DB‑Constraints, sicheren Defaults, App‑Validierung und Backfills, damit Prototypen mit Seed‑Daten in Produktion zuverlässig laufen.

Null‑Daten‑Abstürze verhindern: Datenbank‑Constraints, sinnvolle Defaults und Backfills

Warum Seed‑Daten Null‑Probleme verbergen

„Mit Seed‑Daten funktioniert es“ bedeutet meist, dass die App mit einem kleinen, manuell erstellten Datensatz getestet wurde, der sauber, vollständig und für den Happy Path geformt ist. Jeder Nutzer hat einen Namen, jede Bestellung eine Adresse und jede Einstellung existiert. Der Code wirkt korrekt, weil er nie mit echtem Durcheinander konfrontiert wurde.

In der Realität entstehen Lücken schnell. Menschen brechen Formulare ab, Tabellen kommen mit leeren Spalten und Drittanbieter‑APIs liefern Felder nur manchmal. Selbst sorgfältige Nutzer treffen Entscheidungen, die etwas offenlassen.

Leere Werte tauchen oft nach dem Launch auf, wenn:

  • Ein Anmeldeprozess das Überspringen von Profildaten erlaubt, später aber Seiten diese voraussetzen
  • Ein CSV‑Import leere E‑Mails, fehlende Preise oder inkonsistente Daten enthält
  • Ein Mobile‑Client bei schlechter Verbindung nur ein Teil‑Payload sendet
  • Ein Webhook die Form ändert oder ein Feld während eines Ausfalls null ist
  • Ein Kollege Daten manuell ändert und ein Pflichtfeld leer lässt

Ein Null‑Daten‑Absturz passiert, wenn der Code einen Wert erwartet, aber nichts bekommt. Die App versucht, ein leeres Feld wie echte Daten zu verwenden und wirft einen Fehler. Das kann sich äußern als „cannot read property of null“, ein fehlgeschlagener DB‑Insert, eine kaputte Seite oder ein Hintergrundjob, der endlos wiederholt.

Null‑Daten‑Abstürze zu verhindern ist nicht nur Programmierarbeit. Es ist eine Aufgabe der Datenregeln. Du brauchst zwei Dinge zusammen:

  1. Schütze die Daten davor, fehlerhaft gespeichert zu werden (damit die Datenbank vertrauenswürdig bleibt).

  2. Gehe sicher mit fehlenden Daten um, wenn sie erlaubt sind (damit die App weiterhin funktioniert und Nutzer angeleitet werden, das Problem zu beheben).

KI‑generierte Prototypen sind hier besonders anfällig. Die UI geht oft von perfekten Eingaben aus und die Datenbank akzeptiert alles. Deshalb kann ein Prototyp gut demoen und in Produktion beim ersten echten Nutzer, der ein Feld überspringt oder ein Import Leerstellen enthält, auseinanderfallen.

Wenn du Seed‑Daten als „Best Case“ behandelst, kannst du für den Normalfall planen: unvollständig, inkonsistent und manchmal falsch.

Fang mit klaren Regeln an: was darf fehlen?

Die meisten Apps stürzen nicht wegen „schlechter“ Daten ab. Sie stürzen ab, weil App und Datenbank unterschiedliche Vorstellungen davon haben, was fehlen darf. Bevor du Constraints ergänzt oder Migrationen schreibst, halte die Regeln in einfachem Deutsch fest. Dieser Schritt verhindert oft mehr Null‑Abstürze als jede einzelne Codeänderung.

Treffe eindeutige Entscheidungen zu „erforderlich vs. optional“:

  • E‑Mail: erforderlich für Login und Passwortzurücksetzung, oder optional, wenn nur Telefon oder SSO unterstützt wird
  • Adresszeile 2: optional
  • Profil‑Bio: optional, aber die UI muss leere Zustände handhaben
  • Geburtsdatum: optional oder nur für altersbeschränkte Funktionen erforderlich
  • Firmenname: optional für Privatpersonen, erforderlich für Geschäftskonten

Trenne dann drei Zustände, die unterschiedlich behandelt werden müssen in UI, API und DB:

  • Unbekannt: Du erwartest den Wert, hast ihn aber noch nicht (oft temporär)
  • Nicht bereitgestellt: Der Nutzer hat bewusst leer gelassen
  • Nicht anwendbar: Das Feld macht für diesen Datensatz keinen Sinn

Beispiel: In einem Anmeldeprozess ist profile.bio optional. billing_country kann beim Signup unbekannt sein, muss aber vor einer Zahlung vorhanden sein. vat_id kann für viele Nutzer nicht anwendbar sein.

Entscheide anschließend, was blockiert werden muss und was erlaubt‑aber‑behandelt wird. Blockieren, wenn die App ohne den Wert nicht funktionieren oder rechtliche/sicherheitsrelevante Anforderungen nicht erfüllen kann (Login‑Identifier, Rollen, Besitz‑IDs). Erlauben‑aber‑behandeln, wenn du eine sichere Fallback‑Anzeige zeigen oder das Feld später abfragen kannst (leere Bio, fehlendes Avatarbild).

Schreibe die Regeln als kurze Spezifikation, bevor du Code änderst:

„User.email ist erforderlich und eindeutig. User.address_line_2 ist optional. Profile.bio ist optional und wird als leer angezeigt. Payment.country ist vor dem Erstellen einer Zahlung erforderlich.“

Wenn du einen KI‑generierten Prototyp geerbt hast, fehlt diese Spezifikation meist. Audits beginnen oft hier, weil sie offenlegen, wo Nullwerte akzeptabel und wo sie früh gestoppt werden müssen.

DB‑Constraints, die fehlerhafte Daten am Speichern hindern

Wenn du Null‑Daten‑Abstürze verhindern willst, muss die Datenbank „nein“ sagen, wenn die App etwas speichert, das später kaputt macht. App‑Code ändert sich häufig, Constraints bleiben. Sie sind ein verlässliches Sicherheitsnetz, besonders wenn ein Prototyp nur durch perfekte Seed‑Daten „funktionierte”.

Verwende NOT NULL für Felder, die wirklich erforderlich sind

Füge NOT NULL nur für Felder hinzu, ohne die deine App nicht arbeiten kann. Ein guter Test: „Kann ein echter Nutzer einen wichtigen Ablauf abschließen, wenn dieser Wert fehlt?“ Falls nicht, mach ihn auf DB‑Ebene erforderlich.

Beispielsweise braucht eine orders‑Tabelle meist user_id und created_at. Wenn diese nullable sind, bekommst du irgendwann Reihen, die in der UI ok aussehen, aber Reporting, Mails oder Admin‑Screens brechen.

Füge einfache Regeln mit CHECK‑Constraints hinzu

CHECK‑Constraints sind gut für einfache, lesbare Regeln, die Müllwerte verhindern:

  • Bereiche: quantity > 0, price_cents >= 0
  • Nicht‑leerer Text: trim(display_name) <> ''
  • Erlaubte Werte: status IN ('draft','active','canceled')
  • Sinnvolle Daten: end_date >= start_date

Diese Regeln stoppen „technisch nicht null, trotzdem unbrauchbar“ Daten. Ein leerer String im Namensfeld kann dieselbe kaputte UI erzeugen wie ein Nullwert.

Eindeutigkeit ist ebenfalls wichtig. Nutze UNIQUE für Kennungen, die nie dupliziert werden dürfen, wie email, username oder externe Provider‑IDs (z. B. google_sub). Ohne das können zwei Konten entstehen, die wie derselbe Nutzer aussehen, und nachgelagerter Code wählt die „falsche” Zeile.

Foreign Keys schützen vor Waisen‑Datensätzen. Wenn du Kinderzeilen ohne Eltern zulässt (z. B. profile ohne user), bricht Code, der Beziehungen voraussetzt, auf seltsame Weise. Foreign Keys verhindern verwaiste Datensätze und sorgen für konsistente Deletes/Updates.

Constraints sind die letzte Verteidigungslinie, nicht die einzige. Deine App sollte weiterhin Eingaben validieren und freundliche Fehlermeldungen zeigen, aber die Datenbank sollte die Regeln durchsetzen, damit schlechte Daten nicht über Hintergrundjobs, Admin‑Skripte oder schnelle Patches einschleichen.

Defaults, die fehlende Daten sicher machen (ohne Fehler zu kaschieren)

Defaults helfen, wenn ein Wert für jede Zeile existieren sollte, aber Nutzer und Code ihn nicht immer liefern sollten. Sie können Null‑Daten‑Abstürze verhindern, indem sie „leer“ an Stellen unmöglich machen, wo „leer“ keinen Sinn ergibt.

Ein guter Default spiegelt das reale Produktverhalten wider. Wenn du nicht erklären kannst, was der Wert einem Support‑Mitarbeiter bedeutet, ist es wahrscheinlich kein guter Default.

Gute Defaults: langweilig, vorhersehbar und korrekt

Nutze Defaults für Felder, die unvermeidbar und keine Nutzerwahl sind:

  • created_at oder updated_at Timestampfelder
  • ein status, der mit pending, draft oder active beginnt
  • Zähler wie login_count, beginnend bei 0
  • einfache Flags wie is_deleted, Standard false

Diese Defaults verkleinern die Fehleroberfläche, besonders in KI‑generierten Prototypen, in denen manche Codepfade vergessen, Felder zu setzen.

Defaults, die Bugs verdecken (und schlimmere Daten erzeugen)

Defaults werden gefährlich, wenn sie fehlende Beziehungen oder notwendige Eingaben übertünchen. Sie lassen die App stabil wirken, füllen aber die DB mit Unsinn.

Vermeide Defaults wie:

  • user_id = 0 oder account_id = 1, wenn der echte Besitzer unbekannt ist
  • email = '' (leerer String), um eine fehlende E‑Mail zu umgehen
  • price = 0, wenn ein Preis zur Abrechnung nötig war
  • role = 'admin', weil die UI keine Rolle gesendet hat

Wenn du später Constraints hinzufügst, brechen Reportings, Berechtigungen und Abrechnungslogik auf verwirrende Weise.

Wenn etwas eine Weile fehlen kann, nutze einen expliziten „noch nicht bereit“ Zustand. Beispiel: Ein Profil kann mit status = 'incomplete' starten, bis Nutzer Name und Telefonnummer hinzufügen. Das macht den fehlenden Zustand sichtbar, testbar und einfach in der UI zu handhaben.

Szenario: Ein Signup legt zuerst eine User‑Zeile an und fragt Profilinfos auf der nächsten Seite ab. Ein sicherer Default ist profile_status = 'incomplete', nicht name = 'Unknown'. „Unknown“ sieht wie echte Daten aus, also korrigiert es niemand.

App‑Validierung: Probleme abfangen, bevor sie die DB erreichen

Reduce null bugs at the source
Clean up spaghetti architecture so required data is enforced the same way everywhere.

Null‑Abstürze beginnen oft, bevor die DB die Anfrage sieht. Ein Formular sendet ein leeres Feld, ein API‑Client vergisst eine Property oder ein Webhook liefert ein anderes Payload als getestet. Validierung ist die erste Verteidigungslinie. Sie verwandelt „mysteriöse Serverfehler“ in klare Meldungen und verhindert, dass schlechte Daten ins System gelangen.

Validiere so nahe wie möglich an der Eingabe: Browser‑Formulare, API‑Requests und Integrationen (z. B. Payment‑Provider oder E‑Mail‑Tools). Wenn du Eingaben an drei Stellen akzeptierst, brauchst du Checks an allen drei. Der schwächste Pfad ist der, der in Produktion bricht.

Gute Validierung ist mehr als „erforderlich oder nicht“. Sie normalisiert Daten, damit du speicherst, was du erwartest. Entferne führende/folgende Leerzeichen, entscheide, ob E‑Mails kleingeschrieben werden sollen, und handhabe leere Strings konsistent (oft als null, aber nur wenn deine Regeln das erlauben). Frühe Normalisierung verhindert subtile Fehler wie doppelte Accounts, weil eine E‑Mail ein zusätzliches Leerzeichen hatte.

Wenn Validierung fehlschlägt, gib menschenverständliche Meldungen zurück, keine Stacktraces. Nutzer sollen wissen, was sie korrigieren müssen, und Support muss das Problem reproduzieren können. Eine 400‑Antwort mit einem klaren Satz schlägt einen 500‑Fehler.

Ein einfacher Ansatz, der für die meisten Apps funktioniert:

  • Validiere serverseitig bei jedem Schreibvorgang, auch wenn die UI schon validiert
  • Normalisiere Felder vor dem Speichern (trim, Groß-/Kleinschreibung, Leerstring‑Handhabung)
  • Lehne unbekannte Felder ab, damit Tippfehler nicht stillschweigend zu nulls werden
  • Verwende klare Fehlermeldungen, die sich auf Feldnamen beziehen
  • Logge Validierungsfehler mit genug Kontext (aber niemals Geheimnisse)

Um Regeln synchron zu halten, lege die Validierung an einer Stelle ab und wiederverwende sie. Ein häufiger Fehler in der Produktivsetzung KI‑generierter Prototypen ist: Eine Regel im UI, eine andere im API und gar keine in Webhook‑Handlern. Wähle eine Single Source of Truth (oft ein serverseitiger Validator oder ein geteiltes Schema) und lasse andere Schichten darauf verweisen.

Konkretes Beispiel: Dein Signup‑Formular verlangt einen vollständigen Namen, der Mobile‑Client sendet aber nur E‑Mail und Passwort. Wenn der Server nicht validiert, legst du vielleicht einen Nutzer mit name = null an, und die erste „Welcome, {name}“ Seite stürzt ab. Mit serverseitiger Validierung schlägt die Signup‑Anfrage fehl mit „Vor‑ und Nachname sind erforderlich“ und du speicherst keinen kaputten Datensatz.

Schritt für Schritt: Constraints sicher in einer bestehenden App ergänzen

Behandle Constraints wie ein Rollout, nicht wie einen Schalter. Der sicherste Weg ist: Finde heraus, wo heute Nullwerte existieren, korrigiere alte Reihen, und blockiere dann neue schlechte Reihen.

Beginne mit einem Inventar dessen, was aktuell null sein kann. Schau nicht nur Tabellen an, sondern auch, wo jedes Feld verwendet wird: API‑Antworten, UI‑Renderings, Mails, Hintergrundjobs und Exporte. Eine Spalte, die in einer Ansicht harmlos ist, kann in einer anderen abstürzen, die einen Wert voraussetzt.

Ein praktischer Rollout:

  1. Risikobereiche finden. Liste Spalten, die null erlauben, und suche im Code nach Stellen, die Werte voraussetzen (z. B. .toLowerCase() auf einem Namen aufrufen).
  2. Regel entscheiden. Für jede Spalte wähle: muss vorhanden sein, darf fehlen, oder darf nur zeitweise fehlen (z. B. „bis Onboarding abgeschlossen“).
  3. Alte Reihen backfillen. Aktualisiere alte Datensätze, damit sie der Regel entsprechen. Wenn du nicht korrekt backfillen kannst, nutze einen temporären, klar markierten Zustand plus Plan zur späteren Datensammlung.
  4. Constraints in kleinen Schritten hinzufügen. Ändere eine Spalte (oder Tabelle) pro Release. Halte Migrationen klein, damit Fehler leicht zu diagnostizieren sind.
  5. Constraint aktivieren und beobachten. Füge NOT NULL, CHECK, FOREIGN KEY oder UNIQUE‑Regeln erst hinzu, wenn Daten und App‑Verhalten bereit sind.

Erwarte nach dem Rollout ein paar Fehler. Das ist oft ein Zeichen, dass das System endlich Probleme aufdeckt, die es früher verborgen hat.

Füge einfache Überwachung an neuen Fehlerpunkten hinzu: Zähle Validierungsfehler, tracke DB‑Constraint‑Fehler und logge das Payload (ohne sensible Daten), damit du siehst, was fehlt und woher es kam. Wenn du z. B. users.email nicht null machst, achte auf Spitzen von einer bestimmten Signup‑Route oder einer alten Mobile‑Version.

Habe einen Rollback‑Plan für Migrationen, die in Produktion fehlschlagen:

  • Weiß, wie man die Constraint schnell entfernt oder deaktiviert.
  • Halte ein Skript bereit, um den Backfill in kleineren Chargen neu auszuführen.
  • Sorge dafür, dass die App kurzzeitig sowohl altes als auch neues Schema handhaben kann.

Backfills: vorhandene Reihen reparieren ohne Nutzer zu stören

Clarify your data rules fast
Know which fields should be required, optional, or stage-based before you change anything.

Ein Backfill ist ein geplantes Update, das fehlende Werte für bereits in der DB vorhandene Reihen füllt. Das ist wichtig, weil ein NOT NULL‑Constraint (oder die Pflicht im App‑Code) fehlschlägt, wenn alte Reihen noch Nullwerte enthalten. Seed‑Daten sehen oft perfekt aus. Reale Daten selten.

Entscheide vorab, was der korrekte Wert für fehlende Felder sein soll. Manchmal ist ein sicherer Platzhalter ausreichend (z. B. "Unknown" als Anzeigename), solange die App ihn klar behandelt. Manchmal brauchst du einen echten, abgeleiteten Wert. Fehlt users.timezone, kannst du sie aus der häufigsten Zeitzone der Organisation ableiten oder aus einer aufgezeichneten Signup‑Region, falls vorhanden.

Platzhalter halten den Prozess am Laufen, können aber Probleme verdecken, wenn sie echt aussehen. Ein schlechter Platzhalter kann Dashboards verfälschen oder Mails an den falschen Namen auslösen. Im Zweifelsfall nutze einen offensichtlich „ausgefüllten“ Wert oder ergänze ein Flag wie profile_incomplete = true, damit die Zeilen leicht gefunden und später korrigiert werden können.

Bei großen Tabellen backfille in Chargen. Kleine Chargen reduzieren Sperrzeiten und verringern die Wahrscheinlichkeit, dass du die App in Stoßzeiten verlangsamst:

  • Aktualisiere eine begrenzte Anzahl Zeilen pro Lauf (z. B. 1.000 bis 10.000)
  • Führe während niedriger Last aus und stoppe, wenn Fehlerquoten oder DB‑Load steigen
  • Halte jede Charge idempotent (wiederholbar)

Logge, was sich ändert. Debugging ist einfacher, wenn du beantworten kannst: „Welche Reihen wurden wann und von welchem Skript verändert?“ Mindestens sollten Counts und IDs aufgezeichnet werden. Bei sensiblen Daten logge nur IDs und eine Zusammenfassung.

Behandle Backfills als zweigleisigen Prozess: ein einmaliges Skript und ein wiederholbarer Job. Das Skript behebt heutige Nullwerte. Der wiederholbare Job fängt später ankommende Fälle auf (alte Worker, Importe oder fehlerhafte Retries), bis du sicher bist, dass neue Regeln durchgreifen.

Beispiel: ein Signup‑Flow, der bei unvollständigen Profilen fehlschlägt

Ein häufiger „funktioniert mit Seed‑Daten“ Bug läuft so ab: Ein Nutzer registriert sich, ein Account wird angelegt, aber die Profilzeile ist nur teilweise gefüllt. In Testdaten hat jeder Nutzer ein komplettes Profil, also fällt es nicht auf.

Szenario: Signup legt users an und danach profiles. Das Profil sollte display_name und status haben, aber der Code überspringt manchmal display_name (z. B. Nutzer schließt Tab während Onboarding). Später rendert die App einen Dashboard‑Header, der den Namen voraussetzt.

Der Absturz zeigt sich oft so:

  • Serverfehler beim Rendern einer Seite (Versuch, einen null Namen zu formatieren oder großzuschreiben)
  • Kaputte API‑Antwort (Serializer erwartet String, bekommt null)
  • Frontend‑Fehler (Komponente liest profile.display_name.length und scheitert)

Um Null‑Daten‑Abstürze zu verhindern, müssen Datenbank und App dieselben Regeln haben, und alte Reihen müssen diesen Regeln entsprechen.

Ein einfacher Fix‑Plan

Entscheide zuerst, was „sicher“ bei unvollständiger Anmeldung bedeutet. Wende dieselbe Absicht an vier Stellen an:

  • DB‑Constraint: Erfordere eine Profilzeile für jeden Nutzer und mache status NOT NULL.
  • Default: Setze status mit Default incomplete.
  • App‑Validierung: Wenn display_name nötig ist, um Onboarding abzuschließen, blockiere „Weiter“ und zeige eine klare Meldung.
  • Backfill: Aktualisiere bestehende Nutzer mit null status (oder fehlenden Profilzeilen), damit sie zur neuen Regel passen.

Nach dem Fix verbessert sich die Erfahrung: Statt eines Absturzes im Dashboard sieht der Nutzer eine freundliche Aufforderung wie „Vervollständige dein Profil“ und ein kurzes Formular, um den fehlenden Namen hinzuzufügen. Deine API bleibt vorhersehbar und der Support bekommt weniger „Gestern ging’s noch“-Meldungen.

Häufige Fehler, durch die Null‑Abstürze immer wieder auftreten

Deployment readiness check
Get your app ready for production with fixes that keep data consistent after launch.

Viele Teams folgen demselben Muster: Mit Seed‑Daten sieht alles gut aus, dann überspringt ein echter Nutzer ein Feld, ein Import hinterlässt Lücken oder eine Integration sendet Teil‑Payloads. Wenn du Null‑Daten‑Abstürze dauerhaft vermeiden willst, brauchst du Konsistenz zwischen DB, App und bestehenden Daten.

Ein häufiger Fehler ist, NOT NULL zu früh zu setzen. In einer Live‑App gibt es fast immer alte Reihen, die der neuen Regel nicht entsprechen. Das führt zu fehlgeschlagenen Migrationen oder zu hektischen Hotfixes, die die Constraint wieder entfernen und dich zurückwerfen.

Ein weiteres Problem ist, leere Strings als Ersatz für fehlende Daten zu verwenden, ohne zu klären, was das bedeutet. Ein leerer String kann „unbekannt“, „nicht angegeben“ oder „bewusst leer“ bedeuten. Wenn das niemand definiert, werden Filter und Reports unübersichtlich und du bekommst trotzdem Abstürze, weil Code einen bedeutsamen Wert erwartet.

Wiederkehrende Fehlerquellen:

  • Validierung nur im Browser, während API‑Aufrufe, Skripte und Webhooks weiterhin schlechte Payloads senden können
  • Defaults, die hilfreich wirken, später aber Abrechnung, Steuern oder Analytics zerstören
  • Backfills, die nur den Happy Path behandeln und seltene Reihen liegen lassen, bis ein Nutzer sie trifft
  • Tests, die nur perfekte Eingaben abdecken
  • Seed‑Daten, die zu sauber bleiben und nie Nulls, Leerstrings oder fehlende Beziehungen simulieren

Magische Defaults verdienen besondere Vorsicht. Wenn du created_at für historische Reihen auf „now“ setzt, sind Retention‑Charts falsch. Einen fehlenden Preis auf 0 zu setzen kann dazu führen, dass du kostenpflichtige Funktionen ungewollt gratis abgibst oder Umsatzmetriken verzerrst. Defaults sollen die App sicher machen, aber keine geschäftliche Wahrheit erfinden.

Reality‑Check: Ein Signup erlaubt profile.bio optional, aber eine Willkommens‑Mail geht davon aus, dass die Bio existiert und stürzt ab. Das ist nicht nur ein DB‑Problem. Es ist ein Vertrag zwischen Regeln, Code und Templates.

Wenn du einen KI‑generierten Prototyp geerbt hast (Tools wie Lovable, Bolt, v0, Cursor oder Replit), treten diese Fehler oft zusammen auf: schwache serverseitige Validierung, inkonsistente Null‑Handhabung und nie gegen unordentliche Daten getestete Migrationen.

Kurze Checkliste und nächste Schritte

Behandle fehlende Daten als normal, nicht als selten. Ein guter Fix ist meist fokussiert: klare Regeln, ein paar Constraints und ein sorgfältiger Backfill.

Schnelle Checkliste

Beginne damit, aufzuschreiben, was existieren muss und was leer bleiben kann. Sorge dann dafür, dass dieselbe Regel in DB und App durchgesetzt wird.

  • Liste die wirklich erforderlichen Felder für jede Tabelle (und jede API‑Anfrage) und definiere, was „fehlend“ bedeutet (NULL vs. leerer String).
  • Wähle Defaults nur dort, wo sie sinnvoll sind (z. B. status = 'draft') und vermeide Defaults, die defekte Abläufe verbergen.
  • Bestimme, wo Validation stattfindet (Form, API, Background Job) und sorge für klare Fehlermeldungen für Nutzer.
  • Plane einen Backfill für existierende Reihen, bevor du neue NOT NULL Constraints aktivierst.
  • Bestimme einen Owner für die Regeln (Product entscheidet, was erlaubt ist; Engineering setzt es durch) und dokumentiere die Regeln an einem Ort.

Führe anschließend einen schnellen „bad input“ Durchgang durch. Das findet Überraschungspfade, die mit Seed‑Daten nie aufgetaucht wären.

Smoke‑Tests für fehlende Daten

Führe diese Prüfungen auf wichtigen Nutzerreisen aus: Signup, Checkout, Profilbearbeitung, Importe, Admin‑Screens und öffentliche API‑Endpoints.

  • Sende Formulare mit fehlenden optionalen Feldern und mit leeren erforderlichen Feldern.
  • Spiele einen Import mit ein paar leeren Spalten und seltsamen Leerzeichen nach.
  • Rufe wichtige Endpoints mit fehlenden JSON‑Keys auf (nicht nur Keys auf null gesetzt).
  • Lade Seiten für ältere Accounts, die unvollständige Reihen haben könnten.

Um Rückkehr zu verhindern, ergänze eine kleine Reihe von Tests mit „null und leer“ für deine wichtigsten Endpoints und Background Jobs. Selbst 5–10 Fälle können zukünftige Regressionen stoppen.

Wenn du mit einem KI‑generierten Codebase zu kämpfen hast, das bei realen Daten auseinanderfällt: FixMyMess (fixmymess.ai) bietet ein kostenloses Code‑Audit an, um riskante Null‑Pfade zu finden, und kann gezielte Reparaturen wie Constraint‑Rollouts, Validierungsfixes und Backfills anwenden, damit die App innerhalb weniger Tage produktionsreif wird — oft in 48–72 Stunden.

Häufige Fragen

Why does my app work with seed data but crash in production?

Seed‑Daten sind in der Regel handverlesen, vollständig und konsistent, sodass dein Code nur den „Happy Path“ sieht. Echte Nutzer, Importe und Integrationen bringen schnell fehlende Felder, leere Strings und Teil‑Datensätze ein, auf die dein Code nicht vorbereitet ist.

What’s the first step to stop null-related crashes?

Schreibe einfache Regeln in Klartext für jedes Feld: erforderlich, optional oder nur in bestimmten Phasen erforderlich (z. B. „vor dem Belasten einer Karte“). Ziel ist, dass App und Datenbank sich einig sind, was fehlen darf und wann.

Is an empty string basically the same as NULL?

NULL bedeutet meist „kein Wert vorhanden“, während ein leerer String ein noch existierender Wert ist und sich in Abfragen, Validierungen und der Anzeige anders verhält. Lege pro Feld eine Bedeutung für „fehlend“ fest und normalisiere die Eingabe, damit du nicht beides vermischt.

When should I add NOT NULL constraints?

Setze NOT NULL für Felder, ohne die das Produkt nicht funktionieren kann — z. B. Besitz‑IDs, Login‑Kennungen oder notwendige Zeitstempel. Wenn du unsicher bist, behandle das Feld zuerst als optional, gib der App passende Handhabung, und erzwinge NOT NULL erst, wenn alle Schreibpfade abgesichert sind.

What kinds of problems do CHECK constraints prevent?

Benutze CHECK‑Constraints für einfache Regeln, die zwar nicht‑null, aber trotzdem unbrauchbar sind, z. B. negative Mengen, unmögliche Datumsbereiche oder „leer aber nicht null“ Texte. Sie verhindern Werte, die zwar vorhanden aussehen, aber später Abrechnung, Reporting oder UI zerstören.

Which database defaults are safe, and which ones are dangerous?

Defaults sind praktisch für Werte, die immer existieren sollten, aber nicht vom Client gesetzt werden müssen — z. B. Zeitstempel, Anfangsstatus oder Zähler. Vermeide Defaults, die geschäftliche Wahrheit erfinden (etwa fehlender Preis = 0 oder ein erfundener Besitzer), denn sie verdecken Fehler und verschlechtern die Datenqualität.

Where should validation live to prevent nulls from slipping in?

Validiere auf dem Server bei jedem Schreibvorgang, selbst wenn die UI bereits validiert. Skripte, Webhooks, Importe und alte Clients können die Browservalidierung umgehen. Gib klare 400‑Fehler mit feldbezogenen Meldungen zurück, so dass fehlerhafte Anfragen schnell abgelehnt werden, statt kaputte Reihen zu speichern.

How do I add constraints safely in a live app without breaking it?

Prüfe vorhandene Null‑Werte, lege pro Spalte die Regel fest, backfille alte Reihen und rolle Constraints schrittweise aus. Wenn du die Constraint aktivierst, überwache Fehlerraten und habe ein Rollback‑Skript bereit, falls etwas schiefgeht.

What is a backfill, and why do I need one before adding NOT NULL?

Ein Backfill füllt fehlende Werte in bereits existierenden Reihen nach. Er ist notwendig, weil NOT NULL‑Constraints bei bestehenden Null‑Werten sonst fehlschlagen. Nutze wenn möglich abgeleitete echte Werte; wenn Platzhalter nötig sind, mache sie deutlich als „unvollständig“, damit sie später korrigiert werden können.

Why are AI-generated prototypes especially prone to null-data crashes, and what can I do about it?

KI‑generierte Prototypen gehen oft von perfekten Eingaben aus und fehlen konsistente Server‑Validierung, Schema‑Constraints und sichere Defaults. Dadurch fallen reale, unordentliche Daten schnell zu Abstürzen. Wenn du so einen Code geerbt hast, kann FixMyMess (fixmymess.ai) mit einem kostenlosen Audit starten und gezielt Constraints, Validierungen und Backfills anpassen, um die App in Produktion zu bringen.