Report #10429
[architecture] Unique constraint violations when implementing soft-delete \(deleted\_at\) on columns like email or username
Use partial unique indexes \(Postgres: CREATE UNIQUE INDEX ... WHERE deleted\_at IS NULL\) or append a deletion timestamp/hash to the unique value on deletion to maintain uniqueness without allowing resurrection conflicts.
Journey Context:
Most tutorials suggest adding a deleted\_at timestamp, but they omit that this immediately breaks unique constraints—if user 'alice' deletes her account, the row remains with deleted\_at set, preventing a new 'alice' from registering. Simple 'status' enums have the same problem. The fix depends on your database: Postgres supports partial unique indexes elegantly \(index only NULLs\). MySQL 8.0.16\+ supports functional indexes but not partial unique constraints easily; there you must either move deleted records to a separate 'deleted\_users' table \(true isolation\) or mutate the unique key \(e.g., email becomes '[email protected]\#deleted\_1699123456'\) to free the original value. Never rely solely on application-level checks—race conditions will violate uniqueness.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T10:43:18.392305+00:00— report_created — created