<?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: Eyo</title>
    <description>The latest articles on DEV Community by Eyo (@eyochen).</description>
    <link>https://dev.to/eyochen</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%2F663151%2Fcc5701b3-9045-4e96-95e7-46e5a73c518d.png</url>
      <title>DEV Community: Eyo</title>
      <link>https://dev.to/eyochen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eyochen"/>
    <language>en</language>
    <item>
      <title>A Straightforward Guide for Database Index (B+Tree)</title>
      <dc:creator>Eyo</dc:creator>
      <pubDate>Tue, 21 Oct 2025 14:46:09 +0000</pubDate>
      <link>https://dev.to/eyochen/a-straightforward-guide-for-btrees-33h9</link>
      <guid>https://dev.to/eyochen/a-straightforward-guide-for-btrees-33h9</guid>
      <description>&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;As a software engineer, adding an index is probably the most common fix when you hit a performance issue in your database. You've likely heard people say, "Just add an index, and the query will be much faster." But have you ever wondered &lt;em&gt;why&lt;/em&gt; that actually works?&lt;/p&gt;

&lt;p&gt;In this article, we'll explore what it really means to add an index and why it makes queries so much more efficient. I'll keep everything as straightforward as possible.&lt;/p&gt;

&lt;p&gt;Before we dive in, I'm assuming you have a basic understanding of data structures and algorithms, plus some familiarity with database management systems like MySQL or PostgreSQL. What we're covering here isn't specific to any particular DBMS. Instead, it's a general concept that applies across the board, though each system might implement it a bit differently.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Index is data structure(B+Tree)
&lt;/h1&gt;

&lt;p&gt;When you add an index to a database, you're essentially creating a data structure behind the scenes. More specifically, the database creates something called a &lt;strong&gt;B+Tree&lt;/strong&gt;.&lt;br&gt;
Let's say we have the following table:&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="n"&gt;ID&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;BadgeNumber&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;---|----------|--------------------&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Alice&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1029&lt;/span&gt; 
&lt;span class="mi"&gt;2&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Bob&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1856&lt;/span&gt; 
&lt;span class="mi"&gt;3&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Charlie&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1735&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;David&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1021&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Emma&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1069&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Frank&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1035&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Grace&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1280&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Henry&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1002&lt;/span&gt;
&lt;span class="mi"&gt;9&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Ivy&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1689&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Jack&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1050&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Karen&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1999&lt;/span&gt;
&lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Leo&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1005&lt;/span&gt;
&lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Maria&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;
&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Nathan&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1120&lt;/span&gt;
&lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Olivia&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1431&lt;/span&gt;
&lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Peter&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1618&lt;/span&gt;
&lt;span class="mi"&gt;17&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Quinn&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1008&lt;/span&gt;
&lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Rachel&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1194&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After adding an index on the &lt;code&gt;BadgeNumber&lt;/code&gt; column, the database creates this B+Tree internally:&lt;br&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%2Fbxj18fnkmnbhmtrd1js3.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%2Fbxj18fnkmnbhmtrd1js3.png" alt="B+Tree Overview" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I know this might look intimidating at first—and that's totally fine! We'll break down exactly what a B+Tree is in just a bit. For now, here's the key takeaway: when you create an index, the database builds a B+Tree data structure for you and uses it to find data incredibly quickly.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  B+Tree Breakdown
&lt;/h1&gt;

&lt;p&gt;A B+Tree has two different types of nodes, and understanding the difference between them is key to understanding how the whole thing works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Internal Nodes: The Navigation System
&lt;/h2&gt;

&lt;p&gt;Think of internal nodes as your GPS—they guide you to where you need to go, but they don't actually store your destination.&lt;/p&gt;

&lt;p&gt;Here's how they work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An internal node contains K keys and K+1 children (pointers)&lt;/li&gt;
&lt;li&gt;The keys act as boundaries that divide values into ranges&lt;/li&gt;
&lt;li&gt;The children point to either other internal nodes or leaf nodes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The root node is just a special internal node that happens to sit at the top—it doesn't have a parent.&lt;/p&gt;

&lt;p&gt;Let's look at a concrete example. Suppose we have an internal node with 2 keys and 3 children:&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%2Fmye8cgrk3yjnzu48e5d6.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%2Fmye8cgrk3yjnzu48e5d6.png" alt="B+Tree internal node" width="800" height="378"&gt;&lt;/a&gt; (V represents the value of key)&lt;/p&gt;

&lt;p&gt;Here's what each child represents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ChildA&lt;/code&gt; (V &amp;lt; 1075): Points to all data with values less than 1075&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ChildB&lt;/code&gt; (1075 ≤ V &amp;lt; 1280): Points to all data with values from 1075 up to (but not including) 1280&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ChildC&lt;/code&gt; (v ≥ 1280): Points to all data with values of 1280 and above&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's say we're searching for the value 1120. Here's what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, we ask: "Is 1120 less than 1075?" No, so we skip &lt;code&gt;ChildA&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Next, we ask: "Is 1120 less than 1280?" Yes! So we follow &lt;code&gt;ChildB&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This guarantees that if the value 1120 exists anywhere in the tree, it &lt;em&gt;must&lt;/em&gt; be somewhere in the subtree pointed to by &lt;code&gt;ChildB&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important note&lt;/strong&gt;: Internal nodes don't need to store actual data—they only need to store the right keys for navigation. If you look at the diagram above, you'll notice that the key 1075 doesn't actually exist as a record in the leaf nodes, while 1280 does. That's perfectly fine! Internal nodes are just signposts, not data storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leaf Nodes: The Data Warehouse
&lt;/h2&gt;

