<?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>Go on Kinoko's TIL Log</title><link>https://kinoko-tech-blog-theta.vercel.app/tags/go/</link><description>Recent content in Go on Kinoko's TIL Log</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>pippimotta@gmail.com (Kinoko)</managingEditor><webMaster>pippimotta@gmail.com (Kinoko)</webMaster><copyright>&amp;copy; Brewed by Kinoko</copyright><lastBuildDate>Thu, 06 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://kinoko-tech-blog-theta.vercel.app/tags/go/index.xml" rel="self" type="application/rss+xml"/><item><title>TIL: Go's context.AfterFunc</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/til-sample-post/</link><pubDate>Thu, 06 Aug 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/til-sample-post/</guid><description>&lt;p&gt;Go 1.21 added &lt;code&gt;context.AfterFunc&lt;/code&gt; which registers a function to run (in its own goroutine) after a context is done.&lt;/p&gt;





&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AfterFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// cleanup logic here&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Call stop() if you want to prevent the function from running&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is cleaner than spinning up a goroutine with &lt;code&gt;select&lt;/code&gt; on &lt;code&gt;ctx.Done()&lt;/code&gt; &amp;ndash; the stdlib handles the lifecycle for you.&lt;/p&gt;
&lt;p&gt;Key things to note:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Go 1.21 added <code>context.AfterFunc</code> which registers a function to run (in its own goroutine) after a context is done.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="nx">stop</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nf">AfterFunc</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="c1">// cleanup logic here</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="nx">conn</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1">// Call stop() if you want to prevent the function from running</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="k">defer</span><span class="w"> </span><span class="nf">stop</span><span class="p">()</span></span></span></code></pre></div><p>This is cleaner than spinning up a goroutine with <code>select</code> on <code>ctx.Done()</code> &ndash; the stdlib handles the lifecycle for you.</p>
<p>Key things to note:</p>
<ul>
<li>The function runs in its own goroutine</li>
<li><code>stop()</code> returns <code>true</code> if it successfully prevented the call</li>
<li>Multiple <code>AfterFunc</code> calls on the same context are independent</li>
</ul>
]]></content:encoded></item><item><title>dbtpl Code Generation</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/dbtpl-code-generation/</link><pubDate>Mon, 23 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/dbtpl-code-generation/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;dbtpl (part of the xo ecosystem) connects to a database, introspects the schema, and generates type-safe structs and query methods through Go templates. Hand-written DB structs easily drift from the schema; dbtpl makes the schema the single source of truth.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt;&lt;/p&gt;





&lt;pre tabindex="0"&gt;&lt;code&gt;Database (PostgreSQL / MySQL / SQLite...)
 ↓ introspect schema (tables, columns, types, indexes, FK...)
dbtpl
 ↓ apply Go templates
Generated Go code (structs, query methods)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;No need to maintain DB structs by hand &amp;ndash; when the schema changes, re-run dbtpl to sync.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>dbtpl (part of the xo ecosystem) connects to a database, introspects the schema, and generates type-safe structs and query methods through Go templates. Hand-written DB structs easily drift from the schema; dbtpl makes the schema the single source of truth.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>How it works</strong></p>





<pre tabindex="0"><code>Database (PostgreSQL / MySQL / SQLite...)
    ↓ introspect schema (tables, columns, types, indexes, FK...)
dbtpl
    ↓ apply Go templates
Generated Go code (structs, query methods)</code></pre><p>No need to maintain DB structs by hand &ndash; when the schema changes, re-run dbtpl to sync.</p>
<p><strong>Two modes</strong></p>
<p><strong>Schema mode</strong>: generate from the entire DB schema</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">dbtpl schema postgres://user:pass@host/dbname -o ./models</span></span></code></pre></div><p>Generates a Go struct for each table, including methods related to primary keys, foreign keys, and indexes.</p>
<p><strong>Query mode</strong>: generate type-safe result structs from custom SQL</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">dbtpl query postgres://user:pass@host/dbname <span class="s">&lt;&lt; ENDSQL
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="s">SELECT a.name::varchar AS name, b.type::integer AS my_type
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="s">FROM authors a
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="s">JOIN authortypes b ON a.id = b.author_id
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="s">WHERE a.id = %%authorID int%%
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="s">ENDSQL</span></span></span></code></pre></div><p><code>%%param type%%</code> is dbtpl&rsquo;s query parameter syntax, which generates a corresponding function signature.</p>
<p><strong>What gets generated</strong></p>
<ul>
<li>Go structs matching each table (field types aligned with DB schema)</li>
<li>CRUD query methods (Insert, Update, Delete, Get by PK)</li>
<li>Enum types</li>
<li>Struct tags (<code>db:&quot;column_name&quot;</code> etc.)</li>
</ul>
<p><strong>Template customization</strong></p>
<p>dbtpl&rsquo;s generation logic uses Go <code>text/template</code>. You can dump the built-in templates and modify them:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">dbtpl dump --src base -o ./custom-templates
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># edit the .tpl files in custom-templates/</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">dbtpl schema postgres://... --src ./custom-templates</span></span></code></pre></div><h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>Why not just hand-write structs?</strong></p>
<table>
	<thead>
			<tr>
					<th></th>
					<th>Hand-written structs</th>
					<th>dbtpl generated</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Schema sync</td>
					<td>Manual maintenance, easy to drift</td>
					<td>Re-run to sync</td>
			</tr>
			<tr>
					<td>Type safety</td>
					<td>Relies on the engineer to verify</td>
					<td>Derived from DB schema</td>
			</tr>
			<tr>
					<td>After migration</td>
					<td>Must remember to update structs</td>
					<td>Just re-run</td>
			</tr>
	</tbody>
