<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Hacker News: mweidner</title><link>https://news.ycombinator.com/user?id=mweidner</link><description>Hacker News RSS</description><docs>https://hnrss.org/</docs><generator>hnrss v2.1.1</generator><lastBuildDate>Sat, 02 May 2026 09:45:27 +0000</lastBuildDate><atom:link href="https://hnrss.org/user?id=mweidner" rel="self" type="application/rss+xml"></atom:link><item><title><![CDATA[New comment by mweidner in "The future of version control"]]></title><description><![CDATA[
<p>I'm surprised to see the emphasis on tracking lines of text, which ties in to the complexity of merge vs merge-the-other-way vs rebase. If we are committed to enhancing the change history, it seems wiser to go all in and store high-level, semantically-meaningful changes, like "move this code into an `if` block and add `else` block ...".<p>Consider the first example in the readme, "Left deletes the entire function [calculate]. Right adds a logging line in the middle". If you store the left operation as "delete function calculate<unique identifier>" and the right operation as "add line ... to function calculate", then it's obvious how to get the intended result (calculate is completely deleted), regardless of how you order these operations.<p>I personally think of version control's job not as collaborating on the actual files, but as collaborating on the canonical order of (high-level) operations on those files. This is what a branch is; merge/rebase/cherry-pick are ways of updating a branch's operation order, and you fix a conflict by adding new operations on top. (Though I argue rebase makes the most sense in this model: your end goal is to append to the main branch.)<p>Once you have high-level operations, you can start adding high-level conflict markers like "this operation changed the docs for function foo; flag a conflict on any new calls to foo". Note that you will need to remember some info about operations' original context (not just their eventual order in the main branch) to surface these conflicts.</p>
]]></description><pubDate>Sun, 22 Mar 2026 22:13:47 +0000</pubDate><link>https://news.ycombinator.com/item?id=47482776</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=47482776</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47482776</guid></item><item><title><![CDATA[New comment by mweidner in "The future of version control"]]></title><description><![CDATA[
<p>You can think of the semantics (i.e., specification) of any CRDT as a function that inputs the operation history DAG and outputs the resulting user-facing state. However, algorithms and implementations usually have a more programmatic description, like "here is a function  `(internal state, new operation) -> new internal state`", both for efficiency (update speed; storing less info than the full history) and because DAGs are hard to reason about. But you do see the function-of-history approach in the paper "Pure Operation-Based Replicated Data Types" [1].<p>[1] <a href="https://arxiv.org/abs/1710.04469" rel="nofollow">https://arxiv.org/abs/1710.04469</a></p>
]]></description><pubDate>Sun, 22 Mar 2026 21:55:18 +0000</pubDate><link>https://news.ycombinator.com/item?id=47482608</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=47482608</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47482608</guid></item><item><title><![CDATA[New comment by mweidner in "The future of version control"]]></title><description><![CDATA[
<p>While this is technically correct, folks discussing CRDTs in the context of text editing are typically thinking of a fairly specific family of algorithms, in which each character (or line) is assigned an immutable ID drawn from some abstract total order. That is the sense in which the original post uses the term (without mentioning a specific total order).</p>
]]></description><pubDate>Sun, 22 Mar 2026 21:41:29 +0000</pubDate><link>https://news.ycombinator.com/item?id=47482481</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=47482481</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47482481</guid></item><item><title><![CDATA[New comment by mweidner in "Lies I was told about collaborative editing, Part 2: Why we don't use Yjs"]]></title><description><![CDATA[
<p>Your part 1 post was one of the inspirations for that :)<p>Specifically, it inspired the question: how can one let programmers customize the way edits are processed, to avoid e.g. the "colour" -> "u" anomaly*, without violating CRDT/OTs' strict algebraic requirements? To which the answer is: find a way to get rid of those requirements.<p>*This is not just common behavior, but also features in a formal specification [1] of how collaborative text-editing algorithms should behave! "[The current text] contains exactly the [characters] that have been inserted, but not deleted."<p>[1] <a href="http://www.cs.ox.ac.uk/people/hongseok.yang/paper/podc16-full.pdf" rel="nofollow">http://www.cs.ox.ac.uk/people/hongseok.yang/paper/podc16-ful...</a></p>
]]></description><pubDate>Mon, 16 Mar 2026 21:42:53 +0000</pubDate><link>https://news.ycombinator.com/item?id=47405338</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=47405338</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47405338</guid></item><item><title><![CDATA[New comment by mweidner in ""]]></title><description><![CDATA[
<p>The rebasing step is indeed a transformation. Some info in the "rebasing" link here [1].<p>Unlike traditional Operational Transformation, though, there are no "transformation properties" [2] that this rebasing needs to satisfy. (Normally a central-server OT would need to satisfy TP1, or else users may end up in inconsistent states.) Instead, the rebased operations just need to "make sense" to users, i.e., be a reasonable way to apply your original edit to a slightly-further-ahead state. ProseMirror has this sort of rebasing built in, via its step mappings, which lets the collaboration-specific parts of the algorithm look very simple - perhaps deceptively so.<p>[1] <a href="https://prosemirror.net/docs/guide/#collab" rel="nofollow">https://prosemirror.net/docs/guide/#collab</a>
[2] <a href="https://en.wikipedia.org/wiki/Operational_transformation#Transformation_properties" rel="nofollow">https://en.wikipedia.org/wiki/Operational_transformation#Tra...</a></p>
]]></description><pubDate>Mon, 16 Mar 2026 14:39:01 +0000</pubDate><link>https://news.ycombinator.com/item?id=47399661</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=47399661</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47399661</guid></item><item><title><![CDATA[New comment by mweidner in "Lies I was told about collaborative editing, Part 2: Why we don't use Yjs"]]></title><description><![CDATA[
<p>The PowerSync folks and I worked on a different approach to ProseMirror collaboration here: <a href="https://www.powersync.com/blog/collaborative-text-editing-over-powersync" rel="nofollow">https://www.powersync.com/blog/collaborative-text-editing-ov...</a>
It is neither CRDT nor OT, but does use per-character IDs (like CRDTs) and an authoritative server order of changes (like OT).<p>The current implementation does suffer from the same issue noted for the Yjs-ProseMirror binding: collaborative changes cause the entire document to be replaced, which messes with some ProseMirror plugins. Specifically, when the client receives a remote change, it rolls back to the previous server state (without any pending local updates), applies the incoming change, and then re-applies its pending local updates; instead of sending a minimal representation of this overall change to ProseMirror, we merely calculate the final state and replace with that.<p>This is not an inherent limitation of the collaboration algorithm, just an implementation shortcut (as with the Yjs binding). It could be solved by diffing ProseMirror states to find the minimal representation of the overall change, or perhaps by using ProseMirror's built-in undo/redo features to "map" the remote change through the rollback & re-apply steps.</p>
]]></description><pubDate>Mon, 16 Mar 2026 14:20:23 +0000</pubDate><link>https://news.ycombinator.com/item?id=47399426</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=47399426</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47399426</guid></item><item><title><![CDATA[New comment by mweidner in "Lies I was told about collaborative editing, Part 2: Why we don't use Yjs"]]></title><description><![CDATA[
<p>This was my impression as well. If you ignore the paper and just look at the source code - and carefully study Seph Gentle's Yjs-like RGA implementation [1] - I believe you find that it is equivalent to an RGA-style tree, but with a different rule for sorting insertions that have the same left origin. That rule is hard to describe, but with some effort one can prove that concurrent insertions commute; I'm hoping to include this in a paper someday.<p>[1] <a href="https://josephg.com/blog/crdts-are-the-future/" rel="nofollow">https://josephg.com/blog/crdts-are-the-future/</a></p>
]]></description><pubDate>Mon, 16 Mar 2026 13:05:42 +0000</pubDate><link>https://news.ycombinator.com/item?id=47398481</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=47398481</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47398481</guid></item><item><title><![CDATA[New comment by mweidner in "Is the nested block/tree model the wrong foundation for modern rich text editors"]]></title><description><![CDATA[
<p>Managing "a flat-ish collection of nodes" that can be moved around (without merely deleting and re-inserting nodes) is tricky because of how paragraphs can be split and merged. Notion tackled this for their offline mode: <a href="https://www.youtube.com/watch?v=AKDcWRkbjYs" rel="nofollow">https://www.youtube.com/watch?v=AKDcWRkbjYs</a><p>If you take that as a solved problem, do your concerns change?<p>> Selection & Cursors: Selection across regions is notoriously hard. If "Region A" and "Region B" aren't siblings in a tree, how do we handle a user dragging a selection across both?<p>You could render them in the DOM as an old-fashioned tree, while internally manipulating your "flat" IR, to make selections work nicely.<p>This is not too different from how Yjs-ProseMirror works already: Yjs has its own representation of the state as a CRDT tree, which it converts to a separate ProseMirror tree on each update (& it uses a diff algorithm to map local user edits in the other direction).<p>> Prior Art: Has anyone seen a production system (perhaps in the desktop publishing or CAD world) that successfully treated rich text as a non-hierarchical "content pool"?<p>This might be how Dato CMS works? <a href="https://www.datocms.com/docs/content-modelling" rel="nofollow">https://www.datocms.com/docs/content-modelling</a>
(I say this based off of 5 minutes spent watching someone else use it.)<p>> Are we stuck with trees because they are the "right" abstraction, or just because the browser gives them to us for free?<p>For lists specifically, I would argue the latter. It's natural to think of a list as a flat sequence of list items, in parallel to any surrounding paragraphs; forcing you to wrap your list items in a UL or OL is (to me) a browser quirk.<p>I made some progress fighting this in Tiptap: <a href="https://github.com/commoncurriculum/tiptap-extension-flat-list" rel="nofollow">https://github.com/commoncurriculum/tiptap-extension-flat-li...</a>
Quill.js already models lists in this "flat" way.</p>
]]></description><pubDate>Sat, 07 Feb 2026 00:58:56 +0000</pubDate><link>https://news.ycombinator.com/item?id=46920205</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=46920205</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=46920205</guid></item><item><title><![CDATA[New comment by mweidner in "Why haven't local-first apps become popular?"]]></title><description><![CDATA[
<p>> More specifically, if you can edit different parts of a same document on different devices, then the document should be split across multiple files that can be synced independently<p>A more robust idea is to store a log of changes in files that are synced with Dropbox/OneDrive/etc. To prevent conflict warnings from Dropbox, you'll want each device to write to a separate file. Readers re-assemble the logs into (some) canonical total order, then reduce over that to get the document state.<p>The hard part is re-architecting your app to record all changes, instead of just writing out the current state to disk. However, once you do that, it can form the basis for other features like undo/redo, a view of the file's history, etc.<p>(The changes don't need to be CRDT/OT messages - anything deterministic works, though it's a good idea to make them "rebase-able", i.e., they will still do something reasonable when replayed on top of a collaborator's concurrent changes.)</p>
]]></description><pubDate>Tue, 23 Sep 2025 00:28:08 +0000</pubDate><link>https://news.ycombinator.com/item?id=45341411</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=45341411</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=45341411</guid></item><item><title><![CDATA[New comment by mweidner in "How Figma’s multiplayer technology works (2019)"]]></title><description><![CDATA[
<p>Indeed, Replicache works this way, using server reconciliation (one part of client-side prediction): <a href="https://doc.replicache.dev/concepts/how-it-works" rel="nofollow">https://doc.replicache.dev/concepts/how-it-works</a></p>
]]></description><pubDate>Wed, 20 Aug 2025 02:12:17 +0000</pubDate><link>https://news.ycombinator.com/item?id=44958086</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=44958086</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44958086</guid></item><item><title><![CDATA[New comment by mweidner in "Collaborative Text Editing Without CRDTs or OT"]]></title><description><![CDATA[
<p>This sounds similar to the idea behind articulated (though with ids UUID-counter instead of time-counter): <a href="https://github.com/mweidner037/articulated">https://github.com/mweidner037/articulated</a><p>I will check out Antirez.</p>
]]></description><pubDate>Fri, 23 May 2025 01:46:06 +0000</pubDate><link>https://news.ycombinator.com/item?id=44069065</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=44069065</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44069065</guid></item><item><title><![CDATA[New comment by mweidner in "Collaborative Text Editing Without CRDTs or OT"]]></title><description><![CDATA[
<p>A decentralized, eventually consistent total order on operations is a fully general CRDT, in the sense that you can put whatever (deterministic) operations you want in the total order and clients will end up in eventually consistent states.<p>Whether the converged result is at all reasonable is a different question.</p>
]]></description><pubDate>Thu, 22 May 2025 00:30:02 +0000</pubDate><link>https://news.ycombinator.com/item?id=44057600</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=44057600</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44057600</guid></item><item><title><![CDATA[New comment by mweidner in "Collaborative Text Editing Without CRDTs or OT"]]></title><description><![CDATA[
<p>As the author, same.<p>My best guess is:<p>- Central-server collaborative editing work focuses on Operational Transformation (OT), likely due to inertia (studied since 1989) and the perception that storing an ID per character is inefficient. In fairness, it is, absent the optimizations introduced by RGASplit and Yjs (~2015).<p>- For decentralized editing, OT is very complicated, and CRDTs took over as the solution of interest (studied since 2005). Storing every operation permanently in a log - needed to use the linked approach without a server - feels inefficient, as does server reconciliation's undo/re-apply process. So CRDT research has focused on avoiding those inefficiencies, sacrificing simplicity along the way, instead of just embracing them as the easy way out.<p>To me, the "inefficiencies" seem quite manageable. Storage is cheap, text is small, and you probably want a complete op log anyway for auditing and document histories (cf. git). Server reconciliation's undo/re-apply process can be batched aggressively, e.g., only do it a few times per second; that just makes remote ops take a little longer to show up.<p>Granted, I have not built a complete app around server reconciliation or the linked approach, so perhaps there is a hidden catch. But I am encouraged by the success of Replicache (<a href="https://doc.replicache.dev/concepts/how-it-works" rel="nofollow">https://doc.replicache.dev/concepts/how-it-works</a>), which is where I learned of server reconciliation.</p>
]]></description><pubDate>Thu, 22 May 2025 00:08:07 +0000</pubDate><link>https://news.ycombinator.com/item?id=44057497</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=44057497</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44057497</guid></item><item><title><![CDATA[New comment by mweidner in "Collaborative Text Editing Without CRDTs or OT"]]></title><description><![CDATA[
<p>Even in the absence of a central server, you can still avoid CRDT/OT complexity <i>if</i> you have a decentralized way to eventually total order operations & apply them in that order: <a href="https://mattweidner.com/2025/05/21/text-without-crdts.html#decentralized-variants" rel="nofollow">https://mattweidner.com/2025/05/21/text-without-crdts.html#d...</a><p>As others in the comments argue, this is technically a CRDT (though a fully general one); also, undoing/replaying ops is itself non-trivial to implement. However, I hope this is still simpler than using a traditional CRDT/OT for each data type.</p>
]]></description><pubDate>Wed, 21 May 2025 20:38:59 +0000</pubDate><link>https://news.ycombinator.com/item?id=44056088</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=44056088</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44056088</guid></item><item><title><![CDATA[New comment by mweidner in "Collaborative Text Editing Without CRDTs or OT"]]></title><description><![CDATA[
<p>Indeed, this is close to what Yjs (popular CRDT library) does: each client instance (~ browser tab) chooses a random 32-bit clientId, and character IDs combine this clientId with local counters. <a href="https://github.com/yjs/yjs/blob/987c9ebb5ad0a2a89a0230f3a0c6b31f095d5f2d/src/utils/Doc.js#L64">https://github.com/yjs/yjs/blob/987c9ebb5ad0a2a89a0230f3a0c6...</a><p>Any given collaborative document will probably only see ~1k clientIds in its lifetime, so the odds of a collision are fairly low, though I'd be more comfortable with a 64-bit ID.</p>
]]></description><pubDate>Wed, 21 May 2025 19:40:00 +0000</pubDate><link>https://news.ycombinator.com/item?id=44055419</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=44055419</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44055419</guid></item><item><title><![CDATA[New comment by mweidner in "Collaborative Text Editing Without CRDTs or OT"]]></title><description><![CDATA[
<p>I believe that Automerge internally stores all operations in an eventually consistent total order, which you <i>can</i> use as a substitute for the server in server reconciliation (cf. <a href="https://mattweidner.com/2025/05/21/text-without-crdts.html#decentralized-variants" rel="nofollow">https://mattweidner.com/2025/05/21/text-without-crdts.html#d...</a>). However, Automerge does not actually do that - instead it processes text operations using a traditional CRDT, RGA, probably because (as you point out) undoing and replaying ops is not trivial to implement.</p>
]]></description><pubDate>Wed, 21 May 2025 19:33:50 +0000</pubDate><link>https://news.ycombinator.com/item?id=44055341</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=44055341</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=44055341</guid></item><item><title><![CDATA[New comment by mweidner in "Sync Engines Are the Future"]]></title><description><![CDATA[
<p>Indeed, the simple approach of "send your operations to the server and it will apply them in the order it receives them" gives you good-enough conflict resolution in many cases.<p>It is still tempting to turn to CRDTs to solve the next problem: how to apply server-side changes to a client when the client has its own pending local operations. But this can be solved in a fully general way using server reconciliation, which doesn't restrict your operations or data structures like a CRDT does. I wrote about it here: <a href="https://mattweidner.com/2024/06/04/server-architectures.html#optimistic-local-updates" rel="nofollow">https://mattweidner.com/2024/06/04/server-architectures.html...</a></p>
]]></description><pubDate>Fri, 21 Mar 2025 16:03:03 +0000</pubDate><link>https://news.ycombinator.com/item?id=43437255</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=43437255</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=43437255</guid></item><item><title><![CDATA[New comment by mweidner in "Lies I was told about collab editing, Part 1: Algorithms for offline editing"]]></title><description><![CDATA[
<p>You could avoid the CRDT rules if you only use the LLM on the server. I.e., user comes online and sends their diff to the server, which LLM-merges it into the latest state and then sends that back to all clients.<p>This doesn't help you do merges client-side during live collaboration (for showing your optimistic local updates), but there the low latency reduces conflicts anyway, so you can fall back on a semantically-imperfect CRDT.</p>
]]></description><pubDate>Sat, 07 Dec 2024 04:17:47 +0000</pubDate><link>https://news.ycombinator.com/item?id=42347227</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=42347227</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=42347227</guid></item><item><title><![CDATA[New comment by mweidner in "Lies I was told about collab editing, Part 1: Algorithms for offline editing"]]></title><description><![CDATA[
<p>One challenge is that the algorithms typically used for collaborative text editing (CRDTs and OT) have strict algebraic requirements for what the edit operations do & how they interact. So even if your server is smart enough to process the "Colour" example in a UX-reasonable way, it's very hard to design a corresponding CRDT/OT, for optimistic client-side edits.<p>You can work around this by not using CRDTs/OT. E.g., the server processes operations in receipt order, applying whatever UX logic it wants; clients use a rebase/prediction strategy to still allow optimistic edits on top (cf. <a href="https://doc.replicache.dev/concepts/how-it-works" rel="nofollow">https://doc.replicache.dev/concepts/how-it-works</a>). Doing this for text editing has some challenges, but they're separate from the CRDT/OT challenges discussed here.</p>
]]></description><pubDate>Fri, 06 Dec 2024 23:23:21 +0000</pubDate><link>https://news.ycombinator.com/item?id=42345683</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=42345683</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=42345683</guid></item><item><title><![CDATA[New comment by mweidner in "Faster CRDTs (2021)"]]></title><description><![CDATA[
<p>Indeed, Apple Notes has a reasonably sophisticated CRDT, including support for tables: <a href="https://github.com/dunhamsteve/notesutils/blob/master/notes.md">https://github.com/dunhamsteve/notesutils/blob/master/notes....</a></p>
]]></description><pubDate>Wed, 28 Aug 2024 00:31:21 +0000</pubDate><link>https://news.ycombinator.com/item?id=41374821</link><dc:creator>mweidner</dc:creator><comments>https://news.ycombinator.com/item?id=41374821</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=41374821</guid></item></channel></rss>