Skip to main content

On a phone? for plain text and static diagrams.

Understanding a Codebase

Before you change the code, understand the system.

The question you are answering changes as you go deeper. Press Play and watch it move from the system, to the feature, to the change.
Stage 1 of 10
  1. The system

  2. The feature

  3. The change

  4. Acceleration

The system

What does the system do, and for whom?

Business Context

The first question is not “which file?”

Tickets arrive small. Add a new status. Change this API. Add a new button. Fix this calculation.

The common move is to search for the file whose name looks closest to the ticket, open it, and start typing. Sometimes that works. More often the change lands in the layer you happened to open instead of the layer that owns the behaviour, and the rest of the system finds out in UAT.

The real first question is: how does this part of the system actually work?

Pick a ticket. Every lit layer is a place the change can land, break, or need a test.
  • UI
  • Frontend state
  • API
  • Backend
  • Business logic
  • Database
  • Integrations
  • Tests
  • Reports
  • Notifications

Add a new status. crosses 10 of 10 layers. A status is read everywhere: badges, filters, rules, emails, monthly numbers.

Answering it means building a codebase mental model: a picture in your head of who uses the system, where its code lives, which components talk, how one feature travels through them, where decisions are made, how data changes shape, and what a change would ripple into. This episode builds that model in nine gates, on one realistic system, and only then shows where AI makes it faster.

What does “understanding a codebase” mean?

You progressively move from what does the system do? to where does this feature live? to what could my change affect? The journey at the top of the page is the whole method. By the end, you should be able to answer these thirteen questions about any system you join:

  1. 1What problem does this system solve?
  2. 2Who uses it?
  3. 3What are its major business workflows?
  4. 4How is the repository structured?
  5. 5What are the major architectural components?
  6. 6How does the application start?
  7. 7How does a feature travel through the system?
  8. 8Where does business logic live?
  9. 9How does data move through the system?
  10. 10What could be affected by a change?
  11. 11What should be investigated before coding?
  12. 12How can AI accelerate this investigation?
  13. 13How do we validate AI's findings?

The outcome: you can explain the system before you change the system.

The lab system: a Warranty Claim Application

Customers submit warranty claims for products that failed. Administrators review each claim and approve or reject it. Every gate below investigates this same system, so by the end you will know it the way you would know a codebase after your first two weeks.

You will trace one existing feature, Approve Warranty Claim, and then analyse one change: add REJECTED to the claim status.

  • Next.js
  • React
  • TypeScript
  • NestJS
  • Prisma
  • PostgreSQL
  • REST API
  • Vitest
  • Playwright

The technologies are only the laboratory. The subject is how an engineer builds a mental model.

tree -L 324 lines
1warranty-platform/
2├── apps/
3│ ├── web/
4│ │ ├── app/
5│ │ ├── components/
6│ │ ├── features/
7│ │ └── lib/
8│ └── api/
9│ └── src/
10│ ├── modules/
11│ ├── common/
12│ └── main.ts
13├── packages/
14│ ├── ui/
15│ ├── types/
16│ └── validation/
17├── prisma/
18│ └── schema.prisma
19├── tests/
20│ ├── e2e/
21│ └── fixtures/
22├── package.json
23├── pnpm-workspace.yaml
24└── turbo.json

Gate 01

Business Context

“What does this system do, and for whom?”

Concept

Before you open the code, understand the business. Code is a translation of business decisions; if you do not know the original, you cannot tell a rule from an accident.

Why it matters

Skip it and every name in the code is a guess. You will read ClaimStatus.PENDING without knowing who is waiting, for what, and what happens if they wait too long.

The business perspective: actor, action, process, decision, outcome. No file names yet. Choose an outcome and play the workflow.
Step 1 of 6
  1. Actor

    Customer

    Bought a product that failed within warranty.

  2. Action

    Submits a warranty claim

    Product, serial number, purchase date, receipt photo, description of the fault.

  3. Process

    Claim created

    Status PENDING. The customer receives a reference number.

  4. Actor

    Administrator reviews

    Checks the warranty period, the serial number and the receipt.

  5. Decision

    Valid and within warranty?

    The only decision point in the workflow.

  6. Outcome

    Approved

    Repair or replacement arranged; the customer is notified.

What to inspect

  • Product or onboarding documentation
  • The epic and its acceptance criteria
  • The running app, used once as each role
  • Seed data in prisma/seed.ts
  • A 15 minute conversation with the product owner

Hands-on task

Use the staging app as a customer, then as an administrator. Submit one claim and decide it. Write down every actor, action and outcome you saw, without opening the repository.

Clearance questionCan I describe the claim workflow to a new teammate without mentioning a single file?

Gate 02