</table>
<p><strong>dbtpl vs sqlc</strong></p>
<p>Both generate Go code from a database, but they differ in approach:</p>
<ul>
<li><strong>sqlc</strong>: SQL-query-centric &ndash; write SQL first, then generate the corresponding functions</li>
<li><strong>dbtpl</strong>: schema-centric &ndash; introspects the entire DB, with more template flexibility</li>
</ul>
]]></content:encoded></item><item><title>Go Context</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/go-context/</link><pubDate>Mon, 23 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/go-context/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;Go&amp;rsquo;s &lt;code&gt;context&lt;/code&gt; is the standard solution for propagating cancellation signals, timeouts, and request-scoped data across goroutines. It lets an entire call chain stop cleanly at a unified point, leaving no goroutine leaks.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An HTTP handler often runs multiple goroutines underneath &amp;ndash; hitting the DB, calling external APIs, doing computation. Three scenarios cause trouble:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Timeout&lt;/strong&gt;: the request times out, and downstream work should stop too&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cancellation&lt;/strong&gt;: the user closes the browser mid-request; continuing is wasted work&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request-scoped data&lt;/strong&gt;: auth tokens and trace IDs need to flow down the call chain without adding a parameter to every function&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;context&lt;/code&gt; unifies all three into a single standard interface.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>Go&rsquo;s <code>context</code> is the standard solution for propagating cancellation signals, timeouts, and request-scoped data across goroutines. It lets an entire call chain stop cleanly at a unified point, leaving no goroutine leaks.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>The problem</strong></p>
<p>An HTTP handler often runs multiple goroutines underneath &ndash; hitting the DB, calling external APIs, doing computation. Three scenarios cause trouble:</p>
<ol>
<li><strong>Timeout</strong>: the request times out, and downstream work should stop too</li>
<li><strong>Cancellation</strong>: the user closes the browser mid-request; continuing is wasted work</li>
<li><strong>Request-scoped data</strong>: auth tokens and trace IDs need to flow down the call chain without adding a parameter to every function</li>
</ol>
<p><code>context</code> unifies all three into a single standard interface.</p>
<hr>
<p><strong>Basic usage</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="nx">cancel</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nf">WithTimeout</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span><span class="w"> </span><span class="mi">5</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">defer</span><span class="w"> </span><span class="nf">cancel</span><span class="p">()</span><span class="w"> </span><span class="c1">// must call this, otherwise it leaks resources</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nx">result</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">db</span><span class="p">.</span><span class="nf">QueryContext</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;SELECT ...&#34;</span><span class="p">)</span></span></span></code></pre></div><p>When the timeout expires, <code>ctx.Done()</code> closes, and all context-aware functions stop automatically.</p>
<hr>
<p><strong>How to receive it in a function</strong></p>
<p>Functions that accept a context take <code>ctx context.Context</code> as the first parameter &ndash; this is a mandatory Go convention.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">doWork</span><span class="p">(</span><span class="nx">ctx</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="k">select</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="o">&lt;-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Err</span><span class="p">()</span><span class="w"> </span><span class="c1">// context.DeadlineExceeded or context.Canceled</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">result</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&lt;-</span><span class="nf">longOperation</span><span class="p">():</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nf">process</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><hr>
<p><strong>Four ways to create a context</strong></p>
<table>
	<thead>
			<tr>
					<th>Constructor</th>
					<th>Purpose</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>context.Background()</code></td>
					<td>Root context, used at program entry points</td>
			</tr>
			<tr>
					<td><code>context.TODO()</code></td>
					<td>Placeholder when you are not sure what to use yet</td>
			</tr>
			<tr>
					<td><code>context.WithCancel(parent)</code></td>
					<td>Manual cancellation</td>
			</tr>
			<tr>
					<td><code>context.WithTimeout / WithDeadline</code></td>
					<td>Auto-cancel when time expires</td>
			</tr>
			<tr>
					<td><code>context.WithValue(parent, key, val)</code></td>
					<td>Attach request-scoped values (use sparingly)</td>
			</tr>
	</tbody>
</table>
<p>Every new context is a child of its parent &ndash; <strong>cancellation only propagates from parent to child</strong>. When the parent is cancelled, all children are cancelled too.</p>
<h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>Context must not be stored in a struct</strong></p>
<blockquote>
<p>Always pass it as a parameter, never put it in a struct field.</p>
</blockquote>
<p>A context represents the lifetime of a request, not object state. Storing it in a struct makes the lifetime unclear.</p>
<p><strong><code>WithValue</code> is only for cross-cutting concerns</strong></p>
<p><code>WithValue</code> is for things like tracing IDs and auth tokens &ndash; data every request needs but that would pollute function signatures. Do not use it to pass regular business parameters &ndash; for those, just add a proper parameter.</p>
<p><strong>Mental model</strong></p>
<blockquote>
<p>Context is a control signal for a tree. When the root says &ldquo;stop,&rdquo; every goroutine in the tree stops &ndash; cleanly, no leaks.</p>
</blockquote>
]]></content:encoded></item><item><title>Go Deep Copy vs Shallow Copy</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/go-deep-copy-vs-shallow-copy/</link><pubDate>Mon, 23 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/go-deep-copy-vs-shallow-copy/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;A shallow copy copies the &amp;ldquo;reference to the data&amp;rdquo; &amp;ndash; the original and the copy still share the underlying data. A deep copy copies &amp;ldquo;the data itself&amp;rdquo; &amp;ndash; the two are completely independent. Go&amp;rsquo;s assignment is shallow copy by default; whether the original is affected depends on whether the type is a value type or a reference type.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Value type vs Reference type&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Value type (assignment = full copy)&lt;/th&gt;
					&lt;th&gt;Reference type (assignment = copy reference)&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;int&lt;/code&gt;, &lt;code&gt;float64&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;string&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;slice&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;pointer&lt;/code&gt;, &lt;code&gt;channel&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;array&lt;/code&gt; (&lt;code&gt;[N]T&lt;/code&gt;)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;interface&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;struct&lt;/code&gt; (but fields may contain references)&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Slice: the most common gotcha&lt;/strong&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>A shallow copy copies the &ldquo;reference to the data&rdquo; &ndash; the original and the copy still share the underlying data. A deep copy copies &ldquo;the data itself&rdquo; &ndash; the two are completely independent. Go&rsquo;s assignment is shallow copy by default; whether the original is affected depends on whether the type is a value type or a reference type.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>Value type vs Reference type</strong></p>
<table>
	<thead>
			<tr>
					<th>Value type (assignment = full copy)</th>
					<th>Reference type (assignment = copy reference)</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>int</code>, <code>float64</code>, <code>bool</code>, <code>string</code></td>
					<td><code>slice</code>, <code>map</code>, <code>pointer</code>, <code>channel</code></td>
			</tr>
			<tr>
					<td><code>array</code> (<code>[N]T</code>)</td>
					<td><code>interface</code></td>
			</tr>
			<tr>
					<td><code>struct</code> (but fields may contain references)</td>
					<td></td>
			</tr>
	</tbody>
</table>
<p><strong>Slice: the most common gotcha</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="nx">a</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">b</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">a</span><span class="w">          </span><span class="c1">// shallow copy: b and a share the same underlying array</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nx">b</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">99</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="w">  </span><span class="c1">// [99 2 3]  &lt;- a was modified!</span></span></span></code></pre></div>




