Soft Deletes vs Hard Deletes: Lerne die praktischen Vor- und Nachteile für Analysen, Support, DSGVO-ähnliche Löschungen und Abfragekomplexität sowie sichere Wiederherstellungs-Strategien.

Ein Lösch-Button kann in einer Datenbank zwei sehr unterschiedliche Dinge bedeuten.
Ein Hard Delete entfernt die Zeile. Danach ist der Datensatz weg, sofern du nicht Backups, Logs oder Replikate hast, die ihn noch enthalten. Das ist einfach nachzuvollziehen, aber endgültig.
Ein Soft Delete behält die Zeile, markiert sie aber als gelöscht – meist mit einem Feld wie deleted_at oder is_deleted. Die Anwendung behandelt markierte Zeilen dann als unsichtbar. So bewahrst du zusammenhängende Daten, erhältst Historie und kannst den Datensatz manchmal wiederherstellen.
Diese Entscheidung taucht im Alltag häufiger auf, als viele erwarten. Sie beeinflusst Antworten auf Fragen wie: „Warum ist der Umsatz letzten Monat gefallen?“, „Können Sie mein gelöschtes Projekt wiederherstellen?“, oder „Wir haben eine DSGVO-Löschanfrage – löschen wir wirklich personenbezogene Daten?“ Sie prägt auch, was „gelöscht“ in der UI bedeutet. Nutzer gehen oft davon aus, dass sie es rückgängig machen können – bis sie es nicht können.
Eine praktische Faustregel:
Beispiel: Ein Kunde löscht einen Workspace und merkt später, dass darin wichtige Rechnungen für die Buchhaltung waren. Mit Soft Delete kann der Support ihn wiederherstellen (sofern die App sichere Restore-Prozesse unterstützt). Mit Hard Delete bleibt meist nur die Erklärung über Backups, Verzögerungen oder „das ist nicht möglich.“
Keine der beiden Methoden ist per se „besser“. Die am wenigsten schmerzhafte Wahl hängt davon ab, was du schützen willst: Nutzervertrauen, Berichtsgüte oder Datenschutzkonformität.
Löschentscheidungen wirken sich schnell auf Analysen aus. Sobald du aktive Nutzer, Conversion oder Umsatz trackst, ist „gelöscht“ kein einfacher Zustand mehr, sondern eine Reporting-Entscheidung.
Wenn du hart löschst, sehen viele Metriken sauber aus, weil entfernte Einträge aus Abfragen verschwinden. Du verlierst aber Kontext: frühere Abonnements, frühere Teamgrößen oder wie ein Funnel letzten Monat aussah. Ein gelöschter Kunde kann historische Charts verändern, wenn du Berichte neu laufen lässt – das ist für Finanzen und Growth-Reviews beunruhigend.
Wenn du soft löschst, behältst du die Historie, aber Zahlen können versehentlich aufgebläht werden. Ein einfaches „COUNT users“ könnte Personen einschließen, die weg sind. Ein Churn-Chart kann doppelt zählen, wenn deleted_at in einem Report als Churn interpretiert und in einem anderen ignoriert wird. Auch Umsatz kann kompliziert werden, wenn Rechnungen bleiben, das Konto aber als gelöscht markiert ist.
Was sich als praktikabel erweist, ist ein konsistentes Reporting-Muster:
Das Wichtigste ist Dokumentation, damit Analysten nicht raten müssen. Schreibe auf, was „aktiv“ bedeutet, ob soft-gelöschte Nutzer einbezogen werden und wie Umsatz zugeschrieben wird, wenn ein Konto später gelöscht wird.
Konkretes Beispiel: Ein Workspace wurde versehentlich gelöscht und später wiederhergestellt. Wenn dein Dashboard Workspaces ohne Filter zählt, zeigt es einen plötzlichen Einbruch und eine Erholung, die in der realen Nutzung nicht stattfanden. Mit Snapshots bleibt das historische Chart stabil, während Produktansichten gelöschte Workspaces verbergen können.
Die meisten Supportfälle rund um Löschung klingen gleich: „Ich habe es aus Versehen gelöscht“ oder „Wo ist mein Datensatz?“ Deine Löschstrategie entscheidet, ob Support in Minuten antworten kann oder ob die ehrliche Antwort „Es ist weg“ lautet.
Bei Soft Deletes kannst du meist verifizieren, was passiert ist, und rückgängig machen. Bei Hard Deletes muss der Support oft auf Backups zurückgreifen (falls vorhanden), was langsam, unvollständig oder für einen einzelnen Eintrag unmöglich sein kann. Deshalb ist die Entscheidung nicht nur eine Datenbank-Detailfrage. Sie bestimmt, wie „hilfreich“ dein Produkt nach einem Fehler sein kann.
Wenn du echten Support erwartest, füge ein paar Felder hinzu, die Löschereignisse erklären:
deleted_at (Timestamp)deleted_by (User-ID oder System)delete_reason (optional, kurzer Text)deleted_from_ip oder deleted_from_device (optional)restored_at und restored_by (falls Wiederherstellung unterstützt wird)Selbst ohne komplettes Aktivitätslog erlauben diese Details dem Support zu beantworten: wer hat es gelöscht, wann ist es passiert und war es ein Unfall oder ein automatisches Cleanup.
Hard Deletes können für temporäre Daten in Ordnung sein, aber bei nutzerrelevanten Datensätzen verändern sie, was Support tun kann.
Support kann einen einzelnen Datensatz nicht wiederherstellen, es sei denn, du hast anderswo einen Papierkorb gebaut. Häufig ist nur eine komplette Backup-Wiederherstellung möglich, die andere Daten beeinflusst. Ebenso kann Support nicht leicht beweisen, was geschehen ist, was zu langen Rückfragen führt.
Restore-Features verändern die Arbeitslast ebenfalls. Wenn Nutzer selbst innerhalb eines Zeitfensters wiederherstellen können, sinkt die Anzahl der Tickets. Wenn Restore manuell durch Support erfolgen muss, können die Tickets zwar zunehmen, sind dafür aber schnell und reproduzierbar statt einmalige Ermittlungen.
„Recht auf Vergessenwerden“ bedeutet meist, dass du aufhören musst, personenbezogene Daten zu verarbeiten und sie aus Stellen zu entfernen, wo sie noch nutzbar sind. Es heißt nicht immer, dass du sofort jede historische Aggregat-Tabelle löschen musst, aber du solltest keine identifizierbaren Daten „für den Fall der Fälle“ behalten, wenn kein rechtlicher Grund mehr vorliegt.
Hier wird Soft Delete vs Hard Delete mehr als eine Produktentscheidung. Ein Soft Delete (z. B. Setzen von deleted_at) versteckt den Datensatz oft nur in der App. Die Daten sind weiterhin in der Datenbank, oft für Admins abfragbar und in Exporten, Suchindizes und Analytik-Tabellen vorhanden. Für viele DSGVO-Löschanfragen ist das keine tatsächliche Löschung.
Du brauchst trotzdem ein Purge-Verfahren, wenn:
Backups und Logs werden oft vergessen. Möglicherweise kannst du eine einzelne Zeile nicht aus einem verschlüsselten Backup löschen, aber du kannst Regeln festlegen: Backups verfallen schnell und wiederhergestellte Backups müssen Löschereignisse erneut anwenden, bevor das System live geht. Logs sollten vermeiden, rohe personenbezogene Daten zu speichern, und klare Aufbewahrungsgrenzen haben.
Eine einfache, praktikable Policy ist ein Zwei-Schritt-Löschprozess:
Wenn deine Plattform Exporte unterstützt, behandle exportierte Dateien ebenfalls als Datenspeicher: definiere, wo sie liegen, wer darauf zugreifen kann und wann sie gelöscht werden.
Soft Deletes klingen einfach: deleted_at (oder is_deleted) hinzufügen und die Zeile verbergen. Die versteckten Kosten sind, dass jetzt jeder Ort, an dem du Daten liest, dieses Flag berücksichtigen muss. Vergisst du es einmal, tauchen seltsame Bugs auf: Summen beinhalten gelöschte Einträge, die Suche zeigt „Geister“-Ergebnisse oder ein Nutzer sieht etwas, das er für gelöscht hielt.
UI- und UX-Randfälle tauchen schnell auf. Stell dir vor, ein Team löscht ein Projekt namens „Roadmap“ und will später ein neues „Roadmap“ erstellen. Wenn deine DB eine Unique-Constraint auf Name hat, schlägt die Erstellung fehl, weil die gelöschte Zeile noch existiert. Die Suche kann Nutzer verwirren: wenn gelöschte Items in Listen verborgen, aber in der globalen Suche sichtbar sind, denken Nutzer, die App sei kaputt.
Soft-Delete-Filter werden oft übersehen in:
Performance ist anfangs meist okay, aber die zusätzliche Bedingung kostet Arbeit. Wenn die meisten Zeilen aktiv sind, ist deleted_at IS NULL günstig. Sind viele Zeilen gelöscht, muss die DB mehr überspringen, sofern kein passender Index existiert. Einfach gesagt: es ist wie das Suchen nach aktuellen Dokumenten in einer Schublade, die voller alter Dokumente ist.
Ein separater „Archiv“-Bereich kann Verwirrung reduzieren. Standardansicht zeigt nur aktive Einträge, gelöschte Items landen an einem Ort mit klaren Labels und einem Zeitfenster. In schnell gebauten Tools (z. B. internen Apps auf Koder.ai) verhindert diese Produktentscheidung oft mehr Support-Tickets als jede clevere Query-Optimierung.
Soft Delete ist kein einzelnes Feature, sondern eine Modellentscheidung, die alles Weitere prägt: Abfrageregeln, Restore-Verhalten und was „gelöscht" im Produkt bedeutet.
deleted_at plus deleted_byDas häufigste Muster ist ein nullable Timestamp. Beim Löschen setzt du deleted_at (und oft deleted_by auf die Nutzer-ID). „Aktiv“ sind Datensätze, bei denen deleted_at null ist.
Das funktioniert gut, wenn du ein sauberes Restore brauchst: Wiederherstellen ist einfach deleted_at und deleted_by leeren. Es gibt dem Support auch ein simples Audit-Signal.
Statt eines Timestamps nutzen manche Teams ein status-Feld mit Zuständen wie active, archived und deleted. Das ist nützlich, wenn „archived“ ein echtes Produkt-Feature ist (z. B. aus den meisten Ansichten verborgen, aber in der Abrechnung noch relevant).
Der Nachteil sind die Regeln: Du musst definieren, was jeder Zustand überall bedeutet – Suche, Notifications, Exporte und Analytik.
Für sensible oder wertvolle Objekte kannst du gelöschte Zeilen in eine separate Tabelle verschieben oder Ereignisse in einem append-only Log aufzeichnen.
deleted_at, deleted_bystatus mit benannten ZuständenDas wird oft genutzt, wenn Wiederherstellungen streng kontrolliert sein müssen oder wenn du eine Prüfhistorie willst, ohne gelöschte Daten in Alltagsabfragen zu mischen.
Kinder- oder untergeordnete Datensätze brauchen ebenfalls eine klare Regel. Wenn ein Workspace gelöscht wird, was passiert mit Projekten, Dateien und Mitgliedschaften?
archived (nicht gelöscht)Wähle pro Beziehung eine Regel, notiere sie und bleibe konsistent. Die meisten „Restore ging schief“-Bugs entstehen dadurch, dass Eltern- und Kind-Datensätze unterschiedliche Bedeutungen von „gelöscht“ verwenden.
Ein Restore-Button klingt einfach, kann aber still Permissions brechen, alte Daten an die falsche Stelle zurückholen oder Nutzer verwirren, wenn „wiederhergestellt" nicht das bedeutet, was sie erwarten. Schreib zuerst genau auf, welches Versprechen dein Produkt macht.
Nutze eine kleine, strikte Sequenz, damit Restore vorhersagbar und auditierbar ist.
Wenn du schnell Apps in einem chat-getriebenen Tool wie Koder.ai baust, halte diese Prüfungen als Teil des erzeugten Workflows, sodass jede View und jeder Endpoint dieselben Regeln befolgen.
Das größte Problem bei Soft Deletes ist nicht das Löschen selbst, sondern all die Orte, die vergessen, dass ein Datensatz „weg“ ist. Viele Teams wählen Soft Delete aus Sicherheitsgründen, zeigen dann aber gelöschte Items in Suchergebnissen, Badges oder Summen an. Nutzer merken schnell, wenn ein Dashboard „12 Projekte“ anzeigt, aber nur 11 in der Liste zu sehen sind.
Der zweite häufige Fehler ist Zugriffskontrolle. Wenn ein Nutzer, Team oder Workspace soft-gelöscht ist, darf er sich nicht mehr einloggen, die API aufrufen oder Benachrichtigungen erhalten. Das geht oft verloren, wenn der Login-Check nur per E-Mail nachschlägt, die Zeile findet und das Deleted-Flag nicht prüft.
Typische Fallen, die später Support-Tickets erzeugen:
Eindeutigkeitskollisionen sind besonders fies beim Restore. Wenn jemand ein neues Konto mit derselben E-Mail erstellt, während das alte soft-gelöscht ist, schlägt Restore entweder fehl oder überschreibt die falsche Identität. Entscheide im Voraus: blockiere Wiederverwendung bis zur Purge, erlaube Wiederverwendung aber verhindere Restore, oder stelle die Wiederherstellung unter neuer Kennung wieder her.
Ein typisches Szenario: Support stellt einen soft-gelöschten Workspace wieder her. Der Workspace ist zurück, aber dessen Mitglieder sind weiterhin gelöscht, und eine Integration beginnt wieder alte Datensätze an ein Partner-Tool zu syncen. Aus Sicht des Nutzers hat die Wiederherstellung „halb funktioniert“ und ein neues Durcheinander verursacht.
Bevor du Restore ausrollst, mache folgende Verhaltensweisen explizit:
Ein B2B-SaaS-Team hat einen "Delete workspace"-Button. An einem Freitag entfernt ein Admin während einer Aufräumaktion 40 Workspaces, die inaktiv wirkten. Am Montag beschweren sich drei Kunden, ihre Projekte seien weg und fordern sofortige Wiederherstellung.
Das Team dachte, die Entscheidung sei einfach. War sie nicht.
Erstes Problem: Support kann nicht wiederherstellen, was wirklich gelöscht wurde. Wenn die Workspace-Zeile hart gelöscht und Projekte, Dateien und Mitgliedschaften per Cascade entfernt wurden, bleibt nur das Backup. Das bedeutet Zeit, Risiko und eine unangenehme Antwort für den Kunden.
Zweites Problem: Analytik sieht kaputt aus. Das Dashboard zählt „aktive Workspaces“ über deleted_at IS NULL. Die versehentliche Löschung sorgt für einen plötzlichen Einbruch in den Charts. Schlimmer: ein Wochenbericht vergleicht mit der Vorwoche und meldet einen falschen Churn-Peak. Die Daten sind nicht verloren, aber an den falschen Stellen ausgeschlossen.
Drittes Problem: Eine Datenschutzanfrage trifft ein. Einer der betroffenen Nutzer fordert Löschung seiner personenbezogenen Daten. Ein reines Soft Delete genügt nicht. Das Team braucht einen Plan, um persönliche Felder (Name, E-Mail, IP-Logs) zu purgen, während nicht-personenbezogene Aggregate wie Abrechnungssummen erhalten bleiben.
Viertes Problem: Alle fragen „Wer hat auf Löschen geklickt?“ Ohne Spur kann Support nicht erklären, was passiert ist.
Ein sichereres Muster behandelt Löschung als Ereignis mit klaren Metadaten:
deleted_by, deleted_at und einen Grund oder Ticket-ID speichernSo einen Workflow bauen Teams oft schnell in Plattformen wie Koder.ai und merken später, dass die Lösch-Policy genauso viel Design braucht wie die Funktionen drumherum.
Die Wahl zwischen Soft Deletes und Hard Deletes hängt weniger von einer Vorliebe ab als davon, was deine App nach einer Löschung garantieren muss. Stelle vor der ersten Abfrage diese Fragen:
Eine einfache Methode zur Überprüfung ist, einen realistischen Vorfall durchzuspielen. Beispiel: Jemand löscht einen Workspace aus Versehen am Freitagabend. Am Montag muss Support das Löschereignis sehen, sicher wiederherstellen und vermeiden, dass Daten wieder auftauchen, die wirklich entfernt bleiben sollten. Wenn du auf Koder.ai baust, definiere diese Regeln früh, damit generiertes Backend und UI einer Policy folgen statt Sonderfälle im Code zu verteilen.
Wähle deinen Ansatz, indem du eine einfache Policy schreibst, die du mit Team und Support teilen kannst. Wenn es nicht dokumentiert ist, driftet es und Nutzer merken die Inkonsistenzen.
Fang mit klaren Regeln an:
Baue dann zwei klare Pfade, die sich nie vermischen: einen „Admin Restore“-Pfad für Fehler und einen „Privacy Purge“-Pfad für echte Löschungen. Der Restore-Pfad sollte reversibel und geloggt sein. Der Purge-Pfad sollte final sein und alle identifizierenden Daten entfernen oder anonymisieren, inklusive Backups oder Exporten, falls deine Policy das verlangt.
Füge Guardrails hinzu, damit gelöschte Daten nicht zurück ins Produkt lecken. Am einfachsten ist es, „gelöscht" als First-Class-State in Tests zu behandeln. Füge Review-Checkpoints für jede neue Query, Listen-Seite, Suche, Export und Analytik-Job hinzu. Eine gute Regel: Wenn ein Screen nutzerrelevante Daten zeigt, muss explizit entschieden sein, wie mit gelöschten Datensätzen umgegangen wird (verbergen, mit Label anzeigen oder nur Admins zugänglich).
Wenn du früh im Produkt bist, prototypisiere beide Flows, bevor du das Schema fixierst. In Koder.ai kannst du die Lösch-Policy in der Planungsphase skizzieren, CRUD generieren und Restore- sowie Purge-Szenarien schnell ausprobieren, dann das Datenmodell anpassen, bevor du dich festlegst.