Aller au contenu principal
Tous les articles
IA

Local-first + CRDT : la nouvelle architecture SaaS 2026

IndexedDB, Yjs, sync serveur, matérialisation Postgres : le local-first devient l'architecture SaaS 2026 pour les apps à forte écriture.

Équipe Engineering VALRY LABS14 juillet 202612 min de lecture

La thèse local-first : la donnée vit d'abord sur le device

Le modèle SaaS dominant depuis quinze ans repose sur un postulat unique : le serveur est la source de vérité, le client n'est qu'une vitrine. Chaque lecture part du navigateur, traverse le réseau, frappe une API, interroge une base, puis revient. Chaque écriture suit le chemin inverse, bloquant l'utilisateur jusqu'à la réponse. Sur le papier, c'est simple. Sur un train, en avion, ou sur un réseau capricieux, c'est une expérience catastrophique.

La thèse local-first inverse la polarité. La donnée vit d'abord sur l'appareil de l'utilisateur, dans une base locale persistante (IndexedDB, SQLite WASM, OPFS). Le cloud devient un pair de réplication, pas un oracle. Les lectures deviennent instantanées : pas de réseau, pas de latence, pas d'état de chargement. Les écritures deviennent instantanées : on écrit localement, on met en file, on synchronise quand le réseau revient. L'application fonctionne pleinement hors ligne. La différence d'expérience avec un SaaS toujours en ligne est visible dès la première seconde.

Les bénéfices ne sont pas qu'ergonomiques. La disponibilité augmente (pas de downtime côté utilisateur), la coût d'infrastructure baisse (moins de lectures serveur, moins de charges mises en cache), la résilience s'améliore (un attentat sur la fibre ne stoppe plus le travail). Pour les applications à forte écriture — notes, tâches, formulaires, briefs, CRM terrain — c'est un changement de catégorie. Pour les dashboards analytiques en lecture seule, c'est inutile. La première leçon, c'est que le local-first n'est pas un dogme universel, c'est un outil pour un certain profil d'applications.

Restait un obstacle mathématique : comment synchroniser plusieurs éditeurs sans serveur central séquentialisant les opérations ? C'est là que les CRDT entrent en scène.

CRDT : la mathématique qui rend la fusion automatique possible

Un CRDT (Conflict-free Replicated Data Type) est une structure de données qui converge mathématiquement, quel que soit l'ordre d'arrivée des réplicas. Concrètement : deux utilisateurs modifient le même paragraphe en même temps, hors ligne, puis resynchronisent — le système produit un état final cohérent sans intervention humaine, sans dernière-écriture-gagne, sans merge manuel. C'est la brique qui rend le local-first praticable.

Deux bibliothèques dominent l'écosystème en 2026. Yjs, écrit par Kevin Jahns, s'est imposée comme le choix de production pour la majorité des éditeurs collaboratifs. Son algorithme de référence, YATA, gère les opérations concurrentes sur du texte riche avec une complexité maîtrisée. Automerge, initié par Martin Kleppmann (auteur de « Designing Data-Intensive Applications »), mise sur un modèle JSON-like plus générique et sur l'algorithme Mark-On-Doc pour la fusion de texte. Pour les documents structurés, Automerge est souvent plus naturel ; pour les éditeurs de texte, Yjs reste plus économe en mémoire.

La propriété clé, commune aux deux, est la fusion déterministe : étant donné deux états avec leur historique, le résultat de la fusion est identique quel que soit le chemin parcouru. Cela déplace le problème : on ne demande plus à un serveur central d'arbitrer les conflits, on délègue l'arbitrage à la structure de données elle-même. Le serveur devient un relai d'updates, pas un décideur. C'est ce qui permet à Linear d'ouvrir un ticket hors ligne, à Notion de fusionner deux paragraphes édités simultanément, à Figma de proposer du branching sur un design multijoueur.

La contrepartie : les CRDT ont un coût. En mémoire d'abord (l'historique des opérations peut grossir), en complexité de schéma ensuite (une map CRDT n'est pas une map native, un tableau CRDT se comporte différemment d'un array). C'est un modèle qui demande de la discipline, pas une bibliothèque magique.

La vague production 2025-2026 : le local-first quitte la recherche

