What you will be able to do
- Choose Grep for searching file contents and Glob for matching file paths, and explain why each one fits its job
- Use Edit for targeted changes, and fall back to Read followed by Write when Edit cannot find unique anchor text
- Explore an unfamiliar codebase step by step: search for entry points first, then read only the files the trail leads to
- Find every use of a function that wrapper modules re-export by first listing all its exported names, then searching for each one
Key concept
Match the tool to the question — Each built-in tool answers one kind of question. Glob answers "which files have this name or path?", Grep answers "which files contain this text?", Read loads a file, and Edit or Write change it. Choosing well means asking the narrowest question first and paying for full file reads only once a search has told you where to look.
1.Grep searches contents, Glob matches paths
Claude Code has two search tools, and they answer different questions. Grep looks inside files. Use it to find a function name, an error message string, an import statement, or every place a function is called. Glob looks only at file paths. Use it to find files by name or extension, such as every test file matching **/*.test.tsx or every config file under a directory. A useful check: if the answer depends on what is written inside a file, you need Grep. If it depends only on what the file is called or where it lives, you need Glob.
| Question you are asking | Tool | Key input |
|---|---|---|
| Which files contain this function name, error message or import? | Grep | pattern, optionally narrowed with glob or type; output_mode files_with_matches lists files only |
| Which files have a given name or extension? | Glob | pattern, optionally a path to search under |
| What does this whole file say? | Read | file_path, with offset and limit for large files |
| Change one specific passage in an existing file | Edit | old_string, new_string, replace_all |
| Create a file, or replace its entire contents | Write | file_path, content |
type GrepInput = {
pattern: string;
path?: string;
glob?: string;
type?: string;
output_mode?: "content" | "files_with_matches" | "count";
"-i"?: boolean;
"-o"?: boolean; // print only the matched parts of each line; requires output_mode: "content"
"-n"?: boolean;
"-B"?: number;
"-A"?: number;
"-C"?: number;
context?: number;
head_limit?: number;
offset?: number;
multiline?: boolean;
};Bash can also search, using shell commands like find or grep. For the exam, though, the preferred answer is the dedicated tool. Grep and Glob are read-only and are built for exactly these jobs. Bash runs arbitrary commands and normally needs permission. When a question can be answered from file names alone, don't read any files: running two Glob patterns and comparing the two lists of results is often all the task needs.
An architect asks Claude Code to find every place in a large monorepo that calls a function named `parseInvoice`, including calls inside a minified bundle that is gitignored but still needs to be checked. Which approach correctly locates all call sites?
Correct answer: D — Run Grep across the repo for parseInvoice, then Grep the gitignored bundle's path directly, since a direct path is still searched
- A. Incorrect. The multiline flag changes whether a pattern can match across line boundaries; it has no effect on whether gitignored files are included in the search.
- B. Incorrect. Globbing for files named after the function only finds a file that happens to share the name, not the files where the function is called, so real call sites in other files are missed.
- C. Incorrect. Glob only matches file paths by name pattern; it cannot see whether a file's contents reference parseInvoice, so this cannot identify call sites.
- D. Correct. Grep respects .gitignore and skips gitignored files by default, but passing a gitignored file's path directly still searches it, so a normal repo-wide Grep plus a targeted Grep on the bundle path covers both tracked and gitignored call sites.
2.Read, Write and Edit, and the Read + Write fallback
The three file tools split the work cleanly. Read loads a file's contents. Write creates a file or replaces all of its contents. Edit makes a targeted change: you give it an old_string to find and a new_string to put in its place. That makes Edit the default for small changes to existing files. You don't resend the whole file, and the rest of the file can't be accidentally rewritten.
type FileEditInput = {
file_path: string;
old_string: string;
new_string: string;
replace_all?: boolean;
};
type FileReadInput = {
file_path: string;
offset?: number;
limit?: number;
pages?: string;
};
type FileWriteInput = {
file_path: string;
content: string;
};Edit has one weak point: its anchor has to identify exactly one place in the file. If old_string appears more than once, Edit can't tell which occurrence you mean, and the call fails. replace_all only helps if you really want every occurrence changed. Edit also struggles when a change touches many scattered places, such as pulling several separate blocks together into one. In both cases, the reliable fallback is to Read the whole file, build the corrected version, and Write it back in one go. You then control the entire result instead of hoping each anchor matches.
Option one: make old_string unique by including surrounding lines, such as the function signature above the return. Option two: Read the full file and Write the corrected version. Widening the anchor works when there is enough nearby text that appears nowhere else. When no such text exists, or the change is spread across the file, Read followed by Write is the reliable choice. Using replace_all would be wrong, because it would change all six occurrences.
A developer wants Claude Code to update a deprecated log statement `logger.warn("legacy-path")` that appears twice in the same file, in two different functions, where only one of the two occurrences should change. Claude issues an Edit call with old_string set to exactly that log statement and the call fails. What is the correct next step?
Correct answer: B — Widen old_string to include enough surrounding context to uniquely identify the intended occurrence, then retry Edit with that string
- A. Incorrect. Write overwrites the entire file with exactly the content provided; passing only a single line would delete the rest of the file's contents rather than merging into it.
- B. Correct. Edit requires old_string to match exactly once; when a string occurs more than once, supplying additional surrounding context that appears only around the target occurrence restores uniqueness and lets Edit apply cleanly.
- C. Incorrect. replace_all rewrites every occurrence, which would also change the log statement the developer wants to keep, and reverting one afterward risks losing track of which change was intentional.
- D. Incorrect. Grep is read-only and searches file contents; it has no capability to write or modify files, so it cannot perform the edit.
Sources1
3.Build understanding incrementally and trace through wrappers
Reading every file in a codebase up front fills the context window with code that has nothing to do with the task. The better pattern is to narrow down step by step. Start with Grep to find entry points, such as the route handler, the error message the user reported, or the function named in the bug. Then Read those files and follow their imports one step at a time. Each Read should be justified by something a search or a previous Read showed you. Claude Code's own debugging loop follows this pattern: search for the relevant source files, then read them to understand the code.
Wrapper modules complicate this. A utility module might re-export a function under one or more different names, so a Grep for the original name finds only some of the real callers. The fix has two steps. First, Read the wrapper module and list every name it exports for the function, including the original and each alias. Then Grep the codebase for each of those names in turn. Only the combined results give you the full set of callers, which matters most when you're about to change the function's signature.
An architect asks Claude Code to identify every React test file in a codebase where naming mixes `.test.tsx`, `.spec.tsx`, and older files simply ending in `Test.tsx`, spread across many nested feature directories. Only a list of matching file paths is needed, with no content inspection. Which tool is the most direct fit?
Correct answer: C — Glob, using patterns such as **/*.test.tsx, **/*.spec.tsx, and **/*Test.tsx to match the naming conventions directly
- A. Incorrect. A recursive Bash listing followed by manually reading every file to check its name is far less direct than a purpose-built path-pattern match, and unnecessarily consumes context reading files that were never needed.
- B. Incorrect. Grep searches file contents, not file names; searching for the word test inside file bodies would return unrelated files that mention testing and could miss test files that never use that literal word.
- C. Correct. Glob matches file paths by name pattern, including recursive ** matching, so running it with the three naming conventions directly returns exactly the matching file paths without needing to inspect any file contents.
- D. Incorrect. Read loads the contents of a single file at a given path and does not list directories at all, so pointing it at the project root would not produce a recursive file listing.
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.Glob can find files that mention a function name, because it matches file patterns.Why is that wrong?
Glob matches file paths and names only. It never looks inside a file. Finding text such as a function name, error message or import requires Grep, which searches file contents.
Covered in Grep searches contents, Glob matches paths
2.When Edit fails because the anchor text is not unique, set replace_all or retry the same Edit until it works.Why is that wrong?
replace_all changes every occurrence, not just the one you meant, and retrying an ambiguous anchor fails the same way each time. The reliable fallback is to Read the full file and then Write the corrected contents, because Write replaces the whole file.
Covered in Read, Write and Edit, and the Read + Write fallback
3.To understand a codebase properly, Claude should Read all the relevant-looking files before doing anything else.Why is that wrong?
Reading everything up front wastes context. Search first to find entry points, then read only the files the search points to, following imports from there.
Covered in Build understanding incrementally and trace through wrappers
4.A single Grep for a function's original name finds all of its callers.Why is that wrong?
Callers that import the function through a re-exported alias never use the original name. List every exported name from the wrapper module first, then run a regex search for each one.
Covered in Build understanding incrementally and trace through wrappers
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.https://code.claude.com/docs/en/tools-referenceOfficial docs
“Finds files based on pattern matching”
↩︎ Grep searches contents, Glob matches paths“Absent by default on macOS, Linux, and WSL”
↩︎ Grep searches contents, Glob matches paths“Executes shell commands in your environment”
↩︎ Grep searches contents, Glob matches paths“Makes targeted edits to specific files”
↩︎ Read, Write and Edit, and the Read + Write fallback“Reads the contents of files”
↩︎ Read, Write and Edit, and the Read + Write fallback“Creates or overwrites files”
↩︎ Read, Write and Edit, and the Read + Write fallback“Searches for patterns in file contents”
↩︎ Exam trap 1“Creates or overwrites files”
↩︎ Exam trap 2 - 2.https://code.claude.com/docs/en/agent-sdk/subagentsOfficial docs
“Can examine code but not modify or execute”
↩︎ Grep searches contents, Glob matches paths - 3.
“Read those files to understand the code”
↩︎ Build understanding incrementally and trace through wrappers“Search for the relevant source files”
↩︎ Exam trap 3 - 4.https://code.claude.com/docs/en/common-workflowsOfficial docs
“Start with broad questions, then narrow down to specific areas”
↩︎ Build understanding incrementally and trace through wrappers“trace the login process from front-end to database”
↩︎ Build understanding incrementally and trace through wrappers
Also cited
- https://code.claude.com/docs/en/agent-sdk/agent-loopOfficial docs
“Find files by pattern, search content with regex”
↩︎ Key concept“Find files by pattern, search content with regex”
↩︎ Exam trap 4