Technical note
Designing local-first product flows that survive interrupted state
Local-first product design is a promise about ownership: the user's work should remain understandable, recoverable, and portable when a network, device connection, sensor, file, or app process does not cooperate.
Related public proof: TrailScope case study
That promise changes where the product work happens. The important decisions are not only in the main screen. They are in the transitions between states: before a file becomes trusted data, before phone state becomes watch state, before a recording becomes a durable workout, and before an internal record becomes an exported file another tool can read.
Most product flows are first designed around the clean path. A user starts with valid input, moves through a known sequence, confirms an action, and lands in a finished state. That path matters, but it is rarely where trust is earned. Trust is earned when the product can explain what happened after something was interrupted.
The Happy Path Is Too Small
A route-following trail workflow is a good example because it crosses several ownership boundaries:
- A file exists outside the app.
- The user imports it.
- The app parses and validates it.
- The user reviews the route.
- The route becomes available for a workout.
- A watch receives enough route state to be useful away from the phone.
- The user records against the route.
- The app recovers if the session is interrupted.
- The finished workout can be exported as clean GPX or FIT.
Each step can look like a simple product action, but each one asks a different trust question.
Can the product reject a malformed file without creating confusing partial state? Can the user tell whether a route has only been imported or is actually ready on the watch? Can a recording recover after app lifecycle changes, GPS uncertainty, or device connection changes? Can the exported file be trusted outside the product itself?
If those questions are left as implementation details, the UI eventually exposes the gap. The user sees a spinner that cannot explain what happened, a success state that was too optimistic, or a completed action that did not actually preserve the work.
Start With State Before Screens
For local-first workflows, the first useful artifact is a state map. It does not need to be elaborate. It needs to name what the product must preserve, who owns that state, what can fail, and what the user should be able to do next.
Scroll horizontally to read all table columns.
| State | Product promise | Common interruption |
|---|---|---|
| Imported file | The file was received, not yet trusted | Unsupported GPX shape |
| Validated route | The route can be reviewed and saved | Missing points or bad metadata |
| Watch-ready route | The watch has enough route data | Paired device unavailable |
| Active workout | Recording state is being preserved | GPS changes or app lifecycle interruption |
| Recovered workout | The safest available session state is restored | Process restart or partial sensor data |
| Exported file | Output works beyond the app | GPX/FIT generation or compatibility issue |
This table is less polished than a mockup, but it is more useful early. It forces a team to decide which states are real, which states are temporary, which states can be retried, and which states should never be shown as complete.
Screens can then express those decisions. The product does not need to show every internal term, but the user's available actions should match the actual state. A route that is imported but not validated should not feel the same as a route ready for a workout. A sync request should be retryable without duplicating the route. An export failure should not damage the workout record.
Treat Boundaries As Product Moments
Local-first products tend to fail at boundaries:
- File to app.
- App storage to user-facing library.
- Phone to watch.
- Foreground UI to background work.
- Raw sensor data to durable record.
- Internal record to portable export.
These are not just technical boundaries. They are moments where the product changes who or what owns the user's work.
When a GPX route enters the app, the product should not trust it just because the file exists. It should parse, validate, and explain unsupported input without leaving the user with a route that appears usable but is not. When a route is sent to a watch, the product should treat that as a distinct delivery state rather than a hidden side effect. When a workout is exported, the product should prefer conservative, clean output over a file that looks finished but fails in another tool.
That kind of design can feel slower at first because it creates more named states. It pays back by making failure modes understandable. The user does not have to know how the system works internally; they only need the product to tell the truth about what is safe to do next.
Recovery Is A Main Flow
Recovery is often designed late, after the primary screens feel good. For local-first products, that is backwards. Recovery is part of the primary workflow because interruption is normal.
Phones restart apps. Watches lose connection. Permissions change. GPS quality shifts. A user locks the screen, switches apps, reaches a dead zone, or finishes an activity before every background step has completed.
The practical question is not "can this flow avoid interruption?" It is "what state can the product honestly recover to?"
Useful recovery design follows a few rules:
- Preserve user-created work before trying to polish it.
- Separate temporary progress from committed records.
- Make retries idempotent where possible.
- Avoid turning uncertain state into a fake success.
- Show the next useful action instead of leaking internal error language.
Those rules make the product calmer. A route import can be retried. A watch sync can be requested again. A workout can recover to the safest available record. An export can fail without destroying the underlying activity.
Ask For Evidence Before Release
The best way to find weak state design is to ask for release evidence that crosses the same boundaries as the product.
For a local-first workflow, screenshots are not enough. The evidence should include:
- Valid and invalid import files.
- Partial and repeated device-sync attempts.
- Poor GPS availability and changing permission states.
- App restart or process-death recovery during active work.
- Retried operations that do not duplicate records.
- Exported GPX/FIT files opened outside the app.
This evidence is not ceremonial. It answers the question that matters: can the user trust the product with work they do not want to lose?
It also improves collaboration. Instead of debating whether a screen looks finished, the team can ask whether the workflow survives the conditions it claims to support. That is a better release conversation for local-first products, especially when the team is small and the same person may be carrying product judgment, implementation, QA, and launch readiness.
What Changes In Delivery
Designing for interrupted state changes the order of work:
- Map durable states before polishing screens.
- Treat file, device, sensor, lifecycle, and export boundaries as product moments.
- Make recovery paths normal, not apologetic.
- Verify synced and exported output outside the product.
- Tie launch readiness to evidence, not demo completeness.
The result is less fragile software. More importantly, it is a product that respects the user's effort. If someone imports a route, records a workout, manages a pool, or creates any durable piece of local state, the product should behave as though that state matters.
That is the practical promise of local-first design: the work belongs to the user, and the product is responsible for carrying it through the messy parts.