Under the Hood
Notes from Someone Who Needed to Know Why
I am a working software developer now — building production systems in Ruby, Go, and JavaScript for real clients. But I want to share these journal entries anyway, because the journey matters as much as the destination. These are excerpts from my programming journal and spam DMs to my friend Alex, written at various points along the way. Some entries are out of date. I have grown since writing them. That is the point.
Entry One
I want to mention something that may be obvious to some, but not to me. I have, like every hobbyist technologist, built things in various languages. Games, programs. I even made a P2P chat program in Go — mostly by intuition, borrowing from others' code, running it and hoping for the best. Go was the language that finally made me honest with myself. There was too much going on that I didn't truly understand. So I made a decision: learn C. Not to write C. To understand what everything else is actually doing.
I don't claim to understand C. But every day I spend truly learning it, something shifts. Computers become a little less magic and a little more knowable.
My favorite language is Ruby. You'd think Python, but Ruby won because it doesn't make me write as many for loops (and then I went and learned Go, so I suppose I'm a development hypocrite). Ruby feels like it loves me back. C feels like it respects me — which, in its own way, is better.
When learning C, something small broke everything open. I tried to assign:
int number = scanf("%d")
(which does compile, by the way — don't try this at home)
And I stopped. Because I realized I didn't understand why this was wrong. And then came the question that changed how I think:
"Wait, if C can't assign a number straight up without being pointed to it, how can Ruby do it?"
The answer was the aha moment I didn't know I was looking for. Ruby under the hood calls on C. An implementation language, compiling to assembly, calling on the machine. All that warmth and elegance in Ruby — it's C, being kind on its behalf.
Suddenly for loops in C made sense. getf, scanf, the networking code I'd built in Go while reading someone else's work without fully grasping it — it all clicked into place. I still don't know what I'll build next, but I study Ruby and C every day. And honestly, that's not even what I'm studying. I'm studying the machine. Whatever I build next will be built on real ground.
This is a magnificent feeling. Though I know it's still so little of what there is to know. So back to programming I go.
One more thing before I close this entry: removing my own laziness helped me more than I expected. I learned MiniTest this week, myself, without shortcuts. I got it. Name the class after what you want to test, set up what you expect, assert that it happens. Every developer knows this. But I had been lazy — letting AI write my tests like it was doing me a favor. It wasn't. AI, as capable as it is, cannot know exactly what I want to test. Only I know that. This applies to everything in my second coding journey: I want to know everything. Do things the slow way. The understanding will compound.
Entry Two
Job hunting as a self-taught developer with no production experience is a particular kind of humbling. You apply, you wait, you get rejected, you improve, you repeat. At some point I decided: stop waiting for someone to give me production experience. Go make some.
I reached out to people I knew who needed web development work. Anything, really. And I ended up building Rails apps and Go binaries for real clients. Suddenly, with actual understanding behind me, even Go became fun. (yes, even the for loops) I could finally answer the question every developer gets asked and most dodge: what's your stack?
| Language | Purpose | |----------------|----------------------| | Ruby | Logic | | Go | Systems | | JavaScript | Client visuals |
Clean. Considered. Mine.
I still don't entirely know what this blog post is supposed to say. But I think that's the point. I'm writing my technical feelings as I go, and they keep arriving in the right order.
Entry Three
I found something worth writing down.
To say you won't use AI in this age is to fall behind. To depend on AI entirely is to become ignorant — literally, you stop paying attention. The right relationship with AI is the same as any powerful tool: you have to know what you're asking for, or the tool can't help you.
This became very clear recently. While building a model for a client, my habit of testing each feature as I wrote it quietly became TDD — I didn't plan it that way. But as I wrote tests, they failed. Fields I'd written in haste were missing validation. assert_not told me the truth before the client ever could. (I'm being vague — private work.)
Here's what I realized: because I had written a precise test, AI could fill in exactly what was missing. One clear instruction. Done.
If I truly know what I'm trying to do, I can write the test first — or alongside — and ask AI to help build the feature. As long as I own the tests, I own the work.
Most people seem to suggest the opposite: let AI write the tests, save time. But my stubbornness about truly understanding things led me somewhere different. I think I've come full circle.
I use tests, or I don't ship anything at all. Saving time on tests is technical debt at best, gross negligence at worst. That's the standard I hold myself to.
These days, before I write a single line, I ask myself: do I know how to test this? If the answer is no, I'm not ready to build it yet. I'm still slow. I still look things up constantly. But the things I build tend to last, and I tend to understand why. That feels like the right trade for now.
Entry Four
Something shifted recently, and I want to write it down before it becomes obvious and I forget it was ever a discovery.
I was building a small GUI in Go using Raylib — not for a client, just for fun. Programming has always been my hobby as much as my work, and I wanted to see if I could one day make a small point-and-click adventure. Difficult, humbling, enjoyable. Exactly the right kind of project.
While doing this I found myself writing Go logic, Go tests, hitting Go-specific patterns I had never needed before. Function passing. Closures — where a function returns a function and the variable becomes it, capturing the surrounding state. I picked that up by running into it, not by reading about it. That is the only way I seem to actually learn anything.
And then a thought arrived: if I keep shifting between languages, I will be permanently shallow in all of them.
Ruby, Go, JavaScript — the table in Entry Two still holds. Each language has its place. But in my own practice, outside client work, I stopped shifting. I am not chasing breadth anymore. I am staying in one place long enough to find the edges, the idioms, the surprises. Seeking mastery over coverage.
I do not know everything about Go. I probably never will. But I know more than I did when I was borrowing other people's networking code and hoping for the best. And the understanding keeps compounding, slowly, in the right direction.
That feels like enough for now.