&lt;p&gt;This is where the actual data lives. Each key in a leaf node represents a real record that exists in your database.&lt;br&gt;
(Quick note: Different databases implement this slightly differently. For example, MySQL's Innodb engine stores primary keys in leaf nodes and requires a second lookup to fetch the actual row data. For now, we'll assume leaf nodes store the complete data—it's simpler and covers the general concept.)&lt;/p&gt;

&lt;p&gt;The big difference between leaf nodes and internal nodes is that all leaf nodes are linked together in a chain. Each leaf node points to its neighbors on both sides. This design makes range queries super efficient, which we'll explore in detail later.&lt;br&gt;
Here's what it looks like:&lt;br&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%2Fiuwnzmt0tkalws3odftc.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%2Fiuwnzmt0tkalws3odftc.png" alt="B+Tree leaf node" width="800" height="647"&gt;&lt;/a&gt; Each leaf node stores actual data records and maintains pointers to its neighboring leaf nodes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Key Distinction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's something crucial to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you see the key "1280" in an internal node, it's saying: "values of 1280 and above go this way." It's a signpost, nothing more. It doesn't guarantee that a record with key 1280 actually exists.&lt;/li&gt;
&lt;li&gt;But when you see the key "1280" in a leaf node, it's making a definitive statement: "Yes, there is absolutely a record with key 1280, and here it is—with all its data."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Internal nodes guide you. Leaf nodes deliver.&lt;/p&gt;

&lt;h2&gt;
  
  
  B+Tree Property
&lt;/h2&gt;

&lt;p&gt;Now that we've seen the structure, let's talk about what makes B+Trees special. Don't worry about memorizing these properties—they'll all make perfect sense by the end of the article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. M-way tree: Multiple children per node&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each node in a B+Tree can have up to M children. In our diagrams above, M = 3, which means each node can have up to 3 children. This is what makes B+Trees "wide" rather than "tall"—and that's a good thing for performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Perfectly balanced: Every path is the same length&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a B+Tree, every single search takes exactly the same number of steps, no matter what you're looking for.&lt;/p&gt;

&lt;p&gt;In our example, the distance from the root node to any leaf node is always 2 steps. Always. Whether you're searching for 1050 or 1500, you take the same journey.&lt;/p&gt;

&lt;p&gt;Even better? The tree stays balanced after insertions and deletions. This is probably the most important property of B+Trees because it guarantees that every search operation is consistently efficient. No worst-case scenarios where one search takes 10 times longer than another.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Key relationship: n keys means n+1 children&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a simple rule that holds for all internal nodes: if a node has n keys, it must have exactly n+1 children.&lt;/p&gt;

&lt;p&gt;In our diagrams, every internal node has 2 keys and 3 children (2+1). The keys create boundaries, and you need one more child than you have boundaries to cover all the ranges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Sorted keys: Everything stays in order&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Within each node, the keys are always sorted from smallest to largest. This makes searching incredibly efficient—you can use binary search within a node if needed, and you always know which direction to go.&lt;/p&gt;

&lt;p&gt;Again, don't stress about memorizing these. As we work through examples, you'll see these properties in action and they'll become second nature.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Search
&lt;/h1&gt;

&lt;p&gt;So far, we've talked about what an index is and broken down the structure of a B+Tree. But we haven't answered the big question yet: Why does using a B+Tree make queries so much faster?&lt;/p&gt;

&lt;p&gt;Let's find out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem: Searching Without an Index&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, let's go back to our simple table example and see what happens when we &lt;em&gt;don't&lt;/em&gt; have a B+Tree.&lt;/p&gt;

&lt;p&gt;Imagine we want to find an employee whose badge number is 1320. Without an index, we're stuck with sequential search—we have to check every single record, one by one, until we find it (or reach the end).&lt;/p&gt;

&lt;p&gt;The time complexity? O(n), where n is the number of records in your table.&lt;/p&gt;

&lt;p&gt;If you have 10 records, no big deal. But what about 10 million records? Now you've got a serious bottleneck. This is exactly why we need B+Trees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution: Searching With a B+Tree&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The approach is simple: we compare our target value against the keys in each node, and whenever we find a key that's greater than our target, we follow the pointer just before it. We keep going down until we reach a leaf node.&lt;/p&gt;

&lt;p&gt;Let's walk through finding badge number 1320 step by step:&lt;br&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%2Ftnne695f9frzpizu7a35.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%2Ftnne695f9frzpizu7a35.png" alt="B+Tree Search" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;At the root node (1040 | 1590)

&lt;ul&gt;
&lt;li&gt;We ask: "Is 1040 greater than 1320?" Nope, so we move to the next key.&lt;/li&gt;
&lt;li&gt;We ask again: "Is 1590 greater than 1320?" Yes! So we follow the middle pointer down to the next level.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;At the first level of internal node (1075 | 1280)

&lt;ul&gt;
&lt;li&gt;We ask: "Is 1075 greater than 1320?" No, so we move to the next key.&lt;/li&gt;
&lt;li&gt;We ask: "Is 1280 greater than 1320?" Still no. Since there are no more keys to check, we follow the rightmost pointer down.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;At the leaf node (1320 | 1431)

&lt;ul&gt;
&lt;li&gt;Now we're at a leaf node—this is where the actual data lives!&lt;/li&gt;
&lt;li&gt;We scan the keys and immediately find 1320. Done!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We found our target in just 2 steps down the tree. Compare that to potentially checking 18 records with sequential search.&lt;/p&gt;

&lt;p&gt;That's the power of B+Trees.&lt;/p&gt;

&lt;p&gt;(Quick note: In real implementations, you could use binary search within each node to make things even faster. For now, I'm keeping it simple with linear search within nodes to make the logic crystal clear.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Range Queries: Where B+Trees Really Shine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before we dive into time complexity, let's look at another type of search that shows off one of the B+Tree's coolest features: range queries.&lt;/p&gt;

&lt;p&gt;Suppose we want to find all employees with badge numbers between 1000 and 1200. How do we do that?&lt;/p&gt;

&lt;p&gt;It's actually surprisingly elegant. We use the same approach as a regular search to find the starting point (1000), and then we simply follow the leaf node pointers to scan through all the values in our range.&lt;/p&gt;

&lt;p&gt;Let's walk through it:&lt;br&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%2Fjbg19sak06zrxzi2o49q.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%2Fjbg19sak06zrxzi2o49q.png" alt="B+Tree scan search" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;At root node (1040 | 1059)

&lt;ul&gt;
&lt;li&gt;We ask: "Is 1000 less than 1040?" Yes! So we follow the left pointer down.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;At the first level of internal node (1008 | 1028)

&lt;ul&gt;
&lt;li&gt;We ask: "Is 1000 less than 1008?" Yes! So we follow the left pointer down again.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;At the leaf node (1002 | 1005)

&lt;ul&gt;
&lt;li&gt;Now we're at a leaf node. Instead of stopping, we start scanning sideways using the next pointer.&lt;/li&gt;
&lt;li&gt;We move from leaf to leaf, collecting every value between 1000 and 1200, until we pass our upper bound.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is where those leaf node pointers really prove their worth. Once we've navigated down to our starting point, we can scan through all the values in our range by simply following the linked list of leaf nodes—no need to go back up to the root and traverse down again for each value.&lt;/p&gt;

&lt;p&gt;Without those pointers, we'd have to do a separate top-to-bottom search for every single value in the range. That would be painfully slow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cost Of Search Operation
&lt;/h2&gt;

&lt;p&gt;Now let's see why exactly does using a B+Tree make queries so much faster?&lt;/p&gt;

&lt;p&gt;Take a look at this diagram&lt;br&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%2Fjp3dczz36r0liol6ze25.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%2Fjp3dczz36r0liol6ze25.png" alt="B+Tree cost of search operation" width="800" height="374"&gt;&lt;/a&gt; Here's the key insight: every time we move down one level, we eliminate a huge chunk of our search space.&lt;/p&gt;

&lt;p&gt;Let's trace through an example with 18 total records:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;At the root node:&lt;/strong&gt; We're starting with all 18 records as possibilities. Our search space is 18.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;At the second layer:&lt;/strong&gt; By following one pointer, we've just eliminated 2/3 of the tree. Now our search space is only 6 records.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;At the leaf node level:&lt;/strong&gt; We cut it down again by 2/3. Now we're down to just 2 records to check.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See what's happening? At each level, we're dividing our search space by 3. That's exponential reduction.&lt;/p&gt;

&lt;p&gt;This is why the time complexity of searching in a B+Tree is &lt;strong&gt;O(log_k n)&lt;/strong&gt;, where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;k = the maximum number of children a node can have&lt;/li&gt;
&lt;li&gt;n = the total number of records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bigger k is, the fewer levels you need. And fewer levels means fewer steps.&lt;/p&gt;

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

&lt;p&gt;Let's make this concrete with realistic numbers.&lt;/p&gt;

&lt;p&gt;Imagine a B+Tree storing &lt;strong&gt;1 million records&lt;/strong&gt;, where each node can hold &lt;strong&gt;100 keys&lt;/strong&gt; (which means 101 child pointers). How many steps does it take to find a specific record?&lt;/p&gt;

&lt;p&gt;Let's calculate:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 1 (Root):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The root has 100 keys and 101 pointers&lt;/li&gt;
&lt;li&gt;It can point to 101 different subtrees&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Level 2:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have at most 101 nodes at this level&lt;/li&gt;
&lt;li&gt;Each can hold 100 keys with 101 child pointers&lt;/li&gt;
&lt;li&gt;Total nodes at the next level: 101 × 101 = &lt;strong&gt;10,201&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Level 3:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have at most 10,201 nodes here&lt;/li&gt;
&lt;li&gt;Each points to 101 children&lt;/li&gt;
&lt;li&gt;Total possible leaf nodes: 10,201 × 101 = &lt;strong&gt;1,030,301&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice that 1,030,301 is greater than 1,000,000. This means we need exactly &lt;strong&gt;3 levels&lt;/strong&gt; to store one million records when each node holds 100 keys.&lt;/p&gt;

&lt;p&gt;Mathematically, this is what log₁₀₁(1,000,000) ≈ 3 tells us.&lt;/p&gt;

&lt;p&gt;To find any record among 1 million records:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;With B+Tree:&lt;/strong&gt; 3 steps (worst case)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;With sequential scan:&lt;/strong&gt; 1,000,000 steps (worst case)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why indexes are so powerful.&lt;/p&gt;

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

&lt;p&gt;At this point, you might be wondering: "If each node has 100 keys, don't we need to scan through those 100 keys at every level? Shouldn't that count toward the cost?"&lt;/p&gt;

&lt;p&gt;Great question! And the answer reveals something important about how databases actually work.&lt;/p&gt;

&lt;p&gt;The reason we ignore scanning within nodes comes down to how databases physically store B+Trees—and it's all about the massive speed difference between two types of operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Disk I/O&lt;/strong&gt; (moving between levels): Slow and expensive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory operations&lt;/strong&gt; (scanning within a node): Fast and cheap&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's what actually happens:&lt;/p&gt;

&lt;p&gt;When you traverse from one level to another, the database needs to perform a &lt;strong&gt;disk access&lt;/strong&gt;. In database terminology, it's reading a "page" from disk into memory. This operation is &lt;em&gt;significantly&lt;/em&gt; slow—we're talking orders of magnitude slower than anything happening in RAM.&lt;/p&gt;

&lt;p&gt;But once that data is loaded into memory? Scanning through the 100 keys in that node is blazingly fast. Plus, since the keys are sorted, we can even use binary search to make it even faster.&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%2Fvr4xranh6s1tck21fsrb.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%2Fvr4xranh6s1tck21fsrb.png" alt="Database internal" width="800" height="452"&gt;&lt;/a&gt; The simple summary is: Traversing down levels requires expensive disk I/O operations. Scanning through keys within a node only requires cheap in-memory operations.&lt;/p&gt;

&lt;p&gt;That's why when we talk about search cost in B+Trees, we only count the number of levels we traverse—not the scanning we do within each node. The disk I/O is the real bottleneck.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Insertion
&lt;/h1&gt;

&lt;p&gt;Searching is only half the story. Databases also need to insert new data—and when that happens, the B+Tree needs to adapt while maintaining its efficient structure.&lt;/p&gt;

&lt;p&gt;Inserting data into a B+Tree follows a straightforward two-step process:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Navigate to the right leaf node&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We use the exact same search approach we learned earlier to find where the new value belongs. This ensures the value ends up in the correct position, maintaining the sorted order of the tree.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Determine the insertion scenario&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once we've found the target leaf node, we check its capacity. How much room does it have? Based on the answer, we'll face one of three possible scenarios, each requiring a different approach.&lt;/p&gt;

&lt;p&gt;Before we dive into those scenarios, here's what's important to remember: no matter what happens during insertion, two properties &lt;em&gt;must&lt;/em&gt; remain intact:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Perfect balance&lt;/strong&gt; - Every path from root to leaf stays the same length&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sorted keys&lt;/strong&gt; - Keys remain in order within every node&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These properties are non-negotiable. They're what guarantee that search operations stay fast and efficient, even as the tree grows and changes.&lt;/p&gt;

&lt;p&gt;Now, let's look at what actually happens when we insert...&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 1: Simple Insertion (Leaf Has Space)
&lt;/h2&gt;

&lt;p&gt;This is the easiest case. If the target leaf node isn't full, we just insert the value and we're done.&lt;/p&gt;

&lt;p&gt;Let's say we want to insert value 77 into this B+Tree:&lt;br&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%2Frmae6hnhhr9eholnvi71.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%2Frmae6hnhhr9eholnvi71.png" alt="B+Tree simple insertion" width="800" height="352"&gt;&lt;/a&gt; (Quick note: You might notice some keys are missing in the node—that's totally fine and happens in real B+Trees. As long as the tree maintains its core properties, it's valid.)&lt;/p&gt;

&lt;p&gt;We navigate down to the leaf node (68 | _ ) using our standard search approach. Since there's one empty slot, we simply insert 77 into that node:&lt;br&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%2Fqup4nurc0yelvt9z0xwk.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%2Fqup4nurc0yelvt9z0xwk.png" alt="B+Tree simple insertion" width="800" height="390"&gt;&lt;/a&gt; Done! No splitting, no restructuring—just a straightforward insertion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 2: Leaf Split with Parent Having Space
&lt;/h2&gt;

&lt;p&gt;But what if the leaf node is already full? Then we need to split it.&lt;/p&gt;

&lt;p&gt;Let's walk through an example. We want to insert value 80: &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%2Fj202hi6xhrml53dkxd7d.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%2Fj202hi6xhrml53dkxd7d.png" alt="B+Tree insertion" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We navigate down to the target leaf node (68 | 77), and we immediately see a problem—it's full. &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%2Fgzpj5xwn7ldanksst4nm.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%2Fgzpj5xwn7ldanksst4nm.png" alt="B+Tree insertion" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The leaf node is full, so we have to split. &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%2Fu7eoe23ontzvd6phrpkd.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%2Fu7eoe23ontzvd6phrpkd.png" alt="B+Tree insertion" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Insert the new value (80) in the correct sorted position: (68 | 77 | 80)&lt;/li&gt;
&lt;li&gt;Split this full node into two separate leaf nodes:

&lt;ul&gt;
&lt;li&gt;Left node: (68 | 77)&lt;/li&gt;
&lt;li&gt;Right node: (80 | _ )&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Update the next pointers so the leaf nodes stay linked&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now we need to tell the parent about the new leaf node. We do this by copying the first key from the new right leaf node (80) up into the parent: &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%2Fnpikqcoi8q344xse7t6g.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%2Fnpikqcoi8q344xse7t6g.png" alt="B+Tree insertion" width="800" height="346"&gt;&lt;/a&gt;&lt;br&gt;
After this update, the parent now has three children with clear boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Left child:&lt;/strong&gt; All values &amp;lt; 35&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Middle child:&lt;/strong&gt; All values from 35-80&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right child:&lt;/strong&gt; All values ≥ 80&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tree is now balanced again, and all keys remain sorted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 3: Cascading Splits (Both Leaf and Parent Full)
&lt;/h2&gt;

&lt;p&gt;Now for the tricky case: what if the parent is also full and can't accommodate the new key we're trying to copy up?&lt;/p&gt;

&lt;p&gt;The answer: we recursively split upward until we either find a node with space or create a brand new root node.&lt;/p&gt;

&lt;p&gt;Let's see this in action. We want to insert value 100:&lt;br&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%2F8tixet1th04q2xj9pbvt.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%2F8tixet1th04q2xj9pbvt.png" alt="B+Tree insertion" width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We navigate to the leaf node (99 | 118).&lt;br&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%2Fqk89xd6hil4gdoy82iyo.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%2Fqk89xd6hil4gdoy82iyo.png" alt="B+Tree insertion" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The leaf is full, so we split it just like before:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Insert 100 in sorted order: (99 | 100 | 118)&lt;/li&gt;
&lt;li&gt;Split into two nodes: (99 | 100) and (118 | _)&lt;/li&gt;
&lt;li&gt;Update the leaf pointers&lt;/li&gt;
&lt;li&gt;Copy the first key (118) up to the parent&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%2F3gt3leh8510m6k37pgfo.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%2F3gt3leh8510m6k37pgfo.png" alt="B+Tree insertion" width="800" height="359"&gt;&lt;/a&gt; But wait—the parent is also full! So we need to split it too.&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%2Ftr0sg7tuqgrnlobmkp6w.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%2Ftr0sg7tuqgrnlobmkp6w.png" alt="B+Tree insertion" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
Splitting an internal node is similar to splitting a leaf, but with one key difference:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Insert the new key (118) in sorted order&lt;/li&gt;
&lt;li&gt;Find the middle key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push&lt;/strong&gt; (not copy) the middle key up to the parent&lt;/li&gt;
&lt;li&gt;Split the node around that middle key&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice the difference: with leaf nodes, we &lt;em&gt;copy&lt;/em&gt; the key up. With internal nodes, we &lt;em&gt;push&lt;/em&gt; the key up, removing it from the current level.&lt;/p&gt;

&lt;p&gt;In our case, the grandparent is also full. So we repeat the split process one more time, which creates a brand new root node:&lt;br&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%2Fnl6v7oagipmn5bqdd4ly.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%2Fnl6v7oagipmn5bqdd4ly.png" alt="B+Tree insertion" width="800" height="414"&gt;&lt;/a&gt;&lt;br&gt;
We've successfully inserted our value, and the updates have cascaded all the way to the root.&lt;/p&gt;

&lt;p&gt;The tree's height increased by one level, but here's what matters: all the critical properties remain intact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Perfectly balanced (all paths from root to leaf are the same length)&lt;/li&gt;
&lt;li&gt;✅ Sorted keys (every node maintains sorted order)&lt;/li&gt;
&lt;li&gt;✅ Proper structure (correct number of keys and children)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how B+Trees grow taller—from the bottom up, one split at a time.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Deletion
&lt;/h1&gt;

&lt;p&gt;Deleting a key from a B+Tree follows a similar high-level process to insertion:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Navigate to the leaf node&lt;/strong&gt; containing the key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delete the key&lt;/strong&gt; from that node&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check if the node is now underflow&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After deletion, a node might have too few keys or children—we call this "underflow." Here's when it happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For leaf nodes:&lt;/strong&gt; If the number of keys drops below K/2 (where K is the maximum number of keys)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For internal nodes:&lt;/strong&gt; If the number of children drops below (K+1)/2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When underflow occurs, we need to fix it to keep the tree efficient. We have two strategies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strategy 1: Redistribution (Borrowing)&lt;/strong&gt;&lt;br&gt;
We borrow a key (for leaf nodes) or a child (for internal nodes) from a sibling. But we can only do this if the sibling won't underflow after lending us one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strategy 2: Merge&lt;/strong&gt;&lt;br&gt;
We combine two nodes into one. For this to work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leaf nodes:&lt;/strong&gt; The sum of keys from both nodes must be ≤ K&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal nodes:&lt;/strong&gt; The sum of children from both nodes must be ≤ K+1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No matter which strategy we use, the B+Tree must remain valid—balanced, sorted, and properly structured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 1: Deletion with Merge
&lt;/h2&gt;

&lt;p&gt;Let's say each node can hold at most 3 keys (K = 3). We want to delete the value 71.&lt;br&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%2Fn6e48wkhokh8bziekosv.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%2Fn6e48wkhokh8bziekosv.png" alt="B+Tree deletion" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We find the leaf node containing 71 and delete it:&lt;br&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%2Fu12t5xvkzfih262ll2jw.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%2Fu12t5xvkzfih262ll2jw.png" alt="B+Tree deletion" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the leaf node only has 1 key (68). That's less than K/2 = 1.5, so we have underflow.&lt;br&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%2Ffytudf56x2y9sswo6jkj.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%2Ffytudf56x2y9sswo6jkj.png" alt="B+Tree deletion" width="800" height="356"&gt;&lt;/a&gt;&lt;br&gt;
Let’s analyze our options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redistribution?&lt;/strong&gt; No. Both siblings have only 2 keys. If we borrow one, they'd have 1 key, causing them to underflow too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge?&lt;/strong&gt; Yes! We can merge with either sibling. Let's use the left sibling: (37 | 40) + (68) = 3 keys total, which fits perfectly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Merge with left sibling&lt;br&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%2Fcm9uku8chiu6uglaxvcp.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%2Fcm9uku8chiu6uglaxvcp.png" alt="B+Tree deletion" width="800" height="344"&gt;&lt;/a&gt; After merging, we notice the parent node now has an extra key. It has 2 children but 2 keys—it should only have 1 key for 2 children.&lt;/p&gt;

&lt;p&gt;We remove the key 67 from the parent:&lt;br&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%2Fwk0ck8a9avx69w77rjov.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%2Fwk0ck8a9avx69w77rjov.png" alt="B+Tree deletion" width="800" height="344"&gt;&lt;/a&gt; Done! The B+Tree is valid again, and we've successfully deleted key 71.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 2: Deletion with Redistribution
&lt;/h2&gt;

&lt;p&gt;Same setup: K = 3. This time we want to delete key 71, but the tree looks a bit different:&lt;br&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%2F3unb7g5ahb75owdc660y.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%2F3unb7g5ahb75owdc660y.png" alt="B+Tree deletion" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We find and delete 71:&lt;br&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%2F3xq48oug5g6x5hglw3lc.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%2F3xq48oug5g6x5hglw3lc.png" alt="B+Tree deletion" width="800" height="328"&gt;&lt;/a&gt;&lt;br&gt;
Again, underflow—the leaf node now has only 1 key. Let’s analyze our options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redistribution?&lt;/strong&gt; Yes! Both siblings have 3 keys. We can borrow one without causing them to underflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge?&lt;/strong&gt; No. Both siblings are full (3 values each), so merging would exceed our capacity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's borrow key 55 from the left sibling:&lt;br&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%2Fr00uufto5sd5lc82xxuf.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%2Fr00uufto5sd5lc82xxuf.png" alt="B+Tree deletion" width="800" height="363"&gt;&lt;/a&gt; Here's a problem: the parent still has key 67 as a boundary, but the first key in our leaf node is now 55. The boundary is incorrect!&lt;/p&gt;

&lt;p&gt;We need to update 67 to 55 in the parent node. Remember, internal node keys are just boundaries—we need a value that's ≤ the first key in the right subtree. So 55 works perfectly:&lt;br&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%2F0jaie5k3o15zh6ml3o2t.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%2F0jaie5k3o15zh6ml3o2t.png" alt="B+Tree deletion" width="800" height="327"&gt;&lt;/a&gt; Perfect! The tree is valid and balanced.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario 3: Cascading Merge (Shrinking the Tree)
&lt;/h2&gt;

&lt;p&gt;One more example. K = 3, and we want to delete key 41.&lt;br&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%2Ftryna2x3aszt1jt56day.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%2Ftryna2x3aszt1jt56day.png" alt="B+Tree deletion" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We delete key 41 from the leaf:&lt;br&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%2Fbv3bizupkghrkddcxfol.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%2Fbv3bizupkghrkddcxfol.png" alt="B+Tree deletion" width="800" height="410"&gt;&lt;/a&gt;&lt;br&gt;
Underflow again—the leaf has only 1 key. Let’s analyze leaf node options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redistribution?&lt;/strong&gt; No. Left siblings have 2 keys. Borrowing would cause it to underflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge?&lt;/strong&gt; Yes! We can merge with left sibling. Let's merge with the left sibling: (22 | 29) + (37) = 3 keys.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s merge with left sibling.&lt;br&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%2F0hygod9uqci3zby0y6es.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%2F0hygod9uqci3zby0y6es.png" alt="B+Tree deletion" width="800" height="397"&gt;&lt;/a&gt;&lt;br&gt;
Now the parent is in trouble—it only has 1 child, but internal nodes need at least (K+1)/2 = 2 children. The parent is underflow!&lt;br&gt;
Let’s analyze parent node options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redistribution?&lt;/strong&gt; No. The left sibling only has 2 children. Taking one would cause it to underflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge?&lt;/strong&gt; Yes! The sum of children is 1 + 2 = 3, which is ≤ K+1 = 4.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s merge internal nodes.&lt;br&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%2Fdy549mx5c642nx2cd6yo.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%2Fdy549mx5c642nx2cd6yo.png" alt="B+Tree deletion" width="800" height="412"&gt;&lt;/a&gt;&lt;br&gt;
Now we have a new merged internal node, but we need to figure out what key goes in the middle. We copy the first key (22) from the right leaf node up:&lt;br&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%2F5n1ibigbc9h4vcy9odaz.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%2F5n1ibigbc9h4vcy9odaz.png" alt="B+Tree deletion" width="800" height="451"&gt;&lt;/a&gt; The root now has only 1 child—that's underflow for an internal node. But the root is special: it has no siblings to borrow from or merge with.&lt;/p&gt;

&lt;p&gt;The solution? Remove the root entirely:&lt;br&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%2Fmxqvj4xpipq59jnck2v7.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%2Fmxqvj4xpipq59jnck2v7.png" alt="B+Tree deletion" width="800" height="354"&gt;&lt;/a&gt; The node (9 | 22 | _ ) becomes the new root. The tree's height has shrunk by one level, but it's still perfectly valid and balanced.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cost Of Update Operation
&lt;/h2&gt;

&lt;p&gt;Now that we've walked through insertion and deletion, let's talk about their performance cost.&lt;/p&gt;

&lt;p&gt;The good news? The time complexity is very similar to search operations—&lt;strong&gt;O(log_k n)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's why: every update (insert or delete) starts with searching for the right leaf node. Once we find it, we perform the update and might need to recursively adjust parent nodes if there's overflow or underflow. But remember what we discussed earlier about disk I/O versus in-memory operations? Once the data is loaded into memory, these adjustments happen blazingly fast.&lt;/p&gt;

&lt;p&gt;Update operations aren't free, though. Everything we've discussed applies to &lt;em&gt;one index&lt;/em&gt;. If your table has multiple indexes—say, five or six—every single insert or delete needs to update &lt;em&gt;all&lt;/em&gt; of those B+Trees.&lt;/p&gt;

&lt;p&gt;That's why you might still face performance issues with massive insertions or deletions, even with indexes. Each index adds overhead. It's a classic trade-off: indexes speed up reads but slow down writes.&lt;/p&gt;

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

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

&lt;p&gt;We've covered a lot in this article. We started by asking what an index really is, broke down the structure of B+Trees, and saw exactly how they make queries so much faster.&lt;/p&gt;

&lt;p&gt;Here's the key takeaway: &lt;strong&gt;adding an index isn't magic—it's just creating a smart data structure that the database can use to find data quickly.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;B+Trees are one of the most powerful data structures in computer science. They make finding records incredibly fast while guaranteeing the tree stays balanced after every insertion and deletion. That balance is what keeps performance consistent, no matter how much your data grows.&lt;/p&gt;

&lt;p&gt;Of course, there's always more to explore. We didn't dive into B+Tree variants, composite indexes, or how different database systems like MySQL and PostgreSQL actually implement B+Trees under the hood. But I hope this article gives you a solid foundation—enough that you can confidently explore those topics on your own if you're curious.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Reference
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.databass.dev/" rel="noopener noreferrer"&gt;Database Internals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.db-book.com/" rel="noopener noreferrer"&gt;Database System Concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.jp/-/en/Efficient-MySQL-Performance-Practices-Techniques/dp/1098105095?pd_sl_9fiz59z913_e&amp;amp;ref=pd_sl_9fiz59z913_e" rel="noopener noreferrer"&gt;Efficient MySQL Performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gradebuddy.com/doc/2468227/the-ubiquitous-b-tree/" rel="noopener noreferrer"&gt;The Ubiquitous B-Tree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/innodb-physical-structure.html" rel="noopener noreferrer"&gt;MySQL documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/current/btree.html" rel="noopener noreferrer"&gt;PostgreSQL documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://planetscale.com/blog/btrees-and-database-indexes" rel="noopener noreferrer"&gt;B-trees and database indexes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://15445.courses.cs.cmu.edu/fall2024/" rel="noopener noreferrer"&gt;CMU Intro to Database Systems&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I'm not a database engineer—just a software engineer who loves learning and sharing what I've figured out. If you spot any mistakes or have suggestions, please let me know in the comments. I genuinely appreciate the feedback and want to make sure this is as accurate and helpful as possible.&lt;/p&gt;

</description>
      <category>database</category>
      <category>backend</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>A Straightforward Guide for Go Channel</title>
      <dc:creator>Eyo</dc:creator>
      <pubDate>Sun, 13 Oct 2024 15:29:51 +0000</pubDate>
      <link>https://dev.to/eyochen/a-straightforward-guide-for-go-channel-3ba2</link>
      <guid>https://dev.to/eyochen/a-straightforward-guide-for-go-channel-3ba2</guid>
      <description>&lt;p&gt;Channel is one of Go's most powerful features and serve as the foundation for many concurrency patterns. In this article, I'll introduce channel in a straightforward way, aiming to give you a solid understanding of how it works and why it's so useful.&lt;/p&gt;

&lt;p&gt;Before we dive in, I assume you have a basic grasp of concurrency concepts and Go fundamentals. We won't be covering the basics of goroutines or Go syntax here.&lt;/p&gt;

&lt;p&gt;All the code examples in this article can be found &lt;a href="https://github.com/eyo-chen/go-channel-guide-examples" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Before diving into the technical details, let's understand channels from a high-level perspective using a simple analogy.&lt;/p&gt;

&lt;p&gt;Imagine two friends, John and Emma, living in a world without internet or postal services. John wants to send a gift to Emma. How can he do it?&lt;/p&gt;

&lt;p&gt;A basic approach might be for John and Emma to share a box in a neutral location. John would place the gift in the box, and Emma would retrieve it later. However, this method has a significant drawback: synchronization. Emma doesn't know exactly when John will leave the gift, so she'd have to check the box repeatedly, potentially wasting time and effort.&lt;/p&gt;

&lt;p&gt;To solve this problem, let's introduce a more efficient solution: a magic pipe. This pipe can instantly transport the gift from John to Emma. John simply puts the gift into his end of the pipe, and it appears at Emma's end, ready for her to collect.&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%2Fn1h8oes8i8lhq623qga2.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%2Fn1h8oes8i8lhq623qga2.png" alt="simple channel analogy" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this analogy, the magic pipe represents a channel in Go. John and Emma represents different goroutines, while the gift represents the data being transferred. Just as the magic pipe provides a direct, synchronized way to send gifts, a channel in Go offers a synchronized method for sending data between goroutines.&lt;/p&gt;

&lt;p&gt;This is the essence of channels in Go: &lt;strong&gt;they provide a way to send data from one goroutine to another, ensuring smooth communication and synchronization in concurrent programs.&lt;/strong&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Simple Example
&lt;/h2&gt;

&lt;p&gt;Let's examine a simple example to see channels in action:&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;main&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 a channel&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// start a goroutine to send data to the channel&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// send data to the channel&lt;/span&gt;
        &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="c"&gt;// receive data from the channel&lt;/span&gt;
    &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/*
Output:
1
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a channel &lt;code&gt;ch&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Just like map and slice, channel is also a built-in type in Go. We use &lt;code&gt;make&lt;/code&gt; function to create a channel.&lt;/li&gt;
&lt;li&gt;Because channel is a built-in type, we can pass or return it like other types.&lt;/li&gt;
&lt;li&gt;For each channel, we need to specify the type of the data it will carry. In this case, the channel will carry &lt;code&gt;int&lt;/code&gt; data.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Send data to the channel

&lt;ul&gt;
&lt;li&gt;When &lt;code&gt;&amp;lt;-&lt;/code&gt; operator is on the right side of the channel, it's sending operation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Receive data from the channel

&lt;ul&gt;
&lt;li&gt;When &lt;code&gt;&amp;lt;-&lt;/code&gt; operator is on the left side of the channel, it's receiving operation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To better visualize this process, let's look at a diagram:&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%2Fb3232pqqyfin1lrm84c3.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%2Fb3232pqqyfin1lrm84c3.png" alt="Channel Code Example Diagram" width="800" height="253"&gt;&lt;/a&gt;The diagram illustrates the flow of data between goroutines through the channel. Notice the brief waiting period in the main goroutine between attempting to receive data and successfully receiving it. This waiting is due to the nature of channel, which we'll explore in more detail later.&lt;br&gt;
For now, the key takeaway is that channels provide a powerful tool for sending data between goroutines, enabling smooth communication in concurrent Go programs.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Unbuffered vs. Buffered Channel
&lt;/h2&gt;

&lt;p&gt;Go offers two types of channels: unbuffered and buffered. Let's explore each type to understand their unique characteristics and behaviors.&lt;/p&gt;
&lt;h3&gt;
  
  
  Unbuffered Channels
&lt;/h3&gt;

&lt;p&gt;Unbuffered channels are created without specifying a buffer size, like in our earlier example. The key feature of unbuffered channels is their synchronous nature: &lt;strong&gt;both sending and receiving operations block until the other side is ready.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To better understand this, let's revisit our John and Emma analogy. If John wants to send a gift to Emma using the magic pipe (unbuffered channel), but Emma isn't ready to receive it, John must wait until Emma is prepared. Similarly, if Emma wants to receive a gift, she has to wait until John puts one in the pipe.&lt;/p&gt;

&lt;p&gt;Let's see this in action with a code example:&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;main&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 an unbuffered channel&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sending value 1 to channel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"After sending value 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;// sleep for 3 seconds to ensure the goroutine has time to send the value&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Receiving value from channel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// sleep 1 second to ensure goroutine has time to finish goroutine&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/*
Output:
Sending value 1 to channel
(... waits for 3 seconds ...)
Receiving value from channel
1
After sending value 1
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what's happening:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We create an unbuffered channel &lt;code&gt;ch&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A goroutine is started to send data to the channel.&lt;/li&gt;
&lt;li&gt;The main goroutine sleeps for 3 seconds.&lt;/li&gt;
&lt;li&gt;While the main goroutine is sleeping, the other goroutine tries to send data to the channel.

&lt;ul&gt;
&lt;li&gt;Because the main goroutine isn't ready to receive the data (it's sleeping), the sending operation is blocked.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;After 3 seconds, the main goroutine wakes up and receives the data from the channel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This process is visualized in the following diagram:&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%2Fe3l80dx6d861p80h15og.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%2Fe3l80dx6d861p80h15og.png" alt="unbuffered channel diagram" width="800" height="270"&gt;&lt;/a&gt;The red line in the diagram shows that the child goroutine (sender) must wait until the main goroutine (receiver) is ready to receive the data (after sleeping for 3 seconds).&lt;/p&gt;

&lt;p&gt;Let's examine a more complex example:&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sending value 1 to channel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sending value 2 to channel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sending value 3 to channel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

    &lt;span class="c"&gt;// sleep 3 seconds to ensure the goroutine has time to finish goroutine&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&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;receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;

        &lt;span class="c"&gt;// sleep 5 seconds if the received value is 1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;val&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;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can you predict when does this program sleep?&lt;/p&gt;

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

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

&lt;p&gt;The output of this program is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sending value 1 to channel
Sending value 2 to channel
(... waits for 5 seconds ...)
Received: 1
Received: 2
Sending value 3 to channel
Received: 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A key point to note is that the sending operation &lt;code&gt;ch &amp;lt;- 1&lt;/code&gt; completes as soon as the value is handed off to the receiver. It doesn't wait for the receiver to finish processing the value. In other words, once a sender successfully sends a value, it can continue its goroutine without waiting for the receiver to process the value.&lt;/p&gt;

&lt;p&gt;Here's a diagram illustrating this process:&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%2Fgskcowntt1yshhqfv8g6.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%2Fgskcowntt1yshhqfv8g6.png" alt="unbuffered channel diagram" width="800" height="257"&gt;&lt;/a&gt;In this example, the child goroutine(receiver) blocks the main goroutine(sender) because it sleeps for 5 seconds after receiving value 1. Consequently, the main goroutine must wait until the child goroutine wakes up and is ready to receive the next value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Buffered Channels
&lt;/h3&gt;

&lt;p&gt;Now that we understand unbuffered channels, let's explore buffered channels.&lt;/p&gt;

&lt;p&gt;Buffered channels are created by specifying a buffer size. The key feature of buffered channels is their asynchronous nature: &lt;strong&gt;sending and receiving operations are not blocking until the buffer is full or empty.&lt;/strong&gt;&lt;br&gt;
However, when the buffer is full, the sending operation is blocked until the receiver makes some space in the buffer by receiving data. Same as when the buffer is empty, the receiving operation is blocked until the sender fills some data into the buffer.&lt;/p&gt;

&lt;p&gt;It's important to note that messages are received in the same order they were sent (FIFO - First In, First Out).&lt;/p&gt;

&lt;p&gt;Let's look at an example of a buffered channel:&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;main&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 a buffered channel with capacity 2&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&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;// start timing to calculate the time elapsed&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// delay for two goroutines to complete&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&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;sender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&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;i&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;3&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="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sending %d at %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&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;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sent %d at %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&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;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&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="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// delay start to demonstrate buffer filling up&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&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;i&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;3&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="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received %d at %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="c"&gt;// sleep to simulate slow receiver&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Millisecond&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="c"&gt;/*
Output:
Sending 1 at 9.958µs
Sent 1 at 266.166µs
Sending 2 at 285.833µs
Sent 2 at 288.166µs
Sending 3 at 289.458µs
Sent 3 at 2.002000333s
Received 1 at 2.001790041s
Received 2 at 3.003317541s
Received 3 at 4.004426375s
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we create a buffered channel with a capacity of 2. We then start two goroutines: &lt;code&gt;sender&lt;/code&gt; and &lt;code&gt;receiver&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;sender&lt;/code&gt; goroutine sends data to the channel:

&lt;ul&gt;
&lt;li&gt;It sends values 1, 2, and 3 to the channel without blocking for the first two values.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The &lt;code&gt;receiver&lt;/code&gt; goroutine receives data from the channel:

&lt;ul&gt;
&lt;li&gt;It first delays for 2 seconds to allow the buffer to fill up.&lt;/li&gt;
&lt;li&gt;Then, it receives values 1, 2, and 3 from the channel.&lt;/li&gt;
&lt;li&gt;For each value, it sleeps for 1 second to simulate a slow receiver.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;As we can see from the output, the first two values are sent without blocking. However, when it comes to value 3, the sender has to wait until the receiver makes some space in the buffer by receiving value 1. After that, the sender can send value 3 to the channel.&lt;/p&gt;

&lt;p&gt;Here's a diagram visualizing this process:&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%2Ffecc08o7bzbfpmbijx6j.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%2Ffecc08o7bzbfpmbijx6j.png" alt="buffered channel diagram" width="800" height="432"&gt;&lt;/a&gt;The main takeaway from this diagram is that the sender goroutine has to wait after sending value 2 because the buffer is full. After the receiver receives value 1, it makes some space in the buffer, allowing the sender to send value 3 to the channel.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Direction Channel
&lt;/h2&gt;

&lt;p&gt;Let's revisit our previous example where we created two goroutines: &lt;code&gt;sender&lt;/code&gt; and &lt;code&gt;receiver&lt;/code&gt;. As their names suggest, &lt;code&gt;sender&lt;/code&gt; sends data to the channel, while &lt;code&gt;receiver&lt;/code&gt; receives data from it. This scenario introduces us to the concept of directional channels in Go.&lt;/p&gt;

&lt;p&gt;Directional channels are channels that are restricted to either sending or receiving operations. Go allows us to specify the direction of a channel, making it either send-only or receive-only. This feature enhances type safety and clarifies the intended use of a channel within a function.&lt;/p&gt;

&lt;p&gt;We can specify the direction of a channel using the following syntax:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;chan&amp;lt;-&lt;/code&gt; for a send-only channel&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;-chan&lt;/code&gt; for a receive-only channel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just like the last example, we specify the direction at the function signature.&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;sender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&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;receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&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;In the &lt;code&gt;sender&lt;/code&gt; function, &lt;code&gt;ch chan&amp;lt;- int&lt;/code&gt; indicates that &lt;code&gt;ch&lt;/code&gt; is a send-only channel. Similarly, in the &lt;code&gt;receiver&lt;/code&gt; function, &lt;code&gt;ch &amp;lt;-chan int&lt;/code&gt; specifies that &lt;code&gt;ch&lt;/code&gt; is a receive-only channel.&lt;/p&gt;

&lt;p&gt;Specifying the direction of a channel is crucial for maintaining type safety in your Go programs. When you design a function to only send data, you can pass a send-only channel to that function. This prevents accidental attempts to receive data from the channel within the function, which would be a compile-time error.&lt;/p&gt;

&lt;p&gt;By using directional channels, you make your code's intentions clearer and reduce the likelihood of errors related to channel operations. This practice is particularly valuable in larger codebases where multiple functions might interact with the same channel in different ways.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Close Channel
&lt;/h2&gt;

&lt;p&gt;In Go, we can close a channel using the &lt;code&gt;close&lt;/code&gt; function. &lt;strong&gt;The primary purpose of closing a channel is to notify receivers that no more data will be sent, allowing them to stop waiting for additional data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's examine the behavior of closing a channel for both senders and receivers:&lt;/p&gt;

&lt;p&gt;For senders, attempting to send data on a closed channel will cause a panic:&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Attempting to send to a closed channel..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/*
Output:
Attempting to send to a closed channel...
panic: send on closed channel
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;For receivers, reading from a closed channel immediately returns the zero value of the channel's type without blocking. We can use the "comma ok" idiom to check whether a channel is closed:&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Reading from a closed channel:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value: %v, Channel open: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/*
Output:
Reading from a closed channel:
Value: 0, Channel open: false
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "comma ok" idiom is crucial because it's the only way to distinguish between receiving a zero value from an open channel and receiving from a closed channel.&lt;/p&gt;

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

&lt;p&gt;From the above example, we can see that the sender can't send data to a closed channel, but the receiver can still receive data from a closed channel. Therefore, it's important to note that the responsibility of closing a channel lies with the sender, not the receiver. In Go concurrency patterns, it's common to see the function have following patterns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a channel&lt;/li&gt;
&lt;li&gt;Start a goroutine to send data to the channel

&lt;ul&gt;
&lt;li&gt;Inside the goroutine, close the channel after sending all data&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Return the channel as receive-only channel to the caller&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a simple example of this pattern:&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;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// create a channel&lt;/span&gt;
  &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c"&gt;// start a goroutine to send data to the channel&lt;/span&gt;
  &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// close the channel after sending all data&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// send data to the channel&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="m"&gt;3&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="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}()&lt;/span&gt;

  &lt;span class="c"&gt;// return the channel&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern, though simple, is widely used in the Go concurrency patterns. The key takeaway is that this function creates a channel, and sends data to the channel, so it's responsible for closing the channel.&lt;/p&gt;

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

&lt;p&gt;In Go concurrency patterns, it's common to see receivers continuously reading from a channel until it's closed. Here's an example:&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&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;sender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kt"&gt;int&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;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="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="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&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;receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// keep receiving data from the channel in an infinite loop&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;

        &lt;span class="c"&gt;// if the channel is closed, break the loop&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Channel closed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received value: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;sender&lt;/code&gt; sends two values to the channel and then closes the channel. The &lt;code&gt;receiver&lt;/code&gt; keeps receiving data from the channel in a infinite loop until the channel is closed.&lt;/p&gt;

&lt;p&gt;However, Go provides a more idiomatic way to achieve this using the &lt;code&gt;range&lt;/code&gt; keyword:&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;receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&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;value&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;ch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received value: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using &lt;code&gt;range&lt;/code&gt;, the loop automatically breaks when the channel is closed, making the code more concise and readable.&lt;/p&gt;

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

&lt;p&gt;When using &lt;code&gt;range&lt;/code&gt; to receive data from the channel, always remember to close the channel if the receiver continuously receiving data, otherwise, the program will deadlock.&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&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;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="m"&gt;3&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="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c"&gt;// oops, forgot to close the channel after sending all data&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="c"&gt;// keep receiving data from the channel&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;v&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;ch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&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="c"&gt;/*
Output:
0
1
2
fatal error: all goroutines are asleep - deadlock!
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, after sending three values, the goroutine exits without closing the channel. The main goroutine continues waiting for more data, resulting in a deadlock. Go's runtime is smart enough to detect that no other goroutine can send data to the channel, leading to this error.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Implementing Channel
&lt;/h2&gt;

&lt;p&gt;Throughout this article, we've been using Go's built-in channels. However, we can implement our own channel to solidify our understanding.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: The implementation below involves other concurrency primitives, such as condition variables. If you're not familiar with these concepts, feel free to skip this section. However, I'll do my best to explain everything as clearly as possible.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Before diving into the implementation, let's take a step back and consider what behaviors our channel needs to satisfy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A data structure to hold the queue of messages&lt;/li&gt;
&lt;li&gt;An initialization function to create a channel with a given capacity and specific type&lt;/li&gt;
&lt;li&gt;A send function to add data to the channel (with waiting behavior when the channel is unbuffered or full)&lt;/li&gt;
&lt;li&gt;A receive function to retrieve data from the channel (with waiting behavior when the channel is unbuffered or empty)&lt;/li&gt;
&lt;li&gt;A close function to close the channel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we've outlined the requirements, let's break down the implementation into a series of questions we need to answer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What information should we track about the channel?&lt;/li&gt;
&lt;li&gt;How do we implement the send function?&lt;/li&gt;
&lt;li&gt;How do we implement the receive function?&lt;/li&gt;
&lt;li&gt;How do we implement the close function?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By answering these questions, we'll have successfully implemented our own channel.&lt;/p&gt;

&lt;p&gt;In the following sections, we'll address each of these questions one by one, building our channel implementation step by step.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  1. What information should we keep track of the channel?
&lt;/h3&gt;

&lt;p&gt;To implement our own channel, we need to consider several key pieces of information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data Structure

&lt;ul&gt;
&lt;li&gt;We need a data structure that exhibits FIFO (First In, First Out) behavior. This could be a slice or a linked list. For our implementation, we'll use a linked list.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Capacity

&lt;ul&gt;
&lt;li&gt;We need to keep track of the channel's capacity. This is crucial because senders must wait when the channel is full, and receivers must wait when it's empty.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Closed Flag

&lt;ul&gt;
&lt;li&gt;We need a flag to indicate whether the channel has been closed.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Synchronization Mechanism: 

&lt;ul&gt;
&lt;li&gt;To properly synchronize the send and receive operations, there are three key behaviors we need to implement:

&lt;ul&gt;
&lt;li&gt;Ensures only one goroutine can send or receive data at a time, avoiding race conditions.&lt;/li&gt;
&lt;li&gt;Allows goroutines to wait until certain conditions are met (e.g., a sender waiting until the channel isn't full).&lt;/li&gt;
&lt;li&gt;Enables goroutines to notify others when conditions change (e.g., a receiver notifying senders after receiving data).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;We'll use &lt;code&gt;sync.Cond&lt;/code&gt; to implement this mechanism. For now, don't worry about the details of &lt;code&gt;sync.Cond&lt;/code&gt; - just focus on the behaviors it provides.&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;With these requirements in mind, let's implement our channel struct and initialization function:&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;type&lt;/span&gt; &lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;]&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;queue&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;
    &lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;cond&lt;/span&gt;     &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cond&lt;/span&gt;
    &lt;span class="n"&gt;closed&lt;/span&gt;   &lt;span class="kt"&gt;bool&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;NewChannel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;M&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;
        &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;list&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="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCond&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;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;{}),&lt;/span&gt;
        &lt;span class="n"&gt;closed&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="no"&gt;false&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down each component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;queue&lt;/code&gt;: This is our linked list that will hold the messages.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;capacity&lt;/code&gt;: This tracks the channel's capacity, set during initialization.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cond&lt;/code&gt;: This is our synchronization mechanism, providing the behaviors we described earlier.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;closed&lt;/code&gt;: This flag indicates whether the channel has been closed, initially set to false.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that we're using a generic type &lt;code&gt;M&lt;/code&gt; to specify the type of data the channel will carry. This allows our channel to work with any data type, providing flexibility in its usage.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  2. How to implement the send function?
&lt;/h3&gt;

&lt;p&gt;The main purpose of the send function is to add data to the channel. However, we need to consider several scenarios to ensure proper functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before any operation on the channel, we need to make sure only one goroutine can access the channel at a time.&lt;/li&gt;
&lt;li&gt;If channel is closed, we need to return an error.&lt;/li&gt;
&lt;li&gt;If channel is full, we need to make the sender wait(sleep).&lt;/li&gt;
&lt;li&gt;If the channel is not full, we can add the data to the channel.&lt;/li&gt;
&lt;li&gt;After sending the data, we need to notify all other goroutines to wake up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before diving into the code implementation, let's visualize the send function's process with a diagram:&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%2Fgedgmntu8waipxynz0cp.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%2Fgedgmntu8waipxynz0cp.png" alt="Send process" width="800" height="607"&gt;&lt;/a&gt;This diagram illustrates the step-by-step process of the send function. I would like to point out two important things in this diagram.&lt;/p&gt;

&lt;p&gt;The first important point is how we handle a full channel. When the channel reaches capacity, the sender must wait. But what does "waiting" mean in this context? Essentially, we put the current goroutine to sleep, allowing other goroutines (receivers or other senders) to access the channel. Once awakened, the goroutine rechecks the channel's status and proceeds if space is available. This process repeats in a loop until the channel has room for new data.&lt;/p&gt;

&lt;p&gt;The second key aspect is what happens after a successful send operation. After successfully sending data to the channel, the sender wakes up other goroutines. This step is crucial because we know that sender might sleep for a while, that means other goroutines might also enter a sleep state. Therefore, we need to wake up other goroutines after execution. This is the exact same action we see in the bottom-right corner of the diagram where other goroutines wake it up. It basically means "Hey, I've done my job, you can continue your work!" This ensures smooth coordination between all goroutines interacting with the channel.&lt;/p&gt;

&lt;p&gt;Let's start by writing pseudo-code for the send function:&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;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="n"&gt;M&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="c"&gt;// Acquire exclusive access to the channel&lt;/span&gt;

  &lt;span class="c"&gt;// If channel is closed, return an error&lt;/span&gt;

  &lt;span class="c"&gt;// If channel is full, wait for the channel to have space&lt;/span&gt;

  &lt;span class="c"&gt;// Add the data to the queue&lt;/span&gt;

  &lt;span class="c"&gt;// Notify all other goroutines to wake up&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this structure in mind, let's implement the actual code&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;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="n"&gt;M&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="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;closed&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;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;"send on closed channel"&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PushBack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Broadcast&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation directly translates our pseudo-code into concrete Go syntax. Let's walk through it step by step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We start by acquiring exclusive access to the channel with &lt;code&gt;c.cond.L.Lock()&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;We use &lt;code&gt;defer c.cond.L.Unlock()&lt;/code&gt; to ensure the lock is released when the function exits, regardless of how it exits.&lt;/li&gt;
&lt;li&gt;We check if the channel is closed. If it is, we return an error immediately.&lt;/li&gt;
&lt;li&gt;If the channel is full, we call &lt;code&gt;c.cond.Wait()&lt;/code&gt;. This puts the goroutine to sleep until the channel has space.&lt;/li&gt;
&lt;li&gt;Once we can proceed, we add the message to the queue.&lt;/li&gt;
&lt;li&gt;Finally, we call &lt;code&gt;c.cond.Broadcast()&lt;/code&gt; to notify all waiting goroutines to wake up.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's break down the key concurrency primitives we used above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;c.cond.L.Lock()&lt;/code&gt; and &lt;code&gt;defer c.cond.L.Unlock()&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;We can think of &lt;code&gt;Lock&lt;/code&gt; as a function to require exclusive access to the channel. Only one goroutine can acquire the lock at a time. Other goroutines that require the lock have to wait until the lock is released.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Unlock&lt;/code&gt; is the counterpart of &lt;code&gt;Lock&lt;/code&gt;. It releases the lock so that other goroutines can acquire the lock.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;c.cond.Wait()&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;This function performs two atomic operations:

&lt;ul&gt;
&lt;li&gt;(a) Releases the lock&lt;/li&gt;
&lt;li&gt;(b) Pauses the goroutine&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;We can think of &lt;code&gt;Wait&lt;/code&gt; as a function to say "Hey, I give up my exclusive access for now, I'll sleep for a while, and wait for others to wake me up."&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;c.cond.Broadcast()&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;This function is used to signal all other waiting(sleeping) goroutines to wake up.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Let's take a closer look at the send function's waiting mechanism:&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;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&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;The process of this code is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Because the channel is full, I'll give up my exclusive access and wait until other goroutines wake me up.&lt;/li&gt;
&lt;li&gt;Now I wake up, let's check again if the channel is still full.&lt;/li&gt;
&lt;li&gt;Uh-oh, the channel is still full, let me wait again. I give up my exclusive access and go back to sleep.&lt;/li&gt;
&lt;li&gt;Now I wake up again, let's check again if the channel is still full.&lt;/li&gt;
&lt;li&gt;Yay, the channel is no longer full, I can break out of the loop and continue my execution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From this process, we can see that &lt;code&gt;Wait()&lt;/code&gt; is a mechanism to make the goroutine wait until other goroutines wake it up. Also, note that &lt;code&gt;Broadcast()&lt;/code&gt; is simply to notify all the goroutines waiting on the channel to wake up, so that they can re-check the conditions and proceed with their execution.&lt;/p&gt;

&lt;p&gt;By using these primitives, we ensure that our send function operates correctly in a concurrent environment, handling full channels, closed channels, and notifying other goroutines when the channel's state changes.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  3. How to implement the receive function?
&lt;/h3&gt;

&lt;p&gt;Having implemented the send function, we might assume that the receive function would follow a similar pattern. Initially, we might consider the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before any operation on the channel, we need to make sure only one goroutine can access the channel at a time.&lt;/li&gt;
&lt;li&gt;If channel is closed, we simply return the default value of the channel's type and a boolean indicating closure without waiting.&lt;/li&gt;
&lt;li&gt;If channel is empty, we need to make the receiver wait.&lt;/li&gt;
&lt;li&gt;If the channel is not empty, we can successfully receive data.&lt;/li&gt;
&lt;li&gt;After receiving the data, we need to notify all other goroutines to wake up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on these considerations, let's draft an initial pseudo-code:&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;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;Receive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;M&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="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// Acquire exclusive access to the channel&lt;/span&gt;

  &lt;span class="c"&gt;// If channel is closed, immediately return the default value and false&lt;/span&gt;

  &lt;span class="c"&gt;// If channel is empty, wait for the channel to have a message&lt;/span&gt;

  &lt;span class="c"&gt;// Remove the data from the queue&lt;/span&gt;

  &lt;span class="c"&gt;// Notify all other goroutines to wake up&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, before implementing the actual code, let's consider a potential issue with this approach.&lt;/p&gt;

&lt;p&gt;Suppose the channel is unbuffered, we first send a value to the channel, then we receive the value from the channel.&lt;br&gt;
In this case, it will cause a deadlock, like following:&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%2Fh6vo25uz2jcojghstebj.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%2Fh6vo25uz2jcojghstebj.png" alt="deadlock example" width="800" height="448"&gt;&lt;/a&gt;When sender sends a value to the channel, it has to wait because the queue size is equal to the channel's capacity. Then, the receiver also has to wait because the queue is empty. Both of them are waiting for each other, causing a deadlock.&lt;/p&gt;

&lt;p&gt;To resolve this issue, we can modify our approach. We'll temporarily increase the channel's capacity by 1 and notify the sender to wake up. This allows the sender to successfully add data to the channel. After receiving the data, we'll decrease the capacity back to its original value and notify the sender again.&lt;/p&gt;

&lt;p&gt;Before correcting the pseudo-code, let's first visualize the process using a diagram:&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%2Fs2a0tkn5x0vvt16opc72.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%2Fs2a0tkn5x0vvt16opc72.png" alt="Receive process" width="800" height="613"&gt;&lt;/a&gt;While the process appears complex, it shares some similarities with the send function. For instance, the receiver must wait when the channel is empty, which is similar to the sender waiting when the channel is full. However, there are three things we need to pay attention to.&lt;/p&gt;

&lt;p&gt;Firstly, upon detecting a closed channel, the function immediately returns the default value and false without waiting.&lt;/p&gt;

&lt;p&gt;Secondly, after confirming the channel is open, we take a unique step: increasing the channel's capacity by 1 and notifying any waiting senders. This allows a sender to add data to the channel, effectively preventing potential deadlocks.&lt;/p&gt;

&lt;p&gt;Lastly, once the receiver successfully retrieves data from the channel, it reverses the earlier capacity increase. It decreases the capacity by 1 and again notifies any waiting senders. This maintains the channel's original capacity while ensuring smooth communication between senders and receivers.&lt;/p&gt;

&lt;p&gt;Let's update our pseudo-code with this improved logic:&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;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;Receive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;M&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="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// Acquire exclusive access to the channel&lt;/span&gt;

  &lt;span class="c"&gt;// If channel is closed, immediately return the default value and false&lt;/span&gt;

  &lt;span class="c"&gt;// Increase the capacity of the channel by 1&lt;/span&gt;
  &lt;span class="c"&gt;// Notify all other goroutines to wake up&lt;/span&gt;

  &lt;span class="c"&gt;// If channel is empty, wait for the channel to have a message&lt;/span&gt;

  &lt;span class="c"&gt;// Retrieve and remove first item from queue&lt;/span&gt;

  &lt;span class="c"&gt;// Decrease the capacity of the channel by 1&lt;/span&gt;

  &lt;span class="c"&gt;// Notify all other goroutines to wake up&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's implement the actual code based on this pseudo-code:&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;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;Receive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;M&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="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;closed&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;zero&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Broadcast&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Len&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="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Front&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="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Broadcast&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;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation translates our pseudo-code into concrete Go syntax. Let's walk through it step by step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We start by acquiring exclusive access to the channel with &lt;code&gt;c.cond.L.Lock()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We use &lt;code&gt;defer c.cond.L.Unlock()&lt;/code&gt; to ensure the lock is released when the function exits, regardless of how it exits.&lt;/li&gt;
&lt;li&gt;We check if the channel is closed. If it is, we return the zero value of type &lt;code&gt;M&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We temporarily increase the channel's capacity and broadcast to wake up any waiting senders.&lt;/li&gt;
&lt;li&gt;If the channel is empty, we enter a loop where we wait for data to arrive.&lt;/li&gt;
&lt;li&gt;Once we have data, we remove the first item from the queue using &lt;code&gt;Remove(c.queue.Front())&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We decrease the capacity back to its original value.&lt;/li&gt;
&lt;li&gt;We broadcast again to notify any waiting senders that there's now space in the channel.&lt;/li&gt;
&lt;li&gt;Finally, we return the received message and &lt;code&gt;true&lt;/code&gt; to indicate successful reception.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's check again to see if we solve the deadlock problem we mentioned before:&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%2Fskti6y0z232wf4whd8eu.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%2Fskti6y0z232wf4whd8eu.png" alt="deadlock solved" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The sender attempts to send data to the channel but finds it full, as the size equals the capacity. So, sender has to wait.&lt;/li&gt;
&lt;li&gt;Before the receiver receives data, it first increments the channel's capacity by 1 and broadcasts to wake up any waiting senders. Then, it also has wait because the channel is empty.&lt;/li&gt;
&lt;li&gt;After waking up, the sender rechecks the condition and finds space available (thanks to the increased capacity). It successfully sends data to the channel and broadcasts to wake up the waiting receiver.&lt;/li&gt;
&lt;li&gt;The receiver wakes up and finds the channel no longer empty. It successfully receives data from the channel, decreases the capacity back to its original value, and broadcasts to wake up any waiting senders.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, we can see that the deadlock problem is solved.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  4. How to implement the close function?
&lt;/h3&gt;

&lt;p&gt;The close function is relatively straightforward compared to our send and receive operations. Its main tasks are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Acquire exclusive access to the channel&lt;/li&gt;
&lt;li&gt;Return an error if attempting to close an already closed channel&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;closed&lt;/code&gt; flag to true&lt;/li&gt;
&lt;li&gt;Notify all waiting goroutines to wake up&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since the logic is straightforward, let's implement the code directly:&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;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;Close&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="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;closed&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;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;"close on closed channel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;closed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Broadcast&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Testing the implementation
&lt;/h3&gt;

&lt;p&gt;Now that we've answered all the questions we initially listed, we've successfully implemented our custom channel. Let's verify its functionality with a simple example:&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewChannel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;int&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="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&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;i&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;5&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="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&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;err&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;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Send error: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sent: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&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="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Receive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Channel closed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&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="c"&gt;/*
Output:
Sent: 1
Received: 1
Received: 2
Sent: 2
Sent: 3
Received: 3
Sent: 4
Received: 4
Sent: 5
Channel closed
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test creates an unbuffered channel of integers. A goroutine sends five values through the channel and then closes it. The main goroutine receives and prints these values until the channel is closed.&lt;/p&gt;

&lt;p&gt;The complete code for implementation can be found &lt;a href="https://github.com/eyo-chen/go-channel-guide-examples/blob/main/example09-implement-channel/main.go" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to experiment with this implementation by adjusting the channel capacity or modifying the number of senders and receivers. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: The implementation is only for demonstration purposes. The actual implementation in Go's source code is significantly more complex and optimized for performance and edge cases.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Throughout this article, we've explored the concept of channels in Go, their features, types, and even delved into implementing our own channel. Let's recap the key points we've covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Channels provide a way to send data between goroutines, facilitating communication in concurrent programs.&lt;/li&gt;
&lt;li&gt;They offer built-in synchronization mechanisms, helping manage the flow of data between goroutines.&lt;/li&gt;
&lt;li&gt;Go supports two types of channels:

&lt;ul&gt;
&lt;li&gt;Unbuffered channels: Synchronous in nature, where the sender waits until the receiver has received the data.&lt;/li&gt;
&lt;li&gt;Buffered channels: Asynchronous, allowing the sender to continue without waiting for the receiver, up to the buffer's capacity.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Specifying the direction of a channel (send-only or receive-only) enhances type safety and clarifies the intended use within functions.&lt;/li&gt;

&lt;li&gt;Closing a channel serves as a useful signal to receivers that no more data will be sent, allowing for proper termination of operations.&lt;/li&gt;

&lt;li&gt;While Go provides built-in channel implementations, understanding how to create a custom channel deepens our grasp of concurrency concepts.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;While this article covers a lot, it's important to note that there's much more to explore about channels in Go. For the sake of conciseness and focus, some advanced topics have been intentionally omitted. However, I hope this article has provided you with a solid foundation in understanding Go channels, helping you explore more complex concurrency patterns on your own.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Learning-Go-Idiomatic-Real-World-Programming/dp/1492077216" rel="noopener noreferrer"&gt;Learning Go: An Idiomatic Approach to Real-World Go Programmip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/100-Mistakes-How-Avoid-Them/dp/1617299596" rel="noopener noreferrer"&gt;100 Go Mistakes and How to Avoid Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Learn-Concurrent-Programming-James-Cutajar/dp/1633438384" rel="noopener noreferrer"&gt;Learn Concurrent Programming with Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Concurrency-Go-Tools-Techniques-Developers/dp/1491941197" rel="noopener noreferrer"&gt;Concurrency in Go&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;As I'm not an experienced Go developer, I welcome any feedback. If you've noticed any mistakes or have suggestions for improvement, please leave a comment. Your feedback is greatly appreciated and will help enhance this resource for others.&lt;/p&gt;

</description>
      <category>go</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>A Straightforward Guide for Go Interface</title>
      <dc:creator>Eyo</dc:creator>
      <pubDate>Fri, 13 Sep 2024 15:23:00 +0000</pubDate>
      <link>https://dev.to/eyochen/a-straightforward-guide-for-go-interface-16b2</link>
      <guid>https://dev.to/eyochen/a-straightforward-guide-for-go-interface-16b2</guid>
      <description>&lt;p&gt;As a developer transitioning from JavaScript to Go, I found the concept of interfaces challenging when I first started learning Go.&lt;br&gt;
This guide aims to provide a straightforward explanation of Go interfaces for those in similar situations - whether you're coming from a JavaScript background or are new to programming.&lt;/p&gt;

&lt;p&gt;The goal is to demonstrate the power and flexibility of Go interfaces, while providing practical insights. By the end of this article, I hope you'll have a solid understanding of how to use interfaces effectively in your Go projects.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Let's take a step back from Go and use a simple analogy to understand what an interface is at a high level.&lt;/p&gt;

&lt;p&gt;Imagine we're living in a world without Uber, Lyft, or any ride-sharing services.&lt;br&gt;
There's a person named Tony who owns various types of vehicles, including a car, a truck, and an airplane. He wonders, "How can I make the most of all these vehicles?"&lt;/p&gt;

&lt;p&gt;Meanwhile, there's Tina, a salesperson whose job requires frequent travel. She doesn't enjoy driving and doesn't have a driver's license, so she usually takes taxis. However, as she gets promoted, her schedule becomes busier and more dynamic, and taxis sometimes fall short of meeting her needs.&lt;/p&gt;

&lt;p&gt;Let's look at Tina's schedule from Monday to Wednesday:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monday: She needs to reach Client A's office by 2 PM and must use her laptop during the ride due to an important meeting at 1 PM.&lt;/li&gt;
&lt;li&gt;Tuesday: She has to be at Client B's office by 5 PM. The commute is about 2 hours, so she needs a car where she can lie down and take a nap.&lt;/li&gt;
&lt;li&gt;Wednesday: She needs to get to the airport by 10 AM with a lot of luggage, requiring a spacious vehicle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One day, Tina discovers that Tony has several different types of vehicles, and she can choose the most suitable one based on her needs.&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%2F6hznpu972h4a37l7j8gr.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%2F6hznpu972h4a37l7j8gr.png" alt="conversation between Tony and Tina" width="800" height="618"&gt;&lt;/a&gt;In this scenario, every time Tina wants to go somewhere, she has to visit Tony and listen to his explanation of all the technical details before choosing a suitable car. However, Tina doesn't care about these technical details, nor does she need to know them. She just needs a car that meets her requirements.&lt;/p&gt;

&lt;p&gt;Here's a simple diagram illustrating the relationship between Tina and Tony in this scenario:&lt;br&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%2F74h99wl3pzunhy630r28.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%2F74h99wl3pzunhy630r28.png" alt="Tina directly ask Tony" width="800" height="621"&gt;&lt;/a&gt;As we can see, Tina directly asks Tony. In other words, Tina depends on Tony because she needs to contact him directly whenever she needs a car.&lt;/p&gt;

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

&lt;p&gt;In order to make Tina's life easier, she creates a contract with Tony, which is essentially a list of requirements for the car. Tony will then choose the most suitable car based on this list.&lt;br&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%2F7ltgci8wuseqy1w1vvd3.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%2F7ltgci8wuseqy1w1vvd3.png" alt="Tina makes a contract" width="800" height="611"&gt;&lt;/a&gt;In this example, the contract with a list of requirements helps Tina abstract away the details of the car and focus only on her requirements. All Tina needs to do is define the requirements in the contract, and Tony will choose the most suitable car for her.&lt;/p&gt;

&lt;p&gt;We can further illustrate the relationship in this scenario with this diagram:&lt;br&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%2Fa39h81qarzlf4qspd98s.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%2Fa39h81qarzlf4qspd98s.png" alt="Tina define and use a contract" width="800" height="606"&gt;&lt;/a&gt;Instead of asking Tony directly, Tina can now use the contract to get the car she needs. In this case, she's not dependent on Tony anymore; instead, she depends on the contract. The main purpose of the contract is to abstract away the details of the car, so Tina doesn't need to know the specific details of the car. All she needs to know is that the car satisfies her requirements.&lt;/p&gt;

&lt;p&gt;From this diagram, we can identify the following characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The contract is defined by Tina; it's up to her to decide what requirements she needs.&lt;/li&gt;
&lt;li&gt;The contract acts as a middleman between Tony and Tina. They're not directly dependent on each other; instead, they depend on the contract.

&lt;ul&gt;
&lt;li&gt;Tina can use the same contract with others if Tony stops providing the service.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;There might be multiple cars that satisfy the same requirements

&lt;ul&gt;
&lt;li&gt;For example, both a Tesla Model S and a Mercedes-Benz S-Class could meet Tina's requirements.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this diagram makes sense to you because understanding it is key to grasp the concept of interfaces. Similar diagrams will appear throughout the following sections, reinforcing this important concept.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  What's an interface?
&lt;/h2&gt;

&lt;p&gt;In the previous example, &lt;strong&gt;a contract with a list of requirements is exactly what an interface is in Go.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A contract helps Tina to abstract away the details of the car and focus only on her requirements.

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;An interface abstracts away the details of an implementation and focus only on the behavior.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A contract is defined by a list of requirements.

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;An interface is defined by a list of method signatures.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Any car that satisfies the requirements is said to implement the contract.

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Any type that implements all the methods specified in the interface is said to implement that interface.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A contract is owned by the consumer (in this case, Tina)

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;An interface is owned by who uses it (the caller)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A contract acts as a middleman between the Tina and Tony

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;An interface acts as a middleman between the caller and the implementer&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;There might be multiple cars that satisfy the same requirements

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;There might be multiple implementations of an interface&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A key feature that sets Go apart from many other languages is its use of implicit interface implementation. This means you don't need to explicitly declare that a type implements an interface. As long as a type defines all the methods required by an interface, it automatically implements that interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When working with interfaces in Go, it's important to note that it only provides the list of behavior, not the detail implementation. An interface defines what methods a type should have, not how they should work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Simple Example
&lt;/h2&gt;

&lt;p&gt;Let's walk through a simple example to illustrate how interfaces work in Go.&lt;/p&gt;

&lt;p&gt;First, we'll define a &lt;code&gt;Car&lt;/code&gt; interface:&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;type&lt;/span&gt; &lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Drive&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;This simple &lt;code&gt;Car&lt;/code&gt; interface has a single method, &lt;code&gt;Drive()&lt;/code&gt;, which takes no arguments and returns nothing. Any type that has a &lt;code&gt;Drive()&lt;/code&gt; method with this exact signature is considered to implement the &lt;code&gt;Car&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;Now, let's create a &lt;code&gt;Tesla&lt;/code&gt; type that implements the &lt;code&gt;Car&lt;/code&gt; interface:&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;type&lt;/span&gt; &lt;span class="n"&gt;Tesla&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;Tesla&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Drive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"driving a tesla"&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;The &lt;code&gt;Tesla&lt;/code&gt; type implements the &lt;code&gt;Car&lt;/code&gt; interface because it has a &lt;code&gt;Drive()&lt;/code&gt; method with the same signature as defined in the interface.&lt;/p&gt;

&lt;p&gt;To demonstrate how we can use this interface, let's create a function that accepts any &lt;code&gt;Car&lt;/code&gt;:&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;DriveCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Drive&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Tesla&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;DriveCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;/*
Output:
driving a tesla
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code proves that the &lt;code&gt;Tesla&lt;/code&gt; type implements the &lt;code&gt;Car&lt;/code&gt; interface because we can pass a &lt;code&gt;Tesla&lt;/code&gt; value to the &lt;code&gt;DriveCar&lt;/code&gt; function, which accepts any &lt;code&gt;Car&lt;/code&gt;.&lt;br&gt;
Note: You can find the complete code in &lt;a href="https://github.com/eyo-chen/go-interface-guide-examples/tree/main/example01-simple-example" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's important to understand that &lt;code&gt;Tesla&lt;/code&gt; implements the &lt;code&gt;Car&lt;/code&gt; interface implicitly. There's no explicit declaration like &lt;code&gt;type Tesla struct implements Car interface&lt;/code&gt;. Instead, Go recognizes that &lt;code&gt;Tesla&lt;/code&gt; implements &lt;code&gt;Car&lt;/code&gt; simply because it has a &lt;code&gt;Drive()&lt;/code&gt; method with the correct signature.&lt;/p&gt;

&lt;p&gt;Let's visualize the relationship between the &lt;code&gt;Tesla&lt;/code&gt; type and the &lt;code&gt;Car&lt;/code&gt; interface with a diagram:&lt;br&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%2Fbhm807phdqzja79m601z.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%2Fbhm807phdqzja79m601z.png" alt="simple example" width="800" height="365"&gt;&lt;/a&gt;This diagram illustrates the relationship between the &lt;code&gt;Tesla&lt;/code&gt; type and the &lt;code&gt;Car&lt;/code&gt; interface. Notice that the &lt;code&gt;Car&lt;/code&gt; interface doesn't know anything about the &lt;code&gt;Tesla&lt;/code&gt; type. It doesn't care which type is implementing it, and it doesn't need to know.&lt;/p&gt;

&lt;p&gt;I hope this example helps clarify the concept of interfaces in Go. Don't worry if you're wondering about the practical benefits of using an interface in this simple scenario. We'll explore the power and flexibility of interfaces in more complex situations in the next section.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Usecase
&lt;/h2&gt;

&lt;p&gt;In this section, we'll explore some practical examples to see why interfaces are useful.&lt;/p&gt;
&lt;h3&gt;
  
  
  Polymorphism
&lt;/h3&gt;

&lt;p&gt;What makes interfaces so powerful is their ability to achieve polymorphism in Go.&lt;br&gt;
Polymorphism, a concept in object-oriented programming, allows us to treat different types of objects in the same way. In simpler terms, polymorphism is just a fancy word for "having many forms".&lt;br&gt;
In the Go world, we can think of polymorphism as "one interface, multiple implementations".&lt;/p&gt;

&lt;p&gt;Let's explore this concept with an example. Imagine we want to build a simple ORM (Object-Relational Mapping) that can work with different types of databases. We want the client to be able to insert, update, and delete data from the database easily without worrying about the specific query syntax. &lt;br&gt;
For this example, let's say we only support &lt;code&gt;mysql&lt;/code&gt; and &lt;code&gt;postgres&lt;/code&gt; for now, and we'll focus solely on the &lt;code&gt;insert&lt;/code&gt; operation. Ultimately, we want the client to use our ORM 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;conn&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mysql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"root:root@tcp(127.0.0.1:3306)/test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;myorm&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;myorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MySQL&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Conn&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;orm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;First, let's see how we might achieve this without using an interface.&lt;br&gt;
We'll start by defining &lt;code&gt;MySQL&lt;/code&gt; and &lt;code&gt;Postgres&lt;/code&gt; structs, each with an &lt;code&gt;Insert&lt;/code&gt; method:&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;type&lt;/span&gt; &lt;span class="n"&gt;MySQL&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;Conn&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MySQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&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;data&lt;/span&gt; &lt;span class="k"&gt;map&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="k"&gt;interface&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="c"&gt;// insert into mysql using mysql query&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;Postgres&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;Conn&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Postgres&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&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;data&lt;/span&gt; &lt;span class="k"&gt;map&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="k"&gt;interface&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="c"&gt;// insert into postgres using postgres query&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we'll define an &lt;code&gt;ORM&lt;/code&gt; struct with a &lt;code&gt;driver&lt;/code&gt; field:&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;type&lt;/span&gt; &lt;span class="n"&gt;ORM&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;db&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ORM&lt;/code&gt; struct will be used by the client. We use the &lt;code&gt;any&lt;/code&gt; type for the &lt;code&gt;driver&lt;/code&gt; field because we can't determine the specific type of the driver at compile time.&lt;/p&gt;

&lt;p&gt;Now, let's implement the &lt;code&gt;New&lt;/code&gt; function to initialize the &lt;code&gt;ORM&lt;/code&gt; struct:&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;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ORM&lt;/span&gt; &lt;span class="p"&gt;{&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;ORM&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;db&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;Finally, we'll implement the &lt;code&gt;Insert&lt;/code&gt; method for the &lt;code&gt;ORM&lt;/code&gt; struct:&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;o&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ORM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&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;data&lt;/span&gt; &lt;span class="k"&gt;map&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="k"&gt;interface&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="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;MySQL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&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;case&lt;/span&gt; &lt;span class="n"&gt;Postgres&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&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;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unsupported database driver"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have to use a type switch (&lt;code&gt;switch d := o.db.(type)&lt;/code&gt;) to determine the type of the driver because the &lt;code&gt;db&lt;/code&gt; field is of type &lt;code&gt;any&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While this approach works, it has a significant drawback: if we want to support more database types, we need to add more &lt;code&gt;case&lt;/code&gt; statements. This might not seem like a big issue initially, but as we add more database types, our code becomes harder to maintain.&lt;/p&gt;

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

&lt;p&gt;Now, let's see how interfaces can help us solve this problem more elegantly.&lt;br&gt;
First, we'll define a &lt;code&gt;DB&lt;/code&gt; interface with an &lt;code&gt;Insert&lt;/code&gt; method:&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;type&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&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;data&lt;/span&gt; &lt;span class="k"&gt;map&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="k"&gt;interface&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any type that has an &lt;code&gt;Insert&lt;/code&gt; method with this exact signature automatically implements the &lt;code&gt;DB&lt;/code&gt; interface.&lt;br&gt;
Recall that our &lt;code&gt;MySQL&lt;/code&gt; and &lt;code&gt;Postgres&lt;/code&gt; structs both have &lt;code&gt;Insert&lt;/code&gt; methods matching this signature, so they implicitly implement the &lt;code&gt;DB&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;Next, we can use the &lt;code&gt;DB&lt;/code&gt; interface as the type for the &lt;code&gt;db&lt;/code&gt; field in our &lt;code&gt;ORM&lt;/code&gt; struct:&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;type&lt;/span&gt; &lt;span class="n"&gt;ORM&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;db&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's update the &lt;code&gt;New&lt;/code&gt; function to accept a &lt;code&gt;DB&lt;/code&gt; interface:&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;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ORM&lt;/span&gt; &lt;span class="p"&gt;{&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;ORM&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;db&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;Finally, we'll modify the &lt;code&gt;Insert&lt;/code&gt; method to use the &lt;code&gt;DB&lt;/code&gt; interface:&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;o&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ORM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&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;data&lt;/span&gt; &lt;span class="k"&gt;map&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="k"&gt;interface&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of using a switch statement to determine the database type, we can simply call the &lt;code&gt;Insert&lt;/code&gt; method of the &lt;code&gt;DB&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;Now, clients can use the &lt;code&gt;ORM&lt;/code&gt; struct to insert data into any supported database without worrying about the specific implementation details:&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="c"&gt;// using mysql&lt;/span&gt;
&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mysql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"root:root@tcp(127.0.0.1:3306)/test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;myorm&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;myorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MySQL&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Conn&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;orm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// using postgres&lt;/span&gt;
&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"user=pqgotest dbname=pqgotest sslmode=disable"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;orm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myORM&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;myorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Postgres&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Conn&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;orm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&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 &lt;code&gt;DB&lt;/code&gt; interface, we can easily add support for more database types without modifying the &lt;code&gt;ORM&lt;/code&gt; struct or the &lt;code&gt;Insert&lt;/code&gt; method. This makes our code more flexible and easier to extend.&lt;/p&gt;

&lt;p&gt;Consider the following diagram to illustrate the relationship between the &lt;code&gt;ORM&lt;/code&gt;, &lt;code&gt;MySQL&lt;/code&gt;, &lt;code&gt;Postgres&lt;/code&gt;, and &lt;code&gt;DB&lt;/code&gt; interfaces:&lt;br&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%2Fu43nsfxy2t0dvu650kwg.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%2Fu43nsfxy2t0dvu650kwg.png" alt="polymorphism example" width="800" height="500"&gt;&lt;/a&gt;In this diagram, the &lt;code&gt;ORM&lt;/code&gt; struct depends on the &lt;code&gt;DB&lt;/code&gt; interface, and the &lt;code&gt;MySQL&lt;/code&gt; and &lt;code&gt;Postgres&lt;/code&gt; structs implement the &lt;code&gt;DB&lt;/code&gt; interface. This allows the &lt;code&gt;ORM&lt;/code&gt; struct to use the &lt;code&gt;Insert&lt;/code&gt; method of the &lt;code&gt;DB&lt;/code&gt; interface without knowing the specific implementation details of the &lt;code&gt;MySQL&lt;/code&gt; or &lt;code&gt;Postgres&lt;/code&gt; structs.&lt;/p&gt;

&lt;p&gt;This example demonstrates the power of interfaces in Go. We can have one interface and multiple implementations, allowing us to write more adaptable and maintainable code.&lt;/p&gt;

&lt;p&gt;Note: You can find the complete code in &lt;a href="https://github.com/eyo-chen/go-interface-guide-examples/tree/main/example02-polymorphism" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Making testing easier
&lt;/h3&gt;

&lt;p&gt;Let's consider an example where we want to implement an S3 uploader to upload files to AWS S3. Initially, we might implement it 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="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;S3Uploader&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;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&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;NewS3Uploader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;S3Uploader&lt;/span&gt; &lt;span class="p"&gt;{&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;S3Uploader&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;client&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;S3Uploader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;objectKey&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;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;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_&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;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&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;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutObjectInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objectKey&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&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;NewReader&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;client&lt;/code&gt; field in the &lt;code&gt;S3Uploader&lt;/code&gt; struct is type &lt;code&gt;*s3.Client&lt;/code&gt;, which means the &lt;code&gt;S3Uploader&lt;/code&gt; struct is directly dependent on the &lt;code&gt;s3.Client&lt;/code&gt;. &lt;br&gt;
Let's visualize this with a diagram:&lt;br&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%2Fy1ijcoegk732zlksml0z.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%2Fy1ijcoegk732zlksml0z.png" alt="unit-testing-without-interface" width="800" height="366"&gt;&lt;/a&gt;While this implementation works fine during development, it can pose challenges when we're writing unit tests. For unit testing, we typically want to avoid depending on external services like S3. Instead, we'd prefer to use a mock that simulates the behavior of the S3 client.&lt;/p&gt;

&lt;p&gt;This is where interfaces come to the rescue.&lt;/p&gt;

&lt;p&gt;We can define an &lt;code&gt;S3Manager&lt;/code&gt; interface that includes a &lt;code&gt;PutObject&lt;/code&gt; method:&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;type&lt;/span&gt; &lt;span class="n"&gt;S3Manager&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;PutObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutObjectInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optFns&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&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;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutObjectOutput&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the &lt;code&gt;PutObject&lt;/code&gt; method has the same signature as the &lt;code&gt;PutObject&lt;/code&gt; method in &lt;code&gt;s3.Client&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, we can use this &lt;code&gt;S3Manager&lt;/code&gt; interface as the type for the &lt;code&gt;client&lt;/code&gt; field in our &lt;code&gt;S3Uploader&lt;/code&gt; struct:&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;type&lt;/span&gt; &lt;span class="n"&gt;S3Uploader&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;client&lt;/span&gt; &lt;span class="n"&gt;S3Manager&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we'll modify the &lt;code&gt;NewS3Uploader&lt;/code&gt; function to accept the &lt;code&gt;S3Manager&lt;/code&gt; interface instead of the concrete &lt;code&gt;s3.Client&lt;/code&gt;:&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;NewS3Uploader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="n"&gt;S3Manager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;S3Uploader&lt;/span&gt; &lt;span class="p"&gt;{&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;S3Uploader&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;client&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 this implementation, we can pass any type that has a &lt;code&gt;PutObject&lt;/code&gt; method to the &lt;code&gt;NewS3Uploader&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;For testing purposes, we can create a mock object that implements the &lt;code&gt;S3Manager&lt;/code&gt; interface:&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;type&lt;/span&gt; &lt;span class="n"&gt;MockS3Manager&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MockS3Manager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;PutObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutObjectInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optFns&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&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;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutObjectOutput&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;// mocking logic&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;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutObjectOutput&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;We can then pass this &lt;code&gt;MockS3Manager&lt;/code&gt; to the &lt;code&gt;NewS3Uploader&lt;/code&gt; function when writing unit testing.&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;mockUploader&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewS3Uploader&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;MockS3Manager&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach allows us to test the &lt;code&gt;Upload&lt;/code&gt; method easily without actually interacting with the S3 service.&lt;/p&gt;

&lt;p&gt;After using the interface, our diagram looks like this:&lt;br&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%2Fysunigqdrhlaqdvdfv5o.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%2Fysunigqdrhlaqdvdfv5o.png" alt="unit-testing-with-interface" width="800" height="391"&gt;&lt;/a&gt;In this new structure, the &lt;code&gt;S3Uploader&lt;/code&gt; struct depends on the &lt;code&gt;S3Manager&lt;/code&gt; interface. Both &lt;code&gt;s3.Client&lt;/code&gt; and &lt;code&gt;MockS3Manager&lt;/code&gt; implement the &lt;code&gt;S3Manager&lt;/code&gt; interface. This allows us to use &lt;code&gt;s3.Client&lt;/code&gt; for the real S3 service and &lt;code&gt;MockS3Manager&lt;/code&gt; for mocking during unit tests.&lt;/p&gt;

&lt;p&gt;As you might have noticed, this is also an excellent example of polymorphism in action.&lt;/p&gt;

&lt;p&gt;Note: You can find the complete code in &lt;a href="https://github.com/eyo-chen/go-interface-guide-examples/tree/main/example03-test" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Decoupling
&lt;/h3&gt;

&lt;p&gt;In software design, it's recommended to decouple dependencies between modules. Decoupling means making the dependencies between modules as loose as possible. It helps us develop software in a more flexible way.&lt;/p&gt;

&lt;p&gt;To use an analogy, we can think of a middleman sitting between two modules:&lt;br&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%2Fl222fhd9ebt0tm9w62sj.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%2Fl222fhd9ebt0tm9w62sj.png" alt="decoupling-example" width="800" height="232"&gt;&lt;/a&gt;In this case, Module A depends on the middleman, instead of directly depending on Module B.&lt;/p&gt;

&lt;p&gt;You might wonder, what's the benefit of doing this?&lt;br&gt;
Let's look at an example.&lt;/p&gt;

&lt;p&gt;Imagine we're building a web application that takes an ID as a parameter to get a user's name. In this application, we have two packages: &lt;code&gt;handler&lt;/code&gt; and &lt;code&gt;service&lt;/code&gt;.&lt;br&gt;
The &lt;code&gt;handler&lt;/code&gt; package is responsible for handling HTTP requests and responses.&lt;br&gt;
The &lt;code&gt;service&lt;/code&gt; package is responsible for retrieving the user's name from the database.&lt;/p&gt;

&lt;p&gt;Let's first look at the code for the &lt;code&gt;handler&lt;/code&gt; package:&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;package&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Handler&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// we'll implement MySQLService later&lt;/span&gt;
    &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MySQLService&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isValidID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Invalid user ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusBadRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;userName&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;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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;err&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;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error retrieving user name: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusInternalServerError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userName&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;This code is straightforward. The &lt;code&gt;Handler&lt;/code&gt; struct has a &lt;code&gt;service&lt;/code&gt; field. For now, it depends on the &lt;code&gt;MySQLService&lt;/code&gt; struct, which we'll implement later. It uses &lt;code&gt;h.service.GetUserName(id)&lt;/code&gt; to get the user's name. The handler package's job is to handle HTTP requests and responses, as well as validation.&lt;/p&gt;

&lt;p&gt;Now, let's look at the &lt;code&gt;service&lt;/code&gt; package:&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;package&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;MySQLService&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;sql&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&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;NewMySQLService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MySQLService&lt;/span&gt; &lt;span class="p"&gt;{&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;MySQLService&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sql&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MySQLService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&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;// get user name from database&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the &lt;code&gt;MySQLService&lt;/code&gt; struct has an &lt;code&gt;sql&lt;/code&gt; field, and it retrieves the user's name from the database.&lt;/p&gt;

&lt;p&gt;In this implementation, the &lt;code&gt;Handler&lt;/code&gt; struct is directly dependent on the &lt;code&gt;MySQLService&lt;/code&gt; struct:&lt;br&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%2Fsdorsr74uw0f07ay2wjy.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%2Fsdorsr74uw0f07ay2wjy.png" alt="decoupling-without-interface" width="800" height="308"&gt;&lt;/a&gt;This might not seem like a big deal at first, but if we want to switch to a different database, we'd have to modify the &lt;code&gt;Handler&lt;/code&gt; struct to remove the dependency on the &lt;code&gt;MySQLService&lt;/code&gt; struct and create a new struct for the new database.&lt;br&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%2F1f5o2usd8gj0ikt35tj7.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%2F1f5o2usd8gj0ikt35tj7.png" alt="decoupling-change-without-interface" width="800" height="527"&gt;&lt;/a&gt;This violates the principle of decoupling. Typically, changes in one package should not affect other packages.&lt;/p&gt;

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

&lt;p&gt;To fix this problem, we can use an interface.&lt;br&gt;
We can define a &lt;code&gt;Service&lt;/code&gt; interface that has a &lt;code&gt;GetUserName&lt;/code&gt; method:&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;type&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;GetUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use this &lt;code&gt;Service&lt;/code&gt; interface as the type of the &lt;code&gt;service&lt;/code&gt; field in the &lt;code&gt;Handler&lt;/code&gt; struct:&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;package&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;GetUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&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="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Handler&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;service&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt; &lt;span class="c"&gt;// now it depends on the Service interface&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// same as before&lt;/span&gt;

    &lt;span class="c"&gt;// Get the user from the service&lt;/span&gt;
    &lt;span class="n"&gt;user&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;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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;err&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;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error retrieving user: "&lt;/span&gt;&lt;span class="o"&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;Error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusInternalServerError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// same as before&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this implementation, the &lt;code&gt;Handler&lt;/code&gt; struct is no longer dependent on the &lt;code&gt;MySQLService&lt;/code&gt; struct. Instead, it depends on the &lt;code&gt;Service&lt;/code&gt; interface:&lt;br&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%2Fsrjfic6xng68ptddk3ui.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%2Fsrjfic6xng68ptddk3ui.png" alt="decoupling-with-interface" width="800" height="222"&gt;&lt;/a&gt;In this design, the &lt;code&gt;Service&lt;/code&gt; interface acts as a middleman between &lt;code&gt;Handler&lt;/code&gt; and &lt;code&gt;MySQLService&lt;/code&gt;.&lt;br&gt;
For &lt;code&gt;Handler&lt;/code&gt;, it now depends on behavior, rather than a concrete implementation. It doesn't need to know the details of the &lt;code&gt;Service&lt;/code&gt; interface, such as what database it uses. It only needs to know that the &lt;code&gt;Service&lt;/code&gt; has a &lt;code&gt;GetUserName&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;When we need to switch to a different database, we can just simply create a new struct that implements the &lt;code&gt;Service&lt;/code&gt; interface without changing the &lt;code&gt;Handler&lt;/code&gt; struct.&lt;br&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%2Feu8l06fhngoqj55slds4.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%2Feu8l06fhngoqj55slds4.png" alt="decoupling-change-with-interface" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When designing code structure, we should always depend on behavior rather than implementation.&lt;br&gt;
It's better to depend on something that has the behavior you need, rather than depending on a concrete implementation.&lt;/p&gt;

&lt;p&gt;Note: You can find the complete code in &lt;a href="https://github.com/eyo-chen/go-interface-guide-examples/tree/main/example04-decoupling" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Working With the Standard Library
&lt;/h2&gt;

&lt;p&gt;As you gain more experience with Go, you'll find that interfaces are everywhere in the standard library.&lt;br&gt;
Let's use the &lt;code&gt;error&lt;/code&gt; interface as an example.&lt;/p&gt;

&lt;p&gt;In Go, &lt;code&gt;error&lt;/code&gt; is simply an interface with one method, &lt;code&gt;Error() string&lt;/code&gt;:&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;type&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Error&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that any type with an &lt;code&gt;Error&lt;/code&gt; method matching this signature implements the &lt;code&gt;error&lt;/code&gt; interface. We can leverage this feature to create our own custom error types.&lt;/p&gt;

&lt;p&gt;Suppose we have a function to log error messages:&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;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&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="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"received error: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;While this is a simple example, in practice, the &lt;code&gt;LogError&lt;/code&gt; function might include additional logic, such as adding metadata to the error message or sending it to a remote logging service.&lt;/p&gt;

&lt;p&gt;Now, let's define two custom error types, &lt;code&gt;OrderError&lt;/code&gt; and &lt;code&gt;PaymentDeclinedError&lt;/code&gt;. These have different fields, and we want to log them differently:&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="c"&gt;// OrderError represents a general error related to orders&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;OrderError&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;OrderID&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Message&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;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;OrderError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Error&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order %s: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// PaymentDeclinedError represents a payment failure&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PaymentDeclinedError&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;OrderID&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Reason&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;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;PaymentDeclinedError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Error&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment declined for order %s: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reason&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;Because both &lt;code&gt;OrderError&lt;/code&gt; and &lt;code&gt;PaymentDeclinedError&lt;/code&gt; have an &lt;code&gt;Error&lt;/code&gt; method with the same signature as the &lt;code&gt;error&lt;/code&gt; interface, they both implement this interface. Consequently, we can use them as arguments to the &lt;code&gt;LogError&lt;/code&gt; function:&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;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderError&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;OrderID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Order not found"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentDeclinedError&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;OrderID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Reason&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Insufficient funds"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is another excellent example of polymorphism in Go: one interface, multiple implementations. The &lt;code&gt;LogError&lt;/code&gt; function can work with any type that implements the &lt;code&gt;error&lt;/code&gt; interface, allowing for flexible and extensible error handling in your Go programs.&lt;/p&gt;

&lt;p&gt;Note: You can find the complete code in &lt;a href="https://github.com/eyo-chen/go-interface-guide-examples/tree/main/example05-standard-library" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we've explored the concept of interfaces in Go, starting with a simple analogy and gradually moving to more complex examples.&lt;/p&gt;

&lt;p&gt;Key takeaways about Go interfaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They are all about abstraction&lt;/li&gt;
&lt;li&gt;They are defined as a set of method signatures&lt;/li&gt;
&lt;li&gt;They define behavior without specifying implementation details&lt;/li&gt;
&lt;li&gt;They are implemented implicitly (no explicit declaration needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this article has helped you gain a better understanding of interfaces in Go.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Learning-Go-Idiomatic-Real-World-Programming/dp/1492077216" rel="noopener noreferrer"&gt;Learning Go: An Idiomatic Approach to Real-World Go Programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/100-Mistakes-How-Avoid-Them/dp/1617299596" rel="noopener noreferrer"&gt;100 Go Mistakes and How to Avoid Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.alexedwards.net/blog/interfaces-explained" rel="noopener noreferrer"&gt;Golang Interfaces Explained&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;As I'm not an experienced Go developer, I welcome any feedback. If you've noticed any mistakes or have suggestions for improvement, please leave a comment. Your feedback is greatly appreciated and will help enhance this resource for others.&lt;/p&gt;

</description>
      <category>go</category>
      <category>tutorial</category>
      <category>learning</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Simplifying Go Integration Tests with gofacto: A Powerful Factory for Mock Data</title>
      <dc:creator>Eyo</dc:creator>
      <pubDate>Mon, 26 Aug 2024 13:17:58 +0000</pubDate>
      <link>https://dev.to/eyochen/simplifying-go-integration-tests-with-gofacto-a-powerful-factory-for-mock-data-3m63</link>
      <guid>https://dev.to/eyochen/simplifying-go-integration-tests-with-gofacto-a-powerful-factory-for-mock-data-3m63</guid>
      <description>&lt;p&gt;Writing integration tests with databases is crucial for web application development, as it boosts confidence in our code and ensures our application works as expected. However, &lt;strong&gt;&lt;em&gt;preparing mock data&lt;/em&gt;&lt;/strong&gt; for these tests can be challenging, especially in Go, which lacks a built-in approach or standard library for this task. This article introduces the &lt;a href="https://github.com/eyo-chen/gofacto" rel="noopener noreferrer"&gt;gofacto library&lt;/a&gt;, which simplifies the process of building mock data and inserting it into databases for Go integration tests.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  What is gofacto?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/eyo-chen/gofacto" rel="noopener noreferrer"&gt;gofacto&lt;/a&gt; is a Go library that simplifies the creation and insertion of mock data into databases. It provides an intuitive approach for defining data schemas and efficiently handling database insertions. With gofacto, developers can quickly prepare test data without the burden of writing extensive boilerplate code, allowing them to focus on writing meaningful tests.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Before using gofacto
&lt;/h2&gt;

&lt;p&gt;Let's see what we normally do when writing integration tests with databases in Go. Suppose we have a table named &lt;code&gt;users&lt;/code&gt; in the database, and it has the following schema:&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="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose we want to test a function named &lt;code&gt;getUserByID&lt;/code&gt; that retrieves a user by its ID from the &lt;code&gt;users&lt;/code&gt; table. In order to test this function, we need to prepare some mock data in the database before testing this function. Here's how we normally do it:&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;type&lt;/span&gt; &lt;span class="n"&gt;User&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;ID&lt;/span&gt;      &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;Gender&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Email&lt;/span&gt;   &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// build and insert mock user&lt;/span&gt;
&lt;span class="n"&gt;mockUser&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ID&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;Gender&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"male"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;"aaa@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&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;insertToDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// action&lt;/span&gt;
&lt;span class="n"&gt;result&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;getUserByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// assertion&lt;/span&gt;
&lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;insertToDB&lt;/code&gt; is a function that inserts mock data into the database. It might be a lot complex if we're using raw sql queries.&lt;br&gt;&lt;br&gt;
This approach seems manageable because the schema is simple, and we only deal with one table.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Let's see the case when we deal with two tables, &lt;code&gt;users&lt;/code&gt; and &lt;code&gt;posts&lt;/code&gt;. Each user can have multiple posts, and the relationship between the tables is established by the &lt;code&gt;user_id&lt;/code&gt; field in the &lt;code&gt;posts&lt;/code&gt; table.&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="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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;Suppose we want to test a function named &lt;code&gt;getPostsByUserID&lt;/code&gt; that retrieves all posts by a user's ID from the &lt;code&gt;posts&lt;/code&gt; table.&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;type&lt;/span&gt; &lt;span class="n"&gt;Post&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;ID&lt;/span&gt;      &lt;span class="kt"&gt;int&lt;/span&gt;
  &lt;span class="n"&gt;UserID&lt;/span&gt;  &lt;span class="kt"&gt;int&lt;/span&gt;
  &lt;span class="n"&gt;Title&lt;/span&gt;   &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// build and insert mock user&lt;/span&gt;
&lt;span class="n"&gt;mockUser&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ID&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;Gender&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"male"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;"aaa@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&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;insertToDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// build and insert mock post&lt;/span&gt;
&lt;span class="n"&gt;mockPost1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ID&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;UserID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;mockUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// manually set the foreign key&lt;/span&gt;
  &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;"Post 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Content 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;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;insertToDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockPost1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// build and insert mock post&lt;/span&gt;
&lt;span class="n"&gt;mockPost2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ID&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;UserID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;mockUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// manually set the foreign key&lt;/span&gt;
  &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;"Post 2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Content 2"&lt;/span&gt;&lt;span class="p"&gt;,&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;insertToDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockPost2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// action&lt;/span&gt;
&lt;span class="n"&gt;result&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;getPostsByUserID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// assertion&lt;/span&gt;
&lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We first create a user and then create two posts for that user. Compared to the previous example, it becomes more complex since we deal with two tables and establish the relationship between them.&lt;/p&gt;

&lt;p&gt;What if we want to create multiple posts with different users?&lt;br&gt;
We need to create a user for each post, and it requires more code.&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="c"&gt;// build and insert mock user&lt;/span&gt;
&lt;span class="n"&gt;mockUser1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ID&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;Gender&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"male"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"aaa@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&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;insertToDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockUser1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// build and insert mock user&lt;/span&gt;
&lt;span class="n"&gt;mockUser2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ID&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;Gender&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"female"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"bbb@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&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;insertToDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockUser2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// build and insert mock post&lt;/span&gt;
&lt;span class="n"&gt;mockPost1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ID&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;UserID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;mockUser1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// manually set the foreign key&lt;/span&gt;
  &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;"Post 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Content 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;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;insertToDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockPost1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// build and insert mock post&lt;/span&gt;
&lt;span class="n"&gt;mockPost2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ID&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;UserID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;mockUser2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// manually set the foreign key&lt;/span&gt;
  &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;"Post 2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Content 2"&lt;/span&gt;&lt;span class="p"&gt;,&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;insertToDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockPost2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// action&lt;/span&gt;
&lt;span class="n"&gt;result&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;getPostsByUserID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockUser1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// assertion&lt;/span&gt;
&lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's getting more complex and error-prone when we need to create multiple mock data with different users and posts.&lt;br&gt;&lt;br&gt;
Also note that we only use simple schema for the demonstration purpose, the code will be more complex in the real-world applications.&lt;br&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  What are the problems?
&lt;/h2&gt;

&lt;p&gt;In the above examples, there are some problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write a lot of boilerplate code to prepare mock data in the database

&lt;ul&gt;
&lt;li&gt;Sometimes, we don't care what's the value of the fields, we just need to make sure there's a correct value in each field.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Hardcode the value of ID in the mock data

&lt;ul&gt;
&lt;li&gt;It's not a good practice to hardcode the value of ID in the mock data because the ID is normally auto-incremented in
the database.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Manually establishing relationships between tables

&lt;ul&gt;
&lt;li&gt;This makes the testing code cumbersome and error-prone, especially when creating mock data with multiple related tables.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Using gofacto
&lt;/h2&gt;

&lt;p&gt;Now, let's see how gofacto library can help us solve the above problems, and make the whole process easier.&lt;br&gt;&lt;br&gt;
Let's see the first example with the &lt;code&gt;users&lt;/code&gt; table.&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="c"&gt;// initialize a factory with User struct (also use `WithDB` to pass the database connection)&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gofacto&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="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// build and insert mock user&lt;/span&gt;
&lt;span class="n"&gt;mockUser&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;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;// action&lt;/span&gt;
&lt;span class="n"&gt;result&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;getUserByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// assertion&lt;/span&gt;
&lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to use gofacto, we first use &lt;code&gt;New&lt;/code&gt; function to initialize a new factory with &lt;code&gt;User&lt;/code&gt;. Because we need to insert data into database, using &lt;code&gt;WithDB&lt;/code&gt; to pass the database connection to the factory.&lt;br&gt;
Then, we use &lt;code&gt;Build&lt;/code&gt; function to build the mock data. The &lt;code&gt;Insert&lt;/code&gt; function inserts the mock data into the database and returns the mock data that has been inserted into the database with the auto-incremented ID.&lt;br&gt;&lt;br&gt;
Note that all the field of the mock data is randomly generated by default. It's okay in this case because we don't care about the value of the fields.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;In case we want to specify the value of the fields, we can use &lt;code&gt;Overwrite&lt;/code&gt; function to set the value of the fields.&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;mockUser&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;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                   &lt;span class="n"&gt;Overwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Gender&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"male"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                   &lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c"&gt;// mockUser.Gender == "male"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using &lt;code&gt;Overwrite&lt;/code&gt; function, we only need to specify the fields that we want to overwrite. The other fields will be randomly generated as usual.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Let's see the case where we want to create multiple posts with one user.&lt;br&gt;
In order to make gofacto know the relationship between the tables, we need to define the correct tags in the struct.&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;type&lt;/span&gt; &lt;span class="n"&gt;Post&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;ID&lt;/span&gt;      &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;UserID&lt;/span&gt;  &lt;span class="kt"&gt;int&lt;/span&gt;       &lt;span class="s"&gt;`gofacto:"foreignKey,struct:User"`&lt;/span&gt;
    &lt;span class="n"&gt;Title&lt;/span&gt;   &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tag tells gofacto that the &lt;code&gt;UserID&lt;/code&gt; field is a foreign key that references the &lt;code&gt;ID&lt;/code&gt; field of the &lt;code&gt;User&lt;/code&gt; struct.&lt;br&gt;&lt;br&gt;
Now, we can create multiple posts with one user easily.&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;mockUser&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;mockPosts&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;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BuildList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                    &lt;span class="n"&gt;WithOne&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;mockUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="c"&gt;// must pass pointer to the struct to `WithOne`&lt;/span&gt;
                    &lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c"&gt;// mockPosts[0].UserID == mockUser.ID&lt;/span&gt;
&lt;span class="c"&gt;// mockPosts[1].UserID == mockUser.ID&lt;/span&gt;

&lt;span class="c"&gt;// action&lt;/span&gt;
&lt;span class="n"&gt;result&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;getPostsByUserID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// assertion&lt;/span&gt;
&lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to create multiple posts, we use &lt;code&gt;BuildList&lt;/code&gt; function with the number of posts that we want to create. Then, we use &lt;code&gt;WithOne&lt;/code&gt; function to specify that all the posts belong to one user. The &lt;code&gt;Insert&lt;/code&gt; function returns a list of posts that have been inserted into the database with the auto-incremented ID.&lt;br&gt;&lt;br&gt;
gofacto library makes sure all the fields are correctly set randomly, and the relationship between the tables is correctly established.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Let's see the case where we want to create multiple posts with different users.&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;mockUser1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;mockUser2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;mockPosts&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;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BuildList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                    &lt;span class="n"&gt;WithMany&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="k"&gt;interface&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;mockUser1&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;mockUser2&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                    &lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c"&gt;// mockPosts[0].UserID == mockUser1.ID&lt;/span&gt;
&lt;span class="c"&gt;// mockPosts[1].UserID == mockUser2.ID&lt;/span&gt;

&lt;span class="c"&gt;// action&lt;/span&gt;
&lt;span class="n"&gt;result&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;getPostsByUserID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockUser1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// assertion&lt;/span&gt;
&lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use &lt;code&gt;WithMany&lt;/code&gt; function to specify that each post is associated with a different user.&lt;br&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Multi-Level Associations
&lt;/h2&gt;

&lt;p&gt;In the previous examples, we explored creating mock data with single-level associations. However, real-world applications often involve deeply nested associations. gofacto provides a powerful way to create mock data with multi-level associations.&lt;/p&gt;

&lt;p&gt;Let's consider a more complex example:&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;type&lt;/span&gt; &lt;span class="n"&gt;Expense&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;ID&lt;/span&gt;         &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;UserID&lt;/span&gt;     &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="s"&gt;`gofacto:"foreignKey,struct:User"`&lt;/span&gt;
    &lt;span class="n"&gt;CategoryID&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="s"&gt;`gofacto:"foreignKey,struct:Category,table:categories"`&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;Category&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;ID&lt;/span&gt;     &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;UserID&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="s"&gt;`gofacto:"foreignKey,struct:User"`&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;User&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;ID&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this scenario, an &lt;code&gt;Expense&lt;/code&gt; belongs to both a &lt;code&gt;User&lt;/code&gt; and a &lt;code&gt;Category&lt;/code&gt;. Additionally, a &lt;code&gt;Category&lt;/code&gt; belongs to a &lt;code&gt;User&lt;/code&gt;, creating a two-level association hierarchy.&lt;/p&gt;

&lt;p&gt;To create an expense with its associated user and category, we can leverage the &lt;code&gt;WithOne&lt;/code&gt; method:&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;mockUser&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;mockCategory&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;mockExpense&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;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                      &lt;span class="n"&gt;WithOne&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;mockUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                      &lt;span class="n"&gt;WithOne&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;mockCategory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                      &lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c"&gt;// mockExpense.UserID == mockUser.ID&lt;/span&gt;
&lt;span class="c"&gt;// mockExpense.CategoryID == mockCategory.ID&lt;/span&gt;
&lt;span class="c"&gt;// mockCategory.UserID == mockUser.ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gofacto automatically handles the multi-level associations for us, simplifying the process significantly.&lt;/p&gt;

&lt;p&gt;For a more concise approach, you can pass multiple structs to a single &lt;code&gt;WithOne&lt;/code&gt; call:&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;mockExpense&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;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                      &lt;span class="n"&gt;WithOne&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;mockUser&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;mockCategory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                      &lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;What if we need to create multiple expenses with different categories but the same user?&lt;br&gt;
We can combine &lt;code&gt;WithMany&lt;/code&gt; and &lt;code&gt;WithOne&lt;/code&gt; to achieve 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;mockUser&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;mockCategory1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;mockCategory2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;mockExpenses&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;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BuildList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                       &lt;span class="n"&gt;WithMany&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="k"&gt;interface&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;mockCategory1&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;mockCategory2&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                       &lt;span class="n"&gt;WithOne&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;mockUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                       &lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c"&gt;// mockExpenses[0].UserID == mockUser.ID&lt;/span&gt;
&lt;span class="c"&gt;// mockExpenses[1].UserID == mockUser.ID&lt;/span&gt;
&lt;span class="c"&gt;// mockExpenses[0].CategoryID == mockCategory1.ID&lt;/span&gt;
&lt;span class="c"&gt;// mockExpenses[1].CategoryID == mockCategory2.ID&lt;/span&gt;
&lt;span class="c"&gt;// mockCategory1.UserID == mockUser.ID&lt;/span&gt;
&lt;span class="c"&gt;// mockCategory2.UserID == mockUser.ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
This showcases one of gofacto's most powerful features: the ability to easily build structs with complex association relationships. As long as you define the correct tags in your structs, gofacto handles the complex setup for you. Attempting to build these association relationships without gofacto would require significantly more setup code and complexity.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We've seen how gofacto simplifies writing integration tests with databases in Go. It reduces boilerplate code and makes it easier to prepare mock data with multiple tables and establish relationships between them. Most importantly, gofacto abstracts away the complexity of preparing mock data, allowing developers to focus on writing meaningful tests. To start using gofacto in your Go projects, visit the &lt;a href="https://github.com/eyo-chen/gofacto" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; for installation instructions and more detailed documentation.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Feedback and Further Development
&lt;/h2&gt;

&lt;p&gt;As a new library developer, I'd love to hear your thoughts on gofacto! Any feedback, advice or criticism is appreciated. If you use it in your Go projects, please share your experience. Found a bug or have an idea? Open an issue on the gofacto &lt;a href="https://github.com/eyo-chen/gofacto/issues" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;. Want to contribute code? Pull requests are welcome! Your feedback and contributions will help improve gofacto and benefit the Go community. Thanks for checking it out!&lt;/p&gt;

</description>
      <category>go</category>
      <category>testing</category>
      <category>backend</category>
      <category>opensource</category>
    </item>
    <item>
      <title>A Straightforward Guide for MySQL Locks</title>
      <dc:creator>Eyo</dc:creator>
      <pubDate>Mon, 17 Jun 2024 16:44:25 +0000</pubDate>
      <link>https://dev.to/eyochen/a-straightforward-guide-for-mysql-locks-56i1</link>
      <guid>https://dev.to/eyochen/a-straightforward-guide-for-mysql-locks-56i1</guid>
      <description>&lt;p&gt;In this article, I aim to introduce you to the common and fundamental locks in InnoDB. If you're not familiar, InnoDB is a storage engine for MySQL, and it’s the default one when you create a database. My goal here is to break down these locks in InnoDB using simple analogies and examples. This can help you grasp the basics so you can explore more on your own if you're curious.&lt;/p&gt;

&lt;p&gt;Before diving into each type of lock, I want to point out three things. First, I won't cover every single lock in InnoDB. Second, the details of locks involve other database concepts, such as &lt;a href="https://dev.to/eyo000000/a-straightforward-guide-for-isolation-levels-3h66"&gt;isolation levels&lt;/a&gt; and indexes. I’ll intentionally ignore these concepts to keep this article simple. Lastly, I'm not a MySQL or InnoDB expert—just a regular engineer sharing what I've learned over the past few months. If you find any mistakes, please let me know in the comments. I'd really appreciate it!&lt;/p&gt;

&lt;p&gt;Throughout the article, I’ll use following table as example&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="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;`posts`&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nv"&gt;`id`&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;`user_id`&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;`like_count`&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`id`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="nv"&gt;`idx_user_id`&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`user_id`&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+----+---------+------------+
| id | user_id | like_count |
+----+---------+------------+
|  1 |     100 |          5 |
|  2 |     102 |         10 |
|  3 |     104 |         15 |
+----+---------+------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Here's a quick guide to the diagram we'll use: T1 and T2 represent two separate transactions, with the &lt;code&gt;posts&lt;/code&gt; table shown between them. The arrow represents the timeline, moving from older to newer operations.&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%2F1al2awt5zi9f4aqf3q6q.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%2F1al2awt5zi9f4aqf3q6q.png" alt="transaction diagram" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Exclusive vs. Shared Lock
&lt;/h2&gt;

&lt;p&gt;Both exclusive locks and shared locks are fundamental in InnoDB. &lt;strong&gt;&lt;em&gt;An exclusive lock occurs when a transaction requests write access, while a shared lock occurs when a transaction requests read access.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s use a hotel analogy to explain what exclusive and shared locks are and their compatibility. Imagine a fancy hotel that costs $10,000 per night, but offers tours of its rooms for just $10. Additionally, there are two types of customers: guests (who stay) and visitors (who tour).  The hotel has rules to ensure privacy:&lt;/p&gt;

&lt;p&gt;For visitors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One room can be visited by many people

&lt;ul&gt;
&lt;li&gt;This prevents people from lining up to visit a room&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A room that has been occupied by guests cannot be visited&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For guests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One room can only be occupied by one guest (or group)&lt;/li&gt;
&lt;li&gt;A room with visitors cannot be occupied&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope these policies make sense. Now, let’s relate this to shared and exclusive locks. In this analogy, a &lt;em&gt;room&lt;/em&gt; represents a &lt;em&gt;row&lt;/em&gt;, &lt;em&gt;visitors&lt;/em&gt; represent the &lt;em&gt;shared lock&lt;/em&gt;, and &lt;em&gt;guests&lt;/em&gt; represent the &lt;em&gt;exclusive lock&lt;/em&gt;. Let’s review the policies again and see how they seamlessly translate to shared and exclusive locks.&lt;/p&gt;

&lt;p&gt;Shared Lock&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A row can be added multiple shared lock&lt;/li&gt;
&lt;li&gt;A row with an exclusive lock can’t be added a shared lock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exclusive Lock&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A row can only be added one exclusive lock&lt;/li&gt;
&lt;li&gt;A room with shared lock can’t be added an exclusive lock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a transaction attempts to acquire either a shared lock or an exclusive lock, it must follow these policies.&lt;/p&gt;

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

&lt;p&gt;Let's look at an example in action.&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%2Fo6ktmftmcywjkf99e5z4.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%2Fo6ktmftmcywjkf99e5z4.png" alt="illustration of shared lock and exclusive lock" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please ignore the &lt;code&gt;IS&lt;/code&gt; and the &lt;code&gt;REC_NOT_GAP&lt;/code&gt;. I'll explain what those locks do shortly. For now, just focus on the &lt;code&gt;S&lt;/code&gt; and &lt;code&gt;X&lt;/code&gt; locks. As you might guess, &lt;code&gt;S&lt;/code&gt; represents a shared lock, and &lt;code&gt;X&lt;/code&gt; represents an exclusive lock.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transactions T1 and T2 each start their own transaction&lt;/li&gt;
&lt;li&gt;T1 requests and acquires a shared lock on the row where &lt;code&gt;id=2&lt;/code&gt; by using &lt;code&gt;SELECT … FOR SHARE&lt;/code&gt; statement

&lt;ul&gt;
&lt;li&gt;We can think of &lt;code&gt;FOR SHARE&lt;/code&gt; as adding a shared lock.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;T2 uses the same statement to request and acquire a shared lock on the same row

&lt;ul&gt;
&lt;li&gt;T2 is granted the shared lock because they are compatible with each other.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;T1 then attempts to request an exclusive lock on the same row

&lt;ul&gt;
&lt;li&gt;T1 has to wait until T2 releases its shared lock because a shared lock is not compatible with an exclusive lock.&lt;/li&gt;
&lt;li&gt;The red error message indicates that transaction T1 is waiting for a lock to be released but has exceeded the allowed timeout period.&lt;/li&gt;
&lt;li&gt;When two locks are not compatible, the second lock has to wait until the first lock is released.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Intention Lock
&lt;/h2&gt;

&lt;p&gt;An intention lock is a table-level lock. Most of the time, we don’t need to specify an intention lock because InnoDB will automatically issue it when necessary. According to the &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html#innodb-intention-locks" rel="noopener noreferrer"&gt;MySQL documentation&lt;/a&gt;, &lt;strong&gt;&lt;em&gt;the main purpose of an intention lock is to indicate that a transaction intends to read or write rows in the future.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Recall from the previous example, there are two &lt;code&gt;IS&lt;/code&gt; locks.&lt;br&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%2Fo6ktmftmcywjkf99e5z4.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%2Fo6ktmftmcywjkf99e5z4.png" alt="illustration of intention lock" width="800" height="617"&gt;&lt;/a&gt; An &lt;code&gt;IS&lt;/code&gt; lock represents an intention shared lock (with &lt;code&gt;IX&lt;/code&gt; for intention exclusive lock). It means the transaction T1 and T2 inform InnoDB that they are going to perform a read operation on this table.&lt;/p&gt;

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

&lt;p&gt;Let’s see one more example.&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%2Fx3nxxeziamh14de3kq3r.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%2Fx3nxxeziamh14de3kq3r.png" alt="illustration of intention lock " width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transactions T1 and T2 each start their own transaction.&lt;/li&gt;
&lt;li&gt;T1 attempts to update the row where &lt;code&gt;id=2&lt;/code&gt;, so it requests and acquires the exclusive &lt;code&gt;X&lt;/code&gt; lock and the exclusive intention lock &lt;code&gt;IX&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;T2 uses the same statement to request and acquire a shared lock on a different row, and it also requests and acquires the exclusive &lt;code&gt;X&lt;/code&gt; lock and the exclusive intention lock &lt;code&gt;IX&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It’s not surprising that two exclusive locks are granted because the two transactions access different rows. However, it might be surprising that two exclusive intention locks are added to the same table. In the previous paragraph, we mentioned that exclusive locks are not compatible with each other. Why is it different here?&lt;/p&gt;

&lt;p&gt;It's important to note that the main purpose of an intention lock is not to lock the whole table. Instead, it’s to indicate that a transaction intends to access the rows in this table.&lt;/p&gt;

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

&lt;p&gt;Let's use real-life scenarios to illustrate the purpose and importance of intention locks. The following three scenarios show how hotel staff respond when customers want to stay or visit. Most customers want to stay on the 10th floor because the views from there are stunning.&lt;/p&gt;

&lt;p&gt;Scenario A: &lt;br&gt;
In this scenario, the staff does nothing specific regarding the floor level when a customer comes in to stay or visit.&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%2F22gi7egi7ynm7gkvxxy7.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%2F22gi7egi7ynm7gkvxxy7.png" alt="illustration of intention lock" width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This example can be inefficient as the staff has to check each room when Customer B asks for rooms on the entire floor.&lt;/p&gt;

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

&lt;p&gt;Scenario B:&lt;br&gt;
In this scenario, the staff treats every customer as if they were the president, blocking the entire floor whenever a customer stays or visits.&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%2Fyppr3nfwac15bm2o2ojt.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%2Fyppr3nfwac15bm2o2ojt.png" alt="illustration of intention lock" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This significantly decreases the utilization of rooms when the staff blocks the whole floor for Customer A.&lt;/p&gt;

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

&lt;p&gt;Scenario C:&lt;br&gt;
In this scenario, the hotel does not block the entire floor when a customer stays or visits. Instead, it marks the floor to indicate that someone is occupying 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%2F77jcv1nwrbk1jusp6o89.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%2F77jcv1nwrbk1jusp6o89.png" alt="illustration of intention lock" width="800" height="616"&gt;&lt;/a&gt;This approach is the most efficient so far. The staff doesn’t need to check each room, and visitors are still allowed to visit.&lt;/p&gt;

&lt;p&gt;In the above analogies, &lt;em&gt;floors&lt;/em&gt; represent &lt;em&gt;tables&lt;/em&gt;, &lt;em&gt;rooms&lt;/em&gt; represent &lt;em&gt;rows&lt;/em&gt;, &lt;em&gt;staff&lt;/em&gt; represent &lt;em&gt;InnoDB&lt;/em&gt;, and &lt;em&gt;customers&lt;/em&gt; represent &lt;em&gt;transactions&lt;/em&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In Scenario A, because there’s no table-level lock, InnoDB has to check each row to see if there’s a lock on it when a transaction asks for an exclusive table-level lock, which can be less efficient. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In Scenario B, if InnoDB locks the whole table because a transaction issues a lock on some rows, other transactions have to wait until this transaction releases the lock, significantly decreasing concurrency. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In Scenario C, InnoDB puts a special mark on the table to indicate that there’s a lock on a row, which is the purpose of intention locks. This allows further transactions to access the same table and respond to the table-level exclusive lock without checking each row.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From these scenarios, we can see the importance of intention locks. They improve efficiency and allow concurrent access.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Record Lock
&lt;/h2&gt;

&lt;p&gt;From the &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html#innodb-record-locks" rel="noopener noreferrer"&gt;MySQL documentation&lt;/a&gt;, a record lock is a lock on an index record. Without getting into the details of what an index record means, let's simplify: &lt;strong&gt;&lt;em&gt;a record lock is essentially a row-level lock locks a row.&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
(This isn't entirely accurate in the details of MySQL, but it helps us understand record locks from a high-level perspective.)&lt;/p&gt;

&lt;p&gt;In most cases, a record lock appears when we read or update a row using a primary key or unique index. This is because both of these ensure access to only one row (or none) without touching other rows.&lt;/p&gt;

&lt;p&gt;Let’s reveal the remaining lock we skip in the first example.&lt;br&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%2Fo6ktmftmcywjkf99e5z4.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%2Fo6ktmftmcywjkf99e5z4.png" alt="illustration of record lock" width="800" height="617"&gt;&lt;/a&gt;Both T1 and T2 use the primary key to update a row. Therefore, &lt;code&gt;REC_NOT_GAP&lt;/code&gt; represents a record lock. &lt;code&gt;(S, REC_NOT_GAP)&lt;/code&gt; means shared record lock while &lt;code&gt;(X, REC_NOT_GAP)&lt;/code&gt; means exclusive record lock. &lt;/p&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Gap Lock
&lt;/h2&gt;

&lt;p&gt;According to the &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html#innodb-gap-locks" rel="noopener noreferrer"&gt;MySQL documentation&lt;/a&gt;, a gap lock is a lock on a gap between index records or on the gap before the first or after the last index record. To oversimplify, &lt;strong&gt;&lt;em&gt;a gap lock is a lock on a range of rows.&lt;/em&gt;&lt;/strong&gt; When a gap lock is added to a range of rows, no other transactions are allowed to insert rows within that range. One advantage of gap locks is that they help prevent &lt;a href="https://dev.to/eyo000000/a-straightforward-guide-for-isolation-levels-3h66"&gt;phantom reads&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To understand gap locks better, let's look at the structure of rows from a different perspective. Often, we view the table structure as row by row. However, conceptually, we can think of gaps between each row, including before the first row and after the last row. We can group each gap and the records as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[-infinity, (100, 1)] -&amp;gt; A
[(100, 1), (102, 2)]  -&amp;gt; B
[(102, 2), (104, 3)]  -&amp;gt; C
[(104, 3), infinity]  -&amp;gt; D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this representation, &lt;code&gt;[x, y]&lt;/code&gt; indicates each group, and &lt;code&gt;(secondary index, primary index)&lt;/code&gt; indicates the index value. In our example, it’s &lt;code&gt;(user_id, id)&lt;/code&gt;. A gap lock secures the range from &lt;code&gt;x&lt;/code&gt; up to, but not including &lt;code&gt;y&lt;/code&gt;. For instance, when innoDB indicates that there’s a gap lock on group A, it means other transactions are not allowed to insert the &lt;code&gt;user_id&lt;/code&gt; value before 100 (i.e., from -infinity to 99).&lt;/p&gt;

&lt;p&gt;Keep this representation in mind; it will be helpful for understanding the examples later.&lt;/p&gt;

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

&lt;p&gt;Gap locks typically occur when we use a non-unique secondary index to query rows. Let’s walk through an example to see gap locks in action.&lt;br&gt;
(To better illustrate gap locks, the following examples sort the rows by &lt;code&gt;user_id&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[-infinity, (100, 1)] -&amp;gt; A 🔒(X, GAP)
[(100, 1), (102, 2)]  -&amp;gt; B
[(102, 2), (104, 3)]  -&amp;gt; C
[(104, 3), infinity]  -&amp;gt; D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fro65d2c24jnt4qmtyj65.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%2Fro65d2c24jnt4qmtyj65.png" alt="illustration of gap lock" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When T1 updates the row where &lt;code&gt;user_id = 1&lt;/code&gt;, it issues a gap lock on group A. &lt;code&gt;(X, GAP)&lt;/code&gt; represents an exclusive gap lock, and &lt;code&gt;(S, GAP)&lt;/code&gt; represents a shared gap lock. The three insert operations by T2 show that the gap lock indeed locks the range from -infinity to 99. (Note that gap locks do not lock the row itself.)&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
As mentioned earlier, one advantage of gap locks is preventing phantom reads. Let’s walk through one example, but without a gap lock this time.&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%2Fw5j17t3v6v7z0ru0hyzl.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%2Fw5j17t3v6v7z0ru0hyzl.png" alt="illustration of gap lock" width="800" height="616"&gt;&lt;/a&gt;Because there’s no gap lock when T1 first accesses the rows, T2 is allowed to insert a row into the table. Later, T1 uses the same query but gets a different result set. This situation is called a phantom read: when a transaction executes the same query twice but gets different sets of rows.&lt;/p&gt;

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

&lt;p&gt;Before moving on to the next type of lock, it’s important to note that gap locks are compatible with each other, even though they are exclusive locks. The purpose of a gap lock is not to prevent access to the gap, but to prevent other transactions from inserting into the gap.&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%2F7knzwp11nhif9silnwwo.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%2F7knzwp11nhif9silnwwo.png" alt="illustration of gap lock" width="800" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Next-Key Lock
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;A next-key lock is simply a combination of a record lock and a gap lock.&lt;/em&gt;&lt;/strong&gt; When a lock shows &lt;code&gt;X&lt;/code&gt; or &lt;code&gt;S&lt;/code&gt;, it means they are exclusive and shared next-key locks, respectively. Again, these are just combinations of exclusive (shared) record locks and exclusive (shared) gap locks.&lt;/p&gt;

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

&lt;p&gt;Let’s see how next-key lock perform in different scenarios, as next-key locks are quite common in InnoDB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[-infinity, (100, 1)] -&amp;gt; A 🔒(X)
[(100, 1), (102, 2)]  -&amp;gt; B 🔒(X,GAP)
[(102, 2), (104, 3)]  -&amp;gt; C
[(104, 3), infinity]  -&amp;gt; D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fnh0sdceifus3x8jz4a7x.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%2Fnh0sdceifus3x8jz4a7x.png" alt="illustration of next-key lock" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first row shows the next-key lock &lt;code&gt;X&lt;/code&gt; on 100, which is our group A. The second row shows the gap lock &lt;code&gt;X,GAP&lt;/code&gt; on 102, which is our group B. It’s important to note that a next-key lock does lock the target row itself, while a gap lock does not.&lt;br&gt;
If we combine the next-key lock and gap lock in this example, all the rows before the &lt;code&gt;user_id&lt;/code&gt; value of 102 are locked. That’s why T2 can only insert the value 102.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[-infinity, (100, 1)] -&amp;gt; A 
[(100, 1), (102, 2)]  -&amp;gt; B 🔒(X)
[(102, 2), (104, 3)]  -&amp;gt; C 🔒(X,GAP)
[(104, 3), infinity]  -&amp;gt; D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Ffnbso7x7qy7qdsqx1f3g.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%2Ffnbso7x7qy7qdsqx1f3g.png" alt="illustration of next-key lock" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The update statement by T1 acquires three locks: an intention lock &lt;code&gt;IX&lt;/code&gt; on the table, a next-key lock &lt;code&gt;X&lt;/code&gt; on group B, and a gap lock &lt;code&gt;X,GAP&lt;/code&gt; on group C. The next-key lock secures the range from the &lt;code&gt;user_id&lt;/code&gt; value of 100 to 102 (including 102), and the gap lock secures the range from 102 to 103 (excluding 104). To sum up, the range from 100 to 103 is locked by T1. Therefore, T2 is only allowed to insert values where &lt;code&gt;user_id&lt;/code&gt; is equal to 99 and 104 in this example.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[-infinity, (100, 1)] -&amp;gt; A 
[(100, 1), (102, 2)]  -&amp;gt; B 
[(102, 2), (104, 3)]  -&amp;gt; C 🔒(X)
[(104, 3), infinity]  -&amp;gt; D 🔒(X,GAP)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fzg566hatv6pfevcmy0f9.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%2Fzg566hatv6pfevcmy0f9.png" alt="illustration of next-key lock" width="800" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, all the rows after the &lt;code&gt;user_id&lt;/code&gt; of 102 are locked by T1 because there’s a next-key lock on group C and a gap lock on group D. So, only the first insert statement from T2 is successful.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we explore different types of locks in InnoDB. We look at their simple definitions, how they lock data, and provide plenty of examples in action. I hope you gain a basic understanding after reading this article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exclusive vs. Shared Lock

&lt;ul&gt;
&lt;li&gt;A shared lock allows multiple transactions to read a resource.&lt;/li&gt;
&lt;li&gt;An exclusive lock allows only one transaction to modify a resource.&lt;/li&gt;
&lt;li&gt;Use the hotel analogy to remember their compatibility.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Intention Lock

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;IX&lt;/code&gt; or &lt;code&gt;IS&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A table-level lock.&lt;/li&gt;
&lt;li&gt;Indicates a transaction intends to read or write rows in the future.&lt;/li&gt;
&lt;li&gt;Improves efficiency and allows concurrent access.&lt;/li&gt;
&lt;li&gt;Intention locks are compatible with each other.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Record Lock

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;(X,REC_NOT_GAP)&lt;/code&gt; or &lt;code&gt;(S,REC_NOT_GAP)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A row-level lock that locks a specific row.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Gap Lock

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;(X,GAP)&lt;/code&gt; or &lt;code&gt;(S,GAP)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A lock on a range of rows, preventing other transactions from inserting into the gap.&lt;/li&gt;
&lt;li&gt;Use groupings to understand how gap locks work.&lt;/li&gt;
&lt;li&gt;Helps prevent phantom reads.&lt;/li&gt;
&lt;li&gt;Gap locks are compatible with each other.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Next-Key Lock

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;X&lt;/code&gt; or &lt;code&gt;S&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A combination of record lock and gap lock.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html" rel="noopener noreferrer"&gt;MySQL documentation: InnoDB Locking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.mysql.com/blog-archive/innodb-data-locking-part-1-introduction" rel="noopener noreferrer"&gt;MySQL Blog Post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jahfer.com/posts/innodb-locks/" rel="noopener noreferrer"&gt;A Comprehensive (and Animated) Guide to InnoDB Locking
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.csdn.net/qq_20597727/article/details/87308709" rel="noopener noreferrer"&gt;深入了解mysql--gap locks,Next-Key Locks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Efficient-MySQL-Performance-Practices-Techniques/dp/1098105095" rel="noopener noreferrer"&gt;Efficient MySQL Performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/MySQL-Concurrency-Locking-Transactions-Developers/dp/148426651X" rel="noopener noreferrer"&gt;MySQL Concurrency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>mysql</category>
      <category>concurrency</category>
    </item>
    <item>
      <title>A Straightforward Guide for Isolation Levels</title>
      <dc:creator>Eyo</dc:creator>
      <pubDate>Mon, 15 Apr 2024 15:09:36 +0000</pubDate>
      <link>https://dev.to/eyochen/a-straightforward-guide-for-isolation-levels-3h66</link>
      <guid>https://dev.to/eyochen/a-straightforward-guide-for-isolation-levels-3h66</guid>
      <description>&lt;p&gt;In this article, I aim to present the concept of isolation levels in a straightforward way. While our examples primarily use MySQL, it's important to note that these principles are  applicable across various databases.&lt;/p&gt;

&lt;p&gt;Throughout the article, I’ll use following table as example&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="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="nv"&gt;`posts`&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nv"&gt;`id`&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;`user_name`&lt;/span&gt; &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;`like_count`&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`id`&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;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="c1"&gt;-----+-----------+------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;like_count&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;-----+-----------+------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Alice&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Bob&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt;          &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Charlie&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;-----+-----------+------------+&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each level, we'll discuss the specific concurrency problems, the issues each level resolves, and how MySQL implements these levels behind the scenes.&lt;/p&gt;

&lt;p&gt;(I assume that you have basic understanding of relational databases and transaction concepts.)&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Before getting into the detail of each isolation level, we may need to understand what exactly is isolation level at a high level. Let's take a break from thinking about databases and use transportation as an analogy instead.&lt;/p&gt;

&lt;p&gt;Imagine an intersection with four different directions, and cars in each direction want to turn right, turn left, or go straight.&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%2Fpysjitiwicby181qop4q.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%2Fpysjitiwicby181qop4q.png" alt="isolation level analogy" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Regardless of the direction a car chooses, it must pass through the orange circle area. At any given time, only one car is allowed in the orange circle area, or else a car accident may occur.&lt;/p&gt;

&lt;p&gt;The problem is how to design a policy that prevents car accidents while also avoiding traffic jams.&lt;/p&gt;

&lt;p&gt;After a brief thinking, we may come up three potential policies.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Block one lane while letting the other three go through&lt;/p&gt;

&lt;p&gt;This approach reduces the chances of traffic jams as three lanes remain functional. However, there's a high risk of accidents because it's hard to coordinate three different directions at once.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Block two lanes while letting the other two go through&lt;/p&gt;

&lt;p&gt;Compared to the first policy, this lowers the risk of car accidents but increases the chance of traffic jams since two lanes are blocked.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Block three lanes while letting only one lane go through&lt;/p&gt;

&lt;p&gt;This is probably the safest option because only one lane has cars, so accidents can't happen. However, it leads to significant traffic congestion issues.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From the above three different policies, what we’re doing?&lt;/p&gt;

&lt;p&gt;We’re making a tradeoff between car accident and traffic jam. More generally, we’re making a tradeoff between “challenge of managing simultaneous access by multiple entities” and “efficiency”. And this is what isolation levels are about.&lt;/p&gt;

&lt;p&gt;Each isolation level in databases makes &lt;strong&gt;&lt;em&gt;a tradeoff between addressing database concurrency issues and ensuring efficiency&lt;/em&gt;&lt;/strong&gt;. Opting for a higher isolation level resolves concurrency problems but may introduce inefficiencies, and vice versa.&lt;/p&gt;

&lt;p&gt;Similar to our intersection example, there's no perfect correct policy as every situation is unique. It's the same for isolation levels - &lt;em&gt;there's no universally correct level because every application uses databases differently.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Read Uncommitted
&lt;/h2&gt;

&lt;p&gt;At the read uncommitted isolation level, &lt;strong&gt;&lt;em&gt;a transaction is allowed to read data that has been modified but not yet committed by other transactions.&lt;/em&gt;&lt;/strong&gt; In other words, it reads the latest version of a row, even if the change has not been committed yet.&lt;/p&gt;

&lt;p&gt;Let's look at an example&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%2Flzuttlrksgqm68o3hxn7.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%2Flzuttlrksgqm68o3hxn7.png" alt="read uncommitted illustration" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transactions T1 and T2 each start their own transaction&lt;/li&gt;
&lt;li&gt;T1 updates the &lt;code&gt;like_amount&lt;/code&gt; column to increment it by 1 for the row where &lt;code&gt;id&lt;/code&gt; is equal to 1&lt;/li&gt;
&lt;li&gt;T2 reads the same row, and output the latest changes by T1(10 → 11) which is not yet committed&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;This behavior may seem intuitive at first, but what happens if T1 rollback its changes?&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%2Fey11k98z4sfvqlmebwvv.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%2Fey11k98z4sfvqlmebwvv.png" alt="read uncommitted illustration" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In that case, T2 would have read the same row twice but received inconsistent results. The first read saw T1's uncommitted change, while the second read would see the original data after T1's rollback. This inconsistent read is known as a &lt;strong&gt;&lt;em&gt;dirty read&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Dirty reads are a common phenomenon at the read uncommitted isolation level. In most cases, we want to prevent this from happening because it can lead to data inconsistency and integrity issues.&lt;/p&gt;

&lt;p&gt;Read uncommitted is the lowest isolation level. Although it provides the best performance and concurrency, most databases do not use this as the default level because it causes too many concurrency problems. Dirty read is just one of the issues that can occur at this isolation level; we'll explore more of them throughout this article.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Read committed
&lt;/h2&gt;

&lt;p&gt;At the read committed isolation level, &lt;strong&gt;&lt;em&gt;a transaction can only read data that has been committed by other transactions&lt;/em&gt;&lt;/strong&gt;. This means it accesses the latest committed version of a row, preventing dirty reads where uncommitted changes are visible.&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%2F3opdb0qwk41xpg1a8gix.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%2F3opdb0qwk41xpg1a8gix.png" alt="read committed illustration" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transactions T1 and T2 each start their own transaction&lt;/li&gt;
&lt;li&gt;T1 updates the &lt;code&gt;like_amount&lt;/code&gt; column to increment it by 1 for the row where &lt;code&gt;id&lt;/code&gt; is equal to 1&lt;/li&gt;
&lt;li&gt;T1 reads the same row and sees the incremented &lt;code&gt;like_amount&lt;/code&gt; value, indicating the change has been made&lt;/li&gt;
&lt;li&gt;T2 reads the row and gets the old value of 10 because T1's change is not yet committed&lt;/li&gt;
&lt;li&gt;T1 rollback its transactions&lt;/li&gt;
&lt;li&gt;T2 reads the same row again and still gets the value of 10.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key difference from the read uncommitted example is that T2 only sees the latest committed data. When T2 first read the row, T1's change was uncommitted, so T2 saw the previous committed value of 10.&lt;/p&gt;

&lt;p&gt;The biggest difference between read uncommitted and read committed is that read uncommitted reads the latest uncommitted or committed data, while read committed always reads the latest committed data.&lt;/p&gt;

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

&lt;p&gt;However, what if T1 commits its change instead of rollback?&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%2Ffgffangfg4po2keoq8yx.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%2Ffgffangfg4po2keoq8yx.png" alt="read committed illustration" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only difference in this example is that T1 commit the changes.&lt;/p&gt;

&lt;p&gt;In that case, when T2 reads the same row twice, it would get different results: 10 on the first read and 11 on the second read after T1's commit. This is known as a &lt;strong&gt;&lt;em&gt;non-repeatable read&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Non-repeatable reads refers to the situation where a transaction reads the same row of data twice within the same transaction, but the data it reads changes between the two reads due to another concurrent transaction.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;In addition to non-repeatable reads, another issue can occur: &lt;strong&gt;&lt;em&gt;phantom reads&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A phantom read happens when a transaction retrieves a set of rows twice, but the number of rows changes between the two reads because another committed transaction has inserted or deleted rows from that set.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is very similar to non-repeatable read. Both refer to the result being different even though it’s the same query within a transaction. In this case, a phantom read refers to the difference in the number of rows it returns.&lt;/p&gt;

&lt;p&gt;This situation occurs because MySQL only locks the matched rows, not the entire set. Here's an example:&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%2Fe546qc4lda4w2g2l44bb.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%2Fe546qc4lda4w2g2l44bb.png" alt="read committed illustration" width="800" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The topmost table represents the latest state of the table. It only updates after a transaction commits its own changes, which is why the new row is only inserted after T2 actually commits.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transactions T1 and T2 each start their own transaction&lt;/li&gt;
&lt;li&gt;T1 updates the &lt;code&gt;like_amount&lt;/code&gt; column for the row where &lt;code&gt;user_name&lt;/code&gt; is "Alice"

&lt;ul&gt;
&lt;li&gt;During this operation, MySQL places an exclusive lock on the matched row.&lt;/li&gt;
&lt;li&gt;An exclusive lock prevents other transactions from acquiring a lock on the locked data.&lt;/li&gt;
&lt;li&gt;In this case, other transactions can’t acquire other types of lock on first row.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;T2 inserts a new row into the &lt;code&gt;posts&lt;/code&gt; table&lt;/li&gt;
&lt;li&gt;T1 finds the rows where &lt;code&gt;user_name&lt;/code&gt; is "Alice"

&lt;ul&gt;
&lt;li&gt;It only returns one row because T2 hasn’t committed yet.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;T2 commits its changes&lt;/li&gt;
&lt;li&gt;T1 runs the same query again, but now it returns two rows because T2's insert is visible&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a phantom read: T1 executed the same query twice but got a different set of rows. We’ll explore how higher levels solve this problem.&lt;/p&gt;

&lt;p&gt;Note that any problems occurring at a higher level also occur at a lower level, so non-repeatable read and phantom read also happen at the read uncommitted level.&lt;/p&gt;

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

&lt;p&gt;One final thing we may miss: How does MySQL implement read committed?&lt;/p&gt;

&lt;p&gt;To implement read committed, MySQL takes a &lt;strong&gt;&lt;em&gt;snapshot&lt;/em&gt;&lt;/strong&gt; of the database's current state for each query within a transaction. This snapshot represents the latest committed data at the time of the query, excluding any uncommitted changes.&lt;/p&gt;

&lt;p&gt;When a transaction reads data, it accesses the snapshot instead of the source table. &lt;strong&gt;&lt;em&gt;In the read committed level, a snapshot is refreshed and updated for subsequent queries within the same transaction.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s see diagram to be more clear.&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%2Frwlu63hi6un5aoxgbzms.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%2Frwlu63hi6un5aoxgbzms.png" alt="read committed illustration" width="800" height="619"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When T2 issues the read operation, a snapshot of the current state of the database is constructed, and T2 reads the result from this snapshot, not directly from the source table itself.&lt;/p&gt;

&lt;p&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%2Ffd6ctq14rgo3k8t5yqko.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%2Ffd6ctq14rgo3k8t5yqko.png" alt="read committed illustration" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once T1 commits its change, the source table finally gets updated to reflect T1's modification.&lt;/p&gt;

&lt;p&gt;Now, when T2 makes the second read, the snapshot is refreshed and synchronized with the updated source table. T2 then reads the result from this refreshed snapshot.&lt;/p&gt;

&lt;p&gt;It's important to note that for each query within a transaction at this isolation level, MySQL creates its own separate snapshot of the database.&lt;/p&gt;

&lt;p&gt;While read committed successfully prevents dirty reads with good performance, it does not solve non-repeatable reads and phantom reads. Despite this, some databases, like PostgreSQL, use read committed as the default isolation level. However, this is not the case for MySQL.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Repeatable Read
&lt;/h2&gt;

&lt;p&gt;The primary distinction between the committed read and the repeatable read lies in "when does snapshot refresh" and the "locking strategy."&lt;/p&gt;

&lt;p&gt;Let's start with snapshot refreshing. &lt;strong&gt;&lt;em&gt;In the repeatable read level, a snapshot is constructed by the first read. This snapshot is then used for all subsequent reads in that same transaction until it gets committed or rollback.&lt;/em&gt;&lt;/strong&gt; Consequently, it ensures that each read query consistently returns the same result within the same transaction.&lt;/p&gt;

&lt;p&gt;Let’s again see the diagram.&lt;br&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%2F3m38zns7qrvnawib9xqf.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%2F3m38zns7qrvnawib9xqf.png" alt="repeatable read isolation" width="800" height="619"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transactions T1 and T2 each start their own transaction&lt;/li&gt;
&lt;li&gt;T1 updates the &lt;code&gt;like_amount&lt;/code&gt; column to increment it by 1 for the row where &lt;code&gt;id&lt;/code&gt; is equal to 1&lt;/li&gt;
&lt;li&gt;T2 reads the rows and gets an output of 10

&lt;ul&gt;
&lt;li&gt;This is the first read operation within T2's transaction, so a snapshot is created.&lt;/li&gt;
&lt;li&gt;Since T1 hasn't committed its change yet when the snapshot was constructed, it still shows Alice's &lt;code&gt;like_count&lt;/code&gt; as 10.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;T1 commits its change and update the table&lt;/li&gt;
&lt;li&gt;T2 reads the same rows and still gets the output 10

&lt;ul&gt;
&lt;li&gt;This is the key difference we mentioned earlier.&lt;/li&gt;
&lt;li&gt;Instead of refreshing the snapshot, the second read accesses the old snapshot (gray arrow). In the repeatable read level, a snapshot remains the same throughout a transaction.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This mechanism helps solve the problem of non-repeatable reads.&lt;/p&gt;

&lt;p&gt;It's worth noting that issues resolved by lower isolation levels won't reoccur at higher levels, so we can guarantee that dirty reads won't happen in the repeatable read level either.&lt;/p&gt;

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

&lt;p&gt;Now, let's talk about the locking strategy.&lt;br&gt;
&lt;strong&gt;&lt;em&gt;At the repeatable read level, MySQL locks more than just the matched rows when performing an update.&lt;/em&gt;&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%2Fyrafqvnw8ae7lnyz9xal.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%2Fyrafqvnw8ae7lnyz9xal.png" alt="read committed illustration" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transactions T1 and T2 each start their own transaction&lt;/li&gt;
&lt;li&gt;T1 updates the &lt;code&gt;like_amount&lt;/code&gt; column for the row where &lt;code&gt;user_name&lt;/code&gt; is "Alice"

&lt;ul&gt;
&lt;li&gt;Now, MySQL not only locks matched row but also locks all subsequent rows.&lt;/li&gt;
&lt;li&gt;In simpler terms, MySQL locks the rows from &lt;code&gt;id&lt;/code&gt; 1 to infinity, preventing other transactions from inserting new rows.&lt;/li&gt;
&lt;li&gt;Note that the three dots (...) don't represent actual values; they're just a simple representation to show that MySQL locks all subsequent rows.&lt;/li&gt;
&lt;li&gt;Adding an exclusive(X) lock on the rows prevents other transactions from acquiring any lock on those rows.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;T2 attempts to insert a new row into the &lt;code&gt;posts&lt;/code&gt; table

&lt;ul&gt;
&lt;li&gt;Because all rows are locked, T2 has to wait until T1 releases the lock before inserting new rows.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;T2 gets a lock wait timeout error

&lt;ul&gt;
&lt;li&gt;After a few seconds, T2 receives the lock wait timeout error due to T1 hasn't released the lock within the allowed timeout period.&lt;/li&gt;
&lt;li&gt;The red error message indicates that a transaction is waiting for a lock to be released but exceeding the timeout period allowed for that lock.&lt;/li&gt;
&lt;li&gt;As long as T1 releases the lock before the lock wait timeout, T2 can successfully insert the new row.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because MySQL uses a more rigorous locking strategy to lock subsequent rows, it prevents phantom reads from happening in the repeatable read level.&lt;br&gt;
(For more information about locks in MySQL, you can check out &lt;a href="https://dev.to/eyo000000/a-straightforward-guide-for-mysql-locks-56i1"&gt;here&lt;/a&gt;)&lt;/p&gt;

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

&lt;p&gt;However, there's a caveat to the snapshot mechanism. It only applies to read operations. When performing updates, MySQL will look at the source table to find the matching rows, and then update them accordingly.&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%2F1qsjorzo0q5dwa6j3k9r.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%2F1qsjorzo0q5dwa6j3k9r.png" alt="read committed illustration" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transactions T1 and T2 each start their own transaction&lt;/li&gt;
&lt;li&gt;T1 inserts a new row into table&lt;/li&gt;
&lt;li&gt;T2 reads the rows where &lt;code&gt;like_count&lt;/code&gt; is equal to or greater than 15 and matches one row&lt;/li&gt;
&lt;li&gt;T1 commits its change, updating the source table&lt;/li&gt;
&lt;li&gt;T2 updates the rows where &lt;code&gt;like_count&lt;/code&gt; is equal to or greater than 15, and matches two rows

&lt;ul&gt;
&lt;li&gt;Surprisingly, the query response indicates "2 rows affected," which means that MySQL doesn't use the snapshot to match rows for updating.&lt;/li&gt;
&lt;li&gt;Instead, it reads from the source table to find the matching rows and updates them accordingly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;T2 runs the same read query again but gets two rows, which is different from the first time

&lt;ul&gt;
&lt;li&gt;Because T2 uses the source table to update rows, a new snapshot is re-created for subsequent reads.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From the above example, we can see that phantom reads may still occur in some edge cases, even at the repeatable read level. We should consider this pattern in our applications if it poses a potential issue. The only way to prevent phantom reads in every case is to use the serializable level, which we'll discuss in detail later.&lt;/p&gt;

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

&lt;p&gt;There are still some issues in the repeatable read level, one of which is the &lt;strong&gt;&lt;em&gt;lost update&lt;/em&gt;&lt;/strong&gt; problem.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A lost update refers to a situation where two or more transactions concurrently read the same data, modify it based on the read data, and then finally write the modified data back to the database. Because neither transaction is aware of the modifications made by the other, the second change overwrites the first modification.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In most cases, the lost update occurs in a read-modify-write pattern.&lt;br&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%2Fzjyyq337do06odrgu4l4.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%2Fzjyyq337do06odrgu4l4.png" alt="repeatable read illustration" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transactions T1 and T2 each start their own transaction&lt;/li&gt;
&lt;li&gt;T1 checks Alice’s &lt;code&gt;like_count&lt;/code&gt;, which returns 10&lt;/li&gt;
&lt;li&gt;T2 also checks Alice’s &lt;code&gt;like_count&lt;/code&gt;, obtaining the same result of 10&lt;/li&gt;
&lt;li&gt;T1 updates the &lt;code&gt;like_count&lt;/code&gt; to 15&lt;/li&gt;
&lt;li&gt;T2 updates the &lt;code&gt;like_count&lt;/code&gt; to 20&lt;/li&gt;
&lt;li&gt;T1 commits its changes, resulting in the table value becoming 15 (green)&lt;/li&gt;
&lt;li&gt;T2 then commits its change, overwriting the previous value with 20 (blue)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As we can see, T1 and T2 are both trying to update the same row concurrently, and T2 overwrites the updated value made by T1.&lt;br&gt;
One approach to mitigate this problem is by using atomic operations.&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="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;like_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;like_count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Alice'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another approach is leveraging higher isolation levels to help us.&lt;/p&gt;

&lt;p&gt;Repeatable read is the standard level in MySQL. Through its snapshot mechanism and locking strategy, it solves the issues such as non-repeatable reads and phantom reads. However, there are still unresolved issues.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  Serializable
&lt;/h2&gt;

&lt;p&gt;The overall idea behind the serializable level is that &lt;strong&gt;&lt;em&gt;even though transactions may execute concurrently, MySQL ensures that the end result is the same as if they were executed serially (one after the other).&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A key distinction of this level is &lt;strong&gt;&lt;em&gt;its requirement for every read operation to acquire a shared lock on the data it accesses.&lt;/em&gt;&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%2Fbafkshh7jatmmy70ne2h.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%2Fbafkshh7jatmmy70ne2h.png" alt="serializable level illustration" width="800" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transactions T1 and T2 each start their own transaction&lt;/li&gt;
&lt;li&gt;T1 reads data, causing shared locks on all rows

&lt;ul&gt;
&lt;li&gt;This behavior highlights why serializable is the strictest level.&lt;/li&gt;
&lt;li&gt;MySQL places shared locks on entire rows for a single select statement.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;T2 attempts to insert a new row into the "posts" table

&lt;ul&gt;
&lt;li&gt;Because all the rows are locked, T2 has to wait until T1 releases the locks to insert new rows.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;T2 encounters a lock wait timeout error&lt;/li&gt;
&lt;li&gt;T2 performs an update on the row&lt;/li&gt;
&lt;li&gt;T2 encounters another lock wait timeout error&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because any read query acquires shared locks on rows, any write or update operations by other transactions will have to wait until the shared locks are released. This pattern prevents the problems we mentioned earlier, such as non-repeatable reads and phantom reads.&lt;/p&gt;

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

&lt;p&gt;While serializable enhances data consistency, it also increase the possibility of deadlock.&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%2F8833lv01b9tuu9cdh7hx.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%2F8833lv01b9tuu9cdh7hx.png" alt="serializable level illustration" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Transactions T1 and T2 each start their own transaction&lt;/li&gt;
&lt;li&gt;T1 reads the data, causing shared locks to be acquired on all the rows&lt;/li&gt;
&lt;li&gt;T2 also reads the data, causing shared locks to be acquired on all the rows

&lt;ul&gt;
&lt;li&gt;Shared locks can be held simultaneously by multiple transactions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;T1 attempts to update a row, waiting for T2 to release its shared lock&lt;/li&gt;
&lt;li&gt;T2 attempts to update the same row, waiting for T1's shared lock release&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both transactions are waiting for the other to release its shared lock so that an exclusive lock can be acquired to perform the update. This behavior is called a &lt;strong&gt;&lt;em&gt;deadlock&lt;/em&gt;&lt;/strong&gt;. Since neither transaction can actively release its shared lock, MySQL decides to throw an error immediately without waiting.&lt;/p&gt;

&lt;p&gt;After T2 encounters the error, T1 successfully updates the row.&lt;/p&gt;

&lt;p&gt;Although the serializable level successfully eliminates the problems that the repeatable read level cannot solve and prevents most database concurrency issues, its strict locking mechanism can significantly affect performance. Therefore, it's generally not the recommended default choice for most database applications.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we explored the concept of isolation levels in MySQL, which represent different approaches to managing concurrency and ensuring data consistency in a transactional environment. Each isolation level strikes a balance between addressing potential concurrency issues and maintaining optimal performance.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read Uncommitted

&lt;ul&gt;
&lt;li&gt;The lowest isolation level allows transactions to read data that has been modified but not yet committed by other transactions.&lt;/li&gt;
&lt;li&gt;While offering the best performance, this level is generally not recommended due to the increased risk of encountering dirty reads and inconsistent data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Read Committed

&lt;ul&gt;
&lt;li&gt;By utilizing a snapshot mechanism, this level ensures that transactions only read data that has been committed by other transactions, preventing dirty reads.&lt;/li&gt;
&lt;li&gt;The snapshot is constructed for each query within a transaction.&lt;/li&gt;
&lt;li&gt;However, it does not address non-repeatable reads and phantom reads, where the same query within a transaction may return different results due to concurrent modifications.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Repeatable Read

&lt;ul&gt;
&lt;li&gt;As the default isolation level in MySQL, this level addresses non-repeatable reads by creating a consistent snapshot of the data for each transaction.&lt;/li&gt;
&lt;li&gt;The snapshot is constructed during the initial read operation and remains consistent until the transaction is committed or rolled back.&lt;/li&gt;
&lt;li&gt;However, non-repeatable reads still may occur in some edge cases.&lt;/li&gt;
&lt;li&gt;It also employs a more rigorous locking strategy to prevent phantom reads. While resolving many concurrency issues, it may still encounter problems such as lost updates.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Serializable

&lt;ul&gt;
&lt;li&gt;The highest isolation level provides the strictest data consistency by enforcing a serial execution order for transactions.&lt;/li&gt;
&lt;li&gt;It acquires shared locks on all rows it accesses, effectively preventing lost updates, and other concurrency issues.&lt;/li&gt;
&lt;li&gt;However, this extensive locking mechanism can significantly impact performance and increase the likelihood of deadlocks.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/techschoolguru/understand-isolation-levels-read-phenomena-in-mysql-postgres-c2e"&gt;Deeply understand Isolation levels and Read phenomena in MySQL &amp;amp; PostgreSQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html" rel="noopener noreferrer"&gt;MySQL documentation: Transaction Isolation Levels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://begriffs.com/posts/2017-08-01-practical-guide-sql-isolation.html" rel="noopener noreferrer"&gt;Practical Guide to SQL Transaction Isolation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Efficient-MySQL-Performance-Practices-Techniques/dp/1098105095" rel="noopener noreferrer"&gt;Efficient MySQL Performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Designing-Data-Intensive-Applications-Reliable-Maintainable/dp/1449373321" rel="noopener noreferrer"&gt;Designing Data-Intensive Applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/MySQL-Concurrency-Locking-Transactions-Developers/dp/148426651X" rel="noopener noreferrer"&gt;MySQL Concurrency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>database</category>
      <category>concurrency</category>
      <category>mysql</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
