What I would study first, if I were starting over self-taught
I taught myself to program with no background in the field. Swift was the first language, learned from blog posts, free courses and YouTube, and I started my first job in March 2022.
Four years later, the gaps that actually cost me were not the ones I spent my evenings worrying about. This is what I would tell myself at the start.
What did not matter as much as I feared
Not having a degree. It came up in exactly one interview, as a question about how I learned rather than as an objection. What people asked about was work — a shipped app, code they could read.
Not knowing algorithms in depth. I could not have written a red-black tree and it has never come up. Knowing that a hash lookup is O(1) and a linear scan is O(n) — and noticing when one is nested inside a loop — has come up constantly. That is a much smaller body of knowledge than “algorithms” implies.
Not knowing a second language early. I felt behind for only knowing Swift. In practice, going deep on one language taught me more transferable ideas than a shallow tour of four would have.
What mattered far more than I expected
1. How the machine actually works
The single largest gap. Not assembly — the shape of things.
What a pointer is. Why stack allocation is cheap and heap allocation is not. What a cache line is and why iterating an array is faster than following a linked list. What a system call costs. Why copying a large array is slower than copying a reference to it.
Without this, “performance” is a set of memorised rules. With it, most performance questions become
things you can reason about from first principles. It also makes the parts of Swift that seem
arbitrary — copy-on-write, value semantics, inout, ARC — obviously necessary.
What I would read: Computer Systems: A Programmer’s Perspective, slowly, the first six chapters. Or build something small in C or Rust where none of it is hidden.
2. Git, properly
I used add, commit, push, pull for two years, and every unusual situation was a crisis.
Learning what a commit actually is — a snapshot plus a parent pointer — makes rebase, cherry-pick, reflog and bisect all obvious consequences rather than four separate incantations. It is an afternoon’s work and it removes a recurring low-grade fear.
git reflog alone has saved me more times than I can count. You cannot lose committed work, and not
knowing that costs you sleep.
3. Testing, and specifically what to test
I wrote no tests for a year, then wrote bad ones for a year — asserting that a getter returned what the setter set.
What I wish I had learned earlier is that a test is a design tool before it is a safety net. Code that is hard to test is usually code with tangled dependencies, and the test is telling you about the design. Wanting to write a test for a network layer is what taught me dependency injection, more effectively than any article about dependency injection.
The rule that helped most: test the thing that would be embarrassing if it broke. Not coverage — consequences.
4. Reading other people’s code
I avoided large codebases for years because they were intimidating. That was a mistake, and it was the habit that changed my rate of improvement the most when I fixed it.
The Swift standard library is readable. So is much of swift-corelibs, and any well-maintained
open-source package. Reading Array’s implementation taught me more about copy-on-write in an hour
than a week of blog posts.
The skill is not reading a whole codebase. It is picking one function you use daily and following it down two levels.
5. Writing
Being able to explain a technical decision in clear prose has affected my career more than any individual technical skill.
Pull request descriptions, bug reports, a design document that gets a room to agree, an email that gets a customer to send the information you actually need. And explaining something is the fastest way to discover you did not understand it — which is most of why this site exists.
The order I would do it in
- Ship something. A small, finished, published thing beats six months of tutorials. Finishing teaches you the parts nobody makes courses about — release, crashes, users doing things you did not predict.
- Then go one level down on whatever you just used. You used a database — what is an index? You used a network library — what is a TCP handshake?
- Then learn the tools properly. Git, the debugger, the profiler. Each is a day and each pays back weekly.
- Then a second language, deliberately unlike the first. Swift then Rust taught me what was Swift and what was programming. Swift then Kotlin would have taught me much less.
The thing that matters most
Consistency beats intensity, and it is not close. An hour a day for a year is roughly 365 hours and it compounds; a fourteen-hour weekend is 14 hours and you remember very little of it.
Four years in, I am still mostly filling in gaps, and I expect that to be permanently true. That turned out to be the normal state of the job rather than a symptom of having started without a degree — which is the thing I would most like to have known at the beginning.