Stackbook Logo
data-stateestablished · low operational burden

Soft Delete

Also known as: logical-delete, tombstone-record

Intent

Mark records as deleted instead of physically removing them so they can be restored, audited or purged later.

Problem

Hard deletes are irreversible, break audit trails and can orphan references from other records. Users make mistakes and regulators may require retention. At the same time, deleted data must disappear from normal views.

Forces

  • •Accidental deletes need recovery
  • •References from other tables must stay valid
  • •Queries must never return deleted rows by accident
  • •Privacy laws may require true erasure after a time

Solution

Overview

Soft delete replaces DELETE with an UPDATE that sets a timestamp. The row stays in the table but is hidden from normal reads, which makes undo and audit simple.

How it works

Schema and queries.

ALTER TABLE users ADD COLUMN deleted_at timestamptz;

-- uniqueness only among live rows
CREATE UNIQUE INDEX uniq_live_email ON users (email) WHERE deleted_at IS NULL;

-- "delete"
UPDATE users SET deleted_at = now() WHERE id = 42;

-- default view
CREATE VIEW live_users AS SELECT * FROM users WHERE deleted_at IS NULL;

A nightly job runs a real DELETE for rows older than the retention window.

Alternatives exist. An archive table receives deleted rows through a trigger or application code, keeping the main table lean and its indexes small. Event-sourced systems record a deleted event instead. Temporal tables retain row history automatically. Soft delete is the simplest, but at high churn the archive table approach scales better, because dead rows no longer bloat the hot table and its indexes, and autovacuum or compaction has less to do.

Pitfalls

  • Forgetting the filter in a raw query, report or join, which leaks deleted records.
  • Unique indexes that include deleted rows and block re-registration.
  • Cascading deletes that only soft-delete the parent, leaving children visible.
  • Keeping personal data forever and violating erasure obligations.

Key takeaways

  • Hide by default through views or scopes, not developer discipline.
  • Use partial unique indexes for live rows.
  • Define a retention period and a purge job.
  • Treat privacy erasure as a hard delete, not a soft one.

✓ When to Use

  • •User-facing data where undo or recycle bin is expected
  • •Records referenced by history, billing or audit
  • •Systems with retention windows before true erasure

✗ When Not to Use

  • •Data that must be erased immediately for privacy compliance
  • •High-churn tables where dead rows dominate size
  • •Append-only event stores where deletion is a new event

Pros

  • +Easy restore and clear audit trail
  • +Preserves referential integrity
  • +Simple to implement

Cons

  • −Every query must remember the filter
  • −Tables and indexes grow with dead rows
  • −Conflicts with right-to-erasure unless a purge exists

Cost Profile

Infrastructure

Low - extra rows and index entries

Operational

Low - purge job and monitoring

Cognitive

Medium - everyone must respect the filter

Failure Modes

  • A query forgets the filter and shows deleted data to users

  • Unique constraint blocks re-creating a record with the same email

  • Purge never runs and personal data is retained beyond policy

Real-World Examples

Alternatives

    Related Patterns

    • data-modeling
    • database-indexing

    Competency Domains

    data statesecurity compliance