<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Behzad's Blog]]></title><description><![CDATA[Behzad's Blog]]></description><link>https://blog.behzadk.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 01 Jun 2026 12:30:59 GMT</lastBuildDate><atom:link href="https://blog.behzadk.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[After 5 Years and 100k Lines of Golang]]></title><description><![CDATA[Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tincidunt porttitor volutpat. Curabitur rutrum risus sit amet imperdiet facilisis. Integer risus ligula, tincidunt non faucibus fac]]></description><link>https://blog.behzadk.com/after-5-years-and-100k-lines-of-golang</link><guid isPermaLink="true">https://blog.behzadk.com/after-5-years-and-100k-lines-of-golang</guid><dc:creator><![CDATA[Muzammil Tayyab]]></dc:creator><pubDate>Wed, 11 Mar 2026 00:26:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69acc78886766ac3a6f7c50e/96685b98-b79e-42e8-ae11-94425f4bb65e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tincidunt porttitor volutpat. Curabitur rutrum risus sit amet imperdiet facilisis. Integer risus ligula, tincidunt non faucibus facilisis, molestie non urna. Sed commodo pulvinar tellus, eu euismod lacus gravida a. Integer nec leo eget odio dapibus convallis. Quisque ultrices vitae ipsum ac dictum. Aliquam eu arcu quis felis rhoncus faucibus. Phasellus sagittis lorem ac arcu pharetra tincidunt. Nunc dignissim arcu est, a blandit sapien sodales sit amet. Nunc hendrerit augue non eleifend finibus.</p>
<p>Vivamus interdum mauris at neque scelerisque ultrices. Donec enim nunc, venenatis porta augue non, pharetra suscipit nibh. Fusce sed lacus eget dui sagittis pharetra. Mauris sodales quis nisi quis fringilla. Etiam at convallis dolor. Vestibulum sed lorem mi. Nunc accumsan, orci quis lobortis sollicitudin, mauris neque semper lorem, et porttitor sem risus sodales ligula. Praesent malesuada dolor dapibus consectetur dapibus. Mauris a nibh at ante varius ultricies quis in velit. In in varius tortor, sed scelerisque est. Morbi at condimentum libero, in dignissim tellus.</p>
<p>Integer dolor justo, cursus sed maximus finibus, hendrerit eget mi. Nam id nunc in orci rutrum sodales vitae id justo. Nulla facilisi. Curabitur sed dignissim sapien, id convallis augue. Vestibulum ornare auctor erat. Fusce non dolor ornare, aliquam leo vitae, placerat nibh. Morbi elementum blandit ligula venenatis interdum. Donec aliquet at orci non venenatis. Integer gravida odio ullamcorper metus congue mattis. Nam turpis est, efficitur in lacus quis, scelerisque semper nunc. Duis eget nunc sagittis felis condimentum interdum quis eget mi.</p>
<p>Proin vitae nibh magna. Mauris nec purus at tortor pulvinar rhoncus et quis velit. Nulla fringilla justo quis felis malesuada eleifend a vel leo. Praesent eu blandit magna. Etiam sit amet velit elementum, pulvinar eros nec, laoreet nulla. Ut faucibus cursus nisl vitae lacinia. Nulla in aliquam ante, sed mollis ligula. Donec a justo arcu. Praesent dapibus eleifend arcu vitae vehicula. Maecenas vestibulum ullamcorper leo, quis tincidunt ante scelerisque sed. Nam sollicitudin congue consequat. Sed gravida purus tincidunt leo eleifend accumsan. Nullam molestie, purus in ullamcorper euismod, enim mauris imperdiet odio, ut maximus enim lorem quis nunc. Sed scelerisque libero metus, et suscipit leo venenatis vestibulum. Phasellus gravida gravida felis eu luctus. Aliquam erat volutpat.</p>
<p>Donec iaculis dolor faucibus enim cursus, a ultricies nisl venenatis. Etiam bibendum luctus sapien nec luctus. Vivamus tempus quam quis fermentum gravida. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla rhoncus velit et placerat imperdiet. Praesent a purus at quam feugiat aliquet. Praesent molestie tempus neque, at luctus neque elementum et. Proin sagittis aliquam metus.</p>
]]></content:encoded></item><item><title><![CDATA[Go Interfaces]]></title><description><![CDATA[Go Interfaces: The Shape of Behavior
The Coffee Shop Revelation
Imagine walking into a coffee shop. You don't care if the barista is human, a robot, or a highly trained octopus—as long as they can make coffee. This is exactly how Go interfaces work: ...]]></description><link>https://blog.behzadk.com/go-interfaces</link><guid isPermaLink="true">https://blog.behzadk.com/go-interfaces</guid><dc:creator><![CDATA[Behzad Khokher]]></dc:creator><pubDate>Mon, 08 Sep 2025 16:54:30 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-go-interfaces-the-shape-of-behavior">Go Interfaces: The Shape of Behavior</h1>
<h2 id="heading-the-coffee-shop-revelation">The Coffee Shop Revelation</h2>
<p>Imagine walking into a coffee shop. You don't care if the barista is human, a robot, or a highly trained octopus—as long as they can <em>make coffee</em>. This is exactly how Go interfaces work: they care about what something can do, not what it is.</p>
<p>In most programming explanations, interfaces are described as "contracts" or "abstract types." But that's like explaining water as "H₂O molecules in liquid state"—technically correct but missing the intuitive essence. Let's rebuild your understanding from scratch.</p>
<h2 id="heading-interfaces-are-shapes-not-boxes">Interfaces Are Shapes, Not Boxes</h2>
<p>Traditional object-oriented languages treat types like boxes with labels. You have a "Dog box" and a "Cat box," and you need to explicitly say "this box fits inside the Animal box." Go flips this completely.</p>
<p>In Go, interfaces are more like cookie cutters. They define a shape, and anything that fits that shape can pass through. You never explicitly declare "I implement this interface"—if you fit, you fit.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Speaker <span class="hljs-keyword">interface</span> {
    Speak() <span class="hljs-keyword">string</span>
}

<span class="hljs-keyword">type</span> Dog <span class="hljs-keyword">struct</span> {
    name <span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(d Dog)</span> <span class="hljs-title">Speak</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Woof!"</span>
}

<span class="hljs-keyword">type</span> Robot <span class="hljs-keyword">struct</span> {
    model <span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r Robot)</span> <span class="hljs-title">Speak</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Beep boop"</span>
}

<span class="hljs-comment">// Both Dog and Robot are Speakers, not because we declared it,</span>
<span class="hljs-comment">// but because they have the right shape (the Speak method)</span>
</code></pre>
<h2 id="heading-the-zero-declaration-principle">The Zero-Declaration Principle</h2>
<p>Here's what makes Go interfaces revolutionary: you never write "implements." This isn't just syntactic sugar—it's a fundamental philosophy shift.</p>
<p>Consider this scenario: You're using a third-party package that defines a <code>Logger</code> type. Later, you want to use a different logging library that expects a <code>LogWriter</code> interface. In most languages, you're stuck. But in Go:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Package A (you don't control this)</span>
<span class="hljs-keyword">type</span> FileLogger <span class="hljs-keyword">struct</span> {
    filepath <span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(f FileLogger)</span> <span class="hljs-title">Write</span><span class="hljs-params">(message <span class="hljs-keyword">string</span>)</span></span> {
    <span class="hljs-comment">// writes to file</span>
}

<span class="hljs-comment">// Package B (you don't control this either)</span>
<span class="hljs-keyword">type</span> LogWriter <span class="hljs-keyword">interface</span> {
    Write(<span class="hljs-keyword">string</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ProcessWithLogging</span><span class="hljs-params">(lw LogWriter)</span></span> {
    lw.Write(<span class="hljs-string">"Processing started"</span>)
}

<span class="hljs-comment">// Your code (this just works!)</span>
logger := FileLogger{filepath: <span class="hljs-string">"/var/log/app.log"</span>}
ProcessWithLogging(logger)  <span class="hljs-comment">// FileLogger automatically satisfies LogWriter</span>
</code></pre>
<p>The <code>FileLogger</code> was created without any knowledge of <code>LogWriter</code>, yet they work together perfectly. This is like discovering that your phone charger from 2018 works perfectly with a power bank invented in 2024—not because they coordinated, but because they share the same shape.</p>
<h2 id="heading-the-interface-paradox">The Interface{} Paradox</h2>
<p>The empty interface <code>interface{}</code> (or <code>any</code> in modern Go) is simultaneously the most powerful and most dangerous type in Go. It's like a shape with no edges—everything fits.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">PrintAnything</span><span class="hljs-params">(thing <span class="hljs-keyword">interface</span>{})</span></span> {
    fmt.Println(thing)
}

PrintAnything(<span class="hljs-number">42</span>)
PrintAnything(<span class="hljs-string">"hello"</span>)
PrintAnything([]<span class="hljs-keyword">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>})
PrintAnything(Dog{name: <span class="hljs-string">"Rex"</span>})
</code></pre>
<p>But here's the paradox: the moment you accept everything, you know nothing. It's like having a universal key that opens every door but doesn't tell you what's behind any of them.</p>
<h2 id="heading-interface-composition-building-complex-shapes">Interface Composition: Building Complex Shapes</h2>
<p>Go interfaces can embed other interfaces, creating composite shapes. This isn't inheritance—it's more like combining cookie cutters:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Reader <span class="hljs-keyword">interface</span> {
    Read([]<span class="hljs-keyword">byte</span>) (<span class="hljs-keyword">int</span>, error)
}

<span class="hljs-keyword">type</span> Writer <span class="hljs-keyword">interface</span> {
    Write([]<span class="hljs-keyword">byte</span>) (<span class="hljs-keyword">int</span>, error)
}

<span class="hljs-keyword">type</span> ReadWriter <span class="hljs-keyword">interface</span> {
    Reader
    Writer
}

<span class="hljs-comment">// This is not "ReadWriter extends Reader and Writer"</span>
<span class="hljs-comment">// It's "ReadWriter is the shape that fits both Reader and Writer shapes"</span>
</code></pre>
<p>Think of it as overlapping Venn diagrams. A <code>ReadWriter</code> isn't a special kind of <code>Reader</code> or <code>Writer</code>—it's anything that exists in the overlap where both shapes coincide.</p>
<h2 id="heading-the-implicit-state-machine">The Implicit State Machine</h2>
<p>Interfaces enable a pattern I call the "implicit state machine." Watch how interfaces can represent different states of the same conceptual object:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Document <span class="hljs-keyword">interface</span> {
    Content() <span class="hljs-keyword">string</span>
}

<span class="hljs-keyword">type</span> EditableDocument <span class="hljs-keyword">interface</span> {
    Document
    Update(content <span class="hljs-keyword">string</span>)
}

<span class="hljs-keyword">type</span> PublishableDocument <span class="hljs-keyword">interface</span> {
    Document
    Publish() error
}

<span class="hljs-keyword">type</span> Draft <span class="hljs-keyword">struct</span> {
    content <span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(d *Draft)</span> <span class="hljs-title">Content</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">return</span> d.content
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(d *Draft)</span> <span class="hljs-title">Update</span><span class="hljs-params">(content <span class="hljs-keyword">string</span>)</span></span> {
    d.content = content
}

<span class="hljs-keyword">type</span> Published <span class="hljs-keyword">struct</span> {
    content <span class="hljs-keyword">string</span>
    url     <span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(p Published)</span> <span class="hljs-title">Content</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">return</span> p.content
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(p Published)</span> <span class="hljs-title">Publish</span><span class="hljs-params">()</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"already published"</span>)
}

<span class="hljs-comment">// A Draft is an EditableDocument</span>
<span class="hljs-comment">// A Published is a PublishableDocument</span>
<span class="hljs-comment">// Both are Documents</span>
<span class="hljs-comment">// This creates a compile-time enforced state machine</span>
</code></pre>
<p>Your document naturally transitions through states, and the compiler ensures you can't call <code>Update()</code> on a published document. No runtime checks needed.</p>
<h2 id="heading-the-pointer-receiver-trap">The Pointer Receiver Trap</h2>
<p>Here's a subtle gotcha that trips up even experienced Go developers:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Counter <span class="hljs-keyword">interface</span> {
    Increment()
    Value() <span class="hljs-keyword">int</span>
}

<span class="hljs-keyword">type</span> MyCounter <span class="hljs-keyword">struct</span> {
    count <span class="hljs-keyword">int</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(c *MyCounter)</span> <span class="hljs-title">Increment</span><span class="hljs-params">()</span></span> {
    c.count++
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(c MyCounter)</span> <span class="hljs-title">Value</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> c.count
}

<span class="hljs-keyword">var</span> c Counter
c = MyCounter{count: <span class="hljs-number">0</span>}  <span class="hljs-comment">// COMPILE ERROR!</span>
c = &amp;MyCounter{count: <span class="hljs-number">0</span>} <span class="hljs-comment">// This works</span>
</code></pre>
<p>Why? Because <code>*MyCounter</code> implements <code>Counter</code>, not <code>MyCounter</code>. The pointer type and value type are different citizens in Go's type system. This isn't a bug—it's forcing you to think about whether your interface represents something that can be copied (value receiver) or something with identity (pointer receiver).</p>
<h2 id="heading-interface-segregation-without-the-principle">Interface Segregation Without the Principle</h2>
<p>The Interface Segregation Principle says "clients shouldn't depend on interfaces they don't use." Go naturally encourages this without you trying:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Instead of this monolith:</span>
<span class="hljs-keyword">type</span> Database <span class="hljs-keyword">interface</span> {
    Query(<span class="hljs-keyword">string</span>) Result
    Insert(Record) error
    Update(Record) error
    Delete(<span class="hljs-keyword">int</span>) error
    BeginTransaction() Transaction
    Backup() error
    Restore(<span class="hljs-keyword">string</span>) error
}

<span class="hljs-comment">// Go culture creates this:</span>
<span class="hljs-keyword">type</span> Querier <span class="hljs-keyword">interface</span> {
    Query(<span class="hljs-keyword">string</span>) Result
}

<span class="hljs-keyword">type</span> Inserter <span class="hljs-keyword">interface</span> {
    Insert(Record) error
}

<span class="hljs-keyword">type</span> Transactor <span class="hljs-keyword">interface</span> {
    BeginTransaction() Transaction
}

<span class="hljs-comment">// Functions accept only what they need</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">GenerateReport</span><span class="hljs-params">(q Querier)</span></span> {
    <span class="hljs-comment">// Only needs to query, doesn't care about insert/update/delete</span>
}
</code></pre>
<p>This happens naturally because Go developers quickly learn that smaller interfaces are more reusable. It's like evolution—the interfaces that survive are the ones that do one thing well.</p>
<h2 id="heading-the-testing-superpower">The Testing Superpower</h2>
<p>Interfaces make testing almost trivial. You can create test doubles without any mocking framework:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> EmailSender <span class="hljs-keyword">interface</span> {
    Send(to, subject, body <span class="hljs-keyword">string</span>) error
}

<span class="hljs-comment">// Production implementation</span>
<span class="hljs-keyword">type</span> SMTPEmailSender <span class="hljs-keyword">struct</span> {
    host <span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s SMTPEmailSender)</span> <span class="hljs-title">Send</span><span class="hljs-params">(to, subject, body <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-comment">// Actually sends email via SMTP</span>
}

<span class="hljs-comment">// Test implementation</span>
<span class="hljs-keyword">type</span> RecordingEmailSender <span class="hljs-keyword">struct</span> {
    Sent []<span class="hljs-keyword">string</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *RecordingEmailSender)</span> <span class="hljs-title">Send</span><span class="hljs-params">(to, subject, body <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">error</span></span> {
    r.Sent = <span class="hljs-built_in">append</span>(r.Sent, to)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-comment">// Your function doesn't care which one it gets</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NotifyUser</span><span class="hljs-params">(email EmailSender, userAddr <span class="hljs-keyword">string</span>)</span></span> {
    email.Send(userAddr, <span class="hljs-string">"Notification"</span>, <span class="hljs-string">"Something happened"</span>)
}
</code></pre>
<p>No mocking framework, no code generation, no reflection magic—just plain Go code.</p>
<h2 id="heading-the-interface-witness-pattern">The Interface Witness Pattern</h2>
<p>Here's an advanced pattern that ensures at compile-time that your types implement interfaces:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Stringer <span class="hljs-keyword">interface</span> {
    String() <span class="hljs-keyword">string</span>
}

<span class="hljs-keyword">type</span> MyType <span class="hljs-keyword">struct</span>{}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(m MyType)</span> <span class="hljs-title">String</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"MyType"</span>
}

