Skip to content
Domain 2Task 2.5

Built-in Tools

Learn this interactively

What You Need to Know

Claude Code provides six built-in tools for working with codebases: Read, Write, Edit, Bash, Grep and Glob. Read loads a file's full contents, Write saves a complete file, and Edit replaces exact text in a file. Grep searches what files contain. Glob matches file paths. Every file read into context costs tokens, and the context window has a limit. Each tool has a specific purpose, and the wrong tool wastes time, context tokens or both.

This lesson uses one made-up example throughout: a sports shop that sells sports clothing and equipment online. Its developer uses Claude Code on the shop's codebase: order handling, refunds and their tests. Finding code, changing it and tracing its use each need a different tool.

Grep vs Glob: The Core Distinction

Grep searches file contents for patterns. Use it to find function callers, error messages, import statements and variable assignments. The developer uses Grep to find every file in the shop's codebase that calls processLegacyOrder, because a function call is text inside a file.

// 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: name, extension or directory structure. Use it for test files, configuration files, or all TypeScript files in one directory. The developer uses Glob to find every test file in the shop's codebase, because test files share a naming pattern.

// 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"

Grep finds what is inside files. Glob finds files by their names. So Glob cannot find function callers, because a file's path contains no information about what the file calls. In a technical sense, Grep can find test files by naming pattern: it can search for "test" in filenames via content. Grep is still the wrong tool for paths.

Read, Write, and Edit

Read, Write and Edit handle file operations. Each tool is optimised for a different use case. Edit makes targeted changes by matching unique text. You give Edit the exact text to find and its replacement. Edit is fast and precise because it changes only that text, as in the edit below.

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

Edit needs its anchor to be unique in the file. The anchor is the exact text that Edit searches for. If the anchor appears in several places, Edit cannot tell which occurrence you mean, so Edit fails. This failure is a safety mechanism that stops you from changing text that you never meant to change. Say the shop's checkout file calls processOrder(orderId) three times. An Edit with that short anchor fails.

One option is Read + Write: read the full file, then write the complete modified version to the file. Read + Write works every time, because it does not rely on Edit to find the text to change. It also spends tokens on the whole file for what was usually a one-line change. For that reason, Read + Write is the fallback, not the default.

Current state (checked 6 October 2026): the Edit tool docs give a cheaper option to try first. Widen old_string with more surrounding context until it matches one location, or set replace_all: true to update every occurrence. Both options use Edit and cost almost no extra context. In the checkout file, adding the enclosing function's first line to the anchor gives one match. The order in real work has three steps.

  1. Try Edit with the shortest anchor that is 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. Use Read + Write as the fallback when neither of those options can identify the text to change.

Do not use Read + Write by default for every modification. It wastes context tokens.

Incremental Codebase Understanding

How you explore a codebase matters as much as which tools you use. Reading every file upfront, before you know what you need, is the most costly mistake in exploring a codebase. If you read a 200-file codebase in full, it fills your entire context window. Most of those files are unrelated to the task. Incremental discovery reads a few files first and reads more only when it needs them.

  1. Grep to find entry points. Search for the function name, class name or error message that starts your investigation. The results tell you which files are relevant.
  2. Read to follow imports and trace flows. Read the files that matter to understand the code structure. Follow their 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. One case is a wrapper (submitOrder() that calls processOrder() inside it). Another case is a barrel file that re-exports the function (export { processOrder as submitOrder }). Callers of the new name never mention the original name, so your first Grep never found them. Grep for each new name, across the whole codebase, to get the full list of consumers.
  4. Read only what you need. Justify each file you read by what you found in the previous step.

At the sports shop, a refund bug starts from an error message. The developer Greps for the message, reads the file that the Grep result shows, then follows the imports. This approach uses as little context as possible for as much understanding as possible. The developer spends tokens only on files that matter.

Tracing Function Usage Across Wrapper Modules

In a common pattern, a function is defined in one module. A wrapper re-exports the function, and consumers use it through the wrapper's name. A Grep for the original name misses every consumer that imports through the wrapper, because those files never contain that name. The trace has four steps.

  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.

