When I added a "delete everything" button to Mori (a free period tracker for Android), I expected it to take an afternoon. It took three passes to get right, because "delete" is not one operation — it's a walk through every place data can hide.
Here's the actual checklist, in case it helps someone building a local-first app. This is also why I'm skeptical of any app that claims "your data is deleted" while running servers.
1. The primary store is the easy part
Mori keeps everything in SQLite. DELETE FROM cycles; and friends. Done? Not even close.
2. Derived data
The app doesn't just store records. It computes things from them: cycle statistics, predictions, the current phase. Some of that lives in caches keyed by derived ids. If you only wipe raw records, the UI can resurrect "ghost" predictions from cache. So the delete path walks the derived tables too — forecasts, statistics snapshots, everything.
3. Exports
We let people export JSON / CSV / a readable summary. Those files go where the user puts them, but the app keeps a cache/index of export history. Deleting the data should not leave "exported on Tuesday" breadcrumbs in the UI. That index gets wiped too. (The already-saved files belong to the user — we don't reach into their files to delete them.)
4. Scheduled notifications
This one bit me. Period reminders are local notifications, and Android keeps them scheduled even if your database is empty. Delete all -> pending PendingIntents must be cancelled too, or the phone keeps pinging about a cycle that no longer exists.
5. Identity namespace
We generate an anonymous install id so migrations know which records belong to "this device" — not an account, never uploaded. On delete-all, that id gets reset so a fresh install is indistinguishable from day one. If you keep the id, "delete" was cosmetic.
6. Sync tombstones
Sync is optional and off by default. If someone enabled it, delete-all must also queue tombstones so the encrypted snapshot on the server shrinks to nothing — not just empty the local copy and leave the old blob up there. (Yes, the snapshot is ciphertext; no, that's not the point. The point is the delete has to mean gone.)
Why this is easier offline-first
The whole list is local. There's no "we'll delete it within 90 days", no support ticket, no export request form. Press the button, watch the app go empty, done. If you're evaluating any health app, try the delete path before you trust the record path — it's the part nobody demos.
I built Mori around this exact constraint — records, predictions, exports, reminders all live on the phone. It's free and lives here if you want to poke at it. Happy to answer "but what about X" in the comments.
Top comments (1)
The scheduled notifications catch is the one I would have missed. It is a good reminder that delete is not a database operation, it is a walk through every place a copy can survive.