Linear me levou ao buraco do coelho local-first
(bytemash.net)- Ao usar o Linear, minha visão sobre o desenvolvimento de aplicações web mudou bastante
- O Linear funciona com uma abordagem local-first, oferecendo respostas imediatas e interações sem latência de rede
- Esse modelo faz com que o cliente tenha uma base de dados independente, e as alterações são sincronizadas com o servidor de forma assíncrona
- Mas a dificuldade de implementação para sincronização em ambiente distribuído, resolução de conflitos e tratamento offline é alta
- Soluções diversas do ecossistema local-first, como Jazz, Electric SQL e Zero, estão surgindo, e a experiência do desenvolvedor também está melhorando gradualmente
Visão geral
Ao usar a ferramenta de gerenciamento de projetos Linear, fiquei muito impressionado com a extraordinária velocidade e experiência do usuário do modo local-first. Foi marcante perceber que não se sentia nada de latência de rede, estados de carregamento, atualização de página etc.
Com essa experiência, aprofundei a exploração dos princípios técnicos e dos casos reais de aplicação do paradigma local-first.
Descendo o buraco do coelho
Ao investigar o segredo técnico do Linear, descobri que eles usam o IndexedDB do navegador como um banco de dados de verdade. Todas as mudanças são processadas imediatamente no cliente primeiro e, em seguida, a sincronização ocorre em segundo plano com GraphQL e Websockets.
- O termo local-first pode ser interpretado de diferentes formas, como estratégia de UX (reatividade imediata) ou como uma filosofia de manter os próprios dados em local e sincronizar
- Em um app web tradicional, o servidor era a fonte única da verdade, mas na estrutura local-first cada cliente possui seu próprio banco de dados
- Como a localização do banco de dados se move para perto do usuário, a latência de rede é totalmente removida da interação do usuário
O desafio: isso não é trivial
Ao tentar implementar o mesmo modo do Linear, percebi que a complexidade é considerável.
- Tratamento de troca entre offline e online
- Resolução de conflitos entre clientes distribuídos
- Sincronização parcial (para evitar baixar todos os dados)
- Migração de schema de dados em cache
- Segurança e controle de acesso em ambiente distribuído
- Essas áreas exigem enorme investimento de tempo e esforço de engenharia
O ecossistema local-first em 2025
Em 2025, o ecossistema local-first já conta com várias soluções fortes.
- Electric SQL: motor de sincronização baseado em Postgres
- PowerSync: solução com foco em enterprise
- Jazz: ferramenta que facilita a construção de apps local-first
- Replicache: uma das soluções principais existentes (fim de desenvolvimento)
- Zero: novo direcionamento da equipe do Replicache
- Triplit: sincronização baseada em TripleStore
- Instant: com foco na experiência do desenvolvedor
- LiveStore: fornece uma camada de sincronização em tempo real
Análise aprofundada: Jazz
Jazz chama atenção por prometer que torna um app local-first tão simples quanto uma atualização de estado.
Modelo mental
Jazz introduz os Collaborative Values (CoValues), uma estrutura de colaboração em tempo real.
- Definir schema com Jazz e Zod: essa definição não é apenas um tipo simples, mas funciona como objetos vivos com sincronização automática
- Sem rotas de API separadas, lógica de request/resposta ou DTOs, basta alterar o estado dos objetos para a sincronização acontecer automaticamente
Como o Jazz faz isso
A estrutura interna do Jazz é assim:
- Garantia de unicidade: atribuição automática de IDs únicos para cada dado
- Event sourcing: o histórico de alterações é salvo como eventos, otimizando a eficiência da sincronização em tempo real
- Criptografia ponta a ponta: criptografia no cliente antes da sincronização. O servidor só vê blobs criptografados
- Modelo de permissão por grupo: em vez de ACLs tradicionais, a propriedade é separada com clareza por grupo
Os trade-offs
Essa estrutura é extremamente produtiva para protótipos e desenvolvimento rápido de UI. Mas há aspectos que exigem atenção:
Seu servidor é cego
Com criptografia ponta a ponta, o servidor não pode ler os dados do usuário. Se você não definir com antecedência quais dados o servidor precisa acessar, há limitações para governança, como monitoramento ou prevenção de armazenamento malicioso.
Viagem no tempo é obrigatória
Com event sourcing, todo o histórico de mudanças é mantido de forma permanente. Isso torna Undo/Redo muito prático, mas tem o ponto fraco de dificultar a exclusão quando há exigências legais como GDPR.
Armazenamento dispara
Como nada é removido, o uso de armazenamento cresce progressivamente. Em projetos pequenos isso é aceitável, mas em SaaS em grande escala os custos de armazenamento podem aumentar bastante.
O desenvolvimento local ainda tem detalhes chatos
A autenticação baseada em Passkeys é padrão, mas no desenvolvimento próprio ou em ambiente local há inconvenientes iniciais, como HTTPS, gerenciamento de certificados e migração de chaves. Mesmo assim, há melhorias previstas, como integração com Better Auth.
Mas sinceramente? Ainda vale a pena
Mesmo com essas limitações, a experiência de desenvolvimento e produtividade do Jazz é muito impressionante. Ainda é uma versão inicial, mas há perspectiva de que vários problemas sejam resolvidos gradualmente.
Explorando: Electric SQL e Zero
Diferente do Jazz, Electric SQL e Zero adotam uma abordagem incremental.
- É possível aproveitar as tabelas Postgres existentes
- O Electric SQL permite assinar partes da tabela como uma reactive query (Shape) para sincronizar com a UI
- A forma de tratar mutações é diferente da do Jazz, e há opções diversas, como integração com LiveStore
- Zero é semelhante ao Electric, mas tem suporte embutido para sincronização de mudanças
Quando local-first faz sentido?
A seguir, um resumo do que é adequado e o que é desafiador no paradigma local-first.
Adequado:
- Ferramentas criativas (design, escrita, música etc.)
- Aplicações de suporte à colaboração
- Apps móveis que precisam funcionar offline
- Ferramentas para desenvolvedores
- Aplicativos de produtividade pessoal
Desafiador:
- Lógica de negócio pesada no lado do servidor
- Requisitos rigorosos de auditoria
- Sistemas de análise de alto volume
- Sistemas legacy profundamente integrados
- Sistemas que rejeitam frequentemente solicitações no servidor
Olhando para o futuro
Local-first significa uma mudança de paradigma no desenvolvimento web. O Linear já comprovou um grande impacto na experiência do usuário. Cabe ao desenvolvedor decidir se esses trade-offs estruturais fazem sentido para seu projeto.
Ao criar um app pessoal com Jazz, venho experimentando os prós e contras reais e os limites de abstração na prática. O ecossistema ainda está em fase inicial, e no futuro ferramentas e padrões devem amadurecer e melhorar. No entanto, os benefícios de manter os dados no local são claros e isso parece não desaparecer.
Em novos projetos, se for possível aceitar as restrições, vale muito a pena experimentar local-first. No pior caso, você aprende um novo padrão de arquitetura, e no melhor, consegue criar uma experiência de usuário incrivelmente rápida. Em uma corrida contra respostas de 300ms, isso vira uma vantagem importante.
1 comentários
Opinião do Hacker News
O Zero também oferece recursos semelhantes ao Electric e oferece suporte direto a mutações O principal diferencial do Zero é a sincronização orientada a queries Você pode desenhar o app por unidade de query e não precisa decidir ou configurar antecipadamente o que sincronizar; basta executar a query para sincronizar só a quantidade de dados necessária (ou até menos) Se o cliente não tiver os dados de que precisa, a query passa automaticamente para o servidor e, uma vez sincronizado, fica disponível imediatamente para a consulta seguinte Isso é muito prático
Eu prefiro SSR Eu acho que o cliente deveria manter estado apenas com token de sessão, URL atual e DOM A rede e os servidores continuam ficando mais rápidos A velocidade da luz é fixa, mas ainda não a exploramos plenamente Tecnologias como fibra de núcleo oco também vão reduzir a latência da internet em até 30% Mesmo com RTT de 500ms, se uma página renderizada com SSR carregar em 16ms, ela parecerá muito mais instantânea que a maioria dos sites de hoje na web Acho sem sentido o servidor renderizar a resposta HTML por mais tempo do que um frame de 60Hz Em um núcleo Zen5 dá para serializar 30~40MB de JSON nesse tempo Para o servidor, isso é apenas uma string UTF-8 sofisticada Acho que isso deve ser medido em μs, não em ms Latência alta na transmissão não é desculpa para usar o tempo de CPU de forma preguiçosa Eu saí da “prisão dos milissegundos” usando SQLite Serviços SQL hospedados são uma amarra quando se busca latência abaixo de 1ms Padrões do navegador também podem resolver parte dos problemas de latência de navegação Consulte a Speculation Rules API
O fato de rede e servidor ficarem mais rápidos não é argumento universal para SSR Isso acaba dizendo que há apenas alguns casos de exceção em que SSR é necessário, como quando um servidor tem mais poder computacional ou quando é difícil separar a lógica Na prática, muitas vezes a renderização no cliente é mais rápida A lógica de renderização costuma ser muito mais complexa do que o tamanho dos dados e somando todos os recursos do cliente, pode ficar mais pesada que o servidor Se SSR fosse a única opção, não daria para explicar a euforia atual em torno de WebAssembly Dê uma olhada também no texto sobre computação local No fim, não dá para saber com antecedência qual é melhor, então a escolha precisa ser possível no momento da requisição
Right Tool For The Right Job! Por exemplo, dá para trabalhar em documentos colaborativos como Google Docs e Sheets apenas com SSR? Se você insistir só em SSR, ocorre a situação em que o usuário vê outra pessoa alterando o conteúdo enquanto está no meio da edição Esses tipos de ferramentas funcionam bem porque mudanças têm pouco impacto e, por estarem sempre em colaboração, o usuário percebe e recebe atualizações em tempo real Por outro lado, um serviço de reserva de hotel não precisa de sincronização em tempo real E apps corporativos com sincronização oscilante no meio do caminho e sem desenho prévio suficiente podem ficar difíceis de manter mais tarde, com consistência comprometida pelo aumento de domínio e complexidade de sistema
Se eu pudesse adicionar uma biblioteca fácil assim a apps SSR existentes, eu usaria:
Isso é engenharia de happy path puro Para quem não está no happy path, a realidade é bastante frustrante
O principal uso atual e futuro do SSR é a primeira renderização da página, especialmente em mobile Depois disso, só atualizações de estado no cliente, então tudo precisa ficar mais rápido Se a habilidade de engenharia for baixa, mesmo com SSR não faz muita diferença
Eu desenvolvo um software de gerenciamento de tarefas open source com arquitetura local-first baseada em CRDT A motivação veio porque eu não precisava de recursos de colaboração e o Linear era muito complexo para o meu uso As vantagens dessa arquitetura são:
Fiquei muito impressionado com o Jazz O DX é excelente, com código quase sincrônico e imperativo, o que torna o desenvolvimento divertido Tudo parece imediato, e também dá para trabalhar offline, então a UX é ótima O maior problema é o deploy e a manutenção de longo prazo, principalmente o crescimento contínuo dos dados (a maioria dos clientes não precisa ver tudo, então não me preocupo muito com isso) O maior ponto é que falta uma boa solução para o índice público que muda com frequência Em teoria, um public readable list of ids é possível, e em conversa recente com Anselm ele disse que isso está em progresso No geral, local-first também não sai barato, mas se o Jazz resolver as principais fraquezas da abordagem tradicional de servidor central, acredito que pode substituir o Firestore em quase todos os aspectos
ElectricSQL e TanStack DB são excelentes, mas fico curioso sobre por que tanta concentração em local first para a web Em mobile, com ambientes offline, acredito que local first é bem mais necessário No navegador, a maior parte do tempo a internet já está conectada Além disso, essas tecnologias local-first parecem boas em teoria, mas na prática quebram fácil se não houver resolução de conflitos Foi uma lição que aprendi fazendo apps local-first no mobile, e por isso acabei usando CRDT
Construir apps local-first com tecnologias web é infinitamente mais difícil do que em nativo Em app nativo, só instalar já te dá uso offline por padrão Na web, você depende de truques como AppManifest e ServiceWorker para adicionar suporte offline Em nativo, com um código C de 30 anos lendo e escrevendo livremente arquivos no disco, isso está resolvido Na web, IndexedDB é um pesadelo real, localStorage não escala e OriginPrivateFileSystem também é limitado Se o usuário não visitar o site nem uma vez por mês, o Safari apaga o estado local do navegador Mesmo com JavaScript, Emscripten e builds wasm, você precisa contornar e driblar APIs web assíncronas para conseguir fazer as coisas A Apple já oferece uma solução completa de local+sync desde 2015 com CoreData + CloudKit Penso que o que mais se aproxima no lado Google é o Firebase
Eu, como desenvolvedor do Replicache e do Zero, já pensei bastante sobre por que focar na web Não sei qual é a resposta, mas minha opinião é esta:
O mobile tem funcionalidades offline mais fortes que a web no nível base Mas a maior parte da produtividade/colaboração acontece na web, e a web é um ambiente mais hostil Há problemas complexos de sincronização entre abas, exclusão de armazenamento etc. Por isso local-first acabou evoluindo com foco principal na web
Os mecanismos de sincronização miram primeiro a web porque essas tecnologias ainda são jovens e recebem muitas atualizações Atualizar app mobile ainda é doloroso, enquanto atualizar web app é muito mais fácil Acho que a capacidade de PWA já está bem razoável iOS PWA já tem push notifications, e fico satisfeito só com web apps que parecem apps mobile
O ponto aqui não é “que recurso você pode usar”, e sim a “alegria” percebida em um produto incrivelmente rápido e responsivo Por isso sigo em direção ao local-first
A parte de colaboração de mecanismos local-first/sync parece pouco explorada Eu estou criando um projeto para substituir o Google Sheets de um amigo do trabalho usando Zero Estamos em uma call do Google Meet abrindo a Sheet em tempo real para manipular dados Antes disso, essa experiência eu nunca pensei em implementar com webapp Já fiz muitas UIs ao vivo com WebSocket, mas é muito complexo refletir e gerenciar os dados recebidos no lugar certo Quando há centenas ou milhares de células, esse caos fica inimaginável Com Zero, é escolha de dados por query e alterações por mutator, e tudo sincroniza na mesma hora para todos Isso torna o próprio desenvolvimento muito divertido
Quando o Google Docs saiu (eu tinha 12 anos), ao ver sincronização em tempo real e o cursor colaborativo, pensei: “a web daqui pra frente vai ser toda colaborativa” Na época, o termo cloud computing estava em alta, e, embora eu estivesse errado, também acreditava que colaboração em tempo real era a definição de computação em nuvem Mas esse futuro não virou realidade Vinte anos se passaram e a maioria dos produtos web ainda é experiência CRUD (esse site inclusive) Vez após vez, parecia que a inovação estava por vir; esperei por Meteor.js, React, e outros, e até hoje não é padrão Ainda fico esperando isso virar mainstream, e me parece que cedo ou tarde vai acontecer Só que é bem mais difícil e não há ferramentas suficientemente maduras para todos os casos
Exemplo de colaboração em tempo real: Discord (essencialmente não muda muito do IRC dos anos 90), Zoom (todas as videoconferências) O fato de o HN ser um app CRUD é, em si, uma vantagem Atualizações em tempo real podem até atrapalhar, o que eu também não quero
A velocidade da luz realmente é um limitador A luz leva 56ms para percorrer metade da circunferência da Terra; o caminho real é mais longo, com transformações e camadas extras (load balancer, proteção DDOS etc.) que deixam tudo mais lento A latência frequentemente é pior hoje do que antes
Eu também senti aquela magia inicial da colaboração em tempo real, mas esse futuro ainda parece estar em algum lugar Ainda assim, estou construindo uma ferramenta de mensagens em tempo real estilo Google Docs (sem input/submit): kraa.io/hackernews
Acredito no futuro de local-first e sync engines A tabela de panorama de frameworks local-first é realmente útil Particularmente, o Triplit.dev foi o que mais gostei, e também dá para combiná-lo com TanStack DB PowerSync e NextGraph também são opções que quero explorar Tenho acompanhado apresentações recentes do NextGraph no vídeo da LocalFirst Conf com interesse: vídeo no YouTube
Fico curioso sobre suporte a migração desses tipos de ferramenta Se, no longo prazo, for preciso dar suporte a clientes sem conexão com servidor, não será um obstáculo grande fazer rollback/rollforward de schema a partir de um estado muito antigo? Depurar bugs no frontend de um único usuário já é difícil, imagina quando precisa considerar o estado do schema; a complexidade deve ser absurda
Lembra o Meteor
Essas abordagens também são coisas do passado
Preciso mesmo experimentar o Triplit; estou curioso se você já testou o InstantDB Eu também estava curioso, mas ainda não testei
Tenho acompanhado a abordagem do Electric por um bom tempo e gosto bastante O Electric deixa a complexidade de escrita do lado do desenvolvedor e da API, o que acaba ficando mais limpo Soluções de sincronização bidirecional geralmente funcionam bem só em apps de lista de tarefas simples Quando entram permissões, evolução de lógica de negócio, migração e crescimento de produto, a robustez fica em dúvida O Electric oferece sincronização de leitura apenas no view, enquanto gravação continua pelos mecanismos existentes de API/REST/RPC, então a adoção em projetos atuais é mais fácil
Fico impressionado com a ausência de couchdb/pouchdb na lista Ainda são ferramentas que funcionam bem