How to Understand a New Codebase Fast [2026]
A working method to understand a codebase fast: entry points, one request end to end, tests, git history, seams, and the tools that actually help.
To understand a codebase fast, start from the entry points and the build rather than the file tree, pick one real request and follow it end to end, read the tests to learn intended behavior, and use git history to answer why something exists. Depth on one path beats breadth across the tree. Write your questions down as you go and settle them by experiment rather than assumption.
Below is that method, the tools worth learning, the habits that cost a week, and the case most guides skip: a repository written largely by an AI agent, with nobody to ask.
Why Reading a Codebase Feels Slower Than It Should
Reading code is the job, not the preparation for it. In a field study of 78 professional developers across seven projects and 3,148 working hours, Xia and colleagues found developers spent around 58% of their time on program comprehension.
Peter Naur explained the trap in 1985. In Programming as Theory Building, he argued that a program is not its source code: the working theory of a system lives in the people who built it, and cannot be rebuilt from code and documentation alone.
So you cannot read your way to the theory, you have to rebuild it, by predicting what the system does and then checking. Every technique below makes that loop run faster, whether you inherited a fifteen-year-old service or a repository produced last month by an agent.
Start From the Entry Points and the Build, Not the File Tree
The file tree is a filing decision, not a design. It says where somebody put things, often two architectures ago. Entry points say what runs.
The first hour:
- Get it running. Setup that fails is the fastest inventory of the system's real dependencies: the database, the variables, the service it will not start without.
- Read the entry points, not the source.
main, the route table, the CLI commands, the job scheduler registrations. Ten minutes here beats two hours insrc/utils. - Read the build and deploy config. What gets built, what gets tested, and what ships are three different lists, and the gaps are informative.
- Read the dependency manifest as intent. An ORM, a queue client, a feature flag SDK: each is a decision made before you arrived.
A few commands do most of the orienting:
git log --oneline -20 # what has this team been doing lately
git shortlog -sne --since=1.year # who actually knows this code
rg -n "app\.(get|post|put)\(" src # find the route definitions
If you cannot run it, you are reading fiction. If the terminal work is unfamiliar, Scrimba's guide to command line basics covers the commands this step assumes.
Follow One Real Request End to End
Pick one behavior a user or another system actually triggers and follow it through every layer it touches. This is a vertical slice, and it is the highest-leverage hour you will spend in a new repository.
The reason is structural. A vertical slice crosses every architectural boundary exactly once: entry point, routing, validation, service layer, data access, external calls, response. One trace teaches you the layering, the error handling, and the naming conventions in the order they matter.
Good candidates share four properties:
- It sits in the product's core value, not an admin corner.
- It has a name people say out loud in standup.
- You could plausibly be asked to change it next week.
- It already has a test, which gives you a runnable way in.
Then make it empirical. Add a log line, run the request, and predict the next call before you look. A wrong prediction shows you exactly where your model breaks.
Reading a codebase breadth-first gives you a map of a city you have never walked through. One request end to end is a walk.
Read the Tests to Learn Intended Behavior
Tests are the only documentation that fails when it goes stale. Everything else drifts in silence.
- Integration and end to end tests are the closest thing to a specification of user-visible behavior.
- Unit tests reveal the seams. In Michael Feathers' term from Working Effectively with Legacy Code, a seam is a place where you can alter behavior without editing in that place, and every mock marks one.
- Fixtures and factories tour the domain model, showing the shape of a valid Order or Subscription.
Read the test names first, all of them, before opening a test body. On a decently named suite that is a behavior inventory in ninety seconds.
If nothing is tested, write a characterization test: not a test of what the code should do, but a pin for what it does right now. It turns reading into an experiment and outlives your notes. Scrimba's guide to testing React apps covers the mechanics.
Use Git History to Answer "Why Is This Here?"
The code says what. The history says why, and history is the only place that reasoning survives.
git log -S"featureFlag" --oneline # when this string appeared or vanished
git log -G"retry" --patch # commits whose diff touches a matching line
git log -L:handleCheckout:src/checkout.ts # the life story of one function
git log --follow path/to/file # keep history across renames
git blame -w -M -C path/to/file # blame, ignoring formatting and moves
git bisect run npm test # find the commit that changed behavior
Two deserve explaining. -S is the pickaxe: per the git diff documentation it finds commits that change the number of occurrences of a string, which locates where something was introduced and where it was deleted. -G finds commits whose patch adds or removes a line matching a regex, so it also catches a line modified in place. The Git docs give the example: a commit that rewrites one call site shows up under -G and not -S. The git log documentation covers -L, which traces one function, and --follow, which survives renames.
The blame flags matter as much. Per the git blame documentation, -w ignores whitespace, -M detects lines moved within a file, and -C detects lines moved or copied from other files. Without them you spend an afternoon reading about whoever ran the formatter. And when the odd thing is a behavior rather than a line, git bisect binary-searches history for you.
The commit you want is rarely the last one. It is where the strange thing first appeared, usually next to a ticket number. Scrimba's Git and GitHub guide is a good starting point, and its Learn Git and Github course (Pro, 103 minutes, with Gregor Thomson) goes deeper into log, stash, revert, reset, and rebase.
Find the Seams and the Domain Model
Two things are worth naming once you have traced a slice.
Seams are where the system was built to bend: interfaces, injected constructors, adapters, feature flags. A seam map is a map of the change the team anticipated, and where your first change will be safest.
The domain model is the ten or twenty nouns the business actually has. Find them in the schema, the type definitions, and the fixtures. Getting these right beats any diagram, because every conversation on the team is conducted in them. Where the code's nouns and the team's disagree, you have found history.
| Signal | Where to look | What it tells you |
|---|---|---|
| Injected dependencies | Constructors, adapters | Where behavior can be swapped |
| Fixtures and factories | Test setup, seed scripts | The real shape of the domain model |
| Feature flags | Config, flag SDK calls | Which behavior is still in flight |
| Files with most commits | Commit counts per path | Where the difficulty lives |
Write Down Questions, Then Answer Them by Experiment
Keep a running list in a scratch file. Two columns: the question, and how you would find out. The second column is the discipline, because it forces experiment over assumption. Break something deliberately in a throwaway branch and watch which tests go red.
Save the humans for what the code cannot answer: why this and not the obvious alternative, what broke last time, what looks disposable but is load-bearing.
The Tools Worth Learning
Tools serve the method. None builds the theory for you, but each shortens a loop.
| Tool | The question it answers | Reach for it when |
|---|---|---|
| Go to definition, find references | Where is this defined, who uses it | Constantly. Make it muscle memory |
| Call hierarchy | Who calls this, what does it call | Before you change a function |
| Dependency graph | What depends on what | Scoping a refactor, hunting a cycle |
git blame and the pickaxe |
When did this appear, and why | Any line that looks arbitrary |
| Architecture decision records | What was decided, what was rejected | First, if the repository has them |
| An AI agent with repository access | Where is this handled | An unfamiliar tree |
Go to definition and find references are F12 and Shift+F12 in VS Code. They beat text search because they understand the language rather than the characters, and they are not editor features but Language Server Protocol requests, which is why they work the same everywhere. The same protocol defines call hierarchy, whose incoming calls answer the question you have right before you touch anything.
Dependency graphs come with a caveat. dependency-cruiser visualizes dependencies for JavaScript and TypeScript, and madge finds circular ones. Run either against a large repository and you get a hairball. Scope it to one directory and you get a diagram you can read.
Architecture decision records are the highest-value thing in any repository that has them. An ADR captures one decision with its rationale, trade-offs, and consequences, a practice popularized by Michael Nygard's 2011 post and collected at adr.github.io. Check docs/adr first: ADRs are the only artifact recording the rejected alternatives.
AI agents with repository access are good at the locating problem, and Scrimba's guide to using Claude Code covers that workflow. Scrimba also ships Scrimba Explain, an MCP plugin with a different angle: you ask your coding agent a question about the codebase and get back a narrated video walkthrough rather than a wall of text, which helps when the answer spans three files and a callback. It works with Claude Code, Codex and ChatGPT, and any MCP agent, free during open beta.
One caveat matters more here than almost anywhere. Scrimba's FAQ for Explain says that like any AI tool it can make mistakes, so double-check anything important. A confident wrong explanation of code you do not know yet is worse than no explanation, because you have nothing to check it against. When Birgitta Böckeler tried onboarding onto a legacy codebase with AI help, the tools pointed her toward the right code but invented much of their answers elsewhere. Use agents to locate, then verify.
What Does Not Work
Three habits reliably cost a week.
- Reading the codebase alphabetically, or breadth-first. You spend your attention in filename order, which correlates with nothing. Ten files chosen by a trace teach you more than a hundred chosen by the sidebar.
- Trying to understand everything before changing anything. Understanding is produced by changing. Ship something small and reversible in week one, run the tests, and read the review. A review of your own code is the fastest transfer of a team's theory you will get.
- Trusting comments and docs that have drifted. Comments do not fail when they go stale, which is the whole problem. Naur makes this structural rather than a discipline failure: documentation is a lossy copy of a theory that lived in somebody's head. Treat a comment as evidence of what somebody once believed, and date it with
git blame.
A generated architecture diagram belongs in the same bucket. It renders imports, not design.
When the Codebase Was Written by an AI Agent
In the 2025 Stack Overflow Developer Survey, 84% of respondents said they use or plan to use AI tools, while only about a third trusted the accuracy of the output. The top frustration, at 66%, was AI solutions that are almost right but not quite, and 45.2% said debugging AI-generated code takes longer.
The result is a repository that runs, may well pass its tests, and that nobody ever built a theory of, because nobody wrote it. Naur's problem in its purest form, and it changes what to expect:
- Local consistency, global incoherence. Every file looks reasonable alone. Three of them solve the same problem three ways.
- Plausible names that do not map to a domain model, because no conversation ever named the domain.
- Duplicated logic where a seam should be, because an agent had no reason to anticipate change.
- Tests that assert the implementation rather than the behavior, pinning bugs in place alongside features. A characterization test earns its keep here even though tests exist.
- Comments describing intent that was never true.
The method changes in one way: stop asking why. There is frequently no answer, and hunting for one burns an afternoon. Spend the time on behavior, which is knowable. History is thinner but not worthless, since the prompt or the pull request description often survives in the commit message.
Handing the problem back to an agent is fine as long as the split stays clear. Locating is safe, explaining is not. Ask where the retry logic lives and the answer will probably be right. Ask why it exists in a codebase the agent also wrote, and you get confidence with nothing behind it.
Frequently Asked Questions
How long does it take to understand a new codebase?
Shipping a small change usually takes a few days. Making design decisions confidently takes weeks to months, depending on the size of the system and how much of the original team is still around. The milestone that matters is predicting where a change belongs.
Should I read the whole codebase before making changes?
No. Understanding is produced by changing. Make a small, reversible change in your first week, run the tests, and read the review you get back. A review of your own code transfers a team's knowledge faster than any amount of reading.
What is the fastest way to understand an unfamiliar repository?
Get it running, find the entry points, then pick one real request and follow it through every layer it touches. That trace crosses every architectural boundary once and teaches you the layering, the conventions, and the error handling together. Depth beats breadth across the file tree.
How do I understand a codebase that was written by AI?
Treat behavior as knowable and intent as absent. Expect local consistency with global incoherence, duplicated logic where a seam belongs, and tests that assert the implementation rather than the behavior. Skip the why questions and pin current behavior with characterization tests.
Can AI tools explain a codebase for me?
They are strong at locating code and weaker at explaining it. Use an agent to find where something is handled, then check the explanation against the code and the tests. A confident wrong explanation of unfamiliar code is worse than none, because you cannot catch it.
Key Takeaways
- Developers spend roughly 58% of their time on program comprehension, so reading code is the job, not the overhead before it.
- Start from the entry points and the build. The file tree is a filing decision, not a design.
- Follow one real request end to end. A vertical slice crosses every architectural boundary once, which breadth-first reading never does.
- Tests are the only documentation that fails when it goes stale, so read the test names first and pin untested behavior with a characterization test.
- Git history answers why. The pickaxe finds where a string appeared or vanished, and blame with
-w -M -Cstops you blaming the formatter. - In an AI-written codebase there is nobody to ask, so stop hunting for intent and pin current behavior instead.
Sources
- Xia et al. "Measuring Program Comprehension." IEEE TSE, 2018. https://dl.acm.org/doi/10.1109/TSE.2017.2734091
- Naur, Peter. "Programming as Theory Building." 1985. https://pages.cs.wisc.edu/~remzi/Naur.pdf
- Git docs: diff, log, blame, bisect. https://git-scm.com/docs/git-diff
- Language Server Protocol 3.18. https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/
- VS Code, Code Navigation. https://code.visualstudio.com/docs/editing/editingevolved
- Architecture Decision Records. https://adr.github.io/
- Feathers. Working Effectively with Legacy Code, 2004. https://www.oreilly.com/library/view/working-effectively-with/0131177052/
- Stack Overflow 2025 Developer Survey, AI. https://survey.stackoverflow.co/2025/ai/
- Böckeler, Birgitta. "Onboarding to a legacy codebase with AI." 2024. https://martinfowler.com/articles/exploring-gen-ai/09-ai-help-onboarding-codebase.html
- dependency-cruiser, madge. https://github.com/sverweij/dependency-cruiser