Pendant longtemps, les CRDT étaient un sujet académique. Les premières implémentations étaient lentes, gourmandes, difficiles à intégrer. Le travail de Kleppmann, Jahns, et l'équipe Ink & Switch (le collectif local-first) a fait avancer la théorie ; le travail des équipes Notion, Linear, Figma, Reflect et Roam a fait entrer ces idées en production à grande échelle. En 2026, les patterns sont assez matures pour des équipes qui ne sont pas des GAFAM.

Notion a déployé son moteur multijoueur sur l'ensemble de ses blocks, rendant l'édition simultanée fluide même sur des pages de plusieurs mégaoctets. Linear a poussé l'offline mode jusqu'à permettre la création, l'édition et la réorganisation de tickets complètement hors ligne, avec synchronisation au retour réseau. Figma a industrialisé le branching à la Git sur des documents graphiques multijoueurs. Reflect et Roam ont bâti leur produit entirement sur des CRDT, sans couche serveur traditionnelle.

Ce que ces déploiements démontrent, ce n'est pas que le local-first est facile — ces équipes ont investis des années-homme sur leur sync engine. C'est que la barrière d'entrée a baissé. Yjs et Automerge sont stables, documentés, performants. y-sweet, hocuspocus, y-websocket fournissent des serveurs de sync prêts à l'emploi. y-indexeddb et y-protocols gèrent la persistance locale et la matérialisation serveur. Pour une équipe qui n'a pas les moyens de Notion, l'assemblage est désormais réaliste.

Le seuil psychologique a été franchi : le local-first n'est plus une expérience de laboratoire, c'est un patron d'architecture que l'on voit en production chez des éditeurs de toute taille. La question n'est plus « est-ce possible ? » mais « est-ce pertinent pour ce produit ? ».

La stack : IndexedDB, sync engine, serveur, matérialisation

Une architecture local-first typique en 2026 s'organise en quatre couches. En bas, la persistance locale : IndexedDB (ou OPFS pour les performances brutes) stocke l'état CRDT. La bibliothèque y-indexeddb connecte un document Yjs à IndexedDB, ce qui permet à l'application de relire l'état instantanément au démarrage, sans récupération réseau. C'est cette couche qui délivre la promesse « ça marche tout de suite, même offline ».

Au-dessus, le sync engine. Côté client, l'API Yjs (`awareness`, `updateV2`, `applyUpdate`) gère la production et l'application des mises à jour. Côté serveur, deux options courantes : y-websocket, léger et suffisant pour un MVP, ou y-sweet (maintenu par Drifting in Space), plus robuste, avec gestion de rooms, persistance, et scalabilité horizontale. Hocuspocus, par les auteurs de Tiptap, est une alternative solide quand l'éditeur de texte est au coeur du produit.

La troisième couche est optionnelle mais devient vite indispensable : la matérialisation serveur. Un document CRDT est une structure opérationnelle, pas une structure queryable. Pour implémenter une recherche full-text, un dashboard analytique, ou une API publique, il faut une projection du CRDT vers un modèle relationnel. La famille y-protocols (et notamment y-protocols/awareness, y-protocols/sync) offre des outils pour cela ; en pratique, on écrit un subscriber qui écoute les updates, décode le document en JSON, et écrit dans Postgres. C'est ce que fait y-sweet avec son backend Rust, et c'est le pattern que nous appliquons systématiquement.

La quatrième couche, enfin, est l'authentification et la sécurité. y-sweet et hocuspocus s'intègrent à Supabase Auth via des tokens JWT signés en short-lived. La règle : jamais de clé serveur côté client, toujours une vérification d'identité au niveau du serveur de sync, et une politique par document (qui peut lire, qui peut écrire) évaluée avant l'ouverture du WebSocket.

Les parties difficiles : migrations, queries, partial sync, sécurité

Sur le papier, le local-first s'enchaîne en quatre couches élégantes. En production, quatre catégories de problèmes reviennent systématiquement, et il vaut mieux les connaître avant de s'engager.

Première catégorie : les migrations de schéma sur CRDT. Une base relationnelle se migre avec un script SQL atomique. Un document CRDT, lui, est un historique d'opérations : modifier la forme attendue demande de retraiter cet historique, ou de maintenir des versions côté côté client. Yjs et Automerge proposent des stratégies, mais aucune n'est triviale. En pratique, on évite les breaking changes de schéma, on versionne explicitement les documents, et on prévoit un mécanisme de lazy migration au décodage. C'est moins élégant qu'un `ALTER TABLE`, et il faut l'accepter.

