← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Soft Delete Pattern

Database Intermediate
debt(d7/e5/b7/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints indicate no automated tooling exists (automated: no). Missing WHERE deleted_at IS NULL filters silently return deleted records in production; no linter or SAST tool in the metadata catches this. Only manual code review or runtime observation of incorrect result sets reveals the problem.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix requires adding a column and updating all SELECT/UPDATE queries across the codebase with WHERE deleted_at IS NULL. Common mistakes include broken unique constraints requiring schema changes, plus query audits across multiple files — well beyond a single-line patch but not yet a full architectural rework.

b7 Burden Structural debt — long-term weight of choosing wrong

Closest to 'strong gravitational pull' (b7). The pattern applies to both web and cli contexts and shapes every query written against affected tables. Every developer must remember to add the deleted_at IS NULL filter, unique constraints must be redesigned, and archival/cleanup strategies must be maintained. This persistent filter requirement gravitates every future query and schema decision around it.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception field explicitly states that soft delete is believed to fully solve data recovery, but without an archive/cleanup strategy, soft-deleted rows accumulate indefinitely and degrade performance. Additionally, unique constraints behave unexpectedly (a re-registered deleted email violates UNIQUE), which contradicts how developers expect uniqueness enforcement to work. These are non-obvious, real-world gotchas that competent developers frequently fall into.

About DEBT scoring →

Also Known As

soft delete MySQL deleted_at pattern logical delete paranoid delete

TL;DR

Marking records as deleted with a deleted_at timestamp instead of physically removing them — preserving data for auditing and recovery.

Explanation

Soft delete adds a nullable DATETIME column (commonly deleted_at or is_deleted). All queries filter WHERE deleted_at IS NULL to exclude deleted records. Advantages: data recovery, audit trail, referential integrity preservation. Disadvantages: all queries need the filter, indexes are less efficient as soft-deleted rows accumulate, unique constraints must account for soft-deleted duplicates. Hard deleting after a retention period (archiving) is common.

Common Misconception

Soft delete solves the data recovery problem completely. Without a cleanup/archive strategy, soft-deleted rows accumulate indefinitely, degrading query performance and bloating the table.

Why It Matters

Physical DELETE is irreversible. Soft delete gives you recovery capability and audit history — critical for user data, financial records, and compliance.

Common Mistakes

  • Forgetting the WHERE deleted_at IS NULL filter in some queries — returning deleted records.
  • Unique constraints broken by soft-deleted duplicates — a deleted email re-registered violates UNIQUE.
  • Not archiving old soft-deleted rows — table size grows indefinitely.

Avoid When

  • Avoid soft delete for high-volume append-only data (logs, events) — deleted rows accumulate and degrade performance.
  • Do not use soft delete without a cleanup/archive strategy — tables grow indefinitely.

When To Use

  • Use soft delete for user data, financial records, and any content requiring audit trails or recovery.
  • Use soft delete when related records reference the row via foreign keys — hard delete would violate referential integrity.

Code Examples

✗ Vulnerable
-- Hard delete — irreversible, breaks foreign keys, loses history
DELETE FROM users WHERE id = ?;
✓ Fixed
-- Schema
ALTER TABLE users ADD COLUMN deleted_at DATETIME NULL DEFAULT NULL;
CREATE INDEX idx_users_deleted ON users (deleted_at);

-- Soft delete
UPDATE users SET deleted_at = NOW() WHERE id = ?;

-- All queries filter deleted
SELECT * FROM users WHERE deleted_at IS NULL;

// PHP: unique email workaround for soft-deleted
$stmt = $pdo->prepare(
    'SELECT id FROM users WHERE email = ? AND deleted_at IS NULL'
);

Added 31 Mar 2026
Views 67
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping T 1 ping W 2 pings T 1 ping F 1 ping S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 2 pings F 0 pings S 2 pings S 1 ping M 0 pings T 4 pings W 0 pings T 2 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
ChatGPT 18 Perplexity 9 Google 9 Scrapy 5 Unknown AI 4 Ahrefs 3 Meta AI 2 Claude 1 SEMrush 1 Majestic 1 Sogou 1
crawler 51 crawler_json 2 pre-tracking 1
DEV INTEL Tools & Severity
⚙ Fix effort: Low
⚡ Quick Fix
Add deleted_at DATETIME NULL DEFAULT NULL to tables and filter WHERE deleted_at IS NULL in all SELECT/UPDATE queries
📦 Applies To
web cli
🔗 Prerequisites
🔍 Detection Hints
DELETE FROM users WHERE id = ? without soft delete consideration
Auto-detectable: ✗ No
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: High Context: File Tests: Regenerate


✓ schema.org compliant