<?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: Dani Sam</title>
    <description>The latest articles on DEV Community by Dani Sam (@voidhyr).</description>
    <link>https://dev.to/voidhyr</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3887746%2F8ac56a52-a779-4482-a707-e5775b0e0dde.jpeg</url>
      <title>DEV Community: Dani Sam</title>
      <link>https://dev.to/voidhyr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/voidhyr"/>
    <language>en</language>
    <item>
      <title>Why I'm Moving from Go to C After Building a Load Balancer</title>
      <dc:creator>Dani Sam</dc:creator>
      <pubDate>Sun, 03 May 2026 19:00:17 +0000</pubDate>
      <link>https://dev.to/voidhyr/why-im-moving-from-go-to-c-after-building-a-load-balancer-55go</link>
      <guid>https://dev.to/voidhyr/why-im-moving-from-go-to-c-after-building-a-load-balancer-55go</guid>
      <description>&lt;p&gt;&lt;em&gt;This isn't an anti-Go post. Go is a great language. This is about what I want to understand.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I just finished building an L7 HTTP load balancer in Go.&lt;/p&gt;

&lt;p&gt;It accepts connections. It parses HTTP headers. It forwards requests to backend servers using round-robin. It handles concurrent connections with goroutines. It has health checks. It works.&lt;/p&gt;

&lt;p&gt;And somewhere in the middle of it working, I realized I didn't fully understand what it was doing.&lt;/p&gt;

&lt;p&gt;Not the logic — I understood the logic. I'm talking about what's happening underneath. The goroutine scheduler, the net package, the garbage collector deciding when to free memory — all of it is running, and I can't see it. I'm directing traffic above a layer I've never touched.&lt;/p&gt;

&lt;p&gt;That bothered me more than I expected.&lt;/p&gt;




&lt;h2&gt;
  
  
  Go is hiding things from you. On purpose.
&lt;/h2&gt;

&lt;p&gt;That's not a criticism. That's the whole point of Go. It was designed to let you build reliable network services without managing memory, without dealing with file descriptors, without thinking about what &lt;code&gt;socket()&lt;/code&gt; and &lt;code&gt;bind()&lt;/code&gt; and &lt;code&gt;accept()&lt;/code&gt; actually do. You call &lt;code&gt;net.Listen()&lt;/code&gt; and it works.&lt;/p&gt;

&lt;p&gt;But when you're trying to build a career around infrastructure and protocol design — when you want to eventually write things that operate &lt;em&gt;at&lt;/em&gt; the wire, not &lt;em&gt;above&lt;/em&gt; it — those hidden layers matter.&lt;/p&gt;

&lt;p&gt;In Go, a TCP connection is a &lt;code&gt;net.Conn&lt;/code&gt;. In C, a TCP connection is a file descriptor returned by &lt;code&gt;socket()&lt;/code&gt;, configured by &lt;code&gt;setsockopt()&lt;/code&gt;, bound by &lt;code&gt;bind()&lt;/code&gt;, connected by &lt;code&gt;connect()&lt;/code&gt; or accepted by &lt;code&gt;accept()&lt;/code&gt;. You can inspect it, manipulate it, do things to it that Go will never let you do from inside its abstraction.&lt;/p&gt;

&lt;p&gt;When I build a load balancer in C, I'll write the socket calls myself. I'll choose between blocking and non-blocking I/O. I'll call &lt;code&gt;select()&lt;/code&gt; or &lt;code&gt;epoll()&lt;/code&gt; directly and decide how to handle readability and writability on each file descriptor. I'll feel the event loop in my hands instead of trusting the runtime to manage it.&lt;/p&gt;

&lt;p&gt;That's not harder for the sake of being harder. That's closer to what's actually happening.&lt;/p&gt;




&lt;h2&gt;
  
  
  Memory tells you the truth about your program.
&lt;/h2&gt;

