How much of your codebase does Claude actually need?
Part of the Claude Code guide. Start with Claude Code Doesn't Understand Your Codebase? How to Fix It
In one of my cal.diy experiments, Claude planned a rename in 16.01 seconds using its native tools. With Pharaoh (ours) available, it averaged 16.95 seconds. Both configurations named all fifteen expected files.
On a different question about the same repository, tracing the impact of a permission-check change took 32.03 seconds natively and 16.93 seconds with Pharaoh.
I build Pharaoh, so I would be happy with a result that said a graph made every task faster. These runs gave me a more useful rule: decide what information the task needs before deciding how to provide it.
A function body, a bundle of source files and a call graph each leave something for the agent to work out. The useful comparison is how much of that work remains for the question you are asking.
When I start with native search
If I have a symbol name and want to inspect its implementation, I usually start with search. For example, this locates the permission check and places that mention it in a cal.diy checkout:
rg -n '\bcheckAdminOrOwner\b' apps packages
The results point into source files I can open immediately. An IDE's references and rename tools can add language-aware information, and plain text search remains useful for mocks, configuration and names stored as strings.
That last category matters during a rename. A static call graph is concerned with detected relationships between functions; the string in a test mock can need changing even though no production call edge points to it.
Native search also sees the checkout the agent is working in. When I have uncommitted changes, local search and file reads describe those changes. Pharaoh's hosted interactive map describes its last completed default-branch map, so I keep the two revisions explicit when moving between them.
Search takes more assembly work when the question crosses several layers. Finding a direct call to a helper is a start; finding which controller calls that helper, which shared package resolves the import, and which entry exposes the controller requires following the relationships.
That is part of why Claude Code misses codebase structure.
When a repository pack is convenient
Sometimes the consumer is a chat interface or another tool that works well with an attached document. I want a reviewable snapshot of a subsystem, including implementation details, without giving the consumer filesystem access.
Repomix supports selecting files and directories for that purpose. For example, from a cal.diy checkout:
npx repomix \
--include 'packages/features/auth/signup/utils/**' \
--output /tmp/cal-signup-context.xml
That selection contains the signup utilities. It is not evidence that callers outside the directory have been included. If the question concerns those callers, discovery and scope selection are part of the work to measure.
Repomix also offers structural compression and an MCP server with partial reading and search. A fair evaluation should include those capabilities rather than assume the only option is pasting an entire raw repository into every prompt.
Compression can remove implementation details, so choose the representation according to the task. Keep full source available for questions about behavior. Record the selected files and revision when you want someone else to reproduce a result. See Claude Code context limits when deciding how much of the pack to include.
Gitingest provides another way to turn a repository into a text digest. Packaging tools make source portable; the agent still has to infer the relationships needed for its answer.
There is a data-handling choice here too. A pack contains the selected source and goes wherever you send that document. Pharaoh processes repository source to build its hosted structural map and does not persist source code in that map. Check the processing and storage requirements of the workflow you are evaluating, including the AI service that ultimately receives the context.
When I ask for structural context
For a relationship question, I want a starting point that already names the detected callers or entry points. I ask the connected agent to use get_blast_radius, then open the relevant implementation to check the behavior.
For the permission check, one inspected path runs from an API controller through a conferencing service and shared app helper to checkAdminOrOwner. The returned callers, grouped by module with their files and hop counts, get me to the next file I need to inspect without requiring the agent to find every caller from scratch.
That is a useful division of work. The code knowledge graph supplies detected structure, the source supplies implementation, and the agent reasons about the proposed change using both. Runtime loading can leave gaps, so I still need tests and source inspection at the boundaries where the change matters.
What I have actually measured
These figures come from one saved October 16, 2026 dataset on cal.diy at 54343aa6, with three sessions per task per condition. The recorded model ID was claude-opus-5-5.
| Task | Native tools: mean seconds | With Pharaoh: mean seconds |
|---|---|---|
| Signup-utilities module overview | 15.12 | 22.91 |
| Permission-check change impact | 32.03 | 16.93 |
| Entry points reaching password hashing | 21.85 | 17.16 |
| Rename plan, including tests | 16.01 | 16.95 |
| Find an existing recurring-event helper | 8.99 | 12.05 |
The module overview is a useful warning against treating every architectural question as an automatic graph win. The helper lookup also separates latency from cost: the Pharaoh condition took longer, while its recorded API cost was lower.
These measurements compare two recorded workflows. They do not include a Repomix or Gitingest condition, and the snapshot does not preserve every historical plugin flag or cache condition. I am not publishing a three-way performance ranking.
I also found a bug in the original name-based grader: it sometimes rejected a method name when the answer qualified it with a class. The numbers above are timings, not accuracy scores. Checking that an answer mentions expected items is only one part of evaluating whether its explanation is right.
A comparison worth running on your own repository
Pick a fixed revision and three questions you would actually ask while working. For each one, make an answer key from source before looking at the candidate answers.
An architecture question could ask which production entry points reach a helper within three hops. An implementation question could ask why that helper accepts a missing role and how its type predicate is used. A rename question could ask which source files, tests and string references need to change.
The implementation question is a suggested extension to this experiment, not a task in the timing table above.
Run fresh sessions with the same model and prompt. Give native search a normal development setup, let the packing workflow select useful context, and let the graph workflow use source reads when it needs them. Keep any pure-document-only experiment separate because that changes what the agent is allowed to inspect.
Record preparation time as well as answering time. Save the files selected for each pack, record the map revision, and check how caching affects billed cost. If one condition receives a preselected set of exactly the right files, count the effort used to discover them.
Then inspect the answers. Did a path have the right direction? Did the agent distinguish a source reference from a production caller? Did it invent an endpoint? A concise answer with the expected names can still make the change harder to review.
I use these approaches together. A map helps me find a path worth inspecting; source search checks that path in the code I am editing, and a selected pack lets me carry the implementation into another review tool. The codebase context case study covers these workflows and their scope. If relationship discovery is the part slowing you down, try Pharaoh on one repository and compare it on a task you already understand.