Skip to content

2.5 Built-in Tools

Claude Code provides six built-in tools for working with codebases: Read, Write, Edit, Bash, Grep, and Glob. Each has a specific purpose, and using the wrong tool for a task wastes time, context tokens, or both. The exam deliberately presents scenarios where confusing these tools leads to incorrect answers.

This is the distinction that matters most in this task statement. Get it wrong and you’ll lose marks.

Grep searches file CONTENTS for patterns. Use Grep when you need to find text inside files. Function callers. Error messages. Import statements. Variable assignments. Any time you are searching for what files contain, Grep is the tool.

// Find all files that call processLegacyOrder()
Grep: "processLegacyOrder"
// Find all error messages containing "timeout"
Grep: "timeout"
// Find all files that import a specific module
Grep: "import.*from 'utils/auth'"

Glob matches file PATHS by naming patterns. Use Glob when you need to find files by name, extension, or directory structure. Test files. Configuration files. All TypeScript files in a specific directory. Any time you are searching for files based on their path, Glob is the tool.

// Find all test files
Glob: "**/*.test.tsx"
// Find all configuration files
Glob: "**/config.*"
// Find all MDX files in the domains directory
Glob: "content/domains/**/*.mdx"

The distinction in one sentence: Grep finds what is INSIDE files. Glob finds files by their NAMES.

The exam presents scenarios where a developer uses the wrong tool. Use Glob to find function callers and it fails — Glob matches paths, not contents. Use Grep to find test files by naming pattern and it works technically (by searching for “test” in filenames via content), but it’s the wrong tool, and the exam expects you to identify the correct one.

These three tools handle file operations, each optimised for a different use case.

Edit performs targeted modifications using unique text matching. You specify the exact text to find and its replacement. It’s fast and precise because it touches only the specific text you identify.

Edit:
old_string: "function processOrder(id: string)"
new_string: "function processOrder(id: string, validate: boolean = true)"

When Edit fails: Edit requires unique text matching. If the text you specify appears in multiple places in the file, Edit can’t tell which occurrence you mean, so it fails. That’s a safety mechanism, not a bug — it stops you changing text you never meant to touch.

When Edit can’t find a unique anchor: the exam’s answer. The exam guide names one fallback, Read + Write. Read the full file, then Write the complete modified version back. It works every time, because you’re no longer asking Edit to guess. It also spends a file’s worth of tokens on what was usually a one-line change, which is why it’s the fallback and not the default.

When Edit can’t find a unique anchor: current Claude Code. The Edit tool docs now give you a cheaper move first: widen old_string with more surrounding context until it pins down one location, or set replace_all: true if you actually want every occurrence updated. Both keep you on Edit and cost almost no extra context. In real work, do that before you reach for Read + Write.

The ordering in real work:

  1. Try Edit with the shortest anchor that’s plausibly unique.
  2. On a non-unique match, widen old_string until it matches one location, or use replace_all: true if you want every occurrence changed.
  3. Fall back to Read + Write when neither of those can disambiguate the target.

The ordering on the exam has two steps: Edit first, Read + Write when Edit fails. Both orderings agree on the first step. Don’t default to Read + Write for every modification. The exam penalises that because it burns context tokens.

How you explore a codebase matters as much as which tools you use. There’s a right way and a wrong way.

Wrong: Read all files upfront. Loading every file into context before you know what you need is a context-budget killer. A 200-file codebase read in full swallows your entire context window, mostly on files that have nothing to do with your task. No other exploration mistake costs you more.

Right: Incremental discovery. Start narrow. Expand only as needed.

  1. Grep to find entry points. Search for the function name, class name, or error message that anchors your investigation. This tells you which files are relevant.

  2. Read to follow imports and trace flows. Once you know which files matter, Read them to understand the code structure. Follow import statements to discover related files.

  3. Grep again to trace usage. The files you read in step 2 may expose the function under another name: a wrapper (submitOrder() that calls processOrder() inside it) or a barrel file that re-exports it (export { processOrder as submitOrder }). Callers of the new name never mention the original, so your first Grep never saw them. Grep for each new name, across the whole codebase, to get the full list of consumers. The next section works through an example.

  4. Read only what you need. Each file you read should be justified by what you discovered in the previous step.

That’s minimal context for maximum understanding. You map the codebase progressively, spending tokens only on files that matter to the task.

Tracing Function Usage Across Wrapper Modules

Section titled “Tracing Function Usage Across Wrapper Modules”

