Plenty of apps, for good reasons, don't truly erase a record the moment a user clicks delete. They mark it as deleted and hide it from the normal interface, keeping the actual data around for recovery, auditing, or analytics. That's a completely reasonable design choice. It's also worth checking carefully, because "hidden" and "actually inaccessible" are two different guarantees.
Why soft deletes are common, and reasonable
Truly deleting data immediately and permanently removes any chance of recovering from an accidental click, and can break audit trails a business genuinely needs to keep. Marking a record as deleted, rather than removing it outright, solves both of those problems cleanly. Nothing wrong with the pattern itself.
Where it goes wrong
The interface correctly stops showing a deleted item, so from the user's normal experience, it looks gone. If the underlying data-access logic only filters deleted records out of the specific views the interface uses, but a direct request to that record's ID still returns it, or a bulk export still includes it, "deleted" was really just "hidden from the one screen you happened to check."
A concrete example
A user deletes a message in a chat feature. The interface correctly stops displaying it. If the underlying API endpoint that fetches a specific message by its ID doesn't also check whether it's been marked deleted, requesting that same message's ID directly can still return its full contents, to the same user or, depending on the surrounding checks, potentially to someone else entirely.
How to test your own app
- Delete a test record somewhere in your app, and confirm it disappears from the normal interface as expected.
- Note the specific ID of that record before you lose track of it.
- Try requesting that same ID directly, through the app's underlying API rather than the interface, and see whether it still comes back.
- Check any export or bulk-fetch feature too, to see whether deleted records are quietly still included there.
The fix
Every place a record can be fetched, not just the main views the interface normally uses, needs to check whether it's been marked deleted and exclude it consistently, or make a deliberate, explicit exception if there's a genuine reason to include it, like an admin recovery tool. "Deleted" should mean the same thing everywhere in your app, not just wherever someone happened to remember to check for it.