Repository Structure

“Where does everything live?”

Concept

Now enter the codebase, but do not open random files. Identify the boundaries first: which folders are applications, which are shared, where the schema and tests are, and which files configure the whole workspace.

Why it matters

Skip it and you will edit apps/web/components/StatusBadge.tsx without noticing that packages/ui exports the StatusBadge everyone else imports. Repository structure is not the architecture. It is only the first map.

Where does everything live? Click an area for its responsibility and the files to open first.
  1. warranty-platform/

Web

Next.js app for customers and administrators.

Open first

  • apps/web/app/(admin)/claims/[id]/page.tsx
  • apps/web/features/claims/
  • apps/web/lib/api/claims.ts

What to inspect

  • pnpm-workspace.yaml
  • turbo.json
  • package.json scripts
  • apps/*/package.json
  • packages/*/package.json
  • prisma/schema.prisma

Hands-on task

Run tree -L 3 -I node_modules and cat pnpm-workspace.yaml. For every top-level folder, write one sentence: what it is responsible for and who depends on it.

Clearance questionIf a type changes in packages/types, can I name every application it reaches?

Gate 03

Architecture

“Which major components communicate with each other?”

Concept

Architecture is the runtime picture: which components exist while the system runs, and how they talk. A folder is not a component; a NestJS module that owns a database table and sends email is.

Why it matters

Skip it and you will miss the parts that are not in the request you are looking at: the email sent after approval, the file storage holding receipts, the manufacturer API that receives approved claims.

Which components talk to each other? Click a component to light up its connections. Dashed boxes are outside our code.

NestJS API

Claims module talks to: Next.js (REST /api/claims), Auth module (guards check permissions), PostgreSQL (Prisma), Object storage (signed upload URLs), Notification module (ClaimApproved event), Integrations module (ClaimApproved event).

Evidence: src/modules/claims

What to inspect

  • apps/api/src/app.module.ts imports
  • Environment variables that hold URLs
  • HTTP clients and SDKs in package.json
  • docker-compose.yml services

Hands-on task

Open app.module.ts and list every imported module. Then search the API for outbound calls (rg -n "fetch\(|HttpService|S3Client|createTransport" apps/api/src). Draw boxes and arrows.

Clearance questionCan I draw every component that a claim touches, including the ones outside our code?

Gate 04

Runtime

“What actually happens when I run the application?”

Concept

Follow the start-up path from the command you type to the moment the application is ready. Know how development starts, how production starts, where configuration comes from and what must already be running.

Why it matters

Skip it and the first failure costs you an afternoon: a missing variable, a database that is not up, a port taken, a production build that behaves differently from dev.

What happens when I run it? Step from the command to a ready application. Then break it on purpose and see which file tells you why.
Step 1 of 6

terminal

One command starts everything in development.

Command1 lines
1pnpm dev

Development and production start differently

Start
Devpnpm dev (turbo, watch mode)
Prodnode dist/main.js and next start, from the Dockerfile
Build
DevNone; compiled on the fly
Prodpnpm build: nest build and next build
Configuration
Dev.env on your machine
ProdEnvironment variables injected by the platform
Database
DevPostgreSQL in docker compose
ProdManaged PostgreSQL; migrations run in the release step

What to inspect

  • package.json scripts
  • pnpm-workspace.yaml
  • turbo.json
  • apps/api/src/main.ts
  • apps/web/next.config.ts
  • .env.example
  • docker-compose.yml
  • Dockerfile
  • prisma/schema.prisma datasource

Hands-on task

Clone the repository on a clean machine and follow the runtime trace below. Each time something fails, write down which file told you how to fix it.

Clearance questionIf the app fails to start tomorrow, do I know the first three places to look?

Gate 05

Feature Flow

“How does one feature travel through the system?”

Concept

Pick one existing feature and trace it end to end: Approve Warranty Claim. Do not start coding. Follow the actual implementation from the button to the database, then follow the response back to the screen.

Why it matters

Skip it and you will add your change at the layer you happened to open, not the layer that owns the decision. Tracing one feature teaches you the conventions every other feature follows.

Approve Warranty Claim, traced through the real layers. Eight hops down to the database, eight back up to the screen. Click any layer or press Play.
Hop 1 of 16

Request travelling down

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8

apps/web/features/claims/ApproveClaimButton.tsx

① ApproveClaimButton

What is it?
A React component on ClaimDetailsPage.
Responsibility
Showing the action and its loading and error state.
Receives
A click, the claim id from the page.
Returns
Nothing; it triggers a mutation.
Calls next
useApproveClaim, which calls claimApi.approve()
ApproveClaimButton.tsx8 lines
1export function ApproveClaimButton({ claimId }: { claimId: string }) {
2 const approve = useApproveClaim();
3 return (
4 <Button disabled={approve.isPending} onClick={() => approve.mutate({ claimId })}>
5 Approve
6 </Button>
7 );
8}

