Mensonges sur l’édition collaborative, 1re partie : algorithmes pour l’édition hors ligne
(moment.dev)- D’après l’évaluation menée par Moment pour son éditeur de texte principal, les algorithmes de la famille CRDT et OT peuvent produire, lors de conflits directs hors ligne, des résultats de fusion que les utilisateurs percevraient comme une corruption de données
- Même dans un cas simple où Alice supprime toute une phrase et Bob remplace
ColorparColour, le document final peut ne contenir que la lettreu, soit un résultat non voulu par un humain - Dans les cas d’usage de Moment, environ 20 à 30 % des conflits directs testés ont donné des résultats inacceptables, et leur fréquence comme les réactions négatives les rendent difficiles à défendre comme expérience produit
- Yjs, ShareJS et Peritext mettent en avant l’édition hors ligne, la collaboration avec de longs délais ou la fusion automatique de copies indépendantes, mais les algorithmes ne peuvent pas connaître l’intention ni l’ordre voulu par les utilisateurs et s’appuient donc sur des heuristiques
- L’édition collaborative hors ligne relève moins d’un pur problème d’algorithme que d’un problème UI/UX : il faut une expérience de fusion lisible et jugeable par l’humain, à la manière de l’interface de fusion de git ou de la collaborative history d’Ink & Switch
Les limites révélées par l’évaluation de l’édition collaborative chez Moment
- Début 2024, Moment a commencé à étudier un système d’édition collaborative pour son éditeur de texte principal
- Plusieurs algorithmes affirment résoudre non seulement l’édition simultanée en ligne, mais aussi les cas hors ligne, où un utilisateur modifie un document hors ligne pendant une durée illimitée avant de revenir en ligne et de fusionner automatiquement ses changements
- Après avoir lu les articles et présentations, l’équipe s’attendait d’abord à ce que la communauté de l’édition collaborative soit parvenue à la « bonne réponse » pour l’ensemble du problème
- Lors de l’évaluation, les algorithmes de type CRDT et OT ont résolu des conflits d’édition directs de façon peu intuitive, et les utilisateurs ont perçu ces résultats comme une corruption de données
- Comme l’édition hors ligne augmente fortement la probabilité de conflits directs, ces seuls algorithmes ne suffisaient pas pour créer l’expérience d’édition hors ligne voulue par Moment
Un cas simple de conflit entre suppression et correction orthographique
- Alice et Bob modifient tous deux le même document en étant hors ligne
- Le document contient le texte
The Color of Pomegranates - Bob change l’orthographe de
Coloren anglais britannique,Colour, tandis qu’Alice supprime tout le texte - Quand les deux reviennent plus tard en ligne, les deux modifications entrent en conflit, et le système doit les concilier sans savoir laquelle est venue en premier
- Dans ce cas, Alice et Bob se retrouvent finalement avec un document ne contenant que la lettre
u- Ce n’est ni une phrase valide, ni un mot valide
- Les utilisateurs interprètent ce type de résultat comme une corruption de leurs données par Moment
Les résultats étranges des conflits directs ne sont pas rares
- Ce conflit est un conflit d’édition trivial et direct, mais il se situe dans le périmètre que des algorithmes populaires d’édition collaborative sont censés prendre en charge
- Le résultat réel était un document qu’aucun humain n’aurait écrit de lui-même, et il a été interprété comme une corruption de données par le produit Moment
- Cela varie selon les cas d’usage, mais parmi les conflits directs testés par Moment, environ 20 à 30 % ont produit des résultats difficiles à accepter dans un scénario d’édition hors ligne
- En combinant les retours négatifs et leur fréquence, l’équipe a estimé qu’il serait difficile d’expliquer et de défendre ces résultats auprès des utilisateurs
Le support de l’édition hors ligne revendiqué par les outils
- Au départ, face à ces résultats, l’équipe a pensé avoir mal compris ce que ces outils promettaient
- Pourtant, la description de chaque projet peut se lire comme incluant ce scénario dans son périmètre de support
- Yjs indique explicitement dans son README prendre en charge l’
offline editing - ShareJS explique permettre la collaboration avec n’importe quelle latence, de zéro jusqu’à l’équivalent de longues vacances
- Peritext explique que les utilisateurs peuvent modifier des copies indépendantes d’un document, puis les fusionner automatiquement d’une manière qui préserve autant que possible leur intention
- Yjs indique explicitement dans son README prendre en charge l’
- Moment cherche toujours une solution à ce problème, mais estime difficile de concilier la fréquence et la nature des erreurs observées avec ces affirmations
Les limites fondamentales des algorithmes sans arbitrage
- À mesure que l’évaluation avançait, l’équipe a conclu que ces algorithmes ne produisaient pas le comportement recherché par Moment
- La question restante était de savoir s’il s’agissait d’un problème corrigeable par des contributions, ou d’une limite fondamentale de l’algorithme
- Trois raisons poussent à y voir un problème fondamental
- L’algorithme ne connaît pas les intentions d’Alice et Bob, et ne peut pas leur demander par e-mail ce qu’ils voulaient ni soumettre le cas à une revue comme dans l’interface de Pull Request de GitHub
- Il doit recevoir la proposition de suppression complète d’Alice et la proposition de correction orthographique de Bob, puis décider du résultat au moyen d’heuristiques
- Il fonctionne au niveau des caractères et n’offre que des garanties très faibles sur la sortie
- Alice et Bob auraient pu prendre des décisions d’édition différentes s’ils avaient su ce que l’autre faisait
- Ces algorithmes sont aussi liés au problème de l’impossibilité de respecter un ordre causal
L’édition hors ligne est plutôt un problème UI/UX
- Moment espérait qu’en implémentant un algorithme sophistiqué, elle pourrait obtenir en bonus une véritable prise en charge de l’édition hors ligne, mais l’évaluation a rendu cette lecture difficile à soutenir
- Il était légitime que les utilisateurs voient les résultats de l’algorithme comme une corruption de données ; les résultats étranges font partie de l’algorithme lui-même et se produisent assez souvent pour devenir un vrai problème
- Une autre interprétation est qu’il faut consacrer des ressources UI/UX importantes à l’édition collaborative
- L’algorithme ne peut pas résoudre entièrement le problème, mais il peut constituer une partie de la solution
Interface de fusion git et pistes de recherche
- Il existe déjà une interface de fusion de documents largement adoptée :
git - La question de recherche consiste plutôt à savoir jusqu’où rendre cette expérience plus accessible, plus compréhensible et plus automatisable
- Vers 2009, les discussions portaient beaucoup sur les algorithmes utilisés par git pour fusionner automatiquement les changements
- git a adopté le Myers O(ND) diff algorithm, un algorithme principalement utilisé par les biologistes pour l’analyse de séquences de type BLAST
- Bram Cohen, jugeant les résultats de diff peu intuitifs, a créé le patience diff algorithm, qui a été adopté par bzr, un concurrent de git aujourd’hui abandonné
- À l’époque, la discussion se concentrait sur la production de diffs lisibles par les humains ; aujourd’hui, elle porte davantage sur la capacité d’un algorithme à obtenir ce résultat sans intervention humaine
- Des recherches comme la collaborative history d’Ink & Switch abordent l’édition collaborative comme un problème UI/UX
1 commentaires
Commentaires sur Hacker News
Je suis l’auteur d’Eg-walker et de ShareJS. Le texte peut sembler prendre un ton opposé à mon travail, mais en réalité je suis entièrement d’accord et je dis la même chose sur HN depuis des années.
Les outils actuels de collaboration en temps réel fonctionnent bien quand tout le monde édite ensemble en ligne, mais si des utilisateurs modifient hors ligne ou sur des branches de longue durée, il faut des options d’indication de conflit et de relecture manuelle au moment de la fusion. C’est encore plus vrai pour le code.
Heureusement, des algorithmes comme egwalker conservent la trace de toutes les modifications caractère par caractère de tous les utilisateurs ainsi que de l’ordre causal, c’est-à-dire un ordre des changements comparable à un Git DAG, et disposent donc de bien plus d’informations que Git. On devrait donc pouvoir créer un CRDT capable de détecter et d’indiquer l’étendue des conflits lors de la fusion de branches, puis laisser l’utilisateur les résoudre manuellement.
Cela semble être un problème intéressant du point de vue algorithmique, mais tout à fait soluble, et étrangement presque personne ne semble encore s’y être attaqué dans l’édition de texte. Si quelqu’un veut apporter une contribution originale et utile dans ce domaine, c’est une pièce importante manquante de l’écosystème CRDT, et j’aimerais que quelqu’un s’en empare.
[1] Plus bas dans ce commentaire : https://news.ycombinator.com/item?id=19889174
Le prochain défi amusant, c’est quand des conflits apparaissent autour de la résolution des conflits elle-même.
Évaluer ce genre de systèmes est déjà en soi un problème technique suffisamment difficile, donc je pense que beaucoup d’équipes vont souffrir. Elles méritent donc des conseils pratiques, et nous avons le sentiment que nous aurions aimé le savoir plus tôt.
La partie difficile consiste à indiquer qu’un conflit a été résolu. Cela pourrait être aussi simple que d’ajouter un champ au CRDT, mais dans ce cas je ne sais pas s’il faudrait encore considérer cela comme une solution algorithmique.
[1] https://josephg.com/blog/crdts-go-brrr/
Pour l’édition de code, on s’attend en plus à ce que le résultat de la fusion soit un programme valide, ce qui ouvre un terrier de lapin bien plus profond. Il y avait chez JetBrains un projet qui essayait de résoudre cela avec une fusion basée sur l’AST, mais j’ai entendu dire qu’après être allé beaucoup plus loin, ils avaient conclu que cela n’en valait pas la peine.
Dans les documents écrits par des humains, la seule forme d’« édition hors ligne » que j’autorise est l’ajout de commentaires. Pas d’édition, et pas de fusion automatique.
Pour l’« édition hors ligne » du code source, qui se prête à l’automatisation, on utilise Git, et Git ne prétend pas résoudre les fusions : il montre seulement les révisions. La fusion est un travail supervisé par des humains ou effectué par une automatisation spécialisée qui propose sa meilleure estimation, et sa réussite doit toujours être vérifiée par revue et par tests.
Les algorithmes de fusion mécanique peuvent être plus ou moins performants selon le type de conflit, mais au final aucun CRDT ne peut déterminer si le texte fusionné correspond à ce que les utilisateurs voulaient réellement dire.
L’article Upwelling détaille davantage une distinction qu’il appelle, dans l’écriture, les conflits sémantiques et les conflits grammaticaux : https://inkandswitch.com/upwelling/
J’ai l’impression qu’une collaboration sérieuse finit aussi par devenir un problème de relecture de documents. C’est particulièrement vrai en journalisme ou dans l’édition scientifique, alors que pour des comptes rendus de réunion on peut souvent l’ignorer.
Je voulais aussi faire un lien vers Upwelling, mais je ne me souvenais plus du nom et, faute de temps avant la deadline, je l’ai remplacé par un autre lien.
Un autre côté sombre des implémentations de CRDT, c’est la charge d’infrastructure. J’ai déjà écrit en profondeur là-dessus autrefois[0], et j’ai été heureux de découvrir que Supabase était arrivé à la même conclusion que mes résultats empiriques dans son billet d’il y a quelques années sur une extension CRDT pour Postgres[1].
Si vous allez utiliser des CRDT, il vaut mieux prendre quelque chose comme Redis, ou bien, même si l’usage mémoire fait mal rien que d’y penser, une base sur MyRocks[2] ou RocksDB/LevelDB. Quoi que vous fassiez, il ne faut pas mettre un SGBDR, surtout Postgres, comme backend.
[0]: https://news.ycombinator.com/item?id=40834759
[1]: https://supabase.com/blog/postgres-crdt
[2]: http://myrocks.io
Cette observation de l’article est juste. Les CRDT sont un excellent modèle formel pour les structures de données distribuées, mais l’idée qu’ils doivent résoudre automatiquement tous les conflits — l’idée même de types de données répliqués sans conflit — m’a toujours mis mal à l’aise.
Comme le montre l’article, cela me semble être une tentative sans espoir. Ce qu’il faut, c’est une représentation structurelle correcte qui permette de partager les conflits et de les résoudre de manière collaborative, en redonnant le contrôle aux utilisateurs et en soutenant le processus de résolution. L’un de mes articles préférés, “Turning Conflicts into Collaboration” [1], traite cette idée de façon convaincante.
Dans le cadre de mes travaux de doctorat en cours, j’ai développé “Lazy Merging: From a Potential of Universes to a Universe of Potentials” [2], un modèle formel de représentation structurelle des conflits fondé sur la théorie des treillis. Par coïncidence, c’est aussi un CRDT, mais il n’essaie pas de résoudre automatiquement les conflits et les représente à l’intérieur du document collaboratif. En adoptant une approche mathématique, j’ai pu aboutir à un modèle conceptuel simple qui garantit des propriétés fortes telles que l’exhaustivité, la minimalité et l’unicité de la fusion, même après des conflits antérieurs fusionnés de manière répétée, et le calcul de fusion est lui aussi très simple.
[1] https://doi.org/10.1007/s10606-012-9172-4
[2] https://doi.org/10.14279/tuj.eceasst.82.1226
Le conflit est une notion complexe, et “conflict-free”, bien que techniquement correct pour décrire le résultat, peut prêter à confusion, comme on le voit dans cet article et ci-dessus.
La commutativité est la propriété selon laquelle Bob peut appliquer les modifications dans l’ordre [Bob, Alice] et Alice dans l’ordre [Alice, Bob], tout en aboutissant tous deux au même document. Cela ne signifie pas qu’au niveau d’abstraction supérieur, le document soit “sans conflit” dans un sens sémantiquement pertinent.
Je pense qu’en général, l’idée selon laquelle plusieurs acteurs différents peuvent disposer simultanément d’une autorité sur le même fragment de données sans coordination en temps réel est insoluble. C’est une leçon déjà apprise en systèmes distribués, et cela apparaît clairement dans cet article quand on pense à l’édition distribuée de documents.
Le même principe semble s’appliquer à la double commande dans un cockpit d’avion, à l’éducation des enfants, et à d’autres exemples très différents qui viennent à l’esprit.
Et il est même assez drôle d’appeler ça “résoudre”, parce qu’à l’heure actuelle, une grande partie de la planète semble penser que la sortie chaotique d’un LLM peut quasiment déterminer le résultat final du calcul.
Les CRDT et OT, algorithmes couramment utilisés pour l’édition de texte collaborative, imposent des contraintes algébriques strictes sur ce que font les opérations d’édition et sur la façon dont elles interagissent.
Donc même si le serveur est suffisamment intelligent pour traiter l’exemple “Colour” d’une manière raisonnable du point de vue UX, concevoir le CRDT/OT correspondant pour une édition optimiste côté client est très difficile.
On peut contourner cela en n’utilisant pas de CRDT/OT. Par exemple, le serveur peut traiter les opérations dans l’ordre de réception et appliquer la logique UX souhaitée, tandis que le client utilise une stratégie de rebase/prédiction pour permettre l’édition optimiste par-dessus. Voir : https://doc.replicache.dev/concepts/how-it-works
L’appliquer à l’édition de texte pose aussi des difficultés, mais elles sont distinctes des problèmes CRDT/OT discutés ici.
Je pense que cela vient du fait que les notions mathématique, causale et entropique de conflit sont mélangées au conflit sémantique. J’ai moi-même déjà commis l’erreur inverse par le passé, et quelqu’un m’a alors fermement fait remarquer que je ne savais pas de quoi je parlais.
Dès qu’on commence à considérer des arbres, cela devient bien plus sale. Par exemple, yJS fonctionne sur des documents JSON. Si l’interface n’affiche que les niveaux superficiels et laisse les niveaux profonds repliés, l’utilisateur peut ne jamais voir une modification supprimée.
La catégorie de CRDT qui préserve les conflits — si je me souviens bien, celle où un registre peut contenir plusieurs valeurs — me semble la plus prometteuse. Il faut présenter de tels conflits à l’utilisateur, et on pourrait même les montrer de manière entièrement visuelle. Permettre de parcourir l’historique me semble aussi une alternative pratique pour aider l’utilisateur à comprendre comment quelque chose d’étrange s’est produit, ou comment sa propre modification a disparu.
Je me souviens que Torvalds lui-même était assez pessimiste sur ce qu’on pouvait accomplir avec la fusion automatique. Et ce jugement était juste
Il disait que Git rejetait l’idée qu’un système de contrôle de version puisse, ou doive, « résoudre le problème des fusions » grâce à un algorithme suffisamment intelligent qui ferait automatiquement ce qu’il faut
Je suis d’accord pour dire que l’édition hors ligne est un problème d’UI/UX. La cause plus profonde, c’est l’habitude qu’a l’industrie informatique de répéter de vieilles solutions, ainsi que la croyance selon laquelle « comme, en général, un sac de 5 livres est plus facile à manipuler qu’un sac de 10 livres, il faut mettre 10 livres de marchandises dans un seul sac de 5 livres »
L’image de base de l’« éditeur de texte », c’est un
textareade Mosaic, MacWrite, ou quelque part entre les deux, donc on essaie généralement d’y greffer la fusion avec le minimum de changements. Sous la forme d’une entrée de menu ou de quelques options dans une petite boîte de dialogue. Même quand il existe une prise en charge GUI de la fusion, enfouie dans les menus, cela reste soit de l’horreur diff/merge pour programmeurs, soit une vue précaire à base de texte barré, comme piloter un bateau dans le brouillardMais dans un éditeur de texte avec collaboration hors ligne, la fusion partiellement manuelle est au cœur du processus, et elle devrait aussi être au cœur de la conception de l’éditeur. Malheureusement, MacWrite est un optimum local dont il est difficile de sortir
Par exemple, quand quelqu’un parle d’« imitation par cargo cult » et de « vieilles solutions », la phrase suivante est souvent « n’éditez pas le code comme du texte, éditez-le comme un arbre syntaxique ». Mais le problème reste le même. Il suffit de remplacer « caractère » par « phrase »
Si Bob a ajouté une ligne dans la branche
elsed’une instructionifet qu’Alice a supprimé toute l’instruction, brancheelsecomprise, que doit faire un système intelligent ?Globalement, il semble y avoir un accord sur le fait que cette approche est la bonne
Je suis preneur de questions ou de retours. Je vais être en réunion pendant une heure ou deux, mais j’aime bien parler de ce genre de sujet. Vous pouvez écrire ici ou par e-mail, comme vous préférez : alex@moment.dev
Git n’injecte pas automatiquement des modifications locales sans consentement explicite. bzr n’aurait sans doute même pas rêvé de faire ça. Mais des outils comme Google Docs le font volontiers
Les progrès réalisés jusqu’ici sont chouettes, et j’espère que le programme d’accès anticipé marchera bien
J’ai implémenté differential sync(https://neil.fraser.name/writing/sync/). Je ne comprenais pas les autres approches, et pour l’application grugnotes.com, celle-ci me paraissait la plus simple
L’application est assez bricolée et n’est pas totalement en temps réel, mais, dans l’exemple, la fusion est correctement traitée quel que soit celui qui se reconnecte en premier. Si la suppression revient en ligne en premier, la version
colourest écartée et n’est même pas enregistrée dans l’historique des modificationsIl y aura sûrement d’autres problèmes, et je ne sais pas non plus ce que cela donnerait avec plus de deux utilisateurs, mais pour mon usage cela me convient