{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Ashwin Gopalsamy Library",
  "home_page_url": "https://ashwingopalsamy.in/library/",
  "feed_url": "https://ashwingopalsamy.in/library/feed.json",
  "description": "Books, watching, notes, and links",
  "icon": "https://ashwingopalsamy.in/icon-512.png",
  "favicon": "https://ashwingopalsamy.in/favicon.ico",
  "authors": [
    {
      "name": "Ashwin Gopalsamy",
      "url": "https://ashwingopalsamy.in/",
      "avatar": "https://ashwingopalsamy.in/portrait.jpg"
    }
  ],
  "items": [
    {
      "id": "https://ashwingopalsamy.in/library/notes/designing-rate-limiters-for-payment-systems/",
      "url": "https://ashwingopalsamy.in/library/notes/designing-rate-limiters-for-payment-systems/",
      "title": "Designing Rate Limiters for Payment Systems",
      "content_text": "Why payment rate limits can't treat false rejects like a normal API, and how to design for authorization ingress.",
      "date_published": "2026-04-07T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>Rate limiting in payment systems is different from rate limiting in a typical web API. A false positive – rejecting a legitimate authorization – is a failed transaction. A customer’s card gets declined at checkout. That is not an acceptable failure mode.</p>\n<h2 id=\"the-architecture\">The Architecture<a class=\"h-anchor\" href=\"#the-architecture\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>A payment authorization pipeline typically looks like this:</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    A[Card Network] --&gt;|ISO 8583| B[Parser API]\n    B --&gt;|gRPC| C[Distributor API]\n    C --&gt;|gRPC| D[Account Service]\n    C --&gt;|gRPC| E[Risk Engine]\n    C --&gt;|gRPC| F[Ledger]</pre></figure>\n<p>The rate limiter sits between the Parser API and the Distributor API. It must make a decision in under 1ms.</p>\n<blockquote class=\"callout callout-warning\" role=\"note\" aria-label=\"Warning\" data-callout=\"warning\"><p class=\"callout-head\"><span class=\"callout-icon\"><svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg></span><span class=\"callout-label\">Warning</span></p>\n<p>Never rate-limit at the card network ingress point. Network protocols like ISO 8583 have strict timeout windows. A delayed response is worse than a fast decline: it triggers a reversal cascade.</p>\n</blockquote>\n<h2 id=\"choosing-an-algorithm\">Choosing an Algorithm<a class=\"h-anchor\" href=\"#choosing-an-algorithm\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<h3 id=\"token-bucket\">Token Bucket<a class=\"h-anchor\" href=\"#token-bucket\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>The token bucket algorithm maintains a counter that refills at a fixed rate. Each request consumes one token.</p>\n<div class=\"math math-display\"><span class=\"katex-display\"><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><semantics><mrow><mtext>tokens</mtext><mo stretchy=\"false\">(</mo><mi>t</mi><mo stretchy=\"false\">)</mo><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo fence=\"true\">(</mo><mi>C</mi><mo separator=\"true\">,</mo><mtext> tokens</mtext><mo stretchy=\"false\">(</mo><msub><mi>t</mi><mn>0</mn></msub><mo stretchy=\"false\">)</mo><mo>+</mo><mi>r</mi><mo>⋅</mo><mo stretchy=\"false\">(</mo><mi>t</mi><mo>−</mo><msub><mi>t</mi><mn>0</mn></msub><mo stretchy=\"false\">)</mo><mo fence=\"true\">)</mo></mrow></mrow><annotation encoding=\"application/x-tex\">\\text{tokens}(t) = \\min\\left(C,\\ \\text{tokens}(t_0) + r \\cdot (t - t_0)\\right)</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"katex-base\"><span class=\"katex-strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mord text\"><span class=\"mord\">tokens</span></span><span class=\"mopen\">(</span><span class=\"mord mathnormal\">t</span><span class=\"mclose\">)</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"katex-base\"><span class=\"katex-strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mop\">min</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"minner\"><span class=\"mopen delimcenter\" style=\"top:0em;\">(</span><span class=\"mord mathnormal\" style=\"margin-right:0.0715em;\">C</span><span class=\"mpunct\">,</span><span class=\"mspace\"> </span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord text\"><span class=\"mord\">tokens</span></span><span class=\"mopen\">(</span><span class=\"mord\"><span class=\"mord mathnormal\">t</span><span class=\"msupsub\"><span class=\"vlist-t vlist-t2\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.3011em;\"><span style=\"top:-2.55em;margin-left:0em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"katex-sizing reset-size6 size3 mtight\"><span class=\"mord mtight\">0</span></span></span></span><span class=\"vlist-s\">​</span></span><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.15em;\"><span></span></span></span></span></span></span><span class=\"mclose\">)</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mord mathnormal\" style=\"margin-right:0.0278em;\">r</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">⋅</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mopen\">(</span><span class=\"mord mathnormal\">t</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\">t</span><span class=\"msupsub\"><span class=\"vlist-t vlist-t2\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.3011em;\"><span style=\"top:-2.55em;margin-left:0em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"katex-sizing reset-size6 size3 mtight\"><span class=\"mord mtight\">0</span></span></span></span><span class=\"vlist-s\">​</span></span><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.15em;\"><span></span></span></span></span></span></span><span class=\"mclose\">)</span><span class=\"mclose delimcenter\" style=\"top:0em;\">)</span></span></span></span></span></span></div>\n<blockquote class=\"callout callout-tip\" role=\"note\" aria-label=\"Tip\" data-callout=\"tip\"><p class=\"callout-head\"><span class=\"callout-icon\"><svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/><path d=\"M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8a6 6 0 0 0-12 0c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14\"/></svg></span><span class=\"callout-label\">Tip</span></p>\n<p>Set C to handle your peak burst (Black Friday spike) and r to your sustained p99 throughput. For authorization systems, measure both per-issuer and per-BIN to avoid penalizing an entire bank for one merchant’s spike.</p>\n</blockquote>\n<h3 id=\"sliding-window-log\">Sliding Window Log<a class=\"h-anchor\" href=\"#sliding-window-log\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Tracks the exact timestamp of every request in a time window. Precise, but memory-intensive.</p>\n<div class=\"math math-display\"><span class=\"katex-display\"><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><semantics><mrow><mtext>rate</mtext><mo>=</mo><mfrac><mi>n</mi><mi>W</mi></mfrac></mrow><annotation encoding=\"application/x-tex\">\\text{rate} = \\frac{n}{W}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"katex-base\"><span class=\"katex-strut\" style=\"height:0.6151em;\"></span><span class=\"mord text\"><span class=\"mord\">rate</span></span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"katex-base\"><span class=\"katex-strut\" style=\"height:1.7936em;vertical-align:-0.686em;\"></span><span class=\"mord\"><span class=\"mopen nulldelimiter\"></span><span class=\"mfrac\"><span class=\"vlist-t vlist-t2\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:1.1076em;\"><span style=\"top:-2.314em;\"><span class=\"pstrut\" style=\"height:3em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\" style=\"margin-right:0.1389em;\">W</span></span></span><span style=\"top:-3.23em;\"><span class=\"pstrut\" style=\"height:3em;\"></span><span class=\"frac-line\" style=\"border-bottom-width:0.04em;\"></span></span><span style=\"top:-3.677em;\"><span class=\"pstrut\" style=\"height:3em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\">n</span></span></span></span><span class=\"vlist-s\">​</span></span><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.686em;\"><span></span></span></span></span></span><span class=\"mclose nulldelimiter\"></span></span></span></span></span></span></div>\n<h3 id=\"sliding-window-counter\">Sliding Window Counter<a class=\"h-anchor\" href=\"#sliding-window-counter\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<div class=\"math math-display\"><span class=\"katex-display\"><span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\" display=\"block\"><semantics><mrow><mtext>count</mtext><mo>=</mo><mtext>prev</mtext><mo>×</mo><mrow><mo fence=\"true\">(</mo><mn>1</mn><mo>−</mo><mfrac><msub><mi>t</mi><mtext>elapsed</mtext></msub><mi>W</mi></mfrac><mo fence=\"true\">)</mo></mrow><mo>+</mo><mtext>curr</mtext></mrow><annotation encoding=\"application/x-tex\">\\text{count} = \\text{prev} \\times \\left(1 - \\frac{t_{\\text{elapsed}}}{W}\\right) + \\text{curr}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"katex-base\"><span class=\"katex-strut\" style=\"height:0.6151em;\"></span><span class=\"mord text\"><span class=\"mord\">count</span></span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"katex-base\"><span class=\"katex-strut\" style=\"height:0.7778em;vertical-align:-0.1944em;\"></span><span class=\"mord text\"><span class=\"mord\">prev</span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">×</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"katex-base\"><span class=\"katex-strut\" style=\"height:2.4em;vertical-align:-0.95em;\"></span><span class=\"minner\"><span class=\"mopen delimcenter\" style=\"top:0em;\"><span class=\"delimsizing size3\">(</span></span><span class=\"mord\">1</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mord\"><span class=\"mopen nulldelimiter\"></span><span class=\"mfrac\"><span class=\"vlist-t vlist-t2\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:1.2921em;\"><span style=\"top:-2.314em;\"><span class=\"pstrut\" style=\"height:3em;\"></span><span class=\"mord\"><span class=\"mord mathnormal\" style=\"margin-right:0.1389em;\">W</span></span></span><span style=\"top:-3.23em;\"><span class=\"pstrut\" style=\"height:3em;\"></span><span class=\"frac-line\" style=\"border-bottom-width:0.04em;\"></span></span><span style=\"top:-3.677em;\"><span class=\"pstrut\" style=\"height:3em;\"></span><span class=\"mord\"><span class=\"mord\"><span class=\"mord mathnormal\">t</span><span class=\"msupsub\"><span class=\"vlist-t vlist-t2\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.3361em;\"><span style=\"top:-2.55em;margin-left:0em;margin-right:0.05em;\"><span class=\"pstrut\" style=\"height:2.7em;\"></span><span class=\"katex-sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mord text mtight\"><span class=\"mord mtight\">elapsed</span></span></span></span></span></span><span class=\"vlist-s\">​</span></span><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.2861em;\"><span></span></span></span></span></span></span></span></span></span><span class=\"vlist-s\">​</span></span><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.686em;\"><span></span></span></span></span></span><span class=\"mclose nulldelimiter\"></span></span><span class=\"mclose delimcenter\" style=\"top:0em;\"><span class=\"delimsizing size3\">)</span></span></span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">+</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"katex-base\"><span class=\"katex-strut\" style=\"height:0.4306em;\"></span><span class=\"mord text\"><span class=\"mord\">curr</span></span></span></span></span></span></div>\n<p><strong>Token Bucket:</strong> O(1) memory, O(1) time, allows bursts. No per-client fairness without separate buckets. Best for global throughput protection.</p>\n<p><strong>Sliding Window:</strong> Precise rate tracking, smooth distribution. O(n) memory for log variant, interpolation error for counter variant. Best for per-client or per-issuer fairness.</p>\n<h2 id=\"the-distributed-coordination-problem\">The Distributed Coordination Problem<a class=\"h-anchor\" href=\"#the-distributed-coordination-problem\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">sequenceDiagram\n    participant Client\n    participant Region A\n    participant Region B\n    participant Redis\n    Client-&gt;&gt;Region A: Auth Request 1\n    Client-&gt;&gt;Region B: Auth Request 2\n    Region A-&gt;&gt;Redis: INCR counter\n    Region B-&gt;&gt;Redis: INCR counter\n    Redis--&gt;&gt;Region A: count=1 (allow)\n    Redis--&gt;&gt;Region B: count=2 (allow)</pre></figure>\n<blockquote>\n<p>The fundamental tension: strong consistency (single Redis) adds a network hop to every authorization, while eventual consistency allows brief over-admission. For payment systems, brief over-admission is almost always preferable to adding latency.</p>\n</blockquote>\n<h2 id=\"implementation-in-go\">Implementation in Go<a class=\"h-anchor\" href=\"#implementation-in-go\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">type</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\"> TokenBucket</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> struct</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    mu       </span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">sync</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">Mutex</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    tokens   </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">float64</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    capacity </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">float64</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    rate     </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">float64</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    lastTime </span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">time</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">Time</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">tb </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">TokenBucket</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Allow</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">() </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">bool</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    tb.mu.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Lock</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    defer</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> tb.mu.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Unlock</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    now </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> time.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Now</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    elapsed </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> now.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Sub</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(tb.lastTime).</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Seconds</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    tb.tokens </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> math.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Min</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(tb.capacity, tb.tokens</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">tb.rate</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">elapsed)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    tb.lastTime </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> now</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    if</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> tb.tokens </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">&gt;=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        tb.tokens</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">--</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">        return</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> true</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> false</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<blockquote>\n<p>This implementation uses a mutex for simplicity. In production, consider <code>sync/atomic</code> with CAS operations for lock-free performance, or a sharded bucket per goroutine with periodic merging.</p>\n</blockquote>\n<h2 id=\"key-takeaways\">Key Takeaways<a class=\"h-anchor\" href=\"#key-takeaways\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<blockquote>\n<p><strong>Design decisions for payment rate limiters:</strong></p>\n<ul>\n<li>Token bucket for global protection, sliding window for per-client fairness</li>\n<li>Local-first with async sync beats centralized coordination for latency</li>\n<li>Set capacity from measured burst patterns, not theoretical maximums</li>\n<li>Monitor rejection rate as a business metric, not just an infra metric</li>\n</ul>\n</blockquote>\n<p><strong>Web API Rate Limiting:</strong> Return 429 Too Many Requests. Client retries with backoff. False positives are annoying but recoverable.</p>\n<p><strong>Payment Rate Limiting:</strong> Return decline response code in ISO 8583. No retry – transaction is failed. False positives are declined cards at checkout.</p>\n<p>The difference between rate limiting a REST API and rate limiting an authorization pipeline is the cost of a false positive. In payments, you are not protecting a server – you are deciding whether someone’s groceries get paid for.</p>\n"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/understanding-iso8583-bitmaps/",
      "url": "https://ashwingopalsamy.in/library/notes/understanding-iso8583-bitmaps/",
      "title": "Understanding ISO 8583 Bitmap Parsing",
      "content_text": "How ISO 8583 bitmaps declare which data elements are present, and how to parse them at authorization ingress.",
      "date_published": "2026-04-01T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>Every ISO 8583 message begins with a Message Type Indicator (MTI), followed by one or two bitmaps that declare which data elements are present in the message.</p>\n<h2 id=\"what-is-a-bitmap\">What is a Bitmap?<a class=\"h-anchor\" href=\"#what-is-a-bitmap\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>A bitmap is a binary structure where each bit position corresponds to a data element. If bit N is set to 1, data element N is present in the message. If it is 0, the element is absent.</p>\n<p>The primary bitmap is always 64 bits (8 bytes). If bit 1 of the primary bitmap is set, a secondary bitmap follows, extending the field range from 65 to 128.</p>\n<h2 id=\"parsing-in-go\">Parsing in Go<a class=\"h-anchor\" href=\"#parsing-in-go\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>The parsing logic is straightforward once you understand the bit layout:</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> ParseBitmap</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">data</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> []</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">byte</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) (</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">Bitmap</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">error</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    if</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> len</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(data) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">&lt;</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 8</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">        return</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\"> Bitmap</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">{}, fmt.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Errorf</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"bitmap data too short: </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">%d</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> bytes\"</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">len</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(data))</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    primary </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> binary.BigEndian.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Uint64</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(data[:</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">8</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    bm </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\"> Bitmap</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">{Primary: primary}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    if</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> primary</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">&amp;</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">&lt;&lt;</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">63</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">!=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 0</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">        if</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> len</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(data) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">&lt;</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 16</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">            return</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\"> Bitmap</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">{}, fmt.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Errorf</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"secondary bitmap indicated but data too short\"</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        bm.Secondary </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> binary.BigEndian.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Uint64</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(data[</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">8</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">:</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">16</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">])</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        bm.HasSecondary </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> true</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> bm, </span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">nil</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<h2 id=\"why-this-matters\">Why This Matters<a class=\"h-anchor\" href=\"#why-this-matters\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>The elegance of this design is that the message is self-describing. No schema negotiation, no version headers, no content-type declarations. The bitmap IS the schema.</p>\n<p>This means a parser can handle any valid ISO 8583 message without knowing in advance which fields will be present. The bitmap tells it exactly what to expect and where to find it.</p>\n<h2 id=\"key-takeaways\">Key Takeaways<a class=\"h-anchor\" href=\"#key-takeaways\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<ul>\n<li>Primary bitmap: bits 1-64, always present (8 bytes)</li>\n<li>Secondary bitmap: bits 65-128, present only if bit 1 of primary is set</li>\n<li>Each bit maps to one data element by position</li>\n<li>Bit numbering starts at 1, not 0 (bit 1 is the MSB of the first byte)</li>\n</ul>\n"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/go-error-wrapping/",
      "url": "https://ashwingopalsamy.in/library/notes/go-error-wrapping/",
      "title": "Go Error Wrapping Patterns",
      "content_text": "The fmt.Errorf %w pattern and the nuances of wrapping, unwrapping, and adding context to Go errors.",
      "date_published": "2026-03-28T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>The <code>fmt.Errorf(\"context: %w\", err)</code> pattern is the standard way to add context to errors in Go. But there are nuances worth knowing.</p>\n<p>Always wrap with context that answers “what were you trying to do?” not “what went wrong?” The original error already says what went wrong.</p>\n<p>Bad: <code>fmt.Errorf(\"error: %w\", err)</code>\nGood: <code>fmt.Errorf(\"parsing field DE%d: %w\", fieldNum, err)</code></p>\n"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/litellm-supply-chain-attack/",
      "url": "https://ashwingopalsamy.in/library/notes/litellm-supply-chain-attack/",
      "title": "Anatomy of a Supply Chain Attack: LiteLLM on PyPI",
      "content_text": "How a compromised LiteLLM package on PyPI reached a Cursor MCP plugin: anatomy of a real supply-chain hit.",
      "date_published": "2026-03-25T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>On March 24, 2026, Callum McMahon at FutureSearch was testing a Cursor MCP plugin that pulled in litellm as a transitive dependency. Shortly after, his machine became unresponsive. He traced it to a newly installed litellm package.</p>\n<h2 id=\"what-is-litellm\">What Is LiteLLM?<a class=\"h-anchor\" href=\"#what-is-litellm\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>LiteLLM is a Python library that works like a universal remote for AI APIs. 95 million downloads per month on PyPI. ~40,000 GitHub stars.</p>\n<h2 id=\"the-attack-chain\">The Attack Chain<a class=\"h-anchor\" href=\"#the-attack-chain\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">sequenceDiagram\n    participant A as Attacker (TeamPCP)\n    participant T as Trivy GitHub Repo\n    participant CI as LiteLLM CI (GitHub Actions)\n    participant P as PyPI Registry\n<!--diagram-blank-->\n    Note over A,T: March 1 - Initial Compromise\n    A-&gt;&gt;T: Submit malicious PR (Pwn Request)\n    T--&gt;&gt;A: CI leaks personal access token\n    Note over CI,P: March 24 - Package Takeover\n    CI-&gt;&gt;T: Pull trivy-action@latest (not pinned to SHA)\n    T--&gt;&gt;CI: Serve compromised action\n    CI--&gt;&gt;A: Leak PyPI publisher token\n    A-&gt;&gt;P: Publish litellm v1.82.7 (inline payload)\n    A-&gt;&gt;P: Publish litellm v1.82.8 (.pth persistence)</pre></figure>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    A[\"Trivy Repo\\n(compromised)\"] --&gt; B[\"GitHub Bot Army\"]\n    B --&gt; C[\"PyPI Account Takeover\"]\n    C --&gt; D[\"LiteLLM v1.82.7 / v1.82.8\"]\n    D --&gt; E[\".pth Backdoor\"]\n    E --&gt; F[\"Credential Harvest\"]\n    F --&gt; G[\"Exfil Server (ICP Canister)\"]</pre></figure>\n<h2 id=\"what-the-malware-does\">What the Malware Does<a class=\"h-anchor\" href=\"#what-the-malware-does\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>The payload was triple-nested: a base64 blob decodes to an orchestrator script, which decodes a second base64 blob containing the harvester.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph TD\n    ROOT[\".pth Backdoor\"]\n    ROOT --&gt; ENV[\"ENV Variables\"]\n    ROOT --&gt; AWS[\"~/.aws/credentials\"]\n    ROOT --&gt; DOTENV[\".env Files\"]\n    ROOT --&gt; PROC[\"/proc/self/environ\"]\n    ROOT --&gt; EXFIL[\"Outbound HTTP Exfil to ICP canister\"]\n    ENV --&gt; EXFIL\n    AWS --&gt; EXFIL\n    DOTENV --&gt; EXFIL\n    PROC --&gt; EXFIL</pre></figure>\n<p>Two versions were published:</p>\n<ul>\n<li>v1.82.7: injected payload at line 128 of <code>litellm/proxy/proxy_server.py</code></li>\n<li>v1.82.8: added a <code>.pth</code> file that executes every time Python starts</li>\n</ul>\n<blockquote>\n<p><code>.pth</code> files in <code>site-packages</code> execute code before your application begins, on every Python invocation: pytest, IDE language server, even pip install.</p>\n</blockquote>\n<h2 id=\"check-your-environment\">Check Your Environment<a class=\"h-anchor\" href=\"#check-your-environment\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<div class=\"code-figure\" data-language=\"bash\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">ls</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> ~/.config/sysmon/sysmon.py</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> 2&gt;</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">/dev/null</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> &amp;&amp; </span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">echo</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> \"BACKDOOR FOUND\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">find</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> $(</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">python3</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> -c</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> \"import site; print(' '.join(site.getsitepackages()))\"</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">\\</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">  -name</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> \"litellm_init.pth\"</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> 2&gt;</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">/dev/null</span></span></code></pre></div>\n<blockquote>\n<p>If anything shows up, upgrading the package is not enough. Rotate all LLM provider API keys, cloud credentials, GitHub and PyPI tokens, CI/CD secrets, and SSH keys. Rebuild from known-good images. The last known-clean version is 1.82.6.</p>\n</blockquote>\n"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/consistent-hashing-distributed-caches/",
      "url": "https://ashwingopalsamy.in/library/notes/consistent-hashing-distributed-caches/",
      "title": "Consistent Hashing in Distributed Caches",
      "content_text": "How consistent hashing keeps cache keys stable when nodes join or leave, and why modular hashing fails under churn.",
      "date_published": "2026-03-15T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>When you distribute data across multiple cache nodes, the naive approach is modular hashing: <code>node = hash(key) % num_nodes</code>. This works until you add or remove a node.</p>\n<h2 id=\"the-problem-with-modular-hashing\">The Problem with Modular Hashing<a class=\"h-anchor\" href=\"#the-problem-with-modular-hashing\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>If you have 4 nodes and add a 5th, almost every key remaps to a different node. With <code>hash(key) % 4</code> becoming <code>hash(key) % 5</code>, roughly 80% of your cache is invalidated instantly. Under load, this is a cache stampede.</p>\n<h2 id=\"how-consistent-hashing-works\">How Consistent Hashing Works<a class=\"h-anchor\" href=\"#how-consistent-hashing-works\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Consistent hashing arranges the hash space into a ring. Each node is assigned one or more positions on the ring. A key is hashed to a position, and the first node clockwise from that position owns the key.</p>\n<p>When a node joins, it takes responsibility for a portion of its neighbor’s range. When a node leaves, its range is absorbed by the next node clockwise. In both cases, only <code>K/N</code> keys need to move, where K is the total number of keys and N is the number of nodes.</p>\n<h2 id=\"virtual-nodes\">Virtual Nodes<a class=\"h-anchor\" href=\"#virtual-nodes\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Real implementations use virtual nodes – each physical node gets multiple positions on the ring. This smooths out the distribution and prevents hotspots caused by uneven hash space allocation.</p>\n<h2 id=\"key-takeaways\">Key Takeaways<a class=\"h-anchor\" href=\"#key-takeaways\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<ul>\n<li>Modular hashing invalidates ~(N-1)/N keys on node changes</li>\n<li>Consistent hashing invalidates only ~K/N keys</li>\n<li>Virtual nodes solve the distribution imbalance problem</li>\n<li>Used by DynamoDB, Cassandra, Memcached, and most distributed caches</li>\n</ul>\n"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/slog-structured-logging/",
      "url": "https://ashwingopalsamy.in/library/notes/slog-structured-logging/",
      "title": "Why slog Over zerolog",
      "content_text": "Why slog is my default over zerolog for new Go projects after log/slog landed in the standard library.",
      "date_published": "2026-03-10T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>Go 1.21 introduced <code>log/slog</code> in the standard library. For new projects, I now default to slog over zerolog.</p>\n<p>The API is cleaner, it is part of the standard library (no dependency), and the handler interface makes it trivial to swap backends. The performance gap that once justified zerolog has narrowed significantly.</p>\n"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/uuids-for-idempotency/",
      "url": "https://ashwingopalsamy.in/library/notes/uuids-for-idempotency/",
      "title": "Why UUIDs Matter for Idempotency",
      "content_text": "Why idempotency keys matter in payments: processing the same authorization twice is worse than rejecting it.",
      "date_published": "2026-02-20T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>In payment processing, processing the same authorization twice is worse than rejecting it. A customer charged twice loses trust immediately. Idempotency keys prevent this.</p>\n<h2 id=\"the-problem\">The Problem<a class=\"h-anchor\" href=\"#the-problem\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>A client sends an authorization request. The network drops the response. The client retries. Without idempotency, the server processes the authorization again, resulting in a double charge.</p>\n<h2 id=\"uuids-as-idempotency-keys\">UUIDs as Idempotency Keys<a class=\"h-anchor\" href=\"#uuids-as-idempotency-keys\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>The client generates a UUID before the first attempt and includes it in every retry. The server checks: have I seen this UUID before? If yes, return the original response. If no, process the request and store the UUID with its response.</p>\n<h2 id=\"why-uuid-v7\">Why UUID v7<a class=\"h-anchor\" href=\"#why-uuid-v7\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>UUID v7 embeds a Unix timestamp in the first 48 bits, followed by random bits. This gives you:</p>\n<ul>\n<li><strong>Chronological ordering</strong>: UUIDs sort by creation time, which makes database indexes efficient</li>\n<li><strong>Uniqueness</strong>: the random component prevents collisions even at high throughput</li>\n<li><strong>Expiry</strong>: you can garbage-collect old idempotency records by inspecting the embedded timestamp</li>\n</ul>\n<h2 id=\"key-takeaways\">Key Takeaways<a class=\"h-anchor\" href=\"#key-takeaways\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<ul>\n<li>Idempotency keys prevent duplicate processing in distributed systems</li>\n<li>UUID v7 provides time-ordered uniqueness without coordination</li>\n<li>The embedded timestamp enables efficient cleanup of expired records</li>\n<li>Always generate the idempotency key client-side, never server-side</li>\n</ul>\n"
    },
    {
      "id": "tag:ashwingopalsamy.in,2026-01-05:books/cloud-native-go",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Cloud Native Go",
      "content_text": "",
      "date_published": "2026-01-05T00:00:00.000Z",
      "tags": [
        "books"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "https://www.alexedwards.net/blog",
      "url": "https://www.alexedwards.net/blog",
      "title": "Alex Edwards - Let's Go",
      "content_text": "alexedwards.net",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "articles"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "external_url": "https://www.alexedwards.net/blog"
    },
    {
      "id": "https://antonz.org/all/",
      "url": "https://antonz.org/all/",
      "title": "Anton Zaides - All Articles",
      "content_text": "antonz.org",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "articles"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "external_url": "https://antonz.org/all/"
    },
    {
      "id": "https://mitchellh.com/writing/building-large-technical-projects",
      "url": "https://mitchellh.com/writing/building-large-technical-projects",
      "title": "Building Large Technical Projects",
      "content_text": "mitchellh.com",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "articles"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "external_url": "https://mitchellh.com/writing/building-large-technical-projects"
    },
    {
      "id": "https://blog.cloudflare.com/",
      "url": "https://blog.cloudflare.com/",
      "title": "Cloudflare Blog",
      "content_text": "blog.cloudflare.com",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "articles"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "external_url": "https://blog.cloudflare.com/"
    },
    {
      "id": "https://dave.cheney.net/",
      "url": "https://dave.cheney.net/",
      "title": "Dave Cheney's Blog",
      "content_text": "dave.cheney.net",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "articles"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "external_url": "https://dave.cheney.net/"
    },
    {
      "id": "https://go.dev/doc/effective_go",
      "url": "https://go.dev/doc/effective_go",
      "title": "Effective Go",
      "content_text": "go.dev",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "articles"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "external_url": "https://go.dev/doc/effective_go"
    },
    {
      "id": "https://benhoyt.com/writings/go-1brc/",
      "url": "https://benhoyt.com/writings/go-1brc/",
      "title": "How I Solved the 1BRC in Go",
      "content_text": "benhoyt.com",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "articles"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "external_url": "https://benhoyt.com/writings/go-1brc/"
    },
    {
      "id": "https://divan.dev/posts/go_concurrency_visualize/",
      "url": "https://divan.dev/posts/go_concurrency_visualize/",
      "title": "Visualizing Go Concurrency",
      "content_text": "divan.dev",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "articles"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "external_url": "https://divan.dev/posts/go_concurrency_visualize/"
    },
    {
      "id": "https://peter.bourgon.org/go-for-industrial-programming/",
      "url": "https://peter.bourgon.org/go-for-industrial-programming/",
      "title": "Go for Industrial Programming",
      "content_text": "peter.bourgon.org",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "articles"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "external_url": "https://peter.bourgon.org/go-for-industrial-programming/"
    },
    {
      "id": "https://grafana.com/blog/how-i-write-http-services-in-go-after-13-years/",
      "url": "https://grafana.com/blog/how-i-write-http-services-in-go-after-13-years/",
      "title": "How I Write HTTP Services in Go After 13 Years",
      "content_text": "grafana.com",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "articles"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "external_url": "https://grafana.com/blog/how-i-write-http-services-in-go-after-13-years/"
    },
    {
      "id": "https://blog.pragmaticengineer.com/",
      "url": "https://blog.pragmaticengineer.com/",
      "title": "The Pragmatic Engineer",
      "content_text": "blog.pragmaticengineer.com",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "articles"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "external_url": "https://blog.pragmaticengineer.com/"
    },
    {
      "id": "https://www.uber.com/in/en/blog/engineering/",
      "url": "https://www.uber.com/in/en/blog/engineering/",
      "title": "Uber Engineering Blog",
      "content_text": "uber.com",
      "date_published": "2026-01-01T00:00:00.000Z",
      "tags": [
        "articles"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "external_url": "https://www.uber.com/in/en/blog/engineering/"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/go-maps-iteration-order/",
      "url": "https://ashwingopalsamy.in/library/notes/go-maps-iteration-order/",
      "title": "Go Maps Iteration Order",
      "content_text": "Go map iteration is intentionally random; why sorted-looking output is a trap, not a guarantee.",
      "date_published": "2025-12-25T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>I was working on a minimal word counter. Read from stdin, normalize the input, increment the value for the keys in a <code>map[string]int</code>. The output looked sorted. Alphabetically sorted.</p>\n<p>I ran it again. Same order. Added more input. Still sorted.</p>\n<p>At that point, my instincts kicked in. Go maps are unordered. I knew that. So why was the output behaving so politely?</p>\n<h2 id=\"the-non-negotiable-fact\">The non-negotiable fact<a class=\"h-anchor\" href=\"#the-non-negotiable-fact\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Go maps do not guarantee iteration order. Ever. The language spec is explicit.</p>\n<h2 id=\"what-is-actually-happening-inside-a-go-map\">What is actually happening inside a Go map<a class=\"h-anchor\" href=\"#what-is-actually-happening-inside-a-go-map\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>A Go map is a hash table. Iteration walks buckets in a runtime-defined sequence, not key order.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    subgraph \"Hash Function\"\n        H[\"hash(key)\"]\n    end\n    K1[\"a\"] --&gt; H\n    K2[\"b\"] --&gt; H\n    K3[\"m\"] --&gt; H\n    K4[\"1\"] --&gt; H\n    H --&gt; B0[\"Bucket 0\\n1\"]\n    H --&gt; B1[\"Bucket 1\\n2\"]\n    H --&gt; B3[\"Bucket 3\\na\"]\n    H --&gt; B4[\"Bucket 4\\nb\"]\n    H --&gt; B6[\"Bucket 6\\nm\"]</pre></figure>\n<h2 id=\"why-the-output-looked-sorted\">Why the output looked sorted<a class=\"h-anchor\" href=\"#why-the-output-looked-sorted\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Small number of keys, short ASCII strings, no map resizing during insertion. Under these conditions, hash values distribute nicely across buckets, and the bucket layout in memory often correlates with lexical order. By coincidence.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph TD\n    S[\"Small ASCII key set\"]\n    L[\"Large / diverse key set\"]\n    S --&gt; O1[\"Appears sorted (coincidence)\"]\n    L --&gt; O2[\"Visibly unordered (reality)\"]</pre></figure>\n<h2 id=\"a-simplified-mental-model\">A simplified mental model<a class=\"h-anchor\" href=\"#a-simplified-mental-model\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<div class=\"code-figure\" data-language=\"plaintext\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"plaintext\"><code><span class=\"line\"><span>Input keys:  a   b   m   x   y   z   1   2   9</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>Buckets in memory order:</span></span>\n<span class=\"line\"><span>[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]</span></span>\n<span class=\"line\"><span> 1   2   9   a   b       m           x    y    z</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>Iteration walks buckets left to right:</span></span>\n<span class=\"line\"><span>1 2 9 a b m x y z</span></span></code></pre></div>\n<p>Digits first. Then letters. Within each group, alphabetical-looking. No sorting happened. Change the input distribution, force collisions, trigger a map resize, and this illusion breaks instantly.</p>\n<blockquote>\n<p>Deterministic-looking behavior does not imply guarantees.</p>\n</blockquote>\n"
    },
    {
      "id": "tag:ashwingopalsamy.in,2025-11-20:books/database-internals",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Database Internals",
      "content_text": "",
      "date_published": "2025-11-20T00:00:00.000Z",
      "tags": [
        "books"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/go-floating-point-tolerance-testing/",
      "url": "https://ashwingopalsamy.in/library/notes/go-floating-point-tolerance-testing/",
      "title": "Floating-Point Tolerance Testing in Go",
      "content_text": "Why 0.1 + 0.2 isn't 0.3 in financial Go tests, and how to assert floating-point results with tolerance.",
      "date_published": "2025-08-17T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>Last Tuesday, I was knee-deep in a financial calculation service when my tests started failing. You know that sinking feeling when <code>0.1 + 0.2</code> doesn’t equal <code>0.3</code>? That was my afternoon.</p>\n<p>This got me thinking about when we actually need tolerance-based comparisons versus when we’re just cargo-culting best practices.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    A[\"0.1 + 0.2\"] --&gt; B[\"IEEE 754\"]\n    B --&gt; C[\"0.30000...04\"]\n    D[\"0.3\"] --&gt; E[\"IEEE 754\"]\n    E --&gt; F[\"0.29999...99\"]\n    C --&gt; G[\"Gap = epsilon\"]\n    F --&gt; G</pre></figure>\n<h2 id=\"when-tolerance-matters\">When tolerance matters<a class=\"h-anchor\" href=\"#when-tolerance-matters\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>I was working on a discount calculation system:</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> calculateDiscount</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">price</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">rate</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> float64</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">float64</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> price </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> rate</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> TestDiscountCalculation</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">t</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> *</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">testing</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">T</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    price </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 29.99</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    rate </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 0.15</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    expected </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 4.4985</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    result </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> calculateDiscount</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(price, rate)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    if</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> result </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">!=</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> expected {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        t.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Errorf</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"got </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">%v</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">, want </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">%v</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, result, expected)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>With tolerance, you’d handle it like this:</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> almostEqual</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">a</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">b</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">tolerance</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> float64</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">bool</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> math.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Abs</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(a</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">-</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">b) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">&lt;=</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> tolerance</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> TestDiscountCalculationWithTolerance</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">t</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> *</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">testing</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">T</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    price </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 29.99</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    rate </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 0.15</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    expected </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 4.4985</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    result </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> calculateDiscount</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(price, rate)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    if</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> !</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">almostEqual</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(result, expected, </span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">e-</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">9</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        t.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Errorf</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"got </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">%v</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">, want </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">%v</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, result, expected)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<h2 id=\"when-you-dont-need-tolerance\">When you DON’T need tolerance<a class=\"h-anchor\" href=\"#when-you-dont-need-tolerance\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Some comparisons are fine without tolerance:</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">if</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> value </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">==</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 0.0</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> { } </span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">// Zero has exact representation</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">if</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> math.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">IsNaN</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(result) { } </span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">// Special values</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">if</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> result </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">==</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> math.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Inf</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) { } </span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">// Infinity comparisons</span></span></code></pre></div>\n<p>If your floats haven’t been through different computational paths, exact comparison often works fine.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">flowchart TD\n    A[\"Comparing floats?\"] --&gt; B{\"Result of\\narithmetic?\"}\n    B -- Yes --&gt; C[\"Use epsilon\"]\n    B -- No --&gt; D{\"Integer-\\nconvertible?\"}\n    D -- Yes --&gt; E[\"No epsilon needed\"]\n    D -- No --&gt; F{\"Currency?\"}\n    F -- Yes --&gt; G[\"Use integer cents\"]\n    F -- No --&gt; C</pre></figure>\n<h2 id=\"a-practical-helper\">A practical helper<a class=\"h-anchor\" href=\"#a-practical-helper\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Here’s a utility I use across projects:</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> DefaultFloatTolerance</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">e-</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">9</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> FloatEquals</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">a</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">b</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> float64</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">bool</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> FloatEqualsWithTolerance</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(a, b, DefaultFloatTolerance)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> FloatEqualsWithTolerance</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">a</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">b</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">tolerance</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> float64</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">bool</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    if</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> math.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">IsNaN</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(a) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">&amp;&amp;</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> math.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">IsNaN</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(b) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">        return</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> true</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    if</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> math.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">IsInf</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(a, </span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">&amp;&amp;</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> math.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">IsInf</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(b, </span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">        return</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> math.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Signbit</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(a) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">==</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> math.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Signbit</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(b)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> math.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Abs</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(a</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">-</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">b) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">&lt;=</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> tolerance</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>Use tolerance when your floats have been through computational journeys. Skip it for direct assignments, constants, and same-path calculations.</p>\n"
    },
    {
      "id": "tag:ashwingopalsamy.in,2025-08-15:books/designing-data-intensive-applications",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Designing Data-Intensive Applications",
      "content_text": "",
      "date_published": "2025-08-15T00:00:00.000Z",
      "tags": [
        "books"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/runes-bytes-and-graphemes-in-go/",
      "url": "https://ashwingopalsamy.in/library/notes/runes-bytes-and-graphemes-in-go/",
      "title": "Runes, Bytes, and Graphemes in Go",
      "content_text": "Bytes, runes, and graphemes are not the same length: handling Tamil and emoji correctly in Go strings.",
      "date_published": "2025-08-09T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>I once ran into this problem while handling names in Tamil and emoji in a Go web app: a string that looked short wasn’t, and reversing it produced gibberish.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph TD\n    G[\"Grapheme Cluster\\n(what users see)\"]\n    R1[\"Rune 1\\n(code point)\"]\n    R2[\"Rune 2\\n(combining mark)\"]\n    B1[\"Bytes\\n(1-4 per rune)\"]\n    B2[\"Bytes\\n(1-4 per rune)\"]\n    G --&gt; R1\n    G --&gt; R2\n    R1 --&gt; B1\n    R2 --&gt; B2</pre></figure>\n<h2 id=\"1-bytes\">1. Bytes<a class=\"h-anchor\" href=\"#1-bytes\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Go represents strings as immutable UTF-8 byte sequences.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">s </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> \"வணக்கம்\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">fmt.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Println</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">len</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(s)) </span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">// 21</span></span></code></pre></div>\n<p>21 bytes, not visible symbols. Every Tamil character can span 3 bytes.</p>\n<h2 id=\"2-runes\">2. Runes<a class=\"h-anchor\" href=\"#2-runes\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p><code>string</code> to <code>[]rune</code> gives you code points.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">rs </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> []</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">rune</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(s)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">fmt.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Println</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">len</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(rs)) </span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">// 7</span></span></code></pre></div>\n<p>7 runes, but some Tamil graphemes (like “க்”) combine two runes: க + ்.</p>\n<h2 id=\"3-grapheme-clusters\">3. Grapheme clusters<a class=\"h-anchor\" href=\"#3-grapheme-clusters\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Go’s standard library stops at runes. For visible character units, use <code>github.com/rivo/uniseg</code>.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">for</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> gr </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> uniseg.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">NewGraphemes</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(s); gr.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Next</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(); {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    fmt.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Printf</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">%q\\n</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, gr.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Str</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">())</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>That outputs what a human reads: வ, ண, க், க, ம்.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    T[\"வணக்கம் (Tamil)\"]\n    T --&gt; |\"len()\"| BY[\"21 bytes\"]\n    T --&gt; |\"[]rune\"| RU[\"7 runes\"]\n    T --&gt; |\"uniseg\"| GR[\"5 graphemes\"]</pre></figure>\n<h2 id=\"quick-reference\">Quick reference<a class=\"h-anchor\" href=\"#quick-reference\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<div class=\"table-scroll\"><table>\n<thead>\n<tr>\n<th>Task</th>\n<th>Approach</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Count code points</td>\n<td><code>utf8.RuneCountInString(s)</code></td>\n</tr>\n<tr>\n<td>Count visible units</td>\n<td>Grapheme iteration (uniseg)</td>\n</tr>\n<tr>\n<td>Reverse text</td>\n<td>Parse into graphemes, reverse slice, join</td>\n</tr>\n<tr>\n<td>Slice safely</td>\n<td>Only use <code>s[i:j]</code> on grapheme boundaries</td>\n</tr>\n</tbody>\n</table></div>\n"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/go-was-never-bad/",
      "url": "https://ashwingopalsamy.in/library/notes/go-was-never-bad/",
      "title": "Go Was Never Bad",
      "content_text": "Go is not for everything, and that was never the point. A defense of brutal minimalism for cloud systems.",
      "date_published": "2025-06-21T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>Every time I see that GitHub repo go-is-not-good making the rounds again, I laugh. It’s always shared by people who mistake language cleverness for engineering.</p>\n<p>Let me say this clearly: Go is not for everything. It was never meant to be. But if you’re building cloud-native systems, if you’re working with distributed architecture, if you’re running services in production that actually matter – Go is brutal, minimal, and effective.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    A[\"No generics\"] -- \"Go 1.18\" --&gt; B[\"Resolved\"]\n    C[\"No modules\"] -- \"Go 1.11\" --&gt; D[\"Resolved\"]\n    E[\"Error handling\"] --&gt; F[\"By Design\"]\n    G[\"Concurrency\"] --&gt; H[\"Same as every lang\"]</pre></figure>\n<h2 id=\"generics\">Generics<a class=\"h-anchor\" href=\"#generics\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Go 1.18 brought generics. Not the “look how clever I am” kind. The real-world, clean, no-BS kind. I’ve built production-grade code with generics in Go, and it’s boring in the best possible way.</p>\n<h2 id=\"error-handling\">Error Handling<a class=\"h-anchor\" href=\"#error-handling\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>I respect Go’s approach to error handling. It makes you look at your failure paths. There’s no magic trapdoor. You deal with errors explicitly, predictably, and with clarity. Now with <code>errors.Is</code>, <code>errors.As</code>, and <code>errors.Join</code>, it’s not even painful.</p>\n<h2 id=\"go-does-not-babysit-your-concurrency\">Go does not babysit your Concurrency<a class=\"h-anchor\" href=\"#go-does-not-babysit-your-concurrency\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Go gives you the raw tools to build concurrent systems. It assumes the person writing the code knows what they’re doing. I respect that.</p>\n<h2 id=\"performance\">Performance<a class=\"h-anchor\" href=\"#performance\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>I’ve deployed Go services that boot in under 50ms and run for months without a hiccup. It doesn’t brag. It just works.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    A[\"Go\"] --&gt; B[\"Ship fast, simple\"]\n    C[\"Java / Spring\"] --&gt; D[\"Medium complexity\"]\n    E[\"Rust / Haskell\"] --&gt; F[\"Complex, more guarantees\"]</pre></figure>\n<h2 id=\"personal-take\">Personal take<a class=\"h-anchor\" href=\"#personal-take\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>I’ve built banking systems, transaction processors, and cloud-native APIs with Go. It’s not flashy. But Go keeps things simple and predictable, and that’s exactly what backend engineering demands.</p>\n"
    },
    {
      "id": "tag:ashwingopalsamy.in,2025-06-15:watch/rookie",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Rookie",
      "content_text": "",
      "date_published": "2025-06-15T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/go-goroutine-stack-growth/",
      "url": "https://ashwingopalsamy.in/library/notes/go-goroutine-stack-growth/",
      "title": "How Goroutine Stacks Grow and Shrink",
      "content_text": "How goroutine stacks grow and shrink in Go, and why that is what makes them feel lightweight.",
      "date_published": "2025-06-08T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>If you ask any mid-senior Go dev what makes goroutines lightweight you’ll get the reply:</p>\n<blockquote>\n<p>They start with 2 KB of stack instead of 1 MB like OS threads.</p>\n</blockquote>\n<p>They’re not wrong. But they’re not thinking deep enough.</p>\n<p>Go’s stack model isn’t just a small preallocated buffer; it’s a live, evolving region of memory that resizes in realtime, grows when needed, and shrinks.</p>\n<p>Go’s runtime gives each new goroutine 2 KB of stack. But Go doesn’t panic when you blow past it – it grows the stack dynamically by allocating a new region (typically doubling the size) and copying the old stack frames over.</p>\n<h3 id=\"stack-growth-lifecycle\">Stack Growth Lifecycle<a class=\"h-anchor\" href=\"#stack-growth-lifecycle\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    A[\"New Goroutine\\n2 KB stack\"] --&gt; B[\"Function\\nCall\"]\n    B --&gt; C{\"Stack\\nCheck\"}\n    C --&gt;|Enough| D[\"Continue\\nExecution\"]\n    C --&gt;|Overflow| E[\"Allocate\\n2x Stack\"]\n    E --&gt; F[\"Copy Old\\nFrames\"]\n    F --&gt; G[\"Update\\nPointers\"]\n    G --&gt; D</pre></figure>\n<p>Each goroutine has an upper stack limit of about 1 GB. Hit it and the program panics:</p>\n<div class=\"code-figure\" data-language=\"plaintext\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"plaintext\"><code><span class=\"line\"><span>runtime: goroutine stack exceeds 1000000000-byte limit</span></span>\n<span class=\"line\"><span>fatal error: stack overflow</span></span></code></pre></div>\n<h3 id=\"stack-size-progression\">Stack Size Progression<a class=\"h-anchor\" href=\"#stack-size-progression\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph TD\n    S1[\"2 KB\\n(initial)\"] --&gt; S2[\"4 KB\"]\n    S2 --&gt; S3[\"8 KB\"]\n    S3 --&gt; S4[\"16 KB\"]\n    S4 --&gt; S5[\"32 KB\"]\n    S5 --&gt; S6[\"...\"]\n    S6 --&gt; S7[\"~1 GB\\n(ceiling)\"]\n    S7 -.-&gt;|\"GC shrink\\n(if idle + mostly unused)\"| S5\n    S5 -.-&gt;|\"GC shrink\"| S3</pre></figure>\n<h3 id=\"contiguous-stack-copy\">Contiguous Stack Copy<a class=\"h-anchor\" href=\"#contiguous-stack-copy\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>When a stack outgrows its current allocation, Go allocates a new contiguous block at double the size, copies all frames, and adjusts every internal pointer.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    subgraph \"Old Stack (4 KB)\"\n        O1[\"Frame 1\"]\n        O2[\"Frame 2\"]\n        O3[\"Pointer A\\n→ Frame 1\"]\n    end\n    subgraph \"New Stack (8 KB)\"\n        N1[\"Frame 1\\n(copied)\"]\n        N2[\"Frame 2\\n(copied)\"]\n        N3[\"Pointer A'\\n→ Frame 1\\n(adjusted)\"]\n        N4[\"Free Space\"]\n    end\n    O1 --&gt;|copy| N1\n    O2 --&gt;|copy| N2\n    O3 --&gt;|\"adjust + copy\"| N3</pre></figure>\n<blockquote>\n<p>Stack growth can trigger GC pressure even without heap allocations. A runaway stack holds pointers that get scanned by GC. This is how your 5ms p99 turns into 100ms.</p>\n</blockquote>\n<p>There’s no tuning knob for stack size. No config. No CLI flag. The only way to manage it is through code discipline: avoid recursive goroutines unless they terminate quickly, don’t hold large structs deep in call graphs, and never assume goroutines are free.</p>\n"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/go-scheduler-infinite-loops/",
      "url": "https://ashwingopalsamy.in/library/notes/go-scheduler-infinite-loops/",
      "title": "Go Scheduler, Yield Points, and Infinite Loops",
      "content_text": "How Go's scheduler, yield points, and tight loops interact, and when a busy loop starves other goroutines.",
      "date_published": "2025-05-24T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>I’ve been reviewing performance-critical code, and I keep coming back to this pattern:</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">for</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    if</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> condition {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">        break</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>versus</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">go</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> func</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    for</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">        // background processing</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}()</span></span></code></pre></div>\n<p>The runtime implications are fascinating.</p>\n<h2 id=\"the-gmp-model\">The GMP Model<a class=\"h-anchor\" href=\"#the-gmp-model\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Go’s scheduler uses a GMP model: Goroutines (G) run on Machine threads (M) via Processors (P). Each P has a local run queue plus a global run queue.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph TD\n    subgraph \"Global Run Queue\"\n        GQ[\"G5, G6, G7 ...\"]\n    end\n    subgraph \"P0 (Processor)\"\n        LQ0[\"Local Queue:\\nG1, G2\"]\n        LQ0 --&gt; M0[\"M0\\n(OS Thread)\"]\n        M0 --&gt; CPU0[\"CPU Core 0\"]\n    end\n    subgraph \"P1 (Processor)\"\n        LQ1[\"Local Queue:\\nG3, G4\"]\n        LQ1 --&gt; M1[\"M1\\n(OS Thread)\"]\n        M1 --&gt; CPU1[\"CPU Core 1\"]\n    end\n    GQ -.-&gt;|\"schedule\"| LQ0\n    GQ -.-&gt;|\"schedule\"| LQ1\n    LQ0 -.-&gt;|\"work steal\"| LQ1</pre></figure>\n<h3 id=\"cooperative-scheduling\">Cooperative Scheduling<a class=\"h-anchor\" href=\"#cooperative-scheduling\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">sequenceDiagram\n    participant S as Scheduler\n    participant A as Goroutine A\n    participant B as Goroutine B\n<!--diagram-blank-->\n    S-&gt;&gt;A: Schedule on P0\n    Note over A: Executing...\n    A-&gt;&gt;A: Channel send (yield point)\n    A-&gt;&gt;S: Yield control\n    S-&gt;&gt;B: Schedule on P0\n    Note over B: Executing...\n    B-&gt;&gt;B: runtime.Gosched()\n    B-&gt;&gt;S: Yield control\n    S-&gt;&gt;A: Re-schedule on P0\n    Note over A: Resumes execution</pre></figure>\n<h3 id=\"async-preemption\">Async Preemption<a class=\"h-anchor\" href=\"#async-preemption\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Since Go 1.14, <code>sysmon</code> detects goroutines running longer than ~10ms and forces preemption via SIGURG.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">sequenceDiagram\n    participant SM as sysmon\n    participant G as Goroutine (tight loop)\n    participant S as Scheduler\n<!--diagram-blank-->\n    G-&gt;&gt;G: Running for &gt;10ms (no yield points)\n    SM-&gt;&gt;SM: Detects long-running G\n    SM-&gt;&gt;G: Send SIGURG\n    Note over G: Signal handler fires, saves state\n    G-&gt;&gt;S: Suspended\n    S-&gt;&gt;S: Schedule next goroutine\n    S-&gt;&gt;G: Re-schedule eventually</pre></figure>\n<h2 id=\"choosing-the-right-pattern\">Choosing the Right Pattern<a class=\"h-anchor\" href=\"#choosing-the-right-pattern\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    A{\"Need\\nconcurrency?\"}\n    A --&gt;|No| B[\"for {} loop\\n(direct execution)\"]\n    A --&gt;|Yes| C{\"Bounded\\nwork?\"}\n    C --&gt;|Yes| D[\"Worker Pool\\n(fixed goroutines)\"]\n    C --&gt;|No| E{\"I/O or\\nCPU bound?\"}\n    E --&gt;|I/O| F[\"Goroutine per task\\n(scheduler handles blocking)\"]\n    E --&gt;|CPU| G[\"select {} loop\\n(controlled yielding)\"]</pre></figure>\n<p>For most apps, use goroutines everywhere. For the 5% of performance-critical code, choose based on your scheduler profile.</p>\n"
    },
    {
      "id": "tag:ashwingopalsamy.in,2025-05-18:books/software-architecture-hard-parts",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Software Architecture: The Hard Parts",
      "content_text": "",
      "date_published": "2025-05-18T00:00:00.000Z",
      "tags": [
        "books"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/review-your-own-pr-first/",
      "url": "https://ashwingopalsamy.in/library/notes/review-your-own-pr-first/",
      "title": "Review Your Own PR First",
      "content_text": "The PRs that merge cleanly are the ones authors already self-reviewed. A practical checklist before you ask.",
      "date_published": "2025-01-15T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>Like most developers, I’ve been on both sides of Pull Requests. What I’ve noticed is that the best PRs, the ones that get merged smoothly with minimal back-and-forth, are where the author has already done a thorough self-review.</p>\n<h2 id=\"what-happens-without-self-review\">What Happens Without Self-Review<a class=\"h-anchor\" href=\"#what-happens-without-self-review\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">sequenceDiagram\n    participant Author\n    participant Reviewer\n    Author-&gt;&gt;Reviewer: Push PR for review\n    Reviewer-&gt;&gt;Author: 12 comments (typos, dead code, naming)\n    Author-&gt;&gt;Author: Fix trivial issues\n    Author-&gt;&gt;Reviewer: Push fixes\n    Reviewer-&gt;&gt;Author: 5 more comments\n    Author-&gt;&gt;Author: Fix again\n    Author-&gt;&gt;Reviewer: Push fixes\n    Note over Author,Reviewer: 3+ round trips before merge</pre></figure>\n<h2 id=\"why-self-review-matters\">Why Self-Review Matters<a class=\"h-anchor\" href=\"#why-self-review-matters\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<h3 id=\"a-saves-time\">a. Saves Time<a class=\"h-anchor\" href=\"#a-saves-time\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Clean up obvious issues yourself so reviewers focus on architecture and design.</p>\n<h3 id=\"b-improves-code-quality\">b. Improves Code Quality<a class=\"h-anchor\" href=\"#b-improves-code-quality\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Reading your own diff catches patterns that don’t jump out when you’re writing.</p>\n<h3 id=\"c-builds-trust\">c. Builds Trust<a class=\"h-anchor\" href=\"#c-builds-trust\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>A polished PR signals respect for your colleagues’ time.</p>\n<h2 id=\"how-to-self-review\">How to Self-Review<a class=\"h-anchor\" href=\"#how-to-self-review\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<h3 id=\"a-view-the-diff-as-someone-else\">a. View the Diff As Someone Else<a class=\"h-anchor\" href=\"#a-view-the-diff-as-someone-else\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Open the Files changed tab and read every line as the reviewer.</p>\n<h3 id=\"b-check-commit-messages\">b. Check Commit Messages<a class=\"h-anchor\" href=\"#b-check-commit-messages\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Follow conventional-commit style. Each commit should represent one logical change.</p>\n<h3 id=\"c-document-special-considerations\">c. Document Special Considerations<a class=\"h-anchor\" href=\"#c-document-special-considerations\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Add inline comments for non-obvious decisions.</p>\n<h3 id=\"d-validate-tests\">d. Validate Tests<a class=\"h-anchor\" href=\"#d-validate-tests\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Run them locally. Check coverage for success and failure paths.</p>\n<h3 id=\"e-check-style-and-linting\">e. Check Style and Linting<a class=\"h-anchor\" href=\"#e-check-style-and-linting\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Run your formatter and linter before requesting review.</p>\n<h2 id=\"what-happens-with-self-review\">What Happens With Self-Review<a class=\"h-anchor\" href=\"#what-happens-with-self-review\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">sequenceDiagram\n    participant Author\n    participant Self as Self-Review\n    participant Reviewer\n    Author-&gt;&gt;Self: Read own diff as reviewer\n    Self-&gt;&gt;Author: Catch 12 trivial issues locally\n    Author-&gt;&gt;Author: Fix naming, dead code, edge cases\n    Author-&gt;&gt;Reviewer: Push clean PR\n    Reviewer-&gt;&gt;Author: 1 substantive design comment\n    Author-&gt;&gt;Author: Address feedback\n    Author-&gt;&gt;Reviewer: Push final fix\n    Note over Author,Reviewer: 1 round trip to merge</pre></figure>\n<h2 id=\"self-review-checklist\">Self-Review Checklist<a class=\"h-anchor\" href=\"#self-review-checklist\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph TD\n    Start[\"Open your PR diff\"] --&gt; A{\"Diff readable?\\nClear naming?\"}\n    A --&gt;|Yes| B{\"Commit messages\\nclear and atomic?\"}\n    A --&gt;|No| A1[\"Fix naming\"] --&gt; A\n    B --&gt;|Yes| C{\"Tests pass?\\nCoverage adequate?\"}\n    B --&gt;|No| B1[\"Rewrite commits\"] --&gt; B\n    C --&gt;|Yes| D{\"Docs updated?\\nPR description clear?\"}\n    C --&gt;|No| C1[\"Add tests\"] --&gt; C\n    D --&gt;|Yes| E{\"Linting clean?\"}\n    D --&gt;|No| D1[\"Update docs\"] --&gt; D\n    E --&gt;|Yes| F[\"Ready for review\"]\n    E --&gt;|No| E1[\"Run linter\"] --&gt; E</pre></figure>\n<p>The simplest habit that pays the biggest dividends: before clicking “Request Review”, open the Files changed tab and read every line as if a colleague wrote it.</p>\n"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/go-generics-comparable-constraint/",
      "url": "https://ashwingopalsamy.in/library/notes/go-generics-comparable-constraint/",
      "title": "The comparable Constraint in Go Generics",
      "content_text": "What 'incomparable types in type set' means and how Go's comparable constraint shapes generic maps and sets.",
      "date_published": "2024-12-25T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>While working on a generic function in Go, I once encountered this error: <em>‘incomparable types in type set’</em>.</p>\n<p>It led me to dig deeper into the <code>comparable</code> constraint. With this post, I’ll walk you through what <code>comparable</code> is, why it’s useful, and how you can leverage it for cleaner, type-safe Go code.</p>\n<h2 id=\"why-comparable\">Why comparable?<a class=\"h-anchor\" href=\"#why-comparable\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p><code>comparable</code> is a constraint that ensures a type supports equality comparisons (<code>==</code> and <code>!=</code>).</p>\n<p>In Go, only certain types are inherently comparable: primitive types like <code>int</code>, <code>float64</code> and <code>string</code>, but exclude types like slices, maps, and functions.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">type</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\"> Array</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">T</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\"> comparable</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">struct</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    data []</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">T</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">a </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">Array</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">T</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">]) </span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Contains</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">value</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\"> T</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">bool</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    for</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> _, v </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> range</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> a.data {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">        if</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> v </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">==</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> value {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">            return</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> true</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> false</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<h3 id=\"constraint-hierarchy\">Constraint Hierarchy<a class=\"h-anchor\" href=\"#constraint-hierarchy\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph TD\n    A[\"any\\n(all types)\"]\n    B[\"comparable\\n(supports == and !=)\"]\n    C[\"constraints.Ordered\\n(supports &lt; &gt; &lt;= &gt;=)\"]\n    A --&gt; B\n    B --&gt; C</pre></figure>\n<h3 id=\"any-vs-interface\">any vs. interface{}<a class=\"h-anchor\" href=\"#any-vs-interface\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p><code>any</code> is simply an alias for <code>interface{}</code>. While functionally identical, <code>any</code> better aligns with the intentions of generics and improves readability.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> PrintValues</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">T</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\"> any</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">](</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">values</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> []</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">T</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    for</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> _, v </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> range</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> values {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        fmt.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Println</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(v)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<h3 id=\"real-world-scenarios\">Real-World Scenarios<a class=\"h-anchor\" href=\"#real-world-scenarios\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<h4 id=\"1-deduplication-with-comparable\">1. Deduplication with comparable<a class=\"h-anchor\" href=\"#1-deduplication-with-comparable\" aria-hidden=\"true\" tabindex=\"-1\"></a></h4>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> RemoveDuplicates</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">T</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\"> comparable</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">](</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">input</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> []</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">T</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) []</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">T</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    seen </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> make</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">map</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">[</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">T</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">]</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">bool</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    result </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> []</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">T</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">{}</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    for</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> _, v </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> range</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> input {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">        if</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> !</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">seen[v] {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">            seen[v] </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> true</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">            result </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> append</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(result, v)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> result</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<h3 id=\"compile-time-vs-runtime\">Compile-Time vs Runtime<a class=\"h-anchor\" href=\"#compile-time-vs-runtime\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    subgraph \"Using any (unsafe)\"\n        A1[\"func Contains\\n[T any]\"] --&gt; B1[\"Pass []int\\nas T\"]\n        B1 --&gt; C1[\"Runtime Panic\\n== on slice\"]\n    end\n    subgraph \"Using comparable (safe)\"\n        A2[\"func Contains\\n[T comparable]\"] --&gt; B2[\"Pass []int\\nas T\"]\n        B2 --&gt; C2[\"Compile Error\\n[]int not comparable\"]\n    end</pre></figure>\n<p>The real value of <code>comparable</code> is catching errors at compile time instead of letting them slip into production.</p>\n"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/go-program-entry-point/",
      "url": "https://ashwingopalsamy.in/library/notes/go-program-entry-point/",
      "title": "What Happens Before main() in Go",
      "content_text": "What the Go runtime does before main(): package init order, init functions, and startup orchestration.",
      "date_published": "2024-12-13T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>When we first start with Go, the <code>main</code> function seems almost too simple. But before <code>main</code> even begins, the Go runtime orchestrates careful initialization of all imported packages, runs their <code>init</code> functions and ensures everything is in the right order.</p>\n<h3 id=\"program-startup-sequence\">Program Startup Sequence<a class=\"h-anchor\" href=\"#program-startup-sequence\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">sequenceDiagram\n    participant OS\n    participant Runtime\n    participant Packages\n    participant main\n<!--diagram-blank-->\n    OS-&gt;&gt;Runtime: Execute binary\n    Runtime-&gt;&gt;Runtime: Bootstrap (scheduler, GC, memory)\n    Runtime-&gt;&gt;Packages: Init packages in dependency order\n    Packages-&gt;&gt;Packages: Run init() functions\n    Packages--&gt;&gt;Runtime: All packages initialized\n    Runtime-&gt;&gt;main: Call main()\n    main-&gt;&gt;main: Main goroutine runs\n    main--&gt;&gt;OS: os.Exit or return</pre></figure>\n<h3 id=\"package-dependency-ordering\">Package Dependency Ordering<a class=\"h-anchor\" href=\"#package-dependency-ordering\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Go initializes packages in dependency order, not import order. Leaf packages initialize first.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph TD\n    main[\"main\\n(init last)\"]\n    A[\"Package A\"]\n    B[\"Package B\"]\n    C[\"Package C\"]\n    main --&gt; A\n    main --&gt; B\n    A --&gt; C</pre></figure>\n<p>Init order: C -&gt; A -&gt; B -&gt; main.</p>\n<h3 id=\"multiple-init-functions\">Multiple init Functions<a class=\"h-anchor\" href=\"#multiple-init-functions\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>You can have multiple <code>init</code> functions. They run in order they appear. Go initializes packages in the order of their dependencies.</p>\n<blockquote>\n<p>Use <code>sync.Once</code> when you need lazy or guarded initialization of shared resources. It’s safer than relying on <code>init()</code> ordering.</p>\n</blockquote>\n<h3 id=\"exiting-with-osexit\">Exiting with os.Exit()<a class=\"h-anchor\" href=\"#exiting-with-osexit\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p><code>os.Exit()</code> terminates the program immediately. No deferred functions run.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> main</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    if</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> err </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> doSomethingRisky</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(); err </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">!=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> nil</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">        cleanup</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        os.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Exit</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<h3 id=\"the-main-goroutine\">The main Goroutine<a class=\"h-anchor\" href=\"#the-main-goroutine\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>The <code>main</code> function runs in a special goroutine. If <code>main</code> returns, the entire program exits – even if other goroutines are still working. Use <code>sync.WaitGroup</code> to wait for background goroutines.</p>\n"
    },
    {
      "id": "tag:ashwingopalsamy.in,2024-12-01:books/art-of-doing-science-engineering",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "The Art of Doing Science and Engineering",
      "content_text": "",
      "date_published": "2024-12-01T00:00:00.000Z",
      "tags": [
        "books"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/go-pointers-and-memory-management/",
      "url": "https://ashwingopalsamy.in/library/notes/go-pointers-and-memory-management/",
      "title": "Go Pointers and Memory Management",
      "content_text": "A practical look at Go pointers, escape analysis, and how the runtime keeps memory both efficient and safe.",
      "date_published": "2024-11-17T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>When I first started learning Go, I was intrigued by its approach to memory management. Go handles memory in a way that’s both efficient and safe, but it can be a bit of a black box if you don’t peek under the hood.</p>\n<h2 id=\"understanding-stack-and-heap-memory\">Understanding Stack and Heap Memory<a class=\"h-anchor\" href=\"#understanding-stack-and-heap-memory\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph TD\n    subgraph STACK[\"Goroutine Stack\"]\n        S1[\"Local variables\"]\n        S2[\"Function parameters\"]\n        S3[\"Return addresses\"]\n    end\n    subgraph HEAP[\"Shared Heap\"]\n        H1[\"Escaped variables\"]\n        H2[\"Pointers returned\\nfrom functions\"]\n        H3[\"Closure captures\"]\n    end\n    EA{{\"Escape Analysis\\n(compile time)\"}}\n    EA -- \"does not escape\" --&gt; STACK\n    EA -- \"escapes to heap\" --&gt; HEAP</pre></figure>\n<h2 id=\"passing-by-value-the-default-behavior\">Passing by Value: The Default Behavior<a class=\"h-anchor\" href=\"#passing-by-value-the-default-behavior\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>In Go, when you pass variables like integer, string, or boolean to a function, they are passed by value. A copy is made.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> increment</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">num</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> int</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    num</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">++</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> main</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    n </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 21</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">    increment</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(n)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    fmt.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Println</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(n) </span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">// 21</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<h2 id=\"introducing-pointers\">Introducing Pointers<a class=\"h-anchor\" href=\"#introducing-pointers\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>To modify the original variable inside a function, pass a pointer.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> incrementPointer</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">num</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> *int</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    (</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">num)</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">++</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> main</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    n </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 42</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">    incrementPointer</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">&amp;</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">n)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    fmt.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Println</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(n) </span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">// 43</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<h2 id=\"escape-analysis\">Escape Analysis<a class=\"h-anchor\" href=\"#escape-analysis\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Escape analysis determines whether variables need to live beyond their function scope.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">flowchart TD\n    A[\"Variable declared\"] --&gt; B{\"Returned as\\npointer?\"}\n    B -- Yes --&gt; HEAP[\"Allocate on Heap\"]\n    B -- No --&gt; C{\"Captured by\\nclosure?\"}\n    C -- Yes --&gt; HEAP\n    C -- No --&gt; D{\"Assigned to\\ninterface?\"}\n    D -- Yes --&gt; HEAP\n    D -- No --&gt; E{\"Too large\\nfor stack?\"}\n    E -- Yes --&gt; HEAP\n    E -- No --&gt; STACK[\"Allocate on Stack\"]</pre></figure>\n<p>Run <code>go build -gcflags '-m'</code> on any Go file to see escape analysis decisions.</p>\n<h2 id=\"concurrency-issues--data-race\">Concurrency Issues – Data Race<a class=\"h-anchor\" href=\"#concurrency-issues--data-race\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> main</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> wg </span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">sync</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">WaitGroup</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    counter </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 0</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    counterPtr </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> &amp;</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">counter</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    for</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> i </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 0</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">; i </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">&lt;</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 1000</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">; i</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">++</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        wg.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Add</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">        go</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> func</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">            *</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">counterPtr</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">++</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">            wg.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Done</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        }()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    wg.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Wait</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    fmt.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Println</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"Counter:\"</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">counterPtr)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>This has a data race. Fix with a mutex:</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">var</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> mu </span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">sync</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">.</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">Mutex</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">go</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> func</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    mu.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Lock</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    *</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">counterPtr</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">++</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    mu.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Unlock</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    wg.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Done</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">()</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}()</span></span></code></pre></div>\n<h2 id=\"key-takeaways\">Key Takeaways<a class=\"h-anchor\" href=\"#key-takeaways\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<ul>\n<li>Passing by value is simple but can be inefficient for large data structures</li>\n<li>Using pointers avoids copying but requires synchronization for shared access</li>\n<li>Escape analysis determines stack vs heap allocation</li>\n<li>Go prevents dangling pointers via garbage collection</li>\n<li>Run <code>go test -race</code> to detect data races</li>\n</ul>\n"
    },
    {
      "id": "tag:ashwingopalsamy.in,2024-11-15:watch/the-office",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "The Office",
      "content_text": "",
      "date_published": "2024-11-15T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/git-practices-for-production-codebases/",
      "url": "https://ashwingopalsamy.in/library/notes/git-practices-for-production-codebases/",
      "title": "Git Practices for Production Codebases",
      "content_text": "Git habits that hold up in FinTech production: shared language, safety nets, and reviewable history.",
      "date_published": "2024-11-14T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>Git is the backbone of every software project. Working in FinTech systems, I’ve learned that Git isn’t just a tool – it’s a shared language and a safety net.</p>\n<blockquote>\n<p>In FinTech and regulated environments, every change must be auditable and traceable. Clean Git practices are not just about developer productivity – they are a compliance requirement.</p>\n</blockquote>\n<h2 id=\"commits\">Commits<a class=\"h-anchor\" href=\"#commits\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<h3 id=\"1-write-atomic-commits\">1. Write Atomic Commits<a class=\"h-anchor\" href=\"#1-write-atomic-commits\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>An atomic commit focuses on one thing – fixing a bug, adding a feature, or refactoring.</p>\n<p>Good: <code>feat: add endpoint for retrieving user account balances</code>\nBad: <code>misc: fix bugs and add features</code></p>\n<h3 id=\"2-use-descriptive-commit-messages\">2. Use Descriptive Commit Messages<a class=\"h-anchor\" href=\"#2-use-descriptive-commit-messages\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<div class=\"code-figure\" data-language=\"plaintext\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"plaintext\"><code><span class=\"line\"><span>&lt;type&gt;(&lt;scope&gt;): &lt;subject&gt;</span></span>\n<span class=\"line\"><span>&lt;BLANK LINE&gt;</span></span>\n<span class=\"line\"><span>&lt;body (optional)&gt;</span></span></code></pre></div>\n<blockquote>\n<p>The Conventional Commits Specification formalizes this format. It pairs well with commitlint and enables automatic changelog generation.</p>\n</blockquote>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    A[\"feat(parser): add bitmap validation\"]\n    A --&gt; B[\"&lt;b&gt;type&lt;/b&gt;&lt;br/&gt;feat\"]\n    A --&gt; C[\"&lt;b&gt;scope&lt;/b&gt;&lt;br/&gt;parser\"]\n    A --&gt; D[\"&lt;b&gt;description&lt;/b&gt;&lt;br/&gt;add bitmap validation\"]</pre></figure>\n<h2 id=\"branches\">Branches<a class=\"h-anchor\" href=\"#branches\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<h3 id=\"1-follow-a-consistent-naming-convention\">1. Follow a Consistent Naming Convention<a class=\"h-anchor\" href=\"#1-follow-a-consistent-naming-convention\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<div class=\"code-figure\" data-language=\"plaintext\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"plaintext\"><code><span class=\"line\"><span>&lt;JIRA-ticket-ID&gt;-&lt;type&gt;-&lt;short-description&gt;</span></span></code></pre></div>\n<p>Examples: <code>JIRA-5678-fix-transaction-timeout</code>, <code>JIRA-1234-feature-add-batch-processing</code></p>\n<h3 id=\"2-rebase-for-a-clean-history\">2. Rebase for a Clean History<a class=\"h-anchor\" href=\"#2-rebase-for-a-clean-history\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<div class=\"code-figure\" data-language=\"bash\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">git</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> checkout</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> JIRA-5678-fix-transaction-timeout</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">git</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> pull</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> --rebase</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> origin</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span></code></pre></div>\n<blockquote>\n<p>Rebasing rewrites commit hashes, so it should only be used on local or feature branches. The payoff is a linear history that reads like a narrative.</p>\n</blockquote>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">gitGraph\n    commit id: \"init\"\n    commit id: \"v1.0\"\n    branch JIRA-5678-fix-timeout\n    commit id: \"add retry logic\"\n    commit id: \"handle edge case\"\n    commit id: \"add tests\"\n    checkout main\n    commit id: \"hotfix: logging\"\n    checkout JIRA-5678-fix-timeout\n    merge main id: \"rebase onto main\" type: HIGHLIGHT\n    checkout main\n    merge JIRA-5678-fix-timeout id: \"squash merge\" type: HIGHLIGHT\n    commit id: \"v1.1\"</pre></figure>\n<h2 id=\"pull-requests\">Pull Requests<a class=\"h-anchor\" href=\"#pull-requests\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<h3 id=\"1-use-clear-pr-titles\">1. Use Clear PR Titles<a class=\"h-anchor\" href=\"#1-use-clear-pr-titles\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p><code>[JIRA-ticket-ID] &lt;Type&gt;: &lt;Short Description&gt;</code></p>\n<p>Examples:</p>\n<ul>\n<li><code>[JIRA-5678] Fix: Handle transaction timeout edge cases</code></li>\n<li><code>[JIRA-1234] Feature: Add bulk processing for transactions</code></li>\n</ul>\n<h3 id=\"2-write-pr-descriptions\">2. Write PR Descriptions<a class=\"h-anchor\" href=\"#2-write-pr-descriptions\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Answer three questions: What changed? Why? Does it introduce risks?</p>\n<h3 id=\"3-keep-prs-small\">3. Keep PRs Small<a class=\"h-anchor\" href=\"#3-keep-prs-small\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Large PRs are hard to review and prone to mistakes. A schema migration should be its own PR.</p>\n<h3 id=\"4-self-review-first\">4. Self-Review First<a class=\"h-anchor\" href=\"#4-self-review-first\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Read your diff as if a colleague wrote it before requesting review.</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    A[\"Branch Created\"] --&gt; B[\"Commits\"]\n    B --&gt; C[\"Self-Review\"]\n    C --&gt; D[\"PR Opened\"]\n    D --&gt; E[\"CI Passes\"]\n    E --&gt; F[\"Code Review\"]\n    F --&gt; G[\"Approved\"]\n    G --&gt; H[\"Squash Merge\"]\n    H --&gt; I[\"Branch Deleted\"]</pre></figure>\n<p>In highly critical FinTech systems, precision isn’t optional. Clean commits, structured branches, and clear PRs are safeguards for the stability of the systems we build.</p>\n"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/go-constants-beyond-basics/",
      "url": "https://ashwingopalsamy.in/library/notes/go-constants-beyond-basics/",
      "title": "Go Constants Beyond the Basics",
      "content_text": "Go constants are fixed values with surprising flexibility: typed constants, iota, and compile-time tricks that matter.",
      "date_published": "2024-11-13T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>When I first got into Go, I thought constants were simple and limited – just fixed values, nothing fancy. But as I delved deeper, I found they’re quite versatile. Yes, they’re fixed values, but Go handles them in ways that are both flexible and efficient. Let’s see what that means with some practical examples.</p>\n<h2 id=\"constants-as-type-free-values-until-theyre-used\">Constants as Type-Free Values (Until They’re Used)<a class=\"h-anchor\" href=\"#constants-as-type-free-values-until-theyre-used\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>In Go, constants are often untyped until you actually use them. They have a default kind but can be assigned to variables of different types, as long as the value fits. This makes them adaptable in a way that’s unusual for a statically typed language.</p>\n<p>Here’s how that looks:</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> x</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 10</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">var</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> i </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">int</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> x</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">var</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> f </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">float64</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> x</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">var</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> b </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">byte</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> x</span></span></code></pre></div>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph TD\n    A[\"const x = 5\\n(untyped)\"] --&gt; B[\"used as int\"]\n    A --&gt; C[\"used as float64\"]\n    A --&gt; D[\"used as byte\"]\n    B --&gt; E[\"int\"]\n    C --&gt; F[\"float64\"]\n    D --&gt; G[\"byte\"]</pre></figure>\n<p>A bit analogous to Schrodinger’s paradox, <code>x</code> can be an <code>int</code>, a <code>float64</code>, or even a <code>byte</code> until you assign it. This temporary flexibility lets <code>x</code> work smoothly with different types in your code. No need for casting, which keeps things neat.</p>\n<p>You can even mix constants of different types in expressions and Go will figure out the best type for the result:</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> a</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 1.5</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> b</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 2</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> result</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> a </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> b </span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">// result is float64</span></span></code></pre></div>\n<p>Since <code>a</code> is a floating-point number, Go promotes the whole expression to <code>float64</code>. So you don’t have to worry about losing precision – Go handles it. But be careful: if you try to assign <code>result</code> to an <code>int</code>, you’ll get an error. Go doesn’t allow implicit conversions that might lose data.</p>\n<h2 id=\"constants-in-conditional-compilation\">Constants in Conditional Compilation<a class=\"h-anchor\" href=\"#constants-in-conditional-compilation\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>Go doesn’t have a preprocessor like some other languages, but you can use constants in <code>if</code> statements to include or exclude code at compile time.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> debug</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> false</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> main</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">    if</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> debug {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">        fmt.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Println</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"Debugging enabled\"</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>When <code>debug</code> is <code>false</code>, the compiler knows the <code>if</code> condition will never be true and might leave out the code inside the block. This can make your final binary smaller.</p>\n<h2 id=\"working-with-big-numbers\">Working with Big Numbers<a class=\"h-anchor\" href=\"#working-with-big-numbers\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>One powerful feature of Go’s constants is that they support very large numbers. Untyped numeric constants in Go have “infinite” precision, limited only by memory and the compiler.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> bigNum</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">e</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">1000</span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\"> // This is a valid constant</span></span></code></pre></div>\n<p>Even though <code>bigNum</code> is way bigger than any built-in numeric type like <code>float64</code> or <code>int</code>, Go lets you define it as a constant. You can do calculations with these large numbers at compile time:</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">    a</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">e</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">20</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">    b</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">e</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">30</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">    c</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> a </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> b </span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">// c is 1e50</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">)</span></span></code></pre></div>\n<h2 id=\"typed-constants-with-iota\">Typed Constants with iota<a class=\"h-anchor\" href=\"#typed-constants-with-iota\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>If you’ve been using Go, you’ve probably seen <code>iota</code> for creating enumerated constants.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">    _</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> iota</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">    KB</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> &lt;&lt;</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">10</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> *</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> iota</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">    MB</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">    GB</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">    TB</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">)</span></span></code></pre></div>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    A[\"iota=0\"] --&gt; B[\"1&lt;&lt;0 = 1\\n(Read)\"]\n    C[\"iota=1\"] --&gt; D[\"1&lt;&lt;1 = 2\\n(Write)\"]\n    E[\"iota=2\"] --&gt; F[\"1&lt;&lt;2 = 4\\n(Execute)\"]\n    B --&gt; G[\"Read | Write = 3\"]\n    D --&gt; G</pre></figure>\n<h2 id=\"limitations-with-constants\">Limitations with Constants<a class=\"h-anchor\" href=\"#limitations-with-constants\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>While Go’s constants are flexible, there are some things they can’t do.</p>\n<h3 id=\"1-constants-cannot-be-referenced-by-pointers\">1. Constants Cannot Be Referenced by Pointers<a class=\"h-anchor\" href=\"#1-constants-cannot-be-referenced-by-pointers\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Constants don’t have a memory address at runtime. So you can’t take the address of a constant or use a pointer to it.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> x</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 10</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">var</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> p </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> &amp;</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">x </span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">// Error: cannot take the address of x</span></span></code></pre></div>\n<h3 id=\"2-function-calls-in-constant-declarations\">2. Function Calls in Constant Declarations<a class=\"h-anchor\" href=\"#2-function-calls-in-constant-declarations\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Only certain built-in functions can be used in constant expressions, like <code>len</code>, <code>cap</code>, <code>real</code>, <code>imag</code>, and <code>complex</code>.</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> str</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> \"hello\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> length</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> len</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(str) </span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">// This works</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> pow</span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\"> math.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Pow</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\">3</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"--shiki-light:#6E7781;--shiki-dark:#8B949E\">// Error: math.Pow cannot be used in constant expressions</span></span></code></pre></div>\n"
    },
    {
      "id": "https://ashwingopalsamy.in/library/notes/go-utf8-identifier-limitation/",
      "url": "https://ashwingopalsamy.in/library/notes/go-utf8-identifier-limitation/",
      "title": "Go's UTF-8 Identifier Limitation",
      "content_text": "Where Go's UTF-8 support stops: what works in identifiers, and what non-Latin scripts still can't do.",
      "date_published": "2024-11-12T00:00:00.000Z",
      "tags": [
        "notes"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ],
      "content_html": "<p>I’ve been exploring Go’s UTF-8 support and was curious about how well it handles non-Latin scripts in code.</p>\n<p>Go source files are UTF-8 encoded by default. You can use Unicode characters in variable names and function names. Chinese characters work fine:</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">package</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\"> main</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> \"</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">fmt</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> main</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    消息 </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> \"Hello, World!\"</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    fmt.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Println</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(消息)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<h2 id=\"attempting-to-use-tamil-identifiers\">Attempting to Use Tamil Identifiers<a class=\"h-anchor\" href=\"#attempting-to-use-tamil-identifiers\" aria-hidden=\"true\" tabindex=\"-1\"></a></h2>\n<p>I tried Tamil, my mother tongue:</p>\n<div class=\"code-figure\" data-language=\"go\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"go\"><code><span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">package</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\"> main</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\"> \"</span><span style=\"--shiki-light:#953800;--shiki-dark:#FFA657\">fmt</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\"> main</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    எண்ணிக்கை </span><span style=\"--shiki-light:#CF222E;--shiki-dark:#FF7B72\">:=</span><span style=\"--shiki-light:#0550AE;--shiki-dark:#79C0FF\"> 42</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">    fmt.</span><span style=\"--shiki-light:#8250DF;--shiki-dark:#D2A8FF\">Println</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"--shiki-light:#0A3069;--shiki-dark:#A5D6FF\">\"Value:\"</span><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">, எண்ணிக்கை)</span></span>\n<span class=\"line\"><span style=\"--shiki-light:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>But the compiler throws errors:</p>\n<div class=\"code-figure\" data-language=\"plaintext\"><pre class=\"astro-code astro-code-themes github-light-default github-dark-default\" style=\"--shiki-light:#1f2328;--shiki-dark:#e6edf3;--shiki-light-bg:#ffffff;--shiki-dark-bg:#0d1117; overflow-x: auto;\" tabindex=\"0\" data-language=\"plaintext\"><code><span class=\"line\"><span>./prog.go:6:11: invalid character U+0BCD '்' in identifier</span></span></code></pre></div>\n<h3 id=\"why-combining-marks-fail\">Why Combining Marks Fail<a class=\"h-anchor\" href=\"#why-combining-marks-fail\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Tamil is an abugida. Consonant-vowel sequences are written as units with combining marks. Go’s spec allows Unicode letters in identifiers but excludes combining marks (categories <code>Mn</code>, <code>Mc</code>, <code>Me</code>).</p>\n<figure class=\"diagram\" data-diagram><div class=\"diagram-canvas\" role=\"img\" aria-label=\"Mermaid diagram\"></div><pre class=\"diagram-source\">graph LR\n    A[\"Tamil syllable\"] --&gt; B[\"Base consonant\\n(Lo: valid)\"]\n    A --&gt; C[\"Vowel sign\\n(Mc: continuation only)\"]\n    B --&gt; D[\"Visually incomplete\\nalone\"]\n    C --&gt; D</pre></figure>\n<h3 id=\"chinese-vs-tamil\">Chinese vs Tamil<a class=\"h-anchor\" href=\"#chinese-vs-tamil\" aria-hidden=\"true\" tabindex=\"-1\"></a></h3>\n<p>Chinese characters are classified under “Letter, Other” (<code>Lo</code>) – standalone symbols. Tamil requires combining marks, which Go’s spec explicitly excludes.</p>\n<blockquote>\n<p>Go’s creators primarily aimed for consistent string handling through UTF-8 support. They didn’t necessarily intend for native-language coding in identifiers requiring combining marks.</p>\n</blockquote>\n"
    },
    {
      "id": "tag:ashwingopalsamy.in,2024-07-20:watch/avatar-the-last-airbender",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Avatar: The Last Airbender",
      "content_text": "",
      "date_published": "2024-07-20T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2024-06-10:books/learning-go",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Learning Go",
      "content_text": "",
      "date_published": "2024-06-10T00:00:00.000Z",
      "tags": [
        "books"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2024-05-01:watch/chotta-bheem",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Chotta Bheem",
      "content_text": "",
      "date_published": "2024-05-01T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2024-03-10:watch/suits",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Suits",
      "content_text": "",
      "date_published": "2024-03-10T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2024-01-20:watch/the-social-network",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "The Social Network",
      "content_text": "",
      "date_published": "2024-01-20T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2023-10-14:watch/bojack-horseman",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "BoJack Horseman",
      "content_text": "",
      "date_published": "2023-10-14T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2023-08-14:watch/breaking-bad",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Breaking Bad",
      "content_text": "",
      "date_published": "2023-08-14T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2023-06-08:watch/never-have-i-ever",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Never Have I Ever",
      "content_text": "",
      "date_published": "2023-06-08T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2023-02-12:watch/ben-10",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Ben 10",
      "content_text": "",
      "date_published": "2023-02-12T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2022-09-30:watch/vikings",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Vikings",
      "content_text": "",
      "date_published": "2022-09-30T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2021-12-03:watch/money-heist",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Money Heist",
      "content_text": "",
      "date_published": "2021-12-03T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2020-08-22:watch/mr-robot",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Mr. Robot",
      "content_text": "",
      "date_published": "2020-08-22T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2020-06-05:watch/13-reasons-why",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "13 Reasons Why",
      "content_text": "",
      "date_published": "2020-06-05T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2020-05-01:watch/silicon-valley",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Silicon Valley",
      "content_text": "",
      "date_published": "2020-05-01T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2019-06-03:watch/chernobyl",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Chernobyl",
      "content_text": "",
      "date_published": "2019-06-03T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    },
    {
      "id": "tag:ashwingopalsamy.in,2019-05-20:watch/game-of-thrones",
      "url": "https://ashwingopalsamy.in/library/",
      "title": "Game of Thrones",
      "content_text": "",
      "date_published": "2019-05-20T00:00:00.000Z",
      "tags": [
        "watch"
      ],
      "authors": [
        {
          "name": "Ashwin Gopalsamy",
          "url": "https://ashwingopalsamy.in/"
        }
      ]
    }
  ]
}