<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Blog about web development on WebDevStation</title>
    <link>https://webdevstation.com/</link>
    <description>Follow my journey through this new blog.</description>
    <generator>Hugo</generator>
    <language>en</language>
    <managingEditor>Alex</managingEditor>
    <webMaster>Alex</webMaster>
    <copyright>© 2026 WebDevStation</copyright>
    <lastBuildDate>Tue, 01 Sep 2026 09:20:00 +0200</lastBuildDate>
    <atom:link href="https://webdevstation.com/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How to Test Go Code That Calls an LLM</title>
      <link>https://webdevstation.com/posts/testing-go-code-that-calls-an-llm/</link>
      <pubDate>Tue, 01 Sep 2026 09:20:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/testing-go-code-that-calls-an-llm/</guid>
      <description>Non-determinism does not have to mean untestable. Wrapping the model behind an interface, faking it in unit tests, golden files for prompt assembly, and the small…</description>
      <content:encoded><![CDATA[<p>The first question everyone asks about a feature backed by a language model is &ldquo;how do you even test that?&rdquo; — usually with a shrug, as though non-determinism were a get-out-of-jail card for the whole test suite. It is not. Almost all of the code you write around a model is perfectly deterministic, and the small part that is not can be pinned down with a different kind of test. Here is how I split it.</p>
<h2 id="three-things-tested-three-ways">Three Things, Tested Three Ways</h2>
<p>The confusion comes from treating &ldquo;the LLM feature&rdquo; as one indivisible thing. It is three:</p>
<table>
	<thead>
			<tr>
					<th>What</th>
					<th>Deterministic?</th>
					<th>How to test it</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Prompt assembly, parsing, tool handlers, the loop</td>
					<td>Yes, completely</td>
					<td>Ordinary unit tests</td>
			</tr>
			<tr>
					<td>Wiring to the real API — auth, streaming, errors</td>
					<td>Yes enough</td>
					<td>A few integration tests, run on demand</td>
			</tr>
			<tr>
					<td>Output quality — is the answer any good?</td>
					<td>No</td>
					<td>Evals, scored not asserted</td>
			</tr>
	</tbody>
</table>
<p>The first row is 90% of your code and needs no model at all. Get that boundary right and the rest is manageable.</p>
<h2 id="put-an-interface-in-front-of-the-model">Put an Interface in Front of the Model</h2>
<p>Do not scatter <code>client.Messages.New</code> through your handlers. Define the narrowest interface your code actually needs and depend on that:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Completer turns a prompt into text. It is deliberately narrow — narrower</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// than the SDK — so tests can implement it in five lines.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Completer</span> <span style="color:#66d9ef">interface</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Complete</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">req</span> <span style="color:#a6e22e">Request</span>) (<span style="color:#a6e22e">Response</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Request</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">System</span>   <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Messages</span> []<span style="color:#a6e22e">Message</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Tools</span>    []<span style="color:#a6e22e">Tool</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Response</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Text</span>       <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ToolCalls</span>  []<span style="color:#a6e22e">ToolCall</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">StopReason</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Usage</span>      <span style="color:#a6e22e">Usage</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This is just the dependency-inversion habit that makes any external service testable, and it earns its keep faster here than almost anywhere else. Two things fall out of it:</p>
<p><strong>Your business logic never imports the SDK.</strong> It imports your <code>Request</code> and <code>Response</code> types. When the SDK&rsquo;s union types change shape, one adapter file changes.</p>
<p><strong>The fake is trivial.</strong> No HTTP, no fixtures, no mocking library:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">fakeCompleter</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">responses</span> []<span style="color:#a6e22e">Response</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">err</span>       <span style="color:#66d9ef">error</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">calls</span>     []<span style="color:#a6e22e">Request</span> <span style="color:#75715e">// recorded for assertions</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">f</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">fakeCompleter</span>) <span style="color:#a6e22e">Complete</span>(<span style="color:#a6e22e">_</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">req</span> <span style="color:#a6e22e">Request</span>) (<span style="color:#a6e22e">Response</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">f</span>.<span style="color:#a6e22e">calls</span> = append(<span style="color:#a6e22e">f</span>.<span style="color:#a6e22e">calls</span>, <span style="color:#a6e22e">req</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">f</span>.<span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">Response</span>{}, <span style="color:#a6e22e">f</span>.<span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> len(<span style="color:#a6e22e">f</span>.<span style="color:#a6e22e">responses</span>) <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">Response</span>{}, <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;fakeCompleter: no responses left&#34;</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">resp</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">f</span>.<span style="color:#a6e22e">responses</span>[<span style="color:#ae81ff">0</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">f</span>.<span style="color:#a6e22e">responses</span> = <span style="color:#a6e22e">f</span>.<span style="color:#a6e22e">responses</span>[<span style="color:#ae81ff">1</span>:]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">resp</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Returning a queue rather than a single value is what lets you test multi-turn loops: the first call returns a tool request, the second returns the final answer.</p>
<h2 id="test-the-loop-not-the-model">Test the Loop, Not the Model</h2>
<p>An agent loop has plenty of logic worth testing, none of which needs a model. Does it stop when it should? Does it return a result for every tool call? Does it hand a tool failure back rather than aborting?</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestLoopReturnsResultForFailedTool</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fake</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">fakeCompleter</span>{<span style="color:#a6e22e">responses</span>: []<span style="color:#a6e22e">Response</span>{
</span></span><span style="display:flex;"><span>        {<span style="color:#a6e22e">StopReason</span>: <span style="color:#e6db74">&#34;tool_use&#34;</span>, <span style="color:#a6e22e">ToolCalls</span>: []<span style="color:#a6e22e">ToolCall</span>{
</span></span><span style="display:flex;"><span>            {<span style="color:#a6e22e">ID</span>: <span style="color:#e6db74">&#34;t1&#34;</span>, <span style="color:#a6e22e">Name</span>: <span style="color:#e6db74">&#34;lookup&#34;</span>, <span style="color:#a6e22e">Input</span>: []byte(<span style="color:#e6db74">`{&#34;id&#34;:&#34;missing&#34;}`</span>)},
</span></span><span style="display:flex;"><span>        }},
</span></span><span style="display:flex;"><span>        {<span style="color:#a6e22e">StopReason</span>: <span style="color:#e6db74">&#34;end_turn&#34;</span>, <span style="color:#a6e22e">Text</span>: <span style="color:#e6db74">&#34;I couldn&#39;t find that record.&#34;</span>},
</span></span><span style="display:flex;"><span>    }}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">agent</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">fake</span>, <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#a6e22e">ToolFunc</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;lookup&#34;</span>: <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, []<span style="color:#66d9ef">byte</span>) (<span style="color:#66d9ef">string</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;not found&#34;</span>)
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">got</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">agent</span>.<span style="color:#a6e22e">Run</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#e6db74">&#34;look up record missing&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Fatalf</span>(<span style="color:#e6db74">&#34;Run() error = %v, want nil&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">got</span> <span style="color:#f92672">!=</span> <span style="color:#e6db74">&#34;I couldn&#39;t find that record.&#34;</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;got %q&#34;</span>, <span style="color:#a6e22e">got</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// The failure must reach the model as a tool result, not abort the loop.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">second</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fake</span>.<span style="color:#a6e22e">calls</span>[<span style="color:#ae81ff">1</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">result</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">findToolResult</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">second</span>, <span style="color:#e6db74">&#34;t1&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">IsError</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#e6db74">&#34;tool failure was not marked as an error result&#34;</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>That test catches a real bug — a loop that drops the failed call instead of reporting it — and runs in microseconds with no API key.</p>
<p>The same approach covers the rest of the loop&rsquo;s contract — <a href="/posts/tool-use-in-go-agent-loop/">the invariants from the agent-loop article</a>: that it stops at <code>MaxIterations</code>, that every <code>tool_use</code> block gets exactly one result, that results come back in one message. Table-driven tests fit this beautifully, since each case is just a different queue of canned responses.</p>
<h2 id="golden-files-for-prompt-assembly">Golden Files for Prompt Assembly</h2>
<p>Prompt building is string manipulation, and it drifts. Someone adds a field, reorders a section, changes a heading — and unlike code, a prompt regression produces no compile error and no failing assertion, just slightly worse output that nobody attributes to the change.</p>
<p>Golden files make the diff visible in review:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">update</span> = <span style="color:#a6e22e">flag</span>.<span style="color:#a6e22e">Bool</span>(<span style="color:#e6db74">&#34;update&#34;</span>, <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">&#34;update golden files&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestBuildSystemPrompt</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">tests</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">name</span>   <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">cfg</span>    <span style="color:#a6e22e">Config</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">golden</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    }{
</span></span><span style="display:flex;"><span>        {<span style="color:#e6db74">&#34;default&#34;</span>, <span style="color:#a6e22e">Config</span>{}, <span style="color:#e6db74">&#34;system_default.txt&#34;</span>},
</span></span><span style="display:flex;"><span>        {<span style="color:#e6db74">&#34;with_tools&#34;</span>, <span style="color:#a6e22e">Config</span>{<span style="color:#a6e22e">Tools</span>: <span style="color:#a6e22e">allTools</span>}, <span style="color:#e6db74">&#34;system_with_tools.txt&#34;</span>},
</span></span><span style="display:flex;"><span>        {<span style="color:#e6db74">&#34;terse_mode&#34;</span>, <span style="color:#a6e22e">Config</span>{<span style="color:#a6e22e">Terse</span>: <span style="color:#66d9ef">true</span>}, <span style="color:#e6db74">&#34;system_terse.txt&#34;</span>},
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">tt</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">tests</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Run</span>(<span style="color:#a6e22e">tt</span>.<span style="color:#a6e22e">name</span>, <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">got</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">BuildSystemPrompt</span>(<span style="color:#a6e22e">tt</span>.<span style="color:#a6e22e">cfg</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">path</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">filepath</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#e6db74">&#34;testdata&#34;</span>, <span style="color:#a6e22e">tt</span>.<span style="color:#a6e22e">golden</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">update</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">WriteFile</span>(<span style="color:#a6e22e">path</span>, []byte(<span style="color:#a6e22e">got</span>), <span style="color:#ae81ff">0</span><span style="color:#a6e22e">o644</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">want</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">ReadFile</span>(<span style="color:#a6e22e">path</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">diff</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">cmp</span>.<span style="color:#a6e22e">Diff</span>(string(<span style="color:#a6e22e">want</span>), <span style="color:#a6e22e">got</span>); <span style="color:#a6e22e">diff</span> <span style="color:#f92672">!=</span> <span style="color:#e6db74">&#34;&#34;</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;prompt changed (-want +got):\n%s&#34;</span>, <span style="color:#a6e22e">diff</span>)
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        })
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Run <code>go test ./... -update</code> to accept a change deliberately. The point is not that the golden file is correct — it is that changing it requires saying so out loud, in a diff a reviewer can read.</p>
<p>This also catches the caching bugs from the <a href="/posts/prompt-caching-llm-cost/">prompt caching article</a> before they cost you anything. A golden test on the system prompt fails the moment somebody interpolates <code>time.Now()</code> into it, because the output differs on every run.</p>
<p>Which suggests a second, blunter test worth having:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestPromptAssemblyIsDeterministic</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Go randomises map iteration order, so a prompt built by ranging over a</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// map differs run to run — and silently never caches.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">first</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">BuildSystemPrompt</span>(<span style="color:#a6e22e">cfg</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">20</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">got</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">BuildSystemPrompt</span>(<span style="color:#a6e22e">cfg</span>); <span style="color:#a6e22e">got</span> <span style="color:#f92672">!=</span> <span style="color:#a6e22e">first</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Fatalf</span>(<span style="color:#e6db74">&#34;prompt is not deterministic on run %d&#34;</span>, <span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Twenty iterations is enough to make a randomised map order fail essentially every time.</p>
<h2 id="integration-tests-behind-a-build-tag">Integration Tests, Behind a Build Tag</h2>
<p>You do need a handful of tests that touch the real API — enough to catch an SDK upgrade that changed a union type, a model id that no longer resolves, or streaming that broke. But they cost money and need a key, so they must not run on every <code>go test ./...</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">//go:build integration</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">llm_test</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestStreamingReturnsCompleteMessage</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Getenv</span>(<span style="color:#e6db74">&#34;ANTHROPIC_API_KEY&#34;</span>) <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;&#34;</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Skip</span>(<span style="color:#e6db74">&#34;no API key; skipping integration test&#34;</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ... real call, assert on shape rather than content</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>go test ./...                    <span style="color:#75715e"># fast, free, no key</span>
</span></span><span style="display:flex;"><span>go test -tags<span style="color:#f92672">=</span>integration ./...  <span style="color:#75715e"># the real thing, on demand</span>
</span></span></code></pre></div><p>Assert on <strong>shape</strong>, never on wording. <code>resp.Text</code> being non-empty, <code>StopReason</code> being <code>end_turn</code>, <code>Usage.OutputTokens</code> being greater than zero, a streamed message accumulating to the same content as a non-streamed one. Those hold across model versions. &ldquo;The answer contains the word Paris&rdquo; does not, and a flaky test that fails once a month teaches your team to ignore failures.</p>
<p>There is a middle option that gets you a long way for free: point the SDK at a local <code>httptest.Server</code> via the <code>ANTHROPIC_BASE_URL</code> environment variable and serve canned JSON. That exercises the real SDK — its parsing, its retry behaviour, its streaming decoder — without a key or a bill. It is the closest analogue to what <a href="/posts/how-to-test-database-interactions-go/">go-sqlmock</a> does for the database layer: a real driver, a fake server.</p>
<p>It is also the only sane way to test the paths you cannot easily provoke on purpose:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Rate limiting: does the caller back off, or hammer?</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">mux</span>.<span style="color:#a6e22e">HandleFunc</span>(<span style="color:#e6db74">&#34;/v1/messages&#34;</span>, <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;Retry-After&#34;</span>, <span style="color:#e6db74">&#34;1&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusTooManyRequests</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Fprint</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">`{&#34;type&#34;:&#34;error&#34;,&#34;error&#34;:{&#34;type&#34;:&#34;rate_limit_error&#34;,&#34;message&#34;:&#34;...&#34;}}`</span>)
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>Do the same for a 500, a connection dropped mid-stream, and a malformed body. Those are the failures that actually page you, and they are trivial to simulate and nearly impossible to trigger on demand against the real API.</p>
<h2 id="the-refusal-case-nobody-tests">The Refusal Case Nobody Tests</h2>
<p>Worth its own paragraph because it is so easy to miss: a safety refusal comes back as <strong>HTTP 200</strong> with <code>StopReason</code> set to <code>refusal</code>. Your error handling never fires. Code that goes from <code>if err != nil</code> straight to reading the first content block treats it as an empty answer.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestRefusalIsNotTreatedAsEmptyAnswer</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fake</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">fakeCompleter</span>{<span style="color:#a6e22e">responses</span>: []<span style="color:#a6e22e">Response</span>{
</span></span><span style="display:flex;"><span>        {<span style="color:#a6e22e">StopReason</span>: <span style="color:#e6db74">&#34;refusal&#34;</span>, <span style="color:#a6e22e">Text</span>: <span style="color:#e6db74">&#34;&#34;</span>},
</span></span><span style="display:flex;"><span>    }}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">fake</span>, <span style="color:#66d9ef">nil</span>).<span style="color:#a6e22e">Run</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#e6db74">&#34;...&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">ErrDeclined</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;got %v, want ErrDeclined&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>One line in the fake, and you have covered a branch most production code does not have at all. The sentinel-error pattern behind <code>ErrDeclined</code> is the one from <a href="/posts/error-handling-in-go/">error handling in Go</a>.</p>
<h2 id="evals-for-quality-not-correctness">Evals: For Quality, Not Correctness</h2>
<p>Everything above tests whether your <em>code</em> is right. None of it tells you whether the answers are any good. That needs a different instrument, and the mistake is trying to force it into <code>go test</code>.</p>
<p>An eval is a fixed set of inputs, run against the real model, scored rather than asserted:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">//go:build eval</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestClassificationAccuracy</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">cases</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">loadEvalCases</span>(<span style="color:#a6e22e">t</span>, <span style="color:#e6db74">&#34;testdata/eval/classification.jsonl&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">correct</span> <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">c</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">cases</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">got</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">classify</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">realClient</span>, <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Input</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">got</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Want</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">correct</span><span style="color:#f92672">++</span>
</span></span><span style="display:flex;"><span>        } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Logf</span>(<span style="color:#e6db74">&#34;MISS: input=%q got=%q want=%q&#34;</span>, <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Input</span>, <span style="color:#a6e22e">got</span>, <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Want</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">accuracy</span> <span style="color:#f92672">:=</span> float64(<span style="color:#a6e22e">correct</span>) <span style="color:#f92672">/</span> float64(len(<span style="color:#a6e22e">cases</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Logf</span>(<span style="color:#e6db74">&#34;accuracy: %.1f%% (%d/%d)&#34;</span>, <span style="color:#a6e22e">accuracy</span><span style="color:#f92672">*</span><span style="color:#ae81ff">100</span>, <span style="color:#a6e22e">correct</span>, len(<span style="color:#a6e22e">cases</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// A floor, not an equality check. Below this, something regressed.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">accuracy</span> &lt; <span style="color:#ae81ff">0.90</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;accuracy %.1f%% below the 90%% floor&#34;</span>, <span style="color:#a6e22e">accuracy</span><span style="color:#f92672">*</span><span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Two things make this useful rather than annoying. <strong>The threshold is a floor</strong>, not a target — you are detecting regression, not demanding perfection. And <strong>the misses get logged</strong>, because the list of what it got wrong is the actual output; the pass/fail is almost incidental.</p>
<p>Run evals when you change a prompt, a model, or an effort setting — not on every commit. They cost money and take minutes.</p>
<h2 id="what-not-to-do">What Not to Do</h2>
<p><strong>Do not assert on model wording.</strong> <code>strings.Contains(resp, &quot;Paris&quot;)</code> passes today and fails after a model update that phrases it differently. It is not testing your code.</p>
<p><strong>Do not set temperature to zero and call it deterministic.</strong> Sampling parameters are not even accepted on current models, and identical output was never guaranteed regardless.</p>
<p><strong>Do not mock the SDK&rsquo;s types.</strong> Mocking <code>anthropic.Message</code> and its union blocks is a lot of work to test the adapter you wrote to avoid exactly that. Fake your own interface instead.</p>
<p><strong>Do not let integration tests run by default.</strong> A test suite that needs an API key is a test suite that new contributors cannot run, and CI cost that grows with every push.</p>
<p><strong>Do not skip testing the error paths</strong> because they are &ldquo;just the SDK&rsquo;s job&rdquo;. Rate limits, refusals and mid-stream disconnects are the failures you will actually see in production, and they are the cheapest things in this article to cover.</p>
<h2 id="checklist">Checklist</h2>
<ul>
<li>A narrow interface between your logic and the SDK; business code never imports the SDK.</li>
<li>A fake with a queue of canned responses for multi-turn loops.</li>
<li>Unit tests for the loop&rsquo;s contract: one result per tool call, errors handed back, iteration cap honoured.</li>
<li>Golden files for prompt assembly, plus a determinism test.</li>
<li>Integration tests behind <code>//go:build integration</code>, asserting on shape not wording.</li>
<li>An <code>httptest.Server</code> for 429s, 500s and truncated streams.</li>
<li>A test for the refusal path.</li>
<li>Evals behind their own tag, scored against a floor, misses logged.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>&ldquo;You cannot test LLM code&rdquo; conflates the model with the code around it. The code around it — the prompt builder, the parser, the loop, the tool handlers, the error branches — is ordinary Go, and it becomes easy to test the moment there is an interface between it and the SDK. Push non-determinism out to the edges, cover the edges with evals scored against a floor, and the rest of your suite stays fast, free and green.</p>]]></content:encoded>
      <category>AI Engineering</category>
      <category>Go Programming</category>
      <category>Testing</category>
    </item>
    <item>
      <title>Prompt Caching: The Cheapest Win in Your LLM Bill</title>
      <link>https://webdevstation.com/posts/prompt-caching-llm-cost/</link>
      <pubDate>Mon, 31 Aug 2026 11:15:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/prompt-caching-llm-cost/</guid>
      <description>Prompt caching is a prefix match, and one stray timestamp can silently disable it. How cache breakpoints, TTLs and the usage fields actually work — and how to build…</description>
      <content:encoded><![CDATA[<p>I have written on this blog about caching database reads with <a href="/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/">Ristretto</a> and about teaching <a href="/posts/how-to-make-nginx-cookie-aware/">Nginx to cache by cookie</a>. Prompt caching belongs in the same family, with one difference that makes it far more interesting: the thing you are caching costs real money per byte, and when the cache stops working, nothing breaks. No error, no alert, no failed request. Just a bigger invoice next month.</p>
<h2 id="one-invariant-everything-follows-from-it">One Invariant, Everything Follows From It</h2>
<p><strong>Prompt caching is a prefix match. Any change anywhere in the prefix invalidates everything after it.</strong></p>
<p>That is the whole model. The cache key is derived from the exact bytes of your rendered prompt up to each breakpoint. One byte different at position N — a timestamp, a reordered JSON key, an extra tool in the list — and every cached position at or after N is gone.</p>
<p>The render order matters and is fixed:</p>
<pre tabindex="0"><code>tools  →  system  →  messages
</code></pre><p>Tools render first, at position zero. That has a consequence people discover the expensive way: <strong>change the tool list and you have invalidated everything</strong>, system prompt and entire conversation included. More on that below.</p>
<h2 id="what-it-costs">What It Costs</h2>
<p>Two numbers govern whether caching pays:</p>
<ul>
<li>A cache <strong>read</strong> costs about <strong>0.1×</strong> the base input price.</li>
<li>A cache <strong>write</strong> costs <strong>1.25×</strong> for the 5-minute TTL, <strong>2×</strong> for the 1-hour TTL.</li>
</ul>
<p>So with the default 5-minute TTL, two requests already break even: <code>1.25 + 0.1 = 1.35</code> against <code>2.0</code> uncached. By the third request you are well ahead. With the 1-hour TTL you need three requests to break even, because the write costs double.</p>
<p>Which is why the TTL question is <em>not</em> &ldquo;how long do I want this cached&rdquo; but &ldquo;how far apart do requests sharing this prefix start&rdquo;:</p>
<table>
	<thead>
			<tr>
					<th>Start-to-start gap</th>
					<th>Use</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Under 5 minutes</td>
					<td>5-minute TTL. Every read refreshes the timer, so continuous traffic keeps it warm indefinitely and it is strictly cheaper.</td>
			</tr>
			<tr>
					<td>5–60 minutes</td>
					<td>1-hour TTL. This is the only window where the doubled write price earns its keep.</td>
			</tr>
			<tr>
					<td>Over an hour</td>
					<td>Neither, directly. Re-warm on a schedule or accept the cold miss.</td>
			</tr>
	</tbody>
</table>
<p>The subtlety in row one: a read refreshes the entry at no extra cost, and the lifetime is measured from the <em>start</em> of the request. A four-minute generation leaves about one minute for the next request to begin before a five-minute entry expires. For a chat endpoint under steady load, the 5-minute TTL is the right answer and the 1-hour TTL just doubles your write bill.</p>
<h2 id="making-it-work-in-go">Making It Work in Go</h2>
<p>The syntax is a <code>CacheControl</code> on the last block of whatever you want cached. Because tools render before system, a marker on the final system block caches <strong>both</strong>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">params</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageNewParams</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Model</span>:     <span style="color:#e6db74">&#34;claude-opus-5&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">MaxTokens</span>: <span style="color:#ae81ff">16000</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Tools</span>:     <span style="color:#a6e22e">tools</span>, <span style="color:#75715e">// deterministic order — see below</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">System</span>: []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">TextBlockParam</span>{{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Text</span>:         <span style="color:#a6e22e">systemPrompt</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">CacheControl</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewCacheControlEphemeralParam</span>(), <span style="color:#75715e">// 5-minute default</span>
</span></span><span style="display:flex;"><span>    }},
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Messages</span>: <span style="color:#a6e22e">messages</span>,
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>For the 1-hour TTL:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">CacheControl</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">CacheControlEphemeralParam</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">TTL</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">CacheControlEphemeralTTLTTL1h</span>,
</span></span><span style="display:flex;"><span>},
</span></span></code></pre></div><p>There is also a top-level <code>CacheControl</code> on <code>MessageNewParams</code> that automatically places a breakpoint on the last cacheable block and moves it forward as the conversation grows. For multi-turn chat that is the right default — no marker bookkeeping, and the growing history caches incrementally.</p>
<p><strong>The robust combination for anything agentic:</strong> one explicit breakpoint at the end of the static system prefix, so the expensive shared part has a guaranteed read point no matter what happens later in <code>messages</code>, plus top-level automatic caching for the growing tail.</p>
<p>You get four breakpoints per request, so there is no need to be frugal — place them at genuine stability boundaries.</p>
<h2 id="where-automatic-caching-is-the-wrong-tool">Where Automatic Caching Is the Wrong Tool</h2>
<p>Automatic placement puts the breakpoint at the very end of your prompt. When the prompt <em>ends</em> with something unique per request — a retrieved document, the user&rsquo;s actual question — that is a pure surcharge: every request writes a new cache entry that nothing will ever read.</p>
<p>The signature is unmistakable once you know it: <code>cache_creation_input_tokens</code> is non-zero on every single request, while <code>cache_read_input_tokens</code> never covers the shared prefix.</p>
<p>The fix is an explicit marker at the end of the <strong>shared</strong> portion:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">Messages</span>: []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageParam</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewUserMessage</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">TextBlockParam</span>{
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Text</span>:         <span style="color:#a6e22e">sharedContext</span>, <span style="color:#75715e">// few-shot examples, retrieved docs</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">CacheControl</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewCacheControlEphemeralParam</span>(),
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewTextBlock</span>(<span style="color:#a6e22e">userQuestion</span>), <span style="color:#75715e">// no marker — differs every time</span>
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>},
</span></span></code></pre></div><p>Same rule, restated: put the breakpoint where the prompt <em>stops</em> being shared, not where the prompt ends.</p>
<h2 id="the-minimum-prefix-which-is-not-monotonic">The Minimum Prefix, Which Is Not Monotonic</h2>
<p>A prompt shorter than the model&rsquo;s minimum will not cache — no error, no warning, <code>cache_creation_input_tokens</code> simply comes back zero. And the minimum does not move in the direction you would guess as models get newer:</p>
<table>
	<thead>
			<tr>
					<th>Model</th>
					<th style="text-align: right">Minimum</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Claude Opus 5, Fable 5</td>
					<td style="text-align: right">512 tokens</td>
			</tr>
			<tr>
					<td>Opus 4.8, Sonnet 5, Sonnet 4.6</td>
					<td style="text-align: right">1024 tokens</td>
			</tr>
			<tr>
					<td>Opus 4.7</td>
					<td style="text-align: right">2048 tokens</td>
			</tr>
			<tr>
					<td>Opus 4.6, Haiku 4.5</td>
					<td style="text-align: right">4096 tokens</td>
			</tr>
	</tbody>
</table>
<p>A 3,000-token system prompt caches on Opus 5 and Opus 4.8, and silently does not on Opus 4.6 or Haiku 4.5. If you switched models and your cache hit rate fell off a cliff, this is the first thing to check — and it cuts the other way too: moving to Opus 5 halves the Opus 4.8 minimum, so prompts that were previously too short start caching with no code change at all.</p>
<h2 id="silent-invalidators">Silent Invalidators</h2>
<p>This is the part worth committing to memory, because every one of these is code that looks perfectly reasonable in review.</p>
<table>
	<thead>
			<tr>
					<th>Pattern</th>
					<th>Why it kills the cache</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>time.Now()</code> in the system prompt</td>
					<td>The prefix differs on every single request</td>
			</tr>
			<tr>
					<td>A request ID or UUID early in the content</td>
					<td>Same — every request is unique</td>
			</tr>
			<tr>
					<td><code>json.Marshal</code> of a <code>map</code> in the prompt</td>
					<td>Go randomises map iteration order; the bytes differ run to run</td>
			</tr>
			<tr>
					<td>Ranging over a map to build tool definitions</td>
					<td>Same problem, at position zero, which is the worst place for it</td>
			</tr>
			<tr>
					<td>User or session ID interpolated into the system prompt</td>
					<td>A per-user prefix; nothing shares anything</td>
			</tr>
			<tr>
					<td><code>if flag { system += ... }</code></td>
					<td>Every flag combination is a distinct prefix</td>
			</tr>
			<tr>
					<td>A tool set that varies per user or per mode</td>
					<td>Tools render first — nothing caches across users</td>
			</tr>
	</tbody>
</table>
<p>The Go-specific ones deserve emphasis. Map iteration order in Go is deliberately randomised, so this is not a cache bug that appears under load — it appears on <strong>every request</strong>, and it is invisible because the rendered prompt is semantically identical each time:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Bad: iteration order is randomised, so the bytes differ every run.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">name</span>, <span style="color:#a6e22e">def</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">tools</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">tools</span> = append(<span style="color:#a6e22e">tools</span>, <span style="color:#a6e22e">def</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Good: deterministic.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">names</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">slices</span>.<span style="color:#a6e22e">Sorted</span>(<span style="color:#a6e22e">maps</span>.<span style="color:#a6e22e">Keys</span>(<span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">tools</span>))
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">name</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">names</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">tools</span> = append(<span style="color:#a6e22e">tools</span>, <span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">tools</span>[<span style="color:#a6e22e">name</span>])
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Sorting keys is a one-line fix that most Go LLM code needs and almost none has.</p>
<h2 id="injecting-dynamic-context-without-breaking-everything">Injecting Dynamic Context Without Breaking Everything</h2>
<p>The usual reason a system prompt has a timestamp in it is that the model genuinely needs to know the date, or the user&rsquo;s plan tier, or the current mode. The instinct is to template it into the system prompt. Don&rsquo;t — that is the front of the prefix, and it invalidates everything behind it.</p>
<p>Put dynamic context <strong>after</strong> the cached history instead. On the newest models there is a first-class channel for this: a <code>system</code>-role message appended to <code>messages</code>, rather than an edit to the top-level <code>system</code> field.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// The top-level system prompt stays byte-identical and stays cached.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// The operator instruction goes after the history, invalidating nothing before it.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">messages</span> = append(<span style="color:#a6e22e">messages</span>, <span style="color:#a6e22e">userTurn</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">messages</span> = append(<span style="color:#a6e22e">messages</span>, <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageParam</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Role</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageParamRoleSystem</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Content</span>: []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ContentBlockParamUnion</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewTextBlock</span>(<span style="color:#e6db74">&#34;Terse mode enabled — keep responses under 40 words.&#34;</span>),
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>A message at turn five invalidates nothing before turn five. That is the whole trick.</p>
<p>Two constraints: it must follow a user message and be either the last entry or followed by an assistant turn — it cannot be <code>messages[0]</code>, so use the top-level <code>system</code> for the initial prompt. And support is model-dependent; unsupported models return a 400 saying the <code>system</code> role is not supported, so catch that and fall back to putting the instruction in a user turn.</p>
<h2 id="three-rules-that-beat-marker-placement">Three Rules That Beat Marker Placement</h2>
<p>Fix these before you fiddle with breakpoints.</p>
<p><strong>Freeze the system prompt.</strong> No dates, no user names, no modes. It is the front of the prefix and everything downstream depends on it not moving.</p>
<p><strong>Never change tools or model mid-conversation.</strong> Tools render at position zero, so adding, removing or reordering one invalidates the entire cache. Caches are also model-scoped, so switching models mid-conversation starts from cold. If you need &ldquo;modes&rdquo;, do not swap the tool set — pass the mode as message content.</p>
<p><strong>Forked calls must reuse the parent&rsquo;s exact prefix.</strong> Summarisation passes, sub-agents and side computations usually build their own request. If that fork rebuilds <code>system</code>, <code>tools</code> or <code>model</code> with any difference at all, it misses the parent&rsquo;s cache completely. Copy them verbatim and append the fork-specific content at the end.</p>
<p>That last one is also the argument against a &ldquo;cheap model for the easy stuff&rdquo; cascade, at least as a first move. Caches are per model, so routing between two models forfeits cache reuse across them. Measure the capable model at lower effort before you build the cascade — it is often cheaper <em>and</em> simpler, and it keeps one cache namespace.</p>
<h2 id="verifying-it-forever">Verifying It, Forever</h2>
<p>Every response carries the accounting (the same <code>Usage</code> struct I said to log from day one in <a href="/posts/calling-claude-from-go/">calling an LLM from Go</a>):</p>
<table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Meaning</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>CacheCreationInputTokens</code></td>
					<td>Written to cache this request (you paid ~1.25×)</td>
			</tr>
			<tr>
					<td><code>CacheReadInputTokens</code></td>
					<td>Served from cache (you paid ~0.1×)</td>
			</tr>
			<tr>
					<td><code>InputTokens</code></td>
					<td>Full price, uncached</td>
			</tr>
	</tbody>
</table>
<p><code>InputTokens</code> is the <strong>uncached remainder only</strong> — not the prompt size. Total prompt = all three added together. If an agent ran for an hour and <code>InputTokens</code> reads 4K, the rest came from cache; check the sum, not the one field.</p>
<p>In a healthy multi-turn loop you should see, on each request:</p>
<ul>
<li><code>CacheReadInputTokens</code> — the whole prior prefix, growing turn over turn.</li>
<li><code>CacheCreationInputTokens</code> — roughly the last assistant turn plus the new input. Small.</li>
<li><code>InputTokens</code> — just the tail past the last breakpoint.</li>
</ul>
<p>If <code>CacheCreationInputTokens</code> is instead close to the full conversation size every time, the prefix is being rewritten upstream of your breakpoint. Go find the timestamp.</p>
<p><strong>And then keep checking.</strong> The expensive failure here is never the bad first implementation — it is the regression. Caching works the day you write it, then six weeks later somebody adds a dynamic field to the system prompt or a tool list that stopped being sorted, and every request misses. Nothing fails. Nothing pages. You find out from finance.</p>
<p>So make it a standing assertion, not a one-time look:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestSystemPromptStaysCacheable</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Two identical requests: the second must read from cache.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">first</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">callModel</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">fixture</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">first</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">CacheCreationInputTokens</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Fatalf</span>(<span style="color:#e6db74">&#34;nothing was cached: prompt may be under the model minimum&#34;</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">second</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">callModel</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">fixture</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">second</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">CacheReadInputTokens</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;cache miss on an identical prompt — a silent invalidator crept in&#34;</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>That test costs a few cents to run and catches a regression that otherwise runs for months. Put it behind a build tag so it only runs when you mean it — the same treatment I gave integration tests in <a href="/posts/how-to-test-database-interactions-go/">how to test database interactions in Golang</a>. Better still, put a monitor on the ratio of <code>CacheReadInputTokens</code> to total input tokens and alert when it drops.</p>
<h2 id="checklist">Checklist</h2>
<ul>
<li>Frozen system prompt: no dates, IDs, names or conditional sections.</li>
<li>Deterministic tool list, sorted by name, identical across users.</li>
<li>One explicit breakpoint at the end of the static prefix; automatic caching for the tail.</li>
<li>Breakpoint at the end of the <em>shared</em> portion, not the end of the prompt.</li>
<li>5-minute TTL under continuous traffic; 1-hour only for 5–60 minute gaps.</li>
<li>Prompt above the model&rsquo;s minimum, which changes between models.</li>
<li>Dynamic context appended after the history, never templated into the system prompt.</li>
<li>Forks copy the parent&rsquo;s <code>system</code>, <code>tools</code> and <code>model</code> verbatim.</li>
<li>A test or monitor on the usage fields, checked on every change to prompt assembly.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Prompt caching is the rare optimisation with no quality tradeoff: identical output, roughly a tenth of the input cost, and lower latency as a bonus. It is also unusually fragile, because it hinges on byte-exact prefixes and fails completely silently. Treat the prompt-building path the way you would treat a cache key anywhere else in your system — deterministic, stable, and covered by a test — and it mostly takes care of itself.</p>]]></content:encoded>
      <category>AI Engineering</category>
      <category>Performance Optimization</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Tool Use in Go: Building an Agent Loop You Can Actually Debug</title>
      <link>https://webdevstation.com/posts/tool-use-in-go-agent-loop/</link>
      <pubDate>Sat, 29 Aug 2026 10:10:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/tool-use-in-go-agent-loop/</guid>
      <description>How LLM tool use really works in Go: the agentic loop, the SDK&#39;s tool runner, parallel tool calls, bounded concurrency, returning errors as tool results, and the…</description>
      <content:encoded><![CDATA[<p>&ldquo;Agent&rdquo; is doing a lot of work as a word right now. Strip the marketing off and what is underneath is a <code>for</code> loop: you send a message, the model asks you to run something, you run it, you send the result back, repeat until it stops asking. That is genuinely all it is — and once you have written the loop yourself, most of the mystique evaporates and what is left is a set of very ordinary Go problems.</p>
<h2 id="the-loop-in-full">The Loop, In Full</h2>
<p>Here is a complete manual loop. It is worth reading once even if you end up using the SDK&rsquo;s runner, because everything that goes wrong later is easier to diagnose when you know this shape. It assumes you already have a client and know how to read a response — if not, start with <a href="/posts/calling-claude-from-go/">calling an LLM from Go</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;context&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;encoding/json&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/anthropics/anthropic-sdk-go&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">client</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewClient</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">addTool</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ToolParam</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Name</span>:        <span style="color:#e6db74">&#34;add&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Description</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">String</span>(<span style="color:#e6db74">&#34;Add two integers&#34;</span>),
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">InputSchema</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ToolInputSchemaParam</span>{
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Properties</span>: <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">any</span>{
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;a&#34;</span>: <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">any</span>{<span style="color:#e6db74">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;integer&#34;</span>},
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;b&#34;</span>: <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">any</span>{<span style="color:#e6db74">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;integer&#34;</span>},
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">tools</span> <span style="color:#f92672">:=</span> []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ToolUnionParam</span>{{<span style="color:#a6e22e">OfTool</span>: <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">addTool</span>}}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">messages</span> <span style="color:#f92672">:=</span> []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageParam</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewUserMessage</span>(<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewTextBlock</span>(<span style="color:#e6db74">&#34;What is 2 + 3?&#34;</span>)),
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">resp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">Messages</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageNewParams</span>{
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Model</span>:     <span style="color:#e6db74">&#34;claude-opus-5&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">MaxTokens</span>: <span style="color:#ae81ff">16000</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Messages</span>:  <span style="color:#a6e22e">messages</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Tools</span>:     <span style="color:#a6e22e">tools</span>,
</span></span><span style="display:flex;"><span>        })
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Append the assistant turn BEFORE handling the tool calls.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">messages</span> = append(<span style="color:#a6e22e">messages</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">ToParam</span>())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">toolResults</span> []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ContentBlockParamUnion</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">block</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Content</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">switch</span> <span style="color:#a6e22e">variant</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">block</span>.<span style="color:#a6e22e">AsAny</span>().(<span style="color:#66d9ef">type</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">TextBlock</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">variant</span>.<span style="color:#a6e22e">Text</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ToolUseBlock</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">in</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#a6e22e">A</span> <span style="color:#66d9ef">int</span> <span style="color:#e6db74">`json:&#34;a&#34;`</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#a6e22e">B</span> <span style="color:#66d9ef">int</span> <span style="color:#e6db74">`json:&#34;b&#34;`</span>
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>                <span style="color:#75715e">// block.Input is raw JSON — parse it, never string-match it.</span>
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">variant</span>.<span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">Input</span>.<span style="color:#a6e22e">Raw</span>()), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">in</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">result</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;%d&#34;</span>, <span style="color:#a6e22e">in</span>.<span style="color:#a6e22e">A</span><span style="color:#f92672">+</span><span style="color:#a6e22e">in</span>.<span style="color:#a6e22e">B</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">toolResults</span> = append(<span style="color:#a6e22e">toolResults</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewToolResultBlock</span>(<span style="color:#a6e22e">block</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">result</span>, <span style="color:#66d9ef">false</span>))
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">StopReason</span> <span style="color:#f92672">!=</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">StopReasonToolUse</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">break</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// All results from this turn go back in ONE user message.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">messages</span> = append(<span style="color:#a6e22e">messages</span>, <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewUserMessage</span>(<span style="color:#a6e22e">toolResults</span><span style="color:#f92672">...</span>))
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Five things in there are load-bearing, and four of them are easy to get subtly wrong.</p>
<p><strong><code>resp.ToParam()</code> converts the response into a history entry.</strong> You must append the assistant&rsquo;s turn — including its tool-call blocks — before you send the results, or the next request has results referring to a call that does not exist in the conversation.</p>
<p><strong>Parse the tool input; never pattern-match the raw string.</strong> <code>variant.JSON.Input.Raw()</code> gives you the JSON to unmarshal. Current models vary their JSON string escaping (Unicode escapes, escaped forward slashes), so anything doing <code>strings.Contains</code> on the serialised input is a bug waiting for a release.</p>
<p><strong>All tool results go back in a single user message.</strong> <code>anthropic.NewUserMessage</code> is variadic for exactly this reason. Splitting results across several messages technically works, and it quietly teaches the model to stop issuing parallel calls — which halves your throughput for no visible reason.</p>
<p><strong><code>StopReason</code> is the exit condition</strong>, not &ldquo;did I see any tool blocks&rdquo;. Check it after you have appended the results, not before.</p>
<p><strong>Every tool call needs a result.</strong> If the model asked for three tools and you return two results, the next request is malformed. Including for the one that failed — which brings us to the most useful trick in this whole article.</p>
<h2 id="errors-are-results-not-exceptions">Errors Are Results, Not Exceptions</h2>
<p>The instinct when a tool fails is to abort the loop. Usually that is wrong. Hand the failure back to the model as a tool result flagged as an error, and it will very often recover on its own — retry with a corrected argument, try a different tool, or tell the user what went wrong:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">out</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">run</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">variant</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// isError = true. The model sees the failure and can adapt.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">toolResults</span> = append(<span style="color:#a6e22e">toolResults</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewToolResultBlock</span>(<span style="color:#a6e22e">block</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>(), <span style="color:#66d9ef">true</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">continue</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">toolResults</span> = append(<span style="color:#a6e22e">toolResults</span>, <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewToolResultBlock</span>(<span style="color:#a6e22e">block</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">out</span>, <span style="color:#66d9ef">false</span>))
</span></span></code></pre></div><p>That third parameter is <code>isError</code>. Getting this right turns a class of hard failures into self-correcting ones.</p>
<p>One caveat worth stating plainly: the error text goes into the model&rsquo;s context, so do not put a raw database error with connection strings and internal hostnames in there. Return the error you would show a careful external user. This is the same discipline as deciding what a sentinel error exposes at your HTTP boundary, which I covered in <a href="/posts/error-handling-in-go/">error handling in Go</a>.</p>
<h2 id="let-the-sdk-drive">Let the SDK Drive</h2>
<p>Once you understand the loop, you mostly do not want to maintain it. The Go SDK&rsquo;s tool runner handles the iteration, and generates the JSON schema from your struct tags:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;github.com/anthropics/anthropic-sdk-go/toolrunner&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">GetWeatherInput</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">City</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:&#34;city&#34; jsonschema:&#34;required,description=The city name&#34;`</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">weatherTool</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">toolrunner</span>.<span style="color:#a6e22e">NewBetaToolFromJSONSchema</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;get_weather&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Get current weather for a city&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">in</span> <span style="color:#a6e22e">GetWeatherInput</span>) (<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaToolResultBlockParamContentUnion</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaToolResultBlockParamContentUnion</span>{
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">OfText</span>: <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaTextBlockParam</span>{
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">Text</span>: <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;The weather in %s is sunny, 22°C&#34;</span>, <span style="color:#a6e22e">in</span>.<span style="color:#a6e22e">City</span>),
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>        }, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">runner</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">Beta</span>.<span style="color:#a6e22e">Messages</span>.<span style="color:#a6e22e">NewToolRunner</span>(
</span></span><span style="display:flex;"><span>    []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaTool</span>{<span style="color:#a6e22e">weatherTool</span>},
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaToolRunnerParams</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">BetaMessageNewParams</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaMessageNewParams</span>{
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Model</span>:     <span style="color:#e6db74">&#34;claude-opus-5&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">MaxTokens</span>: <span style="color:#ae81ff">16000</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Messages</span>: []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaMessageParam</span>{
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewBetaUserMessage</span>(<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewBetaTextBlock</span>(<span style="color:#e6db74">&#34;What&#39;s the weather in Kyiv?&#34;</span>)),
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">MaxIterations</span>: <span style="color:#ae81ff">5</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">message</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">runner</span>.<span style="color:#a6e22e">RunToCompletion</span>(<span style="color:#a6e22e">ctx</span>)
</span></span></code></pre></div><p>Note the namespace: this lives under <code>client.Beta.Messages</code>, and the types are the <code>Beta*</code> variants — <code>BetaTextBlock</code>, not <code>TextBlock</code>. Mixing the two is the most common compile error here.</p>
<p><code>MaxIterations</code> is not optional decoration. Without a ceiling, a model that gets into a retry rut can loop until your context deadline, and you pay for every turn. Set it to the smallest number that lets legitimate work finish.</p>
<p>If you need to inspect or gate each step — approvals, audit logging, a check before a destructive tool runs — you do not have to drop back to a manual loop. The runner exposes <code>NextMessage()</code> and an <code>All()</code> iterator so you can step it and look at each message, and its <code>Params</code> field lets you adjust the next request. Reach for the manual loop only when you want control the runner genuinely does not expose.</p>
<h2 id="running-tools-concurrently--with-a-limit">Running Tools Concurrently — With a Limit</h2>
<p>When the model asks for four tools in one turn, running them sequentially wastes the whole point. But the naive concurrent version is the same mistake Go developers make everywhere else:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Don&#39;t. One turn can ask for many tools; this has no ceiling.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">call</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">calls</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#a6e22e">run</span>(<span style="color:#a6e22e">call</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Use a bounded group. The results still have to come back in one message, in a fixed order, so index into a preallocated slice rather than appending from goroutines:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;golang.org/x/sync/errgroup&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">results</span> <span style="color:#f92672">:=</span> make([]<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ContentBlockParamUnion</span>, len(<span style="color:#a6e22e">calls</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">gctx</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">errgroup</span>.<span style="color:#a6e22e">WithContext</span>(<span style="color:#a6e22e">ctx</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">SetLimit</span>(<span style="color:#ae81ff">4</span>) <span style="color:#75715e">// whatever your slowest downstream can absorb</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">call</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">calls</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">out</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">run</span>(<span style="color:#a6e22e">gctx</span>, <span style="color:#a6e22e">call</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Not a group error: hand it to the model instead.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">results</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewToolResultBlock</span>(<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>(), <span style="color:#66d9ef">true</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">results</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewToolResultBlock</span>(<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">out</span>, <span style="color:#66d9ef">false</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Wait</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">messages</span> = append(<span style="color:#a6e22e">messages</span>, <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewUserMessage</span>(<span style="color:#a6e22e">results</span><span style="color:#f92672">...</span>))
</span></span></code></pre></div><p>Each goroutine writes one distinct slice element, so no mutex is needed — different elements are different memory. Appending to a shared slice from several goroutines is a different story, and so is writing to a shared map; <a href="/posts/concurrent-map-writing-and-reading-in-go/">concurrent map writing and reading in Go</a> has the failure mode in detail.</p>
<p>Notice that a tool failure returns <code>nil</code> from <code>g.Go</code>. Returning the error would cancel <code>gctx</code> and kill the sibling tool calls, when what you actually want is to report that one failure to the model and let the others finish. The full set of tradeoffs around <code>SetLimit</code> and error propagation is in <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools in Go with errgroup</a>.</p>
<h2 id="designing-the-tools-themselves">Designing the Tools Themselves</h2>
<p>The loop is the easy part. Tool <em>design</em> is where agents get good or stay bad.</p>
<p><strong>The description is the API documentation, and its reader is the model.</strong> A tool called <code>search</code> described as &ldquo;searches&rdquo; will be called wrongly and often. Say what it searches, what it returns, and when <em>not</em> to use it. Most &ldquo;the agent keeps doing the wrong thing&rdquo; problems are description problems.</p>
<p><strong>Fewer, broader tools beat many narrow ones.</strong> Twenty tools that each wrap one endpoint force the model to plan a long chain and give it twenty chances to pick wrong. One <code>query_orders</code> tool with a few well-named parameters usually outperforms <code>get_order</code>, <code>list_orders_by_user</code>, <code>list_orders_by_date</code> and <code>count_orders</code>.</p>
<p><strong>Constrain the schema.</strong> Enums, required fields and explicit types are enforced before your handler runs. Every constraint you express in the schema is a class of invalid call you never have to validate by hand.</p>
<p><strong>Make results terse.</strong> Tool results occupy context on every subsequent turn of the loop. Returning a 400-row JSON dump costs you tokens on turn two, turn three and turn four. Return the fields the model needs to decide what to do next, and nothing else.</p>
<p><strong>Be deliberate about side effects.</strong> The model will call your tools in orders you did not anticipate. Anything that writes, sends, charges or deletes wants an approval gate — step the runner and confirm — or, at minimum, idempotency so a double call is harmless.</p>
<h2 id="guardrails-that-actually-matter-in-production">Guardrails That Actually Matter in Production</h2>
<p>A tool loop has a cost profile unlike a normal handler: every iteration resends the whole conversation. The bill grows quadratically with the number of turns if you are not careful, and three things keep it honest.</p>
<p><strong>Cap the iterations.</strong> <code>MaxIterations</code> on the runner, or a counter in your manual loop. Non-negotiable.</p>
<p><strong>Bound the wall clock.</strong> A deadline on the context that covers the whole loop, not each request:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithTimeout</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#ae81ff">5</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Minute</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">cancel</span>()
</span></span></code></pre></div><p><strong>Cache the prefix.</strong> Every turn resends the system prompt and the full history. Without prompt caching you pay full price for all of it, every iteration — this is where agent loops get expensive, and it is the one lever with no quality tradeoff at all. It gets <a href="/posts/prompt-caching-llm-cost/">its own article</a>.</p>
<p>Then there is the interaction between concurrency and rate limits. A pool of four tool calls, times however many concurrent user requests, times however many turns each — an agent loop is a very effective way to discover your own rate limits. The token-bucket approach from <a href="/posts/rate-limiting-go-apis/">rate limiting Go APIs</a> works just as well pointed at your own outbound calls as at inbound traffic.</p>
<h2 id="observability-or-you-are-flying-blind">Observability, Or You Are Flying Blind</h2>
<p>When a loop misbehaves, you need to see what the model actually saw. Log per iteration:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;agent turn&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;iteration&#34;</span>, <span style="color:#a6e22e">i</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;stop_reason&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">StopReason</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;tools_called&#34;</span>, <span style="color:#a6e22e">toolNames</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;input_tokens&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">InputTokens</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;output_tokens&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">OutputTokens</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;cache_read&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">CacheReadInputTokens</span>,
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>With a request-scoped logger carrying the conversation id (<a href="/posts/structured-logging-in-go-with-slog/">the pattern from the slog post</a>), you can pull the entire trajectory of one run out of your logs — which is the difference between &ldquo;the agent is flaky&rdquo; and &ldquo;on turn three it called <code>search</code> with an empty query because the description was ambiguous&rdquo;.</p>
<h2 id="pitfalls">Pitfalls</h2>
<table>
	<thead>
			<tr>
					<th>Pitfall</th>
					<th>Fix</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Results returned for only some tool calls</td>
					<td>Return one result per <code>tool_use</code> block, failures included</td>
			</tr>
			<tr>
					<td>Tool results split across several user messages</td>
					<td>One user message, all results, variadic <code>NewUserMessage</code></td>
			</tr>
			<tr>
					<td>Assistant turn not appended before results</td>
					<td><code>messages = append(messages, resp.ToParam())</code> first</td>
			</tr>
			<tr>
					<td>String-matching the raw tool input</td>
					<td><code>json.Unmarshal(variant.JSON.Input.Raw())</code></td>
			</tr>
			<tr>
					<td>Mixing <code>TextBlock</code> and <code>BetaTextBlock</code></td>
					<td>Pick a namespace; the runner is <code>Beta.*</code> throughout</td>
			</tr>
			<tr>
					<td>Loop runs until the deadline</td>
					<td><code>MaxIterations</code>, plus a context timeout for the whole loop</td>
			</tr>
			<tr>
					<td>Tool error cancels its siblings</td>
					<td>Return <code>nil</code> from <code>g.Go</code>; hand the error to the model</td>
			</tr>
			<tr>
					<td>Cost grows faster than expected</td>
					<td>Cache the prefix; keep tool results terse</td>
			</tr>
	</tbody>
</table>
<h2 id="conclusion">Conclusion</h2>
<p>The loop is twenty lines and you should write it once by hand, then let the runner own it. After that, the work that actually improves an agent is not loop code at all: sharper tool descriptions, tighter schemas, terser results, a hard iteration cap, and enough logging to reconstruct a bad run. The interesting engineering is in the tools, not the loop around them.</p>]]></content:encoded>
      <category>AI Engineering</category>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Calling an LLM from Go: Streaming, Timeouts and the Parts That Bite</title>
      <link>https://webdevstation.com/posts/calling-claude-from-go/</link>
      <pubDate>Thu, 27 Aug 2026 09:40:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/calling-claude-from-go/</guid>
      <description>A practical guide to wiring Claude into a Go service with the official SDK: content blocks, adaptive thinking, streaming, context deadlines, typed errors and the…</description>
      <content:encoded><![CDATA[<p>Most of the LLM tutorials I read are Python notebooks. That is fine for a prototype, but the moment the thing has to live inside a service — with timeouts, cancellation, retries and a bill attached — Go&rsquo;s constraints start to matter, and so do the details the notebooks skip. This is the write-up I wanted when I put my first Claude call into a Go HTTP handler.</p>
<h2 id="getting-a-client">Getting a Client</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>go get github.com/anthropics/anthropic-sdk-go
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/anthropics/anthropic-sdk-go&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/anthropics/anthropic-sdk-go/option&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Reads ANTHROPIC_API_KEY from the environment.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">client</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewClient</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Or pass it explicitly, if you load config yourself.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">client</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewClient</span>(<span style="color:#a6e22e">option</span>.<span style="color:#a6e22e">WithAPIKey</span>(<span style="color:#a6e22e">key</span>))
</span></span></code></pre></div><p>Build the client <strong>once</strong>, at startup, and pass it around. It is safe for concurrent use and holds a connection pool — constructing one per request throws away keep-alive and gives you a fresh TLS handshake every time.</p>
<h2 id="the-first-call">The First Call</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">resp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">Messages</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageNewParams</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Model</span>:     <span style="color:#e6db74">&#34;claude-opus-5&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">MaxTokens</span>: <span style="color:#ae81ff">16000</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Messages</span>: []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageParam</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewUserMessage</span>(<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewTextBlock</span>(<span style="color:#e6db74">&#34;Summarise this changelog in three bullets.&#34;</span>)),
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;summarise changelog: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Two things about that snippet are worth slowing down on.</p>
<p><strong>The model is a plain string.</strong> The SDK ships typed constants (<code>anthropic.ModelClaudeOpus4_8</code> and friends), but <code>anthropic.Model</code> is an alias for <code>string</code>, so a model that has no constant yet is passed as its id. Either form compiles; check the SDK release notes before assuming a constant exists for the model you want.</p>
<p><strong><code>MaxTokens</code> is a ceiling, not a target.</strong> It is the point at which generation is cut off mid-sentence, and the model is not told about it. Setting it to <code>500</code> because you want a short answer does not produce a short answer — it produces a truncated one. Ask for brevity in the prompt and leave the ceiling generous. For non-streaming requests, something around <code>16000</code> keeps you clear of both truncation and the SDK&rsquo;s HTTP timeout.</p>
<h2 id="content-is-a-list-of-blocks-not-a-string">Content Is a List of Blocks, Not a String</h2>
<p>This is where the first hour usually goes. A response is not <code>resp.Text</code>. It is <code>resp.Content</code>, a slice of union values that can hold text, thinking, tool calls and more:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">block</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Content</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">switch</span> <span style="color:#a6e22e">variant</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">block</span>.<span style="color:#a6e22e">AsAny</span>().(<span style="color:#66d9ef">type</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">TextBlock</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">variant</span>.<span style="color:#a6e22e">Text</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ThinkingBlock</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Reasoning, when you have asked for it to be shown.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Debug</span>(<span style="color:#e6db74">&#34;model reasoning&#34;</span>, <span style="color:#e6db74">&#34;text&#34;</span>, <span style="color:#a6e22e">variant</span>.<span style="color:#a6e22e">Thinking</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>block.AsAny()</code> is the accessor that gets you a concrete type to switch on. Reaching for <code>resp.Content[0].Text</code> and hoping works right up until the day a thinking block or a tool call lands in position zero, at which point you silently return an empty string. Write the type switch once, in a helper, and use it everywhere:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// firstText returns the first text block in a response, or &#34;&#34; if there is none.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">firstText</span>(<span style="color:#a6e22e">msg</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">Message</span>) <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">block</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">msg</span>.<span style="color:#a6e22e">Content</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">block</span>.<span style="color:#a6e22e">AsAny</span>().(<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">TextBlock</span>); <span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Text</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="thinking-and-why-you-probably-want-it-on">Thinking, and Why You Probably Want It On</h2>
<p>Current Claude models can reason before answering. The recommended mode is <strong>adaptive</strong> — you do not budget tokens for it, the model decides how much thinking a given request deserves:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">adaptive</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ThinkingConfigAdaptiveParam</span>{}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">params</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageNewParams</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Model</span>:     <span style="color:#e6db74">&#34;claude-opus-5&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">MaxTokens</span>: <span style="color:#ae81ff">16000</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Thinking</span>:  <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ThinkingConfigParamUnion</span>{<span style="color:#a6e22e">OfAdaptive</span>: <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">adaptive</span>},
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Messages</span>:  <span style="color:#a6e22e">messages</span>,
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>There is no <code>ThinkingConfigParamOfAdaptive</code> helper — you construct the union literal and take the address of the variant, as above. That trips people up because almost every other option in the SDK <em>does</em> have a constructor function.</p>
<p>A word of warning if you are carrying settings over from older code: the fixed thinking budget (<code>ThinkingConfigParamOfEnabled(N)</code>) is gone on current models and returns a 400 rather than being ignored. If you want to spend <em>less</em>, the lever is effort, not a token budget — and effort lives inside <code>output_config</code>, not at the top level of the request.</p>
<p>The counter-intuitive part: on the newest models, turning thinking <strong>off</strong> is not reliably a cost saving. Lower effort with thinking on generally beats thinking off, and disabling it has failure modes of its own. Leave it on and turn effort down.</p>
<h2 id="streaming">Streaming</h2>
<p>Anything a user waits for should stream. It is not just perceived speed — a long non-streaming request is also the easiest way to hit an HTTP timeout.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">stream</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">Messages</span>.<span style="color:#a6e22e">NewStreaming</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageNewParams</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Model</span>:     <span style="color:#e6db74">&#34;claude-opus-5&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">MaxTokens</span>: <span style="color:#ae81ff">64000</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Messages</span>:  <span style="color:#a6e22e">messages</span>,
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">stream</span>.<span style="color:#a6e22e">Next</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">event</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">stream</span>.<span style="color:#a6e22e">Current</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">switch</span> <span style="color:#a6e22e">ev</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">AsAny</span>().(<span style="color:#66d9ef">type</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ContentBlockDeltaEvent</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">switch</span> <span style="color:#a6e22e">delta</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ev</span>.<span style="color:#a6e22e">Delta</span>.<span style="color:#a6e22e">AsAny</span>().(<span style="color:#66d9ef">type</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">TextDelta</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">delta</span>.<span style="color:#a6e22e">Text</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">stream</span>.<span style="color:#a6e22e">Err</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;stream response: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Two nested type switches is not the prettiest Go you will write, but the shape is stable: outer switch on the event, inner switch on the delta.</p>
<p><strong>Always check <code>stream.Err()</code>.</strong> <code>stream.Next()</code> returning <code>false</code> means &ldquo;no more events&rdquo; — it does not tell you whether that was a clean finish or a dropped connection. A loop that ignores <code>Err()</code> will happily serve a truncated answer as if it were complete.</p>
<p>If you want the whole message <em>and</em> the incremental deltas, accumulate as you go. There is no <code>GetFinalMessage()</code> on the Go stream:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">stream</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">Messages</span>.<span style="color:#a6e22e">NewStreaming</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">params</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">message</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">Message</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">stream</span>.<span style="color:#a6e22e">Next</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">Accumulate</span>(<span style="color:#a6e22e">stream</span>.<span style="color:#a6e22e">Current</span>())
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ... also forward the delta to the user here</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">stream</span>.<span style="color:#a6e22e">Err</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#75715e">// message.Content is now the complete response.</span>
</span></span></code></pre></div><p>Raising <code>MaxTokens</code> to <code>64000</code> in the streaming example is deliberate. Timeouts stop being the binding constraint once you stream, so you can give the model room.</p>
<h3 id="streaming-to-a-browser">Streaming to a Browser</h3>
<p>Server-sent events are the path of least resistance, and Go&rsquo;s <code>http.ResponseController</code> makes the flushing straightforward:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">h</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Handler</span>) <span style="color:#a6e22e">stream</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;Content-Type&#34;</span>, <span style="color:#e6db74">&#34;text/event-stream&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;Cache-Control&#34;</span>, <span style="color:#e6db74">&#34;no-cache&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;X-Accel-Buffering&#34;</span>, <span style="color:#e6db74">&#34;no&#34;</span>) <span style="color:#75715e">// stop nginx buffering the stream</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">rc</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">NewResponseController</span>(<span style="color:#a6e22e">w</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">stream</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">Messages</span>.<span style="color:#a6e22e">NewStreaming</span>(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Context</span>(), <span style="color:#a6e22e">params</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">stream</span>.<span style="color:#a6e22e">Next</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ev</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">stream</span>.<span style="color:#a6e22e">Current</span>().<span style="color:#a6e22e">AsAny</span>().(<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ContentBlockDeltaEvent</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">continue</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">delta</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ev</span>.<span style="color:#a6e22e">Delta</span>.<span style="color:#a6e22e">AsAny</span>().(<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">TextDelta</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">continue</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// SSE data frames must not contain raw newlines.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">payload</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">delta</span>.<span style="color:#a6e22e">Text</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Fprintf</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;data: %s\n\n&#34;</span>, <span style="color:#a6e22e">payload</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#75715e">// client hung up</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">rc</span>.<span style="color:#a6e22e">Flush</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">stream</span>.<span style="color:#a6e22e">Err</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#e6db74">&#34;llm stream failed&#34;</span>, <span style="color:#e6db74">&#34;err&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Fprint</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;event: error\ndata: {}\n\n&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">rc</span>.<span style="color:#a6e22e">Flush</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Fprint</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;event: done\ndata: {}\n\n&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">rc</span>.<span style="color:#a6e22e">Flush</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Three details that cost me an afternoon each:</p>
<ul>
<li><strong><code>X-Accel-Buffering: no</code>.</strong> Without it nginx buffers your stream and delivers the whole thing at the end, which looks exactly like streaming being broken. (Nginx has strong opinions about proxied responses generally — I ran into a related set of them in <a href="/posts/how-to-make-nginx-cookie-aware/">making Nginx cache cookie aware</a>.)</li>
<li><strong>JSON-encode the delta.</strong> A model can and will emit a newline mid-sentence, and a bare newline terminates an SSE frame.</li>
<li><strong>Pass <code>r.Context()</code>, not <code>context.Background()</code>.</strong> When the user closes the tab, the request context cancels, the SDK aborts the HTTP call, and you stop paying for tokens nobody will read.</li>
</ul>
<h2 id="deadlines-and-cancellation">Deadlines and Cancellation</h2>
<p>Context is not decoration here. It is the only thing standing between a slow model call and a goroutine that lives forever:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithTimeout</span>(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Context</span>(), <span style="color:#ae81ff">90</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">cancel</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">resp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">Messages</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">params</span>)
</span></span></code></pre></div><p>Set the deadline against how long the <em>work</em> should take, not a habit. A classification call has no business taking 90 seconds; a long agentic turn on a hard problem might legitimately run for several minutes. If you have not internalised how deadlines propagate through a call chain, <a href="/posts/understanding-golang-context/">understanding Golang context</a> covers the machinery.</p>
<p>The corollary at shutdown: an in-flight model call is exactly the kind of long request that a naive <code>SIGTERM</code> handler will sever. Drain it properly — see <a href="/posts/graceful-shutdown-in-go-web-services/">graceful shutdown in Go web services</a>.</p>
<h2 id="errors-worth-distinguishing">Errors Worth Distinguishing</h2>
<p>The SDK returns typed errors. Use <code>errors.As</code> to get at the status code rather than matching on message strings:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">resp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">Messages</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">params</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">apiErr</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">Error</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">As</span>(<span style="color:#a6e22e">err</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">apiErr</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">switch</span> <span style="color:#a6e22e">apiErr</span>.<span style="color:#a6e22e">StatusCode</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusTooManyRequests</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;rate limited: %w&#34;</span>, <span style="color:#a6e22e">ErrRetryable</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusBadRequest</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Your request is malformed. Retrying will not help.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;bad request to model: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">default</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;model call failed (%d): %w&#34;</span>, <span style="color:#a6e22e">apiErr</span>.<span style="color:#a6e22e">StatusCode</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Not an API error: context cancellation, DNS, TLS, connection reset.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;model call failed: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Wrapping with <code>%w</code> at each layer is what lets the HTTP boundary decide the status code without every layer needing to know about HTTP — the same pattern I laid out in <a href="/posts/error-handling-in-go/">error handling in Go</a>.</p>
<p><strong>The SDK already retries for you.</strong> By default it retries a couple of times on <code>408</code>, <code>409</code>, <code>429</code>, <code>5xx</code> and connection errors, with backoff. Two consequences people miss:</p>
<ol>
<li><strong>Do not add your own retry loop on top</strong> without lowering the SDK&rsquo;s. Three of yours around two of its is nine attempts and a long tail of latency.</li>
<li><strong>Wall-clock can reach <code>timeout × (attempts + 1)</code>.</strong> Your context deadline is the real budget — set it deliberately, because the retry behaviour will happily use all of it.</li>
</ol>
<h2 id="a-refusal-is-not-an-error">A Refusal Is Not an Error</h2>
<p>This one surprises people. If a safety classifier declines a request, you get <strong>HTTP 200</strong> — a perfectly successful response whose <code>StopReason</code> says the model declined:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">StopReason</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">StopReasonRefusal</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Warn</span>(<span style="color:#e6db74">&#34;model declined&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;category&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">StopDetails</span>.<span style="color:#a6e22e">Category</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;explanation&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">StopDetails</span>.<span style="color:#a6e22e">Explanation</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#a6e22e">ErrDeclined</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>err</code> is nil. <code>resp.Content</code> may hold nothing useful. Code that goes straight from <code>if err != nil</code> to reading <code>Content[0]</code> treats this as an empty answer and moves on. Check <code>StopReason</code> before you read content — and note the other values you care about: <code>max_tokens</code> means you were truncated, and <code>tool_use</code> means the model is waiting on you (which is <a href="/posts/tool-use-in-go-agent-loop/">a whole article of its own</a>).</p>
<h2 id="watch-the-usage-numbers">Watch the Usage Numbers</h2>
<p>Every response carries a token accounting:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;model call&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;input_tokens&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">InputTokens</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;output_tokens&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">OutputTokens</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;cache_read&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">CacheReadInputTokens</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;cache_write&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">CacheCreationInputTokens</span>,
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>Log these from day one. They are the only ground truth about what a feature costs, and <code>CacheReadInputTokens</code> in particular is how you find out that your prompt caching silently stopped working three deploys ago — the failure mode there is not an error, just a bigger invoice. That is <a href="/posts/prompt-caching-llm-cost/">its own post</a>, because it is the single biggest lever on what an LLM feature costs to run.</p>
<p>A structured logger pays for itself here: these are five numeric fields per call that you will want to aggregate later, which is exactly the case for <a href="/posts/structured-logging-in-go-with-slog/">structured logging with log/slog</a>.</p>
<h2 id="checklist">Checklist</h2>
<ul>
<li>One client, built at startup, shared across handlers.</li>
<li>Iterate <code>resp.Content</code> with a type switch; never index blindly into it.</li>
<li>Adaptive thinking on; tune cost with effort, not by disabling it.</li>
<li>Stream anything a human waits for, and always check <code>stream.Err()</code>.</li>
<li><code>r.Context()</code> all the way down, with a deliberate deadline.</li>
<li><code>errors.As</code> into <code>*anthropic.Error</code> for status-code branching.</li>
<li>Do not stack your retries on the SDK&rsquo;s.</li>
<li>Check <code>StopReason</code> before reading content — a refusal returns 200.</li>
<li>Log the usage fields from the first commit.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>The API surface is small — one endpoint, one loop, a handful of block types. What makes an LLM call different from any other HTTP call in your service is that it is slow, occasionally non-deterministic, priced per token, and able to succeed while declining to do what you asked. Go gives you good tools for exactly those problems, provided you use the context properly and read the response as the structured thing it is rather than the string you wish it were.</p>]]></content:encoded>
      <category>AI Engineering</category>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Rate Limiting Go APIs with golang.org/x/time/rate</title>
      <link>https://webdevstation.com/posts/rate-limiting-go-apis/</link>
      <pubDate>Tue, 25 Aug 2026 09:00:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/rate-limiting-go-apis/</guid>
      <description>Protect your Go HTTP APIs with token bucket rate limiting: per-client limiters, middleware, the standard rate limit headers, client-side throttling and when to move…</description>
      <content:encoded><![CDATA[<p>Somebody eventually points a badly written script at your API. Not maliciously — usually it is a colleague&rsquo;s retry loop with no back-off, or a cron job that fires every minute and takes ninety seconds. Without a limit, one client can consume the capacity you were saving for everyone else. <code>golang.org/x/time/rate</code> handles this in about as much code as it takes to describe, and it is the piece I now add before the first public endpoint ships.</p>
<h2 id="the-token-bucket-in-one-paragraph">The Token Bucket, in One Paragraph</h2>
<p>Picture a bucket that holds <code>b</code> tokens and refills at <code>r</code> tokens per second. Every request takes one token. If the bucket is empty, the request is rejected (or waits). That is the whole model, and it has one property that makes it the right default: <code>b</code> is a <strong>burst</strong> allowance. A client that has been idle can spend its accumulated tokens all at once, then settles into the steady rate. Real traffic is bursty — a page load firing six API calls should not be punished, while a loop firing six hundred should.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;golang.org/x/time/rate&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// 10 requests per second, bursts of up to 20.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">limiter</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">NewLimiter</span>(<span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">20</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">limiter</span>.<span style="color:#a6e22e">Allow</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// over budget</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Three methods, for three different situations:</p>
<table>
	<thead>
			<tr>
					<th>Method</th>
					<th>Behaviour</th>
					<th>Use it for</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>Allow()</code></td>
					<td>Returns immediately: true or false</td>
					<td>Inbound HTTP — reject with 429</td>
			</tr>
			<tr>
					<td><code>Wait(ctx)</code></td>
					<td>Blocks until a token is free or ctx ends</td>
					<td>Outbound calls you control</td>
			</tr>
			<tr>
					<td><code>Reserve()</code></td>
					<td>Reserves a token, tells you the delay</td>
					<td>When you need to report <code>Retry-After</code></td>
			</tr>
	</tbody>
</table>
<p><code>rate.Limit</code> is a float, so fractional rates work: <code>rate.Every(time.Minute/100)</code> is 100 per minute, and <code>rate.Limit(0.5)</code> is one request every two seconds. <code>rate.Inf</code> disables limiting entirely, which is handy for a per-plan configuration where some tier is unlimited.</p>
<h2 id="a-global-limiter-is-not-enough">A Global Limiter Is Not Enough</h2>
<p>The naive version puts one limiter in front of everything:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">global</span> = <span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">NewLimiter</span>(<span style="color:#ae81ff">100</span>, <span style="color:#ae81ff">200</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">limit</span>(<span style="color:#a6e22e">next</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span>) <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">global</span>.<span style="color:#a6e22e">Allow</span>() {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;too many requests&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusTooManyRequests</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">next</span>.<span style="color:#a6e22e">ServeHTTP</span>(<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This protects your <em>server</em> but not your <em>users</em>: one aggressive client can still eat the entire global budget and everyone else gets 429s. A global limiter is a useful backstop, not a fairness mechanism. What you want is a limiter per client, with the global one behind it.</p>
<h2 id="per-client-limiters">Per-Client Limiters</h2>
<p>Keep a map from client key to limiter, guarded by a mutex, with a janitor that evicts idle entries so the map does not grow forever.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">ratelimit</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;sync&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;golang.org/x/time/rate&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">client</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">limiter</span>  <span style="color:#f92672">*</span><span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limiter</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">lastSeen</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Store hands out one limiter per key and forgets keys that go quiet.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Store</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">mu</span>      <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">Mutex</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">clients</span> <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#f92672">*</span><span style="color:#a6e22e">client</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">rate</span>    <span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limit</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">burst</span>   <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ttl</span>     <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewStore</span>(<span style="color:#a6e22e">r</span> <span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limit</span>, <span style="color:#a6e22e">burst</span> <span style="color:#66d9ef">int</span>, <span style="color:#a6e22e">ttl</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">Store</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">s</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Store</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">clients</span>: make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#f92672">*</span><span style="color:#a6e22e">client</span>),
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">rate</span>:    <span style="color:#a6e22e">r</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">burst</span>:   <span style="color:#a6e22e">burst</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ttl</span>:     <span style="color:#a6e22e">ttl</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">cleanup</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">s</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Limiter returns the limiter for key, creating it on first use.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">s</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Store</span>) <span style="color:#a6e22e">Limiter</span>(<span style="color:#a6e22e">key</span> <span style="color:#66d9ef">string</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limiter</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">mu</span>.<span style="color:#a6e22e">Lock</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">mu</span>.<span style="color:#a6e22e">Unlock</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">c</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">clients</span>[<span style="color:#a6e22e">key</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">c</span> = <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">client</span>{<span style="color:#a6e22e">limiter</span>: <span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">NewLimiter</span>(<span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">rate</span>, <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">burst</span>)}
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">clients</span>[<span style="color:#a6e22e">key</span>] = <span style="color:#a6e22e">c</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">lastSeen</span> = <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">limiter</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">s</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Store</span>) <span style="color:#a6e22e">cleanup</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ticker</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">NewTicker</span>(<span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">ttl</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">ticker</span>.<span style="color:#a6e22e">Stop</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">ticker</span>.<span style="color:#a6e22e">C</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">mu</span>.<span style="color:#a6e22e">Lock</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">c</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">clients</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Since</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">lastSeen</span>) &gt; <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">ttl</span> {
</span></span><span style="display:flex;"><span>                delete(<span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">clients</span>, <span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">mu</span>.<span style="color:#a6e22e">Unlock</span>()
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The mutex is not optional. A bare <code>map[string]*rate.Limiter</code> written from concurrent handlers is a textbook data race, and Go&rsquo;s runtime will happily crash the process with <code>concurrent map writes</code> — the same failure I dug into in <a href="/posts/concurrent-map-writing-and-reading-in-go/">concurrent map writing and reading in Go</a>.</p>
<p>Two design notes. <code>sync.Map</code> is not a better fit here: it is optimised for read-mostly workloads with stable keys, and this map is written on every new client. And an unbounded map is a memory-exhaustion vector if the key is attacker-controlled — hence the TTL. For a hard cap, put an LRU in front of it.</p>
<h2 id="the-middleware">The Middleware</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Middleware</span>(<span style="color:#a6e22e">store</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Store</span>) <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span>) <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">next</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span>) <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">limiter</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">store</span>.<span style="color:#a6e22e">Limiter</span>(<span style="color:#a6e22e">clientKey</span>(<span style="color:#a6e22e">r</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Reserve, rather than Allow, so we can report Retry-After.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">res</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">limiter</span>.<span style="color:#a6e22e">Reserve</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">OK</span>() {
</span></span><span style="display:flex;"><span>                <span style="color:#75715e">// Burst is smaller than the request size; never satisfiable.</span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;rate limit misconfigured&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">delay</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">Delay</span>(); <span style="color:#a6e22e">delay</span> &gt; <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#75715e">// We are not going to wait, so give the token back.</span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">res</span>.<span style="color:#a6e22e">Cancel</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;Retry-After&#34;</span>, <span style="color:#a6e22e">strconv</span>.<span style="color:#a6e22e">Itoa</span>(int(<span style="color:#a6e22e">math</span>.<span style="color:#a6e22e">Ceil</span>(<span style="color:#a6e22e">delay</span>.<span style="color:#a6e22e">Seconds</span>()))))
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;RateLimit-Limit&#34;</span>, <span style="color:#a6e22e">strconv</span>.<span style="color:#a6e22e">Itoa</span>(<span style="color:#a6e22e">limiter</span>.<span style="color:#a6e22e">Burst</span>()))
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;RateLimit-Remaining&#34;</span>, <span style="color:#e6db74">&#34;0&#34;</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Set</span>(<span style="color:#e6db74">&#34;RateLimit-Reset&#34;</span>, <span style="color:#a6e22e">strconv</span>.<span style="color:#a6e22e">Itoa</span>(int(<span style="color:#a6e22e">math</span>.<span style="color:#a6e22e">Ceil</span>(<span style="color:#a6e22e">delay</span>.<span style="color:#a6e22e">Seconds</span>()))))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusTooManyRequests</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">NewEncoder</span>(<span style="color:#a6e22e">w</span>).<span style="color:#a6e22e">Encode</span>(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span>                    <span style="color:#e6db74">&#34;error&#34;</span>: <span style="color:#e6db74">&#34;rate limit exceeded&#34;</span>,
</span></span><span style="display:flex;"><span>                })
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">next</span>.<span style="color:#a6e22e">ServeHTTP</span>(<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>        })
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>res.Cancel()</code> is the line people forget. <code>Reserve</code> takes the token immediately; if you then decide not to wait, cancelling returns it to the bucket. Skip it and every rejected request still consumes budget, so a client that trips the limit stays locked out far longer than intended.</p>
<p>This plugs into any router the same way as the handlers in my <a href="/posts/go-middleware-example/">Go middleware example</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">store</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ratelimit</span>.<span style="color:#a6e22e">NewStore</span>(<span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limit</span>(<span style="color:#ae81ff">10</span>), <span style="color:#ae81ff">20</span>, <span style="color:#ae81ff">10</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Minute</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">chi</span>.<span style="color:#a6e22e">NewRouter</span>()
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Use</span>(<span style="color:#a6e22e">ratelimit</span>.<span style="color:#a6e22e">Middleware</span>(<span style="color:#a6e22e">store</span>))
</span></span></code></pre></div><h2 id="choosing-the-client-key">Choosing the Client Key</h2>
<p>This is where rate limiting is usually got wrong, and it is worth more thought than the algorithm.</p>
<p><strong>Authenticated requests: key on the identity.</strong> An API key or user ID is stable, meaningful, and cannot be spoofed once you have verified the token. If you are issuing JWTs — as in <a href="/posts/user-authentication-with-go-using-jwt-token/">user authentication in Go Echo with JWT</a> — the subject claim is your key.</p>
<p><strong>Anonymous requests: key on the IP, carefully.</strong> <code>r.RemoteAddr</code> behind a proxy is the proxy&rsquo;s address, so every user shares one bucket. But blindly trusting <code>X-Forwarded-For</code> is worse: it is a client-supplied header, and anyone can put whatever they like in it to get a fresh bucket per request.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">clientKey</span>(<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Authenticated callers are keyed on identity.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">userID</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">UserFrom</span>(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Context</span>()); <span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;user:&#34;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">userID</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Only trust the proxy header if the request came from our proxy,</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// and take the address the proxy appended — the rightmost hop.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ip</span>, <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">net</span>.<span style="color:#a6e22e">SplitHostPort</span>(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">RemoteAddr</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">isTrustedProxy</span>(<span style="color:#a6e22e">ip</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">xff</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Header</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;X-Forwarded-For&#34;</span>); <span style="color:#a6e22e">xff</span> <span style="color:#f92672">!=</span> <span style="color:#e6db74">&#34;&#34;</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">parts</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Split</span>(<span style="color:#a6e22e">xff</span>, <span style="color:#e6db74">&#34;,&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">ip</span> = <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">TrimSpace</span>(<span style="color:#a6e22e">parts</span>[len(<span style="color:#a6e22e">parts</span>)<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>])
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;ip:&#34;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">ip</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The rightmost entry is the one your own proxy added; everything to its left came from the client and is unverifiable. If your platform provides a trusted header — Cloudflare&rsquo;s <code>CF-Connecting-IP</code>, or the standard <code>Forwarded</code> from a proxy you control — prefer it.</p>
<p>One more refinement: not all endpoints are equal. <code>POST /reports/export</code> might cost a hundred times what <code>GET /health</code> does. <code>AllowN</code> and <code>ReserveN</code> let you charge by cost:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">cost</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Method</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">MethodPost</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">HasPrefix</span>(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">URL</span>.<span style="color:#a6e22e">Path</span>, <span style="color:#e6db74">&#34;/reports&#34;</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">cost</span> = <span style="color:#ae81ff">25</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">res</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">limiter</span>.<span style="color:#a6e22e">ReserveN</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>(), <span style="color:#a6e22e">cost</span>)
</span></span></code></pre></div><p>Just keep the burst at least as large as your most expensive operation, or <code>res.OK()</code> returns false forever and that endpoint becomes permanently unreachable.</p>
<h2 id="limiting-yourself-too">Limiting Yourself, Too</h2>
<p>Rate limiting is not only defensive. When you are the client of somebody else&rsquo;s API, respecting their limit proactively beats absorbing 429s and retrying. <code>Wait</code> is built for this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Client</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">http</span>    <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Client</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">limiter</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limiter</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewClient</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">Client</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Client</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">http</span>:    <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Client</span>{<span style="color:#a6e22e">Timeout</span>: <span style="color:#ae81ff">10</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>},
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// The upstream allows 5 requests/second; stay under it.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">limiter</span>: <span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">NewLimiter</span>(<span style="color:#ae81ff">5</span>, <span style="color:#ae81ff">5</span>),
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">c</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Client</span>) <span style="color:#a6e22e">Do</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">req</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) (<span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Response</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Blocks until a token is available, or ctx is cancelled.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">limiter</span>.<span style="color:#a6e22e">Wait</span>(<span style="color:#a6e22e">ctx</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;rate limiter: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Do</span>(<span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">WithContext</span>(<span style="color:#a6e22e">ctx</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>Wait</code> returns an error if the context is cancelled or if its deadline arrives before a token would — so a caller that has already given up never sits in the queue. That is the <a href="/posts/understanding-golang-context/">context</a> machinery doing exactly what it is for.</p>
<p>This composes neatly with a bounded worker pool: the pool caps how many requests are <em>in flight</em>, the limiter caps how many <em>start per second</em>. They constrain different things and you usually want both, as I covered in <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools in Go with errgroup</a>.</p>
<h2 id="where-this-approach-stops-working">Where This Approach Stops Working</h2>
<p>Be honest about the limits of an in-process limiter.</p>
<p><strong>It is per instance.</strong> Three replicas with a limit of 10/s allow 30/s in total, and a client bouncing between them gets a fresh bucket each time. For a real global limit you need shared state — Redis with a Lua script that does the token accounting atomically, or a limiter at the edge.</p>
<p><strong>It is lost on restart.</strong> Every deploy resets every bucket. Usually fine; occasionally not.</p>
<p><strong>It costs you a request.</strong> The request still reaches your process, gets routed, and allocates before being rejected. Under a genuine flood, that is exactly the work you cannot afford — which is why volumetric protection belongs at the CDN or load balancer, not in your handler.</p>
<p>My rule of thumb: <strong>application limits enforce fairness and per-plan quotas; edge limits absorb abuse.</strong> They solve different problems and you want both. The nginx layer is a natural place for the coarse one, and it can be surprisingly nuanced — the <a href="/posts/how-to-make-nginx-cookie-aware/">cookie-aware caching</a> tricks work the same way for keying <code>limit_req</code> zones.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">limit_req_zone</span> $binary_remote_addr <span style="color:#e6db74">zone=api:10m</span> <span style="color:#e6db74">rate=100r/s</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">location</span> <span style="color:#e6db74">/api/</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">limit_req</span> <span style="color:#e6db74">zone=api</span> <span style="color:#e6db74">burst=200</span> <span style="color:#e6db74">nodelay</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://backend</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="testing-it">Testing It</h2>
<p>Rate limiting is easy to test badly, because <code>time.Now()</code> is involved. Keep the rates small and explicit rather than sleeping through real seconds:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestLimiterRejectsBurst</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">store</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ratelimit</span>.<span style="color:#a6e22e">NewStore</span>(<span style="color:#a6e22e">rate</span>.<span style="color:#a6e22e">Limit</span>(<span style="color:#ae81ff">1</span>), <span style="color:#ae81ff">3</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Minute</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">h</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ratelimit</span>.<span style="color:#a6e22e">Middleware</span>(<span style="color:#a6e22e">store</span>)(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) { <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusOK</span>) },
</span></span><span style="display:flex;"><span>    ))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">codes</span> <span style="color:#f92672">:=</span> make([]<span style="color:#66d9ef">int</span>, <span style="color:#ae81ff">5</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">codes</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">req</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">httptest</span>.<span style="color:#a6e22e">NewRequest</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">MethodGet</span>, <span style="color:#e6db74">&#34;/&#34;</span>, <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">req</span>.<span style="color:#a6e22e">RemoteAddr</span> = <span style="color:#e6db74">&#34;203.0.113.7:1234&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">rec</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">httptest</span>.<span style="color:#a6e22e">NewRecorder</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">ServeHTTP</span>(<span style="color:#a6e22e">rec</span>, <span style="color:#a6e22e">req</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">codes</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">rec</span>.<span style="color:#a6e22e">Code</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Burst of 3 succeeds, the rest are rejected.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">want</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">int</span>{<span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">429</span>, <span style="color:#ae81ff">429</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">slices</span>.<span style="color:#a6e22e">Equal</span>(<span style="color:#a6e22e">codes</span>, <span style="color:#a6e22e">want</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;got %v, want %v&#34;</span>, <span style="color:#a6e22e">codes</span>, <span style="color:#a6e22e">want</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Then confirm the behaviour under real load before you trust the number. Pointing a load test at the endpoint and watching the ratio of 200s to 429s tells you whether your limit matches the traffic you actually get — the setup in <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">an easy way to load test your web apps</a> is enough for this.</p>
<h2 id="checklist">Checklist</h2>
<ul>
<li>Per-client limiters, not one global bucket, with a global one as backstop.</li>
<li>Key on identity when authenticated; on a <em>verified</em> IP otherwise.</li>
<li>Evict idle limiters so the map cannot grow without bound.</li>
<li><code>res.Cancel()</code> whenever you reject instead of waiting.</li>
<li>Send <code>Retry-After</code> and <code>RateLimit-*</code> headers so good clients can behave.</li>
<li>Burst at least as large as your most expensive weighted operation.</li>
<li><code>Wait(ctx)</code> on the client side of other people&rsquo;s APIs.</li>
<li>Volumetric protection at the edge, fairness in the application.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p><code>golang.org/x/time/rate</code> is one of those packages that does exactly one thing and does it without ceremony. The algorithm is not the hard part — picking a sensible client key, giving tokens back when you reject, and being clear about what an in-process limiter can and cannot promise is where the real work is. Get those right and a single misbehaving script stops being everybody else&rsquo;s problem. And if the API you are the client of happens to be a model provider, an agent loop is a remarkably efficient way to find its limits — see <a href="/posts/tool-use-in-go-agent-loop/">tool use in Go</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Web Development</category>
      <category>Backend Development</category>
      <category>Security</category>
    </item>
    <item>
      <title>Worker Pools in Go: Bounded Concurrency with errgroup</title>
      <link>https://webdevstation.com/posts/worker-pools-in-go-with-errgroup/</link>
      <pubDate>Tue, 18 Aug 2026 11:20:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/worker-pools-in-go-with-errgroup/</guid>
      <description>Stop spawning unbounded goroutines. A practical guide to worker pools in Go using channels, sync.WaitGroup and errgroup.SetLimit — with error propagation,…</description>
      <content:encoded><![CDATA[<p>Goroutines are so cheap that the first concurrent version of anything usually looks like <code>for _, item := range items { go process(item) }</code>. That works beautifully with ten items. With fifty thousand it opens fifty thousand database connections, and the thing you were trying to speed up falls over instead. What you almost always want is a <em>bounded</em> pool: N things in flight, no more. Here is how I build them.</p>
<h2 id="the-problem-with-the-obvious-version">The Problem With the Obvious Version</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Do not ship this.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">fetchAll</span>(<span style="color:#a6e22e">urls</span> []<span style="color:#66d9ef">string</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">wg</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">url</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">urls</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">url</span>)
</span></span><span style="display:flex;"><span>        }()
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Three separate problems:</p>
<ol>
<li><strong>No limit.</strong> <code>len(urls)</code> concurrent requests. The remote service rate-limits you, or your file descriptors run out, or both.</li>
<li><strong>No errors.</strong> <code>fetch</code> returns one and it goes nowhere.</li>
<li><strong>No cancellation.</strong> If the caller gives up, every goroutine keeps running to completion.</li>
</ol>
<p>The concurrency itself is not the mistake — the missing back pressure is.</p>
<h2 id="the-classic-channel-pool">The Classic Channel Pool</h2>
<p>The traditional shape is a jobs channel, a fixed number of workers reading from it, and a results channel:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">job</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ID</span>  <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">URL</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">result</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">JobID</span> <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Body</span>  []<span style="color:#66d9ef">byte</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Err</span>   <span style="color:#66d9ef">error</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">workerPool</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">jobs</span> []<span style="color:#a6e22e">job</span>, <span style="color:#a6e22e">workers</span> <span style="color:#66d9ef">int</span>) []<span style="color:#a6e22e">result</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">jobCh</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">job</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">resCh</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">result</span>, len(<span style="color:#a6e22e">jobs</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">wg</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#a6e22e">workers</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">workerID</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">j</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">jobCh</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">body</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">URL</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">resCh</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">result</span>{<span style="color:#a6e22e">JobID</span>: <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">Body</span>: <span style="color:#a6e22e">body</span>, <span style="color:#a6e22e">Err</span>: <span style="color:#a6e22e">err</span>}:
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>                    <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Feed the workers, stopping early if the caller cancels.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">defer</span> close(<span style="color:#a6e22e">jobCh</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">j</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">jobs</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">jobCh</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">j</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>    close(<span style="color:#a6e22e">resCh</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">out</span> <span style="color:#f92672">:=</span> make([]<span style="color:#a6e22e">result</span>, <span style="color:#ae81ff">0</span>, len(<span style="color:#a6e22e">jobs</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">resCh</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">out</span> = append(<span style="color:#a6e22e">out</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">out</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This is worth understanding because you will read it in a lot of codebases, and because it shows the mechanics plainly. Note two things that are easy to get wrong:</p>
<ul>
<li><strong><code>close(jobCh)</code> is the workers&rsquo; exit signal.</strong> <code>for j := range jobCh</code> ends when the channel closes. Forget the close and <code>wg.Wait()</code> blocks forever.</li>
<li><strong>Every channel send is paired with <code>&lt;-ctx.Done()</code>.</strong> Without that, a worker sending to a full <code>resCh</code> that nobody is reading leaks for the lifetime of the process.</li>
</ul>
<p>It is also about forty lines to do something the standard extended library does in eight.</p>
<h2 id="the-errgroup-version">The errgroup Version</h2>
<p><code>golang.org/x/sync/errgroup</code> is a <code>sync.WaitGroup</code> that also collects the first error and cancels its siblings. <code>SetLimit</code> turns it into a bounded pool:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;golang.org/x/sync/errgroup&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">fetchAll</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">urls</span> []<span style="color:#66d9ef">string</span>) ([][]<span style="color:#66d9ef">byte</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">bodies</span> <span style="color:#f92672">:=</span> make([][]<span style="color:#66d9ef">byte</span>, len(<span style="color:#a6e22e">urls</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">ctx</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">errgroup</span>.<span style="color:#a6e22e">WithContext</span>(<span style="color:#a6e22e">ctx</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">SetLimit</span>(<span style="color:#ae81ff">10</span>) <span style="color:#75715e">// at most 10 in flight</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">url</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">urls</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">body</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">url</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;fetch %s: %w&#34;</span>, <span style="color:#a6e22e">url</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Each goroutine owns exactly one slot: no mutex needed.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">bodies</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">body</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>        })
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Wait</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">bodies</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>That is the whole pool. Behaviour worth knowing:</p>
<ul>
<li><strong><code>g.Go</code> blocks</strong> once the limit is reached, until a slot frees up. The <code>for</code> loop becomes its own back pressure — no jobs channel needed.</li>
<li><strong><code>errgroup.WithContext</code> returns a derived context</strong> that is cancelled the moment any goroutine returns a non-nil error. Shadowing <code>ctx</code> with it, as above, is deliberate: every <code>fetch</code> gets the cancellable one.</li>
<li><strong><code>g.Wait()</code> returns the first error</strong>, and waits for the rest regardless. Later errors are discarded — if you need all of them, collect them yourself (<code>errors.Join</code> is a good fit, see <a href="/posts/error-handling-in-go/">error handling in Go</a>).</li>
<li><strong>Writing to <code>bodies[i]</code></strong> is safe without a mutex because each goroutine writes one distinct element. Different elements of a slice are different memory; that is not a data race. Appending to a shared slice, or writing to a shared map, absolutely is — see <a href="/posts/concurrent-map-writing-and-reading-in-go/">concurrent map writing and reading in Go</a> for what that failure looks like.</li>
</ul>
<h3 id="a-note-on-loop-variables">A Note on Loop Variables</h3>
<p>The example above relies on Go 1.22&rsquo;s per-iteration loop variables. Before 1.22, <code>i</code> and <code>url</code> were shared across iterations and every goroutine would see the final values — the single most common concurrency bug in Go. On older versions you must copy them:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">url</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">urls</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">url</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">url</span> <span style="color:#75715e">// required before Go 1.22</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> { <span style="color:#75715e">/* ... */</span> })
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Since Go 1.22 the copy is unnecessary. Leaving it in is harmless, and I still write it in code that must build on older toolchains. The loop semantics change was one of the more consequential recent additions to the language — I touched on the surrounding rules in <a href="/posts/mastering-for-loops-in-go/">mastering Golang for loops</a>.</p>
<h2 id="streaming-results-instead-of-preallocating">Streaming Results Instead of Preallocating</h2>
<p>Indexing into a preallocated slice only works when you know the number of jobs up front. For a stream, send results down a channel and read them concurrently:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">processStream</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">in</span> <span style="color:#f92672">&lt;-</span><span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">job</span>, <span style="color:#a6e22e">workers</span> <span style="color:#66d9ef">int</span>) (<span style="color:#f92672">&lt;-</span><span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">result</span>, <span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">out</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">result</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">ctx</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">errgroup</span>.<span style="color:#a6e22e">WithContext</span>(<span style="color:#a6e22e">ctx</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">SetLimit</span>(<span style="color:#a6e22e">workers</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Closing `out` after every worker has finished lets the consumer</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// range over it and stop naturally.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">defer</span> close(<span style="color:#a6e22e">out</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">j</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">in</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">body</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">URL</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;job %d: %w&#34;</span>, <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">out</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">result</span>{<span style="color:#a6e22e">JobID</span>: <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">Body</span>: <span style="color:#a6e22e">body</span>}:
</span></span><span style="display:flex;"><span>                    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>                    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Err</span>()
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            })
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>    }()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">out</span>, <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Wait</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The consumer ranges over <code>out</code> and then calls the returned function to get the error:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">results</span>, <span style="color:#a6e22e">wait</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">processStream</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">jobs</span>, <span style="color:#ae81ff">8</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">results</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">save</span>(<span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">wait</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;process stream: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>g.Wait()</code> is safe to call more than once — subsequent calls return the same error immediately.</p>
<h2 id="picking-the-limit">Picking the Limit</h2>
<p>There is no universal number, but there is a reliable way to think about it.</p>
<p><strong>CPU-bound work</strong> — parsing, hashing, image resizing, compression — saturates at roughly the number of cores. More goroutines just add scheduling overhead:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">SetLimit</span>(<span style="color:#a6e22e">runtime</span>.<span style="color:#a6e22e">GOMAXPROCS</span>(<span style="color:#ae81ff">0</span>))
</span></span></code></pre></div><p><strong>I/O-bound work</strong> — HTTP calls, database queries, object storage — spends most of its time waiting, so the useful limit is much higher. But it is not &ldquo;as high as possible&rdquo;: it is whatever the <em>slowest downstream dependency</em> can absorb. If your database pool has 25 connections, a pool of 200 workers means 175 goroutines queueing on a mutex inside <code>database/sql</code> while your latency graph climbs.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Match the constraint that actually binds.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">SetLimit</span>(<span style="color:#a6e22e">db</span>.<span style="color:#a6e22e">Stats</span>().<span style="color:#a6e22e">MaxOpenConnections</span>)
</span></span></code></pre></div><p>For outbound HTTP, remember that Go&rsquo;s default transport keeps only <strong>2</strong> idle connections per host. Exceed that and you are opening a fresh TCP connection — plus a TLS handshake — for each extra request:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">transport</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">DefaultTransport</span>.(<span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Transport</span>).<span style="color:#a6e22e">Clone</span>()
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">transport</span>.<span style="color:#a6e22e">MaxIdleConnsPerHost</span> = <span style="color:#ae81ff">50</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">transport</span>.<span style="color:#a6e22e">MaxConnsPerHost</span> = <span style="color:#ae81ff">50</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">client</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Client</span>{<span style="color:#a6e22e">Transport</span>: <span style="color:#a6e22e">transport</span>, <span style="color:#a6e22e">Timeout</span>: <span style="color:#ae81ff">10</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>}
</span></span></code></pre></div><p>Then set the pool limit to match. Tuning one without the other gets you nothing.</p>
<p>Whatever you pick, measure it. Run the job at 5, 10, 25 and 50 and look at total wall time <em>and</em> downstream latency — the fastest setting for your batch is often the one that makes everything else on the system slower. Load testing is the honest way to find out; I wrote about a lightweight setup in <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">an easy way to load test your web apps</a>.</p>
<h2 id="not-every-goroutine-belongs-in-a-pool">Not Every Goroutine Belongs in a Pool</h2>
<p>A pool is for a <em>batch of similar work</em>. Some situations want something else:</p>
<p><strong>Waiting on several different things at once</strong> — no limit needed, just a group:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">ctx</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">errgroup</span>.<span style="color:#a6e22e">WithContext</span>(<span style="color:#a6e22e">ctx</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">orders</span> []<span style="color:#a6e22e">Order</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() (<span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>) { <span style="color:#a6e22e">user</span>, <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">loadUser</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">id</span>); <span style="color:#66d9ef">return</span> })
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() (<span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>) { <span style="color:#a6e22e">orders</span>, <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">loadOrders</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">id</span>); <span style="color:#66d9ef">return</span> })
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Wait</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;load profile: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Three sequential 100ms calls become one 100ms call. This is the highest-value use of <code>errgroup</code> in a typical request handler, and it needs no pool at all.</p>
<p><strong>Work that must happen in order</strong> — a pool is the wrong shape entirely; you want a single consumer, like the <a href="/posts/simple-queue-implementation-in-golang/">simple queue implementation</a> I wrote about earlier.</p>
<p><strong>Fire-and-forget background work</strong> — resist it. A goroutine started in a request handler outlives the request, holds whatever it captured, and will be killed mid-flight when the process shuts down. If it matters, it belongs in a durable queue; if it does not, do it inline. The same reasoning applies at shutdown time, which I covered in <a href="/posts/graceful-shutdown-in-go-web-services/">graceful shutdown in Go web services</a>.</p>
<p><strong>Only trying if there is capacity</strong> — <code>TryGo</code> starts the goroutine only if a slot is free, and reports whether it did:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">TryGo</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> { <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">prefetch</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">url</span>) }) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Pool is busy; skip this optional work rather than blocking.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">metrics</span>.<span style="color:#a6e22e">PrefetchSkipped</span>.<span style="color:#a6e22e">Inc</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="pitfalls">Pitfalls</h2>
<table>
	<thead>
			<tr>
					<th>Pitfall</th>
					<th>Fix</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>g.Go</code> never returns</td>
					<td>Something inside blocks forever — give every call a context and a timeout</td>
			</tr>
			<tr>
					<td>Results come back in the wrong order</td>
					<td>Index into a preallocated slice, or sort by an explicit sequence number</td>
			</tr>
			<tr>
					<td><code>panic</code> in a worker kills the process</td>
					<td>Recover inside the goroutine and convert it to an error</td>
			</tr>
			<tr>
					<td>Errors vanish</td>
					<td>Return them from <code>g.Go</code>; do not just log them</td>
			</tr>
			<tr>
					<td><code>SetLimit</code> called after <code>g.Go</code></td>
					<td>Panics — set the limit before starting any work</td>
			</tr>
			<tr>
					<td>Unbounded jobs channel eats memory</td>
					<td>Use an unbuffered channel, or let <code>SetLimit</code> provide the back pressure</td>
			</tr>
			<tr>
					<td>Shared map written from workers</td>
					<td><code>sync.Map</code>, a mutex, or per-worker maps merged at the end</td>
			</tr>
	</tbody>
</table>
<p>Panic recovery is worth spelling out, because one bad input taking down the whole process is a common way for batch jobs to fail:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() (<span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> recover(); <span style="color:#a6e22e">r</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;panic processing %s: %v&#34;</span>, <span style="color:#a6e22e">url</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">process</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">url</span>)
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>The named return value <code>err</code> is what makes this work — the deferred function assigns to it after the panic is recovered.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The pattern is small: pick a limit that matches your real bottleneck, use <code>errgroup.WithContext</code> so failures cancel their siblings, return errors instead of logging them, and give every blocking operation a context. Most of the time that is eight lines and no channel plumbing at all. Save the hand-rolled channel pool for the cases where you genuinely need to stream results or vary the shape of the work — and when you do write one, remember to close the jobs channel.</p>
<p>One place this pattern turns up more than you would expect: running the tool calls an LLM asks for, several at a time but not unboundedly — see <a href="/posts/tool-use-in-go-agent-loop/">tool use in Go</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
      <category>Performance Optimization</category>
    </item>
    <item>
      <title>Graceful Shutdown in Go Web Services: Stop Dropping Requests on Deploy</title>
      <link>https://webdevstation.com/posts/graceful-shutdown-in-go-web-services/</link>
      <pubDate>Tue, 11 Aug 2026 18:40:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/graceful-shutdown-in-go-web-services/</guid>
      <description>How to shut down a Go HTTP server without dropping in-flight requests: signal.NotifyContext, Server.Shutdown, draining background workers, and the timeouts that make…</description>
      <content:encoded><![CDATA[<p>The first time I deployed a Go service behind a rolling update, our error dashboard lit up on every single release. Nothing was broken — the new version was fine, the old version was fine. The problem was the half-second in between, where the old process died mid-request and a few dozen users got a connection reset. Fixing it took about twenty lines of code, and I have copied those twenty lines into every service since.</p>
<h2 id="what-actually-happens-on-shutdown">What Actually Happens on Shutdown</h2>
<p>When your orchestrator wants a container gone, it sends <code>SIGTERM</code> and starts a countdown. If the process is still alive when the countdown ends, it gets <code>SIGKILL</code>, which cannot be caught.</p>
<p>A Go program with no signal handling takes the default action for <code>SIGTERM</code>: immediate termination. Every open connection is severed. Any request that was 90% done is simply gone — the client sees a reset, your retry budget takes the hit, and if that request was a payment you now have a support ticket.</p>
<p>Graceful shutdown means using the window between <code>SIGTERM</code> and <code>SIGKILL</code> to:</p>
<ol>
<li>Stop accepting new connections.</li>
<li>Let in-flight requests finish.</li>
<li>Drain background workers.</li>
<li>Close databases, caches and queues.</li>
<li>Exit before the countdown runs out.</li>
</ol>
<h2 id="the-twenty-lines">The Twenty Lines</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;context&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;errors&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;log/slog&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;os&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;os/signal&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;syscall&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">srv</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Server</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Addr</span>:              <span style="color:#e6db74">&#34;:8080&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Handler</span>:           <span style="color:#a6e22e">newRouter</span>(),
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ReadHeaderTimeout</span>: <span style="color:#ae81ff">5</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ReadTimeout</span>:       <span style="color:#ae81ff">15</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">WriteTimeout</span>:      <span style="color:#ae81ff">30</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">IdleTimeout</span>:       <span style="color:#ae81ff">60</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ctx is cancelled the first time we receive SIGINT or SIGTERM.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">stop</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">signal</span>.<span style="color:#a6e22e">NotifyContext</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(),
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Interrupt</span>, <span style="color:#a6e22e">syscall</span>.<span style="color:#a6e22e">SIGTERM</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">stop</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;listening&#34;</span>, <span style="color:#e6db74">&#34;addr&#34;</span>, <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">Addr</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// ListenAndServe always returns a non-nil error; ErrServerClosed</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// is the expected one after Shutdown.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">ListenAndServe</span>(); !<span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ErrServerClosed</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#e6db74">&#34;listen failed&#34;</span>, <span style="color:#e6db74">&#34;err&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Exit</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">stop</span>() <span style="color:#75715e">// restore default handling: a second Ctrl-C now kills us</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;shutdown signal received, draining&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">shutdownCtx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithTimeout</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#ae81ff">20</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">cancel</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">Shutdown</span>(<span style="color:#a6e22e">shutdownCtx</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#e6db74">&#34;graceful shutdown failed, forcing close&#34;</span>, <span style="color:#e6db74">&#34;err&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">Close</span>()
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;shutdown complete&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>That is the whole pattern. A few details are load-bearing:</p>
<p><strong><code>signal.NotifyContext</code> instead of a channel.</strong> Since Go 1.16 this gives you a <code>context.Context</code> that cancels on the listed signals, which composes with everything else that already takes a context. If contexts and cancellation are new to you, <a href="/posts/understanding-golang-context/">understanding Golang context</a> is the background reading.</p>
<p><strong>Calling <code>stop()</code> after the first signal.</strong> It restores the default signal behaviour, so an impatient operator pressing Ctrl-C a second time gets an immediate exit instead of being ignored.</p>
<p><strong>A fresh context for <code>Shutdown</code>.</strong> Deriving it from <code>ctx</code> would be a bug: <code>ctx</code> is already cancelled, so <code>Shutdown</code> would return instantly and drain nothing.</p>
<p><strong>Checking for <code>ErrServerClosed</code>.</strong> <code>ListenAndServe</code> returns it on a clean shutdown. Treating that as a failure produces a scary log line on every normal deploy.</p>
<h2 id="what-shutdown-does-and-does-not-do">What Shutdown Does and Does Not Do</h2>
<p><code>Server.Shutdown</code> closes all open listeners, closes idle connections, and then waits for active ones to become idle. It returns when everything is drained or when its context expires — whichever comes first.</p>
<p>What it does <strong>not</strong> cover:</p>
<ul>
<li><strong>Hijacked connections</strong>, including WebSockets. <code>Shutdown</code> does not wait for them, and it does not close them. You have to track and close them yourself.</li>
<li><strong>Background goroutines</strong> you started outside the request path. Nothing knows about them.</li>
<li><strong>Long-polling or streaming responses.</strong> These are &ldquo;active&rdquo; for as long as they stream, so they will hold the drain open until your timeout fires.</li>
</ul>
<p>For WebSockets, the usual approach is to register a callback that broadcasts a close frame:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">RegisterOnShutdown</span>(<span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">hub</span>.<span style="color:#a6e22e">CloseAll</span>(<span style="color:#a6e22e">websocket</span>.<span style="color:#a6e22e">CloseServiceRestart</span>, <span style="color:#e6db74">&#34;server restarting&#34;</span>)
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p><code>RegisterOnShutdown</code> callbacks run in their own goroutines as soon as <code>Shutdown</code> starts, so they get the whole drain window to do their work.</p>
<h2 id="draining-background-workers-too">Draining Background Workers Too</h2>
<p>Most real services do more than serve HTTP. If you have consumers, cron loops, or a queue like the one in <a href="/posts/simple-queue-implementation-in-golang/">my simple queue implementation</a>, they need to finish too. <code>errgroup</code> keeps this readable:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;golang.org/x/sync/errgroup&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">run</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">srv</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Server</span>{<span style="color:#a6e22e">Addr</span>: <span style="color:#e6db74">&#34;:8080&#34;</span>, <span style="color:#a6e22e">Handler</span>: <span style="color:#a6e22e">newRouter</span>()}
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">queue</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">NewQueue</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">gCtx</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">errgroup</span>.<span style="color:#a6e22e">WithContext</span>(<span style="color:#a6e22e">ctx</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 1. Serve HTTP.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">ListenAndServe</span>(); !<span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ErrServerClosed</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;http server: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 2. Consume the queue until the group context is cancelled.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">queue</span>.<span style="color:#a6e22e">Consume</span>(<span style="color:#a6e22e">gCtx</span>)
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 3. When anything cancels gCtx — a signal, or a failure in another</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">//    goroutine — drain the HTTP server.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">gCtx</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">shutdownCtx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithTimeout</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#ae81ff">20</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">cancel</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">Shutdown</span>(<span style="color:#a6e22e">shutdownCtx</span>)
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">stop</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">signal</span>.<span style="color:#a6e22e">NotifyContext</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(),
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Interrupt</span>, <span style="color:#a6e22e">syscall</span>.<span style="color:#a6e22e">SIGTERM</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">stop</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">run</span>(<span style="color:#a6e22e">ctx</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#e6db74">&#34;service stopped&#34;</span>, <span style="color:#e6db74">&#34;err&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Exit</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;service stopped cleanly&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The nice property here is that failure propagates in both directions. A signal drains the HTTP server; a fatal error in the queue consumer also drains the HTTP server, because <code>errgroup.WithContext</code> cancels <code>gCtx</code> as soon as any goroutine returns an error.</p>
<p>The order of shutdown matters, and it is the reverse of startup: stop accepting work, finish what you have, then close the things that work depends on. Closing your database pool before draining HTTP guarantees a burst of errors from requests that were nearly done.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// After g.Wait() returns, nothing is still using these.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">db</span>.<span style="color:#a6e22e">Close</span>()
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Close</span>()
</span></span></code></pre></div><h2 id="the-load-balancer-problem">The Load Balancer Problem</h2>
<p>Here is the part that surprises people: even a perfectly graceful process can drop requests.</p>
<p>Between the moment your pod receives <code>SIGTERM</code> and the moment the load balancer stops sending it traffic, there is a gap. Endpoint updates propagate asynchronously — through the API server, to kube-proxy or an ingress controller, and finally to the actual routing table. During that gap the balancer is still sending new connections to a server that has already closed its listener. Those connections are refused.</p>
<p>The fix is to keep serving for a few seconds <em>after</em> the signal arrives:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;signal received, waiting for load balancer to deregister&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Keep serving while the endpoint removal propagates.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">5</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">shutdownCtx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithTimeout</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#ae81ff">20</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">cancel</span>()
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">Shutdown</span>(<span style="color:#a6e22e">shutdownCtx</span>)
</span></span></code></pre></div><p>It feels wrong to <code>sleep</code> on purpose, but it is the standard remedy, and Kubernetes has a hook for exactly this so you do not need it in your code:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">lifecycle</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">preStop</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">exec</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">command</span>: [<span style="color:#e6db74">&#34;/bin/sh&#34;</span>, <span style="color:#e6db74">&#34;-c&#34;</span>, <span style="color:#e6db74">&#34;sleep 5&#34;</span>]
</span></span><span style="display:flex;"><span><span style="color:#f92672">terminationGracePeriodSeconds</span>: <span style="color:#ae81ff">45</span>
</span></span></code></pre></div><p><code>preStop</code> runs <em>before</em> <code>SIGTERM</code> is sent, while the pod is already being removed from the endpoints list. By the time your process sees the signal, traffic has stopped arriving.</p>
<p>Whichever way you do it, keep the arithmetic straight:</p>
<pre tabindex="0"><code>preStop sleep (5s) + drain timeout (20s) + close time (2s) &lt; terminationGracePeriodSeconds (45s)
</code></pre><p>If the total exceeds the grace period, you get <code>SIGKILL</code> mid-drain and you are back where you started. Give yourself real headroom — the default grace period is 30 seconds, which is not much once a slow request is in flight.</p>
<p>A readiness probe that starts failing on <code>SIGTERM</code> achieves the same thing more precisely:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">ready</span> <span style="color:#a6e22e">atomic</span>.<span style="color:#a6e22e">Bool</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">init</span>() { <span style="color:#a6e22e">ready</span>.<span style="color:#a6e22e">Store</span>(<span style="color:#66d9ef">true</span>) }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// /readyz</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">readyz</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">ready</span>.<span style="color:#a6e22e">Load</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;shutting down&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusServiceUnavailable</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusOK</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// On signal, before draining:</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ready</span>.<span style="color:#a6e22e">Store</span>(<span style="color:#66d9ef">false</span>)
</span></span></code></pre></div><p>Keep <code>/healthz</code> (liveness) returning 200 the whole time — if liveness fails during shutdown, the kubelet may kill the container instead of letting it drain.</p>
<h2 id="docker-gotchas">Docker Gotchas</h2>
<p>Two container-level mistakes will silently defeat everything above.</p>
<p><strong>Shell-form <code>CMD</code> makes your process PID 2.</strong> Written as <code>CMD ./server</code>, Docker runs <code>/bin/sh -c ./server</code>. The shell is PID 1, receives <code>SIGTERM</code>, and does not forward it. Your server never hears a thing.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span><span style="color:#75715e"># Wrong — signals stop at the shell</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">CMD</span> ./server<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"># Right — exec form, your binary is PID 1</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">CMD</span> [<span style="color:#e6db74">&#34;./server&#34;</span>]<span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p><strong><code>docker stop</code> waits 10 seconds by default.</strong> If your drain window is 20 seconds, you will be killed halfway through. Raise it: <code>docker stop -t 45</code>, or <code>stop_grace_period: 45s</code> in Compose.</p>
<h2 id="verifying-it-works">Verifying It Works</h2>
<p>Do not take it on faith — this is easy to test. Add a slow endpoint, start a request, and signal the process mid-flight:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">mux</span>.<span style="color:#a6e22e">HandleFunc</span>(<span style="color:#e6db74">&#34;/slow&#34;</span>, <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">After</span>(<span style="color:#ae81ff">10</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>):
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Write</span>([]byte(<span style="color:#e6db74">&#34;finished\n&#34;</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Context</span>().<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// The client gave up; Shutdown does not cancel request contexts.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./server &amp;
</span></span><span style="display:flex;"><span>curl -s localhost:8080/slow &amp;     <span style="color:#75715e"># starts a 10s request</span>
</span></span><span style="display:flex;"><span>sleep <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>kill -TERM %1                     <span style="color:#75715e"># signal while it is in flight</span>
</span></span></code></pre></div><p>A correct implementation prints <code>finished</code> after ten seconds and then exits. A broken one prints nothing and the curl reports a reset connection.</p>
<p>For the same check under real traffic, point a load test at the service and restart it mid-run — the technique from <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">an easy way to load test your web apps</a> works well here. With graceful shutdown in place your error count during a restart should be exactly zero; without it, you will see the exact number of requests that were in flight.</p>
<p>Note what <code>Shutdown</code> does <em>not</em> do: it does not cancel <code>r.Context()</code> for in-flight requests. That is deliberate — the request should be allowed to complete. It also means a handler with no timeout of its own can hold the drain open until your shutdown context expires, which is why the <code>WriteTimeout</code> in the first example matters.</p>
<h2 id="checklist">Checklist</h2>
<ul>
<li><code>signal.NotifyContext</code> for <code>SIGINT</code> and <code>SIGTERM</code>.</li>
<li><code>srv.Shutdown</code> with its own fresh, bounded context.</li>
<li><code>errors.Is(err, http.ErrServerClosed)</code> treated as success.</li>
<li>Background workers cancelled through the same context, drained before dependencies close.</li>
<li>Dependencies closed last, in reverse order of startup.</li>
<li>Readiness probe flipped to failing before the drain begins.</li>
<li><code>preStop</code> hook or a deliberate sleep to cover load balancer propagation.</li>
<li>Grace period comfortably larger than the sum of your timeouts.</li>
<li>Exec-form <code>CMD</code> in the Dockerfile.</li>
<li>A test that proves an in-flight request survives a <code>SIGTERM</code>.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Graceful shutdown is one of those features nobody notices when it works — which is precisely the point. Twenty lines in <code>main</code>, a couple of timeouts that add up correctly, and a container that actually forwards signals will turn every deploy from a small burst of errors into a non-event. It is the cheapest reliability win available to a Go service, and worth adding before the next release rather than after the next incident.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
      <category>DevOps</category>
    </item>
    <item>
      <title>Structured Logging in Go with log/slog: A Practical Guide</title>
      <link>https://webdevstation.com/posts/structured-logging-in-go-with-slog/</link>
      <pubDate>Tue, 04 Aug 2026 10:15:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/structured-logging-in-go-with-slog/</guid>
      <description>Replace fmt-style logging with Go&#39;s built-in log/slog package: JSON handlers, levels, context-aware loggers, grouped attributes and request-scoped fields, with…</description>
      <content:encoded><![CDATA[<p>For years, every Go service I wrote started with the same decision: which logging library this time? Since Go 1.21 that decision has a boring, excellent default — <code>log/slog</code> ships with the standard library, speaks JSON out of the box, and needs no dependency at all. This post is the tour I wish I had when I migrated my first service to it.</p>
<h2 id="why-structured-logs-beat-formatted-strings">Why Structured Logs Beat Formatted Strings</h2>
<p>Here is a line the old <code>log</code> package might produce:</p>
<pre tabindex="0"><code>2026/08/04 10:15:02 user 42 checkout failed after 1.2s: payment declined
</code></pre><p>It reads fine. Now try answering &ldquo;how many checkouts failed for users on the EU cluster last Tuesday between 14:00 and 15:00?&rdquo; You are writing a regular expression.</p>
<p>The same event as structured data:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{<span style="color:#f92672">&#34;time&#34;</span>:<span style="color:#e6db74">&#34;2026-08-04T10:15:02.113Z&#34;</span>,<span style="color:#f92672">&#34;level&#34;</span>:<span style="color:#e6db74">&#34;ERROR&#34;</span>,<span style="color:#f92672">&#34;msg&#34;</span>:<span style="color:#e6db74">&#34;checkout failed&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#f92672">&#34;user_id&#34;</span>:<span style="color:#ae81ff">42</span>,<span style="color:#f92672">&#34;duration_ms&#34;</span>:<span style="color:#ae81ff">1204</span>,<span style="color:#f92672">&#34;reason&#34;</span>:<span style="color:#e6db74">&#34;payment declined&#34;</span>,<span style="color:#f92672">&#34;cluster&#34;</span>:<span style="color:#e6db74">&#34;eu&#34;</span>}
</span></span></code></pre></div><p>Now it is a query. Every log aggregator — Loki, Elasticsearch, CloudWatch, BigQuery — indexes those fields directly. The point of structured logging is not prettier output; it is that your logs become a queryable dataset.</p>
<h2 id="the-smallest-useful-setup">The Smallest Useful Setup</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;log/slog&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;os&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">logger</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">NewJSONHandler</span>(<span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Stdout</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">HandlerOptions</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Level</span>: <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">LevelInfo</span>,
</span></span><span style="display:flex;"><span>    }))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Make it the package-level default so slog.Info et al. use it too.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">SetDefault</span>(<span style="color:#a6e22e">logger</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;service started&#34;</span>, <span style="color:#e6db74">&#34;port&#34;</span>, <span style="color:#ae81ff">8080</span>, <span style="color:#e6db74">&#34;env&#34;</span>, <span style="color:#e6db74">&#34;production&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Output:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{<span style="color:#f92672">&#34;time&#34;</span>:<span style="color:#e6db74">&#34;2026-08-04T10:15:02.09Z&#34;</span>,<span style="color:#f92672">&#34;level&#34;</span>:<span style="color:#e6db74">&#34;INFO&#34;</span>,<span style="color:#f92672">&#34;msg&#34;</span>:<span style="color:#e6db74">&#34;service started&#34;</span>,<span style="color:#f92672">&#34;port&#34;</span>:<span style="color:#ae81ff">8080</span>,<span style="color:#f92672">&#34;env&#34;</span>:<span style="color:#e6db74">&#34;production&#34;</span>}
</span></span></code></pre></div><p>The variadic arguments are alternating keys and values. If you prefer something the compiler can check, use typed attributes instead:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;service started&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Int</span>(<span style="color:#e6db74">&#34;port&#34;</span>, <span style="color:#ae81ff">8080</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">String</span>(<span style="color:#e6db74">&#34;env&#34;</span>, <span style="color:#e6db74">&#34;production&#34;</span>),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>Both forms are fine, and you can mix them. The typed form costs a little more typing and saves you from the classic odd-number-of-arguments bug, where a stray value ends up under the key <code>!BADKEY</code>.</p>
<p>For local development, swap the handler and keep everything else:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">handler</span> <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Handler</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Getenv</span>(<span style="color:#e6db74">&#34;ENV&#34;</span>) <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;development&#34;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">handler</span> = <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">NewTextHandler</span>(<span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Stderr</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">HandlerOptions</span>{<span style="color:#a6e22e">Level</span>: <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">LevelDebug</span>})
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">handler</span> = <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">NewJSONHandler</span>(<span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Stdout</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">HandlerOptions</span>{<span style="color:#a6e22e">Level</span>: <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">LevelInfo</span>})
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">SetDefault</span>(<span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">handler</span>))
</span></span></code></pre></div><h2 id="levels-and-changing-them-without-a-redeploy">Levels, and Changing Them Without a Redeploy</h2>
<p><code>slog</code> has four built-in levels — <code>Debug</code> (-4), <code>Info</code> (0), <code>Warn</code> (4), <code>Error</code> (8) — as plain integers, so you can define your own in between if you really need to.</p>
<p>The more useful trick is a <code>LevelVar</code>, which lets you change the level at runtime:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">logLevel</span> = new(<span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">LevelVar</span>) <span style="color:#75715e">// defaults to Info</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">SetDefault</span>(<span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">NewJSONHandler</span>(<span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Stdout</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">HandlerOptions</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Level</span>: <span style="color:#a6e22e">logLevel</span>,
</span></span><span style="display:flex;"><span>    })))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Flip to debug on demand — from a signal handler, an admin endpoint,</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// or a config watcher.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandleFunc</span>(<span style="color:#e6db74">&#34;/debug/level&#34;</span>, <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">l</span> <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Level</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">l</span>.<span style="color:#a6e22e">UnmarshalText</span>([]byte(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">FormValue</span>(<span style="color:#e6db74">&#34;level&#34;</span>))); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;bad level&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusBadRequest</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">logLevel</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">l</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Warn</span>(<span style="color:#e6db74">&#34;log level changed&#34;</span>, <span style="color:#e6db74">&#34;level&#34;</span>, <span style="color:#a6e22e">l</span>)
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Being able to turn on debug logging for two minutes on a misbehaving pod, without a deploy, has saved me more time than any other logging feature. Put that endpoint behind authentication — the <a href="/posts/how-to-control-router-access-permissions-in-go-web-apps/">router access permission patterns</a> I wrote about earlier work well for exactly this kind of internal route.</p>
<h2 id="attaching-context-with-with">Attaching Context With With</h2>
<p><code>logger.With</code> returns a new logger that carries the given attributes on every subsequent call. This is how you stop repeating yourself:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Once, at construction:</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Worker</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">log</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Logger</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewWorker</span>(<span style="color:#a6e22e">id</span> <span style="color:#66d9ef">int</span>, <span style="color:#a6e22e">queue</span> <span style="color:#66d9ef">string</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">Worker</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Worker</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>: <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Default</span>().<span style="color:#a6e22e">With</span>(<span style="color:#e6db74">&#34;worker_id&#34;</span>, <span style="color:#a6e22e">id</span>, <span style="color:#e6db74">&#34;queue&#34;</span>, <span style="color:#a6e22e">queue</span>),
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">w</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Worker</span>) <span style="color:#a6e22e">process</span>(<span style="color:#a6e22e">job</span> <span style="color:#a6e22e">Job</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Every line from this worker carries worker_id and queue automatically.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;job started&#34;</span>, <span style="color:#e6db74">&#34;job_id&#34;</span>, <span style="color:#a6e22e">job</span>.<span style="color:#a6e22e">ID</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;job finished&#34;</span>, <span style="color:#e6db74">&#34;job_id&#34;</span>, <span style="color:#a6e22e">job</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#e6db74">&#34;duration_ms&#34;</span>, <span style="color:#ae81ff">42</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>With</code> does the attribute formatting work once, at call time, rather than on every log line — so a long-lived logger built with <code>With</code> is cheaper than passing the same attributes repeatedly.</p>
<p>Use <code>WithGroup</code> when you want to namespace a block of fields:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">log</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Default</span>().<span style="color:#a6e22e">WithGroup</span>(<span style="color:#e6db74">&#34;http&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;request&#34;</span>, <span style="color:#e6db74">&#34;method&#34;</span>, <span style="color:#e6db74">&#34;GET&#34;</span>, <span style="color:#e6db74">&#34;path&#34;</span>, <span style="color:#e6db74">&#34;/users&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#75715e">// {&#34;level&#34;:&#34;INFO&#34;,&#34;msg&#34;:&#34;request&#34;,&#34;http&#34;:{&#34;method&#34;:&#34;GET&#34;,&#34;path&#34;:&#34;/users&#34;}}</span>
</span></span></code></pre></div><p>Or group inline, for one call:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;upstream call&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">String</span>(<span style="color:#e6db74">&#34;service&#34;</span>, <span style="color:#e6db74">&#34;billing&#34;</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Group</span>(<span style="color:#e6db74">&#34;response&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Int</span>(<span style="color:#e6db74">&#34;status&#34;</span>, <span style="color:#ae81ff">502</span>),
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Duration</span>(<span style="color:#e6db74">&#34;latency&#34;</span>, <span style="color:#ae81ff">1200</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Millisecond</span>),
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h2 id="request-scoped-logging">Request-Scoped Logging</h2>
<p>The pattern that pays off most in a web service: put a logger carrying the request ID into the request context, then let every layer below pull it out. If you have not used <code>context.Context</code> for this kind of request-scoped data before, <a href="/posts/understanding-golang-context/">understanding Golang context</a> covers the rules — including why the key must be an unexported type.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">logging</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;context&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;log/slog&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">ctxKey</span> <span style="color:#66d9ef">struct</span>{}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Into returns a copy of ctx carrying logger.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Into</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">logger</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Logger</span>) <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithValue</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">ctxKey</span>{}, <span style="color:#a6e22e">logger</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// From returns the logger stored in ctx, or the default logger.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">From</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Logger</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">l</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Value</span>(<span style="color:#a6e22e">ctxKey</span>{}).(<span style="color:#f92672">*</span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Logger</span>); <span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">l</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Default</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The middleware that fills it in — a close cousin of the handler-wrapping in my <a href="/posts/go-middleware-example/">Go middleware example</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Middleware</span>(<span style="color:#a6e22e">next</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span>) <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">requestID</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Header</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;X-Request-Id&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">requestID</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;&#34;</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">requestID</span> = <span style="color:#a6e22e">uuid</span>.<span style="color:#a6e22e">NewString</span>()
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Default</span>().<span style="color:#a6e22e">With</span>(
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;request_id&#34;</span>, <span style="color:#a6e22e">requestID</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;method&#34;</span>, <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Method</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;path&#34;</span>, <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">URL</span>.<span style="color:#a6e22e">Path</span>,
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">start</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">rec</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">statusRecorder</span>{<span style="color:#a6e22e">ResponseWriter</span>: <span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">status</span>: <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusOK</span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">next</span>.<span style="color:#a6e22e">ServeHTTP</span>(<span style="color:#a6e22e">rec</span>, <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">WithContext</span>(<span style="color:#a6e22e">logging</span>.<span style="color:#a6e22e">Into</span>(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Context</span>(), <span style="color:#a6e22e">log</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;request completed&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;status&#34;</span>, <span style="color:#a6e22e">rec</span>.<span style="color:#a6e22e">status</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;duration_ms&#34;</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Since</span>(<span style="color:#a6e22e">start</span>).<span style="color:#a6e22e">Milliseconds</span>(),
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// statusRecorder remembers the status code written by the handler.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">statusRecorder</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">status</span> <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">statusRecorder</span>) <span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">code</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">status</span> = <span style="color:#a6e22e">code</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">ResponseWriter</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">code</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now any function that already takes a <code>context.Context</code> can log with full request context and no extra parameters:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">s</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Store</span>) <span style="color:#a6e22e">User</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">id</span> <span style="color:#66d9ef">int64</span>) (<span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">logging</span>.<span style="color:#a6e22e">From</span>(<span style="color:#a6e22e">ctx</span>).<span style="color:#a6e22e">Debug</span>(<span style="color:#e6db74">&#34;loading user&#34;</span>, <span style="color:#e6db74">&#34;user_id&#34;</span>, <span style="color:#a6e22e">id</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ...</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Every line from that request — across every layer — shares a <code>request_id</code>. Tracing one user&rsquo;s bad afternoon becomes a single filter in your log viewer.</p>
<h2 id="redacting-secrets-with-replaceattr">Redacting Secrets With ReplaceAttr</h2>
<p><code>HandlerOptions.ReplaceAttr</code> runs on every attribute before it is written. It is the right place to enforce policy centrally instead of trusting each call site.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">sensitive</span> = <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">bool</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;password&#34;</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">&#34;token&#34;</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">&#34;authorization&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;api_key&#34;</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">&#34;secret&#34;</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">&#34;set-cookie&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">handler</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">NewJSONHandler</span>(<span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Stdout</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">HandlerOptions</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Level</span>:     <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">LevelInfo</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">AddSource</span>: <span style="color:#66d9ef">true</span>, <span style="color:#75715e">// include file:line — useful for errors</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ReplaceAttr</span>: <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">groups</span> []<span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">a</span> <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Attr</span>) <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Attr</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">sensitive</span>[<span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">ToLower</span>(<span style="color:#a6e22e">a</span>.<span style="color:#a6e22e">Key</span>)] {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">String</span>(<span style="color:#a6e22e">a</span>.<span style="color:#a6e22e">Key</span>, <span style="color:#e6db74">&#34;[REDACTED]&#34;</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Rename &#34;time&#34; to &#34;timestamp&#34; to match the rest of our pipeline.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">a</span>.<span style="color:#a6e22e">Key</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">TimeKey</span> <span style="color:#f92672">&amp;&amp;</span> len(<span style="color:#a6e22e">groups</span>) <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">a</span>.<span style="color:#a6e22e">Key</span> = <span style="color:#e6db74">&#34;timestamp&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">a</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>For types you control, implementing <code>LogValuer</code> is even better — the value redacts itself wherever it is logged:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Password</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// LogValue implements slog.LogValuer so a Password never reaches a log sink.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">Password</span>) <span style="color:#a6e22e">LogValue</span>() <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Value</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">StringValue</span>(<span style="color:#e6db74">&#34;[REDACTED]&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;login attempt&#34;</span>, <span style="color:#e6db74">&#34;user&#34;</span>, <span style="color:#e6db74">&#34;alex&#34;</span>, <span style="color:#e6db74">&#34;password&#34;</span>, <span style="color:#a6e22e">Password</span>(<span style="color:#e6db74">&#34;hunter2&#34;</span>))
</span></span><span style="display:flex;"><span><span style="color:#75715e">// {&#34;level&#34;:&#34;INFO&#34;,&#34;msg&#34;:&#34;login attempt&#34;,&#34;user&#34;:&#34;alex&#34;,&#34;password&#34;:&#34;[REDACTED]&#34;}</span>
</span></span></code></pre></div><p><code>LogValuer</code> is also how you log a struct compactly. Give your <code>User</code> type a <code>LogValue</code> that returns a group with just the ID and role, and you never accidentally dump an entire record — with its email address and hashed password — into a log line.</p>
<h2 id="logging-errors-properly">Logging Errors Properly</h2>
<p>Log the error value, not a formatted string, and let the handler decide how to render it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#e6db74">&#34;checkout failed&#34;</span>, <span style="color:#e6db74">&#34;err&#34;</span>, <span style="color:#a6e22e">err</span>, <span style="color:#e6db74">&#34;user_id&#34;</span>, <span style="color:#a6e22e">userID</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;checkout: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Two habits worth keeping:</p>
<ul>
<li><strong>Log once, at the boundary.</strong> If you log here <em>and</em> return the error, every caller up the stack logs it again. That is the same rule I covered in <a href="/posts/error-handling-in-go/">error handling in Go</a>, and <code>slog</code> does not change it.</li>
<li><strong>Do not log <code>context.Canceled</code> as an error.</strong> A user closing a tab is not an incident.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">switch</span> {
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">case</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Canceled</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Debug</span>(<span style="color:#e6db74">&#34;client disconnected&#34;</span>, <span style="color:#e6db74">&#34;path&#34;</span>, <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">URL</span>.<span style="color:#a6e22e">Path</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">case</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#e6db74">&#34;request failed&#34;</span>, <span style="color:#e6db74">&#34;err&#34;</span>, <span style="color:#a6e22e">err</span>, <span style="color:#e6db74">&#34;path&#34;</span>, <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">URL</span>.<span style="color:#a6e22e">Path</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="bridging-libraries-that-use-the-old-log-package">Bridging Libraries That Use the Old log Package</h2>
<p>Dependencies still writing to the standard <code>log</code> package do not have to break your JSON output:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Route everything from the standard logger through slog at Info level.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">SetLogLoggerLevel</span>(<span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">LevelInfo</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Or hand a specific component its own *log.Logger backed by slog.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">srv</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Server</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Addr</span>:     <span style="color:#e6db74">&#34;:8080&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Handler</span>:  <span style="color:#a6e22e">mux</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ErrorLog</span>: <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">NewLogLogger</span>(<span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Default</span>().<span style="color:#a6e22e">Handler</span>(), <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">LevelError</span>),
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>That second line is worth adding to every server you write — <code>http.Server</code> logs connection errors through <code>ErrorLog</code>, and by default they land on stderr as unstructured text that your aggregator will not parse.</p>
<h2 id="performance-notes">Performance Notes</h2>
<p><code>slog</code> is designed so the common path allocates very little, but a few habits matter:</p>
<ul>
<li>
<p>Guard genuinely expensive debug work behind <code>Enabled</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Default</span>().<span style="color:#a6e22e">Enabled</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">LevelDebug</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Debug</span>(<span style="color:#e6db74">&#34;payload&#34;</span>, <span style="color:#e6db74">&#34;body&#34;</span>, <span style="color:#a6e22e">expensiveDump</span>(<span style="color:#a6e22e">req</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
<li>
<p>Prefer <code>LogAttrs</code> in hot paths — it takes typed attributes and skips the <code>any</code> boxing:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">LogAttrs</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">LevelInfo</span>, <span style="color:#e6db74">&#34;cache hit&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">String</span>(<span style="color:#e6db74">&#34;key&#34;</span>, <span style="color:#a6e22e">key</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Int</span>(<span style="color:#e6db74">&#34;size&#34;</span>, len(<span style="color:#a6e22e">val</span>)),
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div></li>
<li>
<p>Build the per-request logger once with <code>With</code>, not per log line.</p>
</li>
</ul>
<p>None of this matters at ten requests per second. All of it matters in a hot loop — the same way <a href="/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/">caching with Ristretto</a> only pays off once you are actually calling the expensive thing often.</p>
<h2 id="checklist">Checklist</h2>
<ul>
<li>One handler, configured once in <code>main</code>, installed with <code>slog.SetDefault</code>.</li>
<li>JSON in production, text locally.</li>
<li>A <code>LevelVar</code> so you can raise verbosity without a redeploy.</li>
<li>A request-scoped logger in the context, carrying the request ID.</li>
<li><code>ReplaceAttr</code> or <code>LogValuer</code> for anything secret.</li>
<li>Errors logged once, at the boundary, as values.</li>
<li><code>http.Server.ErrorLog</code> wired through <code>slog</code>.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p><code>log/slog</code> removed the last dependency I used to add reflexively to every new Go service. It is fast enough, it is in the standard library, and its handler interface means you can change output format or destination without touching a single call site. If you are still logging with <code>fmt.Sprintf</code>, the migration is mostly mechanical — and the first time you filter a week of logs by <code>request_id</code>, you will not want to go back.</p>
<p>It pays off especially well for anything token-billed, where a handful of numeric fields per call is the only record of what a feature costs — <a href="/posts/calling-claude-from-go/">calling an LLM from Go</a> covers which ones to log.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Error Handling in Go: Wrapping, Sentinel Errors and errors.Is/As</title>
      <link>https://webdevstation.com/posts/error-handling-in-go/</link>
      <pubDate>Tue, 21 Jul 2026 09:30:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/error-handling-in-go/</guid>
      <description>A practical guide to Go error handling — wrapping with %w, sentinel errors, custom error types, errors.Is and errors.As, and where each one belongs in a real…</description>
      <content:encoded><![CDATA[<p>Every Go codebase I have joined had the same weak spot: errors. Not the <code>if err != nil</code> part — everybody writes that — but everything after it. Errors get logged three times on the way up, lose their context somewhere in the middle, or get compared with <code>err.Error() == &quot;not found&quot;</code>. In this post I want to share the small set of rules I now apply everywhere, and the standard library features that make them work.</p>
<h2 id="the-one-rule-that-fixes-most-of-it">The One Rule That Fixes Most of It</h2>
<p>Handle an error <strong>once</strong>. Everywhere else, add context and pass it up.</p>
<p>&ldquo;Handling&rdquo; means doing something a caller cannot: returning a 404, retrying, falling back to a default, logging and moving on. If you are not doing one of those, you are just a link in the chain — and a link&rsquo;s only job is to explain where it sits.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Bad: the error is logged here and returned, so it will be logged again</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// by every caller on the way up.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">s</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Store</span>) <span style="color:#a6e22e">User</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">id</span> <span style="color:#66d9ef">int64</span>) (<span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">u</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">query</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">id</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;failed to load user: %v&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">u</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Good: add what this layer knows, and let the caller decide.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">s</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Store</span>) <span style="color:#a6e22e">User</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">id</span> <span style="color:#66d9ef">int64</span>) (<span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">u</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">query</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">id</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;load user %d: %w&#34;</span>, <span style="color:#a6e22e">id</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">u</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The second version produces messages that read like a stack trace written in English:</p>
<pre tabindex="0"><code>handle GET /users/42: load user 42: query users: sql: no rows in result set
</code></pre><h2 id="wrapping-with-w">Wrapping With %w</h2>
<p><code>fmt.Errorf</code> has a special verb, <code>%w</code>, that wraps the original error instead of flattening it to a string. The result behaves like a normal error, but the original stays reachable underneath.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Open</span>(<span style="color:#e6db74">&#34;/etc/app/config.yaml&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">wrapped</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;read config: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">wrapped</span>)
</span></span><span style="display:flex;"><span><span style="color:#75715e">// read config: open /etc/app/config.yaml: no such file or directory</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">wrapped</span>, <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">ErrNotExist</span>)) <span style="color:#75715e">// true</span>
</span></span></code></pre></div><p>That last line is the whole point. <code>%v</code> would have given you the same message, but <code>errors.Is</code> would have returned <code>false</code> because the chain was broken.</p>
<p>A few conventions that keep the output readable:</p>
<ul>
<li>Start the message with a lowercase verb phrase describing what <em>this</em> function was doing: <code>&quot;load user&quot;</code>, <code>&quot;encode response&quot;</code>, <code>&quot;dial redis&quot;</code>.</li>
<li>Do not end with punctuation, and do not include the word &ldquo;error&rdquo; or &ldquo;failed&rdquo; — the chain already reads as a failure.</li>
<li>Put the wrapped error last, after a colon and a space.</li>
</ul>
<p>Since Go 1.20 you can wrap more than one error in a single call, which is handy when you are cleaning up:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">doWork</span>()
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">cerr</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">f</span>.<span style="color:#a6e22e">Close</span>(); <span style="color:#a6e22e">cerr</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;work failed: %w; close failed: %w&#34;</span>, <span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">cerr</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="sentinel-errors-for-conditions-callers-branch-on">Sentinel Errors: For Conditions Callers Branch On</h2>
<p>A sentinel is a package-level error value that callers are meant to recognise.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">store</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;errors&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ErrNotFound</span>  = <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;not found&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ErrConflict</span>  = <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;conflict&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ErrForbidden</span> = <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;forbidden&#34;</span>)
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>Return them wrapped, so the caller gets both the identity and the context:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">s</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Store</span>) <span style="color:#a6e22e">User</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">id</span> <span style="color:#66d9ef">int64</span>) (<span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">u</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">query</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">id</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">sql</span>.<span style="color:#a6e22e">ErrNoRows</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;load user %d: %w&#34;</span>, <span style="color:#a6e22e">id</span>, <span style="color:#a6e22e">ErrNotFound</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;load user %d: %w&#34;</span>, <span style="color:#a6e22e">id</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">u</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And check them with <code>errors.Is</code>, which walks the whole chain:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">u</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">store</span>.<span style="color:#a6e22e">User</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">id</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">switch</span> {
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">case</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">store</span>.<span style="color:#a6e22e">ErrNotFound</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;user not found&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusNotFound</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">case</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#e6db74">&#34;load user&#34;</span>, <span style="color:#e6db74">&#34;err&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;internal error&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Never compare with <code>==</code> when the error might be wrapped, and never compare error strings. <code>errors.Is(err, ErrNotFound)</code> keeps working when someone adds another wrapping layer three months from now; <code>err == ErrNotFound</code> silently stops working.</p>
<p>Keep the list short. Sentinels are part of your package&rsquo;s public API — every one you export is a promise. If callers cannot meaningfully branch on it, it should not be a sentinel.</p>
<h2 id="custom-error-types-when-you-need-to-carry-data">Custom Error Types: When You Need to Carry Data</h2>
<p>A sentinel says <em>what went wrong</em>. A custom type also says <em>with what</em>. Reach for one when the caller needs a field, not just an identity.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// ValidationError reports a single field that failed validation.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">ValidationError</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Field</span>  <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Reason</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">e</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">ValidationError</span>) <span style="color:#a6e22e">Error</span>() <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;field %q is invalid: %s&#34;</span>, <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Field</span>, <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Reason</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>errors.As</code> finds it anywhere in the chain and assigns it to your variable:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">vErr</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">ValidationError</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">As</span>(<span style="color:#a6e22e">err</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">vErr</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusBadRequest</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">NewEncoder</span>(<span style="color:#a6e22e">w</span>).<span style="color:#a6e22e">Encode</span>(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;field&#34;</span>:  <span style="color:#a6e22e">vErr</span>.<span style="color:#a6e22e">Field</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;reason&#34;</span>: <span style="color:#a6e22e">vErr</span>.<span style="color:#a6e22e">Reason</span>,
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Two details that trip people up:</p>
<ol>
<li><strong>Pass a pointer to the target.</strong> <code>errors.As(err, &amp;vErr)</code> where <code>vErr</code> is already a <code>*ValidationError</code>. Passing <code>vErr</code> directly panics.</li>
<li><strong>Be consistent about pointer vs. value receivers.</strong> If <code>Error()</code> is defined on <code>*ValidationError</code>, then only <code>*ValidationError</code> implements <code>error</code>. Return <code>&amp;ValidationError{...}</code>, and match against <code>*ValidationError</code>.</li>
</ol>
<p>If your type wraps another error, give it an <code>Unwrap</code> method so <code>errors.Is</code> can keep walking:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">QueryError</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Query</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Err</span>   <span style="color:#66d9ef">error</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">e</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">QueryError</span>) <span style="color:#a6e22e">Error</span>() <span style="color:#66d9ef">string</span> { <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Query</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;: &#34;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Err</span>.<span style="color:#a6e22e">Error</span>() }
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">e</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">QueryError</span>) <span style="color:#a6e22e">Unwrap</span>() <span style="color:#66d9ef">error</span> { <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Err</span> }
</span></span></code></pre></div><p>Now <code>errors.Is(err, sql.ErrNoRows)</code> still works even with a <code>QueryError</code> in the middle.</p>
<h2 id="collecting-several-errors-with-errorsjoin">Collecting Several Errors With errors.Join</h2>
<p>When you validate a whole struct, failing on the first problem makes for a frustrating API. <code>errors.Join</code> (Go 1.20) combines errors into one value that <code>errors.Is</code> and <code>errors.As</code> can still search.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#a6e22e">CreateUserRequest</span>) <span style="color:#a6e22e">Validate</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">errs</span> []<span style="color:#66d9ef">error</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Email</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;&#34;</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">errs</span> = append(<span style="color:#a6e22e">errs</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">ValidationError</span>{<span style="color:#a6e22e">Field</span>: <span style="color:#e6db74">&#34;email&#34;</span>, <span style="color:#a6e22e">Reason</span>: <span style="color:#e6db74">&#34;required&#34;</span>})
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> len(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Password</span>) &lt; <span style="color:#ae81ff">12</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">errs</span> = append(<span style="color:#a6e22e">errs</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">ValidationError</span>{<span style="color:#a6e22e">Field</span>: <span style="color:#e6db74">&#34;password&#34;</span>, <span style="color:#a6e22e">Reason</span>: <span style="color:#e6db74">&#34;too short&#34;</span>})
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Join returns nil when every element is nil, so this is safe as-is.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#a6e22e">errs</span><span style="color:#f92672">...</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The joined error prints one message per line, and <code>errors.As</code> will find the first <code>*ValidationError</code> inside it.</p>
<h2 id="which-tool-for-which-job">Which Tool for Which Job</h2>
<table>
	<thead>
			<tr>
					<th>Situation</th>
					<th>Reach for</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Adding context on the way up</td>
					<td><code>fmt.Errorf(&quot;...: %w&quot;, err)</code></td>
			</tr>
			<tr>
					<td>Caller branches on a known condition</td>
					<td>Sentinel + <code>errors.Is</code></td>
			</tr>
			<tr>
					<td>Caller needs data about the failure</td>
					<td>Custom type + <code>errors.As</code></td>
			</tr>
			<tr>
					<td>Several independent failures at once</td>
					<td><code>errors.Join</code></td>
			</tr>
			<tr>
					<td>Failure is expected and unremarkable</td>
					<td>Return a plain value, not an error</td>
			</tr>
	</tbody>
</table>
<p>That last row matters more than it looks. A cache miss is not an error. An empty search result is not an error. Reserve errors for situations where the caller genuinely cannot continue as planned.</p>
<h2 id="errors-at-the-http-boundary">Errors at the HTTP Boundary</h2>
<p>The boundary is where errors finally get handled, and it is worth doing that in exactly one place. Middleware is a natural home for it — the same pattern as the <a href="/posts/go-middleware-example/">Go middleware example</a>, just applied to errors instead of responses.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// handlerFunc is like http.HandlerFunc but may return an error.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">handlerFunc</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) <span style="color:#66d9ef">error</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// wrap turns a handlerFunc into a plain http.Handler, translating</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// errors into status codes in one place.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">wrap</span>(<span style="color:#a6e22e">h</span> <span style="color:#a6e22e">handlerFunc</span>) <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">h</span>(<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">==</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">vErr</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">ValidationError</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">switch</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">store</span>.<span style="color:#a6e22e">ErrNotFound</span>):
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;not found&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusNotFound</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">store</span>.<span style="color:#a6e22e">ErrForbidden</span>):
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;forbidden&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusForbidden</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">As</span>(<span style="color:#a6e22e">err</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">vErr</span>):
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">vErr</span>.<span style="color:#a6e22e">Error</span>(), <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusBadRequest</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Canceled</span>):
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// The client hung up. Nothing to report.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">default</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Log the full chain, tell the client nothing.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#e6db74">&#34;request failed&#34;</span>,
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;method&#34;</span>, <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Method</span>, <span style="color:#e6db74">&#34;path&#34;</span>, <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">URL</span>.<span style="color:#a6e22e">Path</span>, <span style="color:#e6db74">&#34;err&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;internal error&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Handlers become quiet and linear:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">getUser</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">id</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">strconv</span>.<span style="color:#a6e22e">ParseInt</span>(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">PathValue</span>(<span style="color:#e6db74">&#34;id&#34;</span>), <span style="color:#ae81ff">10</span>, <span style="color:#ae81ff">64</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">ValidationError</span>{<span style="color:#a6e22e">Field</span>: <span style="color:#e6db74">&#34;id&#34;</span>, <span style="color:#a6e22e">Reason</span>: <span style="color:#e6db74">&#34;must be an integer&#34;</span>}
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">u</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">store</span>.<span style="color:#a6e22e">User</span>(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Context</span>(), <span style="color:#a6e22e">id</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;get user: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">NewEncoder</span>(<span style="color:#a6e22e">w</span>).<span style="color:#a6e22e">Encode</span>(<span style="color:#a6e22e">u</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The <code>context.Canceled</code> case is easy to forget and shows up constantly in production: a user closes the tab, the request context is cancelled, and your dashboard fills with 500s that nothing went wrong for. If you have not met that machinery yet, <a href="/posts/understanding-golang-context/">understanding Golang context</a> covers where those cancellations come from.</p>
<h2 id="common-pitfalls">Common Pitfalls</h2>
<table>
	<thead>
			<tr>
					<th>Pitfall</th>
					<th>What to do instead</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>if err.Error() == &quot;not found&quot;</code></td>
					<td><code>errors.Is(err, ErrNotFound)</code></td>
			</tr>
			<tr>
					<td><code>%v</code> when the caller needs the chain</td>
					<td><code>%w</code></td>
			</tr>
			<tr>
					<td>Logging <em>and</em> returning the same error</td>
					<td>Return it; log once at the boundary</td>
			</tr>
			<tr>
					<td><code>errors.As(err, vErr)</code></td>
					<td><code>errors.As(err, &amp;vErr)</code> — pass a pointer</td>
			</tr>
			<tr>
					<td>Wrapping with the caller&rsquo;s own function name</td>
					<td>Describe the <em>operation</em>, not the function</td>
			</tr>
			<tr>
					<td>Exporting a sentinel nobody branches on</td>
					<td>Keep it unexported, or drop it</td>
			</tr>
	</tbody>
</table>
<p>One more, subtle enough to deserve its own note: <strong>a non-nil interface holding a nil pointer is not nil.</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">find</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">ValidationError</span> { <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">check</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">find</span>() <span style="color:#75715e">// returns a non-nil error holding a nil *ValidationError</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">check</span>() <span style="color:#f92672">==</span> <span style="color:#66d9ef">nil</span>) <span style="color:#75715e">// false — almost certainly not what you wanted</span>
</span></span></code></pre></div><p>Declare the return type as <code>error</code> and return a literal <code>nil</code>, or check the concrete value before returning it.</p>
<h2 id="testing-error-paths">Testing Error Paths</h2>
<p>Assert on identity and type, never on the message text. Messages are for humans and will change.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestUserNotFound</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">store</span>.<span style="color:#a6e22e">User</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#ae81ff">999</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">store</span>.<span style="color:#a6e22e">ErrNotFound</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Fatalf</span>(<span style="color:#e6db74">&#34;got %v, want ErrNotFound&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestValidation</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">CreateUserRequest</span>{}.<span style="color:#a6e22e">Validate</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">vErr</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">ValidationError</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">As</span>(<span style="color:#a6e22e">err</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">vErr</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Fatalf</span>(<span style="color:#e6db74">&#34;got %v, want *ValidationError&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">vErr</span>.<span style="color:#a6e22e">Field</span> <span style="color:#f92672">!=</span> <span style="color:#e6db74">&#34;email&#34;</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;got field %q, want email&#34;</span>, <span style="color:#a6e22e">vErr</span>.<span style="color:#a6e22e">Field</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This pairs nicely with mocking the database layer, which I covered in <a href="/posts/how-to-test-database-interactions-go/">how to test database interactions in Golang applications</a> — you can force <code>sql.ErrNoRows</code> and check that your store translates it into <code>ErrNotFound</code>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Go&rsquo;s error handling is verbose, but it is also unusually honest: every failure is a value you can inspect, wrap and route. Wrap with <code>%w</code> on the way up, export a small set of sentinels for the conditions callers care about, use custom types when they need the details, and handle everything exactly once at the boundary. Do that and the <code>if err != nil</code> blocks stop feeling like noise and start reading like documentation.</p>
<p>One case that breaks the usual rules and deserves a look: an LLM call can return HTTP 200 while declining to answer, so <code>err</code> is nil and there is nothing in the content. <a href="/posts/calling-claude-from-go/">Calling an LLM from Go</a> covers that branch.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
      <category>Testing</category>
    </item>
    <item>
      <title>Why Use Golang: 9 Compelling Reasons for Your Next Project</title>
      <link>https://webdevstation.com/posts/why-use-golang/</link>
      <pubDate>Mon, 16 Jun 2025 15:21:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/why-use-golang/</guid>
      <description>Wondering why use Golang? Explore nine practical reasons — speed, concurrency, tooling and more — that make Go a top choice for modern back-end systems.</description>
      <content:encoded><![CDATA[<p>If you are evaluating programming languages for a new service or migrating an existing codebase, you have probably asked the question <strong><em>“why use Golang?”</em></strong>. In this article we will unpack the concrete, business-focused benefits of Go (often called <strong>Golang</strong>) and show when choosing Go is the right strategic move.</p>
<p>Go was created at Google to solve real-world problems—fast builds, simple deployment, and effortless concurrency—without sacrificing developer happiness. Let’s dive into nine reasons <strong>why you should use Golang</strong> in 2025 and beyond.</p>
<h2 id="quick-snapshot-benefits-at-a-glance">Quick Snapshot: Benefits at a Glance</h2>
<table>
	<thead>
			<tr>
					<th>Reason</th>
					<th>Developer Impact</th>
					<th>Business Impact</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Native concurrency</td>
					<td>Easier parallel code, fewer race conditions</td>
					<td>Better CPU utilization, cost savings</td>
			</tr>
			<tr>
					<td>Fast compilation</td>
					<td>Iterate in seconds, not minutes</td>
					<td>Shorter release cycles</td>
			</tr>
			<tr>
					<td>Simple syntax</td>
					<td>Smaller learning curve</td>
					<td>Faster onboarding</td>
			</tr>
			<tr>
					<td>Robust stdlib</td>
					<td>Fewer external deps</td>
					<td>Reduced maintenance risk</td>
			</tr>
			<tr>
					<td>Single binary deploys</td>
					<td><code>scp</code> &amp; run—no runtime hassles</td>
					<td>Simplified CI/CD &amp; lower ops overhead</td>
			</tr>
			<tr>
					<td>First-class tooling</td>
					<td><code>go test</code>, <code>go vet</code>, <code>go fmt</code> built-in</td>
					<td>Higher code quality</td>
			</tr>
			<tr>
					<td>Memory safety</td>
					<td>Prevents many bugs upfront</td>
					<td>Increased uptime</td>
			</tr>
			<tr>
					<td>Growing ecosystem</td>
					<td>Mature frameworks, libs, &amp; CLIs</td>
					<td>Access to talent &amp; shared solutions</td>
			</tr>
			<tr>
					<td>Backed by giants</td>
					<td>Google, Cloudflare, Uber, etc.</td>
					<td>Long-term viability</td>
			</tr>
	</tbody>
</table>
<hr>
<h2 id="1-native-concurrency-model">1. Native Concurrency Model</h2>
<p>Go’s <strong>goroutines</strong> and <strong>channels</strong> deliver lightweight concurrency without complex thread management. Spawning 100 000 goroutines is commonplace:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">wg</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>{}
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">1e5</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">id</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">id</span>)
</span></span><span style="display:flex;"><span>        }(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Compare that with traditional threads and locks and the <em>why use Golang</em> answer becomes evident—<strong>parallelism is baked into the language</strong>.</p>
<h2 id="2-blazing-fast-compilation--execution">2. Blazing-Fast Compilation &amp; Execution</h2>
<p>Go produces native machine code and compiles large projects in <strong>seconds</strong>. Faster feedback loops boost productivity, and Go’s runtime performance rivals (and often exceeds) higher-level languages like Python or Node.js.</p>
<h2 id="3-pragmatic-readable-syntax">3. Pragmatic, Readable Syntax</h2>
<p>Go deliberately avoids generics-for-everything, implicit magic, and hidden control flow. The result is code that looks similar across companies, which means <strong>reading unfamiliar Go is easy</strong>.</p>
<h2 id="4-a-batteries-included-standard-library">4. A Batteries-Included Standard Library</h2>
<p>Need an HTTP server, JSON encoder, or RSA crypto? It’s already in <code>stdlib</code>. With fewer third-party dependencies, your supply-chain attack surface shrinks.</p>
<h2 id="5-static-binaries--effortless-deployment">5. Static Binaries &amp; Effortless Deployment</h2>
<p><code>go build</code> outputs a single, statically linked binary. Drop it in a container scratch image (~10 MB) or onto a bare server—no JVM, no interpreter.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>GOOS<span style="color:#f92672">=</span>linux GOARCH<span style="color:#f92672">=</span>amd64 go build -ldflags <span style="color:#e6db74">&#34;-s -w&#34;</span> -o app
</span></span></code></pre></div><h2 id="6-first-class-tooling-out-of-the-box">6. First-Class Tooling Out-of-the-Box</h2>
<p>Formatting (<code>go fmt</code>), linting (<code>go vet</code>), profiling (<code>pprof</code>), testing, and coverage are all standard. This uniform toolchain answers <strong>why use Golang</strong> for teams who value consistency.</p>
<h2 id="7-memory-safety--predictable-gc">7. Memory Safety &amp; Predictable GC</h2>
<p>Go’s garbage collector has reached <strong>&lt;1 ms 95th percentile pause times</strong> while retaining simplicity. With escape analysis and value semantics, many allocations disappear at compile time.</p>
<h2 id="8-vibrant-ecosystem--community">8. Vibrant Ecosystem &amp; Community</h2>
<p>Frameworks like <strong>Gin</strong>, <strong>Echo</strong>, and <strong>Fiber</strong> make web development painless; <strong>gRPC</strong> and <strong>Protocol Buffers</strong> have first-class support; and cloud providers ship Go SDKs on day one.</p>
<h2 id="9-proven-in-production-by-industry-leaders">9. Proven in Production by Industry Leaders</h2>
<p>Google (of course), Netflix, Uber, Cloudflare, Stripe, and many others run latency-critical systems in Go. That track record signals Go’s staying power.</p>
<hr>
<h2 id="when-not-to-use-go">When <em>Not</em> to Use Go</h2>
<p>While this article focuses on <strong>why to use Golang</strong>, balanced engineering requires knowing its limits:</p>
<ul>
<li>No generics for higher-kinded types (though basic generics landed in Go 1.18).</li>
<li>Runtime lacks a mature GUI library.</li>
<li>Manual error handling (<code>if err != nil</code>) can feel verbose.</li>
</ul>
<p>If your workload is <strong>numerical heavy-compute</strong> with tight SIMD requirements or requires sophisticated metaprogramming, Rust or C++ may fit better.</p>
<h2 id="conclusion">Conclusion</h2>
<p>So <strong>why use Golang</strong>? Because it delivers a rare combination of developer ergonomics, runtime efficiency, and operational simplicity. From microservices to CLI tools, Go empowers teams to ship reliable software—fast.</p>
<p>Ready to give Go a try? Install it, <code>go mod init</code>, and discover firsthand why thousands of engineers choose Golang every day.</p>
<p>If that convinced you, here is where I would start: <a href="/posts/understanding-golang-context/">understanding Golang context</a> for concurrency you can cancel, <a href="/posts/error-handling-in-go/">error handling in Go</a> for the idiom that trips up most newcomers, and <a href="/posts/one-of-thee-easiest-ways-to-host-go-web-apps/">one of the easiest ways to host your Go web app</a> for getting the result online. For what the language has picked up lately, see <a href="/posts/exciting-features-in-go-1-25/">exciting features coming in Go 1.25</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Understanding Golang Context: Cancellation, Timeouts, and Deadlines</title>
      <link>https://webdevstation.com/posts/understanding-golang-context/</link>
      <pubDate>Mon, 16 Jun 2025 14:01:13 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/understanding-golang-context/</guid>
      <description>Deep-dive into Golang&#39;s context package to manage cancellation, timeouts, deadlines, and request-scoped data across goroutines with practical examples and best…</description>
      <content:encoded><![CDATA[<p>When working with concurrent operations in Go, few topics are as important—and as misunderstood—as the <strong><code>context</code></strong> package. Whether you are building an HTTP API, orchestrating background workers, or integrating with external services, <em>golang context</em> is the idiomatic way to propagate cancellation signals, enforce timeouts, carry deadlines, and pass request-scoped values.</p>
<p>In this article we will demystify the <code>context</code> package, walk through common use-cases, and share production-tested best practices.</p>
<h2 id="why-context-exists">Why Context Exists</h2>
<p>Go’s lightweight goroutines make it trivial to spin up concurrent work, but once you have hundreds (or thousands) of goroutines you need a structured way to:</p>
<ol>
<li>Cancel unfinished work when a client disconnects or a parent task ends.</li>
<li>Enforce upper time bounds to prevent runaway operations.</li>
<li>Propagate deadlines deep into the call graph.</li>
<li>Attach request-level metadata (trace IDs, auth tokens, etc.) without polluting function signatures.</li>
</ol>
<p>The <code>context</code> package solves these problems with two core ideas:</p>
<ul>
<li><strong>Cancellation propagation</strong> via <code>Done()</code> channels.</li>
<li><strong>Immutable trees</strong>—each derived context references its parent, forming a hierarchy that can be cancelled from the root.</li>
</ul>
<h2 id="creating-and-cancelling-contexts">Creating and Cancelling Contexts</h2>
<p>The building blocks are <code>context.Background()</code> (or <code>context.TODO()</code>), <code>context.WithCancel</code>, <code>context.WithTimeout</code>, and <code>context.WithDeadline</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;context&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Start with a root context</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithCancel</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>())
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">cancel</span>() <span style="color:#75715e">// always release resources</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Fire off a worker goroutine</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;worker cancelled:&#34;</span>, <span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Err</span>())
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Simulate some condition that requires cancellation</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">500</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Millisecond</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">cancel</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">100</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Millisecond</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Running this prints:</p>
<pre tabindex="0"><code>worker cancelled: context canceled
</code></pre><h3 id="timeout-helper">Timeout Helper</h3>
<p><code>context.WithTimeout</code> wraps <code>WithCancel</code> plus a timer:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithTimeout</span>(<span style="color:#a6e22e">parent</span>, <span style="color:#ae81ff">2</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span><span style="color:#75715e">// After 2s: ctx.Err() == context.DeadlineExceeded</span>
</span></span></code></pre></div><p>Remember to <strong>always call the returned <code>cancel</code></strong>—even when the timeout expires—so the timer’s internal resources are freed.</p>
<h2 id="passing-context-down-the-call-stack">Passing Context Down the Call Stack</h2>
<p>The first parameter of every context-aware function should be <code>ctx context.Context</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">url</span> <span style="color:#66d9ef">string</span>) ([]<span style="color:#66d9ef">byte</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">req</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">NewRequestWithContext</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">MethodGet</span>, <span style="color:#a6e22e">url</span>, <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">resp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">DefaultClient</span>.<span style="color:#a6e22e">Do</span>(<span style="color:#a6e22e">req</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Body</span>.<span style="color:#a6e22e">Close</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">io</span>.<span style="color:#a6e22e">ReadAll</span>(<span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Body</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>When <code>ctx</code> is cancelled upstream, <code>http.Client</code> aborts the request automatically.</p>
<h2 id="deadlines-vs-timeouts">Deadlines vs. Timeouts</h2>
<p>A <strong>deadline</strong> is an absolute moment (<code>2025-06-16T14:05:00+02:00</code>) while a <strong>timeout</strong> is a relative duration (<code>5s</code>). Internally both are implemented with <code>WithDeadline</code>, but modelling them correctly communicates intent:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">deadline</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">5</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithDeadline</span>(<span style="color:#a6e22e">parent</span>, <span style="color:#a6e22e">deadline</span>)
</span></span></code></pre></div><h2 id="storing-values-in-context">Storing Values in Context</h2>
<p><code>context.WithValue</code> allows passing request-scoped data without modifying every function signature. Use it sparingly:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Key type prevents collisions</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">type</span> <span style="color:#a6e22e">key</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">traceIDKey</span> <span style="color:#a6e22e">key</span> = <span style="color:#e6db74">&#34;traceID&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">ctx</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithValue</span>(<span style="color:#a6e22e">parent</span>, <span style="color:#a6e22e">traceIDKey</span>, <span style="color:#e6db74">&#34;abc-123&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Downstream retrieval</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">v</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Value</span>(<span style="color:#a6e22e">traceIDKey</span>); <span style="color:#a6e22e">v</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>     <span style="color:#a6e22e">traceID</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">v</span>.(<span style="color:#66d9ef">string</span>)
</span></span><span style="display:flex;"><span>     <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;traceID:&#34;</span>, <span style="color:#a6e22e">traceID</span>)
</span></span><span style="display:flex;"><span> }
</span></span></code></pre></div><h3 id="guidelines">Guidelines</h3>
<ol>
<li>Only store immutable, request-specific data (IDs, auth tokens).</li>
<li>Never store optional params that belong in function arguments.</li>
<li>Define unexported key types to avoid collisions across packages.</li>
</ol>
<h2 id="common-pitfalls">Common Pitfalls</h2>
<table>
	<thead>
			<tr>
					<th>Pitfall</th>
					<th>Solution</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Returning <code>nil</code> context</td>
					<td>Accept a <code>context.Context</code> argument and demand callers pass one.</td>
			</tr>
			<tr>
					<td>Forgetting to cancel</td>
					<td>Always <code>defer cancel()</code> after <code>WithCancel / WithTimeout / WithDeadline</code>.</td>
			</tr>
			<tr>
					<td>Blocking select without <code>&lt;-ctx.Done()</code></td>
					<td>Include cancellation in every <code>select</code> that may block.</td>
			</tr>
			<tr>
					<td>Misusing <code>WithValue</code> for configs</td>
					<td>Pass explicit parameters instead.</td>
			</tr>
	</tbody>
</table>
<h2 id="end-to-end-example-http-server-with-timeouts">End-to-End Example: HTTP Server with Timeouts</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;context&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">srv</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Server</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Addr</span>:         <span style="color:#e6db74">&#34;:8080&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ReadTimeout</span>:  <span style="color:#ae81ff">3</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">WriteTimeout</span>: <span style="color:#ae81ff">5</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Handler</span>:      <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#a6e22e">handler</span>),
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Shutdown gracefully on interrupt</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">After</span>(<span style="color:#ae81ff">10</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>) <span style="color:#75715e">// Simulate interrupt</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithTimeout</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#ae81ff">3</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">cancel</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">Shutdown</span>(<span style="color:#a6e22e">ctx</span>)
</span></span><span style="display:flex;"><span>    }()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Serving on :8080&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">ListenAndServe</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ErrServerClosed</span> {
</span></span><span style="display:flex;"><span>        panic(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Server gracefully stopped&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">handler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// r.Context() inherits deadlines from the server</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">After</span>(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>):
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Write</span>([]byte(<span style="color:#e6db74">&#34;done&#34;</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Context</span>().<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;request cancelled&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusRequestTimeout</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="best-practices-checklist">Best Practices Checklist</h2>
<ul>
<li>Pass <code>context.Context</code> as the first parameter; never embed it in structs.</li>
<li>Do not store contexts—pass them along the call chain.</li>
<li>Cancel contexts to free resources early.</li>
<li>Use short-lived timeouts close to I/O boundaries rather than a single large timeout at the root.</li>
<li>Keep functions context-aware; return early on <code>&lt;-ctx.Done()</code>.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>The <em>golang context</em> package brings order to concurrent Go programs by standardising how we propagate cancellation and deadlines. Mastering it unlocks more reliable, resource-efficient applications.</p>
<p>Context shows up everywhere once you start looking for it. Three follow-ups that use it heavily: <a href="/posts/graceful-shutdown-in-go-web-services/">graceful shutdown in Go web services</a>, <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools with errgroup</a>, and <a href="/posts/simple-queue-implementation-in-golang/">a simple queue implementation</a>. It also turns up in every LLM call you will ever write, since those are slow, cancellable and worth a deadline — see <a href="/posts/calling-claude-from-go/">calling an LLM from Go</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Mastering Time in Go: From Basics to Best Practices</title>
      <link>https://webdevstation.com/posts/mastering-time-in-golang/</link>
      <pubDate>Mon, 09 Jun 2025 21:47:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/mastering-time-in-golang/</guid>
      <description>Explore how Go&#39;s time package provides powerful tools for handling dates, durations, and timers in your applications with clear examples and practical tips.</description>
      <content:encoded><![CDATA[<p>Time manipulation is a fundamental aspect of many applications, from logging and benchmarking to scheduling and timeout handling. Go&rsquo;s standard library includes a robust <code>time</code> package that provides elegant solutions for these common challenges. Let&rsquo;s explore how to effectively use Go&rsquo;s time primitives and avoid common pitfalls.</p>
<h2 id="understanding-time-in-go">Understanding Time in Go</h2>
<p>The Go language approaches time handling with its characteristic blend of simplicity and practicality. The <code>time</code> package centers around three key types:</p>
<ul>
<li><code>time.Time</code> - Represents a specific moment in time</li>
<li><code>time.Duration</code> - Represents the elapsed time between two points</li>
<li><code>time.Location</code> - Represents a time zone</li>
</ul>
<p>What makes Go&rsquo;s implementation particularly useful is its handling of monotonic time, which ensures consistent time measurements even when system clocks change.</p>
<h2 id="working-with-current-time">Working with Current Time</h2>
<p>Let&rsquo;s start with some basic operations:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Get the current time</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">now</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Current time:&#34;</span>, <span style="color:#a6e22e">now</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Format the time using the reference time constant</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Mon Jan 2 15:04:05 MST 2006</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Formatted time:&#34;</span>, <span style="color:#a6e22e">now</span>.<span style="color:#a6e22e">Format</span>(<span style="color:#e6db74">&#34;2006-01-02 15:04:05&#34;</span>))
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Get individual components</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Year: %d, Month: %s, Day: %d\n&#34;</span>, 
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">now</span>.<span style="color:#a6e22e">Year</span>(), <span style="color:#a6e22e">now</span>.<span style="color:#a6e22e">Month</span>(), <span style="color:#a6e22e">now</span>.<span style="color:#a6e22e">Day</span>())
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Get Unix timestamp (seconds since January 1, 1970 UTC)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Unix timestamp:&#34;</span>, <span style="color:#a6e22e">now</span>.<span style="color:#a6e22e">Unix</span>())
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>While most programming languages use arbitrary format strings like &ldquo;YYYY-MM-DD&rdquo;, Go uses a specific reference time: <strong>January 2, 2006 at 15:04:05</strong> (or 01/02 03:04:05PM &lsquo;06 -0700). This approach eliminates ambiguity and is easier to remember once you realize the pattern: 01/02 03:04:05PM &lsquo;06.</p>
<h2 id="time-parsing-and-time-zones">Time Parsing and Time Zones</h2>
<p>One of Go&rsquo;s strengths is its handling of time zones and time parsing. Let&rsquo;s see how to work with different formats and time zones:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Parse a time string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">timeStr</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;2025-06-09T21:47:00+02:00&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Parse</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">RFC3339</span>, <span style="color:#a6e22e">timeStr</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Error parsing time:&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Parsed time:&#34;</span>, <span style="color:#a6e22e">t</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Convert to different timezone</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">utc</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">UTC</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;UTC time:&#34;</span>, <span style="color:#a6e22e">utc</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Load a specific location</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">loc</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">LoadLocation</span>(<span style="color:#e6db74">&#34;America/New_York&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Error loading location:&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Convert time to the new location</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">nyTime</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">In</span>(<span style="color:#a6e22e">loc</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;New York time:&#34;</span>, <span style="color:#a6e22e">nyTime</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>A common source of bugs is assuming all times are UTC. In a distributed system, always be explicit about time zones when communicating timestamps between services.</p>
<h2 id="measuring-time-and-performance">Measuring Time and Performance</h2>
<p>Go&rsquo;s <code>time</code> package shines when measuring code performance:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">someExpensiveOperation</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Simulate work</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">100</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Millisecond</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">start</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">someExpensiveOperation</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">elapsed</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Since</span>(<span style="color:#a6e22e">start</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Operation took %s\n&#34;</span>, <span style="color:#a6e22e">elapsed</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Alternatively with the same result</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Operation took %s\n&#34;</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Sub</span>(<span style="color:#a6e22e">start</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The <code>time.Since()</code> function is a convenient shorthand for <code>time.Now().Sub(start)</code>, making benchmarking code cleaner.</p>
<h2 id="working-with-durations">Working with Durations</h2>
<p>The <code>time.Duration</code> type is a nanosecond-precision interval that offers intuitive operations:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Create durations using typed constants</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">second</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">minute</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Minute</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">hour</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Arithmetic with durations</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">total</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">second</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">30</span><span style="color:#f92672">*</span><span style="color:#a6e22e">minute</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">2</span><span style="color:#f92672">*</span><span style="color:#a6e22e">hour</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Total duration: %v (%s)\n&#34;</span>, <span style="color:#a6e22e">total</span>, <span style="color:#a6e22e">total</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Parsing durations from strings</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">d</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">ParseDuration</span>(<span style="color:#e6db74">&#34;1h30m15s&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Error parsing duration:&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Parsed duration: %v\n&#34;</span>, <span style="color:#a6e22e">d</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Converting durations</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;In seconds: %.2f\n&#34;</span>, <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">Seconds</span>())
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;In minutes: %.2f\n&#34;</span>, <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">Minutes</span>())
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;In hours: %.2f\n&#34;</span>, <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">Hours</span>())
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Note that <code>time.Duration</code> has a maximum range of approximately 290 years. For longer time periods, you&rsquo;ll need to use <code>time.Time</code> instead.</p>
<h2 id="timers-and-tickers">Timers and Tickers</h2>
<p>For scheduling operations, Go provides timers and tickers:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Create a timer that will fire once after 2 seconds</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">timer</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">NewTimer</span>(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Timer started at&#34;</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Format</span>(<span style="color:#e6db74">&#34;15:04:05&#34;</span>))
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Wait for the timer to fire</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">timer</span>.<span style="color:#a6e22e">C</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Timer fired at&#34;</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Format</span>(<span style="color:#e6db74">&#34;15:04:05&#34;</span>))
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Create a ticker that fires every second</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ticker</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">NewTicker</span>(<span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">counter</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Process ticker events for 5 seconds</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">ticker</span>.<span style="color:#a6e22e">C</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">counter</span><span style="color:#f92672">++</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Tick&#34;</span>, <span style="color:#a6e22e">counter</span>, <span style="color:#e6db74">&#34;at&#34;</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Format</span>(<span style="color:#e6db74">&#34;15:04:05&#34;</span>))
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">counter</span> <span style="color:#f92672">&gt;=</span> <span style="color:#ae81ff">5</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">ticker</span>.<span style="color:#a6e22e">Stop</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">break</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Ticker stopped&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>A common mistake is forgetting to stop tickers when they&rsquo;re no longer needed, which can lead to goroutine leaks.</p>
<h2 id="performance-tips-and-best-practices">Performance Tips and Best Practices</h2>
<p>After working with Go&rsquo;s time package across multiple production systems, I&rsquo;ve identified several best practices:</p>
<ol>
<li>
<p><strong>Avoid frequent calls to <code>time.Now()</code></strong> in tight loops, as it involves a system call.</p>
</li>
<li>
<p><strong>Use monotonic time for duration measurements</strong>. Go handles this automatically when you call <code>time.Now()</code>, but be aware that serializing and deserializing a <code>time.Time</code> loses the monotonic component.</p>
</li>
<li>
<p><strong>Be consistent with time zones</strong>, especially when storing times in databases. Either standardize on UTC for storage or be explicit about the time zone.</p>
</li>
<li>
<p><strong>For repeated timer operations</strong>, use <code>time.Ticker</code> instead of repeatedly creating new timers.</p>
</li>
<li>
<p><strong>When parsing user input</strong>, prefer to use explicit formats with <code>time.Parse</code> rather than magic formatting functions.</p>
</li>
</ol>
<p>Here&rsquo;s an example of a common anti-pattern and how to fix it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Anti-pattern - creating many timers</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">antiPattern</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">1000</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">timer</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">NewTimer</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">timer</span>.<span style="color:#a6e22e">C</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">doSomething</span>()
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Better approach - reuse a single timer</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">betterApproach</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">timer</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">NewTimer</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">1000</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">timer</span>.<span style="color:#a6e22e">C</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">doSomething</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">timer</span>.<span style="color:#a6e22e">Reset</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">timer</span>.<span style="color:#a6e22e">Stop</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="conclusion">Conclusion</h2>
<p>Go&rsquo;s <code>time</code> package provides an intuitive API for handling all aspects of time in your applications. From simple date formatting to sophisticated time measurements, it offers a consistent approach that aligns with Go&rsquo;s philosophy of simplicity and practicality.</p>
<p>By understanding how Go approaches time handling, you can write more efficient and reliable code, avoid common pitfalls, and leverage the full power of Go&rsquo;s time primitives.</p>
<p><em>See also: <a href="/posts/understanding-golang-context/">understanding Golang context</a> for deadlines and timeouts, and <a href="/posts/mastering-for-loops-in-go/">mastering Golang for loops</a> for the iteration patterns these examples lean on.</em></p>
<hr>
<p><em>How do you handle time operations in your Go applications? Have you encountered any challenging time-related bugs? Share your experiences in the comments below!</em></p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>The Perfect Harmony: Enhancing Your Reading Experience with Music and BookTuning</title>
      <link>https://webdevstation.com/posts/enhancing-reading-experience-with-music-and-booktuning/</link>
      <pubDate>Sun, 08 Jun 2025 21:21:21 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/enhancing-reading-experience-with-music-and-booktuning/</guid>
      <description>Discover how the right music can transform your reading experience and how BookTuning&#39;s AI-powered platform creates personalized playlists perfectly matched to any…</description>
      <content:encoded><![CDATA[<p>I read a lot, and for years I treated music as something that either helped or ruined the session with no pattern I could name. It turns out there is a pattern — and once you know it, you can pick a soundtrack that makes a book land harder instead of fighting it for attention.</p>
<h2 id="the-science-behind-music-and-reading">The Science Behind Music and Reading</h2>
<p>Reading with the right musical accompaniment can create a unique synergy. When chosen thoughtfully, background music can:</p>
<ul>
<li>Create an immersive atmosphere that complements your book&rsquo;s setting</li>
<li>Block out distracting environmental noises</li>
<li>Establish a consistent rhythm that helps maintain focus</li>
<li>Enhance emotional connections to characters and plot developments</li>
</ul>
<p>The key lies in finding music that complements rather than competes with your reading material. This is where specialized tools like BookTuning enter the picture.</p>
<h2 id="booktuning-an-ai-powered-reading-companion">BookTuning: An AI-Powered Reading Companion</h2>
<p><a href="https://booktun.ing">BookTuning</a> approaches the music-reading relationship with technological sophistication. This innovative platform uses artificial intelligence to create personalized music playlists specifically designed to enhance your reading experience.</p>
<h3 id="how-it-works">How It Works</h3>
<p>BookTuning&rsquo;s approach is three-pronged:</p>
<ol>
<li>
<p><strong>Mood-Matched Music</strong>: Their AI analyzes the mood and atmosphere of your chosen book to find musical accompaniment that enhances those specific elements.</p>
</li>
<li>
<p><strong>Genre-Specific Selection</strong>: The platform considers your book&rsquo;s genre—whether fantasy, romance, thriller, or sci-fi—and curates tracks that complement the thematic elements unique to that genre.</p>
</li>
<li>
<p><strong>AI-Generated Descriptions</strong>: Each playlist includes a personalized explanation of why the selected music pairs well with your book, adding a thoughtful layer to the experience.</p>
</li>
</ol>
<h3 id="customization-options">Customization Options</h3>
<p>What impressed me most about BookTuning was the level of personalization available. Users can:</p>
<ul>
<li>Adjust how diverse and unique they want their song selection to be</li>
<li>Opt to discover new tracks rather than familiar ones</li>
<li>Optimize music selection based on their specific reading environment</li>
</ul>
<p>The playlists seamlessly integrate with Spotify, making the transition from selection to listening effortless.</p>
<h2 id="the-personal-experience">The Personal Experience</h2>
<p>As someone who has long struggled with finding the right musical backdrop for different types of books, BookTuning addresses a genuine need. Historical fiction suddenly becomes more immersive with period-appropriate instrumentals, while science fiction takes on new dimensions with ambient electronic compositions.</p>
<p>The platform&rsquo;s ability to match subtle emotional undertones in literature with corresponding musical elements demonstrates a sophisticated understanding of both mediums. During my testing, the AI consistently provided thoughtful pairings that enhanced rather than distracted from the reading experience.</p>
<h2 id="is-booktuning-worth-it">Is BookTuning Worth It?</h2>
<p>For readers who already enjoy pairing music with books but find themselves spending too much time curating playlists, BookTuning offers a valuable service. It removes the friction between wanting that perfect soundtrack and actually getting to your reading.</p>
<p>The AI-powered approach proves particularly helpful when exploring unfamiliar genres or books with complex emotional landscapes. Rather than interrupting your reading flow to adjust your playlist, BookTuning creates a seamless audio environment tailored to your literary journey.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The partnership between literature and music is deeply personal, yet BookTuning has found a way to enhance this relationship through thoughtful technology. By creating customized soundscapes that complement rather than compete with your reading material, it offers a new dimension to how we experience books.</p>
<p>Whether you&rsquo;re a longtime practitioner of reading with musical accompaniment or curious to try this approach for the first time, BookTuning provides an accessible entry point that respects both the power of music and the sanctity of the reading experience.</p>
<p>If you enjoy this kind of small-tool write-up, I also collected the fastest ways I have found to <a href="/posts/aneasywaytogenerateqrcodefast/">generate QR codes</a>.</p>
<hr>
<p><em>Have you tried reading with musical accompaniment? What have been your experiences with tools like BookTuning? Share your thoughts in the comments below!</em></p>]]></content:encoded>
      <category>Tools</category>
    </item>
    <item>
      <title>Implementing Enums in Golang: Patterns and Best Practices</title>
      <link>https://webdevstation.com/posts/implementing-enums-in-golang/</link>
      <pubDate>Fri, 23 May 2025 22:45:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/implementing-enums-in-golang/</guid>
      <description>Discover effective techniques for implementing type-safe enums in Go using constants, iota, custom types, and string mapping. Learn best practices for enum-like…</description>
      <content:encoded><![CDATA[<p>Unlike many other programming languages, Golang doesn&rsquo;t have a built-in enum type. However, Go provides several elegant patterns to implement enum-like behavior with type safety and additional functionality. In this guide, we&rsquo;ll explore how to effectively implement and use enums in Golang.</p>
<h2 id="understanding-golang-enum-patterns">Understanding Golang Enum Patterns</h2>
<p>While Golang doesn&rsquo;t have a dedicated <code>enum</code> keyword like Java or TypeScript, it offers multiple approaches to create enumerated types. Let&rsquo;s explore these patterns from basic to advanced.</p>
<h2 id="basic-golang-enum-using-constants">Basic Golang Enum Using Constants</h2>
<p>The simplest way to implement an enum in Golang is using the <code>const</code> keyword with <code>iota</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// StatusPending is the initial status</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">StatusPending</span> = <span style="color:#66d9ef">iota</span>  <span style="color:#75715e">// 0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// StatusActive indicates the item is active</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">StatusActive</span>          <span style="color:#75715e">// 1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// StatusSuspended indicates the item is temporarily suspended</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">StatusSuspended</span>       <span style="color:#75715e">// 2</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// StatusCancelled indicates the item is permanently cancelled</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">StatusCancelled</span>       <span style="color:#75715e">// 3</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Status Pending:&#34;</span>, <span style="color:#a6e22e">StatusPending</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Status Active:&#34;</span>, <span style="color:#a6e22e">StatusActive</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Status Suspended:&#34;</span>, <span style="color:#a6e22e">StatusSuspended</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Status Cancelled:&#34;</span>, <span style="color:#a6e22e">StatusCancelled</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The <code>iota</code> identifier generates sequential integer constants. It starts at 0 and increments by 1 for each constant in the block. This approach is simple but lacks type safety.</p>
<h2 id="type-safe-golang-enum-pattern">Type-Safe Golang Enum Pattern</h2>
<p>To create a more type-safe enum, we can define a custom type:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Status</span> <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">StatusPending</span> <span style="color:#a6e22e">Status</span> = <span style="color:#66d9ef">iota</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">StatusActive</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">StatusSuspended</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">StatusCancelled</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">currentStatus</span> <span style="color:#a6e22e">Status</span> = <span style="color:#a6e22e">StatusActive</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// This would cause a compile error:</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// currentStatus = 5</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Current status: %d\n&#34;</span>, <span style="color:#a6e22e">currentStatus</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This pattern provides type safety, preventing you from assigning arbitrary integers to your enum type.</p>
<h2 id="string-representation-for-golang-enums">String Representation for Golang Enums</h2>
<p>One limitation of the basic enum patterns is that they don&rsquo;t provide a built-in way to convert enum values to strings. Let&rsquo;s implement this functionality:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Direction</span> <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">North</span> <span style="color:#a6e22e">Direction</span> = <span style="color:#66d9ef">iota</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">East</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">South</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">West</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">d</span> <span style="color:#a6e22e">Direction</span>) <span style="color:#a6e22e">String</span>() <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> [<span style="color:#f92672">...</span>]<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">&#34;North&#34;</span>, <span style="color:#e6db74">&#34;East&#34;</span>, <span style="color:#e6db74">&#34;South&#34;</span>, <span style="color:#e6db74">&#34;West&#34;</span>}[<span style="color:#a6e22e">d</span>]
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">d</span> <span style="color:#a6e22e">Direction</span> = <span style="color:#a6e22e">East</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Direction:&#34;</span>, <span style="color:#a6e22e">d</span>)  <span style="color:#75715e">// Prints: Direction: East</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>By implementing the <code>String()</code> method, we enable automatic string conversion when printing the enum value.</p>
<h2 id="bitmask-enums-in-golang">Bitmask Enums in Golang</h2>
<p>For cases where you need to combine multiple enum values (flags), you can use bitmasks:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Permission</span> <span style="color:#66d9ef">uint</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Read</span> <span style="color:#a6e22e">Permission</span> = <span style="color:#ae81ff">1</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#66d9ef">iota</span>  <span style="color:#75715e">// 1 (001)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Write</span>                        <span style="color:#75715e">// 2 (010)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Execute</span>                      <span style="color:#75715e">// 4 (100)</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">p</span> <span style="color:#a6e22e">Permission</span>) <span style="color:#a6e22e">String</span>() <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">result</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">p</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Read</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">result</span> <span style="color:#f92672">+=</span> <span style="color:#e6db74">&#34;Read &#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">p</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Write</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">result</span> <span style="color:#f92672">+=</span> <span style="color:#e6db74">&#34;Write &#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">p</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Execute</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">result</span> <span style="color:#f92672">+=</span> <span style="color:#e6db74">&#34;Execute &#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">result</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">perm</span> <span style="color:#a6e22e">Permission</span> = <span style="color:#a6e22e">Read</span> | <span style="color:#a6e22e">Write</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Permissions:&#34;</span>, <span style="color:#a6e22e">perm</span>)  <span style="color:#75715e">// Prints: Permissions: Read Write</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Check if Write permission is granted</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">perm</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Write</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Write permission is granted&#34;</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Add Execute permission</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">perm</span> <span style="color:#f92672">|=</span> <span style="color:#a6e22e">Execute</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Updated permissions:&#34;</span>, <span style="color:#a6e22e">perm</span>)  <span style="color:#75715e">// Prints: Updated permissions: Read Write Execute</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This pattern is particularly useful for configuration options or permission systems.</p>
<h2 id="advanced-golang-enum-with-behavior">Advanced Golang Enum with Behavior</h2>
<p>We can extend our enum pattern to include behavior by adding methods:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;strings&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">LogLevel</span> <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Debug</span> <span style="color:#a6e22e">LogLevel</span> = <span style="color:#66d9ef">iota</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Info</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Warning</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Error</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Fatal</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// String returns the string representation of the log level</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">l</span> <span style="color:#a6e22e">LogLevel</span>) <span style="color:#a6e22e">String</span>() <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> [<span style="color:#f92672">...</span>]<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">&#34;DEBUG&#34;</span>, <span style="color:#e6db74">&#34;INFO&#34;</span>, <span style="color:#e6db74">&#34;WARNING&#34;</span>, <span style="color:#e6db74">&#34;ERROR&#34;</span>, <span style="color:#e6db74">&#34;FATAL&#34;</span>}[<span style="color:#a6e22e">l</span>]
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Color returns ANSI color code for console output</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">l</span> <span style="color:#a6e22e">LogLevel</span>) <span style="color:#a6e22e">Color</span>() <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> [<span style="color:#f92672">...</span>]<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;\033[36m&#34;</span>, <span style="color:#75715e">// Cyan for Debug</span>
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;\033[32m&#34;</span>, <span style="color:#75715e">// Green for Info</span>
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;\033[33m&#34;</span>, <span style="color:#75715e">// Yellow for Warning</span>
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;\033[31m&#34;</span>, <span style="color:#75715e">// Red for Error</span>
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;\033[35m&#34;</span>, <span style="color:#75715e">// Magenta for Fatal</span>
</span></span><span style="display:flex;"><span>    }[<span style="color:#a6e22e">l</span>]
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Log prints a message with the appropriate level and color</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">l</span> <span style="color:#a6e22e">LogLevel</span>) <span style="color:#a6e22e">Log</span>(<span style="color:#a6e22e">message</span> <span style="color:#66d9ef">string</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">reset</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;\033[0m&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;%s[%s]%s %s\n&#34;</span>, <span style="color:#a6e22e">l</span>.<span style="color:#a6e22e">Color</span>(), <span style="color:#a6e22e">l</span>.<span style="color:#a6e22e">String</span>(), <span style="color:#a6e22e">reset</span>, <span style="color:#a6e22e">message</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Info</span>.<span style="color:#a6e22e">Log</span>(<span style="color:#e6db74">&#34;Application started&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Debug</span>.<span style="color:#a6e22e">Log</span>(<span style="color:#e6db74">&#34;Connection details: localhost:8080&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Warning</span>.<span style="color:#a6e22e">Log</span>(<span style="color:#e6db74">&#34;High memory usage detected&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Error</span>.<span style="color:#a6e22e">Log</span>(<span style="color:#e6db74">&#34;Failed to connect to database&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Parse log level from string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">userInput</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;warning&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">level</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">Debug</span>; <span style="color:#a6e22e">level</span> <span style="color:#f92672">&lt;=</span> <span style="color:#a6e22e">Fatal</span>; <span style="color:#a6e22e">level</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">EqualFold</span>(<span style="color:#a6e22e">userInput</span>, <span style="color:#a6e22e">level</span>.<span style="color:#a6e22e">String</span>()) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Parsed log level: %s\n&#34;</span>, <span style="color:#a6e22e">level</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">break</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This example demonstrates how to add rich behavior to enum types, making them more powerful and expressive.</p>
<h2 id="enum-validation-in-golang">Enum Validation in Golang</h2>
<p>When accepting enum values from external sources (like API requests), validation is crucial:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;encoding/json&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">PaymentMethod</span> <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">CreditCard</span> <span style="color:#a6e22e">PaymentMethod</span> = <span style="color:#66d9ef">iota</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">DebitCard</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">BankTransfer</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">PayPal</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Crypto</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">p</span> <span style="color:#a6e22e">PaymentMethod</span>) <span style="color:#a6e22e">String</span>() <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> [<span style="color:#f92672">...</span>]<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#e6db74">&#34;CreditCard&#34;</span>, <span style="color:#e6db74">&#34;DebitCard&#34;</span>, <span style="color:#e6db74">&#34;BankTransfer&#34;</span>, <span style="color:#e6db74">&#34;PayPal&#34;</span>, <span style="color:#e6db74">&#34;Crypto&#34;</span>}[<span style="color:#a6e22e">p</span>]
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">p</span> <span style="color:#a6e22e">PaymentMethod</span>) <span style="color:#a6e22e">IsValid</span>() <span style="color:#66d9ef">bool</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">p</span> <span style="color:#f92672">&gt;=</span> <span style="color:#a6e22e">CreditCard</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">p</span> <span style="color:#f92672">&lt;=</span> <span style="color:#a6e22e">Crypto</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// MarshalJSON custom JSON marshaling</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">p</span> <span style="color:#a6e22e">PaymentMethod</span>) <span style="color:#a6e22e">MarshalJSON</span>() ([]<span style="color:#66d9ef">byte</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">String</span>())
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// UnmarshalJSON custom JSON unmarshaling</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">p</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">PaymentMethod</span>) <span style="color:#a6e22e">UnmarshalJSON</span>(<span style="color:#a6e22e">data</span> []<span style="color:#66d9ef">byte</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">s</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>(<span style="color:#a6e22e">data</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">s</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Map string to enum value</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">methodMap</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#a6e22e">PaymentMethod</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;CreditCard&#34;</span>:    <span style="color:#a6e22e">CreditCard</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;DebitCard&#34;</span>:     <span style="color:#a6e22e">DebitCard</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;BankTransfer&#34;</span>:  <span style="color:#a6e22e">BankTransfer</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;PayPal&#34;</span>:        <span style="color:#a6e22e">PayPal</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;Crypto&#34;</span>:        <span style="color:#a6e22e">Crypto</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">val</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">methodMap</span>[<span style="color:#a6e22e">s</span>]; <span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">*</span><span style="color:#a6e22e">p</span> = <span style="color:#a6e22e">val</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;invalid payment method: %s&#34;</span>, <span style="color:#a6e22e">s</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Payment</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Amount</span>  <span style="color:#66d9ef">float64</span>       <span style="color:#e6db74">`json:&#34;amount&#34;`</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Method</span>  <span style="color:#a6e22e">PaymentMethod</span> <span style="color:#e6db74">`json:&#34;method&#34;`</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Details</span> <span style="color:#66d9ef">string</span>        <span style="color:#e6db74">`json:&#34;details&#34;`</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">ProcessPayment</span>(<span style="color:#a6e22e">p</span> <span style="color:#a6e22e">Payment</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">Method</span>.<span style="color:#a6e22e">IsValid</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;invalid payment method: %v&#34;</span>, <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">Method</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Processing %s payment of $%.2f\n&#34;</span>, <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">Method</span>, <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">Amount</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Valid payment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">paymentJSON</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">`{&#34;amount&#34;: 99.99, &#34;method&#34;: &#34;PayPal&#34;, &#34;details&#34;: &#34;user@example.com&#34;}`</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">payment</span> <span style="color:#a6e22e">Payment</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">paymentJSON</span>), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">payment</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Error:&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ProcessPayment</span>(<span style="color:#a6e22e">payment</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Error:&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Invalid payment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">invalidJSON</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">`{&#34;amount&#34;: 199.99, &#34;method&#34;: &#34;Bitcoin&#34;, &#34;details&#34;: &#34;wallet_address&#34;}`</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">invalidPayment</span> <span style="color:#a6e22e">Payment</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">invalidJSON</span>), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">invalidPayment</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Error:&#34;</span>, <span style="color:#a6e22e">err</span>)  <span style="color:#75715e">// This will print: Error: invalid payment method: Bitcoin</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This pattern demonstrates how to handle JSON serialization/deserialization and validation for enum types, which is essential for API development.</p>
<h2 id="implementing-stringer-interface-automatically">Implementing Stringer Interface Automatically</h2>
<p>Writing the <code>String()</code> method manually for large enums can be tedious. The <code>stringer</code> tool can generate this code for you:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">//go:generate stringer -type=Season</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Season</span> <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Winter</span> <span style="color:#a6e22e">Season</span> = <span style="color:#66d9ef">iota</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Spring</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Summer</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Autumn</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">Winter</span>)  <span style="color:#75715e">// Prints: Winter</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">Summer</span>)  <span style="color:#75715e">// Prints: Summer</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>To use this, install the stringer tool and run go generate:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>go install golang.org/x/tools/cmd/stringer@latest
</span></span><span style="display:flex;"><span>go generate
</span></span></code></pre></div><p>This will create a file named <code>season_string.go</code> with the <code>String()</code> method implementation.</p>
<h2 id="conclusion">Conclusion</h2>
<p>While Golang doesn&rsquo;t have built-in enum types, it provides flexible and powerful patterns to implement them. From simple constants to type-safe enums with behavior, these patterns offer different trade-offs in terms of simplicity, type safety, and functionality.</p>
<p>By choosing the right enum pattern for your specific use case, you can write more maintainable, type-safe, and expressive Go code. Remember that the best pattern depends on your requirements - use simpler approaches for basic needs and more advanced patterns when additional functionality is required.</p>
<p>The absence of a dedicated enum type in Golang is not a limitation but rather an opportunity to implement exactly what you need with the language&rsquo;s existing features.</p>
<p>For more on the language features behind these patterns, see <a href="/posts/example-of-how-generics-simplify-golang/">how Golang generics minimize the amount of code you need to write</a> and <a href="/posts/mastering-for-loops-in-go/">mastering Golang for loops</a>. For what has landed in the language more recently, see <a href="/posts/exciting-features-in-go-1-25/">exciting features coming in Go 1.25</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
    </item>
    <item>
      <title>Mastering Golang For Loop: A Comprehensive Guide</title>
      <link>https://webdevstation.com/posts/mastering-for-loops-in-go/</link>
      <pubDate>Fri, 23 May 2025 22:25:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/mastering-for-loops-in-go/</guid>
      <description>Learn how to master Go&#39;s for loop syntax with practical examples covering basic iteration, range loops, nested loops, and advanced patterns for efficient Go…</description>
      <content:encoded><![CDATA[<p>The Golang for loop is a fundamental control structure that makes Go programming both powerful and elegant. In this comprehensive guide, we&rsquo;ll explore the different ways to use for loops in Golang, from basic iteration to more advanced patterns that every Go developer should master.</p>
<h2 id="the-basic-golang-for-loop-syntax">The Basic Golang For Loop Syntax</h2>
<p>The standard Golang for loop has a clean syntax that will feel familiar to developers coming from C-style languages:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Basic for loop with a single condition</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">5</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Iteration:&#34;</span>, <span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This is equivalent to a traditional <code>for</code> loop in other languages. The three components are:</p>
<ol>
<li>Initialization: <code>i := 0</code></li>
<li>Condition: <code>i &lt; 5</code></li>
<li>Post statement: <code>i++</code></li>
</ol>
<h2 id="while-style-golang-for-loops">While-Style Golang For Loops</h2>
<p>Unlike many languages, Golang doesn&rsquo;t have a separate <code>while</code> keyword. Instead, the Golang for loop can act as a while loop by using just a condition:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// While-like loop</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">count</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">count</span> &lt; <span style="color:#ae81ff">5</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Count:&#34;</span>, <span style="color:#a6e22e">count</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">count</span><span style="color:#f92672">++</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="infinite-golang-for-loops">Infinite Golang For Loops</h2>
<p>To create an infinite loop in Golang, simply use the <code>for</code> keyword without any conditions:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Infinite loop</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;This will run forever&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="range-based-golang-for-loops">Range-Based Golang For Loops</h2>
<p>One of the most common patterns in Golang is using the <code>for</code> loop with <code>range</code> to iterate over collections:</p>
<h3 id="iterating-over-slices-and-arrays">Iterating Over Slices and Arrays</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fruits</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">&#34;apple&#34;</span>, <span style="color:#e6db74">&#34;banana&#34;</span>, <span style="color:#e6db74">&#34;cherry&#34;</span>}
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Using range with index and value</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">index</span>, <span style="color:#a6e22e">fruit</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">fruits</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Index: %d, Fruit: %s\n&#34;</span>, <span style="color:#a6e22e">index</span>, <span style="color:#a6e22e">fruit</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If you only need the value</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">fruit</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">fruits</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">fruit</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If you only need the index</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">fruits</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Index:&#34;</span>, <span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="iterating-over-maps">Iterating Over Maps</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">userRoles</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;alice&#34;</span>: <span style="color:#e6db74">&#34;admin&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;bob&#34;</span>:   <span style="color:#e6db74">&#34;user&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;eve&#34;</span>:   <span style="color:#e6db74">&#34;editor&#34;</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">value</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">userRoles</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;%s is an %s\n&#34;</span>, <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">value</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="iterating-over-strings">Iterating Over Strings</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">str</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;Hello, 世界&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// By bytes</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; len(<span style="color:#a6e22e">str</span>); <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;%x &#34;</span>, <span style="color:#a6e22e">str</span>[<span style="color:#a6e22e">i</span>])
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// By runes (Unicode code points)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">str</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;%c &#34;</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="controlling-golang-for-loop-flow-with-break-and-continue">Controlling Golang For Loop Flow with Break and Continue</h2>
<h3 id="using-break">Using Break</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">10</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">5</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">break</span> <span style="color:#75715e">// Exit the loop when i is 5</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Breaking out of nested loops with labels</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">outer</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">3</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">j</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">j</span> &lt; <span style="color:#ae81ff">3</span>; <span style="color:#a6e22e">j</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">*</span><span style="color:#a6e22e">j</span> &gt; <span style="color:#ae81ff">2</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Breaking outer loop&#34;</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">break</span> <span style="color:#a6e22e">outer</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;%d*%d=%d\n&#34;</span>, <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">j</span>, <span style="color:#a6e22e">i</span><span style="color:#f92672">*</span><span style="color:#a6e22e">j</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="using-continue">Using Continue</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">5</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">continue</span> <span style="color:#75715e">// Skip even numbers</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Odd:&#34;</span>, <span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="common-golang-for-loop-patterns">Common Golang For Loop Patterns</h2>
<h3 id="processing-channels">Processing Channels</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ch</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">chan</span> <span style="color:#66d9ef">int</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Producer</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">defer</span> close(<span style="color:#a6e22e">ch</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">5</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">ch</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">i</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Consumer</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">num</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">ch</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Received:&#34;</span>, <span style="color:#a6e22e">num</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="time-based-loops">Time-Based Loops</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Run every second for 5 seconds</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">timeout</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">After</span>(<span style="color:#ae81ff">5</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">tick</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Tick</span>(<span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">timeout</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Timeout!&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">t</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">tick</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Tick at&#34;</span>, <span style="color:#a6e22e">t</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="golang-for-loop-performance-considerations">Golang For Loop Performance Considerations</h2>
<ol>
<li><strong>Pre-allocate slices</strong> when you know the final size to avoid reallocations.</li>
<li><strong>Reuse buffers</strong> when processing large datasets.</li>
<li><strong>Be careful with append</strong> in loops as it may cause multiple allocations.</li>
<li><strong>Consider concurrency</strong> for CPU-bound operations using goroutines.</li>
</ol>
<h2 id="conclusion">Conclusion</h2>
<p>The Golang for loop is a versatile construct that can handle all your iteration needs. Whether you&rsquo;re working with collections, channels, or need precise control over loop execution, Golang provides a clean and efficient way to express your logic. By understanding these patterns and best practices, you&rsquo;ll be able to write more idiomatic and performant Go code.</p>
<p>Remember that while <code>for</code> is the only loop construct in Golang, its flexibility makes it suitable for all iteration scenarios you might encounter in your programs. Mastering the Golang for loop is an essential skill for any Go developer.</p>
<p>Related reading: <a href="/posts/implementing-enums-in-golang/">implementing enums in Golang</a> for the other half of type-safe everyday Go, and <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools in Go with errgroup</a> for what happens when the body of your loop needs to run concurrently. Sorting is one of the most common things people do with a collection they have just iterated, and it has more edge cases than you would expect — see <a href="/posts/how-to-sort-strings-with-go-alphabetically-in-any-language/">how to sort strings with Go alphabetically in any language</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
    </item>
    <item>
      <title>Exciting Features Coming in Go 1.25: What to Expect</title>
      <link>https://webdevstation.com/posts/exciting-features-in-go-1-25/</link>
      <pubDate>Mon, 19 May 2025 08:57:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/exciting-features-in-go-1-25/</guid>
      <description>Explore the key improvements in Go 1.25, including stable profile-guided optimization, enhanced compiler performance, and new standard library additions for more…</description>
      <content:encoded><![CDATA[<p>Go 1.25 is on the horizon, and it&rsquo;s bringing some exciting improvements to the language. Let&rsquo;s explore the confirmed features that will make Go development even more productive and efficient.</p>
<h2 id="profile-guided-optimization">Profile-guided optimization</h2>
<p>One of the most significant additions in Go 1.25 is the stabilization of profile-guided optimization (PGO). After being introduced as an experimental feature in Go 1.20 and improved in subsequent releases, PGO is finally becoming a stable feature in Go 1.25.</p>
<p>Profile-guided optimization allows the compiler to optimize code based on runtime behavior profiles. By analyzing how your application actually runs in production, the compiler can make more intelligent optimization decisions.</p>
<p>Here&rsquo;s how you can use it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// First, run your application with profiling enabled</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">go</span> <span style="color:#a6e22e">build</span> <span style="color:#f92672">-</span><span style="color:#a6e22e">pgo</span>=<span style="color:#a6e22e">off</span> <span style="color:#a6e22e">myapp</span>.<span style="color:#66d9ef">go</span>
</span></span><span style="display:flex;"><span>.<span style="color:#f92672">/</span><span style="color:#a6e22e">myapp</span> <span style="color:#f92672">-</span><span style="color:#a6e22e">cpuprofile</span>=<span style="color:#a6e22e">profile</span>.<span style="color:#a6e22e">pprof</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Then compile with PGO</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">go</span> <span style="color:#a6e22e">build</span> <span style="color:#f92672">-</span><span style="color:#a6e22e">pgo</span>=<span style="color:#a6e22e">profile</span>.<span style="color:#a6e22e">pprof</span> <span style="color:#a6e22e">myapp</span>.<span style="color:#66d9ef">go</span>
</span></span></code></pre></div><p>The benefits are substantial - benchmarks have shown performance improvements of 2-7% for real-world applications, with some hot paths seeing even greater gains.</p>
<h2 id="improved-garbage-collection">Improved Garbage Collection</h2>
<p>Go 1.25 continues the work on improving the garbage collector&rsquo;s performance. The new version brings lower latency and better memory management, particularly for applications with large heaps.</p>
<p>The improvements focus on reducing GC pause times and making them more predictable, which is crucial for applications requiring consistent performance.</p>
<h2 id="enhanced-toolchain-security">Enhanced Toolchain Security</h2>
<p>Security gets a boost in Go 1.25 with improvements to the toolchain. The <code>go</code> command now includes better verification of module authenticity and additional checks to prevent supply chain attacks.</p>
<h2 id="better-error-handling">Better Error Handling</h2>
<p>Error handling in Go has been incrementally improving, and Go 1.25 continues this trend with enhancements to the <code>errors</code> package. The improvements make it easier to wrap, unwrap, and inspect errors in a more structured way.</p>
<p>One of the key improvements is the addition of new functions to the <code>errors</code> package that provide more flexibility when working with error chains. Let&rsquo;s look at some examples of how these new features can be used.</p>
<h3 id="enhanced-error-wrapping">Enhanced Error Wrapping</h3>
<p>Go 1.25 introduces a more powerful way to wrap errors with additional context while preserving the original error information:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;errors&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">processFile</span>(<span style="color:#e6db74">&#34;config.json&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Output: failed to process config.json: file not found</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Check if it&#39;s a specific error type</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">ErrNotFound</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;The file was not found, please check the path&#34;</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Get additional context from the error</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">fileErr</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">FileError</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">As</span>(<span style="color:#a6e22e">err</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">fileErr</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Error occurred with file: %s\n&#34;</span>, <span style="color:#a6e22e">fileErr</span>.<span style="color:#a6e22e">Filename</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// ErrNotFound is a sentinel error</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">ErrNotFound</span> = <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;file not found&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// FileError is a custom error type with additional context</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">FileError</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Filename</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Err</span>      <span style="color:#66d9ef">error</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">e</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">FileError</span>) <span style="color:#a6e22e">Error</span>() <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;failed to process %s: %v&#34;</span>, <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Filename</span>, <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Err</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">e</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">FileError</span>) <span style="color:#a6e22e">Unwrap</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Err</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">processFile</span>(<span style="color:#a6e22e">filename</span> <span style="color:#66d9ef">string</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Simulate a file not found error</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">FileError</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Filename</span>: <span style="color:#a6e22e">filename</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Err</span>:      <span style="color:#a6e22e">ErrNotFound</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="new-error-grouping">New Error Grouping</h3>
<p>Go 1.25 introduces a new way to handle multiple errors together, which is particularly useful for concurrent operations:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;errors&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;sync&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Process multiple files concurrently</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">filenames</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">&#34;config.json&#34;</span>, <span style="color:#e6db74">&#34;data.csv&#34;</span>, <span style="color:#e6db74">&#34;settings.yaml&#34;</span>}
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Create a new error group</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">errGroup</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">ErrorGroup</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Use a wait group to wait for all goroutines</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">wg</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">filename</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">filenames</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">filename</span> <span style="color:#66d9ef">string</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Process the file</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">processFile</span>(<span style="color:#a6e22e">filename</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#75715e">// Add the error to the group</span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">errGroup</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }(<span style="color:#a6e22e">filename</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Wait for all goroutines to complete</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Check if any errors occurred</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">errGroup</span>.<span style="color:#a6e22e">Err</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Errors occurred during processing:&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// We can also iterate through individual errors</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">errGroup</span>.<span style="color:#a6e22e">Range</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>) <span style="color:#66d9ef">bool</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;- %v\n&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">true</span> <span style="color:#75715e">// continue iteration</span>
</span></span><span style="display:flex;"><span>        })
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="improved-error-formatting">Improved Error Formatting</h3>
<p>Go 1.25 also improves how errors are formatted, making it easier to get meaningful information when debugging:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;errors&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">deepFunction</span>()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Print the error with detailed formatting</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;%+v\n&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Output includes the error message and stack trace</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Get a simplified view</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;%v\n&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Output: level3: level2: level1: base error</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">deepFunction</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;level3: %w&#34;</span>, <span style="color:#a6e22e">middleFunction</span>())
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">middleFunction</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;level2: %w&#34;</span>, <span style="color:#a6e22e">baseFunction</span>())
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">baseFunction</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;level1: %w&#34;</span>, <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;base error&#34;</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>These improvements make error handling in Go more robust and expressive, while still maintaining the simplicity and explicitness that Go is known for.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Go 1.25 represents another solid step forward for the language, focusing on performance, security, and developer experience. The stable release of profile-guided optimization is particularly exciting, as it allows for more intelligent performance optimizations based on real-world usage patterns.</p>
<p>As always, Go maintains its commitment to backward compatibility, so you can upgrade with confidence knowing your existing code will continue to work.</p>
<p>Stay tuned for the official release, and in the meantime, you can try out the beta versions to get a head start on these exciting new features.</p>
<p>If you are still weighing Go up rather than upgrading it, <a href="/posts/why-use-golang/">why use Golang</a> makes the wider case. And two additions from recent releases that changed how I write everyday code: <a href="/posts/structured-logging-in-go-with-slog/">structured logging with log/slog</a> and the per-iteration loop variables covered in <a href="/posts/mastering-for-loops-in-go/">mastering Golang for loops</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>An Easy Way to Generate QR Codes Fast</title>
      <link>https://webdevstation.com/posts/aneasywaytogenerateqrcodefast/</link>
      <pubDate>Thu, 08 May 2025 17:00:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/aneasywaytogenerateqrcodefast/</guid>
      <description>Discover the most efficient ways to create QR codes for your projects with these powerful tools and techniques that every developer should know.</description>
      <content:encoded><![CDATA[<p>Every few months a project of mine needs a QR code — a link on a conference badge, Wi-Fi credentials for an office, a deep link into a mobile app. Each time I rediscover that generating one is far easier than it looks, provided you know which tool to reach for.</p>
<h2 id="the-surprising-power-of-qr-codes-in-modern-development">The Surprising Power of QR Codes in Modern Development</h2>
<p>Remember when QR codes seemed like a passing tech fad? Fast forward to today, and these pixelated squares have revolutionized how we connect the physical and digital worlds. As developers, we&rsquo;re constantly looking for frictionless ways to bridge this gap, and QR codes offer an elegant solution hiding in plain sight.</p>
<p>I&rsquo;ve spent considerable time exploring various QR code generation methods for both client projects and personal use. In this post, I&rsquo;ll share the most efficient approaches I&rsquo;ve discovered, helping you implement QR functionality without unnecessary complexity.</p>
<h2 id="why-qr-codes-are-a-developers-secret-weapon">Why QR Codes Are a Developer&rsquo;s Secret Weapon</h2>
<p>Before we dive into implementation specifics, let&rsquo;s acknowledge what makes QR codes particularly valuable in our development toolkit:</p>
<ul>
<li><strong>Friction reduction</strong> — Eliminate tedious URL typing with a simple scan</li>
<li><strong>Protocol versatility</strong> — Handle everything from basic URLs to complex Wi-Fi configurations</li>
<li><strong>Error correction</strong> — Built-in redundancy ensures functionality even with partial damage</li>
<li><strong>Adaptive data density</strong> — Automatically optimize the pattern based on content length</li>
<li><strong>Offline functionality</strong> — No internet required for the scanning process itself</li>
</ul>
<h2 id="streamlined-qr-generation-methods">Streamlined QR Generation Methods</h2>
<h3 id="chrome-devtools-the-hidden-feature-youre-missing">Chrome DevTools: The Hidden Feature You&rsquo;re Missing</h3>
<p>If you need a quick QR code during development, Chrome has you covered with a built-in generator that many developers overlook.</p>
<ol>
<li>Open DevTools (F12 or Cmd+Opt+I)</li>
<li>Click the &ldquo;three dots&rdquo; menu</li>
<li>Navigate to More tools → Network conditions</li>
<li>Find the QR code icon in the toolbar</li>
</ol>
<p>This approach is perfect for quickly sharing your localhost development server with mobile devices for testing.</p>
<h3 id="power-user-libraries-for-programmatic-generation">Power-User Libraries for Programmatic Generation</h3>
<p>When building QR functionality into your applications, these libraries offer the best balance of performance and flexibility:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#75715e">// Using qrcode.js - one of my favorite lightweight options
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">QRCode</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;qrcode&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Generate QR code to a canvas element
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">QRCode</span>.<span style="color:#a6e22e">toCanvas</span>(document.<span style="color:#a6e22e">getElementById</span>(<span style="color:#e6db74">&#39;canvas&#39;</span>), <span style="color:#e6db74">&#39;https://webdevstation.com&#39;</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">errorCorrectionLevel</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;H&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">margin</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">scale</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">8</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">color</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">dark</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;#000000&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">light</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;#ffffff&#39;</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}, <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">error</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">error</span>(<span style="color:#a6e22e">error</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">&#39;QR code generated!&#39;</span>);
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>For backend implementations, Go has some excellent packages. I often use this approach in my Go projects:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Using go-qrcode for server-side QR generation</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;image/png&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;os&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/boombuler/barcode&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/boombuler/barcode/qr&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Create the QR code</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">qrCode</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">qr</span>.<span style="color:#a6e22e">Encode</span>(<span style="color:#e6db74">&#34;https://webdevstation.com&#34;</span>, <span style="color:#a6e22e">qr</span>.<span style="color:#a6e22e">M</span>, <span style="color:#a6e22e">qr</span>.<span style="color:#a6e22e">Auto</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Scale the QR code to 256x256 pixels</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">qrCode</span>, <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">barcode</span>.<span style="color:#a6e22e">Scale</span>(<span style="color:#a6e22e">qrCode</span>, <span style="color:#ae81ff">256</span>, <span style="color:#ae81ff">256</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Create a file to save the QR code</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">file</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Create</span>(<span style="color:#e6db74">&#34;webdevstation-qr.png&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">file</span>.<span style="color:#a6e22e">Close</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Save the QR code as PNG</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">png</span>.<span style="color:#a6e22e">Encode</span>(<span style="color:#a6e22e">file</span>, <span style="color:#a6e22e">qrCode</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;QR code generated successfully&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="the-hidden-gem-qrcodereact-for-react-applications">The Hidden Gem: QRCode.react for React Applications</h3>
<p>For React developers, I&rsquo;ve been particularly impressed with the <code>qrcode.react</code> package, which offers seamless integration with minimal overhead:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-jsx" data-lang="jsx"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">React</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;react&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">QRCodeSVG</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;qrcode.react&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">MyQRCode</span> <span style="color:#f92672">=</span> ({ <span style="color:#a6e22e">url</span> }) =&gt; (
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">QRCodeSVG</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">value</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">url</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">size</span><span style="color:#f92672">=</span>{<span style="color:#ae81ff">256</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">bgColor</span><span style="color:#f92672">=</span>{<span style="color:#e6db74">&#34;#ffffff&#34;</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fgColor</span><span style="color:#f92672">=</span>{<span style="color:#e6db74">&#34;#000000&#34;</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">level</span><span style="color:#f92672">=</span>{<span style="color:#e6db74">&#34;H&#34;</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">includeMargin</span><span style="color:#f92672">=</span>{<span style="color:#66d9ef">false</span>}
</span></span><span style="display:flex;"><span>  /&gt;
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">default</span> <span style="color:#a6e22e">MyQRCode</span>;
</span></span></code></pre></div><p>The SVG output ensures sharp rendering at any size while keeping the bundle size minimal.</p>
<h2 id="advanced-techniques-for-professional-implementation">Advanced Techniques for Professional Implementation</h2>
<h3 id="dynamic-qr-codes-the-game-changer">Dynamic QR Codes: The Game Changer</h3>
<p>Statically generated QR codes work well for permanent links, but for marketing campaigns or situations where the destination might change, dynamic QR codes offer a crucial advantage.</p>
<p>I recently built a solution using Firebase Dynamic Links combined with custom QR generation:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">getDynamicLink</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;firebase/dynamic-links&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">QRCode</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;qrcode&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">async</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">generateDynamicQRCode</span>(<span style="color:#a6e22e">destinationUrl</span>, <span style="color:#a6e22e">campaignId</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Create a short dynamic link first
</span></span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">dynamicLink</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">getDynamicLink</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">longDynamicLink</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`https://myapp.page.link/?link=</span><span style="color:#e6db74">${</span>encodeURIComponent(<span style="color:#a6e22e">destinationUrl</span>)<span style="color:#e6db74">}</span><span style="color:#e6db74">&amp;apn=com.myapp&amp;afl=</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">campaignId</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">suffix</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">option</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;SHORT&#39;</span> }
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Then generate QR code with the dynamic link
</span></span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">qrCodeDataUrl</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">QRCode</span>.<span style="color:#a6e22e">toDataURL</span>(<span style="color:#a6e22e">dynamicLink</span>.<span style="color:#a6e22e">shortLink</span>, {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">errorCorrectionLevel</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;H&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">margin</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">2</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">color</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">dark</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;#3B82F6&#39;</span>, <span style="color:#75715e">// Blue
</span></span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">light</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;#ffffff&#39;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">qrCodeDataUrl</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This approach allows you to change the destination URL without regenerating the QR code itself – invaluable for printed materials or permanent displays.</p>
<h3 id="design-techniques-that-increase-scan-rates">Design Techniques That Increase Scan Rates</h3>
<p>A common misconception is that QR codes must remain strictly black and white. In reality, QR codes can maintain functionality with significant customization when implemented correctly.</p>
<p>The key is understanding the error correction levels:</p>
<ul>
<li><strong>Level L:</strong> 7% error correction</li>
<li><strong>Level M:</strong> 15% error correction</li>
<li><strong>Level Q:</strong> 25% error correction</li>
<li><strong>Level H:</strong> 30% error correction</li>
</ul>
<p>With Level H, you can integrate logos, use custom colors, and even apply moderate styling effects while maintaining reliable functionality.</p>
<h2 id="best-practices-from-real-world-implementation">Best Practices from Real-World Implementation</h2>
<p>Through trial and error across numerous projects, I&rsquo;ve learned that successful QR code implementation comes down to these critical factors:</p>
<ol>
<li>
<p><strong>Test extensively</strong> — Always verify your QR codes on multiple devices and in varying lighting conditions</p>
</li>
<li>
<p><strong>Prioritize contrast</strong> — While custom colors are possible, maintaining high contrast between the foreground and background is essential</p>
</li>
<li>
<p><strong>Size appropriately</strong> — The minimum recommended size is 2cm × 2cm for reliable scanning, but always err toward larger when possible</p>
</li>
<li>
<p><strong>Add clear instructions</strong> — A simple &ldquo;Scan me&rdquo; prompt significantly increases user engagement</p>
</li>
<li>
<p><strong>Include fallback options</strong> — Always provide an alternative access method for users who may have difficulty scanning</p>
</li>
</ol>
<h2 id="the-user-friendly-alternative">The User-Friendly Alternative</h2>
<p>While libraries provide great flexibility for developers, sometimes you need a quick solution without writing code. During my research, I discovered <a href="https://qrcodia.com/">QRcodia</a> – a tool that embodies the clean, minimalist approach I value in web services.</p>
<p>Unlike most free generators that bombard you with ads or hide essential features behind paywalls, <a href="https://qrcodia.com/">QRcodia</a> offers a streamlined experience with useful features completely free:</p>
<ul>
<li><strong>Multiple QR code types</strong>: Create codes for URLs, text, WiFi credentials, contact information (vCard), and even calendar events</li>
<li><strong>Customizable design</strong>: Adjust colors, add logos, and change shapes to match your brand</li>
<li><strong>High-quality downloads</strong>: Export as scalable SVG or high-resolution PNG</li>
<li><strong>No account required</strong>: Generate and download immediately without registration</li>
</ul>
<p>For one of my recent projects where team members needed to frequently create QR codes but weren&rsquo;t developers, I recommended this tool. The ability to customize the visual appearance while maintaining scannability made it particularly valuable for creating branded marketing materials.</p>
<h2 id="conclusion-simplicity-wins">Conclusion: Simplicity Wins</h2>
<p>After exploring dozens of QR code generation methods, I&rsquo;ve found that the most effective approach is nearly always the simplest one that meets your requirements. While feature-rich QR services exist, they often add unnecessary complexity.</p>
<p>My go-to solution remains a lightweight library like qrcode.js for frontend applications or the boombuler/barcode package for Go backend systems. For more complex needs requiring analytics or dynamic destinations, a specialized service can be worth considering.</p>
<p>The beauty of QR technology lies in its accessibility and simplicity – qualities we should preserve in our implementations.</p>
<p>What&rsquo;s your experience with QR code implementation? Have you discovered any clever techniques or libraries that have simplified your development process? I&rsquo;d love to hear about your approaches in the comments below.</p>
<p>If you build a small generator of your own, <a href="/posts/one-of-thee-easiest-ways-to-host-go-web-apps/">one of the easiest ways to host your Go web app</a> covers getting it online for about five dollars a month. And for another tool that quietly improved my week, see <a href="/posts/enhancing-reading-experience-with-music-and-booktuning/">enhancing your reading experience with music and BookTuning</a>.</p>]]></content:encoded>
      <category>Web Development</category>
      <category>Tools</category>
    </item>
    <item>
      <title>One of The Easiest Ways to Host your Go Web App</title>
      <link>https://webdevstation.com/posts/one-of-thee-easiest-ways-to-host-go-web-apps/</link>
      <pubDate>Tue, 05 Sep 2023 09:53:15 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/one-of-thee-easiest-ways-to-host-go-web-apps/</guid>
      <description>Discover how to host Go web applications for as little as $5 per month using DigitalOcean and Docker. Learn this cost-effective, scalable deployment method with…</description>
      <content:encoded><![CDATA[<p>Hello! In this post, I will explain the cost-effective method I use to host my Go web applications
with varying levels of complexity, all starting from as low as $5 per month. This method also allows to easy
deploy and scale your golang application.</p>
<p>As an example, this is how I host <a href="https://whattoreadafter.xyz" title="What to read after: AI book recommendations">whattoreadafter.xyz</a>, a service that recommends books based on the book you just finished reading, by using AI.</p>
<p>Starting off, let&rsquo;s enumerate the tools we&rsquo;ll be using alongside Golang:</p>
<ul>
<li><a href="https://m.do.co/c/2a29ebc23e4a">DigitalOcean</a> - a cloud computing platform that provides virtual machines and other resources.</li>
<li><a href="https://www.docker.com/">Docker</a> - a set of platform as a service products that use OS-level virtualization to deliver software in packages called containers.</li>
</ul>
<p>In order to follow along, you will need to have a DigitalOcean account. If you don&rsquo;t have one, you can
sign up <a href="https://m.do.co/c/2a29ebc23e4a">here</a> and get $200 in credit over 60 days.</p>
<p>Let&rsquo;s get started!
For simplicity sake, our application will be a simple web server that returns current time in UTC format.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">currentTimeHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">currentTime</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">UTC</span>()
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Fprintf</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;Current Time (UTC): %s&#34;</span>, <span style="color:#a6e22e">currentTime</span>.<span style="color:#a6e22e">Format</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">RFC3339</span>))
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandleFunc</span>(<span style="color:#e6db74">&#34;/&#34;</span>, <span style="color:#a6e22e">currentTimeHandler</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Server is running on port 8080&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ListenAndServe</span>(<span style="color:#e6db74">&#34;:8080&#34;</span>, <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now let&rsquo;s host it!</p>
<p>In order for you to easier follow along, I will list the steps we will take to host our application:</p>
<h2 id="1-prepare-a-dockerfile-for-our-application-and-place-it-in-the-root-of-our-project-alongside-the-maingo-file">1. Prepare a Dockerfile for our application and place it in the root of our project alongside the <code>main.go</code> file.</h2>
<p>Example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-dockerfile" data-lang="dockerfile"><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> <span style="color:#e6db74">golang:alpine</span> <span style="color:#66d9ef">AS</span> <span style="color:#e6db74">builder</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">RUN</span> apk add --no-cache --update <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        git <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span>        ca-certificates<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">ADD</span> . /app<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">WORKDIR</span> <span style="color:#e6db74">/app</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">COPY</span> go.mod ./<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">RUN</span>  go mod download<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">RUN</span> CGO_ENABLED<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> GOOS<span style="color:#f92672">=</span>linux GOARCH<span style="color:#f92672">=</span>amd64 go build -a -o /main .<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> <span style="color:#e6db74">alpine</span><span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">COPY</span> --from<span style="color:#f92672">=</span>builder /main ./<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">RUN</span> chmod +x ./main<span style="color:#960050;background-color:#1e0010">
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">ENTRYPOINT</span> [<span style="color:#e6db74">&#34;./main&#34;</span>]<span style="color:#960050;background-color:#1e0010">
</span></span></span></code></pre></div><p> </p>
<h2 id="2-push-your-code-to-a-github-repository-you-can-make-it-private-if-you-want">2. Push your code to a GitHub repository. You can make it private if you want.</h2>
<p> </p>
<h2 id="3-go-to-digitalocean-and-create-a-new-app-you-can-do-so-by-clicking-on-the-apps-tab-in-the-left-sidebar-and-then-clicking-create-app">3. Go to <a href="https://cloud.digitalocean.com/apps">DigitalOcean</a> and create a new App. You can do so by clicking on the &ldquo;Apps&rdquo; tab in the left sidebar and then clicking &ldquo;Create App&rdquo;.</h2>
<p> </p>
<h2 id="4-select-github-as-your-source-and-click-continue">4. Select &ldquo;GitHub&rdquo; as your &ldquo;Source&rdquo; and click &ldquo;Continue&rdquo;.</h2>
<p><img src="/images/2023/do1.png" alt="Hosting Golang on digitalocean" title="Hosting Golang apps on digitalocean"></p>
<h2 id="5-select-the-repository-you-want-to-deploy-and-click-next">5. Select the repository you want to deploy and click &ldquo;Next&rdquo;.</h2>
<p><img src="/images/2023/do2.png" alt="Hosting Golang on digitalocean" title="Hosting Golang apps on digitalocean"></p>
<h2 id="6-select-dockerfile-as-your-build-type-and-click-next">6. Select &ldquo;Dockerfile&rdquo; as your &ldquo;Build type&rdquo; and click &ldquo;Next&rdquo;.</h2>
<p><img src="/images/2023/do3.png" alt="Hosting Golang on digitalocean" title="Hosting Golang apps on digitalocean"></p>
<h2 id="7-configure-environment-variables-if-you-need-to-and-click-next">7. Configure environment variables if you need to and click &ldquo;Next&rdquo;.</h2>
<p> </p>
<h2 id="8-select-region-for-your-app-and-click-next">8. Select region for your app and click &ldquo;Next&rdquo;.</h2>
<p><img src="/images/2023/do4.png" alt="Hosting Golang on digitalocean" title="Hosting Golang apps on digitalocean"></p>
<h2 id="9-in-the-billing-section-select-the-plan-you-want-to-use-you-can-start-with-the-5-per-month-plan-and-scale-up-later-if-you-need-to">9. In the billing section, select the plan you want to use. You can start with the $5 per month plan and scale up later if you need to.</h2>
<p><img src="/images/2023/do5.png" alt="Hosting Golang on digitalocean" title="Hosting Golang apps on digitalocean"></p>
<h2 id="10-click-create-resources-and-wait-for-your-app-to-be-deployed">10. Click &ldquo;Create Resources&rdquo; and wait for your app to be deployed.</h2>
<p> </p>
<h2 id="11-once-your-app-is-deployed-you-can-access-it-by-clicking-on-the-live-app-link">11. Once your app is deployed, you can access it by clicking on the &ldquo;Live App&rdquo; link.</h2>
<p><img src="/images/2023/do6.png" alt="Hosting Golang on digitalocean" title="Hosting Golang apps on digitalocean"></p>
<p>That&rsquo;s it! You have successfully deployed your Golang application!</p>
<p>You can also add your own domain name to your app by clicking on the &ldquo;Settings&rdquo; tab and then clicking on &ldquo;Domains&rdquo;.</p>
<p>Now, every time you push a new commit to your repository, DigitalOcean will automatically build and deploy your application.
This is a great way to host your Golang applications, especially if you are just starting out and don&rsquo;t want to spend a lot of money on hosting.</p>
<p>Thank you for reading! If you have any questions, feel free to reach out to me on <a href="https://twitter.com/oleks_i">Twitter</a>.</p>
<p>Before you point real traffic at it, two things are worth having in place: <a href="/posts/graceful-shutdown-in-go-web-services/">graceful shutdown</a>, so redeploys stop dropping requests, and <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">a load test</a>, so you know what the box can actually take.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>DevOps</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>103 Early Hints in Go, or the new Way of How to Improve Performance of a Web Page written in Go</title>
      <link>https://webdevstation.com/posts/early-hints-in-go-or-the-way-of-how-to-improve-perforamnce-of-web-page/</link>
      <pubDate>Mon, 14 Nov 2022 19:40:21 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/early-hints-in-go-or-the-way-of-how-to-improve-perforamnce-of-web-page/</guid>
      <description>Learn how to implement HTTP 103 Early Hints in Go 1.19+ to significantly improve web page loading performance by enabling browsers to preload resources while waiting…</description>
      <content:encoded><![CDATA[<p>Since Go 1.19 we can use a new <code>103 (Early Hints)</code> http status code when we create web applications. Let&rsquo;s figure out how and when this could help us.
We are going to create a simple golang web server that servers some html content. One html page will be served with <code>103</code> header and another one without.
After loading comparison we will see how early hints can improve page performance.</p>
<p>Early hints is a special HTTP header that is sent before the web server sends the final HTTP response to the client. At this moment it&rsquo;s supported only by Chrome browser.
As soon as the browser requests a page, server immediately returns 103 early hints header. In the meantime, a server will generate a usual HTTP response. This helps us utilize in maximum the loading time by letting browser know what resources it should preload while waiting for the final response from a server.</p>
<p>Enough theory, let&rsquo;s write some code :)</p>
<p>First, I&rsquo;m going to create an index.html with some dummy structure. Also, I will load <code>bootsrap</code> frontend framework to simulate some heavy css and js references during page load.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">html</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">head</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">title</span>&gt;Hello!&lt;/<span style="color:#f92672">title</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">link</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css&#34;</span> <span style="color:#a6e22e">rel</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;stylesheet&#34;</span> <span style="color:#a6e22e">integrity</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi&#34;</span> <span style="color:#a6e22e">crossorigin</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;anonymous&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">head</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">body</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;p-2 bg-success&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">h1</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text-white&#34;</span>&gt;Hello!&lt;/<span style="color:#f92672">h1</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js&#34;</span> <span style="color:#a6e22e">integrity</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3&#34;</span> <span style="color:#a6e22e">crossorigin</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;anonymous&#34;</span>&gt;&lt;/<span style="color:#f92672">script</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">body</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">html</span>&gt;
</span></span></code></pre></div><p>Now we need to serve it. Let&rsquo;s create a server.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//go:embed index.html</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">index</span> <span style="color:#66d9ef">string</span> <span style="color:#75715e">// embeded index.html</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Starting server...&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Handler for the page without early hints.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandleFunc</span>(<span style="color:#e6db74">&#34;/1&#34;</span>, <span style="color:#a6e22e">noHintsHandler</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Handler for the page with early hints</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandleFunc</span>(<span style="color:#e6db74">&#34;/2&#34;</span>, <span style="color:#a6e22e">withHintsHandler</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ListenAndServe</span>(<span style="color:#e6db74">&#34;:8082&#34;</span>, <span style="color:#66d9ef">nil</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">noHintsHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">template</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Parse</span>(<span style="color:#a6e22e">index</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">w</span>, <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">withHintsHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Adding headers with preload information for bootstrap.min.css and bootstrap.bundle.min.js</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Add</span>(<span style="color:#e6db74">&#34;Link&#34;</span>, <span style="color:#e6db74">&#34;&lt;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css&gt;; rel=preload; as=style&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Add</span>(<span style="color:#e6db74">&#34;Link&#34;</span>, <span style="color:#e6db74">&#34;&lt;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js&gt;; rel=preload; as=script&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Sending 103 status code.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusEarlyHints</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">t</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">template</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Parse</span>(<span style="color:#a6e22e">index</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Sending 200 status code.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusOK</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">w</span>, <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now it&rsquo;s time to see our pages in action! Run our server <code>go run main.go</code>, open the page without early hints <code>http://localhost:8082/1</code> in Chrome,
open inspector, go to Lighthouse tab and click on &ldquo;Analyze page load&rdquo; button.
And this is what we can see:
<img src="/images/2022/1.png" alt="Chrome Lighthouse report for the Go page without early hints, showing a First Contentful Paint of 1492.8ms" title="Performance results for the page without early hints">
It takes a while until bootstrap resources got loaded by a browser. As result, FCP (First Contentful Paint) is <code>1492,8ms</code>.</p>
<p>Now, let&rsquo;s do the same for the page with the early hints <code>http://localhost:8082/2</code> And this is a result:
<img src="/images/2022/2.png" alt="Chrome Lighthouse report for the same page served with 103 Early Hints, showing a First Contentful Paint of 437.8ms" title="Performance results for the page with early hints">
As you can see, the page loaded much faster now. Bootstrap dependencies (bootstrap.min.css and bootstrap.bundle.min.js) were preloaded in the beginning and FCP now is <code>437,8ms</code>. More than 3 times faster, quite an impressive result!</p>
<p>However, it does not mean that you have to preload absolutely all resources now. Just try to experiment with these things, see how it affects your page performance and decide for yourself the right balance.</p>
<p>You can find the source code <a href="https://github.com/alexsergivan/blog-examples/tree/master/early-hints">here</a>.</p>
<p>If you want to measure the difference on your own service rather than take my numbers for it, <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">an easy way to load test your web apps</a> shows the setup I use. And for the wins that happen before the request even reaches your handler, have a look at <a href="/posts/how-to-make-nginx-cookie-aware/">how to make Nginx cache cookie aware</a>.</p>]]></content:encoded>
      <category>Performance Optimization</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>Example of how Golang generics minimize the amount of code you need to write</title>
      <link>https://webdevstation.com/posts/example-of-how-generics-simplify-golang/</link>
      <pubDate>Thu, 09 Jun 2022 15:41:52 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/example-of-how-generics-simplify-golang/</guid>
      <description>Explore practical examples of Go 1.18 generics in action by refactoring caching logic in a real-world application to write cleaner, more maintainable code with less…</description>
      <content:encoded><![CDATA[<p>I guess that almost everyone in the go community was exciting when Go 1.18 was released, especially because of generics.
Some days ago I decided to try generics in the real-world application, by refactoring some of its pieces, related to a caching logic.</p>
<p>In our web application, we have multiple resolvers that execute some sql queries and return data in different types. Obviously,
we want to prevent the database overloading by caching the same responses.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// MyExampleType1 type to serve example response 1.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">type</span> <span style="color:#a6e22e">MyExampleType1</span> <span style="color:#66d9ef">struct</span> {}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// MyExampleType2 type to serve example response 2.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">type</span> <span style="color:#a6e22e">MyExampleType2</span> <span style="color:#66d9ef">struct</span> {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// MyCachedResolver1 checks if there are any cached data by specific key.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If nothing in cache, queries the database and adds result to the redis cache.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MyCachedResolver1</span>(<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>) ([]<span style="color:#a6e22e">MyExampleType1</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;resolver_result&#34;</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">found</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">found</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">FetchSomethingHeavyFromDB</span>()
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">valueJson</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">value</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">valueJson</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span><span style="color:#f92672">*</span><span style="color:#ae81ff">12</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>	  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">val</span> []<span style="color:#a6e22e">MyExampleType1</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">value</span>.(<span style="color:#66d9ef">string</span>)), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">val</span>)
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	  }
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">val</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// MyCachedResolver2 checks if there are any cached data by specific key.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If nothing in cache, queries the database and adds result to the redis cache.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MyCachedResolver2</span>(<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>) ([]<span style="color:#a6e22e">MyExampleType2</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;resolver_result_2&#34;</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">found</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">found</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">FetchSomethingEvenMoreHeavierFromDB</span>()
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">valueJson</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">value</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">valueJson</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span><span style="color:#f92672">*</span><span style="color:#ae81ff">12</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>	  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">val</span> []<span style="color:#a6e22e">MyExampleType2</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">value</span>.(<span style="color:#66d9ef">string</span>)), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">val</span>)
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	  }
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">val</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    }
</span></span></code></pre></div><p>As you can see, there is some repetitive code that should be written due to the different types <code>MyExampleType1</code> and <code>MyExampleType2</code>.</p>
<p>Now, let&rsquo;s see how we can improve this situation by using generics. I&rsquo;m going to write a function <code>WithCache()</code> which will be responsible for
setting and getting data to/from redis cache.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>    <span style="color:#75715e">// WithCache adds exec function result into the redis cache.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Pay attention to &#34;T any&#34; that allows us to pass any type to this function.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">WithCache</span>[<span style="color:#a6e22e">T</span> <span style="color:#66d9ef">any</span>](<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>, <span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">key</span> <span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">exec</span> <span style="color:#66d9ef">func</span>() (<span style="color:#a6e22e">T</span>, <span style="color:#66d9ef">error</span>), <span style="color:#a6e22e">ttl</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>) (<span style="color:#a6e22e">T</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    	<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">found</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>    	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">result</span> <span style="color:#a6e22e">T</span>
</span></span><span style="display:flex;"><span>    	<span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">found</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// exec() is a function that should be executed to fetch needed data.</span>
</span></span><span style="display:flex;"><span>    		<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">exec</span>()
</span></span><span style="display:flex;"><span>    		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">result</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    		}
</span></span><span style="display:flex;"><span>    		<span style="color:#a6e22e">jsonValue</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">value</span>)
</span></span><span style="display:flex;"><span>    		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">result</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    		}
</span></span><span style="display:flex;"><span>    		<span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">jsonValue</span>, <span style="color:#a6e22e">ttl</span>)
</span></span><span style="display:flex;"><span>    		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    	}
</span></span><span style="display:flex;"><span>    	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">val</span> <span style="color:#a6e22e">T</span>
</span></span><span style="display:flex;"><span>    	<span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">value</span>.(<span style="color:#66d9ef">string</span>)), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">val</span>)
</span></span><span style="display:flex;"><span>    	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">result</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    	}
</span></span><span style="display:flex;"><span>    	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">val</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    }
</span></span></code></pre></div><p>After that our resolvers could be refactored into this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>     <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MyCachedResolver1</span>(<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>) ([]<span style="color:#a6e22e">MyExampleType1</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;resolver_result_1&#34;</span>
</span></span><span style="display:flex;"><span>       <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">WithCache</span>[[]<span style="color:#a6e22e">MyExampleType1</span>](<span style="color:#a6e22e">redisClient</span>, <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">key</span>, <span style="color:#66d9ef">func</span>() ([]<span style="color:#a6e22e">MyExampleType1</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>		 <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">FetchSomethingHeavyFromDB</span>()
</span></span><span style="display:flex;"><span>	   }, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span><span style="color:#f92672">*</span><span style="color:#ae81ff">12</span>)
</span></span><span style="display:flex;"><span>     }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MyCachedResolver2</span>(<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>) ([]<span style="color:#a6e22e">MyExampleType2</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;resolver_result_2&#34;</span>
</span></span><span style="display:flex;"><span>       <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">WithCache</span>[[]<span style="color:#a6e22e">MyExampleType2</span>](<span style="color:#a6e22e">redisClient</span>, <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">key</span>, <span style="color:#66d9ef">func</span>() ([]<span style="color:#a6e22e">MyExampleType2</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>		 <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">FetchSomethingEvenMoreHeavierFromDB</span>()
</span></span><span style="display:flex;"><span>	   }, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span><span style="color:#f92672">*</span><span style="color:#ae81ff">12</span>)
</span></span><span style="display:flex;"><span>     }
</span></span></code></pre></div><p>Now it looks much better and clearer!</p>
<p>The caching code this example refactors is the one from <a href="/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/">Ristretto — the most performant concurrent cache library for Go</a>, if you want the full context. For another place where a little type machinery removes a lot of duplication, see <a href="/posts/implementing-enums-in-golang/">implementing enums in Golang</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Concurrent Map Writing and Reading in Go, or how to deal with the data races.</title>
      <link>https://webdevstation.com/posts/concurrent-map-writing-and-reading-in-go/</link>
      <pubDate>Fri, 16 Jul 2021 11:10:38 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/concurrent-map-writing-and-reading-in-go/</guid>
      <description>Learn how to effectively handle concurrent map operations in Go using sync.Map and mutex solutions to avoid data race conditions and improve application performance.</description>
      <content:encoded><![CDATA[<p>This time, I will show you how to work with the maps in go effectively and prevent the occurrence of the data race errors. Data races happen when several goroutines access the same resource concurrently and at least one of the accesses is a write.</p>
<p>Let&rsquo;s write a simple program, which generates a map of numbers and print them to the console:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">numbers</span> = make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">int</span>, <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">generateNumbersMap</span>()
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">generateNumbersMap</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Write.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">numbers</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">i</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Read.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">numbers</span>[<span style="color:#a6e22e">i</span>])
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now, if we run it with the data race detector option <code>go run -race main.go</code>, we can see the printed list of numbers in the console without any data race problems.</p>
<p>Everything seems to be good. Is it? Let&rsquo;s add some concurrency to our super complex program 😄 and see what happens:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;sync&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">numbers</span> = make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">int</span>, <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">generateNumbersMap</span>()
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">generateNumbersMap</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">wg</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>{}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Write.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">i</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">numbers</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">i</span>
</span></span><span style="display:flex;"><span>		}(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Read.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">i</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">numbers</span>[<span style="color:#a6e22e">i</span>])
</span></span><span style="display:flex;"><span>		}(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If we run it now, in the console we can notice the data race errors:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#f92672">==================</span>
</span></span><span style="display:flex;"><span>WARNING: DATA RACE
</span></span><span style="display:flex;"><span>Write at 0x00c0001241b0 by goroutine 8:
</span></span><span style="display:flex;"><span>  runtime.mapassign_fast64<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /usr/local/opt/go/libexec/src/runtime/map_fast64.go:92 +0x0
</span></span><span style="display:flex;"><span>  main.generateNumbersMap.func1<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:68 +0xa4
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Previous write at 0x00c0001241b0 by goroutine 7:
</span></span><span style="display:flex;"><span>  runtime.mapassign_fast64<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /usr/local/opt/go/libexec/src/runtime/map_fast64.go:92 +0x0
</span></span><span style="display:flex;"><span>  main.generateNumbersMap.func1<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:68 +0xa4
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Goroutine <span style="color:#ae81ff">8</span> <span style="color:#f92672">(</span>running<span style="color:#f92672">)</span> created at:
</span></span><span style="display:flex;"><span>  main.generateNumbersMap<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:66 +0xb5
</span></span><span style="display:flex;"><span>  main.main<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:33 +0x2f
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Goroutine <span style="color:#ae81ff">7</span> <span style="color:#f92672">(</span>finished<span style="color:#f92672">)</span> created at:
</span></span><span style="display:flex;"><span>  main.generateNumbersMap<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:66 +0xb5
</span></span><span style="display:flex;"><span>  main.main<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:33 +0x2f
</span></span><span style="display:flex;"><span><span style="color:#f92672">==================</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">==================</span>
</span></span><span style="display:flex;"><span>WARNING: DATA RACE
</span></span><span style="display:flex;"><span>Read at 0x00c000146438 by goroutine 41:
</span></span><span style="display:flex;"><span>  main.generateNumbersMap.func2<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:75 +0xc7
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Previous write at 0x00c000146438 by goroutine 7:
</span></span><span style="display:flex;"><span>  main.generateNumbersMap.func1<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:68 +0xb9
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Goroutine <span style="color:#ae81ff">41</span> <span style="color:#f92672">(</span>running<span style="color:#f92672">)</span> created at:
</span></span><span style="display:flex;"><span>  main.generateNumbersMap<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:73 +0x110
</span></span><span style="display:flex;"><span>  main.main<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:33 +0x2f
</span></span><span style="display:flex;"><span><span style="color:#f92672">==================</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Found <span style="color:#ae81ff">2</span> data race<span style="color:#f92672">(</span>s<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>exit status <span style="color:#ae81ff">66</span>
</span></span></code></pre></div><p>There are several strategies that could be used to solve it.
I will show one of them. We are going to introduce a new struct that provides it&rsquo;s own mutex:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">SafeNumbers</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">RWMutex</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">numbers</span> <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>To be able to read and write items concurrently to this structure, we need to create the responsible methods:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">sn</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">SafeNumbers</span>) <span style="color:#a6e22e">Add</span>(<span style="color:#a6e22e">num</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">sn</span>.<span style="color:#a6e22e">Lock</span>()
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">sn</span>.<span style="color:#a6e22e">Unlock</span>()
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">sn</span>.<span style="color:#a6e22e">numbers</span>[<span style="color:#a6e22e">num</span>] = <span style="color:#a6e22e">num</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Here we are basically telling to lock the numbers map, during adding of the new number to it. Other goroutines will wait until it became unlocked again.</p>
<p>And another method for reading:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">sn</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">SafeNumbers</span>) <span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">num</span> <span style="color:#66d9ef">int</span>) (<span style="color:#66d9ef">int</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">sn</span>.<span style="color:#a6e22e">RLock</span>()
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">sn</span>.<span style="color:#a6e22e">RUnlock</span>()
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">number</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">sn</span>.<span style="color:#a6e22e">numbers</span>[<span style="color:#a6e22e">num</span>]; <span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">number</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#ae81ff">0</span>, <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;Number does not exists&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Next, let&rsquo;s refactor our <code>generateNumbersMap()</code> function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">generateNumbersMap</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">wg</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>{}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Init our &#34;safe&#34; numbers map struct.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">safeNumbers</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">SafeNumbers</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">numbers</span>: <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">int</span>{},
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Write.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">i</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">safeNumbers</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>		}(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Read.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">i</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">number</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">safeNumbers</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>			} <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">number</span>)
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>		}(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If we run <code>go run -race main.go</code> now, there will be no more data race issues!</p>
<p>As I mentioned before, there also other ways to solve it. One of them is using of a special go type <code>sync.Map</code>.</p>
<p>Nevertheless, I hope this was helpful and you know now how to work safely with the maps in go. Especially, you should be careful with them when you create the web services, because every http request initiating a new goroutine.</p>
<p>As usual, the source code you can find <a href="https://github.com/alexsergivan/blog-examples/tree/master/concurent-map">here</a>.</p>
<p>If the goroutines writing to that map came from a batch of work, the next thing to fix is usually how many of them there are at once — <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools in Go with errgroup</a> covers bounding that. And to cancel them cleanly when the request goes away, see <a href="/posts/understanding-golang-context/">understanding Golang context</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Performance Optimization</category>
    </item>
    <item>
      <title>Ristretto - the Most Performant Concurrent Cache Library for Go</title>
      <link>https://webdevstation.com/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/</link>
      <pubDate>Tue, 02 Mar 2021 18:19:53 +0100</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/</guid>
      <description>Learn how to implement Ristretto, a high-performance concurrent memory caching library for Go applications. Includes code examples comparing database access with and…</description>
      <content:encoded><![CDATA[<p>Recently, I discovered a surprisingly reliable memory caching solution, which I&rsquo;m planning to use in all my further applications to increase performance. In this blog post, I will share some code examples of how you can integrate <a href="https://github.com/dgraph-io/ristretto">Ristretto</a> caching library into your application.</p>
<p><code>Ristretto is a fast, concurrent cache library built with a focus on performance and correctness.</code></p>
<p>This library was created by the Dgraph team as a contention-free cache for the Dgraph database.</p>
<p>Let&rsquo;s dive into the practical example. We are going to build a simple application that gets a list of users from the database. In the first iteration, there will be no caching layer at all. In the second iteration, we will add a Ristretto caching and compare execution time.</p>
<p>Below, you can see that I defined a <code>repository</code> package with the <code>Repository</code> interface and with <code>InMemoryRepository</code> implementation:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">repository</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Repository interface to handle users data.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Repository</span> <span style="color:#66d9ef">interface</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">GetUsers</span>() <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">InMemoryRepository</span> <span style="color:#66d9ef">struct</span>{}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// NewInMemoryRepository constructs and returns InMemoryRepository.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewInMemoryRepository</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">InMemoryRepository</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">InMemoryRepository</span>{}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// GetUsers returns 50000 dummy users from the in-memory repository.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">InMemoryRepository</span>) <span style="color:#a6e22e">GetUsers</span>() <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">users</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">:=</span><span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">50000</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">users</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;User %d&#34;</span>, <span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">users</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Next, we are going to call a <code>GetUsers()</code> method 100 times to simulate calling of the same function from several places in the real-world applications:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/ristretto/repository&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">:=</span><span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span>&lt;<span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">UsersGetter</span>(<span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">NewInMemoryRepository</span>())
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">UsersGetter</span>(<span style="color:#a6e22e">repository</span> <span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">Repository</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">GetUsers</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Let&rsquo;s measure how much time it takes to execute it with <code>time go run main.go</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>1.46s user
</span></span><span style="display:flex;"><span>0.34s system
</span></span><span style="display:flex;"><span>106% cpu
</span></span><span style="display:flex;"><span>1.686 total
</span></span></code></pre></div><p>Next, we are going to add a caching layer to our application.</p>
<p>Don&rsquo;t forget to get the Ristretto library:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>  go get github.com/dgraph-io/ristretto
</span></span></code></pre></div><p>Inside <code>repository</code> package we inject Ristretto cache:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">repository</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/dgraph-io/ristretto&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">InMemoryRepository</span> <span style="color:#66d9ef">struct</span>{
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cache</span>        <span style="color:#f92672">*</span><span style="color:#a6e22e">ristretto</span>.<span style="color:#a6e22e">Cache</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// NewInMemoryRepository constructs and returns InMemoryRepository.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewInMemoryRepository</span>(<span style="color:#a6e22e">ristrettoCache</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">ristretto</span>.<span style="color:#a6e22e">Cache</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">InMemoryRepository</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">InMemoryRepository</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">cache</span>: <span style="color:#a6e22e">ristrettoCache</span>,
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// GetUsers returns 50000 dummy users from the in-memory repository.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">InMemoryRepository</span>) <span style="color:#a6e22e">GetUsers</span>() <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;users&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">found</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">cache</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If the users data not cached yet, get it from the repository.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">found</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">users</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">:=</span><span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">50000</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">users</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;User %d&#34;</span>, <span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Adds data to the cache for 1h.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">cache</span>.<span style="color:#a6e22e">SetWithTTL</span>(<span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">users</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">10</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Millisecond</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">users</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</span>.(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Next, inside the <code>main()</code> function we initiate a new Ristretto cache and pass it to the <code>InMemoryRepository</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>  <span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/ristretto/repository&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/dgraph-io/ristretto&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">ristrettoCache</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ristretto</span>.<span style="color:#a6e22e">NewCache</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">ristretto</span>.<span style="color:#a6e22e">Config</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">NumCounters</span>: <span style="color:#ae81ff">1e7</span>,     <span style="color:#75715e">// Num keys to track frequency of (10M).</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">MaxCost</span>:     <span style="color:#ae81ff">1</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">30</span>, <span style="color:#75715e">// Maximum cost of cache (1GB).</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">BufferItems</span>: <span style="color:#ae81ff">64</span>,      <span style="color:#75715e">// Number of keys per Get buffer.</span>
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">:=</span><span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span>&lt;<span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">UsersGetter</span>(<span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">NewInMemoryRepository</span>(<span style="color:#a6e22e">ristrettoCache</span>))
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">UsersGetter</span>(<span style="color:#a6e22e">repository</span> <span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">Repository</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">GetUsers</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Let&rsquo;s check how much time it takes to perform the same action:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>0.29s user
</span></span><span style="display:flex;"><span>0.26s system
</span></span><span style="display:flex;"><span>147% cpu
</span></span><span style="display:flex;"><span>0.377 total
</span></span></code></pre></div><p>As you can notice, the total time is 4 times less than in the example without caching layer.</p>
<p>Despite a silly example, I hope you got an idea of how to integrate the Ristretto caching into your application and how it could improve overall performance.</p>
<p>The complete source code you can find <a href="https://github.com/alexsergivan/blog-examples/tree/master/ristretto">here</a>.</p>
<p>Ristretto is safe for concurrent use, which is exactly the problem a plain map does not solve — see <a href="/posts/concurrent-map-writing-and-reading-in-go/">concurrent map writing and reading in Go</a> if you want the failure mode in detail. I also refactored this caching layer with generics in <a href="/posts/example-of-how-generics-simplify-golang/">how Golang generics minimize the amount of code you need to write</a>. For a cache with a very different failure mode — one that costs you money rather than latency when it silently stops working — see <a href="/posts/prompt-caching-llm-cost/">prompt caching</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Performance Optimization</category>
    </item>
    <item>
      <title>An Easy Way to Load Test Your Web Apps</title>
      <link>https://webdevstation.com/posts/an-easy-way-to-loadtest-your-web-apps/</link>
      <pubDate>Fri, 12 Feb 2021 18:56:46 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/an-easy-way-to-loadtest-your-web-apps/</guid>
      <description>Learn how to implement effective load testing for your web applications using the k6 tool and automate performance testing in your GitLab CI/CD pipeline.</description>
      <content:encoded><![CDATA[<p>This time, I want to share my positive experience of load testing of one of our web services, by using <a href="https://k6.io/">K6</a> tool.
Moreover, we will see how easily we can integrate this into the GitLab CI pipeline.</p>
<p>When you develop web applications, it&rsquo;s crucial to have a testing strategy. Nobody argues about the importance of unit,
functional, and integration testing. Nevertheless, very often developers forget to test how their application works under high load.
Even, when we have a &ldquo;green light&rdquo; from all our testing stages, including manual testing, better to not release it to production, until you load test it.
Otherwise, nobody can guarantee, that application will work properly when 50 users will use it simultaneously.</p>
<p>In this article I&rsquo;m going to load test our web application from the <a href="https://webdevstation.com/posts/how-to-show-flash-messages-in-go-echo/">previous article</a>.</p>
<p>For that, we are going to use a K6 tool, written in Go, and uses JavaScript for scripting.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>k6 is a developer-centric, free and open-source load testing tool built for making performance testing a productive and enjoyable experience.
</span></span></code></pre></div><p>Let&rsquo;s install this tool:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>brew install k6
</span></span></code></pre></div><p>If you have different from the macOS operating system, please read about others ways to install <a href="https://k6.io/docs/getting-started/installation">here</a>.</p>
<p>Next, we create a <code>loadtests</code> folder in the root of our project and inside we add a <code>test.js</code> file, where we are going to write our load tests scenarios:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">sleep</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;k6&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">http</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;k6/http&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">check</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;k6&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Rate</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;k6/metrics&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">errorRate</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Rate</span>(<span style="color:#e6db74">&#39;errors&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">options</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Here we define our scenarios.
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">scenarios</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">sign_in_page_test</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">executor</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;constant-vus&#39;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">duration</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;1m&#39;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">vus</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">100</span>, <span style="color:#75715e">// amount of the virtual users
</span></span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">tags</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">test_type</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;signInPage&#39;</span> },
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">exec</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;signInPage&#39;</span>,
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// List of thresholds.
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">thresholds</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">http_req_duration</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;avg&lt;500&#39;</span>], <span style="color:#75715e">// avg response times must be below 0.5s
</span></span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">errors</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;rate&lt;0.1&#39;</span>], <span style="color:#75715e">// &lt;10% errors
</span></span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">signInPage</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">res</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">get</span>(<span style="color:#a6e22e">getDomain</span>() <span style="color:#f92672">+</span> <span style="color:#e6db74">&#39;/user/signin&#39;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Here we check the response status.
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">check</span>(<span style="color:#a6e22e">res</span>, {
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;status is 200&#39;</span><span style="color:#f92672">:</span> (<span style="color:#a6e22e">r</span>) =&gt; <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">status</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If it&#39;s different from 200, add info to the errorRate.
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">errorRate</span>.<span style="color:#a6e22e">add</span>(<span style="color:#f92672">!</span><span style="color:#a6e22e">result</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">sleep</span>(<span style="color:#ae81ff">3</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Gets the domain from the environment variables.
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">getDomain</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">__ENV</span>.<span style="color:#a6e22e">DOMAIN</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Above, we have declared a scenario to load test <code>/user/signin</code> page with 100 virtual users who continuously accessing our page in parallel.</p>
<p>To run this load test, we need to execute this command in the terminal:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>k6 run --env DOMAIN<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;http://localhost:8777&#34;</span> ./loadtests/test.js
</span></span></code></pre></div><p>After some time we will see this results output:
<img src="/images/0221/k6.png" alt="k6 terminal output listing checks, request duration percentiles and the passing thresholds for the load test" title="k6 load test results in the terminal"></p>
<p>As you could notice, all our defined thresholds were satisfied. So far so good!</p>
<p>Now, let&rsquo;s see how we can integrate load testing to the Gitlab CI pipeline. Fortunately, it&rsquo;s easy to do :)</p>
<p>Inside <code>.gitlab-ci.yml</code> we need to add this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">stages</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ae81ff">loadtest</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">loadtesting</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">stage</span>: <span style="color:#ae81ff">loadtest</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">image</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">loadimpact/k6:latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">entrypoint</span>: [ <span style="color:#e6db74">&#39;&#39;</span> ]
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">variables</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">DOMAIN</span>: <span style="color:#e6db74">&#39;[your-testing-domain-here]&#39;</span>  
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">script</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ae81ff">echo &#34;executing K6 load tests in k6 container...&#34;</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#ae81ff">k6 run --env DOMAIN=${DOMAIN} ./loadtests/test.js</span>
</span></span></code></pre></div><p>That was it! I&rsquo;ve described just an idea how you can easily integrate the load testing in your development routine.
In the real-world situations you might create more complex load testing scenarios, which will help you find weak points of your application and prevent unexpected downtimes.</p>
<p>I wish you happy coding and no pagerduty calls during the night!😉</p>
<p>Once you can measure, you have something to optimise against. Two places I usually look first: <a href="/posts/early-hints-in-go-or-the-way-of-how-to-improve-perforamnce-of-web-page/">103 Early Hints in Go</a> for front-end latency, and <a href="/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/">Ristretto caching</a> for the expensive calls behind it. It is also the right tool to prove your <a href="/posts/graceful-shutdown-in-go-web-services/">graceful shutdown</a> really does keep errors at zero during a deploy.</p>]]></content:encoded>
      <category>DevOps</category>
      <category>Testing</category>
    </item>
    <item>
      <title>How to Show Flash Messages in Go web applications (with Echo framework)</title>
      <link>https://webdevstation.com/posts/how-to-show-flash-messages-in-go-echo/</link>
      <pubDate>Thu, 04 Feb 2021 17:28:00 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/how-to-show-flash-messages-in-go-echo/</guid>
      <description>Learn how to implement flash messages in Go web applications using Echo framework and Gorilla Sessions to improve user experience by providing feedback after form…</description>
      <content:encoded><![CDATA[<p>When we create a web application, usually, there a need to communicate with the users to inform them about the results
of their actions. The easiest way to communicate - is to send messages. These messages might be warnings, errors, or just
informational text. In this article, we will improve the UX of our user authentication application from the <a href="https://webdevstation.com/posts/user-authentication-with-go-using-jwt-token/">previous article</a>
by adding an error flash message when the user entered a wrong password and a success message after user authorisation.</p>
<p>We are going to use cookies to store messages in-between requests. To not reinvent the wheel, we will install <a href="https://github.com/gorilla/sessions">Gorilla Sessions</a>
package.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">go</span> <span style="color:#a6e22e">get</span> <span style="color:#a6e22e">github</span>.<span style="color:#a6e22e">com</span><span style="color:#f92672">/</span><span style="color:#a6e22e">gorilla</span><span style="color:#f92672">/</span><span style="color:#a6e22e">sessions</span>
</span></span></code></pre></div><p>Next, let&rsquo;s create a <code>messages</code> package (messages/messages.go):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">messages</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/gorilla/sessions&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/labstack/echo/v4&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Name of the cookie.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">sessionName</span> = <span style="color:#e6db74">&#34;fmessages&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">getCookieStore</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">sessions</span>.<span style="color:#a6e22e">CookieStore</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// In real-world applications, use env variables to store the session key.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">sessionKey</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;test-session-key&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">sessions</span>.<span style="color:#a6e22e">NewCookieStore</span>([]byte(<span style="color:#a6e22e">sessionKey</span>))
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Set adds a new message into the cookie storage.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">name</span>, <span style="color:#a6e22e">value</span> <span style="color:#66d9ef">string</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">session</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">getCookieStore</span>().<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Request</span>(), <span style="color:#a6e22e">sessionName</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">session</span>.<span style="color:#a6e22e">AddFlash</span>(<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">name</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">session</span>.<span style="color:#a6e22e">Save</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Request</span>(), <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Response</span>())
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Get gets flash messages from the cookie storage.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">name</span> <span style="color:#66d9ef">string</span>) []<span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">session</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">getCookieStore</span>().<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Request</span>(), <span style="color:#a6e22e">sessionName</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fm</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">session</span>.<span style="color:#a6e22e">Flashes</span>(<span style="color:#a6e22e">name</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// If we have some messages.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> len(<span style="color:#a6e22e">fm</span>) &gt; <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">session</span>.<span style="color:#a6e22e">Save</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Request</span>(), <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Response</span>())
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Initiate a strings slice to return messages.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">flashes</span> []<span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">fl</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">fm</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#75715e">// Add message to the slice.</span>
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">flashes</span> = append(<span style="color:#a6e22e">flashes</span>, <span style="color:#a6e22e">fl</span>.(<span style="color:#66d9ef">string</span>))
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">flashes</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>We now have a possibility to easily Set and Get flash messages. Let&rsquo;s integrate it, first, to the sign-in form, which we created in the <a href="https://webdevstation.com/posts/user-authentication-with-go-using-jwt-token/">previous article</a>.</p>
<p>I&rsquo;m going to modify <code>controllers/signin.go</code> file. Inside <code>SignIn()</code> function we can replace</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusUnauthorized</span>, <span style="color:#e6db74">&#34;Password is incorrect&#34;</span>)
</span></span></code></pre></div><p>with</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">c</span>, <span style="color:#e6db74">&#34;error&#34;</span>, <span style="color:#e6db74">&#34;Password is incorrect!&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Redirect</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusMovedPermanently</span>, <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Echo</span>().<span style="color:#a6e22e">Reverse</span>(<span style="color:#e6db74">&#34;userSignInForm&#34;</span>))
</span></span></code></pre></div><p>And, if the password is correct, we going to set a message also:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">c</span>, <span style="color:#e6db74">&#34;message&#34;</span>, <span style="color:#e6db74">&#34;Password is correct, you have been authenticated!&#34;</span>)
</span></span></code></pre></div><p>The final <code>SignIn()</code> function will look like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// SignIn will be executed after SignInForm submission.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">SignIn</span>() <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Load our &#34;test&#34; user.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">storedUser</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">LoadTestUser</span>()
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Initiate a new User struct.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">u</span> <span style="color:#f92672">:=</span> new(<span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Parse the submitted data and fill the User struct with the data from the SignIn form.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Bind</span>(<span style="color:#a6e22e">u</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Compare the stored hashed password, with the hashed version of the password that was received</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bcrypt</span>.<span style="color:#a6e22e">CompareHashAndPassword</span>([]byte(<span style="color:#a6e22e">storedUser</span>.<span style="color:#a6e22e">Password</span>), []byte(<span style="color:#a6e22e">u</span>.<span style="color:#a6e22e">Password</span>)); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#75715e">// If the two passwords don&#39;t match, set a message and reload the page.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">c</span>, <span style="color:#e6db74">&#34;error&#34;</span>, <span style="color:#e6db74">&#34;Password is incorrect!&#34;</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Redirect</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusMovedPermanently</span>, <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Echo</span>().<span style="color:#a6e22e">Reverse</span>(<span style="color:#e6db74">&#34;userSignInForm&#34;</span>))
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// If password is correct, generate tokens and set cookies.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">GenerateTokensAndSetCookies</span>(<span style="color:#a6e22e">storedUser</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusUnauthorized</span>, <span style="color:#e6db74">&#34;Token is incorrect&#34;</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">c</span>, <span style="color:#e6db74">&#34;message&#34;</span>, <span style="color:#e6db74">&#34;Password is correct, you have been authenticated!&#34;</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Redirect</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusMovedPermanently</span>, <span style="color:#e6db74">&#34;/admin&#34;</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Once we set messages, we need to display them.</p>
<p>First, I will add message displaying logic to the SignIn form. We need to modify <code>SignInForm()</code> function inside <code>controllers/signin.go</code>, by adding additional data with messages to the template execution function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">tmpl</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">template</span>.<span style="color:#a6e22e">ParseFiles</span>(<span style="color:#a6e22e">path</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#e6db74">&#34;templates&#34;</span>, <span style="color:#e6db74">&#34;signIn.html&#34;</span>), <span style="color:#a6e22e">path</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#e6db74">&#34;templates&#34;</span>, <span style="color:#e6db74">&#34;messages.html&#34;</span>))
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">data</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">interface</span>{})
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">data</span>[<span style="color:#e6db74">&#34;errors&#34;</span>] = <span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">c</span>, <span style="color:#e6db74">&#34;error&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">tmpl</span>.<span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Response</span>().<span style="color:#a6e22e">Writer</span>, <span style="color:#a6e22e">data</span>);
</span></span></code></pre></div><p>The final <code>SignInForm()</code> function will look like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// SignInForm responsible for signIn Form rendering.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">SignInForm</span>() <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">tmpl</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">template</span>.<span style="color:#a6e22e">ParseFiles</span>(<span style="color:#a6e22e">path</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#e6db74">&#34;templates&#34;</span>, <span style="color:#e6db74">&#34;signIn.html&#34;</span>), <span style="color:#a6e22e">path</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#e6db74">&#34;templates&#34;</span>, <span style="color:#e6db74">&#34;messages.html&#34;</span>))
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">data</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">interface</span>{})
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">data</span>[<span style="color:#e6db74">&#34;errors&#34;</span>] = <span style="color:#a6e22e">messages</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">c</span>, <span style="color:#e6db74">&#34;error&#34;</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">tmpl</span>.<span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Response</span>().<span style="color:#a6e22e">Writer</span>, <span style="color:#a6e22e">data</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>As you probably noticed, we introduced <code>messages.html</code> to the <code>template.ParseFiles()</code> function. There we will control how to visualize messages. Let&rsquo;s add this template inside <code>templates</code> folder:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>{{ define &#34;messages&#34; }}
</span></span><span style="display:flex;"><span>  {{ if index . &#34;errors&#34; }}
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;errors&#34;</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;background: darksalmon; padding: 10px;&#34;</span>&gt;
</span></span><span style="display:flex;"><span>        {{ range index . &#34;errors&#34; }}
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">p</span>&gt;{{ . }}&lt;/<span style="color:#f92672">p</span>&gt;
</span></span><span style="display:flex;"><span>        {{ end }}
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  {{ end }}
</span></span><span style="display:flex;"><span>  {{ if index . &#34;messages&#34; }}
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;messages&#34;</span> <span style="color:#a6e22e">style</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;background: darkseagreen; padding: 10px;&#34;</span>&gt;
</span></span><span style="display:flex;"><span>        {{ range index . &#34;messages&#34; }}
</span></span><span style="display:flex;"><span>        &lt;<span style="color:#f92672">p</span>&gt;{{ . }}&lt;/<span style="color:#f92672">p</span>&gt;
</span></span><span style="display:flex;"><span>        {{ end }}
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  {{ end }}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{{ end }}
</span></span></code></pre></div><p>Next, we need to add the reference to the <code>messages</code> template inside <code>signIn.html</code> and <code>admin.html</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>{{ <span style="color:#a6e22e">template</span> <span style="color:#e6db74">&#34;messages&#34;</span> . }}
</span></span></code></pre></div><p>Now, we can try how does it work.</p>
<p>Let&rsquo;s run the server <code>go run main.go</code> and go to the <code>/user/signin</code> path. When we submit the form with the wrong password, we should see this:
<img src="/images/0221/error.png" alt="Error Message" title="Error Message"></p>
<p>When a password is correct, we will be redirected to the <code>/admin</code> path with the message:
<img src="/images/0221/success.png" alt="Success Message" title="Success Message"></p>
<p>In this example, I was using the Echo framework, but it will be super easy to modify the code for your specific use-case.</p>
<p>The complete source code you can find <a href="https://github.com/alexsergivan/blog-examples/tree/master/flashmessages">here</a>.</p>
<p>Flash messages usually appear right after a login or a form post, so the natural companion pieces are <a href="/posts/user-authentication-with-go-using-jwt-token/">user authentication in Go Echo with JWT</a> and, for the layer that wraps every handler, <a href="/posts/go-middleware-example/">the Go middleware example</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>User Authentication in Go Echo with JWT</title>
      <link>https://webdevstation.com/posts/user-authentication-with-go-using-jwt-token/</link>
      <pubDate>Thu, 28 Jan 2021 18:05:51 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/user-authentication-with-go-using-jwt-token/</guid>
      <description>Learn how to implement secure user authentication in Go using JWT (JSON Web Tokens) with the Echo framework. This step-by-step guide covers token creation,…</description>
      <content:encoded><![CDATA[<p>In this article, we will build a simple user authentication functionality using JWT (JSON Web Token).
In the examples, I&rsquo;m going to use a Go <a href="https://echo.labstack.com/">Echo</a> framework. This will allow us
to avoid writing some boilerplate code.</p>
<p>If you are not familiar with a JWT theory, please refer to <a href="https://jwt.io/introduction/">this resource</a>.</p>
<p>I believe that the easiest way to understand how to work with JWT authentication is by solving a real-world problem.
Let&rsquo;s say, that we have a website with an administration section, that should be accessible only by authenticated users, by providing some credentials. If authentication was successful, the user can access the administration section. If the user is inactive during a defined period of time, we should log him out from the system.</p>
<p>In the beginning, I&rsquo;m going to create a <code>main.go</code> file with initialization of the web server and some routers:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/authentication/auth&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/authentication/controllers&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/labstack/echo/v4&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/labstack/echo/v4/middleware&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">e</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">New</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Defining of the admin router group.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">adminGroup</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Group</span>(<span style="color:#e6db74">&#34;/admin&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Router for &#34;/admin&#34; path.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">adminGroup</span>.<span style="color:#a6e22e">GET</span>(<span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#a6e22e">controllers</span>.<span style="color:#a6e22e">Admin</span>())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Starting the server.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Logger</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Start</span>(<span style="color:#e6db74">&#34;:8777&#34;</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And controllers/admin.go:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">controllers</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/labstack/echo/v4&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Admin</span>() <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">String</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusOK</span>, <span style="color:#e6db74">&#34;Hi, you have access!&#34;</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If you run this code and go to <code>http://localhost:8777/admin</code>, you will access this page without any authentication.
Let&rsquo;s protect this path, by adding a JWT authentication.</p>
<p>First, what I&rsquo;m going to create - it&rsquo;s an <code>auth</code> package, where we will keep all JWT related logic.
Please refer to the code below with added explanatory comments:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">auth</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/authentication/user&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/labstack/echo/v4&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/dgrijalva/jwt-go&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">accessTokenCookieName</span>  = <span style="color:#e6db74">&#34;access-token&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Just for the demo purpose, I declared a secret here. In the real-world application, you might need to get it from the env variables.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">jwtSecretKey</span> = <span style="color:#e6db74">&#34;some-secret-key&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GetJWTSecret</span>() <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">jwtSecretKey</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Create a struct that will be encoded to a JWT.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// We add jwt.StandardClaims as an embedded type, to provide fields like expiry time.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Claims</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">Name</span>  <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:&#34;name&#34;`</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">StandardClaims</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// GenerateTokensAndSetCookies generates jwt token and saves it to the http-only cookie.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GenerateTokensAndSetCookies</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">accessToken</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">generateAccessToken</span>(<span style="color:#a6e22e">user</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">setTokenCookie</span>(<span style="color:#a6e22e">accessTokenCookieName</span>, <span style="color:#a6e22e">accessToken</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">setUserCookie</span>(<span style="color:#a6e22e">user</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">generateAccessToken</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>) (<span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Declare the expiration time of the token (1h).</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">expirationTime</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">generateToken</span>(<span style="color:#a6e22e">user</span>, <span style="color:#a6e22e">expirationTime</span>, []byte(<span style="color:#a6e22e">GetJWTSecret</span>()))
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Pay attention to this function. It holds the main JWT token generation logic.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">generateToken</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">expirationTime</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>, <span style="color:#a6e22e">secret</span> []<span style="color:#66d9ef">byte</span>) (<span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Create the JWT claims, which includes the username and expiry time.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">claims</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Claims</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">Name</span>:  <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">Name</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">StandardClaims</span>: <span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">StandardClaims</span>{
</span></span><span style="display:flex;"><span>			<span style="color:#75715e">// In JWT, the expiry time is expressed as unix milliseconds.</span>
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">ExpiresAt</span>: <span style="color:#a6e22e">expirationTime</span>.<span style="color:#a6e22e">Unix</span>(),
</span></span><span style="display:flex;"><span>		},
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Declare the token with the HS256 algorithm used for signing, and the claims.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">token</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">NewWithClaims</span>(<span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">SigningMethodHS256</span>, <span style="color:#a6e22e">claims</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Create the JWT string.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">tokenString</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">token</span>.<span style="color:#a6e22e">SignedString</span>(<span style="color:#a6e22e">secret</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>(), <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">tokenString</span>, <span style="color:#a6e22e">expirationTime</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Here we are creating a new cookie, which will store the valid JWT token.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">setTokenCookie</span>(<span style="color:#a6e22e">name</span>, <span style="color:#a6e22e">token</span> <span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">expiration</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>, <span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span> <span style="color:#f92672">:=</span> new(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Cookie</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Name</span> = <span style="color:#a6e22e">name</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Value</span> = <span style="color:#a6e22e">token</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Expires</span> = <span style="color:#a6e22e">expiration</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Path</span> = <span style="color:#e6db74">&#34;/&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Http-only helps mitigate the risk of client side script accessing the protected cookie.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">HttpOnly</span> = <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">SetCookie</span>(<span style="color:#a6e22e">cookie</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Purpose of this cookie is to store the user&#39;s name.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">setUserCookie</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">expiration</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>, <span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span> <span style="color:#f92672">:=</span> new(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Cookie</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Name</span> = <span style="color:#e6db74">&#34;user&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Value</span> = <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">Name</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Expires</span> = <span style="color:#a6e22e">expiration</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cookie</span>.<span style="color:#a6e22e">Path</span> = <span style="color:#e6db74">&#34;/&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">SetCookie</span>(<span style="color:#a6e22e">cookie</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// JWTErrorChecker will be executed when user try to access a protected path.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">JWTErrorChecker</span>(<span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>, <span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Redirects to the signIn form.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Redirect</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusMovedPermanently</span>, <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Echo</span>().<span style="color:#a6e22e">Reverse</span>(<span style="color:#e6db74">&#34;userSignInForm&#34;</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>After finishing the main JWT token functionality, let&rsquo;s add the SignIn controllers, which will handle user authentication.
First, we need to add the new routers inside <code>main()</code> function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>    <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">GET</span>(<span style="color:#e6db74">&#34;/user/signin&#34;</span>, <span style="color:#a6e22e">controllers</span>.<span style="color:#a6e22e">SignInForm</span>()).<span style="color:#a6e22e">Name</span> = <span style="color:#e6db74">&#34;userSignInForm&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">POST</span>(<span style="color:#e6db74">&#34;/user/signin&#34;</span>, <span style="color:#a6e22e">controllers</span>.<span style="color:#a6e22e">SignIn</span>())
</span></span></code></pre></div><p>In the code below I created a <code>user</code> package with the user structure and a function that loads a dummy user from imaginary database. We gonna need it in our controllers to process and validate user data.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">user</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;golang.org/x/crypto/bcrypt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">User</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">Password</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:&#34;password&#34; form:&#34;password&#34;`</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">Name</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:&#34;name&#34; form:&#34;name&#34;`</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">LoadTestUser</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Just for demonstration purpose, we create a user with the encrypted &#34;test&#34; password.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// In real-world applications, you might load the user from the database by specific parameters (email, username, etc.)</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">hashedPassword</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bcrypt</span>.<span style="color:#a6e22e">GenerateFromPassword</span>([]byte(<span style="color:#e6db74">&#34;test&#34;</span>), <span style="color:#ae81ff">8</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">User</span>{<span style="color:#a6e22e">Password</span>: string(<span style="color:#a6e22e">hashedPassword</span>), <span style="color:#a6e22e">Name</span>: <span style="color:#e6db74">&#34;Test user&#34;</span>}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>After this, we will create a <code>controllers</code> package, where we add <code>SignInForm()</code> and <code>SignIn()</code> functions:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">controllers</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/authentication/auth&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/authentication/user&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/labstack/echo/v4&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;golang.org/x/crypto/bcrypt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;html/template&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;path&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// SignInForm responsible for signIn Form rendering.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">SignInForm</span>() <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">fp</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">path</span>.<span style="color:#a6e22e">Join</span>(<span style="color:#e6db74">&#34;templates&#34;</span>, <span style="color:#e6db74">&#34;signIn.html&#34;</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">tmpl</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">template</span>.<span style="color:#a6e22e">ParseFiles</span>(<span style="color:#a6e22e">fp</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">tmpl</span>.<span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Response</span>().<span style="color:#a6e22e">Writer</span>, <span style="color:#66d9ef">nil</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// SignIn will be executed after SignInForm submission.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">SignIn</span>() <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Load our &#34;test&#34; user.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">storedUser</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">LoadTestUser</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Initiate a new User struct.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">u</span> <span style="color:#f92672">:=</span> new(<span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Parse the submitted data and fill the User struct with the data from the SignIn form.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Bind</span>(<span style="color:#a6e22e">u</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// Compare the stored hashed password, with the hashed version of the password that was received.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bcrypt</span>.<span style="color:#a6e22e">CompareHashAndPassword</span>([]byte(<span style="color:#a6e22e">storedUser</span>.<span style="color:#a6e22e">Password</span>), []byte(<span style="color:#a6e22e">u</span>.<span style="color:#a6e22e">Password</span>)); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#75715e">// If the two passwords don&#39;t match, return a 401 status.</span>
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusUnauthorized</span>, <span style="color:#e6db74">&#34;Password is incorrect&#34;</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// If password is correct, generate tokens and set cookies.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">GenerateTokensAndSetCookies</span>(<span style="color:#a6e22e">storedUser</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">NewHTTPError</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusUnauthorized</span>, <span style="color:#e6db74">&#34;Token is incorrect&#34;</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Redirect</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusMovedPermanently</span>, <span style="color:#e6db74">&#34;/admin&#34;</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now, in the <code>/templates</code> folder we need to create a <code>signIn.html</code> template with the simple SignIn form:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#75715e">&lt;!DOCTYPE html&gt;</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#f92672">html</span> <span style="color:#a6e22e">lang</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;en&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">form</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;w-full&#34;</span> <span style="color:#a6e22e">method</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;post&#34;</span> <span style="color:#a6e22e">action</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/user/signin&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">label</span> <span style="color:#a6e22e">for</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;password&#34;</span>&gt;Password:&lt;/<span style="color:#f92672">label</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">input</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;password&#34;</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;password&#34;</span> <span style="color:#a6e22e">name</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;password&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">button</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;submit&#34;</span>&gt;Sign In&lt;/<span style="color:#f92672">button</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">form</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">html</span>&gt;
</span></span></code></pre></div><p>Let&rsquo;s also modify the <code>admin</code> controller in <code>controllers/admin.go</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">controllers</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/labstack/echo/v4&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Admin</span>() <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Gets user cookie.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">userCookie</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Cookie</span>(<span style="color:#e6db74">&#34;user&#34;</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">String</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusOK</span>, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;Hi, %s! You have been authenticated!&#34;</span>, <span style="color:#a6e22e">userCookie</span>.<span style="color:#a6e22e">Value</span>))
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And finally, we need to add a JWT Middleware to the adminGroup path inside the <code>main()</code> function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Read more about JWT Middleware here: https://echo.labstack.com/middleware/jwt</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">adminGroup</span>.<span style="color:#a6e22e">Use</span>(<span style="color:#a6e22e">middleware</span>.<span style="color:#a6e22e">JWTWithConfig</span>(<span style="color:#a6e22e">middleware</span>.<span style="color:#a6e22e">JWTConfig</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">Claims</span>:                  <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">Claims</span>{},
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">SigningKey</span>:              []byte(<span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">GetJWTSecret</span>()),
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">TokenLookup</span>:             <span style="color:#e6db74">&#34;cookie:access-token&#34;</span>, <span style="color:#75715e">// &#34;&lt;source&gt;:&lt;name&gt;&#34;</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">ErrorHandlerWithContext</span>: <span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">JWTErrorChecker</span>,
</span></span><span style="display:flex;"><span>    }))
</span></span></code></pre></div><p>After that, execute <code>go run main.go</code>, to run the server, and go to <code>/admin</code> path. You will be redirected to the <code>/user/signin</code> path, because you need to be authenticated to access it. That is exactly what we need! Just enter <code>test</code> password and click on <code>Sign In</code> button. You will see this message: <code>Hi, Test user! You have been authenticated!</code> Awesome!</p>
<p>As you remember earlier, we set expiration time for the token: <code>expirationTime := time.Now().Add(1 * time.Hour)</code> It means, that after 1 hour user will be automatically logged-out. This is something what we want to prevent, especially if user is still active and doing some work on our resource.
This is possible to solve, by introducing a Refresh token. This token will have a much longer life-time and will be used for refreshing the Access token.
Let&rsquo;s modify our previous code.</p>
<p>First of all, we need to declare a secret for the Refresh token and cookie name to store the generated JWT. I will store it in a constant, but in the real-world applications please use environment variables for security reasons.</p>
<p>Inside <code>auth</code> package (/auth/auth.go) we need to add these modifications:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">accessTokenCookieName</span>  = <span style="color:#e6db74">&#34;access-token&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">refreshTokenCookieName</span> = <span style="color:#e6db74">&#34;refresh-token&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">jwtSecretKey</span> = <span style="color:#e6db74">&#34;some-secret-key&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">jwtRefreshSecretKey</span> = <span style="color:#e6db74">&#34;some-refresh-secret-key&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GetRefreshJWTSecret</span>() <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">jwtRefreshSecretKey</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GenerateTokensAndSetCookies</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>, <span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">accessToken</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">generateAccessToken</span>(<span style="color:#a6e22e">user</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">setTokenCookie</span>(<span style="color:#a6e22e">accessTokenCookieName</span>, <span style="color:#a6e22e">accessToken</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">setUserCookie</span>(<span style="color:#a6e22e">user</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// We generate here a new refresh token and saving it to the cookie.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">refreshToken</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">generateRefreshToken</span>(<span style="color:#a6e22e">user</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">setTokenCookie</span>(<span style="color:#a6e22e">refreshTokenCookieName</span>, <span style="color:#a6e22e">refreshToken</span>, <span style="color:#a6e22e">exp</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">generateRefreshToken</span>(<span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>) (<span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// Declare the expiration time of the token - 24 hours.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">expirationTime</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">24</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">generateToken</span>(<span style="color:#a6e22e">user</span>, <span style="color:#a6e22e">expirationTime</span>, []byte(<span style="color:#a6e22e">GetRefreshJWTSecret</span>()))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>At this point, when the user is signing-in, we generate 2 tokens: access and refresh. We still need to add logic for updating the access token, if the user is still active. For that, we can add a middleware, where we can check how much time is left for the user&rsquo;s access token, and if this time is less than some period of time (in this example it&rsquo;s 15 mins) we can generate the new tokens, by providing a valid refresh token.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// TokenRefresherMiddleware middleware, which refreshes JWT tokens if the access token is about to expire.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TokenRefresherMiddleware</span>(<span style="color:#a6e22e">next</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span>) <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">HandlerFunc</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#a6e22e">echo</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// If the user is not authenticated (no user token data in the context), don&#39;t do anything.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;user&#34;</span>) <span style="color:#f92672">==</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">next</span>(<span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Gets user token from the context.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">u</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;user&#34;</span>).(<span style="color:#f92672">*</span><span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">Token</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">claims</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">u</span>.<span style="color:#a6e22e">Claims</span>.(<span style="color:#f92672">*</span><span style="color:#a6e22e">Claims</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// We ensure that a new token is not issued until enough time has elapsed.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// In this case, a new token will only be issued if the old token is within</span>
</span></span><span style="display:flex;"><span>		<span style="color:#75715e">// 15 mins of expiry.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Unix</span>(<span style="color:#a6e22e">claims</span>.<span style="color:#a6e22e">ExpiresAt</span>, <span style="color:#ae81ff">0</span>).<span style="color:#a6e22e">Sub</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>()) &lt; <span style="color:#ae81ff">15</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Minute</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Gets the refresh token from the cookie.</span>
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">rc</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Cookie</span>(<span style="color:#a6e22e">refreshTokenCookieName</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">==</span> <span style="color:#66d9ef">nil</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">rc</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#75715e">// Parses token and checks if it valid.</span>
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">tkn</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">ParseWithClaims</span>(<span style="color:#a6e22e">rc</span>.<span style="color:#a6e22e">Value</span>, <span style="color:#a6e22e">claims</span>, <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">token</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">Token</span>) (<span style="color:#66d9ef">interface</span>{}, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>					<span style="color:#66d9ef">return</span> []byte(<span style="color:#a6e22e">GetRefreshJWTSecret</span>()), <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>				})
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>					<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">jwt</span>.<span style="color:#a6e22e">ErrSignatureInvalid</span> {
</span></span><span style="display:flex;"><span>						<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Response</span>().<span style="color:#a6e22e">Writer</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusUnauthorized</span>)
</span></span><span style="display:flex;"><span>					}
</span></span><span style="display:flex;"><span>				}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">tkn</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">tkn</span>.<span style="color:#a6e22e">Valid</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#75715e">// If everything is good, update tokens.</span>
</span></span><span style="display:flex;"><span>					<span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">GenerateTokensAndSetCookies</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">user</span>.<span style="color:#a6e22e">User</span>{
</span></span><span style="display:flex;"><span>						<span style="color:#a6e22e">Name</span>:  <span style="color:#a6e22e">claims</span>.<span style="color:#a6e22e">Name</span>,
</span></span><span style="display:flex;"><span>					}, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>				}
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">next</span>(<span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And finally, we have to attach our middleware to the <code>adminGroup</code> router inside the <code>main()</code> function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>	<span style="color:#a6e22e">adminGroup</span>.<span style="color:#a6e22e">Use</span>(<span style="color:#a6e22e">auth</span>.<span style="color:#a6e22e">TokenRefresherMiddleware</span>)
</span></span></code></pre></div><p>You can run the server again and experiment, how does it work. As an example, you can change the access token lifetime to 1min and investigate how the jwt cookies behave.</p>
<p>That was pretty much it. I hope this article was helpful for you.</p>
<p>The complete source code you can found <a href="https://github.com/alexsergivan/blog-examples/tree/master/authentication">here</a>.</p>
<p>Authentication is only the first half. For deciding what an authenticated user is allowed to reach, see <a href="/posts/how-to-control-router-access-permissions-in-go-web-apps/">how to control router access permissions in Go web apps</a>; for keeping one client from consuming everyone&rsquo;s capacity, <a href="/posts/rate-limiting-go-apis/">rate limiting Go APIs</a>. And to tell the user what actually happened after a failed login, <a href="/posts/how-to-show-flash-messages-in-go-echo/">how to show flash messages in Go web applications</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Web Development</category>
      <category>Security</category>
    </item>
    <item>
      <title>A Simple Queue Implementation in Golang with channels</title>
      <link>https://webdevstation.com/posts/simple-queue-implementation-in-golang/</link>
      <pubDate>Tue, 12 Jan 2021 18:40:24 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/simple-queue-implementation-in-golang/</guid>
      <description>Learn how to implement a simple, efficient queue in Go using channels and goroutines. This practical guide shows you how to process operations sequentially with Go&#39;s…</description>
      <content:encoded><![CDATA[<p>In this post, I&rsquo;m going to show the way how we can implement a simple queue in Golang, using channels.</p>
<p>Let&rsquo;s clarify why would we ever use a queue? There many possible reasons. The most common one it&rsquo;s when
we have a list of operations (actions) and we need to process them one by one. This list either could be
static or dynamic (when new items arriving continuously).
As in the real-life, queue needed to be processed by something or someone. Imagine, a line of people in the
supermarket nearby cashier.
<div style="width:100%;height:0;padding-bottom:40%;position:relative;">
    <iframe src="https://giphy.com/embed/3o752ai3lDpXOw3yDu"
      width="100%" height="100%" style="position:absolute"
      frameBorder="0" allowFullScreen></iframe></div></p>
<p>Every customer in this line holds his own specific products and wants to
pay for them. In other words, every queue item has it own set of data with instructions. A cashier serves
his clients from a line one by one (processes queue items).
What would happen if all customers decided to pay simultaneously? Probably, something like this:
<div style="width:100%;height:0;padding-bottom:40%;position:relative;">
    <iframe src="https://giphy.com/embed/ls4p6mWzR0G9dRpwqm"
      width="100%" height="100%" style="position:absolute"
      frameBorder="0" allowFullScreen></iframe></div></p>
<p>To prevent this situation, let&rsquo;s implement a queue in Go quickly :)
First, we need to create a Queue struct and constructor function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>  <span style="color:#75715e">// Queue holds name, list of jobs and context with cancel.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Queue</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>     <span style="color:#a6e22e">name</span>   <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>     <span style="color:#a6e22e">jobs</span>   <span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">Job</span>
</span></span><span style="display:flex;"><span>     <span style="color:#a6e22e">ctx</span>    <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>
</span></span><span style="display:flex;"><span>     <span style="color:#a6e22e">cancel</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">CancelFunc</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// Job - holds logic to perform some operations during queue execution.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Job</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Name</span>   <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Action</span> <span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> <span style="color:#75715e">// A function that should be executed when the job is running.</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// NewQueue instantiates new queue.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewQueue</span>(<span style="color:#a6e22e">name</span> <span style="color:#66d9ef">string</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">Queue</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithCancel</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Queue</span>{
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">jobs</span>:   make(<span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">Job</span>),
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">name</span>:   <span style="color:#a6e22e">name</span>,
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">ctx</span>:    <span style="color:#a6e22e">ctx</span>,
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">cancel</span>: <span style="color:#a6e22e">cancel</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span></code></pre></div><p>As you have seen above, I&rsquo;ve declared an unbuffered channel (with no capacity) to hold Jobs in a Queue.
Channels will give us a really powerful possibility to work in a concurrent environment.
Moreover, our queue has a context with cancellation. This will help us to understand when all jobs in the queue were
finished in all goroutines and after that, we can free all resources.</p>
<p>In the code below we are going to add some methods to the <code>queue</code> struct.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// AddJobs adds jobs to the queue and cancels channel.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">q</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Queue</span>) <span style="color:#a6e22e">AddJobs</span>(<span style="color:#a6e22e">jobs</span> []<span style="color:#a6e22e">Job</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">wg</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(len(<span style="color:#a6e22e">jobs</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">job</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">jobs</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Goroutine which adds job to the queue.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">job</span> <span style="color:#a6e22e">Job</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">q</span>.<span style="color:#a6e22e">AddJob</span>(<span style="color:#a6e22e">job</span>)
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>    }(<span style="color:#a6e22e">job</span>)
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Cancel queue channel, when all goroutines were done.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">q</span>.<span style="color:#a6e22e">cancel</span>()
</span></span><span style="display:flex;"><span>  }()
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// AddJob sends job to the channel.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">q</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Queue</span>) <span style="color:#a6e22e">AddJob</span>(<span style="color:#a6e22e">job</span> <span style="color:#a6e22e">Job</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">q</span>.<span style="color:#a6e22e">jobs</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">job</span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;New job %s added to %s queue&#34;</span>, <span style="color:#a6e22e">job</span>.<span style="color:#a6e22e">Name</span>, <span style="color:#a6e22e">q</span>.<span style="color:#a6e22e">name</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Next, let&rsquo;s add a <code>Run()</code> method to the <code>Job</code> struct, which should execute a job&rsquo;s action (the main purpose of the job).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Run performs job execution.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">j</span> <span style="color:#a6e22e">Job</span>) <span style="color:#a6e22e">Run</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Job running: %s&#34;</span>, <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">GetName</span>())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">Action</span>()
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Alright, now let&rsquo;s create a worker, who will be responsible for serving our queue.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Worker responsible for queue serving.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Worker</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">Queue</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Queue</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// NewWorker initializes a new Worker.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewWorker</span>(<span style="color:#a6e22e">queue</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Queue</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">Worker</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Worker</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Queue</span>: <span style="color:#a6e22e">queue</span>,
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// DoWork processes jobs from the queue (jobs channel).</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">w</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Worker</span>) <span style="color:#a6e22e">DoWork</span>() <span style="color:#66d9ef">bool</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">for</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// if context was canceled.</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Queue</span>.<span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Work done in queue %s: %s!&#34;</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Queue</span>.<span style="color:#a6e22e">name</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Queue</span>.<span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Err</span>())
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// if job received.</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">job</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Queue</span>.<span style="color:#a6e22e">jobs</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">job</span>.<span style="color:#a6e22e">Run</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>          <span style="color:#66d9ef">continue</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Pay attention to the <code>DoWork()</code> method. There we have a for loop, which is listening for the jobs channel,
and if the channel receives a job, it executes it, by running <code>job.Run()</code>. If the context was canceled, it means,
that all jobs were executed and we can exit from the loop.</p>
<p>Now, let&rsquo;s see how we can work with just created queue functionality on a real example.
I&rsquo;m going to create a simple program, where we need to import new products into our storage.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/queue/queue&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Our products storage.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">products</span> = []<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;books&#34;</span>,
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;computers&#34;</span>,
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>()  {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// New products, which we need to add to our products storage.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">newProducts</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;apples&#34;</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;oranges&#34;</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;wine&#34;</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;bread&#34;</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;orange juice&#34;</span>,
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// New queue initialization.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">productsQueue</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">queue</span>.<span style="color:#a6e22e">NewQueue</span>(<span style="color:#e6db74">&#34;NewProducts&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">jobs</span> []<span style="color:#a6e22e">queue</span>.<span style="color:#a6e22e">Job</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Range over new products.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">newProduct</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">newProducts</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// We need to do this, because variables declared in for loops are passed by reference.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Otherwise, our closure will always receive the last item from the newProducts.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">product</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">newProduct</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Defining of the closure, where we add a new product to our simple storage (products slice)</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">action</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">products</span> = append(<span style="color:#a6e22e">products</span>, <span style="color:#a6e22e">product</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Append job to jobs slice.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">jobs</span> = append(<span style="color:#a6e22e">jobs</span>, <span style="color:#a6e22e">queue</span>.<span style="color:#a6e22e">Job</span>{
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">Name</span>:   <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;Importing new product: %s&#34;</span>, <span style="color:#a6e22e">newProduct</span>),
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">Action</span>: <span style="color:#a6e22e">action</span>,
</span></span><span style="display:flex;"><span>		})
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Adds jobs to the queue.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">productsQueue</span>.<span style="color:#a6e22e">AddJobs</span>(<span style="color:#a6e22e">jobs</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Defines a queue worker, which will execute our queue.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">worker</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">queue</span>.<span style="color:#a6e22e">NewWorker</span>(<span style="color:#a6e22e">productsQueue</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Execute jobs in queue.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">worker</span>.<span style="color:#a6e22e">DoWork</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Prints products storage after queue execution.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">products</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>After executing this program, it will print out this data:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>2021/01/12 22:10:33 New job Importing new product: apples added to NewProducts queue
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 Job running: Importing new product: apples
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 Job running: Importing new product: wine
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 Job running: Importing new product: oranges
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 Job running: Importing new product: bread
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 Job running: Importing new product: orange juice
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 New job Importing new product: orange juice added to NewProducts queue
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 New job Importing new product: oranges added to NewProducts queue
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 New job Importing new product: bread added to NewProducts queue
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 New job Importing new product: wine added to NewProducts queue
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 Work <span style="color:#66d9ef">done</span> in queue NewProducts: context canceled!
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>books computers apples wine oranges bread orange juice<span style="color:#f92672">]</span>%
</span></span></code></pre></div><p>Please notice the last line in the logs:
<code>[books computers apples wine oranges bread orange juice]</code>
As you can see, the order is different from the order in <code>newProducts</code> slice. It happens because of goroutines nature.
Sometimes one goroutine needs more time to finish its work than another. For the scenario, when the order is important,
I will write a separate post later.</p>
<p>For now, that&rsquo;s it :) Hope it was helpful!</p>
<p>The source code you can find <a href="https://github.com/alexsergivan/blog-examples/tree/master/queue">here</a></p>
<p>A queue like this processes work in order, one item at a time. When order does not matter and you want several items in flight instead, reach for <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools in Go with errgroup</a>. Either way, make sure the consumer stops cleanly on shutdown — see <a href="/posts/graceful-shutdown-in-go-web-services/">graceful shutdown in Go web services</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>How to Sort Strings With Go Alphabetically in Any Language</title>
      <link>https://webdevstation.com/posts/how-to-sort-strings-with-go-alphabetically-in-any-language/</link>
      <pubDate>Mon, 04 Jan 2021 15:59:02 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/how-to-sort-strings-with-go-alphabetically-in-any-language/</guid>
      <description>Learn how to properly sort strings in Go across different languages, including handling special characters and non-Latin alphabets like Cyrillic using Go&#39;s…</description>
      <content:encoded><![CDATA[<p>In this article I&rsquo;m going to show how easy we can sort strings alphabetically in different languages, using Go.
It seems like an easy task if we want to sort English words, however, it&rsquo;s not so trivial if we want to sort correctly strings with special characters or in other languages, i.e Cyrillic based.</p>
<p>Let&rsquo;s check this example of cities list:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>  <span style="color:#a6e22e">cities</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Berlin&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Zurich&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Augsburg&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Bünde&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Budapest&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Ürkmez&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Rostock&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Ulm&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Lindau&#34;</span>,
</span></span><span style="display:flex;"><span>  }
</span></span></code></pre></div><p>If we use the standard way to sort strings with <code>sort.Strings(cities)</code> the result will be:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>  <span style="color:#f92672">[</span>Augsburg Berlin Budapest Bünde Lindau Rostock Ulm Zurich Ürkmez<span style="color:#f92672">]</span>
</span></span></code></pre></div><p>As you can notice, <code>Ürkmez</code> ended up at the end of the list. And that&rsquo;s not the correct order.
Fortunately, Go has a powerful library <a href="https://pkg.go.dev/golang.org/x/text/collate">golang.org/x/text/collate</a> which could help us!</p>
<blockquote>
<p>Package collate contains types for comparing and sorting Unicode strings according to a given collation order.</p>
</blockquote>
<p>Let&rsquo;s try to use it!</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>   <span style="color:#a6e22e">c</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">collate</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">language</span>.<span style="color:#a6e22e">German</span>, <span style="color:#a6e22e">collate</span>.<span style="color:#a6e22e">IgnoreCase</span>)
</span></span><span style="display:flex;"><span>   <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">SortStrings</span>(<span style="color:#a6e22e">cities</span>)
</span></span><span style="display:flex;"><span>   <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">cities</span>)
</span></span></code></pre></div><p>It will print this result:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>   <span style="color:#f92672">[</span>Augsburg Berlin Budapest Bünde Lindau Rostock Ulm Ürkmez Zurich<span style="color:#f92672">]</span>
</span></span></code></pre></div><p>Looks awesome! Right?</p>
<p>But what if you don&rsquo;t sure in with language string was written? In this case, we can just use <code>language.Und</code>. Let&rsquo;s check on this example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>  <span style="color:#a6e22e">mixedLanguagesCities</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Ürkmez&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Budapest&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Бохольт&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Арнсберг&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Інцель&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Їндржихув-Градец&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Єна&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Шатору&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Ястшембя-Ґура&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Ґрудзьондз&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Атланта&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Zurich&#34;</span>,
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">c</span> = <span style="color:#a6e22e">collate</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">language</span>.<span style="color:#a6e22e">Und</span>, <span style="color:#a6e22e">collate</span>.<span style="color:#a6e22e">IgnoreCase</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">SortStrings</span>(<span style="color:#a6e22e">mixedLanguagesCities</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">mixedLanguagesCities</span>)
</span></span></code></pre></div><p>The result will be:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>  <span style="color:#f92672">[</span>Budapest Ürkmez Zurich Арнсберг Атланта Бохольт Ґрудзьондз Єна Інцель Їндржихув-Градец Шатору Ястшембя-Ґура<span style="color:#f92672">]</span>
</span></span></code></pre></div><p>As you can see, it sorted correctly the mixed list of German and Ukrainian strings, according to official sorting rules.</p>
<p>That was it! Hope this information was helpful for you 😊</p>
<p>Example code you can find <a href="https://github.com/alexsergivan/blog-examples/blob/master/alphabet/main.go">here</a></p>
<p>For more everyday Go mechanics, see <a href="/posts/mastering-for-loops-in-go/">mastering Golang for loops</a> and <a href="/posts/mastering-time-in-golang/">mastering time in Go</a> — both are full of small details that only bite in production.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Internationalization</category>
    </item>
    <item>
      <title>How to Control Router Access Permissions in Go Web Apps</title>
      <link>https://webdevstation.com/posts/how-to-control-router-access-permissions-in-go-web-apps/</link>
      <pubDate>Wed, 23 Dec 2020 19:10:56 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/how-to-control-router-access-permissions-in-go-web-apps/</guid>
      <description>Learn how to implement URL-based access control and route permissions in Go web applications using the chi router middleware. A practical guide to securing your Go…</description>
      <content:encoded><![CDATA[<p>In this post I&rsquo;m going to describe how can we limit user access to the specific url in golang web application.
I will use <a href="https://github.com/go-chi/chi">chi</a> router - a lightweight, idiomatic and composable router for building
Go HTTP services.</p>
<p>Let&rsquo;s create our main package.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/go-chi/chi&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">chi</span>.<span style="color:#a6e22e">NewRouter</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;/&#34;</span>, <span style="color:#a6e22e">homePageHandler</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;/admin&#34;</span>, <span style="color:#a6e22e">adminPageHandler</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ListenAndServe</span>(<span style="color:#e6db74">&#34;:3000&#34;</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">homePageHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Write</span>([]byte(<span style="color:#e6db74">&#34;This is home page&#34;</span>)) 
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">adminPageHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Write</span>([]byte(<span style="color:#e6db74">&#34;This is admin page&#34;</span>)) 
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>After this, if we go to the <code>/admin</code> page, we will see &ldquo;This is admin page&rdquo;.</p>
<p>Now, let&rsquo;s make this path accessible only for admin.</p>
<p>We have to replace</p>
<p><code>r.Get(&quot;/admin&quot;, adminPageHandler)</code>
With
<code>r.Mount(&quot;/admin&quot;, adminRouter())</code></p>
<p>Mount attaches another http.Handler or chi Router as a subrouter along a routing path.</p>
<p>Then, we have to attach middleware inside adminRouter() function.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">adminRouter</span>() <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">chi</span>.<span style="color:#a6e22e">NewRouter</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Middleware with access rules for router.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Use</span>(<span style="color:#a6e22e">AdminOnly</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;/&#34;</span>, <span style="color:#a6e22e">adminPageHandler</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">r</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>In this middleware we have a simple check is user authorized to access this page or not.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">AdminOnly</span>(<span style="color:#a6e22e">next</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span>) <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// If user is admin, allows access.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">IsLoggedInAdmin</span>(<span style="color:#a6e22e">r</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">next</span>.<span style="color:#a6e22e">ServeHTTP</span>(<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>        } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Otherwise, 403.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusText</span>(<span style="color:#ae81ff">403</span>), <span style="color:#ae81ff">403</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>In sake of demonstration, I&rsquo;m going just to use a random bool function to decide is used admin or not. You can modify this function according to your user authentication model.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">IsLoggedInAdmin</span>(<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) <span style="color:#66d9ef">bool</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">Float32</span>() &lt; <span style="color:#ae81ff">0.5</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And that&rsquo;s it. Looks really simple, Isn&rsquo;t it?</p>
<p>Let&rsquo;s go to to the <code>/admin</code> page again.</p>
<p>As you see, now, sometimes (depends on our random decider), user has no access to this page anymore.</p>
<p>You can find source code <a href="https://github.com/alexsergivan/blog-examples/blob/master/route-auth/main.go">here</a></p>
<p>This builds directly on the pattern from <a href="/posts/go-middleware-example/">Go middleware example: how to alter a handler result</a>. For the authentication half of the problem, see <a href="/posts/user-authentication-with-go-using-jwt-token/">user authentication in Go Echo with JWT</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Web Development</category>
      <category>Security</category>
    </item>
    <item>
      <title>How to test database interactions in golang applications</title>
      <link>https://webdevstation.com/posts/how-to-test-database-interactions-go/</link>
      <pubDate>Tue, 22 Dec 2020 20:12:51 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/how-to-test-database-interactions-go/</guid>
      <description>Testing of functions with database interactions always was challenging. Recently, I found a wonderful library, which will simplify writing tests and mocking database…</description>
      <content:encoded><![CDATA[<p>Testing of functions with database interactions always was challenging. Recently, I found a wonderful library <a href="https://github.com/DATA-DOG/go-sqlmock">go-sqlmock</a> which will simplify writing tests and mocking database queries in golang applications a lot.</p>
<p>And I want to share a short example of how to work with it.</p>
<p>First, we have to install it</p>
<p><code>go get github.com/DATA-DOG/go-sqlmock</code></p>
<p>We have this function with SQL query:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MenuByNameAndLanguage</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">db</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">sql</span>.<span style="color:#a6e22e">DB</span>, <span style="color:#a6e22e">name</span> <span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">langcode</span> <span style="color:#66d9ef">string</span>) (<span style="color:#f92672">*</span><span style="color:#a6e22e">models</span>.<span style="color:#a6e22e">Menu</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">result</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">db</span>.<span style="color:#a6e22e">Query</span>(<span style="color:#e6db74">&#34;SELECT id, langcode, title, link__uri, view_sidemenu FROM menu_link_content_data WHERE menu_name=? AND langcode=?&#34;</span>,
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">name</span>,
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">langcode</span>,
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">Close</span>()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">menuLinks</span> []<span style="color:#a6e22e">models</span>.<span style="color:#a6e22e">MenuLink</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">Next</span>() {
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">menuLink</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">models</span>.<span style="color:#a6e22e">MenuLink</span>{}
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">result</span>.<span style="color:#a6e22e">Scan</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">menuLink</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">menuLink</span>.<span style="color:#a6e22e">Langcode</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">menuLink</span>.<span style="color:#a6e22e">Title</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">menuLink</span>.<span style="color:#a6e22e">URL</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">menuLink</span>.<span style="color:#a6e22e">SideMenu</span>)
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">menuLinks</span> = append(<span style="color:#a6e22e">menuLinks</span>, <span style="color:#a6e22e">menuLink</span>)    
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">menu</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">models</span>.<span style="color:#a6e22e">Menu</span>{
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">Name</span>: <span style="color:#a6e22e">name</span>,
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">Links</span>: <span style="color:#a6e22e">menuLinks</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">menu</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This function just getting menu links by menu name and language.</p>
<p>And now let&rsquo;s test it.</p>
<p>We are going to test that MenuByNameAndLanguage function will return correct Menu structure.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">menu</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;context&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;testing&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/DATA-DOG/go-sqlmock&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/stretchr/testify/assert&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;gitlab.mfb.io/user/graphql_server/models&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestShouldReturnCorrectMenu</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Creates sqlmock database connection and a mock to manage expectations.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">db</span>, <span style="color:#a6e22e">mock</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">sqlmock</span>.<span style="color:#a6e22e">New</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Fatalf</span>(<span style="color:#e6db74">&#34;an error &#39;%s&#39; was not expected when opening a stub database connection&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Closes the database and prevents new queries from starting.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">db</span>.<span style="color:#a6e22e">Close</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Here we are creating rows in our mocked database.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">rows</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">sqlmock</span>.<span style="color:#a6e22e">NewRows</span>([]<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">&#34;id&#34;</span>, <span style="color:#e6db74">&#34;langcode&#34;</span>, <span style="color:#e6db74">&#34;title&#34;</span>, <span style="color:#e6db74">&#34;link__uri&#34;</span>, <span style="color:#e6db74">&#34;view_sidemenu&#34;</span>}).
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">AddRow</span>(<span style="color:#ae81ff">1</span>, <span style="color:#e6db74">&#34;en&#34;</span>, <span style="color:#e6db74">&#34;enTitle&#34;</span>, <span style="color:#e6db74">&#34;/en-link&#34;</span>, <span style="color:#e6db74">&#34;0&#34;</span>).
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">AddRow</span>(<span style="color:#ae81ff">2</span>, <span style="color:#e6db74">&#34;en&#34;</span>, <span style="color:#e6db74">&#34;enTitle2&#34;</span>, <span style="color:#e6db74">&#34;/en-link2&#34;</span>, <span style="color:#e6db74">&#34;0&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// This is most important part in our test. Here, literally, we are altering SQL query from MenuByNameAndLanguage</span>
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">﻿</span>    <span style="color:#75715e">// function and replacing result with our expected result. </span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">mock</span>.<span style="color:#a6e22e">ExpectQuery</span>(<span style="color:#e6db74">&#34;^SELECT (.+) FROM menu_link_content_data*&#34;</span>).
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">WithArgs</span>(<span style="color:#e6db74">&#34;main&#34;</span>, <span style="color:#e6db74">&#34;en&#34;</span>).
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">WillReturnRows</span>(<span style="color:#a6e22e">rows</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ctx</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">TODO</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Calls MenuByNameAndLanguage with mocked database connection in arguments list. </span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">menu</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">MenuByNameAndLanguage</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">db</span>, <span style="color:#e6db74">&#34;main&#34;</span>, <span style="color:#e6db74">&#34;en&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Here we just construction our expecting result.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">menuLinks</span> []<span style="color:#a6e22e">models</span>.<span style="color:#a6e22e">MenuLink</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">menuLink1</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">models</span>.<span style="color:#a6e22e">MenuLink</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ID</span>:       <span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Title</span>:    <span style="color:#e6db74">&#34;enTitle&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Langcode</span>: <span style="color:#e6db74">&#34;en&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">URL</span>:      <span style="color:#e6db74">&#34;/en-link&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">SideMenu</span>: <span style="color:#e6db74">&#34;0&#34;</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">menuLinks</span> = append(<span style="color:#a6e22e">menuLinks</span>, <span style="color:#a6e22e">menuLink1</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">menuLink2</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">models</span>.<span style="color:#a6e22e">MenuLink</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ID</span>:       <span style="color:#ae81ff">2</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Title</span>:    <span style="color:#e6db74">&#34;enTitle2&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Langcode</span>: <span style="color:#e6db74">&#34;en&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">URL</span>:      <span style="color:#e6db74">&#34;/en-link2&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">SideMenu</span>: <span style="color:#e6db74">&#34;0&#34;</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">menuLinks</span> = append(<span style="color:#a6e22e">menuLinks</span>, <span style="color:#a6e22e">menuLink2</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">expectedMenu</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">models</span>.<span style="color:#a6e22e">Menu</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Name</span>:  <span style="color:#e6db74">&#34;main&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Links</span>: <span style="color:#a6e22e">menuLinks</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// And, finally, let&#39;s check if result from MenuByNameAndLanguage equal with expected result.// Here I used Testify library (https://github.com/stretchr/testify).</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">assert</span>.<span style="color:#a6e22e">Equal</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">expectedMenu</span>, <span style="color:#a6e22e">menu</span>)
</span></span><span style="display:flex;"><span>} 
</span></span></code></pre></div><p>As you see everything in this example was pretty straightforward.</p>
<p>For mo details, you can refer to <a href="https://godoc.org/github.com/DATA-DOG/go-sqlmock">GoDocs</a>.</p>
<p>Mocking the database is also the easiest way to test your error paths: force <code>sql.ErrNoRows</code> and assert that your store turns it into a sentinel your handler can branch on. I covered how to build those sentinels in <a href="/posts/error-handling-in-go/">error handling in Go</a>. The same fake-the-boundary trick works for code that calls a language model, which people usually assume is untestable — <a href="/posts/testing-go-code-that-calls-an-llm/">how to test Go code that calls an LLM</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
      <category>Testing</category>
    </item>
    <item>
      <title>Go middleware example. How to alter a handler result</title>
      <link>https://webdevstation.com/posts/go-middleware-example/</link>
      <pubDate>Sun, 20 Dec 2020 20:12:51 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/go-middleware-example/</guid>
      <description>Learn how to implement Go middleware to intercept and modify HTTP handler responses with practical examples using chi router, demonstrating request/response…</description>
      <content:encoded><![CDATA[<p>Let&rsquo;s imagine a situation when you want to alter the result, returned by some http handler to the client.
Fortunately, Golang provides an easy mechanism for that, called a middleware.
I&rsquo;m going to dive directly to the source code, to save your time.</p>
<p>Imagine, we have this simple web server (here I&rsquo;m using a chi router):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;bytes&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;github.com/go-chi/chi&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;io&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">chi</span>.<span style="color:#a6e22e">NewRouter</span>()
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;/&#34;</span>, <span style="color:#a6e22e">myFirstHandler</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ListenAndServe</span>(<span style="color:#e6db74">&#34;:3000&#34;</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">myFirstHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Write</span>([]byte(<span style="color:#e6db74">&#34;This is a main page&#34;</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>When we run this application and visit a frontpage htttp://localhost:3000/, we can see this:
<img src="/images/16122020/1.png" alt="Browser showing the JSON response returned by myFirstHandler on the front page" title="Response from the original Go handler"></p>
<p>Now we got a new requirement to create another handler which should get all response data
from myFirstHandler and add some modification on top.</p>
<p>We can do it easily in this way:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Adds a new router handler with a middleware myMiddleware.  </span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">With</span>(<span style="color:#a6e22e">myMiddleware</span>).<span style="color:#a6e22e">Get</span>(<span style="color:#e6db74">&#34;/other&#34;</span>, <span style="color:#a6e22e">myFirstHandler</span>) 
</span></span></code></pre></div><p>To be able to read a response from other handler, we have to implement our own <code>ResponseWriter</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">MyResponseWriter</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>   <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>
</span></span><span style="display:flex;"><span>   <span style="color:#a6e22e">buf</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">bytes</span>.<span style="color:#a6e22e">Buffer</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Here we are implementing a Write() function from ResponseWriter with our custom instructions. </span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">myrw</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">MyResponseWriter</span>) <span style="color:#a6e22e">Write</span>(<span style="color:#a6e22e">p</span> []<span style="color:#66d9ef">byte</span>) (<span style="color:#66d9ef">int</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>   <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">myrw</span>.<span style="color:#a6e22e">buf</span>.<span style="color:#a6e22e">Write</span>(<span style="color:#a6e22e">p</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And finally, let&rsquo;s write our middleware:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">myMiddleware</span>(<span style="color:#a6e22e">next</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span>) <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Handler</span> {
</span></span><span style="display:flex;"><span>   <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// Create a response writer:</span>
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">myResponseWriter</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">MyResponseWriter</span>{
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">ResponseWriter</span>: <span style="color:#a6e22e">w</span>,
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">buf</span>:            <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">bytes</span>.<span style="color:#a6e22e">Buffer</span>{},
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// Here we are pssing our custom response writer to the next http handler.</span>
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">next</span>.<span style="color:#a6e22e">ServeHTTP</span>(<span style="color:#a6e22e">myResponseWriter</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>      
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// Here we are adding our custom stuff to the response, which we received after http handler execution. </span>
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">myResponseWriter</span>.<span style="color:#a6e22e">buf</span>.<span style="color:#a6e22e">WriteString</span>(<span style="color:#e6db74">&#34; and some additional modifications&#34;</span>)
</span></span><span style="display:flex;"><span>      
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// And, finally, we are copiing everything back to the original response writer.  if _, err := io.Copy(w, myResponseWriter.buf); err != nil {</span>
</span></span><span style="display:flex;"><span>         <span style="color:#a6e22e">log</span>.<span style="color:#960050;background-color:#1e0010">`</span><span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Failed to send out response: %v&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>   })
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now, if we run our server again and go to <code>/other</code> path, we will see this:
<img src="/images/16122020/2.png" alt="Browser showing the same response after the middleware appended its extra field" title="Response after the Go middleware altered it"></p>
<p>This was a silly example, which will never happen in real life, but, I hope you got an overview how you can play
with http handlers and middlewares.</p>
<p>The source code you can found in this repository: <a href="https://github.com/alexsergivan/blog-examples/tree/master/middleware">https://github.com/alexsergivan/blog-examples/tree/master/middleware</a></p>
<p>Middleware is where a lot of cross-cutting concerns end up living. Three I have written about since: <a href="/posts/how-to-control-router-access-permissions-in-go-web-apps/">controlling router access permissions</a>, <a href="/posts/rate-limiting-go-apis/">rate limiting your API</a>, and <a href="/posts/structured-logging-in-go-with-slog/">putting a request-scoped logger in the context</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>How to make Nginx cache cookie aware</title>
      <link>https://webdevstation.com/posts/how-to-make-nginx-cookie-aware/</link>
      <pubDate>Tue, 15 Dec 2020 20:12:51 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/how-to-make-nginx-cookie-aware/</guid>
      <description>Learn how to configure Nginx to cache responses based on specific cookie values, enabling proper A/B testing and personalized content delivery while maintaining…</description>
      <content:encoded><![CDATA[<p>In this post, I&rsquo;m going to describe how we can configure nginx to be able to cache responses based on the specific cookie value.</p>
<p>Let&rsquo;s imagine a situation when you want to do an A/B test on your website, where 50% of users should see new headline text on the page. Other 50% of visitors will continue see the old page. In this case, all your server-side manipulation about splitting users to different versions will be ignored by nginx cache (of course, if you have it).</p>
<p>It happens because nginx, by default, has this configuration:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">proxy_cache_key</span> $scheme$proxy_host$request_uri;
</span></span></code></pre></div><p>So, it will use the same cache key for all users, who requests a page with the same url.</p>
<p>Luckily, nginx allows us easily to customize proxy_cache_key! What we need to do, it&rsquo;s just to add a specific cookie to this key:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">proxy_cache_key</span> $scheme$proxy_host$request_uri$cookie_MY_COOKIE_NAME;
</span></span></code></pre></div><p>And that&rsquo;s it! After this, if userA has MY_COOKIE_NAME=A and userB has MY_COOKIE_NAME=B they will receive different versions of page by the same url.</p>
<p>If you need more complex behaviour, where you won&rsquo;t use hardcoded cookie name in your nginx config, you can do something like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#e6db74">(</span>$http_cookie ~<span style="color:#e6db74">*</span> <span style="color:#e6db74">&#34;ab_(.*?)=([\w-]+)&#34;</span> <span style="color:#e6db74">)</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">set</span> $abcookie $1$2;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">proxy_cache_key</span> $scheme$proxy_host$request_uri$abcookie; 
</span></span></code></pre></div><p>As you can see, you can use $http_cookie and generate proxy_cache_key based on specific cookie patterns.
In this concrete example we check if http cookies contains cookie with regex <code>pattern ab_(.*?)=([\w-]+)</code> and if this cookie exists, we generate new variable for proxy_cache_key`.</p>
<p>Caching at the proxy is only one layer. For the one inside your Go process, see <a href="/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/">Ristretto — the most performant concurrent cache library for Go</a>, and for shaving latency off the browser side, <a href="/posts/early-hints-in-go-or-the-way-of-how-to-improve-perforamnce-of-web-page/">103 Early Hints in Go</a>.</p>]]></content:encoded>
      <category>DevOps</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>Privacy Policy</title>
      <link>https://webdevstation.com/pages/privacy/</link>
      <pubDate>Wed, 12 Feb 2020 18:56:46 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/pages/privacy/</guid>
      <description>Welcome to webdevstation.com (the “Website”). We take your privacy seriously, and we want you to know that we do not collect any personal information from our…</description>
      <content:encoded><![CDATA[<p>Welcome to webdevstation.com (the &ldquo;Website&rdquo;). We take your privacy seriously, and we want you to know that we do not collect any personal information from our visitors. You can browse and enjoy our content without worrying about your data being collected or stored.</p>
<p>Since we don&rsquo;t collect any user data, there is no information to share, sell, or disclose to any third parties. Your visit to our Website is entirely anonymous.</p>
<p>If you have any questions or concerns about this privacy policy, please feel free to contact us.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
