"Read this codebase you've never seen"
📖 Walk me through it — plain English
This lesson is about a newer kind of interview round: instead of inventing an algorithm on a blank page, you're handed a real codebase you've never seen — think tens to hundreds of files — and asked to do three things: explain what it does, find where some feature lives, and make a small safe change. The skill being tested is code comprehension: can you find your way around code someone else wrote? That's literally what your first day at any real job feels like, which is why companies started checking for it. Quick jargon: a repo (repository) is just the folder of all the project's code; an entry point is where the program starts running; OSS means open-source software (publicly available code).
Here's the analogy. Imagine you walk into a huge building you've never visited and need to find a specific office. You don't sprint down random hallways opening every door. You read the lobby directory (what's on each floor), notice the layout (elevators here, offices there), then follow one clear path to a single known room to get your bearings. Reading an unfamiliar codebase is the same move: start from the outside and zoom in, instead of diving into one random file and getting lost.
So how do you actually approach it? Work outer-to-inner, in this order:
- Read the front door first. Open the README and the project's config file (
package.jsonfor JavaScript,pyproject.tomlfor Python). These tell you what the thing is, how to run it, and where it starts. - Map the building before exploring it. Glance at the folder names and say out loud what they probably hold: "routes here, business logic here, database models here." You're sketching the shape of the project, not reading code yet.
- Walk one path end to end. Pick a single user action (say, "log in") and follow it from where the request arrives all the way to the response. One full trace teaches you the structure faster than skimming ten files.
- Let the tests be your tour guide. Test files describe, in plain names, the behaviors the team cares about — they're documentation that's guaranteed to be true.
- When you hit something unknown, search, don't guess. See a name you don't recognize? Use search ("grep") to find everywhere it's used, and look at a real call to see what it does. Don't invent an answer in your head.
Two things that separate a strong showing from a weak one. First, narrate as you go — saying "this looks like a Flask app where each blueprint owns one resource" lets the interviewer hear you building a mental map, which is the whole point. Second, when you make the change, change the smallest thing that works and ideally back it with a test ("a test that fails before my change and passes after"). Adding fifty lines, or cleaning up unrelated code, signals you didn't understand the existing design — that's the opposite of the skill being measured. The practice rep is simple: grab a mid-size open-source project, set a 30-minute timer, read the README, trace one route end to end, and write three sentences on what it is. Do that five times before any onsite.
New in Google's 2026 loop. Spreading to mid-cap. You're dropped into an unfamiliar repo (50–500 files, often an OSS slice) and asked to: explain what it does, find where X happens, modify Y safely. Tests whether you can navigate code you didn't write — the day-1-on-the-job skill LeetCode never measured.
What the round actually is, term by term
It's worth slowing down and naming every piece, because the words get thrown around as if they're obvious. The code comprehension round is an interview where the artifact already exists — you don't write much, you read. The interviewer drops you into a repository you have never opened and gives you tasks of three flavors: a describe task ("what does this service do?"), a locate task ("where does rate-limiting happen?"), and a modify task ("make deleted users return a 410 instead of a 404"). The thing being graded is not whether you memorized an algorithm; it's whether you can build an accurate mental model of unfamiliar code quickly and act on it without breaking things.
A few load-bearing terms, defined inline so the page stands on its own:
- Entry point — the line(s) where execution begins. For a web server it's wherever the app object is created and routes get registered (
app.py,main.go,index.ts); for a CLI it's themain()the OS calls first. Everything else fans out from here, so it is the correct place to start a trace. - Data flow — the path a piece of data travels as the program runs: where it enters (a request body, a CLI arg, a file), how it is transformed (validated, mapped, computed on), and where it finally lands (a database row, an HTTP response, a log line). Following the data, not the file names, is what reveals the real structure.
- Side effect — anything a function does besides returning a value: writing to a database, sending an email, mutating a shared variable, printing, calling another service. Side effects are where bugs and surprises live, so spotting them is half of comprehension.
- Mental model — your internal map of how the system is laid out and how the pieces talk to each other ("requests hit routes, routes call services, services call repos, repos touch the DB"). A correct mental model lets you predict where code should be before you go looking for it.
- Invariant — a rule the code assumes is always true ("a user always has an id," "the list is already sorted," "this is only called after auth"). Existing code is built on invariants; if your change quietly violates one, you create a bug that tests on your line might not catch. Reading for invariants is how you change code safely.
- Reading strategy: entry point → data flow → side effects. The whole method compresses to this arrow. Find where execution starts, follow how the data moves through it, and note what it touches on the way out. Do that for one path and you understand the system far better than someone who read ten files at random.
Why this round exists now — the AI angle
For two decades the gatekeeper was "can you produce code from scratch?" — LeetCode measured generation. But the daily job was never mostly generation; it was mostly reading: figuring out what the last engineer (or the last five) built before you dare touch it. The interview format lagged the job. Two forces finally closed the gap.
When a model can draft a function in seconds, your value shifts from typing it to judging it: is this correct, does it fit the existing patterns, what does it silently break? You cannot review AI-generated code you can't read. Comprehension is the bottleneck skill of the AI era — so it's the thing worth testing.
If anyone can summon a plausible-looking solution, "can you write a binary search" stops separating candidates. "Can you drop into a 200-file repo and make a safe change in 40 minutes" still does. The signal moved from output to navigation.
Put bluntly: in 2026 a huge share of the code you'll be responsible for will be machine-drafted, and your job is to understand it well enough to take ownership. This round is companies checking for exactly that. Treating AI as a teammate whose work you must read and vouch for — rather than an oracle whose word you take — is the mindset the round rewards.
- README + package.json / pyproject.toml — what is this, what runs it, what are the entry points.
- Folder structure — say it out loud: "looks like routes here, business logic here, models here." Map the cake before tasting it.
- One end-to-end trace — pick a single user-visible action, follow it from request to response. Builds your mental map fast.
- Tests as docs — test names + test fixtures tell you what behaviors the team cares about.
- Grep for the unknown — symbol you don't recognize? Find usages. Don't guess.
The reason this order works is that each step shrinks the search space for the next. The README and config file (the package.json / pyproject.toml that lists dependencies, scripts, and the start command) tell you the technology and the entry point, so you no longer have to guess which file boots the app. The folder names tell you roughly where each responsibility lives, so when you trace one path you already half-expect where it goes next. The single end-to-end trace turns that rough guess into a concrete, verified mental model. Tests confirm the invariants — the behaviors the team promised to keep — and grep is your escape hatch the moment a name appears that the map didn't predict. Going inner-to-outer (opening a random deep file first) reverses this and leaves you with no frame to hang anything on.
Walkthrough: reading an unfamiliar function out loud
The micro-skill under all of this is reading one function you've never seen and being able to say three things about it: what it does, why it exists, and what edge cases it handles or misses. Here's a function you might hit in a "locate / modify" task. Read it the way an interviewer wants to hear you read it.
def resolve_account(user_id, *, allow_archived=False):
account = db.accounts.find_one(user_id) # side effect: hits the DB
if account is None:
raise NotFound(f"no account for {user_id}")
if account.archived and not allow_archived:
raise Gone(f"account {user_id} is archived")
return account
Now the narration, in the exact order the reading strategy prescribes:
- Entry point / inputs first. "It takes a
user_idand a keyword-only flagallow_archivedthat defaults toFalse. The*forces callers to pass that flag by name, so a stray positional argument can't accidentally flip it on." Already I know the shape of the contract. - Data flow. "It looks the account up by id, then returns it. So this is a lookup-with-guards: id in, account object out." That's the what.
- Side effects. "The one side effect is the DB read on line 2 — no writes, no email, no mutation. Calling it twice is harmless." Knowing it's read-only tells me a lot about whether it's safe to call from where I'm working.
- Why it exists. "It centralizes two policies in one place: missing accounts become a
NotFound, and archived accounts become aGoneunless the caller explicitly opts in. So every route that needs an account gets consistent error behavior for free." That's the why — it's the rule-enforcer, not just a fetch. - Edge cases. "It handles two: the not-found case and the archived case. The
allow_archivedescape hatch exists precisely so admin or restore flows can still reach archived accounts. What it does not do is validate thatuser_idis well-formed — a malformed id just becomes a miss and aNotFound." Naming what it skips is what separates a real read from a hand-wave.
Notice the invariant this function establishes for everyone downstream: if resolve_account returns, the account exists and is usable. That's why a "modify" task like "deleted users should 410 instead of 404" almost certainly belongs here — and you can locate the right spot in one read, without grepping the whole app, because you understood the function's job rather than just its lines. Before you'd reach for AI at all, you'd confirm by grepping resolve_account's call sites: if every route funnels through it, you change one place; if some routes hit the DB directly, you've just learned the codebase is inconsistent and you should ask which path is canonical.
"This looks like a Flask app — app.py registers blueprints, each blueprint owns a resource, models in db/." Interviewer hears you building structure.
Don't guess what a function does — find one call site, trace the args. Faster than reading the body.
"Is the data flow request → service → repo, or are routes hitting the DB directly?" Saves 10 min of wrong-direction reading.
When asked to change behavior, change the smallest thing that achieves it. Adding 50 lines in a 50-file repo signals you didn't understand the existing abstractions.
If one exists, run it; if not, write the smallest one. "I added a test that fails on main and passes with my change" is the senior phrasing.
Tempting to clean up unrelated code. Don't. Scope creep in a code comprehension round = bad signal.
Ask the model "where does X happen in this repo?" and verify by jumping to the file. Don't ask "what does this codebase do?" — that's your job.
What interviewers look for — and the pitfalls that sink people
The grader isn't tallying how many files you opened. They're listening for evidence that you build an accurate model and act on it carefully. Concretely, strong candidates do these things, and weak ones fall into the matching traps:
- You start outer-to-inner and say the map as it forms.
- You verify claims by jumping to a real line, not asserting from memory.
- You name edge cases and invariants, including the ones the code misses.
- You make the smallest change that works and back it with a test.
- You ask one sharp question instead of guessing the architecture.
- Guessing. Confidently describing what a function "probably" does without opening it — then being wrong. Search and confirm; an unverified claim is worse than "let me check."
- Ignoring edge cases. Tracing the happy path only and missing the null check, the archived branch, the error return. Edge cases are where the design lives.
- Reading inner-to-outer. Opening the biggest file first and getting lost with no frame to hang it on.
- Over-refactoring. "Cleaning up" unrelated code mid-task — scope creep signals you didn't grasp the existing abstractions.
- Treating AI as an oracle. Pasting "what does this repo do?" and reading the answer aloud. That's the candidate's job; using the model to locate and then verifying is fine, outsourcing the understanding is not.
Takeaway: the code comprehension round tests whether you can navigate code you didn't write — the day-1 skill, and the bottleneck skill once AI drafts the code. The method is one arrow: entry point → data flow → side effects, worked outer-to-inner across README, folders, one end-to-end trace, tests, and grep. For any function, be able to say what it does, why it exists, and which edge cases it handles or misses, then make the smallest change backed by the smallest test. Verify, don't guess; respect the existing invariants; use AI to find, never to understand for you.