<span class="hljs-comment">// This line ensures MyType implements Stringer at compile time</span>
<span class="hljs-keyword">var</span> _ Stringer = MyType{}  <span class="hljs-comment">// or = (*MyType)(nil) for pointer receivers</span>
</code></pre>
<p>This "witness" line will fail to compile if <code>MyType</code> doesn't implement <code>Stringer</code>. It's like a unit test that runs at compile time.</p>
<h2 id="heading-real-world-example-the-strategy-pattern-without-the-pattern">Real-World Example: The Strategy Pattern Without the Pattern</h2>
<p>Traditional Strategy pattern requires explicit interface declaration and often a context class. Go makes it invisible:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> PaymentProcessor <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(amount <span class="hljs-keyword">float64</span>)</span> <span class="hljs-title">error</span></span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ProcessOrder</span><span class="hljs-params">(amount <span class="hljs-keyword">float64</span>, processor PaymentProcessor)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-comment">// Validate amount</span>
    <span class="hljs-keyword">if</span> amount &lt;= <span class="hljs-number">0</span> {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"invalid amount"</span>)
    }

    <span class="hljs-comment">// Process payment using whatever strategy was passed</span>
    <span class="hljs-keyword">return</span> processor(amount)
}

<span class="hljs-comment">// Different strategies</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">CreditCard</span><span class="hljs-params">(amount <span class="hljs-keyword">float64</span>)</span> <span class="hljs-title">error</span></span> {
    fmt.Printf(<span class="hljs-string">"Processing $%.2f via credit card\n"</span>, amount)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">PayPal</span><span class="hljs-params">(amount <span class="hljs-keyword">float64</span>)</span> <span class="hljs-title">error</span></span> {
    fmt.Printf(<span class="hljs-string">"Processing $%.2f via PayPal\n"</span>, amount)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Bitcoin</span><span class="hljs-params">(amount <span class="hljs-keyword">float64</span>)</span> <span class="hljs-title">error</span></span> {
    fmt.Printf(<span class="hljs-string">"Processing %.8f BTC\n"</span>, amount/<span class="hljs-number">50000</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}

<span class="hljs-comment">// Usage</span>
ProcessOrder(<span class="hljs-number">99.99</span>, CreditCard)
ProcessOrder(<span class="hljs-number">99.99</span>, PayPal)
ProcessOrder(<span class="hljs-number">99.99</span>, Bitcoin)
</code></pre>
<p>The strategy pattern emerges naturally from Go's type system. No need for abstract base classes or explicit interface implementations.</p>
<h2 id="heading-the-ioreaderwriter-ecosystem">The io.Reader/Writer Ecosystem</h2>
<p>The greatest success story of Go interfaces is the <code>io.Reader</code> and <code>io.Writer</code>. These two simple interfaces:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Reader <span class="hljs-keyword">interface</span> {
    Read([]<span class="hljs-keyword">byte</span>) (<span class="hljs-keyword">int</span>, error)
}

<span class="hljs-keyword">type</span> Writer <span class="hljs-keyword">interface</span> {
    Write([]<span class="hljs-keyword">byte</span>) (<span class="hljs-keyword">int</span>, error)
}
</code></pre>
<p>Power an entire ecosystem. Files, network connections, buffers, compression, encryption—they all speak this common language. It's like how USB became universal not by being complex, but by being simple and consistent.</p>
<h2 id="heading-performance-implications-the-hidden-cost">Performance Implications: The Hidden Cost</h2>
<p>Interfaces aren't free. Every interface call involves a virtual dispatch:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Adder <span class="hljs-keyword">interface</span> {
    Add(<span class="hljs-keyword">int</span>, <span class="hljs-keyword">int</span>) <span class="hljs-keyword">int</span>
}

