<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Writings on Tachera W Sasi</title><link>https://tacherasasi.github.io/posts/</link><description>Recent content in Writings on Tachera W Sasi</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Mon, 04 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tacherasasi.github.io/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Why io.Copy Is One of Go's Most Powerful Functions</title><link>https://tacherasasi.github.io/posts/why-io-copy-is-one-of-gos-most-powerful-functions-9222940b2358/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/why-io-copy-is-one-of-gos-most-powerful-functions-9222940b2358/</guid><description>&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*Nfqq3EkbvVg74Hi1Avbo3A.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Most Go developers use io.Copy() early in their journey.&lt;/p&gt;
&lt;p&gt;Usually for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;copying files&lt;/li&gt;
&lt;li&gt;downloading HTTP responses&lt;/li&gt;
&lt;li&gt;writing buffers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But many never realize this tiny function is sitting at the center of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;proxies&lt;/li&gt;
&lt;li&gt;reverse proxies&lt;/li&gt;
&lt;li&gt;SSH tunnels&lt;/li&gt;
&lt;li&gt;database gateways&lt;/li&gt;
&lt;li&gt;streaming systems&lt;/li&gt;
&lt;li&gt;TCP forwarders&lt;/li&gt;
&lt;li&gt;container runtimes&lt;/li&gt;
&lt;li&gt;load balancers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;is deceptively powerful.&lt;/p&gt;
&lt;p&gt;Especially when you realize:&lt;em&gt;in Go, sockets are just streams.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And streams are just:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;io.Reader&lt;/li&gt;
&lt;li&gt;io.Writer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once that clicks, networking becomes MUCH simpler.&lt;/p&gt;
&lt;h3 id="the-beautiful-simplicity"&gt;The Beautiful Simplicity&lt;/h3&gt;
&lt;p&gt;Consider this tiny PostgreSQL TCP proxy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;io&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;log&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;net&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LISTEN_ADDR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;:5433&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PG_ADDR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;localhost:5432&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ln&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;tcp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LISTEN_ADDR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ln&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;tcp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PG_ADDR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;defer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That’s it.&lt;/p&gt;
&lt;p&gt;No packet parsing.&lt;br&gt;
No protocol handling.&lt;br&gt;
No PostgreSQL driver.&lt;/p&gt;
&lt;p&gt;Yet this already works as a real TCP proxy.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;Because TCP connections in Go implement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;io.Reader&lt;/li&gt;
&lt;li&gt;io.Writer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you can read from them&lt;/li&gt;
&lt;li&gt;write to them&lt;/li&gt;
&lt;li&gt;stream data between them&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="understanding-the-core-idea"&gt;Understanding the Core Idea&lt;/h3&gt;
&lt;p&gt;This line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;means:&lt;em&gt;continuously read bytes from&lt;/em&gt; &lt;em&gt;client and write them into&lt;/em&gt; &lt;em&gt;server&lt;/em&gt; And this line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;means:&lt;em&gt;continuously read bytes from&lt;/em&gt; &lt;em&gt;server and write them into&lt;/em&gt; &lt;em&gt;client&lt;/em&gt; Together they create:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bidirectional communication&lt;/li&gt;
&lt;li&gt;full duplex streaming&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="full-duplex-communication"&gt;Full Duplex Communication&lt;/h3&gt;
&lt;p&gt;Most network protocols are two-way conversations.&lt;/p&gt;
&lt;p&gt;The client sends data.&lt;br&gt;
The server responds.&lt;br&gt;
Both sides can speak independently.&lt;/p&gt;
&lt;p&gt;Your browser and a web server.&lt;br&gt;
SSH client and SSH server.&lt;br&gt;
PostgreSQL client and PostgreSQL server.&lt;/p&gt;
&lt;p&gt;All of them rely on duplex communication.&lt;/p&gt;
&lt;p&gt;Visually:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;client &amp;lt;=========&amp;gt; proxy &amp;lt;=========&amp;gt; server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Your proxy becomes a pipe.&lt;/p&gt;
&lt;p&gt;And io.Copy() is what keeps bytes flowing through that pipe.&lt;/p&gt;
&lt;h3 id="why-goroutines-matter-here"&gt;Why Goroutines Matter Here&lt;/h3&gt;
&lt;p&gt;This part is critical:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Without the goroutine, communication would block.&lt;/p&gt;
&lt;p&gt;You need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;one stream for client -&amp;gt; server&lt;/li&gt;
&lt;li&gt;another stream for server -&amp;gt; client&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;simultaneously.&lt;/p&gt;
&lt;p&gt;That’s what enables real-time two-way communication.&lt;/p&gt;
&lt;p&gt;The goroutine handles one direction.&lt;br&gt;
The main goroutine handles the other.&lt;/p&gt;
&lt;p&gt;Tiny code.&lt;br&gt;
Massive capability.&lt;/p&gt;
&lt;h3 id="sockets-are-just-streams"&gt;Sockets Are Just Streams&lt;/h3&gt;
&lt;p&gt;This is one of the most important concepts in systems programming.&lt;/p&gt;
&lt;p&gt;In Go:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Conn&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A socket behaves like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a file&lt;/li&gt;
&lt;li&gt;a buffer&lt;/li&gt;
&lt;li&gt;stdin/stdout&lt;/li&gt;
&lt;li&gt;an HTTP body&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything becomes composable through interfaces.&lt;/p&gt;
&lt;p&gt;This is why Go networking feels elegant.&lt;/p&gt;
&lt;p&gt;You stop thinking:&lt;em&gt;“I’m working with TCP packets”&lt;/em&gt; and start thinking:&lt;em&gt;“I’m moving streams between readers and writers”&lt;/em&gt; That abstraction is incredibly powerful.&lt;/p&gt;
&lt;h3 id="memory-efficiency"&gt;Memory Efficiency&lt;/h3&gt;
&lt;p&gt;Another reason io.Copy() is excellent:&lt;br&gt;
it streams data incrementally.&lt;/p&gt;
&lt;p&gt;It does NOT:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;load everything into memory&lt;/li&gt;
&lt;li&gt;allocate giant buffers&lt;/li&gt;
&lt;li&gt;wait for full payloads&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Internally, it uses reusable buffers and streams chunks continuously.&lt;/p&gt;
&lt;p&gt;This makes it ideal for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;large files&lt;/li&gt;
&lt;li&gt;long-lived connections&lt;/li&gt;
&lt;li&gt;proxies&lt;/li&gt;
&lt;li&gt;streaming systems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even multi-GB transfers remain memory efficient.&lt;/p&gt;
&lt;h3 id="this-pattern-exists-everywhere"&gt;This Pattern Exists Everywhere&lt;/h3&gt;
&lt;p&gt;Once you notice it, you’ll see this pattern everywhere.&lt;/p&gt;
&lt;h3 id="reverse-proxies"&gt;Reverse Proxies&lt;/h3&gt;
&lt;p&gt;Forward HTTP traffic between clients and servers.&lt;/p&gt;
&lt;h3 id="ssh-tunnels"&gt;SSH Tunnels&lt;/h3&gt;
&lt;p&gt;Forward encrypted streams between hosts.&lt;/p&gt;
&lt;h3 id="database-proxies"&gt;Database Proxies&lt;/h3&gt;
&lt;p&gt;Like PgBouncer or custom DB gateways.&lt;/p&gt;
&lt;h3 id="vpns"&gt;VPNs&lt;/h3&gt;
&lt;p&gt;Moving packets through encrypted streams.&lt;/p&gt;
&lt;h3 id="load-balancers"&gt;Load Balancers&lt;/h3&gt;
&lt;p&gt;Forwarding connections across machines.&lt;/p&gt;
&lt;h3 id="container-systems"&gt;Container Systems&lt;/h3&gt;
&lt;p&gt;Streaming logs, exec sessions, and IPC.&lt;/p&gt;
&lt;p&gt;A huge amount of infrastructure software is basically:&lt;em&gt;carefully managed stream forwarding.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="the-hidden-superpower-of-go"&gt;The Hidden Superpower of Go&lt;/h3&gt;
&lt;p&gt;Go’s standard library encourages composition over complexity.&lt;/p&gt;
&lt;p&gt;Instead of giant networking frameworks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;readers&lt;/li&gt;
&lt;li&gt;writers&lt;/li&gt;
&lt;li&gt;interfaces&lt;/li&gt;
&lt;li&gt;goroutines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;become enough to build real infrastructure.&lt;/p&gt;
&lt;p&gt;This is why Go became dominant in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cloud infrastructure&lt;/li&gt;
&lt;li&gt;Kubernetes tooling&lt;/li&gt;
&lt;li&gt;networking software&lt;/li&gt;
&lt;li&gt;distributed systems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The primitives are small.&lt;br&gt;
But they scale extremely far.&lt;/p&gt;
&lt;h3 id="final-thoughts"&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;io.Copy() looks boring at first.&lt;/p&gt;
&lt;p&gt;But behind that tiny function is a core systems programming idea:&lt;em&gt;move streams between abstractions efficiently&lt;/em&gt; Once you deeply understand that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;proxies make sense&lt;/li&gt;
&lt;li&gt;tunnels make sense&lt;/li&gt;
&lt;li&gt;reverse proxies make sense&lt;/li&gt;
&lt;li&gt;networking becomes less magical&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And you start realizing:&lt;br&gt;
some of the most powerful infrastructure on the internet is built from surprisingly small primitives.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=9222940b2358" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/why-io-copy-is-one-of-gos-most-powerful-functions-9222940b2358?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>The One-Word Bug That Broke Our Worker for 6 Days</title><link>https://tacherasasi.github.io/posts/the-one-word-bug-that-broke-our-worker-for-6-days-9cadd534ee77/</link><pubDate>Tue, 28 Apr 2026 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/the-one-word-bug-that-broke-our-worker-for-6-days-9cadd534ee77/</guid><description>&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*CtVFn53iNGx3lQ2MEe3JKw.png" alt=""&gt;&lt;/p&gt;
&lt;h3 id="for-six-days-our-system-was-lying-to-us"&gt;For six days, our system was lying to us&lt;/h3&gt;
&lt;p&gt;Not crashing. Not failing loudly. Just quietly pretending everything was fine while doing absolutely nothing.&lt;/p&gt;
&lt;p&gt;And the cause?&lt;/p&gt;
&lt;p&gt;One wrong word in a shell script.&lt;/p&gt;
&lt;h3 id="everything-looked-fine"&gt;Everything Looked Fine&lt;/h3&gt;
&lt;p&gt;We run a pretty standard setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Go API (core)&lt;/li&gt;
&lt;li&gt;A background worker (minion)&lt;/li&gt;
&lt;li&gt;Redis for queues&lt;/li&gt;
&lt;li&gt;Docker Compose holding it all together&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We recently moved bulk SMS processing into the worker using Asynq. Clean separation, better scalability, the usual win.&lt;/p&gt;
&lt;p&gt;We deployed.&lt;/p&gt;
&lt;p&gt;The API responded perfectly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;200 OK
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;bulk SMS queued successfully
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Except… no SMS was ever sent.&lt;/p&gt;
&lt;h3 id="the-worker-was-dead-silent"&gt;The Worker Was Dead Silent&lt;/h3&gt;
&lt;p&gt;First instinct: check the worker logs.&lt;/p&gt;
&lt;p&gt;Nothing.&lt;/p&gt;
&lt;p&gt;No errors. No panics. No trace of the task ever being picked up.&lt;/p&gt;
&lt;p&gt;Just one quiet warning after retries were exhausted.&lt;/p&gt;
&lt;p&gt;Meanwhile, everything else worked:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;login alerts ✅&lt;/li&gt;
&lt;li&gt;email notifications ✅&lt;/li&gt;
&lt;li&gt;scheduled jobs ✅&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Only bulk SMS failed.&lt;/p&gt;
&lt;p&gt;At this point, we did what engineers always do when the signal is weak:&lt;/p&gt;
&lt;p&gt;We guessed.&lt;/p&gt;
&lt;h3 id="we-investigated-the-wrong-things"&gt;We Investigated the Wrong Things&lt;/h3&gt;
&lt;p&gt;We went deep into the stack:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Suspected Redis corruption&lt;/li&gt;
&lt;li&gt;Questioned queue priorities&lt;/li&gt;
&lt;li&gt;Looked for serialization bugs&lt;/li&gt;
&lt;li&gt;Added panic recovery middleware&lt;/li&gt;
&lt;li&gt;Logged full payloads&lt;/li&gt;
&lt;li&gt;Added startup diagnostics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We deployed again.&lt;/p&gt;
&lt;p&gt;Still nothing.&lt;/p&gt;
&lt;p&gt;And this is where most people waste days. We almost did.&lt;/p&gt;
&lt;p&gt;Because we were debugging behavior… not reality.&lt;/p&gt;
&lt;h3 id="the-subtle-clue"&gt;The Subtle Clue&lt;/h3&gt;
&lt;p&gt;There was one tiny inconsistency.&lt;/p&gt;
&lt;p&gt;Our worker used to log:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Worker started
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We had changed it to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Worker started (server + scheduler running)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After deploy, the logs still showed the old message.&lt;/p&gt;
&lt;p&gt;That should have been the end of the investigation right there.&lt;/p&gt;
&lt;p&gt;Our code wasn’t running.&lt;/p&gt;
&lt;h3 id="the-real-problem"&gt;The Real Problem&lt;/h3&gt;
&lt;p&gt;The issue wasn’t in Go.&lt;br&gt;
It wasn’t in Redis.&lt;br&gt;
It wasn’t in queues.&lt;/p&gt;
&lt;p&gt;It was in this script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;docker compose build --no-cache api &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;docker compose up -d --remove-orphans&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And this config:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;core&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;minion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Look closely.&lt;/p&gt;
&lt;p&gt;We were building api.&lt;/p&gt;
&lt;p&gt;There is no api.&lt;/p&gt;
&lt;p&gt;Docker didn’t complain. It didn’t warn. It didn’t fail.&lt;/p&gt;
&lt;p&gt;It just did nothing.&lt;/p&gt;
&lt;p&gt;And then docker compose up happily started containers using old cached images.&lt;/p&gt;
&lt;p&gt;So for six days:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The API sometimes rebuilt&lt;/li&gt;
&lt;li&gt;The worker never rebuilt&lt;/li&gt;
&lt;li&gt;The system kept deploying successfully&lt;/li&gt;
&lt;li&gt;Our fixes never actually shipped&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We were debugging code that wasn’t even running.&lt;/p&gt;
&lt;h3 id="the-fix-was-embarrassing"&gt;The Fix Was Embarrassing&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- docker compose build --no-cache api
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+ docker compose build --no-cache core minion
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That’s it.&lt;/p&gt;
&lt;p&gt;Two words instead of one.&lt;/p&gt;
&lt;p&gt;We deployed again.&lt;/p&gt;
&lt;p&gt;Immediately:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New startup logs appeared&lt;/li&gt;
&lt;li&gt;Bulk SMS tasks were picked up&lt;/li&gt;
&lt;li&gt;Jobs executed in milliseconds&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;System fixed.&lt;/p&gt;
&lt;h3 id="what-this-actually-teaches"&gt;What This Actually Teaches&lt;/h3&gt;
&lt;h3 id="1-if-your-fix-doesnt-change-behavior-assume-it-never-deployed"&gt;1. If your fix doesn’t change behavior, assume it never deployed&lt;/h3&gt;
&lt;p&gt;Stop overthinking.&lt;/p&gt;
&lt;p&gt;Before touching Redis, queues, or concurrency… ask:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“Is my new binary even running?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you skip this, you deserve the hours you’re about to lose.&lt;/p&gt;
&lt;h3 id="2-silent-failures-are-worse-than-crashes"&gt;2. Silent failures are worse than crashes&lt;/h3&gt;
&lt;p&gt;Crashes force action.&lt;/p&gt;
&lt;p&gt;Silent no-ops pass CI, pass deploys, and waste days.&lt;/p&gt;
&lt;p&gt;docker compose build should not succeed when the service doesn’t exist.&lt;/p&gt;
&lt;p&gt;But it does.&lt;/p&gt;
&lt;p&gt;So you have to defend against that yourself.&lt;/p&gt;
&lt;h3 id="3-your-deployment-pipeline-is-part-of-your-codebase"&gt;3. Your deployment pipeline is part of your codebase&lt;/h3&gt;
&lt;p&gt;People treat scripts like they’re disposable.&lt;/p&gt;
&lt;p&gt;They’re not.&lt;/p&gt;
&lt;p&gt;That one-line deploy.sh caused more damage than any bug in the Go code.&lt;/p&gt;
&lt;p&gt;Review it. Test it. Break it intentionally.&lt;/p&gt;
&lt;h3 id="4-logging-is-not-optional-for-workers"&gt;4. Logging is not optional for workers&lt;/h3&gt;
&lt;p&gt;If your worker can fail quietly, it will.&lt;/p&gt;
&lt;p&gt;You need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;startup fingerprints (commit hash, build time)&lt;/li&gt;
&lt;li&gt;task-level logs&lt;/li&gt;
&lt;li&gt;explicit success/failure visibility&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Otherwise, you’re blind.&lt;/p&gt;
&lt;h3 id="5-rename-things-properly-or-dont-rename-them-at-all"&gt;5. Rename things properly or don’t rename them at all&lt;/h3&gt;
&lt;p&gt;api became core.&lt;/p&gt;
&lt;p&gt;The script didn’t.&lt;/p&gt;
&lt;p&gt;That mismatch cost six days.&lt;/p&gt;
&lt;p&gt;Refactors don’t end when the code compiles.&lt;/p&gt;
&lt;h3 id="the-real-lesson"&gt;The Real Lesson&lt;/h3&gt;
&lt;p&gt;This wasn’t a debugging failure.&lt;/p&gt;
&lt;p&gt;It was a discipline failure.&lt;/p&gt;
&lt;p&gt;We trusted the pipeline without verifying it.&lt;br&gt;
We chased complex explanations before ruling out simple ones.&lt;br&gt;
We debugged symptoms instead of checking fundamentals.&lt;/p&gt;
&lt;p&gt;And that’s how a one-word mistake beats experienced engineers.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=9cadd534ee77" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/the-one-word-bug-that-broke-our-worker-for-6-days-9cadd534ee77?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>What Building malloc Taught Me About Memory</title><link>https://tacherasasi.github.io/posts/what-building-malloc-taught-me-about-memory-57eae5b35166/</link><pubDate>Fri, 13 Mar 2026 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/what-building-malloc-taught-me-about-memory-57eae5b35166/</guid><description>&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*2dOqSEchtIkDv2Fma09rOA.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Modern developers rarely think about memory.&lt;/p&gt;
&lt;p&gt;We call malloc, new, or let a garbage collector handle everything. Most of the time that’s perfectly fine. But recently I decided to dig deeper and implement a very small version of malloc myself.&lt;/p&gt;
&lt;p&gt;Not to replace real allocators. Just to understand what actually happens under the hood.&lt;/p&gt;
&lt;p&gt;The result was surprisingly simple and incredibly educational.&lt;/p&gt;
&lt;p&gt;This post walks through the core ideas.&lt;/p&gt;
&lt;h3 id="why-understanding-memory-still-matters"&gt;Why Understanding Memory Still Matters&lt;/h3&gt;
&lt;p&gt;Most software today runs on layers of abstraction.&lt;/p&gt;
&lt;p&gt;Frameworks → languages → runtimes → operating systems.&lt;/p&gt;
&lt;p&gt;That’s powerful, but it also means many developers never see the &lt;strong&gt;foundations&lt;/strong&gt; those layers are built on.&lt;/p&gt;
&lt;p&gt;Understanding memory allocation helps with:&lt;/p&gt;
&lt;p&gt;• writing faster software&lt;br&gt;
• debugging memory issues&lt;br&gt;
• understanding how runtimes work&lt;br&gt;
• building systems tools&lt;br&gt;
• designing better APIs&lt;/p&gt;
&lt;p&gt;Even if you work in higher level languages like &lt;strong&gt;Go&lt;/strong&gt; , these concepts still power everything underneath.&lt;/p&gt;
&lt;h3 id="step-1-where-malloc-gets-memory"&gt;Step 1: Where malloc Gets Memory&lt;/h3&gt;
&lt;p&gt;When a program runs, memory roughly looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Code
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Globals
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Heap ← grows upward
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Stack ← grows downward
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;malloc allocates from the &lt;strong&gt;heap&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Under the hood, the allocator asks the OS for more heap space using a system call like sbrk().&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;void *ptr = sbrk(1024);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This moves the &lt;strong&gt;program break&lt;/strong&gt; forward by 1024 bytes and returns a pointer to the new memory.&lt;/p&gt;
&lt;p&gt;Think of it like extending the end of your program’s heap.&lt;/p&gt;
&lt;h3 id="step-2-the-naive-allocator"&gt;Step 2: The Naive Allocator&lt;/h3&gt;
&lt;p&gt;The simplest possible allocator would just call sbrk.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;void *my_malloc(size_t size) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; return sbrk(size);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But this immediately creates a problem.&lt;/p&gt;
&lt;p&gt;If you later call free, the allocator has &lt;strong&gt;no idea&lt;/strong&gt; :&lt;/p&gt;
&lt;p&gt;• how big the allocation was&lt;br&gt;
• where the next block starts&lt;br&gt;
• whether a block can be reused&lt;/p&gt;
&lt;p&gt;So real allocators store &lt;strong&gt;metadata&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="step-3-the-hidden-header-trick"&gt;Step 3: The Hidden Header Trick&lt;/h3&gt;
&lt;p&gt;Every allocation secretly stores a small header before the memory returned to the user.&lt;/p&gt;
&lt;p&gt;Memory layout:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ HEADER ][ USER MEMORY ]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The header might contain:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;size
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;is_free
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;next_block
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Example structure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;block_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;is_free&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;block_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;block_header_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When the allocator returns memory, it actually returns a pointer &lt;strong&gt;after the header&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;return (void *)(block + 1);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which means the user sees:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ptr → usable memory
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While the allocator still has its metadata.&lt;/p&gt;
&lt;h3 id="step-4-tracking-allocations"&gt;Step 4: Tracking Allocations&lt;/h3&gt;
&lt;p&gt;Blocks are usually tracked using a linked list.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;heap_start
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[block A] → [block B] → [block C]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When malloc runs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;search for a free block big enough&lt;/li&gt;
&lt;li&gt;reuse it if possible&lt;/li&gt;
&lt;li&gt;otherwise request more memory from the OS&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This keeps allocations fast and avoids unnecessary system calls.&lt;/p&gt;
&lt;h3 id="step-5-what-free-actually-does"&gt;Step 5: What free Actually Does&lt;/h3&gt;
&lt;p&gt;One surprising thing: free usually does &lt;strong&gt;not return memory to the OS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Instead it just marks the block as reusable.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[HEADER free=1][memory]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Future allocations can reuse that block instead of requesting more memory.&lt;/p&gt;
&lt;h3 id="what-real-allocators-do-differently"&gt;What Real Allocators Do Differently&lt;/h3&gt;
&lt;p&gt;Real allocators (like the ones used by &lt;strong&gt;glibc&lt;/strong&gt;) add several optimizations:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Block splitting&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Free block: 100 bytes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Need: 20
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Split into:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;20 used
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;80 still free
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Block coalescing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Adjacent free blocks merge together to reduce fragmentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Large allocation handling&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Large allocations often use mmap instead of the heap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thread safety&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Modern allocators maintain thread local arenas for performance.&lt;/p&gt;
&lt;h3 id="what-i-learned-from-this"&gt;What I Learned From This&lt;/h3&gt;
&lt;p&gt;Building even a tiny allocator reinforced a few important lessons.&lt;/p&gt;
&lt;h3 id="abstractions-hide-complexity-but-the-fundamentals-still-matter"&gt;Abstractions hide complexity, but the fundamentals still matter&lt;/h3&gt;
&lt;p&gt;Languages like &lt;strong&gt;Go&lt;/strong&gt; make memory management easier, but the underlying principles are still there.&lt;/p&gt;
&lt;h3 id="performance-often-comes-from-understanding-the-layers-below-you"&gt;Performance often comes from understanding the layers below you&lt;/h3&gt;
&lt;p&gt;Understanding memory layout and allocation patterns helps explain why some code performs better than others.&lt;/p&gt;
&lt;h3 id="systems-knowledge-compounds"&gt;Systems knowledge compounds&lt;/h3&gt;
&lt;p&gt;Once you understand memory allocators, many other topics suddenly make more sense:&lt;/p&gt;
&lt;p&gt;• garbage collectors&lt;br&gt;
• runtime design&lt;br&gt;
• kernel memory management&lt;br&gt;
• high performance servers&lt;/p&gt;
&lt;h3 id="final-thoughts"&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;You don’t need to write your own allocator for production software.&lt;/p&gt;
&lt;p&gt;But building one, even a small version, forces you to think like the runtime.&lt;/p&gt;
&lt;p&gt;And that perspective is valuable.&lt;/p&gt;
&lt;p&gt;The more you understand the layers beneath your tools, the better engineer you become.&lt;/p&gt;
&lt;p&gt;GITHUB =&amp;gt; &lt;a href="https://github.com/tacheraSasi/malloc.git"&gt;https://github.com/tacheraSasi/malloc.git&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=57eae5b35166" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/what-building-malloc-taught-me-about-memory-57eae5b35166?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>What 200+ GitHub Repositories Taught Me</title><link>https://tacherasasi.github.io/posts/what-200-github-repositories-taught-me-d55cf5184e52/</link><pubDate>Tue, 10 Mar 2026 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/what-200-github-repositories-taught-me-d55cf5184e52/</guid><description>&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*nOE2wsJ6INiafWrSkEiN4Q.png" alt=""&gt;Golang Fanatic here.&lt;/p&gt;
&lt;p&gt;After building and experimenting with more than &lt;strong&gt;200 repositories on GitHub&lt;/strong&gt; , I expected to come away with deep insights about architecture, design patterns, and cutting edge technology.&lt;/p&gt;
&lt;p&gt;Instead, the most valuable lessons were much simpler.&lt;/p&gt;
&lt;p&gt;Not the fancy stuff.&lt;br&gt;
Not the hype.&lt;/p&gt;
&lt;p&gt;Just the fundamentals that actually make projects useful.&lt;/p&gt;
&lt;p&gt;Here are the biggest things I learned.&lt;/p&gt;
&lt;h3 id="1-finishing-projects-matters-more-than-starting-them"&gt;1. Finishing projects matters more than starting them&lt;/h3&gt;
&lt;p&gt;Most developers love starting projects.&lt;/p&gt;
&lt;p&gt;New repo.&lt;br&gt;
Clean codebase.&lt;br&gt;
Perfect architecture.&lt;/p&gt;
&lt;p&gt;Everything feels exciting at the beginning.&lt;/p&gt;
&lt;p&gt;But the real value comes from &lt;strong&gt;finishing something&lt;/strong&gt; , even if it’s imperfect.&lt;/p&gt;
&lt;p&gt;When you finish projects you learn things you can never learn from half-built code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;deployment problems&lt;/li&gt;
&lt;li&gt;real-world bugs&lt;/li&gt;
&lt;li&gt;usability issues&lt;/li&gt;
&lt;li&gt;performance bottlenecks&lt;/li&gt;
&lt;li&gt;documentation gaps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Shipping forces reality into your ideas.&lt;/p&gt;
&lt;p&gt;A tiny finished tool is worth more than &lt;strong&gt;20 half-built experiments&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="2-simplicity-beats-cleverness"&gt;2. Simplicity beats cleverness&lt;/h3&gt;
&lt;p&gt;Early on I loved writing clever code.&lt;/p&gt;
&lt;p&gt;Fancy abstractions.&lt;br&gt;
Complex architectures.&lt;br&gt;
Overengineered solutions.&lt;/p&gt;
&lt;p&gt;But after maintaining projects over time, you realize something quickly:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Simple code survives.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Complex code becomes a burden.&lt;/p&gt;
&lt;p&gt;The best tools I built were usually the ones with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fewer dependencies&lt;/li&gt;
&lt;li&gt;simple APIs&lt;/li&gt;
&lt;li&gt;predictable behavior&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Good software often looks boring.&lt;/p&gt;
&lt;p&gt;That is usually a good sign.&lt;/p&gt;
&lt;h3 id="3-documentation-is-not-optional"&gt;3. Documentation is not optional&lt;/h3&gt;
&lt;p&gt;You only realize the importance of documentation when you revisit your own project six months later.&lt;/p&gt;
&lt;p&gt;Suddenly you are staring at your own code wondering:&lt;/p&gt;
&lt;p&gt;“Why did I design it like this?”&lt;/p&gt;
&lt;p&gt;Good documentation does three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Explains &lt;strong&gt;why&lt;/strong&gt; something exists&lt;/li&gt;
&lt;li&gt;Shows &lt;strong&gt;how to use it quickly&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Reduces future confusion&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Even a small README with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;installation&lt;/li&gt;
&lt;li&gt;examples&lt;/li&gt;
&lt;li&gt;project goals&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;makes a massive difference.&lt;/p&gt;
&lt;p&gt;The best projects respect the reader.&lt;/p&gt;
&lt;h3 id="4-small-tools-win"&gt;4. Small tools win&lt;/h3&gt;
&lt;p&gt;Some of my most useful repositories were not full applications.&lt;/p&gt;
&lt;p&gt;They were &lt;strong&gt;tiny tools&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;CLI utilities.&lt;br&gt;
Scripts.&lt;br&gt;
Developer helpers.&lt;/p&gt;
&lt;p&gt;Small tools have advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;easier to maintain&lt;/li&gt;
&lt;li&gt;faster to build&lt;/li&gt;
&lt;li&gt;easier for others to adopt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A focused tool that solves &lt;strong&gt;one specific problem well&lt;/strong&gt; can be more impactful than a large system trying to do everything.&lt;/p&gt;
&lt;h3 id="5-consistency-beats-motivation"&gt;5. Consistency beats motivation&lt;/h3&gt;
&lt;p&gt;Motivation is unreliable.&lt;/p&gt;
&lt;p&gt;Some days you want to code for 10 hours.&lt;br&gt;
Other days you want to do nothing.&lt;/p&gt;
&lt;p&gt;But building many projects taught me that progress mostly comes from &lt;strong&gt;consistency&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Small steps every week:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;improving a tool&lt;/li&gt;
&lt;li&gt;fixing a bug&lt;/li&gt;
&lt;li&gt;cleaning up documentation&lt;/li&gt;
&lt;li&gt;finishing a feature&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Over time this compounds into a large body of work.&lt;/p&gt;
&lt;h3 id="6-tooling-matters-more-than-trends"&gt;6. Tooling matters more than trends&lt;/h3&gt;
&lt;p&gt;Developers chase new frameworks constantly.&lt;/p&gt;
&lt;p&gt;But the projects that lasted the longest in my repositories were built with tools that had:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;great tooling&lt;/li&gt;
&lt;li&gt;fast iteration&lt;/li&gt;
&lt;li&gt;simple deployment&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When the tooling is good, you build faster.&lt;/p&gt;
&lt;p&gt;When it’s bad, the project slowly dies.&lt;/p&gt;
&lt;h3 id="7-your-portfolio-becomes-your-thinking"&gt;7. Your portfolio becomes your thinking&lt;/h3&gt;
&lt;p&gt;Looking back at hundreds of repositories is like looking at a timeline of your thinking.&lt;/p&gt;
&lt;p&gt;You see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bad decisions&lt;/li&gt;
&lt;li&gt;experiments&lt;/li&gt;
&lt;li&gt;abandoned ideas&lt;/li&gt;
&lt;li&gt;improvements over time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that is the real value.&lt;/p&gt;
&lt;p&gt;Not every repo needs to succeed.&lt;/p&gt;
&lt;p&gt;Each one captures a moment where you tried to build something and learned from it.&lt;/p&gt;
&lt;h3 id="final-thought"&gt;Final Thought&lt;/h3&gt;
&lt;p&gt;Building many repositories doesn’t make you a better developer by itself.&lt;/p&gt;
&lt;p&gt;But &lt;strong&gt;finishing projects&lt;/strong&gt; , learning from them, and continuously improving your approach does.&lt;/p&gt;
&lt;p&gt;In the end, the biggest lesson from 200+ repositories is simple:&lt;/p&gt;
&lt;p&gt;Build things.&lt;br&gt;
Finish them.&lt;br&gt;
Keep going.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=d55cf5184e52" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/what-200-github-repositories-taught-me-d55cf5184e52?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>After Using 10+ Languages, Go Is Still My Favorite for Backend Systems</title><link>https://tacherasasi.github.io/posts/after-using-10-languages-go-is-still-my-favorite-for-backend-systems-9148165fa6dc/</link><pubDate>Mon, 09 Mar 2026 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/after-using-10-languages-go-is-still-my-favorite-for-backend-systems-9148165fa6dc/</guid><description>&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*AYRooeSNqJ_pq64abMMcsg.png" alt=""&gt;Golang Fanatic Here&lt;/p&gt;
&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;
&lt;p&gt;Over the past few years I’ve experimented with a lot of programming languages.&lt;/p&gt;
&lt;p&gt;PHP, JavaScript, TypeScript, Python, Java, Go, Rust, C, C++, and several others. I enjoy exploring languages because every language teaches you something about how software should be built.&lt;/p&gt;
&lt;p&gt;Some languages are great for rapid prototyping.&lt;br&gt;
Some shine in performance-critical environments.&lt;br&gt;
Others dominate specific ecosystems.&lt;/p&gt;
&lt;p&gt;But when it comes to &lt;strong&gt;building backend systems&lt;/strong&gt; , I keep coming back to Go.&lt;/p&gt;
&lt;p&gt;Not because it’s the most advanced language.&lt;br&gt;
Not because it has the most features.&lt;/p&gt;
&lt;p&gt;Actually, the opposite.&lt;/p&gt;
&lt;p&gt;Go wins because it focuses on &lt;strong&gt;simplicity, performance, and practical tooling&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="1-simplicity"&gt;1. Simplicity&lt;/h3&gt;
&lt;p&gt;One of Go’s biggest strengths is that the language is intentionally small.&lt;/p&gt;
&lt;p&gt;There are no complex generics systems like in C++.&lt;br&gt;
No endless abstractions like in some object-oriented languages.&lt;br&gt;
No massive meta-programming layers.&lt;/p&gt;
&lt;p&gt;At first this feels limiting.&lt;/p&gt;
&lt;p&gt;But after building real systems, you realize something important: &lt;strong&gt;most backend problems don’t need complex language features.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;They need:&lt;/p&gt;
&lt;p&gt;• readable code&lt;br&gt;
• predictable behavior&lt;br&gt;
• easy maintenance&lt;/p&gt;
&lt;p&gt;Go enforces a style that makes codebases easier to understand, especially in teams.&lt;/p&gt;
&lt;p&gt;There’s a reason many companies adopt Go for large services: &lt;strong&gt;engineers can read each other’s code easily.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="2-concurrency-that-actually-feels-simple"&gt;2. Concurrency That Actually Feels Simple&lt;/h3&gt;
&lt;p&gt;Modern backend systems are inherently concurrent.&lt;/p&gt;
&lt;p&gt;You’re dealing with:&lt;/p&gt;
&lt;p&gt;• multiple HTTP requests&lt;br&gt;
• database operations&lt;br&gt;
• background jobs&lt;br&gt;
• external services&lt;br&gt;
• queues and event streams&lt;/p&gt;
&lt;p&gt;In many languages, concurrency means dealing with complex thread models, locks, or async frameworks.&lt;/p&gt;
&lt;p&gt;Go solves this elegantly with &lt;strong&gt;goroutines and channels&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Starting a concurrent task is as simple as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go processRequest()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That single keyword launches a lightweight concurrent task.&lt;/p&gt;
&lt;p&gt;Behind the scenes Go handles scheduling, making it incredibly efficient.&lt;/p&gt;
&lt;p&gt;This model makes it easy to build systems like:&lt;/p&gt;
&lt;p&gt;• API servers&lt;br&gt;
• job processors&lt;br&gt;
• streaming services&lt;br&gt;
• high-throughput workers&lt;/p&gt;
&lt;p&gt;without drowning in concurrency complexity.&lt;/p&gt;
&lt;h3 id="3-fast-compilation-changes-your-workflow"&gt;3. Fast Compilation Changes Your Workflow&lt;/h3&gt;
&lt;p&gt;This is something many developers underestimate.&lt;/p&gt;
&lt;p&gt;Go compiles &lt;strong&gt;extremely fast&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That means your development loop becomes:&lt;/p&gt;
&lt;p&gt;edit → build → run → repeat&lt;/p&gt;
&lt;p&gt;And the build step takes seconds.&lt;/p&gt;
&lt;p&gt;Compare that to some compiled languages where builds can take minutes on larger projects.&lt;/p&gt;
&lt;p&gt;Fast compilation encourages experimentation and rapid iteration.&lt;/p&gt;
&lt;p&gt;It also makes Go great for:&lt;/p&gt;
&lt;p&gt;• CLI tools&lt;br&gt;
• microservices&lt;br&gt;
• infrastructure tooling&lt;/p&gt;
&lt;h3 id="4-tooling-that-just-works"&gt;4. Tooling That Just Works&lt;/h3&gt;
&lt;p&gt;One thing Go gets very right is its &lt;strong&gt;tooling philosophy&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Many languages rely heavily on external tools and third-party plugins.&lt;/p&gt;
&lt;p&gt;Go ships with most of what you need built in:&lt;/p&gt;
&lt;p&gt;• gofmt for automatic code formatting&lt;br&gt;
• go test for testing&lt;br&gt;
• go build for compiling&lt;br&gt;
• go mod for dependency management&lt;br&gt;
• profiling and benchmarking tools&lt;/p&gt;
&lt;p&gt;Because these tools are standardized, Go projects tend to look and behave consistently.&lt;/p&gt;
&lt;p&gt;You spend less time configuring tools and more time building software.&lt;/p&gt;
&lt;h3 id="5-why-go-is-excellent-for-apis"&gt;5. Why Go Is Excellent for APIs&lt;/h3&gt;
&lt;p&gt;Go has become one of the most popular languages for building APIs, and for good reason.&lt;/p&gt;
&lt;p&gt;It hits the sweet spot between &lt;strong&gt;performance and simplicity&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The standard library alone gives you a powerful HTTP server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;http.HandleFunc(&amp;#34;/api&amp;#34;, handler)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;http.ListenAndServe(&amp;#34;:8080&amp;#34;, nil)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No heavy frameworks required.&lt;/p&gt;
&lt;p&gt;This makes Go ideal for:&lt;/p&gt;
&lt;p&gt;• REST APIs&lt;br&gt;
• microservices&lt;br&gt;
• internal tooling services&lt;br&gt;
• high-performance backends&lt;/p&gt;
&lt;p&gt;It’s also incredibly easy to deploy because Go compiles to a &lt;strong&gt;single static binary&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;No runtime dependencies.&lt;br&gt;
No complicated deployments.&lt;/p&gt;
&lt;p&gt;Just ship the binary and run it.&lt;/p&gt;
&lt;h3 id="final-thoughts"&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;Go isn’t perfect.&lt;/p&gt;
&lt;p&gt;It’s not the most expressive language.&lt;br&gt;
It’s not the best choice for everything.&lt;br&gt;
And sometimes it feels intentionally minimal.&lt;/p&gt;
&lt;p&gt;But for backend systems, that minimalism becomes a strength.&lt;/p&gt;
&lt;p&gt;You get:&lt;/p&gt;
&lt;p&gt;• simple code&lt;br&gt;
• strong performance&lt;br&gt;
• built-in tooling&lt;br&gt;
• great concurrency&lt;/p&gt;
&lt;p&gt;After using more than 10 programming languages, Go remains the language I trust the most when building backend systems.&lt;/p&gt;
&lt;p&gt;Not because it’s flashy.&lt;/p&gt;
&lt;p&gt;Because it &lt;strong&gt;gets the job done reliably.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=9148165fa6dc" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/after-using-10-languages-go-is-still-my-favorite-for-backend-systems-9148165fa6dc?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Why Most Engineers Shouldn’t Build Distributed Systems (And Why I Did Anyway)</title><link>https://tacherasasi.github.io/posts/why-most-engineers-shouldnt-build-distributed-systems-and-why-i-did-anyway-20e6ace3f18e/</link><pubDate>Thu, 05 Mar 2026 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/why-most-engineers-shouldnt-build-distributed-systems-and-why-i-did-anyway-20e6ace3f18e/</guid><description>&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*JzWgtsxR_ozYKkQSqV6jVw.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Distributed systems are one of the most fascinating areas in software engineering.&lt;/p&gt;
&lt;p&gt;They’re also one of the fastest ways to accidentally create massive complexity.&lt;/p&gt;
&lt;p&gt;A lot of engineers jump straight into microservices, container orchestration, and distributed architectures because it sounds scalable and modern.&lt;/p&gt;
&lt;p&gt;But in reality, distributed systems introduce an entirely new category of problems.&lt;/p&gt;
&lt;p&gt;When your system runs on a single machine, many things are simple.&lt;/p&gt;
&lt;p&gt;Function calls are instant.&lt;br&gt;
Memory access is predictable.&lt;br&gt;
Failures are easier to understand.&lt;/p&gt;
&lt;p&gt;For example, a local function call might look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;chargeCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;updateInventory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Everything happens inside one process.&lt;br&gt;
No network. No timeouts. No partial failures.&lt;/p&gt;
&lt;p&gt;But once your system becomes distributed, the same operation might involve multiple services communicating over the network.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;orderService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;paymentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CustomerID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;inventoryService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Reserve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now things become much more complicated.&lt;/p&gt;
&lt;p&gt;What happens if the payment service succeeds but the inventory service fails?&lt;/p&gt;
&lt;p&gt;What if the network times out but the request actually succeeded?&lt;/p&gt;
&lt;p&gt;What if the service crashes halfway through the workflow?&lt;/p&gt;
&lt;p&gt;These are common distributed system problems.&lt;/p&gt;
&lt;h3 id="failures-become-normal"&gt;Failures Become Normal&lt;/h3&gt;
&lt;p&gt;In distributed systems, failures are not exceptions.&lt;/p&gt;
&lt;p&gt;They are expected behavior.&lt;/p&gt;
&lt;p&gt;A simple HTTP call can fail in many ways:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;http://node-service/tasks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// network failure &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But what kind of failure is it?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The server might be down&lt;/li&gt;
&lt;li&gt;The network might be slow&lt;/li&gt;
&lt;li&gt;DNS might fail&lt;/li&gt;
&lt;li&gt;The request might succeed but the response gets lost&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Handling these cases correctly requires retries, timeouts, and idempotent operations.&lt;/p&gt;
&lt;h3 id="scheduling-work-across-machines"&gt;Scheduling Work Across Machines&lt;/h3&gt;
&lt;p&gt;One of the challenges I encountered while building a cloud orchestration system was scheduling workloads across multiple nodes.&lt;/p&gt;
&lt;p&gt;A very simplified scheduler might look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt; &lt;span class="nx"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;ID&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Capacity&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Used&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="nx"&gt;Job&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;range&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Capacity&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;Used&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Resources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This looks simple.&lt;/p&gt;
&lt;p&gt;But real systems quickly introduce more challenges:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;nodes going offline&lt;/li&gt;
&lt;li&gt;jobs failing midway&lt;/li&gt;
&lt;li&gt;resource fragmentation&lt;/li&gt;
&lt;li&gt;network delays&lt;/li&gt;
&lt;li&gt;distributed state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A production system requires heartbeat monitoring, leader election, failure recovery, and task rescheduling.&lt;/p&gt;
&lt;p&gt;Suddenly the architecture grows significantly.&lt;/p&gt;
&lt;h3 id="debugging-becomes-harder"&gt;Debugging Becomes Harder&lt;/h3&gt;
&lt;p&gt;Debugging distributed systems is also more complex.&lt;/p&gt;
&lt;p&gt;In a monolith, you can follow logs sequentially.&lt;/p&gt;
&lt;p&gt;In a distributed system, a single request might pass through several services:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;API Gateway -&amp;gt; Auth Service -&amp;gt; Order Service -&amp;gt; Payment Service -&amp;gt; Inventory Service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To debug issues, you often need distributed tracing tools to reconstruct the path of a request.&lt;/p&gt;
&lt;p&gt;Without observability, diagnosing issues becomes extremely difficult.&lt;/p&gt;
&lt;h3 id="do-most-companies-need-this"&gt;Do Most Companies Need This?&lt;/h3&gt;
&lt;p&gt;In many cases, the answer is &lt;strong&gt;no&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A well designed monolith can scale surprisingly far.&lt;/p&gt;
&lt;p&gt;Many successful products run on monolithic architectures much longer than engineers expect.&lt;/p&gt;
&lt;p&gt;Distributed systems should only be introduced when they solve a clear scaling or reliability problem.&lt;/p&gt;
&lt;h3 id="but-there-is-still-value-in-learning-them"&gt;But There Is Still Value in Learning Them&lt;/h3&gt;
&lt;p&gt;Even though most systems don’t need distributed architectures immediately, understanding them provides enormous value.&lt;/p&gt;
&lt;p&gt;Once you understand how systems fail across networks and machines, you begin designing software differently.&lt;/p&gt;
&lt;p&gt;You think more about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fault tolerance&lt;/li&gt;
&lt;li&gt;system boundaries&lt;/li&gt;
&lt;li&gt;reliability&lt;/li&gt;
&lt;li&gt;observability&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those lessons improve every backend system you build.&lt;/p&gt;
&lt;p&gt;Distributed systems are powerful tools.&lt;/p&gt;
&lt;p&gt;But like all powerful tools, they should be used carefully.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=20e6ace3f18e" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/why-most-engineers-shouldnt-build-distributed-systems-and-why-i-did-anyway-20e6ace3f18e?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title> I Think I Accidentally Built a Full-Stack Framework?</title><link>https://tacherasasi.github.io/posts/i-think-i-accidentally-built-a-full-stack-framework/</link><pubDate>Fri, 20 Feb 2026 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/i-think-i-accidentally-built-a-full-stack-framework/</guid><description>&lt;p&gt;&lt;em&gt;Look, I just wanted to share some files.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="the-innocent-beginning"&gt;The Innocent Beginning&lt;/h2&gt;
&lt;p&gt;It started, as these things always do, with a simple problem: I needed to share files between my devices. &amp;ldquo;I&amp;rsquo;ll just write a quick Go server,&amp;rdquo; I said. &amp;ldquo;It&amp;rsquo;ll take an afternoon,&amp;rdquo; I said. &amp;ldquo;It&amp;rsquo;ll be like 200 lines of code,&amp;rdquo; I said.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reader, it was not 200 lines of code.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="what-is-beamdrop-actually"&gt;What Is BeamDrop, Actually?&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/ekilie/beamdrop"&gt;BeamDrop&lt;/a&gt; is a self-hosted file sharing server. You run one command, and boom you&amp;rsquo;ve got a beautiful web interface for uploading, downloading, moving, copying, renaming, and searching files. It&amp;rsquo;s like Dropbox, if Dropbox was a single binary and didn&amp;rsquo;t sell your data to train AI models. &lt;em&gt;(No offense, Dropbox. Actually, some offense.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the pitch: &lt;code&gt;beamdrop -dir /path/to/stuff -p mysecretpassword&lt;/code&gt; and you&amp;rsquo;re live. QR code on the terminal so your phone can connect. Password protection. Shareable links with expiry. WebSocket-powered real-time stats. An S3-compatible API so your CI/CD pipeline can talk to it.&lt;/p&gt;
&lt;p&gt;One binary. No Docker required. No Node.js runtime. No &amp;ldquo;please install these 47 dependencies.&amp;rdquo; Just a single executable that runs on macOS, Linux, and Windows.&lt;/p&gt;
&lt;h2 id="how-we-got-here"&gt;How We Got Here&lt;/h2&gt;
&lt;h3 id="stage-1-the-go-server-day-1"&gt;Stage 1: The Go Server (Day 1)&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;I&amp;rsquo;ll embed the frontend right into the Go binary using &lt;code&gt;embed.FS&lt;/code&gt;. Single binary deployment. I&amp;rsquo;m a genius.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="stage-2-the-react-frontend-week-1"&gt;Stage 2: The React Frontend (Week 1)&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;It needs a UI. I&amp;rsquo;ll use React. And Vite. And TypeScript. And Tailwind. And shadcn/ui. For a file server. This is totally proportional.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="stage-3-the-database-week-2"&gt;Stage 3: The Database (Week 2)&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;I need shareable links, so I need a database. SQLite is fine. I&amp;rsquo;ll use GORM. Oh wait, the SQLite driver needs CGO and that breaks cross-compilation. Let me swap to a pure-Go SQLite implementation so the binary stays fully static. This is still a simple file server.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="stage-4-the-s3-api-week-3"&gt;Stage 4: The S3 API (Week 3)&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;What if it also spoke S3? Like, the actual S3 API. With buckets and objects and presigned URLs. For a file server that I built because I wanted to share a PDF with my phone.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="stage-5-the-realization-now"&gt;Stage 5: The Realization (Now)&lt;/h3&gt;
&lt;p&gt;I stared at my codebase. A Go backend with embedded frontend assets. React with client-side routing. SPA fallback handling. SQLite with migrations. WebSocket connections. JWT authentication. API key management. CORS middleware. Security headers. TLS support. Cross-platform builds.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I built a framework.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t mean to. I was just solving problems one at a time. &amp;ldquo;Oh, the frontend routes break on refresh because Go doesn&amp;rsquo;t know about React Router&amp;rdquo; so I added SPA fallback. &amp;ldquo;Oh, &lt;code&gt;CGO_ENABLED=0&lt;/code&gt; breaks SQLite&amp;rdquo; so I swapped to a pure-Go driver. &amp;ldquo;Oh, I need auth&amp;rdquo; JWT and password hashing. Each fix was reasonable in isolation. Together, they formed a full-stack application framework that compiles to a zero-dependency binary.&lt;/p&gt;
&lt;h2 id="the-architecture-that-i-definitely-planned-from-the-start"&gt;The Architecture (That I Definitely Planned From The Start)&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌──────────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ Single Binary │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ┌────────────────────────┐ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ React + Vite + TS │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ (embedded via Go) │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └────────┬───────────────┘ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ┌────────▼───────────────┐ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ Go HTTP Server │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ ┌──────┐ ┌──────────┐ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ │ SPA │ │ API │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ │Fallbk│ │ Routes │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ └──────┘ └──────────┘ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ ┌──────┐ ┌──────────┐ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ │ Auth │ │WebSocket │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ └──────┘ └──────────┘ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └────────┬───────────────┘ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ┌────────▼───────────────┐ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ SQLite (pure Go) │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │ No CGO required │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └────────────────────────┘ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└──────────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;&amp;ldquo;Yes, this was the plan all along.&amp;rdquo;&lt;/em&gt; Me, lying.&lt;/p&gt;
&lt;h2 id="things-i-learned-along-the-way"&gt;Things I Learned Along The Way&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. SPA routing in embedded Go servers is a trap.&lt;/strong&gt;
If you serve a React app from Go and someone refreshes on &lt;code&gt;/shares&lt;/code&gt;, Go goes &amp;ldquo;I don&amp;rsquo;t have a file called &lt;code&gt;shares&lt;/code&gt;, here&amp;rsquo;s a 404.&amp;rdquo; The fix: if the requested path has no file extension, serve &lt;code&gt;index.html&lt;/code&gt; and let React Router figure it out. Simple in hindsight. Three hours of debugging in practice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. CGO is the final boss of Go cross-compilation.&lt;/strong&gt;
&lt;code&gt;GOOS=linux GOARCH=arm64 go build&lt;/code&gt; is beautiful until one of your dependencies whispers &amp;ldquo;I need C.&amp;rdquo; Swapping &lt;code&gt;mattn/go-sqlite3&lt;/code&gt; for &lt;code&gt;glebarez/sqlite&lt;/code&gt; (which uses &lt;code&gt;modernc.org/sqlite&lt;/code&gt;, a machine-translated C-to-Go SQLite) was the move. Zero CGO. Builds everywhere. The binary is bigger but my sanity is intact.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;embed.FS&lt;/code&gt; is criminally underrated.&lt;/strong&gt;
Go&amp;rsquo;s &lt;code&gt;//go:embed&lt;/code&gt; directive lets you bake your entire frontend build into the binary. No separate static file serving. No &amp;ldquo;make sure the &lt;code&gt;dist&lt;/code&gt; folder is next to the executable.&amp;rdquo; It just works. Ship one file. Run it. Done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. You don&amp;rsquo;t need a framework to build a framework.&lt;/strong&gt;
BeamDrop uses no web framework. No Gin, no Echo, no Fiber. Just &lt;code&gt;net/http&lt;/code&gt; and a &lt;code&gt;ServeMux&lt;/code&gt;. The standard library is enough. I&amp;rsquo;ll die on this hill while manually parsing URL paths.&lt;/p&gt;
&lt;h2 id="is-it-actually-a-framework-though"&gt;Is It Actually a Framework Though?&lt;/h2&gt;
&lt;p&gt;Look, if it has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A backend server with routing and middleware ✅&lt;/li&gt;
&lt;li&gt;An embedded frontend with client-side routing ✅&lt;/li&gt;
&lt;li&gt;A database with ORM and migrations ✅&lt;/li&gt;
&lt;li&gt;Authentication (passwords, JWT, API keys) ✅&lt;/li&gt;
&lt;li&gt;Real-time communication (WebSockets) ✅&lt;/li&gt;
&lt;li&gt;An API layer (REST + S3-compatible) ✅&lt;/li&gt;
&lt;li&gt;Cross-platform single-binary builds ✅&lt;/li&gt;
&lt;li&gt;TLS support ✅&lt;/li&gt;
&lt;li&gt;CORS and security headers ✅&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;hellip;then at what point does &amp;ldquo;my file sharing app&amp;rdquo; become &amp;ldquo;a full-stack framework with a file sharing app built on top of it&amp;rdquo;?&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t have an answer. I just have a very over-engineered way to send a PDF to my phone.&lt;/p&gt;
&lt;h2 id="try-it"&gt;Try It&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install (macOS Apple Silicon)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -L https://github.com/ekilie/beamdrop/releases/latest/download/beamdrop-darwin-arm64.tar.gz &lt;span class="p"&gt;|&lt;/span&gt; sudo tar -C /usr/local/bin -xzf -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;beamdrop -dir ~/Documents -p secretpassword
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. One binary. Full-stack. No regrets. Okay, some regrets. But the QR code in the terminal is really cool.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;BeamDrop is open source at &lt;a href="https://github.com/ekilie/beamdrop"&gt;github.com/ekilie/beamdrop&lt;/a&gt;. Star it if you&amp;rsquo;ve ever accidentally over-engineered something. So, star it.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Escape Analysis in Go: Where Does Your Data Actually Live?</title><link>https://tacherasasi.github.io/posts/escape-analysis-in-go-where-does-your-data-actually-live-d1a0430003d8/</link><pubDate>Fri, 20 Feb 2026 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/escape-analysis-in-go-where-does-your-data-actually-live-d1a0430003d8/</guid><description>&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/300/1*S0qxt-hT88hXkOFCj-nSBw.jpeg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;If you’ve been writing Go for a while, you’ve probably heard the terms &lt;strong&gt;stack&lt;/strong&gt; and &lt;strong&gt;heap&lt;/strong&gt; tossed around. But have you ever wondered how Go decides where to put your variables? That’s exactly what &lt;strong&gt;escape analysis&lt;/strong&gt; is about and understanding it can help you write faster, more efficient Go code.&lt;/p&gt;
&lt;h3 id="stack-vs-heap-a-quick-refresher"&gt;Stack vs Heap: A Quick Refresher&lt;/h3&gt;
&lt;p&gt;Think of memory in two buckets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stack&lt;/strong&gt; Fast, automatically managed. When a function is called, variables are pushed onto the stack. When the function returns, they’re gone. No cleanup needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Heap&lt;/strong&gt; Slower, but longer-lived. Variables here are managed by Go’s garbage collector (GC). They stick around as long as something is referencing them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The stack is cheap. The heap costs more allocating on the heap puts pressure on the GC, which can slow your program down.&lt;/p&gt;
&lt;h3 id="so-what-is-escape-analysis"&gt;So What Is Escape Analysis?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Escape analysis&lt;/strong&gt; is what the Go compiler does at compile time to figure out: &lt;em&gt;“Does this variable need to live on the heap, or can it stay on the stack?”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If a variable is only used inside the function it was created in, it can safely live on the stack. But if a variable “escapes” meaning something outside the function needs to access it it has to be moved to the heap.&lt;/p&gt;
&lt;p&gt;The compiler does this automatically. You don’t have to think about it most of the time. But understanding when things escape can help you avoid unnecessary heap allocations.&lt;/p&gt;
&lt;h3 id="a-simple-example"&gt;A Simple Example&lt;/h3&gt;
&lt;p&gt;go&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, x is created and used inside main. It doesn&amp;rsquo;t escape anywhere. The compiler keeps it on the stack.&lt;/p&gt;
&lt;p&gt;Now consider this:&lt;/p&gt;
&lt;p&gt;go&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;newNumber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// we&amp;#39;re returning a pointer to x &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, we’re returning a &lt;strong&gt;pointer&lt;/strong&gt; to x. That means after newNumber returns, something outside the function is still holding a reference to x. The stack for newNumber is gone at that point so Go moves x to the heap. It has &lt;em&gt;escaped&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id="how-to-see-whats-escaping"&gt;How to See What’s Escaping&lt;/h3&gt;
&lt;p&gt;Go gives you a handy compiler flag to inspect escape analysis decisions:&lt;/p&gt;
&lt;p&gt;bash&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go build -gcflags&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;-m&amp;#34;&lt;/span&gt; ./...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You’ll see output like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./main.go:6:2: moved to heap: x
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./main.go:10:13: ... argument does not escape
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This tells you exactly which variables the compiler decided to allocate on the heap. It’s a great debugging tool when you’re trying to optimize performance.&lt;/p&gt;
&lt;h3 id="common-reasons-a-variable-escapes"&gt;Common Reasons a Variable Escapes&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. Returning a pointer to a local variable&lt;/strong&gt; As shown above if you return &amp;amp;x, x has to live on the heap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Storing a value in an interface&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;go&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When you assign a concrete value to an interface, Go often needs to heap-allocate it because the interface value needs to carry around type information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Sending to a channel or storing in a slice/map&lt;/strong&gt; If the runtime can’t prove the value won’t outlive the current scope, it plays it safe and puts it on the heap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Closures capturing variables&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;go&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;makeCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, count is captured by the closure returned from makeCounter. Since the closure can be called long after makeCounter returns, count escapes to the heap.&lt;/p&gt;
&lt;h3 id="does-this-matter-in-practice"&gt;Does This Matter in Practice?&lt;/h3&gt;
&lt;p&gt;For most applications, not really Go’s garbage collector is fast and well-tuned. But in performance-sensitive code (think: game loops, high-frequency trading, large-scale data pipelines), reducing heap allocations can make a meaningful difference.&lt;/p&gt;
&lt;p&gt;Here’s a rough guide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Don’t return pointers to small, short-lived values&lt;/strong&gt; if you don’t need to. Return by value instead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pre-allocate slices&lt;/strong&gt; when you know the size upfront (make([]int, 0, 100)) to avoid repeated heap allocations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use&lt;/strong&gt;**-gcflags= &amp;ldquo;-m&amp;rdquo; or benchmarks** to identify hot paths with lots of allocations before optimizing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="key-takeaways"&gt;Key Takeaways&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Go automatically decides whether variables live on the &lt;strong&gt;stack&lt;/strong&gt; (fast, temporary) or the &lt;strong&gt;heap&lt;/strong&gt; (slower, GC-managed).&lt;/li&gt;
&lt;li&gt;This decision is made by the compiler using &lt;strong&gt;escape analysis&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A variable “escapes” to the heap when it needs to outlive the function it was created in.&lt;/li&gt;
&lt;li&gt;You can inspect escape decisions with go build -gcflags=&amp;quot;-m&amp;quot;.&lt;/li&gt;
&lt;li&gt;Understanding escape analysis helps you write more allocation-efficient Go code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The beauty of Go is that you don’t &lt;em&gt;have&lt;/em&gt; to think about this most of the time. But when performance matters, escape analysis is one of those things that separates good Go code from great Go code. Happy coding! 🚀&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=d1a0430003d8" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/escape-analysis-in-go-where-does-your-data-actually-live-d1a0430003d8?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Comptime vs. Macros: Metaprogramming in Zig and Rust</title><link>https://tacherasasi.github.io/posts/comptime-vs-macros-metaprogramming-in-zig-and-rust-511fedcdcffe/</link><pubDate>Mon, 10 Nov 2025 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/comptime-vs-macros-metaprogramming-in-zig-and-rust-511fedcdcffe/</guid><description>&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*5v5iBN3T7ascRigMp8HF4Q.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Metaprogramming allows code to generate or manipulate other code, often at compile time, boosting flexibility and performance. Two modern languages tackle this differently: Zig with its &lt;em&gt;&lt;strong&gt;comptime&lt;/strong&gt;&lt;/em&gt; feature and Rust with macros. Let’s explore both with examples, drawing from recent discussions and developments as of 2025.&lt;/p&gt;
&lt;h4 id="comptime-in-zig-seamless-compile-time-execution"&gt;Comptime in Zig: Seamless Compile-Time Execution&lt;/h4&gt;
&lt;p&gt;Zig’s &lt;strong&gt;comptime&lt;/strong&gt; integrates compile-time computation directly into the language, making it feel like regular code. Functions or expressions marked with &lt;code&gt;**comptime**&lt;/code&gt; run during compilation, enabling generics, conditional compilation, and more without separate syntax. It’s restrictive by design to avoid complexity, yet powerful for tasks like serialization or ORM.&lt;/p&gt;
&lt;p&gt;A simple example: generating a factorial at compile time in Zig.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comptime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;: &lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;u32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comptime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Computed at compile time: 120
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;compileLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Logs during compilation
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This runs entirely at compile time, embedding the result in the binary. Recent projects use comptime for auto-generating DLL loading code or erasure coding optimizations. In 2025, discussions highlight how comptime eliminates the need for macros by treating code uniformly.&lt;/p&gt;
&lt;h4 id="macros-in-rust-declarative-and-procedural-power"&gt;Macros in Rust: Declarative and Procedural Power&lt;/h4&gt;
&lt;p&gt;Rust’s macros come in two flavors: declarative (via &lt;code&gt;macro_rules!&lt;/code&gt;) for pattern-matching code generation, and procedural for custom syntax extensions. They’re explicit, ensuring type safety and hygiene (no accidental variable captures). Macros like &lt;code&gt;println!&lt;/code&gt; or &lt;code&gt;vec!&lt;/code&gt; are everyday examples.&lt;/p&gt;
&lt;p&gt;Here’s a declarative macro for a simple vector creator:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;macro_rules! my_vec {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ( $( $x:expr ),* ) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; let mut temp_vec = Vec::new();
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; $(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; temp_vec.push($x);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; )*
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; temp_vec
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fn main() {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; let v = my_vec![1, 2, 3]; // Expands to Vec with pushes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; println!(&amp;#34;{:?}&amp;#34;, v);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This expands at compile time. Advanced uses include deriving traits or composing derives with crates like &lt;code&gt;macro_rules_attribute&lt;/code&gt;. Recent 2025 experiments, like “&lt;strong&gt;Crabtime&lt;/strong&gt; ,” even attempt to emulate Zig’s comptime in Rust using macros, showing cross-inspiration.&lt;/p&gt;
&lt;h4 id="key-comparisons"&gt;Key Comparisons&lt;/h4&gt;
&lt;p&gt;- &lt;strong&gt;Simplicity vs. Explicitness&lt;/strong&gt; : Zig’s comptime is implicit and blends with runtime code, but changes can break callers unexpectedly. Rust macros are explicit, with strong constraints via traits, reducing errors but adding boilerplate.&lt;/p&gt;
&lt;p&gt;- &lt;strong&gt;Type Access&lt;/strong&gt; : Zig provides direct type info at comptime, easing generics. Rust macros handle this through patterns or proc macros, but it’s more involved.&lt;/p&gt;
&lt;p&gt;-&lt;strong&gt;Use Cases&lt;/strong&gt; : Both enable generics and optimizations, but Zig avoids a separate macro system, while Rust’s is versatile for DSLs.&lt;/p&gt;
&lt;p&gt;In 2025, Zig’s approach is praised for reducing complexity, though Rust’s ecosystem offers more mature tools. If you’re building low-level systems, experiment with both. Zig for elegance, Rust for safety.&lt;/p&gt;
&lt;p&gt;Thanks for reading! What’s your take on metaprogramming?&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=511fedcdcffe" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/comptime-vs-macros-metaprogramming-in-zig-and-rust-511fedcdcffe?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>How TypeScript Enums Compile to JavaScript (And Why It Matters)</title><link>https://tacherasasi.github.io/posts/how-typescript-enums-compile-to-javascript-and-why-it-matters-8766881c1728/</link><pubDate>Sun, 31 Aug 2025 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/how-typescript-enums-compile-to-javascript-and-why-it-matters-8766881c1728/</guid><description>&lt;p&gt;When you’re learning TypeScript, it’s easy to overlook that it all boils down to plain JavaScript in the end. Most TypeScript goodies like types, interfaces, and unions vanish after compilation, living only to help catch errors during development. But there’s one feature that sticks around: &lt;strong&gt;enums&lt;/strong&gt;. They’re a bit special, and here’s why.&lt;/p&gt;
&lt;h3 id="what-happens-with-numeric-enums"&gt;What Happens with Numeric Enums&lt;/h3&gt;
&lt;p&gt;Let’s say you create a simple enum like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Direction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Up&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Right&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When TypeScript compiles this, it doesn’t just disappear. Instead, it turns into this JavaScript:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Up&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Up&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Down&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Down&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Left&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Left&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Right&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Right&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This code creates a &lt;strong&gt;two-way street&lt;/strong&gt; : you can look up values both ways. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Direction.Up === 0; // true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Direction[0] === &amp;#34;Up&amp;#34;; // true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So, unlike most TypeScript features, enums don’t just exist for type-checking they become &lt;strong&gt;real objects&lt;/strong&gt; in your JavaScript code.&lt;/p&gt;
&lt;h3 id="why-this-is-a-big-deal"&gt;Why This Is a Big Deal&lt;/h3&gt;
&lt;p&gt;Here’s why you should care:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bundle Size:&lt;/strong&gt; Enums generate actual JavaScript code, which can make your final bundle heavier. If you’re not careful, this can add up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unexpected Perks:&lt;/strong&gt; Enums let you do cool things like reverse lookups (e.g., getting “Up” from 0). Handy, but it might catch you off guard if you’re not expecting it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lighter Options:&lt;/strong&gt; If you just need simple constants, you might want to use const enum or plain string literals instead. They’re leaner and don’t add extra code to your JavaScript.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="the-bottom-line"&gt;The Bottom Line&lt;/h3&gt;
&lt;p&gt;Understanding how enums work under the hood helps you write &lt;strong&gt;smarter, cleaner code&lt;/strong&gt;. Every time you use an enum, keep in mind: you’re not just writing TypeScript you’re creating real JavaScript objects that live in your final app. Use them wisely!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=8766881c1728" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/how-typescript-enums-compile-to-javascript-and-why-it-matters-8766881c1728?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Why Wails Wins at IPC for Go Desktop Apps (and How It Stacks Up Against Tauri &amp; Electron)</title><link>https://tacherasasi.github.io/posts/why-wails-wins-at-ipc-for-go-desktop-apps-and-how-it-stacks-up-against-tauri-electron-5a00b202cf09/</link><pubDate>Wed, 27 Aug 2025 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/why-wails-wins-at-ipc-for-go-desktop-apps-and-how-it-stacks-up-against-tauri-electron-5a00b202cf09/</guid><description>&lt;p&gt;So you’re building a desktop app with web tech, and you hit the classic problem: &lt;em&gt;how do I get my native backend to talk nicely with my frontend?&lt;/em&gt; That’s where IPC (Inter-Process Communication) comes in the secret sauce that makes your Go (or Rust, or Node) logic play well with your slick React/Vue/HTML UI.&lt;/p&gt;
&lt;p&gt;Here’s my hot take: &lt;strong&gt;Wails does IPC better than anyone else right now.&lt;/strong&gt;&lt;br&gt;
It doesn’t just “let things talk.” It &lt;em&gt;represents&lt;/em&gt; your backend on the frontend, type-safe and all, with almost no effort on your part. Let me break down why that matters, and how it compares to Tauri and Electron.&lt;/p&gt;
&lt;h3 id="wails-ipc-that-feels-native"&gt;Wails: IPC That Feels Native&lt;/h3&gt;
&lt;p&gt;Wails is all about Go on the backend + whatever JS framework you like on the frontend. What makes its IPC stand out is how &lt;em&gt;automatic&lt;/em&gt; it feels. You bind your Go structs or methods, and Wails spits out JS/TS files for you no JSON wrangling, no boilerplate glue code.&lt;/p&gt;
&lt;p&gt;Example:&lt;br&gt;
A Go method like Greet(name string) string instantly becomes a JS import you can call like a normal function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Greet&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;../wailsjs/go/main/App&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Boom. Done. Promise-based, error-handled.&lt;/p&gt;
&lt;p&gt;But the real flex? &lt;strong&gt;Struct mapping.&lt;/strong&gt; If you’ve got a Person struct in Go, Wails creates a Person class in JS, with constructors and helpers. Pass it JSON, pass it objectsWails just translates it all. Add JSON tags to your struct fields, and you’re living the dream.&lt;/p&gt;
&lt;p&gt;And when you update your Go code? A quick wails generate syncs everything. Your frontend always has the latest bindings, complete with TypeScript definitions. It really feels like you’re just calling local JS code, except Go is doing the heavy lifting behind the scenes.&lt;/p&gt;
&lt;h3 id="how-it-compares-tauri-vs-electron"&gt;How It Compares: Tauri vs. Electron&lt;/h3&gt;
&lt;p&gt;Let’s size it up against the two heavyweights.&lt;/p&gt;
&lt;h3 id="tauri-rust"&gt;Tauri (Rust)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Good:&lt;/strong&gt; Lightweight, secure, resource-friendly. Rust fans love it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IPC style:&lt;/strong&gt; JS calls Rust functions via invoke(&amp;lsquo;command&amp;rsquo;, { args }). It’s JSON under the hood. Events exist for push messages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downside:&lt;/strong&gt; Type safety isn’t automatic. You &lt;em&gt;can&lt;/em&gt; use tauri-bindgen for type-safe bindings, but it’s an extra step. Struct mapping is manual unless you go all-in on bindgen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; Fantastic performance, but you’ll spend more time wiring things up.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="electron-node"&gt;Electron (Node)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Good:&lt;/strong&gt; Huge ecosystem, proven in production (Slack, VS Code, etc.).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IPC style:&lt;/strong&gt; Old-school channels with ipcRenderer.send/invoke and webContents.send. It works, but it’s raw.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downside:&lt;/strong&gt; Zero codegen, zero type safety. You’re writing boilerplate for preload scripts and JSON messages. Structs? Forget it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; If you’re deep in Node land, it’s familiar. But compared to Wails, it feels… clunky.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="tldr-comparison"&gt;TL;DR Comparison&lt;/h3&gt;
&lt;p&gt;Feature &lt;strong&gt;Wails (Go)&lt;/strong&gt; &lt;strong&gt;Tauri (Rust)&lt;/strong&gt; &lt;strong&gt;Electron (Node)&lt;/strong&gt; IPC Style Auto JS functions/classes JSON commands + events Manual channels/messages Code Generation Yes, built-in Optional (bindgen) None Type Safety Strong, auto TS Partial, extra setup Manual, if you bother Struct Mapping Automatic (JS classes) Manual / via bindgen Manual JSON Ease of Use High (feels native to JS) Medium (setup-heavy) Low (lots of boilerplate) Best For Go lovers + type safety fans Rust fans + perf seekers Node folks + ecosystem&lt;/p&gt;
&lt;h3 id="so-who-should-use-what"&gt;So, Who Should Use What?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pick Wails&lt;/strong&gt; if you want Go to feel like it lives &lt;em&gt;inside&lt;/em&gt; your frontend. It’s the smoothest, most type-safe IPC experience right now.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pick Tauri&lt;/strong&gt; if performance and security are your top concerns, and you don’t mind extra setup.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pick Electron&lt;/strong&gt; if you need Node’s ecosystem or you’re building something huge where library support matters more than IPC elegance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Personally, Wails nails the sweet spot: less boilerplate, more productivity, and it just feels &lt;em&gt;right&lt;/em&gt;. And with Wails v3 on the horizon (better bindings, better tooling), it’s only getting stronger.&lt;/p&gt;
&lt;p&gt;That’s my two cents. What about you? Are you team Wails, Tauri, or Electron? 🚀&lt;/p&gt;
&lt;p&gt;Links if you want to dive deeper: &lt;a href="https://wails.io/"&gt;Wails&lt;/a&gt; | &lt;a href="https://tauri.app/"&gt;Tauri&lt;/a&gt; | &lt;a href="https://www.electronjs.org/"&gt;Electron&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=5a00b202cf09" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/why-wails-wins-at-ipc-for-go-desktop-apps-and-how-it-stacks-up-against-tauri-electron-5a00b202cf09?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>