Publicado primeiro na newsletter Dev na Gringa. Se quiser receber o próximo no seu e-mail, se inscreva.
Aplicações web hoje dependem muito de servidores. Todo dado fica em algum datacenter, e precisamos de internet para acessar qualquer coisa.
Porém, existe um movimento crescente para mudar isso: aplicações local-first.
Fonte: Palestra do Martin Kleppmann na Local-First Conf em 2024
Como um engenheiro de software focado em produtos, eu sou fascinado por aplicações local-first.
O principal motivo é pela experiência incrível que ela fornece aos nossos usuários.
Esse é um movimento que me interessa principalmente por isso.
A capacidade de criar aplicações web que pareçam como um app nativo.
Com nenhum loading ou skeleton. Que carregue instantaneamente. Interações de UI sendo feitas a 60 FPS.
Hoje, vamos ter um artigo um pouco diferente, mais técnico.
Uma introdução a arquitetura local-first.
✨ O que esperar do artigo
- O que é desenvolvimento local-first e porque ele está ganhando tração agora
- Como algumas empresas já usam essa arquitetura com sucesso
- Uma análise profunda dos trade-offs e quando essa abordagem faz sentido
O que é desenvolvimento local-first
Desenvolvimento local-first é uma mudança de paradigma na forma como construímos aplicações.
Em vez dos dados ficarem centralizados em um servidor, cada cliente (navegador, app móvel) mantém uma cópia local completa dos dados. O servidor passa a ter um papel secundário: ajudar na sincronização entre diferentes dispositivos.
A mudança principal é que o cliente passa a ser a fonte primária da verdade, não mais o servidor.
Algumas aplicações famosas já usam esse conceito:
- Figma: permite editar designs mesmo offline
- Linear: toda operação é instantânea pois os dados estão locais
- Notion: você pode continuar escrevendo mesmo sem internet
- Superhuman: email funciona perfeitamente offline
Como isso funciona na prática
A tecnologia que permite esse tipo de arquitetura são os CRDTs (Conflict-free Replicated Data Types).
São estruturas de dados especiais que garantem que diferentes cópias dos dados podem ser modificadas independentemente e depois sincronizadas sem conflitos.
Fonte: Demystifying CRDTs: Enhancing Distributed Systems.
Vamos ver um exemplo prático. Imagine um editor de texto colaborativo:
- Alice começa a digitar um documento no seu laptop
- Bob abre o mesmo documento no computador dele
- Alice perde internet mas continua digitando
- Bob também faz alterações no documento
- Quando Alice reconecta, as mudanças são mescladas automaticamente
Com CRDTs, não precisamos nos preocupar com conflitos de edição. A estrutura de dados garante que as alterações podem ser mescladas de forma determinística.
Algumas aplicações como o Figma e o Linear não usam CRDTs propriamente. Pois, CRDTs são um pouco mais complexos: são feitos para funcionarem de maneira descentralizada.
Como essas aplicações tem uma fonte de verdade final (no servidor), a sua lógica fica um pouco mais simples. Pois não é necessário a questão de descentralização, já que há uma entidade com autoridade central.
Por isso, as sync engines são um pouco mais simples.
Mas, tudo isso começa a partir dos princípios que as CRDTs trazem. Diretamente do blog de engenharia do Figma:
Even if you have a client-server setup, CRDTs are still worth researching because they provide a well-studied, solid foundation to start with. Understanding them helps build intuition on how to create a correct system. From that point, it's possible to relax some of the requirements of CRDTs based on the needs of the application as we have done.
Os trade-offs do desenvolvimento local-first
Como qualquer decisão arquitetural, local-first vem com seus próprios desafios. Vamos analisar os principais trade-offs e como eles afetam diferentes tipos de aplicações.
Sincronização e Consistência
Em uma arquitetura tradicional, o servidor é a fonte da verdade. Em local-first, cada cliente tem sua própria cópia dos dados.
Prós:
- Operações instantâneas para o usuário
- Trabalho offline possível
- Sem single point of failure
Contras:
- Estado pode ficar temporariamente inconsistente
- Resolução de conflitos é complexa (CRDTs ajudam com isso)
- Usuários podem ver versões diferentes do mesmo dado
Fonte: Palestra do Anselm Eickhoff, criador jazz.tools, no meetup Local-first Berlin. Em aplicações local-first, você faz os updates localmente, que eventualmente são sincronizados com outros clientes.
Performance e Recursos
Prós:
- Queries são rápidas (dados locais)
- Menor carga nos servidores
- Zero latência para operações básicas
Contras:
- Dados ocupam espaço no dispositivo
- CRDTs podem crescer muito com histórico
- Sincronização inicial pode ser lenta
Por exemplo, o Figma precisa baixar o arquivo de design completo antes que você possa começar a editar. Em arquivos grandes, isso pode levar alguns segundos.
Segurança e Validações
Este é provavelmente o trade-off mais significativo. Em uma arquitetura tradicional, podemos confiar que o servidor vai validar todas as operações.
Prós:
- Dados sensíveis ficam no dispositivo
- Menor superfície de ataque
- Usuário controla seus dados
Contras:
- Validações precisam ser duplicadas
- Difícil revogar acesso a dados
- Vazamentos são mais difíceis de detectar
Uma estratégia comum é ter validações em camadas:
- Validação local para feedback rápido
- Validação no servidor antes de propagar mudanças
- Reconciliação periódica de dados
Complexidade de Desenvolvimento
Prós:
- Menos infraestrutura para manter
- Escala naturalmente
- Menos código de API
Contras:
- Debugging mais complexo
- CRDTs tem curva de aprendizado
- Ferramental ainda imaturo
James Arthur, fundador do ElectricSQL, compartilhou em uma palestra que um dos maiores desafios em sistemas local-first é rastrear a origem de bugs. Como os dados podem vir de múltiplos dispositivos e serem modificados offline, pode ser difícil entender como o sistema chegou em determinado estado.
Um exemplo comum é quando diferentes clientes têm versões diferentes do schema do banco. Enquanto em sistemas tradicionais isso seria detectado imediatamente (pois há apenas um schema no servidor), em local-first isso pode causar inconsistências sutis que são difíceis de diagnosticar.
Como essa nova versão? Ela mantém o ponto sobre a complexidade de debugging mas usa um exemplo real e citado, ao invés de inventar uma experiência.
Estratégias para mitigar problemas comuns
- Sincronização Seletiva
Em vez de sincronizar todos os dados, permita que o usuário escolha o que manter local:
- Compressão de História
CRDTs podem crescer indefinidamente. Uma solução é comprimir o histórico periodicamente, mantendo apenas as mudanças mais recentes.
- Cache Inteligente
Use estratégias como LRU (Least Recently Used) para manter apenas dados relevantes no cliente:
Quando faz sentido usar local-first
Local-first não é uma bala de prata. Existem casos onde ele faz muito sentido, e outros onde uma arquitetura tradicional pode ser melhor.
Bons casos para local-first:
- Editores de texto/código
- Ferramentas de design
- Apps de notas e documentação
- Jogos e aplicações multiplayer
- Qualquer app que precise funcionar offline
Além disso, um outro bom caso: onde a qualidade do seu produto é essencial para captar clientes.
Foi assim que o Linear cresceu como uma ferramenta de gerenciamento de projetos.
Esse é um espaço que é competitivo. Muitas empresas competem aqui: Asana, Atlassian (Jira/Trello), até mesmo GitHub/GitLab.
Porém, um dos motivos que fez o Linear conseguiu adentrar esse mercado é devido a qualidade do seu sistema.
Aplicações local-first, com suas interações imediatas e rápidas, podem conquistar usuários através da força de sua UX.
Casos onde local-first pode não ser ideal:
- Redes sociais
- E-commerce
- Bancos
- Apps que dependem muito de dados em tempo real do servidor
A regra geral é: se sua aplicação precisa funcionar offline ou ter colaboração em tempo real, local-first pode ser uma boa escolha.
Ferramentas disponíveis
O ecossistema está crescendo rapidamente:
- ElectricSQL: Sincronização Postgres ↔ SQLite
- Yjs: Framework para dados compartilhados
- PouchDB: Banco local que sincroniza com CouchDB
- Automerge: Estruturas CRDT em JavaScript
- Replicache: Biblioteca de sincronização para qualquer backend
- PowerSync: Sincronização com first-party support para o Supabase (Postgres)
Veja mais ferramentas aqui no localfirstweb.dev.
Se quiser saber mais sobre as atualidades desse cenário, também recomendo ver as palestras da Local-First Conf.
🌟 Resumo
- Local-first é uma mudança de paradigma onde dados ficam primariamente no cliente
- Os benefícios principais são performance, suporte offline e colaboração
- CRDTs permitem sincronização sem conflitos mas tem seus próprios desafios
- Trade-offs significativos em segurança e consistência precisam ser considerados
- O ecossistema está amadurecendo rapidamente
A chave é entender que local-first não é um substituto completo para arquiteturas tradicionais - é uma ferramenta diferente com seus próprios casos de uso.
Se você está construindo uma nova aplicação que precisa de colaboração em tempo real ou suporte offline robusto, considere uma arquitetura local-first. As ferramentas estão ficando maduras e os benefícios podem superar os desafios.
O futuro da web pode ser mais descentralizado do que imaginamos. E desenvolvimento local-first é um passo importante nessa direção.
Esse artigo foi publicado na minha newsletter, Dev na Gringa.
Se você gosta do meu conteúdo, considere se inscrever para receber diretamente por e-mail.
Top comments (1)
Bom artigo! Obrigado por compartilhar conosco 👊