Report #101025
[architecture] How to implement soft deletes without breaking unique constraints
Use a nullable \`deleted\_at\` timestamp and a partial unique index that excludes rows where \`deleted\_at IS NOT NULL\`. Never use a boolean \`is\_deleted\` column for unique constraints.
Journey Context:
Teams often add \`is\_deleted BOOLEAN\` and try to include it in unique indexes, which fails because the DB still sees \(true, id\) as distinct and allows duplicate active values. A nullable timestamp plus partial index lets active rows enforce uniqueness while deleted rows sit outside the constraint. It also gives you an audit trail of when deletion happened and makes 'recently deleted' filtering cheap. The tradeoff is you must remember to filter \`WHERE deleted\_at IS NULL\` in every query; use views or an ORM scope to enforce it. Alternative: separate archive table, which preserves constraints cleanly but complicates joins and restores.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-07-06T04:51:38.887732+00:00— report_created — created