Mobile & Products

Offline-first is a data model decision

Offline capability cannot be retrofitted. It is decided where you settle who owns a record and when that record is valid.

Diagram of an offline-first architecture: Android and iOS applications access shared local storage within the device boundary, with no server synchronisation
AI disclosure: Image created with AI © EicDevCon UG (haftungsbeschränkt)

"The app should also work offline" sounds like a requirement for the networking layer. It is in fact a requirement for the data model — and it is very hard to retrofit later.

The usual mistake

The obvious approach is to cache server responses and show the last known state when there is no connection. That works for reads. As soon as the user changes something offline, questions appear that a cache cannot answer:

  • Which version wins when the same information was changed on two devices?
  • What happens to a change whose upload keeps failing?
  • How does the user know their device is not showing current data?

Engineering note

A cache postpones the decision about who owns a record. Offline-first makes it.

The actual decision

Offline-first means the device is the source of record for the data created on it. The server — if there is one — is another participant, not the truth.

That produces concrete constraints in the data model:

  • Every record needs an identity the device can generate, not a server-assigned sequence number.
  • Timestamps must remain meaningful even when the device clock is wrong.
  • Deletions need an explicit representation, otherwise deleted entries come back.

The simplest case: no backend

The most consistent variant is to have no server at all. That removes conflict resolution, account management and the entire question of data transfer.

The trade-off is equally clear: changing device is a deliberate user action, and there is no synchronisation between two devices belonging to the same person.

Decision

This is the variant used in AuraHarmony: all data stays on the device, and there is no server synchronisation.

Do not forget migrations

Local storage means local schema changes. Every version that was ever installed has to be migratable to the current state — even when several versions were skipped in between.

Kotlin
// Migrations are declared explicitly and without gaps.
// A skipped step otherwise surfaces on the user's device.
val migrations = arrayOf(
    MIGRATION_1_2,
    MIGRATION_2_3,
    MIGRATION_3_4,
)

Risk

A missing migration causes data loss on the user's device and cannot be corrected after release.

What this means for planning

The question "should the app work offline" belongs at the start, not in a second phase. It determines identities, timestamps, deletion semantics and migration strategy — that is, the foundation.

The mobile development page puts this field into context.