Why "spinner-first" apps fail in the field
Many mobile applications are built as thin wrappers around REST endpoints: every screen transition fires a network request and blocks the user with a loading indicator. In an office on gigabit Wi-Fi, this appears to work. In an elevator, on a commuter train, or inside a hospital corridor, the app stalls or discards what the user just typed.
Local SQLite as the single source of UI truth
At Sisnex, our default architecture for business-critical mobile apps treats the on-device database (SQLite via SwiftData/GRDB on iOS, Room on Android, or Drift on Flutter) as the primary source of truth for the user interface:
- Reads never touch the network directly: Screens observe local database queries reactively. Opening the app renders cached data in under 16 milliseconds—even in airplane mode.
- Writes commit locally first: When a user submits a form or updates an item, we write the mutation to an on-device outbox queue with a client-generated UUID and update the local state immediately.
- Deterministic background reconciliation: A background worker drains the outbox whenever connectivity returns, sending idempotent requests to our Go API.
Idempotency keys on the backend
Network retries are inevitable on cellular connections: a client may send a POST request, the server commits the transaction, and the response packet is lost when the user enters a tunnel. Without idempotency, the client retries and creates duplicate records or double charges.
Every mutating endpoint we build in Go accepts an Idempotency-Key header tied to the client's outbox entry. If the server sees the same key twice, it returns the stored result without re-executing the side effect.
The result is software that feels calm, instant, and trustworthy regardless of signal bars.