<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Freddy Carrillo</title>
    <description>The latest articles on DEV Community by Freddy Carrillo (@freddster14).</description>
    <link>https://dev.to/freddster14</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3776260%2F79d275f6-d40b-4237-ae57-840859b7e75c.jpeg</url>
      <title>DEV Community: Freddy Carrillo</title>
      <link>https://dev.to/freddster14</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/freddster14"/>
    <language>en</language>
    <item>
      <title>Centralized vs. Decentralized: Why Modern Collaborative Tools choose CRDTs</title>
      <dc:creator>Freddy Carrillo</dc:creator>
      <pubDate>Thu, 28 May 2026 20:16:55 +0000</pubDate>
      <link>https://dev.to/freddster14/centralized-vs-decentralized-why-modern-collaborative-tools-choose-crdts-132j</link>
      <guid>https://dev.to/freddster14/centralized-vs-decentralized-why-modern-collaborative-tools-choose-crdts-132j</guid>
      <description>&lt;p&gt;Real-time collaboration works like magic until two users edit the same line simultaneously. Under the hood, an algorithm must decide whose change wins and get it right every time.&lt;/p&gt;

&lt;p&gt;Software engineers faced this problem and two major approaches emerged: &lt;strong&gt;Operational Transforms&lt;/strong&gt; (&lt;a href="https://en.wikipedia.org/wiki/Operational_transformation" rel="noopener noreferrer"&gt;OT&lt;/a&gt;) and &lt;strong&gt;Conflict-Free Replicated Data Types&lt;/strong&gt; (&lt;a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type" rel="noopener noreferrer"&gt;CRDTs&lt;/a&gt;). The choice determines how your application scales.&lt;/p&gt;

&lt;p&gt;OT requires a centralized system to have total ordering of operations to transform correctly. The core issue is when two servers receiving the same operations in different orders, produce different results. In addition, the number of transformation combinations grow exponentially as users grow, making edge cases and divergence common. &lt;/p&gt;

&lt;p&gt;On the other hand, CRDTs are a decentralized data structure which was design to eliminate the need for a central coordinator. CRDTs achieve strong eventual consistency with its metadata, a unique identifier, relative position from peers and a timestamp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strong eventual consistency&lt;/strong&gt; is achieved mathematically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commutative - changing the order of operations does not change the result.&lt;/li&gt;
&lt;li&gt;Associative - receiving operations in different groups will not change the result.&lt;/li&gt;
&lt;li&gt;Idempotent - receiving the same operation twice will give the same result. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the architecture of my &lt;a href="https://github.com/freddster14/Collaborative-Code-Editor" rel="noopener noreferrer"&gt;Collaborative-Code-Editor&lt;/a&gt;, choosing CRDTs over OT gives me the advantage of not depending on a server for my data conflicts. Using &lt;a href="https://github.com/yjs/yjs" rel="noopener noreferrer"&gt;Yjs&lt;/a&gt; as my CRDT library, not only fixes the problems OT arises but comes with additional features. A unique identifier keeps track of the characters' identity, and relative addressing that points to the character position in my code editor. The relative position matters because when working with other real-time data types that use indices, you will encounter stale data when inserting. In other words, when two users add character at the same time it will be inserted relative to the left and right parents rather than its place in the document.&lt;/p&gt;

&lt;p&gt;Teams that build collaborative tools need to consider how to solve conflict data. CRDTs stand out as this algorithm that in addition to solving OT's scale issues, it lets developers not depend on a central server. In today's world where small teams and solo developer are emerging, they should consider using CRDTs if they need to scale without a central coordination server.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>architecture</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Centralized vs. Decentralized: Why Modern Collaborative Tools choose CRDTs</title>
      <dc:creator>Freddy Carrillo</dc:creator>
      <pubDate>Thu, 28 May 2026 20:16:55 +0000</pubDate>
      <link>https://dev.to/freddster14/centralized-vs-decentralized-why-modern-collaborative-tools-choose-crdts-447o</link>
      <guid>https://dev.to/freddster14/centralized-vs-decentralized-why-modern-collaborative-tools-choose-crdts-447o</guid>
      <description>&lt;p&gt;Real-time collaboration works like magic until two users edit the same line simultaneously. Under the hood, an algorithm must decide whose change wins and get it right every time.&lt;/p&gt;

&lt;p&gt;Software engineers faced this problem and two major approaches emerged: &lt;strong&gt;Operational Transforms&lt;/strong&gt; (&lt;a href="https://en.wikipedia.org/wiki/Operational_transformation" rel="noopener noreferrer"&gt;OT&lt;/a&gt;) and &lt;strong&gt;Conflict-Free Replicated Data Types&lt;/strong&gt; (&lt;a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type" rel="noopener noreferrer"&gt;CRDTs&lt;/a&gt;). The choice determines how your application scales.&lt;/p&gt;

&lt;p&gt;OT requires a centralized system to have total ordering of operations to transform correctly. The core issue is when two servers receiving the same operations in different orders, produce different results. In addition, the number of transformation combinations grow exponentially as users grow, making edge cases and divergence common. &lt;/p&gt;

&lt;p&gt;On the other hand, CRDTs are a decentralized data structure which was design to eliminate the need for a central coordinator. CRDTs achieve strong eventual consistency with its metadata, a unique identifier, relative position from peers and a timestamp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strong eventual consistency&lt;/strong&gt; is achieved mathematically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commutative - changing the order of operations does not change the result.&lt;/li&gt;
&lt;li&gt;Associative - receiving operations in different groups will not change the result.&lt;/li&gt;
&lt;li&gt;Idempotent - receiving the same operation twice will give the same result. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the architecture of my &lt;a href="https://github.com/freddster14/Collaborative-Code-Editor" rel="noopener noreferrer"&gt;Collaborative-Code-Editor&lt;/a&gt;, choosing CRDTs over OT gives me the advantage of not depending on a server for my data conflicts. Using &lt;a href="https://github.com/yjs/yjs" rel="noopener noreferrer"&gt;Yjs&lt;/a&gt; as my CRDT library, not only fixes the problems OT arises but comes with additional features. A unique identifier keeps track of the characters' identity, and relative addressing that points to the character position in my code editor. The relative position matters because when working with other real-time data types that use indices, you will encounter stale data when inserting. In other words, when two users add character at the same time it will be inserted relative to the left and right parents rather than its place in the document.&lt;/p&gt;

&lt;p&gt;Teams that build collaborative tools need to consider how to solve conflict data. CRDTs stand out as this algorithm that in addition to solving OT's scale issues, it lets developers not depend on a central server. In today's world where small teams and solo developer are emerging, they should consider using CRDTs if they need to scale without a central coordination server.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>architecture</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Solving Cross-Origin Cookies</title>
      <dc:creator>Freddy Carrillo</dc:creator>
      <pubDate>Mon, 16 Feb 2026 18:15:15 +0000</pubDate>
      <link>https://dev.to/freddster14/solving-cross-origin-cookies-54j8</link>
      <guid>https://dev.to/freddster14/solving-cross-origin-cookies-54j8</guid>
      <description>&lt;p&gt;Your chat app works locally but your REST API and WebSocket authentication fail in production. Here's why.&lt;/p&gt;

&lt;p&gt;I deployed with Vercel (frontend) and with Render (backend). Deploying on different domains makes for many adjustments during development when dealing with cookies. Without the correct configuration, my app failed to send cookies with HTTP requests. This created a problem, I had dual authentication: the HTTP Only cookies acted as my main security, which was set to protect all my REST API endpoints. My WebSocket authorization was protected by the same method, checking the HTTP only cookie, but also relied on its own token to establish a connection. Both expected the same token but delivered through different mechanisms. &lt;/p&gt;

&lt;p&gt;My HTTP requests were not receiving the cookie, making my REST API authentication fail. After some research, I changed the cookie's settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// From:&lt;/span&gt;
&lt;span class="nx"&gt;sameSite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;// To:&lt;/span&gt;
&lt;span class="nx"&gt;sameSite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Allowing the cookies to be sent and received with all third-party requests which includes cross-origin cookies. To secure the cookies, I set the secure option to be true to only allow cookies from HTTPS requests. Testing it in deployment fixed the REST API authentication, but failed when the browser settings blocked third-party cookies. This also made WebSocket not connect, since there was no cookie to pass and allow.&lt;/p&gt;

&lt;p&gt;Given that constraint, my first attempt was making my frontend proxy the API endpoints. This implementation made domain the same-origin in the browsers perspective, by accessing my API via the client URL. Now the browser saw my API as the same origin, eliminating any cross-origin issues.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before proxy&lt;/span&gt;
&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://message-app.render.com/:path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;// After proxy&lt;/span&gt;
&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://message-app.vercel.com/:path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each HTTP request is from the same origin, Vercel acts as the middle man, changing the API URL. To add, I needed to set a parameter (withCredentials) in the socket's client configuration to tell the browser to include cookies with the WebSocket handshake.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pass token via HTTP requests&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;withCredentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After deployment, the browser now allowed for users to be authenticated, but it was still not allowing my cookies to be passed to the WebSocket connection, and that made sense after I understood why.&lt;/p&gt;

&lt;p&gt;Even in a properly configured cross-origin environment, some browsers still do not send cookies with WebSocket handshakes. A WebSocket handshake is when the HTTP connection upgrades, allowing users to communicate in real time. WebSocket connections upgrade from the HTTP to the WebSocket protocol. While my REST API requests went through Vercel's proxy (same-origin), the WebSocket upgrade connected directly to Render (cross-origin). This made the browser not allow WebSocket access to cookies, so my authentication failed.&lt;/p&gt;

&lt;p&gt;The solution was to store the token in memory and attach it to Socket.io's parameters, which would be extracted in authorization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Fetch token from authenticated endpoint, pass explicitly&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With AuthContext already established with React's Context, on each page load the context will send an HTTP requests to the backend with the HTTP Only cookie attached. Rather than exposing the 5-day HTTP Only cookie token, I create a separate 1-hour token specifically for WebSocket authentication. This token is fetched from an authenticated endpoint (which validates the cookie), stored in memory, and passed via Socket.io's auth parameter. If an attacker somehow steals this token via XSS, their access is limited to 1 hour and cleared on page refresh.&lt;/p&gt;

&lt;p&gt;This dual authentication strategy maintains security while ensuring cross-origin compatibility. If you're deploying a real-time application with frontend and backend on different domains, you'll likely need a similar approach. Browser cookies won't send credentials with WebSocket handshakes, regardless of your CORS configuration.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>authjs</category>
      <category>webdev</category>
      <category>websockets</category>
    </item>
  </channel>
</rss>
