<?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: Ali Malik</title>
    <description>The latest articles on DEV Community by Ali Malik (@amalik18).</description>
    <link>https://dev.to/amalik18</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%2F3730633%2F742e3780-c064-4746-a023-565820c3e189.png</url>
      <title>DEV Community: Ali Malik</title>
      <link>https://dev.to/amalik18</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amalik18"/>
    <language>en</language>
    <item>
      <title>The Rise of Edge Computing</title>
      <dc:creator>Ali Malik</dc:creator>
      <pubDate>Tue, 07 Apr 2026 05:15:49 +0000</pubDate>
      <link>https://dev.to/amalik18/the-rise-of-edge-computing-co2</link>
      <guid>https://dev.to/amalik18/the-rise-of-edge-computing-co2</guid>
      <description>&lt;p&gt;Hey folks I’m back with another post. Today, we’ll be talking about Edge Computing, what it is, why its important, etc.&lt;/p&gt;

&lt;p&gt;Before starting, once again, I’d like to thank all of you for tuning in. If you’re new here, please consider subscribing, it allows me to really feel like you want more of this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/subscribe?" rel="noopener noreferrer"&gt;Subscribe now&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also I’d like to address the absence, I started this blog as a way to teach myself and others, I had initially committed to posting weekly, but with work and school it’s proven to be a challenge to put out high quality articles at a weekly cadence. I will continue to try to release an article weekly, but long-form articles and technical deep-dives will probably happen every other week if not longer.&lt;/p&gt;

&lt;p&gt;Without further delay, let’s dive in…&lt;/p&gt;




&lt;h1&gt;
  
  
  What is Edge Computing?
&lt;/h1&gt;

&lt;p&gt;Before we can dive into edge computing as a whole we need to understand how we got here. Since the introduction of the computing paradigm, its evolution has been marked by a continuous shift from centralized systems to more distributed architectures. We’ve gone from mainframe computers to desktops to now laptops, in each iteration computing power was brought closer and closer to the end user. As the internet became more robust computing was decentralized even further, enabling networked communication and data sharing at a global scale.&lt;/p&gt;

&lt;p&gt;A major milestone in the computing paradigm was the emergence and domination of cloud computing, which offers scalable and on-demand resources virtually all over the world. Cloud computing has completely revolutionized how individuals and businesses utilize computing power, storage, and offered services. Cloud computing has been revolutionary but, as the number of connected devices and the Internet of Things (IoT) increases, new challenges arise that aren’t addressable with traditional cloud computing.&lt;/p&gt;

&lt;p&gt;This is where &lt;em&gt;edge computing&lt;/em&gt; comes into play. Edge computing is a computing paradigm that brings computation power and data storage needs closed to the source of data. Instead of sending all data to a central location (think Cloud Computing and storing everything in the “cloud”) for processing, edge computing processes data at or near the point of generation. By doing so, we’re able to reduce latency, conserve network bandwidth, increase responsiveness, and improve user experience for real-time applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21w1Vf%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F659f7712-6f04-49e9-8ce0-467509e0a2fa_1440x810.avif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21w1Vf%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F659f7712-6f04-49e9-8ce0-467509e0a2fa_1440x810.avif" width="1440" height="810"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Taken from Akamai’s article regarding Edge Computing&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is especially powerful today, where we have billions of interconnected devices generating huge amounts of data, it really highlights the limitations of centralized computing. Various applications like autonomous vehicles, idustrial automation, and smart cities require near instantaneous data processing. Edge computing resolves this problem by bringing the computational power closer to these data sources.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why is Edge Computing Important?
&lt;/h1&gt;

&lt;p&gt;As mentioned above, edge computing provides benefits in areas such as latency, bandwidth, and responsiveness. In addition to those, edge computing can enhance the security posture of a system, increase reliability, and, in some cases, even increase the scalability of a system. We’ll take a look at some of these below:&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduced Latency
&lt;/h3&gt;

&lt;p&gt;One of the biggest attraction points of edge computing is the fact that you can reduce latency by magnitudes of order. By processing data closer to the generation point, applications are able to respond much faster.&lt;/p&gt;

&lt;p&gt;The increased responsivness is criticial for use cases like autonomous vehicles, industrial automation, and healthcare.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Autonomous Vehicles:&lt;/strong&gt; processing sensor data immediately is essential in guaranteeing safety.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Industrial Automation:&lt;/strong&gt; real-time control systems rely on instant feedback so quick processing is necessary.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Healthcare:&lt;/strong&gt; critical monitoring devices need to be able to process and analyze patient data rapidly.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Network Bandwidth Optimization
&lt;/h3&gt;

&lt;p&gt;In the typical centralized computing model data needs to be transferred to a central storage server for processing which, as you can imagine for large amounts of data, can strain the network and increase costs. With edge computing it’s possible to filter the data to send only what is required and aggregate data to reduce redundancy which, ultimately, can lower operational expenses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Increased Security
&lt;/h3&gt;

&lt;p&gt;Processing data locally can improve the security of a system by localizing data. By localizing data, regulatory compliance is easier to achieve.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;p&gt;One can imagine that as the number of connected devices grow, as they have been, centralized computing becomes less and less optimal. Edge computing, on the otherhand, can provide a distributed and load balanced processing system. By offlading processing tasks from the central servers one can achieve a distributed processing framework, coupled with having multiple edge nodes one can achieve a load balanced, distributed processing system.&lt;/p&gt;




&lt;h1&gt;
  
  
  Edge Computing Today
&lt;/h1&gt;

&lt;p&gt;Edge computing is being adopted very rapidly across various industries like manufacturing, retail, healthcare, and energy and utilities.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manufacturing:&lt;/strong&gt; for predictive maintenance and real-time quality control.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Retail:&lt;/strong&gt; enhancing customer experiencing by leveraging real-time analytics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HealthCare&lt;/strong&gt; : in medical devices and for telemedicine applications.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Energy and Utilities:&lt;/strong&gt; monitoring and controlling distributed resources.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these use cases are aided by the various edge computing platforms that exist today. Service offerings such as &lt;a href="https://aws.amazon.com/greengrass/" rel="noopener noreferrer"&gt;AWS IoT Greengrass&lt;/a&gt;, &lt;a href="https://azure.microsoft.com/en-us/products/iot-edge" rel="noopener noreferrer"&gt;Microsoft Azure’s IoT Edge&lt;/a&gt;, and Google Cloud’s IoT Edge have pushed the envelope for providing cloud services at the edge.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://aws.amazon.com/greengrass/" rel="noopener noreferrer"&gt;AWS IoT Greengrass&lt;/a&gt;: extends the AWS service portfolio to edge devices.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://azure.microsoft.com/en-us/products/iot-edge" rel="noopener noreferrer"&gt;Microsoft Azure’s IoT Edge&lt;/a&gt;: deploys cloud workloads to run on edge devices.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Google’s Cloud IoT Edge: extends Google’s data processing and machine learning capabilities to edge devices.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to edge computing platforms there is various edge hardware out there including, but not limited to, &lt;a href="https://www.nvidia.com/en-us/autonomous-machines/embedded-systems/" rel="noopener noreferrer"&gt;NVIDIA’s Jetson Devices&lt;/a&gt; (I’ve personally used these and they’re fricking awesome) and &lt;a href="https://www.intel.com/content/www/us/en/developer/tools/openvino-toolkit/overview.html" rel="noopener noreferrer"&gt;Intel’s OpenVINO Toolkit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Together with a dedicated platform and the hardware to match its possible to get edge applications up and running faster than ever. Having this number of devices does come with some drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security:&lt;/strong&gt; as the number of edge devices increases as does the overall attack surface. This means more robust security measures are required.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deployment Complexity:&lt;/strong&gt; deploying and managing a number of distributed devices is non-trivial.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interoperability:&lt;/strong&gt; heterogenous hardware and software platforms can lead to compatability issues. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All in all, currently the edge compute space looks real good. It’s got an established list of hardware and software platforms, support from major companies, and is only expected to get better and better as the need for edge processing increases. Plus it doesn’t hurt that the global edge market size was valued at $16.45 billion in 2023 and is expected to grow at a CAGR of 36.9% till 2030.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Future?
&lt;/h1&gt;

&lt;p&gt;In the last two years artificial intelligence and machine learning (AI/ML) have taken center stage, this is highlighted by the fact that every major company is either incorporating or releasing Generative AI (GenAI) products. With this being the case, Edge Compute for AI (Edge AI) will enable devices to perform tasks like image recognition, classification, natural language processing, and predictive analytics without having to rely on cloud-based models.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;On-Device AI&lt;/strong&gt; : smartphones and IoT devices will begin to run AI/ML models locally (this can be observed in the new iOS updates with Apple Intelligence).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Edge AI Processors:&lt;/strong&gt; specialized processors geared towards AI workloads will start to show up (NVIDIA is already doing this).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Edge computing will prove to be very useful in areas like autonomous vehicles, smart cities, and augmented/virtual reality (AR/VR) by reducing latency and allowing for quick data processing. It will do the same for real-time systems like the ones present in industrial solutions.&lt;/p&gt;

&lt;p&gt;While the upside to edge computing is limitless there are plenty of downsides, namely, security and privacy, interoperability, and complexity. As the market increases these challenges should begin to subside. This new computing paradigm is poised to create new business and employment opportunities.&lt;/p&gt;




&lt;p&gt;That’s all I have for you today. Hopefully you enjoyed the read. If you liked the content and would like to continue to receive my content please consider subscribing, it goes a long way.&lt;/p&gt;

&lt;p&gt;Lastly, please comment &amp;amp; share, I’d love to hear everyones thoughts.&lt;/p&gt;

&lt;p&gt;Thanks,&lt;/p&gt;

&lt;p&gt;Ali&lt;/p&gt;

&lt;p&gt;Professional Imposter Syndrome is a reader-supported publication. To receive new posts and support my work, consider becoming a subscriber.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/the-rise-of-edge-computing?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share" rel="noopener noreferrer"&gt;Share&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/the-rise-of-edge-computing/comments" rel="noopener noreferrer"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Cryptography for Software Developers</title>
      <dc:creator>Ali Malik</dc:creator>
      <pubDate>Tue, 07 Apr 2026 05:15:03 +0000</pubDate>
      <link>https://dev.to/amalik18/cryptography-for-software-developers-243h</link>
      <guid>https://dev.to/amalik18/cryptography-for-software-developers-243h</guid>
      <description>&lt;p&gt;Hey folks, I am back, officially this time. Thank you for bearing with me during the absence. But I am back and better than ever.&lt;/p&gt;

&lt;p&gt;Today we’ll be taking a look at core cryptography knowledge that I consider essential for software developers.&lt;/p&gt;

&lt;p&gt;Before starting, once again, I’d like to thank all of you for tuning in. If you’re new here, please consider subscribing, it allows me to really feel like you want more of this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/subscribe?" rel="noopener noreferrer"&gt;Subscribe now&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  What is Cryptography?
&lt;/h1&gt;

&lt;p&gt;Cryptography, plain and simple, is the study and practice of hiding information so that only the intended recipient can read the message. Cryptography revolves around four pillars:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confidentiality:&lt;/strong&gt; Ensuring only the intended recipient can read the data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integrity:&lt;/strong&gt; Ensuring data has not been tampered with.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Authentication:&lt;/strong&gt; Verifying the identity of parties involved.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Non-Repudiation:&lt;/strong&gt; Preventing someone from denying they performed an action (e.g., signed a document).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cryptography is everywhere in the digital world, from encrypting WhatsApp messages to ensuring your credit card numbers remain confidential and secure.&lt;/p&gt;

&lt;h1&gt;
  
  
  Terms
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plaintext (PT)&lt;/strong&gt;: the unencrypted text. Just regular text, like what you’re &lt;strong&gt;reading.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ciphertext (CT):&lt;/strong&gt; the encrypted counterpart to PT. For example: &lt;em&gt;1238B45E5AF8E88324699CC6BD9B6DB2&lt;/em&gt; which is the AES-256 representation for “Hello, World”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AES: &lt;a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard" rel="noopener noreferrer"&gt;Advanced Encryption Standard&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DES: &lt;a href="https://en.wikipedia.org/wiki/Data_Encryption_Standard" rel="noopener noreferrer"&gt;Data Encryption Standard&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hash/Digest: A fixed-size fingerprint of data (e.g., SHA-256 hashes "Hello World" to &lt;em&gt;d2a84f4b8b650937ec8f73cd8be2c74add5a911ba64df27458ed8229da804a26&lt;/em&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  The Basics
&lt;/h1&gt;

&lt;p&gt;Let’s start with the basics. In this section we’ll discuss symmetric vs asymmetric cryptography.&lt;/p&gt;

&lt;h2&gt;
  
  
  Symmetric Cryptography
&lt;/h2&gt;

&lt;p&gt;Symmetric cryptography is cryptography that uses the &lt;em&gt;same&lt;/em&gt; key for encryption and decryption, hence the classification &lt;em&gt;symmetric&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24rqjzyow1f59ttg7ejo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24rqjzyow1f59ttg7ejo.png" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For &lt;em&gt;symmetric cryptography&lt;/em&gt; two, or more, communicating parties know the shared secret (the key). An example of a symmetric key algorithm is AES (Advanced Encryption Standard). Symmetric Cryptography offer speed of encryption, efficiency over large amounts of data, and confidentiality.&lt;/p&gt;

&lt;p&gt;Symmetric cryptographic algorithms can be further split into two categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Block Ciphers&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stream Ciphers&lt;/strong&gt; :&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Block Ciphers
&lt;/h3&gt;

&lt;p&gt;As the name suggests, block ciphers encrypt &lt;em&gt;blocks of bits&lt;/em&gt; at a time. For example, AES has a block size of 128 bits. That means that the AES algorithm breaks an input into chunks of 128 bits when encrypting. Block ciphers are very prominent in internet applications such as TLS/SSL (HTTPS), VPN, and BitLocker are some examples.&lt;/p&gt;

&lt;p&gt;Other popular block ciphers include Triple DES (3DES) and BlowFish both with a block size of 64 bits.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Block size is entirely separate from key size. Block size is a fixed-size chunk of data that a cipher processes in every encryption / decryption operation.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Stream Ciphers
&lt;/h3&gt;

&lt;p&gt;Stream ciphers operate on a stream of bits, thus, these ciphers encrypt bits/byte one at a time, individually. Stream ciphers are used heavily in real-time scenarios such as, real-time communication such as VoIP (Voice-over-IP) and mobile communications. Since these ciphers are lightweight they’re perfect for resource-constrained environments.&lt;/p&gt;

&lt;p&gt;An example of a stream cipher, a very important example, is the One-Time Pad (OTP). The importance here lies in the fact that a OTP is mathematically unbreakable, when used correctly. This is because the ciphertext provides absolutely no information about the plaintext if the key is unknown.&lt;/p&gt;




&lt;h2&gt;
  
  
  Asymmetric Cryptography
&lt;/h2&gt;

&lt;p&gt;Asymmetric cryptography is also known by its much more popular pseudonym …public-key cryptography, this is what I’ll use moving forward. Public-Key Cryptography, when compared to Symmetric Cryptography, offer better security and robustness (offers confidentiality, authenticity, and non-repudiation). However, these algorithms are much more resource intensive making it slower than symmetric cryptography. Public-Key Cryptography also greatly simplifies the key exchange process, removing the need to share a single secret key.&lt;/p&gt;

&lt;p&gt;In Public-Key Cryptography, a &lt;em&gt;pair of keys&lt;/em&gt; are used. One of the keys, the &lt;em&gt;public key&lt;/em&gt;, is not a secret, it can be shared with anyone. The other key, the &lt;em&gt;private key&lt;/em&gt;, is the user’s secret that &lt;strong&gt;does not&lt;/strong&gt; get shared with anyone. There is a mathematical relationship between the two keys, however, given a key one cannot derive the other part of the pair (it’s mathematically infeasible). Very common example of a public-key algorithm is RSA (Rivest-Shamir-Adleman).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsgjlhod5s8iofxqst9jc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsgjlhod5s8iofxqst9jc.png" width="800" height="540"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Alice’s (recipient) public key is used to encrypt the message and the private key is used to decrypt the message.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of the most important aspects of Public Key Cryptography is the fact that it greatly simplifies key distribution. No longer does one need to share secrets via a secure channel. The presence of two keys (one public and one private) builds the basis for digital signatures.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, public-key cryptography is slower than it’s counterpart (symmetric cryptography) but another con is that it requires much larger key sizes to achieve comparable security. For example, when comparing RSA and AES, RSA needs a key size of 15360 bits to achieve the same level of security as 256 for AES.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0t23que9xbuepjny72h.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0t23que9xbuepjny72h.jpeg" width="711" height="283"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Comparing Symmetric and Asymmetric Algorithm Key Sizes (Credit)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Hashes and Digests
&lt;/h2&gt;

&lt;p&gt;Sometimes rather than completely encrypting data we just need to verify its integrity, a quick and simple way of doing so is using hashes. Hashes and digest are used pretty interchangeably, they both refer to the result of applying a cryptographic hash function to a piece of data (file, text, etc). The point of the hash/digest is to create a “fingerprint” of sorts for the input data that allows us to track the integrity of the message, to make sure it has not been tampered with.&lt;/p&gt;

&lt;p&gt;So we mentioned a cryptographic hash function above, but what is that? Basically a cryptographic hash function, referred to simply as a hash function, is a _ &lt;strong&gt;one-way function that produces a fixed size output&lt;/strong&gt; _. Hash functions have two really important properties to them: they’re one way and they’re collision resistant.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One Way&lt;/strong&gt; : it should be computationally infeasible (basically impossible) to get the original data from the hash value.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Collision-Resistant&lt;/strong&gt; : it should be computationally infeasible (basically impossible) to find two pieces of data that map to the same hash value.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I mentioned above that hashes are great at verifying data integrity, lets give a few examples of that to better understand the role of hashes/message digests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Software Distribution&lt;/strong&gt; : you may have noticed when downloading a new piece of software often times you’re provided with something called the “SHA” of that distribution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Backups&lt;/strong&gt; : verifying that backups haven’t been tampered with.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storing Passwords:&lt;/strong&gt; passwords aren’t stored in plaintext, for obvious reasons, instead they are hashed and the hashed value is stored. This ensures that even if the password database is hacked/compromised the passwords themselves are not.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Commit Identification:&lt;/strong&gt; if you use Git (a version-control system) you’ll notice random alphanumeric characters representing the commits. Git uses a hash to identify the commit.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many hash functions available, some more popular than others. Examples include: MD5, SHA-1, SHA-256, BLAKE-2, etc.&lt;/p&gt;

&lt;p&gt;Hashes and digest also make the foundation for crypto currencies. They’re crucial in ensuring integrity, security and trust. In an earlier &lt;a href="https://open.substack.com/pub/ahmazin/p/merkle-trees?r=1tezps&amp;amp;utm_campaign=post&amp;amp;utm_medium=web&amp;amp;showWelcomeOnShare=true" rel="noopener noreferrer"&gt;post&lt;/a&gt;, we talked about Merkle Trees and their applications, remember Merkle Trees rely on the concept of hashes to be able to verify transactions. This extends beyond just proof of transactions, with hashes/digests, crypto currencies add functionality like Proof of Stake, data integrity, and digital signatures.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Please don’t use MD5 and SHA-1 they are weak hashes.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Message Authentication Codes and Signatures
&lt;/h2&gt;

&lt;p&gt;I mentioned above that if we wanted to simply check the integrity of a message we could use a hash function. But what if we wanted to make sure that the message was authentic, the sending party is who they say they are? This is where message authentication codes (MACs) come into play. Think of a MAC as the digital fingerprint of the message, it’s unique to that message (and the shared secret key). Formally, a &lt;em&gt;message authentication code (MAC)&lt;/em&gt; is a cryptographic technique used to verify &lt;strong&gt;both&lt;/strong&gt; the &lt;em&gt;integrity&lt;/em&gt; and &lt;em&gt;authenticity&lt;/em&gt; of the message. Remember, authenticity relates to the sending and receiving parties, so a MAC not only makes sure that the message has not been tampered with but also provides verification of the two parties involved.&lt;/p&gt;

&lt;p&gt;So how do MACs work? Well before anything, step 0 if you will, the sender and receiver need to share a secret key beforehand. This key is crucial for verification. Once that’s taken care of we can generate the MAC.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdaisg4n8ol1hb9g2lihy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdaisg4n8ol1hb9g2lihy.png" width="800" height="716"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;If Alice wants to send a message to Bob she would include the MAC of the message with the original message so Bob can verify its authenticity.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once the secret key is shared, when sending a message the sending party generates a MAC of the message and includes it in the transmission. Once the receiving party receives the message, they would also generate a MAC of the message and compare it to the accompanying MAC. If the MACs match everything proceeds as normal, otherwise the message gets rejected.&lt;/p&gt;

&lt;p&gt;Digital signatures are basically the same thing. Instead of a shared secret key the senders private key is used for signing, as we know the senders private key can be verified by the senders public key. Basically, digital signatures are for asymmetric cryptography whereas MACs are for symmetric cryptography.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tips, Reminders &amp;amp; Resources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Please &lt;strong&gt;DO NOT&lt;/strong&gt; roll your own crypto algorithm beyond for fun. If you’re using crypto algorithms for production applications please use current best practices. Check out &lt;a href="https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines" rel="noopener noreferrer"&gt;NIST Standards&lt;/a&gt;, as well as, &lt;a href="https://www.latacora.com/blog/2024/07/29/crypto-right-answers-pq/" rel="noopener noreferrer"&gt;Cryptographic Right Answers&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Please &lt;strong&gt;DO NOT&lt;/strong&gt; use outdated or weak algorithms. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Utilize OWASP Top 10s and &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP Cheat Sheets&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Thanks for reading Professional Imposter Syndrome! Subscribe for free to receive new posts and support my work.&lt;/p&gt;

&lt;p&gt;That’s all I have today. Thank you for reading through the entire post. Hopefully I was able to teach you something new about the ever-evolving and extremely important field of cryptography.&lt;/p&gt;

&lt;p&gt;If you liked the post please like, share, and subscribe. Please share your thoughts on the post.&lt;/p&gt;

&lt;p&gt;My next post will start the Distributed Systems series. I’ll see you there.&lt;/p&gt;

&lt;p&gt;Ali&lt;/p&gt;

&lt;p&gt;Thanks for reading Professional Imposter Syndrome! This post is public so feel free to share it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/cryptography-for-software-developers?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share" rel="noopener noreferrer"&gt;Share&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/cryptography-for-software-developers?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share" rel="noopener noreferrer"&gt;Share&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/cryptography-for-software-developers/comments" rel="noopener noreferrer"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>softwaredevelopment</category>
      <category>software</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Merkle Trees</title>
      <dc:creator>Ali Malik</dc:creator>
      <pubDate>Tue, 07 Apr 2026 05:10:50 +0000</pubDate>
      <link>https://dev.to/amalik18/merkle-trees-j37</link>
      <guid>https://dev.to/amalik18/merkle-trees-j37</guid>
      <description>&lt;p&gt;Hey folks, I’m back for another post. I’m doing decently well in keeping up with my promise of weekly posts. I do, however, apologize for the posts not coming out on set dates and time as I originally promised.&lt;/p&gt;

&lt;p&gt;Before we delve into the weekly topic, I’d like to welcome the 36 (at the time of writing this) new subscribers. I’d just like to say thank you so much for supporting my work here. We went from 12 subscribers from my last post, all the way to 48 at the time of writing this. The growth is beyond what I initially imagined. Thank you to all my subscribers, if you’re new here I hope you’ll enjoy my posts.&lt;/p&gt;

&lt;p&gt;Some of the feedback I’ve received from readers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Also I'm not sure if I said this before but it's a great thing you're doing and I've read all of your articles thus far. I hope you keep doing it and I wish you success!&lt;/p&gt;

