How to read code you didn't write
CS school teaches you to write code. Internships hand you a 200,000-line codebase and say "your first ticket is in there somewhere." This is the gap nobody warns you about, and the skill that actually separates productive interns from struggling ones. Here's how to read code you've never seen before, fast.
The wrong mental model
Most students approach an unfamiliar codebase the way they'd read a textbook: open the README, then start reading file by file from the top. This doesn't work, and not because students are bad at reading, it's because code wasn't written to be read top to bottom. It was written incrementally, by dozens of people, with constraints you can't see.
The professional move is the opposite: don't try to understand everything. Understand exactly what your current ticket needs you to understand. Build the rest of the mental map slowly, one feature at a time, over weeks.
The five-question framework
Before opening a single file, answer these five questions about the codebase:
- What does this app do? One sentence. "It's a marketplace for used cameras." If you can't answer this in one sentence, ask your manager.
- What's the technology stack? Look at
package.json,requirements.txt,go.mod,pom.xml, etc. Note framework names so you know what you're searching documentation for. - How is it deployed? Find
Dockerfile,docker-compose.yml,.github/workflows/, orfly.toml. This tells you where the entry point actually runs. - Where's the entry point? Usually
src/main.py,src/index.js,app.py,server.js. The file that boots the application. - How are tests organized? Find the
tests/or__tests__/directory. Tests are the most honest documentation of how things are supposed to work.
This takes 20 minutes. Now you have a map.
Read the entry point first, but only one level deep
Open the entry point file. Read it once, top to bottom. Don't follow imports yet. Just notice the shape:
- What modules are imported?
- What's the order of operations? (Connect to DB → load config → start server → register routes.)
- What environment variables does it expect?
You'll have questions. Write them down on a scratch document. Don't chase them yet, you'll waste a day going down rabbit holes. The point is to build a high-level mental model first.
Trace one feature end-to-end
Pick one user-visible feature you understand from the outside, usually the simplest one. "Login." "Display a list of products." "Submit a form."
Now trace it through the code from the user's first interaction to the database and back. For a web app, that's:
- The frontend component or page that the user sees.
- The API endpoint it calls (look at the
fetchoraxios.post). - The route handler on the backend.
- Any service / business-logic layer that handler calls.
- The database query or external API call at the bottom.
- The response coming back up.
This single trace teaches you more than reading 50 files in isolation. You see how the codebase fits together, its conventions, its layers, where it puts what kind of logic.
Use grep more than you think you should
The single most undervalued tool for reading unfamiliar code is full-text search. When you find a function call you don't understand, search the codebase for its name to find its definition. When you find a database table reference, search for it. When you find a route path like /api/users/:id, search for that exact string.
Tools that pay off:
- ripgrep (
rg): order of magnitude faster thangrep. Install it. - VS Code's "Find in Files" (Cmd/Ctrl+Shift+F): same idea, GUI.
- "Go to Definition" (F12): jump to the definition of any symbol. This is how you read modern code.
- "Find All References" (Shift+F12): see everywhere a function/class is used. Critical for understanding what something does, its callers tell you its actual purpose.
If you're not using "Go to Definition" and "Find References" constantly, you're reading code with one hand tied behind your back. Spend 20 minutes setting up your editor to support these in the language you're working in.
Practice on real codebases, not toy examples
InternQuest's missions are designed to feel like opening a stranger's repo on day one, broken, partially documented, and full of code you didn't write. The exact muscle you need for an internship.
Try a mission →Read the tests, then read the code
Tests are the cheat sheet for understanding a codebase. They tell you:
- What inputs the code expects.
- What outputs it produces.
- Which edge cases the previous engineer thought were important.
- What "correct behavior" actually means here.
If you're trying to understand a function, find its test file. The test file is usually a sibling: users.py ↔ tests/test_users.py, or UserCard.jsx ↔ UserCard.test.jsx. Read those tests first. They're often clearer than the implementation itself.
Read commits and pull requests for the "why"
Code tells you what. Commit messages tell you why. When you're staring at a piece of code that looks weird and you can't figure out the reason it's like that, run:
git log -p path/to/the/weird/file.py
# or in the editor: open file, then:
git blame path/to/the/weird/file.py
git blame annotates each line with the commit that introduced it. You can then read that commit's full message, and often there's a one-paragraph explanation of why that line is necessary, even if the code itself looks redundant.
If your team uses pull requests, the PR description for that commit usually has even more context. Search the merged PR list for the commit hash.
The "good first ticket" anti-pattern
Most teams give interns a tiny first ticket, a typo fix, a label change. The intent is to get you through the deploy pipeline once. But this is the worst possible first ticket for understanding the codebase, because you don't have to navigate any of it.
After your first deploy, push your manager for a real bug fix or a small feature. The bug fix forces you to read code outside your immediate ticket, which is exactly the muscle you're trying to develop. Two weeks of "real" tickets teaches you more about the codebase than a month of label changes.
What to do when you're lost
You will get lost. Constantly. Here's the loop that gets you unstuck:
- Articulate what you don't know in one sentence. "I don't know how the user gets attached to the request after login." Vague confusion ("this is hard") doesn't lead anywhere.
- Spend 30 minutes trying to answer it yourself, search the codebase, read tests, follow imports.
- If you're still lost, ask. The question should be specific: "I see
req.userbeing read in the route, but I can't find where it's set. Is it a middleware?" - Document what you learn. Keep a personal "codebase notes" doc with answers as you find them. Six weeks in, this becomes your superpower.
A 30-minute time box is critical. Spend an hour stuck and you've wasted productive time; spend three days stuck and you've lost confidence. Asking a question after 30 minutes of honest effort is professional, not weak.
Reading patterns by codebase type
Different codebases reward different reading strategies.
A web app with a backend and frontend
Trace one feature end-to-end (above). Then read the database schema or models, that's the spine of the application. Most logic exists to read, transform, and write the data those models represent.
A library or SDK
Read the public API first. Whatever is exposed by __init__.py, index.js, or the package's main exports, that's what users see. Everything else is implementation detail. Learn the public surface, then dive into one method's implementation when you need to.
A monorepo
Find the deployment manifests and root README. Monorepos contain N independent services; you usually only care about one of them. Identify which directory corresponds to your ticket, then read it as if it were a standalone repo.
Legacy code with no tests
Be very careful with changes. Read git history and any comments aggressively, those are the only documentation. Before changing anything, write a test that captures the current behavior so you can verify your fix doesn't break it.
The mindset shift
The trap students fall into is feeling like they should understand the whole codebase before doing anything. You won't, even people who've been at the company for five years don't fully understand it. They've memorized the parts they touch and have a vague map of the rest.
Your goal isn't comprehension. It's "enough understanding to ship this ticket without breaking things." That's a much smaller, achievable bar, and it's the standard professional engineers are actually held to.
You'll get a little better at reading code every single week. After six months at one company, you'll be able to make changes confidently in 80% of the codebase. After two years, you'll be the person new hires ask. The way you get there is one ticket at a time.
Build the code-reading muscle on broken codebases
InternQuest's missions drop you into an unfamiliar codebase with one specific bug to find. You navigate, hypothesize, fix, and ship, the same loop as your first day at a real internship.
Try a mission →