<span class="hljs-keyword">type</span> SimpleAdder <span class="hljs-keyword">struct</span>{}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(SimpleAdder)</span> <span class="hljs-title">Add</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> a + b
}

<span class="hljs-comment">// Direct call: can be inlined</span>
adder := SimpleAdder{}
result := adder.Add(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>)  <span class="hljs-comment">// Fast</span>

<span class="hljs-comment">// Interface call: cannot be inlined</span>
<span class="hljs-keyword">var</span> adderInterface Adder = SimpleAdder{}
result = adderInterface.Add(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>)  <span class="hljs-comment">// Slightly slower</span>
</code></pre>
<p>For hot paths processing millions of operations, this matters. For everything else, the flexibility is worth the nanoseconds.</p>
<h2 id="heading-the-philosophical-core">The Philosophical Core</h2>
<p>Go interfaces embody a philosophy: describe behavior, not identity. It's duck typing with compile-time safety. It's the realization that in software, what something <em>does</em> matters more than what it <em>is</em>.</p>
<p>This creates a unique form of polymorphism. Not the hierarchical polymorphism of class inheritance, but a lateral polymorphism where unrelated types can play the same role simply by exhibiting the same behavior.</p>
<h2 id="heading-common-pitfalls-and-how-to-avoid-them">Common Pitfalls and How to Avoid Them</h2>
<p><strong>The nil interface trap:</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> r io.Reader
<span class="hljs-keyword">var</span> f *os.File  <span class="hljs-comment">// f is nil</span>