In the sports shop's codebase, processOrder is defined in orders.ts. The barrel file utils/index.ts re-exports it as submitOrder, and three of the function's five consumers import submitOrder from utils. A Grep for processOrder finds the definition, the line in the barrel file and the two consumers that import the original name. That Grep cannot find the other three, because the string processOrder never appears in their files. Read the barrel file, find the rename and Grep for submitOrder, which finds the three. The multi-step trace finds indirect consumers that a single Grep would miss.

Finding Callers and Their Tests

To find every file that calls a deprecated function and the test files that exercise it, run three steps in order.

  1. Grep for the function name. This content search finds every file whose contents reference the function, including any tests that import it directly.
  2. Glob for sibling test files. This path match finds the test file that pairs with each caller by naming convention. For example, OrderProcessor.ts pairs with OrderProcessor.test.tsx. Glob finds that test file even when the test exercises the function indirectly through the source module.
  3. Grep again for wrapper names. When a caller exposes the function through a wrapper, Grep for the wrapper's name to find tests that cover the function indirectly through that wrapper.

A test that reaches the function through its source module may never contain the function's name, so Grep misses it. The test's path follows the source file's name, so Glob finds it.

Say the sports shop deprecates processLegacyOrder, and Grep shows that OrderProcessor.ts and RefundHandler.ts call it. Glob for **/OrderProcessor.test.* and **/RefundHandler.test.* to find their sibling test files. If either source file wraps the function under a new name, Grep for that wrapper to find any remaining tests.

In the exam

The exam deliberately presents scenarios where a developer confuses these tools. The distinction between Grep and Glob matters most in this task statement, the part of the exam guide that this lesson covers. A Glob for function callers fails, because Glob does not search contents. A Grep for test files by naming pattern works technically, but the exam expects you to pick Glob.

The guide names one fallback when Edit cannot find a unique anchor: Read + Write. The exam's ordering has two steps: Edit first, then Read + Write when Edit fails. Its first step agrees with the real-work ordering. The exam penalises using Read + Write by default, because it wastes context tokens.

The deprecation question appears constantly in exam prep. The correct sequence is Grep, then Glob, then Grep again. Starting with Glob is wrong.

Key Concept

Grep searches file contents. Glob matches file paths. Edit is the default for modifications, and on a non-unique match the exam's answer is Read + Write. Never read all files upfront.

Current state

Exam guide v1.0 describes Read + Write as the documented fallback. Its words are "Using Read to load full file contents followed by Write when Edit cannot find unique anchor text". Current state (checked 6 October 2026): the Edit tool reference documents widening old_string or setting replace_all: true as the behaviour on a non-unique match. On the exam, answer Read + Write. In real work, widen old_string first, because it is cheaper.

Current state

Exam guide v1.0 lists Grep and Glob among the built-in tools: "Select and apply built-in tools (Read, Write, Edit, Bash, Grep, Glob)". Current state (checked 14 October 2026): the tools reference says "On macOS, Linux, and WSL, Claude Code leaves Glob and Grep out of the default tool set, and Claude searches with find and grep through the Bash tool instead." Naming Grep and Glob in --tools or --allowedTools when the session starts restores them. On the exam, treat Grep and Glob as built-in tools.

Exam Traps

Exam Trap

Using Glob to find function callers

Glob matches file paths by naming pattern. It cannot search inside files for function calls. Use Grep to search file contents for function names, import statements, or error messages.

Exam Trap

Using Grep to find files by extension or naming pattern

While Grep could technically find filenames mentioned in content, Glob is the purpose-built tool for matching file paths. Use Glob for path patterns such as test files and config files.

Exam Trap

Reading all source files upfront before understanding what is relevant

Loading every file into context is a large waste of context tokens. The correct approach is incremental: Grep to find entry points, then Read to trace flows from those specific entry points.

Exam Trap

Defaulting to Read + Write for every file modification instead of trying Edit first