<pre tabindex="0"><code>a → [ header: ptr → [99, 2, 3], len=3, cap=3 ]
b → [ header: ptr ↗ ]   ← same array</code></pre><p>To deep copy a slice, use <code>copy()</code>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="nx">c</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">copy</span><span class="p">(</span><span class="nx">c</span><span class="p">,</span><span class="w"> </span><span class="nx">a</span><span class="p">)</span><span class="w">      </span><span class="c1">// deep copy</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nx">c</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">0</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span><span class="w">  </span><span class="c1">// [99 2 3]  &lt;- a is not affected</span></span></span></code></pre></div><p><strong>Map: assignment also only copies the reference</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nx">m1</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span><span class="p">{</span><span class="s">&#34;a&#34;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">m2</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">m1</span><span class="w">        </span><span class="c1">// shallow copy</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">m2</span><span class="p">[</span><span class="s">&#34;a&#34;</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">99</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">m1</span><span class="p">[</span><span class="s">&#34;a&#34;</span><span class="p">])</span><span class="w">  </span><span class="c1">// 99 &lt;- m1 was also modified</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">// deep copy a map by looping manually</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">m3</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">for</span><span class="w"> </span><span class="nx">k</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">m1</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="nx">m3</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">v</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><strong>Struct: value copy by default, but watch out for pointer/slice fields</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">Person</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="nx">Name</span><span class="w">   </span><span class="kt">string</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="nx">Scores</span><span class="w"> </span><span class="p">[]</span><span class="kt">int</span><span class="w">   </span><span class="c1">// slice is a reference type!</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">p1</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">Person</span><span class="p">{</span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Alice&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">Scores</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="kt">int</span><span class="p">{</span><span class="mi">90</span><span class="p">,</span><span class="w"> </span><span class="mi">80</span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">p2</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">p1</span><span class="w">           </span><span class="c1">// struct is value-copied, but Scores only copies the slice header</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">p2</span><span class="p">.</span><span class="nx">Name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="s">&#34;Bob&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">p1</span><span class="p">.</span><span class="nx">Name</span><span class="p">)</span><span class="w">      </span><span class="c1">// &#34;Alice&#34; &lt;- string is a value type, not affected</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">p2</span><span class="p">.</span><span class="nx">Scores</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">100</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">p1</span><span class="p">.</span><span class="nx">Scores</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span><span class="w"> </span><span class="c1">// 100 &lt;- the underlying array of Scores is shared!</span></span></span></code></pre></div><p>To deep copy a struct with slices, explicitly copy each reference field:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="nx">p3</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">Person</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="nx">Name</span><span class="p">:</span><span class="w">   </span><span class="nx">p1</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="nx">Scores</span><span class="p">:</span><span class="w"> </span><span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">p1</span><span class="p">.</span><span class="nx">Scores</span><span class="p">)),</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nb">copy</span><span class="p">(</span><span class="nx">p3</span><span class="p">.</span><span class="nx">Scores</span><span class="p">,</span><span class="w"> </span><span class="nx">p1</span><span class="p">.</span><span class="nx">Scores</span><span class="p">)</span></span></span></code></pre></div><p><strong><code>copy()</code> limitation: only copies the outer layer</strong></p>
<p><code>copy()</code> is a built-in function, but it only copies element values &ndash; it does not recursively handle nested reference types.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// Elements are value type → copy() works as a deep copy</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nx">a</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nx">b</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nb">copy</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">a</span><span class="p">)</span><span class="w">  </span><span class="c1">// fully independent, no problem</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">// Elements are reference type → copy() only copies the outer layer, inner layer is still shared</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">a</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[][]</span><span class="kt">int</span><span class="p">{{</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">}}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nx">b</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">([][]</span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nb">copy</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span><span class="w"> </span><span class="nx">a</span><span class="p">)</span><span class="w">  </span><span class="c1">// b[0] and a[0] still point to the same underlying array!</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">b</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">99</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">a</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span><span class="w">  </span><span class="c1">// 99 &lt;- still modified</span></span></span></code></pre></div><p>Conclusion: <code>copy()</code> is sufficient when elements are value types; when elements contain reference types, you need to copy recursively by hand.</p>
<h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>Go has no built-in deep copy</strong></p>
<p>For complex nested structures, there is no silver bullet. Common approaches:</p>
<table>
	<thead>
			<tr>
					<th>Method</th>
					<th>Best for</th>
					<th>Downside</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Manual field-by-field</td>
					<td>Simple structures, performance-sensitive</td>
					<td>Tedious, must update when fields are added</td>
			</tr>
			<tr>
					<td><code>encoding/json</code> marshal then unmarshal</td>
					<td>Quick validation / performance not critical</td>
					<td>Slow, only copies exported fields</td>
			</tr>
			<tr>
					<td><code>proto.Clone()</code></td>
					<td>Protobuf messages</td>
					<td>Only works for proto</td>
			</tr>
			<tr>
					<td>Third-party library</td>
					<td>Complex structures</td>
					<td>Adds a dependency</td>
			</tr>
	</tbody>
</table>
<p><strong>Interview essential: the underlying structure of slice assignment</strong></p>
<p>A Go slice is essentially a struct:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// Conceptually looks like this (not actual Go code)</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">SliceHeader</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="nx">Data</span><span class="w"> </span><span class="kt">uintptr</span><span class="w">  </span><span class="c1">// pointer to the underlying array</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="nx">Len</span><span class="w">  </span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="nx">Cap</span><span class="w">  </span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><code>b := a</code> copies this header, not the underlying array &ndash; that is the root cause of shallow copy.</p>
<p><strong>One-sentence test</strong></p>
<blockquote>
<p>If modifying the copy also changes the original, it is a shallow copy. If not, it is a deep copy.</p>
</blockquote>
]]></content:encoded></item><item><title>atomic.Value &amp; Goroutine Token Refresh</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/atomic-value-goroutine-token-refresh/</link><pubDate>Thu, 19 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/atomic-value-goroutine-token-refresh/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;When multiple goroutines share a value that gets updated periodically (like an auth token), use &lt;code&gt;atomic.Value&lt;/code&gt; for lock-free reads and writes &amp;ndash; lighter than a mutex. Combined with &lt;code&gt;time.Ticker&lt;/code&gt; for periodic refresh and &lt;code&gt;ctx.Done()&lt;/code&gt; for goroutine lifecycle control, this forms a complete concurrent token management pattern.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;10 goroutines publish events concurrently, each needing an AUTHZ token. Another goroutine refreshes the token every 40s (TTL 60s, refreshing 20s early as buffer).&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>When multiple goroutines share a value that gets updated periodically (like an auth token), use <code>atomic.Value</code> for lock-free reads and writes &ndash; lighter than a mutex. Combined with <code>time.Ticker</code> for periodic refresh and <code>ctx.Done()</code> for goroutine lifecycle control, this forms a complete concurrent token management pattern.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>The problem</strong></p>
<p>10 goroutines publish events concurrently, each needing an AUTHZ token. Another goroutine refreshes the token every 40s (TTL 60s, refreshing 20s early as buffer).</p>
<p>How do you let the refresher (writer) and 10 publishers (readers) safely share the same token without a data race?</p>
<p><strong>atomic.Value: lock-free shared value</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">token</span><span class="w"> </span><span class="nx">atomic</span><span class="p">.</span><span class="nx">Value</span><span class="w">  </span><span class="c1">// declared, zero value is nil</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1">// Write (refresher goroutine)</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nx">token</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="s">&#34;new-token-string&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1">// Read (publisher goroutines, can read concurrently, no lock needed)</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="nx">t</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">token</span><span class="p">.</span><span class="nf">Load</span><span class="p">().(</span><span class="kt">string</span><span class="p">)</span><span class="w">  </span><span class="c1">// type assertion to get the actual type</span></span></span></code></pre></div><p><code>atomic.Value</code>&rsquo;s Store/Load are atomic operations &ndash; no mutex needed. Multiple goroutines reading concurrently is completely safe, and an occasional Store will not give any reader a corrupted value.</p>
<p><strong>Complete pattern</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">token</span><span class="w"> </span><span class="nx">atomic</span><span class="p">.</span><span class="nx">Value</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// Refresher goroutine: periodically refresh the token</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nx">ticker</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nf">NewTicker</span><span class="p">(</span><span class="mi">40</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="nx">ticker</span><span class="p">.</span><span class="nf">Stop</span><span class="p">()</span><span class="w">  </span><span class="c1">// remember to release ticker resources</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="k">select</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="o">&lt;-</span><span class="nx">ticker</span><span class="p">.</span><span class="nx">C</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">            </span><span class="nx">newToken</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nf">fetchToken</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">                </span><span class="nx">token</span><span class="p">.</span><span class="nf">Store</span><span class="p">(</span><span class="nx">newToken</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="o">&lt;-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">            </span><span class="k">return</span><span class="w">  </span><span class="c1">// exit when context is cancelled</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">}()</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1">// Publisher goroutines: 10 running concurrently</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="mi">10</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">            </span><span class="k">select</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">            </span><span class="k">case</span><span class="w"> </span><span class="nx">event</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&lt;-</span><span class="nx">eventCh</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">                </span><span class="nx">t</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">token</span><span class="p">.</span><span class="nf">Load</span><span class="p">().(</span><span class="kt">string</span><span class="p">)</span><span class="w">  </span><span class="c1">// read current token</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">                </span><span class="nf">publish</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="nx">t</span><span class="p">,</span><span class="w"> </span><span class="nx">event</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">            </span><span class="k">case</span><span class="w"> </span><span class="o">&lt;-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">                </span><span class="k">return</span><span class="w">  </span><span class="c1">// exit when context is cancelled</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">            </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">    </span><span class="p">}()</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><strong>time.Ticker: periodically firing channel</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="nx">ticker</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nf">NewTicker</span><span class="p">(</span><span class="mi">40</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">// ticker.C is a channel that receives a signal every 40s</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1">// must call ticker.Stop() to release the underlying timer resources</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">defer</span><span class="w"> </span><span class="nx">ticker</span><span class="p">.</span><span class="nf">Stop</span><span class="p">()</span></span></span></code></pre></div><p><strong>ctx.Done(): unified exit signal</strong></p>
<p><code>ctx.Done()</code> returns a channel that gets closed when the context is cancelled. All goroutines listening on it in a <code>select</code> will receive the signal and exit simultaneously.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">select</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">case</span><span class="w"> </span><span class="o">&lt;-</span><span class="nx">ticker</span><span class="p">.</span><span class="nx">C</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="c1">// periodic refresh</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">case</span><span class="w"> </span><span class="o">&lt;-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w">  </span><span class="c1">// stop when told to, no goroutine leak</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>sync.Mutex: a lock to protect critical sections</strong></p>
<p>When multiple goroutines read and write the same data, a mutex ensures only one goroutine can enter at a time:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">mu</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">sharedData</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">// Write</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">sharedData</span><span class="p">[</span><span class="s">&#34;key&#34;</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">42</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1">// Read (also needs lock because map is not concurrent-safe)</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">sharedData</span><span class="p">[</span><span class="s">&#34;key&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1">// Idiomatic: defer unlock to avoid forgetting or leaking on panic</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">defer</span><span class="w"> </span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="nx">sharedData</span><span class="p">[</span><span class="s">&#34;key&#34;</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">42</span></span></span></code></pre></div><p><code>sync.RWMutex</code> is the upgraded version &ndash; allows multiple goroutines to read simultaneously (RLock), but writing is exclusive (Lock):</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">mu</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">RWMutex</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// Multiple goroutines can hold RLock simultaneously</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nx">mu</span><span class="p">.</span><span class="nf">RLock</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">sharedData</span><span class="p">[</span><span class="s">&#34;key&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nx">mu</span><span class="p">.</span><span class="nf">RUnlock</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1">// Write is exclusive</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nx">sharedData</span><span class="p">[</span><span class="s">&#34;key&#34;</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">99</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span></span></span></code></pre></div><p><strong>Why atomic.Value instead of mutex?</strong></p>
<table>
	<thead>
			<tr>
					<th></th>
					<th><code>sync.Mutex</code></th>
					<th><code>atomic.Value</code></th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Best for</td>
					<td>Complex critical sections (multi-step operations)</td>
					<td>Single value read/write</td>
			</tr>
			<tr>
					<td>On read</td>
					<td>Needs Lock/Unlock</td>
					<td>Completely lock-free</td>
			</tr>
			<tr>
					<td>10 goroutines reading frequently</td>
					<td>Lock contention</td>
					<td>No contention, better performance</td>
			</tr>
	</tbody>
</table>
<p>The token scenario: writes are rare (once every 40s), reads are frequent (10 goroutines on every publish) &ndash; <code>atomic.Value</code> is the better choice.</p>
<p><strong>What makes atomic operations work?</strong></p>
<p>&ldquo;Atomic&rdquo; means <strong>indivisible</strong> &ndash; the operation appears to others as either completed or not started, never &ldquo;halfway done.&rdquo;</p>
<p>A normal assignment <code>token = newToken</code> may compile to multiple machine instructions. On a multi-core CPU, another core might read in the middle and get a half-written value (data race).</p>
<p>Atomic operations use special CPU instructions (like x86&rsquo;s <code>CMPXCHG</code>) to guarantee at the hardware level:</p>
<ol>
<li><strong>Indivisibility</strong>: Store is fully written before it becomes visible to other cores</li>
<li><strong>Memory visibility</strong>: adds a memory barrier, ensuring all cores see the latest value instead of getting stuck on a CPU cache</li>
</ol>





<pre tabindex="0"><code>Normal assignment: Core A writes halfway → Core B might read a corrupted value
Atomic:            Core A finishes writing before it is visible → Core B always sees a complete value</code></pre><p>This is why <code>atomic.Value</code> is safe without a mutex &ndash; the guarantee is made at the CPU instruction level, not through software locks.</p>
<p><strong>Why refresh every 40s with a 60s TTL?</strong></p>
<p>The 20s buffer absorbs:</p>
<ul>
<li>Network latency when fetching the new token</li>
<li>The time gap before all goroutines read the new token</li>
</ul>
<p>If you wait until 59s to refresh, any slight delay could cause publishers to send requests with an expired token.</p>
<p><strong>Common cause of goroutine leaks</strong></p>
<p>If a goroutine has no exit mechanism, it runs until the process ends. <code>ctx.Done()</code> is the standard exit signal &ndash; once the context is cancelled, all listening goroutines exit cleanly.</p>
]]></content:encoded></item><item><title>Error &amp; Log by Middleware</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/error-log-by-middleware/</link><pubDate>Tue, 17 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/error-log-by-middleware/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;Each request produces exactly one log entry, handled by middleware. Errors are only wrapped at boundaries with third-party code. Logging follows the OpenTelemetry standard so logs can plug into any observability backend.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Four core principles&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Every request gets exactly one log&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Instead of logging in every handler or function, middleware outputs a single structured log entry when the request completes, containing status code, latency, request ID, and other fields. This avoids scattered log entries for the same request that are hard to correlate.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>Each request produces exactly one log entry, handled by middleware. Errors are only wrapped at boundaries with third-party code. Logging follows the OpenTelemetry standard so logs can plug into any observability backend.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>Four core principles</strong></p>
<p><strong>1. Every request gets exactly one log</strong></p>
<p>Instead of logging in every handler or function, middleware outputs a single structured log entry when the request completes, containing status code, latency, request ID, and other fields. This avoids scattered log entries for the same request that are hard to correlate.</p>
<p><strong>2. Log should be wrapped in a proper way</strong></p>
<p>Logs should be structured (structured logging) &ndash; not <code>fmt.Println(&quot;error:&quot;, err)</code>, but a format with fields:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="nt">&#34;level&#34;</span><span class="p">:</span> <span class="s2">&#34;error&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="nt">&#34;request_id&#34;</span><span class="p">:</span> <span class="s2">&#34;abc-123&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">  <span class="nt">&#34;method&#34;</span><span class="p">:</span> <span class="s2">&#34;POST&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">  <span class="nt">&#34;path&#34;</span><span class="p">:</span> <span class="s2">&#34;/orders&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="nt">&#34;latency_ms&#34;</span><span class="p">:</span> <span class="mi">42</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">  <span class="nt">&#34;error&#34;</span><span class="p">:</span> <span class="s2">&#34;calling payment sdk: connection refused&#34;</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><strong>3. Only wrap errors on boundaries with 3rd party code</strong></p>
<p>A <strong>boundary</strong> is the seam between your code and a system you do not own &ndash; calling a DB, calling a third-party SDK, calling an external API are all boundaries.</p>
<p>Errors from third parties lack your context, so wrap once at the boundary. Between internal functions, just pass the error up.</p>
<p>Given the chain: <code>Handler -&gt; getOrder() -&gt; queryOrderFromDB()</code>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// Layer 3: DB boundary, the only place to wrap</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">queryOrderFromDB</span><span class="p">(</span><span class="nx">ctx</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">Order</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="nx">row</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">db</span><span class="p">.</span><span class="nf">Query</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;SELECT * FROM orders WHERE id = ?&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">        </span><span class="c1">// Wrap at the boundary, describe what operation was happening</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;querying order id=%d: %w&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="c1">// ...</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1">// Layer 2: internal function, pass up directly, do not re-wrap</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">getOrder</span><span class="p">(</span><span class="nx">ctx</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">Order</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="nx">order</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nf">queryOrderFromDB</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="nx">id</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span><span class="c1">// Just return, no new wrap</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">order</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1">// Layer 1: handler, also does not wrap -- let middleware handle the log</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">handleGetOrder</span><span class="p">(</span><span class="nx">w</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="w"> </span><span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">    </span><span class="nx">order</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nf">getOrder</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nf">Context</span><span class="p">(),</span><span class="w"> </span><span class="nx">id</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">        </span><span class="c1">// Hand off to middleware, do not log here</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">        </span><span class="nx">http</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;internal error&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusInternalServerError</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">    </span><span class="c1">// ...</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><strong>If every layer wraps, the error message becomes:</strong></p>