r = f  <span class="hljs-comment">// r is not nil! It has a type (*os.File) but nil value</span>

<span class="hljs-keyword">if</span> r == <span class="hljs-literal">nil</span> {
    <span class="hljs-comment">// This won't execute, even though f was nil</span>
}
</code></pre>
<p><strong>The interface pollution anti-pattern:</strong> Don't create interfaces preemptively. Start with concrete types, extract interfaces when you need them for testing or when you have multiple implementations.</p>
<p><strong>The Accept Interfaces, Return Structs principle:</strong> Functions should accept interfaces (be flexible about input) but return concrete types (be specific about output). This maximizes flexibility for callers.</p>
<h2 id="heading-conclusion-thinking-in-shapes">Conclusion: Thinking in Shapes</h2>
<p>Go interfaces aren't just a language feature—they're a different way of thinking about types and behavior. Instead of asking "what is this thing?", Go asks "what can this thing do?"</p>
<p>Once you internalize this, you stop thinking in hierarchies and start thinking in capabilities. You stop creating elaborate type taxonomies and start defining minimal behavior contracts. Your code becomes more flexible not through complexity, but through simplicity.</p>
<p>The next time you write Go, don't think about interfaces as contracts to implement. Think of them as shapes that your types naturally fit into. Let the compiler discover the relationships that emerge from behavior, rather than declaring relationships that constrain behavior.</p>
<p>This is the Go way: implicit, minimal, and surprisingly powerful.</p>
]]></content:encoded></item><item><title><![CDATA[Go's Enhanced net/http Package: All The Routing You Need]]></title><description><![CDATA[Introduction
For years (test), Go developers have reached for third-party routing libraries like Gorilla Mux, Chi, or Gin when building web applications. The standard library's http.ServeMux was considered too basic, lacking essential features like H...]]></description><link>https://blog.behzadk.com/gos-enhanced-nethttp-package-all-the-routing-you-need</link><guid isPermaLink="true">https://blog.behzadk.com/gos-enhanced-nethttp-package-all-the-routing-you-need</guid><category><![CDATA[Go Language]]></category><category><![CDATA[go net http]]></category><dc:creator><![CDATA[Behzad Khokher]]></dc:creator><pubDate>Mon, 18 Aug 2025 07:03:37 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>For years (test), Go developers have reached for third-party routing libraries like Gorilla Mux, Chi, or Gin when building web applications. The standard library's <code>http.ServeMux</code> was considered too basic, lacking essential features like HTTP method matching and path parameters. However, Go 1.22 brings two enhancements to the net/http package's router: method matching and wildcards, fundamentally changing the routing landscape in Go.</p>
<p>This comprehensive guide explores how Go's enhanced <code>net/http</code> package now provides all the routing capabilities most applications need, potentially eliminating the need for external routing dependencies.</p>
<h2 id="heading-the-evolution-from-basic-to-powerful">The Evolution: From Basic to Powerful</h2>
<h3 id="heading-before-go-122">Before Go 1.22</h3>
<p>Prior to Go 1.22, the <code>http.ServeMux</code> was extremely limited. It could only:</p>
<ul>
<li><p>Match URL paths based on prefix patterns</p>
</li>
<li><p>Route requests regardless of HTTP method</p>
</li>
<li><p>Handle static paths without parameters</p>
</li>
</ul>
<p>This meant developers had to write boilerplate code for even basic REST operations:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Pre-1.22 approach</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">handlePost</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    <span class="hljs-comment">// Manual method checking</span>
    <span class="hljs-keyword">if</span> r.Method != http.MethodGET {
        w.WriteHeader(http.StatusMethodNotAllowed)
        <span class="hljs-keyword">return</span>
    }

    <span class="hljs-comment">// Manual ID extraction</span>
    path := strings.TrimPrefix(r.URL.Path, <span class="hljs-string">"/posts/"</span>)
    id := strings.TrimSuffix(path, <span class="hljs-string">"/"</span>)

    <span class="hljs-comment">// Handle the request...</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    http.HandleFunc(<span class="hljs-string">"/posts/"</span>, handlePost)
    http.ListenAndServe(<span class="hljs-string">":8080"</span>, <span class="hljs-literal">nil</span>)
}
</code></pre>
<h3 id="heading-the-game-changer-go-122">The Game Changer: Go 1.22+</h3>
<p>The new routing features almost exclusively affect the pattern string passed to the two net/http.ServeMux methods Handle and HandleFunc. The syntax has been enhanced to support:</p>
<ol>
<li><p><strong>HTTP Method Routing</strong>: Specify methods directly in patterns</p>
</li>
<li><p><strong>Path Parameters</strong>: Extract dynamic values from URLs using wildcards</p>
</li>
<li><p><strong>Enhanced Pattern Matching</strong>: More sophisticated routing rules</p>
</li>
</ol>
<h2 id="heading-core-features-of-enhanced-routing">Core Features of Enhanced Routing</h2>
<h3 id="heading-1-method-based-routing">1. Method-Based Routing</h3>
<p>That means we can set a route as GET /hello and it will automatically only accept GET requests and return HTTP 405 otherwise. The syntax is straightforward:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    mux := http.NewServeMux()

    <span class="hljs-comment">// Method-specific handlers</span>
    mux.HandleFunc(<span class="hljs-string">"GET /users"</span>, listUsers)
    mux.HandleFunc(<span class="hljs-string">"POST /users"</span>, createUser)
    mux.HandleFunc(<span class="hljs-string">"GET /users/{id}"</span>, getUser)
    mux.HandleFunc(<span class="hljs-string">"PUT /users/{id}"</span>, updateUser)
    mux.HandleFunc(<span class="hljs-string">"DELETE /users/{id}"</span>, deleteUser)

    http.ListenAndServe(<span class="hljs-string">":8080"</span>, mux)
}
</code></pre>
<p>Important notes about method routing:</p>
<ul>
<li><p>There should exactly a single space between the method and the path</p>
</li>
<li><p>If no method is specified, the handler accepts all methods</p>
</li>
<li><p>The router automatically returns 405 Method Not Allowed for mismatched methods</p>
</li>
</ul>
<h3 id="heading-2-path-parameters-with-wildcards">2. Path Parameters with Wildcards</h3>
<p>Wildcards allow you to define variable parts of an URL path in a number of different ways. Go 1.22 introduces several wildcard patterns:</p>
<h4 id="heading-basic-wildcards">Basic Wildcards</h4>
<pre><code class="lang-go">mux.HandleFunc(<span class="hljs-string">"GET /products/{id}"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    productID := r.PathValue(<span class="hljs-string">"id"</span>)
    fmt.Fprintf(w, <span class="hljs-string">"Product ID: %s"</span>, productID)
})
</code></pre>
<h4 id="heading-multiple-wildcards">Multiple Wildcards</h4>
<pre><code class="lang-go">mux.HandleFunc(<span class="hljs-string">"GET /users/{userID}/posts/{postID}"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    userID := r.PathValue(<span class="hljs-string">"userID"</span>)
    postID := r.PathValue(<span class="hljs-string">"postID"</span>)
    fmt.Fprintf(w, <span class="hljs-string">"User: %s, Post: %s"</span>, userID, postID)
})
</code></pre>
<h4 id="heading-catch-all-wildcards">Catch-All Wildcards</h4>
<p>The last wildcard in a pattern can optionally match all remaining path segments by having its name end in ...:</p>
<pre><code class="lang-go">mux.HandleFunc(<span class="hljs-string">"GET /files/{path...}"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    filePath := r.PathValue(<span class="hljs-string">"path"</span>)
    <span class="hljs-comment">// filePath contains everything after /files/</span>
    fmt.Fprintf(w, <span class="hljs-string">"File path: %s"</span>, filePath)
})
</code></pre>
<h3 id="heading-3-pattern-precedence-rules">3. Pattern Precedence Rules</h3>
<p>When route patterns overlap, Go's servemux needs to decide which pattern takes precedence so it can dispatch the request to the appropriate handler. The rule for this is very neat and succinct: the most specific route pattern wins.</p>
<p>Examples of precedence:</p>
<pre><code class="lang-go"><span class="hljs-comment">// These patterns are registered in any order</span>
mux.HandleFunc(<span class="hljs-string">"GET /posts/latest"</span>, handleLatest)    <span class="hljs-comment">// More specific</span>
mux.HandleFunc(<span class="hljs-string">"GET /posts/{id}"</span>, handlePost)        <span class="hljs-comment">// Less specific</span>
mux.HandleFunc(<span class="hljs-string">"GET /posts/{id}/edit"</span>, handleEdit)   <span class="hljs-comment">// More specific than /posts/{id}</span>