Edit is faster and uses less context because it only touches the specific text. Read + Write loads the entire file. Try Edit first. Read + Write is the fallback for when Edit cannot find a unique anchor, not the standard response.

Exam Trap

Widening old_string or setting replace_all when no anchor, however long, would be unique and only one occurrence should change

The exam guide says Read + Write is the fallback when Edit cannot find unique anchor text. Current docs (October 2026) say widen old_string or set replace_all. Widening needs a unique anchor, and replace_all changes every occurrence. So use Read, then Write.

Practice Scenario

A developer needs to find all files that call a deprecated function processLegacyOrder() and also find all test files for those callers. The repository holds several thousand source files, far more than fit in context, and the project's settings do not allow Bash. Which tool sequence is correct?

Build Exercise

Build Exercise

Trace and Refactor a Deprecated Function Using Built-in Tools

Difficulty
30 minutes

What you'll learn

  • Apply Grep for content search and Glob for path matching in the correct sequence
  • Use incremental codebase discovery instead of reading all files upfront
  • Select Edit as the primary modification tool and widen the anchor (or use replace_all) when Edit reports a non-unique match
  • Trace function usage across wrapper modules and barrel files
  • Follow the Grep-then-Glob pattern for finding callers and their test files
  1. Use Grep to search for all callers of a target function (e.g. processLegacyOrder) across the codebase

    Why: Grep searches file contents — it is the correct tool for finding function callers. Using Glob here would fail because Glob matches file paths, not contents. The exam tests this distinction directly and penalises candidates who confuse the two.

    You should see: A list of file paths containing calls to processLegacyOrder, with line numbers and matching lines showing the exact call sites. For example: src/OrderProcessor.ts:42: await processLegacyOrder(orderId).

  2. Use Glob to find test files matching the caller filenames (e.g. **/*.test.tsx)

    Why: Glob matches file paths by naming pattern — it is the correct tool for finding test files by extension or naming convention. This completes the Grep-then-Glob pattern: content search to find callers, then path matching to find their tests.

    You should see: A list of test file paths matching the pattern, such as src/OrderProcessor.test.tsx and src/RefundHandler.test.tsx. These correspond to the caller files found by Grep in the previous step.

  3. Use Read to examine each caller file and understand the usage pattern and context

    Why: Reading files incrementally — only after Grep identifies which files matter — is the correct approach. Reading all source files upfront is a large waste of context tokens that the exam explicitly penalises. Each Read should be justified by what you discovered in the previous step.

    You should see: The full contents of each caller file, showing how processLegacyOrder is called, what parameters are passed, how the return value is used, and whether the function is imported directly or through a wrapper module.

  4. Use Edit to replace the deprecated function call with the new API in each caller file

    Why: Edit is the preferred modification tool because it targets specific text and uses less context than Read + Write. The exam penalises defaulting to Read + Write for every modification. Always try Edit first — it is faster and more precise.

    You should see: Each caller file updated with the new API call replacing the deprecated one. For example, processLegacyOrder(orderId) replaced with processOrder(orderId, { validate: true }). The Edit tool confirms the replacement was made successfully.

  5. When Edit fails with a non-unique match, widen old_string with more surrounding lines until it matches one location (or set replace_all: true if you actually want every occurrence updated). Use Read + Write only if neither option can identify the text to change

    Why: Edit fails when the target text appears multiple times in the file — this is a safety mechanism, not a bug. Per the Edit tool documentation, the documented recovery is to expand the anchor with more surrounding context until it matches one place, or to use replace_all for global replacements. Both keep you on Edit and cost almost nothing in context. Read + Write loads the entire file for what is usually a single-line change — keep it as the option to use when the others fail. That is the answer for real work. On the exam, the guide names Read + Write as the fallback, so answer that.

    You should see: On the first try, Edit fails with an error like: old_string matches 3 locations. On the retry with a wider old_string that includes the surrounding function name or unique adjacent line, Edit succeeds and changes exactly one occurrence. If replace_all: true was the right choice, every occurrence is updated atomically, in one operation.

Sources