Searching a Codebase with Claude Code's Built-In Grep Tool
Claude Code ships with built-in search tools that scan a repository for patterns, function names, config keys, or literal strings and return where each match lives.
Before Claude Code reads a file or proposes an edit, it usually searches first, so it is acting on the actual set of relevant files instead of guessing which ones matter.
Summary
Claude Code's search tools work like a fast, repo-wide pattern search: give them a term or pattern and they return every matching file and line, across as many files as the repo contains.
This is distinct from opening a single file - search operates across the whole tree (or a scoped subset of it) in one pass.
Search is typically the step that runs before the read-edit-run-test loop starts, because Claude Code needs to know which files are actually relevant before it reads them in full.
It is especially valuable for tasks that span multiple files: renaming a function safely requires knowing every call site first, and finding every usage of a config key or error string requires the same repo-wide view.
Because search returns matches, not entire files, it is a scouting step - Claude Code still reads the surrounding context of a match before it edits that file.
Recipe
Quick reference: these are natural-language prompts you type at the Claude Code prompt. Claude Code decides when to invoke its search tools based on what you ask for.
# Find where something is defined
> Where is the function calculateTotal defined in this repo?
# Find every call site before a rename
> Find every place that calls calculateTotal so I can rename it safely.
# Find every usage of a config key, constant, or literal string
> Search the codebase for every reference to the config key MAX_RETRIES.
# Scope a search to a subdirectory or file type
> Search only the api/ directory for calls to the old auth middleware.When to reach for this:
- Before renaming a function, class, or variable, so you know every call site up front.
- Before deleting or deprecating a config key, flag, or exported symbol.
- When tracking down where an error message or log string actually gets printed from.
- When you're new to a repo and need to find the file that owns a given piece of behavior.
- Before a multi-file edit, so Claude Code has the full list of files to touch instead of editing one and missing the rest.
Working Example
A common scenario: you want to rename a function and need to be sure you catch every call site first.
# 1. Start Claude Code in the project root
cd my-project
claude
# 2. Ask it to search before touching anything
> Find every call site of calculateTotal, then rename it to computeOrderTotal
> everywhere, including its definition and any imports.Claude Code's search step returns something conceptually like this (file paths and line numbers, one per match):
src/orders/pricing.ts:42: export function calculateTotal(items, discounts) {
src/orders/checkout.ts:18: import { calculateTotal } from "./pricing";
src/orders/checkout.ts:57: const total = calculateTotal(cartItems, activeDiscounts);
src/reports/summary.ts:9: import { calculateTotal } from "../orders/pricing";
src/reports/summary.ts:31: const lineTotal = calculateTotal(row.items, []);
tests/pricing.test.ts:12: expect(calculateTotal([], [])).toBe(0);
From that match list, Claude Code reads each file around the matched line, then proposes coordinated edits across all of them:
- export function calculateTotal(items, discounts) {
+ export function computeOrderTotal(items, discounts) {
- import { calculateTotal } from "./pricing";
+ import { computeOrderTotal } from "./pricing";
- const total = calculateTotal(cartItems, activeDiscounts);
+ const total = computeOrderTotal(cartItems, activeDiscounts);What this demonstrates:
- Search runs first and returns a match list across every affected file, not just the one you started in.
- The match list, not a guess, is what tells Claude Code which files need edits.
- A rename only counts as safe once the definition, every import, and every call site are updated together.
- Tests that reference the old name show up in the same search, so they don't get left broken.
- The search step and the edit step are separate - matches are text locations, edits still require reading and understanding the surrounding code.
Deep Dive
How It Works
- Claude Code's built-in search tools scan file contents across the repository (or a scoped path you give it) for a pattern, name, or literal string.
- Each match is returned as a file path and line, along with a bit of surrounding context, not the entire file.
- Search typically respects a repo's ignore rules, so build output, dependency folders, and other generated files usually aren't returned as matches.
- Results feed directly into the next step of the read-edit-run-test loop: Claude Code reads the specific files that matched, rather than reading the whole repo.
- Because search is a read-only operation, it's generally lower-friction than a step that runs a command or writes a file, so it can run early and often without interrupting your workflow.
- The same search machinery underlies several everyday requests: "find the function," "find the callers," "find every usage," and "find the file that owns this string" are all variations of the same search-then-read pattern.
Search vs. Read vs. Edit
| Step | What it returns | When Claude Code uses it |
|---|---|---|
| Search | File + line matches for a pattern across many files | First, to discover which files are relevant |
| Read | The full, current contents of one specific file | After search narrows the candidates, or when a file is already known |
| Edit | A proposed diff against a file it just read | Last, once the right file and the right context are both in hand |
Getting Useful Results
# Vague - likely to return noise or miss intent
> Find "total" in the code.
# Specific - names the exact symbol and what you want done with the results
> Find every call site of calculateTotal (the function in src/orders/pricing.ts),
> not just the string "total".
# Scoped - narrows the search when the repo is large or the term is common
> Search only src/api/ for references to the old webhook URL.Being specific about the exact name, and scoping to a directory or file type when the term is common, gets a cleaner match list and a better follow-up edit.
Gotchas
- Assuming search covers everything in the working tree. Generated files, build output, and anything in the repo's ignore rules typically won't show up as matches. Fix: if you genuinely need to search generated output, say so explicitly so Claude Code knows to include it.
- Using a term that's too common. Searching for a short, generic word like "total" or "config" in a large repo can return an overwhelming, mostly irrelevant match list. Fix: search for the exact function, class, or key name, and scope to a directory when possible.
- Treating a search match as proof of a live call site. A pattern match can land inside a comment, a string, or dead code that never actually executes. Fix: ask Claude Code to confirm each match is a real usage before it edits, especially for a rename that touches many files.
- Expecting search to understand dynamic or reflective usage. A pattern match only finds text; it won't catch a function invoked by a dynamically built string or through metaprogramming. Fix: call this out explicitly and ask for extra scrutiny, then verify with a test run after the edit.
- Relying on a search result from earlier in a long session. If files changed since that search ran, the match list may be stale by the time a big multi-file edit happens. Fix: ask Claude Code to re-run the search immediately before a large coordinated edit rather than reusing old results.
- Confusing "found it" with "read it." A match only returns a line and a bit of context, not the whole file. Fix: expect Claude Code to read the full file before it proposes an edit, and don't assume a match alone is enough basis for a change.
- Searching case-sensitively (or not) without meaning to. A rename or config-key search can silently miss variants that differ only in case. Fix: state explicitly whether you want an exact match or a case-insensitive one.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Running a search command yourself in the terminal | You just want to eyeball raw matches without Claude Code acting on them | You want Claude Code to reason about the matches and edit multiple files in the same turn |
| Your editor's "Find in Files" / global search | Quick manual browsing while you're already in the IDE | You need the results to drive an automated, coordinated multi-file edit |
git log -S<term> or git blame | You need to know when a string or symbol was introduced historically | You need every current usage, not history |
| Language server "find references" | You need scope-aware, symbol-level reference resolution in a language with a configured LSP | No LSP is set up, or you're searching for config keys, strings, or patterns rather than a resolvable symbol |
FAQs
What does Claude Code's built-in search actually return?
- A list of matches, each with a file path and line number
- A small amount of surrounding context per match
- Not the full contents of each file - that comes from a separate read step
When does Claude Code decide to search versus just reading a file it already knows about?
If it already knows exactly which file and location are relevant, it can read directly.
Search comes in when the relevant files aren't known yet, which is the common case for renames, usage audits, and "where is this defined" questions.
Does search look at the entire repository, including dependencies and build output?
Generally no. Search typically respects a repo's ignore rules, so folders like build output or installed dependencies are excluded from matches by default.
You can ask Claude Code to include a specific path explicitly if you need it searched anyway.
Can I scope a search to a specific directory or file type?
Yes. Naming a directory ("search only in src/api/") or a file type in your request narrows the search, which is useful in a large repo or when the term you're searching for is common.
Is this the same as running a manual search command myself in the terminal?
Conceptually similar - both look for a pattern across files - but Claude Code's version is built into its workflow, so the matches feed directly into the next read and edit steps in the same turn, without you copying results back in.
Will search find every call site before a rename?
It finds every text match for the pattern you give it, which covers the large majority of real call sites.
It won't catch a call built dynamically at runtime (for example, through a string-constructed function name), so it's worth double-checking anything unusual after a rename.
Does a search match guarantee the code there actually runs?
No. A match can land in a comment, a string literal, or dead code.
Claude Code generally reads the surrounding context before editing, which is exactly the safeguard for filtering out matches that aren't real usages.
How does search fit into Claude Code's read-edit-run-test loop?
Search usually happens before the loop's first read: it identifies which files are relevant, then Claude Code reads those specific files, proposes edits, runs a command, and verifies the result.
Is search case-sensitive?
Behavior can vary by what you ask for, so it's worth being explicit: say whether you want an exact-case match or want variants that differ only in case included.
What if a search on a very large repo returns too many results?
Narrow the request: name the exact symbol instead of a generic word, or scope the search to a specific directory or file type. A more specific request usually produces a cleaner, shorter match list.
Can search find a string literal, like an error message, not just a function name?
Yes. Searching isn't limited to function or symbol names - it works for config keys, error messages, log strings, and other literal text just as well.
Does search need my permission to run, the way a shell command might?
Search is a read-only operation, so in most permission setups it runs without the kind of approval prompt that a command with side effects (like a file write or an install) might trigger.
If I already know the exact file and line, should I still ask Claude Code to search?
Not necessarily - if you already know exactly where something lives, you can point Claude Code straight at it.
Search earns its keep when you don't know all the affected locations yet, which is most of the time for anything spanning more than one file.
Related
- How Claude Code Executes the Read-Edit-Run-Test Loop - where search fits before the read step
- Making Multi-File Edits in a Single Claude Code Turn - what happens after search finds every affected file
- Why Claude Code Re-Reads Files Before Editing Them - why a search match isn't treated as a final answer
- Navigating the Claude Code REPL: Prompts and Slash Commands - the basics of typing requests like these at the prompt
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5. Model names, pricing, and product features move quickly - verify current specifics at platform.claude.com/docs before relying on them.