&lt;p&gt;Go has a garbage collector. It's a good one. You don't think about allocation — you create things, use them, and the runtime figures out when to free them. For most programs, this is exactly the right tradeoff.&lt;/p&gt;

&lt;p&gt;But the GC also means you develop a certain blindness. You never ask: &lt;em&gt;how long does this live? who owns it? when does it go away?&lt;/em&gt; You don't have to. Go answers those questions for you.&lt;/p&gt;

&lt;p&gt;In C, nothing answers those questions for you. You call &lt;code&gt;malloc()&lt;/code&gt;, you use the memory, you call &lt;code&gt;free()&lt;/code&gt; — or you leak it, and &lt;code&gt;valgrind&lt;/code&gt; will tell you exactly where. This forces a precision of thought that Go quietly removes.&lt;/p&gt;

&lt;p&gt;I don't think Go programmers are less rigorous. I think they're rigorous about different things. But I want to be rigorous about memory. I want to know, for every allocation in a network path, where it lives and when it dies. That instinct only comes from writing C.&lt;/p&gt;




&lt;h2&gt;
  
  
  Protocol design lives in C's territory.
&lt;/h2&gt;

&lt;p&gt;Here's the thing that I care about most in the long run: protocol design. Not using HTTP — designing binary protocols. Wire formats. Message framing. Encoding length-prefixed fields. Handling byte order with &lt;code&gt;htons()&lt;/code&gt; and &lt;code&gt;ntohl()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In C, you write a packed struct, cast a buffer to it, and send it over a raw socket. You understand exactly how many bytes are on the wire, in what order, and why. When the receiver reads it, you know precisely what it sees.&lt;/p&gt;

&lt;p&gt;In Go, you reach for &lt;code&gt;encoding/binary&lt;/code&gt; and the abstraction handles byte order for you. Useful. But it doesn't teach you &lt;em&gt;why&lt;/em&gt; byte order matters, or what the machine is actually doing when it serializes that field.&lt;/p&gt;

&lt;p&gt;If I ever want to write a protocol from scratch — not implement HTTP, but &lt;em&gt;design&lt;/em&gt; something that runs on TCP and defines its own framing — I need to be comfortable at that level. C is where that comfort is built.&lt;/p&gt;




&lt;h2&gt;
  
  
  Go did exactly what it was supposed to do. And more.
&lt;/h2&gt;

&lt;p&gt;Building the load balancer in Go was the right choice for the project. Go's concurrency model made handling connections straightforward. The standard library handled HTTP parsing cleanly. The resulting code is readable and correct.&lt;/p&gt;

&lt;p&gt;But here's what I didn't expect: Go was &lt;em&gt;great&lt;/em&gt; for me precisely because of the doubts it created.&lt;/p&gt;

&lt;p&gt;Every time I hit a question I couldn't fully answer — why does this goroutine block here? what is the runtime actually scheduling? what does the kernel see when &lt;code&gt;net.Dial()&lt;/code&gt; runs? — those weren't failures. Those were directions. Each doubt was a pointer to something real underneath that I hadn't looked at yet.&lt;/p&gt;

&lt;p&gt;Go gave me a map of questions I didn't know I had.&lt;/p&gt;

&lt;p&gt;And one of those questions led somewhere I didn't anticipate: embedded systems. The more I pulled on the thread of "what's actually running this code," the more I found myself staring at hardware. Microcontrollers. Registers. Interrupts. The point where software stops and physics begins.&lt;/p&gt;

&lt;p&gt;That's when something clicked.&lt;/p&gt;

&lt;p&gt;Electronics and computer science are not separate fields that happen to overlap. Electronics &lt;em&gt;is&lt;/em&gt; the layer that computer science runs on. Every abstraction — the OS, the runtime, the network stack — eventually bottoms out at a circuit doing something physical. Without that layer, none of the rest exists.&lt;/p&gt;