<span class="hljs-comment">// Request routing:</span>
<span class="hljs-comment">// GET /posts/latest    → handleLatest (exact match wins)</span>
<span class="hljs-comment">// GET /posts/123       → handlePost</span>
<span class="hljs-comment">// GET /posts/123/edit  → handleEdit</span>
</code></pre>
<h3 id="heading-4-exact-path-matching">4. Exact Path Matching</h3>
<p>What if we only want to allow EXACT matches? Well, we can now do that using {$} at the end of the route:</p>
<pre><code class="lang-go">mux.HandleFunc(<span class="hljs-string">"GET /users/{$}"</span>, listUsers)    <span class="hljs-comment">// Only matches /users</span>
mux.HandleFunc(<span class="hljs-string">"GET /users/{id}"</span>, getUser)     <span class="hljs-comment">// Matches /users/123, etc.</span>
</code></pre>
<h2 id="heading-practical-examples">Practical Examples</h2>
<h3 id="heading-building-a-restful-api">Building a RESTful API</h3>
<p>Here's a complete example of a RESTful API using only <code>net/http</code>:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"encoding/json"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"sync"</span>
)

<span class="hljs-keyword">type</span> Task <span class="hljs-keyword">struct</span> {
    ID          <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"id"`</span>
    Title       <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"title"`</span>
    Completed   <span class="hljs-keyword">bool</span>   <span class="hljs-string">`json:"completed"`</span>
}