&lt;p&gt;Thanks for sharing this post, Ali. I have been learning this more deeply during my studies, but I never saw a simple summary and overview like this one. It was a nice journey!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/subscribe?" rel="noopener noreferrer"&gt;Subscribe now&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This may seem like a random topic, but I’m currently taking a cryptography class and my project idea is related to homomorphic encryption. While doing some preliminary research into the topic I stumbled across Merkle trees and distributed filesystems. So, naturally, I decided to make it the topic of my post in hopes of learning more about them and imparting some knowledge.&lt;/p&gt;

&lt;p&gt;So, without further adieu…enjoy.&lt;/p&gt;




&lt;p&gt;Before we can dive into Merkle Trees we first have to talk about plain old trees.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is a Tree?
&lt;/h1&gt;

&lt;p&gt;A tree is a data structure in computer science that represents a hierarchical tree structure composed of nodes. Each node can be connected to &lt;strong&gt;many&lt;/strong&gt; children, but can only have &lt;strong&gt;one&lt;/strong&gt; parent node, this is not the case for the &lt;strong&gt;root node&lt;/strong&gt;. The &lt;strong&gt;root node&lt;/strong&gt; has no parents, it’s the top-most node in the tree. Nodes with no children are called &lt;strong&gt;leaf nodes&lt;/strong&gt;. Below is a figure that explains this in a visual manner:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ssxyknt2zltfziubzdd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ssxyknt2zltfziubzdd.png" width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the visual above lets classify the different types of nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Root Node&lt;/strong&gt; : the root node in the above tree is &lt;strong&gt;Parent&lt;/strong&gt;. The parent node has no…parent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Children Nodes&lt;/strong&gt; : James, Thomas, Nick, and all of the Grandchildren are children nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leaf:&lt;/strong&gt; the leaf nodes of a tree are the children nodes with no children of their own. So in this case, all of the &lt;strong&gt;Grandchildren&lt;/strong&gt; nodes are leaf nodes as they do &lt;strong&gt;not&lt;/strong&gt; have any children of their own.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The constraints put on the nodes is actually quite useful. This means that there are no loops in relations, meaning a node &lt;strong&gt;cannot&lt;/strong&gt; be it’s own ancestor. Secondly, this means that we can consider each child node the root node of its own subtree. That might sound confusing but let’s use some visuals:&lt;/p&gt;

&lt;p&gt;Let’s show the entire tree with leaf nodes and subtrees marked:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnb75pb40kh42kn3s9gbo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnb75pb40kh42kn3s9gbo.png" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As shown, James, the child node of Parent, is it’s own root node within Subtree 1. This is true for all non-leaf children nodes of a tree.&lt;/p&gt;

&lt;p&gt;With that in mind, how can we visit / access each node? This is called “walking the tree” or tree traversal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tree Traversal
&lt;/h2&gt;

&lt;p&gt;We won’t be looking at tree traversal in-depth for this post, however, just at a high-level I’ll briefly describe the two most popular types.&lt;/p&gt;

&lt;p&gt;Unlike linear data structures, which are traversed in a linear fashion (looping over the data structure itself and computing at each element), trees can be traversed in multiple ways. Two of the most common traversal types are breadth-first and depth-first.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Depth-First&lt;/strong&gt; : go as deep into the tree as possible before moving to the next sibling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Breadth-First&lt;/strong&gt; : explore all nodes at the current depth before moving on, also known as level-order&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We now have enough background info to tackle this weeks post…&lt;/p&gt;

&lt;h1&gt;
  
  
  What is a Merkle Tree?
&lt;/h1&gt;

&lt;p&gt;A Merkle tree is a binary tree (just a tree where a node can have up to two children nodes), obviously, in which every leaf node contains the cryptographic hash of a block of data and every non-leaf node contains the cryptographic hash of the hashes of it's children (sounds confusing I’ll show a visual in a second).&lt;/p&gt;

&lt;p&gt;Now, what this specific tree allows us to do is securely verify that arbitrary content exists within the tree.&lt;/p&gt;

&lt;p&gt;Let’s get a visualization of a Merkle Tree:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkaezakcydgqliq1cf58m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkaezakcydgqliq1cf58m.png" width="800" height="903"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As shown, the leaf nodes of the Merkle Tree are hashes of the underlying data block. Now, at the very top we have the root node, known as the Merkle Root. So why is this tree important? It allows us to verify data efficiently and securely without having to check each piece of data individually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inclusivity
&lt;/h2&gt;

&lt;p&gt;So how can we verify that a piece of data is included in the merkle tree? By providing a Merkle Proof, aka an &lt;em&gt;inclusion proof&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In an inclusion proof all we need is the leaf node’s (the data block being verified) sibling and all other intermediate hashes needed to calculate the merkle root hash. I’ll explain visually below:&lt;/p&gt;

&lt;p&gt;Lets say we want to check if data block _ &lt;strong&gt;L1&lt;/strong&gt; _ is in the Merkle Tree. The way we would do that is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Calculate &lt;code&gt;hash(L1)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the Merkle Tree’s &lt;code&gt;Hash 0-1&lt;/code&gt; and &lt;code&gt;Hash 1&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rlnwzhmxf7h8duaqr33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rlnwzhmxf7h8duaqr33.png" width="800" height="841"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we have &lt;code&gt;Hash 1&lt;/code&gt; and &lt;code&gt;Hash 0-1&lt;/code&gt; we can calculate Merkle root and compare the two hashes to verify the exact content of &lt;code&gt;L1.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This method of proofs is actually very advantageous when compared to linear data structures because it scales &lt;em&gt;logarithmically.&lt;/em&gt; This means that we need to compute &lt;code&gt;log(N)&lt;/code&gt; hashes to prove the inclusion of a data element, where &lt;code&gt;N&lt;/code&gt;is the number of leaf nodes. As data grows this logarithmic scaling proves to be much more manageable.&lt;/p&gt;

&lt;p&gt;So in our example above, we have four leaf nodes, so we only need log(4) hashes to verify any data block. Let’s see how that scales:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;8 leaf nodes → log(8) → 3 hashes needed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;16 leaf nodes → log(16) → 4 hashes needed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;128 leaf nodes → log(128) → 7 hashes needed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1,000,000 leaf nodes → log(1000000) → 20 hashes needed&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As the leaf nodes grow, the number of hashes to verify any individual data block grows at a sustainable pace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uses
&lt;/h2&gt;

&lt;p&gt;So where do we see Merkle Trees today? Basically everywhere. The most common and prevalent usage of Merkle Trees is within the Blockchain and Cryptocurrency space. It’s a fundamental part of Blockchain technology, they’re used to verify and organize transactions efficiently within data blocks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Blockchain Technology&lt;/strong&gt; :&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Distributed File Systems&lt;/strong&gt; :&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Version Control Systems&lt;/strong&gt; :&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Peer-to-Peer Networks&lt;/strong&gt; :&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trusted Platform Modules (TPMs)&lt;/strong&gt;:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So basically, Merkle Trees are everywhere from the file system on your computer to your version control system (Git, Mercurial) to distributed databases like Amazon DynamoDB and Google’s BigTable.&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Merkle Trees play a crucial role in ensuring data integrity and efficient verification in distributed systems. Their hierarchical hashing mechanism allows for secure and scalable data verification, making them indispensable in modern technology.&lt;/p&gt;

&lt;p&gt;For next weeks post we’ll be implementing Merkle Trees .&lt;/p&gt;

&lt;p&gt;If you found this post helpful or have questions, please leave a comment below. Don't forget to subscribe to stay updated on future posts exploring fascinating topics in computer science and cryptography.&lt;/p&gt;

&lt;p&gt;Thanks for tuning in,&lt;/p&gt;

&lt;p&gt;Ali.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/merkle-trees/comments" rel="noopener noreferrer"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/merkle-trees?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share" rel="noopener noreferrer"&gt;Share&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading Professional Imposter Syndrome! Subscribe for free to receive new posts and support my work.&lt;/p&gt;