A common codebase pattern: a function is defined in one module, re-exported through a wrapper, and consumed through the wrapper’s name. A simple Grep for the original name misses every consumer who imports through the wrapper.

The correct approach:

  1. Grep for the function definition to find where it is defined
  2. Read the defining file to identify exported names
  3. Grep for each exported name across the codebase to find all consumers
  4. If the function is re-exported through a barrel file (e.g. index.ts), Grep for the barrel file’s module name to find consumers who import from it

Concretely: processOrder is defined in orders.ts. The barrel utils/index.ts re-exports it as submitOrder, and three of its five consumers import submitOrder from utils. A Grep for processOrder finds the definition, the barrel line and the two consumers that import the original name. It cannot find the other three, because the string processOrder never appears in their files. Read the barrel, spot the rename, Grep for submitOrder, and the three turn up.

The multi-step trace catches indirect consumers a single Grep would miss.

This one turns up constantly in exam prep: find every file that calls a deprecated function AND the test files that exercise it. The correct sequence:

  1. Grep for the function name — finds every file whose contents reference the function, including any tests that import it directly (content search)
  2. Glob for sibling test files — finds the test file that pairs with each caller by naming convention, e.g. OrderProcessor.tsOrderProcessor.test.tsx, even when the test exercises the function indirectly through the source module (path matching)
  3. Grep again for wrapper names — when a caller exposes the function through a wrapper (e.g. applyLegacyOrder calls processLegacyOrder internally), Grep for the wrapper name to find tests that cover the function transitively through it

Say Grep reveals that OrderProcessor.ts and RefundHandler.ts call the deprecated function. Glob for **/OrderProcessor.test.* and **/RefundHandler.test.* to pull in their sibling test files, even if those tests never mention processLegacyOrder by name. And if either source file wraps the function under a new name, Grep for the wrapper to catch any remaining tests.

This is Grep, then Glob, then Grep again — content search for direct references, path matching for adjacent tests, content search for indirect coverage. Not Glob first.

A developer needs to find all files that call a deprecated function processLegacyOrder() and also find all test files for those callers. Which tool sequence is correct?

  • A. Glob for **/*processLegacyOrder* to find caller files, then Grep inside that result set for test files. Glob resolves the file list first, so the content search runs over fewer files and stays inside the context budget.
  • B. Read all the source files to search for the function manually, then Read all the test files to pair them with their callers. Reading every file gives complete visibility of each call site and test, so no caller can be missed by a naming mismatch, and the full contents remain available in context for the later refactoring steps.
  • C. Grep for processLegacyOrder to find callers (this also surfaces tests that import the function directly), then Glob for the sibling test file of each caller (e.g. **/OrderProcessor.test.*) to catch tests that exercise the function through the source module without naming it.
  • D. Bash with find and xargs grep for both steps, since a single shell pipeline can locate the callers and their test files in one pass without switching between built-in tools.
Answer & explanation

Correct: C

  • A — Glob matches file paths, not file contents. It cannot find function callers — it would only match files named after the function, which is unlikely. The tools are backwards.
  • B — Reading all files upfront is a context-budget killer. It consumes tokens on irrelevant files and is the exact anti-pattern the exam penalises.
  • C — Grep searches file contents — correct for finding callers and any tests that reference the function by name. Glob matches file paths — correct for finding the test file paired with each source file by naming convention, which is how tests exercise the function indirectly. This is the optimal sequence.
  • D — While technically functional, this bypasses the built-in tools designed for these tasks. The exam expects candidates to select the right built-in tool for each task.

Five exam-style multiple-choice questions on Built-in Tools. Pick an answer, then open the explanation.