<span class="hljs-keyword">type</span> TaskStore <span class="hljs-keyword">struct</span> {
    mu    sync.RWMutex
    tasks <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]Task
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    store := &amp;TaskStore{
        tasks: <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]Task),
    }

    mux := http.NewServeMux()

    <span class="hljs-comment">// Task routes</span>
    mux.HandleFunc(<span class="hljs-string">"GET /api/tasks"</span>, store.listTasks)
    mux.HandleFunc(<span class="hljs-string">"POST /api/tasks"</span>, store.createTask)
    mux.HandleFunc(<span class="hljs-string">"GET /api/tasks/{id}"</span>, store.getTask)
    mux.HandleFunc(<span class="hljs-string">"PUT /api/tasks/{id}"</span>, store.updateTask)
    mux.HandleFunc(<span class="hljs-string">"DELETE /api/tasks/{id}"</span>, store.deleteTask)

    <span class="hljs-comment">// Health check</span>
    mux.HandleFunc(<span class="hljs-string">"GET /health"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        w.WriteHeader(http.StatusOK)
        json.NewEncoder(w).Encode(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">string</span>{<span class="hljs-string">"status"</span>: <span class="hljs-string">"healthy"</span>})
    })

    fmt.Println(<span class="hljs-string">"Server starting on :8080"</span>)
    http.ListenAndServe(<span class="hljs-string">":8080"</span>, mux)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *TaskStore)</span> <span class="hljs-title">listTasks</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    s.mu.RLock()
    <span class="hljs-keyword">defer</span> s.mu.RUnlock()

    tasks := <span class="hljs-built_in">make</span>([]Task, <span class="hljs-number">0</span>, <span class="hljs-built_in">len</span>(s.tasks))
    <span class="hljs-keyword">for</span> _, task := <span class="hljs-keyword">range</span> s.tasks {
        tasks = <span class="hljs-built_in">append</span>(tasks, task)
    }

    w.Header().Set(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"application/json"</span>)
    json.NewEncoder(w).Encode(tasks)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *TaskStore)</span> <span class="hljs-title">createTask</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    <span class="hljs-keyword">var</span> task Task
    <span class="hljs-keyword">if</span> err := json.NewDecoder(r.Body).Decode(&amp;task); err != <span class="hljs-literal">nil</span> {
        http.Error(w, <span class="hljs-string">"Invalid request body"</span>, http.StatusBadRequest)
        <span class="hljs-keyword">return</span>
    }

    s.mu.Lock()
    task.ID = fmt.Sprintf(<span class="hljs-string">"%d"</span>, <span class="hljs-built_in">len</span>(s.tasks)+<span class="hljs-number">1</span>)
    s.tasks[task.ID] = task
    s.mu.Unlock()

    w.Header().Set(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"application/json"</span>)
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(task)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *TaskStore)</span> <span class="hljs-title">getTask</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    id := r.PathValue(<span class="hljs-string">"id"</span>)

    s.mu.RLock()
    task, exists := s.tasks[id]
    s.mu.RUnlock()

    <span class="hljs-keyword">if</span> !exists {
        http.Error(w, <span class="hljs-string">"Task not found"</span>, http.StatusNotFound)
        <span class="hljs-keyword">return</span>
    }

    w.Header().Set(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"application/json"</span>)
    json.NewEncoder(w).Encode(task)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *TaskStore)</span> <span class="hljs-title">updateTask</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    id := r.PathValue(<span class="hljs-string">"id"</span>)

    <span class="hljs-keyword">var</span> updates Task
    <span class="hljs-keyword">if</span> err := json.NewDecoder(r.Body).Decode(&amp;updates); err != <span class="hljs-literal">nil</span> {
        http.Error(w, <span class="hljs-string">"Invalid request body"</span>, http.StatusBadRequest)
        <span class="hljs-keyword">return</span>
    }

    s.mu.Lock()
    task, exists := s.tasks[id]
    <span class="hljs-keyword">if</span> !exists {
        s.mu.Unlock()
        http.Error(w, <span class="hljs-string">"Task not found"</span>, http.StatusNotFound)
        <span class="hljs-keyword">return</span>
    }

    task.Title = updates.Title
    task.Completed = updates.Completed
    s.tasks[id] = task
    s.mu.Unlock()

    w.Header().Set(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"application/json"</span>)
    json.NewEncoder(w).Encode(task)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *TaskStore)</span> <span class="hljs-title">deleteTask</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    id := r.PathValue(<span class="hljs-string">"id"</span>)

    s.mu.Lock()
    _, exists := s.tasks[id]
    <span class="hljs-keyword">if</span> !exists {
        s.mu.Unlock()
        http.Error(w, <span class="hljs-string">"Task not found"</span>, http.StatusNotFound)
        <span class="hljs-keyword">return</span>
    }

    <span class="hljs-built_in">delete</span>(s.tasks, id)
    s.mu.Unlock()

    w.WriteHeader(http.StatusNoContent)
}
</code></pre>
<h3 id="heading-sub-routing-and-api-versioning">Sub-routing and API Versioning</h3>
<p>Sub-routing allows us to group common routes. While <code>ServeMux</code> doesn't have built-in sub-router support like some third-party libraries, you can achieve similar functionality:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// API v1 routes</span>
    v1 := http.NewServeMux()
    v1.HandleFunc(<span class="hljs-string">"GET /users"</span>, v1ListUsers)
    v1.HandleFunc(<span class="hljs-string">"GET /users/{id}"</span>, v1GetUser)

    <span class="hljs-comment">// API v2 routes</span>
    v2 := http.NewServeMux()
    v2.HandleFunc(<span class="hljs-string">"GET /users"</span>, v2ListUsers)
    v2.HandleFunc(<span class="hljs-string">"GET /users/{id}"</span>, v2GetUser)
    v2.HandleFunc(<span class="hljs-string">"GET /users/{id}/profile"</span>, v2GetUserProfile) <span class="hljs-comment">// New in v2</span>

    <span class="hljs-comment">// Main router</span>
    mux := http.NewServeMux()
    mux.Handle(<span class="hljs-string">"/api/v1/"</span>, http.StripPrefix(<span class="hljs-string">"/api/v1"</span>, v1))
    mux.Handle(<span class="hljs-string">"/api/v2/"</span>, http.StripPrefix(<span class="hljs-string">"/api/v2"</span>, v2))

    http.ListenAndServe(<span class="hljs-string">":8080"</span>, mux)
}
</code></pre>
<h3 id="heading-middleware-pattern">Middleware Pattern</h3>
<p>While <code>ServeMux</code> doesn't have built-in middleware support, you can implement it using the standard handler pattern:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Middleware type</span>
<span class="hljs-keyword">type</span> Middleware <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(http.Handler)</span> <span class="hljs-title">http</span>.<span class="hljs-title">Handler</span></span>

