O software local-first está de volta, e os motores de sincronização fazem o trabalho pesado

A maior parte do software que usamos ainda trata o servidor como a única cópia real dos nossos dados. O celular e o notebook são janelas finas para um banco de dados em outro lugar, e quando a conexão cai, a janela fica em branco. O movimento local-first argumenta que isso está invertido. O dispositivo deveria guardar a cópia de trabalho, a rede deveria servir apenas para sincronizá-la, e o app deveria continuar funcionando quando a rede desaparece.
A ideia não é nova, mas finalmente é prática. O que mudou não foi uma virada filosófica entre desenvolvedores. Foram os motores de sincronização e os tipos de dados replicados sem conflito (CRDTs) que amadureceram o suficiente para resolver a parte mais difícil: mesclar duas edições feitas em dois dispositivos sem perder nenhuma delas. Se você constrói software colaborativo hoje, sincronização não é mais um detalhe de infraestrutura que se entrega ao time de backend. É uma decisão de produto com consequências no seu modelo de dados, nas permissões e nos custos de suporte.
O manifesto de 2019 e por que levou sete anos
Em 2019, o grupo de pesquisa Ink & Switch publicou um ensaio intitulado Local-first software: you own your data, in spite of the cloud. Ele listava sete ideais: resposta rápida sem spinners, trabalho que sobrevive à perda de rede, colaboração entre dispositivos e pessoas, dados que sobrevivem à empresa que criou o app, segurança e privacidade por padrão, e usuários que controlam os próprios arquivos. Quase ninguém discordava da lista. O problema era o custo de implementação.
Construir um app local-first costumava significar escrever a própria lógica de mesclagem, a própria camada de armazenamento e o próprio protocolo de replicação. A maioria das equipes escolheu o caminho só na nuvem porque era mais simples, e os usuários toleravam os spinners. Esse cálculo mudou porque bibliotecas como Automerge e Yjs tornaram os CRDTs utilizáveis em JavaScript, e os motores de sincronização assumiram a encanamento ao redor deles.
O que um motor de sincronização faz de fato
Um CRDT é uma estrutura de dados projetada para que réplicas possam ser mescladas em qualquer ordem e ainda convergir para o mesmo resultado, sem um árbitro central. Textos, listas e mapas podem ser modelados assim. Um motor de sincronização fica por cima e cuida do trabalho pouco glamouroso: mover mudanças entre cliente e servidor, replicar apenas o subconjunto de dados que cada usuário pode ver, persistir o estado após reinicializações e reconciliar com um banco de dados autoritativo.
Vários produtos em produção funcionam nesse padrão. A Linear escreveu publicamente sobre seu armazenamento do lado do cliente, que permite ao rastreador de issues responder instantaneamente enquanto as mudanças se sincronizam em segundo plano. A edição multiplayer do Figma é outro caso conhecido de muitos clientes editando o mesmo documento ao mesmo tempo. A lição desses produtos é que a cópia local é com o que o usuário interage, e o servidor vira coordenador em vez de gargalo.
Onde o modelo quebra
CRDTs são excelentes para mesclar edições de texto e conjuntos de itens. São bem menos úteis quando suas regras de negócio dependem de invariantes globais. Se duas pessoas reservam o último assento de um voo estando offline, uma função de mesclagem não consegue decidir quem fica com ele sem uma informação que só o servidor possui. O mesmo vale para pagamentos, contagens de estoque que não podem ficar negativas e qualquer coisa que envolva dinheiro ou compromissos legais.
A migração de esquema é o segundo problema que as equipes subestimam. Quando um cliente três meses atrasado se reconecta, ele precisa entender dados escritos por uma versão mais nova, e a versão nova precisa tolerar dados escritos por uma antiga. Isso exige portões de versão no cliente, depreciação cuidadosa de campos e um plano para clientes que nunca atualizam. O armazenamento é o terceiro. Um celular tem espaço e bateria finitos, então você precisa de políticas de remoção, replicação parcial e uma resposta clara para o que acontece quando o banco local ultrapassa o que o dispositivo comporta.
Um framework de decisão simples
Antes de escolher uma arquitetura local-first, faça três perguntas sobre cada tipo de dado do seu produto. Primeiro, esse dado precisa funcionar offline ou responder em menos de cerca de 100 milissegundos? Segundo, várias pessoas editam o mesmo objeto simultaneamente? Terceiro, se o usuário perdesse o acesso ao fornecedor, o dado ficaria inútil para ele?
Se a resposta às três for sim, você tem um caso forte para local-first, como anotações, gerenciadores de tarefas e ferramentas de design. Se as duas primeiras forem não, um modelo convencional com servidor costuma ser mais barato e mais fácil de raciocinar. A maioria dos produtos acaba híbrida. Rascunhos e conteúdo colaborativo sincronizam localmente, enquanto pagamentos, papéis e cobrança continuam sob autoridade do servidor.
Conclusões práticas
- Classifique cada entidade do seu modelo de dados como mesclada por CRDT, autoritativa no servidor ou somente leitura, e registre isso antes de escrever código.
- Versione o esquema desde o primeiro dia e adicione verificações de versão no cliente para que clientes antigos falhem de forma controlada em vez de corromper dados.
- Teste em Android de entrada com armazenamento limitado, não apenas no iPhone mais recente.
- Faça um teste interno de uma semana offline. Use modo avião no trabalho normal e registre cada ponto em que o app te bloqueia. Esses são seus requisitos reais.
Local-first não substitui a nuvem. É um rebalanceamento: o dispositivo faz mais do trabalho que importa para o usuário, e o servidor faz a coordenação em que é bom. As equipes que acertarem isso vão lançar apps que parecem mais rápidos e sobrevivem a redes ruins. As que tratarem a sincronização como detalhe continuarão mostrando spinners.