&lt;p&gt;A load balancer in Go is real. But it is running on silicon. And I want to understand the whole path, from the wire to the application, without a gap.&lt;/p&gt;

&lt;p&gt;That's not Go's failure. That's Go being honest about what it is — and me being honest about where I want to go.&lt;/p&gt;




&lt;h2&gt;
  
  
  So what's next.
&lt;/h2&gt;

&lt;p&gt;I'm starting C with a specific sequence: raw sockets first, then a TCP echo server, then a custom binary protocol over TCP, then — eventually — something that touches the kernel directly. No web servers, no frameworks, no shortcuts.&lt;/p&gt;

&lt;p&gt;I want to feel what Go is abstracting. Not because abstraction is bad, but because you can't abstract something you don't understand. And I want to understand it.&lt;/p&gt;

&lt;p&gt;And I'll be honest about something: humans change their minds. Maybe in six months I'm deep in embedded — writing firmware, talking to hardware over SPI, thinking in interrupts. Maybe I end up the other direction — implementing TCP itself, building network stacks, living inside the kernel's socket layer. I don't know yet.&lt;/p&gt;

&lt;p&gt;That's not a weakness in the plan. That's just how engineers actually develop. You follow the questions. The questions take you somewhere. You follow those.&lt;/p&gt;

&lt;p&gt;Right now the questions are pointing at C. So that's where I'm going.&lt;/p&gt;

&lt;p&gt;The void meets the wire. That's the direction.&lt;/p&gt;

</description>
      <category>go</category>
      <category>c</category>
      <category>networking</category>
      <category>career</category>
    </item>
    <item>
      <title>My load balancer worked. That's what made me uncomfortable.</title>
      <dc:creator>Dani Sam</dc:creator>
      <pubDate>Mon, 20 Apr 2026 15:57:07 +0000</pubDate>
      <link>https://dev.to/voidhyr/my-load-balancer-worked-thats-what-made-me-uncomfortable-17gm</link>
      <guid>https://dev.to/voidhyr/my-load-balancer-worked-thats-what-made-me-uncomfortable-17gm</guid>
      <description>&lt;p&gt;I didn't expect a load balancer to make me uncomfortable.&lt;/p&gt;

&lt;p&gt;But here we are.&lt;/p&gt;

&lt;p&gt;I built one in Go for my MSc case study. Round-robin distribution, health checks, mutex for concurrency, timeout handling. It worked. Requests going in, responses coming out, servers behaving exactly as they should.&lt;/p&gt;

&lt;p&gt;I should have felt good about it.&lt;/p&gt;

&lt;p&gt;Instead I felt uneasy.&lt;/p&gt;

&lt;p&gt;I kept staring at &lt;code&gt;http.Get()&lt;/code&gt; thinking — I have no idea what's actually happening here. I know what it does. But I don't know what it's doing. TCP handshakes, socket creation, buffer management, kernel calls — Go handles all of it quietly, without ever asking if I understand any of it.&lt;/p&gt;

&lt;p&gt;For a while I told myself that's fine. That's the point of abstractions.&lt;/p&gt;

&lt;p&gt;But then I thought about what kind of engineer I actually want to be.&lt;/p&gt;

&lt;p&gt;Not just someone who can make things work. But someone who knows what's happening at the wire level. Someone who understands the ground beneath the tools they're standing on.&lt;/p&gt;

&lt;p&gt;And I realized — I wasn't that person yet.&lt;/p&gt;

&lt;p&gt;So I'm going back to the foundation. Learning C properly. Raw sockets, memory management, system calls — the actual ground floor of how networked systems work. This week I wrote my first TCP echo server in C with no libraries. Just &lt;code&gt;socket()&lt;/code&gt;, &lt;code&gt;bind()&lt;/code&gt;, &lt;code&gt;listen()&lt;/code&gt;, &lt;code&gt;accept()&lt;/code&gt; — and &lt;code&gt;strace&lt;/code&gt; to watch every syscall it made. It was slower. It was harder. It felt completely different from Go.&lt;/p&gt;