Deuxième catégorie : les queries côté serveur. On ne peut pas faire un `SELECT WHERE` sur un CRDT sérialisé en binaire. Toute interrogation analytique, tout endpoint REST publique, toute recherche full-text nécessite une vue matérialisée. Cela veut dire maintenir une boucle update → decode → write Postgres, gérer la cohérence éventuelle (la vue peut être en retard de quelques centaines de millisecondes), et accepter une complexité opérationnelle supplémentaire. C'est le prix à payer pour bénéficier à la fois de l'offline-first et d'une API serveur classique.

Troisième catégorie : le partial sync, c'est-à-dire la réplication par collection. Un utilisateur qui ouvre l'app ne veut pas télécharger l'intégralité de son workspace sur son téléphone. Il veut ses projets récents, pas l'archive de 2022. Implémenter une réplication par sous-collection demande de découper les documents, de gérer des souscriptions par room, et de maintenir un état de synchronisation par périphérique. y-sweet facilite cela avec son système de broad- et sub-documents, mais cela reste un domaine où l'on passe du temps.

Quatrième catégorie : la sécurité, notamment le rêve du chiffrement de bout en bout. Le local-first se prête naturellement à l'E2EE : la clé de chiffrement vit sur les appareils, le serveur de sync ne voit que des updates opaques. C'est élégant, mais cela entre en conflit direct avec la recherche côté serveur : on ne peut pas indexer du contenu chiffré dans Postgres. Il faut choisir entre E2EE strict (recherche uniquement côté client, sur les documents téléchargés) et serveur déchiffré (recherche centralisée, mais le serveur voit la donnée). Ce compromis est structurel et on ne l'évite pas par de la configuration.

Le patron SaaS 2026 : online-first, local-first, ou hybride ?

Avec ces éléments en tête, la décision architecturale se cristallise. Trois profils émergent en 2026, et le choix dépend moins de la mode que d'une analyse honnête du profil de charge de l'application.

L'online-first reste pertinent pour les applications read-heavy : dashboards analytiques, rapports consolidés, interfaces d'administration, plateformes où la donnée change peu mais doit être lue par beaucoup. Mettre un CRDT derrière un dashboard Metabase n'apporte rien, sinon de la complexité. La règle : si l'utilisateur consulte plus qu'il n'écrit, et s'il travaille toujours connecté, rester sur une architecture serveur classique est le bon choix. Le coût d'ingénierie est minimal, l'outillage est mature, le déploiement est simple.

Le local-first s'impose pour les applications write-heavy : éditeurs de notes, gestionnaires de tâches, CRM terrain, outils de prise de brief, formulaires longue durée, outils de design collaboratif. Partout où l'utilisateur crée, modifie, annule, reprend — et où la latence d'écriture tue l'expérience — le local-first transforme le produit. Sur ces profils, le coût d'ingénierie est payé par le gain UX, et la différenciation concurrentielle est réelle. Un éditeur de notes qui répond en 0 ms bat un éditeur qui répond en 800 ms, peu importe les features.

Le modèle hybride s'applique aux applications collaboratives complexes à la Figma : partie lecture (catalogue de composants, librairies partagées) en serveur classique, partie édition collaborative (le canvas, les commentaires) en CRDT local-first. C'est le modèle le plus puissant mais aussi le plus exigeant — il faut maintenir les deux mondes et leur interface. Pour la plupart des produits, un choix clair online-first ou local-first est plus sain qu'un mélange tiède. La matrice de décision se résume à trois questions : l'utilisateur écrit-il souvent ? Travaille-t-il hors ligne ? La collaboration temps réel est-elle cœur ou périphérique ? À partir des réponses, l'architecture se décide sans ambiguïté.

À retenir

Points clés.

  • Local-first = la donnée vit d'abord sur le device, le cloud devient un pair de sync, pas un oracle.
  • CRDT (Yjs / Automerge) rend la fusion automatique : everyone can edit anything, les conflits convergent mathématiquement.
  • Stack 2026 : IndexedDB + Yjs + y-sweet (ou hocuspocus) + matérialisation Postgres via y-protocols.
  • Parties difficiles : migrations de schéma CRDT, queries côté serveur, partial sync par collection, compromis E2EE vs recherche.
  • Décision 2026 : online-first pour read-heavy (dashboards), local-first pour write-heavy (notes, tasks), hybride pour collaboratif Figma-like.
  • Notion, Linear, Figma, Reflect ont démocratisé les patterns : le local-first n'est plus un sujet de laboratoire.
Local-firstCRDTYjsAutomergeOffline-firstSaaS