What CS 162 Actually Taught Me About Writing Software
An OS course sounds irrelevant to application-level engineering. It isn't. Here's the mental model I built that changed how I think about every concurrent system I touch.
CS 162 (Operating Systems) was the hardest class I've taken at Berkeley. Four projects in PintOS, a scheduler, a user-mode shell, a virtual memory system, a file system. Office hours queues that ran three hours long.
Six months later, it's also the class I think about most.
The Insight That Changed Everything
Before 162, I treated threads like magical background workers. I'd spin one up, share some state, and hope for the best — occasionally puzzling over a race condition that only appeared under load.
The course forced me to think about what a thread actually is: a PC, a stack, a register file. That's it. The shared heap isn't the thread's, it's the process's. The OS scheduler can preempt your thread at any instruction boundary. Any variable access that isn't atomic is potentially broken under concurrency.
Once you really internalize this, you start seeing race conditions before you write them.
The Synchronization Primitives Are Not Enough
The course teaches locks and condition variables until they're automatic. But the deeper lesson is the pattern: monitor invariants.
Every shared data structure has invariants that must hold whenever the lock isn't held. You acquire the lock, temporarily break the invariant to make progress, then restore it before releasing. Condition variables let you sleep until the invariant can be restored.
This sounds obvious written out. But I've reviewed a lot of code since then where these invariants are implicit, inconsistently maintained, and the source of subtle bugs.
How This Maps to Application Code
The same pattern shows up everywhere:
- React state: the invariant is the consistency between your state and the UI.
setStateis your "release lock and wake listeners." - Database transactions: the invariant is data integrity. Transactions are monitors with MVCC as the sleep-and-retry mechanism.
- Message queues: the invariant is message-at-least-once or exactly-once delivery. ACKs are how you signal the invariant is restored.
When something breaks in a distributed system, I now start by asking: what's the invariant, when is it supposed to hold, and is there a window where two actors can both think it holds while making conflicting modifications?
The Actual Useful Thing from Virtual Memory
Page faults. Specifically: demand paging is the original lazy evaluation. The OS pretends memory is there before it actually is, only doing real work when you touch a page.
This mental model is directly useful in ML. Lazy loading datasets, memory-mapped files, torch.no_grad() — they're all trading the same thing: pay the cost only when you have to. Knowing why this is safe (and when it isn't) comes from understanding that the memory system has been doing this since the 1960s.
If you're a Berkeley student wondering whether to take 162: take it. The waitlist is worth fighting through.