&lt;p&gt;It felt like understanding.&lt;/p&gt;

&lt;p&gt;This isn't about Go being wrong for the job. Go is excellent at what it does. It's about me not being okay with borrowed understanding anymore.&lt;/p&gt;

&lt;p&gt;There's a difference between standing on the ground and standing on someone else's abstraction of it. Both get the job done. Only one of them is actually yours.&lt;/p&gt;

&lt;p&gt;I'd rather build slower on solid ground.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydvtkximoven2191kewy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fydvtkximoven2191kewy.png" alt="Load balancer terminal output showing round-robin forwarding and health checks" width="771" height="696"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The load balancer in action — round-robin distribution across three servers, health check catching a server going down and back up.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;If you want to see the code: &lt;a href="https://github.com/voidhyr/go-load-balancer" rel="noopener noreferrer"&gt;go-load-balancer on GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>beginners</category>
      <category>career</category>
    </item>
    <item>
      <title>The Architects and the Tenants: Your Place in the AI-Driven Future of Code</title>
      <dc:creator>Dani Sam</dc:creator>
      <pubDate>Sun, 19 Apr 2026 19:51:34 +0000</pubDate>
      <link>https://dev.to/voidhyr/the-architects-and-the-tenants-your-place-in-the-ai-driven-future-of-code-37lp</link>
      <guid>https://dev.to/voidhyr/the-architects-and-the-tenants-your-place-in-the-ai-driven-future-of-code-37lp</guid>
      <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@rn2917" rel="noopener noreferrer"&gt;reyna&lt;/a&gt; on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;AI can write code now. Pretty good code, honestly.&lt;/p&gt;

&lt;p&gt;And that should make you think — not panic, just think. Because if AI can generate the same Python script, the same REST API, the same web app that you spent weeks learning to build — what exactly are you bringing to the table?&lt;/p&gt;

&lt;p&gt;Most of us started at the surface. High-level languages, frameworks, tutorials. That's fine. That's how everyone starts. But a lot of people never leave the surface. They get comfortable there. And now AI lives there too, and it's faster than all of us.&lt;/p&gt;

&lt;p&gt;Below that surface though — something different is happening.&lt;/p&gt;

&lt;p&gt;Memory. Networking. Hardware. The kernel. The wire. This layer doesn't care about your framework or your prompt. It only speaks one language — understanding. Not usage. Not generation. Actual, hard-won understanding of why things work the way they do.&lt;/p&gt;

&lt;p&gt;Why does this packet drop halfway through a handshake. Why does this process eat memory at 3am. Why does this protocol fall apart under real load. AI can guess at these. The engineer who truly understands systems doesn't guess.&lt;/p&gt;

&lt;p&gt;I think about it this way. There are architects and there are tenants.&lt;/p&gt;

&lt;p&gt;Tenants use what's available. They ship features, use tools, move fast. Nothing wrong with that — the world needs tenants. But tenants are also one better model away from being automated.&lt;/p&gt;

&lt;p&gt;Architects build what the tools run on. They go below the abstraction on purpose. They understand the foundation that everyone else is standing on without realizing it. AI doesn't replace architects — it gives them better tools to build with.&lt;/p&gt;

&lt;p&gt;The surface is getting crowded. AI is making it more crowded every month. But the deeper layers — networking internals, systems programming, embedded hardware, protocol design — those are getting quieter. Because going deep is hard and slow and uncomfortable and most people won't do it.&lt;/p&gt;

&lt;p&gt;That's exactly why it matters.&lt;/p&gt;

&lt;p&gt;The future belongs to the people who understand what's happening below the abstraction. Not because they're anti-AI. But because they understand what AI is actually running on.&lt;/p&gt;

&lt;p&gt;Architect or tenant. The choice is still yours to make.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>career</category>
      <category>beginners</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
