Learn systems, not langauges
Due to the nature of my story and the attention that it's received, as mentioned in my last post, I frequently get emails from developers, college kids, or other people with troubled pasts that are looking for advice on either how to learn or how to get into tech. This blog post will be a compilation of things I have typed out probably a hundred times now, which I hope might help anyone who seeks such advice, and which I can point to that effectively sums up my insight and learnings.
I went from not having written any code in 15+ years, to programming for ~16 hours a day for about three and a half years now. One of the things I am most thankful for, is my past experience programming, while it did not leave me with much remaining skill or experience, it did at least get me in the right direction, and I at least knew what I should be spending my time on.
At the start, I was only able to get the python interpreter and use the bundled 'IDLE' Tkinter python IDE (on a heavily locked-down Windows 10 laptop with 8GB of RAM btw), and there was no internet except at the Recreation building which we had limited hours in, only on weekdays. So I had to save wikipedia articles and copy + paste code from github and stack overflow into .txt files, because there was definitely no access to git at that time. Eventually started writing C and Go and building my own library of data structures before I got my first job a couple years ago. Everything was so fascinating to me, and I sat and absorbed every piece of tech culture, history and lore that I could find. A lot of things were coming back to me, and perhaps the nostalgia is what had so much of an impact on me, is that it reminded me of a time before I had thrown my life away, and things were much more simple.
Roughly twenty thousand hours of programming later, I now spend my work days hacking on database internals.
The first thing I would like to mention about this, is that there are zero short-cuts, and no substitute for all those hours spent. The best thing you can hope for is that the hours you spend will be as rewarding and fruitful as possible. You could easily spend the same amount of time misguided and unaware of what to learn, and end up squandering a significant portion of it.
It's called Tutorial hell because Tutorials are the devil.
There are great resources out there, like Harvard's CS50 or I'm sure some of the code academy lessons are great, and https://boot.dev
(shout-out to Lane, is a really good guy and although I never took any courses myself, I had recommended my 'protege' to sign up and use it to study and learn in between our weekly meetings and it really does a great job of teaching a lot of fundamentals in a structured and gamified way) is pretty great.
But the youtube tutorials
that claim you can learn X in Y minutes, or they have some project you follow along with, is a complete and utter waste of your time.
My biggest piece of advice can be boiled down to this:
Learn systems, not languages or frameworks.
One thing that confuses most new (and not so new) learners, is mistaking a language or framework itself, with the important aspect of 'building software'.
This applies especially for the demographic that I seem to hear from most frequently. People who, like myself, came from (usually back-end) Web, who maybe learned some Rust or Zig and now want to look for Rust jobs and are having a hard time. This is not uncommon because these languages have some considerable hype around them and are generally challenging and an exciting thing to learn.
The lesson here, is that most of companies hiring for Rust jobs are not looking for 'rust developers', they are usually looking for devs with a strong systems background. Usually the language will end up being in C++ or C, but that is almost completely irrelevant here. What they want is someone who deeply understands POSIX/OS API's, filesystem and networking I/O and protocols, memory, atomics and threads, etc, etc.
Learn the languages as a side effect of practicing and studying building software. Learn and experience for yourself what problems Rust solves before you spend time learning it. You cannot truly appreciate the ownership/borrowing model unless you have dealt with the frustration of keeping track of which poorly documented C libraries mutate or free
resources, or had to scan all your code to try to determine what the lifetime
of your own objects are after you have passed their pointers to libraries you wrote a while ago.
It's not any different if your goals are just to get into tech in general.. It may be tempting to spend all your time learning NextJS, React and tailwindcss, because those seem to be what companies are looking for. But the truth is that now that LLM's are quite good at generating this type of code, none of those things are actually valuable to a company by themselves in the long run, if you don't deeply understand the architecture of HTTP servers and web technology, how scalable web apps are built and shipped.
If you are looking to get into web, make sure you are gaining a fundamental understanding of the HTTP protocol, basic networking, containers and the docker/OCI ecosystem, databases, message queue's, learn SQL even if you always use an ORM. Learn (and experience for yourself) what problems React solves by building a reactive UI with just vanilla JS and the DOM API.
Prefer Go to Django, Laravel or NextJS, use net/http
to build your own web framework and understand how they work. Abstractions are great and come in very handy when you are just trying to ship things and you don't want to deal with building a session table and your own middleware to be able to call request.UserID()
or request.IsAdmin()/IsAuthenticated()
in your handlers, but if you don't understand how that framework knows who sent that request, and how they are logged in, then getting good at that framework is not really providing you any real long term value, I would argue that you are doing yourself a disservice by using it.
Ditch the file-explorer. Embrace the CLI.
I'm not saying that everyone needs to be a vim user, but I highly recommend it. You will thank me in a couple months if you spend the time to learn at least the bindings. Working out of the CLI makes you a better programmer, I don't care what anyone says. You may know some ultra cracked dev who clicks around file explorers and some light-mode IDE and knows or uses zero keybindings for anything... but that is the exception, not the rule. Computers take some input -> process it -> produce some output, and CLI tools help you think in this paradigm. I'm not saying you are wrong for using a GUI for everything, I'm saying learning to be good at the CLI is a major career hack if you want to be a programmer.
Install and configure a minimal installation of Arch Linux.
You want to be deeply familiar with your system, write and edit your config files yourself. If you need a cronjob setup or to write a SystemD service file, it should be second nature.
Write shell scripts to automate even normal things. I have hand written shell scripts, some of which are years old, that do stupid things like change my desktop wallpaper, update discord, prune my docker cache, build and deploy my blog, removed unused files from my ~/Downloads directory, initialize key re-mapping on startup, I have a slack bot that listens to a channel and plays the sound of police sirens when we have an incident that needs acknowledgement. These are all small but valuable things that create a better understanding of how all the software you interact with operates.
Build. Build. Build.
There is no substitute for building software if you want to get good at building software.
The following is a list of projects that I recommend. These are geared towards anyone interested in systems, but imho there should not be a distinction. Learn computer science if you want to be a good programmer.
NOTE: Most of these are not easy or beginner projects, these are for after you have learned to program and you are comfortably writing web back-ends.
All of these are really preferable to be written in either C, C++, Rust, or Zig. But C is preferred if you haven't written any of these languages.
hey, didn't he just say 'learn systems, not languages..
then proceeds to tell you specifically which language to use?
touche.. HN commentor... touche
-
Data structures:
Implement your own library for common things like a string type, dynamic array and hashmap, all from scratch. It's preferred that this is used in future projects.
-
coreutils:
cat/less, ls, mv/cp, and find/grep. For grep, you don't have to implement a whole regex engine, you can just do
glob
or have fun designing a more simple engine with custom rules. Otherwise you can make them as simple or as feature complete as you want -
shell:
Lots of fun, and a good warm up for an interpreter or compiler. Learn about environments, subprocesses, signals, traps, inheriting env vars, string interpolation and variable expansion. Learn why
cd
is the only shell command that works when you yeet your$PATH
, and howfork
andexec
work, what even is POSIX and why do people care when they complain that your favorite shell isn't compliant? The general point here is to be as familiar as possible with libc and POSIX/linux api's and system calls. -
http1 server:
Use raw linux sockets and bind/accept/send/recv syscalls, the HTTP parsing doesn't have to be completely up to spec of course, but it should handle basic requests. You can handle each connection on it's own thread, but it is really preferable to learn
epoll
and youraccept
should be non-blocking. Bonus points for connection pooling with an LRU cache. -
Interpreter/Compiler:
This is a lot of fun, can be any language.
writing an interpreter in Go
by Thorsten Ball is a good book (I haven't actually read it but I have heard really good things and I have skimmed some of it, plus the author is a gigachad 11x programmer). Demystify what is happening when you write and run code, you don't have to write an optimizing x86-64 back-end (I sure as hell didn't) to get a ton of value from this project. If this seems very advanced, maybe start by writing the super tiny compiler in your language of choice, or at the very least learn to write recursive descent parsers. Designing your own bytecode and VM is particularly fun, and its super valuable to learn about labels and jump tables and how loops and branches work at the assembly or bytecode level. -
Memory allocator (C):
Write a library that mmap's a large chunk of memory on initialization and learn about how to manage the book-keeping and fragmentation, and what trade-offs are made by different kinds of allocators. Ginger Bill has a fantastic series on allocators found here that I highly recommend.
-
Emulator:
The Chip8 is a great first emulator project. The 8080, and 6502 are much more advanced, but all are very valuable projects to understand particularly CPU internals, memory addressing and probably bit manipulation more than anything.
Some others are a Cache, a basic TUI Text Editor (bummer I have never got around to this one), Stackful coroutine based Async I/O library (this was a lot of fun in Rust, basically implementing goroutines, backed by io_uring).
This post is getting long and out of hand, maybe I'll refactor this out into a separate page of just the project recommendations and gather up some more useful links. But thanks for reading and I hope this is able to help someone.