</description>
      <category>cryptocurrency</category>
      <category>distributedsystems</category>
      <category>datastructures</category>
    </item>
    <item>
      <title>Merkle Trees....continued (#4)</title>
      <dc:creator>Ali Malik</dc:creator>
      <pubDate>Tue, 07 Apr 2026 05:10:18 +0000</pubDate>
      <link>https://dev.to/amalik18/merkle-treescontinued-4-3kgi</link>
      <guid>https://dev.to/amalik18/merkle-treescontinued-4-3kgi</guid>
      <description>&lt;p&gt;Hey folks, we’re back for another post. Today, as promised in &lt;a href="https://dev.to/amalik18/merkle-trees-3-5733-temp-slug-6946772"&gt;the previous post&lt;/a&gt;, we’ll be implementing Merkle Trees. I will do my best to provide the implementation in multiple languages.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I’m currently trying to get better at Golang so I’ll be implementing the tree in that.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/subscribe?" rel="noopener noreferrer"&gt;Subscribe now&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Shout-outs
&lt;/h3&gt;

&lt;p&gt;We’ve gone from 48 subscribers last week to 57 this week. I’m beyond grateful for all the subscribers. Hopefully you all are enjoying the content. I’ve gotten a few requests from subscribers and I’d like to see what folks want to see more of:&lt;/p&gt;

&lt;h1&gt;
  
  
  Recap
&lt;/h1&gt;

&lt;p&gt;Let’s quickly recap what we discussed last time. We talked about the tree data structure and how it represents a hierarchy consisting of nodes classified as root, children, and leaf. We then went on to define and discuss a special type of tree, a Merkle tree.&lt;/p&gt;

&lt;p&gt;We defined a Merkle tree as a binary tree in which every leaf node contains the cryptographic hash of a data element while every non-leaf node contains the cryptographic hash of the sum of cryptographic hashes of its children. Below is our definition in a visual format:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2kexa181rcuvw2sasgl5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2kexa181rcuvw2sasgl5.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Implementation&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Let’s get started implementing the Merkle Tree. We’ll start by defining our high level data structures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;MerkleNode: this will represent a singular node. As we’ve outlined a node can have a left child and a right child. Secondly, a node either contains the hash of a data element or the hash of the concatenation of its childrens hashes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MerkleTree: the tree itself is represented by the root node, the MerkleRoot&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Let's define a Node N&lt;/span&gt;
&lt;span class="c"&gt;// A node can have a left child and a right child&lt;/span&gt;
&lt;span class="c"&gt;// A node also contains the cryptographic hash&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;MerkleNode&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;LeftChild&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MerkleNode&lt;/span&gt;
 &lt;span class="n"&gt;RightChild&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MerkleNode&lt;/span&gt;
 &lt;span class="n"&gt;Hash&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Tree&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;MerkleRoot&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MerkleNode&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above we've defined the  &lt;strong&gt;MerkleNode&lt;/strong&gt;  which contains the members  &lt;strong&gt;LeftChild, RightChild and Data&lt;/strong&gt;. We've also defined the &lt;strong&gt;Tree&lt;/strong&gt; itself. The  &lt;strong&gt;Tree&lt;/strong&gt;  struct only has one member...the MerkleRoot.&lt;/p&gt;

&lt;p&gt;With our data structures set up let’s now tackle actually creating a node. As stated above, a node can have children and that determines what is hashed and stored.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewMerkleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leftChild&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rightChild&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MerkleNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MerkleNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;MerkleNode&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;leftChild&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rightChild&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;oldHash&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;leftChild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rightChild&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;
  &lt;span class="n"&gt;newHash&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sum256&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oldHash&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EncodeToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newHash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sum256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EncodeToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LeftChild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;leftChild&lt;/span&gt;
 &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RightChild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rightChild&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our function &lt;strong&gt;NewMerkleNode&lt;/strong&gt; takes in three inputs; the left child, right child, and the data itself. We then create an empty &lt;strong&gt;MerkleNode&lt;/strong&gt; and explicitly check if we passed in valid values for left and right child. If we did, we update the hash accordingly. We then return the memory address of the node.&lt;/p&gt;

&lt;p&gt;To highlight how this would work, imagine if we wanted to create a brand new leaf node the way we would do that is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;leafNode&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewMerkleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, if we wanted to create a regular node with left and right children it would look something like this (assuming the left and right children have already been created):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewMerkleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leftChild&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rightChild&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we’ve got a way to create nodes, we need a way to construct the tree itself. The gist of constructing a tree is you climb up the tree creating new levels until you reach the top, the root node.&lt;/p&gt;

&lt;p&gt;As a thought exercise, let’s imaging we’re given a bunch of data blocks and we’re asked to construct the Merkle Tree, it would look something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create leaf node corresponding to each data block&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go up one level creating nodes corresponding to the lower level leaf nodes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go up one level creating nodes corresponding to the lower level nodes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And so on….. until we’ve reached a singular node.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing we need to watch out for is an odd number of nodes at any level besides the root node. For example, lets visualize having 5 data blocks to encrypt:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femgqaucxp7pgfm9y6tst.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femgqaucxp7pgfm9y6tst.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can tell there are only 5 nodes, there aren’t an even number of nodes, so we append the last node again to make it look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcg7br3pih5f5ewa5gabl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcg7br3pih5f5ewa5gabl.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No we have an even number of nodes lets start moving up the chain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t4lt1tesbsuqlp7kdeo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t4lt1tesbsuqlp7kdeo.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once again we find ourselves in a situation where we need to replicate the last node at this level (in this case &lt;strong&gt;Hash(“Data Block 5 + Data Block 5”)&lt;/strong&gt;). As we replicate and move up this is what we end up with:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feu3l7aksfgqcweejsakx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feu3l7aksfgqcweejsakx.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So as you can notice, we had to replicate two nodes (the ones in red, obviously). We have to take this into account when coming up with the logic to create the tree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewMerkleTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataBlocks&lt;/span&gt; &lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataBlocks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"must have more than 0 data blocks"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;MerkleNode&lt;/span&gt;

 &lt;span class="c"&gt;// Handle odd number of data blocks by replicating the last one.&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataBlocks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;dataBlocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataBlocks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataBlocks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataBlocks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="c"&gt;// Create leaf Nodes&lt;/span&gt;
 &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;dataBlocks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;NewMerkleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="c"&gt;// Time to construct the tree level by level&lt;/span&gt;
 &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataBlocks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;newLevel&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;MerkleNode&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c"&gt;// If the number of nodes at the level are odd, once again append the last one at the end&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="n"&gt;newLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;NewMerkleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newLevel&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="c"&gt;// return the tree&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;MerkleRoot&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To deal with the possibility of there being an odd number of nodes at any given level, there’s a check to make sure that we have an even number of nodes, if we don’t just append the last node to the array and carry on.&lt;/p&gt;

&lt;p&gt;What this function does is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create leaf nodes from the input data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create nodes level by level making sure to check if there are an even number of nodes every time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Return the &lt;strong&gt;Tree&lt;/strong&gt; struct with the &lt;strong&gt;MerkleRoot&lt;/strong&gt; set to the only element in the nodes array.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Using this function would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt; &lt;span class="n"&gt;testData&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Transaction 1"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Transaction 2"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Transaction 3"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Transaction 4"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Transaction 5"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewMerkleTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, we’ve implemented it but how can we check if it’s right? How can we verify that the hashes line up? With a simple breadth-first search (BFS) of the tree.&lt;/p&gt;

&lt;p&gt;Let’s verify along the way:&lt;/p&gt;

&lt;h4&gt;
  
  
  Data Blocks
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Data: Transaction 1 Hash:dff3b30655dc240deca00ed22fae68fdf8cf465bbe99bb2b2e24259cc1daac3a
Data: Transaction 2 Hash:4ae0e48b754a046b0f08e50e91708ddff4bac4daee30b786dbd67c30d8e00df8
Data: Transaction 3 Hash:2b8fd91deadf550d81682717104df059adc0addd006a0c7b99297e88769b30e5
Data: Transaction 4 Hash:b99ca09efe93055ad86acb5bfc964e16393d8e4672c3a4c5fa08ffabc85065b3
Data: Transaction 5 Hash:40d1474d042b66b26df83eae197368b93d84d8c960d39aec68573796078114a4
Data: Transaction 5 Hash:40d1474d042b66b26df83eae197368b93d84d8c960d39aec68573796078114a4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that &lt;strong&gt;Transaction 5&lt;/strong&gt; is repeated.&lt;/p&gt;

&lt;h4&gt;
  
  
  Level 0 (Immediately above Data Blocks)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Printing levels...
Level 0...

Hash Left: dff3b30655dc240deca00ed22fae68fdf8cf465bbe99bb2b2e24259cc1daac3a Hash Right: 4ae0e48b754a046b0f08e50e91708ddff4bac4daee30b786dbd67c30d8e00df8 New Hash: a31d3187c179a847bc0fbe729e06a0770147ad58b45995ac945032df15ba38e3

Hash Left: 2b8fd91deadf550d81682717104df059adc0addd006a0c7b99297e88769b30e5 Hash Right: b99ca09efe93055ad86acb5bfc964e16393d8e4672c3a4c5fa08ffabc85065b3 New Hash: c6dadc3fe8fde36887f07ed12e0bb073b4165d0921749b98b2ae237f3aed3e07

Hash Left: 40d1474d042b66b26df83eae197368b93d84d8c960d39aec68573796078114a4 &amp;lt; Hash Right: 40d1474d042b66b26df83eae197368b93d84d8c960d39aec68573796078114a4 &amp;lt; New Hash: 745ebfe140c7e62a45766934adff116dcd736890477b37ccd44550284d38e7c2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see each of the data block hashes being used. And the &lt;em&gt;&amp;lt;&lt;/em&gt; marks the replicated node.&lt;/p&gt;

&lt;h4&gt;
  
  
  Level 1
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Level 1...
Hash Left: a31d3187c179a847bc0fbe729e06a0770147ad58b45995ac945032df15ba38e3 Hash Right: c6dadc3fe8fde36887f07ed12e0bb073b4165d0921749b98b2ae237f3aed3e07 New Hash:7e1182c5c00e379261503e757487cfa16cda7010f1ca3ae0115d5b78cfc07509

Hash Left: 745ebfe140c7e62a45766934adff116dcd736890477b37ccd44550284d38e7c2 &amp;lt; Hash Right: 745ebfe140c7e62a45766934adff116dcd736890477b37ccd44550284d38e7c2 &amp;lt; New Hash:c4a338ab87fe2e56055a48e160f007ebb6a8a90303659fd8b97dde9d99a9a164
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same as before you can see where the node was replicated and you can see where the hashes from Level 0 are used here.&lt;/p&gt;

&lt;h4&gt;
  
  
  Level 2 (Merkle Root)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Level 2...

Hash Left: 7e1182c5c00e379261503e757487cfa16cda7010f1ca3ae0115d5b78cfc07509 Hash Right: c4a338ab87fe2e56055a48e160f007ebb6a8a90303659fd8b97dde9d99a9a164 
New Hash: 2c2c4cdf817ca1233db4784bb8752eddca8428c5c88ad7fad7e7235532e33c3c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there we go, we’ve reached the top, the Merkle Root.&lt;/p&gt;

&lt;h1&gt;
  
  
  Verification
&lt;/h1&gt;

&lt;p&gt;Now that we have a merkle tree, how can we verify an element of the original data block?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario&lt;/strong&gt; : you’re hosting a file on Dropbox and need to retrieve it. Dropbox partitions the file and stores the file in &lt;em&gt;chunks&lt;/em&gt; across multiple nodes. When retrieving your file Dropbox will use a Merkle Proof to verify that a chunk belongs to the requested file.&lt;/p&gt;

&lt;p&gt;So given data blocks and a specific data block to verify how can we ensure that the specific data block is part of the Merkle Tree? This is where a Merkle Proof comes in to play.&lt;/p&gt;

&lt;p&gt;A Merkle Proof (defined as a Merkle Consistency Proof in &lt;a href="https://datatracker.ietf.org/doc/html/rfc6962#section-2.1" rel="noopener noreferrer"&gt;RFC6962&lt;/a&gt;) is basically the list of nodes in the Merkle Tree required to verify that the input is contained in the tree.&lt;/p&gt;

&lt;p&gt;Let’s visualize this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj2112ykwfb9n4akywsls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj2112ykwfb9n4akywsls.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s say we want to verify that &lt;strong&gt;Data Block 1&lt;/strong&gt; (blue) is included in the Merkle Root Node for the tree, what nodes do we need to verify that?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We can generate the hash for &lt;strong&gt;Data Block 1&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We need the hash for &lt;strong&gt;Data Block 2 (green)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We need the hash for &lt;strong&gt;Hash (Hash 3 + Hash 4) (green)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We need the hash for &lt;strong&gt;Hash (Hash 5&amp;amp;5 + Hash 5&amp;amp;5) (green)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means for &lt;strong&gt;Data Block 1&lt;/strong&gt; the Merkle Proof is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Hash(Block 2), Hash(Hash 3 + Hash 4), Hash(Hash 5&amp;amp;5 + Hash 5&amp;amp;5)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Let’s implement the verification process. This will be two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Build the proof:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Verify the proof&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Build the Proof
&lt;/h3&gt;

&lt;p&gt;Lets say we’re given an array of data blocks and a specific data block we want to verify. How do we approach this? This is almost entirely identical to building the actual Merkle tree, the only difference is that we’ll append the hashes we need to an array and return them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;MerkleProof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataBlocks&lt;/span&gt; &lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c"&gt;// Find index of the data block we're looking for&lt;/span&gt;
 &lt;span class="c"&gt;// Determines whether we have a left or right sibling&lt;/span&gt;
 &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
 &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;dataBlocks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
   &lt;span class="k"&gt;break&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data not found in datablocks"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="c"&gt;// Get the leaf hashes&lt;/span&gt;
 &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;proof&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
 &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hashedNodes&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MerkleNode&lt;/span&gt;
 &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;dataBlocks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;hashedNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashedNodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewMerkleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="c"&gt;// We will now construct the tree and traverse up to verify&lt;/span&gt;
 &lt;span class="c"&gt;// Basically build the tree and add the necessary nodes to our proof&lt;/span&gt;
 &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashedNodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;nextLevel&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MerkleNode&lt;/span&gt;

  &lt;span class="c"&gt;// If even we have a right sibling, if odd we have a left sibling&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashedNodes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;proof&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashedNodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;proof&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashedNodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashedNodes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashedNodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;nextLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nextLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewMerkleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashedNodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;hashedNodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;nextLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nextLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewMerkleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashedNodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;hashedNodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;hashedNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nextLevel&lt;/span&gt;
  &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;/=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code will give us an array of hashes, that when used will re-construct our MerkleRoot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify the Proof
&lt;/h3&gt;

&lt;p&gt;This part is the easiest, we will quite literally just hash each member of the &lt;em&gt;proof&lt;/em&gt; array until we reach the MerkleRoot.&lt;/p&gt;

&lt;p&gt;To do this we’ll write two helper functions to make our lives easier:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;leafHash&lt;/code&gt;which will hash the contents as if it were a leaf.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;nodeHash&lt;/code&gt;which will hash the contents as if it were two nodes.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;nodeHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leftHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rightHash&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;newHash&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sum256&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leftHash&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rightHash&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EncodeToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newHash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;leafHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sum256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EncodeToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&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 the helper functions in place, lets implement the verifier. We will return &lt;em&gt;true&lt;/em&gt; if the data block is part of the Merkle Tree and &lt;em&gt;false&lt;/em&gt; otherwise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;VerifyMerkleProof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proof&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rootHash&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;leaf&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;leafHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;siblingHash&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;proof&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;leaf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nodeHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;leaf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;siblingHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;leaf&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rootHash&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it we’re done. Now to actually run through it we’ll reuse the data from above, lets check if &lt;strong&gt;Transaction 1&lt;/strong&gt; is in the Merkle Tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MerkleProof&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Transaction 1"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;testData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When executed, &lt;em&gt;output&lt;/em&gt; looks like this (a list of hashes that when hashed with the original data block &lt;em&gt;should&lt;/em&gt; give us the MerkleRoot):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[4ae0e48b754a046b0f08e50e91708ddff4bac4daee30b786dbd67c30d8e00df8 c6dadc3fe8fde36887f07ed12e0bb073b4165d0921749b98b2ae237f3aed3e07 c4a338ab87fe2e56055a48e160f007ebb6a8a90303659fd8b97dde9d99a9a164]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now to verify the proof:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;proof&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;VerifyMerkleProof&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Transaction 1"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MerkleRoot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns &lt;em&gt;true&lt;/em&gt;. So, there you have it. A fully functional Merkle Tree implementation with proof and verification.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;So today we implemented what we talked about last week, Merkle Trees. In our implementation we referred to RFC6962 for guidance, added a Merkle Proof method that returns the list of hashes needed to reconstruct the Merkle Tree, and added a verification method that verifies whether the proof is correct.&lt;/p&gt;

&lt;p&gt;If you liked what you read please like, comment, and share. I’m always looking for feedback and topic suggestions.&lt;/p&gt;

&lt;p&gt;Once again, thanks all for subscribing and tuning in this week. I will see y’all next week.&lt;/p&gt;

&lt;p&gt;Thanks,&lt;/p&gt;

&lt;p&gt;Ali&lt;/p&gt;

&lt;p&gt;Professional Imposter Syndrome is a reader-supported publication. To receive new posts and support my work, consider becoming a subscriber.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/merkle-treescontinued/comments" rel="noopener noreferrer"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/merkle-treescontinued?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share" rel="noopener noreferrer"&gt;Share&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cryptocurrency</category>
      <category>distributedsystems</category>
      <category>datastructures</category>
    </item>
    <item>
      <title>How OpenAI Serves 800 Million Users Without Sharding Postgres</title>
      <dc:creator>Ali Malik</dc:creator>
      <pubDate>Tue, 07 Apr 2026 05:00:00 +0000</pubDate>
      <link>https://dev.to/amalik18/how-openai-serves-800-million-users-without-sharding-postgres-1bo4</link>
      <guid>https://dev.to/amalik18/how-openai-serves-800-million-users-without-sharding-postgres-1bo4</guid>
      <description>&lt;p&gt;OpenAI recently published a blog post titled &lt;a href="https://openai.com/index/scaling-postgresql/" rel="noopener noreferrer"&gt;Scaling PostgreSQL to Power 800 Million ChatGPT Users&lt;/a&gt;, and it’s one of those posts that I think anyone working with databases or distributed systems should read. Not because the techniques are new or groundbreaking, but because of what OpenAI &lt;em&gt;chose not to do&lt;/em&gt;. No sharding. No fancy distributed databases. No custom storage engine. Just a single PostgreSQL primary, roughly 50 read replicas, and a lot of operational discipline.&lt;/p&gt;

&lt;p&gt;When I first read this, I was shocked by how much restraint OpenAI showed in every decision. Oftentimes as engineers we want the fancy solution that’ll look cool and be awesome to talk about and show off, but that doesn’t mean it’s the best solution. The overarching goal of this post is to walk through what OpenAI did, why it works, and what we can take away from it as engineers building systems at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Definitions and Background
&lt;/h2&gt;

&lt;p&gt;Before getting into the specifics of OpenAI’s setup, let’s define a few things that’ll come up throughout this post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sharding:&lt;/strong&gt; its when you split your massive database across multiple independent machines (shards), each responsible for a subset of the original data. This allows you to scale writes horizontally, but it introduces significant complexity: cross-shard queries, distributed transactions, rebalancing, and application-layer routing logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write-Ahead Log (WAL):&lt;/strong&gt; its the mechanism PostgreSQL uses to ensure durability. Every change is first written to a sequential log before being applied to the actual data files. This same log is streamed to read replicas to keep them in sync with the primary. Think of it like a shared notebook: the primary writes every change into the notebook first, and replicas read from that notebook to stay up to date.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MVCC (Multi-Version Concurrency Control):&lt;/strong&gt; its how PostgreSQL handles concurrent access to data. Rather than locking rows during updates, PostgreSQL creates a new version of the row and lets readers continue accessing the old version. This is great for read concurrency, but it has some well-documented downsides for write-heavy workloads (more on this later).&lt;/p&gt;

&lt;p&gt;Now that we have some necessary background, let’s look at what OpenAI actually built.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;OpenAI’s core PostgreSQL deployment is surprisingly straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One primary instance&lt;/strong&gt; on Azure PostgreSQL Flexible Server, handling all writes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;~50 read replicas&lt;/strong&gt; distributed across multiple geographic regions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PgBouncer&lt;/strong&gt; (a connection pooling proxy) in front of every replica&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A caching layer&lt;/strong&gt; absorbing the majority of read traffic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write-heavy workloads migrated to &lt;strong&gt;Azure Cosmos DB&lt;/strong&gt; (a sharded NoSQL system)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it. A single writer node handling writes for 800 million monthly active users, with reads fanned out to replicas. Their database load has grown by more than 10x in the past year, and they’re handling millions of queries per second (QPS) across the cluster.&lt;/p&gt;

&lt;p&gt;So how does a single primary not fall over? The answer comes down to the way it’s accessed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcmsv4t8s7wcblbx5mrhb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcmsv4t8s7wcblbx5mrhb.png" alt="OpenAI's PostgreSQL architecture. A single primary handles all writes and streams WAL to ~50 read replicas distributed across regions. PgBouncer pools connections in front of each replica (dropping connection time from 50ms to 5ms), and a caching layer absorbs the majority of read traffic before it ever hits a replica. Write-heavy workloads are migrated to Cosmos DB to reduce pressure on the primary." width="800" height="428"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;OpenAI’s PostgreSQL architecture. A single primary handles all writes and streams WAL to ~50 read replicas distributed across regions. PgBouncer pools connections in front of each replica (dropping connection time from 50ms to 5ms), and a caching layer absorbs the majority of read traffic before it ever hits a replica. Write-heavy workloads are migrated to Cosmos DB to reduce pressure on the primary.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;p&gt;The key here is that ChatGPT’s workload is a lot more read-heavy. Think about what the typical ChatGPT interaction looks like from a database perspective: fetch user data, fetch conversation history, fetch preferences. Read after read after read. The actual writes (creating a new message, updating a session) are a small fraction of total operations.&lt;/p&gt;

&lt;p&gt;This matters because PostgreSQL’s replication model scales reads almost linearly. Each read replica is a full copy of the database that can independently serve queries. Add another replica, absorb another chunk of read traffic. The primary doesn’t care, it just keeps streaming WAL to the replicas.&lt;/p&gt;

&lt;p&gt;To put even more simply, if your read-to-write ratio is high enough, horizontal read scaling through replication can get you very far. OpenAI is proof of that.&lt;/p&gt;

&lt;p&gt;If you enjoy breakdowns like this, consider subscribing. I write about distributed systems, cryptography, and much, much more!&lt;/p&gt;

&lt;h2&gt;
  
  
  Where PostgreSQL Struggles
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flz8z5gg9j8cq6j385qzc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flz8z5gg9j8cq6j385qzc.png" alt="How PostgreSQL's MVCC creates dead tuples. Updating a single field doesn't modify the row in place. Instead, PostgreSQL copies the entire row, marks the old version as dead, and leaves it for autovacuum to clean up. At OpenAI's scale, each write cascades: one logical change produces a full row copy, a WAL record, and ~50x network amplification as that WAL streams to every replica." width="800" height="537"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;How PostgreSQL’s MVCC creates dead tuples. Updating a single field doesn’t modify the row in place. Instead, PostgreSQL copies the entire row, marks the old version as dead, and leaves it for autovacuum to clean up. At OpenAI’s scale, each write cascades: one logical change produces a full row copy, a WAL record, and ~50x network amplification as that WAL streams to every replica.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;OpenAI doesn’t hide from discussing PostgreSQL’s weaknesses, and the biggest one at their scale is the cost of writes under MVCC. The post directly references a blog that Bohan Zhang (the OpenAI engineer behind this effort) co-authored with Prof. Andy Pavlo at Carnegie Mellon University called &lt;a href="https://www.cs.cmu.edu/~pavlo/blog/2023/04/the-part-of-postgresql-we-hate-the-most.html" rel="noopener noreferrer"&gt;The Part of PostgreSQL We Hate the Most&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So what exactly is the problem? When PostgreSQL updates a row, even a single field, it doesn’t modify the row in place. Instead, it copies the entire row to create a new version. The old version becomes a “dead tuple” that sticks around until the autovacuum process cleans it up.&lt;/p&gt;

&lt;p&gt;For example: imagine you have a user row with 20 columns, and you update just the &lt;code&gt;last_login&lt;/code&gt; timestamp. PostgreSQL doesn’t touch the existing row. It writes a brand new copy of all 20 columns with the updated timestamp, and marks the old row as dead. That dead row still takes up space in the table and in every index that references it, and queries have to skip over it until autovacuum eventually reclaims it.&lt;/p&gt;

&lt;p&gt;This design leads to several compounding issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write amplification&lt;/strong&gt; : A small logical change produces a disproportionately large physical write.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Read amplification&lt;/strong&gt; : Queries have to scan past dead tuples to find the current version of a row.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Table and index bloat&lt;/strong&gt; : Dead tuples accumulate in both tables and indexes, consuming storage and degrading query performance over time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Autovacuum complexity&lt;/strong&gt; : The garbage collector (autovacuum) that reclaims dead tuples requires careful tuning, and long-running transactions can block it entirely.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The practical consequence at OpenAI’s scale is that writes are expensive and they ripple through the entire system. More writes mean more WAL, which means more data streaming to ~50 replicas, which means more network bandwidth consumed and potentially more replication lag. One logical field change on the primary can cascade into 50x the network traffic.&lt;/p&gt;

&lt;p&gt;OpenAI’s solution wasn’t to fix PostgreSQL’s storage engine. It was to &lt;strong&gt;reduce the write surface area&lt;/strong&gt; as aggressively as possible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;They migrated shardable write-heavy workloads to Cosmos DB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They fixed application bugs that were causing redundant writes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They introduced lazy writes to smooth out traffic spikes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They rate-limited backfills (even if the process takes over a week)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They banned new tables from being added to the PostgreSQL cluster entirely&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is worth sitting with. They’re effectively treating the PostgreSQL cluster as a closed system for writes. Existing workloads stay, but all new workloads go to sharded systems. To keep the primary healthy, they’ve drawn a line in the sand.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Patterns Worth Studying
&lt;/h2&gt;

&lt;p&gt;Beyond the high-level architecture, there are several specific patterns in the post that are worth understanding in detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache Stampede Protection
&lt;/h3&gt;

&lt;p&gt;When their caching layer has misses, a naive implementation would send every missed request straight to PostgreSQL, potentially overwhelming it. OpenAI implements a &lt;strong&gt;cache locking (and leasing) mechanism&lt;/strong&gt;: on a cache miss for a given key, only one request acquires a lock and fetches from the database. All other requests for the same key wait for the cache to be repopulated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhfw4t3b3sva3ttp31zr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhfw4t3b3sva3ttp31zr.png" alt="Cache stampede protection via lock-based request coalescing. Without protection (left), a cache miss for a given key sends N duplicate queries to PostgreSQL simultaneously. With protection (right), only one request acquires a lock and fetches from the database, while all other requests for the same key wait for the cache to be repopulated. The database gets hit once regardless of how many concurrent requests arrive." width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Cache stampede protection via lock-based request coalescing. Without protection (left), a cache miss for a given key sends N duplicate queries to PostgreSQL simultaneously. With protection (right), only one request acquires a lock and fetches from the database, while all other requests for the same key wait for the cache to be repopulated. The database gets hit once regardless of how many concurrent requests arrive.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here’s an analogy. Imagine 1,000 people all try to look up the same book in a library at the same time, and the book isn’t on the shelf. Without protection, all 1,000 people walk to the back room and ask the librarian for it simultaneously. With stampede protection, one person goes to the back room, and the other 999 wait at the shelf until the book is returned and reshelved. The librarian (PostgreSQL) only gets asked once.&lt;/p&gt;

&lt;p&gt;This pattern is sometimes called “stampede protection” or “request coalescing.” It’s one of those things that’s easy to skip during initial implementation and then regret during your first major cache failure. If you’re building any system with a caching layer in front of a database, this is worth implementing early.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workload Isolation
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmt4jtos8z7rc9dvz8kf0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmt4jtos8z7rc9dvz8kf0.png" alt="Workload isolation via dedicated replica pools. Without isolation (left), all traffic shares the same replicas, and a heavy analytics query saturating CPU degrades ChatGPT's user-facing latency. With isolation (right), traffic is classified and routed to separate pools: high-priority for user-facing queries, product-isolated for other services, and low-priority for analytics and backfills. A CPU spike in the low-priority pool stays contained and never touches the critical path." width="800" height="497"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Workload isolation via dedicated replica pools. Without isolation (left), all traffic shares the same replicas, and a heavy analytics query saturating CPU degrades ChatGPT’s user-facing latency. With isolation (right), traffic is classified and routed to separate pools: high-priority for user-facing queries, product-isolated for other services, and low-priority for analytics and backfills. A CPU spike in the low-priority pool stays contained and never touches the critical path.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;They split traffic into low-priority and high-priority tiers and route them to separate replica pools. This prevents the “noisy neighbor” problem, where an expensive analytical query or a poorly optimized new feature can saturate CPU and degrade latency for critical-path requests. They apply the same isolation across different products and services as well, so that activity from one product doesn’t affect the performance of another.&lt;/p&gt;

&lt;p&gt;Basically, this is the same principle as Quality of Service (QoS) in networking: classify traffic, isolate resource pools, and make sure lower-priority work can’t starve higher-priority work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Layer Rate Limiting
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijehecnuo4zo9y4vc4uh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijehecnuo4zo9y4vc4uh.png" alt="OpenAI's four layers of rate limiting between a request and the database. Each layer (application, proxy, PgBouncer, query) can independently drop or block traffic. During an incident, operators can identify a specific problematic query digest and block it at the ORM or query layer, turning a full-service Sev0 into a targeted load shed while all other traffic continues flowing normally." width="800" height="475"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;OpenAI’s four layers of rate limiting between a request and the database. Each layer (application, proxy, PgBouncer, query) can independently drop or block traffic. During an incident, operators can identify a specific problematic query digest and block it at the ORM or query layer, turning a full-service Sev0 into a targeted load shed while all other traffic continues flowing normally.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Rate limiting at the application layer alone isn’t sufficient. OpenAI implements rate limiting at four layers: application, connection pooler, proxy, and query. They even enhanced their ORM (Object-Relational Mapping) layer to support blocking specific query digests for targeted load shedding.&lt;/p&gt;

&lt;p&gt;The ability to identify and kill a specific problematic query pattern in real-time during an incident is a powerful operational tool. It turns what would be a full-service degradation into a targeted response.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 12-Table Join That Caused Sev0s
&lt;/h3&gt;

&lt;p&gt;One of the more memorable details: they identified an extremely costly query that joined 12 tables, and that spikes in this query were directly responsible for past Sev0 incidents. Their takeaway is to avoid complex multi-table joins in OLTP (Online Transaction Processing) workloads, and to break them into multiple simpler queries with the join logic handled in the application layer.&lt;/p&gt;

&lt;p&gt;This is a cautionary tale about ORMs. ORM frameworks make it easy to define relationships between models and then traverse them in application code. Under the hood, many ORMs eagerly join across those relationships, producing SQL that no human DBA would write. If you’re using an ORM in a high-traffic system, it’s important to carefully review the actual SQL being generated, not just the ORM code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/openai-postgresql-scaling-800m-users?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share" rel="noopener noreferrer"&gt;Share&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Connection Pooling
&lt;/h2&gt;

&lt;p&gt;Each Azure PostgreSQL instance has a maximum connection limit of 5,000. At OpenAI’s scale, it’s easy to run out of connections or accumulate too many idle ones. They’ve had incidents caused by connection storms that exhausted all available connections.&lt;/p&gt;

&lt;p&gt;To address this, they deployed PgBouncer as a proxy layer to pool database connections, running it in statement or transaction pooling mode. This allows efficient reuse of connections and greatly reduces the number of active client connections. In their benchmarks, average connection time dropped from 50 milliseconds to 5 milliseconds. That’s a 10x improvement just from connection pooling.&lt;/p&gt;

&lt;p&gt;Each read replica gets its own Kubernetes deployment running multiple PgBouncer pods, with a Kubernetes Service load-balancing traffic across them. They also co-locate the proxy, clients, and replicas in the same region to minimize network overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decision Not to Shard
&lt;/h2&gt;

&lt;p&gt;Perhaps the most interesting engineering decision in the entire post is the one they didn’t make: sharding PostgreSQL.&lt;/p&gt;

&lt;p&gt;They explicitly state that sharding existing workloads would require changes to hundreds of application endpoints and could take months or even years. Instead, they chose to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Optimize the single-primary architecture as far as possible&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Migrate &lt;em&gt;new&lt;/em&gt; write-heavy workloads to already-sharded systems (Cosmos DB)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gradually migrate &lt;em&gt;existing&lt;/em&gt; write-heavy workloads off PostgreSQL over time&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a more rational decision. Sharding introduces enormous complexity: cross-shard queries, distributed transactions, rebalancing, application-layer routing, and schema migration coordination across shards. For a read-heavy workload where the single primary still has headroom, the operational cost of sharding doesn’t justify the benefit.&lt;/p&gt;

&lt;p&gt;OpenAI isn’t saying sharding is bad. They’re saying sharding isn’t worth it &lt;em&gt;for them, right now&lt;/em&gt;, given their access patterns and the engineering cost of migration. They’re not ruling it out for the future, but it’s not a near-term priority.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scaling Ceiling
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foizat538wnrt452gwroa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foizat538wnrt452gwroa.png" alt="Star vs. cascading replication topologies for WAL fan-out. In the current star topology (left), the primary streams WAL directly to every replica, creating an O(N) network bottleneck. In the planned cascading topology (right), intermediate replicas relay WAL to downstream nodes, reducing the primary's fan-out to O(2) and enabling 100+ replicas. The trade-off is added latency hops and more complex failover if an intermediate node fails." width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Star vs. cascading replication topologies for WAL fan-out. In the current star topology (left), the primary streams WAL directly to every replica, creating an O(N) network bottleneck. In the planned cascading topology (right), intermediate replicas relay WAL to downstream nodes, reducing the primary’s fan-out to O(2) and enabling 100+ replicas. The trade-off is added latency hops and more complex failover if an intermediate node fails.&lt;/p&gt;

&lt;p&gt;The most interesting forward-looking challenge they discuss is WAL fan-out. As mentioned earlier, the primary streams Write-Ahead Log data to every single replica. At ~50 replicas, this works with large instance types and high-bandwidth networking, but it doesn’t scale indefinitely. Each additional replica adds more network and CPU pressure on the primary.&lt;/p&gt;

&lt;p&gt;Their planned solution is &lt;strong&gt;cascading replication&lt;/strong&gt; : intermediate replicas receive WAL from the primary and relay it to downstream replicas, forming a tree structure instead of a star topology. This would allow them to scale to potentially over 100 replicas without the primary becoming a network bottleneck.&lt;/p&gt;

&lt;p&gt;There’s a trade-off here though. A star topology (primary → all replicas) is simple and has low latency, but the fan-out creates a bottleneck at the center. A tree topology distributes the fan-out load but adds latency hops and complicates failover. If an intermediate replica goes down, its downstream replicas lose their WAL source. OpenAI notes they’re still testing this with the Azure PostgreSQL team and won’t deploy it until failover is reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;To put some concrete numbers on the outcomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Millions of QPS&lt;/strong&gt; across the cluster (combined reads and writes)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;~50 replicas&lt;/strong&gt; with near-zero replication lag&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Low double-digit millisecond&lt;/strong&gt; p99 client-side latency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Five-nines availability&lt;/strong&gt; (99.999%)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One Sev0 in 12 months&lt;/strong&gt; , which occurred during the viral launch of ChatGPT ImageGen when write traffic surged by more than 10x as over 100 million new users signed up within a week&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last data point is revealing. Even with all of these optimizations, a 10x write spike during a viral launch still overwhelmed the system. Writes remain the weak point, and OpenAI knows it.&lt;/p&gt;

&lt;h2&gt;
  
  
  All in All
&lt;/h2&gt;

&lt;p&gt;A few things stand out to me from this post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boring technology works.&lt;/strong&gt; PostgreSQL is 30+ years old. PgBouncer has been around since 2007. Read replicas are as old as relational databases themselves. None of this is new, and that’s the point. OpenAI isn’t succeeding because they have exotic infrastructure. They’re succeeding because they execute the fundamentals with discipline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Access patterns determine architecture.&lt;/strong&gt; The entire architecture is predicated on the workload being read-heavy. If ChatGPT’s workload were write-heavy, this whole approach would fall apart. Understanding your actual read/write ratio, your query distribution, and your hot paths isn’t just an optimization exercise. It determines your entire system design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operational discipline matters more than architectural cleverness.&lt;/strong&gt; The post reads like a list of things they &lt;em&gt;stopped doing&lt;/em&gt;: no new tables on PostgreSQL, no complex joins, no unthrottled backfills, no write-heavy workloads without migration plans. Scaling is as much about what you refuse to let into the system as it is about what you build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sharding is a last resort, not a first instinct.&lt;/strong&gt; The distributed systems community sometimes treats sharding as the default answer to scaling problems. OpenAI’s experience shows that for many workloads, the complexity cost of sharding exceeds the benefit, and that you can go remarkably far with a single primary, read replicas, and careful write management.&lt;/p&gt;

&lt;p&gt;In closing, I think the most valuable lesson here isn’t any single optimization technique. It’s the reminder that understanding your workload deeply, and making deliberate trade-offs based on that understanding, will take you further than reaching for complex distributed systems before you need them.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Have you dealt with similar scaling decisions at work? Did you shard too early, or wish you had sooner? I’d love to hear about it in the comments.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you enjoyed this post, subscribe to Professional Imposter Syndrome for more breakdowns of real-world systems engineering.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source: &lt;a href="https://openai.com/index/scaling-postgresql/" rel="noopener noreferrer"&gt;Scaling PostgreSQL to Power 800 Million ChatGPT Users&lt;/a&gt; — OpenAI Engineering Blog&lt;/em&gt;&lt;/p&gt;

</description>
      <category>openai</category>
      <category>database</category>
      <category>postgres</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Beyond "Pick Two": Real-World Trade Offs</title>
      <dc:creator>Ali Malik</dc:creator>
      <pubDate>Tue, 07 Apr 2026 05:00:00 +0000</pubDate>
      <link>https://dev.to/amalik18/beyond-pick-two-real-world-trade-offs-3o2p</link>
      <guid>https://dev.to/amalik18/beyond-pick-two-real-world-trade-offs-3o2p</guid>
      <description>&lt;p&gt;This is Part 4 of a 4-part series on the CAP Theorem and distributed systems trade-offs. Read &lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-explained?r=1tezps" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;, &lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-cp-systems?r=1tezps" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt;, or &lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-ap-systems?r=1tezps" rel="noopener noreferrer"&gt;Part 3&lt;/a&gt; if you haven’t already.&lt;/p&gt;

&lt;p&gt;We’ve dedicated three posts to treating CP and AP as binary choices. etcd is CP. DynamoDB is AP. Pick a side.&lt;/p&gt;

&lt;p&gt;But that’s not how real systems work.&lt;/p&gt;

&lt;p&gt;MongoDB can be CP or AP depending on your configuration. Cassandra lets you choose &lt;em&gt;per query&lt;/em&gt;. Even PostgreSQL, which we covered as a CP system in Part 2, flips to AP behavior with async replication. The line between strong consistency and high availability isn’t a binary switch…..it’s a &lt;em&gt;spectrum&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And then there are the systems that seem to ignore CAP entirely. Google Spanner claims to be both consistent &lt;em&gt;and&lt;/em&gt; available. CockroachDB markets itself as “ACID at scale.” What’s going on?&lt;/p&gt;

&lt;p&gt;The answer comes down to understanding what CAP actually constrains, what happens when networks &lt;em&gt;don’t&lt;/em&gt; partition, and how some very smart engineers found ways to work around the edges.&lt;/p&gt;

&lt;p&gt;FINAL PART OF THE SERIES. SUBSCRIBE TO SEE HOW IT ALL COMES TOGETHER.&lt;/p&gt;




&lt;h1&gt;
  
  
  PACELC: The Other Half of the Story
&lt;/h1&gt;

&lt;p&gt;CAP Theorem is very prescriptive. It only tells you what happens &lt;em&gt;during&lt;/em&gt; a network partition. But what about all of the other times? The times the network is working just fine? The other 99% of the the time?&lt;/p&gt;

&lt;p&gt;That’s where PACELC (pronounced “pass-elk” but I refuse to pronounce it in any other way than “pace-l-c”). Introduced by Daniel Abadi in 2010, it extends the CAP Theorem by actually including what to choose when the network is fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PACELC&lt;/strong&gt; stands for Partition, Availability, Consistency, Else, Latency, and Consistency.&lt;/p&gt;

&lt;p&gt;The way it’s read is “ &lt;strong&gt;If&lt;/strong&gt; there’s a network &lt;strong&gt;(P)&lt;/strong&gt;artition, choose between &lt;strong&gt;(A)&lt;/strong&gt;vailability and &lt;strong&gt;(C)&lt;/strong&gt;onsistency. &lt;strong&gt;(E)&lt;/strong&gt;lse, choose between &lt;strong&gt;(L)&lt;/strong&gt;atency and &lt;strong&gt;(C)&lt;/strong&gt;onsistency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8m3prsmr2vwgbcuyuj6k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8m3prsmr2vwgbcuyuj6k.png" width="800" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That “Else” is doing a &lt;em&gt;lot&lt;/em&gt; of heavy lifting. Even when your network is perfectly healthy, distributed systems still face a fundamental trade-off: do you want fast responses or strong consistency?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Latency vs Consistency Trade-off
&lt;/h2&gt;

&lt;p&gt;Let’s think about it for a second. To guarantee strong consistency, we showed that a system needs to coordinate between multiple nodes. We established that coordination takes time (a cross-region round-trip is about 50-200 ms). That’s the price of every coordinated read / write.&lt;/p&gt;

&lt;p&gt;You want the most up-to-date value? You need to check with multiple replicas or go to the leader. Those are extra round trips. You want an instant response instead? You can read from the nearest replica, but also come to terms with the fact that it could be slightly behind.&lt;/p&gt;

&lt;p&gt;This trade-off exists even in perfect networks. It takes time to send data and coordination isn’t free.&lt;/p&gt;

&lt;h2&gt;
  
  
  PACELC in Practice
&lt;/h2&gt;

&lt;p&gt;Let’s map the systems we’ve covered to both sides of PACELC:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DynamoDB:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;P+A:&lt;/strong&gt; During a network partition, choose availability via &lt;em&gt;eventually consistent&lt;/em&gt; reads. Eventually consistent reads stay available during partitions, any replica can serve them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;E+L:&lt;/strong&gt; During a clean working network, choose latency. Those default eventually consistent reads optimize for latency by hitting the nearest replica. Flip to strongly consistent reads and you’re trading latency for consistency, even when the network is healthy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cassandra:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;P+A:&lt;/strong&gt; During a network partition, all node remain available for reads and writes. Remember, Cassandra is leaderless, so there’s no leader to lose.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;E+L or E+C:&lt;/strong&gt; During a normal working network, you get to choose what you want: latency or consistency. Remember that Cassandra allows you to choose per query. &lt;code&gt;ONE&lt;/code&gt; optimizes for latency (E+L). &lt;code&gt;QUORUM&lt;/code&gt; optimizes for consistency (E+C). Same cluster, same data, different trade-offs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;MongoDB:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;P+A/C:&lt;/strong&gt; A little different than the others, MongoDB changes between availability and consistency depending on the operation. During partitions, the minority side remains available to serve reads from the secondaries (P+A). But, writes still require the leader, which lives on the majority side (P+C). So reads fall towards P+A and writes fall to P+C.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;E+L/C:&lt;/strong&gt; During normal operations, MongoDB defaults to reading from the primary. Which means you’re getting strongly consistent reads. You can set &lt;code&gt;readPreference&lt;/code&gt; to &lt;code&gt;nearest&lt;/code&gt; which would give you the fastest response. There’s also &lt;code&gt;readConcern&lt;/code&gt; which defaults to &lt;code&gt;local&lt;/code&gt; which says get the most recent data, regardless of whether it’s been replicated.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Traditional RDBMS (PostgreSQL with sync replication):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;P+C:&lt;/strong&gt; During network partitions, if the primary can’t reach it’s synchronous replica, writes block. They don’t immediately fail and they don’t get dropped….it just hangs. The system would rather stall than be inconsistent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;E+C:&lt;/strong&gt; During normal operation, every write still waits for every replica to acknowledge before confirming the write. That’s added latency for &lt;em&gt;every single write&lt;/em&gt; irrespective of partitions. Postgres pays the consistency tax every single time, not just during partitions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice a pattern? PACELC shows why “eventually consistent” systems like DynamoDB feel fast and reliable in normal operation, they’re optimizing for the common case (no partition, low latency) while accepting trade-offs during the rare case (partition, stale reads). Meanwhile, CP systems pay the latency tax &lt;em&gt;all the time&lt;/em&gt;, partition or not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkogwyzyclhf209jwdpzd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkogwyzyclhf209jwdpzd.png" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is why your strongly consistent database feels slower even when nothing is broken. It’s not a bug. It’s the ELC trade-off in action.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Consistency Spectrum
&lt;/h1&gt;

&lt;p&gt;CP vs AP suggests two positions on the consistency scale: strong or eventual. Reality shows us that there is way more to this spectrum.&lt;/p&gt;

&lt;p&gt;We covered some of these in Part 3: read-your-writes, monotonic reads, causal consistency. But here’s the full picture as a spectrum, from strongest to weakest:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linearizability:&lt;/strong&gt; The gold standard. Every operation appears to take effect instantly at a single point in time. Every read returns the most recent write. We established before that this is what CAP calls “consistency.” It is also the &lt;em&gt;most expensive&lt;/em&gt; form of consistency. It requires coordination on every operation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sequential Consistency:&lt;/strong&gt; A little more lax. All operations still appear in &lt;em&gt;some&lt;/em&gt; total order but, may or may not line up according to wall clock. For example, if User A writes at 12:00:01 and User B writes at 12:00:02, the system might order User A first or second. Really doesn’t matter, what matters is that &lt;em&gt;everyone sees the same order&lt;/em&gt;. Imagine it as a globally agreed-upon order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Causal Consistency:&lt;/strong&gt; Even more lax than above. Only cares about cause and effect. If two operations are independent of each other it doesn’t matter what order it’s shown in. However, if two operations are dependent on each other (B on A), everyone &lt;em&gt;must&lt;/em&gt; see A before B.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Read Your Writes:&lt;/strong&gt; Scoped to a single client. If Client A writes something, subsequent reads are &lt;em&gt;guaranteed&lt;/em&gt; to reflect that write. Other clients may or may not see it yet. This is the bare minimum for user experience: “I just posted a comment, why can’t I see it.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monotonic Reads:&lt;/strong&gt; As the name suggests, time only moves in one direction, forward. Once you’ve seen a value, you are &lt;em&gt;guaranteed&lt;/em&gt; to not see an older version of that value. If this wasn’t guaranteed you could refresh your page and see different data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Eventual Consistency:&lt;/strong&gt; The weakest model on this list. Basically, all replicas will &lt;em&gt;eventually&lt;/em&gt; converge to the same value. Eventually. Nobody knows how long that eventually is. It’s not bounded by anything.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sg96xsolzhz15incqqd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sg96xsolzhz15incqqd.png" width="800" height="2170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a useful mental model to have, not a perfect taxonomy. Some of these consistency models apply at the global level and others at the session level. But the information is useful to illustrate that there is plenty of room between &lt;em&gt;linearizability&lt;/em&gt; and &lt;em&gt;eventual consistency&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Middle Matters
&lt;/h2&gt;

&lt;p&gt;Most applications don’t need pure linearizability everywhere and many can’t tolerate bare eventual consistency everywhere either. So what do we do? The answer is…a mix of both.&lt;/p&gt;

&lt;p&gt;Let’s do a thought experiment. Let’s assume we have a social media feed. We don’t really care about having likes and comments ordered perfectly, right? But there are areas where we need guarantees stronger than eventual consistency:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When you publish a post, you should be able to see it immediately (read-your-writes).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once you’ve seen a comment, it shouldn’t just disappear just because the backing store is stale (monotonic reads).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A reply should appear &lt;em&gt;after&lt;/em&gt; a post it’s responding to has published (causal consistency).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those guarantees are definitely weaker than linearizability, but map much better to the expected user experience real applications need. Best part? All three can exist with high availability.&lt;/p&gt;

&lt;p&gt;This is the insight that drives modern database design. Instead of forcing you into “consistent” or “available,” systems give you a dial. Cassandra’s tunable consistency. DynamoDB’s per-request read mode. MongoDB’s read concern and write concern settings. They’re all implementations of this spectrum.&lt;/p&gt;

&lt;p&gt;The question isn’t “CP or AP?” It’s “how much consistency do I actually need for &lt;em&gt;this specific operation&lt;/em&gt;?”&lt;/p&gt;




&lt;h1&gt;
  
  
  Systems That Claim to Beat CAP
&lt;/h1&gt;

&lt;p&gt;Some distributed systems seem to ignore CAP entirely. Strong consistency &lt;em&gt;and&lt;/em&gt; high availability. Globally distributed &lt;em&gt;and&lt;/em&gt; fast. What’s the catch?&lt;/p&gt;

&lt;p&gt;There’s always a catch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Google Spanner: Throw Hardware at the Problem
&lt;/h2&gt;

&lt;p&gt;Google Spanner, a globally distributed SQL database, advertised as “always-on” is always brought up when discussing the CAP theorem. Spanner is able to provide globally distributed, strongly consistent ACID transactions. Sounds too good to be true, no?&lt;/p&gt;

&lt;p&gt;Well it is. Spanner relies on a global timestamp (their secret weapon here) to order events. TrueTime, is a global timestamp with bounded uncertainty, powered by GPS receivers and atomic clocks. Yes, you read that right, &lt;em&gt;actual GPS&lt;/em&gt; receivers atomic clocks are physically bolted to Google server racks. By knowing that an event happened between time T and T+ε (where ε is typically less than 10ms), Spanner can order events globally without the extensive coordination that other CP systems require.&lt;/p&gt;

&lt;p&gt;That solves our latency issue during regular operations. But what about when there’s a network partition?&lt;/p&gt;

&lt;p&gt;During a network partition, it chooses consistency over availability. &lt;strong&gt;Spanner is still CP&lt;/strong&gt;. Full stop. The reason it &lt;em&gt;appears&lt;/em&gt; to also be highly available is that Google’s network almost never partitions, because Google owns the fiber, the switches, the data centers, and the custom hardware. Spanner’s availability story isn’t an architectural breakthrough. It’s an infrastructure advantage that many companies simply can’t replicate.&lt;/p&gt;

&lt;p&gt;Eric Brewer (the guy that coined the CAP Theorem) admits that Spanner is technically CP, it just happens to operate in an environment where the P is heavily minimized.&lt;/p&gt;

&lt;p&gt;If you’re not Google, you don’t have TrueTime. And if your network actually partitions, Spanner makes the same choice as etcd: consistency wins, availability loses.&lt;/p&gt;

&lt;h2&gt;
  
  
  CockroachDB: Make the Pain Invisible
&lt;/h2&gt;

&lt;p&gt;CockroachDB takes a completely different approach. Instead of trying eliminate or minimize partitions like Google, it aims to &lt;em&gt;hide&lt;/em&gt; the partitions from the user.&lt;/p&gt;

&lt;p&gt;Under the hood, CockroachDB uses Raft consensus, the same protocol we covered in Part 2 with etcd, for replication. But where etcd uses a &lt;em&gt;single Raft group&lt;/em&gt; for the whole cluster, CockroachDB splits the data into &lt;em&gt;ranges&lt;/em&gt;, each running its own independent Raft group. Each range replicates across multiple nodes and has its own node leader.&lt;/p&gt;

&lt;p&gt;So how does it stay “available” if it’s running consensus?&lt;/p&gt;

&lt;p&gt;The trick here is scope. During a network partition, CockroachDB &lt;em&gt;does not&lt;/em&gt; go down in its entirety, only the specific ranges that lost their majority. Let’s say that again. During a network partition, only ranges that lose the majority go down. For example, if a partition cuts off one node in a five-node cluster, only the ranges where that node held the deciding vote are affected. Everything else keeps working as if nothing happened.&lt;/p&gt;

&lt;p&gt;It’s technically CP behavior. But by distributing ranges across many nodes and rebalancing automatically, the &lt;em&gt;practical&lt;/em&gt; impact of any single partition is small. Most users won’t notice.&lt;/p&gt;

&lt;p&gt;CockroachDB didn’t hack the CAP Theorem, it just made the decision &lt;em&gt;extremely granular&lt;/em&gt;. You’re still compromising availability during partitions, it’s just for a &lt;em&gt;small-subset of the data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkbr1vxditb8yrhavds0a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkbr1vxditb8yrhavds0a.png" width="800" height="1218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern
&lt;/h2&gt;

&lt;p&gt;As we’ve shown, neither of these systems actually &lt;em&gt;violate&lt;/em&gt; the CAP Theorem. They just engineered around it from opposite directions. Google’s Spanner reduces the &lt;em&gt;probability&lt;/em&gt; of partitions by owning the entirety of the infrastructure. CockroachDB reduces the &lt;em&gt;impact&lt;/em&gt; of partitions by splitting the data into numerous independent ranges.&lt;/p&gt;

&lt;p&gt;The constraints are still there. Networks are still bound to fail and when that inevitably happens, both systems default to choosing consistency over availability. The difference, however, is that both systems have found ways to make the consistency vs. availability choice less painful. Spanner by making partitions almost nonexistent. CockroachDB by making them almost invisible.&lt;/p&gt;

&lt;p&gt;The lesson? CAP describes fundamental constraints. Creative engineering minimizes their impact. But the constraints are still there, waiting for the network to have a bad day.&lt;/p&gt;




&lt;h1&gt;
  
  
  How to Actually Choose
&lt;/h1&gt;

&lt;p&gt;Alrighty, we’re on our fourth post. Four posts of theory, papers, and systems deep-dives. Let’s get a bit practical. How do you &lt;em&gt;actually&lt;/em&gt; decide between consistency and availability for &lt;strong&gt;your&lt;/strong&gt; system?&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with Three Questions
&lt;/h2&gt;

&lt;p&gt;Step 0, before even picking technologies, assess the system you’re building. Answer these questions honestly.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. What happens if users see stale data?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Obviously stale data is not ideal. But this question is more geared towards the &lt;em&gt;user-experience&lt;/em&gt; if stale data is presented. For a bank, a stale bank balance has real financial implications for the user. However, for a social media app, a like counter doesn’t really have that big of an affect. An example to think about, DNS has tolerated stale records for &lt;em&gt;decades&lt;/em&gt;. Decades. That’s not to say to pick a certain path, but to drive home the point “not everything needs to be strongly consistent”.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. What happens if the system is unavailable?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We would all love 100% system uptime. AWS spends billions of dollars to achieve four 9’s of availability (still not 100%). Where stale data affects user-experience, system availability is usually tied to business performance.&lt;/p&gt;

&lt;p&gt;A payment processor being down is lost revenue. An analytics dashboard being down leads to delayed decisions. A configuration service being can lead to failures across all components that config controls (which might be worse than stale data). The answer isn’t always obvious, but you need to think through what happens when your system is down.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. How much consistency do you actually need?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let’s re-read that question, how much consistency do you actually _ &lt;strong&gt;need?&lt;/strong&gt; _ Notice &lt;em&gt;need&lt;/em&gt; not &lt;em&gt;want&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Bank balances are strictly linearizable, there’s no way around it. Shopping carts can get away with something weaker (causal consistency). Social features just need read-your-writes level of consistency and they’re good. You need to be very honest here. Every step up the consistency ladder is a step down the latency or throughput ladder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsh3heybq3y4u86t9zob7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsh3heybq3y4u86t9zob7.png" width="800" height="999"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Approach That Actually Works: Hybrid
&lt;/h2&gt;

&lt;p&gt;What I’ve learned from working on, operating, and researching distributed systems is that almost nobody should go pure CP or pure AP. The right answer is almost always somewhere in the middle. Different data has different requirements and it should be treated that way.&lt;/p&gt;

&lt;p&gt;Here’s what that looks like for an e-commerce system:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User sessions → AP.&lt;/strong&gt; Throw this in DynamoDB with eventually consistent reads. Fast logins, and who cares if a session is slightly stale? If it breaks, the user logs in again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product catalog → AP.&lt;/strong&gt; Fast browsing matters more than showing the absolute latest product description. Cache aggressively. Invalidate when you can.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inventory → Tunable.&lt;/strong&gt; This is the interesting one. You can be optimistic (AP) when browsing — show approximate stock levels. But when the user actually clicks “Buy,” switch to a strongly consistent check before charging their card. Cassandra with &lt;code&gt;ONE&lt;/code&gt; for reads and &lt;code&gt;QUORUM&lt;/code&gt; for the purchase path, or DynamoDB with eventually consistent reads for browsing and strongly consistent reads at checkout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Orders and payments → CP.&lt;/strong&gt; No debate. Money is involved. PostgreSQL with synchronous replication, or a distributed SQL database like CockroachDB. If the system can’t confirm consistency, it should refuse the write.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reviews and social features → AP.&lt;/strong&gt; Eventual consistency is totally fine here. A review showing up 2 seconds late doesn’t hurt anyone.&lt;/p&gt;

&lt;p&gt;The pattern: &lt;strong&gt;use the weakest consistency model that still meets your requirements for each piece of data.&lt;/strong&gt; Stronger consistency isn’t free, it costs latency, throughput, and operational complexity. Don’t pay for it where you don’t need it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72nsbpwa1r46ag5flq5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F72nsbpwa1r46ag5flq5u.png" width="800" height="1716"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  What CAP Actually Taught Us
&lt;/h1&gt;

&lt;p&gt;Four posts. Thousands of words. Multiple papers. Here’s what it all boils down to:&lt;/p&gt;

&lt;h2&gt;
  
  
  CAP Is a Design Tool, Not a Law
&lt;/h2&gt;

&lt;p&gt;CAP theorem doesn’t tell you what to build. It tells you what questions to ask. What should your system do when the network partitions? When nodes fail? When clocks skew? If you haven’t explicitly decided, your system will decide for you. If you’re lucky that looks like 3AM debugging sessions on the weekend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nothing is Free
&lt;/h2&gt;

&lt;p&gt;CAP’s lasting contribution is killing the fantasy that distributed systems can provide everything for free. Every guarantee has a cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Strong consistency costs latency and availability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;High availability costs consistency guarantees.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Partition tolerance isn’t optional, networks fail.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding these costs turns you from someone who hopes their system handles edge cases into someone who &lt;em&gt;knows&lt;/em&gt; what their system will do when things go wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Spectrum Is Your Friend
&lt;/h2&gt;

&lt;p&gt;The real world isn’t CP or AP. It’s a spectrum, and modern systems give you a dial. Cassandra’s tunable consistency, DynamoDB’s read modes, MongoDB’s read/write concerns, all of these exist because the engineers who built them understood that different data deserves different trade-offs.&lt;/p&gt;

&lt;p&gt;The best distributed system for your use case isn’t the most consistent one or the most available one. It’s the one that makes the right trade-offs for your specific requirements. And now you know how to figure out what those trade-offs are.&lt;/p&gt;




&lt;h1&gt;
  
  
  Series Recap
&lt;/h1&gt;

&lt;p&gt;We started this series with a simple question: what does “you can only pick two” actually mean?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-explained?r=1tezps" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;&lt;/strong&gt; busted the myth. CAP isn’t about permanently picking two properties. It’s about what your system does &lt;em&gt;during a network partition&lt;/em&gt; — and that choice is binary: consistency or availability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-cp-systems?r=1tezps" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt;&lt;/strong&gt; explored the cost of being right. etcd, Zookeeper, PostgreSQL, and MongoDB showed us what strong consistency demands: quorum writes, leader elections, blocked operations during partitions, and latency on every write even when the network is healthy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://dev.to/aliqaiser/ap-systems-explained-stale-data-beats-dead-servers-23ib-temp-slug-319092"&gt;Part 3&lt;/a&gt;&lt;/strong&gt; explored the cost of staying up. DynamoDB, Cassandra, and DNS showed us what high availability demands: stale reads, conflict resolution strategies, and the operational complexity of reconciling data after the network heals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 4, this part,&lt;/strong&gt; showed that the binary choice was never the full story. PACELC revealed that you’re making trade-offs even when the network is fine. The consistency spectrum gave you six stops between linearizability and eventual consistency. And the hybrid approach showed that real systems don’t pick one side — they pick different trade-offs for different data.&lt;/p&gt;

&lt;p&gt;Next time someone draws a triangle on a whiteboard and asks you to “pick two,” you’ll know exactly why that’s the wrong question — and what to ask instead.&lt;/p&gt;

&lt;p&gt;THANKS FOR FOLLOWING THE SERIES! SUBSCRIBE FOR MORE DEEP DIVES INTO DISTRIBUTED SYSTEMS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-beyond-pick-two?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share" rel="noopener noreferrer"&gt;Share&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— Ali&lt;/p&gt;




&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://cs-www.cs.yale.edu/homes/dna/papers/abadi-pacelc.pdf" rel="noopener noreferrer"&gt;Consistency Tradeoffs in Modern Distributed Database System Design&lt;/a&gt; — Daniel Abadi (2012), the PACELC paper.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://research.google/pubs/pub39966/" rel="noopener noreferrer"&gt;Spanner: Google’s Globally Distributed Database&lt;/a&gt; — Corbett et al. (2012)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://research.google/pubs/pub45855/" rel="noopener noreferrer"&gt;Spanner, TrueTime and the CAP Theorem&lt;/a&gt; — Eric Brewer (2017), where Brewer himself explains why Spanner is technically CP.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.cockroachlabs.com/docs/stable/architecture/" rel="noopener noreferrer"&gt;CockroachDB Architecture Documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dataintensive.net/" rel="noopener noreferrer"&gt;Designing Data-Intensive Applications&lt;/a&gt; — Martin Kleppmann&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://jepsen.io/" rel="noopener noreferrer"&gt;Jepsen Analysis&lt;/a&gt; — Real-world consistency testing of distributed systems.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Series Index
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-explained?r=1tezps" rel="noopener noreferrer"&gt;CAP Theorem: Beyond the “Pick Two” Myth&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-cp-systems?r=1tezps" rel="noopener noreferrer"&gt;CP Systems Explained: The Hidden Cost of Strong Consistency&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/aliqaiser/ap-systems-explained-stale-data-beats-dead-servers-23ib-temp-slug-319092"&gt;AP Systems Explained: Why Stale Data Beats Dead Servers&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Beyond “Pick Two”: Real-World Trade-offs ← You are here&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>distributedsystems</category>
      <category>database</category>
      <category>systemdesign</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>AP Systems Explained: Stale Data Beats Dead Servers</title>
      <dc:creator>Ali Malik</dc:creator>
      <pubDate>Tue, 24 Feb 2026 15:18:13 +0000</pubDate>
      <link>https://dev.to/amalik18/ap-systems-explained-stale-data-beats-dead-servers-4l99</link>
      <guid>https://dev.to/amalik18/ap-systems-explained-stale-data-beats-dead-servers-4l99</guid>
      <description>&lt;p&gt;This is Part 3 of a 4-part series on the CAP Theorem and distributed systems trade-offs. &lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-explained?r=1tezps" rel="noopener noreferrer"&gt;Read Part 1 here&lt;/a&gt; or &lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-cp-systems?r=1tezps" rel="noopener noreferrer"&gt;Part 2 here&lt;/a&gt; if you haven’t already done so.&lt;/p&gt;

&lt;p&gt;In Part 2, we looked at CP Systems, datastores that would rather outright refuse requests than give the wrong answer. We covered &lt;em&gt;strong consistency&lt;/em&gt; (otherwise known as &lt;em&gt;linearizability&lt;/em&gt;), quorum-based writes, leader elections, and how when put together it’s expensive but right.&lt;/p&gt;

&lt;p&gt;AP Systems make the opposite decision: “I’d rather give you something than nothing.”&lt;/p&gt;

&lt;p&gt;Sounds pretty crazy until you realize that much of our daily internet traffic works this way. From DNS, to the CDNs powering your Netflix habit, to your social media feed, shopping cart, the list keeps going. All of these systems prioritize availability over consistency. These systems do just fine, heck they power our daily habits.&lt;/p&gt;

&lt;p&gt;So what do we do with the inconsistency? We can’t avoid it, right? We got to manage it the best we can.&lt;/p&gt;

&lt;p&gt;Following along? Subscribe to get the rest of the series!&lt;/p&gt;




&lt;h1&gt;
  
  
  The “Always On” Philosophy
&lt;/h1&gt;

&lt;p&gt;An AP system prioritizes &lt;em&gt;availability&lt;/em&gt; over &lt;em&gt;consistency&lt;/em&gt;, the reverse of a CP system, during network partitions. According to the CAP theorem, every non-failing node &lt;strong&gt;must&lt;/strong&gt; respond to requests, even if it can’t guarantee data freshness.&lt;/p&gt;

&lt;p&gt;Okay that’s fine and all but let’s see what that actually looks like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple Nodes Accept Writes
&lt;/h2&gt;

&lt;p&gt;Unlike CP systems where all writes are funneled through a singular leader, AP systems can accept writes on multiple nodes. During a network partition, both sides of the split continue to accept writes independently. This is what makes the system available, there’s no single point of failure for writes.&lt;/p&gt;

&lt;p&gt;Naturally, you’d ask “what if two clients write different values to the same key?” To that I’d say keep reading. We will discuss conflict resolution a little later in the article. But also, both writes would succeed but, you now have a conflict that needs to be resolved once the network failure heals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reads Return Local Values
&lt;/h2&gt;

&lt;p&gt;There is absolutely &lt;em&gt;zero&lt;/em&gt; coordination when it comes to reads. There’s no double-checking with other nodes, no waiting for consensus that this is the latest value. Nodes in an AP system return whatever they have, even if that data is stale.&lt;/p&gt;

&lt;p&gt;This is absolutely okay for social media feeds (nobody cares if a “like” shows up late). But absolutely horrendous for a banking system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Partitioned Nodes Stay Alive
&lt;/h2&gt;

&lt;p&gt;This is one of the main differences between AP and CP systems. Recall, in a CP system, the minority side of a partition stops completely. No reads or writes are served from that side. In an AP system, every node continues to operate. Both sides of the partition are alive and accepting reads/writes, operating as if nothing happened.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21-ZrR%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F1eb2013d-cd7c-41a3-8f76-e1c6152bcedb_4706x2400.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21-ZrR%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F1eb2013d-cd7c-41a3-8f76-e1c6152bcedb_4706x2400.png" title="The fundamental choice during network partitions. CP systems sacrifice availability for consistency (minority nodes stop serving). AP systems sacrifice consistency for availability (all nodes keep serving, creating potential conflicts)." alt="The fundamental choice during network partitions. CP systems sacrifice availability for consistency (minority nodes stop serving). AP systems sacrifice consistency for availability (all nodes keep serving, creating potential conflicts)." width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The fundamental choice during network partitions. CP systems sacrifice availability for consistency (minority nodes stop serving). AP systems sacrifice consistency for availability (all nodes keep serving, creating potential conflicts).&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Cleaning Up the Mess Later
&lt;/h1&gt;

&lt;p&gt;CP Systems avoid conflicts by simply running every write through a leader and requiring quorum on writes. AP systems say YOLO and allow conflicts to happen. These conflicts get dealt with later on. Let’s discuss some of these conflict resolution strategies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Last-Write-Wins (LWW)
&lt;/h2&gt;

&lt;p&gt;This is the simplest approach. Each write has an associated timestamp. When two writes conflict, the one with the later timestamp wins. The other is discarded.&lt;/p&gt;

&lt;p&gt;This is awesome for data systems where the most recent value matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;User preferences (theme choice, language, notification settings, etc).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Status / state data (online/offline, connection state, etc).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cache / Metadata (expiration times, modification timestamps, etc).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Social Media updates (profile bio contents, pictures, profile/cover photos).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What’s the problem with this? Timestamps in distributed systems are not really reliable. Node A’s clock might or might not be &lt;em&gt;slighly&lt;/em&gt; ahead of Node B’s clock. The two writes that occurred at the “same time” get differing timestamps and one of them gets tossed. Data loss could happen unknowingly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21aXdZ%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F958da278-5b57-4a52-95f4-32744abfcd0c_2328x1390.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21aXdZ%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F958da278-5b57-4a52-95f4-32744abfcd0c_2328x1390.png" title="Last-Write-Wins conflict resolution appears simple but can silently lose data. Client 1's write succeeds initially but disappears when nodes reconcile, with no error or notification. Clock skew makes this unpredictable." alt="Last-Write-Wins conflict resolution appears simple but can silently lose data. Client 1's write succeeds initially but disappears when nodes reconcile, with no error or notification. Clock skew makes this unpredictable." width="800" height="477"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Last-Write-Wins conflict resolution appears simple but can silently lose data. Client 1’s write succeeds initially but disappears when nodes reconcile, with no error or notification. Clock skew makes this unpredictable.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For the examples above, however, this is perfectly fine. Nobody cares about losing an intermediate GPS coordinate, or a like on a post, these don’t break anything. For as simple as LWW is its risk profile is tiny.&lt;/p&gt;

&lt;p&gt;Apache Cassandra relies on LWW for conflict resolution by default. Amazon’s DynamoDB avoids this problem at the single-region level almost entirely by means of leader-based quorum writes. However, Global Tables (cross-region replication) rely on LWW to resolve conflicts between regions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vector Clocks
&lt;/h2&gt;

&lt;p&gt;A vector clock, as the name suggests, is a clock….sike. It is &lt;strong&gt;NOT&lt;/strong&gt; a clock reading. At all. A vector clock is a data structure that helps determine the &lt;em&gt;causal ordering of events&lt;/em&gt;, simply put, it helps you determine whether a write happened before another, or whether they happened concurrently.&lt;/p&gt;

&lt;p&gt;Each node maintains a vector of counters (one per node). The counters represent the nodes knowledge of how many events have occurred at every node in the system. On a local event, a node will increment its own counter. When sending a message, the node attaches its vector. When receiving an event, it merges the received vectors.&lt;/p&gt;

&lt;p&gt;Let’s imagine a couple of situations. Imagine write A occurred before write B, you know B is newer thus we’d keep B and discard A. Imagine write A and B occurred simultaneously (neither of them caused one another). This conflict can’t be resolved by itself. This is where conflict resolution techniques come into play.&lt;/p&gt;

&lt;p&gt;In those situations, the system needs to decide whether it will pick one and risk losing data or keep both and hand off the resolution to the application. An example of the latter is Amazon’s DynamoDB, in &lt;a href="https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf" rel="noopener noreferrer"&gt;the original paper&lt;/a&gt;, DynamoDB was designed to return multiple versions of the data and let the application level logic merge them. The most famous example is Amazon’s shopping cart example, if two conflicting cart states exist, merge them and keep all of the items. It’s to be noted that modern DynamoDB dropped this entirely and opted for leader-based writes to prevent conflicts at the storage layer.&lt;/p&gt;

&lt;p&gt;In practice, vector clocks add a level of complexity that not every team wants to deal with. Not every team wants to write conflict resolution strategies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conflict-free Replicated Data Types (CRDTs)
&lt;/h2&gt;

&lt;p&gt;CRDTs are data structures that are designed to make concurrent updates mergeable without any conflicts. No need for coordination nor conflict resolution logic. The math behind CRDTs guarantees eventual convergence.&lt;/p&gt;

&lt;p&gt;I’ll do my best to explain some CRDTs in the following sections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Grow-Only Counter (G-Counter)
&lt;/h3&gt;

&lt;p&gt;The simplest of all CRDTs, a G-Counter is, as its name suggests, a counter that &lt;em&gt;only grows&lt;/em&gt;. It only supports increments.&lt;/p&gt;

&lt;p&gt;Each node maintains its own counter. The total is the sum of all node counters. If two nodes both increment, there’s no problem, we just add them up. This is how _ &lt;strong&gt;likes&lt;/strong&gt; _ work in social media.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Real World Example:&lt;/em&gt; Social media likes, video view counts, Reddit’s upvoting systems all use G-Counters. If two users like a post at the same time during a network partition, both increments are saved when the partition heals.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21LIXo%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F9e514249-c9e6-4b93-addd-fc0ab7a87a7b_4119x1310.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21LIXo%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F9e514249-c9e6-4b93-addd-fc0ab7a87a7b_4119x1310.png" title="G-Counter magic in two steps: (1) Each node only increments its own counter during partition, (2) Merge by taking the maximum of each position. No conflicts possible—the math guarantees all nodes converge to the same total." alt="G-Counter magic in two steps: (1) Each node only increments its own counter during partition, (2) Merge by taking the maximum of each position. No conflicts possible—the math guarantees all nodes converge to the same total." width="800" height="254"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;G-Counter magic in two steps: (1) Each node only increments its own counter during partition, (2) Merge by taking the maximum of each position. No conflicts possible—the math guarantees all nodes converge to the same total.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Observed-Remove Set (OR-Set)
&lt;/h3&gt;

&lt;p&gt;A set that supports both addition and removal concurrently. The way this works is, every add operation gets tagged with a unique identifier (UID). A remove operation only removes UIDs _ &lt;strong&gt;it has observed&lt;/strong&gt; &lt;em&gt;. What this means is if a node has not _seen&lt;/em&gt; an add it can’t remove it. In the simplest of terms, if an add and a remove occur on different nodes, for the remove to be successful the node performing it &lt;strong&gt;must&lt;/strong&gt; have synced with every other node, to &lt;em&gt;see&lt;/em&gt; the addition.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Real-World Example:&lt;/em&gt; We’ll rely on the classic shopping cart example again. Imagine two different users/sessions update the same cart. User A _ &lt;strong&gt;adds&lt;/strong&gt; _ a “Laptop” and User B _ &lt;strong&gt;removes&lt;/strong&gt; _ a “Laptop”. What do you think would happen? A laptop is added to the cart. Why? User B is removing the “version” of a laptop it saw in its cart. Since the _ &lt;strong&gt;add&lt;/strong&gt; _ didn’t get sync’d yet, User B didn’t &lt;em&gt;see&lt;/em&gt; the add, so it can’t remove it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Last-Write-Wins Register (LWW-Register)
&lt;/h3&gt;

&lt;p&gt;This is basically LWW as a CRDT. This CRDT only stores a single value and resolves concurrent writes by keeping the latest write, by timestamp.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Real-World Example:&lt;/em&gt; The easiest one is GPS tracking apps displaying a “last known location” field. A more concrete example is caches. If two nodes update a cache simultaneously, the entry with the freshest timestamp is preserved. Naturally, for a cache only one value is necessary.&lt;/p&gt;

&lt;p&gt;Okay thats enough CRDT explanations. Now, CRDTs sound awesome (they are), but there are constraints. Not every data structure has a CRDT equivalent. They can also grow unboundedly if not properly maintained, taking up &lt;em&gt;a lot&lt;/em&gt; of space. Plus, it doesn’t help that they don’t work for just any arbitrary logic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/subscribe?" rel="noopener noreferrer"&gt;Subscribe now&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  “Eventually”….maybe?
&lt;/h1&gt;

&lt;p&gt;Everyone has heard the term and it usually gets hand-waved to “all nodes eventually see the same data”. While that is true, it’s a bit incomplete.&lt;/p&gt;

&lt;p&gt;What it actually means is, “if no new writes occur, all replicas will &lt;em&gt;eventually&lt;/em&gt; converge to the same value.” Did you notice it? There’s no limit to what “eventually” could be. It could be milliseconds (ms) or minutes.&lt;/p&gt;

&lt;p&gt;In the real world, most AP systems converge fairly quick. Amazon’s DynamoDB replication typically happens within a second across all storage locations within a region. DNS propagation can take hours. The variance is huge.&lt;/p&gt;

&lt;p&gt;What matters is whether your application can handle that time, the inconsistency window. Some can, and some absolutely can’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stronger? Eventual Consistency
&lt;/h2&gt;

&lt;p&gt;Not all forms of eventual consistency are the same. Some are stronger than others. These stronger models give you more guarantees while still being weaker than linearizability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Read-Your-Writes
&lt;/h3&gt;

&lt;p&gt;If Client A performs a write, future reads from the same client are guaranteed to see that write. Other clients may or may not see the updated write just yet, but Client A is guaranteed to see the updated write. This is what users typically expect from most applications (i.e “I just posted a comment, I should be able to see it”).&lt;/p&gt;

&lt;h3&gt;
  
  
  Monotonic Reads
&lt;/h3&gt;

&lt;p&gt;This consistency model guarantees that once a client has read a value, it will never see an older version of that value on subsequent reads. Time does _ &lt;strong&gt;not&lt;/strong&gt; _ go backwards. Without this guarantee, a client can refresh the page and see new data disappear.&lt;/p&gt;

&lt;h3&gt;
  
  
  Causal Consistency
&lt;/h3&gt;

&lt;p&gt;In this consistency model, if Operation A caused Operation B (A happens before B &lt;strong&gt;and&lt;/strong&gt; B depends on A), then everyone must see Operation A before Operation B. You’ll always see them in the right order. The cause comes before the effect, everywhere (hence it’s name….&lt;em&gt;causal&lt;/em&gt;). Imagine seeing a seething reply to a comment &lt;em&gt;without&lt;/em&gt; seeing the comment first. That would be a bit confusing as the end user.&lt;/p&gt;

&lt;p&gt;These consistency models are _ &lt;strong&gt;weaker&lt;/strong&gt; _ than linearizability, which means they don’t take on the CAP theorem trade-offs we discussed in the &lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-cp-systems?r=1tezps" rel="noopener noreferrer"&gt;CP Systems Overview&lt;/a&gt;. Naturally, this means that you can have these guarantees and still be &lt;em&gt;highly available&lt;/em&gt; during network partitions.&lt;/p&gt;

&lt;p&gt;This very clearly highlights the problem with the “pick-two” framing of the CAP theorem. Most applications don’t need &lt;em&gt;true&lt;/em&gt; linearizability, they’re absolutely okay with high availability coupled with these &lt;em&gt;stronger&lt;/em&gt; eventually consistent models.&lt;/p&gt;




&lt;h1&gt;
  
  
  Real-World AP Systems
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Amazon’s DynamoDB: The &lt;em&gt;Always On&lt;/em&gt; DB
&lt;/h2&gt;

&lt;p&gt;The history of DynamoDB goes back to 2004 when Amazon’s e-commerce business suffered a few too many outages. The e-commerce business was pushing their relational databases to their limits even though they had fairly simple usage patterns. This culminated in the release of the famous &lt;a href="https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf" rel="noopener noreferrer"&gt;Dynamo paper&lt;/a&gt;, which laid out the design and implementation of Dynamo, a highly available, leaderless, eventually consistent key-value store. This would later become the foundation for DynamoDB.&lt;/p&gt;

&lt;h3&gt;
  
  
  How DynamoDB Stays Available
&lt;/h3&gt;

&lt;p&gt;Here’s what a lot of people confuse: DynamoDB the service _ &lt;strong&gt;is not&lt;/strong&gt; _ the Dynamo paper.&lt;/p&gt;

&lt;p&gt;The original Dynamo paper laid out a completely leaderless system with sloppy quorum, vector clocks, and application-side conflict resolution. DynamoDB the service threw a lot of that out. DynamoDB now does Multi-Paxos based leader replica election and quorum-based writes (writes require 2/3 storage nodes to acknowledge before committing). Does that sound familiar? It should cause that’s a CP pattern.&lt;/p&gt;

&lt;p&gt;So why is DynamoDB here, in the AP section?&lt;/p&gt;

&lt;p&gt;Because _ &lt;strong&gt;reads are AP by default.&lt;/strong&gt; _ With the default setting of eventually consistent reads, your request can be routed to any of the three replicas. There’s no coordination and no leader dependency. If the replica happens to be behind, you’ll get stale data and the system will remain up.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Trade-Offs in Reality
&lt;/h3&gt;

&lt;p&gt;By default, DynamoDB offers &lt;em&gt;eventually consistent&lt;/em&gt; reads. Which means that you could get stale data. But DynamoDB also offers &lt;em&gt;strongly consistent&lt;/em&gt; reads which get routed to the partition’s leader-node. Now we have the downsides of a CP system: dependence on the leader node being reachable.&lt;/p&gt;

&lt;p&gt;DynamoDB doesn’t make a binary choice between AP and CP. It gives you a &lt;strong&gt;per-request option&lt;/strong&gt; between AP and CP behavior on the read path, while keeping writes CP for durability. This is the kind of nuance that “pick two” completely misses.&lt;/p&gt;

&lt;p&gt;For a vast majority of use cases (i.e shopping carts, user sessions, game state, IoT sensor data) the default eventually consistent reads are more than good enough. DynamoDB’s SLA promises 99.99% availability for standard tables and 99.999% for global tables. That’s less than 5 minutes of downtime per year.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21zxKo%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F0690a497-b58c-4ad5-9e1a-0f793d834c17_6295x4305.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21zxKo%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F0690a497-b58c-4ad5-9e1a-0f793d834c17_6295x4305.png" title="DynamoDB achieves availability through AWS infrastructure speed, not by avoiding coordination. Writes still require 2/3 quorum, but cross-AZ replication completes in milliseconds. The AP trade-off becomes clear with reads: eventually consistent reads are fast but risky, strongly consistent reads sacrifice speed for guaranteed accuracy." alt="DynamoDB achieves availability through AWS infrastructure speed, not by avoiding coordination. Writes still require 2/3 quorum, but cross-AZ replication completes in milliseconds. The AP trade-off becomes clear with reads: eventually consistent reads are fast but risky, strongly consistent reads sacrifice speed for guaranteed accuracy." width="800" height="547"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;DynamoDB achieves availability through AWS infrastructure speed, not by avoiding coordination. Writes still require 2/3 quorum, but cross-AZ replication completes in milliseconds. The AP trade-off becomes clear with reads: eventually consistent reads are fast but risky, strongly consistent reads sacrifice speed for guaranteed accuracy.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Apache Cassandra: The &lt;em&gt;Choose Your Own Consistency&lt;/em&gt; DB
&lt;/h2&gt;

&lt;p&gt;Born from the need of allowing users to instantly search through inbox message history, Cassandra was Facebooks answer to a scalable, reliable, and highly available storage system. It took learnings and design decisions from its predecessors Amazon’s Dynamo (partitioning and replication) and Google’s BigTable (data and storage engine model). Open-sourced in 2008, Cassandra was built to handle ginormous write-throughput across multiple data centers.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Cassandra Stays Available
&lt;/h3&gt;

&lt;p&gt;Cassandra has no single point of failure. There is no leader. Each and every node in the cluster can serve reads and accept writes. Data is distributed similar to DynamoDB, with consistent hashing, and replicated to &lt;em&gt;N&lt;/em&gt; nodes (the recommend amount is 3 nodes in a datacenter).&lt;/p&gt;

&lt;p&gt;This is a proper leaderless design, unlike DynamoDB. Each and every node is an equal. Any node can serve any request.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cassandra also lets you &lt;em&gt;control&lt;/em&gt; the consistency &lt;em&gt;per query&lt;/em&gt; via its &lt;strong&gt;tunable consistency&lt;/strong&gt; :&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ONE&lt;/code&gt; : read / write succeeds after _ &lt;strong&gt;one replica&lt;/strong&gt; _ responds. This is the fastest and &lt;em&gt;least&lt;/em&gt; consistent option.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;QUORUM&lt;/code&gt;: a &lt;em&gt;&lt;strong&gt;majority of replicas (n/2 + 1 number)&lt;/strong&gt;&lt;/em&gt; must respond. Slower than &lt;code&gt;ONE&lt;/code&gt; but more consistent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ALL&lt;/code&gt;: as the name suggests, _ &lt;strong&gt;all replicas&lt;/strong&gt; _ must respond. This is **the slowest option and the most strongly consistent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;LOCAL_QUORUM&lt;/code&gt;: a _ &lt;strong&gt;majority of the local replicas&lt;/strong&gt; _ (locale depends on the datacenter the coordinator dispatching the job is in) must respond.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you set the consistency level to &lt;code&gt;QUORUM&lt;/code&gt; for both reads and writes, you’ve got yourself _ &lt;strong&gt;strong consistency&lt;/strong&gt; &lt;em&gt;. You’ve basically turned Cassandra into a CP system _for that query&lt;/em&gt;. Set both read and write consistency to &lt;code&gt;ONE&lt;/code&gt; and you’ve got a purely AP system. You pick the consistency, per query. Every single time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Trade-Offs in Reality
&lt;/h3&gt;

&lt;p&gt;Cassandra relies on LWW as its default conflict resolution strategy (based on timestamps). This also opens the door for clock skew, bad time-stamping, silently allowing the &lt;em&gt;wrong&lt;/em&gt; write to win.&lt;/p&gt;

&lt;p&gt;Tombstones (deletion markers) can start to pile up and impact read performance if time-to-lives (TTLs), deletes, and compaction behavior aren’t managed carefully.&lt;/p&gt;

&lt;p&gt;Tunable consistency, as awesome as it is, can also be detrimental to your system. Before using tunable consistency, you &lt;em&gt;must&lt;/em&gt; understand each consistency level you choose. If misconfigured, you’ll pay the price in prod.&lt;/p&gt;

&lt;p&gt;As scary as that sounded, Cassandra’s per query tunable consistency is truly where it stands out. Number of views? Use &lt;code&gt;ONE&lt;/code&gt; , it’s fast and slightly stale data is perfectly fine. Inventory reservations? Use &lt;code&gt;QUORUM&lt;/code&gt; , slower but it’s correct.&lt;/p&gt;

&lt;p&gt;For time-series data, messaging systems, and really anything requiring heavy write throughput across data centers, Cassandra’s flexibility is hard to beat.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21zfBh%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252Fe648442e-9bdc-44fd-b484-28b1ec0c13c3_4764x3160.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21zfBh%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252Fe648442e-9bdc-44fd-b484-28b1ec0c13c3_4764x3160.png" title="Cassandra's superpower: you choose your trade-off per query. Need fast social media feeds? Use ONE (5ms, might be stale). Need accurate account balances? Use QUORUM (15ms, strong consistency). Need audit-level certainty? Use ALL (50ms, maximum safety). Same cluster, different guarantees." alt="Cassandra's superpower: you choose your trade-off per query. Need fast social media feeds? Use ONE (5ms, might be stale). Need accurate account balances? Use QUORUM (15ms, strong consistency). Need audit-level certainty? Use ALL (50ms, maximum safety). Same cluster, different guarantees." width="800" height="530"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Cassandra’s superpower: you choose your trade-off per query. Need fast social media feeds? Use ONE (5ms, might be stale). Need accurate account balances? Use QUORUM (15ms, strong consistency). Need audit-level certainty? Use ALL (50ms, maximum safety). Same cluster, different guarantees.When Should You Choose AP?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We’ve seen how DynamoDB, Cassandra, and DNS each make different architectural decisions while staying highly available and remaining on the AP side of the spectrum. Here’s how to tell whether your system should be one of them:&lt;/p&gt;

&lt;p&gt;Go AP when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Availability is non-negotiable&lt;/strong&gt; *.* User-facing applications where downtime directly affects revenue, users, or both.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stale reads are harmless&lt;/strong&gt;. The data doesn’t change meaning even if its a few seconds or minutes behind.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You need to go global&lt;/strong&gt;. AP systems handle multi-region deployments very well. This allows you to avoid cross-region consensus overhead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write throughput matters&lt;/strong&gt;. In &lt;em&gt;leaderless&lt;/em&gt; / &lt;em&gt;multi-leader&lt;/em&gt; designs, multiple nodes can accept writes concurrently. Applications around event logging, IoT telemetry, time-series data, etc basically anything where a lot of writes are expected.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be cautious of AP systems when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Correctness is non-negotiable&lt;/strong&gt;. Financial transactions, payments/ledgers, inventory management, etc. Anything where conflicting writes cause real-world damage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Conflict resolution isn’t simple.&lt;/strong&gt; If your data can’t be merged cleanly (i.e with CRDTs or well-defined rules), you’ll end up writing and maintaining custom resolution logic, something no team wants to do.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Users expect instant consistency.&lt;/strong&gt; “I just saved my document and now it’s gone” is a horrible user experience, even if it re-appears two seconds later.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same question I posed in Part 2, just from the opposite side of the lens: “What’s worse, being briefly wrong or briefly unavailable?”&lt;/p&gt;

&lt;p&gt;If stale data is harmless or easily correctable, go AP. If stale data has the ability to cause real damage, go CP.&lt;/p&gt;




&lt;h1&gt;
  
  
  Up Next: Beyond the “Pick Two” Framing
&lt;/h1&gt;

&lt;p&gt;We’ve dedicated two long posts to treating CP and AP as binary choices. They’re not.&lt;/p&gt;

&lt;p&gt;In Part 4, the grand finale, we’ll look at how modern systems blur the line between the two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Tunable Consistency: you saw this with Cassandra and DynamoDB.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PACELC Theorem: If &lt;em&gt;during a partition&lt;/em&gt; you’re trading between availability and consistency, what are you trading &lt;em&gt;when there is no partition&lt;/em&gt;?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How systems like Spanner and CockroachDB aim for C and A-like behavior and at what cost.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Practical guidance for choosing trade-offs in your own systems.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CAP theorem provides you with a mental model. Part 4 of this series will give you the escape hatch.&lt;/p&gt;




&lt;h2&gt;
  
  
  DNS: The &lt;em&gt;Most Important&lt;/em&gt; Stale Data on Earth
&lt;/h2&gt;

&lt;p&gt;We mentioned DNS way back in &lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-explained?r=1tezps" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;, but it deserves a deeper look here. After all, it is the &lt;em&gt;purest&lt;/em&gt; AP system in existence.&lt;/p&gt;

&lt;p&gt;DNS is a globally distributed, hierarchically organized database. When you enter a URL in your browser, you kickstart a chain of reactions across multiple layers of DNS, from your recursive resolver (ISP provided, or company DNS, or public resolver), to Root, TLD, and Authoritative servers (each with the possibility of holding cached records).&lt;/p&gt;

&lt;h3&gt;
  
  
  How DNS Works
&lt;/h3&gt;

&lt;p&gt;DNS handles billions of queries per second across millions of servers worldwide. Imagine requiring linearizability across each and every query (i.e making every query check with the authoritative server before responding). It would be insanely slow….like &lt;em&gt;extremely&lt;/em&gt; slow. Not only would it be painstakingly slow, it would introduce a single point of failure for the entire internet. _ &lt;strong&gt;THE ENTIRE INTERNET&lt;/strong&gt; _. Nobody, and I mean absolutely nobody, would want that.&lt;/p&gt;

&lt;p&gt;Instead, DNS relies on caching controlled by TTLs (Time-to-Live). Each DNS record has a TTL value that lets resolvers know how long to cache it before checking with the authoritative server again. Naturally, when you update a DNS record, the old record doesn’t just magically disappear. It sits cached on DNS servers around the world until the TTLs expire. This is exactly why DNS changes “take time to propagate” and most likely why your DevOps friend told you to wait after you updated an A record.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Trade-Offs in Reality
&lt;/h3&gt;

&lt;p&gt;With caching and depending on the TTL values, different users around the world can resolve the same domain to different IP addresses, sometimes for minutes and sometimes for &lt;em&gt;hours&lt;/em&gt;. This is a completely tolerable consequence. As opposed to the entirety of DNS going down. A small window of staleness is better than making name resolution slow and failure-prone.&lt;/p&gt;

&lt;p&gt;That’s the most important thing that DNS teaches us: most applications can tolerate a few seconds of stale data. It’s, arguably, the most critical system on the internet and runs on eventual consistency with TTLs measured in hours (&lt;em&gt;hours&lt;/em&gt;). If the backbone of the internet can tolerate that level of staleness, your application can most certainly handle a few seconds.&lt;/p&gt;

&lt;p&gt;Want to know how to pick the right trade-off? Part 4 covers the escape hatches.&lt;/p&gt;




&lt;h1&gt;
  
  
  When Should You Choose AP?
&lt;/h1&gt;

&lt;p&gt;We’ve seen how DynamoDB, Cassandra, and DNS each make different architectural decisions while staying highly available and remaining on the AP side of the spectrum. Here’s how to tell whether your system should be one of them:&lt;/p&gt;

&lt;p&gt;Go AP when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Availability is non-negotiable&lt;/strong&gt; *.* User-facing applications where downtime directly affects revenue, users, or both.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stale reads are harmless&lt;/strong&gt;. The data doesn’t affect the meaning of the data even if its a few seconds or minutes behind.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Being global is an end goal&lt;/strong&gt;. AP systems handle multi-region deployments very well. This allows you to avoid cross-region consensus overhead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write throughput matters&lt;/strong&gt;. In &lt;em&gt;leaderless&lt;/em&gt; / &lt;em&gt;multi-leader&lt;/em&gt; designs, multiple nodes can accept writes concurrently. Applications around event logging, IoT telemetry, time-series data, etc basically anything where a lot of writes are expected.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be cautious of AP systems when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Correctness is non-negotiable&lt;/strong&gt;. Financial transactions, payments/ledgers, inventory management, etc. Anything where conflicting writes cause real-world damage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Conflict resolution isn’t simple.&lt;/strong&gt; If your data can’t be merged cleanly (i.e with CRDTs or well-defined rules), you’ll end up writing and maintaining custom resolution logic, something no team wants to do.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Users expect instant consistency.&lt;/strong&gt; “I just saved my document and now it’s gone” is a horrible user experience, even if it re-appears two seconds later.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same question I posed in Part 2, just from the opposite side of the lens: “What’s worse, being briefly wrong or briefly unavailable?”&lt;/p&gt;

&lt;p&gt;If stale data is harmless or easily correctable, go AP. If stale data has the ability to cause real damage, go CP.&lt;/p&gt;




&lt;h1&gt;
  
  
  Up Next: Beyond the “Pick Two” Framing
&lt;/h1&gt;

&lt;p&gt;We’ve dedicated two long posts to treating CP and AP as binary choices. They’re not.&lt;/p&gt;

&lt;p&gt;In Part 4, the grand finale, we’ll look at how modern systems blur the line between the two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Tunable Consistency: you saw this with Cassandra and DynamoDB.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PACLEC Theorem: If &lt;em&gt;during a partition&lt;/em&gt; you’re trading between availability and consistency, what are you trading &lt;em&gt;when there is no partition&lt;/em&gt;?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How systems like Spanner and CockroachDB aim for C and A-like behavior and at what cost.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Practical guidance for choosing trade-offs in your own systems.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CAP theorem provides you with a mental model. Part 4 of this series will give you the escape hatch.&lt;/p&gt;

&lt;p&gt;— Ali&lt;/p&gt;

&lt;p&gt;Subscribe to get Part 4 when it drops!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-ap-systems?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share" rel="noopener noreferrer"&gt;Share&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf" rel="noopener noreferrer"&gt;Dynamo: Amazon’s Highly Available Key-Value Store&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://cassandra.apache.org/doc/latest/" rel="noopener noreferrer"&gt;Apache Cassandra Documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://crdt.tech/" rel="noopener noreferrer"&gt;CRDTs: Conflict-free Replicated Data Types&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc1034" rel="noopener noreferrer"&gt;DNS RFC 1034 - Domain Concepts and Facilities&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dataintensive.net/" rel="noopener noreferrer"&gt;Designing Data-Intensive Applications&lt;/a&gt; by Martin Kleppmann&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;Amazon DynamoDB Documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.cs.cornell.edu/projects/ladis2009/papers/lakshman-ladis2009.pdf" rel="noopener noreferrer"&gt;Cassandra - A Decentralized Structured Storage System&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>database</category>
      <category>distributedsystems</category>
      <category>systemdesign</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>CP Systems Explained: The Hidden Cost of Strong Consistency</title>
      <dc:creator>Ali Malik</dc:creator>
      <pubDate>Tue, 03 Feb 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/amalik18/cp-systems-explained-the-hidden-cost-of-strong-consistency-1kgp</link>
      <guid>https://dev.to/amalik18/cp-systems-explained-the-hidden-cost-of-strong-consistency-1kgp</guid>
      <description>&lt;p&gt;Let's look at what CP systems actually do under the hood when they choose consistency over availability.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is Part 2 of a 4-part series covering the CAP theorem and distributed systems trade-offs. If you haven’t done so already, &lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-explained" rel="noopener noreferrer"&gt;read Part 1 here&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In &lt;a href="https://www.blog.ahmazin.dev/p/cap-theorem-explained" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;, we examined the CAP theorem holistically and its “pick two” myth. We narrowed the CAP theorem down to this singular question: &lt;strong&gt;what should your system do &lt;em&gt;when&lt;/em&gt; the network fails?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;CP (Consistency, Partition Tolerant) systems answer this question with: “&lt;strong&gt;I’d rather be unavailable than wrong&lt;/strong&gt;.”&lt;/p&gt;

&lt;p&gt;That’s fine and all, but choices have consequences. Consequences don’t always show up easily, sometimes it’s 2 AM on a weekend when you realize your system is taking entirely too long to write to a database.&lt;/p&gt;

&lt;p&gt;Following along? Subscribe to get the rest of the series!&lt;/p&gt;




&lt;h2&gt;
  
  
  Requisites of a “CP” System
&lt;/h2&gt;

&lt;p&gt;As we defined earlier, a CP system is one that prioritizes “consistency” over “availability” during network partitions. What does that look like in the real-world?&lt;/p&gt;

&lt;h3&gt;
  
  
  Blocked Writes
&lt;/h3&gt;

&lt;p&gt;What this means is, if you attempt to &lt;strong&gt;&lt;em&gt;write&lt;/em&gt;&lt;/strong&gt; to your system during a network partition, it will not be acknowledged / confirmed until it’s been replicated to enough nodes. “Enough” is subjective but typically means a majority (i.e 2 of 3 or 3 of 5), this is also referred to as the &lt;strong&gt;&lt;em&gt;quorum&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Always use odd numbers. A 4-node cluster has the same fault tolerance as a 3-node cluster—you're paying for a node that doesn't improve availability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0tjh241jzlsv47qj556.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0tjh241jzlsv47qj556.png" width="800" height="885"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Going back to &lt;strong&gt;etcd&lt;/strong&gt;, a &lt;code&gt;kubectl apply&lt;/code&gt; will not return until at least half of the cluster confirms the write. Similarly, in a Postgres setup with &lt;em&gt;synchronous replication&lt;/em&gt; enabled, an &lt;code&gt;INSERT&lt;/code&gt; waits for the replicas to confirm that the data was received and written.&lt;/p&gt;

&lt;p&gt;And this is exactly why &lt;strong&gt;writes&lt;/strong&gt; are slower in CP systems. You are no longer just writing to a single node and moving on, you have to wait for network round trips &lt;strong&gt;and&lt;/strong&gt; the slowest node in the quorum.&lt;/p&gt;

&lt;h3&gt;
  
  
  Most Up-to-Date Reads
&lt;/h3&gt;

&lt;p&gt;CP systems &lt;em&gt;can&lt;/em&gt; guarantee fresh reads when using linearizable or quorum-backed read modes. You will &lt;strong&gt;never&lt;/strong&gt; read stale data. If you just wrote a value, you’ll read that very value back, or get an error (as shown above, if quorum isn’t met). In MongoDB if you enable linearizable read concern, your reads will go to the primary and wait to confirm that there aren’t any other writes in progress. Back to our etcd example, you can read from follower nodes but the default logic guarantees that you do not read anything that hasn’t been committed to quorum.&lt;/p&gt;

&lt;p&gt;As before, what’s the cost for strong consistent reads? It’s &lt;strong&gt;read latency&lt;/strong&gt;. You can’t simply fetch data from the fastest / nearest replica, there’s a robust level of coordination involved that ensures you’re always getting the freshest data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Partitioned Nodes Stop
&lt;/h3&gt;

&lt;p&gt;In CP systems, if a node can’t reach its peers it’ll refuse requests outright rather than risk returning stale data. There’s no guessing involved, it simply &lt;strong&gt;will not&lt;/strong&gt; serve stale data.&lt;/p&gt;

&lt;p&gt;What does this mean in practice? The node will return an error or simply timeout. Going back to MongoDB, if the primary node can’t reach its secondaries, it will &lt;em&gt;step down&lt;/em&gt; as the primary and refuse writes altogether. In the case of etcd, if the node gets partitioned away, it’ll stop accepting requests until it rejoins the cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Happens During a Partition?
&lt;/h3&gt;

&lt;p&gt;Let’s walk through a concrete example of a network partition that’ll help visualize all of the above.&lt;/p&gt;

&lt;p&gt;Let’s assume we have 5-node &lt;strong&gt;etcd&lt;/strong&gt; cluster with a quorum of 3. Everything is running perfectly, writes go to the leader, get replicated to followers, quorum is reached, and clients are happy.&lt;/p&gt;

&lt;p&gt;Everything is working. Writes go to the leader, get replicated to followers, clients are happy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhnwi9ebxbkky06msmu9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhnwi9ebxbkky06msmu9.png" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then a network partition splits the cluster in a 3-2 configuration. Three nodes can still communicate with each other. Two nodes are isolated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjo1qe7uqyg9yyppclr7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjo1qe7uqyg9yyppclr7.png" width="800" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Majority Side
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;There is still quorum (3 of 5).&lt;/li&gt;
&lt;li&gt;If necessary, a new leader is elected and operations continue.&lt;/li&gt;
&lt;li&gt;Both writes and reads succeed.&lt;/li&gt;
&lt;li&gt;Clients connected to any node in this majority side experiences absolutely nothing out of the ordinary.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Minority Side
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Can’t reach quorum (2 &amp;lt; 3)&lt;/li&gt;
&lt;li&gt;Writes are refused.&lt;/li&gt;
&lt;li&gt;Depending on how the system is configured, reads are also refused.&lt;/li&gt;
&lt;li&gt;Clients connected to this side see timeouts / errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clients connected to the majority side don't notice anything. Writes still work because quorum (3) is reachable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frl9yif1ih9hrjz4yeblz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frl9yif1ih9hrjz4yeblz.png" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clients connected to the minority side see timeouts and errors. The nodes refuse to serve requests because they can't reach quorum.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7z7nodnvjehv0a6u2vg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7z7nodnvjehv0a6u2vg.png" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is what CP systems look like in action. The minority side (nodes that have been partitioned away) don’t return stale data. There’s no guess work, the nodes simply don’t respond. Eventually, when the partition heals, all nodes sync up with the majority side and behave normally again.&lt;/p&gt;

&lt;p&gt;Sure this is cumbersome when you’re the client connected to the minority side, but this is what guarantees you data “consistency” (remember, what we mean here by consistency is actually linearizability).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Costs of Linearizability
&lt;/h3&gt;

&lt;p&gt;As we’ve shown thus far, nothing is free in systems. CP systems pay for linearizability with latency, availability, and operational complexity. Why? Because &lt;strong&gt;&lt;em&gt;consensus&lt;/em&gt;&lt;/strong&gt; is expensive.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Quick Primer on Consensus Protocols
&lt;/h2&gt;

&lt;p&gt;What problem have we identified thus far? CP systems need nodes to &lt;em&gt;agree&lt;/em&gt; on the state of data. Easy when everything is going well, but given the unreliable nature of networks this is harder than it sounds.&lt;/p&gt;

&lt;p&gt;There are two main protocols that allow multiple machines to achieve consensus: Paxos and Raft.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paxos
&lt;/h3&gt;

&lt;p&gt;Paxos is the original consensus protocol. Created by &lt;a href="https://lamport.azurewebsites.net/pubs/paxos-simple.pdf" rel="noopener noreferrer"&gt;Leslie Lamport in 1989&lt;/a&gt;, Paxos is notorious for being difficult to understand (the original paper’s title should be enough proof of that).&lt;/p&gt;

&lt;p&gt;Paxos &lt;strong&gt;guarantees&lt;/strong&gt; three properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validity:&lt;/strong&gt; Only values that were actually proposed can be chosen. There’s no randomness and no garbage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agreement (Consensus):&lt;/strong&gt; All nodes that reach a decision will reach the &lt;em&gt;same&lt;/em&gt; decision. No two nodes can come to different decisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrity:&lt;/strong&gt; Once a value is chosen, it’s permanent. There’s no un-choosing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basically, nodes go through multiple rounds of voting to agree on a specific value. Once a value is chosen it can &lt;strong&gt;&lt;em&gt;never&lt;/em&gt;&lt;/strong&gt; be un-chosen, regardless of what happens.&lt;/p&gt;

&lt;p&gt;These guarantees hold even throughout node crashes, message loss, and packet delays. Paxos never compromises on safety. The downside? Paxos can’t guarantee &lt;strong&gt;&lt;em&gt;liveness.&lt;/em&gt;&lt;/strong&gt; Under some conditions (competing proposers, network issues), Paxos can stall indefinitely. It won’t return a wrong answer, but it might just not answer altogether (do you want to be right all the time or do you want to respond all the time). In production systems this is very rare, techniques like random backoffs and leader election help some of this, but it’s important to note that this is a trade-off in the protocol itself.&lt;/p&gt;

&lt;p&gt;In the real world, Paxos is very difficult to implement properly which led to alternatives. Many systems you’ll encounter in the real world use &lt;strong&gt;Raft&lt;/strong&gt; instead of Paxos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Raft
&lt;/h3&gt;

&lt;p&gt;Raft was created in 2014 with one goal in mind: comprehensibility. The authors of the Raft paper (&lt;a href="https://raft.github.io/raft.pdf" rel="noopener noreferrer"&gt;In Search of an Understandable Consensus Algorithm&lt;/a&gt;) quite literally ran user studies that showed that Raft was easier to understand and comprehend when compared to Paxos.&lt;/p&gt;

&lt;p&gt;Raft, like Paxos, provides the same three guarantees: validity, quorum-based agreement, and integrity. But Raft, to make the protocol more understandable, breaks them into three separate problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leader Election:&lt;/strong&gt; Only one leader per term (time period). All writes go through this leader.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log Completion:&lt;/strong&gt; If an entry is committed, it &lt;strong&gt;will&lt;/strong&gt; be in all future leaders’ logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety:&lt;/strong&gt; A committed write will never be lost, even if the leader goes down.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Similar to Paxos, Raft requires a &lt;strong&gt;majority quorum&lt;/strong&gt; to make decisions. Like our previous example, in a 5-node cluster, you’d need 3 nodes to agree. If you can only reach 2 nodes, the system would stop accepting writes.&lt;/p&gt;

&lt;p&gt;The liveness problem is addressed with randomized election timeouts. When a leader dies, followers wait a random amount of time before conducting an election. This makes split votes (where no candidate is able to reach quorum) rare in practice.&lt;/p&gt;

&lt;p&gt;Let’s see the liveness problem visualized.&lt;/p&gt;

&lt;p&gt;Two nodes timeout simultaneously and both become candidates. This time A squeaks by with 3 votes, but it was close. If one more node had voted for B, we'd have a split.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbik8r7vev0j9o1njgidh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbik8r7vev0j9o1njgidh.png" width="800" height="662"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The worst case. Votes split evenly, no one reaches quorum. The cluster has to wait for timeouts to expire and try again. If this keeps happening, you're stuck without a leader.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhb9lzrvrx8cbc2794by8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhb9lzrvrx8cbc2794by8.png" width="800" height="788"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Randomized timeouts fix this. Each node picks a random timeout (e.g., 150-300ms). Node A wakes up first at 150ms and campaigns unopposed. By the time B wakes up at 243ms, A is already leader. No split, no drama.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsaftqo6klm4enm9g8nn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsaftqo6klm4enm9g8nn.png" width="800" height="742"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One problem both Raft and Paxos share is that neither handles Byzantine faults. Both protocols assume nodes to be honest.&lt;/p&gt;

&lt;p&gt;I’ll save the deep-dive into Raft / Paxos internals for another post. For now all you need to understand is: consensus requires coordination, and coordination is very costly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.notion.so/%%checkout_url%%" rel="noopener noreferrer"&gt;Subscribe now&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Costs of CP Systems
&lt;/h2&gt;

&lt;p&gt;Here’s something that the CAP Theorem fails to mention: even when there is &lt;em&gt;no partition&lt;/em&gt;, CP systems pay a performance price.&lt;/p&gt;

&lt;h3&gt;
  
  
  Latency
&lt;/h3&gt;

&lt;p&gt;Every write requires a confirmation from multiple nodes. In our example of a 5-node cluster, a single write requires 3 confirmations. That’s at least 3 round trips before a write is acknowledged.&lt;/p&gt;

&lt;p&gt;If the nodes are in the same data center, latency is a few milliseconds. However, if the nodes are geographically separated, that latency shoots up to anywhere between 5-200ms, per write.&lt;/p&gt;

&lt;p&gt;That’s why when designing CP systems geographic placement of nodes is important. Global distribution and strong consistency don’t mix well.&lt;/p&gt;

&lt;p&gt;Consensus hates distance. For CP systems latency is equivalent to the &lt;em&gt;slowest node you must wait for&lt;/em&gt; not the average node latency. So in our 5-node cluster, a write requires 3 nodes to agree. The latency for that write is the latency of the slowest member of the agreeing nodes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Throughput
&lt;/h3&gt;

&lt;p&gt;Since all writes flow through a single leader, you can only write as fast as that leader can process and replicate. The leader is responsible for serialization, ordering, and coordinating replication. With multiple writes, CPU, network, and disk resource contention accumulates.&lt;/p&gt;

&lt;p&gt;How do we increase throughput? If you said &lt;em&gt;scaling&lt;/em&gt; you’d be right….partially. Horizontal scaling doesn’t help. Actually, it might be more of a pain. Adding more nodes increases coordination overhead. Past a certain point, horizontal scaling actually &lt;em&gt;makes you slower&lt;/em&gt;. So, we vertically scale. Even then, there are cons to vertical scaling.&lt;/p&gt;

&lt;p&gt;Scaling vertically makes CPU, memory, and disk i/o faster. But it &lt;strong&gt;&lt;em&gt;does not&lt;/em&gt;&lt;/strong&gt; lower network round-trip times.&lt;/p&gt;

&lt;p&gt;There is a throughput ceiling for CP systems. Throughput is directly related to the round-trip time for quorum &lt;strong&gt;&lt;em&gt;and&lt;/em&gt;&lt;/strong&gt; the ordering capacity of the leader.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leader Election Storms
&lt;/h3&gt;

&lt;p&gt;When the leader node dies, the cluster elects a new one. Each election causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coordination overhead as nodes ask for votes and reply.&lt;/li&gt;
&lt;li&gt;Service disruption while the cluster, effectively, pauses to elect a leader.&lt;/li&gt;
&lt;li&gt;Instability as nodes switch roles and reset timers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Elections are annoying. If two nodes start campaigning at the same time, votes can split and no one reaches quorum. Raft uses randomized timeouts to avoid this, but under network jitter or heavy load, you can still get stuck in multiple rounds of failed elections.&lt;/p&gt;

&lt;p&gt;If you’ve used Kubernetes before, you may have run into issues where &lt;strong&gt;etcd&lt;/strong&gt; completely stops responding and it isn’t your doing.&lt;/p&gt;

&lt;p&gt;Let’s go back to our 5-node etcd cluster (Nodes A-E) example, let's say your cloud provider's network hiccups for 500ms, just long enough for the leader to lose contact with 2 followers. It steps down. Election happens, Node B wins. 200ms later, another blip. Node B can't reach quorum now. Steps down. Another election. This keeps going while &lt;code&gt;kubectl&lt;/code&gt; commands pile up in the background. Network stabilizes, you check the logs, and realize you just burned 30 seconds on 3 elections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational Complexity
&lt;/h3&gt;

&lt;p&gt;CP systems are very hard to implement. An added layer of complexity is capacity planning.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Odd Number of Nodes:&lt;/strong&gt; a 4-node cluster and 3-node cluster have the same fault tolerance (losing two nodes will render the system unavailable since quorum can’t be met). So adding a single node doesn’t really help in this case.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quorum / Fault Tolerance Math Matters:&lt;/strong&gt; A 5-node cluster needs 3 nodes for quorum, meaning it can withstand 2 nodes going down. A 3-node cluster needs to 2 nodes for quorum, meaning it can handle only one node going down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure Pains:&lt;/strong&gt; CP systems behave &lt;em&gt;correctly&lt;/em&gt; under failure, but that doesn’t mean its pretty.  Partial failures become an operational pain. CP systems react rather defensively to partial failures, since the system is design around &lt;strong&gt;&lt;em&gt;safety first.&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World CP Systems
&lt;/h2&gt;

&lt;p&gt;Let’s look at a few CP systems you’ll actually encounter or have already encountered.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;etcd&lt;/code&gt; : The Backbone of Kubernetes
&lt;/h3&gt;

&lt;p&gt;If you’ve used / played around with Kubernetes at all, you’ve used &lt;code&gt;etcd&lt;/code&gt; . Whether you knew it or not.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;etcd&lt;/code&gt; is a distributed key-value store designed for reliability over speed. It’s purpose is not to deliver high-throughput workloads, but rather for data that absolutely cannot be wrong. Things like configuration files, coordination, service discovery, etc. Small amount of priceless data that everything else depends on. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;etcd&lt;/code&gt; is responsible for storing the cluster state: pod definitions, service configs, secrets, all of it. When you run a &lt;code&gt;kubectl apply&lt;/code&gt; that manifest that you provide ends up in &lt;code&gt;etcd&lt;/code&gt;. When the scheduler needs to decide where to put a pod, it reads from &lt;code&gt;etcd&lt;/code&gt; . &lt;/p&gt;

&lt;h3&gt;
  
  
  Why CP?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsv03xxsr1gh7do83c6nt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsv03xxsr1gh7do83c6nt.png" alt=" raw `etcd` endraw  is the single source of truth. Every component asks the same question: " width="800" height="1079"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;etcd&lt;/code&gt; is the single source of truth. Every component asks the same question: "What does &lt;code&gt;etcd&lt;/code&gt; say?”&lt;/p&gt;

&lt;p&gt;Kubernetes needs a single source of truth. Every component of Kubernetes (the scheduler, controller manager, kubelets, etc) makes decisions based on what’s stored in &lt;code&gt;etcd&lt;/code&gt;. If two nodes disagree about, say, which pods are running where, you get absolute chaos. Double scheduled pods, orphaned containers, screwed up service routing, and the failures snowball into a nasty ugly mess.&lt;/p&gt;

&lt;p&gt;In these situations, would it make sense for the system to continue to serve inconsistent / wrong data? Or is it more beneficial for the system to stop for a little bit and straighten things out? &lt;code&gt;etcd&lt;/code&gt; handles this by electing a new leader, that brief pause in availability is annoying as an end user, but it is infinitely better than an inconsistent system.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Trade-Offs In Reality
&lt;/h3&gt;

&lt;p&gt;Ever experience a situation where &lt;code&gt;kubectl&lt;/code&gt; commands hang or fail during cluster instability? That’s &lt;code&gt;etcd&lt;/code&gt; behaving like a CP system, as it was designed to be. If &lt;code&gt;etcd&lt;/code&gt; can’t reach quorum, it’ll stop accepting writes. Your &lt;code&gt;kubectl apply&lt;/code&gt; commands time out, it’s safer to deny the write. &lt;/p&gt;

&lt;p&gt;I’ve faced this multiple times, whether its slow disk I/O, network latency, or resource exhaustion. Each of these can lead to failed heartbeat checks which, in turn, leads to leader re-election and suddenly the cluster become unresponsive. Slow writes hang, &lt;code&gt;kubectl&lt;/code&gt; commands just hang. It’s annoying, but that’s the correct behavior as &lt;code&gt;etcd&lt;/code&gt; is designed. &lt;code&gt;etcd&lt;/code&gt; is protecting data integrity.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;etcd&lt;/code&gt; uses Raft for consensus. This means that all writes go through a single leader node and require an acknowledgement from a majority before it gets committed. In the context of this post, &lt;code&gt;etcd&lt;/code&gt; reads must also go through the leader node, since it’s the only one that knows what’s been committed. &lt;code&gt;etcd&lt;/code&gt; also allows for serializable reads (must be configured this way) but, by default, &lt;code&gt;etcd&lt;/code&gt; provides &lt;em&gt;linearizable&lt;/em&gt; reads. &lt;/p&gt;

&lt;h3&gt;
  
  
  Zookeeper: The OG
&lt;/h3&gt;

&lt;p&gt;Before &lt;code&gt;etcd&lt;/code&gt;, there was Zookeeper. It’s much older than &lt;code&gt;etcd&lt;/code&gt; and is the backbone for the coordination for systems like Hadoop, Kafka, and Spark. If you’ve used any of those systems, Zookeeper was running in the background keeping things in order. &lt;/p&gt;

&lt;p&gt;Like &lt;code&gt;etcd&lt;/code&gt;, Zookeeper is also a distributed key-value store. It’s a very specific KV-store (not worth getting into in this post, but fundamentally it only stores &lt;strong&gt;&lt;em&gt;critical&lt;/em&gt;&lt;/strong&gt; data) that handles mission critical data that is absolutely needed to setup and maintain coordination. Things like, who the current leader is, what the current config looks like, etc. &lt;/p&gt;

&lt;h3&gt;
  
  
  Why CP?
&lt;/h3&gt;

&lt;p&gt;Much like &lt;code&gt;etcd&lt;/code&gt; , Zookeeper maintains the coordination for a system. No two nodes should think they’re the leader, no two processes should both hold a resource lock, no node should be serving stale data once a write has been committed. &lt;/p&gt;

&lt;p&gt;If those issues aren’t addressed you can run into split-brain failures, data corruption, or cascading failures. These aren’t failure modes that can just be ignored. Zookeeper, like &lt;code&gt;etcd&lt;/code&gt; , was designed to address these very issues. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Trade-Offs in Reality
&lt;/h3&gt;

&lt;p&gt;Zookeeper has the same quorum related issues as &lt;code&gt;etcd&lt;/code&gt; , for a 5-node cluster if you lose 3 nodes it stops entirely. No writes, no reads, nothing at all. &lt;/p&gt;

&lt;p&gt;The problem with Zookeeper is the way it’s integrated. Zookeeper is often a dependency for other systems such as: Kafka broker coordination, Flink high availability, Hadoop for failover coordination.&lt;/p&gt;

&lt;p&gt;So if Zookeeper goes down, your Kafka topic goes down, suddenly your event stream is down and your entire system stalls. All because one coordination system couldn’t maintain quorum.&lt;/p&gt;

&lt;h3&gt;
  
  
  PostgreSQL: Everybodies Favorite DB
&lt;/h3&gt;

&lt;p&gt;Here’s a shocking on, PostgreSQL &lt;em&gt;can&lt;/em&gt; be a CP system. Not by default, gotta make a few config changes but do that and it can behave like a CP system. &lt;/p&gt;

&lt;p&gt;Out of the box, Postgres with async replication doesn’t completely fit into CP or AP. Writes go to a single primary. Stanbys replay the logs but don’t actually accept their own writes. If the primary dies before replication is completed, those writes are gone. But you never get wrong data. For many, this is fine. You accept the small risk of data loss for better performance.&lt;/p&gt;

&lt;p&gt;But financial systems and booking systems can’t lose a single transaction. Situations where yolo-ing isn’t an option. &lt;/p&gt;

&lt;h3&gt;
  
  
  Making Postgres CP
&lt;/h3&gt;

&lt;p&gt;In order to make Postgres into a CP compliant system we have to enable synchronous replication. Once enabled, every write will wait for &lt;em&gt;at least&lt;/em&gt; one replica to confirm the write before the primary acknowledges the commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;postgresql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;
&lt;span class="n"&gt;synchronous_commit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;
&lt;span class="n"&gt;synchronous_standby_named&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;server_name&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s all it takes to make Postgres CP compliant. Your writes are now redundant across multiple nodes. If your primary goes down, a standby has everything needed to serve data. Zero data loss on a failover.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Trade-Offs in Reality
&lt;/h3&gt;

&lt;p&gt;Now that you’ve turned on synchronous commit, you’re waiting on the network for every write. If your standby is slow, writes are slow. If your standby is unreachable, your writes hang. You’ve successfully traded availability for consistency. &lt;/p&gt;

&lt;p&gt;Many don’t really need this. You can get by with async replication with proper failover. But for those instances where losing data is just not an option, synchronous replication is how you address it.&lt;/p&gt;

&lt;h3&gt;
  
  
  MongoDB: CP If Configured Properly
&lt;/h3&gt;

&lt;p&gt;MongoDB, a NoSQL DBMS that uses a document-oriented data model, can behave as both AP and CP depending on its configuration. &lt;/p&gt;

&lt;p&gt;By default, MongoDB isn’t CP. By default, writes are acknowledged after hitting only the primary and reads are local which means it can be unreplicated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;writeConcern:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;w:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;readConcern:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"local"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is fast, but it’s also how customers allow for weaker consistency guarantees than a pure CP system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making MongoDB CP
&lt;/h3&gt;

&lt;p&gt;It only takes two settings to configure MongoDB as a CP system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WRITE: WAIT FOR MAJORITY OF REPLICAS&lt;/span&gt;
&lt;span class="c1"&gt;// per operation&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;widget&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;writeConcern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;majority&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// per collection&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;writeConcernt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;majority&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// per client&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;writeConcern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;majority&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="c1"&gt;// READS: ONLY RETURN DATA COMMITTED TO MAJORITY&lt;/span&gt;
&lt;span class="c1"&gt;// per operation&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;readConcern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;linearizable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;readPref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// per collection&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withOptions&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;readConcern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;linearizable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;readPreference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// per client&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;readConcern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;linearizable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;readPreference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&lt;/span&gt;&lt;span class="dl"&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 &lt;code&gt;w: "majority"&lt;/code&gt; , writes aren’t acknowledged until they’ve been replicated to a &lt;em&gt;majority&lt;/em&gt; of the replica sets. With the &lt;code&gt;linearizable&lt;/code&gt; read option, reads only return data that’s been committed by the majority. With these two options we have &lt;em&gt;strong consistency&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pitfalls
&lt;/h3&gt;

&lt;p&gt;This has to be explicitly done. MongoDB doesn’t default to a strong consistency model. The cost of these changes are pretty significant, majority writes are slower and linearizable reads can only go to the primary.&lt;/p&gt;

&lt;p&gt;When configuring MongoDB don’t assume a strongly consistent data model. If you need CP, configure it.&lt;/p&gt;

&lt;p&gt;[BUTTON]&lt;/p&gt;




&lt;h3&gt;
  
  
  When Should You Choose CP?
&lt;/h3&gt;

&lt;p&gt;CP systems aren’t  &lt;em&gt;better or worse&lt;/em&gt; than AP systems. They’re both perfect for different use cases.&lt;/p&gt;

&lt;p&gt;Choose CP when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time consistency for shared state is a necessity. Financial transactions, inventory, etc. Anything where &lt;em&gt;eventually being right&lt;/em&gt; is just not good enough.&lt;/li&gt;
&lt;li&gt;Coordination is the primary driver. Like we saw, leader election, distributed locks, and config management are all driven by coordination.&lt;/li&gt;
&lt;li&gt;The blast radius for inconsistencies is large. Disagreeing nodes causing cascading failures??? Choose CP.&lt;/li&gt;
&lt;li&gt;Brief moments of unavailability are tolerable. Leader elections or network issues can cause slight outages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be cautious of CP systems when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Availability is paramount. User-facing app where &lt;em&gt;something&lt;/em&gt; is better than &lt;strong&gt;nothing&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Global write distribution is required. Strong consistency across regions increases write latency and can reduce availability.&lt;/li&gt;
&lt;li&gt;Consistency requirements are weaker than &lt;code&gt;linearizability&lt;/code&gt; . Many systems are fine with causal consistency or read-your-writes. These system don’t require &lt;em&gt;linearizability&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The question you should ask is this: “What’s worse? Being briefly wrong or briefly unavailable?”&lt;/p&gt;

&lt;p&gt;If stale data can cause harm, lean CP. If refusing requests causes more harm, lean AP. &lt;/p&gt;




&lt;h2&gt;
  
  
  Up Next: AP Systems
&lt;/h2&gt;

&lt;p&gt;We’ve discussed CP systems and how they sacrifice latency, throughput, and availability for consistency. What about systems that choose availability over consistency?&lt;/p&gt;

&lt;p&gt;In Part 3 of this 4-part series, we’ll take a look at AP Systems, datastores that prioritize availability over consistency:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How DynamoDB and Cassandra remain available during network partitions?&lt;/li&gt;
&lt;li&gt;Conflict resolutions strategies&lt;/li&gt;
&lt;li&gt;Eventual Consistency is actually okay (sometimes)&lt;/li&gt;
&lt;li&gt;and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The “pick two” framing devolves systems into thinking its a binary decision between CP and AP systems. Reality is a lot more complicated and the trade-off decisions are extremely interesting.&lt;/p&gt;

&lt;p&gt;Thanks again for sticking around. If you enjoyed this post, please subscribe, it gives me the confidence to keep going.&lt;/p&gt;

&lt;p&gt;Ali&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/en/blog/a-guide-to-etcd" rel="noopener noreferrer"&gt;A Guide to etcd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mongodb.com/docs/manual/reference/read-concern/" rel="noopener noreferrer"&gt;MongoDB’s Read Concern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lamport.azurewebsites.net/pubs/paxos-simple.pdf" rel="noopener noreferrer"&gt;Paxos Made Simple (The Original Paper)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://raft.github.io/raft.pdf" rel="noopener noreferrer"&gt;In Search of an Understandable Consensus Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zookeeper.apache.org/doc/r3.5.1-alpha/zookeeperOver.html" rel="noopener noreferrer"&gt;Zookeeper Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/runtime-config-replication.html" rel="noopener noreferrer"&gt;PostgreSQL Replication&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/" rel="noopener noreferrer"&gt;Subscribe&lt;/a&gt; to get Part 3 as soon as it drops!&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>database</category>
      <category>postgres</category>
      <category>backend</category>
    </item>
    <item>
      <title>CAP Theorem Explained: Beyond the "Pick Two" Myth</title>
      <dc:creator>Ali Malik</dc:creator>
      <pubDate>Tue, 03 Feb 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/amalik18/cap-theorem-explained-beyond-the-pick-two-myth-k1n</link>
      <guid>https://dev.to/amalik18/cap-theorem-explained-beyond-the-pick-two-myth-k1n</guid>
      <description>&lt;p&gt;Let's get to the bottom of what Consistency, Availability, and Partition Tolerance actually mean in production distributed systems.&lt;/p&gt;

&lt;p&gt;Hey folks, I’m back. I haven’t posted in a while, I know, I know. Between work, life, and the usual "is anyone even reading this?" doubt spiral, I let this blog collect dust. But I'm back with a new approach.&lt;/p&gt;

&lt;p&gt;Instead of sporadic posts on random topics, I’m committing to deep-dive series on fundamental concepts that I wish I understood better earlier in my career. Technical content that goes beyond the interview answer and into the real production trade-offs.&lt;/p&gt;

&lt;p&gt;This four-part series on CAP theorem is the first of many deep dives I have planned. If you’ve ever felt like you “get” distributed systems in theory but struggle to apply that knowledge in practice, this series is for you.&lt;/p&gt;

&lt;p&gt;Thanks for sticking around (or for just discovering this blog). Let’s learn together.&lt;/p&gt;

&lt;p&gt;Get the full 4-part series straight to your inbox.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;This is Part 1 of a 4-part series on CAP theorem and distributed systems trade-offs. Part 2 drops next week.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you’ve interviewed for mid or senior-level engineering roles within the last decade, you’re sure to have been asked a systems design question that ultimately leads to some version of the CAP theorem. And you probably answered something along the lines of “you can only pick two of three”. You were right…kinda.&lt;/p&gt;

&lt;p&gt;While it’s technically correct, the thought behind it is grotesquely oversimplified. That oversimplification comes back to bite you when you’re debugging why your distributed data store went down.&lt;/p&gt;

&lt;p&gt;My goal is to demystify what the CAP theorem tells us and explain how production systems get around this “pick two” crutch.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is The CAP Theorem?
&lt;/h2&gt;

&lt;p&gt;Let’s go back to the turn of the century, in 2000, &lt;a href="https://www2.eecs.berkeley.edu/Faculty/Homepages/brewer.html" rel="noopener noreferrer"&gt;Eric Brewer&lt;/a&gt;, then running Inktomi Corporation, stood at the podium of ACM’s PODC symposium and made a statement that would trouble developers for decades: a distributed data store can only guarantee at most two of the three of the following: Consistency, Availability, and Partition Tolerance.&lt;/p&gt;

&lt;p&gt;A couple of years later Brewer’s theorem was formalized into a proof by Seth Gilbert and Nancy Lynch. The theorem, the idea behind it, everything is true. But somewhere in the last two decades between all of the whiteboard sessions the nuance got lost.&lt;/p&gt;

&lt;p&gt;What’s the nuance you ask? Well everyone remembers the “pick two” portion of the conjecture but they almost always leave out the most important context: &lt;strong&gt;you only have to pick two &lt;em&gt;during&lt;/em&gt; a network partition&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When the network is healthy, you can have all three. The theorem provides guidance only when things go awry—when you start losing network packets, or switches fail, or when a rodent chews through a fiber line (this has actually happened at Amazon).&lt;/p&gt;




&lt;h2&gt;
  
  
  CAP Theorem: Consistency, Availability, and Partition Tolerance Defined
&lt;/h2&gt;

&lt;p&gt;Let’s be super precise about what C, A, and P actually are and what they mean, because the casual definitions lack a bit of nuance.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is Consistency in CAP Theorem? (Linearizability)
&lt;/h3&gt;

&lt;p&gt;When the CAP theorem talks about consistency, it’s talking about the highest form of consistency, &lt;em&gt;atomic consistency&lt;/em&gt;. If you’re coming from a database background, you’ve heard of weak and strong consistency. Atomic consistency is just a step above strong consistency. Atomic consistency is also known as &lt;strong&gt;&lt;em&gt;linearizability&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Linearizability is when all operations execute &lt;em&gt;atomically&lt;/em&gt; in some order with respect to real-time. For example, if operation A completes before operation B begins, then operation B should logically take effect after operation A.&lt;/p&gt;

&lt;p&gt;Let’s use a real world example to help illustrate this idea. Imagine you’re checking your bank account balance and you see $5000. Let’s say you transfer $1000 out of the account. Now you refresh your balance again. Linearizability &lt;strong&gt;&lt;em&gt;guarantees&lt;/em&gt;&lt;/strong&gt; $4000 (or an error), but never $5000 again. The transfer either happened or it didn’t. There’s no situation in which you will see the old value after the new value has been written.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev" rel="noopener noreferrer"&gt;Subscribe now&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0a6alazp6isbld22oi2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0a6alazp6isbld22oi2.png" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;a lot&lt;/strong&gt; stronger than “all nodes eventually see the same data” — this is called &lt;em&gt;eventual consistency&lt;/em&gt; and it &lt;strong&gt;is NOT&lt;/strong&gt; what the CAP theorem is talking about.&lt;/p&gt;

&lt;p&gt;Why is this important? Because there are a bunch of systems that provide weaker consistency models, such as: monotonic reads, read-your-writes, and causal consistency, while being extremely useful. But all of those models are weaker than linearizability, which means systems can remain &lt;em&gt;highly available&lt;/em&gt; while providing them. The CAP theorem doesn’t constrain systems using these models, only linearizability.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is Availability in CAP Theorem?
&lt;/h3&gt;

&lt;p&gt;CAP’s definition of availability is also very precise, it states “every request to a non-failing node &lt;em&gt;must&lt;/em&gt; receive a response”.&lt;/p&gt;

&lt;p&gt;Notice that this doesn’t say any of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;That the response has to be fast.&lt;/li&gt;
&lt;li&gt;That the response has to be correct.&lt;/li&gt;
&lt;li&gt;That &lt;em&gt;most&lt;/em&gt; nodes have to respond.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if you have a 10 node cluster and 4 of those nodes are partitioned away, those 4 nodes &lt;strong&gt;must&lt;/strong&gt; still respond to requests to be considered “available” under CAP’s definition of “availability”. In this case a 4xx error or a stale read still counts as “available”.&lt;/p&gt;

&lt;p&gt;Here’s a real world example: During a network partition, a MongoDB primary replica set that can’t reach all of the secondary replicas will “step down” and refuse writes altogether. These write requests will time out. Under the CAP theorem that is &lt;strong&gt;unavailability&lt;/strong&gt;, even though the MongoDB process is running as expected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82g52iou9amboj4edgd3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82g52iou9amboj4edgd3.png" width="800" height="68"&gt;&lt;/a&gt;&lt;br&gt;Even the isolated nodes (shown in red) must keep responding to requests to be "available" under CAP's definition. If they timeout or refuse to answer because they can't reach the majority, that's unavailability—even though they're still running and the cluster is "mostly up."
    &lt;/p&gt;

&lt;h3&gt;
  
  
  What Is Partition Tolerance? (And Why You Can't Avoid It)
&lt;/h3&gt;

&lt;p&gt;This is where the “pick two” framing stems.&lt;/p&gt;

&lt;p&gt;Partition tolerance means that your system continues to work as advertised even when the network splits your nodes into groups that can’t communicate. Guess what? This isn’t up to you.&lt;/p&gt;

&lt;p&gt;Network partitions are a fact of life in distributed systems. The internet that we rely on daily, the data centers that serve our applications all have this property. Switches fail, fibers get cut, cloud providers have availability zone outages, packets get dropped, even within a data center there are transient network issues. It’s unavoidable.&lt;/p&gt;

&lt;p&gt;The reality is, &lt;strong&gt;partition tolerance is absolutely mandatory&lt;/strong&gt;. You’re not picking between C, A, and P. You’re really picking between C and A when P inevitably occurs.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Trade-off
&lt;/h2&gt;

&lt;p&gt;Okay let’s observe what happens when the network eventually fails.&lt;/p&gt;

&lt;p&gt;Before doing so, let’s rephrase the CAP theorem and its choices given that P (Partition Tolerance) is absolutely mandatory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency:&lt;/strong&gt; refuse requests that might return stale data (sacrificing availability)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Availability&lt;/strong&gt;: answer all requests even if data is stale (sacrificing consistency)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fii8wsim96qevmz0kws01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fii8wsim96qevmz0kws01.png" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

This diagram helps to visualize the choices available at a network partition.



&lt;p&gt;Once again, take note of the precondition; “&lt;em&gt;when&lt;/em&gt; the network fails”. If the network is fine we can have both consistency and availability. When there's no partition? You can have both. Your bank's mobile app shows you accurate, up-to-date balances while staying highly available—because most of the time, the network works fine. You check your balance, transfer money, and see the updated amount immediately.&lt;/p&gt;

&lt;p&gt;The question the CAP theorem forces you to address is: &lt;strong&gt;What should your system do when the network eventually fails?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8vosmhbkjgv3pig8i6vg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8vosmhbkjgv3pig8i6vg.png" alt="The core trade-off: During a partition, CP systems protect you from seeing wrong data by becoming unavailable. AP systems protect you from downtime by potentially serving stale data. Neither is good or bad, it all depends on what is right for your system." width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;
The core trade-off: During a partition, CP systems protect you from seeing wrong data by becoming unavailable. AP systems protect you from downtime by potentially serving stale data. Neither is good or bad, it all depends on what is right for your system.



&lt;h3&gt;
  
  
  CP Systems: Choosing Consistency Over Availability
&lt;/h3&gt;

&lt;p&gt;CP systems decide that during a network partition, they’ll refuse requests rather than risk returning outdated data.&lt;/p&gt;

&lt;p&gt;So when does this make sense?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Banks&lt;/strong&gt;: If a user makes a transaction is it preferable to be down for a little bit or risk the transaction occurring twice? Probably the former right? We absolutely do not want to tell the customer that the transaction succeeded when it hasn’t. We’d rather be down for like 30 seconds than risk that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inventory Management:&lt;/strong&gt; if we’re not sure whether an item is in stock, is it better to make the service unavailable or to allow the possibility of over-selling? We’d probably be okay with unavailability.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s analyze a real-world system, &lt;strong&gt;etcd&lt;/strong&gt;. From their own website: “etcd is a strongly consistent, distributed key-value store…It gracefully handles leader elections during network partitions and can tolerate machine failure, even in the leader node.”&lt;/p&gt;

&lt;p&gt;Kubernetes uses etcd as its backing store for all cluster configurations. If you’ve used Kubernetes before, you’ve seen &lt;code&gt;kubectl&lt;/code&gt; command failures. One reason: etcd can’t reach a majority of its nodes, it sacrifices availability (&lt;code&gt;kubectl&lt;/code&gt; failures) for linearizability.&lt;/p&gt;

&lt;h3&gt;
  
  
  AP Systems: Choosing Availability Over Consistency
&lt;/h3&gt;

&lt;p&gt;AP systems decide that during a network partition, they’ll risk serving stale data rather than deny requests.&lt;/p&gt;

&lt;p&gt;A few systems that this applies for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Social Media Feeds:&lt;/strong&gt; users would much rather the app serve old / stale content rather than be completely unavailable. Additionally, does it really matter if a user doesn’t see a reaction to their post for a couple of seconds? Not really. The user experience of the app being down is far worse than reaction tallies being off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shopping Carts:&lt;/strong&gt; A very famous example of this is Amazon’s DynamoDB which actually started off addressing their shopping cart problem; it’s an “always on” experience. You can always add to cart. Doesn’t matter if someone else is fiddling with your cart (Amazon will resolve the changes), you will always be able to “add to cart”.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a real-world AP system you don’t have to think very hard, the most pure AP system is none other than DNS itself. DNS, the Domain Name System, is the perfect example of an AP system and we all use it hundreds of times a day without even realizing it.&lt;/p&gt;

&lt;p&gt;When a DNS record is updated, the update doesn’t propagate to every DNS server around the world immediately. Different DNS servers around the world may give an outdated DNS entry, &lt;strong&gt;however&lt;/strong&gt;, DNS is never “unavailable”. It will always give you a response, even if that response is stale. This is exactly why DNS updates take time and why some sites appear unavailable to a few people when going through updates. In the case of DNS, eventual consistency is absolutely better than downtime, that would kill the internet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next Up: The Hidden Costs of Choosing Consistency
&lt;/h2&gt;

&lt;p&gt;In the next part of this series, we'll dive deep into CP systems—databases and services that prioritize consistency over availability during network failures. We’ll take a look at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How these CP systems implement strong consistency.&lt;/li&gt;
&lt;li&gt;The cost of coordination.&lt;/li&gt;
&lt;li&gt;When choosing consistency over availability makes sense.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The “pick two” gives the illusion of being stuck with your choice for the entire lifetime of the system, when in fact the reality is a lot more flexible.&lt;/p&gt;

&lt;p&gt;Subscribe to get Part 2 when it drops.&lt;/p&gt;

&lt;p&gt;Thanks again for sticking around. If you enjoyed this post, please &lt;a href="https://www.blog.ahmazin.dev/" rel="noopener noreferrer"&gt;subscribe&lt;/a&gt;, it gives me the confidence to keep going.&lt;/p&gt;

&lt;p&gt;Ali&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.researchgate.net/publication/221343719_Towards_robust_distributed_systems" rel="noopener noreferrer"&gt;Eric Brewer’s 2000 PODC Presentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://users.ece.cmu.edu/~adrian/731-sp04/readings/GL-cap.pdf" rel="noopener noreferrer"&gt;Brewer’s Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services by Nancy Lynch and Seth Gilbert&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://etcd.io/" rel="noopener noreferrer"&gt;etcd&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>distributedsystems</category>
      <category>systemdesign</category>
      <category>database</category>
      <category>backend</category>
    </item>
    <item>
      <title>Computer Networks 101 (#2)</title>
      <dc:creator>Ali Malik</dc:creator>
      <pubDate>Tue, 17 Sep 2024 16:01:52 +0000</pubDate>
      <link>https://dev.to/amalik18/computer-networks-101-2-4ojf</link>
      <guid>https://dev.to/amalik18/computer-networks-101-2-4ojf</guid>
      <description>&lt;p&gt;Hey folks, I’m back with this weeks post. This week we’ll be taking a deep dive in to Computer Networks. More specifically, I’ll do a deep dive in to the &lt;a href="https://en.wikipedia.org/wiki/OSI_model" rel="noopener noreferrer"&gt;OSI model&lt;/a&gt; and comparing it to the &lt;a href="https://en.wikipedia.org/wiki/Internet_protocol_suite" rel="noopener noreferrer"&gt;TCP/IP model&lt;/a&gt;. My goal is to provide a comprehensive explanation for each layer of the model while also providing concrete examples to help visualize the entire process.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I am not an artist so I’ll be leveraging graphics from various sources. The references will be in each image’s caption.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before we get started, I'd like to thank everyone for tuning in. A special shout-out to all of our subscribers—in just one short week, we're up to 11 subscribers! I appreciate each and every one of you for your support. Now, on to the regularly scheduled program…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/subscribe?" rel="noopener noreferrer"&gt;Subscribe now&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Computer networks are crucial to understand if you’re a software developer or work in the technology space at all. They are the backbone of everything we do online, whether it’s sending an email, posting on social media, or watching Netflix. At the heart of it, a computer network allows devices to communicate and share resources, enabling the seamless exchange of information across vast distances.&lt;/p&gt;

&lt;p&gt;To understand computer networks, there are two popular models that provide insight into how networks operate: the &lt;strong&gt;OSI Model&lt;/strong&gt; and the &lt;strong&gt;TCP/IP Model&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is the OSI Model?
&lt;/h1&gt;

&lt;p&gt;The Open Systems Interconnection (OSI) model is a conceptual framework created by the International Organization for Standardization (ISO) that details how data travels over a network. This is done by outlining seven distinct layers. It’s become the primary mode of teaching and discussing the various networking processes. By the way, OSI being created by ISO…the irony.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is the TCP/IP Model?
&lt;/h1&gt;

&lt;p&gt;In contrast to the OSI model, the &lt;strong&gt;TCP/IP Model&lt;/strong&gt; is a more practical representation of how devices connect and communicate across networks. It's the foundation of the internet and is a bit simpler than the OSI model, with fewer layers to remember.&lt;/p&gt;

&lt;p&gt;Below is a graphic (courtesy of the ) that showcases the very differences in the layers of the OSI model vs. the TCP/IP model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ku2zps9eh12vz5er9ib.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ku2zps9eh12vz5er9ib.png" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig1: Taken from ByteByteGo&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The Layers
&lt;/h1&gt;

&lt;p&gt;Let's discuss each of the layers of the two models. As shown in Fig1, the TCP/IP model condenses the OSI model's Application, Presentation, and Session layers into one layer labeled &lt;strong&gt;Application&lt;/strong&gt;. Keep this in mind as we delve into each layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Layer (L7)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffkl7u4mvasjzy8fgmjlj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffkl7u4mvasjzy8fgmjlj.png" width="800" height="777"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The application layer is the closest layer to the user. It’s responsible for delivering data to applications and providing network services directly to applications. This layer dictates how users interact with the network itself.&lt;/p&gt;

&lt;p&gt;Common protocols that operate at this layer are HTTP/S, FTP (File Transfer Protocol), SMTP (Simple Mail Transfer Protocol), and DNS (Domain Name System).&lt;/p&gt;

&lt;p&gt;An example interaction with this layer could look something like typing in the URL of your favorite website. The HTTP/S protocol (which runs at the application layer) is responsible for fetching the web page from the web server.&lt;/p&gt;

&lt;p&gt;In the TCP/IP model, the Application Layer encompasses not only the OSI's Application Layer but also the Presentation and Session layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Presentation Layer (L6)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy2m1xux2ab0vh3iy6g4d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy2m1xux2ab0vh3iy6g4d.png" width="800" height="752"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The presentation layer is responsible for ensuring that data sent from the application layer of one system is readable by the application layer of another system. Simply, this layer handles the serialization of data. This layer is also responsible for handling data translation, encryption, and compression. Typically, it is here that data encryption/encoding and decryption/decoding take place.&lt;/p&gt;

&lt;p&gt;Common protocols that operate at this layer are Secure Sockets Layer (SSL), Transport Layer Security (TLS).&lt;/p&gt;

&lt;p&gt;An example of this layer at work is when you visit a secure website (HTTPS), the presentation layer is responsible for encrypting and decrypting the data with SSL/TLS.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: In the TCP/IP model, the functionality of the Presentation Layer is included within the Application Layer.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Session Layer (L5)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjyg9emlk3hyuw9ydflm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjyg9emlk3hyuw9ydflm.png" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The session layer is responsible for establishing, managing, and terminating sessions between two devices. A session is defined as a time-limited two way link between two or more communication devices. This layer also manages data streams for each application/service ensuring that they do not interfere with each other. The session layer can also handle dialog control (basically is communication two way or not) and synchronization between two communicating systems. To think about synchronization, imagine a video stream where audio and video are lagging, the session layer may issue a re-synchronization request to to sync the two up.&lt;/p&gt;

&lt;p&gt;Common protocols at this layer are Remote Procedure Call Protocol (RPC), Session Control Protocol (SCP), Password Authentication Protocol (PAP).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: In the TCP/IP model, session management is generally handled by the Transport Layer.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Transport Layer (L4)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn275ow2ltgre9ojt50xr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn275ow2ltgre9ojt50xr.png" width="800" height="739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The transport layer is responsible for…. the transportation of data between appropriate applications between devices. This layer supports reliable data delivery (TCP) or providing fast but not so reliable delivery (UDP). It can break chunks of data into smaller protocol data units called _ &lt;strong&gt;segments&lt;/strong&gt; _ and then reassemble them at the destination host. This layer also deals with reliability, flow control and error handling.&lt;/p&gt;

&lt;p&gt;The most famous protocols at this layer are the Transmission Control Protocol (TCP) and User Datagram Protocol (UDP). TCP is a reliable, ordered, and connection-oriented protocol that ensures a connection is established prior to delivery. UDP is a less reliable, faster, connection-less protocol that favors speed over reliability.&lt;/p&gt;

&lt;p&gt;An example of this layer at work can be anything from sending an email with a large attachment to video streaming. Sending an email with a large attachment, TCP would ensure that the entirety of the email and its content arrive to the destination in an ordered fashion. Whereas, when you’re gaming you might be using UDP that favors the speed of transmission over the reliability of data delivery.]&lt;/p&gt;

&lt;p&gt;This layer is almost identical to the TCP/IP model’s Transport Layer. However, the TCP/IP version of this layer has additional responsibilities which are covered by the Session Layer in the OSI model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Network Layer (L3)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fda75rg573nf5vzkvhcs3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fda75rg573nf5vzkvhcs3.png" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The network layer is responsible for routing data between different networks. If the Transport Layer is the vehicle the Network Layer is the GPS. It’s responsible for packet forwarding. It uses a hosts IP address to determine the best path for data delivery from source to destination. This layer supports connection-less communication, host addressing, and message forwarding. The protocol data unit at this layer is referred to as _ &lt;strong&gt;packets&lt;/strong&gt; _ &lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most well-known protocol that operates at this layer is the Internet Protocol (IP), for example IPv4 and IPv6 are implementations of this protocol. Additionally, Internet Protocol Security (IPsec), Routing Information Protocol (RIP), and Internet Control Message Protocol (ICMP) also operates at this layer.&lt;/p&gt;

&lt;p&gt;An example of this protocol at work would be when you visit a website, the network layer uses your IP address to send requests to the destination web server.&lt;/p&gt;

&lt;p&gt;In the TCP/IP model this layer is referred to as the Network Layer or the Internet Layer. The two are &lt;strong&gt;not&lt;/strong&gt; synonymous. In the TCP/IP model the Internet layer that is referenced is only a subset of the bigger Network Layer. In the TCP/IP model the Internet layer only describes on type of network architecture… the Internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Link Layer (L2)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwaoif6qv3kajwu1dqcht.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwaoif6qv3kajwu1dqcht.png" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The data link layer is responsible for node-to-node communication on the same network. It provides the functional and procedural means to transfer data between networks. It also provides error detection and possible correction. This layer is only concerned with local delivery of _ &lt;strong&gt;frames&lt;/strong&gt; _ (the protocol data unit at this layer), meaning only on the same level of the network. This means that the frames do not cross the boundaries of the local area network.&lt;/p&gt;

&lt;p&gt;This layer is actually, commonly, divided into two sub-layers: Logical Link Control (LLC) and Media Access Control (MAC). The LLC layer multiplexes the protocols running at the Data Link layer and can optionally provide flow control, error notifications, and acknowledgements. The MAC layer controls who can access the media link at any one time.&lt;/p&gt;

&lt;p&gt;Ethernet and WiFi are the most popular protocols that operate at this layer. At the LLC layer we have things like Forward Error Correction (FEC) and Automate Repeated Request (ARQ). Everyone knows examples of ARQ like Go-Back-N, Stop and Wait, and Selective Repeat. For the MAC layer we have protocols like MAC addressing, access methods like Carrier-Sense Multiple Access with Collision Detection/Avoidance (CSMA/CD and CSMA/CA), Spanning Tree Protocol (STP), and Virtual LANs&lt;/p&gt;

&lt;p&gt;In the TCP/IP model the functionality of the Data Link layer is contained in the bottom most layer, the Link Layer. In addition to the responsibilities of the Data Link Layer, the TCP/IP model’s Link layer also takes on additional responsibilities similar to the Physical Layer of the OSI model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Physical Layer (L1)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr215hw2pgc126gec9oc3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr215hw2pgc126gec9oc3.png" width="800" height="668"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last (or first) layer of the OSI model is the Physical Layer. This layer is the lowest layer of the model meaning its most closely associated with the physical connection between devices. This layer is associated with all things electrical and mechanical regarding the connection medium.&lt;/p&gt;

&lt;p&gt;This layer defines how we transfer a stream of bits over the physical link connecting two or more nodes. You’ll find the Ethernet trans-receivers at this layer, USBs, and the physical part of WiFi (the actual radio frequency (RF) portion of things).&lt;/p&gt;

&lt;p&gt;As mentioned above the physical layer and data link layer are combined in the TCP/IP model. The TCP/IP models Link Layer handles the responsibilities of, both, the OSI models Data Link and Physical Layers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Lets see what the above looks like altogether.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2npa0a19bqxe15r2q3b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2npa0a19bqxe15r2q3b.png" width="800" height="993"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Subscribe to Professional Imposter Syndrome
&lt;/h4&gt;

&lt;p&gt;This publication is supported entirely by you, the readers. Please subscribe to support my work and to receive my next publication.&lt;/p&gt;

&lt;p&gt;I hope this deep dive into the OSI and TCP/IP models has provided a clearer understanding of how data travels across networks. Whether you're sending an email, streaming a movie, or browsing the web, these layers work together to make it happen seamlessly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stay tuned for next week's post, where we'll explore more exciting topics in the world of technology!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Catch you all next week.&lt;/p&gt;

&lt;p&gt;Ali&lt;/p&gt;

&lt;p&gt;Thanks for reading Professional Imposter Syndrome! This post is public so feel free to share it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/computer-networks-101?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share" rel="noopener noreferrer"&gt;Share&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/computer-networks-101/comments" rel="noopener noreferrer"&gt;Leave a comment&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/survey/1034779?token=" rel="noopener noreferrer"&gt;Suggest future topics!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>computerscience</category>
      <category>networking</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Life At Amazon (#1)</title>
      <dc:creator>Ali Malik</dc:creator>
      <pubDate>Mon, 09 Sep 2024 16:00:56 +0000</pubDate>
      <link>https://dev.to/amalik18/life-at-amazon-1-37ci</link>
      <guid>https://dev.to/amalik18/life-at-amazon-1-37ci</guid>
      <description>&lt;p&gt;Hey folks I’m back with this weeks post. Apologies that it’s a bit late. Lets just dive in. We’ll take a look at how I got to Amazon and my career thus far at Amazon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/subscribe?" rel="noopener noreferrer"&gt;Subscribe now&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21aExL%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F74640d65-d51b-47d8-a49d-1349d427101b_474x266.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21aExL%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F74640d65-d51b-47d8-a49d-1349d427101b_474x266.jpeg" title="Amazon Web Services Wallpapers - Top Free Amazon Web Services ..." alt="Amazon Web Services Wallpapers - Top Free Amazon Web Services ..." width="474" height="266"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;I graduated college with a BS in Computer Engineering with a focus in Computer Networks. I joined Amazon straight out of college in 2019. I joined as a Systems Analyst (a role that no longer exists) on a team tasked with staffing enough engineers to help launch the AWS Top Secret Cloud.&lt;/p&gt;

&lt;p&gt;One of the perks of this team was that it allowed me to rotate across various AWS teams to explore different roles and find what I truly wanted to do. This gave me exposure to teams ranging from Networking to S3, and even the Groundstation team.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Early Struggles: Burnout and Overwork
&lt;/h2&gt;

&lt;p&gt;The first few months were challenging, I really struggled with the delineation of work life and personal life. I was working from 9am - 6pm during the week and putting in hours over the weekend. In my mind this was how I was going to get ahead, by putting in more hours than anyone. As you can imagine, this started to slowly burn me out. I was finding myself less motivated in my day-to-day because all I could focus on was what I still didn’t know. I wanted to learn everything I could as fast I could learn it.&lt;/p&gt;

&lt;p&gt;This approach was completely unsustainable. As ridiculous as it sounds, retrospectively, this was worth it in the end. This hustle in the beginning allowed me to really find my footing and be useful to the team(s) that I was working with. A fellow colleague gave me the best advice possible at that time: “There’s enough work here to drown. There will always be work.”. This, seemingly, obvious piece of advice really helped me level-set my goals. It allowed me to focus more on the work I was tasked with and less so on the many things I didn’t know. This taught me the importance of pacing yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Finding My Footing: Building Disconnected Edge Solutions
&lt;/h2&gt;

&lt;p&gt;Remember I said the team I was on allowed mobility till I found something I liked? Well I found what I liked. I joined a team building the next generation of Disconnected Edge Solutions. At the time AWS already had an Edge Solution offering, the Snow Family of products. However, the disconnected edge space was largely untapped from AWS, that’s where this team existed. This team is where I built most of my technical expertise. I was exposed to everything from generating customer interest, all the way to physically building the solution on top of helping create the software backend to support the customer needs.&lt;/p&gt;

&lt;p&gt;The work I did on this team built the very foundation of my technical know-how. We solved some very challenging problems while trying to get this product out the door. We had on-device WiFi, computing power at the edge, storage, etc. The work I did on this product introduced me to embedded programming, low-level kernel modifications, the intersection of software and hardware. I stayed on this team until its inevitable liquidation (this was last year).&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Am I Now?
&lt;/h2&gt;

&lt;p&gt;I am now on a team which provides network connectivity to customers. Think of AWS Direct Connect (DX), I work on a product very similar to that. That’s what I’ve been doing the last year or so. Helping productionize the system and slowly deliver it to customers.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I’ve Learned
&lt;/h2&gt;

&lt;p&gt;Reflecting on my journey, I’ve realized a few key lessons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pace Yourself&lt;/strong&gt; : Early on, I fell into the trap of thinking more hours meant faster progress. It’s important to work hard but equally important to avoid burnout. Find a sustainable work-life balance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Find Your Niche&lt;/strong&gt; : Rotating through different teams gave me the chance to discover what I’m passionate about. Take advantage of similar opportunities to explore your interests and strengths.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Learn Continuously&lt;/strong&gt; : The technical challenges I faced—whether embedded programming or building edge solutions—were opportunities to grow. Never shy away from learning new things, even if they seem daunting at first.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Let’s Connect
&lt;/h2&gt;

&lt;p&gt;That’s a quick overview of my career at AWS so far. If you have any questions about Amazon, the interview process, or my experience, feel free to reach out. Also, let me know if you enjoyed this post and if there are any topics you’d like me to cover in future posts.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and I’ll see you next week!&lt;/p&gt;

&lt;p&gt;Ali&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ahmazin.substack.com/survey/724227?token=" rel="noopener noreferrer"&gt;Which Topics Should I Cover?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Professional Imposter Syndrome is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.blog.ahmazin.dev/p/life-at-amazon?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share" rel="noopener noreferrer"&gt;Share&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ahmazin.substack.com/?utm_source=substack&amp;amp;utm_medium=email&amp;amp;utm_content=share&amp;amp;action=share" rel="noopener noreferrer"&gt;Share Professional Imposter Syndrome&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>career</category>
      <category>devjournal</category>
      <category>interview</category>
    </item>
  </channel>
</rss>