<pre tabindex="0"><code>handleGetOrder: getOrder: queryOrderFromDB: querying order id=42: connection refused</code></pre><p>The repeated function names add no new information &ndash; just noise.</p>
<p><strong>Wrapping only at the boundary is much clearer:</strong></p>





<pre tabindex="0"><code>querying order id=42: connection refused</code></pre><p>The rule: <strong>wrap = add new information. No new information, just return err.</strong></p>
<p><strong>4. OTEL compliant logging in middleware</strong></p>
<p>OTEL = <strong>OpenTelemetry</strong>, the CNCF observability standard that defines a unified format and API for traces, metrics, and logs.</p>
<p>When middleware outputs OTEL-compliant logs, you can plug into any backend (Grafana, Datadog, GCP Cloud Logging) without changing application code.</p>





<pre tabindex="0"><code>Request → Middleware (start timer, inject trace context)
           ↓
         Handler (process logic, pass errors up)
           ↓
         Middleware (at the end, log one entry with status / latency / error)</code></pre><h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>Why not let each handler log on its own?</strong></p>
<p>Problems with scattered logging:</p>
<ul>
<li>The same request might get logged multiple times across functions &ndash; hard to correlate</li>
<li>Inconsistent formats &ndash; hard to filter with queries</li>
<li>Duplicate logs &ndash; more noise, higher cost</li>
</ul>
<p>Middleware-centralized logging means: one request = one log entry = one trace. Searching and debugging become much more intuitive.</p>
<p><strong>Error wrap decision guide</strong></p>
<table>
	<thead>
			<tr>
					<th>Location</th>
					<th>Wrap?</th>
					<th>Reason</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Calling DB / third-party SDK</td>
					<td>Yes</td>
					<td>Their errors lack your context</td>
			</tr>
			<tr>
					<td>Calling another internal service</td>
					<td>Usually yes</td>
					<td>Cross-service boundary</td>
			</tr>
			<tr>
					<td>Passing between internal functions</td>
					<td>No</td>
					<td>Just stacks messages with no new information</td>
			</tr>
	</tbody>
