You think you understand how software works. You don’t. Neither did I — until I deleted the assembler and linker from my workflow and wrote programs the way the machine actually reads them.
Most programmers live inside a comfortable abstraction layer cake. You write in a high-level language. A compiler turns that into assembly. An assembler turns that into object code. A linker stitches it together. You press a button and a binary appears. Magic.
The tools that make you fast are the same tools that make you blind.
When you strip away the assembler and the linker — when you hand-encode machine instructions and manually place every byte in memory — something breaks open. Not the program. You. Your assumptions about what a program even is dissolve. A program isn’t a file. It isn’t a function. It’s a sequence of bytes that a CPU interprets as commands. That’s it. Everything above that is a convenience someone built for you.
I’m not saying convenience is bad. I’m saying convenience without comprehension is dangerous.
Think about what happens when your build pipeline fails. When the linker throws an undefined symbol error. When the assembler chokes on a directive you’ve never seen. Most developers panic, copy a Stack Overflow answer, and move on. They never ask: what is the linker actually doing? What bytes is the assembler emitting? They can’t ask, because they’ve never seen the raw machine code their entire career depends on.
Every layer of abstraction is a promise the system makes — and a lie it tells.
Here’s what happened when I went raw. I had to encode x86-64 instructions by hand. I had to calculate offsets for jumps. I had to lay out an ELF binary byte by byte — the header, the program headers, the code segment, the entry point. No assembler to translate mnemonics. No linker to resolve addresses. Just me, a hex editor, and the Intel instruction set reference.
It was agonizing. It was slow. It was the most illuminating thing I’ve done in years.
I learned that a jmp instruction isn’t one thing — it’s a family of opcodes with different encodings depending on how far you’re jumping. I learned that the ELF format is shockingly human-readable once you stop treating it as a black box. I learned that a static binary with no dependencies can be 452 bytes. Not 452 kilobytes. 452 bytes. Your last Docker image was 900 megabytes.
The distance between what your program needs and what your toolchain gives you is measured in orders of magnitude.
Now, am I suggesting you write production code this way? Of course not. That would be insane. But that’s exactly the point — the fact that it would be insane tells you how dependent you’ve become on tools you don’t understand. You’re driving a car and you’ve never opened the hood. You’re a surgeon who’s never touched a scalpel without a robotic arm.
The developers who build the tools you use — the compiler engineers, the OS kernel hackers, the runtime designers — they all know what’s underneath. They live in the machine. That’s why they have agency over the systems you merely use.
Agency isn’t given. It’s taken — one layer of abstraction at a time.
There’s a deeper lesson here that goes beyond programming. Every complex system in your life — your government, your financial infrastructure, your cloud provider — is a stack of abstractions built for your convenience. They work until they don’t. And when they don’t, the people who understand the bottom layer are the ones who survive. Everyone else fills out a support ticket and waits.
I’m not asking you to abandon your tools. I’m asking you to go down there once. Just once. Write a program in raw machine code. Build a binary by hand. Feel the machine under your fingers the way the pioneers did on the Apple II and the Amiga, when there was no assembler, no linker, no safety net — just you and the silicon.
Because here’s the truth nobody tells you: the abstraction isn’t the craft. The machine is the craft. Everything else is scaffolding.
If you can’t build without your tools, your tools own you.
FAQ
Q: Isn't this just gatekeeping? Most developers don't need to know machine code.
A: No. Gatekeeping is telling people they can't program without knowing this. What I'm saying is different: if you NEVER go down to the metal, you're outsourcing your understanding to a black box. You don't need to live there. You need to visit once.
Q: What's the practical payoff? I ship features, not bytes.
A: Debugging impossible build errors. Understanding performance at a level no profiler can show you. Writing smaller, faster, more secure binaries. And — critically — having the confidence that comes from knowing exactly what your code becomes after the toolchain is done with it.
Q: You really think modern toolchains are a problem?
A: The toolchains aren't the problem. The amnesia is. We've created a generation of developers who can ship at incredible speed but can't explain what a relocation entry is or why their binary is 200MB. Speed without understanding is a ticking bomb.