What to inspect

  • The page route under apps/web/app
  • The feature folder apps/web/features/claims
  • The API client in apps/web/lib/api
  • ClaimsController, ApproveClaimDto, ClaimsService
  • ClaimsRepository and the Claim model

Hands-on task

Start at the Approve button. Use go-to-definition, never guessing, until you reach the SQL. At each hop write: what it receives, what it returns, what it calls next.

Clearance questionCan I name the file and function at every hop, in both directions?

Gate 06

Business Logic

“Where are the decisions made?”

Concept

Finding the code is not enough. You must understand the responsibility of the code. Separate UI logic, input validation, authorization, business rules and data access, because each belongs to a different layer.

Why it matters

Skip it and you will copy a rule into the place you are editing. Now the PENDING check exists in the button, the controller and the service, and next year someone changes only one of them.

This lab builds on Gate 05: Feature Flow. Finish it first, then come back.

Go to Gate 05
Finding the code is not enough. For each snippet, choose the layer that should own it.
  1. setIsLoading(true);
  2. if (!claimId) {
      throw new Error("Claim ID required");
    }
  3. @RequirePermission("CLAIM_APPROVE")
  4. if (claim.status !== "PENDING") {
      throw new ConflictException();
    }
  5. prisma.claim.updateMany({ ... })
  6. enum ClaimStatus { PENDING APPROVED }

0 of 6 sorted.

Each layer has one job. The right-hand column is what it must never do.
  1. 1UI

    Presentation

    Deciding what is allowed

  2. 2Controller

    Request handling

    Business rules

  3. 3Service

    Business logic

    HTTP or SQL details

  4. 4Repository / ORM

    Data access

    Deciding outcomes

  5. 5Database

    Persistence

    Workflow decisions

What to inspect

  • Conditions inside services
  • Guards and decorators on controllers
  • DTO decorators and zod schemas
  • Database constraints and enums
  • Conditional rendering in components

Hands-on task

Search for every place that mentions PENDING: rg -n "PENDING" apps packages prisma. Label each hit as UI logic, validation, authorization, business rule or data access.

Clearance questionFor the rule I am about to change, can I point at the one place that enforces it?

Gate 07

Data Flow

“What shape is the data at each step?”

Concept

Request flow is the path a call takes. Data flow is how the data changes shape on that path. The same claim is a form model, a JSON payload, a DTO, a domain object, a database row, a response DTO and a UI model.

Why it matters

Skip it and you will add a field to the DTO but not to the response mapper, or format a date in the service that the UI formats again. Most "it saved but does not show" bugs live between two representations.

This lab builds on Gate 05: Feature Flow. Finish it first, then come back.

Go to Gate 05
The same decision note, in eight representations. Step through and watch the name, type and format change.
Shape 1 of 8

User Input

the textarea and the Approve click

Receipt matches serial number

What changed: Free text, typed by Nurul Huda, the reviewer.

What to inspect

  • Form state and zod schema
  • API client types
  • DTO classes
  • Prisma model in schema.prisma
  • Response mappers (toResponse)
  • UI view-model helpers

Hands-on task

Follow one field, the decision note, from the textarea to the database and back to the screen. Record its name, type and format at every step.

Clearance questionIf I add a field, do I know every representation that must learn about it?

Gate 08

Impact Analysis

“What could this change affect?”

Concept

The business flow has a Rejected outcome, but today the code only supports approval. The ticket: add REJECTED to the claim status. Before touching code, identify every place where that status is interpreted, transformed, displayed or persisted.

Why it matters

A small code change can create a large system impact. The enum compiles in five minutes; the monthly approval-rate report silently changes its denominator.

This lab builds on Gate 05: Feature Flow. Finish it first, then come back.

Go to Gate 05
What could this change affect? Play it to watch the impact ripple out: direct dependents first, then downstream. Click any area for the file, the reason and how to check.
Ring 1 of 3

change

Claim Status

+ REJECTED

Select an area to see the file, the reason, and how to verify it.

What to inspect

  • Every reader of ClaimStatus (rg -n "ClaimStatus|status" --type ts)
  • Exhaustive switches and maps keyed by status
  • SQL and report queries
  • Email templates
  • Seed data and fixtures

Hands-on task

Click through the impact map below, then write your own list for a field your next ticket touches. For each entry: file, reason, and how you will verify it.

Clearance questionCan I list what could break, and how I would notice if it did?

Gate 09

Codebase Clearance

“Can I explain this change before writing it?”

