3 pontos por GN⁺ 2025-08-09 | 1 comentários | Compartilhar no WhatsApp
  • 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

 
GN⁺ 2025-08-09
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

    • É adequado para apps de qualquer tamanho (não dá para sincronizar todos os dados para o cliente)
    • É vantajoso para construir telas iniciais rápidas (carregamento rápido de páginas públicas)
    • O controle de permissões também pode ser resolvido apenas com query, sem um sistema separado Por isso a experiência do Zero se aproxima de um banco de dados reativo como Convex ou RethinkDB Mas como ele usa Postgres padrão, também dá para aproveitar a reatividade imediata do mecanismo de sincronização
  • 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:

      • 50kb (gzipado) de tamanho
      • sem necessidade de mudanças de código hoje ou no futuro
      • suporte de sincronização automática sem degradar UX em ambientes offline ou com baixa largura de banda Se algo assim existir, eu usaria imediatamente O problema que os defensores de SSR ignoram é a suposição de que, para a felicidade dos desenvolvedores e de uma boa UX, vale sacrificar a experiência em ambiente offline ou com baixa largura de banda Alguns até usam isso como argumento para melhorias futuras de rede Na prática, requisitos de baixa largura de banda são sempre um diferencial importante Eu acho especialmente importante em regiões com internet lenta, em áreas remotas, ou em contextos como Comcast
    • 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:

    1. Armazenar dados localmente torna a resposta muito rápida
    2. Exportar/importar toda a base de dados é muito simples
    3. A lógica de servidor é mais leve, com menos overhead de desempenho e menos complexidade de desenvolvimento, concentrando toda a lógica de negócio no cliente
    4. O desenvolvimento de funcionalidades fica mais simples (basta escrever lógica local) Também há desvantagens:
    5. É adequado apenas para dados textuais; para imagens ou arquivos grandes, recomendo serviços separados como object storage
    6. O código de sincronização exige muita cautela no desenvolvimento (bugs podem causar problemas graves)
    7. Criptografia ponta a ponta (E2E) e recursos de colaboração são relativamente complexos A composição técnica é:
    8. Baseada na biblioteca open source Loro CRDT, então eu me concentro só na lógica de negócio
    9. Fluxo de processamento de dados: A ação do usuário atualiza o modelo CRDT, exporta o estado em JSON e atualiza a UI Ao mesmo tempo, os dados são salvos no banco local e sincronizados com o servidor
    10. A camada de armazenamento local foi abstraída com três interfaces comuns: list/save/read, usando IndexedDB (web), filesystem (Electron) e Capacitor Filesystem (iOS/Android) conforme a plataforma
    11. Implementação de criptografia E2E e sincronização incremental O servidor calcula a diferença com base na versão cliente/servidor, criptografa com AES e faz upload O servidor mantém a versão base + o patch incremental Quando a quantidade acumulada de patches cresce, ele faz upload de todo o banco criptografado como uma nova versão base para manter os patches seguintes leves Se você se interessar pelo projeto, confira o repositório Hamsterbase Tasks
  • 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

    • O Jazz é realmente incrível O DX também é incomparável Quando eu usei, só suportava criptografia baseada em passkey e não funcionava bem no Windows, então, na prática, era impossível de usar Penso que em breve terá suporte também para autenticação tradicional E, sobretudo, a criptografia E2E é ótima e a experiência é muito divertida
  • 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:

      • Vantagens da web: loop de feedback rápido, ausência de gatekeepers etc. No geral, prefiro web
      • A maior parte dos softwares de desktop/produtividade acontece na web, e eu quero alta reatividade em apps de produtividade Mas todo mundo está indo para client-server e aí a latência aparece em vários pontos; quero mudar isso
      • Esse tipo de sistema precisa de lógica de banco de dados distribuído complexa no cliente Aí vem o dilema de escolha de linguagem Em tese, portar C++ ou Rust para wasm parece bom, mas, na prática, JS é o mais universal e com melhor acesso tanto na web quanto no mobile (RN etc.)
      • Na prática, apps de produtividade complexos têm sistema cliente complexo e, por isso, a maioria usa tecnologias web implementando tudo em uma linguagem (JS)
    • 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

    • “Funciona bem” não é suficiente A maioria das soluções local-first eventualmente perde dados em algum conflito Diferente disso, couchdb/pouchdb deixam a autoridade final de resolução de conflito com o desenvolvedor O pessoal do ecossistema CRDT parece priorizar conveniência de desenvolvimento mais que segurança de dados É um trade-off que cada um escolhe conforme gosto e objetivo Mas eu não entendo por que isso sobre segurança de dados nem sempre é citado