· By Sajeevan (Saj) Veeriah
Product engineering · 4 min read
A local app needs a way back
Why offline use, saved data, export and recovery need separate acceptance tests in a small personal or business app.
A small app can feel complete when it saves an entry and brings it back after a refresh. The harder question arrives later: can the owner recover the data after replacing a device, losing a browser profile or moving to a new address?
Several projects in my portfolio use local-first or static approaches. Those choices can keep a tool focused and reduce infrastructure. They also make the data boundary worth explaining in ordinary language. This article proposes a recovery design for a fictional equipment register.
Separate four promises
Offline use means the required parts of the app work without a connection. Persistence means data survives the relevant restart. Export means a usable copy can leave the app. Recovery means that copy can reconstruct the required state. Test these as separate behaviours.
For example, an equipment register might store records locally but depend on an uncached script to open. Another might open offline while keeping edits only in memory. Both can look convincing during a short demonstration.
Write the promise from the user's perspective: after closing and reopening the app on this device, saved equipment remains available; after moving to a different device, importing a supported backup restores the specified fields. That wording gives the test an observable result.
Describe where browser data lives
MDN explains that browser storage is generally separated by origin, defined by scheme, hostname and port. Two paths under the same origin do not automatically receive separate storage boundaries. Moving an app to another origin does not automatically move its stored records.
Browser-managed data is best-effort by default. Persistent-storage requests can reduce automatic eviction risk, but they do not protect against the owner clearing data or losing the device. Private browsing can also have different retention behaviour.
Avoid a vague saved badge. Show a clear storage description and the time of the last successful export where useful. If a write fails, keep the user's current input and say that saving failed. A success message should follow the completed write, not the button click.
Sources: [1]
Give the export enough meaning to survive
For the fictional register, a backup could contain a format version, export timestamp and records with stable identifiers. Each record might include the equipment name, location, status and an optional note. These fields are examples, not a universal schema.
Keep display labels separate from stored identifiers so renaming a room does not create duplicate equipment. State whether attachments and history are included. An export that omits those items may still be useful, but it should not be described as a complete backup.
Validate the incoming format before replacing anything. Preview the number of records and conflicts, then offer the supported restore behaviour. If merging is supported, explain how matching identifiers are handled. Keep an existing usable copy until the imported data has passed validation.
Run a restore drill with awkward records
Use a disposable test profile or test instance. Do not delete the only working copy to prove the backup feature.
| Test input or event | Expected evidence |
|---|---|
| Record name includes accents and a comma | Text survives export and import unchanged. |
| Two records share a display name | Stable identifiers keep them distinct. |
| Backup is truncated or has a future format version | Import stops with a clear reason; existing records remain usable. |
| Restore into a clean test instance | Required fields, counts and relationships match the export. |
| Storage write fails | No false saved state; current input remains available. |
Compare actual values, not just record counts. A restore that produces ten records from ten inputs can still have swapped locations or lost notes. Add a record after restoring and export again to check that the recovered state remains usable.
Choose a backup method that matches the store
For a desktop app backed by SQLite, its Online Backup API provides a supported way to copy a live database. That is a more specific operation than assuming that copying an active database file is always sufficient. The right mechanism depends on the database and how it is used.
A backup on the same failing device has a limited recovery role. Decide where an independent copy belongs, how sensitive it is, and who controls it. Do not silently add cloud synchronisation merely to make a local app feel finished.
The handover should let a person answer two questions without the developer present: where is my usable copy, and what exactly does restoring it bring back?
Sources: [2]
Sources and further reading
Sources checked on 11 September 2026. The equipment register and restore drill are proposed examples, not claims that every portfolio application implements these features.