Concept

Clearance is the point where your mental model is complete enough to start. Not perfect: complete enough that the remaining unknowns are written down and owned.

Why it matters

Skip it and you discover the system while changing it, which means your first draft is also your investigation, and your reviewer is the one who finds the gaps.

This lab builds on Gate 08: Impact Analysis. Finish it first, then come back.

Go to Gate 08

Codebase Understanding Clearance

0/10

Can I explain this change and its impact to another developer before writing code?

Business

0 of 4

Architecture

0 of 4

Code

0 of 5

Data

0 of 3

Testing

0 of 3

Impact

0 of 4

What to inspect

  • Your notes from gates 01 to 08
  • The open questions you still have
  • The tests that exist for this area

Hands-on task

Tick every line below that you can answer with evidence. Anything you cannot tick goes into the ticket as an explicit unknown.

Clearance questionCan I explain this change and its impact to another developer before writing code?

AI tips: use AI to accelerate codebase discovery

Once you know how to study a codebase manually, AI can significantly accelerate the investigation. Everything you did in the nine gates is now a checklist for judging what an assistant tells you.

Where it genuinely helps

  • Searching large repositories
  • Following references
  • Summarising modules
  • Finding usages
  • Identifying dependencies
  • Drafting an architecture map
  • Tracing API flows
  • Listing potentially affected files
  • Surfacing unknowns

AI output is a hypothesis until validated against the actual codebase.

Do not ask “explain this codebase” and paste the answer into your notes. Form your own model first, then ask narrow questions that force the assistant to cite files and symbols, and to say UNKNOWN when it does not know. Evidence you can open beats a confident paragraph.

The prompt library

Five prompts, one per gate family. Each forces evidence and gives the assistant permission to not know. Under each prompt: how you validate what comes back. The last one matters most. Strong engineers ask what they know, and then: what don’t I know yet?

Repository mapping

prompt21 lines
1Analyse this repository and create a codebase map.
2
3Identify:
4
5- applications
6- modules
7- shared packages
8- database
9- integrations
10- tests
11- infrastructure
12- configuration
13
14For each area:
15
161. Explain its responsibility.
172. Reference the relevant files.
183. Identify the entry points.
194. Mark uncertain findings as UNKNOWN.
20
21Do not invent implementation details.

How to validate: Validate: open each entry point it names. If a file does not exist or does something else, the map is wrong there, and probably nearby.

This lab builds on Gate 05: Feature Flow. Finish it first, then come back.

Go to Gate 05
An assistant traced Approve Claim. You have the real code from Gate 05. Mark each finding, then check. AI output is a hypothesis until it is validated.
  1. AIApproveClaimButton calls claimApi.approve() through useApproveClaim.

  2. AIThe PENDING check is enforced in ClaimsController.

  3. AIOnly administrators can approve claims.

  4. AIApproving a claim emails the customer.

  5. AIClaims are soft-deleted with a deletedAt column.

  6. AIThe endpoint is POST /api/claims/:id/approve.

Manual vs AI-assisted discovery

The goal is not human, AI, answer. It is human, AI acceleration, human validation, understanding. The assistant shortens the search. The engineer still inspects the evidence, corrects the assumptions and owns the final model.

Both paths end in the same place: a mental model the engineer owns. Filled steps are the engineer's; the outlined step is the assistant's.
Tick 1 of 6

Manual

  1. Developer
  2. Read code
  3. Search files
  4. Follow references
  5. Build mental model

AI-assisted

  1. Developer
  2. Ask AI for initial discovery
  3. Inspect evidence
  4. Validate findings
  5. Correct AI assumptions
  6. Build mental model

Not human, AI, answer. Human, AI acceleration, human validation, understanding.

Key takeaways

  1. Principle 01

    Don't start with the file. Start with the system.

  2. Principle 02

    Understand the business flow before the technical flow.

  3. Principle 03

    Trace one feature end to end.

  4. Principle 04

    Know where the business logic lives.

  5. Principle 05

    Understand the data, not just the request.

  6. Principle 06

    Think about impact before implementation.

  7. Principle 07

    Identify what you don't know.

  8. Principle 08

    Use AI to accelerate discovery, not replace understanding.

Rebuild the order from memory

  1. Gate 01?
  2. Gate 02?
  3. Gate 03?
  4. Gate 04?
  5. Gate 05?
  6. Gate 06?
  7. Gate 07?
  8. Gate 08?
  9. Gate 09?

Which gate comes first?

Closing

Good developers know where the code is.

Good engineers understand why the code is there.

Next: Season 01, Episode 03

Unit testing in development

You can now explain the system and the impact of a change. Next: prove the change works, layer by layer, before anyone else has to.

Publishes