You're paying Claude 3x too much to understand your code

Dan Greer · · 5 min read

Part of the Claude Code guide. Start with Claude Code Doesn't Understand Your Codebase? How to Fix It

The word Claude in raised cream letters on a gold background, crossed by translucent purple and blue shapes

I ask Claude what breaks if I change a function, and I watch what it does. It greps, opens a file, greps again, opens three more, and keeps going until it's confident or out of patience. On my own codebase that one question takes 15 tool calls and 52.9 seconds.

With Pharaoh connected (I build Pharaoh), it takes one call and 10.2 seconds.

That's the whole idea. Pharaoh maps your repo into a graph of functions, callers, modules, endpoints and cron jobs, and hands it to your AI agent over MCP. Your agent stops reading the codebase one street sign at a time and starts reading the map.

The numbers

I asked Claude Code (Opus 5.5) the questions developers ask before they change code, with Pharaoh and without, three times each, and graded every answer against an expected-item key derived from the TypeScript compiler. On questions about how code connects in my own repository:

33.4 s → 13.6 s per question.

17.0¢ → 5.9¢ per question.

100% → 100% of the expected items mentioned.

Less than half the time, about a third of the cost, the same expected items named. Across two graded runs, Claude with Pharaoh was 2.5 to 2.8 times faster and 2.9 to 3.2 times cheaper.

On a big codebase, the accuracy claim changes

Speed is the headline on a tidy codebase. On a big one the savings are smaller.

The cal.diy answer to “what breaks if checkAdminOrOwner changes?” is 9 functions and 2 HTTP endpoints, spread across packages.

After correcting for qualified names, all six native answers from the two final October 16 cal.diy runs contained the eleven items expected for the permission-check question, including the four the original grader had marked incomplete. In the same two runs, every answer with Pharaoh named all eleven, in about half the time. In an earlier run that day, one of the three Pharaoh answers missed the event-types service method and its endpoint.

The original matcher rejected a method name when a dot came before it, so ConferencingAtomsService.getUserConferencingApps could be marked as missing getUserConferencingApps. The change-impact investigation explains the grading error. The evidence supports a difference in time and recorded API spend, not the original claim that native Claude missed those callers. Mentioning the expected items also doesn't prove that every relationship or explanation is correct.

What it's worth

In these runs, one question about how code connects on my own codebase saved about 20 seconds of waiting and 11 cents of API spend.

Those session figures exclude Pharaoh subscription charges and map construction. On a flat Claude subscription, lower recorded API cost doesn't mean the bill falls by the same percentage; Anthropic's cost documentation explains the billing distinction.

Why you can trust these numbers

I didn't grade Pharaoh against Pharaoh.

The 17.0¢ and 5.9¢ figures come from a saved October 16, 2026 run on my own repository: three relationship questions (a module overview, a change impact and the entry points reaching a function), three runs per condition, 18 sessions. The 33.4 and 13.6 seconds are the means for the same subset. The opening 15-call/52.9-second and one-call/10.2-second comparison is one saved pair from an earlier run, not a mean.

Across the wider series there were 182 headless sessions, with $17.35 of API spend. The five result files saved on October 16 hold 150 of those sessions and $14.19 of API spend; the rest aren't in the saved results, so those two totals can't be re-derived. These historical measurements describe that workload, rather than a forecast for your repository.

The task breakdown below comes from a separate saved run the same day on cal.diy revision 54343aa6, with five question types, three repetitions per condition, and 30 sessions altogether. The saved model identifier is claude-opus-5-5, Anthropic's Claude Opus 5.5.

Both conditions ran headless Claude Code with the same questions and model, in a clean copy of the repository without the answer key or agent instruction files, and with Bash, edits, web tools and subagents turned off. The baseline had Read, Grep and Glob; the other condition added Pharaoh. The keys came from the TypeScript 5.9.3 language service, read by hand: its call hierarchy for callers, find-references for the rename and module exports for the overview. On cal.diy I completed them by hand where tRPC routers load handlers through dynamic imports and NestJS controllers reach services through injected fields. Each answer was graded on which expected items it named.

cal.diy itself is open source, and my fork, Pharaoh-so/cal.diy, is public, with the questions and what counts as a complete answer at the end of this post. Run them yourself.

Where the graph lost

The repository context comparison reports these mean timings from the 30-session cal.diy run:

Question type Native tools, mean seconds With Pharaoh, mean seconds
Overview of signup utilities 15.12 22.91
Impact of changing the permission check 32.03 16.93
Entry points reaching password hashing 21.85 17.16
Rename plan, including tests 16.01 16.95
Locate a recurring-event helper 8.99 12.05

The graph condition was slower on the module overview, rename plan and helper lookup. The helper lookup cost less despite taking longer. For the permission-check prompt, recorded spend averaged 17.395¢ without Pharaoh and 7.715¢ with it; these are separate from the own-repository figures above.

The saved results don't preserve every historical plugin version or invocation flag, so they can't isolate the contributions of the graph, routing instructions and caching. These were restricted headless sessions, not stock interactive Claude Code.

For a rename or a quick text search, ordinary search still belongs in the workflow. The guide to choosing codebase context covers the tradeoffs; the guide to managing context limits covers the session side.

Try it on your repo

Install the GitHub App on the account or organization that owns your TypeScript or Python repositories. Then follow the setup steps, sign in with GitHub, and connect your client, using this command for Claude Code.

npx @pharaoh-so/mcp

The Claude Code connection guide covers the process. The free plan keeps one repository live per workspace, while Pro keeps every repository live. Each push to a live repository's default branch triggers a refresh.

The graph holds structure: names, paths, signatures and how they connect. It doesn't keep your source code.

Pharaoh's get_blast_radius excludes test-file callers. Its graph covers the default branch, not other branches or uncommitted edits, so verify the mapped revision before using it to reason about the code you're changing.

Your agent is already smart. Give it the map.


The cal.com answer key

Fork Pharaoh-so/cal.diy at commit 54343aa6, connect Pharaoh, and ask Claude these with and without it:

  1. Module overview of packages/features/auth/signup/utils: its 11 exports and the 7 files outside it that import it.
  2. What breaks if checkAdminOrOwner changes: outOfOfficeEntriesList, getConnectedApps, getEventTypesAppIntegration, integrationsHandler, getUserConferencingApps, UserListTable, DuplicateSegmentDialog, FilterSegmentSelect, SaveFilterSegmentButton, GET /v2/atoms/conferencing and GET /v2/atoms/event-types-app/:appSlug.
  3. Entry points of hashPassword: POST /api/auth/reset-password, POST /api/auth/setup, POST /api/auth/signup, the changePassword tRPC procedure, and three seed scripts.
  4. Renaming getAppFromSlug: 15 files, including a test that mocks it.
  5. Is there a helper for recurring-event config? Yes: isRecurringEvent in packages/lib/isRecurringEvent.ts.

More in Claude Code

← Back to blog