Stop Letting Claude Search Your Codebase cover image

Stop Letting Claude Search Your Codebase

Scott Keck-Warren • May 29, 2026

I kept burning tokens watching Claude search through my repository for information it already could have had.

The pattern was always the same. I'd ask Claude to add a feature, and before it wrote a single line of code, it would spend three thousand tokens just figuring out what files exist, what classes do what, and how the database is structured. Poking around in the events directory to understand what fires and when.

Every conversation did the same discovery work.

It's like hiring a contractor and making them guess where your tools are every job. Sure, they'll eventually find the hammer, but you're paying for that time, and it adds up.

The Fix Is a .ai/docs/ Directory

The solution isn't better prompts. It's pre-loading context, so Claude never has to search in the first place.

I created a .ai/docs/ directory in my project root. Inside it are markdown files written specifically for Claude to read, not for humans. Compact, structured summaries of exactly what Claude needs to do work in this codebase without playing detective first. And it's set up to maintain them automatically as it changes the codebase.

The format matters because you're not writing for readability, you're writing for signal density.

What to Document

Three categories have been worth the most to me.

Module docs

Module docs group related classes so Claude understands the landscape at a glance. Instead of reading six controller files to understand the user system, Claude reads one table:

## UserModule (app/Http/Controllers/Users/, app/Models/User.php)

| Class | Purpose |
|---|---|
| `UserController` | CRUD endpoints |
| `User` | Eloquent model, has podcasts, episodes |
| `UserRepository` | findByEmail, paginate, activeUsers |

Three rows, and Claude knows what exists, where it lives, and what it does without having to scan every file with "User" in it.

Database Tables

Database tables are another high-value target. Columns, types, key relationships, and any quirks worth knowing. If Claude is going to write a query against your guests table, it should know that last_outreach is a denormalized field kept in sync by an event listener, not discover that the hard way.

Events and Listeners

Events and listeners are where I've seen the biggest payoff. Event-driven systems are notoriously hard to navigate because the connections aren't obvious from the files alone. I used to watch Claude open file after file, trying to piece together what fired what. Now I have a doc like this:

## Events (app/Events/Outreach/)

| Event | Trigger |
|---|---|
| `OutreachCreated` | new outreach |
| `OutreachUpdated` | general field update |
| `OutreachStatusUpdated` | status change |
| `OutreachNotesUpdated` | notes change |
| `OutreachFollowUpLogged` | follow-up recorded |
| `OutreachDeleted` | soft delete |

Also fires `Person/PersonLastOutreachUpdated` to keep `people.last_outreach` current.

That last line is doing a lot of work. It's the kind of thing Claude would have to read three or four files to discover on its own, but here it's one sentence.

The @{filepath} Pattern

Once you have these docs, you can reference them directly in your prompts. When I ask Claude to work on anything outreach-related, I'll include @.ai/docs/modules/outreach.md in my message. Claude reads the 20-line table instead of scanning 50 files.

You do the work once and reuse it in every conversation.

You can also tell Claude to check the docs first. If you've got a CLAUDE.md at your project root (which you should), mention the .ai/docs/ directory and tell Claude to check it before exploring the codebase.

Why This Works Better Than You'd Expect

Claude doesn't need your code formatted for humans; it just needs signal density.

A table with 8 events takes maybe 300 tokens. Claude searching through 8 event files for the same information could easily run 2,000. You've pre-compressed it, and every conversation benefits from that work.

There's also a quality benefit. When Claude has good context upfront, it makes better decisions. It knows about the PersonLastOutreachUpdated side effect before it writes code that would break it. It knows UserRepository exists before it writes a raw query in the controller. Searching gives an incomplete context, and you can provide the complete context.

How to Get Started Without Doing All the Work Yourself

You don't have to write these docs by hand.

Ask Claude to generate them. Something like: "Look at app/Events/Outreach/ and generate a compact markdown table listing each event class and what triggers it. Save it to .ai/docs/events/outreach.md."

Claude does the exploration once, writes the compressed summary, and saves it. Every conversation after that can reference the file instead of repeating the discovery.

Then ask Claude to keep the docs updated whenever it touches those files. I've had good results adding this to my project instructions: "When you modify files in a module that has a corresponding doc in .ai/docs/, update that doc to reflect the changes." It's not a perfect system, but it's a lot better than starting from scratch every time.

You Don't Need to Document Everything

Start with the parts Claude asks about most. Every time you catch yourself watching it, poke around looking for something, that's a candidate for a new doc.

The goal isn't complete documentation. It's reducing the discovery tax on every conversation. Even three or four well-maintained docs covering your most complex modules will make a noticeable difference.

Over time, you'll build a picture of where Claude spends its search budget. Those are the spots worth documenting. The rest can stay undocumented for now.

Your tokens will thank you.