<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Dinamo Team on Medium]]></title>
        <description><![CDATA[Stories by Dinamo Team on Medium]]></description>
        <link>https://medium.com/@dinamoteam01?source=rss-dfdd356467------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*_1-9TXoP1k0e5G36jYzixQ.png</url>
            <title>Stories by Dinamo Team on Medium</title>
            <link>https://medium.com/@dinamoteam01?source=rss-dfdd356467------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 24 May 2026 02:25:56 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@dinamoteam01/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Building a Real-time Collaborative Code Editor]]></title>
            <link>https://medium.com/@dinamoteam01/building-a-real-time-collaborative-code-editor-cb842975652f?source=rss-dfdd356467------2</link>
            <guid isPermaLink="false">https://medium.com/p/cb842975652f</guid>
            <category><![CDATA[crdt]]></category>
            <category><![CDATA[real-time-synchronization]]></category>
            <category><![CDATA[visual-studio-code]]></category>
            <category><![CDATA[code-editor]]></category>
            <category><![CDATA[collaborative-editor]]></category>
            <dc:creator><![CDATA[Dinamo Team]]></dc:creator>
            <pubDate>Mon, 28 Sep 2020 01:25:25 GMT</pubDate>
            <atom:updated>2020-09-28T02:20:38.364Z</atom:updated>
            <content:encoded><![CDATA[<h4>Using Conflict-free Replicated Data types</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*VnaVo8njUm8KTmnNUhj9fg.gif" /><figcaption>A demo of Code Spot — a real-time collaborative code editor</figcaption></figure><p>“This piece of code hurts my eyes”.</p><p>That was my first reaction after my friend sent me some Python source code through Messenger. In Python, indentation is important because you cannot write an if-else block or loop without it. And here, Messenger has removed all indentations from the code.</p><p>At the time, I wished there was a tool that allowed for quick collaboration and didn’t require an account so I could easily help my friend with her code.</p><p>A few weeks later, I was fortunate to talk to a friend of mine, who was also passionate about software engineering. At first, he thought it was an easy problem but after some research, the problem turned out to be much more challenging.</p><p>Since we both love challenging problems and working in a team, we have teamed up and created Code Spot, a synchronized code editor with the power of Visual Studio Code. You can check it out at <a href="https://code-spot.net.">https://code-spot.net</a>, or look at the source code on our <a href="https://github.com/DinamoTeam/Code-Spot-P2P">GitHub repo</a>.</p><p>In this article, we will show you why synchronizing text (or code) in real-time is a non-trivial problem, and how we can solve that problem by using Conflict-free Replicated Data type (CRDT).</p><p>But first, let’s understand what a code editor is under the hood.</p><h3>I/ What is a code editor?</h3><p>A code editor is just a plain text editor that has extra functionalities such as code suggestion and syntax highlighting. Under the hood, a text editor associates with each character a number that determines the position of that character.</p><p>For example, in the text “Code” below, ‘C’ is at position 0, ‘o’ is at position 1, ‘d’ is at position 2 and ‘e’ is at position 3</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/263/1*uLjW-bu05yAXkjHK-eFhEQ.png" /><figcaption>An illustration of text editor indices</figcaption></figure><p>When you type a character ‘r’ at the end of the text, you are executing the operation insert(‘r’, 4). And when you delete ‘C’, you are executing delete(0). Notice that when you delete a character, everything to the right of it gets shifted 1 position to the left.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/589/1*IwKVetQQUwEdUV6NRjAHsQ.png" /><figcaption>Simple operations on a text editor</figcaption></figure><p>I know “oder” is not a word, but that’s ok 😊</p><h3>II/ Problem with synchronizing real-time editing</h3><p>Ok. Now you know how a code editor works. You are probably wondering: “What is so difficult about synchronizing text/code in real-time? After inserting/deleting, just send the request to other users so they can update their text. Simple enough.”</p><p>That was exactly what my friend thought too, but the problem turned out to be much more difficult, remember? Let’s attempt to sync text/code and see why it fails.</p><h4>1/ Problem #1: Commutativity</h4><p>Suppose User 1 and 2 are editing the same document in real-time.</p><p>On the screen, they have the text “BCD”. U1 inserts “A” at position 0 and <strong>simultaneously</strong>, U2 inserts “E” at position 3 and then they send each other these requests (probably through a server).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/853/0*0Uk7dcKplFOAV2Jc" /><figcaption>2 users went out of sync after simple insertions</figcaption></figure><p>As you can see, after 2 simple operations, U1 and U2’s screens are now different. This is because inserting at index 0 and then 3 will give a different result from inserting at index 3 and then 0. <strong>Order matters</strong>. Insertion (and deletion) operations <em>by index alone</em> are <strong>not</strong> <strong>commutative</strong>.</p><p><em>To give you a sense of commutativity, adding integers is commutative: 5 + 2 + 3 = 3 + 5 + 2. Applying ‘+’ operations in a </em><strong><em>different order </em></strong><em>gives the </em><strong><em>same result</em></strong><em>. However, adding and dividing is not: 1 + 2 / 3 not equals 1 / 3+ 2. Apply ‘+’ and ‘/’ operations in a </em><strong><em>different order </em></strong><em>gives </em><strong><em>different results.</em></strong></p><h4>2/ Problem #2: Idempotency</h4><p>Now suppose U1 and U2 have the text “ABCDE” on their screens and they delete the character ‘A’ at the same time. We expect they both have “BCDE” on the screen in the end. But…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/853/0*I4gycoxa4feTgpcT" /><figcaption>Position 0 was deleted twice</figcaption></figure><p>because each user receives an extra delete operation, 2 characters are deleted instead of 1. This is not good because this is not what they intended to do. <strong>Deleting the</strong> <strong>same</strong> <strong>character twice should be the same as deleting it once</strong>. In other words, deletion should be <strong>Idempotent.</strong></p><p><em>An example of an idempotent operation is adding by 0: a + 0 = a + 0 + 0 = a + 0 + 0 + 0.</em></p><p>After just a few changes, 2 users’ screens have gone out of sync. Can you imagine what would happen if they type more text or have more people editing at the same time? 😨</p><p>We hope we convinced you that synchronizing text in real-time is pretty hard.</p><p>The 2 problems above are the main challenges of synchronizing text in real-time. Luckily, if somehow we can make our 2 operations (deletion, insertion) <strong><em>commutative and idempotent</em></strong>, synchronization will happen automatically.</p><p>Seriously! Imagine if these operations <em>commute</em>, 2 users can make any kind of changes to the text and send these changes later in the future to the other person. Because the <strong>order</strong> in which the operations are applied <strong>doesn’t matter</strong>, each person can apply their operations and the other person’s operations in a different order, and they’ll both get the same final result.</p><p>Also, because operations are <em>idempotent</em>, 2 users deleting the <em>same</em> character will only result in that <em>particular</em> character being deleted instead of two characters get deleted.</p><p>And this applies to any number of users, not just two users.</p><p>So, synchronizing any operation which has these 2 properties is trivial. To give you one more example, consider the ‘add’ operation in an <em>add-only</em> Set. Because of its commutativity and idempotency, you can add items to a set in any order and will get the same set in the end. If you need to synchronize an add-only Set, do you see how easy this is? :)</p><p><strong>Conclusion:</strong> Real-time editing synchronization is hard because insertions and deletions don’t have commutativity and idempotency properties. If they do, however, synchronous editing becomes trivial.</p><p>In the next section, we’ll discuss how to achieve these 2 properties.</p><p><strong><em>Note</em></strong><em>: All operations you see in a code editor, whether it is undo / redo, drag and drop or even code autocompletion, are just a series of insertions and deletions. Therefore, if we can sync insertion and deletion, we can make a fully-featured Real-time Collaborative Code Editor!</em></p><h3>III/ Conflict-free replicated Data type (CRDT)</h3><p>Let’s bring back the previous example:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/590/1*uZE5OzOpVE6rXuXU2dzcYQ.png" /><figcaption>2 characters were deleted instead of 1</figcaption></figure><p>Position 0 is deleted twice because a <strong>position</strong> <strong>alone</strong> <strong>does not contain enough information.</strong></p><p>This triggers an interesting question: What if we have <strong>extra information</strong> in the request? What if, instead of deleting <em>a character</em> at position 0, we add something to the character to make it <em>unique</em> so now we’re deleting <em>that</em> <em>specific</em> <em>character</em> at position 0?</p><p>This is exactly the idea behind the CRDT used in Code Spot.</p><p>The CRDT we used in Code Spot is nothing more than a normal character added by a unique ID object so that insertion and deletion on that CRDT are commutative and idempotent. The complete design of this ID object is quite involved and deserves its own article. For now, let’s assume this ID is just a number, and see how it gets <strong>very close</strong> at enabling commutativity and idempotency.</p><p>Let’s add a unique number to each character</p><p>U1: A¹ B⁷ C⁸ D²⁴ E⁵¹</p><p>U2: A¹ B⁷ C⁸ D²⁴ E⁵¹</p><p><strong>First, idempotency</strong>: If User 1 and User 2 deletes ‘A’ at the same time:</p><p>U1: B⁷ C⁸ D²⁴ E⁵¹</p><p>U2: B⁷ C⁸ D²⁴ E⁵¹</p><p>They will send each other the request “Delete that <em>particular</em> character with Id = 3” (notice position is no longer needed).</p><p>When User 1 receives User 2’s request and vice versa, <em>the</em> character with Id = 3 no longer exists in their texts. So, these requests will be <strong>discarded</strong>. The final states of the texts:</p><p>U1: B⁷ C⁸ D²⁴ E⁵¹</p><p>U2: B⁷ C⁸ D²⁴ E⁵¹</p><p>=&gt; Great! Idempotency is achieved 😊</p><p><strong>Second, commutativity</strong>:</p><p>This needs a bit more discussion, as we haven’t even told you how to pick an ID. Deletion is easy because the ID is already present. What if we want to insert a new character?</p><p>If we pick a random number, let’s say 40, the request will look like <em>Insert </em>‘a’ <em>with ID = 40. </em>User 2 receives this request from User 1, and he has no clue where to insert ‘a’ at! After all, if we send the position together, we will get hit by Problem #1 again.</p><p>So, the ID needs to not only identify a unique character but also tell the user <em>where</em> that character should be! How can we contain that much information in a simple ID?</p><p>Hmm… Wouldn’t it be neat if our IDs are <strong>always in</strong> <strong>sorted order? </strong>(If you look at our Ids in the previous example, they are sorted). That way, if User 2 receives “<em>Insert ‘a’ with id = 40” </em>request from User 1, he will know exactly where ‘a’ should go:</p><p>U2: B⁷ C⁸ D²⁴ <strong>*a⁴⁰* </strong>E⁵¹</p><p>In other words, if <strong>the sorted order</strong> <strong>of the characters</strong> <strong>according to their IDs</strong> always<strong> matches</strong> exactly <strong>the order of the characters on the screen</strong>, knowing the ID alone is enough to insert (or remove) any character!</p><p>If User 2 now wants to insert a character ‘w’ between ‘a’ and ‘E’, he simply needs to find an ID <em>between 40 and 51</em> to attach to this character ‘w’ (Let’s say he picked 45):</p><p>U2: B⁷ C⁸ D²⁴ a⁴⁰ *<strong>w⁴⁵* </strong>E⁵¹</p><p>U1: B⁷ C⁸ D²⁴ a⁴⁰<strong> </strong>E⁵¹</p><p>When User 2 sends the request to User 1: “Insert character w with id = 45”, User 1 knows exactly where to insert this character ‘w’ because <strong>the</strong> <strong>Ids need to remain sorted</strong>:</p><p>U1: B⁷ C⁸ D²⁴ a⁴⁰ *<strong>w⁴⁵* </strong>E⁵¹</p><p>Even if someone inserts something between ‘B’ and ‘C’, we can pick 7.5 to be the ID between 7 and 8 (Yes! ID can be a decimal number).</p><p>B⁷ *<strong>x⁷⁵*</strong> C⁸ D²⁴ a⁴⁰<strong> </strong>w⁴⁵<strong> </strong>E⁵¹ (Medium doesn’t allow decimal superscript)</p><p>Neat!</p><p>Because of these ‘unique’ IDs (We’ll talk about the ‘’ later), insertions and deletions can be <strong>applied in</strong> <strong>any order</strong>, and you will still get the <strong>same text in the end</strong> (well, unless you do something silly such as delete a character before it is even created… It can happen if you use UDP protocol!).</p><p>To convince you that these IDs work quite well, let’s look at our current CRDT in action:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/853/1*QHs7KbsZpW2O0Wa48aUxPQ.gif" /><figcaption>CRDT in action! (2 users initially have “BCD” on their screens)</figcaption></figure><p>It works quite well doesn’t it 😊</p><p>Now, you might notice that we put ‘unique’ in a quotation. We also mentioned that the actual CRDT is more complex than what we currently have.</p><p>Yeah… the current design of ID is not good enough.</p><p>In fact, they are not even unique! If 2 users insert a character simultaneously between ‘B’ and ‘x’, they’ll probably produce 2 CRDTs with the same ID 7.25!</p><p>Because they have the same ID, we don’t know which one should come first!</p><p>Even worse, if now someone tries to insert something between ‘b’ and ‘a’, we are totally busted! How do you find an ID between 7.25 and… 7.25?!</p><p>Well, you can’t!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/853/1*DRrafUGO4rV_py55R81ioQ.gif" /><figcaption>Problem with our current design of CRDT</figcaption></figure><p>This CRDT is not good enough. A better CRDT is needed. For now, here are 3 requirements any valid CRDT (for our application) must have:</p><p>3 requirements for CRDT:</p><blockquote>· Requirement 1: The ID must be <strong>unique </strong>(globally unique to be exact), so that there is no confusion about which one is which.</blockquote><blockquote>· Requirement 2: The <strong>sorted order</strong> <strong>of our</strong> <strong>CRDTs</strong> according to its ID <strong>must</strong> <strong>match exactly the order of the text</strong> on the screen. (This also implies that we must have a way to compare 2 CRDTs)</blockquote><blockquote>· Requirement 3: Since users can insert anywhere, with however large a number of characters they want, we must be <strong>able to generate an unlimited number of CRDTs <em>between</em> any given 2 CRDTs</strong>.</blockquote><p>Ok. That’s the topic of another day. If you want us to write another article about that, please give us a clap so we know people are interested 😁. For now, let’s sum up what we’ve learned in this article:</p><p>· Synchronizing text in real-time is pretty hard!</p><p>· We need to make insertions and deletions commutative and idempotent to enable synchronous editing</p><p>· How to do that perfectly is still a mystery</p><p>That’s it for this article. Please leave a comment below if you have any questions/feedback. Again, if you want us to write the next article to unlock this ‘mystery’, please give us a clap, that will really boost our motivation 🤗🤗🤗.</p><p>Thank you all 😄</p><h4>References</h4><p>Below are the awesome sources we have used when we were researching how to build this collaborative code editor. Feel free to check them out!</p><p>Digital free pen’s article and video:</p><p><a href="https://digitalfreepen.com/2017/10/06/simple-real-time-collaborative-text-editor.html">https://digitalfreepen.com/2017/10/06/simple-real-time-collaborative-text-editor.html</a></p><p><a href="https://www.youtube.com/watch?v=jIR0Ngov7vo&amp;list=WL">https://www.youtube.com/watch?v=jIR0Ngov7vo&amp;list=WL</a></p><p>Conclave’s case study and video:</p><p><a href="https://conclave-team.github.io/conclave-site/">https://conclave-team.github.io/conclave-site/</a></p><p><a href="https://www.youtube.com/watch?v=jIR0Ngov7vo&amp;list=WL">https://www.youtube.com/watch?v=jIR0Ngov7vo&amp;list=WL</a></p><p>CRDTs and the Quest for Distributed Consistency:<a href="https://www.youtube.com/watch?v=B5NULPSiOGw&amp;t=3s"> https://www.youtube.com/watch?v=B5NULPSiOGw&amp;t=3s</a></p><p>Logoot CRDT paper:<a href="https://hal.inria.fr/inria-00432368/document"> https://hal.inria.fr/inria-00432368/document</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cb842975652f" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>