A developer needs to find all files that call a deprecated function processLegacyOrder() and also find all test files for those callers. Which tool sequence is correct?

  • A. Glob for *processLegacyOrder* to find the callers, then Grep for the matching test files.
  • B. Read every source file to locate the function by hand, then read every test file too.
  • C. Bash with find piped through xargs grep for both steps.
  • D. Grep for processLegacyOrder to find callers, then Glob for their test files (e.g., **/*.test.tsx).
Answer & explanation

Correct: D

  • D is correct because Grep searches file contents, which is what finding callers requires, and Glob matches file paths, which is what finding test files by naming pattern requires. Content search first, then path matching.
  • A is wrong because it has the two tools the wrong way round. Glob matches paths, so it would only find files named after the function, which is not how callers are stored.
  • B is wrong because loading every file before knowing which ones matter spends the context budget on irrelevant code. This is the anti-pattern the exam penalises most directly.
  • C is wrong because it works but sidesteps the built-in tools designed for exactly these two tasks. The exam expects the right built-in tool per task.

A developer tries to use Edit to replace a function call, but Edit fails because the text appears 3 times in the file. Which recovery does the exam guide name?

  • A. Use Bash with sed to replace all three occurrences at once.
  • B. Add a temporary comment to make the target occurrence unique, Edit that line, then remove the comment.
  • C. Split the file so each occurrence lives in its own module, then Edit each one.
  • D. Fall back to Read to load the full file, then Write the complete modified file.
Answer & explanation

Correct: D

  • D is correct because the exam guide names Read + Write as the documented fallback when Edit cannot find unique anchor text: load the full file with Read, then Write the complete modified version.
  • A is wrong because sed bypasses Edit’s unique-match check altogether, so it can rewrite the two occurrences that were meant to stay.
  • B is wrong because editing a file to make a later edit possible is fragile, and it leaves artefacts behind whenever the cleanup step is missed.
  • C is wrong because restructuring a codebase to work around a tool limitation is disproportionate effort.

A developer needs to find all TypeScript test files in the project. Which tool and pattern should they use?

  • A. Grep for “test” to find every file that so much as mentions testing anywhere in the tree.
  • B. Read the project root to list every file in the tree, then filter that list down by hand.
  • C. Glob for **/*.test.tsx, which matches the test files on their path naming convention.
  • D. Bash with find . -name “*.test.tsx”, shelling out rather than using the built-in path search.
Answer & explanation

Correct: C

  • C is correct because Glob matches file paths by naming pattern, and finding files by extension is what it exists for. It returns the matching paths sorted by modification time.
  • A is wrong because Grep searches contents, not paths. Test files do usually contain the word “test”, but so does plenty of ordinary code, so the results are both incomplete and polluted.
  • B is wrong because listing a directory does no pattern filtering. It is manual work in place of a purpose-built tool.
  • D is wrong because find would work, but the exam expects the built-in Glob tool for path-based searches.

A developer needs to understand how a complex module works. They start by reading all 47 source files in the module. What is wrong with this approach?

  • A. Nothing is wrong, since reading everything gives the most complete understanding.
  • B. It spends the context budget before knowing what matters, so Grep for entry points, then Read those.
  • C. They should run Glob first, so the file set is filtered before any reading starts.
  • D. They should ask the team for documentation rather than reading the code at all.
Answer & explanation

Correct: B

  • B is correct because loading 47 files before knowing which are relevant consumes the context window on code that mostly is not. The incremental route is to Grep for an anchor, a function name, a class name or an error string, and then Read outwards from what that turns up, following imports and tracing flows.
  • A is wrong because most of those 47 files will not bear on the question being asked. This is the single biggest anti-pattern in codebase exploration.
  • C is wrong because Glob filters on name, not on relevance. Knowing which files are .ts says nothing about which ones matter to this investigation.
  • D is wrong because documentation may not exist or may have gone stale. The exam is testing code exploration strategy.

A function processOrder is defined in orders.ts. The barrel file utils/index.ts re-exports it under a new name, submitOrder. Five files consume the function: 2 import processOrder from orders.ts directly, 3 import from utils. A simple Grep for “processOrder” finds the definition, the barrel file and the 2 direct importers, but misses the other 3 consumers. Why?

  • A. Grep has a built-in file limit and stops once it has found a certain number of matches.
  • B. The Grep pattern needs a regular expression in order to match the function name at all.
  • C. The 3 missing consumers live on a different branch of the repository and are not checked out.
  • D. The 3 missing consumers import submitOrder, so the string processOrder never appears in their files.
Answer & explanation

Correct: D

  • D is correct because the barrel renamed the export. A consumer that writes import { submitOrder } from “utils” and calls submitOrder() contains no occurrence of processOrder, so a literal search for the original name cannot reach it. The reliable route is multi-step: Grep for the definition, Read the barrel to find every exported name, then Grep again for each of those names.
  • A is wrong because Grep applies no default file limit. It searches everything in the scope it is given.
  • B is wrong because a literal match is perfectly adequate for direct references, and it would match utils.processOrder just as well. What defeats it is the rename at the barrel, not the absence of a regular expression.
  • C is wrong because Grep searches the current working tree. Other branches are not in scope for this investigation.