</table>
]]></content:encoded></item><item><title>Go Generics</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/go-generics/</link><pubDate>Wed, 11 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/go-generics/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;Generics (Go 1.18+) let you write type-parameterized functions &amp;ndash; the type is determined at call time, but type checking happens at compile time. This gives you both flexibility and safety.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Without Generics vs with Generics&lt;/strong&gt;&lt;/p&gt;





&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Without generics: same logic duplicated for each type&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;sumInts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;sumFloats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Or use any, but lose type safety -- errors only show up at runtime&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;sumAny&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// With generics: one function, compile-time type safety&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// T = int&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// T = float64&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Syntax breakdown&lt;/strong&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>Generics (Go 1.18+) let you write type-parameterized functions &ndash; the type is determined at call time, but type checking happens at compile time. This gives you both flexibility and safety.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>Without Generics vs with Generics</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// Without generics: same logic duplicated for each type</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">sumInts</span><span class="p">(</span><span class="nx">nums</span><span class="w"> </span><span class="p">[]</span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">total</span><span class="w"> </span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">nums</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">total</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">total</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">sumFloats</span><span class="p">(</span><span class="nx">nums</span><span class="w"> </span><span class="p">[]</span><span class="kt">float64</span><span class="p">)</span><span class="w"> </span><span class="kt">float64</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">total</span><span class="w"> </span><span class="kt">float64</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">nums</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">total</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">total</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1">// Or use any, but lose type safety -- errors only show up at runtime</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">sumAny</span><span class="p">(</span><span class="nx">nums</span><span class="w"> </span><span class="p">[]</span><span class="kt">any</span><span class="p">)</span><span class="w"> </span><span class="kt">any</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1">// With generics: one function, compile-time type safety</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nx">sum</span><span class="p">[</span><span class="nx">T</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="kt">float64</span><span class="p">](</span><span class="nx">nums</span><span class="w"> </span><span class="p">[]</span><span class="nx">T</span><span class="p">)</span><span class="w"> </span><span class="nx">T</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">total</span><span class="w"> </span><span class="nx">T</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">nums</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">total</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="nx">n</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">total</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="nf">sum</span><span class="p">([]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">})</span><span class="w">       </span><span class="c1">// T = int</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="nf">sum</span><span class="p">([]</span><span class="kt">float64</span><span class="p">{</span><span class="mf">1.1</span><span class="p">,</span><span class="w"> </span><span class="mf">2.2</span><span class="p">})</span><span class="w">  </span><span class="c1">// T = float64</span></span></span></code></pre></div><p><strong>Syntax breakdown</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nx">ptr</span><span class="p">[</span><span class="nx">T</span><span class="w"> </span><span class="kt">any</span><span class="p">](</span><span class="nx">v</span><span class="w"> </span><span class="nx">T</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="nx">T</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">v</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">//       ^^^^^^  ^ ^</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1">//       |       | return type is also T</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">//       |       parameter type is T</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1">//       type parameter: T is the name, any is the constraint</span></span></span></code></pre></div><ul>
<li><code>T</code> &ndash; type parameter name, convention is a single uppercase letter</li>
<li><code>any</code> &ndash; constraint, meaning <code>T</code> can be any type</li>
</ul>
<p><strong>Constraints can be narrowed</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// Only accept numbers</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nx">sum</span><span class="p">[</span><span class="nx">T</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="kt">float64</span><span class="p">](</span><span class="nx">nums</span><span class="w"> </span><span class="p">[]</span><span class="nx">T</span><span class="p">)</span><span class="w"> </span><span class="nx">T</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">// Only accept types that implement String()</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nx">print</span><span class="p">[</span><span class="nx">T</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Stringer</span><span class="p">](</span><span class="nx">v</span><span class="w"> </span><span class="nx">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">...</span><span class="w"> </span><span class="p">}</span></span></span></code></pre></div><p><strong>Type inference: usually no need to specify explicitly</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="nf">ptr</span><span class="p">(</span><span class="s">&#34;hello&#34;</span><span class="p">)</span><span class="w">         </span><span class="c1">// inferred: T = string</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">ptr</span><span class="p">[</span><span class="kt">string</span><span class="p">](</span><span class="s">&#34;hello&#34;</span><span class="p">)</span><span class="w"> </span><span class="c1">// explicit, same result</span></span></span></code></pre></div><h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>Generics vs <code>any</code> vs <code>interface</code></strong></p>
<table>
	<thead>
			<tr>
					<th></th>
					<th><code>any</code></th>
					<th><code>interface</code> (with methods)</th>
					<th>Generics</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Type resolved at</td>
					<td>Runtime</td>
					<td>Runtime</td>
					<td>Compile time</td>
			</tr>
			<tr>
					<td>Type safety</td>
					<td>No</td>
					<td>Partial</td>
					<td>Yes</td>
			</tr>
			<tr>
					<td>Best for</td>
					<td>Truly type-agnostic code</td>
					<td>Constraining behavior</td>
					<td>Same logic across multiple types</td>
			</tr>
	</tbody>
