How to read code
Short summary
Code isn't like reading a book — execution order matters more than readability, and you're usually reading diffs not complete programs. The best approach is multiple passes through different code paths rather than front-to-back reading, and you still need to do this yourself even with AI-generated code.
- Code has three structural differences from English: execution order is determined by the computer not the reader, you read diffs (incremental changes) not finished products, and dependencies span entire codebases while grammatical dependencies stay within paragraphs.
- The "dyadic scanning" technique works better than sequential reading: make multiple passes tracing specific code paths (like the happy path), then fan out to understand how functions are used across call-sites, before doing a final careful end-to-end read.
- AI-generated code still requires careful human review because LLMs make alignment errors (solving problems that don't exist, over-engineering solutions) that don't show up as bugs but violate your engineering values.
Full summary
Code isn't like reading a book because it's built for computers, not humans. The execution order is determined by what the machine needs, not what makes sense to readers. You can't just rearrange lines for better flow. More importantly, engineers spend most of their time reading diffs—small changes to existing code—rather than entire programs from scratch. That's like reading successive drafts of an article one sentence at a time instead of the finished piece. On top of that, code is structurally far more complex than even difficult literature. A single sentence in Shakespeare might depend on context from one paragraph; a line of code can depend on dependencies scattered across thousands of files. War and Peace has about 600,000 words. Most large modern codebases have that many lines.
The standard approach—reading front-to-back like a book—fails because people lose track of execution flow or miss the significance of unchanged code. Goedecke recommends "dyadic scanning," borrowed from how mathematicians read papers. Make multiple passes at different zoom levels. Start by tracing an important path through the code (like the happy path for a new feature) to understand the overall flow without getting bogged down in details. Use tools like ctrl+f or ctrl+click to jump between function calls. Treat everything outside your current focus as a black box. Do separate passes following different threads, tracking how specific functions or data get used across the codebase. Only after you've built that mental map should you read end-to-end, which is now just about catching weird edge cases you missed.
The argument that AI eliminates the need to read code misses the real problem. LLMs produce code that often works but doesn't match your engineering values or constraints. Goedecke found an example where an AI agent noticed a harmless race condition and bloated a three-line change into a 3,000-line diff trying to "fix" it. Even if LLMs make no mistakes, they'll make different trade-offs than your team would. You can't just ask an LLM to review code for you and trust the output. You still have to carefully read both the code itself and any LLM analysis of it.