<span class="hljs-comment">// Logger middleware</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Logger</span><span class="hljs-params">(next http.Handler)</span> <span class="hljs-title">http</span>.<span class="hljs-title">Handler</span></span> {
    <span class="hljs-keyword">return</span> http.HandlerFunc(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        start := time.Now()
        next.ServeHTTP(w, r)
        log.Printf(<span class="hljs-string">"%s %s %v"</span>, r.Method, r.URL.Path, time.Since(start))
    })
}

<span class="hljs-comment">// CORS middleware</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">CORS</span><span class="hljs-params">(next http.Handler)</span> <span class="hljs-title">http</span>.<span class="hljs-title">Handler</span></span> {
    <span class="hljs-keyword">return</span> http.HandlerFunc(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
        w.Header().Set(<span class="hljs-string">"Access-Control-Allow-Origin"</span>, <span class="hljs-string">"*"</span>)
        w.Header().Set(<span class="hljs-string">"Access-Control-Allow-Methods"</span>, <span class="hljs-string">"GET, POST, PUT, DELETE, OPTIONS"</span>)
        w.Header().Set(<span class="hljs-string">"Access-Control-Allow-Headers"</span>, <span class="hljs-string">"Content-Type, Authorization"</span>)

        <span class="hljs-keyword">if</span> r.Method == <span class="hljs-string">"OPTIONS"</span> {
            w.WriteHeader(http.StatusOK)
            <span class="hljs-keyword">return</span>
        }

        next.ServeHTTP(w, r)
    })
}

