25 min read

Every real-world software project spans multiple files. Even a modest web application might include dozens of Python modules, configuration files, templates, stylesheets, tests, and documentation. A production enterprise system can easily contain...

Chapter 13: Working with Multiple Files and Large Codebases

Learning Objectives

By the end of this chapter, you will be able to:

  • Remember the key challenges AI coding assistants face when working with multi-file projects (Bloom's: Remember)
  • Understand how context windows limit AI's ability to see an entire codebase at once and why strategic context selection matters (Bloom's: Understand)
  • Apply techniques for providing cross-file context, including directory trees, interface definitions, and file summaries (Bloom's: Apply)
  • Analyze when to use file-by-file generation versus holistic multi-file generation based on the coupling between components (Bloom's: Analyze)
  • Evaluate different strategies for maintaining naming conventions, style consistency, and dependency coherence across a multi-file project (Bloom's: Evaluate)
  • Create repository maps, context summaries, and consistency-checking workflows that scale vibe coding to large, real-world codebases (Bloom's: Create)

13.1 The Multi-File Challenge

Every real-world software project spans multiple files. Even a modest web application might include dozens of Python modules, configuration files, templates, stylesheets, tests, and documentation. A production enterprise system can easily contain thousands of files across hundreds of directories. This multi-file reality creates a fundamental tension with AI coding assistants, which operate within a finite context window and typically interact with one conversation at a time.

When you were working through the earlier chapters of this book, many of the examples involved single files or small, self-contained scripts. That approach works beautifully for learning, prototyping, and small utilities. But the moment you step into a project with separate models, views, controllers, services, utilities, configuration, and tests, the dynamics change dramatically.

Why Multi-File Projects Are Different

Consider what happens when you ask an AI assistant to add a new feature to an existing project. The assistant needs to understand:

  1. Where the change should go (which files, which modules)
  2. How the existing code is structured (naming conventions, patterns, architecture)
  3. What other code depends on or is depended upon by the code being changed (imports, function calls, shared types)
  4. Why certain design decisions were made (to avoid contradicting them)

In a single-file script, all of this information is right there in the file. In a multi-file project, it is scattered across the codebase. The AI cannot simply "look around" the way a human developer with an IDE can. It sees only what you explicitly provide in the conversation.

Intuition

Think of working with an AI on a multi-file project like collaborating with a brilliant colleague who is sitting in a windowless room. They can reason expertly about anything you show them, but they cannot see the rest of the building. If you need them to design a new room that connects to three existing hallways, you need to describe those hallways in enough detail for the design to work. The quality of their output depends entirely on the quality of the context you provide.

The Context Window Bottleneck

As we discussed in Chapter 9 (Context Management), every AI model has a finite context window measured in tokens. Even models with 200,000-token context windows cannot hold an entire large codebase. A codebase with 50,000 lines of code might require 150,000 to 250,000 tokens just for the source files alone, before any conversation or instructions.

This means you must be selective about what context you provide. The art of working with AI on large codebases is the art of providing just enough context for the AI to do excellent work without overwhelming the window or diluting the signal with irrelevant information.

The Consistency Challenge

When you generate code file by file, each generation is somewhat independent. The AI might use snake_case in one file and camelCase in another. It might name a database connection db in one module and database_connection in another. It might structure error handling with exceptions in one place and return codes in another.

Maintaining consistency across files requires deliberate effort. You need to establish conventions, communicate them clearly, and verify that generated code follows them. This chapter will equip you with the strategies and tools to do exactly that.


13.2 Repository Structure and Navigation

Before you can effectively provide context about a codebase, you need to understand and communicate its structure. Repository structure is the skeleton of your project, and it tells both humans and AI assistants where to find things.

Standard Project Layouts

Most programming ecosystems have established conventions for project structure. Here is a typical Python project layout:

my-project/
├── pyproject.toml
├── README.md
├── src/
│   └── my_project/
│       ├── __init__.py
│       ├── main.py
│       ├── models/
│       │   ├── __init__.py
│       │   ├── user.py
│       │   └── product.py
│       ├── services/
│       │   ├── __init__.py
│       │   ├── auth_service.py
│       │   └── product_service.py
│       ├── api/
│       │   ├── __init__.py
│       │   ├── routes.py
│       │   └── middleware.py
│       └── utils/
│           ├── __init__.py
│           ├── database.py
│           └── validation.py
├── tests/
│   ├── __init__.py
│   ├── test_models.py
│   ├── test_services.py
│   └── test_api.py
└── docs/
    └── architecture.md

When you share a directory tree like this with an AI assistant, it immediately understands the project's architectural layers. It can infer that models/ contains data definitions, services/ contains business logic, api/ contains HTTP endpoints, and utils/ contains shared utilities.

Generating Repository Maps

A repository map is a structured summary of your project that goes beyond a simple directory listing. It includes file sizes, key classes and functions, and the relationships between modules. This is one of the most powerful tools you can provide to an AI assistant.

# A simple repository map might look like this when rendered:
"""
Repository Map: my-project
==========================

src/my_project/models/user.py (45 lines)
  Classes: User, UserRole(Enum)
  Dependencies: dataclasses, enum

src/my_project/models/product.py (62 lines)
  Classes: Product, ProductCategory(Enum)
  Dependencies: dataclasses, enum, decimal

src/my_project/services/auth_service.py (120 lines)
  Classes: AuthService
  Functions: hash_password, verify_token
  Dependencies: models.user, utils.database, jwt, bcrypt

src/my_project/services/product_service.py (95 lines)
  Classes: ProductService
  Functions: calculate_discount
  Dependencies: models.product, utils.database, utils.validation
"""

This kind of map tells the AI far more than a directory listing. It reveals the dependency graph, the size and complexity of each file, and the key abstractions in the system.

Best Practice

Create a repository map at the start of any multi-file vibe coding session. You can generate one manually, use the tool provided in this chapter's code examples, or use your AI assistant's built-in codebase analysis features (many modern tools like Claude Code can explore the filesystem directly). Keep the map updated as files are added or changed.

Using Tree Commands

Most operating systems provide a way to generate directory trees from the command line:

# On macOS/Linux
tree -I '__pycache__|node_modules|.git|*.pyc' --dirsfirst

# On Windows
tree /F /A

# Python-based alternative (cross-platform)
# See example-01-repo-mapper.py in this chapter's code directory

You can paste the output of these commands directly into your AI conversation to give the assistant an overview of the project structure.

Communicating Architecture Through Structure

The way you organize your directories communicates architectural intent. When you tell an AI assistant "this project uses a layered architecture with models, services, and API layers," the assistant can then generate code that respects those layers. If you also provide the directory tree, the assistant can place new files in the correct locations and maintain proper import paths.

Prompt example:

"This project follows a layered architecture:
- models/ contains dataclasses and enums (no business logic)
- services/ contains business logic (depends on models, not on api)
- api/ contains HTTP route handlers (depends on services)
- utils/ contains shared utilities (no dependencies on other project layers)

Here is the current directory tree:
[paste tree output]

Please add a new 'order' feature that follows this same architecture."

This kind of prompt gives the AI the structural rules it needs to generate well-organized code.


13.3 Providing Cross-File Context

The most critical skill for multi-file vibe coding is learning to provide the right cross-file context. This means giving the AI enough information about other files in your project to generate code that integrates correctly, without dumping entire files into the conversation unnecessarily.

The Context Selection Problem

Imagine you are asking the AI to write a new service module that interacts with two existing models, a database utility, and a validation library. You could paste all four files into the conversation, but that might consume thousands of tokens. Alternatively, you could provide just the information the AI actually needs: the interfaces, type signatures, and key behaviors of those dependencies.

Strategy 1: Interface-First Context

The most token-efficient approach is to provide only the public interfaces of the files the AI needs to interact with. This means class signatures, method signatures with type hints, and docstrings, but not the implementation details.

# Instead of pasting the entire user.py file (45 lines), provide just:
"""
# From models/user.py:
@dataclass
class User:
    id: int
    username: str
    email: str
    password_hash: str
    role: UserRole
    created_at: datetime

class UserRole(Enum):
    ADMIN = "admin"
    EDITOR = "editor"
    VIEWER = "viewer"
"""

This gives the AI everything it needs to write code that creates, accepts, or returns User objects, without wasting tokens on implementation details it does not need.

Strategy 2: Dependency Summary Blocks

For larger projects, you can create a concise summary block that describes multiple dependencies at once:

Dependencies for the new OrderService:

1. User (models/user.py): Dataclass with fields id, username, email, role(UserRole enum)
2. Product (models/product.py): Dataclass with fields id, name, price(Decimal), category(ProductCategory enum)
3. Database (utils/database.py): Provides get_connection() -> Connection, execute_query(conn, sql, params) -> list[dict]
4. validate_email(email: str) -> bool from utils/validation.py
5. validate_positive_decimal(value: Decimal) -> bool from utils/validation.py

This compressed format conveys the essential information in very few tokens.

Strategy 3: Full File Inclusion (When Necessary)

Sometimes you need to include entire files. This is appropriate when:

  • The AI needs to modify an existing file (it needs to see the full content to make correct edits)
  • The file is short (under 50 lines) and fully relevant
  • The integration is complex and the implementation details matter (not just the interface)
  • You are debugging an issue that might be caused by implementation details

Common Pitfall

Avoid the temptation to include every possibly-relevant file "just in case." This wastes context window space, increases the chance of the AI getting confused by irrelevant details, and can slow down response generation. Start with interfaces, and only include full files if the AI's output shows it needs more context.

Strategy 4: Example-Driven Context

When the AI needs to generate something that follows an existing pattern, showing one complete example is often more effective than describing the pattern abstractly.

Prompt example:

"Here is our existing ProductService (services/product_service.py) as a reference
for the coding style and patterns we use:

[paste full product_service.py]

Please create a new OrderService following the same patterns, but for
managing orders. An order has: id, user_id, items (list of OrderItem),
total_price, status, and created_at."

This approach leverages the AI's ability to recognize and replicate patterns. It is especially useful for maintaining consistency across similar modules.

Strategy 5: Progressive Context Disclosure

For complex tasks, provide context incrementally rather than all at once:

  1. Start with the high-level architecture and repository map
  2. Ask the AI to outline its approach
  3. Provide the specific files it identifies as needing to see
  4. Have it generate the code
  5. Provide additional context if the first output reveals gaps

This conversational approach helps you identify exactly what context the AI needs, rather than guessing up front.

Real-World Application

Professional developers using AI tools often maintain a "context document" for their project: a markdown file that summarizes the architecture, key abstractions, naming conventions, and common patterns. This document can be pasted at the start of any new AI conversation to quickly bring the assistant up to speed. Some teams check this document into the repository and keep it updated as the project evolves.


13.4 Strategies for Large Context Requirements

Some tasks inherently require understanding a large amount of code. Refactoring a module that is used by twenty other files, adding a cross-cutting concern like logging or authentication, or migrating a database schema that touches multiple models and services all demand broad context. Here are strategies for handling these situations.

Chunked Processing

Break the large task into smaller, sequential steps that each require only a manageable amount of context:

  1. Phase 1: Analysis — Ask the AI to analyze the repository map and identify all files that need to change.
  2. Phase 2: Plan — Provide the identified files (or their interfaces) and ask for a change plan.
  3. Phase 3: Execute — Work through the plan file by file, providing each file's full content when it is its turn to be modified.
  4. Phase 4: Verify — Ask the AI to review the changes for consistency and correctness.
Prompt example (Phase 1):

"I need to add audit logging to all service methods in our application.
Here is the repository map:

[paste repository map]

Which files will need to be modified? What is the best approach
for adding audit logging consistently?"

The Summary Layer Pattern

For very large codebases, create an intermediate "summary layer" that sits between the raw source code and the AI conversation:

Architecture Summary (500 tokens)
  └── Module Summaries (200 tokens each)
       └── File Summaries (50-100 tokens each)
            └── Full Source (variable, only loaded as needed)

You start by providing the architecture summary. When the AI needs more detail about a specific module, you provide its module summary. Only when the AI needs to modify or deeply understand a specific file do you load its full source.

Sliding Window Approach

When processing many files sequentially, maintain a "sliding window" of context. Keep the following in the conversation at all times:

  • The overall task description and conventions
  • The repository map
  • The file currently being worked on
  • The most recently completed file (for consistency reference)

Drop files from the context as you move past them. This prevents the conversation from growing unboundedly while maintaining enough context for consistency.

Advanced

Some AI coding tools support automatic context management. Claude Code, for instance, can explore the filesystem and selectively read files as needed. Cursor has an @codebase feature that indexes your project and retrieves relevant context automatically. When available, these features can dramatically simplify working with large codebases. However, understanding manual context management remains valuable, both for situations where automatic tools fall short and for understanding what the AI is actually working with.

Parallel Workstreams

For truly large changes, consider splitting the work into parallel independent workstreams. If you need to add validation to models, update services to use the new validation, and update API routes to return validation errors, you can work on the models first (they have no dependencies on the other layers), then the services (which depend on models), then the API (which depends on services).

Workstream 1 (Independent): Update all model validations
Workstream 2 (Depends on 1): Update services to use new validation
Workstream 3 (Depends on 2): Update API error responses

Each workstream can be a separate AI conversation with its own focused context.


13.5 File-by-File vs. Holistic Generation

One of the key decisions in multi-file vibe coding is whether to generate code file by file or request multiple files at once. Both approaches have strengths and weaknesses.

File-by-File Generation

In this approach, you generate each file in a separate prompt, providing context about the other files as needed.

Advantages: - More control over each file's content - Easier to review and iterate on individual files - Works well within context window limits - Good for files that are relatively independent

Disadvantages: - Risk of inconsistency between files - Requires careful context management - Can be slow for projects with many files - May miss integration issues until later

Best for: - Large files that benefit from focused attention - Files with complex internal logic - Projects where files are loosely coupled - Situations where you want to review each file carefully

Holistic Generation

In this approach, you describe the entire project or feature and ask the AI to generate all files at once.

Advantages: - Better consistency across files (naming, patterns, types) - AI can reason about inter-file dependencies naturally - Faster for generating initial project scaffolding - Integration issues are caught during generation

Disadvantages: - Each individual file may get less attention to detail - Harder to provide specific guidance for each file - May hit context window limits for large requests - More difficult to iterate on specific parts

Best for: - Initial project scaffolding - Small to medium features (3-8 files) - Tightly coupled components that must work together - Prototype or proof-of-concept development

The Hybrid Approach

In practice, the most effective approach is a hybrid. Use holistic generation for the initial scaffold and tightly coupled groups of files, then switch to file-by-file generation for complex individual components.

Step 1 (Holistic): Generate project structure with all __init__.py files,
                    model definitions, and interface stubs

Step 2 (File-by-file): Implement each service module with full business logic

Step 3 (Holistic): Generate all test files at once (they follow similar patterns
                    and need to be consistent with each other)

Step 4 (File-by-file): Implement complex API routes one at a time

Intuition

Think of holistic generation like sketching the entire floor plan of a house at once. You want all the rooms to connect properly and the overall layout to make sense. File-by-file generation is like detailing each room individually: choosing the fixtures, finishes, and furniture. The floor plan ensures everything connects; the room-by-room detail ensures each space is well-designed. You need both.

Decision Framework

Use this decision framework when choosing between approaches:

Factor File-by-File Holistic
File count > 8 files 2-8 files
Coupling Loose Tight
Complexity per file High Low-Medium
Consistency priority Lower Higher
Context window pressure High Low-Medium
Phase of project Implementation Scaffolding

13.6 Maintaining Consistency Across Files

Consistency is what separates a professional codebase from a collection of scripts. When working with AI across multiple files, you need deliberate strategies to maintain consistency in naming, style, patterns, and conventions.

Establishing a Style Guide

Before generating any code, establish your project's conventions explicitly. Create a concise style guide that you can include in your AI prompts:

## Project Style Guide

### Naming Conventions
- Classes: PascalCase (e.g., UserService, OrderRepository)
- Functions/methods: snake_case (e.g., get_user_by_id, calculate_total)
- Constants: UPPER_SNAKE_CASE (e.g., MAX_RETRIES, DEFAULT_TIMEOUT)
- Private methods: leading underscore (e.g., _validate_input)
- File names: snake_case (e.g., user_service.py, order_repository.py)

### Patterns
- All services take dependencies via constructor injection
- All public methods have docstrings with Args/Returns/Raises sections
- All database operations use context managers for connections
- Error handling uses custom exception classes from errors.py
- All data transfer objects are frozen dataclasses

### Type Hints
- All function signatures have complete type hints
- Use Optional[X] (not X | None) for optional parameters
- Use list[X], dict[X, Y] (lowercase) for generic types
- Return types are always specified, including -> None

Including this in your prompts ensures that every file the AI generates follows the same conventions.

The Consistency Reference Pattern

When generating a new file, always provide at least one existing file as a "consistency reference." This is more effective than an abstract style guide because the AI can match the concrete patterns it sees:

Prompt example:

"Here is our existing UserService as a consistency reference:

[paste user_service.py]

Please create an OrderService that follows the exact same patterns:
- Same constructor pattern (dependency injection)
- Same docstring style
- Same error handling approach
- Same method structure (validate -> process -> persist -> return)

The OrderService should handle creating, retrieving, updating, and
canceling orders."

Automated Consistency Checking

After generating multiple files, use automated tools to verify consistency. The example-03-consistency-checker.py provided with this chapter demonstrates a basic consistency checker that can catch common issues:

  • Mixed naming conventions (some snake_case, some camelCase)
  • Inconsistent import styles (absolute vs. relative imports)
  • Missing type hints in some files but not others
  • Inconsistent docstring formats
  • Different error handling patterns
# Example output from a consistency check:
"""
Consistency Report
==================

Naming Issues:
  - services/order_service.py:42 - method 'getOrderById' uses camelCase
    (project convention: snake_case)

Import Style Issues:
  - api/routes.py:3 - uses relative import 'from ..models import User'
    (project convention: absolute imports)

Docstring Issues:
  - services/auth_service.py:28 - function 'verify_token' missing docstring
    (project convention: all public functions have docstrings)
"""

Best Practice

Run consistency checks after every batch of AI-generated files, not just at the end of the project. Catching inconsistencies early is much easier than fixing them after they have propagated through many files. Consider adding consistency checks to your pre-commit hooks or CI pipeline.

Convention Drift and How to Prevent It

In long vibe coding sessions, conventions can "drift" as the AI gradually shifts away from the established patterns. This happens because:

  1. Each new prompt might not include the full style guide
  2. The AI might introduce slight variations that accumulate
  3. Context from earlier in the conversation may be truncated

To prevent drift:

  • Re-anchor periodically: Include the style guide or a consistency reference every few prompts, not just at the beginning.
  • Review early files against late files: Compare the first file you generated with the most recent one to detect drift.
  • Use a "convention prompt" prefix: Create a standard preamble that you paste at the start of every prompt.
Convention prefix example:

"[Conventions: PascalCase classes, snake_case functions, Google-style
docstrings, dependency injection via __init__, custom exceptions from
errors.py, frozen dataclasses for DTOs, complete type hints.]"

This compact prefix reminds the AI of the conventions without consuming too many tokens.


13.7 Import and Dependency Management

One of the trickiest aspects of multi-file code generation is getting imports right. The AI needs to know exactly what is available from other modules and what the correct import paths are.

The Import Context Problem

When you ask the AI to generate a new service file, it needs to know:

  1. What modules exist in the project
  2. What each module exports (classes, functions, constants)
  3. What the correct import paths are (package structure)
  4. What external libraries are available (installed dependencies)

Without this information, the AI will guess. It might import from models.user when the correct path is my_project.models.user. It might assume a function exists in a utility module when it does not. It might import from a third-party library that is not in your project's dependencies.

Providing Import Maps

An import map is a concise summary of what each module exports and how to import it:

# Import Map for the my_project package:
#
# from my_project.models.user import User, UserRole
# from my_project.models.product import Product, ProductCategory
# from my_project.models.order import Order, OrderItem, OrderStatus
# from my_project.services.auth_service import AuthService
# from my_project.services.product_service import ProductService
# from my_project.services.order_service import OrderService
# from my_project.utils.database import get_connection, execute_query
# from my_project.utils.validation import validate_email, validate_positive_decimal
# from my_project.errors import NotFoundError, ValidationError, AuthorizationError

Including this in your prompt ensures the AI uses the correct import paths.

Managing Third-Party Dependencies

Always specify which third-party libraries are available in your project. You can include a simplified version of your pyproject.toml or requirements.txt:

Available third-party packages:
- flask (2.3+) - web framework
- sqlalchemy (2.0+) - ORM
- pydantic (2.0+) - data validation
- pytest (7.0+) - testing
- bcrypt - password hashing
- pyjwt - JWT tokens

This prevents the AI from importing libraries that are not installed and helps it choose the right library for each task.

Circular Import Prevention

Circular imports are a common problem in multi-file projects, and AI-generated code is particularly susceptible because the AI does not have a complete view of the dependency graph.

To prevent circular imports:

  1. Establish a clear dependency direction: Models should not import from services, services should not import from API routes, etc.
  2. Communicate the dependency rules: Include them in your prompt.
  3. Use interfaces/protocols for reverse dependencies: If a lower layer needs to reference a higher layer, use Protocol types.
Dependency rules:
  models/ → (no project imports)
  utils/ → (no project imports, only stdlib and third-party)
  services/ → models/, utils/
  api/ → services/, models/

  Never import from a higher layer into a lower layer.

Common Pitfall

AI assistants sometimes create circular imports by having a model file import a utility function that itself imports the model. For example, models/user.py imports validate_email from utils/validation.py, but utils/validation.py imports User from models/user.py to provide user-specific validation. Always check for these cycles in generated code. The fix is usually to restructure the code so that the dependency flows in one direction, or to use string annotations for type hints that would otherwise cause circular imports.

Generating __init__.py Files

The __init__.py files control what each package exports and can simplify imports for the rest of the project. Ask the AI to generate these explicitly:

Prompt example:

"Please generate the __init__.py for the models package that re-exports
all public classes. This lets other modules do:
    from my_project.models import User, Product, Order
instead of importing from individual submodules."

This results in cleaner, more consistent imports throughout the project.


13.8 Working with Monorepos

A monorepo is a single repository that contains multiple projects, packages, or services. Major companies like Google, Meta, and Microsoft use monorepos to manage their codebases. Monorepos present unique challenges for AI-assisted development due to their size and complexity.

Monorepo Structure

A typical Python monorepo might look like:

company-platform/
├── packages/
│   ├── auth-service/
│   │   ├── pyproject.toml
│   │   ├── src/auth_service/
│   │   └── tests/
│   ├── billing-service/
│   │   ├── pyproject.toml
│   │   ├── src/billing_service/
│   │   └── tests/
│   ├── shared-models/
│   │   ├── pyproject.toml
│   │   ├── src/shared_models/
│   │   └── tests/
│   └── common-utils/
│       ├── pyproject.toml
│       ├── src/common_utils/
│       └── tests/
├── tools/
│   ├── deploy.py
│   └── lint_all.py
└── README.md

Scoping AI Sessions to Packages

The key to working with monorepos is scoping. Never try to work with the entire monorepo at once. Instead, scope each AI session to a specific package or a small set of related packages:

Prompt example:

"I'm working in the auth-service package of our monorepo. Here's the
structure of this package:

[paste auth-service tree]

It depends on:
- shared-models: provides User, Role, Permission dataclasses
- common-utils: provides DatabaseConnection, CacheClient, Logger

Here are the relevant interfaces from those shared packages:

[paste interface summaries]

I need to add two-factor authentication support to auth-service."

This gives the AI a focused context while still providing the cross-package information it needs.

Shared Library Awareness

In a monorepo, shared libraries are a critical integration point. When working on a specific service, always provide the AI with the interfaces of the shared libraries that service depends on. Even better, provide the actual type definitions since they tend to be compact and crucial for correctness:

# From shared-models/src/shared_models/user.py
@dataclass(frozen=True)
class User:
    id: str
    email: str
    name: str
    roles: list[Role]

@dataclass(frozen=True)
class Role:
    id: str
    name: str
    permissions: list[Permission]

@dataclass(frozen=True)
class Permission:
    resource: str
    action: str  # "read", "write", "delete", "admin"

Cross-Package Changes

Sometimes a change requires modifications across multiple packages. For example, adding a new field to a shared model requires updating the model definition, every service that uses it, and all related tests.

Use the phased approach:

  1. Phase 1: Modify the shared model (one AI session)
  2. Phase 2: For each affected service, update the service code (one AI session per service, providing the updated model as context)
  3. Phase 3: Update tests across all affected packages

Real-World Application

Teams working in monorepos often create a "package dependency map" that shows which packages depend on which shared libraries. This map is invaluable when working with AI because it tells you exactly which package interfaces you need to provide as context. Some teams even automate this, generating a dependency graph that can be included in AI prompts. Tools like pipdeptree or custom scripts that parse pyproject.toml files can generate these maps automatically.


13.9 AI-Assisted Code Navigation

One of the most powerful applications of AI in multi-file projects is not code generation but code navigation. AI excels at helping you understand an unfamiliar codebase, find relevant code, and trace data flows across files.

Understanding Unfamiliar Codebases

When you encounter a new codebase, AI can help you build a mental model quickly:

Prompt example:

"I just joined a project and need to understand the codebase. Here is the
directory tree:

[paste tree]

And here are the first 30 lines of each top-level module:

[paste file headers]

Please give me:
1. A high-level architecture overview
2. The main data flow through the system
3. The key abstractions and design patterns used
4. Potential areas of complexity or technical debt"

The AI can synthesize this information into a coherent overview much faster than you could by reading through files individually.

Tracing Data Flows

When you need to understand how data moves through a system, AI can help trace the path:

Prompt example:

"In this application, when a user submits an order through the web
interface, what is the complete data flow? Please trace it from the
HTTP request to the database write, identifying every function and
file the data passes through.

Here are the relevant files:
[paste api/routes.py, services/order_service.py, models/order.py,
utils/database.py]"

Finding Where to Make Changes

When you need to add a feature or fix a bug, the first question is often "where in the codebase should I make this change?" AI can help answer this:

Prompt example:

"I need to add rate limiting to our API. Based on the following
repository map and the middleware.py file, where should I implement
this? Should it be a new middleware, a decorator, or something else?

[paste repository map]
[paste middleware.py]"

Understanding Dependency Relationships

AI can analyze import statements across files to help you understand the dependency structure:

Prompt example:

"Here are the import statements from each file in our services/ directory:

auth_service.py: from models.user import User; from utils.database import ...
product_service.py: from models.product import Product; from utils.database import ...
order_service.py: from models.order import Order; from models.user import User; ...
notification_service.py: from services.order_service import OrderService; ...

Please draw me a dependency graph and identify any concerning patterns
(circular dependencies, overly coupled modules, etc.)."

Intuition

Using AI for code navigation is like having a senior developer available to pair program with you whenever you need it. You are not asking the AI to write code; you are asking it to help you understand the existing code so that you can make better decisions about what to write and where to put it. This is one of the highest-value uses of AI in professional development.

Building a Codebase Knowledge Base

Over time, you can build up a knowledge base about your codebase by asking the AI to summarize different parts and saving those summaries. This becomes a reusable context document:

# Codebase Knowledge Base

## Architecture
Three-tier web application: API layer (Flask) → Service layer → Data layer (SQLAlchemy)

## Key Design Decisions
- All services are stateless; state lives in PostgreSQL and Redis
- Authentication uses JWT tokens with 1-hour expiry
- All API responses follow the {data, error, meta} envelope pattern

## Module Summaries
### auth_service.py
Handles user registration, login, token generation/validation.
Key dependency: bcrypt for password hashing, PyJWT for tokens.
Critical invariant: tokens must be verified on every protected route.

### order_service.py
Manages order lifecycle: create → validate → charge → fulfill → complete.
Uses saga pattern for distributed transactions.
Key dependency: payment_gateway for charge operations.

This kind of document, built incrementally through AI conversations, becomes increasingly valuable as the project grows.


13.10 Scaling Vibe Coding to Enterprise Codebases

Enterprise codebases present the ultimate challenge for AI-assisted development. They are large (often millions of lines of code), have complex dependency structures, follow established (and sometimes inconsistent) conventions, and involve many developers working in parallel.

The Enterprise Context Problem

In an enterprise codebase, no single developer understands the entire system. This is also true for AI: you cannot provide the entire codebase as context. The strategies discussed in this chapter become essential, but they need to be applied at a larger scale.

Tiered Context Strategy

For enterprise codebases, use a tiered context strategy:

Tier 1: Always Include (50-100 tokens) - Project name and purpose - Language, framework, and key technology choices - Architecture style (microservices, monolith, etc.) - Link to coding standards

Tier 2: Session-Level (200-500 tokens) - Module or service being worked on - Repository map of the relevant section - Key conventions for this part of the codebase

Tier 3: Task-Level (Variable) - Specific files being modified - Interfaces of direct dependencies - Relevant test files

Tier 4: On-Demand (Variable) - Additional files requested by the AI or revealed by errors - Historical context (why a decision was made) - Related documentation

Working with Legacy Code

Enterprise codebases often contain legacy code with different styles, outdated patterns, and insufficient documentation. AI can help here in several ways:

  1. Code Archaeology: Ask the AI to analyze old code and explain what it does, why it might have been written that way, and what the modern equivalent would be.
  2. Incremental Modernization: Use the AI to rewrite individual modules to modern standards while maintaining backward compatibility.
  3. Documentation Generation: Ask the AI to generate documentation for undocumented code.
Prompt example:

"This module was written in 2015 and uses an older style. Please:
1. Explain what it does in plain language
2. Identify any deprecated patterns or potential issues
3. Suggest a modernization plan that maintains the same public API

[paste legacy module]"

Team Conventions and AI

In a team setting, consistency becomes even more important because multiple developers are all using AI to generate code. Consider:

  • Shared prompt libraries: Create a repository of prompt templates that encode team conventions.
  • AI-generated code review checklists: Ensure reviewers check AI-generated code for consistency with team standards.
  • Automated formatting and linting: Run black, isort, ruff, and other tools automatically to enforce at least superficial consistency.
  • Convention documentation: Maintain a living document of coding conventions that can be included in AI prompts.

Best Practice

Establish a team agreement on how AI tools are used: which tools, what conventions to follow in prompts, how AI-generated code is reviewed, and how to handle situations where the AI generates inconsistent or incorrect code. This agreement should be documented and reviewed regularly.

Performance Considerations

When working with very large codebases, context window usage directly impacts performance and cost:

  • Token counting: Be aware of how many tokens your context consumes. A 100-line Python file typically uses 300-600 tokens.
  • Context compression: Use summaries and interfaces rather than full files whenever possible.
  • Conversation management: Start new conversations rather than letting a single conversation grow unboundedly.
  • Caching context: Many AI tools cache repeated context. Structure your prompts to take advantage of this by putting stable context (style guides, architecture docs) before variable context (specific files, current task).

The Future of Multi-File AI Coding

The tooling for multi-file AI coding is evolving rapidly. Several trends are making this easier:

  1. Agentic coding tools (covered in detail in Chapter 36) can autonomously navigate filesystems, read files, run tests, and make changes across multiple files without requiring you to manually provide context.
  2. Codebase indexing allows AI tools to build a searchable index of your entire codebase, retrieving relevant context automatically when needed.
  3. Larger context windows continue to expand, allowing more code to be included in a single prompt.
  4. Multi-model architectures use smaller, faster models for routine tasks (like looking up an import path) while reserving larger models for complex reasoning.

Even as these tools improve, the fundamental principles of this chapter remain relevant. Understanding how to select, organize, and provide context is a skill that will serve you well regardless of which specific tools you use.

Advanced

Some cutting-edge workflows use AI to generate the context for other AI prompts. For example, you might ask one AI session to analyze the codebase and produce a context document, then feed that document into a second AI session that performs the actual code generation. This "meta-prompting" approach can be very effective for large-scale changes, as the first session specializes in understanding the codebase while the second specializes in generating code.


Chapter Summary

Working with multiple files and large codebases is where vibe coding transitions from a novelty to a professional skill. The core challenge is providing AI assistants with enough context to generate correct, consistent, well-integrated code despite their inability to see the entire codebase at once.

The key strategies are:

  1. Repository maps that give the AI a structural overview of the project
  2. Interface-first context that provides the minimum information needed for correct integration
  3. Consistency references that show the AI the patterns and conventions to follow
  4. Phased approaches that break large tasks into manageable chunks
  5. The hybrid generation approach that uses holistic generation for scaffolding and tightly coupled components, and file-by-file generation for complex individual modules
  6. Import maps and dependency rules that prevent incorrect import paths and circular dependencies
  7. Scoped sessions for monorepos that focus on one package or service at a time
  8. AI-assisted navigation that helps you understand unfamiliar code before modifying it
  9. Tiered context strategies that scale these techniques to enterprise codebases

These techniques build directly on the context management foundations from Chapter 9. Where Chapter 9 focused on managing the conversation between you and the AI, this chapter focused on managing the project-level context that the AI needs to do excellent work.

In the next chapter, we will tackle what happens when the AI gets things wrong despite your best context-providing efforts. Chapter 14 covers debugging AI-generated code, recognizing common failure patterns, and developing the skills to quickly identify and fix problems in AI output.


Key Terms

  • Repository map: A structured summary of a project's files, classes, functions, and dependencies, used to give AI assistants a high-level understanding of the codebase.
  • Cross-file context: Information about files other than the one currently being generated, provided to ensure correct integration.
  • Interface-first context: A strategy of providing only the public interfaces (signatures, types, docstrings) of dependency files rather than their full implementation.
  • Holistic generation: Generating multiple related files in a single AI prompt to ensure consistency.
  • File-by-file generation: Generating each file in a separate prompt, with explicit context about other files.
  • Convention drift: The gradual divergence from established coding conventions that occurs over long AI-assisted coding sessions.
  • Import map: A summary of what each module exports and the correct import paths to access those exports.
  • Consistency reference: An existing file provided to the AI as a concrete example of the project's patterns and conventions.
  • Monorepo: A single version control repository containing multiple distinct projects or packages.
  • Tiered context strategy: A layered approach to providing context, with stable high-level information always included and specific details loaded on demand.
  • Sliding window approach: A context management technique where you maintain a fixed amount of context as you work through many files sequentially.
  • Context document: A maintained summary of a project's architecture, conventions, and key decisions, used as a reusable preamble for AI prompts.