A Self-Directed Curriculum for Recovering Engineers
SummaryA structured, layer-by-layer self-study plan for engineers who...
A structured, layer-by-layer self-study plan for engineers who...
A structured, layer-by-layer self-study plan for engineers who want to rebuild their understanding from the metal up. Covers hardware/memory, operating systems, networking, databases, and distributed systems — each with one essential book, one project to build, and one habit to cultivate. Includes a complete HTTP server project specification with working Python code, and realistic time estimates for engineers with limited weekly hours.
A Self-Directed Curriculum for Recovering Engineers
You’ve spent the last fifteen chapters watching abstractions steal understanding from underneath you. Now you do something about it.
This is not a reading list. Reading lists are where ambition goes to die. This is a structured curriculum organized by the layers of the system you work on every day but have never actually seen. Each layer gets one book, one project, and one habit. The book teaches you the vocabulary and the mental model. The project forces you to confront the reality that the model is cleaner than the world. The habit makes sure you don’t forget what you learn the moment you go back to writing CRUD endpoints.
You don’t need to do all of this. You need to do enough of it that when something breaks at 3 AM, you can reason about where and why instead of refreshing the dashboard and hoping the metrics change color.
How This Curriculum Works
Start at whatever layer scares you the most. If you don’t know which layer that is, start at Layer 1 — because if you don’t understand how memory works, everything above it is superstition.
For each layer, the structure is identical:
- The Book: One. Not three. One book that gives you the conceptual foundation. I’ll tell you which chapters to read and which to skip.
- The Project: One build that forces you to make real decisions at that layer. Not a tutorial you copy-paste. A specification you have to satisfy, with enough ambiguity that you’ll have to think.
- The Habit: One ongoing practice that keeps the knowledge alive after you finish the book and the project.
Estimated total time if you work through all five layers: 12–18 months at 3–5 hours per week. You can do a single layer in 8–12 weeks.
Layer 1: Hardware and Memory
The Book: Computer Systems: A Programmer’s Perspective (Bryant & O’Hallaron), universally known as CSAPP.
Read chapters 1, 3, 6, and 9. Chapter 1 gives you the landscape. Chapter 3 teaches you what your code actually becomes after the compiler touches it. Chapter 6 is the memory hierarchy — caches, locality, the reason your “O(n)” algorithm runs ten times slower than a different “O(n)” algorithm. Chapter 9 is virtual memory, the mechanism that makes every process believe it owns the entire machine.
Skip chapters 2, 4, and 5 on a first pass. They’re valuable but not critical for a working programmer recovering from abstraction dependence.
Time estimate: 6–8 weeks at 3 hours/week.
The Project: Build a memory allocator. Implement malloc() and free() in C using a free list. Your allocator needs to handle allocation, deallocation, coalescing adjacent free blocks, and splitting blocks that are larger than requested. You’ll learn why fragmentation exists, why allocators are complex, and why garbage collectors are both a gift and a curse.
The Habit: Before optimizing any code, check the memory access pattern. Run perf stat on your hot paths. Look at cache miss rates. Ask yourself whether your data structure is cache-friendly or whether you’re chasing pointers across the heap. This takes five minutes and will occasionally save you five days.
Layer 2: Operating Systems
The Book: Operating Systems: Three Easy Pieces (Arpaci-Dusseau & Arpaci-Dusseau), known as OSTEP. It’s free online, which removes your last excuse.
Read the virtualization and concurrency sections cover to cover. The persistence section is valuable but overlaps with the database layer below. OSTEP is the rare textbook that is genuinely well-written. The dialogue sections aren’t filler — they anticipate exactly the questions you’ll have.
Time estimate: 6–8 weeks at 3 hours/week.
The Project: Build your own shell. Not a toy that executes single commands — a shell that handles pipes, redirection, background processes, signal handling, and job control. Write it in C or Python. When you’re done, you’ll understand fork(), exec(), file descriptors, and process groups — concepts that underpin every container, every deployment, and every CI/CD pipeline you use.
If you want to go deeper, work through the xv6 lab exercises from MIT 6.S081. xv6 is a teaching operating system small enough to read entirely but complete enough to be real. The labs have you implement system calls, page tables, and a file system.
The Habit: When a process behaves unexpectedly, use strace (Linux) or dtruss (macOS) before reaching for application logs. System call traces show you what your program is actually doing — which files it opens, which sockets it connects to, which signals it receives. Most “application bugs” are syscall misunderstandings.
Layer 3: Networking
The Book: TCP/IP Illustrated, Volume 1 (Stevens). Yes, it was published in 1994. TCP hasn’t changed. The principles of reliable delivery, flow control, and congestion avoidance are exactly as relevant today as they were then, because they’re solving fundamental problems, not trend-driven ones.
Read chapters 1–4 (link layer, IP, ARP basics), chapters 17–24 (TCP in detail), and chapter 14 (DNS). Skip the chapters on obsolete protocols, but don’t skip TCP. If you don’t understand TCP windows, retransmission, and TIME_WAIT, you don’t understand why your microservices have latency spikes.
Time estimate: 8–10 weeks at 3 hours/week.
The Project: Build an HTTP/1.1 server from raw sockets. No frameworks. No HTTP libraries. You, a socket, and RFC 2616.
Here’s the specification:
HTTP/1.1 Server — Project Specification
Must support:
GETandPOSTmethods- Serving static files from a configurable root directory
Content-Typedetection based on file extension (at minimum:.html,.css,.js,.png,.json)- Proper status codes: 200, 301, 400, 404, 405, 500
Content-Lengthheader on all responses- Concurrent connections (at least 10 simultaneous clients)
- Connection keep-alive (persistent connections)
- Request parsing that handles headers of varying length and case
Must not use:
http.server,flask,django,fastapi, or any HTTP framework- Any HTTP parsing library
asyncio(use threads orselect/pollfor concurrency — you need to understand the mechanism)
Stretch goals:
- Chunked transfer encoding
- Basic logging with timestamps
HEADmethod support- Directory listing for paths without
index.html
Here’s a minimal HTTP server in Python using raw sockets. This is your starting point, not your finish line — it handles only single connections and GET requests:
import socket
import os
def handle_request(client_socket, root_dir):
request = client_socket.recv(4096).decode('utf-8', errors='replace')
if not request:
client_socket.close()
return
lines = request.split('\r\n')
method, path, _ = lines[0].split(' ', 2)
if method != 'GET':
response = 'HTTP/1.1 405 Method Not Allowed\r\nContent-Length: 0\r\n\r\n'
client_socket.sendall(response.encode())
client_socket.close()
return
if path == '/':
path = '/index.html'
file_path = os.path.join(root_dir, path.lstrip('/'))
if os.path.isfile(file_path):
with open(file_path, 'rb') as f:
body = f.read()
ext = os.path.splitext(file_path)[1]
content_types = {
'.html': 'text/html', '.css': 'text/css',
'.js': 'application/javascript', '.json': 'application/json',
'.png': 'image/png',
}
ct = content_types.get(ext, 'application/octet-stream')
header = f'HTTP/1.1 200 OK\r\nContent-Type: {ct}\r\nContent-Length: {len(body)}\r\n\r\n'
client_socket.sendall(header.encode() + body)
else:
body = b'<h1>404 Not Found</h1>'
header = f'HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\nContent-Length: {len(body)}\r\n\r\n'
client_socket.sendall(header.encode() + body)
client_socket.close()
def start_server(host='0.0.0.0', port=8080, root_dir='./www'):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((host, port))
server.listen(10)
print(f'Serving {root_dir} on {host}:{port}')
while True:
client, addr = server.accept()
handle_request(client, root_dir)
if __name__ == '__main__':
start_server()
That’s 38 lines. It works. It also can’t handle two clients at once, doesn’t support POST, ignores keep-alive, and will choke on large requests. Fixing those things is the entire point. Every fix teaches you something about how HTTP actually works beneath the framework.
The Habit: When diagnosing a network issue, use tcpdump or Wireshark before reading application logs. Capture the actual packets. Look at TCP handshakes, retransmissions, and RST packets. You’ll find the problem in the packets more often than in the logs, because logs tell you what the application thinks happened. Packets tell you what actually happened.
Layer 4: Databases
The Book: Designing Data-Intensive Applications (Kleppmann), universally called DDIA. This is the single most important technical book for a working software engineer published in the last decade. Read it cover to cover, in order. Every chapter builds on the previous one.
If you take only one thing from this curriculum, make it this book. It explains storage engines, replication, partitioning, transactions, consistency models, batch processing, and stream processing — the entire landscape of how data moves through modern systems. When you finish it, you’ll understand why your database makes the choices it does, instead of just accepting them.
Time estimate: 8–10 weeks at 3 hours/week (it’s dense — read each chapter twice).
The Project: Build a key-value store with on-disk persistence. Implement a simplified LSM tree: writes go to an in-memory sorted structure (a balanced tree or sorted dict), and when it reaches a size threshold, it flushes to a sorted file on disk. Reads check the in-memory structure first, then scan disk files from newest to oldest. Implement compaction — merging multiple disk files into one.
You’ll understand why writes and reads have fundamentally different performance profiles, why write-ahead logs exist, and why your database’s performance characteristics change over time.
The Habit: Before writing a query, look at the execution plan. EXPLAIN ANALYZE in PostgreSQL, EXPLAIN in MySQL. Know what a sequential scan looks like versus an index scan. Know what a hash join costs versus a nested loop. This takes ten seconds per query and prevents entire categories of production incidents.
Layer 5: Distributed Systems
The Book: Start with DDIA chapters 5–9 if you haven’t read them yet (replication, partitioning, transactions, consistency). Then read the Raft paper — “In Search of an Understandable Consensus Algorithm” (Ongaro & Ousterhout, 2014). It’s 18 pages and it’s one of the clearest papers in computer science.
Time estimate: 10–12 weeks at 3–5 hours/week.
The Project: Work through the MIT 6.824 (now 6.5840) Distributed Systems labs. They have you build a key-value store backed by Raft consensus. The labs are publicly available and are the gold standard for hands-on distributed systems education. You’ll implement leader election, log replication, and snapshotting.
If you want something smaller, build a distributed hash table where three nodes coordinate to store and retrieve values, handling the case where any one node can go down.
The Habit: When designing any system with more than one node, draw the failure modes before drawing the architecture. Ask: “What happens when node A can’t reach node B but both can reach node C?” Ask: “What happens when a write succeeds on two replicas and fails on the third?” Every distributed system bug is a failure mode nobody drew on the whiteboard.
The 6-Month Plan (3 Hours/Week)
If you have limited time, here’s how to sequence this:
Months 1–2: Layer 4 (Databases). Start with DDIA — it has the highest return on investment for a working engineer. Build the key-value store project.
Months 3–4: Layer 3 (Networking). Read Stevens selectively (TCP chapters). Build the HTTP server.
Months 5–6: Layer 1 (Hardware/Memory). Read CSAPP chapters 6 and 9 on caches and virtual memory. Build the memory allocator, or at minimum, run perf on code you’ve already written and interpret the results.
This sequence prioritizes practical impact: database knowledge pays off immediately, networking knowledge pays off within weeks, and memory knowledge pays off when you encounter performance problems that nothing else explains.
The Rule
One project at a time. One book at a time. Finish before you start the next one. The engineers who know the most about systems aren’t the ones who read the most — they’re the ones who finished things and sat with the confusion until it became understanding.
You’ve been relying on abstractions because they were handed to you and they worked. Now you know the cost. The curriculum above is not easy, but easy is how you got here. The way out is through the layers, one at a time, building things that break and fixing them until you understand why.