Mobile · PlenoMid-level

Offline-first e sincronização: o app que funciona sem rede

Conexão móvel cai no elevador, no metrô, no interior. Um app que só funciona com rede perfeita frustra o usuário no pior momento. A estratégia offline-first inverte a lógica: o app opera sobre dados locais e trata a rede como um luxo que sincroniza quando aparece. Modelar isso bem — com fila de pendências e resolução de conflitos — é uma competência clássica de pleno.

Por que offline-first

Em vez de "tente buscar na rede; se falhar, mostre erro", o offline-first diz "leia e escreva localmente; a rede é só um detalhe de fundo". O usuário continua produtivo no avião, e quando a conexão volta, tudo se acerta sozinho. O ganho não é só offline: mesmo online, ler do local é instantâneo, sem telas de carregamento.

Ler e escrever local primeiro

O banco local (veja estado e dados no mobile) vira a fonte da verdade imediata para a UI. Quando o usuário cria um item, ele aparece na hora — gravado localmente — sem esperar o servidor. A rede entra depois, em segundo plano.

A analogia: é como anotar pedidos num caderno durante o dia e digitar tudo no sistema à noite. O atendimento (UI) nunca para esperando o sistema central.

A fila de operações pendentes

Cada escrita que ainda não chegou ao servidor entra numa fila de pendências persistida localmente. "Criar pedido 42", "marcar tarefa concluída", "deletar nota". A fila sobrevive ao app ser fechado e é processada em ordem quando há rede.

fila = [
  { op: "criar", tipo: "pedido", id: "tmp-1", dados: {...} },
  { op: "atualizar", tipo: "tarefa", id: 7, campo: "feito" }
]
// quando a rede volta: envia uma a uma, remove ao confirmar

Diagrama do fluxo offline → sync

  [ Usuario age ]
        |
        v
  [ Grava no banco local ]  --> UI atualiza JA
        |
        v
  [ Enfileira pendencia ]
        |
   (sem rede) ... espera ...
        |
   (rede volta)
        v
  [ Envia fila ao servidor ] --> sucesso? remove da fila
        |                         conflito? resolve
        v
  [ Servidor confirma / devolve estado ]

Sincronizar quando a rede voltar

Você observa a conectividade e dispara a sincronização ao reconectar (e, idealmente, periodicamente). O sync é bidirecional: envia suas pendências e baixa o que mudou no servidor desde a última vez (delta sync, usando um timestamp ou cursor).

Resolução de conflitos

O problema difícil: você editou um registro offline e outra pessoa editou o mesmo registro no servidor. Quem vence?

EstratégiaComo funcionaRisco
Last-write-winsA escrita mais recente (por timestamp) sobrescrevePerde silenciosamente a outra edição
Merge por campoCombina alterações de campos diferentesComplexo; campos conflitantes ainda precisam decidir
Resolver com o usuárioMostra as versões e perguntaAtrito na UX; só para casos críticos

É comum em entrevista pedir para explicar last-write-wins e quando ele não serve — por exemplo, num saldo financeiro, onde sobrescrever cegamente perde dinheiro.

Idempotência das requisições

Como a fila pode reenviar uma operação (a rede caiu bem na hora da resposta), o servidor precisa aguentar receber a mesma coisa duas vezes sem duplicar. A solução é idempotência: cada operação carrega um identificador único (idempotency key) e o servidor ignora repetições.

POST /pedidos
Idempotency-Key: tmp-1-abc
// reenviar com a mesma chave nao cria 2 pedidos
Trate a rede como algo que pode falhar a qualquer momento, inclusive depois de processar mas antes de responder. Idempotência e fila persistida são o que torna o sync confiável, não opcional.

Conclusão

Offline-first significa local primeiro, rede depois: escrever no banco, enfileirar a pendência, sincronizar ao reconectar e resolver conflitos com uma estratégia consciente. Somando idempotência para sobreviver a reenvios, você entrega um app que funciona no elevador e se acerta sozinho quando o sinal volta.

Mobile connections drop in elevators, on the subway, out in the countryside. An app that only works with a perfect network frustrates the user at the worst possible moment. The offline-first strategy flips the logic: the app operates on local data and treats the network as a luxury that syncs whenever it shows up. Modeling this well — with a pending queue and conflict resolution — is a classic mid-level skill.

Why offline-first

Instead of "try fetching from the network; if it fails, show an error", offline-first says "read and write locally; the network is just a background detail". The user stays productive on the plane, and when the connection returns, everything sorts itself out. The gain isn't only offline: even online, reading from local storage is instant, with no loading screens.

Read and write locally first

The local database (see state and data on mobile) becomes the immediate source of truth for the UI. When the user creates an item, it shows up right away — saved locally — without waiting for the server. The network comes in later, in the background.

The analogy: it's like jotting down orders in a notebook during the day and typing everything into the system at night. The service (UI) never stops waiting for the central system.

The pending operations queue

Every write that hasn't reached the server yet goes into a pending queue persisted locally. "Create order 42", "mark task as done", "delete note". The queue survives the app being closed and is processed in order when there's a network.

queue = [
  { op: "create", type: "order", id: "tmp-1", data: {...} },
  { op: "update", type: "task", id: 7, field: "done" }
]
// when the network returns: send one by one, remove on confirmation

Offline → sync flow diagram

  [ User acts ]
        |
        v
  [ Write to local database ]  --> UI updates RIGHT AWAY
        |
        v
  [ Enqueue pending op ]
        |
   (no network) ... wait ...
        |
   (network returns)
        v
  [ Send queue to server ] --> success? remove from queue
        |                       conflict? resolve
        v
  [ Server confirms / returns state ]

Syncing when the network returns

You observe connectivity and trigger sync on reconnect (and, ideally, periodically). Sync is bidirectional: it sends your pending operations and downloads what changed on the server since last time (delta sync, using a timestamp or cursor).

Conflict resolution

The hard problem: you edited a record offline and someone else edited the same record on the server. Who wins?

StrategyHow it worksRisk
Last-write-winsThe most recent write (by timestamp) overwritesSilently loses the other edit
Field-level mergeCombines changes to different fieldsComplex; conflicting fields still need a decision
Resolve with the userShows the versions and asksUX friction; only for critical cases

Interviewers often ask you to explain last-write-wins and when it does not fit — for example, a financial balance, where blindly overwriting loses money.

Request idempotency

Since the queue may resend an operation (the network dropped right as the response was coming), the server must tolerate receiving the same thing twice without duplicating it. The solution is idempotency: each operation carries a unique identifier (idempotency key) and the server ignores repeats.

POST /orders
Idempotency-Key: tmp-1-abc
// resending with the same key does not create 2 orders
Treat the network as something that can fail at any moment, including after processing but before responding. Idempotency and a persisted queue are what make sync reliable, not optional.

Conclusion

Offline-first means local first, network later: write to the database, enqueue the pending operation, sync on reconnect and resolve conflicts with a conscious strategy. Add idempotency to survive resends, and you deliver an app that works in the elevator and sorts itself out when the signal returns.

Leia também

Voltar à trilhaBack to the track