There's a well-known (if hard to pin down precisely) idea in software engineering: developers spend far more time reading code than writing it — often cited at somewhere around 10 times more. Whatever the exact ratio, the underlying truth is one every working developer recognizes instantly. The job isn't really "write code." It's "understand systems well enough that the code you eventually write fits correctly into them." And understanding, almost by definition, is a reading-heavy activity.
This article looks at where all that reading actually goes — the codebases, the APIs, the documentation, the forums, the GitHub issues, the AI tools — and the strategies professionals use to make sense of unfamiliar systems quickly and reliably.
Reading Existing Codebases: The Real Onboarding
Ask any developer who's joined a new team what their first two weeks looked like, and "reading code" will dominate the answer — far more than writing it. Before you can safely change a system, you need a mental model of it: what depends on what, where the dangerous edges are, and which parts are stable versus which are actively being rewritten.
This kind of reading isn't passive skimming. Experienced developers approach an unfamiliar codebase with specific strategies:
-
Start from the entry point. Where does execution begin? A
main()function, an HTTP router, a job queue consumer — finding the entry point gives you a thread to follow outward. - Follow the data, not just the code. Understanding how a request or a piece of data moves through a system — what transforms it, what validates it, where it's persisted — often reveals architecture faster than reading files in alphabetical order ever would.
- Use the test suite as documentation. Well-written tests describe intended behavior more reliably than comments do, because tests have to stay correct or the build breaks. Reading tests first, before diving into implementation, is a common and effective shortcut.
-
Look at git history and blame.
git log -pon a file, orgit blameon a confusing line, often surfaces the commit message or PR that explains why something was written a certain way — context that's invisible from the code alone. - Trace one feature end-to-end before trying to understand everything. Trying to hold an entire large codebase in your head at once is a losing strategy. Picking a single user-facing feature and following it through every layer it touches builds a working mental model much faster.
This kind of reading is slow at first and gets dramatically faster with repetition — which is exactly why senior developers, who've internalized common architectural patterns, can often navigate a brand-new codebase faster than the team's own junior developers, despite having zero prior exposure to it.
Understanding APIs: Reading Before Calling
Every time a developer integrates a new API — internal or third-party — there's a reading phase before there's a calling phase. This includes:
- Reading the API reference for the specific endpoint or method, not just skimming a quickstart example.
- Checking request and response shapes carefully, including optional fields, default values, and what happens when fields are omitted.
- Reading about authentication and rate limits, which are frequently the source of production incidents that have nothing to do with business logic and everything to do with not reading the fine print.
- Reading error responses and status codes, since how an API fails is often more important to handle correctly than how it succeeds.
For internal APIs without polished documentation, this reading often shifts to the source code directly — the controller or handler function, the request validation logic, and any middleware that touches the request before it reaches business logic. In statically typed languages like Go, type signatures themselves become a form of documentation: reading a function's parameter and return types often answers questions faster than prose ever could.
Documentation: The First Stop, Not the Last Resort
Official documentation remains the highest-signal resource available, even in an era of AI-assisted coding — precisely because it's maintained by the people who built the thing. Reading documentation effectively is itself a skill:
- Read changelogs before adopting a new version. Breaking changes, deprecations, and migration notes live here, and skipping this step is a common source of "it worked yesterday" bugs.
- Read the "why," not just the "how." The best documentation explains design philosophy and trade-offs, not just method signatures. Understanding why a library is structured a certain way makes it far easier to use correctly.
-
Cross-reference multiple doc sources. Official docs, inline code comments, and auto-generated API references (like Go's
pkg.go.devor Python's Sphinx-generated docs) often complement each other — one might have a conceptual overview while another has precise type signatures.
Stack Overflow, GitHub Issues, and AI Tools: Reading the Crowd
When official documentation runs out — and it often does, especially for obscure errors or edge-case behavior — developers turn to community sources. But reading these effectively requires some skepticism and triage:
Stack Overflow remains useful, but experienced developers read it critically: checking the date of an answer, the version of the library being discussed, and the comments underneath an accepted answer, which frequently contain corrections or caveats the original answer missed.
GitHub Issues are often a richer resource than Stack Overflow for fast-moving libraries, because maintainers respond directly, and you can often find the exact bug you're hitting, described by someone else, with a workaround or a linked fix. Reading strategies here include:
- Searching closed issues, not just open ones — many questions have already been answered and the issue closed.
- Reading the linked pull request that closed an issue, since it often shows the actual code change and reasoning.
- Checking whether an issue is still open in the latest version, since library behavior shifts over time.
AI coding tools (chat-based assistants, in-editor completions) have added a new layer to this workflow, but they haven't replaced reading — they've shifted what gets read. Developers now frequently read AI-generated explanations or code suggestions critically, verifying claims against actual documentation or source code before trusting them, since AI tools can be confidently wrong about API details, especially for fast-changing libraries or less common languages. The most effective developers treat AI output as a draft or a starting hypothesis worth verifying, not a final, citable source.
Learning Unfamiliar Technologies: A Reading-Heavy Process
When picking up a new language, framework, or tool, the actual hands-on coding usually comes after a substantial reading phase:
- Official "getting started" guides for first exposure to syntax and core concepts.
- Idiomatic style guides (like Go's official style guidelines or a framework's recommended project structure), since writing code that merely works is different from writing code that fits the ecosystem's conventions.
- Reading well-regarded open source projects in that technology to see real-world patterns, not just textbook examples.
- Reading the standard library, especially in languages like Go, where the standard library is intentionally written to be exemplary, readable code — making it a learning resource in its own right.
This reading-first approach pays off because it front-loads pattern recognition. A developer who's read several idiomatic Go projects will write more idiomatic Go from the start, rather than writing Python-flavored Go and fixing it later through trial and error.
Reverse Engineering Projects: Reading With No Map
Sometimes there's no documentation at all — an inherited project, an abandoned internal tool, or legacy code with the original author long gone. This is reading at its most demanding, and it has its own toolkit:
- Static analysis and dependency graphs, used to map out which modules depend on which, before reading any individual file in depth.
- Running the code with a debugger attached, stepping through execution to see actual runtime behavior rather than inferring it from static reading alone.
- Searching for any test coverage, however thin, since tests — even outdated ones — hint at original intent.
-
Looking for old commit messages, README fragments, or even comments marked
TODOorFIXME, which often preserve fragments of reasoning that never made it into formal documentation.
Reverse engineering also requires resisting the urge to rewrite immediately. Reading first — understanding what a confusing piece of code is actually accomplishing, even if it's accomplishing it badly — prevents the common mistake of "fixing" code in a way that silently breaks an edge case the original (ugly) implementation was actually handling correctly.
Debugging by Reading: The Compiler and the Stack Trace as Documents
Debugging is, in large part, an exercise in careful reading. Compiler errors, stack traces, and log output are dense, information-rich documents — and reading them properly is a skill that separates fast debugging from frustrated guesswork.
- Reading the full stack trace, not just the top line. The actual point of failure is sometimes several frames removed from where the error message first appears.
- Reading compiler errors literally before interpreting them loosely. A surprising number of confusing compiler errors say exactly what's wrong, but get misread because the developer is scanning rather than reading carefully — especially with verbose error output in languages like TypeScript or Rust.
- Reading logs around the failure, not just at it. The events leading up to an error often explain why it happened, not just that it happened.
- Reading the diff between working and broken states. When something "used to work," comparing the current code against a known-good commit is often faster than reasoning about the system from scratch.
This is also where reading and writing genuinely intersect: a well-placed debug log statement is, in effect, writing code in order to generate something to read. Debugging is less about typing fixes and more about generating and interpreting evidence.
Search Strategies Professionals Use
Effective searching is itself a reading-adjacent skill, and experienced developers tend to search more precisely than newer developers:
- Searching error messages verbatim, in quotes, to find exact matches rather than vague paraphrases that return irrelevant results.
- Stripping out variable names and file paths from error messages before searching, since those are unique to your codebase and won't appear in anyone else's search results.
-
Searching within a specific GitHub repository using
repo:qualifiers, rather than the open web, when the issue is likely library-specific. -
Searching code itself, using tools like GitHub's code search or
grep/ripgreplocally, to find every usage of a function or pattern across a codebase — often faster and more reliable than searching documentation for the same answer. - Iterating on search terms deliberately — starting broad, then narrowing based on what the first round of results reveals about the right vocabulary for the problem.
This last point matters more than it might seem. Knowing the correct terminology for a problem is often the actual bottleneck in solving it — once you know it's called a "race condition" or a "N+1 query," the search results (and the fix) tend to follow quickly.
Key Takeaways
- Reading dominates the job because understanding precedes writing. Code that's written without first understanding the surrounding system tends to be code that gets rewritten later.
- Tests and git history are underrated documentation. They often explain intent more reliably than comments, which can go stale.
- Community resources require critical reading, not blind trust. Dates, versions, and comment threads matter as much as the accepted answer itself.
- AI tools shift but don't eliminate reading. Verifying AI-generated suggestions against real documentation or source code is now part of the reading workflow, not a replacement for it.
- Debugging is a reading discipline. Stack traces, compiler errors, and logs are dense documents that reward careful, literal reading over quick scanning.
- Precise search vocabulary is a force multiplier. Knowing what to call a problem is frequently the real unlock, more than the search tool itself.
Writing code will always be the most visible part of the job — it's the part that compiles, ships, and shows up in a pull request. But the quiet, constant activity underneath it all is reading: of codebases, APIs, documentation, community knowledge, and the system's own error messages. Developers who read deliberately and critically tend to write code faster, with fewer surprises, than developers who skip straight to typing. In that sense, becoming a better developer is, in no small part, about becoming a better reader.
Top comments (0)