Back to blog

· 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.

Proposed recovery path: a working copy is exported to an independent backup, validated and restored into a clean test instance. Compare restored values with the original; keep the working copy until recovery is verified.
Proposed recovery path: a working copy is exported to an independent backup, validated and restored into a clean test instance. Compare restored values with the original; keep the working copy until recovery is verified. View full-size diagram

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.

Run a restore drill with awkward records
Test input or eventExpected evidence
Record name includes accents and a commaText survives export and import unchanged.
Two records share a display nameStable identifiers keep them distinct.
Backup is truncated or has a future format versionImport stops with a clear reason; existing records remain usable.
Restore into a clean test instanceRequired fields, counts and relationships match the export.
Storage write failsNo 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.

  1. MDN: Storage quotas and eviction criteria; live browser storage documentation
  2. SQLite: Online Backup API; live documentation
Back to all posts