Intermédiaire·4 min·9 juin 2026

Pourquoi ton doc collaboratif perd tes données quand tu es hors ligne

🎧 Résumé audio0:00 / 0:00
Deux utilisateurs hors ligne éditent la même note. Quand ils se resynchronisent, l'un des deux voit ses modifs disparaître. Loro a trouvé la solution.
Pourquoi ton doc collaboratif perd tes données quand tu es hors ligne

Pourquoi ça compte pour toi

Si tu construis un outil collaboratif (doc partagé, tableau blanc, base de données temps réel), tu dois comprendre ce bug silencieux : il n'y a pas d'erreur, les données ne sont pas perdues, mais l'utilisateur ne les voit pas. Ça arrive avec Yjs, Automerge, Loro — tous les CRDTs du marché. Loro propose maintenant une réponse : les Mergeable Containers.

Ce qu'il faut retenir

  • 1.Le problème : quand deux pairs créent en même temps un conteneur enfant (Text, List, Map) au même endroit, chacun génère un ID différent → conflit non résolvable
  • 2.La cause : l'identité du conteneur enfant dépend de l'opération qui l'a créé, pas de sa position logique dans la structure
  • 3.La solution : les Mergeable Containers utilisent un ID déterministe basé sur la position logique (parent + clé + type) au lieu de l'opération

Tu galères avec le jargon ?

Lis la version réécrite en mode débutant — toutes les idées, sans le jargon.

Le bug silencieux des CRDTs collaboratifs

Imagine : deux utilisateurs travaillent sur une note partagée. Tous les deux sont hors ligne. L'utilisateur A ajoute du contenu, l'utilisateur B ajoute du contenu ailleurs. Ils se reconnectent, la synchronisation se termine.

Résultat ? Les modifications de l'un d'eux disparaissent de l'écran.

Mais attends — il n'y a pas d'erreur. Les données existent toujours dans l'historique. Pourtant, le client ne peut en afficher qu'une seule version. L'autre conteneur créé en concurrence existe bel et bien, mais il est invisible. Du point de vue de l'appli, c'est une perte de données.

Ce problème hante Loro, Yjs et Automerge depuis des années. La communauté l'a rapporté maintes fois.

Pourquoi ça arrive

Les CRDTs gèrent bien les éditions concurrentes — plusieurs utilisateurs qui tapent au même endroit d'un texte, ou qui ajoutent dans une liste en même temps. Aucun problème.

Mais le vrai souci arrive avant : quand deux pairs créent en même temps le premier conteneur enfant à la même clé. Par exemple, tu crées une note vide. L'utilisateur A ajoute un champ content (un Text). L'utilisateur B ajoute le même champ content (un autre Text). Tous deux hors ligne.

Quand ils se synchronisent, chaque création a généré un ID d'opération différent. Donc deux Text différentes. La règle de conflit du Map décide laquelle reste visible. L'autre ? Invisible, mais pas supprimée.

La racine du problème : l'identité d'un conteneur enfant inclut l'opération qui l'a créé. Deux créations concurrentes = deux identités différentes.

Comment les root containers évitent le problème

Dans Loro et Yjs, les conteneurs racine (top-level) s'accèdent par nom : doc.getMap('settings') ou doc.getText('title'). Ce nom est stable. Il ne dépend pas de qui l'a créé ni de quelle opération. Tant que deux pairs accèdent au même nom, ils parlent du même objet.

Pour les conteneurs enfants, avant les Mergeable Containers, il n'y avait qu'un contournement : pré-initialiser tous les enfants dès la création du parent. "Chaque note a un Text ? Crée-le tout de suite, pas plus tard."

Mais ça ne tient pas à l'échelle si tu ne connais pas à l'avance tous les enfants (migrations de schéma, conteneurs créés par date, index dynamiques).

La solution : déterminisme à la place du hasard

Les Mergeable Containers donnent aux enfants sélectionnés ce que les root containers ont naturellement : une identité stable basée sur la position logique, pas l'opération de création.

Concrètement :

  • Au lieu de : map.get_text('content') — crée un Text à la demande, de façon aléatoire
  • Tu fais : map.get_mergeable_text('content') — crée un Text basé sur (parent ID + clé + type)

Deux pairs, même clé, même type ? Ils génèrent le même ID. Pas de conflit caché.

Deux couches de représentation

Loro sépare deux niveaux :

  1. Le CID synthétique (ID du conteneur) : dérivé du parent, de la clé et du type. Tous les pairs calculent la même chose sans avoir besoin de l'opération d'origine.

  2. Le marqueur Map : contrôle quel enfant fusionnable est actuellement visible. Si un pair crée un Text fusionnable et un autre un Map fusionnable à la même clé ? Le Map l'emporte selon la règle de conflit habituelle. Mais l'état du Text reste préservé en arrière-plan — tu peux basculer d'un type à l'autre sans perdre les données.

Qui en profite vraiment

  • Les applis organisées par date : "Une List par jour"
  • Les migrations de schéma : "On ajoute progressivement une Map settings au doc"
  • Les sous-documents dynamiques : "Un conteneur par utilisateur ou par entité"
  • Les compteurs de révision, les maps de configuration avec clés découvertes au fil du temps

Partout où tu ne peux pas pré-initialiser, les Mergeable Containers règlent le problème.

Et concrètement pour toi ?

Choisis ton profil — la lecture de l'article change selon qui tu es.

🔭 Curieux

Pour toi, ce bug révèle que la vraie complexité du web collaboratif ne se voit pas : deux utilisateurs éditent, ça semble marcher, mais une modif disparaît silencieusement. Loro propose une solution mathématique plus propre. C'est un exemple concret de comment l'IA et les outils collaboratifs s'évoluent sous le capot.

Newsletters Noésis

3 minutes d'IA dans ta boîte mail, chaque matin.

Rejoins les francophones qui comprennent, essaient et progressent avec l'IA. Choisis ce que tu veux recevoir. Désabonnement en 1 clic.