<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Manzi Niyongira Osee</title>
    <description>The latest articles on DEV Community by Manzi Niyongira Osee (@manzi_niyongiraosee_1586).</description>
    <link>https://dev.to/manzi_niyongiraosee_1586</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4211731%2F4b1df10b-2340-471e-ac32-ad96f112ea13.jpg</url>
      <title>DEV Community: Manzi Niyongira Osee</title>
      <link>https://dev.to/manzi_niyongiraosee_1586</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manzi_niyongiraosee_1586"/>
    <language>en</language>
    <item>
      <title>I built a leaderless service-discovery system in Go. Here's what broke along the way</title>
      <dc:creator>Manzi Niyongira Osee</dc:creator>
      <pubDate>Tue, 27 Oct 2026 21:29:18 +0000</pubDate>
      <link>https://dev.to/manzi_niyongiraosee_1586/i-built-a-leaderless-service-discovery-system-in-go-heres-what-broke-along-the-way-2apg</link>
      <guid>https://dev.to/manzi_niyongiraosee_1586/i-built-a-leaderless-service-discovery-system-in-go-heres-what-broke-along-the-way-2apg</guid>
      <description>&lt;p&gt;For the past few weeks I've been building &lt;strong&gt;Atlas&lt;/strong&gt;, a distributed service-discovery system in Go. It answers one question every microservice setup eventually asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Where is the &lt;code&gt;payments&lt;/code&gt; service right now, and which copies of it are actually alive?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most tools answer that with a central database or a leader node (Consul's servers, etcd, ZooKeeper). I wanted to see how far I could get &lt;strong&gt;without any leader at all&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/manziosee/atlas" rel="noopener noreferrer"&gt;https://github.com/manziosee/atlas&lt;/a&gt;&lt;br&gt;
🟢 &lt;strong&gt;Live demo (read-only):&lt;/strong&gt; &lt;a href="https://atlas-edge.fly.dev/dashboard/#token=atlas-demo-read-only-public-token-2027" rel="noopener noreferrer"&gt;open the dashboard&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Every node runs the same ~14 MB binary. Nodes find each other with &lt;strong&gt;SWIM gossip&lt;/strong&gt;, the same family of protocols behind HashiCorp's memberlist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Each node pings a random peer every second.&lt;/li&gt;
&lt;li&gt;No answer? It asks up to three other nodes to try (&lt;code&gt;ping-req&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Still nothing? The peer is marked &lt;strong&gt;suspect&lt;/strong&gt;, and the rumour spreads by gossip.&lt;/li&gt;
&lt;li&gt;If the peer is actually alive, it &lt;strong&gt;refutes&lt;/strong&gt; by bumping its incarnation number. Otherwise it's declared &lt;strong&gt;dead&lt;/strong&gt; within seconds.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On top of that sits a &lt;strong&gt;service registry&lt;/strong&gt;. Every node stores registrations in SQLite and replicates them to every other node over the same gossip. So any node can answer "where is payments?" from its own memory, with no network hop to a central store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://atlas-edge.fly.dev/v1/lookup/payments?tag=primary"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Three bugs that taught me the most
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. The cluster that never healed.&lt;/strong&gt;&lt;br&gt;
My chaos test cut a node off the network, waited for the others to declare it dead, then plugged it back in. It stayed dead. Forever. Each side had marked the other dead, gossip only goes to &lt;em&gt;live&lt;/em&gt; members, and nodes only contacted their seeds at startup. Nothing ever crossed the healed link. The fix was small (keep pulling state from a random seed every 30 seconds), but finding it meant really understanding the protocol.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Health checks that flapped.&lt;/strong&gt;&lt;br&gt;
One slow response marked a service unhealthy; the next one marked it healthy again. Now a service needs 3 consecutive failures to go down and 2 passes to come back, and checks can be HTTP, TCP or gRPC-health.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. "Only the owner can update this."&lt;/strong&gt;&lt;br&gt;
A registration belongs to the node that accepted it. That's simple and consistent, but clients behind a load balancer kept hitting the wrong node and getting &lt;code&gt;409&lt;/code&gt;. Now every node gossips its API address and quietly forwards the write to the owner, using the caller's own credential.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in it today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Membership with failure detection, suspicion, refutation and partition recovery&lt;/li&gt;
&lt;li&gt;Replicated registry with TTL heartbeats, idempotent registration, &lt;code&gt;If-Match&lt;/code&gt; updates and draining for zero-downtime deploys&lt;/li&gt;
&lt;li&gt;Routing that prefers healthy instances in your region, with consistent hashing&lt;/li&gt;
&lt;li&gt;HTTP + typed gRPC APIs, DNS (A/AAAA/SRV), and a live event stream you can resume&lt;/li&gt;
&lt;li&gt;Client-side load balancing in Go: &lt;code&gt;http://payments.atlas/...&lt;/code&gt; and &lt;code&gt;grpc.NewClient("atlas:///payments")&lt;/code&gt; just work&lt;/li&gt;
&lt;li&gt;Go and Python clients, a CLI, and an embedded dashboard&lt;/li&gt;
&lt;li&gt;Encrypted gossip (AES-256-GCM), scoped and expiring tokens, signed releases, CI with race tests, chaos tests and a 1,000-node simulation
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;atlas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTransport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;atlas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InstanceOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"eu"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;httpClient&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://payments.atlas/v1/charges"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// round-robin across live instances&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Honest positioning
&lt;/h2&gt;

&lt;p&gt;Atlas isn't trying to replace Consul. Consul, Netflix's Eureka and Fly.io's Corrosion inspired a lot of it. The goal was to build these ideas from first principles and make the result small, secure by default and easy to run on an edge platform. Building it taught me more about distributed systems than anything I've read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to help?
&lt;/h2&gt;

&lt;p&gt;It's open source (Apache 2.0), and there's real work for any level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🟢 &lt;strong&gt;Good first issues:&lt;/strong&gt; runnable examples, docs, a Node.js/TypeScript client&lt;/li&gt;
&lt;li&gt;🟡 &lt;strong&gt;Medium:&lt;/strong&gt; a cluster map with live latency in the dashboard, TLS rotation tests&lt;/li&gt;
&lt;li&gt;🔴 &lt;strong&gt;Deep end:&lt;/strong&gt; chaos tests with packet loss, a multi-region demo, per-node cryptographic identity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/manziosee/atlas/issues" rel="noopener noreferrer"&gt;https://github.com/manziosee/atlas/issues&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feedback on the design is just as welcome as code. If something looks wrong to you, it probably is, and I'd love to know.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm also open to backend / infrastructure / platform engineering roles (remote or relocation). If your team works on problems like this, reach me at &lt;a href="mailto:manziosee@gmail.com"&gt;manziosee@gmail.com&lt;/a&gt; or here on DEV.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>go</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
