< expLog

EE > Orient yourself: construct a mental model

With your goals giving you a sense of direction, it's time to orient yourself in the system: and that means building a mental model of how things work, where the code is, how it happens to be running in production, and building mechanical sympathy with the system in general.

Build a first approximation by thinking through how you would build it

To get started, I take whatever I understand of the project, and imagine how I would have built it myself. There are generally a lot of assumptions I'll have to make, as someone completely new to the area; but at least I'll have taken a stab at reasoning through the consequences of those assumptions. Most of us do this implicitly anyways as we make assumptions on how things work; I just like to make it a little more explicit.

Reading about the architectures of different systems including their corresponding tradeoffs, conducting – and preparing for – system design interviews, and simply experiencing different software stacks will help build your muscles here. I highly recommend looking up the AOSA series of books, Beautiful Code, and engineering blogs by different companies (or their summaries).

Then see what is, and contrast it with what you expected

The risk with building the first approximation is confirmation bias: you might start exploring looking for proof that your first approximation is correct. Instead, I want you to do the exact opposite: look for places where your mental model does not match reality at all. The more surprising the difference, the more you have to learn in that area, and the more carefully you want to test your assumptions.

I'll remind you again later in this note, but you must respect Chesterton's fence and understand why things are the way they are, and how they got to the point they're at.

For example, your solution to the problem might have assumed that certain data could be served from cache; but you find that the actual code simply recomputes it each time – digging deeper you might find that the cache hit rate was remarkably low and the added cost & complexity of the cache simply weren't worth it. Or you might find that the original authors had planned to add a cache, but simply never got around to it.

Only believe empirical evidence

Anybody who thinks “Just read the code and think about it” – that's an insane statement – you can't even read all the code in a big system, you have to do experiments on the system.

John Carmack

One of my most frequent mistakes – also repeated constantly by candidates I've interviewed – is to assume the program we wrote (or read) does what we think it does without actually running it. You need to take a look at how the system is actually running, preferably in production, before believing you understand it in any meaningful way.

The best antidote to confirmation bias is to actively look for counter-examples; and looking for real metrics, numbers, core-dumps and logs is easily the best way to identify where things just don't match your expectations. Though if I'm honest, I generally distrust instrumentation till I've had a chance to validate it for myself.

Making sure you're looking at behavior in production is also very important: “it runs on my computer” is necessary, but nowhere near sufficient for claiming understanding of your software. For something like a mobile app, the complexity isn't necessarily in the code within the app itself but in how it interacts with the surrounding operating system. Unit tests can help, but integration tests and production runs are much more truthful.


Test for conscious competence

I feel comfortable with my mental model of a system when I can generally zero in on bugs simply based on a description of where they happen and the symptoms observed; at that point I have some confidence in being consciously competent and start believing my intuition. A little.

Further reading: Copy Construct has written extensively on effective mental models for programmers.