<span class="hljs-comment">// Apply middleware</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">applyMiddleware</span><span class="hljs-params">(handler http.Handler, middlewares ...Middleware)</span> <span class="hljs-title">http</span>.<span class="hljs-title">Handler</span></span> {
    <span class="hljs-keyword">for</span> i := <span class="hljs-built_in">len</span>(middlewares) - <span class="hljs-number">1</span>; i &gt;= <span class="hljs-number">0</span>; i-- {
        handler = middlewares[i](handler)
    }
    <span class="hljs-keyword">return</span> handler
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    mux := http.NewServeMux()
    mux.HandleFunc(<span class="hljs-string">"GET /api/users"</span>, listUsers)

    <span class="hljs-comment">// Apply middleware to the entire mux</span>
    handler := applyMiddleware(mux, Logger, CORS)

    http.ListenAndServe(<span class="hljs-string">":8080"</span>, handler)
}
</code></pre>
<h2 id="heading-migration-considerations">Migration Considerations</h2>
<h3 id="heading-when-to-use-standard-library">When to Use Standard Library</h3>
<p>The enhanced <code>ServeMux</code> is ideal when:</p>
<ul>
<li><p>Building simple to moderate complexity APIs</p>
</li>
<li><p>Minimizing dependencies is a priority</p>
</li>
<li><p>Standard routing patterns suffice</p>
</li>
<li><p>You don't need advanced features like route groups or complex middleware chains</p>
</li>
</ul>
<h3 id="heading-when-to-consider-third-party-routers">When to Consider Third-Party Routers</h3>
<p>Routers like gorilla/mux still provide more capabilities than the standard library. Consider alternatives when you need:</p>
<ul>
<li><p>Regular expression routing</p>
</li>
<li><p>Advanced middleware management</p>
</li>
<li><p>Route grouping with shared middleware</p>
</li>
<li><p>Request/response helpers</p>
</li>
<li><p>More sophisticated parameter validation</p>
</li>
</ul>
<h2 id="heading-performance-considerations">Performance Considerations</h2>
<p>The reference implementation for this proposal matches requests about as fast as the current ServeMux on Julien Schmidt's static benchmark. The Go team prioritized correctness and maintainability over raw performance, understanding that for typical servers, that usually access some storage backend over the network, I'd guess the matching time is negligible.</p>
<h2 id="heading-best-practices">Best Practices</h2>
<h3 id="heading-1-pattern-organization">1. Pattern Organization</h3>
<pre><code class="lang-go"><span class="hljs-comment">// Group related patterns together</span>
mux.HandleFunc(<span class="hljs-string">"GET /api/users"</span>, listUsers)
mux.HandleFunc(<span class="hljs-string">"POST /api/users"</span>, createUser)
mux.HandleFunc(<span class="hljs-string">"GET /api/users/{id}"</span>, getUser)
mux.HandleFunc(<span class="hljs-string">"PUT /api/users/{id}"</span>, updateUser)
mux.HandleFunc(<span class="hljs-string">"DELETE /api/users/{id}"</span>, deleteUser)

<span class="hljs-comment">// Not scattered throughout the code</span>
</code></pre>
<h3 id="heading-2-use-specific-patterns">2. Use Specific Patterns</h3>
<p>Just because Go's servemux supports overlapping routes, it doesn't mean that you should use them! Keep patterns clear and avoid unnecessary overlaps.</p>
<h3 id="heading-3-explicit-method-specification">3. Explicit Method Specification</h3>
<p>Always specify HTTP methods for better API clarity:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Good</span>
mux.HandleFunc(<span class="hljs-string">"GET /users"</span>, listUsers)
mux.HandleFunc(<span class="hljs-string">"POST /users"</span>, createUser)

<span class="hljs-comment">// Less clear</span>
mux.HandleFunc(<span class="hljs-string">"/users"</span>, handleUsers) <span class="hljs-comment">// Handles all methods</span>
</code></pre>
<h3 id="heading-4-error-handling">4. Error Handling</h3>
<p>The enhanced router automatically handles 405 Method Not Allowed, but you should still handle 404s and other errors appropriately:</p>
<pre><code class="lang-go">mux.HandleFunc(<span class="hljs-string">"/"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(w http.ResponseWriter, r *http.Request)</span></span> {
    <span class="hljs-comment">// Catch-all for unmatched routes</span>
    http.NotFound(w, r)
})
</code></pre>
<h2 id="heading-gotchas-and-edge-cases">Gotchas and Edge Cases</h2>
<h3 id="heading-1-go-version-in-gomod">1. Go Version in go.mod</h3>
<p>Enhanced routing patterns introduced in 1.22 do not work in 1.23 unless go mod has go version explicitly declared. Always ensure your <code>go.mod</code> specifies the correct version:</p>
<pre><code class="lang-go">module myapp

<span class="hljs-keyword">go</span> <span class="hljs-number">1.22</span>
</code></pre>
<h3 id="heading-2-pattern-conflicts">2. Pattern Conflicts</h3>
<p>There is a potential edge case where you have two overlapping route patterns but neither one is obviously more specific than the other. The server will panic at startup if conflicts are detected:</p>
<pre><code class="lang-go"><span class="hljs-comment">// This will panic!</span>
mux.HandleFunc(<span class="hljs-string">"GET /posts/new/{id}"</span>, handler1)
mux.HandleFunc(<span class="hljs-string">"GET /posts/{author}/latest"</span>, handler2)
<span class="hljs-comment">// Both match /posts/new/latest</span>
</code></pre>
<h3 id="heading-3-trailing-slashes">3. Trailing Slashes</h3>
<p>Be aware of how trailing slashes affect routing:</p>
<pre><code class="lang-go">mux.HandleFunc(<span class="hljs-string">"GET /users"</span>, listUsers)     <span class="hljs-comment">// Matches /users only</span>
mux.HandleFunc(<span class="hljs-string">"GET /users/"</span>, listAllUsers) <span class="hljs-comment">// Matches /users/ and /users/*</span>
mux.HandleFunc(<span class="hljs-string">"GET /users/{$}"</span>, exactMatch) <span class="hljs-comment">// Matches /users/ exactly</span>
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Go 1.22's enhanced <code>net/http</code> package represents a significant milestone in the language's evolution. These features let you express common routes as patterns instead of Go code, dramatically simplifying web application development.</p>
<p>For many applications, the standard library now provides all the routing functionality needed, eliminating dependencies and reducing complexity. While third-party routers still have their place for advanced use cases, the gap has narrowed considerably.</p>
<p>The Go team's approach—studying existing solutions, extracting the most-used features, and integrating them thoughtfully—demonstrates their commitment to making Go an excellent choice for building production systems. Whether you're starting a new project or considering a migration, Go's enhanced routing capabilities deserve serious consideration.</p>
<h2 id="heading-additional-resources">Additional Resources</h2>
<ul>
<li><p><a target="_blank" href="https://go.dev/blog/routing-enhancements">Official Go Blog: Routing Enhancements for Go 1.22</a></p>
</li>
<li><p><a target="_blank" href="https://pkg.go.dev/net/http#ServeMux">net/http.ServeMux Documentation</a></p>
</li>
<li><p><a target="_blank" href="https://go.dev/doc/go1.22">Go 1.22 Release Notes</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/golang/go/issues/61410">Proposal: Enhanced ServeMux Routing</a></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>