<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Reliability on Tachera W Sasi, Backend &amp; Infra Engineer (Go, Self-Hosted, Observability)</title><link>https://tacherasasi.github.io/tags/reliability/</link><description>Recent content in Reliability on Tachera W Sasi, Backend &amp; Infra Engineer (Go, Self-Hosted, Observability)</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Tue, 28 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tacherasasi.github.io/tags/reliability/index.xml" rel="self" type="application/rss+xml"/><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;h2 id="tags-devops-docker-golang"&gt;&amp;mdash;2&amp;quot;
tags: [&amp;ldquo;devops&amp;rdquo;, &amp;ldquo;docker&amp;rdquo;, &amp;ldquo;golang&amp;rdquo;]&lt;/h2&gt;
&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>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;h2 id="2"&gt;&amp;mdash;2&amp;quot;&lt;/h2&gt;
&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></channel></rss>