</table>
<p><strong>When to use Generics?</strong>
When the same logic needs to work across multiple types and you want compile-time type error detection. Pointer helpers and collection operations (map, filter, reduce) are the most common examples.</p>
]]></content:encoded></item><item><title>Go Modern Syntax Fixes</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/go-modern-syntax-fixes/</link><pubDate>Wed, 11 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/go-modern-syntax-fixes/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;Go 1.22+ introduced several language-level changes. &lt;code&gt;go fix&lt;/code&gt; can automatically upgrade old patterns to modern ones: loop variable capture, &lt;code&gt;interface{}&lt;/code&gt; to &lt;code&gt;any&lt;/code&gt;, and inline pointer helper functions can all be removed or simplified.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. Remove unnecessary loop variable captures&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before Go 1.22, the for loop variable was &lt;strong&gt;shared across all iterations&lt;/strong&gt; at the same memory address. Closures captured the same variable, causing a classic bug:&lt;/p&gt;





&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Before Go 1.22: all goroutines print the same value (the last one)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Old workaround: redeclare inside the loop to force a new variable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// capture a new variable per iteration&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Starting from Go 1.22, each iteration has an &lt;strong&gt;independent variable&lt;/strong&gt;, making &lt;code&gt;v := v&lt;/code&gt; redundant. &lt;code&gt;go fix&lt;/code&gt; removes it automatically.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>Go 1.22+ introduced several language-level changes. <code>go fix</code> can automatically upgrade old patterns to modern ones: loop variable capture, <code>interface{}</code> to <code>any</code>, and inline pointer helper functions can all be removed or simplified.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>1. Remove unnecessary loop variable captures</strong></p>
<p>Before Go 1.22, the for loop variable was <strong>shared across all iterations</strong> at the same memory address. Closures captured the same variable, causing a classic bug:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// Before Go 1.22: all goroutines print the same value (the last one)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">items</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">v</span><span class="p">)</span><span class="w"> </span><span class="p">}()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">// Old workaround: redeclare inside the loop to force a new variable</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">items</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">v</span><span class="w">  </span><span class="c1">// capture a new variable per iteration</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">v</span><span class="p">)</span><span class="w"> </span><span class="p">}()</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Starting from Go 1.22, each iteration has an <strong>independent variable</strong>, making <code>v := v</code> redundant. <code>go fix</code> removes it automatically.</p>
<p><strong>2. Replace <code>interface{}</code> with <code>any</code></strong></p>
<p>Go 1.18 introduced <code>any</code> as a type alias for <code>interface{}</code>. They are identical, but <code>any</code> is shorter:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// Old</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">process</span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">m</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1">// New</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">process</span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="kt">any</span><span class="p">)</span><span class="w"> </span><span class="p">{}</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">m</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">any</span></span></span></code></pre></div><p><strong>3. Remove inline pointer helper functions</strong></p>
<p>Taking a pointer to a literal used to require a helper function (because Go does not allow <code>&amp;&quot;hello&quot;</code> directly):</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// Old workaround</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">strPtr</span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">s</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">intPtr</span><span class="p">(</span><span class="nx">i</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="kt">int</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">i</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nx">name</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nf">strPtr</span><span class="p">(</span><span class="s">&#34;Alice&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">age</span><span class="w">  </span><span class="o">:=</span><span class="w"> </span><span class="nf">intPtr</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span></span></span></code></pre></div><p>Modern Go can replace all type-specific helpers with a single generic version, or just declare a variable and take its address:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// One generic helper replaces all</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nx">ptr</span><span class="p">[</span><span class="nx">T</span><span class="w"> </span><span class="kt">any</span><span class="p">](</span><span class="nx">v</span><span class="w"> </span><span class="nx">T</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="nx">T</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">v</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">// Or just declare and reference</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nx">name</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;Alice&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">req</span><span class="p">.</span><span class="nx">Name</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">name</span></span></span></code></pre></div><p><strong>What is a literal?</strong></p>
<p>A literal is a fixed value written directly in code &ndash; not a variable, not a computed result, just the value as written:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="s">&#34;hello&#34;</span><span class="w">        </span><span class="c1">// string literal</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="mi">42</span><span class="w">             </span><span class="c1">// integer literal</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kc">true</span><span class="w">           </span><span class="c1">// boolean literal</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">[]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">}</span><span class="w"> </span><span class="c1">// slice literal</span></span></span></code></pre></div><p>Go does not allow taking a pointer to a literal (<code>&amp;&quot;hello&quot;</code> is a compile error) because literals have no memory address. You must assign to a variable first &ndash; that is why pointer helper functions existed.</p>
<p>For details on generics, see the Go Generics post.</p>
<h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>What is <code>go fix</code>?</strong></p>
<p><code>go fix</code> is an official Go refactoring tool that applies mechanical fixes for each version&rsquo;s breaking changes or language updates. Running it once when upgrading saves a lot of manual edits.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">go fix ./...</span></span></code></pre></div><p><strong>Why is the Go 1.22 loop variable change important?</strong></p>
<p>This bug existed for over ten years and was one of Go&rsquo;s most famous gotchas &ndash; nearly every Go developer hit it at least once. Go 1.22 changed the behavior to match intuition, which is a significant language-level fix.</p>
]]></content:encoded></item><item><title>JSON &amp; Marshal Unmarshal</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/json-marshal-unmarshal/</link><pubDate>Thu, 05 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/json-marshal-unmarshal/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;Marshal converts an in-memory data structure into a transmittable format (like JSON). Unmarshal does the reverse &amp;ndash; it turns JSON back into a data structure. Most APIs use JSON because it is human-readable, natively supported by JavaScript, and universally available across languages.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Marshal / Unmarshal&lt;/strong&gt;&lt;/p&gt;





