<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Aran Wilkinson</title>
      <link>https://aran.dev</link>
      <description>Software engineer — Go, distributed systems, Google Cloud. Based in Huddersfield, UK.</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://aran.dev/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Thu, 11 Jun 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Resume</title>
          <pubDate>Thu, 11 Jun 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/resume/</link>
          <guid>https://aran.dev/resume/</guid>
          <description xml:base="https://aran.dev/resume/"></description>
      </item>
      <item>
          <title>Introducing Headcode: A Unified API for UK Rail Data</title>
          <pubDate>Wed, 27 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/introducing-headcode/</link>
          <guid>https://aran.dev/posts/introducing-headcode/</guid>
          <description xml:base="https://aran.dev/posts/introducing-headcode/">&lt;p&gt;UK rail data is brilliantly open and freely available. It is also completely fragmented across dozens of discrete, historically siloed sources. If you want to build an application that tracks trains, you have to piece together obscure feeds from the Rail Data Marketplace, Network Rail Open Data, NaPTAN, and the Office of Rail and Road.&lt;&#x2F;p&gt;
&lt;p&gt;Much of this fragmentation goes back to the legacy of British Rail and the complexities of privatisation. You will encounter Darwin, the real-time passenger information engine operated by the Rail Delivery Group, which feeds the physical departure screens on the platforms. You will stumble into TRUST and TOPS, the legacy Network Rail systems that monitor real-time train progression against schedules and track individual pieces of rolling stock. You will see raw signals from Train Describer (TD) berths tracking train movements piece-by-piece via trackside axle counters.&lt;&#x2F;p&gt;
&lt;p&gt;Integrating directly with official rail data means wrangling huge CSV datasets, legacy XML endpoints, and complex Kafka streams while constantly translating complex data structures. There are open-source proxies like Huxley that wrap Darwin, and excellent mature tools built around this data like Realtime Trains for power users or OpenTrainTimes for live signalling maps. But if you are a typical developer who just wants a clean JSON payload of what trains are leaving a station right now, the barrier to entry is absurdly high.&lt;&#x2F;p&gt;
&lt;p&gt;I am building Headcode to solve this. The premise is straightforward: one integration instead of twelve, with intelligence baked in. Headcode consumes these scattered raw feeds, performs the complex reconciliation, normalisation, and enrichment, and exposes the result through clean REST endpoints. Downstream developers get the data they need without maintaining stream ingestion pipelines.&lt;&#x2F;p&gt;
&lt;p&gt;The name comes from the terminology used by the UK rail industry itself. A &quot;headcode&quot; is the official Train Reporting Number assigned to every single service to uniquely identify it to signallers and routing systems. For example, &lt;code&gt;1A65&lt;&#x2F;code&gt;. The &lt;code&gt;1&lt;&#x2F;code&gt; indicates the class of train, such as an express passenger service. The &lt;code&gt;A&lt;&#x2F;code&gt; specifies the destination area. The &lt;code&gt;65&lt;&#x2F;code&gt; is the individual identifying number for that specific service. It is a precise identifier, which is exactly what a good API should provide.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-api-experience&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-api-experience&quot; aria-label=&quot;Anchor link for: the-api-experience&quot;&gt;The API experience&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I wanted the developer experience to feel like a modern platform, not a raw scrape of a government database. Every API request should return insights or connected data that you physically cannot get from a single raw feed alone. The service needs to add value in the middle layer.&lt;&#x2F;p&gt;
&lt;p&gt;One of the biggest points of friction in UK rail data is the identifier systems. The industry uses several overlapping codes for the exact same physical location:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CRS codes&lt;&#x2F;strong&gt;: the three-letter customer-facing codes you see on passenger tickets, like &lt;code&gt;PAD&lt;&#x2F;code&gt; for London Paddington.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;TIPLOCs&lt;&#x2F;strong&gt;: seven-character codes used for routing and scheduling, like &lt;code&gt;PADTON&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;STANOX codes&lt;&#x2F;strong&gt;: a five-digit internal numbering system used by legacy signalling systems.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you query a raw Darwin live feed, it will identify a station using a TIPLOC. If you query a retail ticketing feed, you will get a CRS. Headcode treats these identifiers natively and forgivingly. You can query the API with any of these codes. The service understands the cross-references and translates them on the fly, returning all known identifiers in the response payload. You do not have to maintain your own mapping tables synced with CORPUS or constantly query reference databases to translate what the API gives you. You ask for Paddington using &lt;code&gt;PAD&lt;&#x2F;code&gt;, and the API knows exactly what you mean.&lt;&#x2F;p&gt;
&lt;p&gt;The data returned is strictly unified. Headcode seamlessly blends real-time live data with static and historic datasets. When you pull a list of delays or the train order for a specific platform, the response is enriched with static accessibility information, station operator details, and physical coordinates sourced from NaPTAN. You get the whole picture in a single structured response, rather than having to stitch together four separate HTTP calls.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-you-can-build-with-it&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-you-can-build-with-it&quot; aria-label=&quot;Anchor link for: what-you-can-build-with-it&quot;&gt;What you can build with it&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Right now, Headcode handles the heavy lifting of continuous ingestion. It takes in massive static reference dumps alongside live Darwin Kafka streams, translating thousands of real-time operational messages a second into structured relational data.&lt;&#x2F;p&gt;
&lt;p&gt;You can hit the API for live station departure and arrival boards directly derived from Darwin. You can pull comprehensive station metadata with fuzzy searching. If a user types &quot;Paddingon&quot; instead of &quot;Paddington&quot;, the API will still resolve the correct station and return the full metadata tree without throwing a 404.&lt;&#x2F;p&gt;
&lt;p&gt;There are specific endpoints for detailed service descriptions and network disruptions sourced from operational train alerts or station messages. You can query platform train-order endpoints to see exactly which trains are queued to arrive at a specific platform and in what sequence. Calculating platform order is surprisingly hard when you are looking at baseline timetables because trains run out of order all the time. Headcode does the active calculation based on real-time track progression and provides the corrected sequence.&lt;&#x2F;p&gt;
&lt;p&gt;The entire service is secured via API-key Bearer authentication sitting on a custom auth layer.&lt;&#x2F;p&gt;
&lt;p&gt;To provide a concrete example of what you can build on top of this platform, I am currently developing a Bluesky bot. It will act as an active webhook consumer of the Headcode API (webhooks are a planned feature coming soon to the platform). Instead of aggressively polling endpoints to check if a train is late, it will listen for events and automatically post live train information, severe network disruptions, and active delays for specific high-traffic routes. The bot serves as a demo of the platform in the wild, proving that you can build reactive social tools without needing to understand Kafka messaging protocols or trackside axle counter logic.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;under-the-hood&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#under-the-hood&quot; aria-label=&quot;Anchor link for: under-the-hood&quot;&gt;Under the hood&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The backend is written entirely in Go. The service is deployed as a monolith. I run multiple instances of the service, each responsible for a subset of the system. The system is dealing with constant firehoses of Kafka messages and concurrent HTTP requests. Go&#x27;s native concurrency model handles stream ingestion effortlessly without complex worker pool orchestration. The compiled binary gives me predictable memory usage and low-latency API responses.&lt;&#x2F;p&gt;
&lt;p&gt;The core product is served as REST JSON. I am using OpenAPI code generation to maintain strict contracts between the server responses and the generated documentation. I use ConnectRPC over HTTP&#x2F;2 specifically for health checks and internal service state. ConnectRPC provides a lightweight, strictly typed protocol that is perfect for internal monitoring without pulling in the bloat of standard gRPC dependencies.&lt;&#x2F;p&gt;
&lt;p&gt;PostgreSQL serves as the backbone of the entire system, handling millions of messages per day. I use &lt;code&gt;pgx&lt;&#x2F;code&gt; for the driver and &lt;code&gt;sqlc&lt;&#x2F;code&gt; for database interaction. What makes &lt;code&gt;sqlc&lt;&#x2F;code&gt; brilliant is that it compiles plain SQL queries directly into type-safe Go code. It removes the need for unpredictable ORMs and keeps exact database access patterns explicit during code reviews.&lt;&#x2F;p&gt;
&lt;p&gt;I rely heavily on Postgres extensions to push the heavy lifting down to the database layer. PostGIS powers all the spatial queries. When an endpoint needs to find all stations within a specific physical radius of a GPS coordinate, PostGIS handles the geospatial maths natively and incredibly fast. For text searching, &lt;code&gt;pg_trgm&lt;&#x2F;code&gt; provides trigram matching. This is what enables the fuzzy searching on station names. Standing up and managing a separate Elasticsearch cluster just to handle user typos is unnecessary architectural overhead when Postgres handles it directly and efficiently.&lt;&#x2F;p&gt;
&lt;p&gt;The infrastructure is intentionally simple and vertically scaled. The stack runs on a Rocky Linux VM hosted on Proxmox. I chose Rocky Linux for its strict bug-for-bug compatibility with RHEL and sheer stability as a hypervisor guest. Everything is explicitly provisioned with Terraform. Caddy acts as the web server, and traffic is routed securely back to the internet via Cloudflare Tunnels.&lt;&#x2F;p&gt;
&lt;p&gt;It is a robust, low-maintenance setup ideal for the current scale of Headcode. I will likely migrate the workloads to something else to scale out the system in the future. But spinning up a monolithic VM gets the product built, shipped, and tested with real user traffic without fighting abstraction layers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;taking-it-for-a-spin&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#taking-it-for-a-spin&quot; aria-label=&quot;Anchor link for: taking-it-for-a-spin&quot;&gt;Taking it for a spin&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Because Headcode abstracts away the domain complexities of the UK rail network, developers can spend their time actually building transit applications instead of debugging XML parsers and cross-referencing obscure alphanumeric identifiers.&lt;&#x2F;p&gt;
&lt;p&gt;While Headcode is currently in closed beta, the documentation is completely open. You can read the comprehensive API documentation, explore the response schemas, and see how the endpoints are structured right now at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.headcode.dev&quot;&gt;docs.headcode.dev&lt;&#x2F;a&gt;. I will be posting further updates on the Bluesky bot, API access availability, and new Headcode features over the coming weeks as the system expands.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Introducing jjw: a workspace manager for jj</title>
          <pubDate>Sat, 16 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/introducing-jjw-jj-workspace-manager/</link>
          <guid>https://aran.dev/posts/introducing-jjw-jj-workspace-manager/</guid>
          <description xml:base="https://aran.dev/posts/introducing-jjw-jj-workspace-manager/">&lt;p&gt;I&#x27;ve just released &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;jjw&quot;&gt;jjw&lt;&#x2F;a&gt;, a small command line tool for managing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jj-vcs&#x2F;jj&quot;&gt;jj&lt;&#x2F;a&gt; workspaces.&lt;&#x2F;p&gt;
&lt;p&gt;The short version is that &lt;code&gt;jjw&lt;&#x2F;code&gt; gives me a nicer workflow for creating, switching between, and deleting isolated jj workspaces. The slightly longer version is that I wanted a reliable way to run multiple coding agents in parallel without them trampling over each other&#x27;s working copies, local state, or setup.&lt;&#x2F;p&gt;
&lt;p&gt;It is inspired by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;agarcher&#x2F;wt&quot;&gt;wt&lt;&#x2F;a&gt;, a Git worktree manager, but I decided to adapt it for working with jj and jj&#x27;s workspace and bookmark model.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-i-built-it&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-i-built-it&quot; aria-label=&quot;Anchor link for: why-i-built-it&quot;&gt;Why I built it&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve been using &lt;code&gt;jj&lt;&#x2F;code&gt; more recently and really like the model. It makes a lot of day-to-day version control operations feel calmer, especially once you get used to the working copy being a commit and the operation log being there as a safety net.&lt;&#x2F;p&gt;
&lt;p&gt;At the same time, my development workflow has changed. I often want to try a few ideas at once, or hand off different tasks to different LLM coding agents. That works best when each task has its own clean, isolated environment.&lt;&#x2F;p&gt;
&lt;p&gt;You can already do this with &lt;code&gt;jj workspace add&lt;&#x2F;code&gt;, but I found myself wanting a bit more structure around it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;consistent workspace names and locations&lt;&#x2F;li&gt;
&lt;li&gt;matching bookmarks for pull requests&lt;&#x2F;li&gt;
&lt;li&gt;quick shell integration so creating or switching workspaces actually moves my shell there&lt;&#x2F;li&gt;
&lt;li&gt;lifecycle hooks for setting up or tearing down per-workspace state&lt;&#x2F;li&gt;
&lt;li&gt;safe deletion and cleanup once work has been merged&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;That became &lt;code&gt;jjw&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-jjw-does&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-jjw-does&quot; aria-label=&quot;Anchor link for: what-jjw-does&quot;&gt;What jjw does&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;jjw&lt;&#x2F;code&gt; manages workspaces inside a configured directory in your repository. A basic setup looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;orkspace_dir&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; w&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;orkspaces&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;efault_branch&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; m&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ain&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once configured, creating a workspace is as simple as:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; create&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; feature-x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Behind the scenes, &lt;code&gt;jjw&lt;&#x2F;code&gt; will:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;create a jj workspace at &lt;code&gt;workspaces&#x2F;feature-x&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;create a bookmark for the workspace&lt;&#x2F;li&gt;
&lt;li&gt;allocate a workspace index, if configured&lt;&#x2F;li&gt;
&lt;li&gt;run any configured &lt;code&gt;post_create&lt;&#x2F;code&gt; hooks&lt;&#x2F;li&gt;
&lt;li&gt;ask the shell integration to move you into the new workspace&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;From there you can work normally, push the bookmark with &lt;code&gt;jj git push&lt;&#x2F;code&gt;, open a pull request, and eventually clean things up when the work is merged.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-commands&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-commands&quot; aria-label=&quot;Anchor link for: the-commands&quot;&gt;The commands&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The core commands are intentionally small:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; create&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; feature-x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;      #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; create workspace + bookmark, then cd into it&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; list&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;                  #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; list workspaces&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; cd&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; feature-x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;          #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; switch to a workspace&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; exit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;                  #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; return to the main repo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; delete&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; feature-x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;      #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; delete workspace, bookmark, and files&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; cleanup&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;               #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; remove clean workspaces with merged bookmarks&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There are also configuration and utility commands:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; config&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; config&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; get&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; workspace_dir&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; root&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; version&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The goal is not to wrap every part of jj. It is only there to smooth over the workspace lifecycle.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hooks-for-real-project-setup&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hooks-for-real-project-setup&quot; aria-label=&quot;Anchor link for: hooks-for-real-project-setup&quot;&gt;Hooks for real project setup&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The feature that makes this particularly useful for me is lifecycle hooks.&lt;&#x2F;p&gt;
&lt;p&gt;A lot of projects need a bit of per-workspace setup. Maybe you need a unique dev server port, a generated &lt;code&gt;.env&lt;&#x2F;code&gt; file, a temporary database name, or some other local resource. When running multiple agents in parallel, those details matter a lot more.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;jjw&lt;&#x2F;code&gt; supports hooks such as:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pre_create&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;post_create&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pre_delete&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;post_delete&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Hooks receive useful environment variables, including:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;JJW_NAME&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;JJW_PATH&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;JJW_BOOKMARK&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;JJW_REPO_ROOT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;JJW_JJ_ROOT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;JJW_WORKSPACE_DIR&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;JJW_INDEX&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For example, you can use the allocated index to assign each workspace a different port:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;h&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ooks&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ost_create&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;cript&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;scripts&#x2F;setup-ports.sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#!&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;&#x2F;bin&#x2F;bash&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PORT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;$(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; JJW_INDEX&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Dev server port: &lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PORT&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That is the kind of small automation that makes parallel workspaces much easier to live with.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;getting-started&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#getting-started&quot; aria-label=&quot;Anchor link for: getting-started&quot;&gt;Getting started&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The easiest way to install &lt;code&gt;jjw&lt;&#x2F;code&gt; is with &lt;code&gt;go install&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;go&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; install&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; github.com&#x2F;aranw&#x2F;jjw@latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Alternatively, you can build it from source. This requires &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;just.systems&quot;&gt;Just&lt;&#x2F;a&gt; to be installed.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;jjw.git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;cd&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; jjw&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;just&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; install&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then add the shell integration for your shell:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; zsh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;eval&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; init&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; zsh&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; bash&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;eval&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; init&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; bash&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; fish&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; init&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; fish&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; source&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;jjw&lt;&#x2F;code&gt; currently expects &lt;code&gt;jj&lt;&#x2F;code&gt; 0.25+ and a colocated jj&#x2F;git repository.&lt;&#x2F;p&gt;
&lt;p&gt;In a repository where you want to use it, initialise the config:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; config&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then create your first workspace:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;jjw&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; create&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; my-change&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;what-s-next&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;What&#x27;s next&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This is an initial release, so I expect the edges to become clearer as I use it more. The current focus is keeping the tool boring and predictable: create workspaces, connect them to bookmarks, run hooks, and clean them up safely.&lt;&#x2F;p&gt;
&lt;p&gt;If you use &lt;code&gt;jj&lt;&#x2F;code&gt;, run multiple local workstreams, or are experimenting with coding agents, I&#x27;d be interested to hear whether this fits your workflow.&lt;&#x2F;p&gt;
&lt;p&gt;The project is on GitHub here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;jjw&quot;&gt;github.com&#x2F;aranw&#x2F;jjw&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Why I built a reference Go project (and taught an AI agent to clone it)</title>
          <pubDate>Wed, 13 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/why-i-built-a-reference-go-project/</link>
          <guid>https://aran.dev/posts/why-i-built-a-reference-go-project/</guid>
          <description xml:base="https://aran.dev/posts/why-i-built-a-reference-go-project/">&lt;p&gt;I get asked about how to structure a Go project often. Not the big architectural questions about microservices versus monoliths, but the mundane stuff: where does the config go, why isn&#x27;t there a &lt;code&gt;pkg&#x2F;&lt;&#x2F;code&gt; directory, what&#x27;s the difference between &lt;code&gt;internal&#x2F;books&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;internal&#x2F;database&#x2F;books&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I used to answer with a wall of text. Then I started linking to specific repos I&#x27;d worked on, which helped but came with baggage — real projects accumulate weird one-off decisions that don&#x27;t generalise. So I built a reference instead: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;example-project-structure&quot;&gt;example-project-structure&lt;&#x2F;a&gt;, a minimal Go HTTP&#x2F;RPC service backed by PostgreSQL. A fake bookstore with just enough domain (books, authors, genres) to show joins and type conversions without drowning in features.&lt;&#x2F;p&gt;
&lt;p&gt;That was reason one. Reason two turned out to be more interesting.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;structure-as-a-document-an-agent-can-read&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#structure-as-a-document-an-agent-can-read&quot; aria-label=&quot;Anchor link for: structure-as-a-document-an-agent-can-read&quot;&gt;Structure as a document an agent can read&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve been spending time with AI coding agents lately, and I kept running into the same friction: every time I start a new project, I want it in a structure I already know and like. But AI output is non-deterministic. Ask an agent to scaffold a project twice and you&#x27;ll get two different structures with the odd similarity. One run gives you &lt;code&gt;models&#x2F;&lt;&#x2F;code&gt;, the next gives you &lt;code&gt;entities&#x2F;&lt;&#x2F;code&gt;, neither matches what you actually want. So I thought: why not give it a concrete example to bootstrap from instead of hoping it guesses right?&lt;&#x2F;p&gt;
&lt;p&gt;So I wrote the reference repo with a second reader in mind. The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;example-project-structure&#x2F;tree&#x2F;main&#x2F;docs&quot;&gt;&lt;code&gt;docs&#x2F;&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; directory isn&#x27;t just for humans — &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;example-project-structure&#x2F;blob&#x2F;main&#x2F;docs&#x2F;layout.md&quot;&gt;&lt;code&gt;layout.md&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; maps every directory and explains what lives where, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;example-project-structure&#x2F;blob&#x2F;main&#x2F;docs&#x2F;design-decisions.md&quot;&gt;&lt;code&gt;design-decisions.md&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; captures the reasoning behind choices like &quot;Service not Repository&quot; and &quot;package-by-feature not package-by-layer&quot;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;example-project-structure&#x2F;blob&#x2F;main&#x2F;docs&#x2F;adding-an-entity.md&quot;&gt;&lt;code&gt;adding-an-entity.md&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is a step-by-step walkthrough an agent can follow mechanically.&lt;&#x2F;p&gt;
&lt;p&gt;Then I took it a step further. The repo includes a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;example-project-structure&#x2F;blob&#x2F;main&#x2F;BOOTSTRAP.md&quot;&gt;&lt;code&gt;BOOTSTRAP.md&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; file that&#x27;s written specifically for an AI agent. It assumes the agent has already cloned the repo and copied its contents into a new project directory. From there it walks through every rename, every file to strip, every conditional based on what the user actually wants (OpenAPI only? ConnectRPC only? No database?). Each step lists &lt;em&gt;what to change&lt;&#x2F;em&gt; and &lt;em&gt;every place it appears&lt;&#x2F;em&gt; so the agent doesn&#x27;t need to rediscover the structure on its own.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve paired this with a Claude Code skill (not open source yet, still refining it) that asks the user a handful of questions (project name, module path, API style, database choice) and then follows &lt;code&gt;BOOTSTRAP.md&lt;&#x2F;code&gt; to scaffold a new project. When paired with some details about what you&#x27;re building, the AI can produce a fairly decent starting point for a project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-the-structure-actually-looks-like&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-the-structure-actually-looks-like&quot; aria-label=&quot;Anchor link for: what-the-structure-actually-looks-like&quot;&gt;What the structure actually looks like&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The project is small by design — just enough to show the patterns without drowning in features:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── api&#x2F;              # protobuf &amp;amp; OpenAPI schemas&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── apigen&#x2F;           # generated API stubs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── cmd&#x2F;bookstore&#x2F;    # binary entry point&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└── internal&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ├── books&#x2F;        # domain package (one per feature)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ├── database&#x2F;     # data access, separated from transport&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    └── server&#x2F;       # HTTP&#x2F;RPC wiring&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s no &lt;code&gt;pkg&#x2F;&lt;&#x2F;code&gt;, no &lt;code&gt;models&#x2F;&lt;&#x2F;code&gt;, no &lt;code&gt;handlers&#x2F;&lt;&#x2F;code&gt; directory. Each of those absences is a deliberate choice, and I&#x27;ve written up the reasoning in the repo&#x27;s docs.&lt;&#x2F;p&gt;
&lt;p&gt;Rather than cramming everything into one post, I&#x27;m breaking the details into separate pieces:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The structure itself&lt;&#x2F;strong&gt; — the directory tree, what each package owns, the layering rules, and what&#x27;s deliberately missing&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Design decisions&lt;&#x2F;strong&gt; — why Service over Repository, why package-by-feature wins over time, why manual DI in &lt;code&gt;runner.go&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The BOOTSTRAP.md approach&lt;&#x2F;strong&gt; — writing documentation that serves both humans and AI agents, how the Claude Code skill works, and what I&#x27;ve learnt about making repos agent-friendly&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For now, the repo is public and the docs are thorough. Have a look at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;example-project-structure&quot;&gt;example-project-structure&lt;&#x2F;a&gt; — I&#x27;ll dig into the design decisions and the BOOTSTRAP.md approach in follow-up posts.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Now</title>
          <pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/now/</link>
          <guid>https://aran.dev/now/</guid>
          <description xml:base="https://aran.dev/now/">&lt;ul&gt;
&lt;li&gt;living in Huddersfield, UK&lt;&#x2F;li&gt;
&lt;li&gt;working remotely as a Golang Engineer&lt;&#x2F;li&gt;
&lt;li&gt;our little one is coming up on their first birthday, which has been the best and most tiring year of my life&lt;&#x2F;li&gt;
&lt;li&gt;exploring UK rail data and building Go services to process real-time train information with Kafka and PostgreSQL (&lt;a href=&quot;&#x2F;posts&#x2F;processing-uk-rail-data-in-real-time&#x2F;&quot;&gt;wrote about it here&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;planning to rework parts of my home smart lighting project, which has been on hold for a bit&lt;&#x2F;li&gt;
&lt;li&gt;leaning into AI tools to bring ideas to life quicker, iterating on more projects than I&#x27;d have time for otherwise. I did &lt;a href=&quot;&#x2F;posts&#x2F;how-i-lost-a-database-and-learned-to-actually-use-ai&#x2F;&quot;&gt;lose a database&lt;&#x2F;a&gt; along the way, which taught me to be more structured about it&lt;&#x2F;li&gt;
&lt;li&gt;squeezing in photography and DIY on the house when I get the odd free weekend&lt;&#x2F;li&gt;
&lt;li&gt;still writing about projects and Go on &lt;a href=&quot;&#x2F;posts&quot;&gt;the blog&lt;&#x2F;a&gt;, trying to keep a steady pace&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;about-now&quot;&gt;About &#x2F;now&lt;&#x2F;h2&gt;
&lt;p&gt;Inspired by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sive.rs&#x2F;now&quot;&gt;Derek Sivers&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nownownow.com&#x2F;about&quot;&gt;nownownow.com&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aboutideasnow.com&#x2F;&quot;&gt;aboutideasnow.com&lt;&#x2F;a&gt;, this is a &quot;now&quot; page. It&#x27;s a simple way to tell you what I&#x27;m up to right now.&lt;&#x2F;p&gt;
&lt;p&gt;This page is periodically updated manually to reflect what I&#x27;m currently working on, learning, and doing. It&#x27;s a snapshot of my life at this moment in time.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How I lost a database and learned to actually use AI</title>
          <pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/how-i-lost-a-database-and-learned-to-actually-use-ai/</link>
          <guid>https://aran.dev/posts/how-i-lost-a-database-and-learned-to-actually-use-ai/</guid>
          <description xml:base="https://aran.dev/posts/how-i-lost-a-database-and-learned-to-actually-use-ai/">&lt;p&gt;A while back I was working on a personal project, a small homelab setup that collected boiler and heating measurements into a PostgreSQL database. I was using AI to help me with some database migration work, restructuring tables and migrate them to a being partitioned. The AI gave me a sequence of SQL commands, confident and clear. I copied and pasted them into my database editor and ran them. No backups. The data was gone.&lt;&#x2F;p&gt;
&lt;p&gt;It was my fault. Not in a hand-wavy &quot;we&#x27;re all responsible&quot; kind of way. I literally ran the commands without properly understanding them. The AI suggested dropping the original table before I&#x27;d verified the temp table was intact, and I just did it.&lt;&#x2F;p&gt;
&lt;p&gt;But here&#x27;s the thing: I didn&#x27;t feel reckless at the time. The session had been going well. The AI suggested a command, I ran it, it worked. Then another, worked again. Each successful step built a kind of momentum, and by the time we got to the destructive operation my guard was completely down. The prior successes had done their job. I was confident we were on the right track.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s the pattern worth watching out for. It&#x27;s not that AI gives you bad commands (often it doesn&#x27;t). It&#x27;s that a long run of good ones quietly erodes your scepticism, so when it eventually gives you something that doesn&#x27;t quite do what you expected, you&#x27;re not in the right headspace to catch it. You&#x27;ve been lulled into executing rather than reviewing.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;There&#x27;s a story that did the rounds recently where a developer claimed a Cursor&#x2F;Claude agent deleted his company&#x27;s production database. Ibrahim Diallo &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;idiallo.com&#x2F;blog&#x2F;ai-didnt-delete-your-database-you-did&quot;&gt;makes a blunt point about it&lt;&#x2F;a&gt;: the AI didn&#x27;t do anything. You gave it the access, you ran the process, and somewhere in your architecture there was an endpoint capable of wiping your entire production database. That&#x27;s not an AI problem.&lt;&#x2F;p&gt;
&lt;p&gt;He&#x27;s right. But I think it&#x27;s worth unpacking &lt;em&gt;why&lt;&#x2F;em&gt; people end up there, because it&#x27;s not simply carelessness. AI assistants are unnervingly fluent. They produce SQL, shell commands, and migration scripts with the same confident tone regardless of whether the operation is reversible or catastrophic. There&#x27;s no hesitation, no flag on the dangerous bit. You have to bring your own scepticism, and after twenty minutes of a productive session where everything has worked exactly as described, that scepticism is the first thing to go.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;After losing that data I spent some time thinking about what had actually gone wrong in my process, beyond the obvious &quot;take backups&quot; lesson. The answer wasn&#x27;t complicated: I had no structure around what the AI was doing. I was having a conversation, following its lead, and executing whatever it produced. There were no checkpoints, no moments where I stepped back and asked whether what was happening matched what I actually wanted.&lt;&#x2F;p&gt;
&lt;p&gt;Working out a better approach took months, not sessions. It meant experimenting across different projects, paying attention to where AI added real value and where it quietly created more problems than it solved. I don&#x27;t think that kind of learning can be shortcut by reading someone else&#x27;s process, but I can at least describe what I landed on.&lt;&#x2F;p&gt;
&lt;p&gt;I start by writing a PRD, a short document that captures what needs to happen and why, at a level of abstraction I can actually reason about. I put this together with AI help, but I review it and own it before anything gets built. It&#x27;s not a chat transcript, it&#x27;s a document I&#x27;d be comfortable handing to a colleague. The point is to have something I&#x27;ve thought through &lt;em&gt;before&lt;&#x2F;em&gt; I start generating code, so that I have a reference point to check against when things start moving fast.&lt;&#x2F;p&gt;
&lt;p&gt;From the PRD I generate tasks. Small, atomic ones. Each task includes the specific code changes involved, so when the AI picks one up it knows exactly what it&#x27;s introducing and modifying. This keeps it focused on a narrow scope rather than trying to hold an entire codebase in context, and it means if something goes wrong I know roughly what were working on, what went wrong and why. Handing it a whole PRD to implement in one go is a reliable way to watch it confidently go sideways halfway through. Smaller tasks mean shorter context, which means fewer hallucinations and less drift.&lt;&#x2F;p&gt;
&lt;p&gt;At every stage (PRD, tasks, code) I&#x27;m the one verifying that what&#x27;s happening matches what I intended. The AI is generating, I&#x27;m reviewing.&lt;&#x2F;p&gt;
&lt;p&gt;For code changes, I commit at logical points as I go using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jj-vcs.github.io&#x2F;jj&#x2F;&quot;&gt;Jujutsu&lt;&#x2F;a&gt;, a version control system that makes frequent checkpointing feel natural rather than ceremonial. Sensible intervals mean that if something goes wrong I&#x27;m not scrambling to unpick a large, tangled diff.&lt;&#x2F;p&gt;
&lt;p&gt;For commands (SQL, terminal operations, anything running directly against a live system) the approach is different because you can&#x27;t just roll back to a previous commit. Here I slow down and actually read what the AI has given me before running it. If it&#x27;s touching a database, I take a backup first. The momentum problem from my opening story is exactly what I&#x27;m trying to resist: the temptation to keep executing because the last five commands worked fine. A destructive SQL statement looks just as routine as a &lt;code&gt;SELECT&lt;&#x2F;code&gt; in the flow of a session, and it&#x27;s easy to treat it the same way.&lt;&#x2F;p&gt;
&lt;p&gt;There are a few areas I still want to explore. One is sandboxing tooling like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;agent-safehouse.dev&#x2F;&quot;&gt;Agent Safehouse&lt;&#x2F;a&gt;, which aims to constrain what an AI agent can actually reach. Another is VM-based AI agents, where the entire environment the agent operates in is disposable and isolated. Both feel like they&#x27;re attacking the right problem: rather than relying entirely on the human to catch the dangerous command, you limit the blast radius of what can go wrong in the first place.&lt;&#x2F;p&gt;
&lt;p&gt;The other thing I want to look at is database branching. The idea is borrowed from version control: instead of running migrations against the database you care about, you create an isolated copy, run your changes there, and only merge when you&#x27;re confident it&#x27;s right. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;neon.tech&quot;&gt;Neon&lt;&#x2F;a&gt; offers this as a managed service, using copy-on-write storage so branches are created in seconds without duplicating data. Each branch gets its own connection string, completely isolated from the parent. If my original project had been running on something like that, I could have branched the database before letting the AI anywhere near it and thrown the branch away when it went wrong.&lt;&#x2F;p&gt;
&lt;p&gt;What I&#x27;m less clear on is how to get something similar locally. There are a handful of tools in this space. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sastraxi&#x2F;pgsh&quot;&gt;pgsh&lt;&#x2F;a&gt; uses Postgres template databases to let you branch and switch between database states from the command line. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;postgres-ai&#x2F;database-lab-engine&quot;&gt;Database Lab Engine&lt;&#x2F;a&gt; takes a different approach, using ZFS copy-on-write snapshots to create thin clones of full databases. And at the simpler end, Postgres schemas can act as a kind of lightweight namespacing, letting you create isolated environments within a single database by manipulating the search path, which is useful for testing even if it&#x27;s not true branching.&lt;&#x2F;p&gt;
&lt;p&gt;I haven&#x27;t tried any of these yet, but the general pattern is appealing: make it cheap to create a disposable copy of your database state so that destructive operations have a clear undo path. That would have saved me a lot of pain.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;All of this is really just structure around one idea: the AI doesn&#x27;t know which of its suggestions will ruin your afternoon, and it won&#x27;t tell you. The fluency is real. The judgement isn&#x27;t. Those are different things, and the longer a session goes on, the easier it is to forget that. The shift in how I think about AI is probably the most useful thing to come out of losing that data. I&#x27;ve stopped treating it as a collaborator who has my back, and started treating it as a very fast, capable tool that will do exactly what you ask without judgement.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Services</title>
          <pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/services/</link>
          <guid>https://aran.dev/services/</guid>
          <description xml:base="https://aran.dev/services/">&lt;p&gt;Alongside my day job, I take on a small number of freelance projects — usually teams who need hands-on engineering help with Go, distributed systems, or cloud infrastructure.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m selective about what I take on. I&#x27;d rather do a few things well than spread myself thin, so if your project isn&#x27;t a good fit for my skills, I&#x27;ll say so.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-can-help-with&quot;&gt;What I can help with&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Backend engineering and system design&lt;&#x2F;strong&gt; — I build services in Go: APIs, workers, data pipelines, event-driven architectures. I&#x27;m comfortable taking a blank repo to production, or picking up an existing codebase and making it better.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Cloud infrastructure&lt;&#x2F;strong&gt; — Most of my experience is with Google Cloud — Cloud Run, Compute Engine, Cloud Functions, BigQuery, Pub&#x2F;Sub, Cloud Storage. I design, build, and operate cloud-native systems that scale without becoming a maintenance headache.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Data and integration&lt;&#x2F;strong&gt; — Real-time pipelines, Kafka producers and consumers, database design and migration (mostly PostgreSQL), and connecting systems that were never meant to talk to each other.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Architecture and technical strategy&lt;&#x2F;strong&gt; — Sometimes you just need someone to look at what you&#x27;ve got, work out what&#x27;s working, and help plan what comes next: architecture reviews, technology choices, migration planning.&lt;&#x2F;p&gt;
&lt;p&gt;My primary stack is Go, PostgreSQL, Kafka, and Google Cloud, with Docker, Terraform, GitHub Actions, and gRPC alongside. I&#x27;ve spent time with Rust too, though Go is where I&#x27;m most productive.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-i-work&quot;&gt;How I work&lt;&#x2F;h2&gt;
&lt;p&gt;I like to stay close to your team and codebase rather than working in isolation — I just do it on my own schedule. That might mean evening commits, async code reviews, or catching up on Slack outside your usual hours. I&#x27;m based in the UK (GMT), and engagements typically run from a few weeks to a few months.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;get-in-touch&quot;&gt;Get in touch&lt;&#x2F;h2&gt;
&lt;p&gt;If you think I could help with something you&#x27;re working on, the easiest way to start is to book a short call — no commitment, just a conversation about what you need and whether I&#x27;m the right person for it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cal.com&#x2F;aranw&#x2F;15min?duration=15&quot;&gt;Book a call&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can also reach me by email at &lt;a href=&quot;mailto:aran@aran.dev&quot;&gt;aran@aran.dev&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Using CUE to unify IoT sensor data</title>
          <pubDate>Fri, 17 Oct 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/cue/using-cue-to-unify-iot-sensor-data/</link>
          <guid>https://aran.dev/posts/cue/using-cue-to-unify-iot-sensor-data/</guid>
          <description xml:base="https://aran.dev/posts/cue/using-cue-to-unify-iot-sensor-data/">&lt;p&gt;I&#x27;ve been building a home automation system that processes motion sensor data from various IKEA Zigbee devices. Each sensor type sends a different data structure, and I needed to extract consistent information from all of them without sacrificing type safety.&lt;&#x2F;p&gt;
&lt;p&gt;I spent a long time trying to make Home Assistant work for my setup. The visual automation builder felt limiting once I needed conditional logic beyond simple triggers, and writing automations in YAML became tedious. Adding complex logic like correlating motion across multiple rooms or applying time-weighted logic was difficult. I ended up fighting the system rather than solving the problem.&lt;&#x2F;p&gt;
&lt;p&gt;My current home automation setup looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;┌─────────────┐  ┌─────────────┐&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│   TRÅDFRI   │  │  VALLHORN   │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│   Sensor    │  │   Sensor    │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└──────┬──────┘  └──────┬──────┘&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │                │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       └────────┬───────┘&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         Zigbee │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                ▼&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       ┌──────────────────┐&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │  zigbee2mqtt     │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │  (Zigbee bridge) │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       └────────┬─────────┘&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                ▼&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       ┌──────────────────┐&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │   MQTT Broker    │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │                  │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │ Topics:          │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │  bedroom&#x2F;motion  │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │  hallway&#x2F;motion  │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       │  kitchen&#x2F;motion  │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       └────┬─────────┬───┘&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            │         │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            │         │ JSON messages&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ┌───────┘         └───────┐&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ▼                         ▼&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;┌──────────────┐      ┌──────────────┐&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│    Node-RED  │      │     Home     │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│              │      │   Assistant  │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  - Visual    │      │              │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│    flows     │◄────►│  - Visual    │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│  - Custom    │      │    automata  │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│    logic     │      │  - YAML      │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└──────────────┘      └──────┬───────┘&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                             │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                             │ HTTP API&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                             ▼&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    ┌──────────────────┐&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    │ Philips Hue Hub  │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    │                  │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    │  ┌────┐  ┌────┐  │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    │  │Bulb│  │Bulb│  │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    │  └────┘  └────┘  │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    │                  │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    │  ┌──────┐        │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    │  │Sensor│        │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    │  └──────┘        │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    └──────────────────┘&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.home-assistant.io&#x2F;&quot;&gt;Home Assistant&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nodered.org&#x2F;&quot;&gt;Node-RED&lt;&#x2F;a&gt; handling some of my automation logic, and controlling devices through various hubs like Philips Hue. This works for many people, but I wanted more control and flexibility that I knew I could get with a custom Go service. I could build exactly what I wanted with the language I already knew.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m building a Go service to replace this entire automation layer—Home Assistant, Node-RED, and the device-specific hubs. The service subscribes directly to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;MQTT&quot;&gt;MQTT&lt;&#x2F;a&gt; topics where zigbee2mqtt publishes sensor data, processes that data, and sends commands back to control lights and other devices.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-system-architecture&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-system-architecture&quot; aria-label=&quot;Anchor link for: the-system-architecture&quot;&gt;The system architecture&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The sensors connect via Zigbee, a low-power wireless protocol common in home automation. I&#x27;m running &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.zigbee2mqtt.io&#x2F;&quot;&gt;zigbee2mqtt&lt;&#x2F;a&gt; which bridges between Zigbee devices and an MQTT broker. My Go service subscribes to MQTT topics and processes the JSON messages that sensors publish whenever their state changes.&lt;&#x2F;p&gt;
&lt;p&gt;Currently, I&#x27;m using two different sensor types:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.zigbee2mqtt.io&#x2F;devices&#x2F;E1525_E1745.html&quot;&gt;IKEA TRÅDFRI motion sensor&lt;&#x2F;a&gt; – the older model that reports light levels as a boolean&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.zigbee2mqtt.io&#x2F;devices&#x2F;E2134.html&quot;&gt;IKEA VALLHORN motion sensor&lt;&#x2F;a&gt; – the newer model with precise lux readings and motion timing&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Eventually, I&#x27;ll add others including Philips Hue sensors. Each type has its own unique data structure with different fields. I&#x27;d seen this pattern before—small differences that compound into maintenance headaches as the codebase grows.&lt;&#x2F;p&gt;
&lt;p&gt;The zigbee2mqtt documentation shows what each sensor exposes. The TRÅDFRI reports light levels as a boolean &lt;code&gt;illuminance_above_threshold&lt;&#x2F;code&gt;, whilst the VALLHORN gives you actual lux readings in an &lt;code&gt;illuminance&lt;&#x2F;code&gt; field. The VALLHORN tells you precisely how many seconds have passed since motion stopped via &lt;code&gt;no_occupancy_since&lt;&#x2F;code&gt;; the TRÅDFRI doesn&#x27;t provide this at all. Both detect motion with an &lt;code&gt;occupancy&lt;&#x2F;code&gt; boolean, but handling these differences in pure Go means type switches, assertions, and duplicated logic across every method that touches sensor data.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cuelang.org&#x2F;&quot;&gt;CUE&lt;&#x2F;a&gt; is an open-source data validation language and inference engine that lets you define schemas, validate data against those schemas, and template data. Unlike JSON Schema which only validates, or Protocol Buffers which require compilation, CUE combines validation and data templating in a single language with a syntax that feels familiar to JSON and Go.&lt;&#x2F;p&gt;
&lt;p&gt;From the beginning I knew &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cuelang.org&#x2F;&quot;&gt;CUE&lt;&#x2F;a&gt; would let me validate sensor data at the boundary and transform it into consistent structures, meaning the rest of my code doesn&#x27;t need to know which sensor type it&#x27;s processing. CUE handles the differences between sensor types in both directions, but this article focuses on the inbound transformation (I&#x27;ll cover outbound MQTT messages in a follow-up article). This article shows my CUE implementation alongside what a pure Go approach might have looked like, demonstrating where CUE removes the boilerplate.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-pure-go-approach-and-its-limitations&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-pure-go-approach-and-its-limitations&quot; aria-label=&quot;Anchor link for: the-pure-go-approach-and-its-limitations&quot;&gt;The pure Go approach and its limitations&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s what processing sensor data might have looked like if I&#x27;d built this system in pure Go without CUE:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Separate structs for each sensor&amp;#39;s raw data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; TradfriRawData&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Battery&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;                   int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;  `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;battery&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Occupancy&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;                 bool&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;occupancy&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    IlluminanceAboveThreshold&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; bool&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;illuminance_above_threshold&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Linkquality&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;               int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;  `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;linkquality&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; VallhornRawData&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Battery&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;          int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;   `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;battery&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Occupancy&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;        bool&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;  `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;occupancy&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Illuminance&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;      int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;   `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;illuminance&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    NoOccupancySince&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;  `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;no_occupancy_since,omitempty&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Optional, pointer to handle null&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Linkquality&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;      int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;   `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;linkquality&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Voltage&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;          int&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;   `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;voltage&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; MotionSensorMessage&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    SensorID&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;   string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;sensor_id&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Timestamp&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;  string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;timestamp&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Floor&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;      string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;floor&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    SensorType&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;sensor_type&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    RawData&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    any&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;raw_data&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Processing requires routing based on sensor type&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; ProcessMQTTMessage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; payload&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; metadata&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; SensorMetadata&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MotionSensorMessage&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; rawData&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; any&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    switch&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; metadata&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;SensorType&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ikea_tradfri_motion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; tradfri&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; TradfriRawData&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; json&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unmarshal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;tradfri&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;failed to unmarshal tradfri data: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%w&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        rawData&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; tradfri&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ikea_vallhorn_motion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; vallhorn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; VallhornRawData&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; json&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unmarshal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;vallhorn&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;failed to unmarshal vallhorn data: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%w&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        rawData&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; vallhorn&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    default&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;unknown sensor type: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; metadata&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;SensorType&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MotionSensorMessage&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        SensorID&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;   metadata&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;SensorID&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Timestamp&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;  time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Now&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Format&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;RFC3339&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Floor&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;      metadata&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Floor&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        SensorType&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; metadata&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;SensorType&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        RawData&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    rawData&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Every method needs type switches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;m &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MotionSensorMessage&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; IsMotionDetected&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    switch&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; data&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; m&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;RawData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; TradfriRawData&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Occupancy&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; VallhornRawData&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Occupancy&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    default&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;unknown sensor data type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;m &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MotionSensorMessage&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; IsLightSufficient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; bool&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    switch&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; data&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; m&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;RawData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; TradfriRawData&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Field is inverted - true means dark&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;IlluminanceAboveThreshold&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; VallhornRawData&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Consider light sufficient if illuminance is above 50 lux&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Illuminance&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 50&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    default&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;unknown sensor data type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;m &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MotionSensorMessage&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; GetSecondsSinceMotion&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; bool&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    vallhorn&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; ok&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; m&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;RawData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;VallhornRawData&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ok&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Not supported on this sensor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; vallhorn&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Occupancy&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Motion detected, field not present&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; vallhorn&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;NoOccupancySince&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Field is null&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;vallhorn&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;NoOccupancySince&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Typed structs don&#x27;t solve the fundamental problem. Every method that extracts data requires type switches to handle the union. Optional fields like &lt;code&gt;NoOccupancySince&lt;&#x2F;code&gt; need pointers to distinguish between &quot;not present&quot;, &quot;null&quot;, and &quot;has a value&quot;. The VALLHORN sensor provides precise timing about how long a room has been empty, but extracting this value requires checking the sensor type, verifying occupancy is false, checking the pointer isn&#x27;t nil, and dereferencing it.&lt;&#x2F;p&gt;
&lt;p&gt;Add a third sensor type and you extend every type switch with another case. When IKEA releases a firmware update that changes a field, you hunt through multiple functions to fix it. I knew I&#x27;d be adding more sensor types and dealing with firmware variations, so I chose CUE from the start rather than fighting this complexity.&lt;&#x2F;p&gt;
&lt;p&gt;You could build similar behaviour in pure Go with interfaces and careful abstraction. CUE handles this declaratively. You define schemas with constraints and transformations, and CUE validates the incoming data whilst extracting normalised structures.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-cue-provides&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-cue-provides&quot; aria-label=&quot;Anchor link for: what-cue-provides&quot;&gt;What CUE provides&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;CUE is a configuration language that handles everything from defining schemas and generating configuration files to validating data and computing transformations. It&#x27;s used for API definitions, build configurations, policy validation, and more. In this system, I use it to validate sensor data at the boundary and transform it into normalised structures that my Go code can work with consistently.&lt;&#x2F;p&gt;
&lt;p&gt;Instead of validating battery levels with scattered if statements through your Go code, the schema catches invalid data at the boundary:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;cue&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;battery&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;100&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;CUE definitions (prefixed with &lt;code&gt;#&lt;&#x2F;code&gt;) work like type definitions in Go. You can reference them and compose them with other schemas, but they don&#x27;t produce output by themselves. This lets you build a union type for all supported sensors:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;cue&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;#MotionSensor&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; #IkeaTradfriMotionSensorStatus&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; #IkeaVallhornMotionSensorStatus&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The language handles both validation and transformation. This means you can turn the TRÅDFRI&#x27;s boolean light sensor and the VALLHORN&#x27;s lux reading into a single &lt;code&gt;light_sufficient&lt;&#x2F;code&gt; field without writing Go type switches.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-the-schemas&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#building-the-schemas&quot; aria-label=&quot;Anchor link for: building-the-schemas&quot;&gt;Building the schemas&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I started with a base schema that all sensors share:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;cue&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;#MotionSensorBase&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    sensor_id&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    timestamp&lt;&#x2F;span&gt;&lt;span&gt;?&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    floor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;basement&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ground&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;upstairs&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;attic&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    sensor_type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    raw_data&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; _&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;!&lt;&#x2F;code&gt; operator marks required fields, whilst &lt;code&gt;?&lt;&#x2F;code&gt; marks optional ones.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;raw_data&lt;&#x2F;code&gt; field uses CUE&#x27;s top type &lt;code&gt;_&lt;&#x2F;code&gt;, which accepts any value. This allows each sensor type to define its own structure for &lt;code&gt;raw_data&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Each sensor gets its own schema that composes the base with sensor-specific constraints:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;cue&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;#IkeaTradfriMotionSensorStatus&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; #MotionSensorBase&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    sensor_type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ikea_tradfri_motion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    raw_data&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        battery&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;100&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        occupancy&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        illuminance_above_threshold&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        linkquality&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;255&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt; operator composes schemas. Each sensor gets its own constraints that match IKEA&#x27;s hardware specifications.&lt;&#x2F;p&gt;
&lt;p&gt;The VALLHORN schema includes additional fields that the older TRÅDFRI doesn&#x27;t support:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;cue&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;#IkeaVallhornMotionSensorStatus&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; #MotionSensorBase&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    sensor_type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ikea_vallhorn_motion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    raw_data&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        battery&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;100&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        occupancy&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        illuminance&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;65535&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        linkquality&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;255&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Only present when occupancy is false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        no_occupancy_since&lt;&#x2F;span&gt;&lt;span&gt;?&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; null&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        voltage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;no_occupancy_since&lt;&#x2F;code&gt; field is optional (marked with &lt;code&gt;?&lt;&#x2F;code&gt;) and can be either null or a positive integer. When motion stops, the sensor starts counting seconds and reports them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;extracting-normalised-data&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#extracting-normalised-data&quot; aria-label=&quot;Anchor link for: extracting-normalised-data&quot;&gt;Extracting normalised data&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The extraction schemas contain the conditional logic that would otherwise live in Go type switches. Here&#x27;s motion detection:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;cue&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;#ExtractMotionFromSensor&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    sensor&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; #MotionSensor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    motion_detected&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensor_type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ikea_tradfri_motion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ||&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;       sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensor_type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ikea_vallhorn_motion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        motion_detected&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;raw_data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;occupancy&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Optional field for precise timing on supported sensors&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    no_occupancy_seconds&lt;&#x2F;span&gt;&lt;span&gt;?&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensor_type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ikea_vallhorn_motion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Only present when occupancy is false and sensor supports it&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;raw_data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;occupancy&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;           sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;raw_data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;no_occupancy_since&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; _|_&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;           sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;raw_data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;no_occupancy_since&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; null&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            no_occupancy_seconds&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;raw_data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;no_occupancy_since&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In Go, you&#x27;d write a method on each sensor struct to extract this data. Add a new sensor and you update every extraction method. With CUE, you add one conditional block to the schema and the extraction works everywhere you use it.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;no_occupancy_seconds&lt;&#x2F;code&gt; field shows how CUE handles sensor-specific features. The VALLHORN provides this timing information, the TRÅDFRI doesn&#x27;t. Making the field optional means sensors without this capability simply don&#x27;t include it. Automation logic can check if it exists and use it for precise timing (&quot;turn off lights if no motion for 10 minutes&quot;), or fall back to basic occupancy detection when it&#x27;s not available.&lt;&#x2F;p&gt;
&lt;p&gt;Light detection demonstrates how CUE normalises different measurement approaches:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;cue&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;#ExtractAmbientLightFromSensor&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    sensor&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; #MotionSensor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    light_sufficient&lt;&#x2F;span&gt;&lt;span&gt;?&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    has_ambient_sensor&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensor_type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ikea_tradfri_motion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;raw_data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;illuminance_above_threshold&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; _|_&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            has_ambient_sensor&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;            &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; This field is inverted - true means dark&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            light_sufficient&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;raw_data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;illuminance_above_threshold&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensor_type&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ikea_vallhorn_motion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;raw_data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;illuminance&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; _|_&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            has_ambient_sensor&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;            &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; 50 lux is sufficient for movement without lights&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            light_sufficient&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sensor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;raw_data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;illuminance&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 50&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The TRÅDFRI&#x27;s boolean gets inverted (it indicates darkness rather than light). The VALLHORN&#x27;s lux reading gets compared to a threshold. Application code doesn&#x27;t need to know about these differences. It just checks &lt;code&gt;light_sufficient&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-the-go-code-works-with-cue&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-the-go-code-works-with-cue&quot; aria-label=&quot;Anchor link for: how-the-go-code-works-with-cue&quot;&gt;How the Go code works with CUE&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;With CUE handling validation and transformation, the Go code becomes generic. In my Go code, I&#x27;ve decided to wrap optional fields in an Option type that provides convenience helpers like &lt;code&gt;IsPresent()&lt;&#x2F;code&gt; and &lt;code&gt;MustGet()&lt;&#x2F;code&gt; methods for safely handling values that may or may not exist.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the actual implementation:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Generic processor that works for ALL sensor types&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; MotionSensorProcessor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    cueCtx&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;                    *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;cue&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    extractMotionSchema&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;       cue&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Value&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; #ExtractMotionFromSensor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    extractAmbientLightSchema&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; cue&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Value&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; #ExtractAmbientLightFromSensor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; This function works for Vallhorn, Tradfri, or any future sensor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;p &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MotionSensorProcessor&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; ProcessSensorData&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;sensorData&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; MotionSensor&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; statusSchema&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; cue&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Value&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Encode to CUE and validate against the sensor-specific schema&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    sensorDataAsCUE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; p&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;cueCtx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Encode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensorData&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; statusSchema&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensorDataAsCUE&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Validate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;schema validation failed: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%w&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Extract motion event using CUE&amp;#39;s extraction schema&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    extractMotion&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sensor&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sensorData&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    extractMotionAsCUE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; p&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;cueCtx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Encode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;extractMotion&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    unifiedMotion&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; p&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;extractMotionSchema&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;extractMotionAsCUE&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Decode back to a clean Go struct&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; motionEvent&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; MotionEvent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; unifiedMotion&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Decode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;motionEvent&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;failed to decode motion event: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%w&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Identical code for all sensor types&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; motionEvent&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;MotionDetected&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;        handleMotionDetected&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensorData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Floor&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Works for Vallhorn (which has the field) and gracefully handles&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Tradfri (which doesn&amp;#39;t) via the Option type&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; motionEvent&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;NoOccupancySeconds&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;IsPresent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        seconds&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; motionEvent&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;NoOccupancySeconds&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MustGet&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; seconds&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 600&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;            handleNoMotionTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensorData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Floor&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; seconds&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Extract ambient light reading using CUE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    extractAmbientLight&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sensor&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sensorData&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    extractAmbientLightAsCUE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; p&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;cueCtx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Encode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;extractAmbientLight&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    unifiedAmbientLight&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; p&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;extractAmbientLightSchema&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;extractAmbientLightAsCUE&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; lightReading&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; AmbientLightReading&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; unifiedAmbientLight&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Decode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;lightReading&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;failed to decode ambient light: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%w&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Handles both Tradfri&amp;#39;s boolean and Vallhorn&amp;#39;s lux reading&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; CUE normalised them both into a single light_sufficient field&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; lightReading&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;HasAmbientSensor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; lightReading&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;LightSufficient&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;IsPresent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;lightReading&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;LightSufficient&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MustGet&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;            handleDarkness&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensorData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Floor&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Vallhorn-specific handler - ONLY handles unmarshalling&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; VallhornHandler&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    processor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;    *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MotionSensorProcessor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    statusSchema&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; cue&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Value&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; #IkeaVallhornMotionSensorStatus&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;h &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;VallhornHandler&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; HandleMQTT&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; payload&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    sensorID&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; floor&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; parseSensorTopic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;topic&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;failed to parse topic: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%w&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; THIS IS THE ONLY VALLHORN-SPECIFIC CODE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; rawData&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; VallhornRawData&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; json&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unmarshal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;rawData&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;failed to unmarshal: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%w&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Build the generic sensor data structure&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    sensorData&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; MotionSensor&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        SensorID&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;   sensorID&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Timestamp&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;  time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Now&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Format&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;RFC3339&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Floor&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;      floor&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        SensorType&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ikea_vallhorn_motion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        RawData&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    rawData&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Everything from here is generic and reusable&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; h&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;processor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ProcessSensorData&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensorData&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; h&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;statusSchema&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Tradfri-specific handler - ONLY handles unmarshalling&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; TradfriHandler&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    processor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;    *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MotionSensorProcessor&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    statusSchema&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; cue&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Value&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; #IkeaTradfriMotionSensorStatus&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;h &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;TradfriHandler&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; HandleMQTT&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; payload&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    sensorID&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; floor&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; parseSensorTopic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;topic&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;failed to parse topic: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%w&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; THIS IS THE ONLY TRADFRI-SPECIFIC CODE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; rawData&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; TradfriRawData&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; json&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unmarshal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;rawData&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;failed to unmarshal: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%w&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Build the generic sensor data structure&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    sensorData&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; MotionSensor&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        SensorID&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;   sensorID&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Timestamp&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;  time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Now&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Format&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;RFC3339&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Floor&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;      floor&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        SensorType&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ikea_tradfri_motion&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        RawData&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    rawData&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Same generic processor works for Tradfri too&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; h&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;processor&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ProcessSensorData&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sensorData&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; h&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;statusSchema&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each handler&#x27;s sensor-specific code is about five lines: just unmarshalling JSON into the correct struct type. Everything else is generic processing. The same &lt;code&gt;ProcessSensorData&lt;&#x2F;code&gt; function validates, extracts motion events, extracts ambient light readings, and executes business logic without knowing which specific sensor it&#x27;s processing.&lt;&#x2F;p&gt;
&lt;p&gt;The extraction schemas in CUE contain all the conditional logic. The Go code just encodes, unifies, and decodes. No type switches. No conditional chains.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-you-gain&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-you-gain&quot; aria-label=&quot;Anchor link for: what-you-gain&quot;&gt;What you gain&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;When an MQTT message arrives, validation against the CUE schema happens at the boundary. Invalid data gets rejected with specific error messages about what&#x27;s wrong rather than causing runtime panics deep in the application.&lt;&#x2F;p&gt;
&lt;p&gt;The schema files document the exact structure and valid ranges for each sensor. Adding a new sensor type means defining one schema and making the necessary changes to the extraction schemas for motion and ambient light. The existing generic processing code continues to work.&lt;&#x2F;p&gt;
&lt;p&gt;That said, CUE validates structure but tracking application state (like whether a room has been unoccupied for 10 minutes) still requires Go code. CUE tells you what the current state is, not what to do with it. Time-series analysis, rate limiting, and state machines live in your application layer.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a learning curve if you&#x27;re coming from Protobuf, JSON Schema or Go structs. CUE&#x27;s syntax looks familiar but behaves differently, especially around definitions versus concrete values. The documentation helps, but expect some initial friction.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-cue-instead-of-alternatives&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-cue-instead-of-alternatives&quot; aria-label=&quot;Anchor link for: why-cue-instead-of-alternatives&quot;&gt;Why CUE instead of alternatives&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;JSON Schema can validate incoming data but can&#x27;t transform it. You&#x27;d still need Go type switches to extract normalised structures from different sensor types.&lt;&#x2F;p&gt;
&lt;p&gt;Protocol Buffers require a compilation step and don&#x27;t handle optional fields as elegantly. The &lt;code&gt;no_occupancy_since&lt;&#x2F;code&gt; field that&#x27;s sometimes present, sometimes null, and sometimes absent would need Protobuf&#x27;s wrapper types (like &lt;code&gt;google.protobuf.Int32Value&lt;&#x2F;code&gt;) or custom handling logic.&lt;&#x2F;p&gt;
&lt;p&gt;CUE does both validation and transformation in one tool. The same schemas that validate incoming MQTT messages also define the extraction logic that normalises the data. You maintain one set of schemas that handles both concerns.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-next&quot; aria-label=&quot;Anchor link for: what-s-next&quot;&gt;What&#x27;s next&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I built this system with CUE from the start, and the flexibility it provides compared to a pure Go solution has worked well for this use case. The schemas sit at the boundary where MQTT messages enter the system. Every sensor type gets validated, and the rest of the codebase works with clean, normalised structures. The system currently handles four motion sensors across four floors, controlling lights on two floors.&lt;&#x2F;p&gt;
&lt;p&gt;Adding new sensor types means defining a schema and updating the extraction logic. When I add Philips Hue sensors, I&#x27;ll extend the &lt;code&gt;#MotionSensor&lt;&#x2F;code&gt; union type and make the necessary changes to the extraction schemas for motion and ambient light detection. The generic processing code won&#x27;t need to change.&lt;&#x2F;p&gt;
&lt;p&gt;This article covered inbound sensor data transformation. But in the coming weeks, I&#x27;ll be writing more about this system, starting with how CUE handles the outbound side: generating device-specific MQTT commands for different bulb types. There&#x27;s a lot more to explore around building automation rules, managing state, and handling edge cases in a distributed sensor network.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;thanks&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#thanks&quot; aria-label=&quot;Anchor link for: thanks&quot;&gt;Thanks&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Thanks to Paul, Roger and Daniel from the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cue.dev&#x2F;&quot;&gt;CUE&lt;&#x2F;a&gt; team for their help and support with CUE and this article.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Building an MQTT client in Go</title>
          <pubDate>Tue, 30 Sep 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/mqtt/building-a-mqtt-client-in-go/</link>
          <guid>https://aran.dev/posts/mqtt/building-a-mqtt-client-in-go/</guid>
          <description xml:base="https://aran.dev/posts/mqtt/building-a-mqtt-client-in-go/">&lt;p&gt;MQTT (Message Queuing Telemetry Transport) connects millions of IoT devices worldwide, from smart home sensors to industrial monitoring systems. Whilst many Go developers reach for existing libraries, building your own client wrapper teaches you the protocol&#x27;s nuances and gives you precise control over connection handling, message routing, and error recovery.&lt;&#x2F;p&gt;
&lt;p&gt;This article walks through building a production-ready MQTT client wrapper using the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;eclipse-paho&#x2F;paho.golang&quot;&gt;Eclipse Paho Go library&lt;&#x2F;a&gt;. You&#x27;ll see how to handle concurrent message processing, implement topic wildcard matching, and manage connection lifecycle properly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-anatomy-of-mqtt-communication&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-anatomy-of-mqtt-communication&quot; aria-label=&quot;Anchor link for: the-anatomy-of-mqtt-communication&quot;&gt;The anatomy of MQTT communication&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;MQTT operates as a publish-subscribe protocol where clients connect to a central broker. Publishers send messages to named topics, subscribers register interest in topic patterns, and the broker routes messages accordingly.&lt;&#x2F;p&gt;
&lt;p&gt;The protocol&#x27;s power lies in its topic hierarchy and wildcard system. Topics use forward slashes as separators (&lt;code&gt;sensors&#x2F;temperature&#x2F;living-room&lt;&#x2F;code&gt;), whilst wildcards enable flexible subscriptions: &lt;code&gt;+&lt;&#x2F;code&gt; matches exactly one level (&lt;code&gt;sensors&#x2F;+&#x2F;living-room&lt;&#x2F;code&gt; catches all sensor types in the living room), and &lt;code&gt;#&lt;&#x2F;code&gt; matches zero or more levels (&lt;code&gt;sensors&#x2F;#&lt;&#x2F;code&gt; catches everything under sensors).&lt;&#x2F;p&gt;
&lt;p&gt;Quality of Service (QoS) levels control delivery guarantees: QoS 0 sends messages once with no confirmation, QoS 1 ensures at-least-once delivery through acknowledgements, and QoS 2 provides exactly-once delivery via a four-step handshake.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;design-decisions-and-architecture&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#design-decisions-and-architecture&quot; aria-label=&quot;Anchor link for: design-decisions-and-architecture&quot;&gt;Design decisions and architecture&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The client wrapper needs to solve several problems that basic MQTT libraries don&#x27;t address well: concurrent message handling, automatic reconnection with subscription restoration, topic-based message routing, and graceful shutdown coordination.&lt;&#x2F;p&gt;
&lt;p&gt;The core design uses a connection manager from the autopaho library for automatic reconnection, a handler registry that maps topic patterns to functions, and a wait group to coordinate graceful shutdown of message processors.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Client&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    config&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;      ClientConfig&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    conn&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;        *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;autopaho&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ConnectionManager&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    handlers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;    map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MessageHandler&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    wg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;          *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;conc&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;WaitGroup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    mu&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;          sync&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Mutex&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    handlersMu&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;  sync&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;RWMutex&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    isConnected&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Two separate mutexes prevent deadlocks: &lt;code&gt;mu&lt;&#x2F;code&gt; protects connection state changes, whilst &lt;code&gt;handlersMu&lt;&#x2F;code&gt; guards the handler registry during concurrent read operations when messages arrive.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;MessageHandler&lt;&#x2F;code&gt; type accepts a context and payload, enabling handlers to access topic information and implement timeouts:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; MessageHandler&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; payload&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;implementing-connection-management&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#implementing-connection-management&quot; aria-label=&quot;Anchor link for: implementing-connection-management&quot;&gt;Implementing connection management&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Connection handling requires balancing automatic reconnection with application control. The autopaho library provides connection management, but you need to coordinate this with your handler system. Local brokers like Mosquitto running on your Home Assistant instance offer significant advantages: sub-millisecond latency, no internet dependency, and predictable connection behaviour.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;c &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Client&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Connect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;mu&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Lock&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;mu&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unlock&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;isConnected&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;conn&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;AwaitConnection&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;connecting to MQTT broker: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%w&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;isConnected&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;AwaitConnection&lt;&#x2F;code&gt; method blocks until the connection establishes or the context cancels. This approach lets callers implement their own timeout strategies whilst ensuring the client tracks connection state accurately. With a local Mosquitto broker, connections typically establish within milliseconds rather than the seconds required for internet-based brokers.&lt;&#x2F;p&gt;
&lt;p&gt;Authentication configuration happens during client creation. For local setups, you might skip authentication entirely or use simple username&#x2F;password pairs:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; For local broker without authentication&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;brokerURL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; url&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;tcp:&#x2F;&#x2F;homeassistant.local:1883&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; For local broker with basic authentication&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;brokerURL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; url&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;tcp:&#x2F;&#x2F;username:password@homeassistant.local:1883&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;topic-wildcard-matching-implementation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#topic-wildcard-matching-implementation&quot; aria-label=&quot;Anchor link for: topic-wildcard-matching-implementation&quot;&gt;Topic wildcard matching implementation&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;MQTT&#x27;s wildcard system requires careful implementation to match the specification exactly. The &lt;code&gt;topicMatches&lt;&#x2F;code&gt; function handles three cases: exact matches, single-level wildcards (&lt;code&gt;+&lt;&#x2F;code&gt;), and multi-level wildcards (&lt;code&gt;#&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; topicMatches&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;pattern&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; bool&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; pattern&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; topic&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; strings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HasSuffix&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;pattern&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        prefix&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; pattern&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;pattern&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; prefix&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; strings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HasPrefix&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;topic&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; prefix&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    patternParts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; strings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;pattern&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    topicParts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; strings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;topic&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Implementation continues...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The function splits topics into segments and compares them level by level. Special handling for &lt;code&gt;&#x2F;#&lt;&#x2F;code&gt; patterns ensures they match both the exact prefix and longer paths.&lt;&#x2F;p&gt;
&lt;p&gt;Testing wildcard matching requires comprehensive examples:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sensors&#x2F;+&#x2F;temperature&lt;&#x2F;code&gt; matches &lt;code&gt;sensors&#x2F;bedroom&#x2F;temperature&lt;&#x2F;code&gt; but not &lt;code&gt;sensors&#x2F;bedroom&#x2F;living-room&#x2F;temperature&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;sensors&#x2F;#&lt;&#x2F;code&gt; matches &lt;code&gt;sensors&lt;&#x2F;code&gt;, &lt;code&gt;sensors&#x2F;bedroom&lt;&#x2F;code&gt;, and &lt;code&gt;sensors&#x2F;bedroom&#x2F;temperature&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;+&#x2F;temperature&lt;&#x2F;code&gt; matches &lt;code&gt;bedroom&#x2F;temperature&lt;&#x2F;code&gt; but not &lt;code&gt;sensors&#x2F;bedroom&#x2F;temperature&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;concurrent-message-processing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#concurrent-message-processing&quot; aria-label=&quot;Anchor link for: concurrent-message-processing&quot;&gt;Concurrent message processing&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Message handlers run concurrently to prevent slow handlers from blocking others. Each incoming message spawns a goroutine that processes handlers matching the topic pattern.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;c &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Client&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; handleMessage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;publish&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;paho&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Publish&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; publish&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Topic&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    handlers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;findMatchingHandlers&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;topic&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; handler&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; range&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; handlers&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        currentHandler&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; handler&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Go&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; WithTopic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Background&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; topic&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; currentHandler&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; publish&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Payload&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;                &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Handle error appropriately&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;currentHandler&lt;&#x2F;code&gt; variable prevents closure capture issues where all goroutines might execute the same handler. The wait group tracks active handlers for graceful shutdown.&lt;&#x2F;p&gt;
&lt;p&gt;Context enhancement provides handlers with topic information without requiring additional parameters:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; WithTopic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;WithValue&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; topicKey&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; topic&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; GetTopic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    v&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; ok&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Value&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;topicKey&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ok&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; v&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;graceful-shutdown-and-cleanup&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#graceful-shutdown-and-cleanup&quot; aria-label=&quot;Anchor link for: graceful-shutdown-and-cleanup&quot;&gt;Graceful shutdown and cleanup&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Proper shutdown coordination ensures no messages are lost during application termination. The disconnect process waits for active handlers to complete before closing the connection.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;c &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Client&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Disconnect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; cancel&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;WithTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 20&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Second&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; cancel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    waitDone&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;chan&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    go&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Wait&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;        close&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;waitDone&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    select&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;waitDone&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Handlers completed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Done&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; errors&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;New&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;timeout waiting for handlers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;conn&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Disconnect&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The timeout prevents indefinite blocking if handlers don&#x27;t complete promptly. Applications can choose appropriate timeout values based on their handler behaviour.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;practical-usage-patterns&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#practical-usage-patterns&quot; aria-label=&quot;Anchor link for: practical-usage-patterns&quot;&gt;Practical usage patterns&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Real applications typically use multiple handlers for different message types. A home automation system might separate temperature, humidity, and motion sensor handlers:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;AddHandler&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sensors&#x2F;+&#x2F;temperature&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; handleTemperature&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;AddHandler&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sensors&#x2F;+&#x2F;humidity&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; handleHumidity&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;AddHandler&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;motion&#x2F;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; handleMotion&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;AddHandler&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;alerts&#x2F;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; handleAlerts&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Handler registration automatically subscribes to topics when the client is connected. This eliminates the common pattern of manual subscription after connection establishment.&lt;&#x2F;p&gt;
&lt;p&gt;Error handling in handlers should be defensive. Network issues, malformed payloads, or database connection problems shouldn&#x27;t crash the entire client:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; handleTemperature&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; payload&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; GetTopic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; reading&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; TemperatureReading&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; json&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unmarshal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;reading&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Printf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Invalid temperature data from &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; topic&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Don&amp;#39;t retry malformed data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; database&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Store&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;reading&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Printf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Failed to store temperature reading: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Retry database errors&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;testing-strategies&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#testing-strategies&quot; aria-label=&quot;Anchor link for: testing-strategies&quot;&gt;Testing strategies&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Testing MQTT clients requires either a test broker or careful mocking. The Eclipse Mosquitto project provides a public test broker at &lt;code&gt;test.mosquitto.org:1883&lt;&#x2F;code&gt; for development and testing.&lt;&#x2F;p&gt;
&lt;p&gt;Integration tests should verify handler registration, message routing, and connection recovery:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; TestHandlerRouting&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;testing&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    client&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; NewClient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; testConfig&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    require&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NoError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; received&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;AddHandler&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;test&#x2F;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; payload&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        received&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; append&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;received&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Publish&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;test&#x2F;sensor1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;data1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Publish&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;test&#x2F;sensor2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;data2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Verify received contains expected data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Unit tests for topic matching don&#x27;t require a broker connection and can run quickly:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; TestTopicMatching&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;testing&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    cases&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        pattern&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        expected&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;       bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sensors&#x2F;+&#x2F;temperature&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sensors&#x2F;bedroom&#x2F;temperature&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sensors&#x2F;+&#x2F;temperature&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sensors&#x2F;bedroom&#x2F;humidity&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sensors&#x2F;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sensors&#x2F;bedroom&#x2F;temperature&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; range&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; cases&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; topicMatches&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;pattern&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;topic&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        assert&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Equal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;expected&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; result&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;performance-considerations-and-optimisations&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#performance-considerations-and-optimisations&quot; aria-label=&quot;Anchor link for: performance-considerations-and-optimisations&quot;&gt;Performance considerations and optimisations&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Message throughput depends on handler performance and goroutine overhead. For high-volume scenarios, consider message batching or worker pools instead of spawning unlimited goroutines.&lt;&#x2F;p&gt;
&lt;p&gt;The handler registry uses a simple map with read-write mutex protection. Applications with thousands of topic patterns might benefit from more sophisticated data structures like prefix trees or regular expression compilation.&lt;&#x2F;p&gt;
&lt;p&gt;Connection configuration affects performance and reliability. Shorter keepalive intervals detect network issues faster but increase bandwidth usage. Longer retry delays reduce broker load during outages but slow recovery.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;security-and-authentication&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#security-and-authentication&quot; aria-label=&quot;Anchor link for: security-and-authentication&quot;&gt;Security and authentication&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Production deployments should use TLS encryption and authentication. MQTT supports username&#x2F;password authentication, client certificates, and OAuth tokens depending on broker configuration.&lt;&#x2F;p&gt;
&lt;p&gt;URL-based authentication configuration keeps credentials in one place:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; For username&#x2F;password authentication&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;brokerURL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; url&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;tcp:&#x2F;&#x2F;username:password@broker.example.com:1883&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; For TLS without authentication&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;brokerURL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; url&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;tls:&#x2F;&#x2F;broker.example.com:8883&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Client certificates require additional configuration in the autopaho client setup. OAuth token handling needs custom authentication callbacks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;extending-the-client&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#extending-the-client&quot; aria-label=&quot;Anchor link for: extending-the-client&quot;&gt;Extending the client&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The basic client provides a foundation for more sophisticated features. Message persistence can store and replay messages during connection outages. Message transformation can validate, filter, or modify payloads before handler execution.&lt;&#x2F;p&gt;
&lt;p&gt;Rate limiting prevents handlers from overwhelming downstream systems during message bursts. Circuit breakers can disable handlers that repeatedly fail, preventing cascading failures.&lt;&#x2F;p&gt;
&lt;p&gt;Metrics collection helps monitor client health. Track connection uptime, message rates, handler execution times, and error frequencies to understand system behaviour.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;common-pitfalls-and-solutions&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#common-pitfalls-and-solutions&quot; aria-label=&quot;Anchor link for: common-pitfalls-and-solutions&quot;&gt;Common pitfalls and solutions&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Handler goroutines without proper error handling can cause memory leaks through panic recovery. Always handle errors gracefully and avoid panics in handler code. I learned this the hard way when a malformed JSON payload from a failing Xiaomi sensor crashed my entire monitoring service.&lt;&#x2F;p&gt;
&lt;p&gt;Connection state races occur when multiple goroutines check &lt;code&gt;IsConnected()&lt;&#x2F;code&gt; simultaneously. The client&#x27;s internal locking prevents these issues, but application code should handle connection errors appropriately. During Home Assistant restarts, expect temporary connection failures and design handlers to gracefully handle them.&lt;&#x2F;p&gt;
&lt;p&gt;Topic subscription ordering matters for wildcard patterns. More specific patterns should be registered before general ones to ensure proper message routing precedence. In my setup, device-specific handlers like &lt;code&gt;energy&#x2F;evcharger&#x2F;power&lt;&#x2F;code&gt; register before the catch-all &lt;code&gt;energy&#x2F;#&lt;&#x2F;code&gt; pattern.&lt;&#x2F;p&gt;
&lt;p&gt;Reconnection loops can overwhelm brokers if retry delays are too short. The autopaho library includes exponential backoff, but applications should monitor connection attempt frequency. Home Assistant&#x27;s built-in Mosquitto addon handles reconnections well, but external brokers might have stricter connection limits.&lt;&#x2F;p&gt;
&lt;p&gt;Battery-powered Aqara devices introduce their own challenges. These sensors often send irregular updates or enter sleep modes that affect message timing. Design your handlers to accommodate missing data and use reasonable timeouts when waiting for sensor responses.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;integrating-with-home-assistant-architecture&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#integrating-with-home-assistant-architecture&quot; aria-label=&quot;Anchor link for: integrating-with-home-assistant-architecture&quot;&gt;Integrating with Home Assistant architecture&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;My MQTT client sits alongside Home Assistant rather than replacing it, handling custom analytics and backup monitoring that complement the main automation system. Home Assistant manages device discovery, user interfaces, and real-time automation, whilst my Go services handle historical analysis, custom alerting, and data export.&lt;&#x2F;p&gt;
&lt;p&gt;This separation works well because each system excels at different tasks. Home Assistant&#x27;s visual interface makes it perfect for configuring automation rules and monitoring device status. Go services excel at data processing, custom calculations, and integration with external systems like energy supplier APIs.&lt;&#x2F;p&gt;
&lt;p&gt;The MQTT broker becomes the coordination layer between systems. Home Assistant publishes state changes and device readings, my Go services process this data and publish derived metrics, and both systems can subscribe to each other&#x27;s topics for coordination.&lt;&#x2F;p&gt;
&lt;p&gt;Within my home lab I run Home Assistant in a VM on a thin pc and my Go services run in separate VMs. If a system needs maintenance, the others can continue operating independently, with MQTT message retention ensuring no data loss during brief outages.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Building an MQTT client wrapper teaches you the protocol&#x27;s subtleties whilst providing exactly the functionality your application needs. The combination of automatic reconnection, concurrent message processing, and graceful shutdown handling creates a robust foundation for IoT and messaging applications.&lt;&#x2F;p&gt;
&lt;p&gt;The complete implementation demonstrates Go&#x27;s strengths for network programming: excellent concurrency primitives, clear error handling, and strong typing that prevents common messaging bugs.&lt;&#x2F;p&gt;
&lt;p&gt;Start with the basic client and extend it based on your specific requirements. Monitor its behaviour in production and adjust timeouts, retry logic, and error handling based on real usage patterns.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Go 1.25&#x27;s new JSON encoding package</title>
          <pubDate>Thu, 11 Sep 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/go-125/go-125-new-json-encoding-pkg/</link>
          <guid>https://aran.dev/posts/go-125/go-125-new-json-encoding-pkg/</guid>
          <description xml:base="https://aran.dev/posts/go-125/go-125-new-json-encoding-pkg/">&lt;p&gt;&lt;strong&gt;⚠️ Important: The JSON v2 packages are experimental and their APIs may change in future releases. Use &lt;code&gt;GOEXPERIMENT=jsonv2&lt;&#x2F;code&gt; to enable these features for testing.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Go 1.25 arrived on 12th August 2025. This release included no language changes compared to recent versions. The most notable addition however was the inroduction of an experimental JSON implementation that improves encoding and decoding performance whilst offering more flexible JSON data handling.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-json-v2-matters&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-json-v2-matters&quot; aria-label=&quot;Anchor link for: why-json-v2-matters&quot;&gt;Why JSON v2 matters&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;JSON processing creates significant overhead in many Go applications. The new experimental package addresses this directly. You can enable it with &lt;code&gt;GOEXPERIMENT=jsonv2&lt;&#x2F;code&gt; at build time.&lt;&#x2F;p&gt;
&lt;p&gt;The experimental JSON v2 package addresses several long-standing issues with the current &lt;code&gt;encoding&#x2F;json&lt;&#x2F;code&gt; implementation. These include performance bottlenecks in both marshalling and unmarshalling operations, inconsistent behaviour around edge cases like duplicate keys and case sensitivity, limited flexibility in controlling JSON processing behaviour, and memory allocation patterns that impact garbage collection performance.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enabling-the-experimental-json-v2&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#enabling-the-experimental-json-v2&quot; aria-label=&quot;Anchor link for: enabling-the-experimental-json-v2&quot;&gt;Enabling the experimental JSON v2&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Go 1.25 includes the new experimental JSON implementation, which you enable by setting the environment variable &lt;code&gt;GOEXPERIMENT=jsonv2&lt;&#x2F;code&gt; at build time.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Enable the experimental JSON v2 implementation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;GOEXPERIMENT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;j&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;v&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; go&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; your-app.go&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Or set it for your entire build process&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;export&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; GOEXPERIMENT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;jsonv2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;go&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; your-app.go&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Alternatively, you can use the build tag:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Using build tag instead of environment variable&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;go&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;tags=goexperiment.jsonv2&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; your-app.go&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When enabled, this experiment has two major effects. The existing &lt;code&gt;encoding&#x2F;json&lt;&#x2F;code&gt; package uses the new JSON implementation under the hood whilst maintaining full API compatibility. New packages become available: &lt;code&gt;encoding&#x2F;json&#x2F;v2&lt;&#x2F;code&gt; and &lt;code&gt;encoding&#x2F;json&#x2F;jsontext&lt;&#x2F;code&gt; for more advanced use cases.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;architecture-separation-of-concerns&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#architecture-separation-of-concerns&quot; aria-label=&quot;Anchor link for: architecture-separation-of-concerns&quot;&gt;Architecture: separation of concerns&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;encoding&#x2F;json&#x2F;jsontext&lt;&#x2F;code&gt; provides low-level, high-performance JSON tokenizers (Decoder) and encoders (Encoder). This package focuses solely on the syntactic aspects of JSON.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;encoding&#x2F;json&#x2F;v2&lt;&#x2F;code&gt; handles the semantic conversion between Go types and JSON values, built upon jsontext. This separation of syntactic and semantic analysis improves code clarity, enables better performance through specialised low-level operations, and provides greater flexibility for custom JSON processing needs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;internal-modernisation-of-json-v1&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#internal-modernisation-of-json-v1&quot; aria-label=&quot;Anchor link for: internal-modernisation-of-json-v1&quot;&gt;Internal modernisation of JSON v1&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A crucial aspect of the JSON v2 experiment is that &lt;strong&gt;the existing &lt;code&gt;encoding&#x2F;json&lt;&#x2F;code&gt; package is internally modernised to use the new v2 infrastructure under the hood&lt;&#x2F;strong&gt;. This means when you enable &lt;code&gt;GOEXPERIMENT=jsonv2&lt;&#x2F;code&gt;, all your existing JSON code benefits from the new technology whilst maintaining full API compatibility.&lt;&#x2F;p&gt;
&lt;p&gt;This internal modernisation approach provides several benefits:&lt;&#x2F;p&gt;
&lt;p&gt;You can incrementally adopt v2 features whilst keeping existing code unchanged. Options allow you to configure behaviour anywhere from entirely v1-compatible to entirely v2-style, enabling smooth transitions. This means you&#x27;re not forced into an all-or-nothing migration approach.&lt;&#x2F;p&gt;
&lt;p&gt;As new features are added to v2, they automatically become available in v1. For example, v2&#x27;s new struct tag options like &lt;code&gt;inline&lt;&#x2F;code&gt; and &lt;code&gt;format&lt;&#x2F;code&gt;, plus the more performant &lt;code&gt;MarshalJSONTo&lt;&#x2F;code&gt; and &lt;code&gt;UnmarshalJSONFrom&lt;&#x2F;code&gt; interface methods, become available to existing v1 code without any changes required on your part.&lt;&#x2F;p&gt;
&lt;p&gt;Having a single underlying implementation also reduces the maintenance burden significantly. Bug fixes, performance improvements, and new functionality benefit both API versions simultaneously, so you get the benefits regardless of which API you&#x27;re using.&lt;&#x2F;p&gt;
&lt;p&gt;This approach ensures you can test the new implementation with your existing codebase simply by enabling the experiment, without changing any code.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;performance-improvements-the-numbers&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#performance-improvements-the-numbers&quot; aria-label=&quot;Anchor link for: performance-improvements-the-numbers&quot;&gt;Performance improvements: the numbers&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;JSONv2 delivers significant speed improvements across different operations. The official blog reports unmarshal performance improvements of up to 10x, with marshal performance roughly at parity. For detailed benchmarks, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-json-experiment&#x2F;jsonbench#readme&quot;&gt;go-json-experiment&#x2F;jsonbench&lt;&#x2F;a&gt; repository provides comprehensive analysis showing performance gains that vary by operation type:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Unmarshalling Performance:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-json-experiment&#x2F;jsonbench#unmarshal-into-concrete-types&quot;&gt;Concrete types&lt;&#x2F;a&gt;: 2.7x to 10.2x faster than JSONv1&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-json-experiment&#x2F;jsonbench#unmarshal-into-interfaces&quot;&gt;Interface types&lt;&#x2F;a&gt; (&lt;code&gt;any&lt;&#x2F;code&gt;, &lt;code&gt;map[string]any&lt;&#x2F;code&gt;, &lt;code&gt;[]any&lt;&#x2F;code&gt;): 2.3x to 5.7x faster than JSONv1&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-json-experiment&#x2F;jsonbench#unmarshal-into-jsontextvalue&quot;&gt;Raw JSON values&lt;&#x2F;a&gt;: 10.2x to 21.1x faster than JSONv1&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Marshalling Performance:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-json-experiment&#x2F;jsonbench#marshal-from-interfaces&quot;&gt;Interface types&lt;&#x2F;a&gt;: 1.6x to 3.6x faster than JSONv1&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-json-experiment&#x2F;jsonbench#marshal-from-jsontextvalue&quot;&gt;Raw JSON values&lt;&#x2F;a&gt;: 5.6x to 12.0x faster than JSONv1&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-json-experiment&#x2F;jsonbench#marshal-from-concrete-types&quot;&gt;Concrete types&lt;&#x2F;a&gt;: 1.4x faster to 1.2x slower than JSONv1&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;em&gt;Note: These specific benchmark figures are from the external jsonbench repository. Individual results may vary based on your specific use cases and data structures.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The performance improvements stem from the architectural separation between syntax parsing and semantic marshalling. The low-level &lt;code&gt;jsontext&lt;&#x2F;code&gt; package handles tokenisation efficiently, whilst the higher-level package focuses purely on type conversion. This eliminates redundant work that occurred in the monolithic v1 implementation.&lt;&#x2F;p&gt;
&lt;p&gt;Memory allocation patterns also improve dramatically. The new implementation reduces heap allocations during unmarshalling, which decreases garbage collection frequency. This leads to more predictable performance characteristics across different workload types.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enhanced-streaming-performance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#enhanced-streaming-performance&quot; aria-label=&quot;Anchor link for: enhanced-streaming-performance&quot;&gt;Enhanced streaming performance&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;You can achieve additional performance benefits by switching from regular MarshalJSON and UnmarshalJSON to their streaming alternatives (MarshalJSONTo and UnmarshalJSONFrom). The Go team reports this converts certain O(n²) runtime scenarios into O(n). Switching from UnmarshalJSON to UnmarshalJSONFrom in the k8s OpenAPI spec improved performance by orders of magnitude.&lt;&#x2F;p&gt;
&lt;p&gt;The streaming API works by processing JSON data directly through encoders and decoders rather than creating intermediate byte slices. This eliminates copying overhead and reduces memory pressure for large documents. Custom types can implement streaming methods to take advantage of this approach.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;behavioural-changes-and-new-options&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#behavioural-changes-and-new-options&quot; aria-label=&quot;Anchor link for: behavioural-changes-and-new-options&quot;&gt;Behavioural changes and new options&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;JSON v2 introduces several important behavioural changes that improve consistency and security.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;case-sensitivity&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#case-sensitivity&quot; aria-label=&quot;Anchor link for: case-sensitivity&quot;&gt;Case sensitivity&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;JSONv1 matches struct fields case-insensitively when unmarshalling. A JSON field called &quot;firstname&quot; would successfully map to a Go struct field named &quot;FirstName&quot;. JSONv2 changes this behaviour to case-sensitive matching by default, requiring exact case matches between JSON keys and struct field names or json tags.&lt;&#x2F;p&gt;
&lt;p&gt;This change prevents potential security issues where different case variations might be processed unexpectedly. You can restore the original case-insensitive behaviour using the &lt;code&gt;MatchCaseInsensitiveNames(true)&lt;&#x2F;code&gt; option if needed for backward compatibility.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;duplicate-key-handling&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#duplicate-key-handling&quot; aria-label=&quot;Anchor link for: duplicate-key-handling&quot;&gt;Duplicate key handling&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;JSONv1 silently allows duplicate names in JSON objects, keeping the value from the last occurrence. This behaviour can create security vulnerabilities where malicious JSON might include duplicate keys to override expected values.&lt;&#x2F;p&gt;
&lt;p&gt;JSONv2 rejects duplicate object names with an error by default. This stricter parsing helps prevent security issues but may break code that previously relied on duplicate key handling. The &lt;code&gt;AllowDuplicateNames(true)&lt;&#x2F;code&gt; option restores the original permissive behaviour when necessary.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;nil-slice-and-map-handling&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#nil-slice-and-map-handling&quot; aria-label=&quot;Anchor link for: nil-slice-and-map-handling&quot;&gt;Nil slice and map handling&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The marshalling behaviour for nil slices and maps changes between versions. JSONv1 marshals nil slices and maps as JSON &lt;code&gt;null&lt;&#x2F;code&gt; values. JSONv2 marshals nil slices as empty arrays (&lt;code&gt;[]&lt;&#x2F;code&gt;) and nil maps as empty objects (&lt;code&gt;{}&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;This change creates more consistent JSON output where the structure remains predictable regardless of whether collections are nil or empty. You can restore the original null-marshalling behaviour using &lt;code&gt;FormatNilSliceAsNull(true)&lt;&#x2F;code&gt; and &lt;code&gt;FormatNilMapAsNull(true)&lt;&#x2F;code&gt; options.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;flexible-options-system&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#flexible-options-system&quot; aria-label=&quot;Anchor link for: flexible-options-system&quot;&gt;Flexible options system&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;JSON v2 provides a comprehensive options system for controlling marshalling and unmarshalling behaviour. The options system allows fine-grained control over parsing strictness, formatting preferences, and compatibility modes. You can combine multiple options using &lt;code&gt;JoinOptions&lt;&#x2F;code&gt; to create custom configurations for different use cases.&lt;&#x2F;p&gt;
&lt;p&gt;Available options include multiline formatting for pretty-printed output, legacy semantics compatibility for smooth transitions from v1, HTML escaping control, and various parsing strictness settings. This flexibility ensures you can configure JSONv2 to match your specific requirements whilst taking advantage of the performance improvements.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;caller-specified-customisation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#caller-specified-customisation&quot; aria-label=&quot;Anchor link for: caller-specified-customisation&quot;&gt;Caller-specified customisation&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Beyond the options system, JSON v2 introduces a powerful feature allowing callers to specify custom JSON representations for any type, where caller-specified functions take precedence over type-defined methods or default behaviour.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;package&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;encoding&#x2F;json&#x2F;v2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;encoding&#x2F;json&#x2F;jsontext&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;time&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Custom marshaler for time.Time that formats as Unix timestamp&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    marshalers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; json&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MarshalFunc&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Time&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; t&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unix&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    data&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Name&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Time&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Time&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;timestamp&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;example&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Time&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Now&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Use custom marshaler&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    result&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; json&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Marshal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; json&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;WithMarshalers&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;marshalers&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;        panic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Output: {&amp;quot;name&amp;quot;:&amp;quot;example&amp;quot;,&amp;quot;timestamp&amp;quot;:1725897600}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This feature is particularly powerful when you need to define JSON behaviour for types you don&#x27;t control, such as protocol buffer messages or external library types. It&#x27;s also useful when different parts of your application need different JSON representations for the same types based on context. You can even override the default JSON representation of any type, including built-in types, without modifying the type definition itself.&lt;&#x2F;p&gt;
&lt;p&gt;The streaming variants (&lt;code&gt;MarshalToFunc&lt;&#x2F;code&gt; and &lt;code&gt;UnmarshalFromFunc&lt;&#x2F;code&gt;) provide even better performance by working directly with encoders and decoders. In practice, this becomes especially valuable when integrating with third-party APIs that expect specific JSON formats for common types like timestamps or UUIDs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;real-world-impact&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#real-world-impact&quot; aria-label=&quot;Anchor link for: real-world-impact&quot;&gt;Real-world impact&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;For applications processing significant amounts of JSON data, these improvements deliver measurable benefits. Reduced latency in API responses becomes particularly noticeable under load. Lower memory pressure from reduced allocations means better resource utilisation in constrained environments. Improved security through stricter parsing defaults helps prevent issues with malformed or malicious JSON inputs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;looking-forward&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#looking-forward&quot; aria-label=&quot;Anchor link for: looking-forward&quot;&gt;Looking forward&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;JSON v2 is expected to potentially become the default in Go 1.26, though this depends on feedback from the current experiment. As the official blog states, the outcome may result in &quot;anything from abandonment of the effort, to adoption as stable packages of Go 1.26.&quot; This timeline provides opportunity to test and provide feedback whilst the API continues to evolve.&lt;&#x2F;p&gt;
&lt;p&gt;The design of &lt;code&gt;encoding&#x2F;json&#x2F;v2&lt;&#x2F;code&gt; will continue evolving based on community feedback. Developers should try the new API and provide input on the proposal issue.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Go 1.25&#x27;s experimental JSON v2 package delivers substantial performance improvements alongside enhanced security through stricter defaults. The architectural separation between syntactic parsing and semantic marshalling provides a solid foundation for future enhancements.&lt;&#x2F;p&gt;
&lt;p&gt;For developers working with APIs and data-intensive applications, testing with &lt;code&gt;GOEXPERIMENT=jsonv2&lt;&#x2F;code&gt; provides a straightforward way to evaluate these improvements. As the experiment progresses towards potential adoption in Go 1.26, now is the time to explore these capabilities and provide feedback that will shape the future of JSON processing in Go.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Go 1.24&#x27;s new tool directive in go.mod</title>
          <pubDate>Fri, 31 Jan 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/go-124/go-124-new-tool-directive/</link>
          <guid>https://aran.dev/posts/go-124/go-124-new-tool-directive/</guid>
          <description xml:base="https://aran.dev/posts/go-124/go-124-new-tool-directive/">&lt;p&gt;Go 1.24 introduces a fantastic new feature with the tool directive, simplifying how you manage project dependencies by integrating tools directly into your &lt;code&gt;go.mod&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;p&gt;In distributed systems with numerous microservices, teams often prioritise consistency over progression, leading to tool version stagnation. The challenge of coordinating tool updates across a large estate of services frequently results in teams sticking to older, familiar versions rather than adopting beneficial new features. The tool directive addresses this by making tool version management explicit and trackable within each project&#x27;s &lt;code&gt;go.mod&lt;&#x2F;code&gt;. This visibility helps teams make informed decisions about updates and reduces the friction of rolling out new tool versions across services.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;understanding-the-tool-directive&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#understanding-the-tool-directive&quot; aria-label=&quot;Anchor link for: understanding-the-tool-directive&quot;&gt;Understanding the Tool Directive&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Prior to Go 1.24, developers faced challenges in ensuring all team members used consistent tool versions. The new tool directive simplifies this process by encapsulating tool requirements within your module&#x27;s configuration.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Example with a typical &lt;code&gt;go.mod&lt;&#x2F;code&gt; (Go 1.23):&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;module&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; github&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;com&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;aranw&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;go&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;124&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;tool&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;directive&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;go&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;23&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To incorporate the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dmarkham&#x2F;enumer&quot;&gt;github.com&#x2F;dmarkham&#x2F;enumer&lt;&#x2F;a&gt; tool, you&#x27;d previously run:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;go&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; install&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; github.com&#x2F;dmarkham&#x2F;enumer@latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With Go 1.24, add the tool to your &lt;code&gt;go.mod&lt;&#x2F;code&gt; using:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;go&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; get&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;tool&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; github.com&#x2F;dmarkham&#x2F;enumer@latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Updated &lt;code&gt;go.mod&lt;&#x2F;code&gt;:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;module&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; github&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;com&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;aranw&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;go&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;124&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;tool&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;directive&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;go&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;24&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;tool&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; github&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;com&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;dmarkham&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;enumer&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	github&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;com&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;dmarkham&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;enumer&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; v1&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;5&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; indirect&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we have the &lt;code&gt;enumer&lt;&#x2F;code&gt; tool specified in our &lt;code&gt;go.mod&lt;&#x2F;code&gt; file. It is available for us to use in our project via the Go toolchain without needing separate installation on our system. This is a great improvement for teams working on projects that require specific tools to be installed. It ensures that everyone is using the same version of the tools and reduces the risk of version drift.&lt;&#x2F;p&gt;
&lt;p&gt;Version drift is a common issue with distributed systems. So much so that teams will often stick with older versions of tools to avoid the hassle of updating across multiple services. The tool directive in Go 1.24 simplifies this process by making tool version management explicit and trackable within each project&#x27;s &lt;code&gt;go.mod&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To call the &lt;code&gt;enumer&lt;&#x2F;code&gt; tool in your project, you can use the following command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;go&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; tool&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; enumer&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;type=Direction&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;utilizing-the-tool-directive-in-code-generation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#utilizing-the-tool-directive-in-code-generation&quot; aria-label=&quot;Anchor link for: utilizing-the-tool-directive-in-code-generation&quot;&gt;Utilizing the Tool Directive in Code Generation&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The addition of the tool directive enhances code generation workflows by eliminating manual tool installations. Traditionally, you&#x27;d use:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;go:generate enumer -type=Direction -json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Relying on the tool being installed on your system.&lt;&#x2F;p&gt;
&lt;p&gt;Now, we can leverage Go&#x27;s built-in tool execution with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;go:generate go tool enumer -type=Direction -json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This approach executes the specified tool using the one defined in your &lt;code&gt;go.mod&lt;&#x2F;code&gt;, ensuring consistency across your development environment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;under-the-hood-mechanics&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#under-the-hood-mechanics&quot; aria-label=&quot;Anchor link for: under-the-hood-mechanics&quot;&gt;Under-the-Hood Mechanics&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;When you include a tool via the directive, Go&#x27;s module system handles its installation. The tool is stored in your project&#x27;s &lt;code&gt;go.mod&lt;&#x2F;code&gt; file, making it available for execution without separate installations. This mechanism prevents version drift and dependency conflicts, ensuring reliability across team members.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;best-practices-and-considerations&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#best-practices-and-considerations&quot; aria-label=&quot;Anchor link for: best-practices-and-considerations&quot;&gt;Best Practices and Considerations&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The tool directive helps maintain consistent development environments through centralised tool management in &lt;code&gt;go.mod&lt;&#x2F;code&gt;. This approach prevents version drift whilst eliminating the need for manual tool installation across team members. By leveraging Go&#x27;s caching system for efficient tool retrieval and execution, your team can focus on development rather than tool management.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The introduction of the &#x27;tool&#x27; directive in Go 1.24 represents a substantial advancement in managing dependencies within Go modules. By incorporating this feature, developers can ensure that dependencies remain current and consistent across different environments, thereby enhancing development efficiency and minimising version discrepancies between team members.&lt;&#x2F;p&gt;
&lt;p&gt;In summary, integrating the &#x27;tool&#x27; directive is a recommended best practice for modern Go projects aiming to maintain control over their dependencies. Following the guidelines outlined above will enable developers to fully utilise this powerful new feature and ensure their projects remain robust and scalable.&lt;&#x2F;p&gt;
&lt;div class=&quot;note-container&quot;&gt;
    
            &lt;div class=&quot;note-header&quot;&gt;
                
                    &lt;div class=&quot;note-icon&quot;&gt;
                        &lt;p&gt;Info&lt;&#x2F;p&gt;

                    &lt;&#x2F;div&gt;
                
            &lt;&#x2F;div&gt;
            &lt;div class=&quot;note-content&quot;&gt;
                &lt;p&gt;Checkout my follow up article on how to separate your tool dependencies with a separate &lt;code&gt;go.mod&lt;&#x2F;code&gt; file &lt;a href=&quot;&#x2F;posts&#x2F;go-124&#x2F;fixing-go-124-tool-directive-biggest-problem&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

            &lt;&#x2F;div&gt;
        
    &lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Building an HTTP server in Go with zero dependencies</title>
          <pubDate>Sun, 19 Jan 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/go-no-dependencies/go-no-dependencies-zero-dependency-http-server/</link>
          <guid>https://aran.dev/posts/go-no-dependencies/go-no-dependencies-zero-dependency-http-server/</guid>
          <description xml:base="https://aran.dev/posts/go-no-dependencies/go-no-dependencies-zero-dependency-http-server/">&lt;p&gt;Go 1.22 brought enhancements to the &lt;code&gt;net&#x2F;http&lt;&#x2F;code&gt; package&#x27;s router. Enabling method matching and wildcard patterns for HTTP routes. The standard library now provides a robust router that can handle most use cases, reducing the need for third-party routing libraries.&lt;&#x2F;p&gt;
&lt;p&gt;Zero dependencies or minimal dependencies are a common requirement for many projects. In this post we will explore how we can remove one more dependency from our Go projects by implementing a zero dependency HTTP server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prior-to-go-1-22&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#prior-to-go-1-22&quot; aria-label=&quot;Anchor link for: prior-to-go-1-22&quot;&gt;Prior to Go 1.22&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Before Go 1.22, the standard library&#x27;s &lt;code&gt;net&#x2F;http&lt;&#x2F;code&gt; package did not provide a router. Developers often reached for third-party libraries such as &lt;code&gt;gorilla&#x2F;mux&lt;&#x2F;code&gt; or &lt;code&gt;chi&lt;&#x2F;code&gt; to handle routing in their applications. These libraries provide additional features such as middleware, sub-routers, and more. For example, with chi you can define routes with method verbs &lt;code&gt;mux.Get(&quot;&#x2F;users&quot;, handleUsers)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;net&#x2F;http&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;github.com&#x2F;go-chi&#x2F;chi&#x2F;v5&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; handleUser&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ResponseWriter&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Request&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Handler implementation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; createUser&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ResponseWriter&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Request&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Handler implementation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; chi&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewRouter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;users&#x2F;{id}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; handleUser&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;users&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; createUser&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ListenAndServe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;:8080&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Go 1.22 introduced HTTP method verbs in routes for the standard library HTTP package e.g. &lt;code&gt;http.HandleFunc(&quot;GET &#x2F;users&quot;, handleUsers)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A key difference between the standard library and third-party libraries is the lack of middleware support. Middleware is a common pattern in web applications to add additional functionality to HTTP handlers. For example, logging, authentication, and error handling. Third party libraries such as &lt;code&gt;gorilla&#x2F;mux&lt;&#x2F;code&gt; and &lt;code&gt;chi&lt;&#x2F;code&gt; provide middleware support out of the box.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-a-zero-dependency-http-server&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#building-a-zero-dependency-http-server&quot; aria-label=&quot;Anchor link for: building-a-zero-dependency-http-server&quot;&gt;Building a Zero Dependency HTTP Server&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;To build our zero dependency HTTP server we will use the standard library&#x27;s &lt;code&gt;net&#x2F;http&lt;&#x2F;code&gt; package. We will create a simple HTTP server that listens on port &lt;code&gt;8080&lt;&#x2F;code&gt; and responds with a &lt;code&gt;Hello, World!&lt;&#x2F;code&gt; message.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;package&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;context&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;encoding&#x2F;json&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;log&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;net&#x2F;http&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;os&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;os&#x2F;signal&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;time&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Create new mux for routing&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    mux&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewServeMux&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Add routes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    mux&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandleFunc&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;GET &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ResponseWriter&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Request&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        w&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Header&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Set&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Content-Type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        w&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;WriteHeader&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;StatusOK&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        json&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewEncoder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;w&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Encode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello, World!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Create the HTTP server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    srv&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Server&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Addr&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;:8080&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Handler&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; mux&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Start server in a goroutine to allow for graceful shutdown&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    go&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Printf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;server starting on :8080&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; srv&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ListenAndServe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ErrServerClosed&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Fatalf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;server error: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Wait for interrupt signal to gracefully shut down the server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    quit&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;chan&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; os&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Signal&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    signal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Notify&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;quit&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; os&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Interrupt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    &amp;lt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;quit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Create a deadline for shutdown&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; cancel&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;WithTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Background&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Second&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; cancel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; srv&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Shutdown&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Fatal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;server forced to shutdown:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the above code snippet we have defined a simple HTTP server that listens on port &lt;code&gt;8080&lt;&#x2F;code&gt;. So far it looks very similar to our previous third party solution using Chi. The key difference is that we are now using the standard library&#x27;s &lt;code&gt;http.ServeMux&lt;&#x2F;code&gt; to handle routing.&lt;&#x2F;p&gt;
&lt;p&gt;The server implementation includes graceful shutdown handling, which is important for production services. When the server receives an interrupt signal (Ctrl+C), it:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Stops accepting new requests&lt;&#x2F;li&gt;
&lt;li&gt;Allows in-flight requests to complete&lt;&#x2F;li&gt;
&lt;li&gt;Times out after 10 seconds if requests haven&#x27;t completed&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This ensures that requests are handled properly even when the server is shutting down, rather than abruptly terminating connections.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;adding-routes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#adding-routes&quot; aria-label=&quot;Anchor link for: adding-routes&quot;&gt;Adding Routes&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;To add routes to our server we can use the &lt;code&gt;http.ServeMux&lt;&#x2F;code&gt;&#x27;s &lt;code&gt;HandleFunc&lt;&#x2F;code&gt; method. For example, to add a route that responds with a &lt;code&gt;Hello, World!&lt;&#x2F;code&gt; message:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;mux&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandleFunc&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;GET &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ResponseWriter&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Request&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    w&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Header&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Set&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Content-Type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    w&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;WriteHeader&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;StatusOK&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    json&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewEncoder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;w&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Encode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello, World!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In our example we created a route that responds to &lt;code&gt;GET &#x2F;&lt;&#x2F;code&gt; requests with a &lt;code&gt;Hello, World!&lt;&#x2F;code&gt; message. The &lt;code&gt;HandleFunc&lt;&#x2F;code&gt; method takes a string that defines the HTTP method and path. The string is split by a space to separate the method and path. The second argument is a function that takes an &lt;code&gt;http.ResponseWriter&lt;&#x2F;code&gt; and &lt;code&gt;http.Request&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The router also supports path parameters. For example, we can capture dynamic values from the URL:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; User&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    ID&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;   string&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; `&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;json:&amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;mux&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandleFunc&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;GET &#x2F;users&#x2F;{id}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ResponseWriter&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Request&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Extract the id parameter from the URL&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    userID&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;PathValue&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;id&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Create a user object with the ID from the URL&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    user&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; User&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        ID&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;   userID&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Set response headers and encode JSON response&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    w&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Header&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Set&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Content-Type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    w&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;WriteHeader&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;StatusOK&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    json&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewEncoder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;w&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Encode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the above example, we&#x27;re using Go 1.22&#x27;s new path parameter syntax &lt;code&gt;{id}&lt;&#x2F;code&gt; to capture a variable part of the URL. When a request is made to &lt;code&gt;&#x2F;users&#x2F;123&lt;&#x2F;code&gt;, the &lt;code&gt;PathValue()&lt;&#x2F;code&gt; method makes it easy to extract the &lt;code&gt;id&lt;&#x2F;code&gt; value without any string manipulation or regular expressions. This is a significant improvement over the old router where you&#x27;d need to parse URL segments manually or rely on third-party libraries.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;PathValue()&lt;&#x2F;code&gt; method returns an empty string if the parameter isn&#x27;t found, making it safe to use without additional error checking for parameter existence. This gives us a clean way to handle dynamic routes while keeping our handler code simple and readable.&lt;&#x2F;p&gt;
&lt;p&gt;For more complex routing patterns, you can use wildcard patterns to match multiple segments in a URL. For example, to match all requests to &lt;code&gt;&#x2F;static&#x2F;...&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Example wildcard pattern&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;mux&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandleFunc&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;GET &#x2F;static&#x2F;...&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ResponseWriter&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Request&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Handle static files&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;...&lt;&#x2F;code&gt; wildcard at the end of a pattern matches the remainder of the URL path, allowing you to create flexible routing patterns. For example, &lt;code&gt;&#x2F;static&#x2F;...&lt;&#x2F;code&gt; will match any path under &lt;code&gt;&#x2F;static&#x2F;&lt;&#x2F;code&gt;, making it perfect for serving static files or handling nested routes. Wildcard patterns can only appear at the end of a pattern.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;middleware&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#middleware&quot; aria-label=&quot;Anchor link for: middleware&quot;&gt;Middleware&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Middleware is a common pattern in web applications to add functionality to HTTP handlers. Common use cases for middleware include logging, authentication, and error handling among others. In the absence of a third-party router, we will need to implement our own set of helper functions to provide middleware functionality.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Middleware is a function that wraps an http.HandlerFunc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Middleware&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandlerFunc&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandlerFunc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; WithLogging logs the request method and path along with the time taken&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; WithLogging&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandlerFunc&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandlerFunc&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ResponseWriter&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Request&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        start&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Now&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;        next&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;w&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Printf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; %s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; took &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Method&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;URL&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Path&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Since&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;start&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; WithRecovery catches panics in request handling&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; WithRecovery&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandlerFunc&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandlerFunc&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ResponseWriter&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Request&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; recover&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Printf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;panic: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;w&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Internal Server Error&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;StatusInternalServerError&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;        next&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;w&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this snippet we define a &lt;code&gt;Middleware&lt;&#x2F;code&gt; type that wraps an &lt;code&gt;http.HandlerFunc&lt;&#x2F;code&gt;. We have also defined two common middlewares &lt;code&gt;WithLogging&lt;&#x2F;code&gt; and &lt;code&gt;WithRecovery&lt;&#x2F;code&gt;. &lt;code&gt;WithLogging&lt;&#x2F;code&gt; logs the request method and path along with the time taken to process the request. &lt;code&gt;WithRecovery&lt;&#x2F;code&gt; catches panics in the request handling and returns an &lt;code&gt;Internal Server Error&lt;&#x2F;code&gt; response.&lt;&#x2F;p&gt;
&lt;p&gt;Using the middleware is simple, we just need to wrap our handler functions with the middleware we want to apply:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;mux&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandleFunc&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;GET &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; WithLogging&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;WithRecovery&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ResponseWriter&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Request&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    w&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Header&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Set&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Content-Type&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;application&#x2F;json&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    w&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;WriteHeader&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;StatusOK&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    json&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewEncoder&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;w&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Encode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello, World!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This demonstrates how you can achieve the common middleware pattern without having to depend on external third-party router libraries. The middleware functions are simple to test and compose, allowing us to add cross-cutting concerns to our handlers while keeping the core request handling logic clean.&lt;&#x2F;p&gt;
&lt;p&gt;A common helper function for chaining middleware is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Chain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;h&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandlerFunc&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; m&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ...&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Middleware&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; http&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandlerFunc&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;m&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;--&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        h&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; m&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;h&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; h&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This function takes a handler function and a list of middleware functions and chains them together. This allows you to apply multiple middleware functions to a single handler function.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Usage example&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;mux&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;HandleFunc&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;GET &#x2F;users&#x2F;{id}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Chain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    handleUser&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    WithLogging&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    WithRecovery&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When defining a chain of middleware functions, the order in which they are applied matters. Middleware functions are applied in the order they are passed to the &lt;code&gt;Chain&lt;&#x2F;code&gt; function. For example, in our usage example above, logging will be performed before recovery.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Go 1.22&#x27;s enhanced HTTP router brings many features we previously relied on third-party libraries for directly into the standard library. With method-based routing, path parameters, and wildcard support, we can now build robust HTTP servers with zero external dependencies.&lt;&#x2F;p&gt;
&lt;p&gt;While libraries like Chi and Gorilla Mux offer additional features, the standard library&#x27;s router is now capable of handling many common use cases. This means:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Fewer dependencies to manage&lt;&#x2F;li&gt;
&lt;li&gt;No version compatibility concerns&lt;&#x2F;li&gt;
&lt;li&gt;Smaller binary sizes&lt;&#x2F;li&gt;
&lt;li&gt;Code that&#x27;s more likely to remain stable over time&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For simple to medium complexity HTTP servers, the standard library solution demonstrated in this post might be all you need. However, if your application requires more advanced routing features like subrouters, regular expression matching, or extensive middleware chains, then a third-party router might still be the better choice.&lt;&#x2F;p&gt;
&lt;p&gt;Consider starting with the standard library approach and only reaching for external dependencies when you have specific requirements that aren&#x27;t met by &lt;code&gt;net&#x2F;http&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Processing UK rail data in real-time</title>
          <pubDate>Fri, 10 Jan 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/processing-uk-rail-data-in-real-time/</link>
          <guid>https://aran.dev/posts/processing-uk-rail-data-in-real-time/</guid>
          <description xml:base="https://aran.dev/posts/processing-uk-rail-data-in-real-time/">&lt;p&gt;Railway systems generate an endless stream of real-time data that needs to be captured, processed, and stored efficiently. When I was approached for a freelance project to build a Kafka consumer in Go using franz-go and integrate it with PostgreSQL, I knew this would be an interesting challenge. The result was a service designed to reliably consume real-time train information and persist it into a database. What started as a straightforward Kafka consumer evolved into a robust system, capable of handling millions of daily messages about train movements, schedules, and disruptions across the rail network. This project demonstrates modern Go service architecture, robust data processing patterns, and integration with industry-standard messaging and storage systems.&lt;&#x2F;p&gt;
&lt;p&gt;Working with real-time railway data presents unique challenges. Trains don&#x27;t always run according to schedule, and tracking these deviations requires a system that&#x27;s both resilient and highly available. The solution leverages Go&#x27;s concurrency model to efficiently process messages whilst maintaining data consistency. Through careful consideration of failure scenarios, I&#x27;ve built a system that consumers of this data can rely on to receive accurate, timely information about train movements and delays for integration into their own applications.&lt;&#x2F;p&gt;
&lt;p&gt;In this post, I&#x27;ll walk through some of the key architectural decisions, challenges faced, and lessons learned while building this system. Whether you&#x27;re working with Kafka, building data processing pipelines, or interested in Go-based microservices, you&#x27;ll find practical insights from my experience developing this project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;project-overview&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#project-overview&quot; aria-label=&quot;Anchor link for: project-overview&quot;&gt;Project Overview&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The service serves as a critical component in a rail information system, bridging the gap between real-time data streams and persistent storage. Its primary responsibilities are:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Consuming train data from Darwin JSON Kafka Topic&lt;&#x2F;li&gt;
&lt;li&gt;Processing and validating incoming messages&lt;&#x2F;li&gt;
&lt;li&gt;Storing processed data in a PostgreSQL database using table partitioning&lt;&#x2F;li&gt;
&lt;li&gt;Providing health checks and monitoring endpoints&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;system-architecture&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#system-architecture&quot; aria-label=&quot;Anchor link for: system-architecture&quot;&gt;System Architecture&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;message-processing-pipeline&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#message-processing-pipeline&quot; aria-label=&quot;Anchor link for: message-processing-pipeline&quot;&gt;Message Processing Pipeline&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The system processes railway data through a series of well-defined stages, starting with Kafka consumption and ending with persistent storage. At its core, it uses the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;twmb&#x2F;franz-go&#x2F;&quot;&gt;franz-go&lt;&#x2F;a&gt; library for reliable Kafka interaction. The consumer supports SASL authentication and configurable consumer groups for secure, scalable message processing.&lt;&#x2F;p&gt;
&lt;p&gt;Message flow is managed through Go channels, providing natural backpressure and buffering. Messages accumulate in the channel before being passed to the database layer. While the system currently processes messages individually, it&#x27;s designed to support batch processing if needed. This approach keeps the implementation simple while leaving room for optimization.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;c &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Consumer&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; processMessages&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; kafka&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Message&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;   select&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;   case&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;messageChan&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;       return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;   case&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Done&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;       return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Error handling and shutdown procedures ensure system reliability. When errors occur, detailed logs capture message context and error metadata. During shutdown, the system:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Stops consuming new messages&lt;&#x2F;li&gt;
&lt;li&gt;Processes remaining buffered messages&lt;&#x2F;li&gt;
&lt;li&gt;Commits Kafka offsets&lt;&#x2F;li&gt;
&lt;li&gt;Closes database connections gracefully&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;database-management&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#database-management&quot; aria-label=&quot;Anchor link for: database-management&quot;&gt;Database Management&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;PostgreSQL serves as the persistent storage layer, using table partitioning for efficient data management. The base table structure uses RANGE partitioning on the &lt;code&gt;created_at&lt;&#x2F;code&gt; column:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;CREATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; TABLE&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; IF&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; NOT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; EXISTS&lt;&#x2F;span&gt;&lt;span&gt; messages (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    id UUID,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    message&lt;&#x2F;span&gt;&lt;span&gt; JSONB &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    created_at &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TIMESTAMP&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TZ&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; DEFAULT&lt;&#x2F;span&gt;&lt;span&gt; CURRENT_TIMESTAMP,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    PRIMARY KEY&lt;&#x2F;span&gt;&lt;span&gt; (id, created_at)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;PARTITION&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; BY&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    RANGE&lt;&#x2F;span&gt;&lt;span&gt; (created_at);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For a detailed explanation of PostgreSQL table partitioning patterns and benefits, see my article on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;postgresql-table-partitioning&#x2F;&quot;&gt;PostgreSQL Table Partitioning&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The service manages partitions through a dedicated partitioner that runs both on startup and periodically:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Partitioner&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    db&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sql&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;DB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;p &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Partitioner&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Ensure&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; date&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Time&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    tableName&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;messages_&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%04d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;_&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%02d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;_&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%02d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Year&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Month&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Day&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    startDate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Date&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Year&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Month&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Day&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;        0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;UTC&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    endDate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; startDate&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;AddDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    query&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        CREATE TABLE IF NOT EXISTS &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; PARTITION OF messages&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        FOR VALUES FROM (&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;) TO (&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        tableName&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; startDate&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Format&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;2006-01-02 15:04:05&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        endDate&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Format&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;2006-01-02 15:04:05&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    _&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; p&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;db&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ExecContext&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; query&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;p &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Partitioner&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Start&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Create partitions for today and tomorrow on startup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    now&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Now&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    p&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Ensure&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; now&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    p&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Ensure&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; now&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;AddDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Run partition creation every 6 hours&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    ticker&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewTicker&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;6&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Hour&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    go&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        for&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            select&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            case&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ticker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;C&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;                if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; p&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Ensure&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Now&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                    log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Printf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;failed to ensure partition: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            case&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Done&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                ticker&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Stop&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;                return&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The 6-hour interval provides redundancy in partition creation, preventing gaps if the service experiences downtime. Creating tomorrow&#x27;s partition on startup ensures message processing continues seamlessly across midnight UTC.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;testing-strategy&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#testing-strategy&quot; aria-label=&quot;Anchor link for: testing-strategy&quot;&gt;Testing Strategy&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Integration tests form the backbone of the testing approach. Using Docker containers, tests run against real Kafka and PostgreSQL instances:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; TestMessageProcessing&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;testing&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;   kafka&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewKafkaContainer&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;   postgres&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; test&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewPostgresContainer&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;t&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;   &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Test setup and assertions&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Utility packages handle container lifecycle and test data setup, making it straightforward to write comprehensive tests. While unit tests with mocks supplement coverage, integration tests have proven more valuable for catching real-world issues, particularly around:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Transaction handling&lt;&#x2F;li&gt;
&lt;li&gt;Error scenarios&lt;&#x2F;li&gt;
&lt;li&gt;Message ordering&lt;&#x2F;li&gt;
&lt;li&gt;Database interactions&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;deployment&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#deployment&quot; aria-label=&quot;Anchor link for: deployment&quot;&gt;Deployment&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The service runs on a Virtual Private Server (VPS), deployed alongside its PostgreSQL instance. This co-location minimises latency for database operations. Kafka runs as an external service that the system connects to - I have no visibility into its configuration or infrastructure.&lt;&#x2F;p&gt;
&lt;p&gt;The VPS runs Fedora Linux, which uses systemd as its init system and service manager. With systemd being a core part of Fedora, there&#x27;s no need to install additional process managers like Supervisor or PM2. This reduces complexity and potential points of failure. systemd provides process supervision, automatic restarts on failure, and centralised logging through journald. Here&#x27;s the systemd service configuration:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unit&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;Description&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;Railway Service Systemd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;ConditionPathExists&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;opt&#x2F;railway&#x2F;railway-linux-amd64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;After&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;network.target&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Service&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;ExecStart&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;opt&#x2F;railway&#x2F;railway-linux-amd64 -&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;etc&#x2F;railway&#x2F;railway.yaml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;Restart&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;always&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Before moving to production, I needed to validate that the service worked correctly with the provided VPS architecture and infrastructure. To achieve this, the service underwent a thorough 7-day staging validation period.&lt;&#x2F;p&gt;
&lt;p&gt;This validation phase was crucial for verifying several critical aspects of the system. The service successfully handled server restarts, maintaining data consistency and resuming message processing automatically. Database writes remained reliable throughout the test period.&lt;&#x2F;p&gt;
&lt;p&gt;No manual intervention was needed during this validation phase, demonstrating the robustness of the systemd configuration and overall system design. This gave confidence that the service would operate reliably in the production environment using the provided infrastructure.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Building reliable data pipelines requires careful attention to detail. This project showcases that through its clean architecture and robust Kafka consumer implementation. The production deployment on Fedora, with each component designed with reliability in mind, keeps the service running smoothly.&lt;&#x2F;p&gt;
&lt;p&gt;The testing approach proved particularly valuable. Integration tests with containerised dependencies caught real-world issues early. Error handling and monitoring ensure problems are visible and quickly resolved. systemd&#x27;s built-in features provide robust process management.&lt;&#x2F;p&gt;
&lt;p&gt;What started as a simple Kafka consumer evolved into a production-ready service. It reliably processes railway data day after day, demonstrating how Go&#x27;s simplicity and powerful concurrency model make it an excellent choice for data processing systems.&lt;&#x2F;p&gt;
&lt;p&gt;For more on the technologies used in this project, see my posts on &lt;a href=&quot;&#x2F;posts&#x2F;getting-started-with-golang-and-kafka&#x2F;&quot;&gt;getting started with Go and Kafka using franz-go&lt;&#x2F;a&gt; and &lt;a href=&quot;&#x2F;posts&#x2F;postgresql-table-partitioning&#x2F;&quot;&gt;PostgreSQL table partitioning with Go&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Engineering arrogance</title>
          <pubDate>Mon, 06 Jan 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/engineering-arrogance/</link>
          <guid>https://aran.dev/posts/engineering-arrogance/</guid>
          <description xml:base="https://aran.dev/posts/engineering-arrogance/">&lt;p&gt;Innovation and rapid execution drive success in technology companies. When engineering teams collaborate effectively, they iterate quickly and maintain competitive advantages. However, arrogance within engineering teams can silently erode this foundation. Arrogance, ego and passive-aggressive attitudes within teams can stifle creativity, foster hostility, and hinder innovation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-engineering-arrogance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-engineering-arrogance&quot; aria-label=&quot;Anchor link for: what-is-engineering-arrogance&quot;&gt;What is Engineering Arrogance?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Engineering arrogance manifests when technical expertise becomes a barrier for others. It transforms code reviews into battlegrounds, technical decisions into power plays, and learning opportunities into ego threats. Instead of driving innovation, this behavior weaponises expertise for personal validation, prioritising ego over teamwork.&lt;&#x2F;p&gt;
&lt;p&gt;The impacts surface in daily engineering work through ego battles during code reviews, inflexible &lt;em&gt;&quot;my way or the highway&quot;&lt;&#x2F;em&gt; mentalities, gatekeeping of knowledge, and undermining of new ideas. While not every team experiences all these behaviors, even a few can poison team dynamics. When arrogance becomes ingrained in engineering culture, it leads to something far worse: technology stagnation.&lt;&#x2F;p&gt;
&lt;p&gt;This mindset fundamentally corrupts the purpose of technical expertise. Rather than serving as a foundation for collaboration and growth, engineering arrogance builds barriers where bridges for collaboration should exist, ultimately undermining the very innovation it claims to protect.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ego-in-engineering-arrogance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ego-in-engineering-arrogance&quot; aria-label=&quot;Anchor link for: ego-in-engineering-arrogance&quot;&gt;Ego in Engineering Arrogance&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Engineering expertise naturally builds confidence. Yet unchecked ego transforms that confidence into an obstacle. While healthy confidence drives engineers to excel and push boundaries, ego left unchecked is dangerous, converting constructive technical discussions into battles for dominance.&lt;&#x2F;p&gt;
&lt;p&gt;This transformation often begins subtly. Engineers start equating their technical expertise with their professional worth. Every code review and design discussion feels like a referendum on their competence. When an engineer&#x27;s identity becomes intrinsically tied to their technical decisions, admitting mistakes transforms from a learning opportunity into a perceived threat, and this defensive posture erodes the collaborative environment needed for innovation.&lt;&#x2F;p&gt;
&lt;p&gt;The impact compounds over time in unpredictable ways. Senior engineers might cling to outdated patterns. They dismiss fresh ideas to maintain their perceived authority, turning technical discussions into exercises in defending territory rather than exploring solutions. Engineers may double down on problematic approaches instead of acknowledging mistakes. These choices turn temporary technical compromises into permanent architectural decisions that live with projects for years.&lt;&#x2F;p&gt;
&lt;p&gt;These behaviours eventually reshape the systems themselves into rigid structures resistant to change and innovation. Teams spend more time maintaining legacy approaches and accumulating technical debt than evolving their systems to meet future needs. What begins as personal ego turns into institutional stagnation. Resulting in platforms becoming difficult to change as the mindsets that maintain them.&lt;&#x2F;p&gt;
&lt;p&gt;Innovation dies quietly. Teams stop brainstorming freely, fearing ridicule or dismissal. Technical discussions lose their vibrancy. What should be dynamic exploration becomes a predictable exercise in confirming existing beliefs. This quiet conformity doesn&#x27;t just stifle individual creativity—it systematically undermines the collaborative innovation that drives technology forward, leaving teams trapped in cycles of maintaining increasingly outdated systems.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;toxic-code-reviews-the-battleground-for-ego&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#toxic-code-reviews-the-battleground-for-ego&quot; aria-label=&quot;Anchor link for: toxic-code-reviews-the-battleground-for-ego&quot;&gt;Toxic Code Reviews: The Battleground for Ego&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Code reviews should make code better and help teams grow together. But when arrogance creeps in, they turn into painful ego battles where showing technical superiority matters more than actual improvement.&lt;&#x2F;p&gt;
&lt;p&gt;Nitpicking is probably the most common toxic review pattern. A reviewer gets hung up on tiny details like variable names or where the brackets go, completely missing the bigger picture. Sure, coding standards matter - but when someone&#x27;s correcting every little thing just to show they&#x27;re in charge, it&#x27;s not about code quality anymore. It wastes everyone&#x27;s time and kills motivation to improve things.&lt;&#x2F;p&gt;
&lt;p&gt;Then there&#x27;s the reviewers who can&#x27;t be bothered to explain themselves. They drop comments like &lt;em&gt;&quot;this is bad&quot;&lt;&#x2F;em&gt; or &lt;em&gt;&quot;needs work&quot;&lt;&#x2F;em&gt; without saying why. It&#x27;s like they&#x27;re too important to explain their thinking. When engineers get this kind of useless feedback, they either have to guess what the reviewer wants or go back and forth endlessly. Pretty soon people stop trying to contribute meaningfully.&lt;&#x2F;p&gt;
&lt;p&gt;The worst offenders take it public. They&#x27;ll mock code in team channels with comments like &lt;em&gt;&quot;you clearly don&#x27;t understand how things work here&quot;&lt;&#x2F;em&gt; or &lt;em&gt;&quot;this is what happens when people don&#x27;t learn our architecture first.&quot;&lt;&#x2F;em&gt;. Instead of helping someone learn, they&#x27;re just showing off their supposed superiority. Each time this happens, it teaches everyone a lesson: keep your head down, don&#x27;t try anything new, and definitely don&#x27;t ask questions that might make you look inexperienced.&lt;&#x2F;p&gt;
&lt;p&gt;When code reviews become about ego rather than improvement, the whole system breaks down. Good ideas get buried because people are more worried about avoiding criticism than solving problems. Teams that should be collaborating end up divided into camps of harsh reviewers and defensive contributors. The very process that should make engineering better ends up holding everyone back.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;technological-stagnation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#technological-stagnation&quot; aria-label=&quot;Anchor link for: technological-stagnation&quot;&gt;Technological Stagnation&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Engineering arrogance kills innovation in predictable ways. It murders risk-taking and every mistake gets ridiculed in code reviews or design meetings, engineers learn to play it safe. Instead of trying bold solutions, they stick to what won&#x27;t attract attention. The codebase fills up with minor tweaks and safe changes while big architectural problems go unsolved.&lt;&#x2F;p&gt;
&lt;p&gt;This safety-first mentality spreads. Engineers stop speaking up in meetings. They keep their ideas to themselves rather than risk getting shot down. Pretty soon, the loudest voices - usually the most arrogant ones - are the only ones heard. The team ends up doing things &quot;the way we&#x27;ve always done them&quot; not because it&#x27;s right, but because challenging the status quo isn&#x27;t worth the hassle.&lt;&#x2F;p&gt;
&lt;p&gt;The damage gets worse when senior engineers start hoarding knowledge. They treat their expertise like a treasure to guard rather than something to share. Junior engineers struggle with the same problems over and over because the &lt;em&gt;&quot;experts&quot;&lt;&#x2F;em&gt; are too busy proving how smart they are to actually teach anyone. Each team ends up solving the same problems differently because knowledge is stuck in silos.&lt;&#x2F;p&gt;
&lt;p&gt;All of this results in a codebase that looks exactly like the team culture that built it - rigid, fragmented, and resistant to change. New features take forever because everyone&#x27;s afraid to touch the &lt;em&gt;&quot;critical&quot;&lt;&#x2F;em&gt; parts that only one person understands. Technical debt piles up because fixing it would mean admitting previous approaches were wrong. The platform becomes a museum of past decisions that everyone&#x27;s afraid to update.&lt;&#x2F;p&gt;
&lt;p&gt;This stagnation costs money and time. Teams move slowly not because they&#x27;re being careful, but because they&#x27;re paralysed. Innovation dies - buried under layers of &lt;em&gt;&quot;that&#x27;s not how we do things here&quot;&lt;&#x2F;em&gt; and &lt;em&gt;&quot;you don&#x27;t understand the context.&quot;&lt;&#x2F;em&gt;. What started as engineering arrogance ends as engineering paralysis.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;passive-aggressive-arrogance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#passive-aggressive-arrogance&quot; aria-label=&quot;Anchor link for: passive-aggressive-arrogance&quot;&gt;Passive-Aggressive Arrogance&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Not all engineering arrogance is obvious. Sometimes it&#x27;s hidden in subtle behaviors - the eye rolls during standups, or that teammate who always says &lt;em&gt;&quot;interesting...&quot;&lt;&#x2F;em&gt; in a tone that clearly means &quot;that&#x27;s stupid.&quot; This quieter version of arrogance might seem less harmful than outright criticism, but it&#x27;s just as toxic to team culture.&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;ll spot it in a hundred little ways. There&#x27;s the engineer who sits silently through design discussions, then sends passive-aggressive messages picking apart decisions after everyone&#x27;s left the room. Or the one who responds to new ideas with &lt;em&gt;&quot;Well, you can try that if you want...&quot;&lt;&#x2F;em&gt; in a tone that suggests you&#x27;re about to crash and burn. They&#x27;ll agree to changes in meetings but then never quite find time to review the PR, letting it rot in the queue until everyone forgets about it.&lt;&#x2F;p&gt;
&lt;p&gt;The damage runs deep because it&#x27;s harder to call out. When someone says &lt;em&gt;&quot;I&#x27;m just asking questions...&quot;&lt;&#x2F;em&gt; while systematically undermining a technical decision, or responding to every suggestion with &quot;maybe you know something I don&#x27;t...&quot; they&#x27;re creating doubt while maintaining plausible deniability. These behaviors shut down innovation just as effectively as direct criticism, but they do it so quietly that many teams don&#x27;t realise what&#x27;s happening until their culture is thoroughly poisoned.&lt;&#x2F;p&gt;
&lt;p&gt;These passive-aggressive engineers often see themselves as the &lt;em&gt;&quot;voice of reason&quot;&lt;&#x2F;em&gt; or the &lt;em&gt;&quot;technical conscience&quot;&lt;&#x2F;em&gt; of the team. But instead of openly discussing concerns, they express disapproval through sighs in meetings, frowning, pointed questions that aren&#x27;t really questions, and minimal engagement with anything they didn&#x27;t propose. When their ideas aren&#x27;t chosen, they&#x27;ll technically comply while doing everything possible to prove they were right all along - even if it means quietly undermining team decisions.&lt;&#x2F;p&gt;
&lt;p&gt;This behavior spreads. Other engineers start adopting the same tactics because direct communication feels too risky. Soon you&#x27;ve got a team full of people speaking in subtexts and hidden meanings instead of actually solving problems. Technical discussions become exercises in reading between the lines, and the energy that should go into innovation gets spent on navigating personality politics instead.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;strategies-for-combating-arrogance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#strategies-for-combating-arrogance&quot; aria-label=&quot;Anchor link for: strategies-for-combating-arrogance&quot;&gt;Strategies for Combating Arrogance&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Fighting engineering arrogance isn&#x27;t about grand gestures - it&#x27;s about changing daily habits and interactions. The fix starts with both individual engineers and their leaders making different choices in everyday situations.&lt;&#x2F;p&gt;
&lt;p&gt;For engineers, it begins with letting go of code ownership. Your code isn&#x27;t your baby - it&#x27;s just one possible solution to a problem. When someone suggests changes, they&#x27;re not attacking you personally. They&#x27;re trying to make the solution better. Try responding to feedback with curiosity: &lt;em&gt;&quot;That&#x27;s an interesting point - could you help me understand your concerns?&quot;&lt;&#x2F;em&gt;. Instead of defending your approach, explore alternatives. Remember that the best engineers aren&#x27;t the ones who write perfect code - they&#x27;re the ones who help their teams deliver better solutions.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re leading engineering teams, you&#x27;ve got the power to reshape the culture through your actions. When was the last time you admitted a mistake in front of your team? Or openly changed your mind after hearing a junior engineer&#x27;s perspective? These moments matter. Show your team it&#x27;s okay to be wrong sometimes. Celebrate the engineer who caught a serious bug in their own code before it hit production. Highlight the team that scrapped two weeks of work because they found a better approach. Make it clear that learning beats looking smart.&lt;&#x2F;p&gt;
&lt;p&gt;The real change happens in small moments. It&#x27;s the senior engineer taking time to explain a complex system to a new hire. It&#x27;s the team lead who asks for input instead of dictating solutions. It&#x27;s the developer who starts their code review with what they learned from reading their colleague&#x27;s approach. These daily choices might feel small, but they will slowly transform the culture from one of competition to one of collective growth.&lt;&#x2F;p&gt;
&lt;p&gt;Building a better engineering culture is not a one-time fix - it is an ongoing practice. The goal is not to eliminate all ego (we&#x27;re human after all), but to create an environment where learning and improvement matter more than looking smart. When teams get this right, they don&#x27;t just write better code - they build a place where great engineers want to stay and grow.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Engineering arrogance isn&#x27;t born from having the smartest person in the room - it comes from creating environments where everyone can contribute their best work. Collaborative innovation flourishes when arrogance is set aside in favor of humility and curiosity. Teams don&#x27;t just tolerate diverse opinions, they actively seek them out.&lt;&#x2F;p&gt;
&lt;p&gt;The real impact happens in daily choices: how we review code, how we share knowledge, and how we respond to new ideas. These small moments shape whether our teams build bridges or walls between each other.&lt;&#x2F;p&gt;
&lt;p&gt;The next time you&#x27;re about to dismiss an idea because &lt;em&gt;&quot;that&#x27;s not how we do things here,&quot;&lt;&#x2F;em&gt; pause. Ask yourself what you might learn instead. What looks like a mistake might be the seed of your next breakthrough.&lt;&#x2F;p&gt;
&lt;p&gt;Building an environment of trust and respect isn&#x27;t just good for team morale - it drives real technological progress. When engineers feel safe to experiment and contribute, they create solutions that last.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Building with the Google Sheets API in Go</title>
          <pubDate>Sun, 05 Jan 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/building-with-google-sheets-api-in-go/</link>
          <guid>https://aran.dev/posts/building-with-google-sheets-api-in-go/</guid>
          <description xml:base="https://aran.dev/posts/building-with-google-sheets-api-in-go/">&lt;p&gt;Getting started with the Google Sheets API can be challenging for Go developers. From understanding A1 notation to managing batch operations. There are several concepts and patterns to understand. This guide walks you through building a robust integration with the Google Sheets API in Go.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up-your-environment&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#setting-up-your-environment&quot; aria-label=&quot;Anchor link for: setting-up-your-environment&quot;&gt;Setting Up Your Environment&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;First things first. You&#x27;ll need to set up your development environment by installing the required dependencies:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;go&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; get&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; google&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;golang&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;org&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;api&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;v4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;go&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; get&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; golang&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;org&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;oauth2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;google&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here&#x27;s a basic configuration structure we&#x27;ll use throughout our examples. It&#x27;s straightforward but flexible enough for most use cases.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; SheetsConfig&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    CredentialsFile&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    TokenFile&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;       string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    SpreadsheetID&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;   string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Range&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;           string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; NewSheetsClient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;config&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; SheetsConfig&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Service&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Background&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    b&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; os&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ReadFile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;CredentialsFile&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;reading credentials file: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    config&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; google&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ConfigFromJSON&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;SpreadsheetsScope&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;parsing credentials: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    client&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; getClient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;TokenFile&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    srv&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;New&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;creating sheets client: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; srv&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;understanding-sheet-ranges&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#understanding-sheet-ranges&quot; aria-label=&quot;Anchor link for: understanding-sheet-ranges&quot;&gt;Understanding Sheet Ranges&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A1 notation is the backbone of working with the Sheets API. A1 notation is the standard way to reference cells in spreadsheets. Letters represent columns (A, B, C...) while numbers represent rows (1, 2, 3...). When combined, they pinpoint specific cells - A1 is the first cell in the top-left corner, B3 is the cell in the second column and third row. To work with multiple cells at once, use a colon to show a range, like A1:B10. The system continues past 26 columns by doubling up letters - after Z comes AA, then AB, and so on.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s make this simpler for us in Go with a helper that turns complex range calculations into intuitive operations.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Range represents a spreadsheet range in A1 notation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Range&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Sheet&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    StartCol&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    EndCol&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;   string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    StartRow&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    EndRow&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;   int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; String returns the A1 notation representation of the range.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; For example: &amp;quot;Sheet1!A1:B2&amp;quot; or &amp;quot;Sheet1!A1:A&amp;quot; for a single column.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;r &lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Range&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;EndCol&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;EndCol&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;StartCol&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;EndRow&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Sheet&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;StartCol&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;StartRow&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;EndCol&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Sheet&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;StartCol&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;StartRow&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;EndCol&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;EndRow&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; isValidColumn checks if a column name consists of uppercase letters A-Z&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; isValidColumn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;col&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; bool&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; col&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; range&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; col&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ||&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;Z&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; NewRange creates a Range from a sheet name and column range.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; It returns an error if the input parameters are invalid.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; NewRange&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;sheet&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; startCol&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; endCol&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; startRow&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; endRow&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Range&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sheet&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sheet name cannot be empty&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;isValidColumn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;startCol&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;invalid start column: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; startCol&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; endCol&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;isValidColumn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;endCol&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;invalid end column: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; endCol&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; startRow&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;start row must be positive, got: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; startRow&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; endRow&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;end row cannot be negative, got: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; endRow&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; endRow&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; endRow&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; startRow&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;end row (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;) cannot be less than start row (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; endRow&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; startRow&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Range&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Sheet&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    sheet&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        StartCol&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; startCol&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        EndCol&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;   endCol&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        StartRow&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; startRow&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        EndRow&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;   endRow&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;creating-and-configuring-spreadsheets&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#creating-and-configuring-spreadsheets&quot; aria-label=&quot;Anchor link for: creating-and-configuring-spreadsheets&quot;&gt;Creating and Configuring Spreadsheets&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Consistency is key when creating new spreadsheets. A well-structured sheet makes future operations smoother. Here&#x27;s a pattern that works well in practice.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; SheetConfig&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Name&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Headers&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Frozen&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;  bool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; CreateConfiguredSheet&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;srv&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Service&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; title&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; configs&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;SheetConfig&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Spreadsheet&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    spreadsheet&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Spreadsheet&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Properties&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;SpreadsheetProperties&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            Title&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; title&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Sheets&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sheet&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;configs&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; i&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; config&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; range&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; configs&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        sheet&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sheet&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            Properties&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;SheetProperties&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                Title&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Name&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Frozen&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            sheet&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Properties&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;GridProperties&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;GridProperties&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                FrozenRowCount&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        spreadsheet&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Sheets&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; sheet&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    created&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; srv&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Spreadsheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Create&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;spreadsheet&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Do&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;failed to create spreadsheet: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Configure headers for each sheet&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; i&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; config&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; range&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; configs&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Headers&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            range&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; NewRange&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Name&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; columnToLetter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Headers&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            values&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;                make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Headers&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; j&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; header&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; range&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Headers&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                values&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;j&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; header&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            valueRange&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ValueRange&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                Range&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;  range&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                Values&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; values&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            _&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; srv&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Spreadsheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Values&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Update&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;created&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;SpreadsheetId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; range&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; valueRange&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;                ValueInputOption&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;RAW&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Do&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;                return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;failed to set headers for sheet &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Name&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; created&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; columnToLetter converts a column number to A1 notation letter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; columnToLetter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;col&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; result&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; col&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        col&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;--&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;rune&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;col&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;%&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;26&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; result&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        col&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &#x2F;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 26&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; result&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;efficient-data-operations&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#efficient-data-operations&quot; aria-label=&quot;Anchor link for: efficient-data-operations&quot;&gt;Efficient Data Operations&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Scale changes everything. When your application needs to handle larger datasets, batch operations become crucial. They&#x27;re faster and more reliable. Here&#x27;s a pattern that&#x27;s proven effective in production environments.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; BatchUpdate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    updates&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    ranges&lt;&#x2F;span&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; NewBatchUpdate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;BatchUpdate&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;BatchUpdate&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        updates&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        ranges&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;  make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;b &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;BatchUpdate&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; AddUpdate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;range&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; values&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    b&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;updates&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;range&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; values&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    b&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ranges&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; append&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;b&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ranges&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; range&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;b &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;BatchUpdate&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Execute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;srv&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Service&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; spreadsheetId&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ValueRange&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; range&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; range&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; b&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ranges&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        data&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; append&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ValueRange&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            Range&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;  range&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            Values&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; b&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;updates&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;range&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    req&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;BatchUpdateValuesRequest&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        ValueInputOption&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;RAW&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        Data&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            data&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    _&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; srv&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Spreadsheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Values&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;BatchUpdate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;spreadsheetId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; req&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Do&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;error-handling-and-retry-logic&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#error-handling-and-retry-logic&quot; aria-label=&quot;Anchor link for: error-handling-and-retry-logic&quot;&gt;Error Handling and Retry Logic&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;APIs can be unpredictable. Network calls might fail. Rate limits could kick in. A robust retry mechanism makes all the difference.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; RetryConfig&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    MaxAttempts&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    InitialDelay&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Duration&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    MaxDelay&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;     time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Duration&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; WithRetry&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;config&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; RetryConfig&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; operation&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; lastErr&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    delay&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;InitialDelay&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; attempt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; attempt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;MaxAttempts&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; attempt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; operation&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            lastErr&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;isRetryable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;                return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;delay&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;            delay&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; min&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;delay&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;MaxDelay&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;operation failed after &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; attempts: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;MaxAttempts&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; lastErr&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; isRetryable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;err&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; bool&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Check for specific Google Sheets API errors that are retryable&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; gerr&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; ok&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;googleapi&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; ok&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        switch&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; gerr&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Code&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        case&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 429&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Too Many Requests&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;             500&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Internal Server Error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;             503&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Service Unavailable&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;practical-usage-example&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#practical-usage-example&quot; aria-label=&quot;Anchor link for: practical-usage-example&quot;&gt;Practical Usage Example&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have a understanding of ranges, batch operations, and error handling, let&#x27;s put everything together in a real-world scenario. In this example we will look at managing product inventory.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Product&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    ID&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;          string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Name&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;        string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Stock&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;       int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    LastUpdated&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Time&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; UpdateInventory&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;srv&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Service&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; spreadsheetId&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; products&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Product&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    batch&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; NewBatchUpdate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; _&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; product&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; range&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; products&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        values&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                product&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ID&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                product&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Name&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                product&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Stock&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                product&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;LastUpdated&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Format&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;RFC3339&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        range&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; NewRange&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Inventory&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;D&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; findProductRow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;srv&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; spreadsheetId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; product&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ID&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        batch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;AddUpdate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;range&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; values&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; WithRetry&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;RetryConfig&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        MaxAttempts&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;   3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        InitialDelay&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;  time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Second&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        MaxDelay&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;      time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Second&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; batch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Execute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;srv&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; spreadsheetId&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; findProductRow&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;srv&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sheets&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Service&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; spreadsheetId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; productId&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Implementation to find the row number for a product&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; This would typically involve searching the ID column&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; and returning the appropriate row number&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Returns a new row number if product not found&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;best-practices-and-common-pitfalls&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#best-practices-and-common-pitfalls&quot; aria-label=&quot;Anchor link for: best-practices-and-common-pitfalls&quot;&gt;Best Practices and Common Pitfalls&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;When working with the Sheets API at scale, following these best practices will help you build more reliable applications.&lt;&#x2F;p&gt;
&lt;p&gt;Always batch your operations. The API has rate limits. Batching helps you stay well within them while improving performance. Our BatchUpdate pattern shows how to do this effectively.&lt;&#x2F;p&gt;
&lt;p&gt;Put in place proper error handling and retry logic. Network hiccups happen. The Sheets API occasionally returns 429 or 503 errors. Your application should handle these gracefully.&lt;&#x2F;p&gt;
&lt;p&gt;Be careful with range calculations. A1 notation can be confusing. That&#x27;s why we built the Range struct. Always validate your ranges before performing operations.&lt;&#x2F;p&gt;
&lt;p&gt;Consider caching frequently accessed data locally. If you&#x27;re reading the same ranges repeatedly, a local cache can work wonders. Just remember to implement proper invalidation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The Google Sheets API opens up interesting possibilities for Go applications. It&#x27;s powerful but requires careful handling. Start simple. Get the basics right. Then build up to more complex operations.&lt;&#x2F;p&gt;
&lt;p&gt;The patterns we&#x27;ve covered here will help you avoid common pitfalls. They&#x27;re battle-tested and ready for production use. But don&#x27;t stop here. Explore the API&#x27;s advanced features. Try out formatting capabilities. Experiment with conditional formatting and chart creation.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Building Stria to streamline payment data integrations</title>
          <pubDate>Fri, 03 Jan 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/building-stria-streamling-data-integrations/</link>
          <guid>https://aran.dev/posts/building-stria-streamling-data-integrations/</guid>
          <description xml:base="https://aran.dev/posts/building-stria-streamling-data-integrations/">&lt;p&gt;I&#x27;ve been working on a new side project. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stria.io&quot;&gt;Stria&lt;&#x2F;a&gt;, a service that will make payment data integration straightforward and reliable. As I get closer to launching I wanted to share a bit more about what I&#x27;ve been building and why.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem-i-m-solving&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-problem-i-m-solving&quot; aria-label=&quot;Anchor link for: the-problem-i-m-solving&quot;&gt;The Problem I&#x27;m Solving&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Every time I&#x27;ve needed payment data in the past in analytics tools or reporting systems. I&#x27;ve ended up building a custom solution which has turned into the same headache. What should be a &quot;quick integration&quot; with Stripe&#x27;s API becomes weeks of:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Wrestling with edge cases&lt;&#x2F;li&gt;
&lt;li&gt;Building retry logic for API rate limits&lt;&#x2F;li&gt;
&lt;li&gt;Managing webhook reliability&lt;&#x2F;li&gt;
&lt;li&gt;Maintaining yet another piece of infrastructure&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I figured there had to be a better way.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-stria&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-stria&quot; aria-label=&quot;Anchor link for: what-is-stria&quot;&gt;What is Stria?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Stria will sync your payment data to a desired output. I&#x27;m starting with a Stripe integration. But in the future I will expand to other payment providers. Instead of building and maintaining complex custom integrations yourself. Stria will manage the complexity, syncing to databases, Google Sheets or other destinations.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;technical-implementation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#technical-implementation&quot; aria-label=&quot;Anchor link for: technical-implementation&quot;&gt;Technical Implementation&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s a look under the hood at what I&#x27;m building:&lt;&#x2F;p&gt;
&lt;p&gt;The backend is being built in Go, chosen for its reliability, performance, and excellent standard library support. This has proven invaluable when working with HTTP services and API integrations.&lt;&#x2F;p&gt;
&lt;p&gt;For infrastructure, I&#x27;m deploying on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fly.io&quot;&gt;Fly.io&lt;&#x2F;a&gt; to ensure scalability as the service grows. In the future I may need to expand on this and utilise GCP&#x27;s managed services.&lt;&#x2F;p&gt;
&lt;p&gt;At the core of Stria is a custom sync engine. This ensures that your data stays consistent even when dealing with network issues or API downtime.&lt;&#x2F;p&gt;
&lt;p&gt;From day one, I&#x27;ve built comprehensive observability into the system. This means I can identify and resolve any issues that might arise, ensuring reliable service for users.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;initial-feature-set&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#initial-feature-set&quot; aria-label=&quot;Anchor link for: initial-feature-set&quot;&gt;Initial Feature Set&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;For the initial release, I&#x27;m focusing on:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Stripe integration&lt;&#x2F;li&gt;
&lt;li&gt;Google Sheets integration&lt;&#x2F;li&gt;
&lt;li&gt;Scheduled syncing&lt;&#x2F;li&gt;
&lt;li&gt;Real-time syncing&lt;&#x2F;li&gt;
&lt;li&gt;Reliable data consistency&lt;&#x2F;li&gt;
&lt;li&gt;Simple pricing starting at $5&#x2F;month&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;what-s-coming-next&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-s-coming-next&quot; aria-label=&quot;Anchor link for: what-s-coming-next&quot;&gt;What&#x27;s Coming Next&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;While I&#x27;m focused on getting the core functionality rock-solid, I&#x27;m already planning:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Additional payment provider integrations&lt;&#x2F;li&gt;
&lt;li&gt;More destination options&lt;&#x2F;li&gt;
&lt;li&gt;Advanced filtering capabilities&lt;&#x2F;li&gt;
&lt;li&gt;Custom webhook notifications&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;early-access&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#early-access&quot; aria-label=&quot;Anchor link for: early-access&quot;&gt;Early Access&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m getting close to launch and will be looking for early users soon. If you&#x27;re interested in:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Testing the integration&lt;&#x2F;li&gt;
&lt;li&gt;Providing feedback on features&lt;&#x2F;li&gt;
&lt;li&gt;Being among the first to use Stria&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Feel free to reach out to me on X or Bluesky.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-in-public&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#building-in-public&quot; aria-label=&quot;Anchor link for: building-in-public&quot;&gt;Building in Public&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I believe in building in public and sharing the journey. I&#x27;ll be sharing here on my blog, on Stria&#x27;s website and social media.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll be sharing more technical deep dives about:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The challenges of building reliable sync systems&lt;&#x2F;li&gt;
&lt;li&gt;Handling webhook idempotency&lt;&#x2F;li&gt;
&lt;li&gt;Managing API rate limits effectively&lt;&#x2F;li&gt;
&lt;li&gt;Lessons learned along the way&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Stay tuned for updates as I get closer to launch and check out Stria at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stria.io&quot;&gt;stria.io&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>PostgreSQL table partitioning with Go</title>
          <pubDate>Wed, 18 Dec 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/postgresql-table-partitioning/</link>
          <guid>https://aran.dev/posts/postgresql-table-partitioning/</guid>
          <description xml:base="https://aran.dev/posts/postgresql-table-partitioning/">&lt;p&gt;PostgreSQL offers several ways to partition tables, including by range, list, or hash. There are various partitioning strategies available. This post will focus on a practical example using date-based partitioning from a recent project of mine. Demonstrating the concepts and implementation of table partitioning in practice.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-problem&quot; aria-label=&quot;Anchor link for: the-problem&quot;&gt;The Problem&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s start with a simple table that stores messages:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;CREATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; TABLE&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; messages&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    id UUID &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;PRIMARY KEY&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    message&lt;&#x2F;span&gt;&lt;span&gt; JSONB &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    created_at &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TIMESTAMP&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TZ&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; DEFAULT&lt;&#x2F;span&gt;&lt;span&gt; CURRENT_TIMESTAMP&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As this table grows, queries filtering on specific columns might become slower, and managing the data becomes more challenging. Partitioning can help address these concerns.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;implementing-partitioning&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#implementing-partitioning&quot; aria-label=&quot;Anchor link for: implementing-partitioning&quot;&gt;Implementing Partitioning&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;For our example, we&#x27;ll partition the table by date using the &lt;code&gt;PARTITION BY RANGE&lt;&#x2F;code&gt; clause:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;CREATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; TABLE&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; messages&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    id UUID &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    message&lt;&#x2F;span&gt;&lt;span&gt; JSONB &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    created_at &lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TIMESTAMP&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;TZ&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; DEFAULT&lt;&#x2F;span&gt;&lt;span&gt; CURRENT_TIMESTAMP,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    PRIMARY KEY&lt;&#x2F;span&gt;&lt;span&gt; (created_at, id)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;PARTITION&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; BY&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; RANGE&lt;&#x2F;span&gt;&lt;span&gt; (created_at);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notice two important changes:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The PRIMARY KEY now includes both &lt;code&gt;created_at&lt;&#x2F;code&gt; and &lt;code&gt;id&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ve added the PARTITION BY RANGE clause&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;creating-partitions&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#creating-partitions&quot; aria-label=&quot;Anchor link for: creating-partitions&quot;&gt;Creating Partitions&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In our example, each partition will represent a single day of data:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;CREATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; TABLE&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; messages_y2024m12d13&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; PARTITION&lt;&#x2F;span&gt;&lt;span&gt; OF messages&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    FOR&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; VALUES&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; FROM&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;2024-12-13 00:00:00&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;TO&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;2024-12-14 00:00:00&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;CREATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; TABLE&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; messages_y2024m12d14&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; PARTITION&lt;&#x2F;span&gt;&lt;span&gt; OF messages&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    FOR&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; VALUES&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; FROM&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;2024-12-14 00:00:00&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;TO&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;2024-12-15 00:00:00&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;working-with-partitioned-tables&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#working-with-partitioned-tables&quot; aria-label=&quot;Anchor link for: working-with-partitioned-tables&quot;&gt;Working with Partitioned Tables&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;When it comes to inserting data you don&#x27;t need to worry about inserting into a specific partition. All you need to do is insert into the parent table. Postgres handles the routing of data to the correct partition automatically based on your partition key.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;--&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Postgres automatically routes this insert to the correct partition&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; messages (id, &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;, created_at)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;VALUES&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;some-uuid&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;::jsonb,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;2024-12-13 14:30:00&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;timestamp&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;tz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;automating-partition-management&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#automating-partition-management&quot; aria-label=&quot;Anchor link for: automating-partition-management&quot;&gt;Automating Partition Management&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In a production environment, you&#x27;ll likely want to automate the creation of new partitions. Using Go you can create a function that helps create partitions:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; createPartition&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;db&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sql&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;DB&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; date&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Time&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    tableName&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;messages_y&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%04d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;m&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%02d&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%02d&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Year&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Month&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Day&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    startDate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Date&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Year&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Month&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Day&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;        0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;UTC&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    endDate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; startDate&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;AddDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    query&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        CREATE TABLE IF NOT EXISTS &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; PARTITION OF messages&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        FOR VALUES FROM (&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;) TO (&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        tableName&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; startDate&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Format&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;2006-01-02 15:04:05&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        endDate&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Format&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;2006-01-02 15:04:05&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    _&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; db&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Exec&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You could then automate the creating of partitions for future dates:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; ensurePartitions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;db&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sql&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;DB&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; daysAhead&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    now&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Now&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; daysAhead&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        partitionDate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; now&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;AddDate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; i&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; createPartition&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;db&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; partitionDate&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;failed to create partition for &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%w&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;                partitionDate&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Format&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;2006-01-02&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;benefits-of-partitioning&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#benefits-of-partitioning&quot; aria-label=&quot;Anchor link for: benefits-of-partitioning&quot;&gt;Benefits of Partitioning&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;Queries can quickly find relevant data by skipping partitions that don&#x27;t match the search criteria, leading to better performance across large datasets&lt;&#x2F;li&gt;
&lt;li&gt;Drop a partition. That&#x27;s all it takes to remove old data&lt;&#x2F;li&gt;
&lt;li&gt;Database maintenance operations like index rebuilds and VACUUM run more efficiently on smaller, individual partitions, reducing the overall maintenance window for your database&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Table partitioning is a powerful feature in Postgres that can help manage large datasets efficiently. By using declarative partitioning and automating partition creation. You can maintain a clean and efficient database design while letting Postgres handle the complexity of routing data to the correct partitions.&lt;&#x2F;p&gt;
&lt;p&gt;Success with table partitioning depends on key practices. Always insert data through the parent table. Automate the creation of new partitions to prevent maintenance overhead becoming unmanageable. Partition keys should be included in the primary key for optimal performance. Plan your partition strategy to align with your specific query patterns. Following these fundamentals, you&#x27;ll be well-positioned to leverage the full benefits of Postgres partitioning.&lt;&#x2F;p&gt;
&lt;p&gt;For a real-world example of table partitioning in action, see my post on &lt;a href=&quot;&#x2F;posts&#x2F;processing-uk-rail-data-in-real-time&#x2F;&quot;&gt;processing UK rail data in real-time&lt;&#x2F;a&gt; where I use date-based partitioning to handle millions of daily messages from Kafka.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>YAML configuration: an unexpected bug</title>
          <pubDate>Mon, 09 Dec 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/yaml-cfg-unexpected-bug/</link>
          <guid>https://aran.dev/posts/yaml-cfg-unexpected-bug/</guid>
          <description xml:base="https://aran.dev/posts/yaml-cfg-unexpected-bug/">&lt;p&gt;Few weeks ago I wrote about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;yaml-cfg-the-best-of-a-bad-bunch&#x2F;&quot;&gt;YAML configuration&lt;&#x2F;a&gt; and why I believe they are the best of a bad bunch of formats. Within the post I talked abouit adding the ability for users to do string replacement with &lt;code&gt;$LOG_LEVEL&lt;&#x2F;code&gt; bash style variables. This worked great in situations where you had a variable on it&#x27;s own e.g.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;l&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;og&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  l&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;evel&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$LOG_LEVEL&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But I did not take into account secrets that may contain dollars in them. This ended up causing an unexpected bug in production adding a secret containing a dollar symbol into a yaml configuration file.&lt;&#x2F;p&gt;
&lt;p&gt;In the end I have decided  to go back on my original design and plan to support &lt;code&gt;$LOG_LEVEL&lt;&#x2F;code&gt; in favour of only supporting &lt;code&gt;${LOG_LEVEL}&lt;&#x2F;code&gt; style variables.&lt;&#x2F;p&gt;
&lt;p&gt;Bugs and unexpected behaviour happens and it is important to be able to admit when a solution is flawed and you have to go back on a design. Now my configuration library only supports &lt;code&gt;${LOG_LEVEL}&lt;&#x2F;code&gt; which I believe is enough. Adding many configuration options for environment variables added complexity.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>YAML configuration: the best of a bad bunch</title>
          <pubDate>Sat, 23 Nov 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/yaml-cfg-the-best-of-a-bad-bunch/</link>
          <guid>https://aran.dev/posts/yaml-cfg-the-best-of-a-bad-bunch/</guid>
          <description xml:base="https://aran.dev/posts/yaml-cfg-the-best-of-a-bad-bunch/">&lt;p&gt;Over the years, I’ve worked on a variety of systems, each with its own approach to configuration. From &lt;code&gt;system.properties&lt;&#x2F;code&gt; in Java apps to &lt;code&gt;wp-config.php&lt;&#x2F;code&gt; in WordPress, &lt;code&gt;.ini&lt;&#x2F;code&gt; and &lt;code&gt;.toml&lt;&#x2F;code&gt; files, JSON, and, of course, environment variables. None of these solutions are perfect. When dealing with application configuration you try to aim for a balance between: readability, maintainability, and flexibility. These qualities can often be at odds with each other.&lt;&#x2F;p&gt;
&lt;p&gt;After years of using various methods for configuration I have eventually settled on what I believe to be the most pragmatic approach. Combining YAML for structured configuration with environment variables. I&#x27;ve found this hybrid method aligns with modern development practices, especially in containerised environments, while remaining developer-friendly.&lt;&#x2F;p&gt;
&lt;p&gt;Golang&#x27;s built-in &lt;code&gt;os.ExpandEnv&lt;&#x2F;code&gt; function can handle basic environment variable expansion, but it lacks support for default values when variables are undefined. To address this limitation, I extended the functionality in my YAML configuration library to provide a fallback behaviour, ensuring graceful handling of missing environment variables.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-approach-yaml-enhanced-os-expandenv&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-approach-yaml-enhanced-os-expandenv&quot; aria-label=&quot;Anchor link for: my-approach-yaml-enhanced-os-expandenv&quot;&gt;My Approach: YAML + Enhanced &lt;code&gt;os.ExpandEnv&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Using YAML as the base configuration format allows me to define structured defaults that are easy to read, maintain, and version-control. Environment variables then handle dynamic values for environment-specific customisations, with custom logic to support default values for additional flexibility.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-yaml-is-superior-to-environment-variables&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-yaml-is-superior-to-environment-variables&quot; aria-label=&quot;Anchor link for: why-yaml-is-superior-to-environment-variables&quot;&gt;Why YAML is Superior to Environment Variables&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;While environment variables are popular thanks to methologies like Twelve-Factor applications and containerised runtimes, they often create hidden dependencies when developers use &lt;code&gt;os.Getenv&lt;&#x2F;code&gt; directly within code. This practice can obscure configuration requirements and make application behavior unpredictably dependent on environment state.&lt;&#x2F;p&gt;
&lt;p&gt;I believe YAML offers a more robust approach through. Maintaining all configuration in a single, structured file loaded at startup, a YAML configuration file ensures settings are visible, traceable, and maintainable. A YAML configuration file establishes a clear boundary between configuration and code, enabling consistent behavior, simplified validation, and more effective testing.&lt;&#x2F;p&gt;
&lt;p&gt;Of course environment variables remain valuable for specific use cases like managing secrets and environment-specific overrides. I believe however, YAML proves more effective as the primary configuration method.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;yaml-with-placeholders&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#yaml-with-placeholders&quot; aria-label=&quot;Anchor link for: yaml-with-placeholders&quot;&gt;YAML with Placeholders&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Here’s an example of a YAML configuration file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;l&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;og&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  l&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;evel&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;info&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;atabase&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ostgres&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;rl&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;postgres:&#x2F;&#x2F;postgres:postgres@127.0.0.1:5432&#x2F;default?sslmode=disable&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;eb&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  h&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ost&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 127.0.0.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ort&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 8974&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This file might be named &lt;code&gt;config.local.yaml&lt;&#x2F;code&gt; to clearly indicate it’s for local development. It provides sensible defaults that work out of the box for other developers.&lt;&#x2F;p&gt;
&lt;p&gt;In production, we can easily override these values using environment variables, as per the Twelve-Factor App principles. For instance:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;l&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;og&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  l&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;evel&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$LOG_LEVEL&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;atabase&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ostgres&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;rl&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$DATABASE_URL&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;eb&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  h&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ost&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$WEB_HOST&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ort&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;$WEB_PORT&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;LOG_LEVEL=debug&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;DATABASE_URL=postgres:&#x2F;&#x2F;prod_user:prod_pass@db.example.com:5432&#x2F;prod&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;WEB_PORT=80&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This approach works well for dynamic settings like credentials or deployment-specific URLs. However, some configuration variables—such as the web port—rarely change once defined. Keeping these &quot;static defaults&quot; in the YAML file avoids overloading the environment with unnecessary variables.&lt;&#x2F;p&gt;
&lt;p&gt;To merge YAML configuration with environment variables, I use &lt;code&gt;os.ExpandEnv&lt;&#x2F;code&gt;. However, this function has a key limitation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-os-expandenv-falls-short&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-os-expandenv-falls-short&quot; aria-label=&quot;Anchor link for: why-os-expandenv-falls-short&quot;&gt;Why &lt;code&gt;os.ExpandEnv&lt;&#x2F;code&gt; Falls Short&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;While &lt;code&gt;os.ExpandEnv&lt;&#x2F;code&gt; is excellent for basic environment variable substitution, it doesn’t support fallback values. If an environment variable isn’t defined, the placeholder is simply replaced with an empty string.&lt;&#x2F;p&gt;
&lt;p&gt;This limitation can lead to brittle configurations where missing environment variables cause unexpected behaviour. For example, if &lt;code&gt;DATABASE_URL&lt;&#x2F;code&gt; isn’t set in the example above, it will result in an invalid database configuration.&lt;&#x2F;p&gt;
&lt;p&gt;To address this, I extended the behaviour of &lt;code&gt;os.ExpandEnv&lt;&#x2F;code&gt; to support default values using a &lt;code&gt;${ENV_NAME:default}&lt;&#x2F;code&gt; syntax.&lt;&#x2F;p&gt;
&lt;p&gt;This is where some custom logic and using &lt;code&gt;os.LookupEnv&lt;&#x2F;code&gt; can be combined together to provide even more flexibility.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;enhanced-logic-for-default-values&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#enhanced-logic-for-default-values&quot; aria-label=&quot;Anchor link for: enhanced-logic-for-default-values&quot;&gt;Enhanced Logic for Default Values&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Here’s the function I use to handle both &lt;code&gt;$ENV_NAME&lt;&#x2F;code&gt; and &lt;code&gt;${ENV_NAME:default}&lt;&#x2F;code&gt; placeholders:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; parseEnv replaces $ENV_NAME and ${ENV_NAME:default} placeholders.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Default values are only supported with ${ENV_NAME:default}.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; parseEnv&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;input&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;	&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Regex to match both $ENV_NAME and ${ENV_NAME:default}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	re&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; regexp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;MustCompile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;\$(\w+)|\$\{(\w+)(?::([^}]*))?\}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; re&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ReplaceAllStringFunc&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;match&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		parts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; re&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;FindStringSubmatch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		if&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;parts&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;			return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; match&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; No match, return as-is&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Check if it&amp;#39;s $ENV_NAME or ${ENV_NAME:default}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; parts&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;			&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; $ENV_NAME style&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;			varName&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; parts&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;			if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; value&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; found&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; os&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;LookupEnv&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;varName&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; found&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;				return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; value&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;			}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;			return&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; If not found, replace with an empty string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; ${ENV_NAME:default} style&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		varName&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; parts&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		defaultValue&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; parts&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; value&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; found&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; os&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;LookupEnv&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;varName&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; found&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;			return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; value&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; defaultValue&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Fallback to default value if not found&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h4 id=&quot;how-it-works&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-it-works&quot; aria-label=&quot;Anchor link for: how-it-works&quot;&gt;How It Works&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$ENV_NAME&lt;&#x2F;code&gt;: This is replaced by the corresponding environment variable&#x27;s value, or an empty string if not set.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;${ENV_NAME}&lt;&#x2F;code&gt;: This is replaced by the corresponding environment variable&#x27;s value, or an empty string if not set.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;${ENV_NAME:default}&lt;&#x2F;code&gt;: This adds a fallback mechanism. If the variable isn’t set, the specified default value is used instead.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;example&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#example&quot; aria-label=&quot;Anchor link for: example&quot;&gt;Example&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;Given the following environment variables:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;LOG_LEVEL=debug&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The configuration file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;l&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;og&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  l&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;evel&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;${LOG_LEVEL:info}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;atabase&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ostgres&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;rl&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;${DATABASE_URL:postgres:&#x2F;&#x2F;localhost:5432&#x2F;default}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;eb&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  h&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ost&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;${WEB_HOST:127.0.0.1}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ort&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;${WEB_PORT:8080}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Would result in:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;l&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;og&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  l&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;evel&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;debug&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;d&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;atabase&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ostgres&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;rl&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;postgres:&#x2F;&#x2F;localhost:5432&#x2F;default&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;eb&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  h&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ost&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;127.0.0.1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ort&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;8080&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;benefits-of-this-approach&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#benefits-of-this-approach&quot; aria-label=&quot;Anchor link for: benefits-of-this-approach&quot;&gt;Benefits of This Approach&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This proposed solution combines structured YAML files with environment variables to create a robust configuration system. YAML provides a version-controlled foundation with clear defaults, while environment variables enable flexible overrides for different deployment environments. Extending Golang&#x27;s environment variable handling with custom default logic, we ensure graceful fallbacks while maintaining clarity. This hybrid approach delivers both the stability of structured configuration with flexibility needed for modern deployment scenarios.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;trade-offs&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#trade-offs&quot; aria-label=&quot;Anchor link for: trade-offs&quot;&gt;Trade-Offs&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;While this configuration approach offers a balance of flexibility and structure, it does comes with notable trade-offs. The custom parsing logic will increases the maintenance overhead. The system may also require additional validation steps to ensure configuration completeness and correctness.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion-a-balanced-approach&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion-a-balanced-approach&quot; aria-label=&quot;Anchor link for: conclusion-a-balanced-approach&quot;&gt;Conclusion: A Balanced Approach&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;For me, combining YAML’s readability and structure with the flexibility of environment variables—and augmenting Go’s &lt;code&gt;os.LookupEnv&lt;&#x2F;code&gt; to support default values—creates a configuration system that balances developer productivity and operational needs.&lt;&#x2F;p&gt;
&lt;p&gt;It is not a perfect solution, and there are limitations to this approach, but it strikes a balance that has proven reliable and effective across a variety of projects.&lt;&#x2F;p&gt;
&lt;p&gt;For a more complete solution for parsing YAML configuration, I’ve created my own library for loading, parsing, and validating configurations. If you&#x27;re interested in checking it out, you can find it on my GitHub: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;yamlcfg&#x2F;&quot;&gt;aranw&#x2F;yamlcfg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;update-an-unexpected-bug&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#update-an-unexpected-bug&quot; aria-label=&quot;Anchor link for: update-an-unexpected-bug&quot;&gt;Update: an unexpected bug&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;After using my updated YAML Configuration library in production I came across an unexpected bug where configuration values that contained a dollar &lt;code&gt;$&lt;&#x2F;code&gt; symbol would trigger the environment variable parsing logic unexpectedly.&lt;&#x2F;p&gt;
&lt;p&gt;I wrote a follow up post regarding this and the fix ultimatelly deciding to remove support for &lt;code&gt;$ENV_VAR&lt;&#x2F;code&gt; style variables.&lt;&#x2F;p&gt;
&lt;p&gt;You can checkout the post &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;yaml-cfg-unexpected-bug&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Deploying NixOS on Proxmox as an LXC container</title>
          <pubDate>Sat, 26 Oct 2024 13:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/deploying-nixos-on-proxmox/</link>
          <guid>https://aran.dev/posts/deploying-nixos-on-proxmox/</guid>
          <description xml:base="https://aran.dev/posts/deploying-nixos-on-proxmox/">&lt;p&gt;I&#x27;ve been using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;&quot;&gt;NixOS&lt;&#x2F;a&gt; for a while now, primarily to provision my personal and work Mac systems. While Nix can be complex and sometimes challenging to debug, I’ve enjoyed its unique approach to system configuration. However, I haven&#x27;t explored much beyond that scope. For some time, I’ve become particularly interested in using it for containers, as NixOS offers compelling features—reproducibility, declarative configuration, and reliable upgrades—which, in theory, make it an excellent choice. How well this theory holds up is something I’m eager to explore further. In this post, I’m documenting my steps for installing NixOS on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.proxmox.com&#x2F;&quot;&gt;Proxmox&lt;&#x2F;a&gt;, my chosen virtual environment solution for my home server, as deploying NixOS onto my home server should help me further explore using it for configuring my containers&lt;&#x2F;p&gt;
&lt;h2 id=&quot;loading-the-nixos-container-image&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#loading-the-nixos-container-image&quot; aria-label=&quot;Anchor link for: loading-the-nixos-container-image&quot;&gt;Loading the NixOS Container Image&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The first step is to load the NixOS container image onto my Proxmox host as a CT template, so that new containers can be created with this base image. At the time of writing, the latest NixOS LXD container image I found was version &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hydra.nixos.org&#x2F;build&#x2F;276346783&quot;&gt;276346783&lt;&#x2F;a&gt;, but you’ll likely want to download the most recent image available &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hydra.nixos.org&#x2F;job&#x2F;nixos&#x2F;release-24.05&#x2F;nixos.lxdContainerImage.x86_64-linux&quot;&gt;here&lt;&#x2F;a&gt;. You’ll need to copy the URL under &#x27;Link&#x27; for the system tarball; for my build, this was &lt;code&gt;https:&#x2F;&#x2F;hydra.nixos.org&#x2F;build&#x2F;276346783&#x2F;download&#x2F;1&#x2F;nixos-system-x86_64-linux.tar.xz&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Next, in Proxmox, I loaded the container using the &#x27;Download from URL&#x27; button on the &quot;CT Templates&quot; page for my hosts local storage. Clicking on the button a modal will open up, enter the URL, and gave it a descriptive name, for example I used &#x27;nixos-2024-10-25-system-x86_64-linux.tar.xz&#x27;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;create-a-nixos-container&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#create-a-nixos-container&quot; aria-label=&quot;Anchor link for: create-a-nixos-container&quot;&gt;Create a NixOS Container&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;At first, I attempted to configure a new container through the Proxmox UI. However, based on various resources I found online, there were some issues with this approach, so I ultimately needed to create a new container via the command line.&lt;&#x2F;p&gt;
&lt;p&gt;After referencing the NixOS Wiki and other sources, I came up with the following command to create a new container:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pct create &amp;quot;$(pvesh get &#x2F;cluster&#x2F;nextid)&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --arch amd64 \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;quot;local-lvm:vztmpl&#x2F;nixos-2024-10-25-system-x86_64-linux.tar.xz&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --ostype unmanaged \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --description nixos \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --hostname &amp;quot;thermic&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --net0 name=eth0,bridge=vmbr0,ip=dhcp,firewall=1 \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --storage &amp;quot;local-lvm&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --memory &amp;quot;2048&amp;quot; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --rootfs local-lvm:10 \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --unprivileged 1 \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --features nesting=1 \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --cmode console \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --onboot 1 \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --start 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Some of these settings are specific to my setup and may differ for yours, but hopefully, this provides a helpful starting point.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;logging-into-nixos&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#logging-into-nixos&quot; aria-label=&quot;Anchor link for: logging-into-nixos&quot;&gt;Logging into NixOS&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;NixOS should now be running as an LXC container on Proxmox. If you navigate to the console, you may initially see a black screen, which might seem odd. However, simply press &#x27;Enter,&#x27; and you&#x27;ll be prompted with a login screen. Enter the username &#x27;root&#x27; and press &#x27;Enter&#x27; again to log into your fresh NixOS container.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#next-steps&quot; aria-label=&quot;Anchor link for: next-steps&quot;&gt;Next Steps&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now that I have the container up and running, my next steps are to create a configuration to install PostgreSQL, setup any monitoring I need and to set up and run a Go app I’ve been developing.&lt;&#x2F;p&gt;
&lt;p&gt;Looking ahead, I’m also interested in building a preconfigured NixOS image with all necessary configuration and setup, ready to run any application I choose.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;resources&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#resources&quot; aria-label=&quot;Anchor link for: resources&quot;&gt;Resources&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;Proxmox_Linux_Container&quot;&gt;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;Proxmox_Linux_Container&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Why Go uses composition over inheritance</title>
          <pubDate>Mon, 24 Jun 2024 15:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/go-and-composition-over-inheritance/</link>
          <guid>https://aran.dev/posts/go-and-composition-over-inheritance/</guid>
          <description xml:base="https://aran.dev/posts/go-and-composition-over-inheritance/">&lt;p&gt;Recently, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;andrewcairns&quot;&gt;Andrew Cairns&lt;&#x2F;a&gt; shared a post on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;andrewcairns&#x2F;status&#x2F;1801965389267521846&quot;&gt;Twitter&#x2F;X&lt;&#x2F;a&gt; featuring a shortened written version of his fantastic video, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=HNzP1aLAffM&quot;&gt;Composition over Inheritance Explained by Games&lt;&#x2F;a&gt;. As I read through the article and watched the video again, I reflected on how, in Go, we don’t face the same concerns.&lt;&#x2F;p&gt;
&lt;p&gt;Go&#x27;s design philosophy emphasises simplicity, clarity, and efficiency, strongly favouring composition over inheritance. By leveraging composition, Go encourages the use of small, self-contained components that can be easily combined to build complex systems. This approach not only enhances code readability and maintainability but also provides greater flexibility and control over implementation details.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, Go&#x27;s interface system, which supports implicit implementation, further promotes the modular and reusable nature of compositional design, helping developers avoid common pitfalls associated with inheritance, such as the fragile base class problem.&lt;&#x2F;p&gt;
&lt;p&gt;Go&#x27;s approach to inheritance is fundamentally different from the traditional class-based inheritance found in languages like PHP, TypeScript, and Java. Instead of relying on class hierarchies, Go uses structs and interfaces to achieve behaviour composition. Behaviour is added to structs through methods, and interfaces are implemented implicitly, allowing for flexible and decoupled design.&lt;&#x2F;p&gt;
&lt;p&gt;While traditional object-oriented programming languages like PHP, TypeScript, and Java rely on explicit interfaces, class hierarchies, abstract classes, and mixins or traits to achieve polymorphism and reuse, Go uses implicit interfaces and struct embedding to compose behaviours and decouple the definition of an interface from its implementation. This approach avoids the complexities and rigidity of deep inheritance hierarchies, promoting better modularity, readability, and maintainability. Go&#x27;s straightforward and flexible interface system makes its design simpler and more efficient, fostering robust and adaptable codebases.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;composition-vs-inheritance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#composition-vs-inheritance&quot; aria-label=&quot;Anchor link for: composition-vs-inheritance&quot;&gt;Composition vs Inheritance&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Composition&lt;&#x2F;strong&gt; and &lt;strong&gt;Inheritance&lt;&#x2F;strong&gt; are two fundamental concepts in object-oriented programming (OOP).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Composition&lt;&#x2F;strong&gt; involves building complex types by combining objects of other types. It promotes the creation of small, reusable components that can be assembled to provide more complex functionalities. This approach encourages flexibility and modularity, as components can be easily replaced or modified without affecting the entire system.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Inheritance&lt;&#x2F;strong&gt;, on the other hand, allows a class to inherit properties and methods from another class. This establishes a hierarchical relationship between the parent (superclass) and child (subclass), enabling code reuse and the extension of existing functionality. However, inheritance can lead to rigid structures and tight coupling, making systems harder to modify and maintain.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-go-implements-composition-and-inheritance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-go-implements-composition-and-inheritance&quot; aria-label=&quot;Anchor link for: how-go-implements-composition-and-inheritance&quot;&gt;How Go Implements Composition and Inheritance&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have an understanding of composition and inheritance, let&#x27;s take a look at how Go implements composition through the use of structs and interfaces. Go allows developers to create flexible and modular code without relying on traditional inheritance hierarchies.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;structs-and-embedding&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#structs-and-embedding&quot; aria-label=&quot;Anchor link for: structs-and-embedding&quot;&gt;Structs and Embedding&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In Go, structs are the primary way to define complex data types. A struct is a sequence of named elements, called fields, each field has a name and a type. Field names can be specified explicitly or implicitly using embedding. Composition is achieved by embedding one struct within another either named explicitly or implicitly. This allows the embedded struct to become a part of the containing struct, effectively allowing the latter to inherit the fields and methods of the former.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Base&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Num&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Container&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    Base&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Embedded struct&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Str&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;  string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this example, we have two structs: &lt;code&gt;Base&lt;&#x2F;code&gt; and &lt;code&gt;Container&lt;&#x2F;code&gt;. The &lt;code&gt;Base&lt;&#x2F;code&gt; struct is embedded into the &lt;code&gt;Container&lt;&#x2F;code&gt; struct, allowing users of the &lt;code&gt;Container&lt;&#x2F;code&gt; struct to access the &lt;code&gt;Num&lt;&#x2F;code&gt; field found in the &lt;code&gt;Base&lt;&#x2F;code&gt; struct. A working example can be found at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gobyexample.com&#x2F;struct-embedding&quot;&gt;Go by Example: Struct Embedding&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;interfaces-and-implicit-implementation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#interfaces-and-implicit-implementation&quot; aria-label=&quot;Anchor link for: interfaces-and-implicit-implementation&quot;&gt;Interfaces and Implicit Implementation&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Go uses an implicit interface implementation, which means that Go types must implement a set of method signatures defined in a Go interface in order to be considered to conform to that interface. Unlike other languages where you must explicitly declare that a type implements an interface, if a type has all the methods defined in an interface, it is considered to implement that interface automatically.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Geometry&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; interface&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    Area&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; float64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    Perimeter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; float64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Rectangle&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    width&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; height&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; float64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Circle&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    radius&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; float64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;r &lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Rectangle&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Area&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; float64&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;width&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;r &lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Rectangle&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Perimeter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; float64&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;width&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;r&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;height&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;c &lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Circle&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Area&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; float64&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Pi&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;radius&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;radius&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;c &lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Circle&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Perimeter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; float64&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; math&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Pi&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; c&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;radius&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Measure&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;g&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Geometry&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Area&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Perimeter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here we have two structs: &lt;code&gt;Circle&lt;&#x2F;code&gt; and &lt;code&gt;Rectangle&lt;&#x2F;code&gt;. These both have &lt;code&gt;Area&lt;&#x2F;code&gt; and &lt;code&gt;Perimeter&lt;&#x2F;code&gt; methods, meaning both structs implement the &lt;code&gt;Geometry&lt;&#x2F;code&gt; interface and can be passed into the &lt;code&gt;Measure&lt;&#x2F;code&gt; method. A working example can be found at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gobyexample.com&#x2F;interfaces&quot;&gt;Go By Example: Interfaces&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Go&#x27;s design philosophy emphasises simplicity, clarity, and efficiency. This is clearly reflected in its preference for composition over inheritance. By encouraging the use of small, self-contained components, Go enables developers to build flexible and modular systems. This compositional approach not only enhances code readability and maintainability but also provides greater control over implementation details, resulting in more adaptable and robust codebases.&lt;&#x2F;p&gt;
&lt;p&gt;The simplicity that Go brings to this design is evident in the code produced with the language. Without the need to worry about class hierarchies and architectural issues like the fragile base class problem, developers can focus more on implementation.&lt;&#x2F;p&gt;
&lt;p&gt;The use of structs and embedding, combined with Go&#x27;s interface system that supports implicit implementation, avoids the pitfalls of deep inheritance hierarchies found in traditional object-oriented programming languages like PHP, TypeScript, and Java. This makes Go&#x27;s approach to software design both innovative and practical.&lt;&#x2F;p&gt;
&lt;p&gt;For those looking to delve deeper into these concepts, exploring the resources mentioned below can provide valuable insights. By embracing Go’s compositional model, developers can create more maintainable, scalable, and efficient software systems, aligning with the modern demands of software development.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;benefits-of-go-s-composition-and-inheritance-approach&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#benefits-of-go-s-composition-and-inheritance-approach&quot; aria-label=&quot;Anchor link for: benefits-of-go-s-composition-and-inheritance-approach&quot;&gt;Benefits of Go&#x27;s Composition and Inheritance Approach&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Flexibility&lt;&#x2F;strong&gt;: By composing types using struct embedding and implicit interfaces, Go provides greater flexibility in how objects are constructed, interact, and are used.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Modularity&lt;&#x2F;strong&gt;: Components can be developed, tested, and maintained independently, promoting cleaner and more modular code.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Simplicity&lt;&#x2F;strong&gt;: Avoiding deep inheritance hierarchies keeps the codebase simpler and easier to understand.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Reusability&lt;&#x2F;strong&gt;: Methods and functionalities can be reused across different types through embedding and interfaces without the constraints of traditional inheritance.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;further-reading-and-resources&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#further-reading-and-resources&quot; aria-label=&quot;Anchor link for: further-reading-and-resources&quot;&gt;Further Reading and Resources&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;For further research and resources, consider these sources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;martinfowler.com&#x2F;books&#x2F;eaa.html&quot;&gt;Patterns of Enterprise Application Architecture&lt;&#x2F;a&gt; by Martin Fowler, which covers subjects such as Inheritance, Class Table Inheritance and more.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;andrewcairns&quot;&gt;Andrew Cairns&lt;&#x2F;a&gt; fantastic &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=HNzP1aLAffM&quot;&gt;Composition over Inheritance Explained by Games&lt;&#x2F;a&gt; video.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Composition_over_inheritance&quot;&gt;Composition over inheritance&lt;&#x2F;a&gt; on Wikipedia&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gopl.io&#x2F;&quot;&gt;The Go Programming Language&lt;&#x2F;a&gt; by Alan A. A. Donovan and Brian W. Kernighan, which provides comprehensive coverage of Go’s design and idioms.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go.dev&#x2F;doc&#x2F;effective_go&quot;&gt;Effective Go&lt;&#x2F;a&gt;, an official document that offers guidelines and best practices for writing clear and idiomatic Go code.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gobyexample.com&#x2F;struct-embedding&quot;&gt;Go by Example: Struct Embedding&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gobyexample.com&#x2F;interfaces&quot;&gt;Go By Example: Interfaces&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.codecademy.com&#x2F;resources&#x2F;docs&#x2F;go&#x2F;composition&quot;&gt;codecademy: Go Composition&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Devlog 2 - Eventbus and SaaS model</title>
          <pubDate>Sat, 01 Jun 2024 12:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/stria-devlog/devlog-2-eventbus-and-saas-model/</link>
          <guid>https://aran.dev/posts/stria-devlog/devlog-2-eventbus-and-saas-model/</guid>
          <description xml:base="https://aran.dev/posts/stria-devlog/devlog-2-eventbus-and-saas-model/">&lt;p&gt;It&#x27;s been a while since I wrote a devlog. I aim to do these more regularly, but I got busy, sidetracked, and simply forgot. I kept reminding myself to get back to it but never did.&lt;&#x2F;p&gt;
&lt;p&gt;In today’s devlog, I want to discuss the custom event bus I&#x27;ve written in Go to handle internal event delivery within my codebase. I have numerous events that need to trigger various actions, so this was a crucial component. Additionally, I want to touch on pivoting and changing my mind regarding the product&#x27;s direction, whether it pertains to internal system components, the development approach, or other aspects.&lt;&#x2F;p&gt;
&lt;p&gt;Initially, I aimed to build this product as a self-hosted solution with a perpetual licence, and that remains a goal. However, I&#x27;ve also decided to support, or potentially support, a SaaS model in the future. For this reason, I&#x27;ve shifted from using SQLite to Postgres.&lt;&#x2F;p&gt;
&lt;p&gt;SQLite is a fantastic product and database, and I will undoubtedly use it for future projects. However, the current product is becoming more complex, and supporting a SaaS model introduces the challenge of managing tenanted SQLite databases. While this is likely feasible, especially with tools like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;turso.tech&#x2F;libsql&quot;&gt;libSQL&lt;&#x2F;a&gt;, I prefer to avoid this complexity for now. For a simpler product, I might explore libSQL as a solution.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;event-bus&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#event-bus&quot; aria-label=&quot;Anchor link for: event-bus&quot;&gt;Event Bus&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Integrating an external system like Stripe into an application can be complex. However, if you only need to respond to a handful of events and perform specific tasks based on those events, it can be straightforward. For example, you receive a webhook for a charge, update a user&#x27;s subscription within your app, and you are done.&lt;&#x2F;p&gt;
&lt;p&gt;In my situation, I wanted to consume as many Stripe events as possible and build custom workflows based on multiple events and scenarios. This required a more robust solution. My goal was to provide users with the ability to trigger custom workflows, which necessitated access to as many events as Stripe emits. As of writing this, Stripe can emit 229 different event types, and I aim to support as many custom workflows as possible.  Writing handling logic for all of these events would be complex, so I am utilising code generation and an internal event bus library that I&#x27;ve built to simplify this process.&lt;&#x2F;p&gt;
&lt;p&gt;The event bus utilises the publish&#x2F;subscribe design pattern to ensure loose coupling and separation of concerns between components. It allows any service within my codebase to easily publish or subscribe to events using a simple function call with the appropriate Golang struct. This enables any service to subscribe to a particular event effortlessly.&lt;&#x2F;p&gt;
&lt;p&gt;For example the subscription for an event containing the &lt;code&gt;stripe.Change&lt;&#x2F;code&gt; entity it would look something like this.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;eventbus.Subscribe[*stripeevents.Event[stripe.Charge]]()(eventbus.HandlerFunc[*stripeevents.Event[stripe.Charge]](s.HandleChargeEvent))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This event can be triggered by 6 of the 229 &lt;code&gt;stripe.EventType&lt;&#x2F;code&gt; we use a switch statement to catch these events publishing the event in the correct stripe struct. In this example we&#x27;re publishing a &lt;code&gt;stripe.Charge&lt;&#x2F;code&gt; entity.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;case stripe.EventTypeChargeExpired, stripe.EventTypeChargeFailed, stripe.EventTypeChargePending, stripe.EventTypeChargeRefundUpdated, stripe.EventTypeChargeRefunded, stripe.EventTypeChargeSucceeded, stripe.EventTypeChargeUpdated:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    if err := publish(ctx, data, stripe.Charge{}, inEventType); err != nil {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        slog.Error(&amp;quot;publishing event&amp;quot;, &amp;quot;event_type&amp;quot;, inEventType, &amp;quot;error&amp;quot;, err)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        if errors.Is(err, ErrEventDataParsing) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            return http.StatusBadRequest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        return http.StatusInternalServerError&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The publisher is far simpler, requiring just a simple call to the generic Publish function:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;eventbus.Publish[Event[T]]()(ctx, e)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Currently, the library relies on the singleton pattern, which means there is a single instance of the event bus shared across the entire application. This approach simplifies access and management of events, but it can also introduce limitations in flexibility and scalability. In the future, I may refactor this to support multiple instances to better handle different use cases and improve modularity. However, in its current form, it suits my needs.&lt;&#x2F;p&gt;
&lt;p&gt;Eventually, I plan to open-source the event bus library, but I feel like right now it lacks some features that I want, such as middleware support and improved concurrent handling of event handlers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;saas-model&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#saas-model&quot; aria-label=&quot;Anchor link for: saas-model&quot;&gt;SaaS Model&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve decided to potentially support a SaaS model of the product earlier in the development process. While I could have probably built this using the original database choice, SQLite, it presented complexities that I preferred to avoid at this stage. Although &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;turso.tech&#x2F;libsql&quot;&gt;libSQL&lt;&#x2F;a&gt; might have simplified some aspects, it introduced its own challenges. Despite being a fork of SQLite, libSQL lacks the extensive support and documentation that SQLite offers, making it harder to ensure everything is implemented correctly. The differences between libSQL and SQLite added another layer of difficulty, complicating the alignment between the two.&lt;&#x2F;p&gt;
&lt;p&gt;In the future, I might explore and utilise the multi-tenant features of libSQL for another project. However, given the current complexities I&#x27;m already managing, I decided it was best to reduce potential issues by not incorporating libSQL at this time.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next-devlog&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#next-devlog&quot; aria-label=&quot;Anchor link for: next-devlog&quot;&gt;Next devlog&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In my next devlog, I hope to demo some of the onboarding process for my application. When a user signs up, we need to request the Stripe API Key and Stripe Webhook Secret from the user. These values must be stored securely in the database to prevent accidental leaks and ensure that bad actors cannot read them, even if they are accidentally returned to the user. Access to the secrets will be managed through specific functions that handle decryption securely.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>wip: wifi-qr Zig Rewrite</title>
          <pubDate>Mon, 27 May 2024 21:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/wip-wifi-qr-zig-rewrite/</link>
          <guid>https://aran.dev/posts/wip-wifi-qr-zig-rewrite/</guid>
          <description xml:base="https://aran.dev/posts/wip-wifi-qr-zig-rewrite/">&lt;p&gt;A couple of years ago, I frequently needed to join WiFi networks on external devices for testing. Entering long, randomly generated passwords multiple times per day became quite tedious. To address this, I created a simple bash script that queries the macOS Keychain to retrieve the current WiFi network&#x27;s password and generates a QR code to simplify the process. The project can be found on my GitHub: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;wifi-qr&quot;&gt;aranw&#x2F;wifi-qr&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The functionality of the project has always been limited due to using Bash. I&#x27;ve wanted to rewrite the project and have always thought it would be a great candidate for rewriting in various languages due to its simplicity and number of features.&lt;&#x2F;p&gt;
&lt;p&gt;Zig is a language I&#x27;ve been interested in but hadn&#x27;t found a project to use it with until recently. Last year, I explored the excellent &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;ziglings&#x2F;exercises#&quot;&gt;ziglings&#x2F;exercises&lt;&#x2F;a&gt; project, which offers a delightful learning experience. You solve a series of broken programmes, using hints to fix them and gradually learn the language. Over the May bank holiday, I decided to revisit the Ziglings exercises to see what had changed in Zig, as it is an actively developed language. Ziglings is a great way to learn about these updates. After completing just over half of the exercises, I wanted to try Zig with a real project and thought my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;wifi-qr&quot;&gt;wifi-qr&lt;&#x2F;a&gt; project would be a perfect candidate for a rewrite.&lt;&#x2F;p&gt;
&lt;p&gt;Rewriting the initial requirements for the project in Zig was fairly straightforward. Using articles, Ziglings, and code examples from projects like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mitchellh.com&#x2F;ghostty&quot;&gt;Ghostty&lt;&#x2F;a&gt;, I managed to implement the logic for retrieving the password from the macOS Keychain. The QR code generation part proved to be the most challenging. Initially, I tried to integrate a C QR encoding library from GitHub (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nayuki&#x2F;QR-Code-generator&#x2F;&quot;&gt;nayuki&#x2F;QR-Code-generator&lt;&#x2F;a&gt;), but I struggled with the C to Zig calls. After numerous failed attempts, I eventually discovered a Zig implementation (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bensengupta&#x2F;qr&quot;&gt;bensengupta&#x2F;qr&lt;&#x2F;a&gt;), which was straightforward to integrate and use.&lt;&#x2F;p&gt;
&lt;p&gt;Overall, the experience was really enjoyable, and the language was a joy to work with. Currently, my rewrite does not have all the features of the bash script, and I will need to add some missing features to achieve feature parity between the two versions. There are also a few additional features I always wanted to add but found difficult to implement in Bash, which should be much easier in a more feature-rich programming language like Zig.&lt;&#x2F;p&gt;
&lt;p&gt;Now that I have the basic functionality working, the next step is to implement additional features. These include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Linux support&lt;&#x2F;strong&gt;: Ensuring the script can retrieve WiFi passwords and generate QR codes on Linux systems.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Automatic WiFi network detection&lt;&#x2F;strong&gt;: Automatically retrieving the current WiFi network if none is provided via the CLI.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Windows support&lt;&#x2F;strong&gt;: Extending the script to work on Windows platforms.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Multiple QR code formats&lt;&#x2F;strong&gt;: Enabling the generation of QR codes in various formats.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;My progress on the rewrite can currently be found on a branch in my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;wifi-qr&#x2F;tree&#x2F;feat&#x2F;zig-rewrite&quot;&gt;aran&#x2F;wifi-qr&lt;&#x2F;a&gt; repository.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Devlog 1 - Foundations</title>
          <pubDate>Wed, 15 May 2024 18:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/stria-devlog/devlog-1-foundations/</link>
          <guid>https://aran.dev/posts/stria-devlog/devlog-1-foundations/</guid>
          <description xml:base="https://aran.dev/posts/stria-devlog/devlog-1-foundations/">&lt;p&gt;Over the past few months, I&#x27;ve played around with multiple side projects. While working on these and the Stripe integrations, I realised I&#x27;d been through this process before—integrating Stripe, writing custom logic to handle Stripe Webhooks and building out features driven from the integration and wondered whether I had to repeat this each time.&lt;&#x2F;p&gt;
&lt;p&gt;I decided to create a standalone application that provides all the necessary functionality for my side projects. This application would sit alongside any project, offering hooks, notifications, and other essential features. I&#x27;ve named the project &quot;stria&quot; for now, though this might change. I&#x27;m considering selling it under a perpetual licence with free minor&#x2F;patch updates. Major releases would be available as paid upgrades for existing users.&lt;&#x2F;p&gt;
&lt;p&gt;These devlogs aim to keep me on track. In the first entry, I will cover the technology choices for the project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;technology-choices&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#technology-choices&quot; aria-label=&quot;Anchor link for: technology-choices&quot;&gt;Technology Choices&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m trying to minimise external dependencies and rely primarily on the Go standard library.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;config&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#config&quot; aria-label=&quot;Anchor link for: config&quot;&gt;Config&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Configuration is managed through YAML config files and environment variables. Initially, I used  &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkl-lang.org&quot;&gt;Pkl&lt;&#x2F;a&gt;, but it required the Pkl CLI tool for parsing. While Pkl shows promise, it currently lacks a pure Go implementation and requires the Java CLI tool, which is a significant dependency. Therefore, I opted not to include it in my applications for now.&lt;&#x2F;p&gt;
&lt;p&gt;The current YAML-based solution uses &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gopkg.in&#x2F;yaml.v3&quot;&gt;gopkg.in&#x2F;yaml.v3&lt;&#x2F;a&gt; and the standard library&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;os#ExpandEnv&quot;&gt;os.ExpandEnv&lt;&#x2F;a&gt; function. This transforms any &lt;code&gt;${SOME_ENV_VAR}&lt;&#x2F;code&gt; or &lt;code&gt;$SOME_ENV_VAR&lt;&#x2F;code&gt; in the YAML file before unmarshalling it into the application&#x27;s config structure.&lt;&#x2F;p&gt;
&lt;p&gt;Although this is a simple library I&#x27;m proud of the solution that this has evolved into it&#x27;s easy to understand and not overly complicated. The library can be found on Github &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;yamlcfg&quot;&gt;aranw&#x2F;yamlcfg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;database-sqlite&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#database-sqlite&quot; aria-label=&quot;Anchor link for: database-sqlite&quot;&gt;Database: SQLite&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;For data storage I&#x27;ve picked SQLite or more specifically &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;turso.tech&#x2F;&quot;&gt;Turso&lt;&#x2F;a&gt;&#x27;s libSQL fork. The libSQL fork of SQLite means I require CGO. Right now I&#x27;m still unsure if this is the right choice and may ultimately change this part I could switch back to SQLite or even PostgreSQL. I use the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sqlc-dev&#x2F;sqlc&quot;&gt;sqlc&lt;&#x2F;a&gt; library to code generate a bunch of the service layer code so if needed I think it should be fairly easy to swap out.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;migrations&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#migrations&quot; aria-label=&quot;Anchor link for: migrations&quot;&gt;Migrations&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;golang-migrate&#x2F;migrate&quot;&gt;go-migrate&#x2F;migrate&lt;&#x2F;a&gt; is my current solution for migrations. While it supports &quot;up&quot; and &quot;down&quot; migrations, I don&#x27;t agree with the concept of &quot;down&quot; migrations. They often create more issues than they resolve, especially since not all database operations can be safely undone and deleted data is often irrecoverable.&lt;&#x2F;p&gt;
&lt;p&gt;Ideally, I prefer up-only migrations. If changes need to be undone, a new up migration should be applied to fix the issue. In the future, I hope to develop a custom library to address this need.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;frontend-templ&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#frontend-templ&quot; aria-label=&quot;Anchor link for: frontend-templ&quot;&gt;Frontend: Templ&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;One of the core goals of the project is to ensure easy distribution. To achieve this, I aimed to integrate both the frontend and backend into a single binary. Go&#x27;s standard library for templating has its challenges, so I opted for the fantastic &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;templ.guide&#x2F;&quot;&gt;templ&lt;&#x2F;a&gt; library for the frontend. Coupled with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;htmx.org&#x2F;&quot;&gt;HTMX&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alpinejs.dev&#x2F;&quot;&gt;alpinejs&lt;&#x2F;a&gt;, this setup provides excellent interactivity and a modern user interface.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;http-router&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#http-router&quot; aria-label=&quot;Anchor link for: http-router&quot;&gt;HTTP Router&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Currently, I use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go-chi.io&#x2F;#&#x2F;&quot;&gt;chi&lt;&#x2F;a&gt; as the solution for the HTTP router. Chi is a lightweight and idiomatic HTTP router that easily integrates with other Go HTTP handlers, facilitating the composition of functionality. In the long term, I&#x27;d like to replace this with Go 1.22&#x27;s new and improved HTTP multiplexer. However, I still need to devise an effective approach for transitioning from Chi to this new multiplexer.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;sessions&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#sessions&quot; aria-label=&quot;Anchor link for: sessions&quot;&gt;Sessions&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Having encrypted cookies to store session state is vital. To address this, I chose the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;alexedwards&#x2F;scs&quot;&gt;alexedwards&#x2F;scs&lt;&#x2F;a&gt; library. I selected this library due to its comprehensive feature set and support for a wide range of storage backends for sessions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;additional-features&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#additional-features&quot; aria-label=&quot;Anchor link for: additional-features&quot;&gt;Additional Features&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In the future I have other features that I want to add and integrate and those will require other technology choices to be hard. I have some of them decided but some are still up in the air as to what I want to use.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-next&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-next&quot; aria-label=&quot;Anchor link for: what-is-next&quot;&gt;What is next?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve built a strong foundation and am progressing well with the project. Over the next few weeks, I aim to make further advancements and document these developments. However, my progress is currently slow since I have to fit in development whenever possible—on weekends, before and after work, and during holidays. Despite these challenges, I&#x27;m excited about tackling the upcoming tasks, which include implementing webhook receivers, setting up password authentication, automating various processes, and syncing workflows. I&#x27;m looking forward to sharing more updates as I continue to refine and expand the project.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>yamlcfg: a Go library for YAML and environment variable config</title>
          <pubDate>Tue, 14 May 2024 16:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/yamlcfg-library/</link>
          <guid>https://aran.dev/posts/yamlcfg-library/</guid>
          <description xml:base="https://aran.dev/posts/yamlcfg-library/">&lt;p&gt;Introducing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;yamlcfg&quot;&gt;yamlcfg&lt;&#x2F;a&gt;, a new library designed to simplify YAML and Environment Variable configuration in Golang applications.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m excited to introduce yamlcfg, a new library designed to simplify YAML and Environment Variable configuration in Golang applications&lt;&#x2F;p&gt;
&lt;p&gt;This library utilises  &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gopkg.in&#x2F;yaml.v3&quot;&gt;gopkg.in&#x2F;yaml.v3&lt;&#x2F;a&gt; and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;os#ExpandEnv&quot;&gt;os.ExpandEnv&lt;&#x2F;a&gt; function from Go’s standard library to replace any &lt;code&gt;${SOME_ENV_VAR}&lt;&#x2F;code&gt; or &lt;code&gt;$SOME_ENV_VAR&lt;&#x2F;code&gt; placeholders in a YAML file before unmarshalling the content into the application&#x27;s configuration structure&lt;&#x2F;p&gt;
&lt;p&gt;Currently, I&#x27;m focusing on keeping this library lightweight and straightforward. However, there are some enhancements in the pipeline. The next planned feature is to enable loading configuration files from &lt;code&gt;embed.FS&lt;&#x2F;code&gt;. I anticipate additional features will be incorporated as the project evolves&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Encrypting and decrypting with Go&#x27;s crypto&#x2F;aes and crypto&#x2F;cipher</title>
          <pubDate>Wed, 08 May 2024 20:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/using-golang-crypto-aes-and-crypto-cipher-packages/</link>
          <guid>https://aran.dev/posts/using-golang-crypto-aes-and-crypto-cipher-packages/</guid>
          <description xml:base="https://aran.dev/posts/using-golang-crypto-aes-and-crypto-cipher-packages/">&lt;p&gt;In an upcoming side project, I needed to encrypt and decrypt values stored in a SQLite database. This article offers an introductory overview of encryption and decryption practices but is not intended as a comprehensive guide. Encryption is a complex field that requires careful implementation to ensure data security. While searching for resources on this topic, I encountered inconsistencies and issues with the examples provided, prompting me to document what I learned for future reference.&lt;&#x2F;p&gt;
&lt;p&gt;For this example we will use the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;crypto&#x2F;aes&quot;&gt;&lt;code&gt;crypto&#x2F;aes&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;crypto&#x2F;cipher&quot;&gt;&lt;code&gt;crypto&#x2F;cipher&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;encoding&#x2F;base64&quot;&gt;&lt;code&gt;encoding&#x2F;base64&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; Golang packages to handle encrypting&#x2F;decrypting and encoding&#x2F;decoding our secrets.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;encrypting&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#encrypting&quot; aria-label=&quot;Anchor link for: encrypting&quot;&gt;Encrypting&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In cryptography, encryption is the process of concealing information by converting it into a coded format that can only be decoded and read by someone possessing the correct key. This practice, dating back to ancient civilizations such as Greece and Rome, employed various methods to secure communications. Today, encryption remains a critical component of modern software development, where implementing it correctly is essential to prevent the accidental leakage of sensitive information.&lt;&#x2F;p&gt;
&lt;p&gt;To encrypt in Go, we will use multiple packages from the &lt;code&gt;crypto&lt;&#x2F;code&gt; package, specifically the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;crypto&#x2F;aes&quot;&gt;&lt;code&gt;crypto&#x2F;aes&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;crypto&#x2F;cipher&quot;&gt;&lt;code&gt;crypto&#x2F;cipher&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; packages.&lt;&#x2F;p&gt;
&lt;p&gt;To begin with, a secret key is essential for encryption. In this post, we will focus on symmetric key encryption, though an alternative method involves the use of public-key encryption which we might explore in a future post. For this example, I used a random string generator to generate a 32-byte-long string from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.random.org&#x2F;strings&#x2F;&quot;&gt;random.org&#x2F;strings&lt;&#x2F;a&gt;. In a production environment, this would likely be configured outside of our application and passed in, but for our simple example, we will hard code this value.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;package main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;quot;crypto&#x2F;aes&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;quot;crypto&#x2F;cipher&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;quot;crypto&#x2F;rand&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;quot;encoding&#x2F;base64&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;quot;io&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;quot;fmt&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;func Encrypt(secret, value string) (string, error) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	block, err := aes.NewCipher([]byte(secret))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	if err != nil {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		return &amp;quot;&amp;quot;, err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	plainText := []byte(value)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&#x2F;&#x2F; The IV needs to be unique, but not secure. Therefore it&amp;#39;s common to&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&#x2F;&#x2F; include it at the beginning of the ciphertext.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	ciphertext := make([]byte, aes.BlockSize+len(plainText))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	iv := ciphertext[:aes.BlockSize]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	if _, err := io.ReadFull(rand.Reader, iv); err != nil {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		return &amp;quot;&amp;quot;, err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	stream := cipher.NewCFBEncrypter(block, iv)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	stream.XORKeyStream(ciphertext[aes.BlockSize:], plainText)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	return base64.RawStdEncoding.EncodeToString(ciphertext), nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;func main() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	in := &amp;quot;Hello World&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	secret := &amp;quot;uMnkGpBn9q4nnwQws8NSRhpXpFdQDBXg&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	encrypted, err := Encrypt(secret, in)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	if err != nil {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		panic(err)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	fmt.Printf(&amp;quot;Encrypted: %s\n&amp;quot;, encrypted)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you execute the command &lt;code&gt;go run main.go&lt;&#x2F;code&gt; from the example above, the output will be something like &lt;code&gt;Encrypted: 2PK2Q&#x2F;Lj8OTU2j+Ak3qjaHiPJX8KMamxtIO2&lt;&#x2F;code&gt;, with the encrypted value varying with each run.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s delve deeper into what happens inside the Encrypt function. First, we create a &lt;code&gt;cipher.Block&lt;&#x2F;code&gt; by calling &lt;code&gt;aes.NewCipher&lt;&#x2F;code&gt; and providing it with our secret key. This &lt;code&gt;cipher.Block&lt;&#x2F;code&gt; enables us to encrypt and decrypt input. The length of our secret key determines the AES encryption standard used: 16, 24, or 32 bytes for AES-128, AES-192, or AES-256, respectively.&lt;&#x2F;p&gt;
&lt;p&gt;Next, we need to determine where to store our encrypted input, also known as ciphertext. Relying solely on our secret key for encryption is insufficient; we also require an element of randomness. This is where the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Initialization_vector&quot;&gt;initialization vector (IV)&lt;&#x2F;a&gt; comes into play. Typically, the IV must be random or pseudorandom, although in some cases, it only needs to be unpredictable or unique.&lt;&#x2F;p&gt;
&lt;p&gt;At this point we have everything we need to encrypt our input. This is accomplished using a &lt;code&gt;cipher.Stream&lt;&#x2F;code&gt;, which is created by calling &lt;code&gt;cipher.NewCFBEncrypter&lt;&#x2F;code&gt; with our &lt;code&gt;cipher.Block&lt;&#x2F;code&gt; and the initialization vector (IV). The &lt;code&gt;cipher.Stream&lt;&#x2F;code&gt; includes the &lt;code&gt;XORKeyStream&lt;&#x2F;code&gt; function, which takes a sequence of bytes (our input data) and mixes it with a sequence from the cipher&#x27;s key stream. This key stream is generated using the secret key. The &lt;code&gt;XORKeyStream&lt;&#x2F;code&gt; function effectively scrambles the input data by applying the XOR operation between the data bytes and the key stream bytes.&lt;&#x2F;p&gt;
&lt;p&gt;Once we have our encrypted string, the final step is to base64 encode it. This encoding allows us to safely store the values or transmit them via HTTP, ensuring compatibility and preventing data corruption during transfer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;decrypting&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#decrypting&quot; aria-label=&quot;Anchor link for: decrypting&quot;&gt;Decrypting&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have encryption set up, the next step is decrypting, which mirrors the encryption process but in reverse. First, we decode the base64-encoded ciphertext to retrieve the binary data. Next, we initialise our &lt;code&gt;cipher.Block&lt;&#x2F;code&gt; and extract the initialization vector (IV) from the start of the ciphertext. The remaining part is the actual encrypted data. The IV and &lt;code&gt;cipher.Block&lt;&#x2F;code&gt; are then used to create a &lt;code&gt;cipher.NewCFBDecrypter&lt;&#x2F;code&gt;, which provides a &lt;code&gt;cipher.Stream&lt;&#x2F;code&gt;. This stream performs the reverse XOR operation that was used during encryption, effectively decrypting the data.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;package main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;import (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;quot;crypto&#x2F;aes&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;quot;crypto&#x2F;cipher&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;quot;encoding&#x2F;base64&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;quot;errors&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;quot;fmt&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;func Decrypt(secret, value string) (string, error) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	ciphertext, err := base64.RawStdEncoding.DecodeString(value)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	if err != nil {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		return &amp;quot;&amp;quot;, fmt.Errorf(&amp;quot;decoding base64: %w&amp;quot;, err)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	block, err := aes.NewCipher([]byte(secret))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	if err != nil {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		return &amp;quot;&amp;quot;, err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&#x2F;&#x2F; The IV needs to be unique, but not secure. Therefore it&amp;#39;s common to&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&#x2F;&#x2F; include it at the beginning of the ciphertext.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	if len(ciphertext) &amp;lt; aes.BlockSize {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		return &amp;quot;&amp;quot;, errors.New(&amp;quot;ciphertext too short&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	iv := ciphertext[:aes.BlockSize]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	ciphertext = ciphertext[aes.BlockSize:]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	stream := cipher.NewCFBDecrypter(block, iv)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&#x2F;&#x2F; XORKeyStream can work in-place if the two arguments are the same.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	stream.XORKeyStream(ciphertext, ciphertext)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	return string(ciphertext), nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;func main() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	in := &amp;quot;p6e1TZTF7i50N4Q&#x2F;9CjvdKDG36ZemI6nZC70&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	secret := &amp;quot;uMnkGpBn9q4nnwQws8NSRhpXpFdQDBXg&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	decrypted, err := Decrypt(secret, in)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	if err != nil {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		panic(err)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	fmt.Printf(&amp;quot;Decrypted: %s\n&amp;quot;, decrypted)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;An interesting difference in the decryption process compared to encryption is that when calling the &lt;code&gt;stream.XORKeyStream&lt;&#x2F;code&gt; function, we pass the &lt;code&gt;ciphertext&lt;&#x2F;code&gt; variable as both arguments. This technique is used because &lt;code&gt;XORKeyStream&lt;&#x2F;code&gt; can modify the input data in-place, turning encrypted data back into plaintext.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The encryption and decryption processes are crucial for secure data handling in modern applications. By encrypting data, we transform sensitive information into a format that can be safely transmitted or stored. The subsequent decryption ensures that this data can be accurately and safely recovered by authorised parties. Utilising encoding schemes like base64 can further enhance the compatibility and manageability of encrypted data across different systems and environments. Employing these methods will be vital for the side project I am working on, and I am confident that this functionality can also be utilised in my day-to-day work in the future. In follow-up articles, I plan to explore public-key encryption and its potential uses.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re looking for a simpler, command-line approach to file encryption, check out my post on &lt;a href=&quot;&#x2F;posts&#x2F;age-encryption-guide&#x2F;&quot;&gt;age: a practical guide to modern file encryption&lt;&#x2F;a&gt; which covers the age tool as a modern GPG alternative.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>You probably don&#x27;t need to mock</title>
          <pubDate>Tue, 16 Apr 2024 09:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/you-probably-dont-need-to-mock/</link>
          <guid>https://aran.dev/posts/you-probably-dont-need-to-mock/</guid>
          <description xml:base="https://aran.dev/posts/you-probably-dont-need-to-mock/">&lt;p&gt;With the clickbait title out of the way, we can discuss why mocking may not be as crucial for Go testing as commonly perceived. Testing is vital to ensuring code reliability and functionality. However, the frequent use of mocks to replicate external dependencies can create a misleading sense of safety and test quality. While useful, mocks often fail to fully reflect the true complexity of system functionalities.&lt;&#x2F;p&gt;
&lt;p&gt;Go is praised for its simplicity and robust testing features, integrated into its language and toolchain, making testing straightforward. By integrating Docker with Go’s testing tools, developers can create tests that more closely replicate production environments, enhancing both maintainability and efficiency. This post explores the view that extensive mocking is unnecessary, proposing that combining Go with Docker can lead to more reliable and simpler tests. I aim to provide Go developers with insights and strategies to enhance their testing approaches, moving beyond the traditional reliance on mocks prevalent in other languages.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;limitations-of-mocks-in-testing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#limitations-of-mocks-in-testing&quot; aria-label=&quot;Anchor link for: limitations-of-mocks-in-testing&quot;&gt;Limitations of Mocks in Testing&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Consider the scenario of testing an application’s interaction with a database where developers use mocks to simulate the interactions between the database and your application. Initially effective, but as the application and its database interactions evolve—through added fields, complex queries, and schema changes—the team often spends substantial time updating mocks to match these developments, inadvertently introducing errors and fostering false confidence.&lt;&#x2F;p&gt;
&lt;p&gt;For instance, integrating technologies like Kafka for asynchronous data processing presents challenges. Mocks, while simplifying tests for such systems and the complexities around message delivery, ordering, and failure handling, may not accurately reflect real-world issues such as network latencies or partitioning challenges, leading to unanticipated production failures.&lt;&#x2F;p&gt;
&lt;p&gt;These incidents highlight the limitations of mocks: a critical bug slips into production due to subtle discrepancies in handling conditions like null values. Moreover, when production systems update, existing mocks may no longer accurately represent the interactions, leading to further bugs. An additional personal frustration with mocks is their tendency to cause test suites to expand, sometimes becoming more extensive than the actual code being tested.&lt;&#x2F;p&gt;
&lt;p&gt;These examples underscore the shortcomings of mocks in capturing the full complexity of real systems. Initially beneficial for their speed and isolation, mocks can lead to significant oversights, emphasising the need for testing methods that more accurately replicate the intricacies of production environments. Essentially, mocks test our assumptions about how a system operates, highlighting the critical need for these assumptions to closely align with the actual complexities of the systems under test.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;go-s-approach-to-testing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#go-s-approach-to-testing&quot; aria-label=&quot;Anchor link for: go-s-approach-to-testing&quot;&gt;Go&#x27;s Approach to Testing&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Go promotes simplicity and efficiency in its testing approach, contrasting with other languages that rely on external frameworks, libraries, and tooling. Go&#x27;s integrated &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;testing&quot;&gt;testing&lt;&#x2F;a&gt; package and tooling supports tests, benchmarks, and examples directly, simplifying setup and encouraging consistent test writing as part of development workflows. With Go, developers do not need to choose which test library or runner they want to use resulting in a consistent test environment across projects.&lt;&#x2F;p&gt;
&lt;p&gt;A key feature of Go is its use of interfaces for loose coupling, which simplifies testability. Interfaces in Go are implicitly implemented, allowing developers to inject specific implementations for testing, enhancing both code quality and maintainability.&lt;&#x2F;p&gt;
&lt;p&gt;Go also promotes testing practices that closely mirror real-world conditions. For interactions with databases or external systems like message queues, Go, combined with tools like Docker, enables testing against real instances, improving test reliability without the reliance on mocks. While this approach adds complexity, it simplifies certain aspects of testing by eliminating the need for extensive mock setups.&lt;&#x2F;p&gt;
&lt;p&gt;Leveraging Go&#x27;s interfaces and integrating practical tools for real-world testing, developers can achieve more dependable, maintainable tests and improve test quality. This method not only makes the testing process more efficient but also ensures greater confidence in the software’s resilience and functionality.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-go-example&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-go-example&quot; aria-label=&quot;Anchor link for: a-go-example&quot;&gt;A Go Example&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ve explored the limitations of mocks and Go&#x27;s testing philosophy. Now, let&#x27;s put these concepts into practice with some examples. We&#x27;ll examine how reliance on mocks can complicate test code, lead to lower quality tests and mask a bug that may not be caught before going to production.&lt;&#x2F;p&gt;
&lt;p&gt;Consider a simple example of a function that retrieves a leaderboard using Postgres. Typically, database logic is isolated in a separate database or repository package. This function could be a key dependency in our service layer, responsible for fetching the leaderboard for further actions, such as outputting it via an API or sending an email to a tournament winner.&lt;&#x2F;p&gt;
&lt;p&gt;Typically, we would start with a function signature like the following:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Score&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	UserID&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	Score&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;     int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	Rank&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;      int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Database&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    Conn&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;pgx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Conn&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;d &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Database&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; GetLeaderboard&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Score&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Service&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	DB&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Database&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;s &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Service&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; GetLeaderboard&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;However, the &lt;code&gt;Database&lt;&#x2F;code&gt; struct presents an immediate issue, as it would require us to develop an elaborate mock to replicate the functionality of &lt;code&gt;pgx.Conn&lt;&#x2F;code&gt; so that we could test it. What you would commonly find in situations like this is in our &lt;code&gt;Service&lt;&#x2F;code&gt; we replace the &lt;code&gt;Database&lt;&#x2F;code&gt; with an interface for handling the retrieval of scores from our database.&lt;&#x2F;p&gt;
&lt;p&gt;To accommodate our mocked example, we would modify our struct to use an interface as follows:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; LeaderboardRetriever&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; interface&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;	Get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Score&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Service&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	LBR&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; LeaderboardRetriever&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;s &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Service&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; GetLeaderboard&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ve introduced an additional layer of abstraction in our code, enabling us to substitute the &lt;code&gt;pgx.Conn&lt;&#x2F;code&gt; implementation detail with our mock.&lt;&#x2F;p&gt;
&lt;p&gt;At first glance, this new abstraction might seem innocuous, and it appears we can now easily test our Service and function. However, since we are retrieving a leaderboard from the database, our function depends heavily on a crucial part of the SQL query:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;SELECT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;    t&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;user_id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;    t&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;high_score&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;    RANK&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;OVER&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;ORDER BY&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; t&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;high_score&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; DESC&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span&gt; rank&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    SELECT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    user_id,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;    MAX&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;score) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span&gt; high_score&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    FROM&lt;&#x2F;span&gt;&lt;span&gt; scores&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    GROUP BY&lt;&#x2F;span&gt;&lt;span&gt; user_id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;ORDER BY&lt;&#x2F;span&gt;&lt;span&gt; rank&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This query is essential as it fetches the highest score for each user and assigns a rank based on those scores in descending order.&lt;&#x2F;p&gt;
&lt;p&gt;Consider the scenario where we use a mock for testing, making the assumption that our database logic and SQL query is correct and returns the expected scores:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	m&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;EXPECT&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Background&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Times&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Return&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;withmocks&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Score&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;UserID&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; HighScore&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; Rank&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		{&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;UserID&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; HighScore&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; Rank&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;However, imagine if a subtle change were made to the query during development, like altering the ranking order from descending to ascending: &lt;code&gt;RANK() OVER (ORDER BY t.high_score DESC) AS rank&lt;&#x2F;code&gt; becomes &lt;code&gt;RANK() OVER (ORDER BY t.high_score ASC) AS rank&lt;&#x2F;code&gt;. This minor change might be easily overlooked during a code review with hundreds of lines of code. The tests would still pass because the mock assumes the query returns results in the expected order. Such a change could go into production, resulting in a malfunctioning leaderboard. This subtle bug is demonstrated in our repository within the &lt;code&gt;withmocks&lt;&#x2F;code&gt; package.&lt;&#x2F;p&gt;
&lt;p&gt;Using a real Postgres instance for our tests, such as one running in Docker, could help catch this mistake. We could verify that the results returned from the database are correctly ordered through our test assertions.&lt;&#x2F;p&gt;
&lt;p&gt;For a complete code example, visit the repository &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;dont-need-mocks-example&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This example, while basic, does not cover database migrations, seeding, test isolation, and other crucial considerations when using real databases in tests. Here, our custom Go test libraries utilizing Docker come into play.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;practical-alternatives-to-mocks-in-go&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#practical-alternatives-to-mocks-in-go&quot; aria-label=&quot;Anchor link for: practical-alternatives-to-mocks-in-go&quot;&gt;Practical Alternatives to Mocks in Go&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In Go, the standard library provides a robust foundation for testing via the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;testing&quot;&gt;testing&lt;&#x2F;a&gt; package, yet it does not include built-in support for mock testing. However, the language’s design, particularly its interface system, naturally facilitates mocking. For more sophisticated mocking requirements, third-party libraries and tools are often sought, especially for generating complex mock objects which attempt to simulate the behaviour of real, intricate objects. Nevertheless, Go does have some built-in packages and features that enable alternative methods for testing, meaning developers do not necessarily need to rely on mocks.&lt;&#x2F;p&gt;
&lt;p&gt;An integral part of Go’s design, interfaces naturally lend themselves to creating testable code. By designing components with interfaces, you can easily substitute dependencies with stub implementations for testing purposes. For instance, consider a service that fetches data from an external API. By defining an interface for the API client, you can inject a stub that returns predetermined data during tests, thereby avoiding the need for an actual API call.&lt;&#x2F;p&gt;
&lt;p&gt;Go’s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;net&#x2F;http&#x2F;httptest&quot;&gt;httptest&lt;&#x2F;a&gt; package is another example of the standard library’s support for testing without mocks. It allows developers to create in-memory HTTP servers and clients, facilitating the testing of HTTP interactions in isolation. For example, testing an endpoint by spinning up a temporary HTTP server that returns expected responses can ensure that HTTP clients behave as intended when interacting with a real server.&lt;&#x2F;p&gt;
&lt;p&gt;The alternatives to mocks in Go testing underscore the language’s commitment to simplicity and effectiveness. By utilising interfaces and leveraging the Go standard library, developers can create robust and maintainable tests that faithfully represent production environments. Go encourages developers to experiment with these strategies, enhancing their testing practices while staying true to Go’s principles.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;test-libraries-with-go-and-docker&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#test-libraries-with-go-and-docker&quot; aria-label=&quot;Anchor link for: test-libraries-with-go-and-docker&quot;&gt;Test Libraries with Go and Docker&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s explore how we can leverage Go&#x27;s testing capabilities with Docker to emulate real-world systems, such as queues and other external dependencies, enhancing test reliability and realism.&lt;&#x2F;p&gt;
&lt;p&gt;Docker is ideal for creating isolated test environments. Using a Docker Compose file to set up and link containers ensures consistent settings that mimic production environments closely.&lt;&#x2F;p&gt;
&lt;p&gt;I however prefer a different approach to integrating Docker with Go. I often integrate Docker with Go by building test libraries that run Docker containers for the system under test. I&#x27;ve applied this technique, in multiple previous roles, building test libraries for systems like Postgres, Kafka, RabbitMQ, Google Cloud Spanner, and Google Cloud Pub&#x2F;Sub.&lt;&#x2F;p&gt;
&lt;p&gt;For instance, consider a Go service interacting with a PostgreSQL database. With Docker, we can launch a PostgreSQL container via our Go test library, and run migrations and data seeding, all within the current test. We can extend this method to ensure each test has a separate Postgres database, providing true test isolation between test cases.&lt;&#x2F;p&gt;
&lt;p&gt;Similarly, for services using message queues like Kafka or RabbitMQ, Docker can simulate these systems, allowing us to test the full message processing lifecycle in a controlled environment. An example of this approach is detailed at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;kafkatest&quot;&gt;aranw&#x2F;kafkatest&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;While this strategy offers significant benefits, it is crucial to follow best practices in managing Docker resources, ensuring data isolation between tests, and effectively integrating these tests into CI&#x2F;CD pipelines. Proper cleanup and resource management are essential for maintaining the efficiency and reliability of your test suite.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ve explored Go&#x27;s testing capabilities and Docker&#x27;s role in running applications. We&#x27;ve underscored the pivotal message: robust testing doesn&#x27;t need to rely on traditional mocks. Go’s philosophy, prioritising simplicity and practicality, combined with Docker’s capability to mimic production environments, offers developers a superior approach to crafting meaningful, real-world tests.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve seen how interface-based testing, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;net&#x2F;http&#x2F;httptest&quot;&gt;httptest&lt;&#x2F;a&gt; package and Docker&#x27;s containerisation can lead to tests that are not just aligned with real-life scenarios but are also easier to maintain. These methods enhance the development lifecycle, ensuring applications perform as anticipated in their true operating conditions.&lt;&#x2F;p&gt;
&lt;p&gt;This approach emphasises thorough testing, focusing on how applications perform in their intended environments.&lt;&#x2F;p&gt;
&lt;p&gt;Using Go with Docker meets modern software development needs for building reliable systems. Go developers can use these tools to not only improve testing but also to better understand how applications behave in their environments.&lt;&#x2F;p&gt;
&lt;p&gt;Embracing Go&#x27;s testing capabilities and Docker’s ability to emulate our systems under test offers a pathway to not only better testing but also to more insightful software development.&lt;&#x2F;p&gt;
&lt;p&gt;In future posts I plan to write more about how I write tests, the libraries I use and what I believe to be best practices.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Continuous deployment with Go and GitHub Actions</title>
          <pubDate>Mon, 11 Mar 2024 09:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/continuous-deployment-with-go-and-github-actions/</link>
          <guid>https://aran.dev/posts/continuous-deployment-with-go-and-github-actions/</guid>
          <description xml:base="https://aran.dev/posts/continuous-deployment-with-go-and-github-actions/">&lt;p&gt;The ability to deploy new features and fixes rapidly and reliably is a significant advantage. Continuous Deployment (CD) plays a crucial role in this context by automating the deployment process, ensuring that code changes are pushed to production or staging environments after successfully passing quality checks. This level of automation not only accelerates the development cycle but it also diminishes the likelihood of errors making it to production. Combining Continuous Integration and Continuous Deployment together makes for a powerful combination for companies and teams of all sizes.&lt;&#x2F;p&gt;
&lt;p&gt;For those working with Go, a language celebrated for its simplicity and performance, incorporating Continuous Deployment into our workflows can substantially boost both productivity and the reliability of our applications. GitHub Actions, an integral automation tool within GitHub, provides a straightforward pathway to implement CD pipelines directly within our repositories, eliminating the need for external services.&lt;&#x2F;p&gt;
&lt;p&gt;Following the themes of my previous articles &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;continuous-integration-with-go-and-github-actions&quot;&gt;Continuous Integration with Go and GitHub Actions&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;github-actions-go-private-modules&quot;&gt;GitHub Actions and Go Private Modules&lt;&#x2F;a&gt;, this article aims to guide software engineers, GitHub Actions users, and Go developers through the process of setting up a Continuous Deployment pipeline. I&#x27;ve written this guide to help you with the knowledge and tools necessary to automate your deployment process, making your development cycle more efficient, secure, and swift.&lt;&#x2F;p&gt;
&lt;p&gt;Through my journey in software engineering, I&#x27;ve had the opportunity to implement Continuous Deployment using Go and GitHub Actions across multiple projects at various companies. This experience has proven invaluable, not only in streamlining development processes but also in enhancing team collaboration and developer productivity. Sharing these insights, I hope to help others in leveraguing the full potential of CD in their Go projects.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s dive into Continuous Deployment with Go and GitHub Actions, exploring the benefits, strategies, and best practices.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;understanding-continuous-deployment&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#understanding-continuous-deployment&quot; aria-label=&quot;Anchor link for: understanding-continuous-deployment&quot;&gt;Understanding Continuous Deployment&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Continuous Deployment (CD) is a software release process that automates the delivery of code changes to production environments after passing predefined tests and quality checks. This automation ensures that software can be released to users quickly and reliably, making CD a cornerstone of modern DevOps practices.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-is-continuous-deployment&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-continuous-deployment&quot; aria-label=&quot;Anchor link for: what-is-continuous-deployment&quot;&gt;What is Continuous Deployment?&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;At its core, Continuous Deployment automates the steps a code change must go through from being deployed in a production environment. Unlike Continuous Integration (CI), which focuses on the integration and unit testing phases, CD takes automation a step further. It ensures that code changes passing all automated tests are deployed directly to the production environment without manual intervention. However, it&#x27;s important to note that this automation does not mean a lack of control. Many companies and teams implement protection rules within their CD pipelines, especially for critical environments like production. These rules can require manual approvals before a deployment is allowed to proceed. This hybrid approach allows developers to focus more on development while still providing a safety net, significantly reducing the time and effort required for manual deployments, yet ensuring that critical checks and balances are in place.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;benefits-of-continuous-deployment-with-go&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#benefits-of-continuous-deployment-with-go&quot; aria-label=&quot;Anchor link for: benefits-of-continuous-deployment-with-go&quot;&gt;Benefits of Continuous Deployment with Go&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Implementing Continuous Deployment for Go projects offers several advantages:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Speed and Efficiency&lt;&#x2F;strong&gt;: Automating the deployment process reduces the time from code commit to production deployment, enabling faster feedback and iteration cycles.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Reliability&lt;&#x2F;strong&gt;: Automating deployments reduces the chances of human error. Automated tests ensure that the system deploys only high-quality code to production.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Improved Productivity&lt;&#x2F;strong&gt;: Developers can concentrate on writing code and improving the product without being bogged down by the operational aspects of deployment.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced Collaboration&lt;&#x2F;strong&gt;: Teams can work more efficiently by relying on a consistent and transparent deployment process, fostering a culture of trust and collaboration.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;continuous-deployment-vs-continuous-integration&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#continuous-deployment-vs-continuous-integration&quot; aria-label=&quot;Anchor link for: continuous-deployment-vs-continuous-integration&quot;&gt;Continuous Deployment vs. Continuous Integration&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Continuous Integration (CI) and Continuous Deployment (CD) serve distinct roles in software development. CI focuses on integrating code into a shared repository frequently, where it automatically builds and tests. This process aims to detect issues early. On the other hand, CD automates the deployment of code to production after passing all tests, ensuring users always access the latest features and fixes. While CI ensures code is always deployable, CD automates the deployment, making the process seamless&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#prerequisites&quot; aria-label=&quot;Anchor link for: prerequisites&quot;&gt;Prerequisites&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In my previous article &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;continuous-integration-with-go-and-github-actions&quot;&gt;Continuous Integration with Go and GitHub Actions&lt;&#x2F;a&gt;, we setup a Continuous Integration (CI) pipeline for a Go project using GitHub Actions. We&#x27;ll build upon that knowledge and extend our CI pipeline to include Continuous Deployment (CD). If you haven&#x27;t read the previous article, I recommend doing so to familiarise yourself with the basics of GitHub Actions and CI for Go projects.&lt;&#x2F;p&gt;
&lt;p&gt;To recap we setup a new Go project, added tests, and configured a GitHub Actions workflow to run our tests on every push to the repository. We&#x27;ll use the same project and create a new workflow file to extend on our CI pipeline to include CD. To see the full setup, you can find the repository &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;go-github-actions-example&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now we&#x27;ll first start by changing our example project to include a simple web server. We&#x27;ll then create a new workflow file to deploy our project to a staging environment.&lt;&#x2F;p&gt;
&lt;p&gt;In the repositories first pull request we added a simple web server to our project. This demonstrates the current continuous integration pipeline. You can view the pull request &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;go-github-actions-example&#x2F;pull&#x2F;1&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fly-io&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#fly-io&quot; aria-label=&quot;Anchor link for: fly-io&quot;&gt;Fly.io&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;For this article we&#x27;ll be using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fly.io&#x2F;&quot;&gt;Fly&lt;&#x2F;a&gt; to deploy our project. Fly is a platform as a service that allows us to deploy our project close to our users. As a prerequisite we&#x27;ll need to create a new Fly account and install the Fly CLI. You can find the installation instructions &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fly.io&#x2F;docs&#x2F;getting-started&#x2F;installing-fly&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;. Once you have installed the Fly CLI and authenticated with your account you can start creating your applications. For our example we will need two applications, one for staging and one for production. This can be done by following the instructions &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fly.io&#x2F;docs&#x2F;apps&#x2F;launch&#x2F;&quot;&gt;here&lt;&#x2F;a&gt; and creating a new application for each environment. Ensure you create a new configuration file for each environment.&lt;&#x2F;p&gt;
&lt;p&gt;For my example I have created two applications &lt;code&gt;go-github-actions-example-staging&lt;&#x2F;code&gt; and &lt;code&gt;go-github-actions-example-production&lt;&#x2F;code&gt;. I have also created two configuration files &lt;code&gt;fly-staging.toml&lt;&#x2F;code&gt; and &lt;code&gt;fly-production.toml&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;github-environments&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#github-environments&quot; aria-label=&quot;Anchor link for: github-environments&quot;&gt;GitHub Environments&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;We&#x27;ll also need to create two new environments in our GitHub repository. We can do this by navigating to the &lt;code&gt;Environments&lt;&#x2F;code&gt; tab in our repository settings. We&#x27;ll create two new environments &lt;code&gt;staging&lt;&#x2F;code&gt; and &lt;code&gt;production&lt;&#x2F;code&gt;. We&#x27;ll use these environments when deploying our project.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;continuous-deployment-with-go-and-github-actions&#x2F;images&#x2F;github-repo-environments.png&quot; alt=&quot;GitHub Repository Environments&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For the &lt;code&gt;production&lt;&#x2F;code&gt; environment we can add protection rules ensuring that only certain users can approve deployments to this environment.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;continuous-deployment-with-go-and-github-actions&#x2F;images&#x2F;github-repo-production-environment.png&quot; alt=&quot;GitHub Repository Production Environment&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For this simple example I have put myself as a required reviewer for the &lt;code&gt;production&lt;&#x2F;code&gt; environment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up-your-continuous-deployment-pipeline&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#setting-up-your-continuous-deployment-pipeline&quot; aria-label=&quot;Anchor link for: setting-up-your-continuous-deployment-pipeline&quot;&gt;Setting Up Your Continuous Deployment Pipeline&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;We will now explore creating a new GitHub Actions workflow file for our Continuous Deployment of our application. This workflow will be triggered by a push to the main branch and will deploy our project to a staging environment. We&#x27;ll also copy the testing phases from our CI workflow into our CD workflow.&lt;&#x2F;p&gt;
&lt;p&gt;In a new file &lt;code&gt;.github&#x2F;workflows&#x2F;deploy-staging.yml&lt;&#x2F;code&gt; we&#x27;ll add the following:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; D&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;eploy Staging&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ush&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    b&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ranches&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; m&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ain&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;j&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;obs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  b&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;uild&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;uns-on&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;buntu-latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;nvironment&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;taging&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;teps&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ctions&#x2F;checkout@v4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; S&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;et up Go 1.21&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ctions&#x2F;setup-go@v5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ith&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;          g&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;o-version&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1.21&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;olangci-lint&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;olangci&#x2F;golangci-lint-action@v3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ith&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;          v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; v&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;1.54&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; R&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;un Tests&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o test -v .&#x2F;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; B&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;uild&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o build -v .&#x2F;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  d&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;eploy-staging&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; D&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;eploy app to Staging&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;uns-on&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;buntu-latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;eeds&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;uild&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;teps&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ctions&#x2F;checkout@v4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;uperfly&#x2F;flyctl-actions&#x2F;setup-flyctl@master&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;lyctl deploy --local-only --config fly-staging.toml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;nv&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;          F&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;LY_API_TOKEN&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{{ secrets.FLY_API_TOKEN }}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this workflow, we have two jobs. The first job &lt;code&gt;build&lt;&#x2F;code&gt; runs our tests and builds our project. It&#x27;s important to note that we&#x27;ve copied the testing phases from our CI workflow into our CD workflow. This ensures that our project is tested before it&#x27;s deployed to a our environments. Given more time we could refactor this into a reusable action to avoid duplication. The second job &lt;code&gt;deploy-staging&lt;&#x2F;code&gt; deploys our project to a staging environment using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fly.io&#x2F;&quot;&gt;Fly&lt;&#x2F;a&gt;. We use the &lt;code&gt;superfly&#x2F;flyctl-actions&#x2F;setup-flyctl&lt;&#x2F;code&gt; action to setup Fly and then use the &lt;code&gt;flyctl deploy&lt;&#x2F;code&gt; command to deploy our project. We pass the &lt;code&gt;FLY_API_TOKEN&lt;&#x2F;code&gt; secret to authenticate with Fly.&lt;&#x2F;p&gt;
&lt;p&gt;Something to note with the &lt;code&gt;flyctl&lt;&#x2F;code&gt; command is that we are giving it the &lt;code&gt;--local-only&lt;&#x2F;code&gt; flag. This flag tells the &lt;code&gt;flyctl&lt;&#x2F;code&gt; command to perform builds locally using the local docker daemon. The default is builds are built on remote hosts. This is useful for our GitHub Actions workflow as we don&#x27;t need to worry about setting up a docker daemon on our runners.&lt;&#x2F;p&gt;
&lt;p&gt;Now when we push to the main branch, our project will run the Continuous Integration and Continuous Deployment pipelines. Running both at this point is perhaps not ideal, but it&#x27;s a good starting point.&lt;&#x2F;p&gt;
&lt;p&gt;Now we have staging deployments up and running, we can look at deploying to production. But before we do that, we need to first create releases for our project.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;github-releases&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#github-releases&quot; aria-label=&quot;Anchor link for: github-releases&quot;&gt;GitHub Releases&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;GitHub Releases are a great way to package software releases. They provide a way to package software, release notes, and links to the software&#x27;s assets. We can use GitHub Releases to create a release for our project and then deploy that release to production.&lt;&#x2F;p&gt;
&lt;p&gt;In this example we&#x27;ll create releases using the GitHub user interface. We&#x27;ll then use the release tag to deploy our project to production. A future improvement could be to automate this process using GitHub Actions.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;continuous-deployment-with-go-and-github-actions&#x2F;images&#x2F;releases-page.png&quot; alt=&quot;GitHub Releases Page&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To create a release, navigate to the releases page by clicking on the &lt;code&gt;Releases&lt;&#x2F;code&gt; header in the right hand side of your repository home. For the first release, click on &lt;code&gt;Create a new release&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;continuous-deployment-with-go-and-github-actions&#x2F;images&#x2F;create-release-page.png&quot; alt=&quot;Create Release Page&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;From this page we need to create our release tag, title, and description. We could also add assets to our release but for this usecase we won&#x27;t. When it comes to the title I usually keep this the same as the release tag. The description is a good place to list the changes in this release, GitHub has the ability to autogenerate this for you based on the commits included in the release.&lt;&#x2F;p&gt;
&lt;p&gt;For my first release I created a &lt;code&gt;v0.1.0&lt;&#x2F;code&gt; release.&lt;&#x2F;p&gt;
&lt;p&gt;Once you&#x27;ve created your release, you&#x27;ll be able to see it on the releases page. However we won&#x27;t be able to use it to deploy to production just yet. We first need to add a new workflow to our project to deploy to production.&lt;&#x2F;p&gt;
&lt;p&gt;In a new file &lt;code&gt;.github&#x2F;workflows&#x2F;deploy-production.yml&lt;&#x2F;code&gt; we&#x27;ll add the following:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; D&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;eploy Production&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ush&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    t&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ags&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; v&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;j&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;obs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  b&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;uild&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;uns-on&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;buntu-latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;nvironment&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; p&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;roduction&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;teps&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ctions&#x2F;checkout@v4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; S&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;et up Go 1.21&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ctions&#x2F;setup-go@v5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ith&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;          g&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;o-version&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1.21&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;olangci-lint&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;olangci&#x2F;golangci-lint-action@v3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ith&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;          v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; v&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;1.54&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; R&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;un Tests&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o test -v .&#x2F;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; B&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;uild&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o build -v .&#x2F;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  d&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;eploy-production&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; D&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;eploy app to Production&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;uns-on&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;buntu-latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;eeds&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;uild&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;teps&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ctions&#x2F;checkout@v4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;uperfly&#x2F;flyctl-actions&#x2F;setup-flyctl@master&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;lyctl deploy --local-only --config fly-production.toml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;nv&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;          F&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;LY_API_TOKEN&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; $&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{{ secrets.FLY_API_TOKEN }}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What you will notice for this new workflow is that it&#x27;s very similar to the staging deployment workflow. The main difference is that it&#x27;s triggered by a tag push and it deploys to a production environment. In this example we&#x27;re using the &lt;code&gt;v*&lt;&#x2F;code&gt; pattern to match any tag that starts with &lt;code&gt;v&lt;&#x2F;code&gt;. This is a common pattern for version tags.&lt;&#x2F;p&gt;
&lt;p&gt;Now that we have our production deployment workflow, we can use GitHub Releases to deploy our project to production. To do this we need to create a new release and tag it.&lt;&#x2F;p&gt;
&lt;p&gt;Now after we&#x27;ve tagged our next release, we can see in our list of Deployments that we have a new production deployment waiting to be approved.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;continuous-deployment-with-go-and-github-actions&#x2F;images&#x2F;github-deployments.png&quot; alt=&quot;GitHub Deployments&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Heading to the &lt;code&gt;Actions&lt;&#x2F;code&gt; tab we can select our &lt;code&gt;Deploy Production&lt;&#x2F;code&gt; workflow and see that it&#x27;s waiting for approval before it can proceed.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;continuous-deployment-with-go-and-github-actions&#x2F;images&#x2F;github-prod-deploy-waiting.png&quot; alt=&quot;GitHub Production Deployment Waiting&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Clicking on the &lt;code&gt;Review deployments&lt;&#x2F;code&gt; will open a Review Deployment modal where we can approve the deployment and add an optional comment.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;continuous-deployment-with-go-and-github-actions&#x2F;images&#x2F;github-review-deployment.png&quot; alt=&quot;GitHub Review Deployment&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Once our deployment has been approved and our workflow has completed we can see that our project has been deployed to production.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;continuous-deployment-with-go-and-github-actions&#x2F;images&#x2F;github-prod-deploy-success.png&quot; alt=&quot;GitHub Production Deployment Success&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In this article we&#x27;ve explored the benefits of Continuous Deployment and how we can implement it for our Go projects using GitHub Actions. We&#x27;ve also looked at how we can use GitHub Releases to deploy our project to production.&lt;&#x2F;p&gt;
&lt;p&gt;Implementing Continuous Deployment alongside Continuous Integration can significantly improve the speed, reliability, and efficiency of your development process and the quality of your software. By automating the deployment process, you can improve the iteration cycle and reduce the likelihood of errors making it to production. Along with ensuring deployments are regular and reliable, Continuous Deployment fosters a culture of trust and collaboration within your team.&lt;&#x2F;p&gt;
&lt;p&gt;There are still various improvements and changes we could make to our deployment process. For example, we could automate the creation of releases using a tool like, we could also test and build our application in one job to avoid duplication.&lt;&#x2F;p&gt;
&lt;p&gt;Exploring the benefits of Continuous Deployment and its implementation for Go projects using GitHub Actions has illuminated a pathway to enhancing software delivery. The integration with GitHub Releases for deploying projects to production further amplifies the efficacy of this approach.&lt;&#x2F;p&gt;
&lt;p&gt;By embracing Continuous Deployment in tandem with Continuous Integration, the development process can achieve remarkable gains in speed, reliability, and efficiency, concurrently elevating the quality of software. Automation of the deployment process not only streamlines the iteration cycle but also significantly diminishes the chances of errors reaching production. Moreover, the adoption of Continuous Deployment cultivates a culture imbued with trust and collaboration within teams.&lt;&#x2F;p&gt;
&lt;p&gt;Looking ahead, there are several avenues for augmenting our deployment processes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Automating release creation: Leveraging tools like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;googleapis&#x2F;release-please&quot;&gt;release-please&lt;&#x2F;a&gt; can streamline the release process, ensuring that new versions are systematically prepared and documented.&lt;&#x2F;li&gt;
&lt;li&gt;Optimising job configurations: Consolidating testing and building phases into a single job could reduce redundancy, enhancing the efficiency of our Continuous Integration&#x2F;Continuous Deployment (CI&#x2F;CD) pipeline.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These enhancements not only promise to refine the deployment process but also position our projects for greater adaptability and success in the evolving landscape of software development.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Deploying Docker images to Fly.io</title>
          <pubDate>Tue, 05 Mar 2024 13:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/deploying-docker-images-to-fly-io/</link>
          <guid>https://aran.dev/posts/deploying-docker-images-to-fly-io/</guid>
          <description xml:base="https://aran.dev/posts/deploying-docker-images-to-fly-io/">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fly.io&quot;&gt;Fly.io&lt;&#x2F;a&gt; is a Platform as a Service (PaaS) enabling developers to deploy and run their applications globally, closer to their users. The Fly.io platform, notable for its simplicity and user-friendly design, has recently gained popularity.&lt;&#x2F;p&gt;
&lt;p&gt;Fly.io transforms Docker containers into Firecracker VMs and strategically deploys them worldwide. There are a couple of different ways to build and deploy applications on Fly.io, the most straightforward method involves the &lt;code&gt;flyctl&lt;&#x2F;code&gt; CLI tool. With &lt;code&gt;flyctl&lt;&#x2F;code&gt;, you can effortlessly build and deploy your application using the single command &lt;code&gt;flyctl deploy&lt;&#x2F;code&gt;. However, this article will explore other deployment strategies.&lt;&#x2F;p&gt;
&lt;p&gt;With Continuous Integration (CI) and Continuous Deployment (CD) workflows, you&#x27;ll build and push your Docker images to a container registry. This practice facilitates a consistent build process for images and deploying the same image across various environments. This article will show you how to push Docker images to Fly.io&#x27;s container registry for deployment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tagging-images&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tagging-images&quot; aria-label=&quot;Anchor link for: tagging-images&quot;&gt;Tagging Images&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;When building and pushing Docker images to external registries, it&#x27;s essential to tag the image with the registry&#x27;s URL. For Fly.io, the container registry URL is &lt;code&gt;registry.fly.io&lt;&#x2F;code&gt;. Like with other registries, you must include both an image name and a tag. For instance, when using Docker Hub, you tag an image as follows:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;registry.hub.docker.com&#x2F;library&#x2F;&amp;lt;dockerimagename&amp;gt;:&amp;lt;tag&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For Fly.io, the tagging format is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;registry.fly.io&#x2F;&amp;lt;your-app&amp;gt;:&amp;lt;tag&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The key distinction is that, unlike Docker Hub, where your Docker image name can be arbitrary, with Fly.io, it must match your application&#x27;s name.&lt;&#x2F;p&gt;
&lt;p&gt;You can tag your Docker image using the following Docker command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;❯&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; docker&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; myapp:latest&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; registry.fly.io&#x2F;myapp:latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can also tag your Docker image on build using the &lt;code&gt;docker build&lt;&#x2F;code&gt; command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;❯&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; docker&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; registry.fly.io&#x2F;myapp:latest&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;pushing-images&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#pushing-images&quot; aria-label=&quot;Anchor link for: pushing-images&quot;&gt;Pushing Images&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Pushing Docker images to external registries, like Docker Hub or Fly.io, involves tagging your image with the registry&#x27;s URL, image name, and tag. External registries allow for secure storage and easy distribution of your containerised applications. Before pushing Docker images, you must authenticate with the external registry.&lt;&#x2F;p&gt;
&lt;p&gt;To authenticate with Fly&#x27;s container registry, you can use the &lt;code&gt;flyctl&lt;&#x2F;code&gt; CLI tool. The command is as follows:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;❯&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; flyctl&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; auth&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; docker&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Authentication&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; successful.&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; You&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; can&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; now&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; and&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; push&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; images&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; to&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; registry.fly.io&#x2F;{your-app}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This command authenticates your Docker client with Fly.io&#x27;s container registry, allowing you to push images to the registry. This process works by creating an entry in your &lt;code&gt;~&#x2F;.docker&#x2F;config.json&lt;&#x2F;code&gt; file, and for macOS users, it also adds the username and password to your keychain.&lt;&#x2F;p&gt;
&lt;p&gt;Now that you&#x27;re authenticated, push it to Fly.io&#x27;s container registry.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;❯&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; docker&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; push&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; registry.fly.io&#x2F;myapp:latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;deploying-an-image&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#deploying-an-image&quot; aria-label=&quot;Anchor link for: deploying-an-image&quot;&gt;Deploying An Image&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Once a Docker image is pushed to Fly.io&#x27;s container registry, you can deploy it to one of your Fly applications. The &lt;code&gt;flyctl&lt;&#x2F;code&gt; CLI tool provides a simple command to deploy your application using the Docker image from the registry.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;❯&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; flyctl&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; deploy&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-app&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; myapp&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-image&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; registry.fly.io&#x2F;myapp:latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In future articles and projects, I plan to delve deeper into using manually tagged docker images within CI&#x2F;CD pipelines. One use case involves pushing and deploying images to ensure consistent builds across multiple environments.&lt;&#x2F;p&gt;
&lt;p&gt;For automating deployments to Fly.io with GitHub Actions, see my post on &lt;a href=&quot;&#x2F;posts&#x2F;continuous-deployment-with-go-and-github-actions&#x2F;&quot;&gt;continuous deployment with Go and GitHub Actions&lt;&#x2F;a&gt; which covers setting up staging and production pipelines.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>I suck at technical interviews</title>
          <pubDate>Tue, 27 Feb 2024 08:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/i-suck-at-technical-interviews/</link>
          <guid>https://aran.dev/posts/i-suck-at-technical-interviews/</guid>
          <description xml:base="https://aran.dev/posts/i-suck-at-technical-interviews/">&lt;p&gt;It&#x27;s difficult for me to admit, but I struggle with technical interviews. I don&#x27;t believe it&#x27;s due to a lack of abilities or insufficient practice on platforms like LeetCode; rather, I think nerves and my dyslexia play a significant role. I tend not to highlight my dyslexia, as I&#x27;ve managed to overcome it in several areas over the years, and I&#x27;m concerned that disclosing it might negatively impact my chances if interviewers perceive it as a drawback.&lt;&#x2F;p&gt;
&lt;p&gt;Reflecting on a recent interview, I realised I was completely unprepared. A recruiter had sent the interview details to my email, but they ended up in my spam folder. Unaware, I joined the technical interview without any preparation, which, in my mind, led to my downfall. During the interview, I was tasked with reviewing the code of a caching library and was informed that it contained seven or eight bugs. Despite my efforts, I couldn&#x27;t identify any. It wasn&#x27;t until the interviewer provided some hints that the bugs began to emerge. Feeling embarrassed, I continued to struggle, repeatedly uttering &quot;erm&quot; and &quot;I&#x27;m not sure.&quot; This situation is far from ideal for any candidate, as no employer seeks to hire an engineer who struggles with code reviews.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t always believe the fault lies entirely with me, however. There are times when I have been mentally prepared and entered the interview with confidence, only for the interviewer&#x27;s approach to unsettle me, making me nervous and causing me to revert to a state where I struggle to read, review code, or solve technical problems. Some past experiences with technical interviews have left me feeling mentally scarred, and shaking off those negative memories has proven challenging. Every time I enter a technical interview, I find myself reflecting on those past experiences. I dwell on them, worrying that history will repeat itself. I fear encountering the same situations as before—interviewers laughing or becoming disengaged, focusing on something else while I&#x27;m on camera. Struggling to navigate through a technical challenge under these circumstances could happen again, further unsettling me and impairing my performance.&lt;&#x2F;p&gt;
&lt;p&gt;Moving forward, I realise the importance of being more vocal about my struggles with live coding interviews. The negative experiences have significantly impacted my performance, highlighting the need for me to request alternative ways to be assessed. I must advocate for accommodations that align with my strengths, such as using an editor I&#x27;m familiar with, participating in take-home tests that allow me to work through challenges in my own time, or facing challenges that reflect either my current daily work or tasks I would be performing in the role. This approach would not only bolster my confidence but also provide a fairer measure of my capabilities. Often, the tasks set during interviews do not mirror the company&#x27;s actual work or the day-to-day responsibilities of the role, frequently venturing into areas beyond my expertise.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s important to highlight that not all of my experiences with technical interviews have been negative. When matched with the right interviewer and given a challenge within my realm of expertise, I have thrived, delivering creative solutions that have led to job offers. This contrast underscores the inconsistency and often the unfairness of the technical interviewing process. There are plenty of articles, blog posts, and anecdotes from past colleagues that critique the methods of interviewing in the software engineering industry. My experiences, together with those of others, underscore the need for change in the technical interview process.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Building your Go toolkit</title>
          <pubDate>Sun, 11 Feb 2024 10:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/building-your-golang-toolkit/</link>
          <guid>https://aran.dev/posts/building-your-golang-toolkit/</guid>
          <description xml:base="https://aran.dev/posts/building-your-golang-toolkit/">&lt;p&gt;After nearly 10 years of working with Go, including over 6 of those years as a full-time Go developer, I&#x27;ve come to rely on a set of tools and libraries that have significantly boosted my productivity. In this guide, I&#x27;ll share the tools and libraries I use and discuss the importance of curating your own toolkit.&lt;&#x2F;p&gt;
&lt;p&gt;Other developers have shared insights on building services and the libraries they depend on, which I highly recommend exploring to aid in assembling your toolkit. Noteworthy examples include Mat Ryer&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pace.dev&#x2F;blog&#x2F;2018&#x2F;05&#x2F;09&#x2F;how-I-write-http-services-after-eight-years.html&quot;&gt;&quot;How I write services in Go&quot;&lt;&#x2F;a&gt; and his follow up, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;grafana.com&#x2F;blog&#x2F;2024&#x2F;02&#x2F;09&#x2F;how-i-write-http-services-in-go-after-13-years&#x2F;&quot;&gt;&quot;How I write Go HTTP services after 13 years&quot;&lt;&#x2F;a&gt; where he shares the libraries he utilises.&lt;&#x2F;p&gt;
&lt;p&gt;The primary goal of building your toolkit is to streamline your development process, enabling you to focus more on creating rather than configuring. This guide aims not only to highlight the importance of a personalised toolkit but also to serve as a foundation for developers looking to enhance their efficiency. By curating a set of tools and libraries tailored to your needs, you can significantly reduce cognitive load and boost productivity.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-philosophy-behind-a-personal-toolkit&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-philosophy-behind-a-personal-toolkit&quot; aria-label=&quot;Anchor link for: the-philosophy-behind-a-personal-toolkit&quot;&gt;The Philosophy Behind a Personal Toolkit&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Personal toolkits extend beyond Go and are pivotal in software development, offering tailored sets of tools and libraries. Go&#x27;s ethos of simplicity and efficiency underscores the value of such a personalised approach, especially when large organisations similarly cultivate their own frameworks to boost productivity.&lt;&#x2F;p&gt;
&lt;p&gt;Cognitive bandwidth is finite. Decisions, from selecting libraries to choosing debugging tools, consume mental resources, leading to potential fatigue. A curated toolkit can mitigate this by reducing decision-making and providing reliable, familiar tools, thereby conserving energy for creativity and complex problem-solving.&lt;&#x2F;p&gt;
&lt;p&gt;The process of curating a toolkit enhances efficiency by minimising the time dedicated to tool-related decisions and familiarising oneself with chosen tools, which expedites development and enables tackling complex problems more confidently.&lt;&#x2F;p&gt;
&lt;p&gt;Deep engagement with a limited set of tools promotes mastery and the discovery of advanced features, often overlooked when frequently switching between tools. Furthermore, standardising toolkits within teams simplifies collaboration, easing code sharing and maintaining coding standards.&lt;&#x2F;p&gt;
&lt;p&gt;Ultimately, the philosophy behind a personal toolkit aims to enhance efficiency and reduce cognitive load without stifling creativity. It&#x27;s about building a foundation for development that prioritises solving problems and crafting solutions effectively.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;core-components-of-my-golang-toolkit&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#core-components-of-my-golang-toolkit&quot; aria-label=&quot;Anchor link for: core-components-of-my-golang-toolkit&quot;&gt;Core Components of My Golang Toolkit&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;For my toolkit, I&#x27;ve assembled a set of tools and libraries that have proven invaluable in my development process. While these components are essential for me, they may not be for you. It&#x27;s crucial to tailor your toolkit to your unique needs.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;http&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#http&quot; aria-label=&quot;Anchor link for: http&quot;&gt;HTTP&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I typically rely on the &lt;code&gt;net&#x2F;http&lt;&#x2F;code&gt; package for building services, given its robust foundation for creating HTTP servers and clients. However, its limitations prior to Go 1.21 led me to often use the &lt;code&gt;github.com&#x2F;go-chi&#x2F;chi&#x2F;v5&lt;&#x2F;code&gt; router for added functionality. This preference may evolve with the enhancements in the &lt;code&gt;net&#x2F;http&lt;&#x2F;code&gt; package introduced in Go 1.22.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;concurrency&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#concurrency&quot; aria-label=&quot;Anchor link for: concurrency&quot;&gt;Concurrency&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Go&#x27;s concurrency model is a standout feature. For basic concurrency patterns, the &lt;code&gt;sync&lt;&#x2F;code&gt; package suffices. Yet, for more complex scenarios, I find the &lt;code&gt;github.com&#x2F;sourcegraph&#x2F;conc&lt;&#x2F;code&gt; package invaluable for its user-friendly API and handling of intricate cases.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;database&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#database&quot; aria-label=&quot;Anchor link for: database&quot;&gt;Database&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Favouring Postgres, I predominantly use the &lt;code&gt;github.com&#x2F;jackc&#x2F;pgx&#x2F;v5&lt;&#x2F;code&gt; package for database interactions and connection pooling. This, combined with &lt;code&gt;github.com&#x2F;golang-migrate&#x2F;migrate&#x2F;v4&lt;&#x2F;code&gt; for database migrations and &lt;code&gt;github.com&#x2F;georgysavva&#x2F;scany&#x2F;v2&lt;&#x2F;code&gt; for scanning rows into structs, significantly enhances productivity and efficiency with Postgres.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;logging&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#logging&quot; aria-label=&quot;Anchor link for: logging&quot;&gt;Logging&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Although I&#x27;ve previously used &lt;code&gt;github.com&#x2F;rs&#x2F;zerolog&lt;&#x2F;code&gt; and &lt;code&gt;github.com&#x2F;sirupsen&#x2F;logrus&lt;&#x2F;code&gt;, I&#x27;ve recently transitioned to the standard library&#x27;s &lt;code&gt;log&#x2F;slog&lt;&#x2F;code&gt; package for its simplicity and one less external dependency.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;testing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#testing&quot; aria-label=&quot;Anchor link for: testing&quot;&gt;Testing&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The standard library&#x27;s &lt;code&gt;testing&lt;&#x2F;code&gt; package meets most of my testing needs. However, for more intuitive assertions, I use &lt;code&gt;github.com&#x2F;go-quicktest&#x2F;qt&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For integration tests, particularly with systems like Postgres, Google Cloud Spanner, Kafka, etc., I rely on &lt;code&gt;github.com&#x2F;ory&#x2F;dockertest&#x2F;v3&lt;&#x2F;code&gt; to create custom test environments.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tooling&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tooling&quot; aria-label=&quot;Anchor link for: tooling&quot;&gt;Tooling&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;So far I&#x27;ve covered libraries, but tooling is also an important part of my toolkit. I use &lt;code&gt;github.com&#x2F;golangci&#x2F;golangci-lint&lt;&#x2F;code&gt; for linting, &lt;code&gt;github.com&#x2F;golang&#x2F;tools&#x2F;tree&#x2F;master&#x2F;gopls&lt;&#x2F;code&gt; for my language server, &lt;code&gt;air&lt;&#x2F;code&gt; for hot reloading, &lt;code&gt;github.com&#x2F;a-h&#x2F;templ&lt;&#x2F;code&gt; for templating and more. These tools are chosen based on project requirements and personal preference.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;future-additions&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#future-additions&quot; aria-label=&quot;Anchor link for: future-additions&quot;&gt;Future Additions&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Each component in my toolkit is chosen for its fit with my needs and project requirements, reflecting the dynamic nature of software development. As Go evolves, so too does my selection of tools and libraries, ensuring I&#x27;m equipped for the challenges of current and future projects.&lt;&#x2F;p&gt;
&lt;p&gt;Adaptability is key; my toolkit flexibly incorporates tools like Kafka, gRPC, or Redis as needed, always supported by the latest and most efficient libraries. Currently, I&#x27;m evaluating &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;connectrpc.com&#x2F;&quot;&gt;ConnectRPC&lt;&#x2F;a&gt; for its potential in upcoming projects, illustrating my commitment to leveraging emerging technologies to maintain a cutting-edge development environment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-your-toolkit&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#building-your-toolkit&quot; aria-label=&quot;Anchor link for: building-your-toolkit&quot;&gt;Building Your Toolkit&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Building a Golang toolkit is akin to embarking on a unique adventure, one that is deeply influenced by individual development style, project requirements, and team dynamics. This journey doesn’t follow a linear path; instead, it encourages you to explore, experiment, and curate a set of tools and libraries that align with your specific needs.&lt;&#x2F;p&gt;
&lt;p&gt;Begin by reviewing past projects. What challenges did you face, and which libraries or tools helped you overcome them? Identifying patterns in your past choices can provide valuable insights into your preferences and requirements. Perhaps you’ve consistently leaned towards certain routers for web services or found specific ORMs (Object Relational Mapping) or database packages that fit your workflow perfectly. Recognising these tendencies is the first step towards building a toolkit that feels like a natural extension of your development process.&lt;&#x2F;p&gt;
&lt;p&gt;Consider the scope of your current and upcoming projects. Are you focusing on web applications, microservices, or CLI tools? Each domain might benefit from different sets of libraries. For example, web services might thrive with routers like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-chi&#x2F;chi&quot;&gt;chi&lt;&#x2F;a&gt; or frameworks like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gin-gonic&#x2F;gin&quot;&gt;Gin&lt;&#x2F;a&gt;, while CLI development could benefit from packages like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;spf13&#x2F;cobra&quot;&gt;cobra&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Personal preferences play a significant role in shaping your toolkit. Do you prioritize minimalism and the Unix philosophy of &quot;doing one thing well,&quot; or do you prefer the comprehensive features offered by more extensive frameworks? Your toolkit should reflect this preference, balancing simplicity with functionality as you see fit.&lt;&#x2F;p&gt;
&lt;p&gt;Team dynamics cannot be overlooked if you’re working in a collaborative environment. A shared toolkit, built on familiar libraries and tools, can significantly enhance team productivity and project coherence. Engage in discussions to understand your team&#x27;s preferences and experiences, aiming for a common ground that supports efficient collaboration. For example it might be beneficial to develop your own internal libraries or tools that are more tailored to your team&#x27;s specific needs, workflows and environments than existing open source libraries.&lt;&#x2F;p&gt;
&lt;p&gt;Experimentation is crucial. With a preliminary list of tools and libraries in hand, test them in small-scale projects or components of larger applications. This hands-on approach allows you to evaluate their fit for your needs, ease of use, and performance implications.&lt;&#x2F;p&gt;
&lt;p&gt;In essence, building your Golang toolkit is a dynamic process of discovery, evaluation, and refinement. By drawing on your experiences, staying open to new ideas, and fostering collaboration, you can curate a personalised toolkit that not only enhances your productivity but also enriches your development journey.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;staying-updated-and-flexible&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#staying-updated-and-flexible&quot; aria-label=&quot;Anchor link for: staying-updated-and-flexible&quot;&gt;Staying Updated and Flexible&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;It is important that you stay informed about new developments within the Go ecosystem and be ready to reassess and adjust your toolkit. The tech landscape evolves, and so will your preferences and project requirements. This flexibility ensures that your development environment remains efficient, enjoyable, and aligned with your goals.&lt;&#x2F;p&gt;
&lt;p&gt;The last thing you want to do is develop a toolkit that you never update or change. This will lead to stagnation and inefficiencies. Instead, be proactive in your approach to your toolkit, always looking for new tools and libraries that could streamline your workflow or help you overcome new challenges.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Curating a personalised Golang toolkit is a continuous journey that enhances your efficiency and job satisfaction. This guide has introduced the philosophy behind a custom toolkit, essential components, and a strategy for building one that suits your unique needs. The focus has been on tools and libraries, underscoring their importance in your development workflow.&lt;&#x2F;p&gt;
&lt;p&gt;However, project structuring, another critical aspect of your toolkit, has not been covered here. Proper structuring is key to scalability and maintainability but will be the subject of future articles. Keep an eye out as we dive deeper into optimizing your Go projects&#x27; architecture.&lt;&#x2F;p&gt;
&lt;p&gt;Remember, the right toolkit evolves with you, accommodating changes in technology and project demands. Stay adaptable, engage with the Go community, and let your toolkit reflect your development ethos. Here&#x27;s to the journey of building a toolkit that not only meets your needs but also makes coding a pleasure.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Continuous integration with Go and GitHub Actions</title>
          <pubDate>Tue, 06 Feb 2024 10:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/continuous-integration-with-go-and-github-actions/</link>
          <guid>https://aran.dev/posts/continuous-integration-with-go-and-github-actions/</guid>
          <description xml:base="https://aran.dev/posts/continuous-integration-with-go-and-github-actions/">&lt;div class=&quot;note-container&quot;&gt;
    
            &lt;div class=&quot;note-header&quot;&gt;
                
                    &lt;div class=&quot;note-icon&quot;&gt;
                        &lt;p&gt;Info&lt;&#x2F;p&gt;

                    &lt;&#x2F;div&gt;
                
            &lt;&#x2F;div&gt;
            &lt;div class=&quot;note-content&quot;&gt;
                &lt;p&gt;Checkout my previous article on how to use private Go modules in local development and GitHub Actions workflows &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;github-actions-go-private-modules&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

            &lt;&#x2F;div&gt;
        
    &lt;&#x2F;div&gt;
&lt;div class=&quot;note-container&quot;&gt;
    
            &lt;div class=&quot;note-header&quot;&gt;
                
                    &lt;div class=&quot;note-icon&quot;&gt;
                        &lt;p&gt;Info&lt;&#x2F;p&gt;

                    &lt;&#x2F;div&gt;
                
            &lt;&#x2F;div&gt;
            &lt;div class=&quot;note-content&quot;&gt;
                &lt;p&gt;Checkout my latest post on how to Continuous Deployment with Go and GitHub Actions &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;continuous-deployment-with-go-and-github-actions&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

            &lt;&#x2F;div&gt;
        
    &lt;&#x2F;div&gt;
&lt;h2 id=&quot;introduction&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#introduction&quot; aria-label=&quot;Anchor link for: introduction&quot;&gt;Introduction&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In a previous &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aran.dev&#x2F;posts&#x2F;github-actions-go-private-modules&#x2F;&quot;&gt;article&lt;&#x2F;a&gt;, I explored how to use private Go modules in local development and GitHub Actions workflows. I did not cover how to use GitHub Actions to create a Continuous Integration (CI) workflow for a Go project. In this article, I&#x27;ll expand on that and explore how to use GitHub Actions to create a Continuous Integration (CI) workflow for a Go project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;getting-started-with-go&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#getting-started-with-go&quot; aria-label=&quot;Anchor link for: getting-started-with-go&quot;&gt;Getting Started with Go&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;To get started we first need a Go project. In this section we&#x27;ll delve into setting up a Go project and creating a simple Go application. I&#x27;ll create a new Go project and initialise a new Go module.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;❯&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; go&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; mod&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; init&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; github.com&#x2F;aranw&#x2F;go-github-actions-example&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will create a new Go module in the current directory. We can then create a new Go file and add some code.&lt;&#x2F;p&gt;
&lt;p&gt;Here we have the &lt;code&gt;main.go&lt;&#x2F;code&gt; file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;package&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; sayHello&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Aran&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;msg&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; sayHello&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is a simple Go application that prints &quot;Hello Aran&quot; to the console. Giving us a simple project to work with. We can also add a test file for our project.&lt;&#x2F;p&gt;
&lt;p&gt;Here we have the &lt;code&gt;main_test.go&lt;&#x2F;code&gt; file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;package&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;testing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; TestSayHello&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;testing&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; sayHello&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Aran&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    want&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello Aran&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; msg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; want&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        t&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errorf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;got &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;; want &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; msg&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; want&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div class=&quot;note-container&quot;&gt;
    
            &lt;div class=&quot;note-header&quot;&gt;
                
                    &lt;div class=&quot;note-icon&quot;&gt;
                        &lt;p&gt;Info&lt;&#x2F;p&gt;

                    &lt;&#x2F;div&gt;
                
            &lt;&#x2F;div&gt;
            &lt;div class=&quot;note-content&quot;&gt;
                &lt;p&gt;As an example for later I&#x27;m going to omit the contents of the &lt;code&gt;main_test.go&lt;&#x2F;code&gt; for testing to demonstrate what a failed GitHub Workflow looks like!&lt;&#x2F;p&gt;

            &lt;&#x2F;div&gt;
        
    &lt;&#x2F;div&gt;
&lt;p&gt;This is a very simple example but it gives us a good starting point for our Continous Integration workflow with GitHub Actions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;creating-a-github-actions-workflow&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#creating-a-github-actions-workflow&quot; aria-label=&quot;Anchor link for: creating-a-github-actions-workflow&quot;&gt;Creating a GitHub Actions Workflow&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;GitHub Actions is a powerful automation tool integrated within GitHub&#x27;s ecosystem, offering a seamless way to automating workflows directly within your repository. In this segment we will look at using GitHub Actions to create a Continuous Integration (CI) workflow specifically tailored for our Go project.&lt;&#x2F;p&gt;
&lt;p&gt;GitHub Actions facilitates a variety of automation tasks within the software development lifecycle, from simple code linting to complex deployments. For Go developers, this means leveraging GitHub&#x27;s infrastructure to automate testing, building and deploying applications. For this article, we will focus on creating a simple CI workflow for our Go project and in future articles we will explore complex workflows like deploying to a cloud provider.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;creating-a-workflow-file&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#creating-a-workflow-file&quot; aria-label=&quot;Anchor link for: creating-a-workflow-file&quot;&gt;Creating a Workflow File&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Typically the workflow file is a written in YAML and is stored in the &lt;code&gt;.github&#x2F;workflows&lt;&#x2F;code&gt; directory in the root of your repository. The workflow file contains the definition of the workflow, including the triggers, jobs and steps. A repository can have multiple workflow files, each defining a different workflow. Advanced options can be to manage these workflow files in separate repositories or using an intermediate language like Cue or Jsonnet to generate the workflow files. In future articles we will explore these advanced options.&lt;&#x2F;p&gt;
&lt;p&gt;For our Go project we will create a new workflow file called &lt;code&gt;ci.yml&lt;&#x2F;code&gt; in the &lt;code&gt;.github&#x2F;workflows&lt;&#x2F;code&gt; directory. This file will contain the definition of our CI workflow.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; C&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;I&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ush&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; p&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ull_request&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;j&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;obs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  b&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;uild&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;uns-on&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;buntu-latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;teps&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ctions&#x2F;checkout@v4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; S&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;et up Go 1.21&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ctions&#x2F;setup-go@v5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ith&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;            g&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;o-version&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1.21&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; R&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;un Tests&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o test -v .&#x2F;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Right now in the workflow file we have defined a simple workflow that runs on every push and pull request to the repository. The workflow contains a single job called &lt;code&gt;build&lt;&#x2F;code&gt; that runs on the latest version of Ubuntu. The job contains three steps:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;First we checkout the repository codebase into the runner.&lt;&#x2F;li&gt;
&lt;li&gt;Then we setup the runner with Go 1.21 using the &lt;code&gt;actions&#x2F;setup-go&lt;&#x2F;code&gt; action.&lt;&#x2F;li&gt;
&lt;li&gt;Finally we run the tests using the &lt;code&gt;go test&lt;&#x2F;code&gt; command.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Now we are only testing our code in the above example and for a more complete Continuous Integration workflow we should also include static analysis, linting and building. We can add these steps to our workflow file to create a more complete CI workflow. Using the &lt;code&gt;golangci-lint&lt;&#x2F;code&gt; action we can easily add linting and other static analysis tools to our workflow.&lt;&#x2F;p&gt;
&lt;p&gt;Ideally this step should be done before running the tests. This is because we want to catch any issues with our code before running the tests.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;olangci-lint&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;olangci&#x2F;golangci-lint-action@v3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    w&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ith&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ersion&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; v&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;1.54&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By default &lt;code&gt;golangci-lint&lt;&#x2F;code&gt; comes with some sane defaults but you can also configure it to your liking by adding a &lt;code&gt;.golangci.yml&lt;&#x2F;code&gt; file to the root of your repository. For more details on how to configure &lt;code&gt;golangci-lint&lt;&#x2F;code&gt; checkout the official documentation &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;golangci-lint.run&#x2F;usage&#x2F;configuration&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We can also add a step to build our Go application.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; B&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;uild&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o build -v .&#x2F;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have a complete CI workflow we can commit the workflow file to our repository and push it to GitHub.&lt;&#x2F;p&gt;
&lt;p&gt;{{&amp;lt; figure
src=&quot;.&#x2F;images&#x2F;github-actions.png&quot;
alt=&quot;GitHub Actions Failed Workflow&quot;
&amp;gt;}}&lt;&#x2F;p&gt;
&lt;p&gt;This is a good example of a failing Workflow. In this example the Workflow failed to due to the contents of the test file being missing. Now I&#x27;ll add the contents to the test file and push the changes to GitHub.&lt;&#x2F;p&gt;
&lt;p&gt;{{&amp;lt; figure
src=&quot;.&#x2F;images&#x2F;github-actions-1.png&quot;
alt=&quot;GitHub Actions Workflow Overview&quot;
&amp;gt;}}&lt;&#x2F;p&gt;
&lt;p&gt;From the overview page we can see both our failed and passed workflows. We can click on the workflow to see the details of the workflow.&lt;&#x2F;p&gt;
&lt;p&gt;{{&amp;lt; figure
src=&quot;.&#x2F;images&#x2F;github-actions-2.png&quot;
alt=&quot;GitHub Actions Workflow Details&quot;
&amp;gt;}}&lt;&#x2F;p&gt;
&lt;div class=&quot;note-container&quot;&gt;
    
            &lt;div class=&quot;note-header&quot;&gt;
                
                    &lt;div class=&quot;note-icon&quot;&gt;
                        &lt;p&gt;Info&lt;&#x2F;p&gt;

                    &lt;&#x2F;div&gt;
                
            &lt;&#x2F;div&gt;
            &lt;div class=&quot;note-content&quot;&gt;
                &lt;p&gt;In this screenshot you&#x27;ll see some warnings about Node.js 16 actions being deprecated. This comes from the Golang CI Linter action and has been fixed in the project&#x27;s master branch but v3 is still the latest tagged version!&lt;&#x2F;p&gt;

            &lt;&#x2F;div&gt;
        
    &lt;&#x2F;div&gt;
&lt;p&gt;Now we have a complete CI workflow for our Go project. We can use this workflow to test our code on every push and pull request to the repository. We can also use this workflow to run other tasks like building and deploying our Go application which we will explore in future articles.&lt;&#x2F;p&gt;
&lt;p&gt;To checkout the complete workflow file and example Go project you can visit the GitHub repository &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;go-github-actions-example&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In this article, we explored how we can use GitHub Actions to set up a Continuous Integration (CI) workflow for a Go project. The journey towards automating and optimising our development workflows is both exciting and immensely rewarding. When introduced into a project, CI&#x2F;CD practices can significantly enhance the quality, reliability, and efficiency of the software development lifecycle.&lt;&#x2F;p&gt;
&lt;p&gt;Implementing continuous integration with GitHub Actions brings significant advantages to Go projects and their development teams. It nurtures a culture of transparency and real-time collaboration, significantly reducing integration issues and fostering a proactive approach to bug fixes and code enhancement. Automated workflows provide instant feedback on changes, streamlining the development process by minimizing manual errors and saving valuable time. This level of automation accelerates the development cycle, enabling teams to focus more on innovation and rapidly deliver value to their users. Ultimately, GitHub Actions and Continuous Integration empower development teams to uphold high quality standards, enhance productivity, and facilitate smoother collaboration.&lt;&#x2F;p&gt;
&lt;p&gt;While we have touched upon the basics of integrating GitHub Actions with Go, there are several advanced patterns and practices that we have not covered in this article. Here are a few topics that I plan to cover in future articles:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Continuous Deployment&lt;&#x2F;strong&gt;: Beyond continuous integration, automating the deployment processes ensures that applications are seamlessly and reliably deployed. Future articles will delve into setting up continuous deployment workflows, enabling Go applications to be deployed automatically to various environments, including staging and production.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Guarding Pull Requests&lt;&#x2F;strong&gt;: Ensuring that only high-quality, tested code makes its way into the project&#x27;s main branches is crucial. We&#x27;ll explore strategies for guarding pull requests, such as requiring status checks to pass before merging and implementing automated code reviews, to maintain code quality and stability.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Versioning&lt;&#x2F;strong&gt;: Managing application versions in a systematic and automated manner is key to tracking releases and ensuring consistency across environments. Future articles will look at how we can automate this process with GitHub Actions workflows, facilitating better release management and compatibility tracking.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Local Development&lt;&#x2F;strong&gt;: We&#x27;ll explore how to test GitHub Actions workflows locally, enabling developers to validate their workflows before pushing changes to the repository. Using the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nektos&#x2F;act&quot;&gt;nektos&#x2F;act&lt;&#x2F;a&gt; tool, we can simulate GitHub Actions locally, ensuring that our workflows are functioning as expected.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For a more in-depth understanding of GitHub Actions and how to leverage it for Go projects, I recommend exploring the official docoumentation for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;actions&quot;&gt;GitHub Actions&lt;&#x2F;a&gt;. The documentation provides comprehensive guidance on setting up workflows, creating custom actions, and integrating with various tools and services.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Getting started with Go and Kafka using franz-go</title>
          <pubDate>Sat, 03 Feb 2024 22:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/getting-started-with-golang-and-kafka/</link>
          <guid>https://aran.dev/posts/getting-started-with-golang-and-kafka/</guid>
          <description xml:base="https://aran.dev/posts/getting-started-with-golang-and-kafka/">&lt;h2 id=&quot;introduction&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#introduction&quot; aria-label=&quot;Anchor link for: introduction&quot;&gt;Introduction&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In this Getting Started guide, we will explore integrating Golang with Apache Kafka. Kafka, is found in many distributed systems, as it excels in handling large-scale and real-time data streams. Its architecture, designed for high throughput and scalability, makes it indispensable in modern data processing pipelines.&lt;&#x2F;p&gt;
&lt;p&gt;Golang, with its concise syntax and efficient concurrency management, stands as an ideal ally for Kafka. This blend of Kafka&#x27;s data handling prowess and Golang&#x27;s execution efficiency creates a formidable combination, perfect for building high-performance applications.&lt;&#x2F;p&gt;
&lt;p&gt;In this guide, we will delve into the mechanics of Kafka, uncovering its core components. We will also explore how Golang&#x27;s features complement Kafka&#x27;s capabilities. Our journey will be lined with detailed examples and practical insights, serving both as a reference and a learning experience.&lt;&#x2F;p&gt;
&lt;p&gt;Before we proceed any further, it is important to note that this guide focuses primarily on the integration of Kafka with Golang for publishing and consuming messages. Running and operating a Kafka cluster involves intricate details and complexities that are beyond the scope of this article. Our aim is to provide a clear and practical approach to using Kafka within Golang applications, rather than delving into the specifics of Kafka cluster management. For those interested in Kafka cluster operations, numerous comprehensive resources and official documentation are available for a more in-depth exploration.&lt;&#x2F;p&gt;
&lt;p&gt;With this understanding, let’s move forward into the heart of integrating Kafka with Golang.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;kafka-and-golang-a-perfect-match&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#kafka-and-golang-a-perfect-match&quot; aria-label=&quot;Anchor link for: kafka-and-golang-a-perfect-match&quot;&gt;Kafka and Golang - A Perfect Match?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Apache Kafka is an open-source streaming platform. Kafka is used to build high performance data pipelines, streaming analytics, and event-driven applications. Kafka is a distributed system consisting of servers and clients. The servers are known as brokers and the clients can be either producers or consumers. Kafka is designed to be highly scalable, fault-tolerant, and fast. Kafka is used by many companies including: Cloudflare, Uber, Netflix, LinkedIn and many more.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;understanding-kafka&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#understanding-kafka&quot; aria-label=&quot;Anchor link for: understanding-kafka&quot;&gt;Understanding Kafka&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Kafka, at its core, is a distributed streaming platform. It is designed to handle large volumes of data in real-time. Initially conceived at LinkedIn, it has grown into a critical component for managing streaming data in coutless industries. Kafka operates on a publish-subscribe model, with key components including producers, consumers, and brokers.&lt;&#x2F;p&gt;
&lt;p&gt;Below the diagram shows the high-level architecture of Kafka.&lt;&#x2F;p&gt;
&lt;p&gt;{{&amp;lt; figure
src=&quot;images&#x2F;kafka-architecture-dark.svg&quot;
alt=&quot;Kafka Architecture&quot;
&amp;gt;}}&lt;&#x2F;p&gt;
&lt;p&gt;Some of the key components of Kafka&#x27;s architecture include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Producers&lt;&#x2F;strong&gt; - Producers are clients that publish data to Kafka topics.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Consumers&lt;&#x2F;strong&gt; - Consumers are clients that subscribe to Kafka topics and consume data.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Brokers&lt;&#x2F;strong&gt; - Brokers are servers that manage the persistence and replication of data.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Topics&lt;&#x2F;strong&gt; - Topics are categories that data is published to and consumed from.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;why-golang&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-golang&quot; aria-label=&quot;Anchor link for: why-golang&quot;&gt;Why Golang?&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Golang is a statically typed, compiled programming language designed at Google. Golang is known for it&#x27;s simple syntax and powerful concurrency model, making it particularly suited for distributed systems. When it comes to Kafka integration, Golang&#x27;s features offer several benefits:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Efficiency and Performance&lt;&#x2F;strong&gt; - Golang&#x27;s efficient execution model maximises Kafka&#x27;s throughput and scalability.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Simplicity and Readability&lt;&#x2F;strong&gt; - The straightforward syntax makes the implementation of Kafka clients more maintainable.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Robust Concurrency Model&lt;&#x2F;strong&gt; - Golang&#x27;s goroutines and channels provide a robust environment for handling concurrent Kafka streams.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;When it comes to interacting with Kafka in Golang, there are several options available. The most popular are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;IBM&#x2F;sarama&quot;&gt;sarama&lt;&#x2F;a&gt; - A Golang library for Apache Kafka.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;confluentinc&#x2F;confluent-kafka-go&quot;&gt;confluent-kafka-go&lt;&#x2F;a&gt; - Confluent&#x27;s Golang client&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;segmentio&#x2F;kafka-go&quot;&gt;kafka-go&lt;&#x2F;a&gt; - Kafka library by Segment&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;twmb&#x2F;franz-go&quot;&gt;franz-go&lt;&#x2F;a&gt; - Feature complete Kafka library in pure Go&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of these libraries has its own strengths and weaknesses. For this guide, we will focus on the &lt;code&gt;franz-go&lt;&#x2F;code&gt; library. It is a pure Go implementation of Kafka, with no dependencies on C bindings.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;crafting-the-kafka-producer-in-golang&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#crafting-the-kafka-producer-in-golang&quot; aria-label=&quot;Anchor link for: crafting-the-kafka-producer-in-golang&quot;&gt;Crafting the Kafka Producer in Golang&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have a better understanding of Kafka and Golang, let&#x27;s explore how we can integrate the two together. We will start by building a Kafka producer in Golang. A Kafka producer is an application that publishes data to Kafka topics. This process is critical for feeding data in the Kafka ecosystem.&lt;&#x2F;p&gt;
&lt;p&gt;Ensure that you have Kafka installed and running. If you don&#x27;t have Kafka installed, you can follow the official &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kafka.apache.org&#x2F;quickstart&quot;&gt;quickstart guide&lt;&#x2F;a&gt; to get up and running. Another alternative is to use Redpanda, a Kafka-compatible event streaming platform built for modern architectures. Redpanda is a drop-in replacement for Kafka, and it is built for performance and scale. You can find the installation instructions for Redpanda &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.redpanda.com&#x2F;current&#x2F;get-started&#x2F;quick-start&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setting-up-a-golang-kafka-producer&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#setting-up-a-golang-kafka-producer&quot; aria-label=&quot;Anchor link for: setting-up-a-golang-kafka-producer&quot;&gt;Setting up a Golang Kafka Producer&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Getting set up with the &lt;code&gt;twmb&#x2F;franz-go&lt;&#x2F;code&gt; library is straightforward. We can install the library using the &lt;code&gt;go get&lt;&#x2F;code&gt; command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; go&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; get&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; github.com&#x2F;twmb&#x2F;franz-go@latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The first step for creating a Kafka producer is initialising a Kafka client. We can do this by creating a new client with the &lt;code&gt;kgo.NewClient&lt;&#x2F;code&gt; function. This function takes a variadic list of options that can be used to configure the client.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;package&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;	&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;context&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;	&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;github.com&#x2F;twmb&#x2F;franz-go&#x2F;pkg&#x2F;kgo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	opts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Opt&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;SeedBrokers&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;localhost:9092&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;DefaultProduceTopic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;my-topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ClientID&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;my-client-id&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	client&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewClient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;opts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; TODO: handle&#x2F;log this error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		return&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Close&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have a Kafka client, we can start producing messages to Kafka. This is really simple to do with the &lt;code&gt;franz-go&lt;&#x2F;code&gt; library. But first we need to create our message.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	record&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Record&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		Value&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello World&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		Topic&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;my-first-topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have our &lt;code&gt;kgo.Record&lt;&#x2F;code&gt; which contains our message and the topic we want to send it to, we can produce the message to Kafka.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ProduceSync&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Background&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; record&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;FirstErr&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; TODO: handle&#x2F;log this error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		return&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Lets now put all of this together into a complete example.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;package&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;	&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;context&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;	&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;github.com&#x2F;twmb&#x2F;franz-go&#x2F;pkg&#x2F;kgo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	opts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Opt&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;SeedBrokers&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;localhost:9092&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;DefaultProduceTopic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;my-topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ClientID&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;my-client-id&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	client&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewClient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;opts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; TODO: handle&#x2F;log this error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		return&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Close&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	record&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Record&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		Value&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Hello World&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		Topic&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;my-topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ProduceSync&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Background&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; record&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;FirstErr&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; TODO: handle&#x2F;log this error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		return&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It should be noted the above example does not handle any errors. In a production system, you would want to handle errors and log them accordingly. You would also want to look at other best practices such as message serialisation and graceful shutdowns.&lt;&#x2F;p&gt;
&lt;p&gt;By following the steps above, we have successfully created a Kafka producer in Golang using the &lt;code&gt;franz-go&lt;&#x2F;code&gt; library. This producer is now capable of publishing messages to a Kafka topic, forming the basis of a data pipeline into Kafka. In the next section we will turn our attention to other side of this equation: consuming message from Kafka.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-the-kafka-consumer&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#building-the-kafka-consumer&quot; aria-label=&quot;Anchor link for: building-the-kafka-consumer&quot;&gt;Building the Kafka Consumer&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In the previous section, we explored how to create a Kafka producer in Golang. Now we will turn our attention to building a Kafka consumer. Building a robust Kafka consumer is essential for processing data from Kafka topics, especially high-throughput data streams.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;franz-go&lt;&#x2F;code&gt; library is known for being a feature complete Kafka library in pure Go. It provides a robust environment for handling Kafka streams, making it an ideal choice for building Kafka consumers in Golang.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setting-up-a-golang-kafka-consumer&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#setting-up-a-golang-kafka-consumer&quot; aria-label=&quot;Anchor link for: setting-up-a-golang-kafka-consumer&quot;&gt;Setting up a Golang Kafka Consumer&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Similar to the Kafka producer, we start with creating a Kafka client. We can do this by creating a new client with the &lt;code&gt;kgo.NewClient&lt;&#x2F;code&gt; function. This function takes a variadic list of options that can be used to configure the client. For a consumer however we need to specify a few other options such as the group id and the topics we want to consume from.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;package&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;	&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;context&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;	&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;	&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;github.com&#x2F;twmb&#x2F;franz-go&#x2F;pkg&#x2F;kgo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	opts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Opt&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;SeedBrokers&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;localhost:9092&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ClientID&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;consumer-client-id&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ConsumerGroup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;my-group-identifier&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ConsumeTopics&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;my-topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	client&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewClient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;opts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; TODO: handle&#x2F;log this error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		return&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Close&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have a Kafka client, we can start consuming messages from Kafka. This is really simple to do with the &lt;code&gt;franz-go&lt;&#x2F;code&gt; library.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		fetches&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;PollFetches&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Background&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; errs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fetches&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errors&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;errs&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;			&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; All errors are retried internally when fetching, but non-retriable errors are&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;			&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; returned from polls so that users can notice and take action.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;			panic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sprint&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;errs&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; We can iterate through a record iterator...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		iter&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fetches&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;RecordIter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		for&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;iter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Done&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;			record&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; iter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Next&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;			fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;record&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Value&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;from an iterator!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Lets now put all of this together into a complete example.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;package&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;	&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;context&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;	&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;	&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;github.com&#x2F;twmb&#x2F;franz-go&#x2F;pkg&#x2F;kgo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	opts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Opt&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;SeedBrokers&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;localhost:9092&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ClientID&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;consumer-client-id&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ConsumerGroup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;my-group-identifier&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ConsumeTopics&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;my-topic&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	client&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; kgo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewClient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;opts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; TODO: handle&#x2F;log this error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		return&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Close&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	for&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		fetches&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;PollFetches&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Background&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; errs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fetches&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Errors&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;errs&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;			&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; All errors are retried internally when fetching, but non-retriable errors are&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;			&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; returned from polls so that users can notice and take action.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;			panic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Sprint&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;errs&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;		&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; We can iterate through a record iterator...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		iter&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; fetches&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;RecordIter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		for&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;iter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Done&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;			record&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; iter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Next&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;			fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;record&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;Value&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;from an iterator!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This example only shows consuming messages from a single topic and using a very simplistic solution. In a production system, you would want to handle errors, log them accordingly, and look at other best practices. The &lt;code&gt;franz-go&lt;&#x2F;code&gt; library provides a lot of flexibility and features for consuming messages from Kafka.&lt;&#x2F;p&gt;
&lt;p&gt;Some best practices that you would want to consider as well in a production system include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Offset Management&lt;&#x2F;strong&gt; - Ensure you are tracking which messages you have consumed, avoiding data loss or duplication.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Concurrency&lt;&#x2F;strong&gt; - Leverage Golang&#x27;s concurrency model to consume messages in parallel, enhancing throughput and efficiency.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Error Handling&lt;&#x2F;strong&gt; - Implement robust error handling and re-balances to ensure your consumer remains stable under various conditions.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;By following the steps above, you have created a simple Kafka consumer in Golang capable of reading and processing messages from Kafka topics. This consumer forms the foundation for any data processing pipline that requires data from Kafka.&lt;&#x2F;p&gt;
&lt;p&gt;In this getting started guide we have explored how to leverage Golang to produce and consume messages from Kafka.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Through this guide, we have explored the versatile and feature-rich &lt;code&gt;franz-go&lt;&#x2F;code&gt; library for integrating Kafka with Golang. We&#x27;ve uncovered the synergy between Kafka&#x27;s scalable messaging capabilities and Golang&#x27;s efficiency and simplicity. This forms a powerful combination for building high-performance data pipelines and event-driven applications. Although producing messages was fairly straightforward, consuming messages requires a bit more consideration and the simplification of the example above is not suitable for production. Kafka consumers require careful management of offsets and error handling in order to ensure data is processed correctly.&lt;&#x2F;p&gt;
&lt;p&gt;I have collected the above examples together along with a Docker Compose file that runs Redpanda and a console for managing Redpanda so that you can run the examples yourself. You can find the code and instructions for running the examples &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;aranw&#x2F;franz-go-examples&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re looking for more on Kafka with Go, I&#x27;ve also written about &lt;a href=&quot;&#x2F;posts&#x2F;building-future-proof-data-pipeline-with-golang-and-kafka&#x2F;&quot;&gt;building a future-proof data pipeline with Go and Kafka&lt;&#x2F;a&gt; covering a real-world high-throughput system, and &lt;a href=&quot;&#x2F;posts&#x2F;processing-uk-rail-data-in-real-time&#x2F;&quot;&gt;processing UK rail data in real-time&lt;&#x2F;a&gt; which uses franz-go with PostgreSQL table partitioning.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Building a future-proof data pipeline with Go and Kafka</title>
          <pubDate>Thu, 11 Jan 2024 11:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/building-future-proof-data-pipeline-with-golang-and-kafka/</link>
          <guid>https://aran.dev/posts/building-future-proof-data-pipeline-with-golang-and-kafka/</guid>
          <description xml:base="https://aran.dev/posts/building-future-proof-data-pipeline-with-golang-and-kafka/">&lt;h2 id=&quot;introduction&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#introduction&quot; aria-label=&quot;Anchor link for: introduction&quot;&gt;Introduction&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The device data processing system is an essential component for business operations. It was, however, becoming unfit for purpose as new systems and teams needed access to the data. The previous system had various limitations, was limited to one cloud platform and was restrictive in how consumers could access the data. We decided to incorporate a new architecture by developing a new system, which presented a significant challenge for stakeholders. They needed quick and efficient access to crucial data but didn&#x27;t want to introduce what were considered legacy components into new systems.&lt;&#x2F;p&gt;
&lt;p&gt;We decided to rebuild the data processing pipeline. Device data arrives in high frequencies every second from many devices. The significant challenges of handling and processing data swiftly led us to choose Golang and Kafka. Golang and Kafka&#x27;s ability to manage high data throughput efficiently made it ideal for processing and publishing data.&lt;&#x2F;p&gt;
&lt;p&gt;Each message contains device readings. These readings can include frequency, state of charge, voltage, and many more. The readings are either tracked on a sub-second or second-level interval. The reading interval depends on the device configuration. The configuration can vary due to various factors. These include device types, sites, and other options. The aim was to build a new system that is robust and flexible. The system had to be able to handle the inconsistent data structures and high volume of data.&lt;&#x2F;p&gt;
&lt;p&gt;We chose Kafka as the new system&#x27;s core messaging platform, using it to streamline data flow between various cloud providers and platforms. This greatly improved our data access and utilisation within the organisation.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve discussed our project&#x27;s challenges and goals. We will now delve into the technical solution.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;technical-deep-dive&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#technical-deep-dive&quot; aria-label=&quot;Anchor link for: technical-deep-dive&quot;&gt;Technical Deep-Dive&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;At the heart of our system is a single Golang service. This service pulls messages from RabbitMQ. Then, it splits them into smaller messages.&lt;&#x2F;p&gt;
&lt;p&gt;In our first approach, we tried to define all the fields into a Golang struct. We then used Golang&#x27;s standard JSON encoding library to handle the decoding. However, we encountered several challenges due to the nature of the messages. Messages often varied with device configurations, leading to issues such as missing fields and data type inconsistencies, which ultimately caused decoding problems. These hurdles underscored the need for a new approach. Acknowledging these challenges, we decided to shift our message decoding strategy and refactor our message handling.&lt;&#x2F;p&gt;
&lt;p&gt;In the refactor, we adopted the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tidwall&#x2F;gjson&quot;&gt;tidwall&#x2F;gjson&lt;&#x2F;a&gt; package for message handling, enabling us to generically handle retrieval of fields from incoming messages. This approach ensured our system remained versatile, capable of adapting to varied data structures with ease. Utilising the gjson package for message decoding allowed us to eliminate the reliance on Golang structs and hard-coded fields, thereby significantly enhancing both performance and efficiency in decoding messages.&lt;&#x2F;p&gt;
&lt;p&gt;A standout feature of our revised system is its exceptional flexibility in field handling, eliminating the need for predefined fields. This design allows our service to seamlessly accommodate new fields as they emerge, without requiring updates or deployments to implement these changes.&lt;&#x2F;p&gt;
&lt;p&gt;With the technical framework in place, our attention turned to the crucial aspect of performance.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;performance-improvements&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#performance-improvements&quot; aria-label=&quot;Anchor link for: performance-improvements&quot;&gt;Performance Improvements&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Through custom observability metrics, we meticulously tracked the performance of our message handling logic, ensuring the system operated as anticipated. These metrics offered real-time data and insights, enabling us to pinpoint areas for improvement. Furthermore, they played a crucial role in measuring the impact of our ongoing changes and guiding our optimisation efforts.&lt;&#x2F;p&gt;
&lt;p&gt;Initially, our system relied on Golang structs and custom unmarshalling logic, leading to performance constraints. The variability in data from different devices and their settings introduced complexities that necessitated expensive type-checking logic within the code&#x27;s critical paths, slowing down message processing times. As a result, our throughput was capped at approximately 2 Megabits per second (Mbps). Although the performance was acceptable, each new variation in data required alterations or additional custom unmarshalling logic, rendering this approach unsustainable in the long run.&lt;&#x2F;p&gt;
&lt;p&gt;Recognising the need for improving performance and maintainability, we revisited our strategy. Using the metrics from our monitoring platform we were able to do the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Refactor RabbitMQ message retrieval&lt;&#x2F;li&gt;
&lt;li&gt;Identify and fixed bottlenecks with message parsing and handling&lt;&#x2F;li&gt;
&lt;li&gt;Develop an improved strategy for monitoring and benchmarking performance going forward&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These improvements significantly boosted the service&#x27;s overall performance. Thanks to our continuous monitoring, we we&#x27;re able to make informed adjustments, leading to a notable increase in throughput to approximately 4 Mbps. Effectively, these changes doubled our previously recorded throughput and message handling capacity.&lt;&#x2F;p&gt;
&lt;p&gt;Addressing the next major challenge required us to investigate alternatives to using hard-coded Golang structs and custom unmarshalling logic. We continued to encounter new message types and discovered missing data fields, the need for a more adaptable approach became evident. Constant modifications to our message handling logic proved to be unsustainable. Thus, we shifted towards a generic solution for message processing, reducing our reliance on intensive type-checking. This transition not only streamlined message processing and simplified maintenance but also significantly decreased the time required to handle each message. The impact of this change was profound, nearly doubling our throughput once more to approximately 8&#x2F;9 Mbps and enabling our service to process an average of 120,000 messages per second.&lt;&#x2F;p&gt;
&lt;p&gt;With these improvements in performance, we are now ready to explore future enhancements. The next phase of our project focuses on expanding our capabilities and exploring new features. We will also examine how we expose those to stakeholders in the future.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;future-scope&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#future-scope&quot; aria-label=&quot;Anchor link for: future-scope&quot;&gt;Future Scope&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Looking ahead, our journey with this data processing system is far from over. Successfully integrating data into Kafka is merely the initial step of an ongoing saga. The forthcoming crucial phase will involve the transformation of high-frequency data into aggregated streams. These streams will be meticulously segmented into specific time windows, meticulously designed to cater to the unique requirements of diverse teams across the organisation. By doing so, we aim to provide tailored data streams that enable departments to consume and leverage data more effectively, enhancing decision-making processes and operational efficiency. This strategic approach will not only streamline data accessibility but also ensure that each team has the insights needed to drive their initiatives forward.&lt;&#x2F;p&gt;
&lt;p&gt;A key focus will be on aggregating the data into specific time windows. These include 50 milliseconds, 1 second, and 30-minute increments. These varied time windows will help stakeholders have a comprehensive view of the data. They cater to both real-time monitoring and longer-term analytical needs. This level of detail will allow for more nuanced insights. It will also improve decision-making processes across departments.&lt;&#x2F;p&gt;
&lt;p&gt;Furthermore, we will focus on enhancing our benchmarking and test coverage. Our goal is to establish a robust framework. It can detect any regressions in performance proactively. We aim to maintain the high standards of efficiency and reliability that we&#x27;ve set.&lt;&#x2F;p&gt;
&lt;p&gt;Lastly, identifying our current system&#x27;s limitations is imperative. We intend to use the improved monitoring to analysis and pinpoint potential bottlenecks and scalability challenges. With a clear understanding of these limitations, we&#x27;ll devise mitigation strategies. These strategies may include adopting new technologies, revising our system architecture, or refining our data processing.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Our project has enhanced data processing capabilities by harnessing the power of Golang and Kafka, effectively addressing the complexities associated with high-frequency data. Through incremental improvements, we&#x27;ve managed to increase throughput from 2&#x2F;3 Mbps to 8&#x2F;9 Mbps. Moving forward, our focus remains on further optimising data streams and scrutinising the scalability of our system. This effort has not only significantly improved our processing speed but also set a strong base for better handling of high-frequency data.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re interested in getting started with Kafka in Go, check out my &lt;a href=&quot;&#x2F;posts&#x2F;getting-started-with-golang-and-kafka&#x2F;&quot;&gt;Getting started with Go and Kafka using franz-go&lt;&#x2F;a&gt; guide which covers the fundamentals of building producers and consumers. I also wrote about &lt;a href=&quot;&#x2F;posts&#x2F;processing-uk-rail-data-in-real-time&#x2F;&quot;&gt;processing UK rail data in real-time&lt;&#x2F;a&gt; using a similar Kafka-based architecture.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Age: a practical guide to modern file encryption</title>
          <pubDate>Sun, 25 Jun 2023 09:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/age-encryption-guide/</link>
          <guid>https://aran.dev/posts/age-encryption-guide/</guid>
          <description xml:base="https://aran.dev/posts/age-encryption-guide/">&lt;p&gt;&lt;code&gt;age&lt;&#x2F;code&gt; is a modern alternative to tools like to older file encryption tools like GPG. It is designed to be simple, modern and secure. &lt;code&gt;age&lt;&#x2F;code&gt; is built using modern encryption primitives and is designed to be easy to use. &lt;code&gt;age&lt;&#x2F;code&gt; works by creating or using public&#x2F;private key pairs to encrypt and decrypt files. The public key can be shared with others to allow them to encrypt files that only you can decrypt. &lt;code&gt;age&lt;&#x2F;code&gt; is designed to be simple and easy to use.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;age&lt;&#x2F;code&gt; can be found on GitHub &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;FiloSottile&#x2F;age&quot;&gt;here&lt;&#x2F;a&gt;. In this article, I will explore &lt;code&gt;age&lt;&#x2F;code&gt; and demonstrate how to use it to encrypt and decrypt files.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;installation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#installation&quot; aria-label=&quot;Anchor link for: installation&quot;&gt;Installation&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;age&lt;&#x2F;code&gt; is available for a variety of platforms including Linux, macOS, Windows, and more! The easiest way to install &lt;code&gt;age&lt;&#x2F;code&gt; is to use a package manger for your platform. A list of available places to install &lt;code&gt;age&lt;&#x2F;code&gt; can be found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;FiloSottile&#x2F;age&#x2F;tree&#x2F;main#installation&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;usage&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#usage&quot; aria-label=&quot;Anchor link for: usage&quot;&gt;Usage&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;age&lt;&#x2F;code&gt; can encrypt and decrypt files with either using a passphrase or a public&#x2F;private key pair. The following examples demonstrate how to use &lt;code&gt;age&lt;&#x2F;code&gt; to encrypt and decrypt files.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;using-age-with-a-passphrase&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#using-age-with-a-passphrase&quot; aria-label=&quot;Anchor link for: using-age-with-a-passphrase&quot;&gt;Using age with a passphrase&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;To encrypt a file using a passphrase, use the following command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; age&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-passphrase&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-output&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; output.txt.age&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; input.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; passphrase&lt;&#x2F;span&gt;&lt;span&gt; (leave&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; empty&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; to&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; autogenerate&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; secure&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; one&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Confirm&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; passphrase:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To decrypt a file using a passphrase, use the &lt;code&gt;age&lt;&#x2F;code&gt; command with the &lt;code&gt;--decrypt&lt;&#x2F;code&gt; option:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;❯&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; age&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-decrypt&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-output&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; input.txt&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; output.txt.age&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; passphrase:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;using-age-with-a-public-private-key-pair&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#using-age-with-a-public-private-key-pair&quot; aria-label=&quot;Anchor link for: using-age-with-a-public-private-key-pair&quot;&gt;Using age with a public&#x2F;private key pair&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;To encrypt a file using a public&#x2F;private key pair, use the following command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;❯&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; age-keygen&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ~&#x2F;.config&#x2F;age&#x2F;keys.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;age-keygen:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; warning:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; writing&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; secret&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; key&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; to&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; world-readable&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; file&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Public&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; key:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; age1hpct7gdhldvx3q0lj94t5nfp2zhqwq6ksemk65l2xrrvzlykavzsa9ngh0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To encrypt a file using a public&#x2F;private key pair, use the following command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;tar&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; cvz&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ~&#x2F;data&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; age&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;r&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; age1hpct7gdhldvx3q0lj94t5nfp2zhqwq6ksemk65l2xrrvzlykavzsa9ngh0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; data.tar.gz.age&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To decrypt a file using a public&#x2F;private key pair, use the following command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;❯&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; age&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;-decrypt&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ~&#x2F;.config&#x2F;age&#x2F;keys.txt&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;o&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; data.tar.gz&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; data.tar.gz.age&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;using-age-with-ssh-keys&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#using-age-with-ssh-keys&quot; aria-label=&quot;Anchor link for: using-age-with-ssh-keys&quot;&gt;Using age with SSH Keys&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;age&lt;&#x2F;code&gt; can also be used with SSH keys. For example you can use public keys retrieved from GitHub to encrypt files that only the owner of the private key can decrypt. To do this, you can use the following command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;❯&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; curl&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; https:&#x2F;&#x2F;github.com&#x2F;aranw.keys&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; age&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;R&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; input.txt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; output.txt.age&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To decrypt the file this will only be possible with my private key. This is a great way to share files with others without the need for a shared secret or passphrase.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;using-a-yubikey-with-age&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#using-a-yubikey-with-age&quot; aria-label=&quot;Anchor link for: using-a-yubikey-with-age&quot;&gt;Using a YubiKey with age&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Another fantastic feature of &lt;code&gt;age&lt;&#x2F;code&gt; is the ability to use plugins to extend its functionality. For example, you can use a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.yubico.com&#x2F;&quot;&gt;YubiKey&lt;&#x2F;a&gt; to encrypt and decrypt files. To install the age YubiKey plugin, you can visit the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;str4d&#x2F;age-plugin-yubikey&quot;&gt;GitHub repository&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Once installed you can use the following command to setup age to work with your YubiKey:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;❯&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; age-plugin-yubikey&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will start an interactive prompt that will help configure &lt;code&gt;age&lt;&#x2F;code&gt; and your YubiKey to work together. Once completed you&#x27;ll be able to use your YubiKey to encrypt and decrypt files given the correct recipient id and configuration.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#next&quot; aria-label=&quot;Anchor link for: next&quot;&gt;Next&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;age&lt;&#x2F;code&gt; is a great tool for encrypting files and is a great alternative to older tools like GPG. I think the next steps for me to explore and combining &lt;code&gt;age&lt;&#x2F;code&gt; with &lt;code&gt;sops&lt;&#x2F;code&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;sops&quot;&gt;Secrets OPerationS&lt;&#x2F;a&gt; from Mozilla.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re interested in encryption in Go more broadly, I&#x27;ve also written about &lt;a href=&quot;&#x2F;posts&#x2F;using-golang-crypto-aes-and-crypto-cipher-packages&#x2F;&quot;&gt;using Go&#x27;s crypto&#x2F;aes and crypto&#x2F;cipher packages&lt;&#x2F;a&gt; for AES encryption and decryption in your own applications.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Exploring Go 1.21: generics in the standard library and more</title>
          <pubDate>Fri, 23 Jun 2023 09:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/exploring-the-latest-release-candidate-of-golang-1-21/</link>
          <guid>https://aran.dev/posts/exploring-the-latest-release-candidate-of-golang-1-21/</guid>
          <description xml:base="https://aran.dev/posts/exploring-the-latest-release-candidate-of-golang-1-21/">&lt;p&gt;Exciting developments are on the horizon for the Go programming language as it enters the release candidate phase of its latest version Go 1.21. Among the most highly anticipated addition is the introduction of generics support to the standard library. This new release not only brings generics to the standard library but it also includes changes to the Golang&#x27;s tooling, a new structured logging package, an experimental wasi port and more. In this article, we delve into the exciting features of this release candidate, highlighting some of my favourite new features coming in Go 1.21.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;go-toolchain&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#go-toolchain&quot; aria-label=&quot;Anchor link for: go-toolchain&quot;&gt;Go Toolchain&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The Go toolchain is a collection of tools that are used to compile, test, and run Go code. With the upcoming release of Go 1.21, developers will gain the ability to select their preferred version. This functionality can be leveraged by adding &lt;code&gt;go&lt;&#x2F;code&gt; or &lt;code&gt;toolchain&lt;&#x2F;code&gt; lines either to the projects &lt;code&gt;go.mod&lt;&#x2F;code&gt; or &lt;code&gt;go.work&lt;&#x2F;code&gt; files. Consequently, the go toolchain will seamlessly download, cache and utilise the specified version. If desired, automatic toolchain switching can be disabled. However, in such cases, the go command will refuse to execute within a directory containing a &lt;code&gt;go.mod&lt;&#x2F;code&gt; or &lt;code&gt;go.work&lt;&#x2F;code&gt; file that specifies a newer version the currently running one. In the future, I plan to write more articles that delve into the this new feature in more depth and the possibilities it enables. For more information on the new go toolchain, check out the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go.dev&#x2F;doc&#x2F;toolchain&quot;&gt;documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;min-max-and-clear-built-in-functions&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#min-max-and-clear-built-in-functions&quot; aria-label=&quot;Anchor link for: min-max-and-clear-built-in-functions&quot;&gt;min, max, and clear built-in functions&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;One notable addition in this release is the introduction of three new built-in functions: min, max, and clear. These functions aim to provide developers with enhanced convenience and flexibility when working with data.&lt;&#x2F;p&gt;
&lt;p&gt;The min and max functions are designed to simplify the process of finding the minimum and maximum values for the given values. With the introduction of these new functions, finding the smallest or largest values becomes a straightforward task, reducing both coding effort and potential errors.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, the clear function offers a convenient way to delete or zero out all elements. This can be particularly useful in scenarios where the reusability of data structures is crucial, allowing developers to easily initalise or reset them without the need for manual iteration or complex workarounds. Check out the documentation for the new &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tip.golang.org&#x2F;ref&#x2F;spec#Min_and_max&quot;&gt;min&#x2F;max&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tip.golang.org&#x2F;ref&#x2F;spec#Clear&quot;&gt;clear&lt;&#x2F;a&gt; functions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;structured-logging-with-log-slog&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#structured-logging-with-log-slog&quot; aria-label=&quot;Anchor link for: structured-logging-with-log-slog&quot;&gt;Structured Logging with log&#x2F;slog&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The log&#x2F;slog library is a new structured logging package that is being introduced in Go 1.21. This new logger brings immense convenience and eliminates the need for external libraries like zap or zerolog. With the introduction of this new package, developers will be able to easily log structured data without the need for additional dependencies. Developers will no longer need to spend time evulating and integrating external logging libraries, instead they can simply use the log&#x2F;slog library to log structured data. Open source libraries and projects will also benefit from this new package as it will allow them to provide structured logging without the need for external dependencies or interfaces to other logging libraries. More information about the new &lt;code&gt;log&#x2F;slog&lt;&#x2F;code&gt; package can be found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;log&#x2F;slog&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;more&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#more&quot; aria-label=&quot;Anchor link for: more&quot;&gt;More!&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In this article, I have provided an overview of some of the features included in Go 1.21. However, it is important to note that there are numerous other exciting additions in this release. For a comprehensive understanding of the upcoming release, I recommend visiting the official Go &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go.dev&#x2F;blog&#x2F;go1.21rc&quot;&gt;blog&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tip.golang.org&#x2F;doc&#x2F;go1.21&quot;&gt;release notes&lt;&#x2F;a&gt; for detailed information about the new functionalities and enhancements coming in Go 1.21.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Using Go private modules with GitHub Actions</title>
          <pubDate>Sat, 10 Jun 2023 15:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/github-actions-go-private-modules/</link>
          <guid>https://aran.dev/posts/github-actions-go-private-modules/</guid>
          <description xml:base="https://aran.dev/posts/github-actions-go-private-modules/">&lt;div class=&quot;note-container&quot;&gt;
    
            &lt;div class=&quot;note-header&quot;&gt;
                
                    &lt;div class=&quot;note-icon&quot;&gt;
                        &lt;p&gt;Info&lt;&#x2F;p&gt;

                    &lt;&#x2F;div&gt;
                
            &lt;&#x2F;div&gt;
            &lt;div class=&quot;note-content&quot;&gt;
                &lt;p&gt;I&#x27;ve followed up this post with articles on &lt;a href=&quot;&#x2F;posts&#x2F;continuous-integration-with-go-and-github-actions&#x2F;&quot;&gt;Continuous Integration with Go and GitHub Actions&lt;&#x2F;a&gt; and &lt;a href=&quot;&#x2F;posts&#x2F;continuous-deployment-with-go-and-github-actions&#x2F;&quot;&gt;Continuous Deployment with Go and GitHub Actions&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;

            &lt;&#x2F;div&gt;
        
    &lt;&#x2F;div&gt;
&lt;p&gt;As the number of repositories within a project increases so does the complexity of the dependencies. As time goes on you begin to want to share code between the repositories. This is where Golangs Module system comes in handy. Creating new private modules is easy but challenges arise when you want to use those modules in your projects. We&#x27;ll explore how to configure local development and GitHub Actions to use private Go modules.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;creating-a-private-go-module&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#creating-a-private-go-module&quot; aria-label=&quot;Anchor link for: creating-a-private-go-module&quot;&gt;Creating a Private Go Module&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Creating a private Go module is pretty simple. Initialising a new module by running &lt;code&gt;go mod init github.com&#x2F;{organisation&#x2F;{name}&lt;&#x2F;code&gt; will create a new module. You can then add your code to the module and commit it to your repository. To make the module private you can push it to a private repository on GitHub.&lt;&#x2F;p&gt;
&lt;p&gt;Now that we have our module we&#x27;ll want to use it with other Go projects.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;local-development&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#local-development&quot; aria-label=&quot;Anchor link for: local-development&quot;&gt;Local Development&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;For this we&#x27;ll have to alter our local development configuration to allow the Go tooling to access the private module.&lt;&#x2F;p&gt;
&lt;p&gt;The first thing we need to configure on our local development environment is the &lt;code&gt;GOPRIVATE&lt;&#x2F;code&gt; environment variable. This variable tells the Go tooling which modules should be considered private and should not be access via proxies or checksum database. We can set this variable to our GitHub organisation &lt;code&gt;GOPRIVATE=github.com&#x2F;{organisation}&lt;&#x2F;code&gt;. This variable can include multiple addresses as a comma separated list. This will allow the Go tooling to access any private modules within the organisation.&lt;&#x2F;p&gt;
&lt;p&gt;The next thing we need to do is configure the Go tooling to use our GitHub credentials. There are two ways to configure this. The first is to use the &lt;code&gt;.netrc&lt;&#x2F;code&gt; file. This file is used to store credentials for remote hosts. The file is usually located at &lt;code&gt;~&#x2F;.netrc&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The file should contain the following:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;machine&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;hos&lt;&#x2F;span&gt;&lt;span&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;login&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;LOGI&lt;&#x2F;span&gt;&lt;span&gt;N&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;password&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;PASSWOR&lt;&#x2F;span&gt;&lt;span&gt;D&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Although the file suggests using a password GitHub does not actually support this. Instead we can use a personal access token. You can create a new personal access token by going to &lt;code&gt;Settings &amp;gt; Developer settings &amp;gt; Personal access tokens&lt;&#x2F;code&gt;. Once you have created a new token you can add it to the &lt;code&gt;.netrc&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;p&gt;The other authentication method is to use SSH keys. This is the method I prefer as it allows me to use my existing SSH keys. To use this method we need to change our git configuration to use SSH instead of HTTPS. We can do this by adding the following to our git configuration:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;url &amp;quot;ssh:&#x2F;&#x2F;git@github.com&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    insteadOf&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now with the &lt;code&gt;GOPRIVATE&lt;&#x2F;code&gt; environment variable and our authentication method configured we can use our private modules in our projects locally.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;github-actions&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#github-actions&quot; aria-label=&quot;Anchor link for: github-actions&quot;&gt;GitHub Actions&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have our local development environment configured we need to configure GitHub Actions to use our private modules.&lt;&#x2F;p&gt;
&lt;p&gt;Like local development we&#x27;ll need to configure the &lt;code&gt;GOPRIVATE&lt;&#x2F;code&gt; environment variable and our authentication method. This can be like local either using Personal Access Tokens or SSH keys. Recently I used the SSH key method to configure GitHub Actions. In this article we will explore how to configure GitHub Actions to use SSH keys.&lt;&#x2F;p&gt;
&lt;p&gt;The first thing we will configure is the &lt;code&gt;GOPRIVATE&lt;&#x2F;code&gt; environment variable. We can do this by adding the following to our workflow:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;nv&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  G&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;OPRIVATE&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ithub.com&#x2F;{organisation}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The next step is configuring the SSH Key. To use this we&#x27;ll need to add a Deploy SSH key to our private module. We can do this by going to &lt;code&gt;Settings &amp;gt; Deploy keys&lt;&#x2F;code&gt; and adding a new key. We&#x27;ll need to give the key a name and add the public key. The public key can be generated by running &lt;code&gt;ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot; -f github-actions&lt;&#x2F;code&gt;. This will generate a new SSH key pair. We can then add the public key as our Deploy Key. The private key will be used in our GitHub Actions workflow.&lt;&#x2F;p&gt;
&lt;p&gt;Adding the private key to our GitHub Actions should be done using GitHub Secrets. We can add the private key to our GitHub Secrets by going to our organisation settings &lt;code&gt;Settings &amp;gt; Organizations&lt;&#x2F;code&gt; then selecting our organisation then to &lt;code&gt;Secrets and variables &amp;gt; Actions&lt;&#x2F;code&gt;. We can then add a new secret with the name &lt;code&gt;GO_MODULE_PRIVATE_KEY&lt;&#x2F;code&gt; and the contents of the generated private key.&lt;&#x2F;p&gt;
&lt;p&gt;Now that we have our private key configured we can add it to our GitHub Actions workflow. We can do this by adding the following step to our workflows jobs:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;dd SSH Go Module Private Key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;nv&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;      S&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;SH_AUTH_SOCK&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;tmp&#x2F;ssh_agent.sock&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;      mkdir -p ~&#x2F;.ssh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;      ssh-keyscan github.com &amp;gt;&amp;gt; ~&#x2F;.ssh&#x2F;known_hosts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;      ssh-agent -a $SSH_AUTH_SOCK &amp;gt; &#x2F;dev&#x2F;null&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;      ssh-add - &amp;lt;&amp;lt;&amp;lt; &amp;quot;${{ secrets.GO_MODULE_PRIVATE_KEY }}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;      echo &amp;quot;SSH_AUTH_SOCK=$SSH_AUTH_SOCK&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this snippet you can see the use of &lt;code&gt;ssh-add&lt;&#x2F;code&gt; to add the private key to the SSH agent and the use of &lt;code&gt;echo&lt;&#x2F;code&gt; to add the SSH agent to the environment. This will allow the Go tooling to use the private key to access the private module.&lt;&#x2F;p&gt;
&lt;p&gt;The next thing we will have to add now is the Git configuration making Git and the Go tooling use SSH instead of HTTPS. We can do this by adding the following step to our workflow:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; S&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;etup access for private go modules&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    git config --global url.&amp;quot;ssh:&#x2F;&#x2F;git@github.com&#x2F;&amp;quot;.insteadOf https:&#x2F;&#x2F;github.com&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have all of the necessary configuration we can use our private module in our GitHub Actions workflows. Here&#x27;s a full workflow example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; G&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o Vet &amp;amp; Test&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;on&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  p&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ull_request&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;j&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;obs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;  t&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;est&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Test&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;uns-on&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;buntu-20.04&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    s&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;teps&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ctions&#x2F;checkout@v3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; S&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;et up Go&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;      u&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ses&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ctions&#x2F;setup-go@v4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;dd SSH Go Module Private Key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;      e&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;nv&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;        S&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;SH_AUTH_SOCK&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;tmp&#x2F;ssh_agent.sock&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;      r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        mkdir -p ~&#x2F;.ssh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        ssh-keyscan github.com &amp;gt;&amp;gt; ~&#x2F;.ssh&#x2F;known_hosts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        ssh-agent -a $SSH_AUTH_SOCK &amp;gt; &#x2F;dev&#x2F;null&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        ssh-add - &amp;lt;&amp;lt;&amp;lt; &amp;quot;${{ secrets.GO_MODULE_PRIVATE_KEY }}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        echo &amp;quot;SSH_AUTH_SOCK=$SSH_AUTH_SOCK&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; S&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;etup access for private go modules&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;      r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;        git config --global url.&amp;quot;ssh:&#x2F;&#x2F;git@github.com&#x2F;&amp;quot;.insteadOf https:&#x2F;&#x2F;github.com&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; n&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ame&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; R&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;un tests&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;      r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;un&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o test&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Of course you can make your workflow do more than just run tests. You can use this workflow to build your Go application, deploy to a server or even create a Docker image.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;We explored how to configure local development and GitHub Actions to use private Go modules. We looked at how to configure the &lt;code&gt;GOPRIVATE&lt;&#x2F;code&gt; environment variable and how to configure the Go tooling to use our GitHub credentials. We also looked at how to configure GitHub Actions to use the same configuration as our local development environment.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Understanding Go&#x27;s sync.Cond</title>
          <pubDate>Fri, 31 Mar 2023 22:30:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/golang-sync-cond/</link>
          <guid>https://aran.dev/posts/golang-sync-cond/</guid>
          <description xml:base="https://aran.dev/posts/golang-sync-cond/">&lt;h2 id=&quot;exploring-sync-cond-from-the-golang-sync-package&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#exploring-sync-cond-from-the-golang-sync-package&quot; aria-label=&quot;Anchor link for: exploring-sync-cond-from-the-golang-sync-package&quot;&gt;Exploring sync.Cond from the Golang sync Package&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;After a colleague shared a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=zd_dtrhjIQ0&quot;&gt;video&lt;&#x2F;a&gt; with me about the Go programming language reviewing the use of Go Generics and the &lt;code&gt;Cond&lt;&#x2F;code&gt; type from the go standard library &lt;code&gt;sync&lt;&#x2F;code&gt; package. The code being reviewed can be found on Github &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;benthosdev&#x2F;benthos&#x2F;tree&#x2F;main&#x2F;internal&#x2F;checkpoint&quot;&gt;here&lt;&#x2F;a&gt;. I decided to take a closer look at the usefulness of &lt;code&gt;sync.Cond&lt;&#x2F;code&gt; in managing synchronization in concurrent programs. Before watching the video, I had never used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;sync#Cond&quot;&gt;sync.Cond&lt;&#x2F;a&gt; but had experience using the other types of the &lt;code&gt;sync&lt;&#x2F;code&gt; package. Intrigued, I started to research more about it and discovered how beneficial it is for preventing race conditions and deadlocks.&lt;&#x2F;p&gt;
&lt;p&gt;For Golang developers, the &lt;code&gt;sync&lt;&#x2F;code&gt; package is a popular and helpful tool for synchronizing concurrent operations. The &lt;code&gt;sync.Cond&lt;&#x2F;code&gt; type is specifically designed to help developers synchronize shared resources and is particularly useful when dealing with heavily loaded concurrent systems. It does so by allowing goroutines to pause their execution until a particular condition is met, enabling other goroutines to carry on uninterrupted until the condition is satisfied.&lt;&#x2F;p&gt;
&lt;p&gt;Using &lt;code&gt;sync.Cond&lt;&#x2F;code&gt; is surprisingly easy. It works in a very similar manner to &lt;code&gt;sync.Mutex&lt;&#x2F;code&gt; which is more common in Go projects. I think for me a particularly intriguing use case is concurrently accessing and appending items into a slice. For example, say you need a particular number of items from a slice to perform a particular operation or within a particular window say the last 5 seconds then &lt;code&gt;sync.Cond&lt;&#x2F;code&gt; is very useful for this kind of work.&lt;&#x2F;p&gt;
&lt;p&gt;Firstly to create a new &lt;code&gt;sync.Cond&lt;&#x2F;code&gt; we can use the &lt;code&gt;sync.NewCond&lt;&#x2F;code&gt; method which takes a parameter that implements the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;sync#Locker&quot;&gt;Locker&lt;&#x2F;a&gt; interface. If we are to use a type from the &lt;code&gt;sync&lt;&#x2F;code&gt; package to satisfy the &lt;code&gt;Locker&lt;&#x2F;code&gt; interface then we&#x27;ll either want to use &lt;code&gt;sync.Mutux&lt;&#x2F;code&gt; or &lt;code&gt;sync.RWMutux&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s now look at how we can implement this for our example.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; List stores T in a slice and provides a way to get the last n items from the list&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; List&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; any&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	items&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;T&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	cond&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;  *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Cond&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; NewList creates a new list of T&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; NewList&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; any&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;List&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	return&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;List&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		items&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		cond&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;  sync&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;NewCond&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Mutex&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this generic example, we create a &lt;code&gt;List&lt;&#x2F;code&gt; that can store any type.&lt;&#x2F;p&gt;
&lt;p&gt;Next, we need to look at how we can add new items to the list. When using &lt;code&gt;sync.Mutux&lt;&#x2F;code&gt; we need to use the underlying Locker that was passed into the &lt;code&gt;NewCond()&lt;&#x2F;code&gt; call.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Add adds a new item reading to the list&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;l &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;List&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;item&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; T&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;cond&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;L&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Lock&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;cond&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;L&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unlock&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;items&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; append&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;items&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; item&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;cond&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Broadcast&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A call to &lt;code&gt;Lock()&lt;&#x2F;code&gt; is called immediately when we call the &lt;code&gt;Add()&lt;&#x2F;code&gt; function followed by deferring a call to &lt;code&gt;Unlock()&lt;&#x2F;code&gt;. This is still essential as we want to modify the items slice safely. What is different in this &lt;code&gt;Add()&lt;&#x2F;code&gt; function is the call to &lt;code&gt;Broadcast()&lt;&#x2F;code&gt;. The call to broadcast signals all waiting goroutines that they can now wake.&lt;&#x2F;p&gt;
&lt;p&gt;Preventing race conditions and deadlocks is essential for developing reliable concurrent programs. A race condition occurs when two or more goroutines access and modify the same shared resource without proper synchronization, leading to unpredictable behaviour and data corruption. The way &lt;code&gt;sync.Cond&lt;&#x2F;code&gt; can broadcast to waiting for goroutines.&lt;&#x2F;p&gt;
&lt;p&gt;On the other hand, a deadlock happens when two or more goroutines wait for each other to release a resource, resulting in a situation where none of them can proceed. Both issues can cause programs to crash or behave incorrectly, making them difficult to debug and maintain. Therefore, preventing race conditions and deadlocks is critical for building robust concurrent programs.&lt;&#x2F;p&gt;
&lt;p&gt;We can look at how we might implement a &lt;code&gt;Get&lt;&#x2F;code&gt; call that can retrieve a given number of items from the list.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Get returns the last n items readings&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;l &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;List&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; Get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Context&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; count&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;T&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; error&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;cond&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;L&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Lock&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;cond&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;L&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Unlock&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; cancel&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	ctx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; cancel&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;WithCancel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; cancel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	go&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		&amp;lt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Done&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;cond&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Broadcast&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	for&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;items&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; count&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;		l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;cond&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Wait&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;		if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; ctx&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Err&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;			return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	temps&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; :=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;items&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;	l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;items&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; l&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;items&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;count&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;	return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; temps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nil&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The most interesting part of this function is the for loop. This loop blocks the function from going any further if there are not enough items in the list to satisfy the caller. If there are not enough items then the function is blocked by the call to &lt;code&gt;Wait()&lt;&#x2F;code&gt;. This call will block until another goroutine signals it by calling either &lt;code&gt;Signal()&lt;&#x2F;code&gt; or &lt;code&gt;Broadcast()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Some interesting additions in the &lt;code&gt;Get&lt;&#x2F;code&gt; code example above are the use of a cancellable context to provide cancellation and a way to prevent goroutines deadlocking.&lt;&#x2F;p&gt;
&lt;p&gt;In conclusion, the &lt;code&gt;sync.Cond&lt;&#x2F;code&gt; is an incredibly useful tool for synchronisation with shared resources in highly loaded concurrent systems.&lt;&#x2F;p&gt;
&lt;p&gt;A full working copy of the example above can be found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go.dev&#x2F;play&#x2F;p&#x2F;Ow6-tdO6yN8&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The never-ending cycle of home DIY projects</title>
          <pubDate>Wed, 29 Mar 2023 22:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/endless-diy-projects/</link>
          <guid>https://aran.dev/posts/endless-diy-projects/</guid>
          <description xml:base="https://aran.dev/posts/endless-diy-projects/">&lt;p&gt;There&#x27;s nothing quite like starting a new DIY project, but it&#x27;s not always as straightforward as you may think. There is always a constant worry that a new project may go wrong which could turn it into an even bigger project and uncover more underlying issues.&lt;&#x2F;p&gt;
&lt;p&gt;It all starts straightforwardly enough. You have an idea for a new project, you dive headfirst into it, full steam ahead. But as you get into the project, and you start uncovering issues and problems you did not anticipate. Maybe the previous property owner opted to take shortcuts during their DIY projects. Before you know it you have identified more work that was not originally planned. The original project you set out to complete has turned into a monster of endless home improvement projects.&lt;&#x2F;p&gt;
&lt;p&gt;To avoid falling into this vicious cycle, it&#x27;s important to approach any new project with a realistic approach and be prepared that it might not be as simple as you first thought. Don&#x27;t bite off more than you can chew, take the time to evaluate the project and plan appropriately.&lt;&#x2F;p&gt;
&lt;p&gt;One of the biggest mistakes DIYers make is underestimating the time required to complete a project. Don&#x27;t be fooled by the seemingly simple nature of a task - take the time to realistically estimate how long it might take to complete it and be prepared for the reality that it may take longer.&lt;&#x2F;p&gt;
&lt;p&gt;If a project begins to spiral out of control or you find it too daunting to handle on your own, don&#x27;t hesitate to ask for help. Whether it&#x27;s from a friend or a professional, getting an extra set of hands can make all the difference. It can save you time, money, and the headache of a never-ending cycle of home DIY projects.&lt;&#x2F;p&gt;
&lt;p&gt;While DIY projects can be exciting, they can also lead to a never-ending list of projects if not approached with care. To avoid getting caught in this cycle, take a realistic approach and plan carefully. Try to estimate the time as accurately as possible, and when needed, seek help.&lt;&#x2F;p&gt;
&lt;p&gt;With planning and perseverance, you can complete your DIY projects with confidence, hopefully avoiding the never-ending cycle of home improvement projects. Hopefully, with good planning, you avoid the never-ending cycle of home improvement projects.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Learning Rust</title>
          <pubDate>Sun, 19 Mar 2023 16:30:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/learning-rust/</link>
          <guid>https://aran.dev/posts/learning-rust/</guid>
          <description xml:base="https://aran.dev/posts/learning-rust/">&lt;p&gt;I previously wrote a blog post about &lt;a href=&quot;&#x2F;posts&#x2F;rust-programming-language&#x2F;&quot;&gt;rust&lt;&#x2F;a&gt; back in June 2022 and I&#x27;ve still not really managed to spend a lot of time learning it. Busy personal and work life makes it difficult sometimes to focus on new projects and hobbies. But after a recently set of power outages I figured I needed a new &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Dead_man%27s_switch&quot;&gt;dead man switch&lt;&#x2F;a&gt; type alert system to notify me when my personal home server hasn&#x27;t been in contact after a specificed amount of time. Whether I have lost power or internet it would be useful for me to be notified when that might happen. I figured I needed a project to help me focus my efforts.&lt;&#x2F;p&gt;
&lt;p&gt;The next thing I needed was something to follow. What better use of AI than asking for an course outline for learning Rust.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chatgpt-response&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#chatgpt-response&quot; aria-label=&quot;Anchor link for: chatgpt-response&quot;&gt;ChatGPT Response&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The response I got from ChatGPT is as follows:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-1-getting-started&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#step-1-getting-started&quot; aria-label=&quot;Anchor link for: step-1-getting-started&quot;&gt;Step 1: Getting Started&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Install Rust and set up your development environment&lt;&#x2F;li&gt;
&lt;li&gt;Learn the basics of the Rust programming language, including its syntax, data types, control flow, and functions&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;step-2-intermediate-concepts&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#step-2-intermediate-concepts&quot; aria-label=&quot;Anchor link for: step-2-intermediate-concepts&quot;&gt;Step 2: Intermediate Concepts&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Dive deeper into Rust&#x27;s ownership model, including borrowing and lifetimes&lt;&#x2F;li&gt;
&lt;li&gt;Learn about Rust&#x27;s built-in concurrency primitives, such as threads and channels&lt;&#x2F;li&gt;
&lt;li&gt;Explore Rust&#x27;s standard library and learn how to use crates from the Rust  community&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;step-3-advanced-topics&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#step-3-advanced-topics&quot; aria-label=&quot;Anchor link for: step-3-advanced-topics&quot;&gt;Step 3: Advanced Topics&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Learn about Rust macros and how to use them to write more concise and expressive code&lt;&#x2F;li&gt;
&lt;li&gt;Explore Rust&#x27;s unsafe features and learn how to use them safely and correctly&lt;&#x2F;li&gt;
&lt;li&gt;Learn about Rust&#x27;s advanced features, such as trait objects and higher kinded types&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;step-4-projects&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#step-4-projects&quot; aria-label=&quot;Anchor link for: step-4-projects&quot;&gt;Step 4: Projects&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Build small Rust projects to practice and reinforce your knowledge of the language&lt;&#x2F;li&gt;
&lt;li&gt;Join the Rust community and contribute to open source Rust projects
Build larger Rust projects and gain experience working with Rust&#x27;s advanced features and tools&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;step-5-continued-learning&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#step-5-continued-learning&quot; aria-label=&quot;Anchor link for: step-5-continued-learning&quot;&gt;Step 5: Continued Learning&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Read Rust documentation and books to stay up-to-date with new developments in the language&lt;&#x2F;li&gt;
&lt;li&gt;Attend Rust conferences and meetups to network with other Rust developers and learn from their experiences&lt;&#x2F;li&gt;
&lt;li&gt;Practice coding challenges and participate in coding competitions to improve your Rust skills.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;getting-started-with-the-course&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#getting-started-with-the-course&quot; aria-label=&quot;Anchor link for: getting-started-with-the-course&quot;&gt;Getting Started with the course&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Not a bad course it even included a statement at the end telling me to take regular breaks and to avoid burnout. Learning a new language will be a challenge but with persistence and practice it&#x27;ll hopefully not take me too long to learn.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Just write</title>
          <pubDate>Sun, 19 Mar 2023 16:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/just-write/</link>
          <guid>https://aran.dev/posts/just-write/</guid>
          <description xml:base="https://aran.dev/posts/just-write/">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;robert.bearblog.dev&#x2F;&quot;&gt;Robert Birming&lt;&#x2F;a&gt; recently wrote an article named &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;robert.bearblog.dev&#x2F;just-write&#x2F;&quot;&gt;Just Write&lt;&#x2F;a&gt; and it got me realising that to improve my own writing I need to &quot;Just Write&quot;. I need to not care about who I am writing for, what I am writing for and why I am writing. I shouldn&#x27;t care about my grammar, spelling and other mistakes that I make I think this is often a reason I use to not write and release blog posts. I get scared of what people might think or the mistakes I might write.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The Rust programming language</title>
          <pubDate>Sun, 26 Jun 2022 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/rust-programming-language/</link>
          <guid>https://aran.dev/posts/rust-programming-language/</guid>
          <description xml:base="https://aran.dev/posts/rust-programming-language/">&lt;p&gt;While working with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;go.dev&#x2F;&quot;&gt;Golang&lt;&#x2F;a&gt; often read about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; as an alternative.
Looking Rust is a low-level multi-paradigm, general-purpose statically-typed programming language designed for performance and safety.
Rust makes it possible to write software that is safe and performant.&lt;&#x2F;p&gt;
&lt;p&gt;One of Rust&#x27;s biggest differences compared to other languages comes from the way it deals with objects and memory.
Rust can check at compile-time whether the programmer has performed any incorrect operations on objects which could then to segmentation faults.
Ownership is a set of rules that governs how a Rust program manages memory.
If the programmer violates any rules, the program will not compile.
Ownership rules are checked at compile time resulting in no slow down while your program is running this helps make Rust one of if not the fastest programming language.
Along with other cool features and tools Rust has a lot of things that intrigue me and I wanted to try it out.&lt;&#x2F;p&gt;
&lt;p&gt;Next steps for learning Rust for me is to learn more about the the language features work and the Rust ecosystem and tools.
Couple of topics that I will need to do deep dives into are: Ownership, Cargo, Variables, Control Flows, Smart Pointers and plenty more.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Cleanup</title>
          <pubDate>Wed, 17 Nov 2021 20:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/cleanup/</link>
          <guid>https://aran.dev/posts/cleanup/</guid>
          <description xml:base="https://aran.dev/posts/cleanup/">&lt;p&gt;Often end up finding I start to run out of space on my filesystem after a few months of use so I needed to find a way to search for directories that I know I can safely delete.&lt;&#x2F;p&gt;
&lt;p&gt;For example for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Node.js&quot;&gt;Node.js&lt;&#x2F;a&gt; projects often end up with very large &lt;code&gt;node_modules&lt;&#x2F;code&gt; directory.&lt;&#x2F;p&gt;
&lt;p&gt;The following command will list all &lt;code&gt;node_modules&lt;&#x2F;code&gt; from the current directory and there size.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; List all directories named node_modules and their sizes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; find&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;node_modules&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; d&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;prune&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;print&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; xargs&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; du&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;chs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It always wroth running the command above first to confirm that all the directories it finds are correct. Once validated the directories are safe to delete the command can then be altered to delete these directories and all the files and directories under them.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Remove all node_modules folders (from current directory down)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; find&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; .&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;name&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;node_modules&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; d&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;prune&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;print&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;exec&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rm&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;rf&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Doing this can for &lt;code&gt;node_modules&lt;&#x2F;code&gt; and other similar folders like &lt;code&gt;vendor&lt;&#x2F;code&gt; for PHP projects can help save lots of space.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Dot dev</title>
          <pubDate>Sun, 24 Mar 2019 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/posts/dot-dev/</link>
          <guid>https://aran.dev/posts/dot-dev/</guid>
          <description xml:base="https://aran.dev/posts/dot-dev/">&lt;p&gt;Like many others, I recently rushed to get hold of one of the brand new &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;get.dev&#x2F;&quot;&gt;dot dev&lt;&#x2F;a&gt; domains waiting for them to become available via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;domains.google&quot;&gt;Google Domains&lt;&#x2F;a&gt;. I didn&#x27;t really have a plan for what I&#x27;d do with whatever domains I picked up but just knew I wanted &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aran.dev&quot;&gt;aran.dev&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aranw.dev&quot;&gt;aranw.dev&lt;&#x2F;a&gt;. I&#x27;ve been using aranw as my go-to username for a few years now along with some others but I wanted to be the first to get aran.dev and use that as my main domain. After a bit of drama with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;domains.google&quot;&gt;Google Domains&lt;&#x2F;a&gt; going down and also charging me twice, I ended up getting the domains I wanted. Great! Now, what do I do with them? I&#x27;ve tried a few times in the past to do a blog and always had issues, whether it&#x27;s related to tooling, hosting and&#x2F;or commitment to writing on it, I wanted to try again.&lt;&#x2F;p&gt;
&lt;p&gt;So here is my latest website, and blog. I&#x27;ve decided to use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aran.dev&quot;&gt;aran.dev&lt;&#x2F;a&gt; as the primary domain and other domains I own will ultimately get configured to redirect to this domain. For technology, I&#x27;ve gone super simple and decided to use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gohugo.io&quot;&gt;Hugo&lt;&#x2F;a&gt; to statically generate the content with hosting provided by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.netlify.com&#x2F;&quot;&gt;Netlify&lt;&#x2F;a&gt;. This has to be probably the simplest solution I&#x27;ve done and the easiest. At the moment I&#x27;ve made this blog closed source but will ultimately make it open source on my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;&quot;&gt;GitHub&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>About</title>
          <pubDate>Sat, 23 Mar 2019 17:38:20 +0000</pubDate>
          <author>Unknown</author>
          <link>https://aran.dev/about/</link>
          <guid>https://aran.dev/about/</guid>
          <description xml:base="https://aran.dev/about/">&lt;p&gt;Hey, I&#x27;m Aran. I&#x27;m a software engineer based in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;goo.gl&#x2F;maps&#x2F;H3BiEBdV1KKwNbKN9&quot;&gt;Huddersfield, UK&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I mostly work in Go, building backend services, data pipelines, and event-driven systems. I&#x27;ve spent a lot of time with Google Cloud (Cloud Run, BigQuery, Pub&#x2F;Sub, Cloud Functions), PostgreSQL, and Kafka. Most of my work involves helping teams ship reliable systems that handle real traffic, whether that&#x27;s greenfield builds or sorting out existing architectures that have grown complicated.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve worked across a few different industries, which has taught me that the interesting problems are usually more about how teams work together and how data flows through systems than about picking the right framework.&lt;&#x2F;p&gt;
&lt;p&gt;Greenfield projects are where I&#x27;m happiest. Starting from scratch with event-driven designs, getting the data model right, and building something that can grow without falling over — that&#x27;s the work I find most satisfying. I&#x27;ve written about some of this on the &lt;a href=&quot;&#x2F;posts&quot;&gt;blog&lt;&#x2F;a&gt;, covering everything from Go patterns to PostgreSQL partitioning to real-time data processing.&lt;&#x2F;p&gt;
&lt;p&gt;I also take on freelance work from time to time — if you&#x27;ve got a project that sounds interesting, I&#x27;m happy to chat. Have a look at my &lt;a href=&quot;&#x2F;services&quot;&gt;services&lt;&#x2F;a&gt; page, or just &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cal.com&#x2F;aranw&#x2F;15min?duration=15&quot;&gt;book a call&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Outside of work, I make pizza (proper dough, proper oven), cycle when the weather cooperates, and do an unreasonable amount of DIY on a late Victorian house that always needs something.&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
