<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Go Language - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Go Language - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 13 May 2026 20:28:24 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/go/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Create Dynamic Emails in Go with React Email  ]]>
                </title>
                <description>
                    <![CDATA[ Backend applications are required to send emails to users to deliver notifications and maintain communication outside the application interface. These emails usually contain information specific to ea ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-dynamic-emails-in-go-with-react-email/</link>
                <guid isPermaLink="false">69e689acc9501dd0102dc758</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Orim Dominic Adah ]]>
                </dc:creator>
                <pubDate>Mon, 20 Apr 2026 20:16:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/62917f79-c4d8-40e2-8eb7-87b63560e546.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Backend applications are required to send emails to users to deliver notifications and maintain communication outside the application interface. These emails usually contain information specific to each user, such as the user's name or address, making them dynamic.</p>
<p>This article walks you through building a dynamic email template with React Email, converting it to HTML, and injecting data into it using Go templates. It also contains an optional section that shows you how to send and test the email delivery with MailHog.</p>
<p>To follow along with this article, you need to have Go and Node.js installed on your computer. You should also have a basic understanding of React and some familiarity with Go templates, though these aren't strict requirements because you can pick them up as you practise along.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-is-react-email">What is React Email?</a></p>
</li>
<li><p><a href="#heading-go-templates">Go Templates</a></p>
<ul>
<li><a href="#heading-go-template-delimiters">Go Template Delimiters</a></li>
</ul>
</li>
<li><p><a href="#heading-create-dynamic-emails-in-go-with-react-email">Create Dynamic Emails in Go with React Email</a></p>
<ul>
<li><p><a href="#heading-set-up-the-project">Set Up the Project</a></p>
</li>
<li><p><a href="#heading-set-up-react-email">Set Up React Email</a></p>
</li>
<li><p><a href="#heading-create-a-react-email-template">Create a React Email Template</a></p>
</li>
<li><p><a href="#heading-set-up-go-templates-from-html-files">Set Up Go Templates from HTML Files</a></p>
</li>
<li><p><a href="#heading-render-the-dynamic-email-in-the-browser">Render the Dynamic Email in the Browser</a></p>
</li>
<li><p><a href="#heading-send-and-test-email-with-go-mail-and-mailhog">Send and Test Email with go-mail and MailHog</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-react-email">What is React Email?</h2>
<p><a href="https://react.email/">React Email</a> is a JavaScript library that helps you build dynamic email templates with React. If you already know basic React, React Email provides a better developer experience for building dynamic email templates. Here are some reasons why:</p>
<ul>
<li><p><strong>Familiar syntax with React:</strong> If you know React already, React Email eliminates the hassle in learning a separate templating language, using inefficient drag-and-drop UIs, or writing emaiil templates from scratch with HTML tables.</p>
</li>
<li><p><strong>Reusable built-in components</strong>: React Email provides ready-to-use UI components like <a href="https://react.email/components/buttons">Buttons</a> and <a href="https://react.email/components/footers">Footers</a> so you don't have to start from scratch, making email development seamless and fast.</p>
</li>
<li><p><strong>Consistency across email clients</strong>: React Email generates email templates that have been tested and work well across popular email clients. This helps eliminate worries over emails rendering inconsistently across email clients.</p>
</li>
<li><p><strong>Email development tooling</strong>: React Email has features for previewing and assessing emails built with it. Some of these features include:</p>
<ul>
<li><p>A local development server that lets you preview mobile and desktop views of your emails in your web browser as you develop the emails in real time</p>
</li>
<li><p>An email delivery feature that sends your email to a real email address for preview</p>
</li>
<li><p>A compatibility checker that shows you how well your email is supported across popular email clients</p>
</li>
<li><p>A spam scorer that analyses your email to determine if it's likely to be marked as spam</p>
</li>
</ul>
</li>
<li><p><strong>Tailwind integration</strong>: <a href="https://tailwindcss.com/">Tailwind</a> is a popular CSS framework that provides classes for styling HTML and making it responsive. React Email integrates with Tailwind easily for creating beautiful emails.</p>
</li>
</ul>
<p>All these features are free to use.</p>
<p>In this article, you'll learn how to generate an HTML file from a React Email template, convert it to a Go template, and inject data into the template for previewing.</p>
<h2 id="heading-go-templates">Go Templates</h2>
<p>The Go <a href="https://pkg.go.dev/html/template">html/template</a> package allows you to define reusable HTML templates that can be populated with dynamic data. These templates contain placeholders (called actions) that are evaluated by Go's templating engine and replaced with actual values during execution.</p>
<img src="https://cdn.hashnode.com/uploads/covers/66e28b713f978a0e2cd2b763/7ff18217-2ff1-43fc-96b5-01681fbd0ac5.png" alt="Golang HTML template parsing and execution" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>First, you give the package HTML content that contains Go-specific annotations. It converts the HTML content to a Go HTML template and the Go-specific annotations to actions in the template. The template is then executed with data to produce HTML output that contains the data.</p>
<pre><code class="language-go">package main

import (
	"html/template"
	"os"
)

func main() {
	tmpl := template.New("hello")
	tmpl, _ = tmpl.Parse(`&lt;p&gt;Hello {{.}}&lt;/p&gt;`)
	tmpl.Execute(os.Stdout, "Gopher")
}

// Output: &lt;p&gt;Hello Gopher&lt;/p&gt;
// Playground: https://goplay.tools/snippet/KxbkWPIArz5
</code></pre>
<p>In the code snippet above:</p>
<ul>
<li><p><code>template.New</code> creates an empty template object with the name "hello"</p>
</li>
<li><p><code>tmpl.Parse(`&lt;p&gt;Hello {{.}}&lt;/p&gt;`)</code> parses the HTML string <code>&lt;p&gt;Hello {{.}}&lt;/p&gt;</code> to create a Go HTML template and saves it in <code>tmpl</code> . The <code>{{.}}</code> part of the HTML string is an action which acts as a placeholder for data. <code>{{</code> and <code>}}</code> are called delimiters and <code>.</code> is the data access identifier.</p>
</li>
<li><p><code>tmpl.Execute(os.Stdout, "Gopher")</code> populates the action with data - the "Gopher", string, and writes the resulting HTML output to the console.</p>
</li>
</ul>
<h3 id="heading-go-template-delimiters">Go Template Delimiters</h3>
<p>In the previous code snippet, you used double curly braces (<code>{{</code> and <code>}}</code>) as the delimiters in the Go template. Delimiters are symbols that Go uses to determine what parts of the input string represent an action – that is, a statement to be evaluated.</p>
<p>You can change the delimiters by using the <code>Delims</code> method on a template. An example is shown in the snippet below:</p>
<pre><code class="language-go">package main

import (
	"html/template"
	"os"
)

func main() {
	tmpl := template.New("hello")
	tmpl, _ = tmpl.Delims("((", "))").Parse(`&lt;p&gt;Hello ((.))&lt;/p&gt;`)
	tmpl.Execute(os.Stdout, "Gopher")
}

// Output: &lt;p&gt;Hello Gopher&lt;/p&gt;
// Playground: https://goplay.tools/snippet/00RuDzvZYwN
</code></pre>
<p>In the snippet above, <code>((</code> and <code>))</code> are used as the delimiters for the <code>hello</code> template.</p>
<p>This is important because you'll set your delimiters to prevent conflicts between Go's default delimiters and React's curly braces in React Email templates.</p>
<h2 id="heading-create-dynamic-emails-in-go-with-react-email">Create Dynamic Emails in Go with React Email</h2>
<p>The image below summarizes how the sample application you'll build in this article works:</p>
<img src="https://cdn.hashnode.com/uploads/covers/66e28b713f978a0e2cd2b763/289f0ea5-1de7-46f7-b463-32f6c61fa750.png" alt="From React Email templates to Email HTML with Dynamic Data" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>You'll create a React Email template that contains Go template annotations. Next, you'll use Node.js to create HTML files from it. Go will parse the HTML file to create a Go template, execute it, and send it.</p>
<p>Optionally, you'll use go-mail to send the email and MailHog, a local SMTP server, to preview it in your browser.</p>
<h3 id="heading-set-up-the-project">Set Up the Project</h3>
<p>First, make sure that you have Go and Node.js installed on your computer already. Clone this <a href="https://github.com/orimdominic/freeCodeCamp-go-react-email">freeCodeCamp-go-react-email</a> repository and checkout the <code>01-setup</code> branch using <code>git checkout 01-setup</code>.</p>
<p>The project contains a <code>main.go</code> file in the <code>cmd</code> directory and a <code>go.mod</code> file. It also contains a <code>.gitignore</code> file to instruct Git to ignore all <code>node_modules</code> directories.</p>
<p>Run <code>go run cmd/main.go</code> in the terminal of the project. If you see "It works!" logged to the console, you have set it up properly and you can continue to the next section.</p>
<h3 id="heading-set-up-react-email">Set Up React Email</h3>
<p>In the project root directory, create a <code>mailer</code> directory which will serve as the mailer package. It will hold all functionality related to creating and sending mails.</p>
<p>In the <code>mailer</code> directory, you'll create the <code>emails</code> Node.js project that will handle React Email functionality. To create the project:</p>
<ul>
<li><p>Create a directory called <code>_emails</code> in the <code>mailer</code> directory. The name of the directory starts with an underscore because it should be ignored when the <code>go build</code> command is run. So it won't be included in the Go compiled executable file.</p>
</li>
<li><p>Run <code>npm init -y</code> in the root terminal of the <code>_emails</code> directory to initialise the Node.js project in it. This will create a <code>package.json</code> file in the directory.</p>
</li>
<li><p>Update the value of the name field in <code>package.json</code> to "emails" to make the package name more conventional. This step is not compulsory.</p>
</li>
</ul>
<p>Next, install the required React Email libraries by running the following commands in the root terminal of the <code>_emails</code> directory:</p>
<pre><code class="language-shell">npm install @react-email/ui @types/react -D -E
npm install react-email react react-dom -E
</code></pre>
<p>After the installation is complete, replace the <code>scripts</code> field of the <code>package.json</code> file with the code snippet below:</p>
<pre><code class="language-json">  "scripts": {
    "dev": "email dev --dir ./src",
    "export": "email export --pretty --dir ./src --outDir ../templates"
  },
</code></pre>
<p>The <code>dev</code> script starts and runs the server for previewing the React Email templates in the browser. You will write the template with React and store it in the <code>src</code> directory under <code>_emails</code>. The <code>export</code> script transpiles the template files in the <code>src</code> directory from JSX (or TSX) to HTML and stores them in a directory called <code>templates</code>, a direct child of the <code>mailer</code> directory – not a child of the <code>_emails</code> directory.</p>
<p>The <code>templates</code> directory is stored as a child directory of the <code>mailer</code> directory because the Go project needs only the HTML output stored in the <code>templates</code> directory and not the contents of <code>_emails</code>.</p>
<p>If you've completed all these steps, you have set up React Email in the <code>emails</code> Node.js project. To view the current status of the project at this point, visit <a href="https://github.com/orimdominic/freeCodeCamp-go-react-email/tree/02-setup-react-email">freeCodeCamp-go-react-email/02-setup-react-email</a>.</p>
<p>In the next section, you'll create a React Email template and preview it in the browser.</p>
<h3 id="heading-create-a-react-email-template">Create a React Email Template</h3>
<p>In this section, you'll create a React Email template and preview it in the browser. You'll also build and export the template to HTML files.</p>
<p>Create a directory called <code>src</code> inside the <code>_emails</code> directory. Inside the <code>src</code> directory, create a file called <code>welcome.tsx</code>. Copy and paste the content of the snippet below into <code>welcome.tsx</code>.</p>
<pre><code class="language-typescript">import {
  Body,
  Button,
  Container,
  Head,
  Heading,
  Html,
  Img,
  Preview,
  Section,
  Tailwind,
  Text,
} from "react-email";

interface WelcomeEmailProps {
  username?: string;
  company?: string;
  gophers?: string[];
}

const WelcomeEmail = ({
  username = "Nicole",
  company = "GoWorld",
  gophers = ["Tinky Winky", "Dipsy", "Laa-Laa", "Po"],
}: WelcomeEmailProps) =&gt; {
  const previewText = `Welcome to \({company}, \){username}!`;

  return (
    &lt;Html&gt;
      &lt;Head /&gt;
      &lt;Preview&gt;{previewText}&lt;/Preview&gt;
      &lt;Tailwind&gt;
        &lt;Body className="m-auto font-sans"&gt;
          &lt;Container className="mb-10 mx-auto p-5 max-w-[465px]"&gt;
            &lt;Section className="mt-10"&gt;
              &lt;Img
                src={`https://storage.googleapis.com/gopherizeme.appspot.com/gophers/69428e5ec867c34bb4a49d5a063fdbc2a6633aed.png`}
                width="80"
                height="80"
                alt="Logo"
                className="my-0 mx-auto"
              /&gt;
            &lt;/Section&gt;
            &lt;Heading className="text-2xl font-normal text-center p-0 my-8 mx-0"&gt;
              Welcome to &lt;strong&gt;{company}&lt;/strong&gt;, {username}!
            &lt;/Heading&gt;
            &lt;Text className="text-start text-base"&gt;Hello {username},&lt;/Text&gt;
            &lt;Text className="text-start text-base leading-relaxed"&gt;
              We're excited to have you onboard at &lt;strong&gt;{company}&lt;/strong&gt;.
              We hope you enjoy your journey with us. If you have any questions
              or need assistance, feel free to reach out to any of the following
              gophers:
            &lt;/Text&gt;
            &lt;div className="text-start text-base leading-relaxed"&gt;
              &lt;ul className="pl-3"&gt;
                {gophers.map((gopher) =&gt; (
                  &lt;li&gt;{gopher}&lt;/li&gt;
                ))}
              &lt;/ul&gt;
            &lt;/div&gt;
            &lt;Section className="text-center mt-[32px] mb-[32px]"&gt;
              &lt;Button
                className="py-2.5 px-5 bg-white rounded-md text-base font-semibold no-underline text-center bg-black text-white"
                href={`https://go.dev`}
              &gt;
                Get Started
              &lt;/Button&gt;
            &lt;/Section&gt;
            &lt;Text className="text-start text-base text-white"&gt;
              Cheers,
              &lt;br /&gt;
              The {company} Team
            &lt;/Text&gt;
          &lt;/Container&gt;
        &lt;/Body&gt;
      &lt;/Tailwind&gt;
    &lt;/Html&gt;
  );
};

export default WelcomeEmail;
</code></pre>
<p>The code snippet above is the React Email template that you'll use in this article. To preview it, navigate to the terminal of the <code>_emails</code> root directory and run <code>npm run dev</code> . Use your web browser to visit the preview URL displayed on the terminal. Click on the "welcome" link on the left sidebar and you should see a UI similar to the one in the screenshot below:</p>
<img src="https://cdn.hashnode.com/uploads/covers/66e28b713f978a0e2cd2b763/9a1253d2-7502-40d5-9081-7974c7a83f36.png" alt="React Email preview UI" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>In the UI above, React Email renders the email with the default values supplied to the <code>welcome</code> email template.</p>
<p>Stop the server by clicking on the terminal that runs it by pressing <code>CTRL + C</code>. Build the HTML output of the <code>src</code> directory and export it by running <code>npm run export</code> in the terminal of the <code>_emails</code> root directory. This creates a <code>templates</code> directory within the <code>mailer</code> directory where the exported HTML files are stored. In the <code>templates</code> directory, you'll see a <code>welcome.html</code> file – the HTML output from <code>welcome.tsx</code>.</p>
<p>To see the current status of the project, visit <a href="https://github.com/orimdominic/freeCodeCamp-go-react-email/tree/03-create-react-email-template">freeCodeCamp-go-react-email/03-create-react-email-template</a>.</p>
<h3 id="heading-set-up-go-templates-from-html-files">Set Up Go Templates from HTML Files</h3>
<p>You have created a React Email template, previewed it, built it, and exported it as an HTML file. In this section, you'll update the React Email template to use the delimiters you set and not React's curly braces. You'll also create a Go template from the HTML file.</p>
<p>To get started, replace the content of <code>welcome.tsx</code> with the code snippet below to to use <code>((</code> and <code>))</code> as delimiters and remove TypeScript types:</p>
<pre><code class="language-typescript">import {
  Body,
  Button,
  Container,
  Head,
  Heading,
  Html,
  Img,
  Preview,
  Section,
  Tailwind,
  Text,
} from "react-email";

const WelcomeEmail = () =&gt; {
  const previewText = `Welcome to ((.Company)), ((.Username))!`;

  return (
    &lt;Html&gt;
      &lt;Head /&gt;
      &lt;Preview&gt;{previewText}&lt;/Preview&gt;
      &lt;Tailwind&gt;
        &lt;Body className="m-auto font-sans"&gt;
          &lt;Container className="mb-10 mx-auto p-5 max-w-[465px]"&gt;
            &lt;Section className="mt-10"&gt;
              &lt;Img
                src={`https://storage.googleapis.com/gopherizeme.appspot.com/gophers/69428e5ec867c34bb4a49d5a063fdbc2a6633aed.png`}
                width="80"
                height="80"
                alt="Gopher"
                className="my-0 mx-auto"
              /&gt;
            &lt;/Section&gt;
            &lt;Heading className="text-2xl font-normal text-center p-0 my-8 mx-0"&gt;
              Welcome to &lt;strong&gt;((.Company))&lt;/strong&gt;, ((.Username))!
            &lt;/Heading&gt;
            &lt;Text className="text-start text-base"&gt;Hello ((.Username)),&lt;/Text&gt;
            &lt;Text className="text-start text-base leading-relaxed"&gt;
              We're excited to have you onboard at &lt;strong&gt;((.Company))&lt;/strong&gt;
              . We hope you enjoy your journey with us. If you have any
              questions or need assistance, feel free to reach out to any of the
              following Gophers:
            &lt;/Text&gt;
            &lt;div className="text-start text-base leading-relaxed"&gt;
              &lt;ul className="pl-3"&gt;
                ((range .Gophers))
                &lt;li&gt;((.))&lt;/li&gt;
                ((end))
              &lt;/ul&gt;
            &lt;/div&gt;
            &lt;Section className="text-center mt-[32px] mb-[32px]"&gt;
              &lt;Button
                className="py-2.5 px-5 bg-white rounded-md border text-black text-base font-semibold no-underline text-center"
                href={`https://go.dev`}
              &gt;
                Get Started
              &lt;/Button&gt;
            &lt;/Section&gt;

            &lt;Text className="text-start text-base"&gt;
              Cheers,
              &lt;br /&gt;
              The ((.Company)), Team
            &lt;/Text&gt;
          &lt;/Container&gt;
        &lt;/Body&gt;
      &lt;/Tailwind&gt;
    &lt;/Html&gt;
  );
};

export default WelcomeEmail;
</code></pre>
<p>Run <code>npm run export</code> in the root terminal of the <code>_emails</code> directory to build and export this version of the React Email template to HTML. The HTML generated will contain Go template annotations that will become actions when parsed by Go to form a Go HTML template.</p>
<p>In the <code>mailer</code> directory, create a file named <code>fs.go</code>. The code in the file will be used to embed the files in the <code>templates</code> directory for use in the Go application. Copy and paste the content of the snippet below into <code>fs.go</code>:</p>
<pre><code class="language-go">package mailer

import (
	"embed"
	"io/fs"
)

//go:embed templates/*
var embedded embed.FS
var templateFS, _ = fs.Sub(embedded, "templates")
</code></pre>
<p><code>//go:embed templates/*</code> tells the Go compiler to embed files from the current directory (<code>mailer</code>) into the compiled binary of the Go application. You need this to access the HTML template files from the Go application. <code>templateFS</code> will be used to access the files in the <code>templates</code> subdirectory.</p>
<p>Create another file in the <code>mailer</code> directory and name it <code>mailer.go</code>. <code>mailer.go</code> will contain code used to parse HTML files to make Go HTML templates and also send emails. Copy the content of the code snippet below into <code>mailer.go</code>:</p>
<pre><code class="language-go">package mailer

import (
	"html/template"
	"io"
)

const (
	welcomeMailKey = "welcome_mail"
)

func setUpTemplates() (map[string]*template.Template, error) {
	templates := make(map[string]*template.Template)

	tmpl := template.New("welcome.html").Delims("((", "))")
	welcomeEmailTmpl, err := tmpl.ParseFS(templateFS, "welcome.html")
	if err != nil {
		return nil, err
	}

	templates[welcomeMailKey] = welcomeEmailTmpl

	return templates, nil
}

type Mailer struct {
	templates map[string]*template.Template
}

// NewMailer creates a new mailer
func NewMailer() (*Mailer, error) {
	tpls, err := setUpTemplates()
	if err != nil {
		return nil, err
	}

	return &amp;Mailer{
		templates: tpls,
	}, nil
}

type WelcomEmailData struct {
	Username string
	Company  string
	Gophers  []string
}

func (mailer *Mailer) WriteWelcomeMail(w io.Writer, data WelcomEmailData) error {
	tmpl := mailer.templates[welcomeMailKey]
	err := tmpl.Execute(w, data)

	return err
}
</code></pre>
<p>In the code snippet above:</p>
<ul>
<li><p><code>setUpTemplates</code> creates a template object, <code>tmpl</code>, and sets its delimiters. <code>tmpl</code> parses <code>welcome.html</code> to convert it to a Go template and stores the template with <code>welcomeEmailTmpl</code> as its identifier. After that, <code>welcomeEmailTmpl</code> is added to the <code>templates</code> map with <code>welcomeMailKey</code> as its key and <code>templates</code> is returned.</p>
</li>
<li><p><code>NewMailer</code> creates and returns a <code>Mailer</code> object which holds the templates map and methods to work with the mail templates.</p>
</li>
<li><p>WriteWelcomeMail is a method on <code>Mailer</code> that's used to execute the welcome email template with real data.</p>
</li>
</ul>
<p>To view the current status of the codebase at this point, visit <a href="https://github.com/orimdominic/freeCodeCamp-go-react-email/tree/04-create-golang-template">freeCodeCamp-go-react-email/04-create-golang-template</a>.</p>
<h3 id="heading-render-the-dynamic-email-in-the-browser">Render the Dynamic Email in the Browser</h3>
<p>In this section, you'll create a simple web server to view the rendered email template containing the dynamic values passed to it.</p>
<p>Replace the content of <code>main.go</code> with the code snippet below:</p>
<pre><code class="language-go">package main

import (
	"fmt"
	"net/http"
	"os"

	pkgMailer "github.com/orimdominic/freeCodeCamp-go-react-email/mailer"
)

func main() {
	mailer, err := pkgMailer.NewMailer()
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		os.Exit(1)
	}

	http.HandleFunc("/mail", func(w http.ResponseWriter, r *http.Request) {
		username := r.URL.Query().Get("username")
		company := r.URL.Query().Get("company")
		gophers := []string{"Tinky Winky", "Dipsy", "Laa-Laa", "Po"}

		err := mailer.WriteWelcomeMail(w, pkgMailer.WelcomEmailData{
			Username: username,
			Company:  company,
			Gophers:  gophers,
		})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	})

	port := ":8888"
	err = http.ListenAndServe(port, nil)
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		os.Exit(1)
	}
}
</code></pre>
<p>The code snippet above first creates a mailer object using the <code>NewMailer</code> function from <code>mailer.go</code>. After the error handling, it creates a simple web server running on port <code>8888</code> with a <code>GET /mail</code> route.</p>
<p>The <code>GET /mail</code> route accepts two query parameters: <code>username</code> and <code>company</code>, which will be used as the dynamic data for the email. The result of executing the template with <code>WriteWelcomeMail</code> is written as an HTML response on the browser. You'll use this route to test the functionality of the <code>mailer</code> package.</p>
<p>Before you start the server, you should build and export the React Email templates so that your HTML files always have the most recent changes from React Email templates. Instead of navigating between different directories to build, export and run the server, you can use a Makefile.</p>
<p>Navigate to the terminal of the root directory of the project and create a file called <code>Makefile</code>. Copy and paste the content of the code snippet below into it:</p>
<pre><code class="language-plaintext">run: email-build
	go run cmd/main.go

email-build: mailer/_emails
	npm --prefix mailer/_emails run export
</code></pre>
<p>The <code>run</code> script of the Makefile above builds and exports the React Email templates as HTML to the <code>mailer/templates</code> directory and then starts the Go application. Ensure that <code>Makefile</code> uses hard tabs, not spaces for indentation.</p>
<p>Run <code>make run</code> in the terminal of the root directory of the project and visit <code>http://localhost:8888/mail?username=Nicole&amp;company=GoWorld</code> in the browser. You'll see the email rendered on the browser UI.</p>
<img alt="Go template executed in the browser" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Replace the values of <code>username</code> and <code>company</code> in the URL to test the email with different values.</p>
<p>With this setup, you can integrate the result of executing the template with your mail client and the email recipient will see the email as it's displayed in the browser.</p>
<p>To view the current status of the codebase at this point, visit <a href="https://github.com/orimdominic/freeCodeCamp-go-react-email/tree/05-render-dynamic-email">freeCodeCamp-go-react-email/05-render-dynamic-email</a>.</p>
<h3 id="heading-send-and-test-email-with-go-mail-and-mailhog">Send and Test Email with go-mail and MailHog</h3>
<p>In the previous section, you supplied data to execute your template, but it was rendered in the browser, not an email client. In this section, you'll use go-mail to send the email and MailHog to intercept and view it.</p>
<p>This section is optional. If you don't have MailHog installed locally, you'll need Docker Compose to set it up for this project. Make sure Docker Compose is installed on your computer before proceeding.</p>
<p>In your terminal, navigate to the root directory of the project and run <code>go get github.com/wneessen/go-mail</code> to install go-mail. Create a <code>compose.yml</code> file in the root directory of the project and paste the contents of the code snippet below into it:</p>
<pre><code class="language-yaml">services:
  mailhog:
    image: mailhog/mailhog
    restart: no
    logging:
      driver: "none" # disable saving logs
    ports:
      - 1025:1025 # smtp server
      - 8025:8025 # web ui
</code></pre>
<p>In your terminal, navigate to the project's root directory and run <code>docker compose up</code> to pull and start the MailHog SMTP server. MailHog listens for emails on port <code>1025</code> and exposes a web UI at <code>http://localhost:8025</code> where you can view intercepted emails. Depending on your internet connection, the initial image pull may take a few minutes.</p>
<p>Replace <code>mailer.go</code> with the content of the code snippet below:</p>
<pre><code class="language-go">package mailer

import (
	"html/template"
	"io"

	"github.com/wneessen/go-mail"
)

const (
	welcomeMailKey = "welcome_mail"
    sender = "noreply@localhost.com"
)

func setUpTemplates() (map[string]*template.Template, error) {
	templates := make(map[string]*template.Template)

	tmpl := template.New("welcome.html").Delims("((", "))")
	welcomeEmailTmpl, err := tmpl.ParseFS(templateFS, "welcome.html")
	if err != nil {
		return nil, err
	}

	templates[welcomeMailKey] = welcomeEmailTmpl

	return templates, nil
}

type Mailer struct {
	client    *mail.Client
	templates map[string]*template.Template
}

// NewMailer creates a new mailer
func NewMailer() (*Mailer, error) {
	tpls, err := setUpTemplates()
	if err != nil {
		return nil, err
	}

	c, err := mail.NewClient(
		"localhost",
		mail.WithPort(1025),
		mail.WithTLSPolicy(mail.NoTLS),
	)

	if err != nil {
		return nil, err
	}

	return &amp;Mailer{
		client:    c,
		templates: tpls,
	}, nil
}

type WelcomEmailData struct {
	Username string
	Company  string
	Gophers  []string
}

func (mailer *Mailer) WriteWelcomeMail(w io.Writer, data WelcomEmailData) error {
	tmpl := mailer.templates[welcomeMailKey]
	err := tmpl.Execute(w, data)

	return err
}

func (mailer *Mailer) SendWelcomeMail(to string, data WelcomEmailData) error {
	m := mail.NewMsg()
	m.From(sender)
	m.To(to)
	m.Subject("Welcome to " + data.Company)
	m.SetBodyHTMLTemplate(mailer.templates[welcomeMailKey], data)

	err := mailer.client.DialAndSend(m)
	return err
}
</code></pre>
<p>The new changes to <code>mailer.go</code> include:</p>
<ul>
<li><p>An import of the go-mail</p>
</li>
<li><p>The creation of a <code>sender</code> constant which represents the email of the sender</p>
</li>
<li><p>The creation of a mail client with go-mail</p>
</li>
<li><p>The creation of a <code>SendWelcomeMail</code> method on the <code>mailer</code> struct which creates an email with <code>welcomeEmailTmpl</code>, executes it, and sends it to a receiver.</p>
</li>
</ul>
<p>In <code>main.go</code>, update the <code>GET /mail</code> route to use <code>SendWelcomeMail</code> instead of <code>WriteWelcomeMail</code>. You can use any email address you want. The snippet below uses <code>fcc@go.dev</code>:</p>
<pre><code class="language-go">		err := mailer.SendWelcomeMail("fcc@go.dev", pkgMailer.WelcomEmailData{
			Username: username,
			Company:  company,
			Gophers:  gophers,
		})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		fmt.Fprint(w, "Email sent")
</code></pre>
<p>Ensure that the mail server is running by visiting <a href="http://localhost:8025">http://localhost:8025</a> in your web browser. In another terminal, from the root directory of the project, run <code>make run</code> to start the server. Test the functionality of the route by visiting <code>http://localhost:8888/mail?username=Nicole&amp;company=GoWorld</code> once again. Next, check the email server by visiting <a href="http://localhost:8025">http://localhost:8025</a>. You should see a UI similar to the one in the screenshot below:</p>
<img src="https://cdn.hashnode.com/uploads/covers/66e28b713f978a0e2cd2b763/3cba1adf-f0e7-4539-8f65-931fef1aa73b.png" alt="MailHog UI for previewing mails" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Click on "Welcome to Helix" to view the email.</p>
<p>To view the current status of the codebase at this point, visit <a href="https://github.com/orimdominic/freeCodeCamp-go-react-email/tree/06-send-email">freeCodeCamp-go-react-email/06-send-email</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>By following along with this tutorial, you have:</p>
<ul>
<li><p>Learned how to create Go email templates from React Email templates</p>
</li>
<li><p>Learned how to use Makefiles to run custom scripts</p>
</li>
<li><p>Previewed your email in the browser and tested it using MailHog</p>
</li>
</ul>
<p>You can now skip the hassle of writing raw HTML email tables or learning a new templating language. With React Email and Go templates, you have a cleaner, more developer-friendly way to build and send beautiful emails.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Bank Ledger in Golang with PostgreSQL using the Double-Entry Accounting Principle. ]]>
                </title>
                <description>
                    <![CDATA[ The Hidden Bugs in How Most Developers Store Money Imagine you're building the backend for a million-dollar fintech app. You store each user's balance as a single number in the database. It feels simp ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-bank-ledger-in-go-with-postgresql-using-the-double-entry-accounting-principle/</link>
                <guid isPermaLink="false">69c4173d10e664c5dac8cea1</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PostgreSQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ banking ]]>
                    </category>
                
                    <category>
                        <![CDATA[ accounting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ double entry ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Paul Babatuyi ]]>
                </dc:creator>
                <pubDate>Wed, 25 Mar 2026 17:11:25 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/faea1d4c-5319-4746-96b0-315f37017e26.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-the-hidden-bugs-in-how-most-developers-store-money">The Hidden Bugs in How Most Developers Store Money</h2>
<p>Imagine you're building the backend for a million-dollar fintech app. You store each user's balance as a single number in the database. It feels simple: just update the number when money moves.</p>
<p>But with one line of code like <code>UPDATE accounts SET balance = balance - 100</code>, you've created a system that can silently lose millions. A server crash, a race condition, or a clever attack, and suddenly money vanishes or appears out of thin air.</p>
<p>There's no audit trail, no way to know what happened, and no way to prove it didn't happen on purpose.</p>
<p>This isn't just a theoretical risk. It's a trap that's caught even experienced developers. The world's most trusted financial systems avoid it by using double-entry accounting. Every transaction creates two records: a debit on one account, a credit on another. This lets you reconstruct every cent from history, catch inconsistencies, and audit every transaction.</p>
<p>There are no deletes, and no silent updates. Just an append-only trail that makes fraud and bugs much harder to hide.</p>
<p>In this guide, you'll build a robust backend in Go and PostgreSQL, using patterns inspired by real fintech companies. You'll learn how to design a double-entry ledger, generate type-safe SQL with sqlc, and write transactions that are safe even under heavy load.</p>
<p>By the end, you'll understand why these patterns matter –&nbsp;and how to use them to build software you can trust with real money.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-prerequisites-and-project-overview">Prerequisites and Project Overview</a></p>
</li>
<li><p><a href="#heading-the-double-entry-foundation-how-every-penny-is-accounted-for">The Double-Entry Foundation</a></p>
</li>
<li><p><a href="#heading-type-safe-sql-with-sqlc-no-more-surprises">Type-Safe SQL with sqlc</a></p>
</li>
<li><p><a href="#heading-the-store-layer-transactions-and-automatic-retries">The Store Layer: Transactions and Retries</a></p>
</li>
<li><p><a href="#heading-the-service-layer-where-business-logic-meets-double-entry">The Service Layer: Business Logic</a></p>
</li>
<li><p><a href="#heading-the-api-layer-secure-predictable-and-boring-by-design">The API Layer</a></p>
</li>
<li><p><a href="#heading-running-it-locally-your-first-end-to-end-test">Running It Locally</a></p>
</li>
<li><p><a href="#heading-testing-prove-the-system-works">Testing: Prove the System Works</a></p>
</li>
<li><p><a href="#heading-deployment-engineering-decisions-that-matter-in-production">Deployment</a></p>
</li>
<li><p><a href="#heading-conclusion-building-for-the-real-world">Conclusion</a></p>
</li>
</ul>
<h3 id="heading-project-resources">Project Resources:</h3>
<p>Here's the project repository: <a href="https://github.com/PaulBabatuyi/double-entry-bank-Go">https://github.com/PaulBabatuyi/double-entry-bank-Go</a></p>
<p>And here's the front-end repository: <a href="https://github.com/PaulBabatuyi/double-entry-bankhttps://github.com/PaulBabatuyi/double-entry-bank">https://github.com/PaulBabatuyi/double-entry-bank</a></p>
<p>You can find the live frontend here: <a href="https://golangbank.app">https://golangbank.app</a></p>
<img src="https://cdn.hashnode.com/uploads/covers/6968db1b0578d1643036e600/2240e617-5a6d-4742-995f-6ecb8fecb56e.png" alt="Double-entry frontend transaction" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>You can find the live Swagger back-end API here: <a href="https://golangbank.app/swagger">https://golangbank.app/swagger</a></p>
<img src="https://cdn.hashnode.com/uploads/covers/6968db1b0578d1643036e600/3a6c1e02-5ceb-43e4-86a3-0530735b79cb.png" alt="Backend API endpoints (Swagger)" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h2 id="heading-prerequisites-and-project-overview">Prerequisites and Project Overview</h2>
<p>Before you dive in, make sure you have the following installed:</p>
<ul>
<li><p>Go 1.23 or newer</p>
</li>
<li><p>Docker and Docker Compose</p>
</li>
<li><p><code>golang-migrate</code> CLI: <code>go install github.com/golang-migrate/migrate/v4/cmd/migrate@latest</code></p>
</li>
<li><p><code>sqlc</code> CLI: <code>go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest</code></p>
</li>
</ul>
<p>You'll also need a basic understanding of PostgreSQL and REST APIs to follow along.</p>
<p>If you've built a CRUD app before, you're ready for this. The project uses sqlc for type-safe queries, JWT for authentication, and a layered architecture that keeps business logic, persistence, and HTTP handling cleanly separated.</p>
<p>Here's how the project is organized:</p>
<pre><code class="language-plaintext">.
├── cmd/                # Server entrypoint
│   └── main.go
├── internal/
│   ├── api/            # HTTP handlers &amp; middleware
│   ├── db/             # Store layer (transactions, sqlc)
│   └── service/        # Business logic (ledger operations)
├── postgres/
│   ├── migrations/     # SQL migration files
│   └── queries/        # sqlc query files
├── docs/               # Swagger docs
├── Dockerfile, docker-compose.yml, Makefile
└── README.md
</code></pre>
<p>The architecture follows a clear three-layer pattern:</p>
<ul>
<li><p><strong>API Layer</strong>: Handles HTTP requests, authentication, and routing.</p>
</li>
<li><p><strong>Service Layer</strong>: Contains the business logic. This is where double-entry rules are enforced.</p>
</li>
<li><p><strong>Store Layer</strong>: Manages database transactions and persistence.</p>
</li>
</ul>
<p>Every request flows from the handler, through the service, to the store, and finally to PostgreSQL. This separation makes the code easier to test, debug, and extend.</p>
<h3 id="heading-backend-request-flow">Backend Request Flow</h3>
<pre><code class="language-mermaid">graph TD
    A[HTTP Request] --&gt; B[Handler - API Layer]
    B --&gt; C[LedgerService - Business Logic]
    C --&gt; D[Store - Persistence Layer]
    D --&gt; E[(PostgreSQL)]
    E --&gt; D
    D --&gt; C
    C --&gt; B
    B --&gt; F[HTTP Response]
</code></pre>
<h2 id="heading-the-double-entry-foundation-how-every-penny-is-accounted-for">The Double-Entry Foundation: How Every Penny is Accounted For</h2>
<p>Let's get to the heart of what makes this system bulletproof: double-entry accounting. Every operation – a deposit, withdrawal, or transfer&nbsp;– creates two entries that always balance. This is the secret sauce that keeps banks, payment apps, and even crypto exchanges from losing track of money.</p>
<p>Picture a simple deposit of $1,000:</p>
<pre><code class="language-plaintext">| Account              | Debit   | Credit  |
|----------------------|---------|---------|
| User Account         |         | 1,000   |
| Settlement Account   | 1,000   |         |
</code></pre>
<p>Total debits always equal total credits. This is the fundamental rule. Every single operation in this system produces exactly this structure, with no exceptions.</p>
<p>Now picture a $200 transfer from User A to User B. Notice there are four entries, not two – both sides of both accounts are recorded:</p>
<pre><code class="language-plaintext">| Account       | Debit   | Credit  | Description           |
|---------------|---------|---------|-----------------------|
| User A        | 200     |         | Transfer to User B    |
| User B        |         | 200     | Transfer from User A  |
</code></pre>
<p>Both entries share the same <code>transaction_id</code>, so you can always retrieve the complete picture of what happened with a single query. There's no guessing and no reconstructing, as the ledger tells the full story.</p>
<h3 id="heading-why-the-settlement-account-goes-negative">Why the Settlement Account Goes Negative</h3>
<p>This trips up newcomers, so it's worth explaining explicitly. When a user deposits \(1,000, the settlement account is debited \)1,000. After several user deposits, the settlement balance will be negative. That's correct and expected: it represents the total amount of real-world money currently held inside the system on behalf of users. The invariant is:</p>
<pre><code class="language-plaintext">SUM(all user account balances) + settlement balance = 0
</code></pre>
<p>If that ever doesn't hold, something is broken.</p>
<h3 id="heading-enforcing-the-rules-in-the-database">Enforcing the Rules in the Database</h3>
<p>The database itself enforces these rules, not just the application code. Here's the core of the <code>entries</code> table migration:</p>
<pre><code class="language-sql">CREATE TABLE IF NOT EXISTS entries (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    account_id UUID NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT,
    debit NUMERIC(19,4) NOT NULL DEFAULT 0.0000 CHECK (debit &gt;= 0),
    credit NUMERIC(19,4) NOT NULL DEFAULT 0.0000 CHECK (credit &gt;= 0),
    transaction_id UUID NOT NULL,
    operation_type operation_type NOT NULL,
    description TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,

    CONSTRAINT check_single_side CHECK (
        (debit &gt; 0 AND credit = 0) OR (debit = 0 AND credit &gt; 0)
    )
);
</code></pre>
<p>Let's break down why each piece matters:</p>
<ul>
<li><p><strong>Single-sided entries are impossible.</strong> The <code>check_single_side</code> constraint means every entry must be either a debit or a credit, never both. If you try to insert an invalid row, the database rejects it – there's no way around it.</p>
</li>
<li><p><strong>Every transaction is linked.</strong> Both the debit and credit entries share the same <code>transaction_id</code> (a UUID). This lets you fetch both sides of any operation instantly, making audits and debugging straightforward.</p>
</li>
<li><p><strong>Operation types are explicit.</strong> The <code>operation_type</code> column is an enum at the database level, so only valid types like <code>deposit</code>, <code>withdrawal</code>, or <code>transfer</code> are allowed. There are no typos and no surprises.</p>
</li>
</ul>
<h3 id="heading-the-settlement-account-the-systems-anchor">The Settlement Account: The System's Anchor</h3>
<p>Every real-world ledger needs a way to represent money entering or leaving the system. That's what the settlement account does. Here's how it's seeded in the database:</p>
<pre><code class="language-sql">INSERT INTO accounts (id, name, balance, currency, is_system)
SELECT gen_random_uuid(), 'Settlement Account', 0.0000, 'USD', TRUE
WHERE NOT EXISTS (
    SELECT 1 FROM accounts WHERE is_system = TRUE AND name = 'Settlement Account'
);
</code></pre>
<p>The settlement account represents the "outside world." When a user deposits money, it comes from the settlement account. When they withdraw, it goes back. Using <code>WHERE NOT EXISTS</code> makes this migration idempotent –&nbsp;that is, safe to run multiple times without creating duplicates.</p>
<h2 id="heading-type-safe-sql-with-sqlc-no-more-surprises">Type-Safe SQL with sqlc: No More Surprises</h2>
<p>In financial systems, you can't afford surprises from your database layer. That's why this project uses sqlc, a tool that turns your SQL queries into type-safe Go code at compile time.</p>
<p>With sqlc, you see exactly what SQL runs, catch mistakes before they hit production, and avoid the "magic" (and hidden bugs) of most ORMs. Every query is explicit, every type is checked, and you get the best of both worlds: raw SQL power with Go's safety.</p>
<h3 id="heading-why-numeric-becomes-string-and-not-float64">Why NUMERIC Becomes String (and Not float64)</h3>
<p>Here's a subtle but critical detail from <code>sqlc.yaml</code>:</p>
<pre><code class="language-yaml">overrides:
    - db_type: "pg_catalog.numeric"
      go_type: "string"
    - column: "entries.debit"
      go_type: "string"
    - column: "entries.credit"
      go_type: "string"
    - column: "accounts.balance"
      go_type: "string"
    - db_type: "operation_type"
      go_type: "string"
</code></pre>
<p><strong>Why string, not float64?</strong> Floating point arithmetic is imprecise. <code>0.1 + 0.2</code> in most programming languages does not equal exactly <code>0.3</code>.</p>
<p>For money, you need exact decimal arithmetic. This project uses <code>shopspring/decimal</code> for all calculations and stores amounts as strings, converting at the service layer boundary. The database column itself is <code>NUMERIC(19,4)</code>, which stores exact decimals – no float rounding ever touches your money.</p>
<h3 id="heading-preventing-race-conditions-locking-with-for-update">Preventing Race Conditions: Locking with FOR UPDATE</h3>
<p>One of the most important queries in the system is <code>GetAccountForUpdate</code>:</p>
<pre><code class="language-sql">SELECT * FROM accounts
WHERE id = $1
LIMIT 1
FOR UPDATE; -- locks row for update, prevents TOCTOU races
</code></pre>
<p>This query uses <code>FOR UPDATE</code> to lock the account row during a transaction. Why? Imagine two requests both see a \(500 balance and both try to withdraw \)400. Without locking, both would succeed, and you'd end up with a negative balance. With <code>FOR UPDATE</code>, the second transaction waits until the first finishes, eliminating this classic race condition.</p>
<h3 id="heading-calculating-the-true-balance-always-trust-the-entries">Calculating the True Balance: Always Trust the Entries</h3>
<p>The real source of truth for any account is the sum of its entries, not the denormalized <code>balance</code> column. Here's the reconciliation query:</p>
<pre><code class="language-sql">SELECT CAST(
    (COALESCE(SUM(credit), 0::NUMERIC) - COALESCE(SUM(debit), 0::NUMERIC))
    AS NUMERIC(19,4)
) AS calculated_balance
FROM entries
WHERE account_id = $1;
</code></pre>
<p>This computes the true balance from the ledger itself. It's how you catch bugs, audit the system, and prove that every penny is accounted for. The <code>balance</code> column on accounts is a denormalized cache for fast reads –&nbsp;and this query is the ground truth that validates it.</p>
<h2 id="heading-the-store-layer-transactions-and-automatic-retries">The Store Layer: Transactions and Automatic Retries</h2>
<p>Every financial operation in this system runs inside a transaction –&nbsp;no exceptions. This is enforced by the <code>ExecTx</code> pattern in the store layer:</p>
<pre><code class="language-go">func (store *Store) ExecTx(ctx context.Context, fn func(q *sqlc.Queries) error) error {
    const maxAttempts = 10
    var lastErr error
    for attempt := 0; attempt &lt; maxAttempts; attempt++ {
        lastErr = store.execTxOnce(ctx, fn)
        if lastErr == nil {
            return nil
        }
        if !isSerializationError(lastErr) {
            return lastErr
        }
        if attempt &lt; maxAttempts-1 {
            if waitErr := sleepWithContext(ctx, retryWait(attempt)); waitErr != nil {
                return waitErr
            }
        }
    }
    return fmt.Errorf("transaction failed after %d attempts due to serialization conflicts: %w", maxAttempts, lastErr)
}
</code></pre>
<h3 id="heading-why-serializable-isolation">Why Serializable Isolation?</h3>
<p>The transaction uses PostgreSQL's strictest isolation level: <code>sql.LevelSerializable</code>. This is like running transactions one at a time, eliminating entire classes of concurrency bugs. If two operations would conflict, PostgreSQL aborts one and returns a serialization error (SQLSTATE 40001).</p>
<h3 id="heading-automatic-retries-handling-real-world-concurrency">Automatic Retries: Handling Real-World Concurrency</h3>
<p>When a serialization error occurs, the code automatically retries with exponential backoff:</p>
<pre><code class="language-go">func retryWait(attempt int) time.Duration {
    base := 50 * time.Millisecond
    for i := 0; i &lt; attempt; i++ {
        base *= 2
        if base &gt;= time.Second {
            return time.Second
        }
    }
    return base
}

func sleepWithContext(ctx context.Context, d time.Duration) error {
    select {
    case &lt;-ctx.Done():
        return ctx.Err()
    case &lt;-time.After(d):
        return nil
    }
}
</code></pre>
<p>The backoff starts at 50ms and doubles each attempt, capping at 1 second. Up to 10 attempts are made. If the client disconnects mid-retry, <code>sleepWithContext</code> detects the cancelled context and returns immediately. This means no wasted resources.</p>
<h2 id="heading-the-service-layer-where-business-logic-meets-double-entry">The Service Layer: Where Business Logic Meets Double-Entry</h2>
<p>The service layer is the heart of the system. Its job is to translate business operations – deposits, withdrawals, transfers – into double-entry journal entries that always balance.</p>
<h3 id="heading-deposit-crediting-the-user-debiting-the-settlement">Deposit: Crediting the User, Debiting the Settlement</h3>
<p>Every deposit creates two entries: a credit to the user's account and a matching debit to the settlement account. Both entries share the same transaction ID.</p>
<pre><code class="language-go">func (s *LedgerService) Deposit(ctx context.Context, accountID uuid.UUID, amountStr string) error {
    amount, err := validatePositiveAmount(amountStr)
    if err != nil {
        return err
    }
    return s.store.ExecTx(ctx, func(q *sqlc.Queries) error {
        settlement, err := q.GetSettlementAccountForUpdate(ctx)
        if err != nil {
            return fmt.Errorf("settlement account not found: %w", err)
        }
        account, err := q.GetAccountForUpdate(ctx, accountID)
        if err != nil {
            return fmt.Errorf("account not found: %w", err)
        }
        if account.Currency != settlement.Currency {
            return ErrCurrencyMismatch
        }
        txID := uuid.New()
        // 1. Credit user account
        _, err = q.CreateEntry(ctx, sqlc.CreateEntryParams{
            AccountID:     accountID,
            Debit:         decimal.Zero.StringFixed(4),
            Credit:        amount.StringFixed(4),
            TransactionID: txID,
            OperationType: "deposit",
            Description:   sql.NullString{String: "External deposit", Valid: true},
        })
        if err != nil { return err }
        // 2. Debit settlement (opposing entry)
        _, err = q.CreateEntry(ctx, sqlc.CreateEntryParams{
            AccountID:     settlement.ID,
            Debit:         amount.StringFixed(4),
            Credit:        decimal.Zero.StringFixed(4),
            TransactionID: txID,
            OperationType: "deposit",
            Description:   sql.NullString{String: fmt.Sprintf("Deposit to account %s", accountID), Valid: true},
        })
        if err != nil { return err }
        // 3. Update both balances atomically
        if err = q.UpdateAccountBalance(ctx, sqlc.UpdateAccountBalanceParams{
            Balance: amount.StringFixed(4), ID: accountID,
        }); err != nil { return err }
        return q.UpdateAccountBalance(ctx, sqlc.UpdateAccountBalanceParams{
            Balance: amount.Neg().StringFixed(4), ID: settlement.ID,
        })
    })
}
</code></pre>
<p>Two things are worth highlighting. First, both accounts are locked with <code>GetAccountForUpdate</code> and <code>GetSettlementAccountForUpdate</code> before any entries are written. This prevents any other concurrent transaction from reading a stale balance and acting on it.</p>
<p>Second, <code>amount.Neg()</code> is used to debit the settlement. Its balance goes down, representing real money now held inside the system.</p>
<h3 id="heading-withdraw-debiting-the-user-crediting-the-settlement">Withdraw: Debiting the User, Crediting the Settlement</h3>
<p>Withdrawals are the mirror image of deposits. The key difference is the insufficient funds check, which must happen inside the transaction after the lock is acquired:</p>
<pre><code class="language-go">balanceDec, err := decimal.NewFromString(account.Balance)
if err != nil {
    return errors.New("invalid balance")
}
if balanceDec.LessThan(amount) {
    return ErrInsufficientFunds
}
</code></pre>
<p>Checking balance inside the transaction after <code>FOR UPDATE</code> is critical. Checking it before, outside the transaction, would create a classic time-of-check-to-time-of-use (TOCTOU) race. Two concurrent withdrawals could both pass the check, then both execute, overdrawing the account.</p>
<p>The entries for a $500 withdrawal look like this:</p>
<pre><code class="language-plaintext">| Account              | Debit   | Credit  |
|----------------------|---------|---------|
| User Account         | 500     |         |
| Settlement Account   |         | 500     |
</code></pre>
<p>The settlement is credited because real money is leaving the system, and it's being "returned" to the outside world.</p>
<h3 id="heading-transfer-user-to-user-no-settlement-involved">Transfer: User-to-User, No Settlement Involved</h3>
<p>Transfers move money directly between two user accounts. The settlement account isn't involved. Both accounts are locked, currency is validated, and an insufficient funds check runs before any entries are created:</p>
<pre><code class="language-go">func (s *LedgerService) Transfer(ctx context.Context, fromID, toID uuid.UUID, amountStr string) error {
    amount, err := validatePositiveAmount(amountStr)
    if err != nil { return err }
    if fromID == toID {
        return ErrSameAccountTransfer
    }
    return s.store.ExecTx(ctx, func(q *sqlc.Queries) error {
        fromAcc, err := q.GetAccountForUpdate(ctx, fromID)
        if err != nil { return err }
        toAcc, err := q.GetAccountForUpdate(ctx, toID)
        if err != nil { return err }
        if fromAcc.Currency != toAcc.Currency {
            return ErrCurrencyMismatch
        }
        fromBalance, _ := decimal.NewFromString(fromAcc.Balance)
        if fromBalance.LessThan(amount) {
            return ErrInsufficientFunds
        }
        txID := uuid.New()
        // Debit sender, credit receiver — same transaction ID
        // ... CreateEntry calls + UpdateAccountBalance calls
    })
}
</code></pre>
<p>A $200 transfer creates exactly two entries under the same <code>transaction_id</code>:</p>
<pre><code class="language-plaintext">| Account  | Debit   | Credit  |
|----------|---------|---------|
| Sender   | 200     |         |
| Receiver |         | 200     |
</code></pre>
<h3 id="heading-reconcileaccount-trust-but-verify">ReconcileAccount: Trust, But Verify</h3>
<p>Reconciliation is how you prove the system is correct. The <code>ReconcileAccount</code> function compares the stored <code>balance</code> column against the sum of all credits minus debits in the entries table:</p>
<pre><code class="language-go">func (s *LedgerService) ReconcileAccount(ctx context.Context, accountID uuid.UUID) (bool, error) {
    account, err := s.store.GetAccount(ctx, accountID)
    if err != nil { return false, fmt.Errorf("account not found: %w", err) }

    calculatedStr, err := s.store.GetAccountBalance(ctx, accountID)
    if err != nil { return false, fmt.Errorf("failed to calculate balance: %w", err) }

    calculated, _ := decimal.NewFromString(calculatedStr)
    stored, _ := decimal.NewFromString(account.Balance)

    if !stored.Equal(calculated) {
        log.Error().
            Str("stored_balance", account.Balance).
            Str("calculated", calculated.StringFixed(4)).
            Msg("Balance mismatch detected")
        return false, fmt.Errorf("balance mismatch: stored %s, calculated %s",
            account.Balance, calculated.StringFixed(4))
    }
    return true, nil
}
</code></pre>
<p>If they don't match, something has gone wrong: a bug, a direct database modification, or a race condition that slipped through. In production, this check can run as a background job to catch issues before they become incidents.</p>
<h2 id="heading-the-api-layer-secure-predictable-and-boring-by-design">The API Layer: Secure, Predictable, and Boring (By Design)</h2>
<p>The API layer is where your business logic meets the outside world. Its job is to be secure, predictable, and, if you've done things right, a little bit boring.</p>
<h3 id="heading-jwt-authentication-secrets-matter">JWT Authentication: Secrets Matter</h3>
<p>Authentication is handled with JWTs. The secret used to sign tokens must be at least 32 characters long (as shorter secrets are insecure and can be brute-forced). This is enforced at startup:</p>
<pre><code class="language-go">// internal/api/middleware.go
func InitTokenAuth(secret string) error {
    if secret == "" {
        return errors.New("JWT_SECRET environment variable is required")
    }
    if len(secret) &lt; 32 {
        return errors.New("JWT_SECRET must be at least 32 characters")
    }
    TokenAuth = jwtauth.New("HS256", []byte(secret), nil)
    return nil
}
</code></pre>
<p>The server will refuse to start if the secret is missing or too short. There's no fallback and no default: the system fails loudly rather than running insecurely.</p>
<h3 id="heading-the-handler-pattern-parse-authorize-validate-call-respond">The Handler Pattern: Parse, Authorize, Validate, Call, Respond</h3>
<p>Every handler follows the same recipe: extract JWT claims, parse the account ID, fetch the account and verify ownership, decode the request body, call the service, and respond. Authorization always happens before calling the service layer. The service knows nothing about users, keeping business logic clean and testable.</p>
<pre><code class="language-go">// internal/api/handler.go
func (h *Handler) Register(w http.ResponseWriter, r *http.Request) {
    var input struct {
        Email    string `json:"email"`
        Password string `json:"password"`
    }
    if err := json.NewDecoder(r.Body).Decode(&amp;input); err != nil {
        respondError(w, http.StatusBadRequest, "invalid input")
        return
    }
    // ... hash password, create user, generate JWT ...
}
</code></pre>
<h3 id="heading-amount-normalization-defensive-by-default">Amount Normalization: Defensive by Default</h3>
<p>API clients send amounts in different formats –&nbsp;sometimes as strings, sometimes as numbers. The normalization logic ensures all amounts are handled safely:</p>
<pre><code class="language-go">// internal/api/amount.go
func normalizeAmountInput(value interface{}) (string, error) {
    switch v := value.(type) {
    case string:
        return strings.TrimSpace(v), nil
    case json.Number:
        return strings.TrimSpace(v.String()), nil
    case float64:
        return strconv.FormatFloat(v, 'f', -1, 64), nil
    default:
        return "", errors.New("amount must be a number or string")
    }
}
</code></pre>
<p>The decoder uses <code>dec.UseNumber()</code> so JSON numbers arrive as <code>json.Number</code> rather than <code>float64</code>, preserving full precision. The <code>float64</code> case exists as a safety fallback only.</p>
<h3 id="heading-frontend-deployment-boundary">Frontend Deployment Boundary</h3>
<p>The backend no longer serves static frontend files. The frontend is deployed separately at <code>https://golangbank.app</code> from its own repository: <code>https://github.com/PaulBabatuyi/double-entry-bank</code>.</p>
<h2 id="heading-running-it-locally-your-first-end-to-end-test">Running It Locally: Your First End-to-End Test</h2>
<pre><code class="language-bash">git clone https://github.com/PaulBabatuyi/double-entry-bank-Go.git
cd double-entry-bank-Go
cp .env.example .env
# Edit .env — set JWT_SECRET with: openssl rand -base64 32
make postgres
make migrate-up
make server
</code></pre>
<p>Once the server is running:</p>
<ul>
<li><p><strong>Frontend</strong>: <a href="https://golangbank.app">https://golangbank.app</a></p>
</li>
<li><p><strong>Swagger UI</strong>: <a href="http://localhost:8080/swagger/index.html">http://localhost:8080/swagger/index.html</a> (local dev) or <a href="https://golangbank.app/swagger">https://golangbank.app/swagger</a> (production)</p>
</li>
<li><p><strong>Health check</strong>: <a href="http://localhost:8080/health">http://localhost:8080/health</a></p>
</li>
</ul>
<p>The Swagger UI lets you explore every endpoint, authorize with your JWT token, and test operations directly in the browser.</p>
<h2 id="heading-testing-prove-the-system-works">Testing: Prove the System Works</h2>
<p>Testing financial systems is non-negotiable, and claims about correctness need to be backed by code. This project tests all three layers, each targeting a different kind of failure.</p>
<h3 id="heading-service-layer-core-financial-logic">Service Layer: Core Financial Logic</h3>
<p>The most important tests live in <code>internal/service/ledger_test.go</code>. They run against a real PostgreSQL database – not mocks –&nbsp;because mock-based tests can give a false sense of security. Real database tests catch issues that only appear in production-like environments.</p>
<pre><code class="language-go">func TestDeposit_Success(t *testing.T) {
    ledger := setupTestLedger(t)
    accountID := createTestAccount(t, ledger, "0.00")

    err := ledger.Deposit(context.Background(), accountID, "100.00")
    require.NoError(t, err)

    balance := getAccountBalance(t, ledger, accountID)
    assert.Equal(t, "100.0000", balance)
}

func TestWithdraw_InsufficientFunds(t *testing.T) {
    ledger := setupTestLedger(t)
    accountID := createTestAccount(t, ledger, "50.00")

    err := ledger.Withdraw(context.Background(), accountID, "100.00")
    assert.ErrorIs(t, err, ErrInsufficientFunds)
}
</code></pre>
<p>The <code>createTestAccount</code> helper uses the settlement account's currency automatically, which is important: all accounts must share a currency for transfers to work, and tests that silently use a different currency will fail in confusing ways.</p>
<h3 id="heading-concurrency-test-proving-serializable-isolation-works">Concurrency Test: Proving Serializable Isolation Works</h3>
<p>This is the most important test in the suite:</p>
<pre><code class="language-go">func TestConcurrentDeposits(t *testing.T) {
    ledger := setupTestLedger(t)
    accountID := createTestAccount(t, ledger, "0.00")

    var wg sync.WaitGroup
    wg.Add(2)
    go func() {
        defer wg.Done()
        _ = ledger.Deposit(context.Background(), accountID, "100.00")
    }()
    go func() {
        defer wg.Done()
        _ = ledger.Deposit(context.Background(), accountID, "100.00")
    }()
    wg.Wait()

    balance := getAccountBalance(t, ledger, accountID)
    assert.Equal(t, "200.0000", balance)
}
</code></pre>
<p>Two goroutines deposit simultaneously. The serializable isolation level and retry logic ensure both operations succeed and neither overwrites the other. Without the <code>FOR UPDATE</code> locks and transaction retry logic, this test would fail non-deterministically – which is exactly the kind of bug that's impossible to reproduce in development but devastating in production.</p>
<h3 id="heading-store-layer-transaction-mechanics">Store Layer: Transaction Mechanics</h3>
<p>Tests in <code>internal/db/store_test.go</code> verify the retry infrastructure itself, without needing a database connection:</p>
<pre><code class="language-go">func TestIsSerializationError(t *testing.T) {
    pqErr := &amp;pq.Error{Code: "40001"}
    assert.True(t, isSerializationError(pqErr))
    assert.False(t, isSerializationError(errors.New("some other error")))
}

func TestRetryWait(t *testing.T) {
    assert.Equal(t, 50*time.Millisecond, retryWait(0))
    assert.Equal(t, 100*time.Millisecond, retryWait(1))
    assert.Equal(t, 200*time.Millisecond, retryWait(2))
    assert.Equal(t, time.Second, retryWait(5)) // capped
}

func TestSleepWithContext_Cancel(t *testing.T) {
    ctx, cancel := context.WithCancel(context.Background())
    cancel() // cancel immediately
    err := sleepWithContext(ctx, 50*time.Millisecond)
    assert.Error(t, err) // should return immediately, not wait
}
</code></pre>
<h3 id="heading-api-layer-authentication-and-input-handling">API Layer: Authentication and Input Handling</h3>
<p>Handler tests in <code>internal/api/handler_test.go</code> verify that the HTTP layer behaves correctly at its boundaries:</p>
<pre><code class="language-go">func TestRegisterHandler_BadRequest(t *testing.T) {
    h := setupTestHandler(t)
    req := httptest.NewRequest(http.MethodPost, "/register", nil)
    rw := httptest.NewRecorder()
    h.Register(rw, req)
    assert.Equal(t, http.StatusBadRequest, rw.Code)
}

func TestRegisterHandler_Success(t *testing.T) {
    h := setupTestHandler(t)
    _ = InitTokenAuth("fV7sliKV3qn657I60wEFtw/Auk/0bNU9zdp30wFzfDg=")

    email := "testuser_" + uuid.New().String() + "@example.com"
    body, _ := json.Marshal(map[string]string{"email": email, "password": "testpassword123"})

    req := httptest.NewRequest(http.MethodPost, "/register", bytes.NewReader(body))
    rw := httptest.NewRecorder()
    h.Register(rw, req)
    assert.Equal(t, http.StatusCreated, rw.Code)
}
</code></pre>
<p>Using <code>uuid.New().String()</code> in the email ensures each test run creates a unique user, preventing conflicts on repeated runs against the same database.</p>
<p>Middleware tests verify the security boundary itself:</p>
<pre><code class="language-go">func TestInitTokenAuthFromEnv_MissingSecret(t *testing.T) {
    os.Unsetenv("JWT_SECRET")
    err := InitTokenAuthFromEnv()
    assert.Error(t, err) // must fail without a secret
}
</code></pre>
<h3 id="heading-running-the-tests">Running the Tests</h3>
<pre><code class="language-bash"># Start the database
make postgres

# Run all tests with race detection
make test

# Run with coverage report
make coverage

# Run tests the same way CI does (includes migrations)
make ci-test
</code></pre>
<p>The <code>-race</code> flag is non-negotiable for financial code. It instruments the binary to detect data races at runtime –&nbsp;something static analysis can't catch. If a race exists, the race detector will find it.</p>
<h2 id="heading-deployment-engineering-decisions-that-matter-in-production">Deployment: Engineering Decisions That Matter in Production</h2>
<p>The deployment setup for this project reflects several engineering decisions worth understanding, regardless of what platform you deploy to.</p>
<h3 id="heading-migrations-on-container-start">Migrations on Container Start</h3>
<p>The Docker entrypoint runs <code>golang-migrate up</code> before starting the Go binary:</p>
<pre><code class="language-sh"># docker-entrypoint
migrate -path /app/postgres/migrations -database "$migrate_db_url" up
exec /usr/local/bin/ledger
</code></pre>
<p>Running migrations at startup rather than as a separate CI step has trade-offs. The upside is simplicity: the container is always self-consistent when it starts. The downside is that each deployment takes slightly longer. For a solo project or small team, this is the right call. At scale you'd separate migrations from deployment.</p>
<h3 id="heading-startup-retry-logic">Startup Retry Logic</h3>
<p>The entrypoint retries migrations up to 12 times with a 5-second sleep between attempts:</p>
<pre><code class="language-sh">max_attempts=12
attempt=1
while [ "\(attempt" -le "\)max_attempts" ]; do
    migration_output=$(migrate ... up 2&gt;&amp;1)
    # If "connection refused" or "timeout", keep retrying
    # If any other error, fail immediately
    attempt=$((attempt + 1))
done
</code></pre>
<p>The critical distinction is which errors trigger a retry. Network-transient errors (connection refused, timeout) are retried. Everything else&nbsp;–&nbsp;a bad migration SQL, a missing tabl&nbsp;–&nbsp;fails immediately. This avoids waiting the full 60 seconds when a deployment has a real problem.</p>
<h3 id="heading-db-url-fallback-chain">DB URL Fallback Chain</h3>
<p>In cloud environments, the internal database URL is often a different variable than what you configure locally. The <code>resolveDBURL</code> function handles this transparently:</p>
<pre><code class="language-go">func resolveDBURL() string {
    connStr := strings.TrimSpace(os.Getenv("DB_URL"))
    fallbackVars := []string{"INTERNAL_DATABASE_URL", "RENDER_DATABASE_URL", "DATABASE_URL"}
    // Falls back through the chain if DB_URL is empty or resolves to localhost
    ...
}
</code></pre>
<p>This pattern means local developers set <code>DB_URL</code> in <code>.env</code> and don't need to think about it, while the deployed container automatically uses the internal database connection without any manual wiring.</p>
<h3 id="heading-http-server-timeouts">HTTP Server Timeouts</h3>
<p>The server is configured with explicit timeouts:</p>
<pre><code class="language-go">srv := &amp;http.Server{
    Addr:              ":" + port,
    Handler:           r,
    ReadTimeout:       15 * time.Second,
    WriteTimeout:      15 * time.Second,
    IdleTimeout:       60 * time.Second,
    ReadHeaderTimeout: 5 * time.Second,
}
</code></pre>
<p>Without timeouts, a slow or malicious client can hold connections open indefinitely, eventually exhausting the server's resources. <code>ReadHeaderTimeout</code> is particularly important: it limits how long the server waits for the HTTP headers before closing the connection, protecting against Slowloris-style attacks.</p>
<h2 id="heading-conclusion-building-for-the-real-world">Conclusion: Building for the Real World</h2>
<p>You've just walked through the core patterns that power real fintech systems:</p>
<ul>
<li><p>Double-entry ledger with database-enforced constraints</p>
</li>
<li><p>Settlement account for tracking external cash flows</p>
</li>
<li><p>Serializable transactions with exponential backoff retry</p>
</li>
<li><p>Reconciliation endpoint for verifying correctness</p>
</li>
<li><p>Type-safe queries with sqlc</p>
</li>
<li><p>Row-level locking to prevent race conditions</p>
</li>
<li><p>Tests that prove correctness under concurrency</p>
</li>
</ul>
<p>These aren't just Go patterns. They're the same principles used at companies like Monzo, Stripe, and Nubank. The implementation details differ, but the underlying ideas are the same: every dollar is accounted for, every operation is atomic, and the system can always explain where every penny went.</p>
<p>What's next? Three concrete next steps:</p>
<ol>
<li><p><strong>Add idempotency keys</strong> to prevent duplicate transactions on retries. If a client retries a deposit because of a network timeout, you need to detect and reject the duplicate.</p>
</li>
<li><p><strong>Add Prometheus metrics</strong> for transaction latency and failure rates. You want to know when your p99 latency spikes before your users do.</p>
</li>
<li><p><strong>Add a scheduled reconciliation job</strong> that runs <code>ReconcileAccount</code> for every account on a schedule and alerts on mismatches. Catch bugs automatically, before they become customer complaints.</p>
</li>
</ol>
<p>The developer who stores balance as a single number and updates it directly will eventually have an incident. The developer who builds a ledger has an audit trail, a reconciliation tool, and a system that can explain every penny.</p>
<p>That's the real reason fintech engineers build this way: not because it's more complex, but because it's more honest about what money actually is.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Get Started Coding in Golang ]]>
                </title>
                <description>
                    <![CDATA[ In the world of Software Engineering, there are plenty of programming languages to learn. And there are both low-level and high-level options. I’ve tried my hand at a few of them, and the one language ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-get-started-coding-in-golang/</link>
                <guid isPermaLink="false">69b2e2251be92d8f177786a8</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Programming Blogs ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Njong Emy ]]>
                </dc:creator>
                <pubDate>Thu, 12 Mar 2026 15:56:21 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/a429db13-593a-4afe-be02-2c94ae1f246f.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In the world of Software Engineering, there are plenty of programming languages to learn. And there are both low-level and high-level options.</p>
<p>I’ve tried my hand at a few of them, and the one language that feels like it touches both worlds is Golang – popularly known as Go. Although Go is a high-level language, it has pretty amazing performance that brings it close to the low-level edge.</p>
<p>Go is a fast, statically typed programming language. Types are declared at compile time, and you don’t need to run the code before catching errors. It’s also a general-purpose language that you can use for backend, cloud, servers, and more.</p>
<p>Go has built-in testing support, so you don’t need to install extra testing libraries. Go also has some features of object-oriented languages, but in its own way. It mirrors some OOP concepts, but also uses concepts like interfaces, structs, and so on.</p>
<p>In this tutorial, we're going to be looking at a few basic concepts you need to know when getting started in any programming language. A few of them are generic to many programming languages, but some of them are concepts specific to the Go programming language. We'll take a look at:</p>
<ul>
<li><p>Variables</p>
</li>
<li><p>Formatting strings</p>
</li>
<li><p>Arrays and slices</p>
</li>
<li><p>Loops</p>
</li>
<li><p>Functions</p>
</li>
<li><p>Maps</p>
</li>
<li><p>Structs, and</p>
</li>
<li><p>Package scope</p>
</li>
</ul>
<p>By the end of the article, you'll be grounded in the pure basics of Go, and we'll run a few examples to see how these work on the command line.</p>
<h3 id="heading-what-well-cover">What we'll cover:</h3>
<ol>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-how-to-install-go">How to Install Go</a></p>
</li>
<li><p><a href="#heading-how-to-write-your-first-go-program">How to Write Your First Go Program</a></p>
</li>
<li><p><a href="#heading-how-to-work-with-variables-and-numbers-in-go">How to Work with Variables and Numbers in Go</a></p>
</li>
<li><p><a href="#heading-how-to-format-strings-in-go">How to Format Strings in Go</a></p>
</li>
<li><p><a href="#heading-how-to-work-with-arrays-and-slices-in-go">How to Work with Arrays and Slices in Go</a></p>
</li>
<li><p><a href="#heading-how-to-work-with-loops-in-go">How to Work with Loops in Go</a></p>
</li>
<li><p><a href="#heading-how-to-work-with-functions-in-go">How to Work with Functions in Go</a></p>
</li>
<li><p><a href="#heading-how-to-work-with-maps-in-go">How to Work with Maps in Go</a></p>
</li>
<li><p><a href="#heading-how-to-work-with-structs-in-go">How to Work with Structs in Go</a></p>
</li>
<li><p><a href="#heading-package-scope-in-go">Package Scope in Go</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this tutorial, it’ll be helpful if you know the basics of any programming language, such as variables, data types, and data structures. I’ll assume that you’ve worked with at least one programming language before.</p>
<h2 id="heading-how-to-install-go">How to Install Go</h2>
<p>To install Go, head to <a href="http://golang.org/">golang.org</a>. Depending on what OS you are running, the documentation provides different installation options.</p>
<p>In my case, I’m using <strong>WSL</strong> (Windows Subsystem for Linux) with Ubuntu, so I’ll install Go inside that environment.</p>
<p>First, update your package list:</p>
<pre><code class="language-plaintext">sudo apt update
sudo apt upgrade -y
</code></pre>
<p>Next, install a tool to download files from the internet. We’ll use <strong>wget</strong>:</p>
<pre><code class="language-plaintext">sudo apt install -y wget
</code></pre>
<p>Now download the Go binary package:</p>
<pre><code class="language-plaintext">wget https://dl.google.com/go/go1.24.2.linux-amd64.tar.gz
</code></pre>
<p>Extract the archive to <code>/usr/local</code>:</p>
<pre><code class="language-plaintext">sudo tar -C /usr/local -xzf go1.24.2.linux-amd64.tar.gz
</code></pre>
<p>After installing Go, add the Go binary directory to your <strong>PATH</strong> so the <code>go</code> command is available in the terminal:</p>
<pre><code class="language-ruby">export PATH=$PATH:/usr/local/go/bin
</code></pre>
<p>You can make this change permanent by adding the line above to your <code>~/.bashrc</code> or <code>~/.profile</code>.</p>
<p>To confirm Go was installed successfully, run:</p>
<pre><code class="language-go">go version
</code></pre>
<p>You should see the installed Go version printed in the terminal.</p>
<p>If you use VS Code, you can also install the Go extension for syntax highlighting.</p>
<h2 id="heading-how-to-write-your-first-go-program">How to Write Your First Go Program</h2>
<p>Before we get into writing our first program, we need to establish how Go puts its code together. In Go, every file or code is part of a package.</p>
<p>In this example, we’ll create a file called <code>main.go</code>. The name isn’t special to Go, but it’s a common convention for the file that contains the program’s entry point.</p>
<pre><code class="language-go">package main

import "fmt"

func main() {
	fmt.Println("Hello, ninjas")
}
</code></pre>
<p>This is the most important file in our project. We'll begin by making the code we’ll write in this file part of a package we call <code>main</code>.</p>
<p>The <code>fmt</code> import is a package from the Go standard library. <code>fmt</code> is for formatting strings and outputting to the console. Notice that the <code>Println</code> function starts with a capital letter, because within the <code>fmt</code> package, the method is exported. Variables or methods that are exported in Go should begin with capital letters. The <code>Println</code> prints a line to the console.</p>
<p>The <code>main</code> function serves as the entry point of a Go program. A compiled Go program must contain <strong>exactly one</strong> <code>main</code> <strong>function inside a</strong> <code>package main</code>. (A larger codebase can still contain multiple programs, each in its own directory with its own <code>package main</code>.)</p>
<p>So when you run a Go program, the Go toolchain builds all files in the same package together and then starts execution from the <code>main()</code> function. The filename <code>main.go</code> does not determine execution order – it’s simply a common naming convention.</p>
<p>Unlike other custom packages, which are used to bundle application logic into libraries or reusable code, the <code>main</code> package is used to specify that a program is a standalone executable.</p>
<p>To run the code, you can simply type <code>go run main.go</code>, specifying the Go file that we want to execute.</p>
<pre><code class="language-go">Hello, ninjas
</code></pre>
<h2 id="heading-how-to-work-with-variables-and-numbers-in-go">How to Work with Variables and Numbers in Go</h2>
<h3 id="heading-how-to-declare-variables">How to Declare Variables</h3>
<p>A variable is just a store for data. Go does not allow unused local variables. If you declare a variable inside a function and never use it, the compiler will raise an error. This helps prevent cluttered code and unused definitions.</p>
<p>Let’s see how to declare variables in Go by declaring a string</p>
<pre><code class="language-go">package main

import "fmt"

func main() {

	// strings
	var nameOne string = "emy"

	fmt.Println(nameOne)
}
</code></pre>
<p>Because Go is a statically typed language, every variable must have a type at compile time. Here, we have a variable <code>nameOne</code>, it’s type, and then an initial value assigned to it.</p>
<p>But what if we don't want to state the variable type ourselves? Fortunately, Go lets us define variables without a type stated at compile time</p>
<pre><code class="language-go">// strings
var nameOne string = "emy"
var nameTwo = "blessing"
var nameThree string
</code></pre>
<p>For <code>nameTwo</code>, we don’t need to explicitly state the type because Go directly infers it. If you hover above the variable, you can see that Go already tells you the type. The third variable, <code>nameThree</code>, has no value assigned to it just yet. We’ve only just declared it.</p>
<p>If you log the output to the console, you can see <code>nameOne</code> and <code>nameTwo</code> displayed, but you can’t see <code>nameThree</code>. It’s also logged, you just can’t see it because it has no value.</p>
<pre><code class="language-go">emy blessing 
</code></pre>
<p>There’s an even shorter and simpler way to declare variables in Go:</p>
<pre><code class="language-go">nameFour := "peaches"
</code></pre>
<p>Here, Go also infers the variable type based on the value assigned to it. Notice how we also didn’t use the <code>var</code> keyword. You can use this method inside any function, not just in <code>main</code>. Just keep in mind that you can't use this method of declaring variables when reassigning or updating a variable, declaring constants, or declaring safe types.</p>
<h3 id="heading-how-to-declare-integers">How to Declare Integers</h3>
<p>When it comes to declaring integers, we pretty much use the same technique as above:</p>
<pre><code class="language-go">var ageOne int = 20
var ageTwo = 30
ageThree := 40

fmt.Println(ageOne, ageTwo, ageThree)
</code></pre>
<p>When declaring integers, we can specify the amount of memory or the bit size we want an integer to have. We can declare the integer as <code>int8</code>, <code>int16</code>, or <code>int64</code>.</p>
<p>Each of these memory sizes can hold a specific range of numbers. <code>int8</code>, for example, can only hold integers between -128 and 127. Anything bigger than that would throw an error:</p>
<pre><code class="language-go">// bits and memory
	var numOne int8 = 25
	var numTwo int8 = -128

	var numThree int8 = 129 // will throw an error
</code></pre>
<p>Another type of integer in Go is the unsigned integer, declared with <code>uint</code>. You’ll use this to declare only positive integers – you can’t use it to store negative values.</p>
<p><code>uint</code>, just like the <code>int</code> type, can also be associated with bit sizes. You could have a <code>uint8</code>, <code>uint16</code>, and so on. But the ranges for these are different. You can check the complete list of bit sizes for the integer type and their ranges on this <a href="https://go.dev/ref/spec#Numeric_types">Go page</a>.</p>
<p><code>int</code> is specifically used to declare whole integer numbers. To declare decimal numbers, you can use a float. The concept is the same with <code>int</code>, where you can have floats with different bit sizes. But you can only have <code>float32</code> and <code>float64</code>, where the latter can store bigger numbers than the former.</p>
<h2 id="heading-how-to-format-strings-in-go">How to Format Strings in Go</h2>
<h3 id="heading-printing-strings-to-the-console">Printing Strings to the Console</h3>
<p>We came across the <code>fmt</code> package at the beginning of the article. Let’s go through some of the methods this package exposes.</p>
<p>First, we have the <code>Print()</code> method which logs some output the console. This method is used to log simple strings to the command line when we just want to see the output of something, and don’t really care about the readability.</p>
<p>If we have two simple strings as shown below;</p>
<pre><code class="language-go">// Print
fmt.Print("Hello, ")
fmt.Print("world!")

// Output: Hello, world!
</code></pre>
<p>You can see that the code above is not on separate lines, and this is the downside of the <code>Print</code> function. If we had multiple things to log to the command line, this function would just lump them together and readability could suffer.</p>
<p>But we can enforce a new line with <code>Print()</code> by using the escape character <code>\\n</code> .</p>
<pre><code class="language-go">fmt.Print("hello! \\n")
fmt.Print("new line \\n")

/*
	hello! 
	new line 
*/
</code></pre>
<p>Using escape characters throughout our code can get tiring very quickly. Luckily for us, Go has a function that helps us achieve the perfect separate line formatting we are looking for. We don’t need to use escape characters when we use <code>Println</code> .</p>
<pre><code class="language-go">fmt.Println("Hello, friends.")
fmt.Println("How are you?")

/*
	Hello, friends.
	How are you?
*/
</code></pre>
<h3 id="heading-format-specifiers">Format Specifiers</h3>
<p>Sometimes, you may want to log a string to the console, but you may also want to have variables within that string. This is called a formatted string, and we can achieve this with the help of format specifiers. These format specifiers are reserved characters that Go uses at runtime to position variables within strings.</p>
<p>Let’s use a concrete example to see the concept better,</p>
<pre><code class="language-go">name := "Emy"
age := 27

fmt.Printf("my age is %v and my name is %v", age, name)

// Output: my age is Emy and my name is 27
</code></pre>
<p>The format specifier <code>%v</code> is the default format specifier that stands for variable. We can see that in the position of the format specifiers, Go has placed the values of the arguments we passed to the <code>Printf</code> (<code>age</code> and <code>name</code>). It's important to note that the order in which we pass these arguments is important.</p>
<p>As we just saw above, the output of this code:</p>
<pre><code class="language-go">name := "Emy"
age := 27

fmt.Printf("my age is %v and my name is %v", name, age)
</code></pre>
<p>Would be <code>my age is Emy and my name is 27</code>. The order in which we pass the arguments is the same way in which the compiler will replace the format specifier within our string.</p>
<p>Something new you might also notice is that we didn't use <code>Print</code> or <code>Println</code> in this code like we did before. When working with formatted directives, Go puts the functions <code>Printf</code>, <code>Sprintf</code>, and <code>Appendf</code> at our disposal.</p>
<ul>
<li><p><code>Printf</code> directs the output to the console, as we’ve seen.</p>
</li>
<li><p>With <code>Sprintf</code>, we don't direct the output to the command line, but we can store it in a variable and then use that variable somewhere else in our code.</p>
</li>
<li><p>It gets a little more complex when working with <code>Appendf</code>, which formats according to a format specifier and appends the result to a byte slice (bytes are a bit out of the scope of this article, so we won’t dwell on them).</p>
</li>
</ul>
<p>Besides the <code>%v</code> format specifier, we can also use <code>%q</code> if we want the embedded variable to have quotes around it.</p>
<pre><code class="language-go">// strings
	name := "Emy"

	fmt.Printf("my name is %q", name)

// output: my name is "Emy"
</code></pre>
<p>This will work for the <code>name</code> variable, but not really for the <code>age</code> because it’s an integer.</p>
<pre><code class="language-go">name := "Emy"
age := 27

fmt.Printf("my age is %q and my name is %q", name, age)

//Output: my age is '\\\\x1b' and my name is "Emy"
</code></pre>
<p>We also have the <code>%T</code> specifier, which is used to output the type of a variable.</p>
<p>If we wanted to get the type of <code>age</code>:</p>
<pre><code class="language-go">fmt.Printf("This is a variable of type %T", age)
</code></pre>
<p>The output of this would be:</p>
<pre><code class="language-go">This is a variable of type int
</code></pre>
<p>You can check out other format specifiers on the official <a href="https://pkg.go.dev/fmt">fmt package page</a>.</p>
<h2 id="heading-how-to-work-with-arrays-and-slices-in-go">How to Work with Arrays and Slices in Go</h2>
<h3 id="heading-arrays-in-go">Arrays in Go</h3>
<p>Arrays in Go are a little bit of a mouthful. Let’s take a look at how to define an array:</p>
<pre><code class="language-go">var ages = [3]int{20, 25, 30}
</code></pre>
<p>In this code, we have the variable name on the left side as is typical. On the right side, we have <code>[3]</code> to specify the size of the array, the type as <code>int</code> , and the values for the array inside the squiggly braces.</p>
<p>Once we declare an array, we can never change its size. This is kind of a bummer if you ask me, because during coding, you don’t always know the size of an array when you declare it (more on this below when we talk about slices).</p>
<p>Arrays in Go also can’t contain multiple types. The array we declared above can’t also contain a string, for example.</p>
<p>If we log the array to the console alongside its length with <code>fmt.Println(ages, len(ages))</code>, on the console we get:</p>
<pre><code class="language-go">[20 25 30] 3
</code></pre>
<h3 id="heading-slices-in-go">Slices in Go</h3>
<p>If you need to define an array where you don’t know or don’t want to specify the size during declaration, you can use a slice. Slices are abstractions of arrays, and are more flexible than arrays because they have dynamic sizing.</p>
<pre><code class="language-go">var scores = []int{100, 50, 60} // we don't specify the size
scores[2] = 25 // to update a value
scores = append(scores, 50) // returns a new scores slice with 50 appended to it  (you cannot do this with arrays)
fmt.Println(scores, len(scores)) // [100 50 60 50] 4
</code></pre>
<p>An important part of working with arrays, slices, or any data structures that store data is knowing how to get the elements we want. We may only want to grab data that belongs to a certain range, or data at certain positions, based on certain conditions, and so on.</p>
<p>When it comes to dealing with ranges, say you want to output the slice elements from position 1 (that is, <a href="https://en.wikipedia.org/wiki/Zero-based_numbering">index 0</a>, the first element) right up to the third position (index 2).</p>
<pre><code class="language-go">rangeOne := scores[0:3] // [100 50 60]
</code></pre>
<p>The range <code>scores[0:3]</code> indicates that the code should list the scores from index 0 up to index 3 minus 1. So it doesn’t include the element at index 3.</p>
<p>If you wanted to log from index 2 in the slice right up to the end, for example, you’d write <code>scores[2:]</code> . Similarly, you could log from the beginning of the slice up to and excluding some position (in this case, index 3) like so, <code>scores[:3]</code> .</p>
<h2 id="heading-how-to-work-with-loops-in-go">How to Work with Loops in Go</h2>
<h3 id="heading-how-to-iterate-loops">How to Iterate Loops</h3>
<p>Loops in Go are similar to loops in other programming languages. The difference is that Go focuses on the <code>for</code> loop and doesn’t really dwell on <code>while</code>, <code>do-while</code>, or <code>for-each</code>.</p>
<pre><code class="language-go">x := 0
for x &lt; 5 {
	fmt.Println("the value of x is", x)
	x++
}
</code></pre>
<p>The loop above is straightforward and just prints the value of <code>x</code> from 0 to 4.</p>
<p>To create a loop that uses an iterator, you can write this:</p>
<pre><code class="language-go">for i := 0; i &lt; 5; i++ {
	fmt.Println("value of i is", i)
}
    /*
		value of i is 0
		value of i is 1
		value of i is 2
		value of i is 3
		value of i is 4
	*/
</code></pre>
<p>It does pretty much the same thing as the loop above it, but we declared the iterator, it’s range, and we iterate over it in the same expression.</p>
<p>What if you wanted to iterate or loop over a slice, for example? We can also use an iterator to achieve this as we have above.</p>
<pre><code class="language-go">names := []string{"emy", "ble", "winkii"}

for i := 0; i &lt; len(names); i++ {
		fmt.Println(names[i])
}
</code></pre>
<h3 id="heading-how-to-use-the-range-keyword">How to Use the <code>range</code> Keyword</h3>
<p>Another interesting keyword related to iterations is <code>range</code>. Using the <code>range</code> keyword, you can use loops to perform actions on elements in a slice. <code>range</code> provides the index and value of the element in a slice, and allows you to access those within the loop.</p>
<pre><code class="language-go">for index, value := range names {
		fmt.Printf("the position of %v is %v \\\\n", value, index)
	}
</code></pre>
<p>What if you didn’t want to use the index and only the value? Well, <code>range</code> requires that you define an index and a value. So if you rewrite your loop like this:</p>
<pre><code class="language-go">for index, value := range names {
		fmt.Printf("the value is %v \\\\n", value)
}
</code></pre>
<p>Go is going to throw an error because you’re declaring the <code>index</code> but not using it.</p>
<p>Luckily, there’s a way to bypass this, and Go does it neatly using the blank identifier, <code>_</code>. We use this identifier when a method or function expects that you define a return value that you don't need.</p>
<pre><code class="language-go">for _, value := range names {
		fmt.Printf("the value is %v \\\\n", value)
}
</code></pre>
<p>You could apply the same strategy if you wanted only the index, but not the value.</p>
<pre><code class="language-go">for index, _ := range names {
		fmt.Printf("the index is %v \\\\n", index)
}
</code></pre>
<h2 id="heading-how-to-work-with-functions-in-go">How to Work with Functions in Go</h2>
<p>A function is a reusable block of code. Functions in Go are mostly created outside the <code>main</code> function. This way, other files can access and use them.</p>
<pre><code class="language-go">package main

import (
	"fmt"
)

func sayGreeting(n string){
	fmt.Printf("Good morning")
}

func main() {
	sayGreeting("emy")
}
</code></pre>
<p>Go also allows you to pass functions as parameters to other functions.</p>
<pre><code class="language-go">func cycleNames(n []string, f func(string)) {
	for _, value := range n {
		f(value)
	}
}
</code></pre>
<p>The function above takes a slice and a function as parameters. The function passed as a parameter in turn takes a string. You could pass a slice of names alongside the greeting function, so that for each name in the slice, you run the greeting function to print a greeting for that name.</p>
<pre><code class="language-go">func main(){
	cycleNames([]string{"emy", "pearl"}, sayGreeting)
}
</code></pre>
<p>When passing the <code>sayGreeting</code> function as a parameter, you don’t invoke it immediately because that’s done within the <code>cycleNames</code> function already. You only pass its reference.</p>
<h3 id="heading-functions-with-return-values">Functions with <code>return</code> Values</h3>
<p>Functions may also need to have a return value. So, how does Go handle that? Let’s see a small example:</p>
<pre><code class="language-go">package main

import "fmt"

func sayHello(name string) string {
	fmt.Printf("Hello %v", name)
	return name
}

func main() {
	sayHello("Emy")
}

// Output: Hello Emy
</code></pre>
<p>Just like with variables, we must specify the data type for every function parameter, and also specify the data type of the expected return value. In the example above, our function <code>sayHello</code> takes a string and returns a string.</p>
<p>Functions can also have multiple return values, as we see in the example below:</p>
<pre><code class="language-jsx">package main

import "fmt"

func sayHello(name string, age int) (string, int) {
	fmt.Printf("Hello %v, you are %v years old", name, age)
	return name, age
}

func main() {
	sayHello("Emy", 27)
}
</code></pre>
<p>Just like the previous example, we have to specify data types for our function parameters and return values.</p>
<h2 id="heading-how-to-work-with-maps-in-go">How to Work with Maps in Go</h2>
<p>A map in Go is a built-in, unordered collection of unique key-value pairs, similar to dictionaries in Python or hash tables in other languages. Maps provide fast lookups, insertions, and deletions.</p>
<p>With maps, all the keys must be of the same data type, and so must the values. If one key is a string, then all the other keys must also be strings. The same concept applies for values.</p>
<pre><code class="language-go">scores := map[string]float64{
		"maths":           20,
		"english":         15,
		"french":          14,
		"spanish":         12,
	}

	fmt.Println(scores)
	fmt.Println(scores["maths"])

	/*
	map[english:15 french:14 maths:20 spanish:12]
	20
	*/
</code></pre>
<p>You can also loop through maps to get their keys and corresponding values:</p>
<pre><code class="language-go">// loops maps
	for key, value := range scores {
		fmt.Println(key, "-", value)
	}

	/*
	french - 14
	spanish - 12
	maths - 20
	english - 15
	*/
</code></pre>
<p>When it comes to mutating maps, it's important to note that maps are reference types. Reference types are types whose variables don’t store the actual data, but rather an internal pointer to the actual data. This means that if the same data is assigned to multiple variables, when one instance gets modified, the original gets modified as well.</p>
<p>Let’s understand this with an example;</p>
<pre><code class="language-go">package main

import "fmt"

func main() {
	scores := map[string]float64{
		"maths":   20,
		"english": 15,
		"french":  14,
		"spanish": 12,
	}

	scores2 := scores

	scores2["maths"] = 15
	fmt.Println(scores)
}

// Output: map[english:15 french:14 maths:15 spanish:12]
</code></pre>
<p>If you check out the output, you can see that we modified the math score for <code>scores2</code>, but that modification also affected the original map, <code>scores</code> .</p>
<h2 id="heading-how-to-work-with-structs-in-go">How to Work with Structs in Go</h2>
<p>A big downside of using maps is that we can only store one data type for keys and one data type for values. This feels restrictive. We need a data structure that lets us store a collection of data with different data types. This is where structs (or structures) come in.</p>
<p>Let’s look at an example:</p>
<pre><code class="language-go">type Book struct {
	ID     uint
	Title  string
	Author string
	Year   int
	UserID uint
}
</code></pre>
<p>Here we have a Book struct. Structs are defined by specifying the key of the data you want the struct to carry, and the data type associated with that key.</p>
<pre><code class="language-go">package main

import "fmt"

type Book struct {
	ID     uint
	Title  string
	Author string
	Year   int
    UserID uint
}

func main() {

	var book1 Book

	book1 = Book{1, "Jane Eyre", "Jane Austen", 1990, 6}

	fmt.Println(book1)
}
</code></pre>
<p>We initialised the <code>Book</code> struct by providing it with the data we stated in its definition. If you check the output of this (and ignore that Jane Austen didn’t write Jane Eyre, of course):</p>
<pre><code class="language-go">{1 Jane Eyre Jane Austen 1990 6}
</code></pre>
<p>If you look closely, structs resemble classes from the OOP languages. They define properties (just as with methods, properties starting with a capital letter are public and can be exported), and these properties can be used within methods or functions. The difference comes at the level of inheritance, because structs, unlike classes, support only composition and not inheritance.</p>
<p>We can also individually modify the properties of a struct using dot notation, like so:</p>
<pre><code class="language-go">	var book1 Book

	book1 = Book{1, "Jane Eyre", "Jane Austen", 1990, 6}

	book1.Title = "Things Fall Apart"
	book1.Author = "Chinua Achebe"

	fmt.Println(book1)
</code></pre>
<p>If you check the output again, you can see that what you initialised the struct to contain has been modified at the level of the <code>Title</code> and <code>Author</code>.</p>
<pre><code class="language-go">{1 Things Fall Apart Chinua Achebe 1990 6}
</code></pre>
<p>When working with real systems, structs come in very handy when creating models or designing the structure of data you want to store in your database.</p>
<h2 id="heading-package-scope-in-go">Package Scope in Go</h2>
<h3 id="heading-importing-functions-within-the-same-package">Importing Functions Within the Same Package</h3>
<p>When coding real applications, it's uncommon to write all our application files in one file. This can get messy very quickly. Normally, we would write a function in one file and then call it from another file.</p>
<p>In this case, you can declare variables and functions in other files and still use them in your entry point file. Say you create another <code>greetings.go</code> file and declared some variable and method in it like this:</p>
<pre><code class="language-go">// greetings.go
package main

import "fmt"

var points = []int{20, 90, 100, 45, 70}

func sayHello(n string) {
	fmt.Println("Hello", n)
}
</code></pre>
<p>Notice that there is no <code>func main()</code> in this file (remember that we can have only one throughout our application).</p>
<p>Now, in your <code>main.go</code> file, you can reference the <code>sayHello()</code> function and <code>points</code> variable you created.</p>
<pre><code class="language-go">// main.go
func main() {
	sayHello("emy")

	for _, v := range points {
		fmt.Println(v)
	}
}
</code></pre>
<p>To run this, you’ll need to have both files running at the same time. That is:</p>
<pre><code class="language-bash">go run main.go greetings.go
</code></pre>
<p>You can see that the output is the result of running the <code>sayHello()</code> function and looping over the <code>points</code> slice.</p>
<p>You could also define functions and variables in the <code>main.go</code> file and pass them to the <code>greetings.go</code> file. Remember that all these functions and variables passed between files must be declared outside the <code>main()</code> function.</p>
<h3 id="heading-importing-functions-across-different-packages">Importing Functions Across Different Packages</h3>
<p>What if your application has, say, 100 files? Will you run all those files simultaneously? No, you won't. Since our Go code can be organised in packages, we can import a function from one package within another package. Let’s rewrite our code above, but using two different packages.</p>
<p>In our <code>greetings.go</code> file, we now have our code under a new package, <code>greetings</code> , and our <code>SayHello</code> function begins with a capital letter so that other files can import and use it.</p>
<pre><code class="language-jsx">// greetings.go
package greetings

import "fmt"

var Points = []int{20, 90, 100, 45, 70}

func SayHello(n string) {
	fmt.Println("Hello", n)
}
</code></pre>
<p>In our <code>main.go</code> file,</p>
<pre><code class="language-jsx">// main.go
package main

import (
	"fmt"
	"greetings"
)

func main() {
	greetings.SayHello("emy")

	for _, v := range points {
		fmt.Println(v)
	}
}
</code></pre>
<p>As you can see, we’ve imported the <code>greetings</code> package we declared in another file, and we can access the <code>SayHello()</code> method from it with dot notation.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you’ve learned some key Go concepts. You’re now familiar with how variables, strings, arrays and slices, loops, functions, maps, structs, and package scope works in Go.</p>
<p>At this point, you should try to take that further by building something a little bigger so you can see just how powerful and amazing Golang is.</p>
<p>The official <a href="https://go.dev/tour/welcome/1">Tour of Go</a> website is also an amazing resource to reference if you want to explore these concepts a little deeper.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Find the Top-K Items: Heap and Streaming Approaches in Go ]]>
                </title>
                <description>
                    <![CDATA[ Finding the top K items in a dataset pops up everywhere: from highlighting the hottest posts in a social feed, to detecting the largest transactions in a financial system, or spotting the heaviest use ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-find-the-top-k-items-heap-and-streaming-approaches-in-go/</link>
                <guid isPermaLink="false">69b0a865abc0d95001af32a2</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gabor Koos ]]>
                </dc:creator>
                <pubDate>Tue, 10 Mar 2026 23:25:25 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/4cf09440-765e-416a-8d15-ff12496f595d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Finding the top K items in a dataset pops up everywhere: from highlighting the hottest posts in a social feed, to detecting the largest transactions in a financial system, or spotting the heaviest users in a telemetry stream.</p>
<p>At first glance, you might think sorting everything and picking the top K is fine, but that approach quickly becomes a bottleneck as data grows or flows in continuously.</p>
<p>In this article, we'll explore <strong>heap-based</strong> and <strong>streaming</strong> approaches in Go that keep only the elements that matter. You'll see how to process data efficiently, maintain bounded memory, and make performance predictable - even when datasets are massive or unending.</p>
<p>The theory is language-agnostic, but we'll use Go's <code>container/heap</code> package to demonstrate the implementation details.</p>
<p>By the end, you'll have a practical toolkit for top-K queries that works in real-world applications, from dashboards to analytics pipelines.</p>
<h2 id="heading-what-well-cover">What We'll Cover</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-the-problem-and-the-naive-approach">The Problem and the Naïve Approach</a></p>
</li>
<li><p><a href="#heading-efficient-top-k-algorithms-in-go">Efficient Top-K Algorithms in Go</a></p>
<ul>
<li><p><a href="#heading-what-is-a-min-heap">What is a Min-Heap?</a></p>
</li>
<li><p><a href="#heading-how-it-works-for-top-k">How it works for Top-K:</a></p>
</li>
<li><p><a href="#heading-implementing-a-min-heap-in-go">Implementing a Min-Heap in Go</a></p>
</li>
<li><p><a href="#heading-streaming-online-top-k">Streaming / Online Top-K</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-trade-offs-amp-practical-considerations">Trade-offs &amp; Practical Considerations</a></p>
<ul>
<li><p><a href="#heading-memory-usage">Memory Usage</a></p>
</li>
<li><p><a href="#heading-time-complexity">Time Complexity</a></p>
</li>
<li><p><a href="#heading-batch-vs-streaming">Batch vs Streaming</a></p>
</li>
<li><p><a href="#heading-multi-dimensional-top-k">Multi-dimensional Top-K</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To get the most out of this tutorial, you should be familiar with:</p>
<ul>
<li><p><strong>Basic data structures</strong>: especially <strong>heaps / priority queues</strong>. I'll explain the minimum you need, but prior exposure helps.</p>
</li>
<li><p><strong>Big O notation</strong>: understanding time and space <a href="https://en.wikipedia.org/wiki/Big_O_notation">complexity</a> will help you appreciate the efficiency of different approaches.</p>
</li>
<li><p><strong>Basic Go programming</strong>: variables, slices, loops, and functions.</p>
</li>
<li><p><strong>Arrays and slices</strong>: understanding how to traverse and manipulate collections.</p>
</li>
<li><p><strong>Sorting concepts</strong>: knowing what it means to sort a collection, even if you don't implement it yourself.</p>
</li>
<li><p><strong>Channels (optional)</strong>: for the streaming example, a basic understanding of Go channels will make it easier to follow.</p>
</li>
</ul>
<p>If some of these are unfamiliar, you can still follow the article, but you may need to refer to the Go <a href="https://go.dev/doc/">documentation</a> or basic tutorials to fill in the gaps. This tutorial is designed to be hands-on and practical, so I'll explain the key concepts as we go.</p>
<h2 id="heading-the-problem-and-the-naive-approach">The Problem and the Naïve Approach</h2>
<p>Let's start with a simple example. Suppose we have a list of user scores:</p>
<pre><code class="language-go">scores := []int{50, 20, 80, 70, 60}
</code></pre>
<p>And we want to find the top 3 scores. The naïve approach is to</p>
<ol>
<li><p><strong>Sort the entire slice</strong> in ascending or descending order.</p>
</li>
<li><p><strong>Select the last K elements</strong> (or first K, depending on sort order).</p>
</li>
</ol>
<pre><code class="language-go">package main

import (
    "fmt"
    "sort"
)

func main() {
    scores := []int{50, 20, 80, 70, 60}
    sort.Sort(sort.Reverse(sort.IntSlice(scores))) // sort descending
    top3 := scores[:3]
    fmt.Println(top3) // Output: [80 70 60]
}
</code></pre>
<p>Sorting the entire dataset takes O(n log n) time, even if we only need the top-K elements. For small datasets, this is fine, but for large datasets or streams, sorting everything is unnecessary and can waste both CPU and memory.</p>
<p>This motivates more efficient strategies, such as <em>heap-based</em> or <em>streaming</em> approaches, which maintain only the top-K elements as we traverse the data.</p>
<h2 id="heading-efficient-top-k-algorithms-in-go">Efficient Top-K Algorithms in Go</h2>
<p>Instead of sorting the entire dataset, we can use a <em>min-heap</em> to efficiently maintain the top-K elements.</p>
<h3 id="heading-what-is-a-min-heap">What is a Min-Heap?</h3>
<p>A min-heap is a <a href="https://www.geeksforgeeks.org/dsa/complete-binary-tree/">complete binary tree</a> where <strong>the value of each node is less than or equal to the values of its children</strong>. For example:</p>
<img src="https://cdn.hashnode.com/uploads/covers/68b08746916c71e1ed2db58e/14776800-bbaa-4f79-98f5-abdb16b8c004.webp" alt="Min-Heap Example" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>In a min-heap, the smallest element is always at the root. This property allows us to efficiently maintain the top-K largest elements by <strong>keeping a min-heap of size K</strong>.</p>
<p>Once the heap is full, if a new element is less than or equal to the root (the smallest top-K score), we reject it. If it's greater, we replace the root (which is no longer in the top-K) and <strong>re-heapify</strong> to restore the min-heap property.</p>
<p>Re-heapifying is the <strong>repair step</strong> after that replacement. Since we overwrite the root, the min-heap rule can be violated at the top of the tree.</p>
<p>To fix it, we inspect both children to find the smaller one, and only then decide whether to swap: if the parent is greater than that smaller child, we swap with that child and continue this <em>sift-down</em> process. We never swap with the larger child first, because that could still leave a smaller child above a larger parent and break the min-heap property.</p>
<p>The process stops when the parent is less than or equal to both children (or when we reach a leaf). Because each swap moves one level down, the repair touches at most the height of the heap, so it takes O(log K) time.</p>
<p>The following diagram illustrates the process: we insert 35 into a heap of size 5 containing 10, 15, 20, 25 and 30:</p>
<img src="https://cdn.hashnode.com/uploads/covers/68b08746916c71e1ed2db58e/0cdf7205-ade8-4432-aeff-150d88a3a8de.webp" alt="Heap Insertion Example" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-how-it-works-for-top-k">How it works for Top-K:</h3>
<ol>
<li><p>Maintain a min-heap of size K.</p>
</li>
<li><p>Fill the heap with the first K elements.</p>
</li>
<li><p>For each remaining element, compare it with the root (<code>h[0]</code>, the smallest of the current top-K).</p>
</li>
<li><p>If it's not larger than the root, reject it in O(1). Otherwise replace the root and re-heapify in O(log K).</p>
</li>
<li><p>After processing all elements, the heap contains the top-K elements because smaller elements are continuously discarded.</p>
</li>
</ol>
<h3 id="heading-implementing-a-min-heap-in-go">Implementing a Min-Heap in Go</h3>
<p>Go's standard library provides a <code>container/heap</code> package that makes it easy to implement a min-heap. Here's how we can use it to maintain the top-K scores:</p>
<pre><code class="language-go">package main

import (
    "container/heap"
    "fmt"
)

type IntHeap []int
func (h IntHeap) Len() int           { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] &lt; h[j] } // min-heap
func (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x interface{}) { *h = append(*h, x.(int)) }
func (h *IntHeap) Pop() interface{} {
    old := *h
    n := len(old)
    x := old[n-1]
    *h = old[0 : n-1]
    return x
}

func topK(nums []int, k int) []int {
    if k &lt;= 0 {
        return []int{}
    }

    h := &amp;IntHeap{}
    heap.Init(h)

    for _, num := range nums {
        if h.Len() &lt; k {
            heap.Push(h, num)
            continue
        }

        // h[0] is the root of the min-heap (the smallest value in current top-K).
        // If num is not larger, we can reject it in O(1).
        if num &lt;= (*h)[0] {
            continue
        }

        // Replace root and restore heap order in O(log K).
        (*h)[0] = num
        heap.Fix(h, 0)
    }

    result := make([]int, h.Len())
    for i := len(result)-1; i &gt;= 0; i-- {
        result[i] = heap.Pop(h).(int)
    }
    return result
}

func main() {
    data := []int{50, 20, 80, 70, 60}
    fmt.Println(topK(data, 3)) // Output: [80 70 60]
}
</code></pre>
<p>In this implementation, we define an <code>IntHeap</code> type that implements the <code>heap.Interface</code>: the <code>Len</code>, <code>Less</code>, <code>Swap</code>, <code>Push</code>, and <code>Pop</code> methods.</p>
<ul>
<li><p><code>IntHeap</code> is a slice of integers that represents our min-heap. A slice can represent a binary tree in a compact form, where the parent-child relationships are determined by indices (for a node at index <code>i</code>, its left child is at <code>2*i + 1</code> and its right child is at <code>2*i + 2</code>).</p>
</li>
<li><p><code>Len</code> returns the number of elements in the heap.</p>
</li>
<li><p><code>Less</code> defines the ordering for the heap, and since we want a min-heap, we return true if the element at index <code>i</code> is less than the element at index <code>j</code>.</p>
</li>
<li><p><code>Swap</code> swaps the elements at the given indices.</p>
</li>
<li><p><code>Push</code> adds a new element to the heap by appending it to the underlying slice.</p>
</li>
<li><p><code>heap.Pop(h)</code> removes and returns the smallest element from this min-heap. Internally, Go's <code>container/heap</code> first swaps the root with the last element, restores heap order, and then calls our <code>Pop</code> method, which removes and returns the last element of the underlying slice.</p>
</li>
</ul>
<p>The <code>topK</code> function first fills the heap to size <code>K</code>. After that, it uses <code>h[0]</code> as a fast threshold check, because <code>h[0]</code> is always the root of the min-heap (the smallest value currently in the top-K set).</p>
<p>If a new number is less than or equal to <code>h[0]</code>, we reject it immediately in O(1). If it is larger, we replace the root and call <code>heap.Fix(h, 0)</code> to re-heapify in O(log K). <code>heap.Fix</code> restores heap order after a value change and may move the element down or up as needed. In this specific root-replacement case, it moves downward.</p>
<p>If <code>K</code> is larger than the number of input elements, <code>topK</code> simply returns all available elements, sorted in descending order.</p>
<p>Finally, we pop all elements while filling the result slice from right to left, so the returned top-K scores are in descending order.</p>
<h3 id="heading-streaming-online-top-k">Streaming / Online Top-K</h3>
<p>The same min-heap logic can be applied to <strong>streaming data</strong>. Instead of waiting for all values first, we process each item as it arrives:</p>
<ol>
<li><p>Read one value from the stream.</p>
</li>
<li><p>If the heap has fewer than <code>K</code> elements, push the value.</p>
</li>
<li><p>Otherwise, compare with the root (<code>h[0]</code>): reject it if it's not larger, or replace the root and re-heapify.</p>
</li>
</ol>
<p>At any moment, the heap contains the best <code>K</code> values seen so far. This is ideal for logs, metrics, event pipelines, or any unbounded input where storing everything isn't practical.</p>
<p>Below is a simple implementation using a channel to simulate incoming values (reusing the <code>IntHeap</code> type from the previous section):</p>
<pre><code class="language-go">func topKFromStream(stream &lt;-chan int, k int) []int {
    if k &lt;= 0 {
        return []int{}
    }

    h := &amp;IntHeap{}
    heap.Init(h)

    for value := range stream {
        if h.Len() &lt; k {
            heap.Push(h, value)
            continue
        }

        if value &lt;= (*h)[0] {
            continue
        }

        (*h)[0] = value
        heap.Fix(h, 0)
    }

    result := make([]int, h.Len())
    for i := len(result) - 1; i &gt;= 0; i-- {
        result[i] = heap.Pop(h).(int)
    }
    return result
}

func main() {
    stream := make(chan int)

    go func() {
        defer close(stream)
        for _, v := range []int{50, 20, 80, 70, 60, 90, 10} {
            stream &lt;- v
        }
    }()

    fmt.Println(topKFromStream(stream, 3)) // Output: [90 80 70]
}
</code></pre>
<p>This pattern gives you online top-K updates with <strong>bounded memory</strong> (you only need to store the top-K elements, not the whole dataset), while still returning a descending top-K result at the end.</p>
<p>If the stream ends before <code>K</code> elements arrive, <code>topKFromStream</code> returns all seen elements, sorted in descending order.</p>
<p>Of course, in a real application, you may want to handle edge cases (like empty streams, non-integer values (if you generalize the heap type), or concurrent access) and consider how to gracefully shut down the stream processing.</p>
<p>Note that although we chose Go for our examples, the same min-heap approach applies in any language with a priority queue or heap data structure, such as Python's <code>heapq</code>, Java's <code>PriorityQueue</code>, or C++'s <code>std::priority_queue</code>.</p>
<p>If your language doesn't have a built-in heap, you can implement one using an array and the standard heap operations (sift-up, sift-down). The core logic of maintaining a bounded heap of size K and using the root as a threshold remains consistent across implementations.</p>
<h2 id="heading-trade-offs-amp-practical-considerations">Trade-offs &amp; Practical Considerations</h2>
<p>When implementing top-K queries, choosing the right strategy depends on dataset size, latency requirements, and memory limits. The same algorithm can behave very differently depending on whether data is small and static or large and continuously arriving.</p>
<h3 id="heading-memory-usage">Memory Usage</h3>
<p>With naive sorting, you must have the full dataset in memory before sorting. The min-heap approach uses O(K) working memory, since the heap never grows beyond K elements. This difference becomes especially important in streaming scenarios, where a heap lets you keep bounded memory no matter how many items eventually arrive.</p>
<h3 id="heading-time-complexity">Time Complexity</h3>
<p>A heap-based top-K computation runs in O(N log K), which is typically much cheaper than full sorting when K is far smaller than N. Full sorting remains O(N log N), even if you only need a tiny top slice at the end. With the optimized root check (<code>h[0]</code>), rejected values are O(1) after warmup, while accepted updates still cost O(log K), making throughput easier to reason about under load.</p>
<h3 id="heading-batch-vs-streaming">Batch vs Streaming</h3>
<p>In batch workloads, where all records are available up front, both full sorting and heap-based methods can work well. For smaller datasets, full sorting is often the simplest solution. As data grows larger - especially when K is much smaller than N - a heap-based approach reduces unnecessary work by avoiding a complete sort.</p>
<p>In streaming workloads, an online heap is typically the better fit. It processes each value as it arrives and maintains only the best K elements seen so far, which keeps memory usage bounded and avoids storing historical data that no longer matters.</p>
<p>In distributed systems, the pattern usually extends one step further: compute the top-K independently on each partition, then <strong>merge</strong> those partial results. With P partitions, this produces up to P × K candidate elements that need combining.</p>
<p>A straightforward approach is to push all candidates into another min-heap of size K, which takes O(PK log K) in the worst case. If each partition already returns its local top-K in sorted order, and the merge stage only needs to extract the final global top-K (not fully merge all P × K items), a heap-based <a href="https://en.wikipedia.org/wiki/K-way_merge_algorithm">k-way merge</a> over the P "heads" can do this in O(K log P) time, which is much more efficient.</p>
<p>At scale, the merge phase can still become a bottleneck, so practical systems carefully balance partition size, fan-out, and merge strategy to keep pipelines performant.</p>
<h3 id="heading-multi-dimensional-top-k">Multi-dimensional Top-K</h3>
<p>Some ranking problems are not one-dimensional. If ordering depends on multiple attributes, such as score, timestamp, and tie-breakers, you can still apply the same heap pattern by defining a custom comparator that captures your priority rules.</p>
<p>More advanced query types, such as <a href="https://blog.gaborkoos.com/posts/2025-07-31-Skyline-Queries-For-Non-Academics/">skyline-style selection</a>, may require different techniques, but for many practical cases a carefully defined heap ordering remains a robust and efficient foundation.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Top-K is a small problem that shows up in big systems. The difference between "sort everything" and "keep only what matters" may seem minor at first - until your dataset grows, your stream never ends, or your service needs to respond in milliseconds.</p>
<p>A min-heap turns the problem into a bounded one. Instead of scaling with the size of your data, your working set scales with K. That shift is what makes the approach powerful in practice, whether you're processing a batch job, consuming a live stream, or merging results across partitions.</p>
<p>Once you internalize this pattern, you'll start recognizing it everywhere: leaderboards, monitoring systems, analytics pipelines, ranking engines. The implementation is compact, the guarantees are clear, and the performance scales predictably - and that's usually what good engineering looks like.</p>
<p>From here, you can explore more advanced structures and techniques to tackle even bigger or more complex top-K challenges:</p>
<ul>
<li><p><strong>Max-heaps and dual heaps</strong>: for sliding-window top-K or tracking both largest and smallest elements.</p>
</li>
<li><p><strong>Order-statistics trees / augmented BSTs</strong>: fast rank queries and updates in a sorted structure.</p>
</li>
<li><p><strong>Skip lists with priorities</strong>: probabilistic balancing and efficient streaming updates.</p>
</li>
</ul>
<p>These advanced patterns build on the min-heap foundation, letting you handle larger datasets, tighter latency constraints, or more sophisticated ranking criteria. Mastering them will put you in a position to design truly high-performance, real-world ranking and analytics systems.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Understanding Escape Analysis in Go – Explained with Example Code ]]>
                </title>
                <description>
                    <![CDATA[ In most languages, the stack and heap are two ways a program stores data in memory, managed by the language runtime. Each is optimized for different use cases, such as fast access or flexible lifetimes. Go follows the same model, but you usually don’... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/understanding-escape-analysis-in-go/</link>
                <guid isPermaLink="false">698e1c7090ca92017618cb24</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                    <category>
                        <![CDATA[ optimization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ heap ]]>
                    </category>
                
                    <category>
                        <![CDATA[ escape analysis ]]>
                    </category>
                
                    <category>
                        <![CDATA[ memory-management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ memory ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Eti Ijeoma ]]>
                </dc:creator>
                <pubDate>Thu, 12 Feb 2026 18:31:12 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770921059389/c45b42cb-8cff-4de5-b3d1-7c7adad402c5.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In most languages, the stack and heap are two ways a program stores data in memory, managed by the language runtime. Each is optimized for different use cases, such as fast access or flexible lifetimes.</p>
<p>Go follows the same model, but you usually don’t decide between the stack and the heap directly. Instead, the Go compiler decides where values live. If the compiler can prove a value is only needed within the current function call, it can keep it on the stack. If it cannot prove that, the value “escapes” and is placed on the heap. This technique is called <strong>escape analysis</strong>.</p>
<p>This matters because heap allocations increase garbage collector work. In code that runs often, that extra work can show up as more CPU spent in GC, more allocations, and less predictable performance.</p>
<p>In this article, I’ll explain what escape analysis is, the common patterns that trigger heap allocation, and how to confirm and reduce avoidable allocations.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-do-you-really-need-to-care-about-escape-analysis">Do You Really Need to Care About Escape Analysis?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-memory-layout-and-lifecycle">Memory Layout and Lifecycle</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sharing-down-and-sharing-up">Sharing Down and Sharing Up</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-escape-analysis-in-practice">Escape Analysis in Practice</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-escape-analysis-to-guide-performance">How to Use Escape Analysis to Guide Performance</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-further-reading">Further Reading</a></p>
</li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<ul>
<li><p>Familiarity with Go fundamentals (functions, variables, structs, slices, maps)</p>
</li>
<li><p>Basic understanding of pointers in Go (<code>&amp;</code> and <code>*</code>)</p>
</li>
<li><p>A general idea of how goroutines work</p>
</li>
</ul>
<h2 id="heading-do-you-really-need-to-care-about-escape-analysis">Do You Really Need to Care About Escape Analysis?</h2>
<p>Before we go deeper, I want to call this out clearly. For the correctness of your program, it doesn’t matter whether a variable lives on the stack or on the heap, or whether you know that detail. The Go compiler is smart enough to place values where they need to be so that your program behaves correctly.</p>
<p>Most of the time, you don’t need to think about this at all. It only starts to matter when performance becomes a problem. If your program is already fast enough, you’re done, and there’s no point trying to squeeze out extra speed.</p>
<p>You should only start caring about stack vs heap when you have benchmarks that show your program is too slow, and those same benchmarks point to heavy heap allocation and garbage collection as part of the problem.</p>
<h2 id="heading-memory-layout-and-lifecycle"><strong>Memory Layout and Lifecycle</strong></h2>
<p>To get a better understanding of what escape analysis is, you first need a simple picture of how Go lays out memory while your program runs. At this level, it comes down to the stack each goroutine uses, how stack frames are carved out of that stack, and when values move to the heap where the garbage collector can see them.</p>
<h3 id="heading-goroutine-stacks-and-stack-frames">Goroutine Stacks and Stack Frames</h3>
<p>When a Go program starts, the runtime creates the <code>main</code> goroutine, and every <code>go</code> statement creates a new goroutine, each with its own stack.</p>
<p>There’s not a single global stack for the whole process. As of writing this article, with Go v1.25.7, each goroutine gets an initial contiguous block of 2,048 bytes of memory, which acts as its stack. The stack is where Go stores data that belongs to function calls. When a goroutine calls a function, Go reserves a chunk of that goroutine’s stack for the function’s local data. That chunk is called a <strong>stack frame</strong>.</p>
<p>It holds the function’s local variables and the call state needed to return and continue execution. If that function calls another function, a new frame is added on top. When the inner function returns, its frame becomes invalid, and the goroutine continues in the caller’s frame.</p>
<p>A stack frame only lives for as long as the function is active. Once the function returns, anything stored in its frame is considered invalid, even if the raw bytes are still in memory and will be reused later. Code must not rely on those values after the return</p>
<p>Go stacks can grow. A goroutine starts with a small stack and the runtime grows it when needed, but the lifetime rule stays the same. A value is safe in a stack frame only if nothing can still reference it after the function returns. If it might be referenced later, it can’t stay in that frame and must be placed somewhere safer.</p>
<h3 id="heading-pointers-and-lifetime">Pointers and Lifetime</h3>
<p>In Go, taking an address like <code>p := &amp;x</code> means you now have a pointer in one stack frame that refers to a value which may have been created in another frame. When you pass that pointer into a function, Go still passes by value. The callee gets its own pointer variable on its own stack frame, but the address inside still points to the same underlying value. So pointers are how you share access to one value across several frames without copying the value itself.</p>
<p>Lifetime becomes important when a pointer can outlive the frame where the pointed value was created. As long as both the pointer and the value live inside frames that are still active in the current call stack, everything is safe.</p>
<p>Once a pointer might still exist after the original frame has returned, the value can no longer stay in that frame, because that frame will become invalid. At that point, the value has to be placed in a safer location so that no pointer ever points into dead stack memory.</p>
<h2 id="heading-sharing-down-and-sharing-up">Sharing Down and Sharing Up</h2>
<p>Now that you have a picture of stacks, frames, and pointers, we can look at two common ways pointers move through your code. I’ll call them sharing down and sharing up. The names aren’t special Go terms. They’re just a simple way to describe how a pointer moves along the call stack.</p>
<h3 id="heading-sharing-down">Sharing Down</h3>
<p>Sharing down means a function passes a pointer or reference to functions it calls. The pointer moves deeper into the call stack, but the value it points to still belongs to a frame that is active.</p>
<p>Example code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    n := <span class="hljs-number">10</span>
    multiply(&amp;n) 
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">multiply</span><span class="hljs-params">(v *<span class="hljs-keyword">int</span>)</span></span> {
   *v = *v * <span class="hljs-number">2</span>
}
</code></pre>
<p>In <code>main</code>, you take the address of <code>n</code> and pass it into <code>multiply</code>. While <code>multiply</code> runs, both the <code>main</code> frame and the <code>multiply</code> frames are active. The pointer in <code>multiply</code> points to a value that still lives in an active frame, so this situation is safe from a lifetime point of view.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770153560595/0681b535-3668-406d-acaf-6e27679d52e1.png" alt="Diagram showing two stack frames on one goroutine, with the upper frame pointing to a value in the lower frame to illustrate sharing down on the stack" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>In the diagram below, after the <code>multiply</code> function runs and returns, the <code>multiply</code> frame becomes invalid, and we don’t need to do anything because the stack pointer is simply popped back to the previous frame's address. This action automatically reclaims all the memory used by that function in one step, so the garbage collector is not involved in cleaning up stack memory</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770153730555/9093b49a-a8bc-497d-be06-77738a89a6c4.png" alt="Diagram showing two stack frames with a value in the upper frame updated through a pointer stored in the lower frame, again illustrating sharing down entirely on the stack" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-sharing-up">Sharing Up</h3>
<p>Sharing up means a function returns a pointer, or stores it somewhere that will still be around after the function returns. The pointer moves back up the call stack or into some longer-lived state while the frame that created the value is about to end, so that value can no longer be tied to that one frame.</p>
<p>The same idea shows up when you share a value with another goroutine, because Go doesn’t let one goroutine hold pointers into another goroutine’s stack, so shared data needs a lifetime that is not tied to a single stack.</p>
<h4 id="heading-heap-garbage-collection-and-lifetime">Heap, garbage collection, and lifetime</h4>
<p>Values that might outlive a single stack frame can’t stay in that frame. The compiler places them on the heap instead. The heap is a separate region of memory that isn’t tied to one function call. Any goroutine can hold pointers to heap values, and those values stay valid as long as something in the program can still reach them. You can think of the heap as storage for “<em>might live longer than this call</em>”.</p>
<p>The garbage collector is what keeps this safe. Periodically, the runtime starts from a set of roots (global variables, active stack frames, some internal state) and follows all the pointers it can see. Any heap value that is still reachable is kept. Any heap value that is no longer reachable is treated as garbage and its memory is reclaimed.</p>
<p>This means a pointer in <code>main</code> will never legally point into dead stack memory. Either the value stayed in an active frame, or it was placed on the heap where the GC can track its lifetime. The tradeoff is that more heap allocations and longer-lived objects require the GC to do more work.</p>
<p>Here’s an example:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-keyword">type</span> Car <span class="hljs-keyword">struct</span> {
    Brand <span class="hljs-keyword">string</span>
    Model <span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// main receives a pointer from a function it called and this is sharing up</span>
    carPtr := makeCar(<span class="hljs-string">"Volkswagen"</span>, <span class="hljs-string">"Golf"</span>) 

    fmt.Printf(<span class="hljs-string">"I received a car: %s %s\n"</span>, carPtr.Brand, carPtr.Model)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">makeCar</span><span class="hljs-params">(b, m <span class="hljs-keyword">string</span>)</span> *<span class="hljs-title">Car</span></span> {
    myCar := Car{
        Brand: b,
        Model: m,
    }
    <span class="hljs-keyword">return</span> &amp;myCar
}
</code></pre>
<p>In the above code:</p>
<ol>
<li><p>In <code>makeCar</code> (the callee frame), Go creates a local variable <code>myCar</code>. Because you return <code>&amp;myCar</code>, the compiler allocates the <code>Car</code> value on the heap, and let’s <code>myCar</code> hold the heap address <code>0xc00029fa0</code>.</p>
</li>
<li><p>When <code>makeCar</code> returns, that address is copied into <code>carPtr</code> in <code>main</code> (the top frame). <code>carPtr</code> is just another stack variable, but its value is still <code>0xc00029fa0</code>, so now <code>main</code> also points to the same heap <code>Car</code>.</p>
</li>
<li><p>On the right, the heap bubble shows the actual <code>Car</code> value at <code>0xc00029fa0</code>. Both <code>car</code> (while <code>makeCar</code> is running) and <code>carPtr</code> (after it returns) reach that same value through their pointers.</p>
</li>
<li><p>Once <code>makeCar</code> is done, its frame drops into the “invalid memory” region, but the <code>Car</code> stays alive on the heap because <code>main</code> still holds <code>carPtr</code>. That’s the escape: the value stops being tied to the callee frame and gets heap lifetime instead.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770255008545/5ef80c9b-3203-4ca3-a26e-f1e2462912cf.png" alt="Diagram showing a caller and callee stack frame both holding a pointer to the same value in heap memory, illustrating a value being shared up and escaping the stack" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-escape-analysis-in-practice">Escape Analysis in Practice</h2>
<p>Escape analysis is how the Go compiler decides whether a value lives on the stack or on the heap. It’s not only about returning pointers – it follows how addresses move through your code. If a value might outlive the current function, the compiler can’t keep it in that stack frame and moves it to the heap. Since only the compiler sees the full picture, the useful thing is to ask it to show these decisions and then link them back to your code.</p>
<p>To do that, we can pass compiler flags using <code>-gcflags</code> when running <code>go build</code> or <code>go run</code>. If you want to see the available options, you can check <code>go tool compile -h</code>. In that list, <code>-m</code> prints the compiler’s optimisation decisions, including escape analysis output. If you want more details, you can use <code>-m=2</code> or <code>-m=3</code> for a more verbose output. The <code>-l</code> flag disables inlining, so the report is easier to read because the compiler is not merging small functions into their callers.</p>
<p>So, the command will look like this:</p>
<pre><code class="lang-bash">go run -gcflags=<span class="hljs-string">'all=-m -l'</span> .
</code></pre>
<p>Or for a build:</p>
<pre><code class="lang-bash">go build -gcflags=<span class="hljs-string">'all=-m -l'</span> .
</code></pre>
<h2 id="heading-how-to-use-escape-analysis-to-guide-performance">How to Use Escape Analysis to Guide Performance</h2>
<p>You can think of escape analysis as the thing that turns your code choices into GC work. When a value escapes, it gets heap lifetime, and the garbage collector has to visit it. In hot paths, lots of small escaping values show up as extra GC time and jitter in latency. When a value stays in a stack frame, it becomes invalid and dies with the frame and the GC does not care about it.</p>
<p>Here are five simple practices that help performance without making</p>
<ol>
<li><p><strong>Prefer values for small data:</strong> If the function doesn’t need to mutate the caller’s data, use value types for small structs and basic types when passing arguments and returning results. It’s cheap to copy an <code>int</code> or a small struct, and it often keeps lifetimes local to a single call.</p>
</li>
<li><p><strong>Use pointers when sharing or mutation is part of the design:</strong> opt for pointers when you genuinely need shared mutable state or want to avoid copying large structs.</p>
</li>
<li><p><strong>Avoid creating long-lived references by accident</strong>: Be careful when returning pointers to locals, capturing variables in closures, or storing addresses in long-lived structs, maps, or interfaces. These patterns are the ones most likely to push values out of a stack frame.</p>
</li>
<li><p><strong>Pass in reusable buffers on hot paths</strong>: On code paths that run very often, the problem is usually not one big allocation, but many small ones happening in a loop. A common cause is functions that always create a new buffer inside, even when the caller could have passed one in.</p>
<p> A simple way to cut those extra allocations is to let the caller own the buffer. The caller allocates a <code>[]byte</code> once, then passes it into the function each time. The function only fills the buffer instead of creating a new one.</p>
<p> Here’s an example of how a bad function allocates a new buffer every call:</p>
<pre><code class="lang-go"> <span class="hljs-keyword">package</span> main

 <span class="hljs-comment">// Bad: helper allocates every call.</span>
 <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fillBad</span><span class="hljs-params">()</span> []<span class="hljs-title">byte</span></span> {
     buf := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">byte</span>, <span class="hljs-number">4096</span>)
     <span class="hljs-comment">// pretend we read into it</span>
     buf[<span class="hljs-number">0</span>] = <span class="hljs-number">1</span>
     <span class="hljs-keyword">return</span> buf
 }

 <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">hotPathBad</span><span class="hljs-params">()</span></span> {
     <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1</span>_000_000; i++ {
         b := fillBad() <span class="hljs-comment">// allocates 1,000,000 times</span>
         _ = b
     }
 }

 <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
     hotPathBad()
 }
</code></pre>
<p> When we run escape analysis with this:</p>
<pre><code class="lang-bash"> go run -gcflags=<span class="hljs-string">'-m -l'</span> .
</code></pre>
<p> We see the following:</p>
<pre><code class="lang-plaintext"> ./main.go:5:13: make([]byte, 4096) escapes to heap
</code></pre>
<p> If we were only allocating a few times, we could choose not to worry – but the real problem is how this looks inside the loop. <code>hotPathBad</code> calls <code>fillBad</code> on every iteration, so each call allocates a new 4 KB slice on the heap. If this loop runs many times, you end up creating a lot of short-lived heap objects. The garbage collector then has to find and clean up all those buffers, which adds extra work that you could have avoided by reusing a single buffer.  </p>
<p> Here’s an example of a better version where the caller allocates once and reuses:</p>
<pre><code class="lang-go"> <span class="hljs-keyword">package</span> main

 <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fill</span><span class="hljs-params">(buf []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-title">int</span></span> {
     <span class="hljs-comment">// pretend we read into it</span>
     buf[<span class="hljs-number">0</span>] = <span class="hljs-number">1</span>
     <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
 }

 <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">hotPath</span><span class="hljs-params">()</span></span> {
     buf := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">byte</span>, <span class="hljs-number">4096</span>) 

     <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1</span>_000_000; i++ {
         n := fill(buf) 
         _ = buf[:n]
     }
 }

 <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
     hotPath()
 }
</code></pre>
<p> In this version, <code>hotPath</code> controls the buffer. It allocates <code>buf</code> once, then passes it into <code>fill</code> on every loop. You still read the same data, but you avoid creating a new slice on each call. That reduces avoidable allocations in the hot path.</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In Go, where a value ends up is not decided by how you create it. It’s decided by how long that value must remain valid and how it is referenced as your code runs.</p>
<p>The practical takeaway is not to avoid pointers. It’s to be deliberate about lifetime. Value semantics can keep lifetimes tight and reduce GC work, while pointers can be the right choice when you need shared state or in-place updates. The balance is to write the clear version first, then look at your benchmarks and profiles to see if anything actually really needs to change.</p>
<h2 id="heading-further-reading">Further Reading</h2>
<p>Language Mechanics On Stacks And Pointers - William Kennedy</p>
<p><a target="_blank" href="https://go.dev/doc/gc-guide">Go Compiler: Escape Analysis Flaws</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Unit Testing in Go - A Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ If you're learning Go and you’re already familiar with the idea of unit testing, the main challenge is usually not why to test, but how to test in Go. Go takes a deliberately minimal approach to testing. There are no built-in assertions, no annotatio... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/unit-testing-in-go-a-beginners-guide/</link>
                <guid isPermaLink="false">696535ad7a48c374647910f2</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                    <category>
                        <![CDATA[ unit testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gabor Koos ]]>
                </dc:creator>
                <pubDate>Mon, 12 Jan 2026 17:55:57 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768240528981/73c9c9f6-4942-4c39-9e62-87f540fd2233.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you're learning Go and you’re already familiar with the idea of <strong>unit testing</strong>, the main challenge is usually not <em>why</em> to test, but <em>how</em> to test in Go.</p>
<p>Go takes a deliberately minimal approach to testing. There are no built-in assertions, no annotations, and no special syntax. Instead, tests are written as regular Go code using a small standard library package, and run with a single command. This can feel unusual at first if you're coming from ecosystems with richer testing frameworks, but it quickly becomes predictable and easy to reason about.</p>
<p>In this article, we'll look at how unit testing works in Go in practice. We'll write a few small tests, run them from the command line, and cover the most common patterns you'll see in real Go codebases, such as table-driven tests and testing functions that return errors. We'll focus on the essentials and won't cover more advanced topics like mocks or external frameworks.</p>
<p>The goal is to show how familiar testing concepts translate into idiomatic Go. By the end, you should feel comfortable reading and writing basic unit tests and integrating them into your regular Go workflow.</p>
<h2 id="heading-what-well-cover">What We'll Cover:</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-writing-your-first-test">Writing Your First Test</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-running-your-test">Running Your Test</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-divide-by-zero">Divide by Zero</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-terrorf-vs-tfatalf">t.Errorf vs t.Fatalf</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-table-driven-tests">Table-Driven Tests</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-table-driven-add-test">Table-Driven Add Test</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-table-driven-divide-test">Table-Driven Divide Test</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-exercise">Exercise</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-testing-functions-that-return-errors">Testing Functions That Return Errors</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-safe-divide-function">Safe Divide Function</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-writing-tests-for-safedivide">Writing Tests for SafeDivide()</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-exercise-1">Exercise</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-best-practices-and-tips">Best Practices and Tips</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-name-tests-clearly">Name Tests Clearly</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-keep-tests-small-and-focused">Keep Tests Small and Focused</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-table-driven-tests-for-repetitive-cases">Use Table-Driven Tests for Repetitive Cases</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-check-errors-explicitly">Check Errors Explicitly</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-avoid-panics-when-possible">Avoid Panics When Possible</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-run-tests-frequently">Run Tests Frequently</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-keep-tests-in-the-same-package">Keep Tests in the Same Package</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-tfatalf-vs-terrorf-appropriately">Use t.Fatalf vs t.Errorf Appropriately</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-solutions-to-exercises">Solutions to Exercises</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-subtract-function-and-tests">Subtract Function and Tests</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-safesubtract-function-and-tests">SafeSubtract Function and Tests</a></p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you start, you should be comfortable with:</p>
<ul>
<li><p>Writing and running basic Go programs</p>
</li>
<li><p>Defining and calling functions in Go</p>
</li>
<li><p>Understanding basic Go types (int, string, bool, and so on)</p>
</li>
<li><p>Using the Go command-line tool (go run, go build)</p>
</li>
<li><p>Basic understanding of unit tests: what a test is and why it's useful</p>
</li>
<li><p>Familiarity with Test-Driven Development concepts like testing before or alongside writing code</p>
</li>
<li><p>Awareness of common testing ideas such as assertions, test coverage, and checking error conditions</p>
</li>
</ul>
<p>You don't need prior experience with Go's <code>testing</code> package or Go-specific test patterns, as this guide will cover all of that.</p>
<h2 id="heading-writing-your-first-test">Writing Your First Test</h2>
<p>Let's start with a simple function to test. Imagine you have a small <code>calc</code> package with an <code>Add</code> function:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-comment">// Add returns the sum of two integers</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Add</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> a + b
}
</code></pre>
<p>To test this function, create a new file named <code>calc_test.go</code> in the same package. In Go, test files must end with <code>_test.go</code> to be recognized by the testing tool.</p>
<p>Inside <code>calc_test.go</code>, you write a test function:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestAdd</span><span class="hljs-params">(t *testing.T)</span></span> {
    got := Add(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>)
    want := <span class="hljs-number">5</span>
    <span class="hljs-keyword">if</span> got != want {
        t.Errorf(<span class="hljs-string">"Add(2, 3) = %d; want %d"</span>, got, want)
    }
}
</code></pre>
<p>Here's what's happening:</p>
<ul>
<li><p>The function name starts with <code>Test</code> and takes a single <code>*testing.T</code> parameter. Go automatically discovers and runs any function that follows this convention.</p>
</li>
<li><p>The <code>t.Errorf</code> call reports a test failure. Unlike some frameworks, Go doesn't provide special assertions – you simply check a condition and call <code>t.Errorf</code> or <code>t.Fatalf</code> if it fails.</p>
</li>
<li><p>Each test is a standalone function. You can write as many as you like, and Go will run them all.</p>
</li>
</ul>
<h3 id="heading-running-your-test">Running Your Test</h3>
<p>Once the file is saved, you can run your test with:</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span>
</code></pre>
<p>This runs tests for the current package (files ending with <code>_test.go</code>). If you want to run tests recursively in all subdirectories of your project, use:</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span> ./...
</code></pre>
<p>The <code>./...</code> pattern is shorthand for "run tests in this directory and all subdirectories". This is especially useful in larger projects where your code is spread across multiple packages.</p>
<p>If everything is working, you should see output indicating that the test passed:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span>
PASS
ok      _/C_/projects/Articles/Go_Testing       0.334s
</code></pre>
<p>You can add the <code>-v</code> flag for verbose output:</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span> -v
</code></pre>
<p>This will show you the names of the tests as they run:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok      _/C_/projects/Articles/Go_Testing       0.356s
</code></pre>
<p>Not much difference for a single test, but it becomes useful as you add more tests.</p>
<p>Now let's see what happens if the test fails. Change the expected value in <code>calc_test.go</code> to an incorrect one:</p>
<pre><code class="lang-go">  ...
    want := <span class="hljs-number">6</span> <span class="hljs-comment">// Incorrect expected value</span>
  ...
</code></pre>
<p>Run the tests again:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span>
--- FAIL: TestAdd (0.00s)
    calc_test.go:9: Add(2, 3) = 5; want 6
FAIL
<span class="hljs-built_in">exit</span> status 1
FAIL    _/C_/projects/Articles/Go_Testing       0.340s
</code></pre>
<p>or with verbose output:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
=== RUN   TestAdd
    calc_test.go:9: Add(2, 3) = 5; want 6
--- FAIL: TestAdd (0.00s)
FAIL
<span class="hljs-built_in">exit</span> status 1
FAIL    _/C_/projects/Articles/Go_Testing       0.337s
</code></pre>
<p>Of course, your tests should always check for the correct expected values! A failing (but correct) test is a sign that your code needs to be fixed.</p>
<p>We only created one test file and one test function with one assertion here, but Go's testing tool can handle many files and functions at once. Behind the scenes, Go will automatically:</p>
<ul>
<li><p>Find <strong>all</strong> <code>_test.go</code> files in the specified packages (for example, current directory for <code>go test</code>, or recursively in all subdirectories with <code>go test ./...</code>).</p>
</li>
<li><p>Identify functions that start with <code>Test</code> and have the correct signature.</p>
</li>
<li><p>Compile them together with your package into a temporary test binary.</p>
</li>
<li><p>Execute each test function and report the results.</p>
</li>
</ul>
<p>To prove this, let's quickly add a <code>Divide</code> function to our package:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc.go</span>
...
<span class="hljs-comment">// Divide returns the result of dividing a by b</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Divide</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> a / b
}
</code></pre>
<p>(Note that this is an <strong>integer division</strong>, so fractional parts are discarded. <code>Divide(5, 2)</code> would return <code>2</code>.)</p>
<p>And another test file with a corresponding test:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc_2_test.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestDivide</span><span class="hljs-params">(t *testing.T)</span></span> {
    got := Divide(<span class="hljs-number">10</span>, <span class="hljs-number">2</span>)
    want := <span class="hljs-number">5</span>    
    <span class="hljs-keyword">if</span> got != want {
        t.Errorf(<span class="hljs-string">"Divide(10, 2) = %d; want %d"</span>, got, want)
    }    
}
</code></pre>
<p>Now when you run <code>go test</code>, both <code>TestAdd</code> and <code>TestDivide</code> will be executed:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span>
PASS
ok      _/C_/projects/Articles/Go_Testing       0.325s
</code></pre>
<p>Or:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
=== RUN   TestDivide
--- PASS: TestDivide (0.00s)
PASS
ok      _/C_/projects/Articles/Go_Testing       0.323s
</code></pre>
<h3 id="heading-divide-by-zero">Divide by Zero</h3>
<p>What happens if we try to <code>Divide</code> by zero? Let's add another test case for that:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
...
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestDivideByZero</span><span class="hljs-params">(t *testing.T)</span></span> {
    <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">if</span> r := <span class="hljs-built_in">recover</span>(); r == <span class="hljs-literal">nil</span> { <span class="hljs-comment">// Check if a panic occurred</span>
            t.Errorf(<span class="hljs-string">"Divide did not panic on division by zero"</span>)
        }
    }()
    Divide(<span class="hljs-number">10</span>, <span class="hljs-number">0</span>) <span class="hljs-comment">// This should cause a panic</span>
}
</code></pre>
<p>This test checks that the <code>Divide</code> function panics when dividing by zero. When you run the tests again, you'll see that this new test also passes:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
=== RUN   TestDivide
--- PASS: TestDivide (0.00s)
=== RUN   TestDivideByZero
--- PASS: TestDivideByZero (0.00s)
PASS
ok      _/C_/projects/Articles/Go_Testing       0.312s
</code></pre>
<p>(Note that in real-world Go code, it's better to return <code>(int, error)</code> for unsafe operations instead of panicking.)</p>
<p>Feel free to experiment by adding more test cases, changing expected values, and exploring how Go's testing framework handles different scenarios.</p>
<h3 id="heading-terrorf-vs-tfatalf"><code>t.Errorf</code> vs <code>t.Fatalf</code></h3>
<p>In the examples above, we used <code>t.Errorf</code> to report test failures. This function logs the error but allows the test to continue running. This is useful when you want to check multiple conditions in a single test function.</p>
<p>In contrast, <code>t.Fatalf</code> logs the error and immediately stops the execution of the current test. Use <code>t.Fatalf</code> when continuing the test after a failure doesn't make sense or could cause misleading results.</p>
<p>For example, in the <code>TestDivideByZero</code> test, if the <code>Divide</code> function does not panic, we use <code>t.Errorf</code> to report the failure but continue to the end of the test. But if we had additional checks after the division, we might want to use <code>t.Fatalf</code> to stop execution immediately upon failure.</p>
<p>While <code>t.Errorf</code> and <code>t.Fatalf</code> use <code>fmt</code>-style formatting, for simple messages without formatting, you can also use <code>t.Error</code> and <code>t.Fatal</code>, respectively.</p>
<p>In the next section, we'll look at <em>table-driven tests</em>, a common Go pattern for testing multiple cases efficiently.</p>
<h2 id="heading-table-driven-tests">Table-Driven Tests</h2>
<p>In Go, it's common to want to run the same test logic for multiple inputs and expected outputs. Rather than writing a separate test function for each case, Go developers often use <strong>table-driven tests</strong>. This pattern keeps your tests concise, readable, and easy to extend.</p>
<h3 id="heading-table-driven-add-test">Table-Driven <code>Add</code> Test</h3>
<p>Let's rewrite our Add test using a table-driven approach (and delete <code>calc_2_test.go</code> for clarity):</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestAddTableDriven</span><span class="hljs-params">(t *testing.T)</span></span> {
    tests := []<span class="hljs-keyword">struct</span> {<span class="hljs-comment">// Define a struct for each test case and create a slice of them</span>
        name <span class="hljs-keyword">string</span>
        a, b <span class="hljs-keyword">int</span>
        want <span class="hljs-keyword">int</span>
    }{
        {<span class="hljs-string">"both positive"</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>},
        {<span class="hljs-string">"positive + zero"</span>, <span class="hljs-number">5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>},
        {<span class="hljs-string">"negative + positive"</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">3</span>},
        {<span class="hljs-string">"both negative"</span>, <span class="hljs-number">-2</span>, <span class="hljs-number">-3</span>, <span class="hljs-number">-5</span>},
    }

    <span class="hljs-keyword">for</span> _, tt := <span class="hljs-keyword">range</span> tests {<span class="hljs-comment">// Loop over each test case</span>
        t.Run(tt.name, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {<span class="hljs-comment">// Run each case as a subtest</span>
            got := Add(tt.a, tt.b)
            <span class="hljs-keyword">if</span> got != tt.want {<span class="hljs-comment">// Check the result</span>
                t.Errorf(<span class="hljs-string">"Add(%d, %d) = %d; want %d"</span>, tt.a, tt.b, got, tt.want) <span class="hljs-comment">// Report failure if it doesn't match</span>
            }
        })
    }
}
</code></pre>
<p>Here's how it works:</p>
<ul>
<li><p>We define a <strong>slice of structs</strong>, each representing a test case.</p>
</li>
<li><p>Each struct contains the test name, input values, and the expected result.</p>
</li>
<li><p>We loop over the slice and call <code>t.Run(tt.name, func(t *testing.T) { ... })</code> to run each test as a <strong>subtest</strong>.</p>
</li>
<li><p>If a subtest fails, you can see which one by its name in the output.</p>
</li>
</ul>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span>
PASS
ok      _/C_/projects/Articles/Go_Testing       0.452s
</code></pre>
<p>Or to see detailed output:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
=== RUN   TestAddTableDriven
=== RUN   TestAddTableDriven/both_positive
=== RUN   TestAddTableDriven/positive_+_zero
=== RUN   TestAddTableDriven/negative_+_positive
=== RUN   TestAddTableDriven/both_negative
--- PASS: TestAddTableDriven (0.00s)
    --- PASS: TestAddTableDriven/both_positive (0.00s)
    --- PASS: TestAddTableDriven/positive_+_zero (0.00s)
    --- PASS: TestAddTableDriven/negative_+_positive (0.00s)
    --- PASS: TestAddTableDriven/both_negative (0.00s)
PASS
ok      _/C_/projects/Articles/Go_Testing       0.385s
</code></pre>
<h3 id="heading-table-driven-divide-test">Table-Driven Divide Test</h3>
<p>We can apply the same pattern to <code>Divide</code>, including checking for divide-by-zero:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
...
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestDivideTableDriven</span><span class="hljs-params">(t *testing.T)</span></span> {
    tests := []<span class="hljs-keyword">struct</span> { <span class="hljs-comment">// Define test cases</span>
        name     <span class="hljs-keyword">string</span>
        a, b     <span class="hljs-keyword">int</span>
        want     <span class="hljs-keyword">int</span>
        wantPanic <span class="hljs-keyword">bool</span>
    }{
        {<span class="hljs-string">"normal division"</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-literal">false</span>},
        {<span class="hljs-string">"division by zero"</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">true</span>},
    }

    <span class="hljs-keyword">for</span> _, tt := <span class="hljs-keyword">range</span> tests { <span class="hljs-comment">// Loop over</span>
        t.Run(tt.name, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> { <span class="hljs-comment">// Run subtest</span>
            <span class="hljs-keyword">if</span> tt.wantPanic { <span class="hljs-comment">// Check for expected panic</span>
                <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> { <span class="hljs-comment">// Recover from panic</span>
                    <span class="hljs-keyword">if</span> r := <span class="hljs-built_in">recover</span>(); r == <span class="hljs-literal">nil</span> {
                        t.Errorf(<span class="hljs-string">"Divide(%d, %d) did not panic"</span>, tt.a, tt.b)
                    }
                }()
            }
            got := Divide(tt.a, tt.b) <span class="hljs-comment">// Tests that do not panic</span>
            <span class="hljs-keyword">if</span> !tt.wantPanic &amp;&amp; got != tt.want {
                t.Errorf(<span class="hljs-string">"Divide(%d, %d) = %d; want %d"</span>, tt.a, tt.b, got, tt.want)
            }
        })
    }
}
</code></pre>
<p>This example shows how to handle both normal and panic cases in a single table-driven test:</p>
<ul>
<li><p>The <code>wantPanic</code> field tells the test whether we expect a panic.</p>
</li>
<li><p>We use <code>defer</code> and <code>recover</code> to check for a panic when needed.</p>
</li>
<li><p>Normal test cases still check the result as usual.</p>
</li>
</ul>
<p>Run all tests as before:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
=== RUN   TestAddTableDriven
=== RUN   TestAddTableDriven/both_positive
=== RUN   TestAddTableDriven/positive_+_zero
=== RUN   TestAddTableDriven/negative_+_positive
=== RUN   TestAddTableDriven/both_negative
--- PASS: TestAddTableDriven (0.00s)
    --- PASS: TestAddTableDriven/both_positive (0.00s)
    --- PASS: TestAddTableDriven/positive_+_zero (0.00s)
    --- PASS: TestAddTableDriven/negative_+_positive (0.00s)
    --- PASS: TestAddTableDriven/both_negative (0.00s)
=== RUN   TestDivideTableDriven
=== RUN   TestDivideTableDriven/normal_division
=== RUN   TestDivideTableDriven/division_by_zero
--- PASS: TestDivideTableDriven (0.00s)
    --- PASS: TestDivideTableDriven/normal_division (0.00s)
    --- PASS: TestDivideTableDriven/division_by_zero (0.00s)
PASS
ok      _/C_/projects/Articles/Go_Testing       0.321s
</code></pre>
<p>Subtest names make it easy to see which case passed or failed.</p>
<h3 id="heading-exercise">Exercise</h3>
<p>Try creating your own table-driven test for a new function, <code>Subtract(a, b int) int</code>. Include at least four test cases:</p>
<ul>
<li><p>Both positive numbers</p>
</li>
<li><p>Positive minus zero</p>
</li>
<li><p>Negative minus positive</p>
</li>
<li><p>Both negative</p>
</li>
</ul>
<p>Then run your tests and verify the output.</p>
<h2 id="heading-testing-functions-that-return-errors">Testing Functions That Return Errors</h2>
<p>Many Go functions return an error as the last return value. Writing tests for these functions is slightly different from testing pure functions like our <code>Add</code> or <code>Divide</code>, because you need to check both the result and whether an error occurred.</p>
<h3 id="heading-safe-divide-function">Safe Divide Function</h3>
<p>Let's add a <code>SafeDivide</code> function to return an error instead of panicking:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc.go</span>
...
<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>
...
<span class="hljs-comment">// SafeDivide returns the result of dividing a by b.</span>
<span class="hljs-comment">// It returns an error if b is zero.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">SafeDivide</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
    <span class="hljs-keyword">if</span> b == <span class="hljs-number">0</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, fmt.Errorf(<span class="hljs-string">"cannot divide by zero"</span>)
    }
    <span class="hljs-keyword">return</span> a / b, <span class="hljs-literal">nil</span>
}
</code></pre>
<h3 id="heading-writing-tests-for-safedivide">Writing Tests for <code>SafeDivide()</code></h3>
<p>We can use a table-driven test again:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestSafeDivide</span><span class="hljs-params">(t *testing.T)</span></span> {
    tests := []<span class="hljs-keyword">struct</span> {
        name      <span class="hljs-keyword">string</span>
        a, b      <span class="hljs-keyword">int</span>
        want      <span class="hljs-keyword">int</span>
        wantError <span class="hljs-keyword">bool</span>
    }{
        {<span class="hljs-string">"normal division"</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-literal">false</span>},
        {<span class="hljs-string">"division by zero"</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">true</span>},
    }

    <span class="hljs-keyword">for</span> _, tt := <span class="hljs-keyword">range</span> tests {
        t.Run(tt.name, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
            got, err := SafeDivide(tt.a, tt.b)
            <span class="hljs-keyword">if</span> tt.wantError {
                <span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> {
                    t.Errorf(<span class="hljs-string">"SafeDivide(%d, %d) expected error, got nil"</span>, tt.a, tt.b)
                }
                <span class="hljs-keyword">return</span> <span class="hljs-comment">// stop here, no need to check `got`</span>
            }
            <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                t.Errorf(<span class="hljs-string">"SafeDivide(%d, %d) unexpected error: %v"</span>, tt.a, tt.b, err)
            }
            <span class="hljs-keyword">if</span> got != tt.want {
                t.Errorf(<span class="hljs-string">"SafeDivide(%d, %d) = %d; want %d"</span>, tt.a, tt.b, got, tt.want)
            }
        })
    }
}
</code></pre>
<p>What's happening here:</p>
<ul>
<li><p>We added a <code>wantError</code> field to indicate whether the test expects an error.</p>
</li>
<li><p>If an error is expected, we check that <code>err != nil</code>. If not (that is, <code>err == nil</code>), we fail the test.</p>
</li>
<li><p>If no error is expected, we check both the returned value (<code>got</code>) and that <code>err == nil</code>.</p>
</li>
<li><p>Using <code>t.Run</code> subtests keeps everything organized and readable.</p>
</li>
</ul>
<p>Running the tests again:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
...
=== RUN   TestSafeDivide
=== RUN   TestSafeDivide/normal_division
=== RUN   TestSafeDivide/division_by_zero
--- PASS: TestSafeDivide (0.00s)
    --- PASS: TestSafeDivide/normal_division (0.00s)
    --- PASS: TestSafeDivide/division_by_zero (0.00s)
PASS
ok      _/C_/projects/Articles/Go_Testing       0.323s
</code></pre>
<p>Showing that both normal and error cases are handled correctly.</p>
<h3 id="heading-exercise-1">Exercise</h3>
<p>Update your <code>Subtract(a, b int) int</code> function to a <code>SafeSubtract(a, b int) (int, error)</code> variant that returns an error if the result would be negative. Then write a table-driven test that covers:</p>
<ul>
<li><p>A positive result</p>
</li>
<li><p>Zero result</p>
</li>
<li><p>A negative result (should return an error)</p>
</li>
</ul>
<h2 id="heading-best-practices-and-tips">Best Practices and Tips</h2>
<p>Writing tests in Go is straightforward, but there are a few conventions and tips that make your tests more readable, maintainable, and idiomatic:</p>
<h3 id="heading-name-tests-clearly">Name Tests Clearly</h3>
<p>First, make sure you use descriptive names for test functions and subtests. A good name explains what you're testing and under what conditions.</p>
<p>Here’s an example:</p>
<pre><code class="lang-go">t.Run(<span class="hljs-string">"Divide positive numbers"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> { ... })
t.Run(<span class="hljs-string">"Divide by zero returns error"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> { ... })
</code></pre>
<h3 id="heading-keep-tests-small-and-focused">Keep Tests Small and Focused</h3>
<p>Each subtest should verify one thing, and each test function should cover a single function or method.</p>
<p>Try to avoid combining multiple unrelated checks in the same test function, and use table-driven tests help keep multiple similar checks concise without losing clarity.</p>
<h3 id="heading-use-table-driven-tests-for-repetitive-cases">Use Table-Driven Tests for Repetitive Cases</h3>
<p>If you find yourself writing multiple similar test functions, switch to a table-driven pattern. It makes it easier to add new cases, reduces duplicated code, and keeps output organized with <code>t.Run</code>.</p>
<h3 id="heading-check-errors-explicitly">Check Errors Explicitly</h3>
<p>In Go, functions often return <code>error</code>. So make sure you always check for errors in tests, even if you expect <code>nil</code>.</p>
<p>You can use the <code>wantError</code> pattern in table-driven tests for clarity.</p>
<pre><code class="lang-go"><span class="hljs-keyword">if</span> tt.wantError {
    <span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> {
        t.Errorf(<span class="hljs-string">"expected error, got nil"</span>)
    }
}
</code></pre>
<h3 id="heading-avoid-panics-when-possible">Avoid Panics When Possible</h3>
<p>Panics are fine for some internal checks, but in production code, prefer returning an error.</p>
<p>Your tests can check for panics using <code>defer</code> and <code>recover</code>, but this should be the exception rather than the norm.</p>
<h3 id="heading-run-tests-frequently">Run Tests Frequently</h3>
<p>Try to make running tests a habit: <code>go test -v ./...</code>. Frequent testing helps catch mistakes early and reinforces TDD practices.</p>
<h3 id="heading-keep-tests-in-the-same-package">Keep Tests in the Same Package</h3>
<p>By convention, tests live in the same package as the code they test. You can create <code>_test.go</code> files for testing, and Go automatically recognizes them.</p>
<p>Only use a separate <code>package calc_test</code> if you want to test your code from the outside, like a consumer. External test packages (just like every other external package) cannot access unexported identifiers.</p>
<h3 id="heading-use-tfatalf-vs-terrorf-appropriately">Use t.Fatalf vs t.Errorf Appropriately</h3>
<ul>
<li><p><code>t.Errorf</code> reports a failure but continues running the test.</p>
</li>
<li><p><code>t.Fatalf</code> stops the test immediately, which is useful if subsequent code depends on successful setup.</p>
</li>
</ul>
<p>These tips will help you write clean, maintainable, and idiomatic Go tests that are easy to read and extend. Following these practices early in your Go journey will make testing less intimidating and more effective.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Unit testing in Go may feel different at first, especially if you're coming from ecosystems with heavy frameworks and assertions. But the simplicity of Go's testing tools is one of its strengths: once you understand the conventions, writing, running, and organizing tests becomes predictable and intuitive.</p>
<p>In this guide, you've seen how to:</p>
<ul>
<li><p>Write basic test functions with the testing package</p>
</li>
<li><p>Run tests from the command line and interpret the results</p>
</li>
<li><p>Use table-driven tests to cover multiple cases efficiently</p>
</li>
<li><p>Handle functions that return errors and check for expected failures</p>
</li>
</ul>
<p>Beyond these fundamentals, testing is not just about verifying correctness, it's also about confidence. Well-tested code allows you to refactor, experiment, and add new features with less fear of breaking existing functionality.</p>
<p>As you continue writing Go code, try to integrate testing early, follow the idiomatic patterns you've learned, and explore more advanced topics such as:</p>
<ul>
<li><p>Using <em>mocks</em> or <em>interfaces</em> to isolate dependencies</p>
</li>
<li><p>Benchmark tests with <code>testing.B</code></p>
</li>
<li><p>Coverage analysis with <code>go test -cover</code></p>
</li>
</ul>
<p>The key takeaway is that testing in Go is accessible, flexible, and powerful, even without fancy frameworks. By building these habits now, you'll write code that's more reliable, maintainable, and enjoyable to work with.</p>
<h2 id="heading-solutions-to-exercises">Solutions to Exercises</h2>
<h3 id="heading-subtract-function-and-tests">Subtract Function and Tests</h3>
<pre><code class="lang-go"><span class="hljs-comment">// calc.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Subtract</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> a - b
}
</code></pre>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestSubtractTableDriven</span><span class="hljs-params">(t *testing.T)</span></span> {
    tests := []<span class="hljs-keyword">struct</span> {
        name <span class="hljs-keyword">string</span>
        a, b <span class="hljs-keyword">int</span>
        want <span class="hljs-keyword">int</span>
    }{
        {<span class="hljs-string">"both positive"</span>, <span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>},
        {<span class="hljs-string">"positive minus zero"</span>, <span class="hljs-number">5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>},
        {<span class="hljs-string">"negative minus positive"</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">-5</span>},
        {<span class="hljs-string">"both negative"</span>, <span class="hljs-number">-3</span>, <span class="hljs-number">-2</span>, <span class="hljs-number">-1</span>},
    }

    <span class="hljs-keyword">for</span> _, tt := <span class="hljs-keyword">range</span> tests {
        t.Run(tt.name, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
            got := Subtract(tt.a, tt.b)
            <span class="hljs-keyword">if</span> got != tt.want {
                t.Errorf(<span class="hljs-string">"Subtract(%d, %d) = %d; want %d"</span>, tt.a, tt.b, got, tt.want)
            }
        })
    }
}
</code></pre>
<h3 id="heading-safesubtract-function-and-tests">SafeSubtract Function and Tests</h3>
<pre><code class="lang-go"><span class="hljs-comment">// calc.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">SafeSubtract</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
    result := a - b
    <span class="hljs-keyword">if</span> result &lt; <span class="hljs-number">0</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, fmt.Errorf(<span class="hljs-string">"result would be negative"</span>)
    }
    <span class="hljs-keyword">return</span> result, <span class="hljs-literal">nil</span>
}
</code></pre>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestSafeSubtract</span><span class="hljs-params">(t *testing.T)</span></span> {
    tests := []<span class="hljs-keyword">struct</span> {
        name      <span class="hljs-keyword">string</span>
        a, b      <span class="hljs-keyword">int</span>
        want      <span class="hljs-keyword">int</span>
        wantError <span class="hljs-keyword">bool</span>
    }{
        {<span class="hljs-string">"positive result"</span>, <span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>, <span class="hljs-literal">false</span>},
        {<span class="hljs-string">"zero result"</span>, <span class="hljs-number">3</span>, <span class="hljs-number">3</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">false</span>},
        {<span class="hljs-string">"negative result"</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">true</span>},
    }

    <span class="hljs-keyword">for</span> _, tt := <span class="hljs-keyword">range</span> tests {
        t.Run(tt.name, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
            got, err := SafeSubtract(tt.a, tt.b)
            <span class="hljs-keyword">if</span> tt.wantError {
                <span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> {
                    t.Errorf(<span class="hljs-string">"SafeSubtract(%d, %d) expected error, got nil"</span>, tt.a, tt.b)
                }
                <span class="hljs-keyword">return</span>
            }
            <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                t.Errorf(<span class="hljs-string">"SafeSubtract(%d, %d) unexpected error: %v"</span>, tt.a, tt.b, err)
            }
            <span class="hljs-keyword">if</span> got != tt.want {
                t.Errorf(<span class="hljs-string">"SafeSubtract(%d, %d) = %d; want %d"</span>, tt.a, tt.b, got, tt.want)
            }
        })
    }
}
</code></pre>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Real-Time Systems for Web Developers: From Theory to a Live Go + React App ]]>
                </title>
                <description>
                    <![CDATA[ Many developers think that “real-time” is about Websockets, Live data, or instant refreshes on web application dashboards. And although these concepts are closely related to what real-time means, the systems engineering definition is a bit different.... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/real-time-systems-for-web-developers-from-theory-to-a-live-go-react-app/</link>
                <guid isPermaLink="false">695e96b3ee29df356daaf7c0</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websockets ]]>
                    </category>
                
                    <category>
                        <![CDATA[ realtime apps ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Emmanuel Etukudo ]]>
                </dc:creator>
                <pubDate>Wed, 07 Jan 2026 17:24:03 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767748231282/074fcd8b-8808-4a3d-8c9c-17d5a7b388e6.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Many developers think that “real-time” is about Websockets, Live data, or instant refreshes on web application dashboards.</p>
<p>And although these concepts are closely related to what real-time means, the systems engineering definition is a bit different. A real-time system is not defined by how fast it is, but how predictable it is. </p>
<p>In this tutorial, you’ll learn about what real-time systems are, why most web applications are not real-time, and how to build a <strong>soft real-time system</strong> with tools you’re likely already familiar with: Go, React, and TypeScript.</p>
<p>At the end of this tutorial, we’ll build a live application that:</p>
<ul>
<li><p>processes time-sensitive events</p>
</li>
<li><p>enforces deadlines</p>
</li>
<li><p>drops work when it’s too late </p>
</li>
<li><p>and visualises latency and missed deadlines in real-time</p>
</li>
</ul>
<p>  This article will help shape your mind the next time you’re building a real-time system.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-a-real-time-system-really-means">What a Real-Time System Really Means</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-types-of-real-time-systems">Types of Real-Time Systems</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-most-web-apps-are-not-real-time">Why Most Web Apps Are Not Real-Time</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-what-we-are-going-to-build">What We Are Going to Build</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-system-architecture">System Architecture</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-go-is-a-good-fit-for-our-use-case">Why Go is a Good Fit for Our Use Case</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-generating-events-with-go">Generating Events with Go</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-deadline-aware-processing">Deadline-aware Processing</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-applying-back-pressure">Applying Back-Pressure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-streaming-events-to-the-browser">Streaming Events to the Browser</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-consuming-a-websocket-event-react-typescript">Consuming a WebSocket event (React + TypeScript)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-making-react-real-time-friendly">Making React Real-Time Friendly</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-creating-the-statsbar-component">Creating the StatsBar Component</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-creating-the-events-table">Creating the Events Table</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-putting-it-all-together">Putting it All Together</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This tutorial assumes that you have basic knowledge of <code>Go</code>, <code>React</code>, and <code>WebSockets</code>, particularly working with go-routines and consuming <code>WebSockets</code> events in <code>React</code>. If you don’t, I strongly recommend reviewing introductory tutorials before continuing so you can get the most out of this guide.  </p>
<p>Helpful references include:</p>
<ul>
<li><p><strong>Go Concurrency and Goroutines:</strong> The official Go blog covers concurrency patterns, including goroutines and channels, which are <a target="_blank" href="https://go.dev/blog/concurrency-patterns">fundamental for writing concurrent Go code</a>.</p>
</li>
<li><p><strong>WebSockets with Go:</strong> A thorough WebSocket tutorial using the popular gorilla/websocket package that shows how to set up a WebSocket server in Go and manage connections and messages. <a target="_blank" href="https://tutorialedge.net/golang/go-websocket-tutorial">Here’s a Go WebSocket tutorial (with gorilla/websocket)</a>.</p>
</li>
<li><p><strong>WebSockets in React:</strong> A complete guide to WebSockets in React, explaining how to open and manage a WebSocket connection and handle incoming messages in components. <a target="_blank" href="https://ably.com/blog/websockets-react-tutorial">Here’s a complete guide to WebSockets with React</a>.</p>
</li>
</ul>
<p>Technologies we’ll work with include:</p>
<ul>
<li><p><code>Go</code>, for building our backend system and enforcing real-time guarantees</p>
</li>
<li><p><code>React</code>, for building a responsive frontend <code>UI</code> that displays streamed events</p>
</li>
<li><p><code>Websocket</code>, for low-latency delivery of data from the backend to the client</p>
</li>
</ul>
<h2 id="heading-what-a-real-time-system-really-means">What a Real-Time System Really Means</h2>
<p>In a traditional web application, correctness is measured by whether the system produced the right result. In a real-time system, correctness is measured by whether the system produced the right result <strong>before the deadline</strong>. If the result from the test is “No”, then the system has failed – even though the result is correct.</p>
<h3 id="heading-types-of-real-time-systems">Types of Real-Time Systems</h3>
<p>There are a few different types of real-time systems that you should be aware of, each with varying levels of strictness:</p>
<table><tbody><tr><td><p><strong>Realtime System</strong></p></td><td><p><strong>Niche applicable&nbsp;</strong></p></td></tr><tr><td><p><strong>Hard Real-time: </strong>Missing a deadline is catastrophic here.</p></td><td><p>Flight Control &amp; Pacemakers</p></td></tr><tr><td><p><strong>Soft Real-time:</strong> Missing deadline degrades quality but does not crash the system.&nbsp;</p></td><td><p>Video Streaming &amp; Trading Dashboards</p></td></tr><tr><td><p><strong>Firm Real-time</strong>: Late results are useless and should be discarded.</p></td><td><p>Car Auction Web/Mobile Apps</p></td></tr></tbody></table>

<p>The majority of web-based real-time systems fall under the soft real-time category, and that’s exactly what we’ll build here.</p>
<h3 id="heading-why-most-web-apps-are-not-real-time">Why Most Web Apps Are Not Real-Time</h3>
<p>There are many reasons a system may not be real-time, and typically, even those marketed as real-time applications lack this guarantee.</p>
<p>Here’s why:</p>
<ol>
<li><p>Websockets guarantee delivery, not timeliness</p>
</li>
<li><p>Massage queues are optimized for durability and throughput </p>
</li>
<li><p>Infinite buffering hides deadlines </p>
</li>
<li><p>User Interfaces (UIs) render when they can, not when they should  </p>
</li>
</ol>
<p>In other words, data will arrive eventually, but nothing enforces when it must be processed. That’s exactly the gap we’ll address in this tutorial.</p>
<h2 id="heading-what-we-are-going-to-build">What We Are Going to Build</h2>
<p>In this tutorial, we’ll build a Deadline-Aware Live Event Monitor. You can think of it as a simplified real-time system for sensor data, trading events, alerts, or live telemetry.</p>
<p>Our app will have these features and constraints:</p>
<ul>
<li><p>Events are generated at a fixed rate</p>
</li>
<li><p>Each event has a deadline </p>
</li>
<li><p>The backend processes events only if they can be completed on time</p>
</li>
<li><p>Late events are marked or dropped </p>
</li>
<li><p>The frontend visualizes:</p>
<ul>
<li><p>processing latency </p>
</li>
<li><p>missed deadlines</p>
</li>
<li><p>and system health </p>
</li>
</ul>
</li>
</ul>
<p>This will give us the required metrics to measure the real-time behavior of the system instead of guessing.</p>
<h2 id="heading-system-architecture">System Architecture</h2>
<p>The high-level system architecture looks like this:</p>
<pre><code class="lang-plaintext">
+-------------+     +------------------+     +----------------+
| Event       | --&gt; | Deadline-Aware   | --&gt; | WebSocket      |
| Generator   |     | Go Processor     |     | Server         |
+-------------+     +------------------+     +----------------+
                                                     |
                                                     v
                                           +----------------+
                                           | React Dashboard|
                                           +----------------+
</code></pre>
<p>Let’s break down the responsibilities:</p>
<p><strong>Backend:</strong></p>
<ul>
<li><p>Generates time-sensitive events </p>
</li>
<li><p>Enforces deadline </p>
</li>
<li><p>Applies back pressure</p>
</li>
<li><p>Streams result to client (frontend)</p>
</li>
</ul>
<p><strong>Front End:</strong></p>
<ul>
<li><p>Consumes real-time events</p>
</li>
<li><p>Renders live metrics </p>
</li>
<li><p>Remains responsive under load </p>
</li>
</ul>
<h4 id="heading-time-is-part-of-your-data-model">Time is part of your Data Model</h4>
<p>In a real-time system, time is explicit, not implicit. This means that each event processed includes:</p>
<ul>
<li><p>when it was created </p>
</li>
<li><p>how long is it allowed to live, and</p>
</li>
<li><p>when it was processed </p>
</li>
</ul>
<p>Conceptually, a typical data model for an event looks like this:</p>
<pre><code class="lang-typescript">{
  id: <span class="hljs-built_in">string</span>
  createdAt: <span class="hljs-built_in">number</span>
  deadlineMs: <span class="hljs-built_in">number</span>
  processedAt?: <span class="hljs-built_in">number</span>
  status: <span class="hljs-string">"on-time"</span> | <span class="hljs-string">"late"</span> | <span class="hljs-string">"dropped"</span>
}
</code></pre>
<p>This is the mindset shift we hope to establish: in a real-time system, time is an essential component for your system that guarantees accuracy.</p>
<h2 id="heading-why-go-is-a-good-fit-for-our-use-case">Why Go is a Good Fit for Our Use Case</h2>
<p>Go is not a hard real-time language, but it’s excellent for soft real-time workloads. This is because of its:</p>
<ul>
<li><p>Cheap goroutines</p>
</li>
<li><p>Structured concurrency with channels</p>
</li>
<li><p>Deadline propagation via <code>context.Context</code></p>
</li>
<li><p>Simple runtime behavior</p>
</li>
</ul>
<p>Most importantly, Go makes it easy to <strong>fail fast</strong>, which is essential for real-time systems.</p>
<h3 id="heading-generating-events-with-go">Generating Events with Go</h3>
<p>We’ll begin the development of our backend system by first defining the Event struct and creating a fixed-rate event generator function:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Event <span class="hljs-keyword">struct</span> {
        ID         <span class="hljs-keyword">string</span>
        CreatedAt time.Time
        DeadlineMs  time.Duration
}
</code></pre>
<p>Here we’ve created an <code>Event</code> struct with the following properties:</p>
<ul>
<li><p><code>ID</code> a unique identifier that helps in the management of each event processed by the system</p>
</li>
<li><p><code>CreatedAt</code> to track the time the event was created</p>
</li>
<li><p><code>Deadline</code> to help evaluate if the event met the assigned deadline or if it failed</p>
</li>
</ul>
<p>Next, we’ll create the event generator <code>startGenerator</code> function:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">startGenerator</span><span class="hljs-params">(out <span class="hljs-keyword">chan</span>&lt;- Event)</span></span> {
        ticker := time.NewTicker(<span class="hljs-number">50</span> * time.Millisecond)
        <span class="hljs-keyword">defer</span> ticker.Stop()

        <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> ticker.C {
                event := Event{
                        ID:         uuid.New().String(),
                        CreatedAt:  time.Now(),
                        DeadlineMs: <span class="hljs-number">100</span>,
                }


                <span class="hljs-keyword">select</span> {
                <span class="hljs-keyword">case</span> out &lt;- event:
                <span class="hljs-keyword">default</span>:
                  <span class="hljs-comment">// Drop event when load peaks on the goroutine</span>
                }
        }
}
</code></pre>
<p>Here, the event generator function accepts a <code>Go</code> channel as a parameter and uses a <code>time.Ticker</code> channel that fires every <strong>50 milliseconds</strong>. On each tick, it creates a new <code>Event</code> with a unique<code>ID</code>, a creation timestamp, and a <strong>deadline of 100 milliseconds</strong> (<code>DeadlineMs: 100</code>).</p>
<p>The generator then attempts to send the event into the output channel using a non-blocking send. If the channel is ready, the event is delivered immediately. If the channel is not ready (for example, because downstream consumers are slow or overloaded), the <code>default</code> case is executed, and the event is dropped.</p>
<p>Why do we have to drop the event here? Well, because hiding overload drops real-time guarantees. In short, <strong>dropping events is a deliberate backpressure strategy</strong>: it prevents overload from cascading through the system and protects latency bounds, which is often more important than completeness in real-time streaming systems.</p>
<h3 id="heading-deadline-aware-processing">Deadline-aware Processing</h3>
<p>Next, we’ll create the <code>processEvent</code> function to handle the processing of the event. In Go, you can enforce deadlines by using <code>context.WithTimeout</code> like this:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">processEvent</span><span class="hljs-params">(event Event)</span> <span class="hljs-title">string</span></span> {
        ctx, cancel := context.WithTimeout(
                context.Background(),
                event.Deadline,
        )

        <span class="hljs-keyword">defer</span> cancel()
        workDone := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})

        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
                time.Sleep(<span class="hljs-number">50</span> * time.Millisecond)
                <span class="hljs-built_in">close</span>(workDone)

        }()

        <span class="hljs-keyword">select</span> {
        <span class="hljs-keyword">case</span> &lt;-workDone:
                <span class="hljs-keyword">return</span> <span class="hljs-string">"on-time"</span>
        <span class="hljs-keyword">case</span> &lt;-ctx.Done():
                <span class="hljs-keyword">return</span> <span class="hljs-string">"late"</span>
        }
}
</code></pre>
<p>Here we’ve intentionally ensured that work finishes before the deadline or it fails immediately.</p>
<p>In the <code>processEvent</code> function, each event is processed under a hard deadline enforced by a context with a timeout. The timeout duration is derived directly from the event’s deadline, meaning the event is only considered valid within the specified time window.</p>
<p>The actual work is executed in a separate goroutine, which simulates processing by sleeping for <strong>50 milliseconds</strong> and then signaling completion by closing the <code>workDone</code> channel. We’ve intentionally structured this so that work either completes within the deadline or is treated as a failure immediately.</p>
<h3 id="heading-applying-back-pressure"><strong>Applying Back-Pressure</strong></h3>
<p>In real-time systems, queues do not solve overload – they merely postpone it. When incoming events arrive faster than they can be processed, a queue continues to grow, increasing the time each event spends waiting.</p>
<p>Buffers can also hide failure. By absorbing excess load, they create the illusion that the system is healthy, even as processing delays grow beyond acceptable limits. This hidden degradation is dangerous because the system continues operating in a compromised state, producing results that are technically correct but operationally useless due to lateness.</p>
<p>As queues and buffers grow, latency increases silently. There is often no explicit error or signal that deadlines are being missed – the system simply becomes slower over time. In real-time systems, this silent latency growth is especially harmful because it violates the assumption that results are delivered within a known and bounded time window.</p>
<p>For these reasons, I strongly recommend that you use bounded channels. When the system becomes overwhelmed, bounded channels enforce back-pressure by refusing additional work. Instead of blocking indefinitely or growing unbounded queues, the system drops events when it cannot keep up.</p>
<p>This behavior makes failures visible. Dropped events are an explicit signal that the system is operating beyond its capacity. Rather than degrading unpredictably, the system degrades in a controlled and observable way. In this context, dropping events is a feature, not a bug, because it preserves latency guarantees for the events that do get processed and allows operators to detect, reason about, and respond to overload conditions immediately.</p>
<h3 id="heading-streaming-events-to-the-browser">Streaming Events to the Browser</h3>
<p>Next, we’ll build the Websocket broadcast system to push processed events to the frontend using the <a target="_blank" href="http://github.com/gorilla/websocket">Gorilla</a> <code>Go</code> websocket package (but feel free to use any package of your choice).</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
        <span class="hljs-string">"encoding/json"</span>
        <span class="hljs-string">"net/http"</span>
        <span class="hljs-string">"github.com/gorilla/websocket"</span>
)

<span class="hljs-keyword">var</span> upgrader = websocket.Upgrader{
        CheckOrigin: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(r *http.Request)</span> <span class="hljs-title">bool</span></span> {
                <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
        },
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">wsHandler</span><span class="hljs-params">(out &lt;-<span class="hljs-keyword">chan</span> Event)</span> <span class="hljs-title">http</span>.<span class="hljs-title">HandlerFunc</span></span> {
        <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
                conn, _ := upgrader.Upgrade(w, r, <span class="hljs-literal">nil</span>)
                <span class="hljs-keyword">defer</span> conn.Close()
                <span class="hljs-keyword">for</span> event := <span class="hljs-keyword">range</span> out {
                        data, _ := json.Marshal(event)
                        conn.WriteMessage(websocket.TextMessage, data)
                }
        }
}
</code></pre>
<p>Here, we simply upgrade an incoming <code>HTTP</code> request to a <code>WebSocket</code> connection and continuously reads events from our <code>Event</code> channel we created earlier, serializing each event to <code>JSON</code> and broadcasting it to the connected client. It acts purely as a transport layer, pushing already-processed events to clients with low latency.  </p>
<p>It’s important to note that <code>WebSockets</code> do not make a system real-time. <code>WebSockets</code> merely provide low-latency delivery from the backend to the client. The real-time guarantees are established earlier in the backend pipeline through deliberate design choices: fixed-rate event generation, explicit per-event deadlines, bounded queues, non-blocking sends, deadline-aware processing using contexts, and fail-fast behavior when deadlines are exceeded.</p>
<p>By the time an event is sent over a <code>WebSocket</code>, it has already either met its real-time constraints or been discarded. The <code>WebSocket</code> layer simply transports the result – it doesn’t enforce or create real-time behavior.</p>
<h3 id="heading-consuming-a-websocket-event-react-typescript">Consuming a WebSocket Event (React + TypeScript)</h3>
<p>Up until now, we’ve been building the backend of our real-time event generator and broadcast system. In the next sections, we’ll build the frontend of the system using <code>React</code> and <code>TypeScript</code>.</p>
<p>We’ll start by initializing a <code>WebSocket</code> client to consume incoming events from the backend using the traditional WebSocket browser interface.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> socket = <span class="hljs-keyword">new</span> WebSocket(<span class="hljs-string">"ws://localhost:8080/ws"</span>);
socket.onmessage = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> data = <span class="hljs-built_in">JSON</span>.parse(event.data);
  buffer.push(data);
};
</code></pre>
<p>Here we simply initialize a new <code>WebSocket</code>, passing along the WebSocket URL from the backend. You have to reference the same URL. Instead of rendering every message immediately, it’s recommended that you always batch updates.</p>
<h3 id="heading-making-react-real-time-friendly">Making React Real-Time Friendly</h3>
<p>Next, let’s create a <code>React</code> <code>useRealTimeEvents</code> hook to handle streaming and processing of event broadcasts from the backend. Rendering on every message causes render storms, UI lag, and misleading dashboards. Instead, we render on animation frames.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useEffect, useRef, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { RealTimeEvent } <span class="hljs-keyword">from</span> <span class="hljs-string">'types/types'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useRealTimeEvents</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [events, setEvents] = useState&lt;RealTimeEvent[]&gt;([]);
  <span class="hljs-keyword">const</span> buffer = useRef&lt;RealTimeEvent[]&gt;([]);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> ws = <span class="hljs-keyword">new</span> WebSocket(<span class="hljs-string">'ws://localhost:8080/ws'</span>);
    ws.onmessage = <span class="hljs-function">(<span class="hljs-params">msg</span>) =&gt;</span> {
      buffer.current.push(<span class="hljs-built_in">JSON</span>.parse(msg.data));
    };

    <span class="hljs-keyword">let</span> raf: <span class="hljs-built_in">number</span>;

    <span class="hljs-keyword">const</span> flush = <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">if</span> (buffer.current.length &gt; <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">const</span> pendingEvents = buffer.current.slice(<span class="hljs-number">0</span>);

        setEvents(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> {
          <span class="hljs-keyword">const</span> next = [...prev, ...pendingEvents];
          <span class="hljs-keyword">return</span> next.slice(<span class="hljs-number">-50</span>);
        });
      }
      raf = requestAnimationFrame(flush);
    };

    raf = requestAnimationFrame(flush);

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      ws.close();
      cancelAnimationFrame(raf);
    };
  }, []);
  <span class="hljs-keyword">return</span> events;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> useRealTimeEvents;
</code></pre>
<p>The UI is part of the real-time system. It’s important to note that the system broadcasts messages to the frontend in milliseconds. This behavior already crosses the web browser refresh rate threshold.</p>
<p>In certain situations, you might even guess that the use of <code>setTime</code> should come in handy here. And that’s a good alternative – but there’s a better solution: using <code>requestAnimationFrame()</code>.</p>
<p>The <code>requestAnimationFrame()</code> takes in a callback function <code>flush</code> which is regulated by the animation frame, ensuring that we don’t cross the refresh rate threshold before the next repaint. You can learn more about it <code>requestAnimationFrame()</code> <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope/requestAnimationFrame">here</a>.</p>
<h3 id="heading-creating-the-statsbar-component">Creating the StatsBar Component</h3>
<p>Next, let’s create a little <code>statusBar</code> component to show events that arrived within the deadline and those that came in late.</p>
<p>Create a new component <code>StatsBar</code> and add the code below:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { <span class="hljs-keyword">type</span> FC } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { RealTimeEvent } <span class="hljs-keyword">from</span> <span class="hljs-string">'types/types'</span>;

<span class="hljs-keyword">const</span> StatsBar: FC&lt;{ events: RealTimeEvent[] }&gt; = <span class="hljs-function">(<span class="hljs-params">{ events }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> late = events.filter(<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> e.status === <span class="hljs-string">'late'</span>).length;
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"flex flex-row gap-2 bg-gray-500 w-full py-2.5 px-2"</span>&gt;
      &lt;strong&gt;Events: {events.length} | &lt;/strong&gt;
      &lt;strong&gt;Late: {late}&lt;/strong&gt;
    &lt;/div&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> StatsBar;
</code></pre>
<p>Here we are creating a minimal stats component to show the total number of events and those that arrived late by looping through the incoming event list whose statuses are late.</p>
<h3 id="heading-creating-the-events-table">Creating the Events Table</h3>
<p>Next, we’ll create a <code>EventTable</code> component to display the events.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { <span class="hljs-keyword">type</span> FC } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { RealTimeEvent } <span class="hljs-keyword">from</span> <span class="hljs-string">'types/types'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> EventsTable: FC&lt;{ events: RealTimeEvent[] }&gt; = <span class="hljs-function">(<span class="hljs-params">{ events }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> formatDate = <span class="hljs-function">(<span class="hljs-params">date: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(date).toLocaleString();
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"w-full"</span>&gt;
      &lt;div className=<span class="hljs-string">"relative overflow-x-auto bg-neutral-primary-soft shadow-xs rounded-base border border-default"</span>&gt;
        &lt;table className=<span class="hljs-string">"w-full text-sm text-left rtl:text-right text-body"</span>&gt;
          &lt;thead className=<span class="hljs-string">"text-sm text-body bg-neutral-secondary-soft border-b rounded-base border-default"</span>&gt;
            &lt;tr&gt;
              &lt;th scope=<span class="hljs-string">"col"</span> className=<span class="hljs-string">"px-6 py-3 font-medium"</span>&gt;
                ID
              &lt;/th&gt;
              &lt;th scope=<span class="hljs-string">"col"</span> className=<span class="hljs-string">"px-6 py-3 font-medium"</span>&gt;
                Satus
              &lt;/th&gt;
              &lt;th scope=<span class="hljs-string">"col"</span> className=<span class="hljs-string">"px-6 py-3 font-medium"</span>&gt;
                Created At
              &lt;/th&gt;
              &lt;th scope=<span class="hljs-string">"col"</span> className=<span class="hljs-string">"px-6 py-3 font-medium"</span>&gt;
                Processed At
              &lt;/th&gt;
              &lt;th scope=<span class="hljs-string">"col"</span> className=<span class="hljs-string">"px-6 py-3 font-medium"</span>&gt;
                Deadline
              &lt;/th&gt;
            &lt;/tr&gt;
          &lt;/thead&gt;
          &lt;tbody&gt;
            {events.map(<span class="hljs-function">(<span class="hljs-params">e, i</span>) =&gt;</span> (
              &lt;tr
                key={e.id + i}
                className=<span class="hljs-string">"bg-neutral-primary border-b border-default"</span>
              &gt;
                &lt;td className=<span class="hljs-string">"px-6 py-4"</span>&gt;{e.id.slice(<span class="hljs-number">0</span>, <span class="hljs-number">6</span>)}&lt;/td&gt;
                &lt;td className=<span class="hljs-string">"px-6 py-4"</span>&gt;{e.status}&lt;/td&gt;
                &lt;td className=<span class="hljs-string">"px-6 py-4"</span>&gt;{formatDate(e.createdAt)}&lt;/td&gt;
                &lt;td className=<span class="hljs-string">"px-6 py-4"</span>&gt;{formatDate(e.processedAt)}&lt;/td&gt;
                &lt;td className=<span class="hljs-string">"px-6 py-4"</span>&gt;{e.deadlineMs}&lt;/td&gt;
              &lt;/tr&gt;
            ))}
          &lt;/tbody&gt;
        &lt;/table&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<p>Here we loop through all incoming events and display the event’s <code>id</code>, <code>status</code>, and <code>deadline</code>. These metrics will help us gain insight into the performance of our real-time events broadcast system.</p>
<h3 id="heading-putting-it-all-together">Putting it All Together</h3>
<p>At this point, we have implemented a complete, end-to-end real-time application that connects a deadline-aware <code>Go</code> backend to a lightweight <code>React</code> frontend. On the backend, events are generated at a fixed rate, processed under explicit deadlines, and dropped when the system is under load to preserve real-time guarantees. Only events that meet these guarantees are forwarded to connected clients via <code>WebSocket</code> broadcast.</p>
<p>On the frontend, we’ve built the <code>useRealtimeEvents</code> hook to establish a persistent <code>WebSocket</code> connection and continuously stream events from the backend as they arrive. The <code>StatsBar</code> component provides immediate visibility into the system’s behavior by summarizing key characteristics of the event stream, while the <code>EventTable</code> component renders individual events in the order they are received. Together, these components clearly mirror the behavior of the system under normal conditions in real-time.</p>
<p>With both the frontend and backend components in place, the application now functions as a real-time monitor. The backend enforces timelines and correctness, and the frontend simply reflects the outcome of those decisions in real-time. There is no buffering or replay logic on the client side – what’s displayed on the <code>UI</code> is exactly what the system was able to process within the specified deadline.</p>
<p>Finally, replace your <code>Welcome</code> component with the code below to display the <code>StatusBar</code> and <code>EventsTable</code> component.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useRealtimeEvents } <span class="hljs-keyword">from</span> <span class="hljs-string">"./hooks/useRealtimeEvents"</span>;
<span class="hljs-keyword">import</span> { StatsBar } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/StatsBar"</span>;
<span class="hljs-keyword">import</span> { EventTable } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/EventTable"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> events = useRealtimeEvents();

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;h1&gt;Real-Time Event Monitor&lt;/h1&gt;
      &lt;StatsBar events={events} /&gt;
      &lt;EventTable events={events} /&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>The React frontend is scaffolded using <a target="_blank" href="https://react.dev/learn/creating-a-react-app">create-react-app</a>, but the same approach can be used for other frameworks, such as <code>Next.js</code> or <code>Vite</code>. The complete source code, including the frontend and backend, is available in the repository <a target="_blank" href="https://github.com/emmanueletukudo/realtime-go-react">here</a>. You can reach out to me on the <a target="_blank" href="https://x.com/eetukudo_">X platform</a> if you need my assistance.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>If you’ve followed this tutorial up to this point, congratulations! You’ve learnt the most critical part of building resilient deadline-aware real-time systems.</p>
<p>Remember, real-time systems are not about being fast, but about how <strong>predictable they are.</strong> You don’t need a Real-Time Operating System (RTOS), a PhD, or specialized hardware to start learning real-time design.</p>
<p>All you need to excel is to respect time, bound your resources, and accept that sometimes, dropping data is the correct behavior. If you understand that, you’re already thinking like a real-time systems engineer.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Smart HomeKit Virtual Light in Go ]]>
                </title>
                <description>
                    <![CDATA[ Recently, I wanted to understand how smart home devices actually work. When you scan a QR code and a light appears in your Home app, what's really happening? When you tap "on", what bytes travel across your network? The best way I know to understand... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-smart-homekit-virtual-light-in-go/</link>
                <guid isPermaLink="false">69449f453cbff85be8965f37</guid>
                
                    <category>
                        <![CDATA[ HAP ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ homekit ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Apple ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Matter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ protocols ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rez Moss ]]>
                </dc:creator>
                <pubDate>Fri, 19 Dec 2025 00:41:41 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766104866742/8d8c0158-95d0-493b-a311-cd99189654e1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Recently, I wanted to understand how smart home devices actually work. When you scan a QR code and a light appears in your Home app, what's really happening? When you tap "on", what bytes travel across your network?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765414117210/e65d5768-792d-4aae-8669-db0ac7a9c60d.png" alt="Virtual HomeKit Light QR code" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The best way I know to understand something is to build it, so I created a virtual HomeKit light in Go. And in this tutorial, I’ll walk you through how I went about it. We’ll pull back the curtain on smart home protocols so you understand how they work, in depth. Let’s dive in.</p>
<h3 id="heading-what-well-cover">What we’ll cover:</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-homekit-actually-is">What HomeKit Actually Is</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-smart-home-protocol-landscape">The Smart Home Protocol Landscape</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-homekit-discovery-works">How HomeKit Discovery Works</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-pairing-process-what-happens-when-you-scan-the-qr-code">The Pairing Process: What Happens When You Scan the QR Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-setup-uri-whats-in-that-qr-code">The Setup URI: What's in That QR Code?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-happens-when-you-toggle-the-light">What Happens When You Toggle the Light</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-accessory-database-model">The Accessory Database Model</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-persisting-pairing-data">Persisting Pairing Data</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-event-notifications">Event Notifications</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-complete-implementation">The Complete Implementation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-i-learned">What I Learned</a></p>
</li>
</ol>
<h2 id="heading-what-youll-need">What You'll Need</h2>
<p>Before we start building, let's make sure you have the right setup. This project requires two things:</p>
<ol>
<li><p><strong>Go 1.21 or later</strong>: We're using some modern Go features, and the brutella/HAP library works best with recent versions. You can check your version with <code>go version</code>. If you need to upgrade, grab the latest from go.dev</p>
</li>
<li><p><strong>An Apple HomeKit environment</strong>: This means an iPhone or iPad running iOS 15+ with the Home app. You'll also want to be on the same WiFi network as the machine running your virtual light. HomeKit is entirely local, so your phone needs to be able to reach your development machine directly.</p>
</li>
</ol>
<p>One thing that tripped me up initially is that if you’re running this on a Linux server or inside a container, make sure mDNS traffic isn’t being blocked. Your firewall needs to allow UDP port 5353 (for mDNS discovery) and whatever port your accessory runs on (we'll use 51826). On a Mac this usually just works.</p>
<h2 id="heading-what-homekit-actually-is">What HomeKit Actually Is</h2>
<p>HomeKit is Apple's smart home framework. It's comprised of three things:</p>
<ol>
<li><p><strong>a protocol (HAP)</strong> that defines how devices talk to each other,</p>
</li>
<li><p><strong>a security model</strong> that encrypts and authenticates everything,</p>
</li>
<li><p><strong>and an ecosystem</strong> (the Home app, Siri, automations)</p>
</li>
</ol>
<p>Here, we’ll be focused on the protocol layer. We're building something that speaks HAP well enough that Apple's ecosystem accepts it as a real accessory.</p>
<h2 id="heading-the-smart-home-protocol-landscape">The Smart Home Protocol Landscape</h2>
<p>Before getting started, let's understand what we're dealing with. There are two protocols at play here:</p>
<ol>
<li><p><strong>HomeKit Accessory Protocol (HAP):</strong> Apple's original smart home protocol from 2014. It runs over your local WiFi network, uses mDNS for discovery, and encrypts everything with Curve25519 and ChaCha20-Poly1305. Every HomeKit device you've ever used speaks HAP.</p>
</li>
<li><p><strong>Matter</strong>: The new industry standard (2022) backed by Apple, Google, Amazon, and others. Matter is actually built on many of the same cryptographic primitives as HAP. When Apple added Matter support, they essentially made HomeKit bilingual, as it can speak both protocols.</p>
</li>
</ol>
<p>Here's what's interesting: Matter devices that connect to Apple Home still end up being controlled through HomeKit's infrastructure. Matter is the pairing and discovery layer, but once a device is in your Home, Apple's ecosystem takes over.</p>
<p>For this project, I'm using the HAP protocol directly via the <code>brutella/hap</code> library. This lets us see exactly what's happening without Matter's additional abstraction layer.</p>
<h2 id="heading-how-homekit-discovery-works">How HomeKit Discovery Works</h2>
<p>When you run a HomeKit accessory on your network, it doesn't just sit there waiting. It actively announces itself using <strong>mDNS</strong> (multicast DNS), also called Bonjour on Apple platforms.</p>
<p>The accessory broadcasts a service record that looks like this:</p>
<pre><code class="lang-plaintext">_hap._tcp.local.
  name: Virtual Light._hap._tcp.local.
  port: 51826
  txt: 
    c#=1          // config number (changes trigger rediscovery)
    ff=0          // feature flags
    id=XX:XX:XX   // device ID (like a MAC address)
    md=Virtual Light  // model name
    pv=1.1        // protocol version
    s#=1          // state number
    sf=1          // status flag (1=not paired, 0=paired)
    ci=5          // category (5=lightbulb)
    sh=XXXXXX     // setup hash
</code></pre>
<p>Your iPhone is constantly listening for <code>_hap._tcp.local.</code> broadcasts. When it sees one with <code>sf=1</code> (unpaired), it shows up in "Add Accessory" as available.</p>
<p>Let's see this in code. Here's the minimal server setup:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"log"</span>

    <span class="hljs-string">"github.com/brutella/hap"</span>
    <span class="hljs-string">"github.com/brutella/hap/accessory"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    light := accessory.NewLightbulb(accessory.Info{
        Name:         <span class="hljs-string">"Virtual Light"</span>,
        Manufacturer: <span class="hljs-string">"My Smart Home"</span>,
    })

    server, err := hap.NewServer(hap.NewFsStore(<span class="hljs-string">"./data"</span>), light.A)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }

    server.Pin = <span class="hljs-string">"00102003"</span>
    server.Addr = <span class="hljs-string">":51826"</span>

    server.ListenAndServe(context.Background())
}
</code></pre>
<p>When <code>ListenAndServe</code> runs, it:</p>
<ol>
<li><p>Generates a unique device ID if one doesn't exist</p>
</li>
<li><p>Starts listening on port 51826</p>
</li>
<li><p>Registers the mDNS service record</p>
</li>
<li><p>Waits for connections</p>
</li>
</ol>
<p>At this point, your iPhone can discover it. But what happens when you try to pair it?</p>
<h2 id="heading-the-pairing-process-what-happens-when-you-scan-the-qr-code">The Pairing Process: What Happens When You Scan the QR Code</h2>
<p>This is where it gets interesting. HomeKit uses the <strong>SRP (Secure Remote Password)</strong> protocol for pairing. It’s the same protocol used in things like 1Passwords authentication.</p>
<p>When you scan the QR code or enter the PIN, here's the actual sequence:</p>
<h3 id="heading-step-1-pair-setup-m1-ios-accessory">Step 1: Pair Setup M1 (iOS → Accessory)</h3>
<pre><code class="lang-plaintext">iOS sends: { method: "pair-setup", state: 1 }
</code></pre>
<p>Your phone initiates pairing, telling the accessory "I want to pair with you."</p>
<h3 id="heading-step-2-pair-setup-m2-accessory-ios">Step 2: Pair Setup M2 (Accessory → iOS)</h3>
<pre><code class="lang-plaintext">Accessory sends: { 
  state: 2,
  salt: &lt;16 random bytes&gt;,
  public_key: &lt;SRP public key B&gt;
}
</code></pre>
<p>The accessory generates an SRP salt and public key. The PIN code you entered isn't sent over the network – instead, it's used to derive a verifier locally.</p>
<h3 id="heading-step-3-pair-setup-m3-ios-accessory">Step 3: Pair Setup M3 (iOS → Accessory)</h3>
<pre><code class="lang-plaintext">iOS sends: {
  state: 3,
  public_key: &lt;SRP public key A&gt;,
  proof: &lt;SRP proof M1&gt;
}
</code></pre>
<p>Your iPhone uses the PIN to compute its own SRP values and sends a proof that it knows the PIN.</p>
<h3 id="heading-step-4-pair-setup-m4-accessory-ios">Step 4: Pair Setup M4 (Accessory → iOS)</h3>
<pre><code class="lang-plaintext">Accessory sends: {
  state: 4,
  proof: &lt;SRP proof M2&gt;
}
</code></pre>
<p>The accessory verifies the proof. If the PIN was wrong, pairing fails here. If correct, it sends its own proof back.</p>
<h3 id="heading-step-5-6-key-exchange">Step 5-6: Key Exchange</h3>
<p>Now both sides have a shared secret derived from SRP. They use this to establish an encrypted channel and exchange long term Ed25519 public keys. These keys are stored permanently. This is why your lights still work after rebooting your router.</p>
<p>The whole dance takes about 2 seconds. After this, <code>sf</code> in the mDNS record changes from <code>1</code> to <code>0</code> and the accessory disappears from "Add Accessory".</p>
<h2 id="heading-the-setup-uri-whats-in-that-qr-code">The Setup URI: What's in That QR Code?</h2>
<p>The QR code contains a URI that encodes everything needed for pairing:</p>
<pre><code class="lang-plaintext">X-HM://0ABCDEFGH1234
        ^^^^^^^^^^^^
        |       |
        |       +-- Setup ID (4 chars)
        +---------- Encoded payload (9 chars, base-36)
</code></pre>
<p>The payload packs three things into 45 bits:</p>
<ol>
<li><p><strong>Category:</strong> what type of accessory this is (5 = lightbulb, 6 = outlet, 10 = thermostat, and so on)</p>
</li>
<li><p><strong>Flags:</strong> how the accessory can pair (2 = supports IP ,wifi pairing , 4 = supports BLE pairing , 6 = supports both)</p>
</li>
<li><p><strong>PIN code</strong> as integer</p>
</li>
</ol>
<p>This lets your iPhone know what icon to show and the PIN to use, all from scanning a single QR code.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">generateSetupURI</span><span class="hljs-params">(pin, setupID <span class="hljs-keyword">string</span>, category <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-comment">// PIN "00102003" becomes integer 102003</span>
    <span class="hljs-keyword">var</span> pinInt <span class="hljs-keyword">uint64</span>
    <span class="hljs-keyword">for</span> _, c := <span class="hljs-keyword">range</span> pin {
        <span class="hljs-keyword">if</span> c &gt;= <span class="hljs-string">'0'</span> &amp;&amp; c &lt;= <span class="hljs-string">'9'</span> {
            pinInt = pinInt*<span class="hljs-number">10</span> + <span class="hljs-keyword">uint64</span>(c-<span class="hljs-string">'0'</span>)
        }
    }

    <span class="hljs-comment">// Bit layout:</span>
    <span class="hljs-comment">// [39:32] = category (5 = lightbulb)</span>
    <span class="hljs-comment">// [31:28] = flags (2 = IP pairing supported)</span>
    <span class="hljs-comment">// [26:0]  = PIN code</span>
    payload := (<span class="hljs-keyword">uint64</span>(category) &lt;&lt; <span class="hljs-number">32</span>) | (<span class="hljs-number">2</span> &lt;&lt; <span class="hljs-number">28</span>) | (pinInt &amp; <span class="hljs-number">0x7FFFFFF</span>)

    <span class="hljs-comment">// Encode as base-36 (0-9, A-Z)</span>
    <span class="hljs-keyword">const</span> chars = <span class="hljs-string">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</span>
    encoded := <span class="hljs-string">""</span>
    <span class="hljs-keyword">for</span> payload &gt; <span class="hljs-number">0</span> {
        encoded = <span class="hljs-keyword">string</span>(chars[payload%<span class="hljs-number">36</span>]) + encoded
        payload /= <span class="hljs-number">36</span>
    }

    <span class="hljs-keyword">for</span> <span class="hljs-built_in">len</span>(encoded) &lt; <span class="hljs-number">9</span> {
        encoded = <span class="hljs-string">"0"</span> + encoded
    }

    <span class="hljs-keyword">return</span> <span class="hljs-string">"X-HM://"</span> + encoded + setupID
}
</code></pre>
<p>When your iPhone camera sees <code>X-HM://</code>, it knows this is a HomeKit code. It decodes the payload to extract the category (so it can show the right icon) and the PIN (so you don't have to type it). The setup ID helps with identification when multiple unpaired accessories are on the network.</p>
<h2 id="heading-what-happens-when-you-toggle-the-light">What Happens When You Toggle the Light</h2>
<p>Now for the part I was most curious about. When you tap the light button in the Home app, what actually travels across your network?</p>
<h3 id="heading-step-1-encrypted-session">Step 1: Encrypted Session</h3>
<p>Your iPhone doesn't just send commands in plaintext. Every paired session uses the longterm keys exchanged during pairing to establish a session key. All communication is encrypted with ChaCha20Poly1305.</p>
<h3 id="heading-step-2-hap-request">Step 2: HAP Request</h3>
<p>Inside the encrypted channel, HomeKit uses a simple HTTP like protocol. A "turn on" command looks like this:</p>
<pre><code class="lang-plaintext">PUT /characteristics HTTP/1.1
Host: Virtual Light._hap._tcp.local
Content-Type: application/hap+json

{
  "characteristics": [{
    "aid": 1,        // accessory ID
    "iid": 10,       // instance ID (the "On" characteristic)
    "value": true    // new state
  }]
}
</code></pre>
<h3 id="heading-step-3-accessory-response">Step 3: Accessory Response</h3>
<p>The accessory processes the request and responds like this:</p>
<pre><code class="lang-plaintext">HTTP/1.1 204 No Content
</code></pre>
<p>If something went wrong, it'll return a status object with an error code.</p>
<p>In our Go code, we hook into this with a callback:</p>
<pre><code class="lang-go">light.Lightbulb.On.OnValueRemoteUpdate(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(on <span class="hljs-keyword">bool</span>)</span></span> {
    <span class="hljs-keyword">if</span> on {
        fmt.Println(<span class="hljs-string">"💡 Light ON"</span>)
    } <span class="hljs-keyword">else</span> {
        fmt.Println(<span class="hljs-string">"💡 Light OFF"</span>)
    }
})
</code></pre>
<p>This callback fires when the <code>value</code> in that PUT request changes. The <code>brutella/hap</code> library handles all the decryption, parsing, and response generation.</p>
<h2 id="heading-the-accessory-database-model">The Accessory Database Model</h2>
<p>HomeKit organizes everything into a hierarchy:</p>
<pre><code class="lang-plaintext">Accessory (aid=1)
└── Services
    ├── AccessoryInformation (iid=1)
    │   ├── Name (iid=2)
    │   ├── Manufacturer (iid=3)
    │   ├── Model (iid=4)
    │   └── SerialNumber (iid=5)
    │
    └── Lightbulb (iid=9)
        ├── On (iid=10)           ← boolean
        ├── Brightness (iid=11)   ← int 0-100
        └── Hue (iid=12)          ← float 0-360
</code></pre>
<p>Each characteristic has an <code>iid</code> (instance ID). When you change brightness to 75%, the PUT request targets <code>aid=1, iid=11, value=75</code>.</p>
<p>This model is why HomeKit accessories are interoperable. Every lightbulb, regardless of manufacturer, has the same characteristic structure.</p>
<h2 id="heading-persisting-pairing-data">Persisting Pairing Data</h2>
<p>When your accessory pairs with a controller (iPhone), it stores:</p>
<ul>
<li><p>The controller's Ed25519 public key</p>
</li>
<li><p>A controller ID (36chars UUID)</p>
</li>
<li><p>Permission level (admin or regular user)</p>
</li>
</ul>
<p>The accessory also has its own keypairs that must persist across restarts. If you lose this, all paired controllers become orphaned – that is, they think they’re paired, but the accessory doesn't recognize them.</p>
<p>As mentioned earlier, we need to save pairing info so that if the app/device restarts, it can communicate with Homekit again. You could use a database, but for a single accessory, a JSON file works fine. If the process crashes mid-session, you won’t lose pairing data.</p>
<p>I wrote a simple JSON store to keep everything in one file:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> JSONStore <span class="hljs-keyword">struct</span> {
    path <span class="hljs-keyword">string</span>
    data <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>][]<span class="hljs-keyword">byte</span>
    mu   sync.RWMutex
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *JSONStore)</span> <span class="hljs-title">Set</span><span class="hljs-params">(key <span class="hljs-keyword">string</span>, value []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-title">error</span></span> {
    s.mu.Lock()
    <span class="hljs-keyword">defer</span> s.mu.Unlock()
    s.data[key] = value
    <span class="hljs-keyword">return</span> s.save()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *JSONStore)</span> <span class="hljs-title">Get</span><span class="hljs-params">(key <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">([]<span class="hljs-keyword">byte</span>, error)</span></span> {
    s.mu.RLock()
    <span class="hljs-keyword">defer</span> s.mu.RUnlock()
    <span class="hljs-keyword">if</span> v, ok := s.data[key]; ok {
        <span class="hljs-keyword">return</span> v, <span class="hljs-literal">nil</span>
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, fmt.Errorf(<span class="hljs-string">"key not found: %s"</span>, key)
}
</code></pre>
<p>The HAP library stores several keys:</p>
<ul>
<li><p><code>uuid</code> – accessory's unique identifier</p>
</li>
<li><p><code>public</code> / <code>private</code> – Ed25519 keypair</p>
</li>
<li><p><code>*-pairings</code> – paired controller keys</p>
</li>
</ul>
<p>If you delete this JSON file, the accessory (our virtual-light) forgets all its paired controllers. Your iPhone still thinks it's paired, but the accessory doesn't recognize it anymore – you'll see "No Response" in the Home app. The fix removes the accessory from the Home app and pairs it fresh using the QR code again.</p>
<h2 id="heading-event-notifications">Event Notifications</h2>
<p>One thing I didn't expect is that HomeKit supports push notifications from accessories. When our light state changes (maybe from a physical switch), we can notify all connected controllers:</p>
<pre><code class="lang-go">light.Lightbulb.On.SetValue(<span class="hljs-literal">true</span>)  <span class="hljs-comment">// This triggers notifications</span>
</code></pre>
<p>Under the hood, the accessory maintains persistent connections with controllers. When a characteristic changes, it sends an EVENT message:</p>
<pre><code class="lang-plaintext">EVENT/1.0 200 OK
Content-Type: application/hap+json

{
  "characteristics": [{
    "aid": 1,
    "iid": 10,
    "value": true
  }]
}
</code></pre>
<p>This is how your Home app updates in realtime when someone else turns on a light.</p>
<h2 id="heading-the-complete-implementation">The Complete Implementation</h2>
<p>Here's everything together:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"encoding/json"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"os"</span>
    <span class="hljs-string">"os/signal"</span>
    <span class="hljs-string">"sync"</span>
    <span class="hljs-string">"syscall"</span>

    <span class="hljs-string">"github.com/brutella/hap"</span>
    <span class="hljs-string">"github.com/brutella/hap/accessory"</span>
    <span class="hljs-string">"github.com/skip2/go-qrcode"</span>
)

<span class="hljs-keyword">const</span> (
    pinCode  = <span class="hljs-string">"00102003"</span>
    setupID  = <span class="hljs-string">"VLTX"</span>
    category = <span class="hljs-number">5</span>
    dbFile   = <span class="hljs-string">"data.json"</span>
)

<span class="hljs-keyword">type</span> JSONStore <span class="hljs-keyword">struct</span> {
    path <span class="hljs-keyword">string</span>
    data <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>][]<span class="hljs-keyword">byte</span>
    mu   sync.RWMutex
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewJSONStore</span><span class="hljs-params">(path <span class="hljs-keyword">string</span>)</span> *<span class="hljs-title">JSONStore</span></span> {
    s := &amp;JSONStore{
        path: path,
        data: <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>][]<span class="hljs-keyword">byte</span>),
    }
    s.load()
    <span class="hljs-keyword">return</span> s
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *JSONStore)</span> <span class="hljs-title">load</span><span class="hljs-params">()</span></span> {
    file, err := os.ReadFile(s.path)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span>
    }
    json.Unmarshal(file, &amp;s.data)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *JSONStore)</span> <span class="hljs-title">save</span><span class="hljs-params">()</span> <span class="hljs-title">error</span></span> {
    file, err := json.MarshalIndent(s.data, <span class="hljs-string">""</span>, <span class="hljs-string">"  "</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }
    <span class="hljs-keyword">return</span> os.WriteFile(s.path, file, <span class="hljs-number">0644</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *JSONStore)</span> <span class="hljs-title">Set</span><span class="hljs-params">(key <span class="hljs-keyword">string</span>, value []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-title">error</span></span> {
    s.mu.Lock()
    <span class="hljs-keyword">defer</span> s.mu.Unlock()
    s.data[key] = value
    <span class="hljs-keyword">return</span> s.save()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *JSONStore)</span> <span class="hljs-title">Get</span><span class="hljs-params">(key <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">([]<span class="hljs-keyword">byte</span>, error)</span></span> {
    s.mu.RLock()
    <span class="hljs-keyword">defer</span> s.mu.RUnlock()
    <span class="hljs-keyword">if</span> v, ok := s.data[key]; ok {
        <span class="hljs-keyword">return</span> v, <span class="hljs-literal">nil</span>
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, fmt.Errorf(<span class="hljs-string">"key not found: %s"</span>, key)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *JSONStore)</span> <span class="hljs-title">Delete</span><span class="hljs-params">(key <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    s.mu.Lock()
    <span class="hljs-keyword">defer</span> s.mu.Unlock()
    <span class="hljs-built_in">delete</span>(s.data, key)
    <span class="hljs-keyword">return</span> s.save()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *JSONStore)</span> <span class="hljs-title">KeysWithSuffix</span><span class="hljs-params">(suffix <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">([]<span class="hljs-keyword">string</span>, error)</span></span> {
    s.mu.RLock()
    <span class="hljs-keyword">defer</span> s.mu.RUnlock()
    <span class="hljs-keyword">var</span> keys []<span class="hljs-keyword">string</span>
    <span class="hljs-keyword">for</span> k := <span class="hljs-keyword">range</span> s.data {
        <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(k) &gt;= <span class="hljs-built_in">len</span>(suffix) &amp;&amp; k[<span class="hljs-built_in">len</span>(k)-<span class="hljs-built_in">len</span>(suffix):] == suffix {
            keys = <span class="hljs-built_in">append</span>(keys, k)
        }
    }
    <span class="hljs-keyword">return</span> keys, <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">generateSetupURI</span><span class="hljs-params">(pin, setupID <span class="hljs-keyword">string</span>, category <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">var</span> pinInt <span class="hljs-keyword">uint64</span>
    <span class="hljs-keyword">for</span> _, c := <span class="hljs-keyword">range</span> pin {
        <span class="hljs-keyword">if</span> c &gt;= <span class="hljs-string">'0'</span> &amp;&amp; c &lt;= <span class="hljs-string">'9'</span> {
            pinInt = pinInt*<span class="hljs-number">10</span> + <span class="hljs-keyword">uint64</span>(c-<span class="hljs-string">'0'</span>)
        }
    }

    payload := (<span class="hljs-keyword">uint64</span>(category) &lt;&lt; <span class="hljs-number">32</span>) | (<span class="hljs-number">2</span> &lt;&lt; <span class="hljs-number">28</span>) | (pinInt &amp; <span class="hljs-number">0x7FFFFFF</span>)

    <span class="hljs-keyword">const</span> chars = <span class="hljs-string">"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</span>
    encoded := <span class="hljs-string">""</span>
    <span class="hljs-keyword">for</span> payload &gt; <span class="hljs-number">0</span> {
        encoded = <span class="hljs-keyword">string</span>(chars[payload%<span class="hljs-number">36</span>]) + encoded
        payload /= <span class="hljs-number">36</span>
    }

    <span class="hljs-keyword">for</span> <span class="hljs-built_in">len</span>(encoded) &lt; <span class="hljs-number">9</span> {
        encoded = <span class="hljs-string">"0"</span> + encoded
    }

    <span class="hljs-keyword">return</span> <span class="hljs-string">"X-HM://"</span> + encoded + setupID
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    light := accessory.NewLightbulb(accessory.Info{
        Name:         <span class="hljs-string">"Virtual Light"</span>,
        Manufacturer: <span class="hljs-string">"My Smart Home"</span>,
    })

    light.Lightbulb.On.OnValueRemoteUpdate(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(on <span class="hljs-keyword">bool</span>)</span></span> {
        <span class="hljs-keyword">if</span> on {
            fmt.Println(<span class="hljs-string">"💡 Light ON"</span>)
        } <span class="hljs-keyword">else</span> {
            fmt.Println(<span class="hljs-string">"💡 Light OFF"</span>)
        }
    })

    store := NewJSONStore(dbFile)

    server, err := hap.NewServer(store, light.A)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }

    server.Pin = pinCode
    server.SetupId = setupID
    server.Addr = <span class="hljs-string">":51826"</span>

    fmt.Println(<span class="hljs-string">"=============================================="</span>)
    fmt.Println(<span class="hljs-string">"       Virtual HomeKit Light"</span>)
    fmt.Println(<span class="hljs-string">"=============================================="</span>)
    fmt.Println(<span class="hljs-string">"PIN: 001-02-003"</span>)
    fmt.Println()

    setupURI := generateSetupURI(pinCode, setupID, category)
    <span class="hljs-keyword">if</span> qr, err := qrcode.New(setupURI, qrcode.Medium); err == <span class="hljs-literal">nil</span> {
        fmt.Println(qr.ToSmallString(<span class="hljs-literal">false</span>))
    }

    fmt.Println(<span class="hljs-string">"Manual: Home app → + → More Options → Virtual Light"</span>)
    fmt.Printf(<span class="hljs-string">"Data stored in: %s\n"</span>, dbFile)
    fmt.Println(<span class="hljs-string">"=============================================="</span>)

    ctx, cancel := context.WithCancel(context.Background())
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        c := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> os.Signal, <span class="hljs-number">1</span>)
        signal.Notify(c, os.Interrupt, syscall.SIGTERM)
        &lt;-c
        cancel()
    }()

    fmt.Println(<span class="hljs-string">"Running... (Ctrl+C to stop)"</span>)
    server.ListenAndServe(ctx)
}
</code></pre>
<p>Run it, pair it, and watch the terminal as you toggle from your phone. Each "💡 Light ON" is the end of an encrypted request that traveled from your phone, through your router, to this Go process.</p>
<h2 id="heading-what-i-learned">What I Learned</h2>
<p>Building this cleared up several things I'd been fuzzy on:</p>
<ol>
<li><p><strong>HomeKit is entirely local.</strong> There are no cloud servers involved in controlling devices – your commands go directly from phone to device over your LAN. This is why HomeKit devices work when your internet is down.</p>
</li>
<li><p><strong>The security model is solid.</strong> SRP for pairing means the PIN never crosses the network. Ed25519 + ChaCha20 for sessions means that even someone sniffing your WiFi sees only encrypted blobs.</p>
</li>
<li><p><strong>Matter doesn't replace HAP.</strong> At least not in Apple's ecosystem. Matter handles discovery and pairing across ecosystems, but Apple Home still uses HAP concepts internally.</p>
</li>
<li><p><strong>The protocol is HTTPish.</strong> Once you get past the encryption, it’s just PUT/GET requests with JSON bodies – surprisingly approachable.</p>
</li>
</ol>
<h3 id="heading-thanks-for-reading">Thanks for reading!</h3>
<p>The <a target="_blank" href="https://github.com/rezmoss/virtual-light">code is here</a> if you want to experiment yourself. You could try adding brightness control, or create a switch instead of a light. The best way to understand a protocol is to speak it ;)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an LSM Tree Storage Engine from Scratch – Full Handbook ]]>
                </title>
                <description>
                    <![CDATA[ Databases are one of the most important parts of a software system. They allow us to store huge amounts of data in an organized way and retrieve it efficiently when we need it. In the early days, when the volume of data was relatively small, engineer... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-an-lsm-tree-storage-engine-from-scratch-handbook/</link>
                <guid isPermaLink="false">6944631e80f40a442d1799df</guid>
                
                    <category>
                        <![CDATA[ Databases ]]>
                    </category>
                
                    <category>
                        <![CDATA[ lsmtree ]]>
                    </category>
                
                    <category>
                        <![CDATA[ storage solutions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ heap ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ramesh Sinha ]]>
                </dc:creator>
                <pubDate>Thu, 18 Dec 2025 20:25:02 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766089431510/433ff03f-8aca-4a87-82d3-0b6d6c1f371c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Databases are one of the most important parts of a software system. They allow us to store huge amounts of data in an organized way and retrieve it efficiently when we need it.</p>
<p>In the early days, when the volume of data was relatively small, engineers prioritized fast data retrieval and stored data in <a target="_blank" href="https://en.wikipedia.org/wiki/B-tree">B-tree structures</a> that made searching efficient.</p>
<p>But over time, we started building systems that needed to ingest massive amounts of data like logs, metrics, likes, chats and tweets. This made it necessary to design a storage system that would make writing faster.</p>
<p>One such storage system is the LSM-tree (Log-Structured Merge tree).</p>
<p>In this tutorial, rather than immediately diving into the theoretical concepts of an LSM-Tree Storage system, I’ll take a practical, problem-driven approach. I believe that learning through solving problems is far more effective and engaging than simple memorization of concepts.</p>
<p>By approaching these ideas progressively, my goal is to guide you step by step through real-world engineering challenges and solutions, giving you a front-row seat to the intricacies of building a robust storage system from scratch.</p>
<p>We’ll begin by identifying real-world challenges that arise in database design – like handling write-heavy workloads, ensuring data durability, or managing efficient storage. These challenges will set the stage for each feature and component of LSM-Trees.</p>
<p>Through this method, we’ll explore the foundations of LSM-Tree storage systems and dive deeper into their key components: MemTable, SSTable, Write-Ahead Log (WAL), and Manifest File.</p>
<p>We’ll also examine the Write and Read paths, explore Durability and Crash-Recovery mechanisms, and conclude with one of the most critical processes: Compaction.</p>
<p>By the end of this handbook, you’ll understand not just what these components are but also why they are designed the way they are and how they solve the unique challenges of building modern, high-performance databases.</p>
<h3 id="heading-what-well-cover"><strong>What We’ll Cover:</strong></h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-an-lsm-tree">What is an LSM Tree?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-preface-setting-up-to-build-an-lsm-tree-database">Preface: Setting up to Build an LSM-Tree Database</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-initial-feature-set-laying-the-foundation-of-the-database-system">Initial Feature Set: Laying the Foundation of the Database System</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-memtable-in-memory-data-storage">MemTable: In-Memory Data Storage</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sstable-persisting-data-for-durability">SSTable: Persisting Data for Durability</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-wal-write-ahead-log-crash-recovery-made-simple">The WAL (Write Ahead Log ): Crash Recovery Made Simple</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-manifest-file-tracking-the-state-of-the-database">Manifest File: Tracking the State of the Database</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-update-and-delete-handling-mutability-in-an-immutable-system">Update and Delete: Handling Mutability in an Immutable System</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-compaction-cleaning-up-stale-and-deleted-data">Compaction: Cleaning Up Stale and Deleted Data</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-complete-code">Complete Code</a></li>
</ul>
</li>
</ol>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>While this tutorial is designed to be comprehensive and approachable, it’ll be helpful if you come in with some foundational knowledge in the following areas:</p>
<ul>
<li><p><strong>Programming in Golang</strong>: Familiarity with Go syntax, error handling, and standard libraries (example <code>os,</code> <code>encoding/gob</code>, <code>container/heap</code>) will make it easier to work through the implementation examples.</p>
</li>
<li><p><strong>Basic data structures and algorithms:</strong> Concepts such as maps, heaps, some sorting algorithms, and early termination are leveraged throughout the tutorial.</p>
</li>
<li><p><strong>Understanding persistent storage:</strong> Awareness of the differences between in-memory and disk-based storage, as well as sequential versus random read/write operations will be helpful in grasping performance-related trade-offs.</p>
</li>
<li><p><strong>General database knowledge:</strong> If you're familiar with key-value databases or CRUD operations (Create, Read, Update, Delete), you’ll have a head start.</p>
</li>
<li><p><strong>Concurrency</strong>: Basic understanding of threads and concurrency.</p>
</li>
</ul>
<p>While having experience in these areas will deepen your understanding of the concepts and reduce the learning curve, I will provide sufficient detail and practical explanations at every step ensuring you gain the insights necessary to follow along and build your own LSM-tree-based storage engine.</p>
<h2 id="heading-what-is-an-lsm-tree">What is an LSM Tree?</h2>
<p>A log-structured merge-tree (or LSM tree) is a data structure that makes database writes super fast by recording new data in memory first, then periodically sorting and merging it into larger files on disk.</p>
<p>The “log” in its name refers to the fact that it saves data in a log-structured format (rather than simply storing it). We will come to what those logs are in a little bit.</p>
<p>LSM trees keep appending new data to the existing data, instead of looking for something that exists and updating it. In other words, you don't have to spend any CPU cycles thinking about where to store data – just append it at the end.</p>
<p>An LSM tree also has "tree" in its name, but does it actually store data in a tree? Not really. The “tree” here is mostly an abstract concept. It refers to the hierarchical organization of levels (L0, L1, L2, and so on), not a tree data structure with nodes and pointers. Again, we will come to those levels in a little bit, but for now, let’s just say it makes sense to call it a tree given that it stores data in a leveled fashion.</p>
<p>Just note that there isn't a tangible tree structure in play (like a binary trees or graph) – it’s not node-based storage.</p>
<p>Finally, there is the "merge" part of the name. For now, suffice it to say that you’ll soon see how this storage engine merges data to save storage by avoiding duplication.</p>
<p>Personally, I think that "Log-Structured Merge <strong>System</strong>" would be clearer than "tree," but "LSM tree" is the established term in the industry, so that's what we'll use.</p>
<h2 id="heading-preface-setting-up-to-build-an-lsm-tree-database">Preface: Setting up to Build an LSM-Tree Database</h2>
<p>Now that we have set the context, lets put this theory into practice and start building our own LSM-tree-based database storage engine from scratch.</p>
<p>To follow along with this tutorial:</p>
<ul>
<li><p>Make sure you have Golang installed on your system. If not, you can download and install it from the <a target="_blank" href="https://go.dev/">official Go website</a>.</p>
</li>
<li><p>Set up your development environment and create a new Go module for this project by running: <code>go mod init lsm-db</code></p>
</li>
<li><p>Keep a code editor or IDE ready to try out the examples.</p>
</li>
</ul>
<h3 id="heading-initial-feature-set-laying-the-foundation-of-the-database-system">Initial Feature Set: Laying the Foundation of the Database System</h3>
<p>When I’m designing or building a system, I like to think that the system already exists, and I assume that I can just start calling functions that support the features of the system. I’ll follow that pattern here and assume that the following functions of the LSM tree exist and we can invoke those functions from <a target="_blank" href="http://main.go">main.go</a>.</p>
<pre><code class="lang-go">db, err := NewDB[<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>](<span class="hljs-number">3</span>, <span class="hljs-number">3</span>) <span class="hljs-comment">// there is a feature to create new with some parameters we will get to</span>
db.Put(<span class="hljs-string">"a"</span>, <span class="hljs-string">"apple"</span>) <span class="hljs-comment">// a feature to add key value</span>
db.Delete(<span class="hljs-string">"a"</span>) <span class="hljs-comment">// a feature to delete a key</span>
val, _ := db.Get(<span class="hljs-string">"a"</span>) <span class="hljs-comment">// a feature to get value given a key</span>
</code></pre>
<p>As we progress through this journey, I’ll introduce essential features such as in-memory storage, flushing data to disk, and handling duplicate keys. We’ll also explore more advanced components, including a Write-Ahead Log (WAL) to ensure crash tolerance, a Manifest file to maintain the database state across application restarts, and a Compaction process to clean up redundant or stale data by merging older SSTables.</p>
<p>By the end of this tutorial, you will gain a clear understanding of how all these components work together to form a robust and efficient LSM-tree-based storage system.</p>
<h3 id="heading-memtable-in-memory-data-storage">MemTable: In-Memory Data Storage</h3>
<p>We’re building a database storage system, so of course you’ll need a way to store data. This means you need some kind of backing storage. This backing storage in an LSM tree is called a MemTable. The "Mem" refers to its in-memory storage. The benefit of in-memory storage is that it’s orders of magnitude faster than storing on disk.</p>
<p>For simplicity, at the core of the MemTable you can use a map (or dictionary depending on the programming language) as the underlying data structure to store key-value pairs. The map allows for fast lookups, insertions, and deletions, making it ideal for in-memory storage where performance is crucial. So the structure for MemTable will look like:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> MemTable[K comparable, V any] <span class="hljs-keyword">struct</span> {
    data <span class="hljs-keyword">map</span>[K]V <span class="hljs-comment">// this is primary storage map. It's generic so that you</span>
                  <span class="hljs-comment">// can store any kind of data</span>
}
</code></pre>
<p>Above code defines a <code>MemTable</code> struct, where <code>data</code> is a map that acts as the main storage for our key-value pairs. Since the <code>data</code> field is a map, you’ll be able to quickly add, retrieve, or delete values associated with a given key.</p>
<p>You must have noticed something new in the code. The use of <code>&lt;K comparable, V any&gt;</code>. This syntax is Go’s <strong>generic types</strong> feature, which allows us to write flexible code that can handle different data types.</p>
<p>Generics are a way to write code that is independent of any specific data type. They allow you to write functions and data structures that can work with a string, int, float, or any custom type you define, without sacrificing type safety.</p>
<p>In the above code, K and V are type parameters. They say: "This MemTable can work with any Key type K that is comparable, and any value type V."</p>
<p>Now that you have the MemTable, think of what functions it should provide to its clients. Well, the clients need to be able to save and retrieve values associated with a key, so the following functions would fit naturally:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(m *MemTable[K, V])</span> <span class="hljs-title">Put</span><span class="hljs-params">(key K, value V)</span></span> {
    m.data[key] = value
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(m *MemTable[K, V])</span> <span class="hljs-title">Get</span><span class="hljs-params">(key K)</span> <span class="hljs-params">(V, <span class="hljs-keyword">bool</span>)</span></span> {
    value, ok := m.data[key]
    <span class="hljs-keyword">var</span> zero V
    <span class="hljs-keyword">if</span> !ok {
        <span class="hljs-keyword">return</span> zero, <span class="hljs-literal">false</span>
    }
    <span class="hljs-keyword">return</span> value, <span class="hljs-literal">true</span>
}
</code></pre>
<p>The above code has <code>Put</code> and a <code>Get</code> functions – let’s break them down:</p>
<ul>
<li><p><strong>Put</strong>: This function allows the client to insert a key-value pair into the MemTable. If the key already exists in the map, its value will be updated with the new value provided as an argument. This is effectively the <code>write</code> operation of our key-value store.</p>
</li>
<li><p><strong>Get</strong>: This function is responsible for retrieving a value associated with a give key from the MemTable. It returns two values, the value itself (of type <code>V</code>) and a boolean (<code>true</code> or <code>false</code>). The boolean indicates whether the key was found in the map. If the key does not exist, the function return a <code>zero value</code> (more on that below) along with <code>false</code>.</p>
</li>
</ul>
<p>Did you notice <code>var zero V</code>?</p>
<p>It's pretty interesting. Think of a situation where we don't get a value from the map – say the key is not there, or something else is wrong. What should the function <code>Get</code> return in that case? Can it return an int (0), or a string "Not found", or some random object (foo)? You don't know anything about the type yet (Generics), so you can't tell it what to return.</p>
<p>In this case, the compiler comes to the rescue. Go has this zero value concept: everything should have a zero value. An int has 0, string has "", bool has false, and pointer, slice, and map have nil. By saying <code>var zero V</code> you are telling the compiler, "I don't know the type yet, you figure out the type while compiling and put that type here as the return type." Neat!</p>
<p>I missed one thing though: how would a client invoke these functions? Right, we need a way to build the MemTable type.</p>
<p>To construct and initialize a MemTable, we can use a <strong>factory function</strong>: a common programming pattern for creating and returning new objects or instances without directly exposing the underlying implementation details.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewMemTable</span>[<span class="hljs-title">K</span> <span class="hljs-title">comparable</span>, <span class="hljs-title">V</span> <span class="hljs-title">any</span>]<span class="hljs-params">()</span> *<span class="hljs-title">MemTable</span>[<span class="hljs-title">K</span>, <span class="hljs-title">V</span>]</span> {
    <span class="hljs-keyword">return</span> &amp;MemTable[K, V]{
        data: <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[K]V),
    }
}
</code></pre>
<p>Notice how we’ve initialized the data field using the built-in <code>make</code> function. Here’s why we do this:</p>
<p>Go has a built-in function called <code>make</code>, which is used to allocate and initialize slices, maps, and channels. This allocation ensures that they are ready for use without the risk of runtime panics.</p>
<p>You might wonder, why not use the <code>new</code> function to allocate the map? After all, developers coming from other programming backgrounds (like C++ or Java) might expect to use <code>new</code> for all types of memory allocation. But Go <strong>differentiates how it manages memory for composite types versus basic/numeric types</strong>, and that’s where <code>make</code> comes in.</p>
<p>This distinction matters because the <code>new</code> function only <strong>allocates memory</strong> for an object and returns a <em>pointer</em> to that memory. The object itself is not initialized, meaning that while the memory is allocated, the map isn’t ready to use. If we try to perform operations (like adding a key-value pair) on a <code>map</code> only allocated using <code>new</code>, it will cause a runtime panic because the map wasn’t correctly initialized.</p>
<p>For example:</p>
<pre><code class="lang-go">m := <span class="hljs-built_in">new</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>) <span class="hljs-comment">// Allocates a pointer to an uninitialized map</span>
(*m)[<span class="hljs-string">"a"</span>] = <span class="hljs-number">1</span>            <span class="hljs-comment">// This will panic because the map is not initialized</span>
</code></pre>
<p>On the other hand, <code>make</code> both allocates and <strong>initializes the map</strong>, ensuring it’s fully functional right away. That’s why the correct way to create a map is:</p>
<pre><code class="lang-go">m1 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>) <span class="hljs-comment">// Initializes the map properly</span>
m1[<span class="hljs-string">"a"</span>] = <span class="hljs-number">1</span>                <span class="hljs-comment">// This works as expected</span>
</code></pre>
<p>Now that you have the MemTable which can store data in memory, let's hook it up and use it.</p>
<p>But before that, do you remember at the beginning that I used functional invocations like <code>db.Put</code> and <code>db.Get</code>? Well, what is <code>db</code>? Because we are building a database storage system, it makes more sense to name the interface <code>db</code> instead of MemTable, right? And to be honest, it seems like MemTable is going to be part of the database system, not the whole system, doesn't it?</p>
<p>Even if it's not intuitive at the moment to define something like a DB type, let's just do it. Trust me, it will start to get clearer as we move along. This <code>db</code> type will wrap adding and retrieving data from MemTable.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> DB[K comparable, V any] <span class="hljs-keyword">struct</span> {
    memtable *MemTable[K, V]
}

<span class="hljs-comment">// factory function for DB type</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewDB</span>[<span class="hljs-title">K</span> <span class="hljs-title">comparable</span>, <span class="hljs-title">V</span> <span class="hljs-title">any</span>]<span class="hljs-params">()</span> <span class="hljs-params">(*DB[K, V], error)</span></span> {
    memtable := NewMemTable[K, V]()
    <span class="hljs-keyword">return</span> &amp;DB[K, V]{
        memtable: memtable,
    }, <span class="hljs-literal">nil</span>
}
</code></pre>
<p>Let's just define the Put and Get functions which will invoke corresponding functions in MemTable:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(db *DB[K, V])</span> <span class="hljs-title">Put</span><span class="hljs-params">(key K, value V)</span> <span class="hljs-title">error</span></span> {
    db.memtable.Put(key, value)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(db *DB[K, V])</span> <span class="hljs-title">Get</span><span class="hljs-params">(key K)</span> <span class="hljs-params">(V, error)</span></span> {
    <span class="hljs-keyword">if</span> val, ok := db.memtable.Get(key); ok {
        <span class="hljs-keyword">return</span> val, <span class="hljs-literal">nil</span>
    }
    <span class="hljs-keyword">var</span> zero V
    <span class="hljs-keyword">return</span> zero, errors.New(<span class="hljs-string">"key not found"</span>)
}
</code></pre>
<p>Let's integrate whatever we’ve built so far and run it. To run add below code in main.go and run using <code>go run main.go</code></p>
<pre><code class="lang-go">db, err := NewDB[<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>]()
<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    log.Fatalf(<span class="hljs-string">"Failed to create DB: %v"</span>, err)
}
db.Put(<span class="hljs-string">"a"</span>, <span class="hljs-string">"apple"</span>)
val, _ := db.Get(<span class="hljs-string">"a"</span>)
log.Printf(<span class="hljs-string">"Get('a') = %s (should be 'apple')"</span>, val)
</code></pre>
<p>Look at that, you have built an in-memory database where your clients can store and fetch data from. It’s using generics so you can store any kind of values (int, string, objects).</p>
<p>Now say you shipped this solution and then it crashes. Your clients will lose all the data. Why will this crash? For one thing, memory is limited and at some point you’re going to run out of it. So there are two major problems with just in-memory storage:</p>
<ol>
<li><p>It's not durable.</p>
</li>
<li><p>Unbounded memory usage is going to crash the system.</p>
</li>
</ol>
<p>How do we solve these problems?</p>
<p>Here's a thought: what if we flush the MemTable data to disk at some regular interval? That way we can ensure that MemTable doesn't grow out of bounds. Also if the db crashes, we won’t lose all the data. We’ll still lose the data that hasn’t been flushed yet, but that's way better than losing all of it.</p>
<h3 id="heading-sstable-persisting-data-for-durability">SSTable: Persisting Data for Durability</h3>
<p>An SSTable is a sorted string table. I wish they’d called it a "Secondary Storage" table, but historically keys and values were strings – hence sorted string table. An SSTable is a persistent, ordered, and immutable file that stores key-value pairs. It’s a file stored on disk, so it's pretty clear that it’s persistent (durable).</p>
<p>Let’s discuss a couple key features of the SSTable:</p>
<ul>
<li><p><strong>It’s ordered</strong>: There is an incentive to store the keys in a sorted order, and it makes searching keys faster and efficient. If not for that, you'd have to scan the whole file to be able to find a key. Later, I will point out some code that leverages sorted storage.</p>
</li>
<li><p><strong>It’s immutable</strong>: Once an SSTable file is written, it can’t be modified. To update or delete a key, you must write a new record in a newer SSTable. This simplifies the design and makes reads and writes very predictable.</p>
</li>
</ul>
<p>But wait, how does that simplify the design?</p>
<p>One of the most complex things in software engineering is dealing with concurrency. Let’s say you’re writing to a file and another thread updates it underneath. How do you know you have the correct data?</p>
<p>With immutable design, you don't have to worry about this at all. You are 100% confident that the data you are reading has not been altered by anybody else. I’ll take that as a massive simplification: you don't have to deal with locks, starvation, staleness, and so on.</p>
<h4 id="heading-how-does-it-make-the-write-path-predictable">How does it make the write path predictable?</h4>
<p>I will answer this partially here and come back to it when we have completed some more implementation. You’ll see that every write in our code follows the exact same steps. There is not a single different condition or edge case.</p>
<p>In a traditional database (using a B-Tree), a typical write involves:</p>
<ol>
<li><p>Finding the data on disk.</p>
</li>
<li><p>Reading the block of data from disk into memory.</p>
</li>
<li><p>Modifying the data in memory.</p>
</li>
<li><p>Writing the entire block back to disk.</p>
</li>
</ol>
<p>The more steps, the more unpredictable performance can get, because the write can be fast if data is already in the memory cache or slow if there are multiple disk seeks needed.</p>
<p>Granted, our code is an overly simplified version, but the extension of this concept still stands true in real LSM implementations.</p>
<h4 id="heading-how-does-it-make-the-read-path-predictable">How does it make the read path predictable?</h4>
<p>Read is predictable because any number of threads can read the same SSTable file at the same time without any problem, with full confidence that data has not been updated.</p>
<p>In contrast, when reading from a mutable data structure, you have to worry that another thread might be in the process of changing the data you are trying to read.</p>
<p>To prevent this, B-Tree-based databases use complex locking mechanisms, and that adds overhead and unpredictability.</p>
<p>I should raise a caution here: the Read in LSM tree storage is not always predictable. It can be faster if data is read from memory and it can be very slow if multiple SSTables need to be looked up to find the key.</p>
<p>Having said that, you don't have to worry about other performance bottlenecks because of locks. Meaning, in B-Tree storage, your read query can be slower because another write query is holding a lock. In simple low-concurrency use cases, you will mostly get amazing read performance from a B-Tree structure, but this advantage wears off as concurrency increases.</p>
<p>LSM tree was built for highly concurrent, write-heavy use cases, and at times slower reads are a trade-off.</p>
<p>The takeaway that gives you ammunition to design better is that B-trees are better for read-heavy workloads. Reads are generally faster and more consistent, but performance can have unpredictable outliers under high write concurrency due to locking.</p>
<p>An LSM tree is better for write-heavy workloads. Writes are much faster. Reads are generally slower and more variable, but their performance profile is more predictable under high write concurrency because there is no read-write locking.</p>
<p>Let's implement an SSTable to see how it works.</p>
<p><strong>The write path:</strong></p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeSSTable</span>[<span class="hljs-title">K</span> <span class="hljs-title">comparable</span>, <span class="hljs-title">V</span> <span class="hljs-title">any</span>]<span class="hljs-params">(memtable *MemTable[K, V], path <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">(*SSTable[K, V], error)</span></span> {
    file, err := os.Create(path)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }
    <span class="hljs-keyword">defer</span> file.Close()

    pairs := <span class="hljs-built_in">make</span>([]Pair[K, V], <span class="hljs-number">0</span>, <span class="hljs-built_in">len</span>(memtable.data))
    <span class="hljs-keyword">for</span> k, v := <span class="hljs-keyword">range</span> memtable.data {
        pairs = <span class="hljs-built_in">append</span>(pairs, Pair[K, V]{Key: k, Value: v})
    }

    sort.Slice(pairs, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(i, j <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">bool</span></span> {
        <span class="hljs-keyword">return</span> any(pairs[i].Key).(<span class="hljs-keyword">string</span>) &lt; any(pairs[j].Key).(<span class="hljs-keyword">string</span>)
    })

    encoder := gob.NewEncoder(file)
    <span class="hljs-keyword">for</span> _, pair := <span class="hljs-keyword">range</span> pairs {
        <span class="hljs-keyword">if</span> err := encoder.Encode(pair); err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
        }
    }

    <span class="hljs-keyword">return</span> &amp;SSTable[K, V]{path: path}, <span class="hljs-literal">nil</span>
}
</code></pre>
<p>The following things are important to note from the above code:</p>
<ol>
<li><p><code>sort.Slice</code>: Remember I spoke about order earlier? So we store data in the SSTable in a sorted fashion, and we will see how we leverage it in the read path.</p>
</li>
<li><p>I have used the gob encoding package. An encoder makes life simpler for you because it streams the data to and from Go data structures to binary streams that can be stored on disk. It handles all the complexity of representing types, field names, and values in a standardized binary format, so that you don't have to.</p>
</li>
</ol>
<p><strong>The read path:</strong></p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *SSTable[K, V])</span> <span class="hljs-title">Get</span><span class="hljs-params">(key K)</span> <span class="hljs-params">(V, error)</span></span> {
    file, err := os.Open(s.path)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">var</span> zero V
        <span class="hljs-keyword">return</span> zero, err
    }
    <span class="hljs-keyword">defer</span> file.Close()

    decoder := gob.NewDecoder(file)

    <span class="hljs-keyword">for</span> {
        <span class="hljs-keyword">var</span> pair Pair[K, V]
        <span class="hljs-keyword">if</span> err := decoder.Decode(&amp;pair); err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">if</span> err == io.EOF {
                <span class="hljs-keyword">break</span>
            }
            <span class="hljs-keyword">var</span> zero V
            <span class="hljs-keyword">return</span> zero, err
        }

        <span class="hljs-comment">// for simple comparison we are assuming key is just string</span>
        keyInDB := any(pair.Key).(<span class="hljs-keyword">string</span>)
        <span class="hljs-keyword">if</span> keyInDB == any(key).(<span class="hljs-keyword">string</span>) {
            <span class="hljs-keyword">if</span> any(pair.Value).(<span class="hljs-keyword">string</span>) == TOMBSTONE {
                <span class="hljs-keyword">var</span> zero V
                <span class="hljs-keyword">return</span> zero, ErrDeleted
            }
            <span class="hljs-keyword">return</span> pair.Value, <span class="hljs-literal">nil</span>
        }

        <span class="hljs-keyword">if</span> keyInDB &gt; any(key).(<span class="hljs-keyword">string</span>) {
            <span class="hljs-keyword">var</span> zero V
            <span class="hljs-keyword">return</span> zero, ErrNotFound
        }
    }

    <span class="hljs-keyword">var</span> zero V
    <span class="hljs-keyword">return</span> zero, ErrNotFound
}
</code></pre>
<p>On the read path, look at <code>keyInDB &gt; any(key).(string)</code>. This is one of the examples of how we took advantage of storing data in a sorted key order. The moment we find a key in the SSTable <strong>that is greater than the key</strong> we are looking for, we stop looking because it’s obvious all other keys will be greater than this, so we won't find our key anymore.</p>
<p>Now that you have implemented the SSTable, you just have to decide when to flush data from the MemTable to the SSTable. You can just define max size for MemTable and flush it to disk on the write path when the max size is reached.</p>
<p>I am skipping some variables, boilerplate code, and simplifying things for brevity. I will post a GitHub link with the complete implementation later.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> DB[K comparable, V any] <span class="hljs-keyword">struct</span> {
    memtable        *MemTable[K, V]
    maxMemtableSize <span class="hljs-keyword">int</span>
    memtableSize    <span class="hljs-keyword">int</span>
    sstables        []*SSTable[K, V]
    sstableCounter  <span class="hljs-keyword">int</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewDB</span>[<span class="hljs-title">K</span> <span class="hljs-title">comparable</span>, <span class="hljs-title">V</span> <span class="hljs-title">any</span>]<span class="hljs-params">(maxMemtableSize <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(*DB[K, V], error)</span></span> {
    sstables := <span class="hljs-built_in">make</span>([]*SSTable[K, V], <span class="hljs-number">0</span>)
    memtable := NewMemTable[K, V]()

    <span class="hljs-keyword">return</span> &amp;DB[K, V]{
        memtable:        memtable,
        maxMemtableSize: maxMemtableSize,
        sstables:        sstables,
    }, <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(db *DB[K, V])</span> <span class="hljs-title">Put</span><span class="hljs-params">(key K, value V)</span> <span class="hljs-title">error</span></span> {
    db.memtable.Put(key, value)
    db.memtableSize++

    <span class="hljs-keyword">if</span> db.memtableSize &gt;= db.maxMemtableSize {
        <span class="hljs-keyword">if</span> err := db.flushMemtable(); err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">return</span> err
        }
    }

    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(db *DB[K, V])</span> <span class="hljs-title">flushMemtable</span><span class="hljs-params">()</span> <span class="hljs-title">error</span></span> {
    sstablePath := fmt.Sprintf(<span class="hljs-string">"data-%d.sstable"</span>, db.sstableCounter)
    sstable, err := writeSSTable(db.memtable, sstablePath)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }

    db.sstables = <span class="hljs-built_in">append</span>(db.sstables, sstable)
    db.sstableCounter++
    db.memtable = NewMemTable[K, V]()
    db.memtableSize = <span class="hljs-number">0</span>

    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p>You'll notice that every time we flush to disk, we write to a new SSTable versus using a single SSTable for the whole database. This is the immutability aspect we discussed earlier.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(db *DB[K, V])</span> <span class="hljs-title">Get</span><span class="hljs-params">(key K)</span> <span class="hljs-params">(V, error)</span></span> {
    <span class="hljs-keyword">if</span> val, ok := db.memtable.Get(key); ok {
        <span class="hljs-keyword">return</span> val, <span class="hljs-literal">nil</span>
    }

    <span class="hljs-keyword">for</span> i := <span class="hljs-built_in">len</span>(db.sstables) - <span class="hljs-number">1</span>; i &gt;= <span class="hljs-number">0</span>; i-- {
        sstable := db.sstables[i]
        val, err := sstable.Get(key)

        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">var</span> zero V

            <span class="hljs-keyword">if</span> err == ErrDeleted {
                <span class="hljs-keyword">return</span> zero, ErrNotFound
            }
            <span class="hljs-keyword">if</span> err == ErrNotFound {
                <span class="hljs-keyword">continue</span>
            }
            <span class="hljs-keyword">return</span> zero, err
        }

        <span class="hljs-keyword">return</span> val, <span class="hljs-literal">nil</span>
    }

    <span class="hljs-keyword">var</span> zero V
    <span class="hljs-keyword">return</span> zero, ErrNotFound
}
</code></pre>
<p>One important aspect to note on the read path is that we are reading the newest SSTable first. This is because the newest SSTable has the most updated value for the key.</p>
<p>So, say you have a key "a" with value "apple", and along the way you update that value for "a" to be "apricot". You'd have flushed it to a new SSTable (for immutability), and so if you were to read an older SSTable, first you'd get the older value. So by reading the newer SSTable first, we get the correct value and we don't have to worry about updating older SSTables.</p>
<h3 id="heading-the-wal-write-ahead-log-crash-recovery-made-simple">The WAL (Write Ahead Log ): Crash Recovery Made Simple</h3>
<p>Now that we have an SSTable, our data is durable and we are safe from losing data upon crashes. Are we really safe, though? Think of a scenario where a crash happens before we flush to the SSTable. We know MemTable has a max threshold, and until then, data lives in memory. So we’re still prone to losing data if a crash happens before the flush.</p>
<p>This is where the WAL (Write Ahead Log) comes into the picture. It’s the single most important aspect of the LSM tree.</p>
<p>We’ll follow a simple rule: "Before we write a piece of data to the in-memory MemTable, we first write it to a log file on disk."</p>
<p>If a crash happens and the database starts again, the first thing it does is look for a WAL, read it if one is found, and replay all the data into MemTable. This process reconstructs the MemTable to the exact state it was in right before the crash.</p>
<p>It's natural to think that if all of your writes are first written to disk it will impact performance. You aren’t wrong, but at the same time there are nuances.</p>
<p>The writes to WAL are different in that they are append-only sequential writes, meaning random disk seeks are not required. On a traditional spinning hard drive (HDD), this is fast because the disk's read/write head does not have to move to a new location. On a modern solid-state drive (SSD), sequential writes are also much faster than random writes.</p>
<p>Whatever small performance impact we accept is a trade-off for durability.</p>
<p>Now that we know what WAL does, let's implement it. Two key functions of WAL are to write to a file on disk and replay MemTable upon start.</p>
<p>Note that in the factory function below (NewWAL), the file has been opened in append mode.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewWAL</span>[<span class="hljs-title">K</span> <span class="hljs-title">comparable</span>, <span class="hljs-title">V</span> <span class="hljs-title">any</span>]<span class="hljs-params">(path <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">(*WAL[K, V], error)</span></span> {
    file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, <span class="hljs-number">0644</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }
    <span class="hljs-keyword">return</span> &amp;WAL[K, V]{
        file:    file,
        encoder: gob.NewEncoder(file),
    }, <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(wal *WAL[K, V])</span> <span class="hljs-title">Write</span><span class="hljs-params">(key K, value V)</span> <span class="hljs-title">error</span></span> {
    entry := WALEntry[K, V]{Key: key, Value: value}
    <span class="hljs-keyword">return</span> wal.encoder.Encode(&amp;entry)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ReplayWAL</span>[<span class="hljs-title">K</span> <span class="hljs-title">comparable</span>, <span class="hljs-title">V</span> <span class="hljs-title">any</span>]<span class="hljs-params">(path <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">(*MemTable[K, V], error)</span></span> {
    memtable := NewMemTable[K, V]()
    file, err := os.Open(path)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">if</span> os.IsNotExist(err) {
            <span class="hljs-comment">// If the file doesn't exist, that's fine. Return an empty memtable.</span>
            <span class="hljs-keyword">return</span> memtable, <span class="hljs-literal">nil</span>
        }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }
    <span class="hljs-keyword">defer</span> file.Close()

    decoder := gob.NewDecoder(file)
    <span class="hljs-keyword">for</span> {
        <span class="hljs-keyword">var</span> entry WALEntry[K, V]
        <span class="hljs-keyword">if</span> err := decoder.Decode(&amp;entry); err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">if</span> err == io.EOF {
                <span class="hljs-keyword">break</span> <span class="hljs-comment">// We've reached the end of the file.</span>
            }
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
        }
        memtable.Put(entry.Key, entry.Value)
    }

    <span class="hljs-keyword">return</span> memtable, <span class="hljs-literal">nil</span>
}
</code></pre>
<p>A couple notes about the above code:</p>
<ul>
<li><p><strong>NewWAL</strong>: This function creates an instance of the WAL for our database. It takes in the file path where the WAL data should be stored and opens the file using Go’s <code>os.OpenFile</code> function. Also, a <code>gob.Encoder</code> is initialized to simplify the encoding of Go data structures into binary format for efficient storage in the WAL file.</p>
</li>
<li><p><strong>Write</strong>: The Write function appends a new key-value pair to the WAL file. Every write operation to the MemTable first calls this function to ensure the update is durably recorded:</p>
</li>
<li><p><strong>ReplayWAL:</strong> This is the most important function. In the event of crash, this function comes to our rescue by reconstructing the MemTable from the WAL file. It replays the entries stored in the WAL file and writes it into MemeTable. Following it how it works:</p>
<ol>
<li><p>The function begins by creating a new empty MemTable instance that will be populated with key-value pairs.</p>
</li>
<li><p>It then attempts to open the WAL file. If the file does not exist (example – if this is the first startup), the function assumes there’s nothing to recover and simply returns the empty MemTable.</p>
</li>
<li><p>A <code>gob.Decoder</code> is used to read the WAL file, which helps to deserialize the saved binary-encoded <code>WALEntry</code> data back into key-value pairs.</p>
</li>
<li><p>For each successfully decoded <code>WALEntry</code>, the key-value pair is added back into the MemTable using the <code>Put</code> function.</p>
</li>
</ol>
</li>
</ul>
<p>With this, the database can fully recover its state by replaying all the operations recorded in the WAL.</p>
<p>As far as integration is concerned, every time you create a new DB, you should think of replaying from an existing WAL and opening the WAL in append mode. Also, Put should first write to WAL.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewDB</span>[<span class="hljs-title">K</span> <span class="hljs-title">comparable</span>, <span class="hljs-title">V</span> <span class="hljs-title">any</span>]<span class="hljs-params">(maxMemtableSize <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(*DB[K, V], error)</span></span> {
    walPath := <span class="hljs-string">"db.wal"</span>
    memtable, err := ReplayWAL[K, V](walPath) <span class="hljs-comment">// this is the replay</span>
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }
    <span class="hljs-comment">//open WAL in append mode</span>
    wal, err := NewWAL[K, V](walPath)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }

    <span class="hljs-keyword">return</span> &amp;DB[K, V]{
        memtable:        memtable,
        maxMemtableSize: maxMemtableSize,
        memtableSize:    <span class="hljs-built_in">len</span>(memtable.data),
        wal:             wal,
        walPath:         walPath,
        sstables:        <span class="hljs-built_in">make</span>([]*SSTable[K, V], <span class="hljs-number">0</span>),
    }, <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(db *DB[K, V])</span> <span class="hljs-title">Put</span><span class="hljs-params">(key K, value V)</span> <span class="hljs-title">error</span></span> {
<span class="hljs-comment">//first write to WAL</span>
    <span class="hljs-keyword">if</span> err := db.wal.Write(key, value); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }

    db.memtable.Put(key, value)
    db.memtableSize++

    <span class="hljs-keyword">if</span> db.memtableSize &gt;= db.maxMemtableSize {
        <span class="hljs-keyword">if</span> err := db.flushMemtable(); err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">return</span> err
        }
    }

    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<h3 id="heading-manifest-file-tracking-the-state-of-the-database">Manifest File: Tracking the State of the Database</h3>
<p>By this point, the database is pretty robust and durable, but an important question lingers: upon restarts, how does our database know about SSTables? Knowing about all SSTables is important for fetching data.</p>
<p>So say our database crashed after writing several SSTables. Without knowing about these SSTables, the database will create a new slice of SSTables and all of our old data is gone – queries won't read those files.</p>
<p>To solve this problem, we introduce an inventory of SSTables called MANIFEST. Every time we successfully create a new SSTable in flushMemtable, we add its path to the MANIFEST and save the MANIFEST to disk.</p>
<p>The very first thing NewDB does on startup is read the MANIFEST. This gives it the list of all the file paths, and it uses this list to perfectly reconstruct its SSTables slice.</p>
<p>In short, MANIFEST determines the state of the DB.</p>
<p>Manifest contains a slice of SSTablePaths. The Read function will read the MANIFEST file to restore the knowledge of the SSTables. The Write function will write a new manifest file.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Manifest <span class="hljs-keyword">struct</span> {
    SSTablePaths []<span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ReadManifest</span><span class="hljs-params">(path <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">(*Manifest, error)</span></span> {
    file, err := os.Open(path)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">if</span> os.IsNotExist(err) {
            <span class="hljs-comment">// If manifest doesn't exist, return empty manifest</span>
            <span class="hljs-keyword">return</span> &amp;Manifest{SSTablePaths: []<span class="hljs-keyword">string</span>{}}, <span class="hljs-literal">nil</span>
        }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }
    <span class="hljs-keyword">defer</span> file.Close()

    <span class="hljs-keyword">var</span> manifest Manifest
    decoder := gob.NewDecoder(file)
    err = decoder.Decode(&amp;manifest)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }

    <span class="hljs-keyword">return</span> &amp;manifest, <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">WriteManifest</span><span class="hljs-params">(path <span class="hljs-keyword">string</span>, manifest *Manifest)</span> <span class="hljs-title">error</span></span> {
    tmpPath := path + <span class="hljs-string">".tmp"</span>
    file, err := os.Create(tmpPath)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }

    encoder := gob.NewEncoder(file)
    <span class="hljs-keyword">if</span> err := encoder.Encode(manifest); err != <span class="hljs-literal">nil</span> {
        file.Close()
        os.Remove(tmpPath)
        <span class="hljs-keyword">return</span> err
    }

    <span class="hljs-keyword">if</span> err := file.Close(); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }
    <span class="hljs-comment">// Atomic Rename</span>
    <span class="hljs-keyword">return</span> os.Rename(tmpPath, path)
}
</code></pre>
<p>You'll notice that we aren’t modifying the existing MANIFEST file directly. Instead, we’re creating a temporary file, writing all the data to it, closing it, and then <code>atomically renaming</code> it to replace the old MANIFEST.</p>
<p>The <code>os.Rename()</code> operation is atomic on most filesystems, meaning it either completely succeeds or completely fails – there's no in-between state. This is crucial because if the system crashes while updating the MANIFEST, we need to ensure we don't end up with a corrupted file. We’ll discuss this again below when we’re talking about compaction.</p>
<p>With this approach, we either have the old valid MANIFEST or the new valid MANIFEST, never a partially written corrupted file.</p>
<p>From an integration standpoint, NewDB will read the manifest and set its SSTable slice based on that. The flush method, given that it writes to SSTable, will also write SSTable info to manifest to keep the db updated about new SSTables.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> DB[K comparable, V any] <span class="hljs-keyword">struct</span> {
    memtable        *MemTable[K, V]
    maxMemtableSize <span class="hljs-keyword">int</span>
    memtableSize    <span class="hljs-keyword">int</span>
    sstables        []*SSTable[K, V]
    sstableCounter  <span class="hljs-keyword">int</span>
    wal             *WAL[K, V]
    walPath         <span class="hljs-keyword">string</span>
    manifest        *Manifest
    manifestPath    <span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewDB</span>[<span class="hljs-title">K</span> <span class="hljs-title">comparable</span>, <span class="hljs-title">V</span> <span class="hljs-title">any</span>]<span class="hljs-params">(maxMemtableSize <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(*DB[K, V], error)</span></span> {
    walPath := <span class="hljs-string">"db.wal"</span>
    memtable, err := ReplayWAL[K, V](walPath)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }

    wal, err := NewWAL[K, V](walPath)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }

    manifestPath := <span class="hljs-string">"MANIFEST"</span>
    manifest, err := ReadManifest(manifestPath)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }

    sstables := <span class="hljs-built_in">make</span>([]*SSTable[K, V], <span class="hljs-built_in">len</span>(manifest.SSTablePaths))
    <span class="hljs-keyword">for</span> i, path := <span class="hljs-keyword">range</span> manifest.SSTablePaths {
        sstables[i] = &amp;SSTable[K, V]{path: path}
    }

    <span class="hljs-keyword">return</span> &amp;DB[K, V]{
        memtable:        memtable,
        maxMemtableSize: maxMemtableSize,
        memtableSize:    <span class="hljs-built_in">len</span>(memtable.data),
        wal:             wal,
        walPath:         walPath,
        manifest:        manifest,
        manifestPath:    manifestPath,
        sstables:        sstables,
    }, <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(db *DB[K, V])</span> <span class="hljs-title">flushMemtable</span><span class="hljs-params">()</span> <span class="hljs-title">error</span></span> {
    sstablePath := fmt.Sprintf(<span class="hljs-string">"data-%d.sstable"</span>, db.sstableCounter)
    sstable, err := writeSSTable(db.memtable, sstablePath)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }

    db.sstables = <span class="hljs-built_in">append</span>(db.sstables, sstable)
    db.sstableCounter++

    db.manifest.SSTablePaths = <span class="hljs-built_in">append</span>(db.manifest.SSTablePaths, sstablePath)
    <span class="hljs-keyword">if</span> err := WriteManifest(db.manifestPath, db.manifest); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }

    db.memtable = NewMemTable[K, V]()
    db.memtableSize = <span class="hljs-number">0</span>

    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p>At this point, our DB has almost everything. It can write to memory (MemTable), persist to disk (sstable), and recover from crashes (WAL and manifest). You should include the update and delete feature for completeness – so let’s look at those next.</p>
<h3 id="heading-update-and-delete-handling-mutability-in-an-immutable-system">Update and Delete: Handling Mutability in an Immutable System</h3>
<p>By this time, you should know that in an LSM storage system, data is never updated – rather, new data is written. For example, if you have a data pair ("a": "apple") and over time this has to change to the pair ("a": "apricot"), a new pair will be written to a different SSTable without any change to the existing pair. And yes, this leads to duplicates.</p>
<p>Also, interestingly, data isn't even deleted during write operations. The reason for that is, in a traditional sense, if you have to delete ("a":"apple"), you will have to find where it lives on disk and remove it. This makes writes slow. So instead, a clever mechanism is used: instead of removing the data directly, you can mark the key as deleted by writing a special <code>TOMBSTONE</code> value.</p>
<p>So, in the case of deleting (a : apple), you wouldn't remove the key from any SSTable. Instead, you’d write a new key-value pair such as ("a": "TOMBSTONE"). Here’s what this achieves:</p>
<ul>
<li><p>The <code>"TOMBSTONE"</code> serves as a marker within the SSTable, telling the system that the key <code>"a"</code> has been logically deleted, even though it still physically exists in older SSTables.</p>
</li>
<li><p>During future reads, any value associated with <code>"TOMBSTONE"</code> will be treated as deleted, ensuring that the entry no longer shows up in query results.</p>
</li>
<li><p>This mechanism avoids the need for immediate deletions or expensive in-place updates, making write operations faster and simpler.</p>
</li>
</ul>
<p>But this also raises the following questions:</p>
<ol>
<li><p>How do you accurately read when there are duplicates? Meaning, how do users get ("a": "apricot") instead of ("a": "apple") because the former is latest and accurate?</p>
</li>
<li><p>How do you handle deletes to ensure deleted keys are not returned (and instead, a proper error message is returned)?</p>
</li>
<li><p>These stale and deleted data are garbage. How do you get rid of them to save on storage space?</p>
</li>
</ol>
<p>As long as data is in MemTable (in-memory map), the duplicates are easy to handle: new values will just replace the old values.</p>
<p>But it gets tricky when data is in multiple SSTables. There is a very simple solution to this problem, and that is to just read the newer SSTable before older ones. That way, you will always read the latest value for a given key and exit early.</p>
<p>The following code in the read path ensures reading from newer SSTables before moving to older ones (note the loop starts from <code>len(db.sstables) - 1</code>):</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(db *DB[K, V])</span> <span class="hljs-title">Get</span><span class="hljs-params">(key K)</span> <span class="hljs-params">(V, error)</span></span> {
    <span class="hljs-comment">// Check memtable first</span>
    <span class="hljs-keyword">if</span> val, ok := db.memtable.Get(key); ok {
        <span class="hljs-keyword">if</span> any(val).(<span class="hljs-keyword">string</span>) == TOMBSTONE {
            <span class="hljs-keyword">var</span> zero V
            <span class="hljs-keyword">return</span> zero, ErrNotFound
        }
        <span class="hljs-keyword">return</span> val, <span class="hljs-literal">nil</span>
    }

    <span class="hljs-comment">// Then check sstables from newest to oldest</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-built_in">len</span>(db.sstables) - <span class="hljs-number">1</span>; i &gt;= <span class="hljs-number">0</span>; i-- {
        sstable := db.sstables[i]
        val, err := sstable.Get(key)

        <span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">return</span> val, <span class="hljs-literal">nil</span>
        }

        <span class="hljs-keyword">var</span> zero V
        <span class="hljs-keyword">if</span> err == ErrDeleted {
            <span class="hljs-keyword">return</span> zero, ErrNotFound
        }
        <span class="hljs-keyword">if</span> err == ErrNotFound {
            <span class="hljs-keyword">continue</span>
        }
        <span class="hljs-keyword">return</span> zero, err
    }

    <span class="hljs-keyword">var</span> zero V
    <span class="hljs-keyword">return</span> zero, ErrNotFound
}
</code></pre>
<p>And for delete, you could just add a new value "TOMBSTONE":</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(db *DB[K, V])</span> <span class="hljs-title">Delete</span><span class="hljs-params">(key K)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">return</span> db.Put(key, any(TOMBSTONE).(V))
}
</code></pre>
<p>Note: This implementation assumes V is a string type. In a production system, you would need a more robust way to handle tombstones that works with any value type.</p>
<p>Handling deleted keys becomes simple now. You can check for the value (in MemTable and SSTable) and return an error if the value is "TOMBSTONE":</p>
<pre><code class="lang-go"><span class="hljs-comment">// db.go</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(db *DB[K, V])</span> <span class="hljs-title">Get</span><span class="hljs-params">(key K)</span> <span class="hljs-params">(V, error)</span></span> {
    <span class="hljs-keyword">if</span> val, ok := db.memtable.Get(key); ok {
        <span class="hljs-keyword">if</span> any(val).(<span class="hljs-keyword">string</span>) == TOMBSTONE { <span class="hljs-comment">//got TOMBSTONE, return zero</span>
            <span class="hljs-keyword">var</span> zero V
            <span class="hljs-keyword">return</span> zero, ErrNotFound
        }
        <span class="hljs-keyword">return</span> val, <span class="hljs-literal">nil</span>
    }
    <span class="hljs-comment">// ... rest of function</span>
}
</code></pre>
<pre><code class="lang-go"><span class="hljs-comment">// sstable.go</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *SSTable[K, V])</span> <span class="hljs-title">Get</span><span class="hljs-params">(key K)</span> <span class="hljs-params">(V, error)</span></span> {
    <span class="hljs-comment">// ... earlier code</span>

    keyInDB := any(pair.Key).(<span class="hljs-keyword">string</span>)
    <span class="hljs-keyword">if</span> keyInDB == any(key).(<span class="hljs-keyword">string</span>) {
        <span class="hljs-keyword">if</span> any(pair.Value).(<span class="hljs-keyword">string</span>) == TOMBSTONE {
            <span class="hljs-keyword">var</span> zero V
            <span class="hljs-keyword">return</span> zero, ErrDeleted
        }
        <span class="hljs-keyword">return</span> pair.Value, <span class="hljs-literal">nil</span>
    }

    <span class="hljs-comment">// ... rest of function</span>
}
</code></pre>
<h3 id="heading-compaction-cleaning-up-stale-and-deleted-data">Compaction: Cleaning Up Stale and Deleted Data</h3>
<p>We have handled all the scenarios so far except for one. It’s not a concern of serving read/write traffic but something that’s important for the health of the storage system.</p>
<p>Over time, the system has developed a lot of garbage (stale, deleted data) and needs a garbage collection mechanism. Compaction is a background maintenance process that cleans up and reorganizes data in an LSM storage system.</p>
<p>As the system grows, multiple SSTables have been created. This leads to reads needing multiple file operations to get values. By compacting (or merging) multiple SSTables into a single one, you avoid disk operation overhead. Along the way, you should also permanently delete data that has been TOMBSTONED.</p>
<p>Note: Compaction is the only time data is permanently deleted from an LSM storage system.</p>
<p>To grasp the concept of compaction, we are going to implement something called <code>Full Compaction</code> where you will merge all the existing SSTables into one larger SSTable. In real-world database implementations, the strategy is more complex, there are multi level compaction involved.</p>
<h4 id="heading-compaction-algorithm">Compaction Algorithm</h4>
<p>We’re going to implement <code>K-way merge</code> to perform compaction. It’s a general algorithm that takes K sorted lists and merges them into a single, combined sorted list. In this case, the K sorted lists are the SSTables, and you are going to merge all of them into a single SSTable.</p>
<p>Our SSTables are already sorted, so the idea of merging them involves:</p>
<ol>
<li><p>Taking the smallest (first) keys from each SSTable</p>
</li>
<li><p>Finding the smallest among those keys</p>
</li>
<li><p>Storing the found smallest key into new SSTable file</p>
</li>
<li><p>Fetching next key from the SSTable the smallest key belongs to</p>
</li>
<li><p>Repeating this process for all SSTables</p>
</li>
</ol>
<p>Here’s a simple example with numbers:</p>
<pre><code class="lang-bash">Assume we have 3 sorted lists:
List A : [4, 8, 12]
List B : [3, 9]
List C : [7, 10, 11]

In the first iteration, we will take (4, 3, 7) because those are the smallest keys <span class="hljs-keyword">for</span> individual lists. 
We find the smallest among those, <span class="hljs-built_in">which</span> is 3, and store 3 <span class="hljs-keyword">in</span> the result list.

In the second iteration, we will take (4, 9, 7). Note that 3 has already been accounted <span class="hljs-keyword">for</span>. 
We pick 4 and store it to the result list.

Repeating this until all lists are empty, we get:
Result List : [3, 4, 7, 8, 9, 10, 11, 12]
</code></pre>
<p>The core part of this algorithm is to find the smallest key among the smallest keys from the individual SSTables. Fortunately, we have a data structure called <code>Min-Heap</code> that does this for us. So, you’re going to take the smallest key from each SSTable and put them all onto a Min Heap for it to return the smallest among those. We’re going to leverage go’s <code>container/heap</code> package to get the Min-Heap data structure and corresponding algorithm to find minimum value and put it at the top of heap.</p>
<p>Min Heap needs you to provide a function for it to determine what is the smaller key between two keys, as it uses that logic to determine global minimum. The following function is implemented for that:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(h MinHeap[K, V])</span> <span class="hljs-title">Less</span><span class="hljs-params">(i, j <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">bool</span></span> {
    <span class="hljs-comment">// again for simple comparison assume string key</span>
    keyI := any(h[i].Pair.Key).(<span class="hljs-keyword">string</span>)
    keyJ := any(h[j].Pair.Key).(<span class="hljs-keyword">string</span>)
    <span class="hljs-keyword">if</span> keyI != keyJ {
        <span class="hljs-keyword">return</span> keyI &lt; keyJ
    }
    <span class="hljs-comment">// this is needed for the case when you have duplicate keys,</span>
    <span class="hljs-comment">// you will want to pick the one that is in newer sstable because that is latest</span>
    <span class="hljs-keyword">return</span> h[i].SSTableIndex &gt; h[j].SSTableIndex
}
</code></pre>
<p>One important aspect about the above shown <code>Less</code> function is how it handles ties. So if we have two pairs with same key, which is lesser? Let’s assume two pairs as <code>(a: apple)</code> and <code>(a: apricot)</code>, where (a: apple) is the older value (written to an older SSTable), which pair should the Less function return as the lesser value?</p>
<p>The answer is the one which is in the newer SSTable (see <code>h[i].SSTableIndex &gt; h[j].SSTableIndex</code>). It ensures that the SSTable with higher index (that is, latest) becomes the lesser value, so (a: apricot) wins. It’s is important to always get the newer value of a given key.</p>
<p>The code for compaction looks something like the following. Note that we’re discarding deleted values (TOMBSTONE) and the older values.</p>
<pre><code class="lang-go"><span class="hljs-comment">// put this in a new file compaction.go</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">MergeSSTables</span>[<span class="hljs-title">K</span> <span class="hljs-title">comparable</span>, <span class="hljs-title">V</span> <span class="hljs-title">any</span>]<span class="hljs-params">(sstables []*SSTable[K, V], newPath <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">(*SSTable[K, V], error)</span></span> {
    newFile, err := os.Create(newPath) <span class="hljs-comment">// create a new sstable file</span>
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }
    <span class="hljs-keyword">defer</span> newFile.Close() <span class="hljs-comment">// prevent memory leak by ensuring file is closed</span>

    newEncoder := gob.NewEncoder(newFile) <span class="hljs-comment">// initialize encoder for new SSTable file</span>


    files := <span class="hljs-built_in">make</span>([]*os.File, <span class="hljs-built_in">len</span>(sstables)) <span class="hljs-comment">// open all the sstables</span>
    decoders := <span class="hljs-built_in">make</span>([]*gob.Decoder, <span class="hljs-built_in">len</span>(sstables)) <span class="hljs-comment">// initialize one decoder per ssltable file</span>
    <span class="hljs-keyword">for</span> i, sstable := <span class="hljs-keyword">range</span> sstables {
        files[i], err = os.Open(sstable.path)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
        }
        <span class="hljs-keyword">defer</span> files[i].Close() <span class="hljs-comment">// prevent memory leak by ensuring file is closed</span>
        decoders[i] = gob.NewDecoder(files[i])
    }

    <span class="hljs-comment">// read first pair from each sstable and store in a pair array</span>
    pairs := <span class="hljs-built_in">make</span>([]Pair[K, V], <span class="hljs-built_in">len</span>(decoders))
    emptySSTables := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">bool</span>, <span class="hljs-built_in">len</span>(decoders)) <span class="hljs-comment">// track empty sstables</span>
    <span class="hljs-keyword">for</span> i, decoder := <span class="hljs-keyword">range</span> decoders {
        <span class="hljs-keyword">if</span> err := decoder.Decode(&amp;pairs[i]); err != <span class="hljs-literal">nil</span> {
            <span class="hljs-keyword">if</span> err == io.EOF {
                emptySSTables[i] = <span class="hljs-literal">true</span>
                <span class="hljs-keyword">continue</span>
            }
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
        }
    }

    <span class="hljs-comment">// push those pairs onto heap</span>
    h := &amp;MinHeap[K, V]{}
    <span class="hljs-keyword">for</span> i, pair := <span class="hljs-keyword">range</span> pairs {
        <span class="hljs-keyword">if</span> !emptySSTables[i] {
            heap.Push(h, &amp;HeapItem[K, V]{Pair: pair, SSTableIndex: i})
        }
    }

    <span class="hljs-comment">// init the min-heap calculation algorithm from container/heap package</span>
    heap.Init(h)

    <span class="hljs-keyword">var</span> lastKey K
    firstKey := <span class="hljs-literal">true</span>

    <span class="hljs-comment">// pop the min item from heap and store it into new sstable</span>
    <span class="hljs-keyword">for</span> h.Len() &gt; <span class="hljs-number">0</span> {
        item := heap.Pop(h).(*HeapItem[K, V])

        <span class="hljs-comment">// If this key is a duplicate of the last one we saw, skip it</span>
        <span class="hljs-keyword">if</span> !firstKey &amp;&amp; item.Pair.Key == lastKey {
            <span class="hljs-comment">// We only care about the version from the newest SSTable,</span>
            <span class="hljs-comment">// which we have already processed</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">if</span> any(item.Pair.Value).(<span class="hljs-keyword">string</span>) != TOMBSTONE {
                <span class="hljs-comment">// discard deleted</span>
                <span class="hljs-keyword">if</span> err := newEncoder.Encode(item.Pair); err != <span class="hljs-literal">nil</span> {
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
                }
            }
        }

        lastKey = item.Pair.Key
        firstKey = <span class="hljs-literal">false</span>

        <span class="hljs-comment">// Push the next item from the same SSTable into the heap</span>
        <span class="hljs-keyword">var</span> nextPair Pair[K, V]
        <span class="hljs-keyword">if</span> err := decoders[item.SSTableIndex].Decode(&amp;nextPair); err == <span class="hljs-literal">nil</span> {
            heap.Push(h, &amp;HeapItem[K, V]{Pair: nextPair, SSTableIndex: item.SSTableIndex})
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> err != io.EOF {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
        }
    }

    <span class="hljs-keyword">return</span> &amp;SSTable[K, V]{path: newPath}, <span class="hljs-literal">nil</span>
}
</code></pre>
<p>All the compaction magic has been packed in one function, <code>MergeSSTables</code>. The function has the following logical steps (and you can check the inline comments in the code to follow along):</p>
<ol>
<li><p>We create a new destination SSTable file and initialize corresponding <code>gob.Encoder</code></p>
</li>
<li><p>We open all the existing SSTable files, and store their references to <code>files array</code>. Also, we initialize one <code>gob.Decoder</code> per exiting SSTable file. To prevent memory leak, a <code>defer</code> statement ensures that each file will be closed once the function completes its work.</p>
</li>
<li><p>Each <code>decoder</code> reads the first key-value pair from its corresponding SSTable and stores it in the <code>pairs</code> array.</p>
</li>
<li><p>SSTables that are already exhausted (for example, are empty or have hit the end of the file) are marked as such in the <code>emptySSTables</code> slice, and we skip pushing them onto the heap.</p>
</li>
<li><p>We push each pair from the pairs array to <code>Min-Heap</code> and then initialize the <code>Min-Heap</code> calculation algorithm. This algorithm is present in Go’s <code>container/heap</code> package.</p>
</li>
<li><p>Each time the smallest key-value pair is popped from the min-heap, it’s compared with the previously processed key (<code>lastKey</code>). Duplicate keys (those whose values are already written) are skipped.</p>
</li>
<li><p>Values marked with a <code>"TOMBSTONE"</code> (logically deleted entries) are ignored and not written to the new SSTable, effectively cleaning up deleted data.</p>
</li>
<li><p>To continue the merge, the next key-value pair from the same SSTable (as the one we just processed) is read and pushed onto the heap, unless the end of the SSTable (<code>io.EOF</code>) has been reached.</p>
</li>
</ol>
<p>To integrate this with the DB, you could use a compaction threshold and trigger compaction as part of the flush when this threshold is reached:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> DB[K comparable, V any] <span class="hljs-keyword">struct</span> {
    memtable            *MemTable[K, V]
    maxMemtableSize     <span class="hljs-keyword">int</span>
    memtableSize        <span class="hljs-keyword">int</span>
    sstables            []*SSTable[K, V]
    sstableCounter      <span class="hljs-keyword">int</span>
    wal                 *WAL[K, V]
    walPath             <span class="hljs-keyword">string</span>
    manifest            *Manifest
    manifestPath        <span class="hljs-keyword">string</span>
    compactionThreshold <span class="hljs-keyword">int</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewDB</span>[<span class="hljs-title">K</span> <span class="hljs-title">comparable</span>, <span class="hljs-title">V</span> <span class="hljs-title">any</span>]<span class="hljs-params">(maxMemtableSize <span class="hljs-keyword">int</span>, compactionThreshold <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(*DB[K, V], error)</span></span> {
    walPath := <span class="hljs-string">"db.wal"</span>
    memtable, err := ReplayWAL[K, V](walPath)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }

    wal, err := NewWAL[K, V](walPath)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }

    manifestPath := <span class="hljs-string">"MANIFEST"</span>
    manifest, err := ReadManifest(manifestPath)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err
    }

    sstables := <span class="hljs-built_in">make</span>([]*SSTable[K, V], <span class="hljs-built_in">len</span>(manifest.SSTablePaths))
    <span class="hljs-keyword">for</span> i, path := <span class="hljs-keyword">range</span> manifest.SSTablePaths {
        sstables[i] = &amp;SSTable[K, V]{path: path}
    }

    <span class="hljs-keyword">return</span> &amp;DB[K, V]{
        wal:                 wal,
        walPath:             walPath,
        memtable:            memtable,
        memtableSize:        <span class="hljs-built_in">len</span>(memtable.data),
        maxMemtableSize:     maxMemtableSize,
        manifestPath:        manifestPath,
        manifest:            manifest,
        sstables:            sstables,
        compactionThreshold: compactionThreshold,
    }, <span class="hljs-literal">nil</span>
}

<span class="hljs-comment">// a new compact function</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(db *DB[K, V])</span> <span class="hljs-title">Compact</span><span class="hljs-params">()</span> <span class="hljs-title">error</span></span> {
    compactedSSTablePath := fmt.Sprintf(<span class="hljs-string">"data-compacted-%d.sstable"</span>, db.sstableCounter)
    compactedSSTable, err := MergeSSTables(db.sstables, compactedSSTablePath)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }
    <span class="hljs-comment">// write new SSTable to MANIFEST file</span>
    db.manifest.SSTablePaths = []<span class="hljs-keyword">string</span>{compactedSSTablePath}
    <span class="hljs-keyword">if</span> err := WriteManifest(db.manifestPath, db.manifest); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }
    <span class="hljs-comment">//note delete only after writing manifest</span>
    <span class="hljs-keyword">for</span> _, sstable := <span class="hljs-keyword">range</span> db.sstables {
        <span class="hljs-keyword">if</span> err := os.Remove(sstable.path); err != <span class="hljs-literal">nil</span> {
            log.Printf(<span class="hljs-string">"Failed to remove old sstable %s: %v"</span>, sstable.path, err)
        }
    }

    db.sstables = []*SSTable[K, V]{compactedSSTable}
    db.sstableCounter++

    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(db *DB[K, V])</span> <span class="hljs-title">flushMemtable</span><span class="hljs-params">()</span> <span class="hljs-title">error</span></span> {
    sstablePath := fmt.Sprintf(<span class="hljs-string">"data-%d.sstable"</span>, db.sstableCounter)
    sstable, err := writeSSTable(db.memtable, sstablePath)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }

    db.sstables = <span class="hljs-built_in">append</span>(db.sstables, sstable)
    db.sstableCounter++

    db.manifest.SSTablePaths = <span class="hljs-built_in">append</span>(db.manifest.SSTablePaths, sstablePath)
    <span class="hljs-keyword">if</span> err := WriteManifest(db.manifestPath, db.manifest); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }

    db.memtable = NewMemTable[K, V]()
    db.memtableSize = <span class="hljs-number">0</span>

    <span class="hljs-comment">// trigger compaction</span>
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(db.sstables) &gt;= db.compactionThreshold {
        <span class="hljs-keyword">if</span> err := db.Compact(); err != <span class="hljs-literal">nil</span> {
            log.Printf(<span class="hljs-string">"Compaction failed: %v"</span>, err)
            <span class="hljs-keyword">return</span> err
        }
    }

    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p>Notice the <code>Compact()</code> function in the integrated DB code? This is where we invoke previously defined the <code>MergeSSTables</code> function to trigger the compaction process. After invoking <code>MergeSSTables</code>, we write a new SSTable to the MANIFEST file and then delete the older SSTables.</p>
<p>Previously, in the <a class="post-section-overview" href="#heading-manifest-file-tracking-the-state-of-the-database">Manifest File: Tracking the State of the Database</a>, I spoke about atomic renaming <code>os.Rename(tmpPath, path)</code>. Let’s talk about why the atomic renaming of MANIFEST matters for compaction.</p>
<p>During compaction, we're making a major change to the database state: replacing multiple SSTables with a single compacted one. The MANIFEST update is critical here because it's the source of truth for which SSTables exist.</p>
<p>Let’s think about what could go wrong without atomic renaming:</p>
<ol>
<li><p>You start writing the new MANIFEST (which points to the compacted SSTable)</p>
</li>
<li><p>System crashes mid-write</p>
</li>
<li><p>MANIFEST is corrupted and unreadable</p>
</li>
<li><p>On restart, the database has no idea which SSTables exist</p>
</li>
<li><p>All data is effectively lost</p>
</li>
</ol>
<p>With atomic renaming:</p>
<ol>
<li><p>We write the new MANIFEST to MANIFEST.tmp</p>
</li>
<li><p>We fully close and sync it to disk</p>
</li>
<li><p>We atomically rename MANIFEST.tmp to MANIFEST using <code>os.Rename(tmpPath, path)</code></p>
</li>
<li><p>If crash happens before step 3: old MANIFEST is intact, we retry compaction</p>
</li>
<li><p>If crash happens during step 3: atomic operation either completes or doesn't – no corruption</p>
</li>
<li><p>If crash happens after step 3: new MANIFEST is in place, we're good</p>
</li>
</ol>
<p>This is also why we delete the old SSTables only after successfully updating the MANIFEST. If we deleted them before updating MANIFEST and then crashed, the MANIFEST would still point to files that no longer exist.</p>
<h4 id="heading-complete-picture">Complete Picture:</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765740593067/c18083ad-bf8a-4cae-92d3-d690a61dac52.png" alt="c18083ad-bf8a-4cae-92d3-d690a61dac52" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations! You've built a working LSM tree storage engine from scratch. By following the problem-driven approach – discovering issues and implementing solutions as they arose – you've experienced how engineers think about building robust storage systems. I hope this is better than just memorizing the concepts.</p>
<p><strong>Key Takeaways</strong></p>
<ul>
<li><p><strong>Append-only writes</strong> make LSM-trees fast for write-heavy workloads</p>
</li>
<li><p><strong>Immutability</strong> eliminates complex concurrency issues</p>
</li>
<li><p><strong>Trade-off</strong> is that LSM-tree favor writes over reads (opposite of B-trees)</p>
</li>
<li><p><strong>Durability</strong> requires multiple mechanism working together (WAL, MANIFEST, atomic operations)</p>
</li>
<li><p><strong>Background maintenance</strong> (compaction) is essential for long-term health and cost.</p>
</li>
</ul>
<p>Important note: This is a learning implementation. This means that I intentionally simplified the code, so it’s <strong>not production-ready</strong>. Key limitations include:</p>
<ul>
<li><p>No concurrency control (missing mutexes/locks)</p>
</li>
<li><p>No bloom filters for efficient lookups</p>
</li>
<li><p>Simplified compaction strategy</p>
</li>
<li><p>Type safety issues with generic tombstones</p>
</li>
<li><p>Missing robust error recovery</p>
</li>
</ul>
<h3 id="heading-complete-code">Complete Code:</h3>
<p>Like I’ve mentioned before, I've omitted boilerplate code and helper functions for brevity. The complete, runnable implementation is available <a target="_blank" href="https://github.com/justramesh2000/lsm-db">at this GitHub repo</a>.</p>
<p>To learn more about production LSM implementations, study RocksDB, LevelDB, or read the original LSM tree paper by O'Neil et al: <a target="_blank" href="https://www.cs.umb.edu/~poneil/lsmtree.pdf">https://www.cs.umb.edu/~poneil/lsmtree.pdf</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Get Started with PocketBase: Build a Lightweight Backend in Minutes ]]>
                </title>
                <description>
                    <![CDATA[ If you’re a developer looking for a simple, fast, and self-hosted backend, PocketBase might be exactly what you need. It’s an open-source backend written in Go that lets you set up a complete backend with database, authentication, file storage, and r... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-get-started-with-pocketbase-build-a-lightweight-backend-in-minutes/</link>
                <guid isPermaLink="false">691770eb5879bafaa784b694</guid>
                
                    <category>
                        <![CDATA[ backend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Fri, 14 Nov 2025 18:11:55 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763143867523/1a4c8cd4-629f-4dd7-8a82-5b6d0d34d90e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you’re a developer looking for a simple, fast, and self-hosted backend, <a target="_blank" href="https://github.com/pocketbase/pocketbase">PocketBase</a> might be exactly what you need.</p>
<p>It’s an open-source backend written in Go that lets you set up a complete backend with database, authentication, file storage, and real-time updates, all in a single executable file.</p>
<p>In this guide, we’ll explore what makes PocketBase special, how to set it up, and how you can deploy it to the cloud.</p>
<h2 id="heading-what-well-cover">What We’ll Cover:</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-pocketbase">What is PocketBase</a>?</p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-developers-love-pocketbase">Why Developers Love PocketBase</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-install-pocketbase">How to Install Pocketbase</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-extending-pocketbase-with-javascript">Extending PocketBase with JavaScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-using-sdks-to-interact-with-pocketbase">Using SDKs to Interact with PocketBase</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-self-hosting-pocketbase-using-sevalla">Self-Hosting PocketBase using Sevalla</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-security-and-open-source-nature">Security and Open Source Nature</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-pocketbase">When to Use PocketBase</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-pocketbase">What is PocketBase?</h2>
<p>PocketBase is an all-in-one backend that provides everything you need to power a modern web or mobile app, eliminating the need for large infrastructure.</p>
<p>It includes an embedded <a target="_blank" href="https://sqlite.org/">SQLite</a> database, real-time subscriptions, file and user management, a clean admin dashboard, and a REST-style API.</p>
<p>Since it runs from a single file, you can deploy it almost anywhere, from a VPS to your local machine or even a Raspberry Pi.</p>
<p>It’s designed for developers who want control and simplicity at the same time. You don’t need to manage separate servers for authentication, storage, and API endpoints. PocketBase handles all of this out of the box. You can use it as a standalone backend or embed it in your Go application to create a custom solution.</p>
<h2 id="heading-why-developers-love-pocketbase">Why Developers Love PocketBase</h2>
<p><a target="_blank" href="https://pocketbase.io/docs/how-to-use/">PocketBase</a> focuses on speed and simplicity. You don’t need to install multiple packages or services.</p>
<p>Once downloaded, you can start it with a single command. Then it’ll launch a web-based admin dashboard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762853066915/e2f3af60-0a75-4b76-b55f-613b67b3e783.png" alt="PocketBase dashboard" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The database is built using SQLite, which means data is stored locally by default, but you can use extensions to connect it with your existing workflows or cloud storage.</p>
<p>Another major advantage is its real-time capabilities. Every change in the database can be broadcast instantly to connected clients through WebSocket subscriptions. This makes it perfect for building apps like chat systems, dashboards, and collaboration tools that require instant updates.</p>
<h2 id="heading-how-to-install-pocketbase">How to Install PocketBase</h2>
<p>Getting PocketBase running takes less than a minute. You can download a <a target="_blank" href="https://pocketbase.io/docs/">prebuilt executable</a> from the official releases page.</p>
<p>It supports all major platforms, including Windows, macOS, and Linux.</p>
<p>Once downloaded, extract the archive and navigate to the folder in your terminal. Run the following command:</p>
<pre><code class="lang-python">./pocketbase serve
</code></pre>
<p>This command starts a local server and launches the admin dashboard at <code>http://127.0.0.1:8090/_/</code>. From there, you can create collections, add users, upload files, and manage data. There’s no setup wizard or dependency installation – everything is self-contained inside that one binary.</p>
<p>If you’re a Go developer, you can also install PocketBase as a Go module and use it directly in your project to build custom logic or extend the existing API.</p>
<h3 id="heading-using-pocketbase-as-a-go-framework">Using PocketBase as a Go Framework</h3>
<p>PocketBase can act as a Go framework, letting you build your own backend logic while keeping everything in one file. Here’s a simple example that shows how you can extend it with a custom route.</p>
<pre><code class="lang-python">package main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"github.com/pocketbase/pocketbase"</span>
    <span class="hljs-string">"github.com/pocketbase/pocketbase/core"</span>
)
func main() {
    app := pocketbase.New()
    app.OnServe().BindFunc(func(se *core.ServeEvent) error {
        se.Router.GET(<span class="hljs-string">"/hello"</span>, func(re *core.RequestEvent) error {
            <span class="hljs-keyword">return</span> re.String(<span class="hljs-number">200</span>, <span class="hljs-string">"Hello world!"</span>)
        })
        <span class="hljs-keyword">return</span> se.Next()
    })
    <span class="hljs-keyword">if</span> err := app.Start(); err != nil {
        log.Fatal(err)
    }
}
</code></pre>
<p>Once the file is ready, run these commands:</p>
<pre><code class="lang-python">go mod init myapp &amp;&amp; go mod tidy
go run main.go serve
</code></pre>
<p>You’ll now have a working backend with both the PocketBase dashboard and your custom <code>/hello</code> endpoint available at the same time.</p>
<p>This makes PocketBase flexible, you can use it as a ready-to-run backend or as part of a more complex Go project.</p>
<h2 id="heading-extending-pocketbase-with-javascript">Extending PocketBase with JavaScript</h2>
<p>PocketBase includes a built-in JavaScript engine that lets you extend its behavior without modifying or recompiling the Go code. This makes it easy to add custom logic, validation, automation, and event-driven workflows directly inside your backend.</p>
<p>You can create JavaScript files inside the pb_hooks folder, and PocketBase will automatically load and run them. These scripts can listen to events like record creation, updates, authentication, and more.</p>
<p>Here’s a simple example that sends a welcome email whenever a new user signs up.</p>
<p>Create a file named pb_hooks/user_email.js inside your PocketBase directory:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/// &lt;reference path="../pb_data/types.d.ts" /&gt;</span>

onRecordAfterCreate(<span class="hljs-string">"users"</span>, <span class="hljs-keyword">async</span> (e) =&gt; {
    <span class="hljs-keyword">const</span> user = e.record;

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"New user registered:"</span>, user.email);

    <span class="hljs-comment">// Example: send a welcome email using a third-party API</span>
    <span class="hljs-keyword">const</span> emailResponse = <span class="hljs-keyword">await</span> $http.send({
        <span class="hljs-attr">url</span>: <span class="hljs-string">"https://api.example.com/send-email"</span>,
        <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
            <span class="hljs-attr">to</span>: user.email,
            <span class="hljs-attr">subject</span>: <span class="hljs-string">"Welcome to our app!"</span>,
            <span class="hljs-attr">message</span>: <span class="hljs-string">`Hi <span class="hljs-subst">${user.username}</span>, thanks for signing up!`</span>
        }),
        <span class="hljs-attr">headers</span>: {
            <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>
        }
    });

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Email status:"</span>, emailResponse.status);
});
</code></pre>
<p>This script runs automatically whenever a new record is created in the users collection. It picks up the user’s email, logs it, and uses PocketBase’s built-in HTTP client ($http) to call an external email service.</p>
<p>You can use the same pattern to validate data before saving, trigger webhooks, block actions, or update related records. Since everything runs inside PocketBase, you don’t need extra servers or functions to automate backend logic.</p>
<p>This makes it friendly for teams who may not be comfortable with Go but still want to add dynamic logic to their backend. You can find more details in the official documentation under “<a target="_blank" href="https://pocketbase.io/docs/js-overview/">Extend with JavaScript</a>.”</p>
<h2 id="heading-using-sdks-to-interact-with-pocketbase">Using SDKs to Interact with PocketBase</h2>
<p>To make it easier to communicate with your backend, PocketBase provides official SDKs for JavaScript and Dart.</p>
<p>The JavaScript SDK works well for browser-based or Node.js projects, while the Dart SDK is ideal for mobile apps built with Flutter. Both SDKs provide an easy way to connect, authenticate users, and perform CRUD operations without manually writing HTTP requests.</p>
<p>For example, in JavaScript you can connect and fetch data like this:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> PocketBase <span class="hljs-keyword">from</span> <span class="hljs-string">'pocketbase'</span>

const pb = new PocketBase(<span class="hljs-string">'http://127.0.0.1:8090'</span>)
const records = <span class="hljs-keyword">await</span> pb.collection(<span class="hljs-string">'posts'</span>).getList(<span class="hljs-number">1</span>, <span class="hljs-number">20</span>)
console.log(records)
</code></pre>
<p>This simplicity allows you to focus on building your frontend while PocketBase handles authentication, database operations, and real-time updates.</p>
<h2 id="heading-self-hosting-pocketbase-using-sevalla">Self-Hosting PocketBase using Sevalla</h2>
<p>When you are ready to move beyond testing, PocketBase gives you two options. You can self-host it using your own infrastructure or use their managed cloud version at <a target="_blank" href="https://pocketbase.io/">Pocketbase.io</a>.</p>
<p>Self-hosting gives you full control and is usually preferred by technical teams who want to keep sensitive data in-house.</p>
<p>You can choose any cloud provider, like AWS, DigitalOcean, or others to set up Pocketbase. But I will be using Sevalla.</p>
<p><a target="_blank" href="https://sevalla.com/">Sevalla</a> is a PaaS provider designed for developers and dev teams shipping features and updates constantly in the most efficient way. It offers application hosting, database, object storage, and static site hosting for your projects.</p>
<p>I am using Sevalla for two reasons:</p>
<ul>
<li><p>Every platform will charge you for creating a cloud resource. Sevalla comes with a $50 credit for us to use, so we won’t incur any costs for this example.</p>
</li>
<li><p>Sevalla has a <a target="_blank" href="https://docs.sevalla.com/templates/overview">template for PocketBase</a>, so it simplifies the manual installation and setup for each resource you will need for installation.</p>
</li>
</ul>
<p><a target="_blank" href="https://app.sevalla.com/login">Log in</a> to Sevalla and click on Templates. You can see Pocketbaseas one of the templates.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762853103062/870636bf-11df-4e3d-a73a-ebc0664818c2.png" alt="Sevalla Templates" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Click on the “PocketBase” template. You will see the resources needed to provision the application. Click on “Deploy Template”:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762853130642/f8ec7556-6544-4826-8c85-105e3893ca0c.png" alt="Sevalla Deployment" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>You can see the resource being provisioned. Once the deployment is complete, go to the PocketBase application and click on “Visit app”</p>
<p>You will see a 404 message. Add <code>_</code> to the url and you will see the login dashboard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762853157824/6399c01d-7ae1-4c06-9f1d-9d7951bf04f7.png" alt="Sevalla Deployment" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>To login for the first time, you will need a superuser login. To create that, go back to the application and click “Logs”. You will see a url starting with <a target="_blank" href="http://0.0.0.0.">https://0.0.0.0.</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762853191932/d217e02f-91e8-4d28-ad4f-7eeb8d44b113.png" alt="Pocketbase Deployment" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Replace the 0.0.0.0 with your new cloud URL and copy paste the full path into the browser. You will see the option to create a super user for your PocketBase deployment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762853227941/26c7086f-f95e-467a-a1db-97c34eadbcd4.png" alt="Pocketbase Create Super User" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Once you have created the super user, you can again go to <code>/_</code> and login using the username and password. You should now see the PocketBase dashboard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762853283532/8cfc48e4-383a-4e11-820e-d64d22ed937d.png" alt="Pocketbase Dashboard" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>You now have a production-grade Pocketbase server running on the cloud. You can use this to set up tables for your database and use the JavaScript or other SDKs to interact with Pocketbase.</p>
<h2 id="heading-security-and-open-source-nature">Security and Open Source Nature</h2>
<p>PocketBase is open source and licensed under MIT, which means you’re free to use it in personal or commercial projects. If you find a bug or security issue, you can report it to the maintainers, and they’ll address it promptly.</p>
<p>The project’s transparent development and active community make it a solid choice for startups, indie developers, and hobbyists who prefer to own their infrastructure.</p>
<p>Because it’s still in active development, backward compatibility isn’t guaranteed before version 1.0. But it’s already stable enough for small to medium-scale applications.</p>
<h2 id="heading-when-to-use-pocketbase">When to Use PocketBase</h2>
<p>PocketBase is perfect for projects that need a simple backend with low maintenance. It’s ideal for prototypes, small SaaS products, indie apps, internal tools, and educational projects.</p>
<p>Instead of setting up a complex stack with PostgreSQL, Node.js, and nginx, you can get your backend running instantly and focus on your product.</p>
<p>Suppose your project later grows into something bigger. In that case, you can migrate to a more complex setup or continue using PocketBase as a lightweight service for specific features like authentication or real-time data sync.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>PocketBase brings back the joy of fast development without complicated setups. With just one executable, you get a backend that supports authentication, real-time updates, file uploads, and an admin dashboard. It’s open source, fast, and customizable, making it a great choice for developers who want to move quickly without giving up control.</p>
<p>Whether you’re building a personal app, a startup MVP, or an internal dashboard, PocketBase gives you the power to set up a full backend in minutes. You can start small, extend it as needed, and deploy it anywhere  –  all while keeping your workflow simple and efficient.</p>
<p><em>Hope you enjoyed this article. Find me on</em> <a target="_blank" href="https://linkedin.com/in/manishmshiva"><em>Linkedin</em></a> <em>or</em> <a target="_blank" href="https://manishshivanandhan.com/"><em>visit my website</em></a><em>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Closures in Go ]]>
                </title>
                <description>
                    <![CDATA[ If you've written code in JavaScript, Python, or Rust, you've probably heard the word closure before. The concept has subtle differences in each language, but the core idea is the same: a closure is a function that captures variables from its surroun... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-closures-in-go/</link>
                <guid isPermaLink="false">68ffd791ca908949cd4f2afc</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ closure ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gabor Koos ]]>
                </dc:creator>
                <pubDate>Mon, 27 Oct 2025 20:35:29 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761597115311/04035f6b-6bd0-4889-8433-3f0d652f2586.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you've written code in JavaScript, Python, or Rust, you've probably heard the word <em>closure</em> before. The concept has subtle differences in each language, but the core idea is the same: a closure is a function that captures variables from its surrounding scope. This allows the function to "remember" the environment in which it was created, even when it's executed outside that environment, which has powerful implications for how we write and structure our code.</p>
<p>In this article, we'll explore how closures work in Go, a statically typed language known for its simplicity and efficiency. We'll look at how to create closures, how they capture variables, and some practical use cases.</p>
<h2 id="heading-what-well-cover">What We'll Cover</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-closures-really-are-in-go">What Closures Really Are in Go</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-go-makes-this-work">How Go Makes This Work</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-multiple-independent-closures">Multiple Independent Closures</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-the-classic-loop-trap">The Classic Loop Trap</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-closures-in-go">How to Create Closures in Go</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-returning-closures-from-functions">Returning Closures From Functions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-named-inner-functions">Named Inner Functions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-inline-closures-in-loops-or-goroutines">Inline Closures in Loops or Goroutines</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-closures-with-parameters">Closures With Parameters</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-capturing-multiple-variables">Capturing Multiple Variables</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-closures-in-structs">Closures in Structs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-note-on-method-receivers">Note on Method Receivers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-key-takeaways">Key Takeaways</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-closures-and-concurrency">Closures and Concurrency</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-independent-state-across-goroutines">Independent State Across Goroutines</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-capturing-shared-variables-safely">Capturing Shared Variables Safely</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-practical-patterns-with-closures">Practical Patterns with Closures</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-memoization-caching">Memoization / Caching</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-event-handlers-callbacks">Event Handlers / Callbacks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-encapsulated-pipelines-producers">Encapsulated Pipelines / Producers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-deferred-execution-with-captured-state">Deferred Execution with Captured State</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-implement-interfaces-dynamically">How to Implement Interfaces Dynamically</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-key-takeaways-1">Key Takeaways</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-closures-affect-memory-and-performance">How Closures Affect Memory and Performance</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-variables-may-live-longer-than-expected">Variables May Live Longer Than Expected</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-many-closures-can-add-overhead">Many Closures Can Add Overhead</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-and-debug-closures">How to Test and Debug Closures</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-isolate-the-closure">Isolate the Closure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-check-captured-variables">Check Captured Variables</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-logging-or-debug-prints">Use Logging or Debug Prints</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-test-concurrency-carefully">Test Concurrency Carefully</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-key-takeaways-2">Key Takeaways</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-best-practices-and-takeaways-for-using-closures-in-go">Best Practices and Takeaways for Using Closures in Go</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this article, you should have a basic understanding of Go programming, including functions and variable scope. If you're new to Go, consider checking out the <a target="_blank" href="https://tour.golang.org/">official Go tour</a> to get up to speed.</p>
<h2 id="heading-what-closures-really-are-in-go">What Closures Really Are in Go</h2>
<p>At its simplest, a closure in Go is a function that <strong>references variables defined outside of it</strong>. That may sound abstract, so let's start with an example you can run right away:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">counter</span><span class="hljs-params">()</span> <span class="hljs-title">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
    n := <span class="hljs-number">0</span>
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
        n++
        <span class="hljs-keyword">return</span> n
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    next := counter()
    fmt.Println(next()) <span class="hljs-comment">// 1</span>
    fmt.Println(next()) <span class="hljs-comment">// 2</span>
    fmt.Println(next()) <span class="hljs-comment">// 3</span>
}
</code></pre>
<p>When you call <code>counter()</code>, it returns another function, but that function <strong>keeps access to the variable</strong> <code>n</code> that lived inside counter.</p>
<p>Even though <code>counter()</code> has already finished running, <code>n</code> hasn't disappeared. Each time you call <code>next()</code>, it updates the same <code>n</code> that was created during the original <code>counter()</code> call.</p>
<p>This is the defining property of a closure:</p>
<blockquote>
<p>A closure "closes over" its environment, keeping the variables it needs alive for as long as the closure itself exists.</p>
</blockquote>
<h3 id="heading-how-go-makes-this-work">How Go Makes This Work</h3>
<p>Normally, local variables in Go live on the <strong>stack</strong>, which is cleared when a function returns.</p>
<p>But if a nested function needs to keep using one of those variables, Go's compiler performs what's called <em>escape analysis</em>: it sees that the variable will outlive the function call, so it moves that variable to the <strong>heap</strong>, where it can stay alive as long as something references it - in this case, the closure.</p>
<p>You can actually ask the compiler to show you this process:</p>
<pre><code class="lang-bash">go build -gcflags=<span class="hljs-string">"-m"</span> main.go
</code></pre>
<p>You might see output like:</p>
<pre><code class="lang-bash">./main.go:6:6: moved to heap: n
</code></pre>
<p>That tells you the variable <code>n</code> "escaped" the stack so the closure could use it safely later.</p>
<h3 id="heading-multiple-independent-closures">Multiple Independent Closures</h3>
<p>Each call to a function that returns a closure creates a new, independent environment:</p>
<pre><code class="lang-go">a := counter()
b := counter()
fmt.Println(a()) <span class="hljs-comment">// 1</span>
fmt.Println(a()) <span class="hljs-comment">// 2</span>
fmt.Println(b()) <span class="hljs-comment">// 1</span>
</code></pre>
<p>Here, <code>a</code> and <code>b</code> are two separate closures, each with its own <code>n</code>. Calling <code>a()</code> increments its own <code>n</code>, while calling <code>b()</code> starts from its own separate <code>n</code>.</p>
<h2 id="heading-the-classic-loop-trap">The Classic Loop Trap</h2>
<p>One of the most common surprises for Go developers comes when closures are used inside a loop. Even experienced programmers often fall into this trap.</p>
<p>Consider this example:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    funcs := <span class="hljs-built_in">make</span>([]<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span>, 0)</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
        funcs = <span class="hljs-built_in">append</span>(funcs, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            fmt.Println(i)
        })
    }
    <span class="hljs-keyword">for</span> _, f := <span class="hljs-keyword">range</span> funcs {
        f()
    }
}
</code></pre>
<p>You might expect this to print <code>0</code>, <code>1</code>, and <code>2</code>, but it actually prints</p>
<pre><code class="lang-bash">3
3
3
</code></pre>
<p><strong>Why Does this Happen?</strong></p>
<p>Inside the loop, each function literal <strong>captures the variable</strong> <code>i</code> itself, not its value at that moment.</p>
<p>The loop reuses the same <code>i</code> variable for all iterations. By the time the loop finishes, <code>i</code> equals 3, and <strong>all the closures see this same</strong> <code>i</code> when they run later.</p>
<p><strong>How to Fix It</strong></p>
<p>There are two common idiomatic fixes:</p>
<ol>
<li>Shadow the loop variable:</li>
</ol>
<pre><code class="lang-go"><span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
    i := i <span class="hljs-comment">// new variable shadows the loop variable</span>
    funcs = <span class="hljs-built_in">append</span>(funcs, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        fmt.Println(i)
    })
}
</code></pre>
<ol start="2">
<li>Pass the variable as a parameter to an inner function:</li>
</ol>
<pre><code class="lang-go"><span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
    funcs = <span class="hljs-built_in">append</span>(funcs, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> { fmt.Println(x) }
    }(i))
}
</code></pre>
<p>Both approaches create a new variable for each iteration, so each closure captures its own independent value.</p>
<h2 id="heading-how-to-create-closures-in-go">How to Create Closures in Go</h2>
<p>There are a few different ways to create closures in Go. Let's explore some common patterns.</p>
<h3 id="heading-returning-closures-from-functions">Returning Closures From Functions</h3>
<p>The most common pattern is having a function return a closure that keeps its own state:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">makeCounter</span><span class="hljs-params">()</span> <span class="hljs-title">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
    n := <span class="hljs-number">0</span>
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
        n++
        <span class="hljs-keyword">return</span> n
    }
}

c1 := makeCounter()
fmt.Println(c1()) <span class="hljs-comment">// 1</span>
fmt.Println(c1()) <span class="hljs-comment">// 2</span>
</code></pre>
<p>Each call to <code>makeCounter</code> creates a new closure with its own <code>n</code>, as we saw earlier.</p>
<h3 id="heading-named-inner-functions">Named Inner Functions</h3>
<p>You can also give a name to a function literal for readability or debugging:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">makeCounter</span><span class="hljs-params">()</span> <span class="hljs-title">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
    n := <span class="hljs-number">0</span>
    next := <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">incr</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
        n++
        <span class="hljs-keyword">return</span> n
    }
    <span class="hljs-keyword">return</span> next
}
</code></pre>
<p>This works the same way but gives the inner function a name (<code>incr</code>), which can be helpful in stack traces. Other than that, it behaves just like an anonymous function.</p>
<h3 id="heading-inline-closures-in-loops-or-goroutines">Inline Closures in Loops or Goroutines</h3>
<p>Closures are often defined inline, especially for loops or goroutines:</p>
<pre><code class="lang-go"><span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>)</span></span> {
        fmt.Println(x)
    }(i)
}
</code></pre>
<p>Here, we pass <code>i</code> as a parameter to the closure, ensuring each goroutine gets its own copy of the value, avoiding the loop variable trap.</p>
<h3 id="heading-closures-with-parameters">Closures With Parameters</h3>
<p>Closures can accept their own arguments:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">adder</span><span class="hljs-params">(base <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">func</span><span class="hljs-params">(<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
        <span class="hljs-keyword">return</span> base + x
    }
}

add5 := adder(<span class="hljs-number">5</span>)
fmt.Println(add5(<span class="hljs-number">10</span>)) <span class="hljs-comment">// 15</span>
</code></pre>
<p>Here, <code>adder</code> returns a closure that adds a fixed <code>base</code> value to whatever argument it receives.</p>
<h3 id="heading-capturing-multiple-variables">Capturing Multiple Variables</h3>
<p>Closures can capture multiple outer variables:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">multiplier</span><span class="hljs-params">(factor <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">func</span><span class="hljs-params">(<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    offset := <span class="hljs-number">2</span>
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
        <span class="hljs-keyword">return</span> x*factor + offset
    }
}

m := multiplier(<span class="hljs-number">3</span>)
fmt.Println(m(<span class="hljs-number">4</span>)) <span class="hljs-comment">// 14</span>
</code></pre>
<p>In this example, the closure captures both <code>factor</code> and <code>offset</code> from its surrounding scope - <code>factor</code> is a parameter, while <code>offset</code> is a local variable.</p>
<h3 id="heading-closures-in-structs">Closures in Structs</h3>
<p>Closures can also be stored in structs, just like any other function value. This is a useful pattern when you want objects with dynamic or stateful behavior.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Counter <span class="hljs-keyword">struct</span> {
    Next <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewCounter</span><span class="hljs-params">()</span> <span class="hljs-title">Counter</span></span> {
    n := <span class="hljs-number">0</span>
    <span class="hljs-keyword">return</span> Counter{
        Next: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
            n++
            <span class="hljs-keyword">return</span> n
        },
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    c := NewCounter()
    fmt.Println(c.Next()) <span class="hljs-comment">// 1</span>
    fmt.Println(c.Next()) <span class="hljs-comment">// 2</span>
}
</code></pre>
<p>Here, the <code>Next</code> field holds a closure that captures the variable <code>n</code>. Each instance of <code>Counter</code> has its own independent state, without needing a separate type or mutex.</p>
<p>This pattern shows how closures can act as lightweight objects: bundling behavior and state together.</p>
<h3 id="heading-note-on-method-receivers">Note on Method Receivers</h3>
<p>Closures in Go don't implicitly capture the method receiver like some languages do. If you want a closure to use the receiver inside a method, you typically assign it to a local variable:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Counter <span class="hljs-keyword">struct</span> {
    n <span class="hljs-keyword">int</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(c *Counter)</span> <span class="hljs-title">MakeIncrementer</span><span class="hljs-params">()</span> <span class="hljs-title">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
    r := c <span class="hljs-comment">// capture receiver explicitly</span>
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
        r.n++
        <span class="hljs-keyword">return</span> r.n
    }
}
</code></pre>
<p>This ensures the closure references the intended receiver rather than introducing unexpected behavior.</p>
<p>Unlike JavaScript or Python, Go closures capture lexical variables, not the implicit <code>this</code> or <code>self</code>.</p>
<h3 id="heading-key-takeaways">Key Takeaways</h3>
<ul>
<li><p>Closures can be returned from functions, named, inlined, or even stored in structs.</p>
</li>
<li><p>They capture outer variables, not copies of their values.</p>
</li>
<li><p>Used this way, closures can replace small types or interfaces for lightweight encapsulation.</p>
</li>
</ul>
<h2 id="heading-closures-and-concurrency">Closures and Concurrency</h2>
<p>Closures are powerful in Go, but when you combine them with concurrency, their captured variables can act in unexpected ways if you're not careful.</p>
<h3 id="heading-independent-state-across-goroutines">Independent State Across Goroutines</h3>
<p>Each closure keeps its own captured variables alive, even when used in concurrent goroutines:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">makeWorker</span><span class="hljs-params">(start <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
    counter := start
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
        counter++
        <span class="hljs-keyword">return</span> counter
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    worker1 := makeWorker(<span class="hljs-number">0</span>)
    worker2 := makeWorker(<span class="hljs-number">100</span>)

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> { fmt.Println(worker1()) }() <span class="hljs-comment">// prints 1</span>
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> { fmt.Println(worker2()) }() <span class="hljs-comment">// prints 101</span>
}
</code></pre>
<p>Here, <code>worker1</code> and <code>worker2</code> have independent <code>counter</code> variables, so they don't interfere with each other. Each closure maintains independent state, even in separate goroutines.</p>
<h3 id="heading-capturing-shared-variables-safely">Capturing Shared Variables Safely</h3>
<p>When multiple closures share a variable, you must coordinate access. For example:</p>
<pre><code class="lang-go">counter := <span class="hljs-number">0</span>
ch := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)

<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// increments a shared variable</span>
        ch &lt;- <span class="hljs-number">1</span>
    }()
}

<span class="hljs-comment">// aggregate safely</span>
<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
    counter += &lt;-ch
}
fmt.Println(counter) <span class="hljs-comment">// 3</span>
</code></pre>
<p>The closure captures the outer variable <code>ch</code> (a channel), which is safe because channels serialize access. Using a buffered channel here wouldn't change the behavior of the closure: it still captures its own <code>n</code> and sends the values to the channel independently.</p>
<p>Closures themselves <strong>don't synchronize shared state, you still need channels or mutexes</strong>.</p>
<h2 id="heading-practical-patterns-with-closures">Practical Patterns with Closures</h2>
<p>Closures in Go aren't just a language curiosity, they're a powerful tool for writing stateful, reusable, and flexible code. Here are a few practical patterns that go beyond the basics.</p>
<h3 id="heading-memoization-caching">Memoization / Caching</h3>
<p>Closures can capture an internal map or cache to store results of expensive computations:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">memoize</span><span class="hljs-params">(f <span class="hljs-keyword">func</span>(<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span>) <span class="hljs-title">func</span><span class="hljs-params">(<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    cache := <span class="hljs-keyword">map</span>[<span class="hljs-keyword">int</span>]<span class="hljs-keyword">int</span>{}
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
        <span class="hljs-keyword">if</span> val, ok := cache[x]; ok {
            <span class="hljs-keyword">return</span> val
        }
        result := f(x)
        cache[x] = result
        <span class="hljs-keyword">return</span> result
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    fib := memoize(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(n <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
        <span class="hljs-keyword">if</span> n &lt;= <span class="hljs-number">1</span> {
            <span class="hljs-keyword">return</span> n
        }
        <span class="hljs-keyword">return</span> fib(n<span class="hljs-number">-1</span>) + fib(n<span class="hljs-number">-2</span>)
    })
    fmt.Println(fib(<span class="hljs-number">10</span>)) <span class="hljs-comment">// 55</span>
}
</code></pre>
<p>Here, the <code>memoize</code> function returns a closure that caches results of the Fibonacci function, avoiding redundant calculations.</p>
<h3 id="heading-event-handlers-callbacks">Event Handlers / Callbacks</h3>
<p>Closures are perfect for defining event handlers or callbacks that need to maintain state:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Button <span class="hljs-keyword">struct</span> {
    onClick <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(b *Button)</span> <span class="hljs-title">Click</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> b.onClick != <span class="hljs-literal">nil</span> {
        b.onClick()
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    count := <span class="hljs-number">0</span>
    button := Button{
        onClick: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            count++
            fmt.Println(<span class="hljs-string">"Button clicked"</span>, count, <span class="hljs-string">"times"</span>)
        },
    }

    button.Click() <span class="hljs-comment">// Button clicked 1 times</span>
    button.Click() <span class="hljs-comment">// Button clicked 2 times</span>
}
</code></pre>
<p>In this example, the closure captures the <code>count</code> variable, allowing the button to keep track of how many times it has been clicked.</p>
<h3 id="heading-encapsulated-pipelines-producers">Encapsulated Pipelines / Producers</h3>
<p>Closures can wrap stateful logic for channels and pipelines:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">producer</span><span class="hljs-params">(start <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">func</span><span class="hljs-params">(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span></span> {
    n := start
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(ch <span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span></span> {
        <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
            ch &lt;- n
            n++
        }
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    ch := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, <span class="hljs-number">3</span>)
    <span class="hljs-keyword">go</span> producer(<span class="hljs-number">5</span>)(ch)
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
        fmt.Println(&lt;-ch) <span class="hljs-comment">// 5, 6, 7</span>
    }
}
</code></pre>
<p>Here, the <code>producer</code> function returns a closure that sends a sequence of numbers to a channel, maintaining its own state with <code>n</code>.</p>
<h3 id="heading-deferred-execution-with-captured-state">Deferred Execution with Captured State</h3>
<p>Using a closure with <code>defer</code> lets you capture variables at the moment the defer statement is executed, which is especially useful in loops or resource cleanup:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
        <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>)</span></span> {
            fmt.Println(x)
        }(i) <span class="hljs-comment">// capture current i</span>
    }
}
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">2
1
0
</code></pre>
<p>Here, each deferred closure captures the value of <code>i</code> at the time of the <code>defer</code> statement, so they print in reverse order when the function exits.</p>
<h3 id="heading-how-to-implement-interfaces-dynamically">How to Implement Interfaces Dynamically</h3>
<p>Closures can also be used to implement interfaces without defining a full struct type. For example, a simple function can satisfy a single-method interface:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Greeter <span class="hljs-keyword">interface</span> {
    Greet() <span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">MakeGreeter</span><span class="hljs-params">(name <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">Greeter</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">struct</span>{ Greeter }{
        Greeter: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> { <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello, "</span> + name },
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    g := MakeGreeter(<span class="hljs-string">"Alice"</span>)
    fmt.Println(g.Greet()) <span class="hljs-comment">// Hello, Alice</span>
}
</code></pre>
<p>Here, the closure captures <code>name</code>, allowing the returned object to implement the <code>Greet</code> method dynamically.</p>
<h3 id="heading-key-takeaways-1">Key Takeaways</h3>
<ul>
<li><p>Closures allow memoization and caching without extra structs.</p>
</li>
<li><p>Storing closures in structs provides customizable behavior for objects.</p>
</li>
<li><p>Closures can encapsulate stateful concurrent pipelines, keeping logic localized and safe.</p>
</li>
<li><p>Closures with <code>defer</code> capture variables at the time of deferment, useful for cleanup or logging.</p>
</li>
<li><p>They enable dynamic interface implementations without boilerplate types.</p>
</li>
</ul>
<h2 id="heading-how-closures-affect-memory-and-performance">How Closures Affect Memory and Performance</h2>
<p>Closures are powerful, but capturing variables from outer scopes has memory and performance implications.</p>
<h3 id="heading-variables-may-live-longer-than-expected">Variables May Live Longer Than Expected</h3>
<p>Because closures keep references to captured variables (and move them to the heap if necessary, as we saw earlier), these variables live as long as the closure itself, which can increase memory usage:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    bigData := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">byte</span>, <span class="hljs-number">10</span>_000_000) <span class="hljs-comment">// 10MB</span>
    f := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> { <span class="hljs-keyword">return</span> <span class="hljs-built_in">len</span>(bigData) }
    _ = f
}
</code></pre>
<p>In this example, <code>bigData</code> remains in memory as long as the closure <code>f</code> exists, even if <code>bigData</code> is no longer needed elsewhere.</p>
<h3 id="heading-many-closures-can-add-overhead">Many Closures Can Add Overhead</h3>
<p>Each closure carries a small environment for its captured variables. Creating thousands of closures is usually fine, but in high-performance or memory-sensitive code, this can add measurable overhead.</p>
<ul>
<li><p>Captured variables may be heap-allocated.</p>
</li>
<li><p>Each closure has a small hidden struct for its environment.</p>
</li>
</ul>
<p>Alternatives include <strong>structs</strong> or <strong>plain functions</strong> when you need maximum efficiency.</p>
<h2 id="heading-how-to-test-and-debug-closures">How to Test and Debug Closures</h2>
<p>Closures can sometimes behave in unexpected ways when capturing variables or working with concurrency. Here are some tips to test and debug them effectively.</p>
<h3 id="heading-isolate-the-closure">Isolate the Closure</h3>
<p>Test the closure independently of its outer function to verify its behavior:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestCounter</span><span class="hljs-params">(t *testing.T)</span></span> {
    counter := makeCounter()
    <span class="hljs-keyword">if</span> counter() != <span class="hljs-number">1</span> {
        t.Error(<span class="hljs-string">"expected 1"</span>)
    }
    <span class="hljs-keyword">if</span> counter() != <span class="hljs-number">2</span> {
        t.Error(<span class="hljs-string">"expected 2"</span>)
    }
}
</code></pre>
<p>This ensures the closure maintains state correctly.</p>
<h3 id="heading-check-captured-variables">Check Captured Variables</h3>
<p>Remember: closures capture variables by reference, not value. Be mindful of loop variables or shared state:</p>
<pre><code class="lang-go"><span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
    i := i <span class="hljs-comment">// shadow loop variable</span>
    t.Run(fmt.Sprintf(<span class="hljs-string">"i=%d"</span>, i), <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
        <span class="hljs-keyword">if</span> i != i { <span class="hljs-comment">// simplified check</span>
            t.Fail()
        }
    })
}
</code></pre>
<p>This helps avoid the loop trap in tests.</p>
<h3 id="heading-use-logging-or-debug-prints">Use Logging or Debug Prints</h3>
<p>Printing internal closure state is often the fastest way to debug subtle behavior:</p>
<pre><code class="lang-go">adder := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(base <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">func</span><span class="hljs-params">(<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(x <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
        fmt.Printf(<span class="hljs-string">"base=%d, x=%d\n"</span>, base, x)
        <span class="hljs-keyword">return</span> base + x
    }
}
result := adder(<span class="hljs-number">5</span>)(<span class="hljs-number">10</span>) <span class="hljs-comment">// logs: base=5, x=10</span>
</code></pre>
<h3 id="heading-test-concurrency-carefully">Test Concurrency Carefully</h3>
<p>When closures are used in goroutines, race conditions can creep in. Use the Go race detector:</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span> -race ./...
</code></pre>
<p>This flags any shared variable access that isn’t properly synchronized.</p>
<h3 id="heading-key-takeaways-2">Key Takeaways</h3>
<ul>
<li><p>Test closures independently to ensure captured state behaves as expected.</p>
</li>
<li><p>Be cautious with loop variables and shared state.</p>
</li>
<li><p>Use logging and the race detector to debug concurrency issues.</p>
</li>
</ul>
<h2 id="heading-best-practices-and-takeaways-for-using-closures-in-go">Best Practices and Takeaways for Using Closures in Go</h2>
<p>Closures are a versatile feature in Go, but like any tool, they work best when used thoughtfully. Here are some practical guidelines:</p>
<ul>
<li><p><strong>Encapsulate state cleanly</strong>: Use closures to maintain private state without introducing extra structs or types. Counters, memoization caches, and small factories are common patterns.</p>
</li>
<li><p><strong>Be careful in loops</strong>: Always capture loop variables correctly to avoid the classic loop trap. Shadowing the variable or passing it as a parameter to the closure are idiomatic solutions.</p>
</li>
<li><p><strong>Handle concurrency explicitly</strong>: Closures can safely maintain independent state in goroutines, but they do not synchronize shared state automatically. When multiple closures share variables, coordinate access with channels or mutexes.</p>
</li>
<li><p><strong>Mind memory usage</strong>: Captured variables may escape to the heap, so long-lived closures can retain more memory than expected. Avoid capturing large objects unless necessary.</p>
</li>
<li><p><strong>Leverage closures in structs</strong>: Storing closures in struct fields allows objects to have dynamic or customizable behavior without extra boilerplate, making your code more flexible.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Closures in Go allow functions to carry state, encapsulate behavior, and interact safely with concurrency patterns, all while keeping your code clean and expressive. By understanding how closures capture variables, how they behave in loops and goroutines, and their memory implications, you can use them confidently to write more idiomatic and maintainable Go code.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Cache Golang API Responses for High Performance ]]>
                </title>
                <description>
                    <![CDATA[ Go makes it easy to build APIs that are fast out of the box. But as usage grows, speed at the language level is not enough. If every request keeps hitting the database, crunching the same data, or serializing the same JSON over and over, latency cree... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-cache-golang-api-responses/</link>
                <guid isPermaLink="false">68ef76f4e9381bb61f442e34</guid>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cache ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Temitope Oyedele ]]>
                </dc:creator>
                <pubDate>Wed, 15 Oct 2025 10:27:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1760523799795/3b48a898-77fc-4983-90b5-6e21e8019f1e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Go makes it easy to build APIs that are fast out of the box. But as usage grows, speed at the language level is not enough. If every request keeps hitting the database, crunching the same data, or serializing the same JSON over and over, latency creeps up and throughput suffers. Caching is the tool that keeps performance high by storing work that has already been done so that future requests can reuse it instantly. Let’s look at four practical ways to cache APIs in Go, each explained with an analogy and backed by simple code you can adapt.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-response-caching-with-local-and-redis-storage">Response Caching with Local and Redis Storage</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-database-query-result-caching">Database Query Result Caching</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-http-caching-with-etag-and-cache-control">HTTP Caching with ETag and Cache-Control</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-stale-while-revalidate-with-background-refresh">Stale-While-Revalidate with Background Refresh</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-response-caching-with-local-and-redis-storage">Response Caching with Local and Redis Storage</h2>
<p>When the process of generating an API response becomes expensive, the fastest solution is to store the entire response. Think of a coffee shop during the morning rush. If every customer orders the same latte, the barista could grind beans and steam milk for each order, but the line would move slowly. A smarter move is to brew a pot once and pour from it repeatedly. To handle both speed and scale, the shop keeps a small pot at the counter for instant pours and a larger urn in the back for refills. In software terms, the counter pot is a local in-memory cache such as <a target="_blank" href="https://pkg.go.dev/github.com/dgraph-io/ristretto">Ristretto</a> or <a target="_blank" href="https://pkg.go.dev/github.com/allegro/bigcache">BigCache</a>, and the urn is <a target="_blank" href="https://redis.io/">Redis,</a> which allows multiple API servers to share the same cached responses.</p>
<p>In Go, this two-tier setup usually follows a cache-aside pattern: look in local memory first, fall back to Redis if needed, and only compute the result when both layers miss. Once computed, the value is saved in Redis for everyone and in memory for immediate reuse on the next call.</p>
<pre><code class="lang-go">val, ok := local.Get(key)
<span class="hljs-keyword">if</span> !ok {
    val, err = rdb.Get(ctx, key).Result()
    <span class="hljs-keyword">if</span> err == redis.Nil {
        val = computeResponse() <span class="hljs-comment">// expensive DB or logic</span>
        _ = rdb.Set(ctx, key, val, <span class="hljs-number">60</span>*time.Second).Err()
    }
    local.Set(key, val, <span class="hljs-number">1</span>)
}
w.Header().Set(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"application/json"</span>)
w.Write([]<span class="hljs-keyword">byte</span>(val))
</code></pre>
<p>In the code above, the first attempt is to retrieve the response from the local cache, which returns instantly if the key or data exists. If not found, it queries Redis as the second layer. If Redis also returns nothing, the expensive computation runs and its result is stored in Redis with a sixty seconds expiration so other services can access it, then placed in the local cache for immediate reuse. After which, the response is written back to the client as JSON.</p>
<p>This gives you the best of both worlds: lightning-fast responses for repeat calls and a consistent cache across all your API servers.</p>
<h2 id="heading-database-query-result-caching">Database Query Result Caching</h2>
<p>Sometimes the API itself is simple but the real cost hides in the database. Imagine a newsroom waiting for election results. If every editor keeps calling the counting office for the same numbers, the phone lines may jam. Instead, one reporter calls once, writes the result on a board, and every editor copies from there. The board is the cache, and it saves both time and pressure on the office.</p>
<p>In Go, you can apply the same principle by caching query results. Rather than hitting the database for each identical request, you store the result in Redis with a key that represents the query intent. When the next request comes in, you pull from Redis, skip the database, and respond faster.</p>
<pre><code class="lang-go">key := fmt.Sprintf(<span class="hljs-string">"q:UserByID:%d"</span>, id)
<span class="hljs-keyword">if</span> b, err := rdb.Get(ctx, key).Bytes(); err == <span class="hljs-literal">nil</span> {
    <span class="hljs-keyword">var</span> u User
    _ = json.Unmarshal(b, &amp;u)
    <span class="hljs-keyword">return</span> u
}

u, _ := repo.GetUser(ctx, id) <span class="hljs-comment">// real DB call</span>
bb, _ := json.Marshal(u)
_ = rdb.Set(ctx, key, bb, <span class="hljs-number">2</span>*time.Minute).Err()
<span class="hljs-keyword">return</span> u
</code></pre>
<p>Here, we construct a cache key that uniquely identifies the query using the user ID, then attempts to fetch the serialized result from Redis. If the key exists, it deserializes the bytes back into a <code>User</code> struct and returns immediately without touching the database. On a cache miss, it executes the actual database query through the repository, serializes the <code>User</code> object to JSON, stores it in Redis with a two-minute expiration, and returns the result.</p>
<p>This pattern dramatically reduces database load and response time for read-heavy APIs, but you must remember to clear or refresh entries when data changes, or set short time-to-live values to keep results reasonably fresh.</p>
<h2 id="heading-http-caching-with-etag-and-cache-control">HTTP Caching with ETag and Cache-Control</h2>
<p>Not all caching has to happen inside the server. The HTTP standard already provides tools that let clients or CDNs reuse responses. By setting headers like <code>ETag</code> and <code>Cache-Control</code>, you can tell the client whether the response has changed. If nothing is new, the client keeps its own copy and the server only sends a lightweight 304 response.</p>
<p>It is similar to a manager posting notices on an office board. Each sheet carries a small stamp. Employees compare the stamp against the one they already have. If it matches, they know their copy is still valid and skip taking a new one. Only when the stamp changes do they replace it.</p>
<p>In Go this is straightforward. Compute an ETag from the response body, compare it with what the client sends, and decide whether to return the full payload or just the 304.</p>
<pre><code class="lang-go">etag := computeETag(responseBytes)
<span class="hljs-keyword">if</span> match := r.Header.Get(<span class="hljs-string">"If-None-Match"</span>); match == etag {
    w.WriteHeader(http.StatusNotModified)
    <span class="hljs-keyword">return</span>
}

w.Header().Set(<span class="hljs-string">"ETag"</span>, etag)
w.Header().Set(<span class="hljs-string">"Cache-Control"</span>, <span class="hljs-string">"public, max-age=60"</span>)
w.Write(responseBytes)
</code></pre>
<p>The code above generates an ETag, which is a fingerprint or hash of the response content, then checks if the client sent an <code>If-None-Match</code> header with a matching ETag from a previous request. If the ETags match, the content hasn't changed, so the server responds with a 304 Not Modified status and sends no body, saving bandwidth. When the ETags don't match or the client has no cached version, the server attaches the new ETag and a <code>Cache-Control</code> header that allows public caching for sixty seconds, then sends the full response.</p>
<p>This approach reduces bandwidth, lowers CPU usage, and pairs well with CDNs that can cache and serve responses directly.</p>
<h2 id="heading-stale-while-revalidate-with-background-refresh">Stale-While-Revalidate with Background Refresh</h2>
<p>There are cases where serving slightly old data is acceptable if it keeps the API fast. Stock dashboards, analytics summaries, or feed endpoints often fit this model. Instead of making users wait for fresh data on every request, you can serve the cached value immediately and refresh it quietly in the background. This technique is called Stale-While-Revalidate.</p>
<p>Picture a stock ticker screen in a lobby. The numbers may be a few seconds behind, but they are still useful to anyone glancing at the board. Meanwhile, a background process fetches the latest figures and updates the ticker. The reader never stares at a blank screen and the system stays responsive even during spikes.</p>
<p>In Go, this can be built by storing not just the cached data but also timestamps that define when the data is fresh, when it can still be served as stale, and when it must be recomputed. The <code>singleflight</code> package helps ensure that only one goroutine does the refresh work, preventing a dogpile of updates.</p>
<pre><code class="lang-go">entry := getEntry(key) <span class="hljs-comment">// {data, freshUntil, staleUntil}</span>
<span class="hljs-keyword">switch</span> {
<span class="hljs-keyword">case</span> time.Now().Before(entry.freshUntil):
    <span class="hljs-keyword">return</span> entry.data
<span class="hljs-keyword">case</span> time.Now().Before(entry.staleUntil):
    <span class="hljs-keyword">go</span> refreshSingleflight(key) <span class="hljs-comment">// background refresh</span>
    <span class="hljs-keyword">return</span> entry.data
<span class="hljs-keyword">default</span>:
    <span class="hljs-keyword">return</span> refreshSingleflight(key) <span class="hljs-comment">// must refresh now</span>
}
</code></pre>
<p>Here, the code retrieves a cache entry containing the data along with two timestamps marking the freshness and staleness boundaries. If the current time falls before the fresh threshold, the data is considered fully fresh and returned immediately. If time has passed the fresh threshold but remains within the stale window, the code returns the slightly outdated data instantly while launching a background goroutine to refresh it asynchronously, ensuring the next request gets updated information. Once time exceeds even the stale boundary, the data is too old to serve, so the code blocks and performs a synchronous refresh before returning.</p>
<p>This keeps latency low while still ensuring the cache updates regularly, a balance between freshness and performance.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Caching is not a single tactic but a set of strategies that fit different needs. Full response caching eliminates repeat work at the top level. Query result caching protects the database from repeated load. HTTP caching leverages the protocol to cut down data transfer. Stale-While-Revalidate strikes a compromise that favors speed without leaving data stale for too long.</p>
<p>In practice, these approaches are often layered. A Go API might use local memory and Redis for responses, apply query-level caching for hot tables, and set ETags so clients avoid unnecessary downloads. With the right mix, you can cut latency by orders of magnitude, handle far more traffic, and save both compute and database resources.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn How to Use Pointers in Go – With Example Code ]]>
                </title>
                <description>
                    <![CDATA[ Pointers are a fundamental but often dreaded concept in every programming language that supports them. Luckily for us, Go makes working with pointers straightforward and safe. In this article, we will demystify pointers in Go. You'll learn: What poi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-how-to-use-pointers-in-go-with-example-code/</link>
                <guid isPermaLink="false">68e3da89c573b56a3f251e06</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ pointers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gabor Koos ]]>
                </dc:creator>
                <pubDate>Mon, 06 Oct 2025 15:04:41 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759763060124/8b3f21fa-052e-4c18-a1b4-9fe4fd456830.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Pointers are a fundamental but often dreaded concept in every programming language that supports them. Luckily for us, Go makes working with pointers straightforward and safe.</p>
<p>In this article, we will demystify pointers in Go. You'll learn:</p>
<ul>
<li><p>What pointers are and how to use them</p>
</li>
<li><p>How to declare pointers and dereference them</p>
</li>
<li><p>Common pitfalls, like nil pointers and reference types</p>
</li>
<li><p>Pointer receivers in structs (a key reason pointers are so useful in Go)</p>
</li>
<li><p>A bonus look at weak pointers (Go 1.24+) for advanced memory management</p>
</li>
</ul>
<p>By the end, you'll have a solid understanding of pointers and be confident using them in your own Go programs.</p>
<h2 id="heading-what-well-cover">What We’ll Cover:</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-pointer">What is a Pointer?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-understanding-memory">Understanding Memory</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-stack-vs-heap">Stack vs Heap</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-pointer">The Pointer</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-declaring-and-using-pointers">Declaring and Using Pointers</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-pointers-to-basic-types">Pointers to Basic Types</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-getting-an-address-with-amp">Getting an Address with &amp;</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-pointers-to-structs">Pointers to Structs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-pointers-to-other-user-types">Pointers to Other User Types</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-why-use-pointers">Why Use Pointers?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-avoiding-copies">Avoiding Copies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sharing-and-mutating-state">Sharing and Mutating State</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-method-receivers">Method Receivers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-interfacing-with-low-level-apis">Interfacing with Low-Level APIs</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-common-pitfalls-and-misunderstandings">Common Pitfalls and Misunderstandings</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-nil-pointers">Nil Pointers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-reference-types">Reference Types</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-pointer-receivers">Pointer Receivers</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-value-receivers-vs-pointer-receivers">Value Receivers vs Pointer Receivers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-idiomatic-go">Idiomatic Go</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-exercises-for-the-reader">Exercises for the Reader</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-bonus-weak-pointers-go-124">Bonus: Weak Pointers (Go 1.24+)</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-are-weak-pointers">What Are Weak Pointers?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-weak-pointers">When to Use Weak Pointers?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-summary-amp-best-practices">Summary &amp; Best Practices</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-solutions-to-exercises">Solutions to Exercises</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This article assumes you have a basic understanding of Go, including:</p>
<ul>
<li><p>Variables and basic types (<code>int</code>, <code>string</code>, and so on)</p>
</li>
<li><p>Functions and function calls</p>
</li>
<li><p>Structs and methods</p>
</li>
</ul>
<p>Familiarity with memory concepts (like copying vs referencing values) can be helpful, but it is not required. I’ll explain all examples in a beginner-friendly way.</p>
<h2 id="heading-what-is-a-pointer">What is a Pointer?</h2>
<h3 id="heading-understanding-memory">Understanding Memory</h3>
<p>Memory in a computer is a large sequence of bytes, each with a unique address. Every variable in a program occupies one or more contiguous bytes in memory, depending on its type:</p>
<ul>
<li><p>An <code>int32</code> typically occupies 4 bytes.</p>
</li>
<li><p>An <code>int64</code> typically occupies 8 bytes.</p>
</li>
<li><p>A <code>bool</code> usually occupies 1 byte.</p>
</li>
</ul>
<p>Structs, arrays, and slices occupy the sum of their fields' sizes, plus potential padding for alignment (for quick access). Each variable has a unique memory address, which is where its data is stored.</p>
<p>For example, consider:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> a <span class="hljs-keyword">int32</span> = <span class="hljs-number">100</span>
<span class="hljs-keyword">var</span> b <span class="hljs-keyword">bool</span> = <span class="hljs-literal">true</span>
</code></pre>
<p>This will look something like this in memory:</p>
<p><img src="https://i.ibb.co/B2jZ3Smc/memory.png" alt="Memory Layout" width="600" height="400" loading="lazy"></p>
<p><code>a</code> occupies 4 bytes and holds the value <code>100</code>. <code>b</code> occupies 1 byte and holds <code>true</code>. A variable's address is simply the location in memory where its data starts (0×02022 for <code>a</code>, and 0x0207 for <code>b</code> in this example).</p>
<h3 id="heading-stack-vs-heap">Stack vs Heap</h3>
<p>In Go, variables can be allocated on the <strong>stack</strong> or the <strong>heap</strong>. The stack is a region of memory that stores local variables and function call information. It’s fast to allocate and deallocate, as it works in a last-in-first-out manner.</p>
<p>The heap is a larger pool of memory used for dynamic allocation. Variables allocated on the heap can outlive the function that created them, making them suitable for data that needs to be shared or modified across different parts of a program.</p>
<p>The Go runtime automatically manages memory allocation and garbage collection, so you don't need to worry about manually freeing memory like in some other languages.</p>
<p>The stack and heap are just implementation details. As a Go programmer, you typically don't need to worry about where a variable is allocated. The Go compiler and runtime handle this for you. You definitely don't have to worry about it in this article – just know they exist and that pointers can point to values in either location.</p>
<h3 id="heading-the-pointer">The Pointer</h3>
<p>A pointer is simply a variable that <strong>stores the memory address of another variable</strong>. From the diagram above, you can see that an address is basically an integer value (which happens to represent a location in memory). On a 64-bit system, addresses are typically 8 bytes (64 bits) long, so a pointer variable will also occupy 8 bytes.</p>
<p>In Go, you declare a pointer using the <code>*</code> operator. Pointers also have a <strong>type</strong>, which is the type of the variable they point to. For example:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> p *<span class="hljs-keyword">int32</span> <span class="hljs-comment">// a pointer to an int32</span>
</code></pre>
<p>You can get the address of a variable using the <code>&amp;</code> operator:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> a <span class="hljs-keyword">int32</span> = <span class="hljs-number">100</span>
<span class="hljs-keyword">var</span> p *<span class="hljs-keyword">int32</span> = &amp;a <span class="hljs-comment">// p now holds the address of a</span>
</code></pre>
<p>In memory:</p>
<p><img src="https://i.ibb.co/4ZGzs3Rz/pointer.png" alt="Pointer Example" width="600" height="400" loading="lazy"></p>
<p><code>a</code> holds the value <code>100</code> at address <code>0x0202</code>. <code>p</code> holds the address of <code>a</code> (<code>0x0202</code>), and <code>p</code> itself is stored at its own address (<code>0x0207</code>).</p>
<p>The reason pointers carry type information is that you can <strong>dereference</strong> them: follow the address to access the underlying value. This is also done using the <code>*</code> operator:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> a <span class="hljs-keyword">int32</span> = <span class="hljs-number">100</span>
<span class="hljs-keyword">var</span> p *<span class="hljs-keyword">int32</span> = &amp;a <span class="hljs-comment">// p now holds the address of a</span>

fmt.Println(*p) <span class="hljs-comment">// prints the value at the address p points to, which is the value of a: 100</span>
</code></pre>
<p>This dual use of <code>*</code> is a common source of confusion, so let's clarify:</p>
<ol>
<li><p>In a <em>type declaration</em> (like <code>var p *int32</code>), <code>*</code> indicates that <code>p</code> is a pointer to an <code>int32</code>.</p>
</li>
<li><p>In an <em>expression</em> (like <code>*p</code>), <code>*</code> dereferences the pointer, giving you access to the value it points to.</p>
</li>
</ol>
<p>Next, let's break down how to declare and use pointers in practice, so you can see how <code>&amp;</code> and <code>*</code> work together.</p>
<h2 id="heading-declaring-and-using-pointers">Declaring and Using Pointers</h2>
<p>Now that we know what pointers are conceptually, let's see how they look in real Go code.</p>
<h3 id="heading-pointers-to-basic-types">Pointers to Basic Types</h3>
<p>As we saw earlier, you can declare a pointer variable with the <code>*</code> operator in the type:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> p *<span class="hljs-keyword">int</span>      <span class="hljs-comment">// p is a pointer to an int, but currently nil</span>
fmt.Println(p)  <span class="hljs-comment">// &lt;nil&gt;</span>
</code></pre>
<p>Like every variable in Go, if you don't initialize it, it defaults to the zero value for its type. For pointers, the zero value is <code>nil</code>, meaning it doesn't point to any valid memory address.</p>
<p>You can also use the built-in <code>new</code> function to allocate a value and get its pointer:</p>
<pre><code class="lang-go">p := <span class="hljs-built_in">new</span>(<span class="hljs-keyword">int</span>)  <span class="hljs-comment">// p is a pointer to an int with a zero value (0 for int)</span>
fmt.Println(*p) <span class="hljs-comment">// prints 0, the zero value for int</span>
</code></pre>
<h3 id="heading-getting-an-address-with-amp">Getting an Address with <code>&amp;</code></h3>
<p>The <code>&amp;</code> operator retrieves the address of an existing variable:</p>
<pre><code class="lang-go">x := <span class="hljs-number">42</span>
p := &amp;x <span class="hljs-comment">// p now holds the address of x</span>

fmt.Println(*p) <span class="hljs-comment">// 42</span>
*p = <span class="hljs-number">99</span>         <span class="hljs-comment">// change the value at the address p points to (which is x)</span>
fmt.Println(x)  <span class="hljs-comment">// 99</span>
fmt.Println(p)  <span class="hljs-comment">// prints the memory address of x, e.g., 0xc0000140b8</span>
fmt.Println(&amp;x) <span class="hljs-comment">// prints the same address as p</span>
</code></pre>
<h3 id="heading-pointers-to-structs">Pointers to Structs</h3>
<p>Pointers can point to any type. They are especially common with <strong>structs</strong>:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> User <span class="hljs-keyword">struct</span> {
    Name <span class="hljs-keyword">string</span>
    Age  <span class="hljs-keyword">int</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    u := User{<span class="hljs-string">"Alice"</span>, <span class="hljs-number">30</span>}
    p := &amp;u                 <span class="hljs-comment">// pointer to User</span>
    fmt.Println((*p).Age)   <span class="hljs-comment">// 30</span>
    fmt.Println(p.Name)     <span class="hljs-comment">// Alice - shorthand for (*p).Name</span>
}
</code></pre>
<p>You can access fields with either <code>(*p).Name</code> or simply <code>p.Name</code>. Go <strong>automatically dereferences struct pointers</strong> for convenience.</p>
<p>We'll explore struct pointers more in the "Pointer Receivers" section.</p>
<h3 id="heading-pointers-to-other-user-types">Pointers to Other User Types</h3>
<p>You can create pointers to any named type:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Point <span class="hljs-keyword">struct</span> {
    X, Y <span class="hljs-keyword">int</span>
}
p := &amp;Point{X: <span class="hljs-number">1</span>, Y: <span class="hljs-number">2</span>} <span class="hljs-comment">// pointer to Point</span>
fmt.Println(p.X, p.Y)   <span class="hljs-comment">// 1 2 - shorthand for (*p).X and (*p).Y</span>
</code></pre>
<p>The syntax works exactly the same for user-defined types as it does for built-ins.</p>
<h2 id="heading-why-use-pointers">Why Use Pointers?</h2>
<p>At first glance, pointers may look like mental gymnastics. Why not just use values directly? Here are some key reasons to use pointers in Go:</p>
<h3 id="heading-avoiding-copies">Avoiding Copies</h3>
<p>When you assign a value in Go, it's copied:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> User <span class="hljs-keyword">struct</span> {
    Name <span class="hljs-keyword">string</span>
    Age  <span class="hljs-keyword">int</span>
}

u1 := User{<span class="hljs-string">"Alice"</span>, <span class="hljs-number">30</span>}
u2 := u1  <span class="hljs-comment">// copy</span>
u2.Age = <span class="hljs-number">40</span>

fmt.Println(u1.Age) <span class="hljs-comment">// 30</span>
fmt.Println(u2.Age) <span class="hljs-comment">// 40</span>
</code></pre>
<p>When the struct is small with just a few fields (like this example), copying is cheap. But if it's large (hundreds of fields or nested data), copying can be inefficient. Passing a pointer avoids making a full copy:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Birthday</span><span class="hljs-params">(u *User)</span></span> {
    u.Age++
}

u := User{<span class="hljs-string">"Bob"</span>, <span class="hljs-number">29</span>}
Birthday(&amp;u)
fmt.Println(u.Age) <span class="hljs-comment">// 30</span>
</code></pre>
<h3 id="heading-sharing-and-mutating-state">Sharing and Mutating State</h3>
<p>Sometimes you want multiple parts of your program to work with the same object. With values, each assignment makes a copy:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Counter <span class="hljs-keyword">struct</span> {
    value <span class="hljs-keyword">int32</span>
}

c1 := Counter{value: <span class="hljs-number">0</span>} <span class="hljs-comment">// c1 is a Counter</span>
c2 := c1  <span class="hljs-comment">// c2 is a copy - another Counter</span>

c2.value++
fmt.Println(c1.value) <span class="hljs-comment">// 0</span>
fmt.Println(c2.value) <span class="hljs-comment">// 1</span>
</code></pre>
<p>Using pointers ensures both variables refer to the same underlying data:</p>
<pre><code class="lang-go">pc1 := &amp;Counter{value: <span class="hljs-number">0</span>} <span class="hljs-comment">// pc1 is a pointer to a Counter</span>
pc2 := pc1   <span class="hljs-comment">// copy of the pointer - both point to the same Counter</span>

pc2.value++
fmt.Println(pc1.value) <span class="hljs-comment">// 1</span>
fmt.Println(pc2.value) <span class="hljs-comment">// 1</span>
</code></pre>
<p>This diagram illustrates the two memory layouts:</p>
<p><img src="https://i.ibb.co/tPXSr9FN/counter.png" alt="Value vs Pointer" width="600" height="400" loading="lazy"></p>
<p><code>c1</code> and <code>c2</code> are stored separately at <code>0x0202</code> and <code>0x0207</code>, each with its own 4-byte <code>value</code> field. In the second example, <code>pc1</code> and <code>pc2</code> are stored at <code>0x0202</code> and <code>0x020a</code> respectively, and both hold the same address (<code>0x1002</code>) pointing to a single <code>Counter</code> instance in the heap, having its own 4-byte <code>value</code> field.</p>
<h3 id="heading-method-receivers">Method Receivers</h3>
<p>Go methods can have <strong>value receivers</strong> or <strong>pointer receivers</strong>. Pointer receivers are needed when:</p>
<ul>
<li><p>The method should modify the struct</p>
</li>
<li><p>The struct is large and copying would be costly</p>
</li>
<li><p>You want consistency (it's common to make all receivers pointers if some need to be)</p>
</li>
</ul>
<p>We'll cover this in detail in the "Pointer Receivers" section.</p>
<h3 id="heading-interfacing-with-low-level-apis">Interfacing with Low-Level APIs</h3>
<p>Some libraries and system calls require you to pass memory addresses, not copies. Pointers make this possible, while still being type-safe in Go.</p>
<h2 id="heading-common-pitfalls-and-misunderstandings">Common Pitfalls and Misunderstandings</h2>
<h3 id="heading-nil-pointers">Nil Pointers</h3>
<p>If you declare a pointer without initializing it, it will be <code>nil</code>. Dereferencing a nil pointer will immediately cause a runtime panic:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> p *<span class="hljs-keyword">int</span>
fmt.Println(*p) <span class="hljs-comment">// panic: runtime error: invalid memory address or nil pointer dereference</span>
</code></pre>
<p>This is Go's way of telling you that you tried to follow an address that doesn't exist. To safely use pointers, you always need to give them a valid target before dereferencing:</p>
<pre><code class="lang-go">x := <span class="hljs-number">42</span>
p := &amp;x          <span class="hljs-comment">// p points to x</span>
fmt.Println(*p)  <span class="hljs-comment">// 42</span>

q := <span class="hljs-built_in">new</span>(<span class="hljs-keyword">int</span>)    <span class="hljs-comment">// allocates memory for an int, initializes it to 0</span>
fmt.Println(*q)  <span class="hljs-comment">// 0</span>
</code></pre>
<p>Both <code>&amp;</code> and <code>new</code> ensure that the pointer points to valid memory.</p>
<h3 id="heading-reference-types">Reference Types</h3>
<p>In Go, everything is passed by value. But this value depends on the <strong>inner representation</strong> of the type. For example, a slice is stored in memory as:</p>
<pre><code class="lang-go"><span class="hljs-keyword">struct</span> {
    ptr *ElementType <span class="hljs-comment">// pointer to the underlying array</span>
    <span class="hljs-built_in">len</span> <span class="hljs-keyword">int</span>          <span class="hljs-comment">// length of the slice</span>
    <span class="hljs-built_in">cap</span> <span class="hljs-keyword">int</span>          <span class="hljs-comment">// capacity of the slice</span>
}
</code></pre>
<p>When you pass a slice to a function, you are passing a copy of this struct. The <code>ptr</code> field still points to the same underlying array, so changes to the elements of the slice inside the function will affect the original slice.</p>
<p>Because of this behavior, slices are often referred to as <strong>reference types</strong>: you don’t need to use pointers with them to share or mutate data. Other reference types in Go include maps and channels. Strings are also considered reference types, but they are immutable, so you cannot change their contents.</p>
<p>Note that pointers themselves are not reference types: they are simply variables that hold memory addresses.</p>
<p>(To complicate things a bit further, if you pass a slice to a function and then re-slice it or append to it, you are modifying the copy of the slice struct. The original slice outside the function will not see these changes!)</p>
<h2 id="heading-pointer-receivers">Pointer Receivers</h2>
<p>When defining methods on structs in Go, you can choose between value receivers and pointer receivers. Understanding the difference is key to writing correct and efficient Go code.</p>
<h3 id="heading-value-receivers-vs-pointer-receivers">Value Receivers vs Pointer Receivers</h3>
<p><strong>Value receiver</strong>: the method gets a copy of the struct. Any modifications inside the method do not affect the original struct.</p>
<p><strong>Pointer receiver</strong>: the method gets a copy of the pointer, which still points to the original struct. Modifications inside the method affect the original struct.</p>
<p>Example:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Counter <span class="hljs-keyword">struct</span> {
    value <span class="hljs-keyword">int</span>
}
</code></pre>
<p>If you try to increment the counter using a value receiver, it won't work as expected:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(c Counter)</span> <span class="hljs-title">Inc</span><span class="hljs-params">()</span></span> {
    c.value++ <span class="hljs-comment">// INCORRECT: modifies the copy, not the original</span>
}

c := Counter{value: <span class="hljs-number">5</span>}
c.Inc()
fmt.Println(c.value) <span class="hljs-comment">// still 5</span>
</code></pre>
<p>With a pointer receiver, it works correctly:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(c *Counter)</span> <span class="hljs-title">Inc</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// note the shorthand syntax to (*c).value</span>
    c.value++ <span class="hljs-comment">// CORRECT: modifies the original via the pointer</span>
}

c := Counter{value: <span class="hljs-number">5</span>}
c.Inc()
fmt.Println(c.value) <span class="hljs-comment">// now 6</span>
</code></pre>
<p>Even though the method receives a copy of the pointer, both the copy and the original pointer point to the same struct in memory (in the heap), so changes inside the method affect the original.</p>
<h3 id="heading-idiomatic-go">Idiomatic Go</h3>
<ul>
<li><p>Small structs can use value receivers if mutation isn't needed.</p>
</li>
<li><p>Large structs or any struct that must be mutated should use pointer receivers.</p>
</li>
<li><p>If some methods need pointer receivers, it's common to make all methods use pointer receivers for consistency.</p>
</li>
</ul>
<p>Pointer receivers are arguably the most common and practical use of pointers in Go. They allow methods to mutate state safely without unnecessary copies.</p>
<h2 id="heading-exercises-for-the-reader">Exercises for the Reader</h2>
<p>To solidify your understanding of pointers, try the following exercises:</p>
<ol>
<li><p>Write a function that swaps two integers using pointers.</p>
</li>
<li><p>Create a struct representing a <code>Rectangle</code> with width and height. Write methods to calculate the area and to scale the rectangle by a factor, using pointer receivers.</p>
</li>
</ol>
<h2 id="heading-bonus-weak-pointers-go-124">Bonus: Weak Pointers (Go 1.24+)</h2>
<p>Go 1.24 introduced weak references, which let you hold a reference to a value without preventing it from being garbage-collected. This is useful when you want a cache or auxiliary data structure without prolonging the lifetime of the objects.</p>
<h3 id="heading-what-are-weak-pointers">What Are Weak Pointers?</h3>
<p>A weak pointer is a pointer that does not count toward keeping the referenced object alive. If the only references to an object are weak, the garbage collector can still free it.</p>
<p>Weak pointers are provided by the runtime/weak package:</p>
<pre><code class="lang-go"><span class="hljs-keyword">import</span> <span class="hljs-string">"runtime/weak"</span>

<span class="hljs-keyword">type</span> Cache <span class="hljs-keyword">struct</span> {
    data weak.Map[<span class="hljs-keyword">string</span>, *User]
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    c := Cache{data: weak.MakeMap[<span class="hljs-keyword">string</span>, *User]()}
    u := &amp;User{<span class="hljs-string">"Alice"</span>, <span class="hljs-number">30</span>}

    c.data.Set(<span class="hljs-string">"alice"</span>, u) <span class="hljs-comment">// weak reference stored</span>
}

<span class="hljs-comment">// ...later</span>
<span class="hljs-keyword">if</span> user, ok := c.data.Get(<span class="hljs-string">"alice"</span>); ok {
    fmt.Println(user.Name) <span class="hljs-comment">// Alice</span>
} <span class="hljs-keyword">else</span> {
    fmt.Println(<span class="hljs-string">"User has been garbage collected"</span>)
}
</code></pre>
<p>If <code>u</code> is no longer referenced elsewhere, the garbage collector can reclaim it, even though it exists in the <code>weak.Map</code>.</p>
<p>Essentially, a weak pointer can turn into a nil pointer if the object it points to has been garbage collected. You should always check if it's nil before dereferencing it.</p>
<h3 id="heading-when-to-use-weak-pointers">When to Use Weak Pointers</h3>
<ul>
<li><p>Caches: Keep objects around if they're still in use, but don't prevent GC if not.</p>
</li>
<li><p>Avoid memory leaks: Especially in long-running services where temporary objects could otherwise accumulate.</p>
</li>
<li><p>Auxiliary indexing: Like mapping IDs to objects without controlling their lifetimes.</p>
</li>
</ul>
<p>Weak pointers are an advanced feature and should be used judiciously. Most Go programs will never need them, but in certain scenarios, they can be very useful.</p>
<h2 id="heading-summary-amp-best-practices">Summary &amp; Best Practices</h2>
<ul>
<li><p>Pointers store addresses of variables and allow you to share and mutate data efficiently.</p>
</li>
<li><p>Use <code>&amp;</code> to get an address, <code>*</code> to dereference and declare.</p>
</li>
<li><p>Pointer receivers let methods mutate structs without unnecessary copies.</p>
</li>
<li><p>Be cautious with nil pointers to avoid panics.</p>
</li>
<li><p>Reference types (slices, maps, channels) already share underlying data.</p>
</li>
<li><p>Weak pointers (Go 1.24+) provide advanced memory management for caches or auxiliary structures.</p>
</li>
</ul>
<p>While not as powerful as in languages like C/C++, Go pointers are safe and easy to use. Experiment with them in small programs, and you'll quickly see how they can help you write more efficient and easy-to-read Go code.</p>
<h2 id="heading-solutions-to-exercises">Solutions to Exercises</h2>
<ol>
<li>Swapping two integers using pointers:</li>
</ol>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">swap</span><span class="hljs-params">(a, b *<span class="hljs-keyword">int</span>)</span></span> {
    *a, *b = *b, *a
}

x, y := <span class="hljs-number">1</span>, <span class="hljs-number">2</span>
swap(&amp;x, &amp;y)
fmt.Println(x, y) <span class="hljs-comment">// 2 1</span>
</code></pre>
<p>Here, <code>swap</code> takes two pointers to integers and swaps their values by dereferencing them. Without pointers, you would only swap copies of the integers, leaving the originals unchanged.</p>
<ol start="2">
<li>Rectangle struct with methods:</li>
</ol>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Rectangle <span class="hljs-keyword">struct</span> {
    Width, Height <span class="hljs-keyword">float64</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *Rectangle)</span> <span class="hljs-title">Area</span><span class="hljs-params">()</span> <span class="hljs-title">float64</span></span> {
    <span class="hljs-keyword">return</span> r.Width * r.Height
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *Rectangle)</span> <span class="hljs-title">Scale</span><span class="hljs-params">(factor <span class="hljs-keyword">float64</span>)</span></span> {
    r.Width *= factor
    r.Height *= factor
}

rect := Rectangle{Width: <span class="hljs-number">3</span>, Height: <span class="hljs-number">4</span>}
fmt.Println(rect.Area()) <span class="hljs-comment">// 12</span>
rect.Scale(<span class="hljs-number">2</span>)
fmt.Println(rect.Area()) <span class="hljs-comment">// 48</span>
</code></pre>
<p>Because <code>Scale</code> modifies the rectangle, it uses a pointer receiver. The <code>Area</code> method could use either a value or pointer receiver since it doesn't modify the struct, but using a pointer receiver is consistent and avoids copying the struct.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Build a Full Stack Movie Streaming App with Go, React, MongoDB, OpenAI ]]>
                </title>
                <description>
                    <![CDATA[ We’ve just posted a full-stack course on the freeCodeCamp.org YouTube channel that will teach you to build a complete, production-ready movie streaming application named MagicStream, complete with AI-powered movie recommendations. This course is desi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-full-stack-movie-streaming-app-with-go-react-mongodb-openai/</link>
                <guid isPermaLink="false">68dd9632756047816483e15e</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MongoDB ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Wed, 01 Oct 2025 20:59:30 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759352344795/21e9357b-d611-4223-a1f3-704960eae165.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>We’ve just posted a full-stack course on the <a target="_blank" href="http://freeCodeCamp.org">freeCodeCamp.org</a> YouTube channel that will teach you to build a complete, production-ready movie streaming application named MagicStream, complete with AI-powered movie recommendations. This course is designed to give you a deep, practical understanding of a modern, powerful tech stack. Gavin Lon teaches this course.</p>
<p>The foundation of this project is built on high-performance technologies. On the backend, the course uses Go (Golang) paired with the lightning-fast Gin-Gonic framework to create a robust and efficient Web API. This combination is fantastic for handling concurrent requests, making the application quick and reliable. For the frontend, you'll be building a responsive and engaging user interface using React. All your data, from user profiles to movie details, will be stored in MongoDB, offering flexible and scalable data management. This combination of Go, React, and MongoDB provides a well-rounded and impressive skillset for any full-stack developer.</p>
<p>You'll learn to connect your Go backend directly to OpenAI’s models using the power of the LangChainGo library. This connection allows you to build a genuine, intelligent service that can analyze data and provide personalized movie suggestions. Also, you'll implement secure user authentication, including registration, login, and access token validation via middleware. Security is a priority, so the course even covers the best practice of storing access tokens in http-only cookies to prevent Cross-Site Scripting (XSS) attacks.</p>
<p>Once the core features are built and working, you’ll will learn to deploy the applicaiton. You’ll get hands-on experience taking your database live by deploying MongoDB to Atlas, hosting your Go/Gin-Gonic API on Render, and deploying the React client to Vercel.</p>
<p>Watch the full course on <a target="_blank" href="https://youtu.be/jBf7of9JTV8">the freeCodeCamp.org YouTube channel</a> (15-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/jBf7of9JTV8" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Common Slice Mistakes in Go and How to Avoid Them ]]>
                </title>
                <description>
                    <![CDATA[ Slices are one of the most fundamental and powerful data structures in Go. They provide a dynamic array-like interface that's both flexible and efficient. However, they can be very tricky when implementing. And if not implemented correctly, they can ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/common-slice-mistakes-in-go/</link>
                <guid isPermaLink="false">68dc0acc3daa8b6bcf710179</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Temitope Oyedele ]]>
                </dc:creator>
                <pubDate>Tue, 30 Sep 2025 16:52:28 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759251032781/125e9bfc-2e78-4f71-b423-2d045bf82a9f.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Slices are one of the most fundamental and powerful data structures in Go. They provide a dynamic array-like interface that's both flexible and efficient. However, they can be very tricky when implementing. And if not implemented correctly, they can cause subtle bugs that would be very challenging to track down.</p>
<p>You'll likely think it's a problem with your algorithm or logic, spending hours debugging complex workflows. In contrast, the real issue stems from a simple misunderstanding of how slices behave under the hood. The most frustrating part? Your code might work perfectly in development with small datasets, only to fail mysteriously in production with larger data or under concurrent access.</p>
<p>In this article, we'll explore seven common mistakes developers make when working with slices in Go and provide practical solutions to prevent them.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-passing-slices-by-value-and-expecting-structural-changes">Passing Slices by Value and Expecting Structural Changes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-slice-header-sharing-and-unintended-mutations">Slice Header Sharing and Unintended Mutations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-memory-leaks-with-large-slice-references">Memory Leaks with Large Slice References</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-incorrect-loop-variable-usage-with-slices-of-pointers">Incorrect Loop Variable Usage with Slices of Pointers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-modifying-slice-during-range-iteration">Modifying Slice During Range Iteration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-nil-slice-vs-empty-slice-confusion">Nil Slice vs Empty Slice Confusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-slice-bounds-and-panic-errors">Slice Bounds and Panic Errors</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-passing-slices-by-value-and-expecting-structural-changes">Passing Slices by Value and Expecting Structural Changes</h2>
<p>A common misunderstanding is expecting that modifications to a slice's structure (length/capacity changes) in a function will affect the original slice outside the function.</p>
<p>While slice elements can be modified through function parameters (because slices contain a pointer to the underlying data), the slice header itself (containing length and capacity) is passed by value.</p>
<p>Below is an example of this misconception:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">appendToSlice</span><span class="hljs-params">(s []<span class="hljs-keyword">int</span>)</span></span> {
    s = <span class="hljs-built_in">append</span>(s, <span class="hljs-number">4</span>) <span class="hljs-comment">// This creates a new slice header</span>
    fmt.Println(<span class="hljs-string">"Inside function:"</span>, s) <span class="hljs-comment">// [1, 2, 3, 4]</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    slice := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>}
    appendToSlice(slice)
    fmt.Println(<span class="hljs-string">"After function call:"</span>, slice) <span class="hljs-comment">// Still [1, 2, 3]</span>
}
</code></pre>
<p>In this code, the append operation inside the function creates a new slice header, but this change doesn't affect the original slice in the calling function.</p>
<h3 id="heading-how-to-prevent-it">How to Prevent It</h3>
<p>To modify a slice's structure from within a function, either return the modified slice or use a pointer to the slice:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Method 1: Return the modified slice</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">appendToSlice</span><span class="hljs-params">(s []<span class="hljs-keyword">int</span>)</span> []<span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">append</span>(s, <span class="hljs-number">4</span>)
}

<span class="hljs-comment">// Method 2: Use a pointer to the slice</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">appendToSlicePtr</span><span class="hljs-params">(s *[]<span class="hljs-keyword">int</span>)</span></span> {
    *s = <span class="hljs-built_in">append</span>(*s, <span class="hljs-number">4</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Using method 1</span>
    slice1 := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>}
    slice1 = appendToSlice(slice1)
    fmt.Println(<span class="hljs-string">"Method 1:"</span>, slice1) <span class="hljs-comment">// [1, 2, 3, 4]</span>

    <span class="hljs-comment">// Using method 2</span>
    slice2 := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>}
    appendToSlicePtr(&amp;slice2)
    fmt.Println(<span class="hljs-string">"Method 2:"</span>, slice2) <span class="hljs-comment">// [1, 2, 3, 4]</span>
}
</code></pre>
<p>Both approaches ensure that changes to the slice structure are visible to the caller.</p>
<h2 id="heading-slice-header-sharing-and-unintended-mutations">Slice Header Sharing and Unintended Mutations</h2>
<p>Another common mistake is not realizing that slices created from the same underlying array share data. And not knowing this can cause unexpected mutations when you modify one slice.</p>
<p>Slices in Go are reference types that contain a pointer to the underlying array, along with length and capacity information. When you create a slice from another slice, they both point to the same underlying data.</p>
<p>Below is an example of how this can lead to surprising behavior:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    original := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>}
    subset := original[<span class="hljs-number">1</span>:<span class="hljs-number">4</span>] <span class="hljs-comment">// Creates [2, 3, 4]</span>

    fmt.Println(<span class="hljs-string">"Original:"</span>, original) <span class="hljs-comment">// [1, 2, 3, 4, 5]</span>
    fmt.Println(<span class="hljs-string">"Subset:"</span>, subset)     <span class="hljs-comment">// [2, 3, 4]</span>

    subset[<span class="hljs-number">0</span>] = <span class="hljs-number">99</span> <span class="hljs-comment">// Modify the first element of subset</span>

    fmt.Println(<span class="hljs-string">"Original after modification:"</span>, original) <span class="hljs-comment">// [1, 99, 3, 4, 5]</span>
    fmt.Println(<span class="hljs-string">"Subset after modification:"</span>, subset)     <span class="hljs-comment">// [99, 3, 4]</span>
}
</code></pre>
<p>In this code, modifying the <code>subset</code> slice also changes the <code>original</code> slice because they share the same underlying array.</p>
<h3 id="heading-how-to-prevent-it-1"><strong>How to Prevent It</strong></h3>
<p>To prevent unintended mutations, use the <code>copy()</code> function to create independent slices:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    original := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>}

    <span class="hljs-comment">// Create an independent copy</span>
    subset := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">int</span>, <span class="hljs-number">3</span>)
    <span class="hljs-built_in">copy</span>(subset, original[<span class="hljs-number">1</span>:<span class="hljs-number">4</span>])

    fmt.Println(<span class="hljs-string">"Original:"</span>, original) <span class="hljs-comment">// [1, 2, 3, 4, 5]</span>
    fmt.Println(<span class="hljs-string">"Subset:"</span>, subset)     <span class="hljs-comment">// [2, 3, 4]</span>

    subset[<span class="hljs-number">0</span>] = <span class="hljs-number">99</span>

    fmt.Println(<span class="hljs-string">"Original after modification:"</span>, original) <span class="hljs-comment">// [1, 2, 3, 4, 5] - unchanged</span>
    fmt.Println(<span class="hljs-string">"Subset after modification:"</span>, subset)     <span class="hljs-comment">// [99, 3, 4]</span>
}
</code></pre>
<p>The <code>copy()</code> function ensures that the data is duplicated rather than shared, preventing unintended side effects.</p>
<h2 id="heading-memory-leaks-with-large-slice-references"><strong>Memory Leaks with Large Slice References</strong></h2>
<p>Keeping references to small slices that are derived from large slices is considered a minor yet serious mistake. This is because it prevents the garbage collector from freeing the large underlying array, leading to memory leaks.</p>
<p>When you create a slice from a larger slice, the new slice still references the entire original array, even if it only uses a small portion of it.</p>
<p>Below is an example of how this memory leak can occur:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">processLargeData</span><span class="hljs-params">()</span> []<span class="hljs-title">byte</span></span> {
    largeData := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">byte</span>, <span class="hljs-number">1</span>&lt;&lt;<span class="hljs-number">30</span>) <span class="hljs-comment">// Allocate 1GB</span>
    <span class="hljs-comment">// ... fill largeData with important information ...</span>

    <span class="hljs-comment">// Extract just the first 100 bytes</span>
    <span class="hljs-keyword">return</span> largeData[:<span class="hljs-number">100</span>] <span class="hljs-comment">// Memory leak: entire 1GB stays in memory</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    result := processLargeData()
    <span class="hljs-comment">// Even though result is only 100 bytes, 1GB remains allocated</span>
    fmt.Printf(<span class="hljs-string">"Result length: %d\n"</span>, <span class="hljs-built_in">len</span>(result))
}
</code></pre>
<p>In this code, even though we only need the first 100 bytes, the entire 1GB array remains in memory because our returned slice still references it.</p>
<h3 id="heading-how-to-prevent-it-2"><strong>How to Prevent It</strong></h3>
<p>To prevent memory leaks, copy the needed data to a new slice when working with large datasets:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">processLargeData</span><span class="hljs-params">()</span> []<span class="hljs-title">byte</span></span> {
    largeData := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">byte</span>, <span class="hljs-number">1</span>&lt;&lt;<span class="hljs-number">30</span>) <span class="hljs-comment">// Allocate 1GB</span>
    <span class="hljs-comment">// ... fill largeData with important information ...</span>

    <span class="hljs-comment">// Create independent copy of needed data</span>
    result := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">byte</span>, <span class="hljs-number">100</span>)
    <span class="hljs-built_in">copy</span>(result, largeData[:<span class="hljs-number">100</span>])

    <span class="hljs-keyword">return</span> result <span class="hljs-comment">// largeData can now be garbage collected</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    result := processLargeData()
    <span class="hljs-comment">// Only 100 bytes remain in memory</span>
    fmt.Printf(<span class="hljs-string">"Result length: %d\n"</span>, <span class="hljs-built_in">len</span>(result))
}
</code></pre>
<p>By copying the data to a new slice, you allow the garbage collector to free the large array when it's no longer needed.</p>
<h2 id="heading-incorrect-loop-variable-usage-with-slices-of-pointers"><strong>Incorrect Loop Variable Usage with Slices of Pointers</strong></h2>
<p>There are scenarios or instances where you create what looks like a perfectly reasonable loop to collect pointers, but somehow all your pointers end up pointing to the same value. This is because Go reuses the same loop variable throughout all iterations, so taking its address always results in the same memory location.</p>
<p>Below is an example of how this mistake manifests:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> ptrs []*<span class="hljs-keyword">int</span>

    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
        ptrs = <span class="hljs-built_in">append</span>(ptrs, &amp;i) <span class="hljs-comment">// Wrong: all pointers reference the same variable</span>
    }

    <span class="hljs-comment">// Print the values</span>
    <span class="hljs-keyword">for</span> j, ptr := <span class="hljs-keyword">range</span> ptrs {
        fmt.Printf(<span class="hljs-string">"ptrs[%d] = %d\n"</span>, j, *ptr)
    }
    <span class="hljs-comment">// Output: All pointers show the same value (3)</span>
}
</code></pre>
<p>In this code, all pointers in the slice point to the same loop variable <code>i</code>, which has the final value of <code>3</code> after the loop completes.</p>
<h3 id="heading-how-to-prevent-it-3"><strong>How to Prevent It</strong></h3>
<p>To fix this issue, create a new variable in each iteration or use slice indexing:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Method 1: Create a new variable in each iteration</span>
    <span class="hljs-keyword">var</span> ptrs1 []*<span class="hljs-keyword">int</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
        j := i <span class="hljs-comment">// Create new variable</span>
        ptrs1 = <span class="hljs-built_in">append</span>(ptrs1, &amp;j)
    }

    <span class="hljs-comment">// Method 2: Use a slice and index into it</span>
    values := []<span class="hljs-keyword">int</span>{<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>}
    <span class="hljs-keyword">var</span> ptrs2 []*<span class="hljs-keyword">int</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-keyword">range</span> values {
        ptrs2 = <span class="hljs-built_in">append</span>(ptrs2, &amp;values[i])
    }

    <span class="hljs-comment">// Method 3: Using make and direct assignment</span>
    values2 := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">int</span>, <span class="hljs-number">3</span>)
    <span class="hljs-keyword">var</span> ptrs3 []*<span class="hljs-keyword">int</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++ {
        values2[i] = i
        ptrs3 = <span class="hljs-built_in">append</span>(ptrs3, &amp;values2[i])
    }

    <span class="hljs-comment">// All methods now work correctly</span>
    fmt.Println(<span class="hljs-string">"Method 1:"</span>, *ptrs1[<span class="hljs-number">0</span>], *ptrs1[<span class="hljs-number">1</span>], *ptrs1[<span class="hljs-number">2</span>]) <span class="hljs-comment">// 0 1 2</span>
    fmt.Println(<span class="hljs-string">"Method 2:"</span>, *ptrs2[<span class="hljs-number">0</span>], *ptrs2[<span class="hljs-number">1</span>], *ptrs2[<span class="hljs-number">2</span>]) <span class="hljs-comment">// 0 1 2</span>
    fmt.Println(<span class="hljs-string">"Method 3:"</span>, *ptrs3[<span class="hljs-number">0</span>], *ptrs3[<span class="hljs-number">1</span>], *ptrs3[<span class="hljs-number">2</span>]) <span class="hljs-comment">// 0 1 2</span>
}
</code></pre>
<p>These approaches ensure that each pointer references a unique memory location with the correct value.</p>
<h2 id="heading-modifying-slice-during-range-iteration"><strong>Modifying Slice During Range Iteration</strong></h2>
<p>Modifying a slice while iterating over it with a <code>range</code> loop, can lead to issues like skipped elements, infinite loops, or processing the wrong data, depending on the type of modification.</p>
<p>When you use <code>range</code> on a slice, Go evaluates the slice's length at the beginning of the loop. However, if you modify the slice during iteration, the actual slice length may change while the loop continues based on the original length.</p>
<p>Below is an example of how this can cause problems:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">removeEvenNumbers</span><span class="hljs-params">()</span></span> {
    numbers := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>}
    fmt.Println(<span class="hljs-string">"Original:"</span>, numbers)

    <span class="hljs-comment">// Dangerous: modifying slice during range iteration</span>
    <span class="hljs-keyword">for</span> i, num := <span class="hljs-keyword">range</span> numbers {
        <span class="hljs-keyword">if</span> num%<span class="hljs-number">2</span> == <span class="hljs-number">0</span> {
            <span class="hljs-comment">// Remove even number by slicing</span>
            numbers = <span class="hljs-built_in">append</span>(numbers[:i], numbers[i+<span class="hljs-number">1</span>:]...)
        }
    }

    fmt.Println(<span class="hljs-string">"After removal:"</span>, numbers) <span class="hljs-comment">// Unexpected result!</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    removeEvenNumbers()
    <span class="hljs-comment">// Output might be: [1 3 5 7 8] - notice 8 wasn't removed!</span>
}
</code></pre>
<p>In this code, removing elements during iteration causes the indices to shift, leading to some elements being skipped. The number <code>8</code> remains because when <code>6</code> is removed, <code>8</code> shifts to a position that has already been processed by the loop.</p>
<h3 id="heading-how-to-prevent-it-4"><strong>How to Prevent It</strong></h3>
<p>To safely modify slices during iteration, iterate in reverse order, use a separate result slice, or collect indices first:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Method 1: Iterate in reverse to avoid index shifting issues</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">removeEvenNumbersReverse</span><span class="hljs-params">()</span></span> {
    numbers := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>}
    fmt.Println(<span class="hljs-string">"Original:"</span>, numbers)

    <span class="hljs-keyword">for</span> i := <span class="hljs-built_in">len</span>(numbers) - <span class="hljs-number">1</span>; i &gt;= <span class="hljs-number">0</span>; i-- {
        <span class="hljs-keyword">if</span> numbers[i]%<span class="hljs-number">2</span> == <span class="hljs-number">0</span> {
            numbers = <span class="hljs-built_in">append</span>(numbers[:i], numbers[i+<span class="hljs-number">1</span>:]...)
        }
    }

    fmt.Println(<span class="hljs-string">"After removal:"</span>, numbers) <span class="hljs-comment">// [1, 3, 5, 7]</span>
}

<span class="hljs-comment">// Method 2: Build a new slice with desired elements</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">filterOddNumbers</span><span class="hljs-params">()</span> []<span class="hljs-title">int</span></span> {
    numbers := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>}
    <span class="hljs-keyword">var</span> result []<span class="hljs-keyword">int</span>

    <span class="hljs-keyword">for</span> _, num := <span class="hljs-keyword">range</span> numbers {
        <span class="hljs-keyword">if</span> num%<span class="hljs-number">2</span> != <span class="hljs-number">0</span> { <span class="hljs-comment">// Keep odd numbers</span>
            result = <span class="hljs-built_in">append</span>(result, num)
        }
    }

    <span class="hljs-keyword">return</span> result <span class="hljs-comment">// [1, 3, 5, 7]</span>
}

<span class="hljs-comment">// Method 3: Collect indices first, then modify</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">removeEvenNumbersByIndex</span><span class="hljs-params">()</span></span> {
    numbers := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>}
    <span class="hljs-keyword">var</span> toRemove []<span class="hljs-keyword">int</span>

    <span class="hljs-comment">// First pass: collect indices of even numbers</span>
    <span class="hljs-keyword">for</span> i, num := <span class="hljs-keyword">range</span> numbers {
        <span class="hljs-keyword">if</span> num%<span class="hljs-number">2</span> == <span class="hljs-number">0</span> {
            toRemove = <span class="hljs-built_in">append</span>(toRemove, i)
        }
    }

    <span class="hljs-comment">// Second pass: remove in reverse order</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-built_in">len</span>(toRemove) - <span class="hljs-number">1</span>; i &gt;= <span class="hljs-number">0</span>; i-- {
        idx := toRemove[i]
        numbers = <span class="hljs-built_in">append</span>(numbers[:idx], numbers[idx+<span class="hljs-number">1</span>:]...)
    }

    fmt.Println(<span class="hljs-string">"Result:"</span>, numbers) <span class="hljs-comment">// [1, 3, 5, 7]</span>
}
</code></pre>
<p>These approaches ensure that your modifications don't interfere with the iteration process, giving you predictable and correct results.</p>
<h2 id="heading-nil-slice-vs-empty-slice-confusion"><strong>Nil Slice vs Empty Slice Confusion</strong></h2>
<p>Another source of confusion is not understanding the difference between <code>nil</code> slices and empty slices, which can lead to inconsistent behavior in your applications.</p>
<p>A <code>nil</code> slice has no underlying array, while an empty slice has an underlying array but contains no elements.</p>
<p>Below is an example that demonstrates the differences:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> nilSlice []<span class="hljs-keyword">int</span>
    emptySlice := []<span class="hljs-keyword">int</span>{}
    emptySlice2 := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">int</span>, <span class="hljs-number">0</span>)

    fmt.Printf(<span class="hljs-string">"nilSlice == nil: %t\n"</span>, nilSlice == <span class="hljs-literal">nil</span>)       <span class="hljs-comment">// true</span>
    fmt.Printf(<span class="hljs-string">"emptySlice == nil: %t\n"</span>, emptySlice == <span class="hljs-literal">nil</span>)   <span class="hljs-comment">// false</span>
    fmt.Printf(<span class="hljs-string">"emptySlice2 == nil: %t\n"</span>, emptySlice2 == <span class="hljs-literal">nil</span>) <span class="hljs-comment">// false</span>

    <span class="hljs-comment">// JSON marshaling behaves differently</span>
    nilJSON, _ := json.Marshal(nilSlice)
    emptyJSON, _ := json.Marshal(emptySlice)

    fmt.Printf(<span class="hljs-string">"Nil slice JSON: %s\n"</span>, nilJSON)   <span class="hljs-comment">// null</span>
    fmt.Printf(<span class="hljs-string">"Empty slice JSON: %s\n"</span>, emptyJSON) <span class="hljs-comment">// []</span>
}
</code></pre>
<p>This difference can cause issues when working with JSON APIs or when functions expect specific slice states.</p>
<h3 id="heading-how-to-prevent-it-5"><strong>How to Prevent It</strong></h3>
<p>Be explicit about your intentions and handle both cases consistently. A good practice would be to check length instead of <code>nil</code> when it matters:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">processSlice</span><span class="hljs-params">(s []<span class="hljs-keyword">int</span>)</span></span> {
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(s) == <span class="hljs-number">0</span> { <span class="hljs-comment">// Works for both nil and empty slices</span>
        fmt.Println(<span class="hljs-string">"Slice is empty"</span>)
        <span class="hljs-keyword">return</span>
    }
    fmt.Printf(<span class="hljs-string">"Processing %d elements\n"</span>, <span class="hljs-built_in">len</span>(s))
}

<span class="hljs-comment">// Initialize nil slices when needed</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ensureSliceInitialized</span><span class="hljs-params">(s []<span class="hljs-keyword">int</span>)</span> []<span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">if</span> s == <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">int</span>, <span class="hljs-number">0</span>) <span class="hljs-comment">// or []int{} if you prefer non-nil empty slice</span>
    }
    <span class="hljs-keyword">return</span> s
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> nilSlice []<span class="hljs-keyword">int</span>
    emptySlice := []<span class="hljs-keyword">int</span>{}

    processSlice(nilSlice)  <span class="hljs-comment">// Works consistently</span>
    processSlice(emptySlice) <span class="hljs-comment">// Works consistently</span>

    nilSlice = ensureSliceInitialized(nilSlice)
    fmt.Printf(<span class="hljs-string">"After initialization: %t\n"</span>, nilSlice == <span class="hljs-literal">nil</span>) <span class="hljs-comment">// false</span>
}
</code></pre>
<p>This approach ensures consistent behavior regardless of whether you're working with <code>nil</code> or empty slices.</p>
<h2 id="heading-slice-bounds-and-panic-errors"><strong>Slice Bounds and Panic Errors</strong></h2>
<p>The final common mistake is not validating slice bounds before accessing elements. When you fail to validate slice bound, you’re making it prone to runtime panics that can crash your application.</p>
<p>Go doesn't provide automatic bounds checking for slice operations, so it's your responsibility to ensure that indices are within valid ranges.</p>
<p>Below is an example of unsafe slice operations:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">dangerousSliceOperations</span><span class="hljs-params">(s []<span class="hljs-keyword">int</span>, index <span class="hljs-keyword">int</span>, start <span class="hljs-keyword">int</span>, end <span class="hljs-keyword">int</span>)</span></span> {
    <span class="hljs-comment">// Dangerous: can panic if index is out of bounds</span>
    value := s[index]
    fmt.Printf(<span class="hljs-string">"Value at index %d: %d\n"</span>, index, value)

    <span class="hljs-comment">// Also dangerous: can panic if bounds are invalid</span>
    subset := s[start:end]
    fmt.Printf(<span class="hljs-string">"Subset [%d:%d]: %v\n"</span>, start, end, subset)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    slice := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>}

    <span class="hljs-comment">// These will cause panics</span>
    <span class="hljs-comment">// dangerousSliceOperations(slice, 10, 2, 8) // index out of bounds</span>
    <span class="hljs-comment">// dangerousSliceOperations(slice, 0, -1, 3) // negative index</span>
}
</code></pre>
<p>These operations will cause runtime panics when the bounds are invalid, potentially crashing your application.</p>
<h3 id="heading-how-to-prevent-it-6"><strong>How to Prevent It</strong></h3>
<p>To prevent this, you need to always validate bounds before accessing slice elements:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Safe element access with error handling</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">safeGetElement</span><span class="hljs-params">(s []<span class="hljs-keyword">int</span>, index <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
    <span class="hljs-keyword">if</span> index &lt; <span class="hljs-number">0</span> || index &gt;= <span class="hljs-built_in">len</span>(s) {
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, fmt.Errorf(<span class="hljs-string">"index %d out of bounds for slice of length %d"</span>, index, <span class="hljs-built_in">len</span>(s))
    }
    <span class="hljs-keyword">return</span> s[index], <span class="hljs-literal">nil</span>
}

<span class="hljs-comment">// Safe slice operations with error handling</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">safeGetSubslice</span><span class="hljs-params">(s []<span class="hljs-keyword">int</span>, start, end <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">([]<span class="hljs-keyword">int</span>, error)</span></span> {
    <span class="hljs-keyword">if</span> start &lt; <span class="hljs-number">0</span> || end &gt; <span class="hljs-built_in">len</span>(s) || start &gt; end {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, fmt.Errorf(<span class="hljs-string">"invalid slice bounds [%d:%d] for slice of length %d"</span>, start, end, <span class="hljs-built_in">len</span>(s))
    }
    <span class="hljs-keyword">return</span> s[start:end], <span class="hljs-literal">nil</span>
}

<span class="hljs-comment">// Bounds-checking helper that clamps values</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">clampedSlice</span><span class="hljs-params">(s []<span class="hljs-keyword">int</span>, start, end <span class="hljs-keyword">int</span>)</span> []<span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">if</span> start &lt; <span class="hljs-number">0</span> {
        start = <span class="hljs-number">0</span>
    }
    <span class="hljs-keyword">if</span> end &gt; <span class="hljs-built_in">len</span>(s) {
        end = <span class="hljs-built_in">len</span>(s)
    }
    <span class="hljs-keyword">if</span> start &gt; end {
        start = end
    }
    <span class="hljs-keyword">return</span> s[start:end]
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    slice := []<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>}

    <span class="hljs-comment">// Safe access with error handling</span>
    <span class="hljs-keyword">if</span> value, err := safeGetElement(slice, <span class="hljs-number">2</span>); err == <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"Element at index 2: %d\n"</span>, value)
    }

    <span class="hljs-keyword">if</span> subset, err := safeGetSubslice(slice, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>); err == <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"Subset [1:4]: %v\n"</span>, subset)
    }

    <span class="hljs-comment">// Bounds-clamped access (never panics)</span>
    clamped := clampedSlice(slice, <span class="hljs-number">-1</span>, <span class="hljs-number">10</span>)
    fmt.Printf(<span class="hljs-string">"Clamped slice: %v\n"</span>, clamped) <span class="hljs-comment">// [1, 2, 3, 4, 5]</span>
}
</code></pre>
<p>These approaches provide safe alternatives that either handle errors gracefully or ensure that operations never exceed valid bounds.</p>
<h2 id="heading-wrapping-up"><strong>Wrapping Up</strong></h2>
<p>In this article, we looked at seven frequent problems that might happen when working with slices in Go. These issues often stem from the subtle behavior of Go's slice implementation, particularly around memory sharing, the distinction between slice headers and underlying arrays, and the reference semantics of slices.</p>
<p>By understanding these pitfalls and implementing the prevention strategies we've discussed, you can write more robust and efficient Go applications. Remember to always consider slice capacity vs length, be mindful of shared underlying data, validate bounds before accessing elements, and understand the implications of passing slices to functions.</p>
<p>Mastering these concepts will help you harness the full power of Go's slices while avoiding the common traps that can lead to bugs and performance issues in your applications.</p>
<p>Don't forget to share.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