&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ShippingClass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;name&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Lang&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;lang&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Unmarshal: JSON string → struct&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;jsonStr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`{&amp;#34;name&amp;#34;: &amp;#34;標準配送&amp;#34;, &amp;#34;lang&amp;#34;: &amp;#34;zh&amp;#34;}`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ShippingClass&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nb"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jsonStr&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// obj.Name == &amp;#34;標準配送&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Marshal: struct → JSON string&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// data == {&amp;#34;name&amp;#34;:&amp;#34;標準配送&amp;#34;,&amp;#34;lang&amp;#34;:&amp;#34;zh&amp;#34;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is not limited to JSON &amp;ndash; serialization for XML, YAML, and protobuf is also called marshal/unmarshal. Same concept, different formats.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>Marshal converts an in-memory data structure into a transmittable format (like JSON). Unmarshal does the reverse &ndash; it turns JSON back into a data structure. Most APIs use JSON because it is human-readable, natively supported by JavaScript, and universally available across languages.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>Marshal / Unmarshal</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">ShippingClass</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="nx">Name</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:&#34;name&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="nx">Lang</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:&#34;lang&#34;`</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">// Unmarshal: JSON string → struct</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nx">jsonStr</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">`{&#34;name&#34;: &#34;標準配送&#34;, &#34;lang&#34;: &#34;zh&#34;}`</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="kd">var</span><span class="w"> </span><span class="nx">obj</span><span class="w"> </span><span class="nx">ShippingClass</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">jsonStr</span><span class="p">),</span><span class="w"> </span><span class="o">&amp;</span><span class="nx">obj</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1">// obj.Name == &#34;標準配送&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1">// Marshal: struct → JSON string</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nx">data</span><span class="p">,</span><span class="w"> </span><span class="nx">_</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1">// data == {&#34;name&#34;:&#34;標準配送&#34;,&#34;lang&#34;:&#34;zh&#34;}</span></span></span></code></pre></div><p>This is not limited to JSON &ndash; serialization for XML, YAML, and protobuf is also called marshal/unmarshal. Same concept, different formats.</p>
<p><strong>Why do most APIs use JSON?</strong></p>
<ul>
<li><strong>Human-readable</strong>: you can read it directly when debugging; binary formats like protobuf cannot do this</li>
<li><strong>Native JS support</strong>: browsers parse JSON at zero cost, no extra handling on the frontend</li>
<li><strong>Universal</strong>: almost every language has a mature JSON library</li>
<li><strong>Less verbose than XML</strong>: XML&rsquo;s opening and closing tags add a lot of overhead</li>
</ul>
<h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>What are Go struct tags?</strong></p>
<p>The <code>json:&quot;name&quot;</code> tag tells Go&rsquo;s JSON library which key name this field maps to in JSON. Without a tag, it defaults to the field name (case-sensitive):</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">Example</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="nx">DisplayName</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:&#34;display_name&#34;`</span><span class="w">  </span><span class="c1">// JSON key is display_name</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="nx">Age</span><span class="w">         </span><span class="kt">int</span><span class="w">    </span><span class="s">`json:&#34;age,omitempty&#34;`</span><span class="w"> </span><span class="c1">// omitempty: omit this key when value is zero</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="nx">Internal</span><span class="w">    </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:&#34;-&#34;`</span><span class="w">             </span><span class="c1">// never output to JSON</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><strong>Downsides of JSON</strong></p>
<p>JSON is not without problems. In high-performance scenarios (internal microservice communication), protobuf is the usual choice:</p>
<table>
	<thead>
			<tr>
					<th></th>
					<th>JSON</th>
					<th>Protobuf</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Readability</td>
					<td>Human-readable</td>
					<td>Binary</td>
			</tr>
			<tr>
					<td>Performance</td>
					<td>Slower (string parsing)</td>
					<td>Fast (binary decode)</td>
			</tr>
			<tr>
					<td>Schema</td>
					<td>Not enforced</td>
					<td>Enforced (.proto definition)</td>
			</tr>
			<tr>
					<td>Use case</td>
					<td>External public APIs</td>
					<td>Internal microservices</td>
			</tr>
	</tbody>
</table>
]]></content:encoded></item><item><title>GOPROXY</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/goproxy/</link><pubDate>Wed, 25 Feb 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/goproxy/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;Fetching private Go modules directly from GitHub in CI/CD easily gets rate-limited (403) when there are too many requests. The fix is to set &lt;code&gt;GOPROXY&lt;/code&gt; in your CD yaml to point at an internal Athens proxy server &amp;ndash; Athens caches the modules, so subsequent builds never hit GitHub again.&lt;/p&gt;





&lt;pre tabindex="0"&gt;&lt;code&gt;CI / go build
 |
 Athens (internal proxy, with cache)
 | (only on first fetch)
 GitHub / VCS&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;Go has three environment variables that control how modules are fetched:&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>Fetching private Go modules directly from GitHub in CI/CD easily gets rate-limited (403) when there are too many requests. The fix is to set <code>GOPROXY</code> in your CD yaml to point at an internal Athens proxy server &ndash; Athens caches the modules, so subsequent builds never hit GitHub again.</p>





<pre tabindex="0"><code>CI / go build
      |
   Athens (internal proxy, with cache)
      | (only on first fetch)
   GitHub / VCS</code></pre><h2 id="explanation">Explanation</h2>
