Module 1: Onboarding with AI — 5-10x Faster
Building the Mental Model — Architecture, Layers, Entry Points
Building the Mental Model — Architecture, Layers, Entry Points
Capsule description
You explored the codebase. You asked the 5 questions. You have scattered findings: "it uses FastAPI", "there's a services/ directory", "the entry point is main.py", "it looks like it uses the repository pattern". But scattered findings aren't comprehension. Comprehension is when you can predict what happens if you change something — and you get it right.
That's a mental model: an internal representation of how the system works. It's not a pretty diagram or a formal document. It's your ability to answer "if I modify this function, what breaks?" without running the tests. Senior developers navigate large codebases because they have robust mental models. Juniors get lost because they don't have them — not because they're less intelligent, but because nobody taught them to build them systematically.
In this capsule you're going to learn to build a mental model in three levels (high-level, mid-level, low-level) using Claude Code as an accelerator. The process that takes a senior developer 1-2 weeks of reading, you'll complete in 1-2 hours with the right techniques. Not because you skip steps — but because Claude Code reads and connects information orders of magnitude faster than a human.
What a Codebase Mental Model Is
Practical definition
A codebase mental model is your internal representation of:
- What components exist — modules, services, models, utilities
- How they relate — who calls whom, who depends on whom
- How data flows — where it enters, how it's transformed, where it's stored
- What patterns govern the decisions — conventions, styles, implicit rules
- Where the traps are — hidden coupling, side effects, inconsistencies
Why "reading the code" isn't enough
Reading code without a mental framework is like reading a dictionary from A to Z to learn a language. Technically possible, practically useless. Code is a graph — not a linear sequence. Files reference each other, dependencies are sometimes circular, and the real execution flow rarely follows the order of the files in the directory.
A mental model gives you the map. Without a map, you walk blindly. With a map, you navigate with confidence.
The mental model test
How do you know if your mental model is good? Ask yourself this question:
"If I change the signature of the
process_payment()function, what files break and why?"
If you can answer with confidence and verify that you got it right, your mental model is functional. If you can't, there are gaps.
Weak Mental Model:
─────────────────────
"The project has Python files.
There's a tests folder.
It uses a database."
→ You can't predict anything.
Every change is an experiment.
Functional Mental Model:
──────────────────────────
"FastAPI app with 3 layers: routes → services → repositories.
Routes in api/v1/, each file is a domain (users, orders, payments).
Services contain business logic, never access the DB directly.
Repositories use SQLAlchemy, each model has its repository.
If I change process_payment() in services/payments.py,
api/v1/payments.py breaks (it calls it in POST /payments)
and tests/test_payments.py (it tests it directly)."
→ You can predict consequences.
Every change is informed.
The 3 Levels of the Mental Model
A complete mental model has three levels of depth. Each level answers different questions and requires different techniques to build it.
Level 1: High-Level — Architecture and Layers
What it answers: "What is this system made of and how is it organized?"
This is the bird's-eye view. It identifies:
- Application layers — Presentation, business, data, infrastructure
- Main components — The system's "big blocks"
- Boundaries — Where one component ends and another begins
- Entry points — Where requests enter the system
- Exit points — Where information leaves (DB, external APIs, files)
How to build it with Claude Code:
# Prompt 1: General architecture view
claude "Analyze the directory structure of this project and describe
the architecture in layers. Identify:
1. What the main layers are (presentation, business, data)
2. Which directory corresponds to each layer
3. What the system's entry points are
4. What the main external dependencies are"
Expected output:
The project follows a 3-layer architecture:
LAYER 1 - Presentation (api/)
├── api/v1/routes/ → HTTP endpoints (FastAPI routers)
├── api/middleware/ → Auth middleware, CORS, logging
└── api/dependencies/ → Dependency injection for FastAPI
LAYER 2 - Business (services/)
├── services/user_service.py → User logic
├── services/order_service.py → Order logic
├── services/payment_service.py → Payment logic
└── services/notification.py → Emails, webhooks
LAYER 3 - Data (repositories/ + models/)
├── repositories/user_repo.py → User queries
├── repositories/order_repo.py → Order queries
├── models/ → SQLAlchemy models
└── database.py → Connection and session
Entry points:
- api/main.py → FastAPI app, router mounting
- cli/manage.py → Administration commands
External dependencies:
- PostgreSQL (via SQLAlchemy)
- Redis (via redis-py, for cache)
- Stripe API (for payments)
- SendGrid (for emails)
# Prompt 2: Layer diagram
claude "Generate a text diagram showing this project's layers
and how they communicate with each other. Include the direction
of the dependencies (who depends on whom)."
Expected output:
┌─────────────────────────────────────────────────┐
│ ENTRY POINTS │
│ main.py (HTTP) │ manage.py (CLI) │
└───────────┬─────────────┴──────────┬────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────┐
│ LAYER 1: PRESENTATION │
│ api/v1/routes/ │ middleware/ │ dependencies/ │
│ (FastAPI) │ (auth,CORS) │ (DI) │
└───────────────────────┬─────────────────────────┘
│ calls
▼
┌─────────────────────────────────────────────────┐
│ LAYER 2: BUSINESS │
│ services/user_service.py │
│ services/order_service.py │
│ services/payment_service.py │
│ services/notification.py │
└───────────────────────┬─────────────────────────┘
│ calls
▼
┌─────────────────────────────────────────────────┐
│ LAYER 3: DATA │
│ repositories/ │ models/ │ database.py │
└───────────┬─────┴───────────┴──────┬────────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────────┐
│PostgreSQL│ │ Redis/Stripe/ │
│ │ │ SendGrid │
└──────────┘ └──────────────┘
Dependency rule: Layer N only calls Layer N+1.
Never the other way. Repositories never call Services.
Level 2: Mid-Level — Modules and Dependencies
What it answers: "How do the modules connect to each other?"
This level maps the concrete dependencies between files and modules. It's not enough to know that "services calls repositories" — you need to know which ones and how.
How to build it with Claude Code:
# Prompt 3: Dependency map between modules
claude "Analyze this project's imports and generate a
dependency map. For each main module, list:
1. What modules it depends on (imports)
2. What modules depend on it (who imports it)
3. Whether there are circular dependencies"
Expected output:
DEPENDENCY MAP:
services/order_service.py
├── DEPENDS ON:
│ ├── repositories/order_repo.py (queries)
│ ├── repositories/user_repo.py (validate user exists)
│ ├── services/payment_service.py (process payment)
│ └── services/notification.py (send confirmation)
│
└── DEPENDED ON BY:
├── api/v1/routes/orders.py (POST/GET/PUT endpoint)
└── cli/manage.py (create-test-order command)
services/payment_service.py
├── DEPENDS ON:
│ ├── repositories/payment_repo.py (save transaction)
│ └── core/stripe_client.py (Stripe API)
│
└── DEPENDED ON BY:
├── services/order_service.py (process order payment)
└── api/v1/routes/payments.py (webhook endpoint)
⚠️ CIRCULAR DEPENDENCY DETECTED:
services/order_service.py → services/notification.py
services/notification.py → services/order_service.py
(notification imports order_service to get the order data)
# Prompt 4: Deep dive into a specific module
claude "Analyze services/order_service.py in detail:
1. What functions it exposes (the module's public API)
2. What each function does in one line
3. What side effects it has (DB writes, API calls, emails)
4. What exceptions it can raise"
Expected output:
services/order_service.py — Public API:
create_order(user_id, items) -> Order
→ Validates user, computes total, creates order in DB
→ Side effects: DB write, Stripe charge, email
→ Raises: UserNotFoundError, PaymentFailedError, InsufficientStockError
get_order(order_id) -> Order
→ Reads order from DB, includes items
→ Side effects: none (read-only)
→ Raises: OrderNotFoundError
cancel_order(order_id, reason) -> Order
→ Changes status to cancelled, refund if already charged
→ Side effects: DB write, Stripe refund, email
→ Raises: OrderNotFoundError, OrderAlreadyCancelledError
update_order_status(order_id, new_status) -> Order
→ Validates status transition, updates in DB
→ Side effects: DB write, email if status is "shipped"
→ Raises: OrderNotFoundError, InvalidStatusTransitionError
Level 3: Low-Level — Key Functions and Data Structures
What it answers: "How do the critical pieces work internally?"
This level is selective. You don't map all the functions — only the critical ones: the ones that handle money, authentication, complex data transformations, or the ones with the most dependencies.
How to build it with Claude Code:
# Prompt 5: Critical function analysis
claude "Analyze the create_order() function in services/order_service.py.
Trace the complete flow step by step:
1. What it receives as input
2. What validations it does
3. In what order it calls other services/repositories
4. What happens if it fails at each step (error handling)
5. What it returns at the end
Include the happy path and the error paths."
Expected output:
create_order(user_id: int, items: list[OrderItem]) -> Order
HAPPY PATH:
──────────────────────────────────────────────
1. user = user_repo.get_by_id(user_id)
└── If it doesn't exist → raise UserNotFoundError
2. for item in items:
│ product = product_repo.get_by_id(item.product_id)
│ └── If it doesn't exist → raise ProductNotFoundError
│ if product.stock < item.quantity:
│ └── raise InsufficientStockError
└── Computes subtotal per item
3. total = sum(subtotals) + calculate_tax(subtotals)
4. payment = payment_service.charge(user, total)
└── If Stripe fails → raise PaymentFailedError
5. order = order_repo.create(
user_id=user_id,
items=items,
total=total,
payment_id=payment.id,
status="confirmed"
)
6. for item in items:
product_repo.decrease_stock(item.product_id, item.quantity)
7. notification.send_order_confirmation(user, order)
└── If email fails → log warning (do NOT raise, non-critical)
8. return order
ERROR PATHS:
──────────────────────────────────────────────
- Step 1 fails → UserNotFoundError (no side effects yet)
- Step 2 fails → ProductNotFoundError or InsufficientStockError (no side effects)
- Step 4 fails → PaymentFailedError (Stripe didn't charge, no DB side effects)
- Step 5 fails → DB error → ⚠️ PROBLEM: Stripe already charged but order wasn't created
There's no Stripe rollback in this path. POTENTIAL BUG.
- Step 6 fails → DB error → Order exists but stock wasn't updated. BUG.
# Prompt 6: Main data structures
claude "Identify the 5 most important data structures in this
project (DB models, DTOs, config objects). For each one:
1. Name and location
2. Main fields
3. Relationships with other data structures
4. Where it's created and where it's consumed"
Expected output:
MAIN DATA STRUCTURES:
1. Order (models/order.py)
├── Fields: id, user_id, status, total, created_at, payment_id
├── Relationships: belongs_to User, has_many OrderItems, has_one Payment
├── Created in: services/order_service.py (create_order)
└── Consumed in: api/v1/routes/orders.py, services/notification.py
2. User (models/user.py)
├── Fields: id, email, name, hashed_password, is_active, created_at
├── Relationships: has_many Orders, has_one Profile
├── Created in: services/user_service.py (register_user)
└── Consumed in: api/v1/routes/users.py, middleware/auth.py
3. OrderItem (models/order_item.py)
├── Fields: id, order_id, product_id, quantity, unit_price
├── Relationships: belongs_to Order, belongs_to Product
├── Created in: services/order_service.py (inside create_order)
└── Consumed in: services/order_service.py (calculations), api (display)
4. Product (models/product.py)
├── Fields: id, name, description, price, stock, category_id
├── Relationships: belongs_to Category, has_many OrderItems
├── Created in: cli/manage.py (seed data), admin routes
└── Consumed in: services/order_service.py, api/v1/routes/products.py
5. PaymentResult (services/payment_service.py — dataclass)
├── Fields: id, stripe_id, amount, status, error_message
├── Relationships: associated with Order via payment_id
├── Created in: payment_service.charge()
└── Consumed in: order_service.create_order()
Building the Mental Model with Claude Code — Complete Process
Step 1: Level 1 in 10 minutes
Open your terminal at the project root and run these prompts in sequence:
# Step 1a: General structure
claude "Give me an overview of this project's architecture.
What layers it has, how the directories are organized,
and what the main entry points are."
# Step 1b: Layer diagram
claude "Generate an ASCII diagram showing the project's layers
and the direction of the dependencies between them."
# Step 1c: External dependencies
claude "What external dependencies does this project use?
Analyze requirements.txt (or pyproject.toml) and group by category:
framework, database, external APIs, utilities."
Estimated time: 10-15 minutes. When you finish you have the high-level map.
Step 2: Level 2 in 20 minutes
# Step 2a: Dependency map
claude "Generate a dependency map between the main modules.
For each file in services/, show what it depends on
and what depends on it."
# Step 2b: Circular dependencies
claude "Are there circular dependencies in this project?
Analyze all the imports and report any cycles."
# Step 2c: Most connected modules
claude "Which are the 5 files with the most dependencies
(both incoming and outgoing)? These are the 'hubs'
of the project — the riskiest ones to modify."
Estimated time: 15-20 minutes. When you finish you have the map of connections.
Step 3: Level 3 in 30 minutes (selective)
# Step 3a: Critical functions
claude "Which are the 5 most critical functions in this project?
Criteria: they handle money, authentication, or have more than
5 dependencies. For each one, describe the flow step by step."
# Step 3b: Data structures
claude "Which are the main data models?
For each one: fields, relationships, where it's created, where it's consumed."
# Step 3c: Error handling
claude "How does this project handle errors?
Is there a consistent pattern? Where are the gaps in error handling?"
Estimated time: 25-30 minutes. When you finish you have depth in the critical areas.
Total: ~60 minutes for a functional mental model of a 5K-10K-line codebase.
Visualizing the Mental Model
The findings Claude Code generates are useful but ephemeral if you don't persist them. These are three techniques to make your mental model visible and shareable.
Technique 1: ASCII diagram (fast, inline)
claude "Generate an ASCII diagram showing:
1. The project's 3 layers
2. The modules within each layer
3. The dependency arrows between modules
Use box-drawing characters (┌ ─ └ │ → ▼)"
Advantage: It can go in any README, comment, or Slack message. Limitation: It becomes illegible with more than 10-15 components.
Technique 2: Mermaid Diagram (professional, renderable)
claude "Generate a Mermaid diagram showing the project's
architecture. Use graph TD to show the layer hierarchy
and the dependencies between modules."
Expected output:
graph TD
subgraph "Layer 1: Presentation"
ROUTES[api/v1/routes/]
MIDDLEWARE[middleware/]
DEPS[dependencies/]
end
subgraph "Layer 2: Business"
USER_SVC[user_service]
ORDER_SVC[order_service]
PAYMENT_SVC[payment_service]
NOTIFICATION[notification]
end
subgraph "Layer 3: Data"
USER_REPO[user_repo]
ORDER_REPO[order_repo]
PAYMENT_REPO[payment_repo]
MODELS[models/]
end
subgraph "External"
DB[(PostgreSQL)]
REDIS[(Redis)]
STRIPE[Stripe API]
SENDGRID[SendGrid]
end
ROUTES --> USER_SVC
ROUTES --> ORDER_SVC
MIDDLEWARE --> USER_SVC
ORDER_SVC --> ORDER_REPO
ORDER_SVC --> PAYMENT_SVC
ORDER_SVC --> NOTIFICATION
PAYMENT_SVC --> PAYMENT_REPO
PAYMENT_SVC --> STRIPE
USER_SVC --> USER_REPO
NOTIFICATION --> SENDGRID
USER_REPO --> MODELS
ORDER_REPO --> MODELS
MODELS --> DB
Advantage: It renders in GitHub, Notion, documentation. Professional. Limitation: It requires your platform to support Mermaid.
Technique 3: Dependency list (exhaustive, greppable)
claude "Generate a flat list of all the project's dependencies
in the format 'A → B (reason)'. One line per dependency.
Order by source module."
Expected output:
DEPENDENCY LIST:
api/v1/routes/orders.py → services/order_service.py (business logic)
api/v1/routes/orders.py → api/dependencies/auth.py (JWT validation)
api/v1/routes/users.py → services/user_service.py (business logic)
api/v1/routes/payments.py → services/payment_service.py (webhook handler)
services/order_service.py → repositories/order_repo.py (DB queries)
services/order_service.py → repositories/user_repo.py (user validation)
services/order_service.py → services/payment_service.py (charge)
services/order_service.py → services/notification.py (email)
services/payment_service.py → repositories/payment_repo.py (DB queries)
services/payment_service.py → core/stripe_client.py (Stripe API)
services/notification.py → services/order_service.py (get order data) ⚠️ CIRCULAR
Advantage: Easy to search with grep/ctrl+f. Complete. Limitation: It doesn't show the visual structure.
Validating the Mental Model
Building a mental model without validating it is like writing tests without running them. Validation is the most important step and the most skipped one.
Technique 1: Predict and verify
Pick a function and predict what happens if you modify it:
# Step 1: Make your prediction BEFORE asking Claude Code
# Your prediction: "If I rename create_order() to place_order(),
# these break: orders.py (route), manage.py (CLI), test_orders.py"
# Step 2: Verify with Claude Code
claude "If I rename the create_order() function in
services/order_service.py to place_order(),
what files break and why?"
If your prediction was right: Your Level 2 mental model is correct for that module. If your prediction was wrong: You found a gap. Update your mental model.
Technique 2: Make a small real change
# Make a harmless change to verify you understand the flow
claude "Add a log message at the start of create_order() that says
'Creating order for user {user_id} with {len(items)} items'.
Use the logger that already exists in the project."
If Claude Code does it correctly and the tests pass, it confirms that:
- ✅ Your mental model of where the function is is correct
- ✅ Your mental model of how it's called is correct
- ✅ The project has logging configured where you thought
If something fails, your mental model has a specific gap you can correct.
Technique 3: Trace a request end-to-end
claude "Trace the complete flow of a POST /orders request
from when it hits the server to when it returns the response.
Include every function that runs in order, every module
touched, and every side effect (DB, API, email)."
Compare the result with your mental model. The points where it differs are your gaps.
Comparison: Complete vs Partial Mental Model
| Criterion | Partial Mental Model | Complete Mental Model |
|---|---|---|
| Build time | 15-20 minutes | 45-60 minutes |
| Coverage | Only Level 1 (layers) | Levels 1, 2, and 3 |
| Predict consequences | "Something in services probably breaks" | "These 3 specific files break" |
| Make changes | Fearfully, many manual tests | Confidently, targeted verifications |
| Find bugs | When they show up in production | Before making the change (in the analysis) |
| Useful for others | Hard to communicate ("it's complicated") | Documentable and shareable |
| Maintenance | Goes stale fast (you have no base) | Easy to update (you adjust the map) |
When is a partial mental model enough?
- ✅ You're going to make ONE specific, small change
- ✅ The codebase is small (<2K lines)
- ✅ There's up-to-date documentation you can consult
When do you need a complete mental model?
- ✅ You're going to make significant changes or refactoring
- ✅ The codebase is medium-large (5K+ lines)
- ✅ There's no reliable documentation
- ✅ You're going to work on the project for weeks/months
Trade-off: The extra 30 minutes you invest in completing the mental model pay off 10x the first time you avoid a bug because you correctly predicted a consequence.
Connection with the Project
In the Module Project: Onboarding to an Open-Source Codebase (this module's project):
- You'll use the 3 levels of the mental model to document your understanding of the open-source project you choose
- Level 1 will give you the project's general structure for your onboarding doc
- Level 2 will show you how the modules connect — critical information for your first change
- Level 3 (selective) will help you identify the critical functions where you'll make your validation change
- The "predict and verify" technique is exactly how you'll validate your understanding in the deliverable
Everything you learn here applies directly to the module project.
Troubleshooting
Problem 1: Claude Code generates a mental model that's too superficial
Cause: The prompt is too generic ("explain this project to me"). Solution: Use specific prompts per level. Instead of asking for "an overview", ask:
claude "Identify the architecture layers, which directory
corresponds to each layer, and what the dependency rule
between layers is (who can call whom)."
Problem 2: The dependency map has too much information
Cause: You asked for all the dependencies of all the files at once. Solution: Focus on one layer or module at a time:
# Instead of: "map of ALL dependencies"
# Use: one module at a time
claude "Dependency map ONLY for services/order_service.py.
What it imports and who imports it."
Problem 3: Level 3 takes too long
Cause: You're trying to analyze all the functions in depth. Solution: Be selective. Only dig into the 3-5 most critical functions:
claude "Which are the 3 functions in this project that carry the most
risk if modified incorrectly? (Criteria: they handle money,
sensitive data, or have the most dependencies)"
Problem 4: The Mermaid diagrams don't render correctly
Cause: Syntax errors in the generated Mermaid or special characters. Solution: Ask Claude Code to validate the diagram:
claude "Review this Mermaid diagram and fix any syntax errors.
Make sure all the nodes have valid IDs (no spaces or
special characters)."
Problem 5: Your prediction failed but you don't know why
Cause: A gap in the mental model you can't identify on your own. Solution: Ask Claude Code to explain the difference:
claude "I predicted that changing create_order() would break 3 files:
orders.py, manage.py, test_orders.py. But notification.py
also broke. Explain to me why notification.py depends on
create_order() — I didn't see that connection."
Exercises
Exercise 1: Level 1 Mental Model of httpx (Easy)
Clone the httpx project (pip install httpx and find its source code or clone it from GitHub). Use Claude Code to generate Level 1 of the mental model: layers, main components, and entry points. Your deliverable is an ASCII diagram of the layers.
See solution
# Clone httpx
git clone https://github.com/encode/httpx.git
cd httpx
# Generate Level 1
claude "Analyze the structure of this Python project (httpx).
Identify:
1. The main architecture layers or components
2. Which directory or file corresponds to each component
3. What the public entry points are (what a user imports)
4. The main external dependencies"
Expected output (simplified):
httpx/
├── _client.py → Main entry point (Client, AsyncClient)
├── _models.py → Request, Response, URL, Headers
├── _transports/ → HTTP transport layer (sync and async)
├── _content.py → Content encoding/decoding
├── _urls.py → URL parsing and construction
├── _auth.py → Authentication handlers
└── _config.py → SSL, timeout, proxy config
Layers:
1. Public API: Client, AsyncClient (_client.py)
2. Models: Request, Response (_models.py)
3. Transport: BaseTransport, HTTPTransport (_transports/)
4. Utilities: config, auth, urls, content
Entry point for users:
from httpx import Client, get, post
Explanation: Level 1 gives you the bird's-eye view. httpx has a relatively flat architecture — it's not a web framework with many layers, but an HTTP library with a clear public API (_client.py) and well-separated internal components.
Exercise 2: Dependency Map of a Module (Easy)
Using the same httpx project, generate the dependency map of the _client.py file. What modules does it import? Who imports _client.py?
See solution
claude "Analyze httpx/_client.py and generate its dependency map:
1. What internal httpx modules it imports
2. What external dependencies it uses
3. Who imports _client.py (within the project)"
Expected output:
httpx/_client.py — DEPENDENCIES:
IMPORTS (internal dependencies):
├── _models.py (Request, Response)
├── _transports/ (BaseTransport, HTTPTransport, AsyncHTTPTransport)
├── _config.py (SSLConfig, Timeout, Proxy)
├── _auth.py (Auth)
├── _urls.py (URL)
├── _content.py (encode content)
└── _exceptions.py (HTTPStatusError, etc.)
IMPORTS (external dependencies):
├── httpcore (low-level HTTP transport)
├── typing (type hints)
└── contextlib (context managers)
IMPORTED BY:
├── __init__.py (re-exports Client, AsyncClient, get, post, etc.)
└── _api.py (convenience functions: get(), post(), etc.)
Explanation: _client.py is the project's central hub. It imports almost all the internal modules and is the only file that end users touch (indirectly via init.py). This tells you that any change in _client.py has high impact.
Exercise 3: Predict and Verify (Medium)
Without asking Claude Code first, predict: "If I remove the Timeout class from _config.py, what files break?". Write your prediction. Then verify with Claude Code. Report the difference between your prediction and reality.
See solution
# Step 1: Your prediction (write it before running)
# Prediction: "_client.py breaks because it imports Timeout.
# _api.py probably too. And tests."
# Step 2: Verify
claude "If I remove the Timeout class from httpx/_config.py,
what project files break and why?
List each affected file."
Expected output:
Files that break:
1. _client.py — imports Timeout directly, uses it in __init__
and in send() to configure per-request timeouts
2. _api.py — uses Timeout as a default in get(), post(), etc.
3. __init__.py — re-exports Timeout as part of the public API
4. _transports/default.py — receives Timeout to configure httpcore
5. tests/test_config.py — tests Timeout directly
6. tests/test_timeouts.py — specific tests for timeout behavior
TOTAL: 6 files (possibly more in tests)
Explanation: If your prediction was 2-3 files and reality was 6, your mental model had gaps at Level 2 (you didn't know that _transports/ and init.py also used Timeout directly). That's exactly what this exercise reveals.
Exercise 4: Mermaid Flow Diagram (Medium)
Use Claude Code to generate a Mermaid diagram of the flow of an HTTP request in httpx: from when the user calls client.get(url) to when they receive a Response. Include the modules the request passes through.
See solution
claude "Generate a Mermaid diagram (sequenceDiagram) showing
the flow of an HTTP request in httpx. From when the user
calls client.get(url) to when they receive a Response.
Show each module/class that participates in the flow."
Expected output:
sequenceDiagram
participant User
participant Client as _client.Client
participant Models as _models.Request
participant Auth as _auth.Auth
participant Transport as _transports.HTTPTransport
participant HTTPCore as httpcore
User->>Client: client.get(url)
Client->>Client: _build_request(method, url, ...)
Client->>Models: Request(method, url, headers, content)
Client->>Auth: auth_flow(request)
Auth-->>Client: request with auth headers
Client->>Transport: handle_request(request)
Transport->>HTTPCore: httpcore.request(...)
HTTPCore-->>Transport: httpcore.Response
Transport-->>Client: Response
Client->>Client: _build_response(transport_response)
Client-->>User: Response(status=200, ...)
Explanation: The sequence diagram shows that a simple get() passes through at least 5 modules. This is the kind of insight that Level 3 reveals and that's impossible to get with Level 1 alone.
Exercise 5: Compare the Mental Model of Two Projects (Hard)
Clone two projects: httpx and requests (Python's original HTTP library). Generate Level 1 of the mental model for both. Compare their architectures. Which one has clearer layers? Which one has more coupling? Document it in a paragraph per project.
See solution
# httpx
cd httpx
claude "Generate Level 1 of this project's mental model:
layers, components, entry points, external dependencies."
# requests
cd ../requests
git clone https://github.com/psf/requests.git
cd requests
claude "Generate Level 1 of this project's mental model:
layers, components, entry points, external dependencies."
Expected comparison:
httpx:
- Clear modular architecture: _client, _models, _transports separated
- Well-defined dependencies (httpcore for transport)
- Consistent type hints
- Native async support (AsyncClient as a first-class citizen)
- Clear boundaries between layers
requests:
- More monolithic architecture: a lot of logic in api.py and sessions.py
- urllib3 as a transport dependency (older, larger)
- No significant type hints (legacy project)
- No async (requires a separate library: aiohttp or httpx)
- Less clear boundaries between "what requests does" vs "what urllib3 does"
Conclusion: httpx has a more modern and modular architecture.
requests is a legacy project with accumulated tech debt. This
comparison is exactly the kind of analysis you'll do in the refactoring
modules (4-7) of this guide.
Explanation: Comparing the mental models of two projects that solve the same problem is a powerful technique for understanding architectural trade-offs. It shows you that an "HTTP client" can be implemented in very different ways.
Exercise 6: Complete Mental Model in 60 Minutes (Hard)
Choose a Python project of 5K-10K lines you have NOT seen before (suggestions: typer, rich, textual). In 60 minutes, build the 3 levels of the mental model using Claude Code. Document your result in a markdown file with: layer diagram, dependency map of the 3 most connected modules, and detailed flow of a critical function.
See solution
# Example with typer
git clone https://github.com/tiangolo/typer.git
cd typer
# Minutes 0-10: Level 1
claude "Analyze this Python project (typer). Give me:
1. Architecture layers/components
2. Public entry points
3. External dependencies"
# Minutes 10-30: Level 2
claude "Generate the dependency map for the 5 most important
files in typer/. For each one: what it imports and
who imports it."
claude "Are there circular dependencies? Which are the 3 files
with the most connections (hub files)?"
# Minutes 30-50: Level 3
claude "Analyze the main function that executes a CLI command
in typer. Trace the flow from when the user types a command
in the terminal to when the corresponding Python function runs."
# Minutes 50-60: Document
claude "Generate a markdown document with:
1. A Mermaid diagram of typer's architecture
2. A dependency map of the 3 hub files
3. A detailed execution flow of a command"
Explanation: This exercise simulates the module project exactly. The key is the time-boxing: 60 minutes is enough for a functional mental model of a 5K-10K-line project with Claude Code. Without AI, this same process would take 1-2 weeks.
Summary
In this capsule you learned:
- A mental model is your internal representation of how a codebase works. It lets you predict the consequences of changes.
- It's built in 3 levels: high-level (layers, entry points), mid-level (modules, dependencies), low-level (functions, data structures).
- Claude Code accelerates the building: specific prompts per level generate the information you need in minutes, not days.
- Scattered findings aren't comprehension. You need to connect them with integration questions: the story of a request, boundaries, inconsistencies.
- Visualizing the mental model (ASCII, Mermaid, dependency lists) makes it persistent and shareable.
- Validating is the most important step and the most skipped one. Make predictions and verify them. The errors reveal the gaps.
- The perfect mental model doesn't exist. The goal is functional: to be able to predict 80% of the consequences of a change.
Next capsule: Documenting Findings with Claude Code — where you turn your mental model into a tangible artifact that others can use.
Additional Resources
-
"Working Effectively with Legacy Code" — Michael Feathers Chapter 16: "I Don't Understand the Code Well Enough to Change It." The book that formalized the importance of understanding before modifying. Relevant to the "make a small change to validate understanding" technique.
-
"Software Architecture in Practice" — Bass, Clements, Kazman The chapters on architectural views (module view, component-and-connector view, allocation view) formalize the 3 levels of mental model we cover.
-
"Documenting Software Architectures" — Clements et al. Techniques to document and communicate architecture. Complements this capsule's visualization section.
-
Mermaid.js Documentation — https://mermaid.js.org/ Complete reference for Mermaid diagrams. Useful for generating more complex visualizations (sequence diagrams, class diagrams, state diagrams).
-
Claude Code Documentation — Explore Subagent In Module 2 you'll learn to use the Explore subagent, which is the specialized tool for the exploration techniques you saw here. What you did manually with prompts, Explore automates.
-
"A Philosophy of Software Design" — John Ousterhout The concepts of "deep modules" and "shallow modules" complement the idea of a mental model by levels. A "deep" module has a simple interface but complex implementation — exactly what your Level 3 needs to map.
-
"The Pragmatic Programmer" — Hunt, Thomas The concept of "tracer bullets" (making a minimal end-to-end change to validate the architecture) is exactly what we do in the mental model validation section.
-
C4 Model — https://c4model.com/ A formal framework for documenting software architecture in 4 levels (Context, Containers, Components, Code) that aligns with our 3-level mental model approach.