Reading Code Like an Editor
The first pass is for structure, the second is for intent. Most bugs live between them.
When I review a change, I read it twice, and the two passes are looking for different things. The first pass is structural: what files changed, how the data moves, where the seams are. The second pass is about intent: does this code mean what the author thinks it means?
Most of the bugs I have caught in review live in the gap between those passes.
Pass one: structure
On the first pass I deliberately ignore details. I want a map. Which module owns the state, which function is the entry point, what is new versus moved. If I cannot sketch the shape of the change on a napkin, I am not ready to read the lines.
- Skim for shape. Additions, deletions, and moved blocks show where the author's attention went.
- Find the entry point. Every change has one; start there and follow the data.
- Name the seams. Interfaces and boundaries are where assumptions leak.
Pass two: intent
Only now do I read line by line. The question is not whether it compiles but whether it does what the name says. Naming is where intent is either preserved or lost, and a misleading name is a bug waiting for a reader.
I keep a short list of smells that almost always mean a second look:
- A function whose body needs a comment to explain its name.
- A boolean parameter that changes behaviour rather than configuration.
- A catch block that logs and continues.
- A test that asserts on a mock instead of an outcome.
The trick that helps most
Read the tests before the implementation. Tests are the author's attempt to state intent in executable form. When the tests and the code disagree, one of them is wrong, and the argument between them is where the real review happens.
A note on tone
Review is a conversation about the code, not the person. The most useful comment I have ever received was not 'this is wrong' but 'I do not understand why this is here'. That is an invitation, and it is much easier to answer.