<p>Go has three environment variables that control how modules are fetched:</p>
<table>
	<thead>
			<tr>
					<th>Variable</th>
					<th>Example Value</th>
					<th>Controls</th>
					<th>What It Means</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>GOPROXY</code></td>
					<td><code>https://internal-proxy.company.com</code></td>
					<td>Where modules are downloaded from</td>
					<td>Go only fetches from the internal proxy; if the proxy doesn&rsquo;t have it, the build fails (unless you append <code>,direct</code>)</td>
			</tr>
			<tr>
					<td><code>GONOSUMDB</code></td>
					<td><code>company.com/*</code></td>
					<td>Checksum verification source</td>
					<td>Matching modules skip the public <code>sum.golang.org</code> for verification</td>
			</tr>
			<tr>
					<td><code>GOPRIVATE</code></td>
					<td><code>company.com/*</code></td>
					<td>Shorthand for both above</td>
					<td>Simultaneously skips the public proxy and public checksum DB</td>
			</tr>
	</tbody>
</table>
<p><strong>GOPROXY fallback chain</strong></p>
<p><code>GOPROXY</code> supports comma-separated sources &ndash; Go tries them in order:</p>





<pre tabindex="0"><code>GOPROXY=https://internal-proxy.company.com,direct</code></pre><p><code>direct</code> is not another proxy &ndash; it means fetching directly from VCS (GitHub). If the internal proxy doesn&rsquo;t have a particular public module, it falls back to <code>direct</code>.</p>
<h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>Why can&rsquo;t private modules be sent to <code>sum.golang.org</code>?</strong>
<code>sum.golang.org</code> is a public service. By default, Go sends the module path there for checksum verification. If a private module&rsquo;s path gets sent, it leaks internal repo information. Use <code>GONOSUMDB</code> or <code>GOPRIVATE</code> to exclude them.</p>
<p><strong><code>GOPRIVATE</code> vs setting <code>GOPROXY</code> + <code>GONOSUMDB</code> separately</strong>
<code>GOPRIVATE=company.com/*</code> is equivalent to setting both <code>GONOSUMDB=company.com/*</code> and <code>GONOPROXY=company.com/*</code> (skip proxy, go straight to VCS). If you&rsquo;re using an internal Athens proxy, you usually still need to explicitly set <code>GOPROXY</code>, because <code>GOPRIVATE</code> tells Go &ldquo;don&rsquo;t use a proxy&rdquo; &ndash; which would bypass your Athens instance and hit GitHub directly.</p>
<p><strong>How to pass a GitHub token in Docker builds?</strong>
If your Dockerfile runs <code>go build</code> and needs to fetch private modules, don&rsquo;t use <code>ARG</code> for the token &ndash; <code>ARG</code> values end up in the build history and can be recovered from image layers even after the file is deleted.</p>
<p>The correct approach is Docker BuildKit&rsquo;s secret mount:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">RUN</span> --mount<span class="o">=</span><span class="nv">type</span><span class="o">=</span>secret,id<span class="o">=</span>github_token <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="nv">GITHUB_TOKEN</span><span class="o">=</span><span class="k">$(</span>cat /run/secrets/github_token<span class="k">)</span> <span class="se">\
</span></span></span><span class="line"><span class="ln">3</span><span class="cl">    go build ./...</span></span></code></pre></div><p>The secret is only visible during this <code>RUN</code> step and is never written to any image layer.</p>
]]></content:encoded></item><item><title>Go Context Cancel Propagation</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/go-context-cancel-propagation/</link><pubDate>Mon, 23 Feb 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/go-context-cancel-propagation/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;An optional external call should not share a cancellable context &amp;ndash; if that call errors and triggers cancel, the entire context is cancelled, affecting operations that shouldn&amp;rsquo;t be impacted.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;Scenario from a PR review:&lt;/p&gt;





&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ❌ Problematic version&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;GetCampaign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Campaign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cancel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithCancel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// campaign components are optional&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FetchComponents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// defer cancel() fires here, ctx is cancelled&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// all subsequent operations using the same ctx are affected&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;doSomethingElse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The problem: &lt;code&gt;FetchComponents&lt;/code&gt; fails -&amp;gt; returns error -&amp;gt; &lt;code&gt;defer cancel()&lt;/code&gt; executes -&amp;gt; this &lt;code&gt;ctx&lt;/code&gt; is cancelled. If this ctx is shared elsewhere, everything blows up together.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>An optional external call should not share a cancellable context &ndash; if that call errors and triggers cancel, the entire context is cancelled, affecting operations that shouldn&rsquo;t be impacted.</p>
<h2 id="explanation">Explanation</h2>
<p>Scenario from a PR review:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// ❌ Problematic version</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="o">*</span><span class="nx">Service</span><span class="p">)</span><span class="w"> </span><span class="nf">GetCampaign</span><span class="p">(</span><span class="nx">ctx</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">Campaign</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="nx">cancel</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nf">WithCancel</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="nf">cancel</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="c1">// campaign components are optional</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="nx">components</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">s</span><span class="p">.</span><span class="nx">client</span><span class="p">.</span><span class="nf">FetchComponents</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w">  </span><span class="c1">// defer cancel() fires here, ctx is cancelled</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="c1">// all subsequent operations using the same ctx are affected</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="nx">result</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">s</span><span class="p">.</span><span class="nf">doSomethingElse</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="o">...</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>The problem: <code>FetchComponents</code> fails -&gt; returns error -&gt; <code>defer cancel()</code> executes -&gt; this <code>ctx</code> is cancelled. If this ctx is shared elsewhere, everything blows up together.</p>
<p>Since campaign components are <strong>optional</strong>, the correct approach is to absorb the error from this call and not propagate it upward:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// ✅ Correct version: optional call errors don&#39;t affect the main flow</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="o">*</span><span class="nx">Service</span><span class="p">)</span><span class="w"> </span><span class="nf">GetCampaign</span><span class="p">(</span><span class="nx">ctx</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="nx">Campaign</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="c1">// don&#39;t create a cancel context here</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nx">components</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">s</span><span class="p">.</span><span class="nx">client</span><span class="p">.</span><span class="nf">FetchComponents</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">        </span><span class="c1">// optional -- log and continue, don&#39;t return error</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">        </span><span class="nx">log</span><span class="p">.</span><span class="nf">Warn</span><span class="p">(</span><span class="s">&#34;failed to fetch optional components&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;err&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="nx">components</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">nil</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="nx">result</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">s</span><span class="p">.</span><span class="nf">doSomethingElse</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="o">...</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>Context cancellation propagation direction</strong></p>
<p>Cancellation only propagates from parent to child, never in reverse. However, <code>cancel()</code> directly cancels the ctx you created &ndash; if other code also holds a reference to this ctx (or child contexts derived from it), they all get cancelled together.</p>
<p><strong>If you need to fully isolate an optional call (unaffected by parent cancellation)</strong></p>
<p>Go 1.21+ has <code>context.WithoutCancel()</code>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// create a ctx that doesn&#39;t inherit the parent&#39;s cancel signal</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">detachedCtx</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nf">WithoutCancel</span><span class="p">(</span><span class="nx">ctx</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">components</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">s</span><span class="p">.</span><span class="nx">client</span><span class="p">.</span><span class="nf">FetchComponents</span><span class="p">(</span><span class="nx">detachedCtx</span><span class="p">)</span></span></span></code></pre></div><p>This way, even if the parent request is cancelled (e.g. client disconnects), this optional call still runs to completion.</p>
<p><strong>When to use which approach:</strong></p>
<table>
	<thead>
			<tr>
					<th>Scenario</th>
					<th>Approach</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Optional call error shouldn&rsquo;t affect main flow</td>
					<td>Absorb the error, don&rsquo;t return it</td>
			</tr>
			<tr>
					<td>Optional call shouldn&rsquo;t be affected by parent cancellation</td>
					<td><code>context.WithoutCancel()</code></td>
			</tr>
			<tr>
					<td>Optional call needs its own timeout</td>
					<td><code>context.WithTimeout(context.WithoutCancel(ctx), ...)</code></td>
			</tr>
	</tbody>
</table>
]]></content:encoded></item></channel></rss>