Claude Certification Program · v1.0 · Effective July 2026 · All four tracks open

Home › Study guides › CCAR-F › Domain 2 › Lesson 2.5

CCAR-F · Domain 2 · 18% of the exam · Lesson 2.5 · 22 min read

Selecting built-in tools: Read, Write, Edit, Bash, Grep, Glob

Grep searches contents, Glob matches paths, Edit needs a unique anchor and falls back to Read plus Write, and why an agent explores a codebase incrementally.

Written against task statement 2.5 of the official CCAR-F exam guide (Version 1.0, effective July 2026). An independent resource, not affiliated with Anthropic; the practice questions are written from scratch.

2.5.1 Six tools and one afternoon in unfamiliar code

Picture Dana, an engineer who joined a payments team on Monday. On Thursday she gets her first real task: every refund must record a reason code, a short label saying why the money went back. The change belongs in a billing service she has never opened. Its codebase, all the code files that make up the service, is a few hundred files of TypeScript, a common programming language. There is no architecture document, and the person who wrote most of it left last year.

Her questions come in the order they occur to anyone in her seat. Who calls the refund function? Where are the tests, the automated checks that prove the code still works? How does a refund travel through the code? And where exactly does her change go? A function is a named block of code that does one job, such as applyRefund. Other code calls it by writing its name, which makes it run.

A language model on its own cannot answer any of that. It has never seen Dana's code, and it cannot open a file. What it can do is ask for actions. A developer productivity agent built on the Claude Agent SDK gives it a small, fixed set of actions to ask for: the built-in tools. Six of them cover most everyday work on code. Read loads a file, Write creates or replaces one, and Edit changes one spot in a file. Bash runs a command in the terminal, the text window where developers type instructions to the computer. Grep searches inside files for text, and Glob finds files by their names.

These are the same tools that power Claude Code. The model never runs them itself: it asks for one by name, and the SDK carries out the request on the machine where the agent runs and hands back the result. We'll follow Dana's afternoon and, at every step, ask which tool fits her question and why the others would fail.

2.5.2 Grep searches inside files, Glob searches for files

Dana's first two questions sound alike, but two different tools answer them, and the exam returns to this pair again and again. "Who calls applyRefund?" is about what is WRITTEN inside files. "Where are the test files?" is about what files are CALLED. The agent has one tool for each.

Grep searches the contents of files for a pattern. By default it returns the list of files that contain a match; asked for content, it returns the matching lines with their line numbers. Anything you would find by opening files and scanning for text is a Grep job: every caller of a function, the file that produces a particular error message, every file that imports a module. An import is a line at the top of a code file that pulls in code from another file, called a module, so this file can use it. For Dana's first question the agent runs Grep for applyRefund and gets back a short list of files.

Grep's pattern is a regular expression: a compact way of describing text in which a few characters have special meanings. A plain word such as applyRefund is already a valid pattern, and it matches itself. The special characters let one pattern cover variations: refund(s|ed) matches both "refunds" and "refunded", because the bar means "or".

Glob matches file paths against a pattern and never looks inside a file. A path is a file's address, folder by folder, such as src/billing/refunds.ts. The pattern is a glob pattern, a filename template with wildcards: * stands for any run of characters within a name, and ** for any number of folders. So **/*.test.tsx reads as "any file, in any folder, whose name ends in .test.tsx", which is how Dana's team names its test files. For her second question the agent runs Glob with that pattern and gets the test files back.

Think of a library. Glob walks the shelves reading spines: every book whose title ends in "Test". Grep opens the books and scans the pages for a phrase. That is why each tool fails at the other's job: no file is named after the functions it calls, and a content search for "test" returns every file that merely mentions the word. The two meet in one place: Grep's glob input takes a glob pattern such as **/*.ts, so it searches only the files whose names match.

Tool What it does, and the question it answers What it is not for
Grep Searches file contents for a pattern: "which files mention applyRefund?" Finding files by name or extension
Glob Matches file paths against a pattern: "which files are named *.test.tsx?" Finding text inside files
Read Returns a file's contents with line numbers: "what is in this file?" Listing a folder (a command, run through Bash)
Write Creates a file, or overwrites it with the full content given: "replace this file" Changing one line of an existing file
Edit Replaces one exact, unique piece of text with another: "change this spot" Text that appears more than once
Bash Runs a terminal command: "run the tests", "who last changed this file?" Searching, when the agent has Grep and Glob

Memorise the first two rows cold; recognise the rest.

2.5.3 Read and Write move whole files; Edit changes one spot

Dana has her callers and her tests. Now she needs to see the code, and then to change it. Three tools work on a file itself, and they differ in how much of it they touch.

Read takes a file's path and returns the whole file with line numbers, which is how the agent sees code at all. A very large file comes back as a first page, with a notice saying how to fetch the rest using the offset and limit inputs. Read works on files only; to list what is in a folder, the agent runs the ls command through Bash.

Write works at the same scale in the other direction. It creates a file, or overwrites an existing one, with the complete content it is given. It does not add to the end and it does not merge, so anything missing from the new content is gone.

Edit is the scalpel. It takes an old_string, the exact text to find, and a new_string, the text to put in its place. The old_string is often called the anchor, because it pins down where the change goes. The match is exact: no regular expressions, no fuzzy matching. Three checks must pass before an Edit applies.

  • Read first. The agent has normally read the file earlier in the conversation, so it edits what is really there rather than what it remembers.
  • Exact match. The old_string appears in the file exactly as written. One extra space or tab is enough to miss.
  • Unique. The old_string appears exactly ONCE in the file.

The third check is the one the exam cares about.

Here is Dana's change. In src/billing/refunds.ts, the function applyRefund writes one line to the log, the running record the service keeps of what it did. The team wants that line to carry the reason code. The agent reads the file, finds the line and sends an Edit. Look at the two strings: old_string is copied character for character from the file, and new_string adds reasonCode.

{
  "file_path": "/repo/src/billing/refunds.ts",
  "old_string": "logger.info(\"refund applied\", { orderId });",
  "new_string": "logger.info(\"refund applied\", { orderId, reasonCode });"
}

Match the tool to the size of the change. A one-line change to an existing file is an Edit; a brand-new file, or one rewritten from top to bottom, is a Write. Using Write for one line means sending back the entire file, and any line the agent reproduces imperfectly is silently changed. That is why the tools reference sends partial changes through Edit.

2.5.4 When Edit cannot find a unique anchor

Now the interesting failure. Dana's second change is in src/billing/adjustments.ts, which records every movement of money on an order. It holds four near-identical functions, one each for refunds, fees, credits and corrections. All four end with the same line, logger.info("adjustment posted", { orderId });, and only the refund one should gain the reason code. The agent sends an Edit with that line as old_string, and Edit refuses. The text appears four times, and Edit does not guess which one was meant.

Think of telling a courier to deliver to "the blue door on Main Street" when the street has four blue doors. A good courier comes back and asks. The refusal works the same way, and it is a feature: an ambiguous edit that went ahead might change the fees function instead, a bug nobody would notice for weeks.

The tools reference lists two ways to stay with Edit. The agent can widen the anchor, adding enough surrounding lines to pin down one occurrence. Or it can set replace_all: true, which changes every occurrence and is right only when every copy should change. Neither fits here. The four functions end with the same few lines, so a unique anchor would have to stretch back to the function's name and copy most of its body. And replace_all would add a reason code to fees and credits, which have none.

The reliable fallback, and the one the exam expects, is two steps: Read the whole file, then Write it back with the change made. The agent reads adjustments.ts, so every line is in front of it. It produces the full corrected content, with the reason code added in the refund function only, and writes the file. Nothing is ambiguous, because Write searches for nothing. It replaces the file with exactly what the agent decided the file should say.

Why is this the fallback and not the default? Cost and risk. Read plus Write pulls the entire file into the conversation, sends the entire file back, and relies on the agent to reproduce every untouched line faithfully. Edit sends two short strings and cannot damage anything outside its anchor. So the order is Edit first, and Read plus Write when no unique anchor will do.

Edit fails on a non-unique anchor, Read plus Write takes over

Editold_string = the log line
Refusedfound 4 times, not unique
Readthe whole file, with line numbers
Writethe full corrected file
Edit refuses when old_string appears more than once; reading the whole file and writing the corrected file back makes the change without searching for anything.

2.5.5 Build understanding a step at a time

Dana's hardest question is "how does a refund actually flow through this code?" The tempting plan is to have the agent read everything under src/billing/ and then explain. Resist it. Every file the agent reads goes into its context window, the working memory that holds the whole conversation, and stays there. A few hundred files fill that memory with code that never mattered, and the agent's answers get worse as it fills. Anthropic's best-practices guide for Claude Code calls this failure "the infinite exploration".

The alternative is incremental exploration. Find one entry point, a spot in the code where the trail starts, such as the line that raises a known error. Read the file it lives in, and let that file tell you where to look next. It is how a detective works a case, following one lead to the next instead of interviewing the whole town. In tool terms it is a loop: Grep, Read, follow, Grep again.

  1. GREP FOR AN ENTRY POINT. Dana knows an error message customers have seen, "Refund exceeds captured amount", meaning more than was charged. Grep for that text finds it in exactly one place, src/billing/refunds.ts.
  2. READ THAT FILE. The agent reads refunds.ts and sees applyRefund, the check that raises the error, and two imports at the top, from captures and ledger.
  3. FOLLOW THE IMPORTS. Each import names the next file. The agent reads ledger.ts, where the refund is recorded, and skips captures.ts, which handles taking payment rather than returning it.
  4. GREP AGAIN. In ledger.ts the agent learns the record is posted by postAdjustment. Grep for that name shows who else calls it, and the map grows by one hop.

Incremental exploration

Grepan entry point: an error message, a function name
Readthe file that matched
Follow importsthe modules it depends on
Grep againthe names you just learned
Grep again → Grep · repeat until the flow is traced
Each step produces the next lead, so the agent reads only the files on the refund path and nothing else.

Four steps in, the agent has read two files out of several hundred, and every next move is a lead rather than a guess. Bash has a place in this work too: listing a folder, checking who last changed a file, running the tests after a change. For searching, reach for Grep and Glob when the agent has them. They can only read, so an agent that only needs to look can have them without Bash, which can run any command at all.

Read everything first, or explore incrementally?

Read every file under src/hundreds of files in context
Ask for an overview with no scopethe infinite exploration
Glob for **/*.ts and read them allnames, not leads
Grep for an entry point, Read it, follow imports, Grep againa few files read, flow traced
Reading a codebase up front fills the context with files that never matter; a Grep-then-Read trail reads only what the question touches.

2.5.6 Tracing usage through wrapper modules

There is one place where a single Grep quietly misleads you, and Dana walks straight into it. Her first search, Grep for applyRefund, returned two callers. But older codebases are full of wrapper modules. A wrapper module is a file that takes a function from another file and passes it on, either under a new name or wrapped inside a small function of its own. Code that uses the wrapper's name is really calling applyRefund, and a search for applyRefund never finds it.

Look at src/billing/index.ts, a wrapper module only a few lines long. Each export offers a name to the rest of the code, which other files can then import. It creates two aliases, second names for the same function. Anyone who imports processRefund or calls refundOrder is running applyRefund without ever writing its name.

import { applyRefund } from "./refunds";

export { applyRefund as processRefund };   // alias 1: a different exported name

export async function refundOrder(orderId: string, amount: number) {
  return applyRefund({ orderId, amount });   // alias 2: a wrapper function
}

The method is two steps, in this order. First, identify every exported name in the wrapper: the agent reads index.ts and lists what it offers, here processRefund and refundOrder. Second, Grep for each of those names across the codebase. The second search finds a third caller in src/jobs/nightlyReconcile.ts, a scheduled job that runs by itself every night and calls refundOrder. Its refunds need a reason code too, so the job must pass one in, and a single search would have missed it.

Think of a colleague called Robert whom half the office calls Bob. Ask who has emailed "Robert" and you miss everyone who wrote to "Bob". A function's callers are a set of names, not one, and the set is complete only once you have followed every export. List the names first, then search; searching for names you have not listed yet is guessing.

2.5.7 The exam traps

Every trap here is a tool applied to the wrong kind of question, or the right tool at the wrong scale. Name the kind of question and the trap answers itself.

  • ✗ Glob to find the callers of a function. ✓ Grep. Callers are text inside files, and Glob sees only paths.
  • ✗ Grep to find all test files. ✓ Glob with **/*.test.tsx. Test files are known by their names, not their contents.
  • ✗ Write to change one line of an existing file. ✓ Edit on a unique anchor. Write replaces the whole file and loses anything the agent failed to reproduce.
  • ✗ Retrying an Edit that failed on a non-unique anchor, or forcing it through every copy. ✓ Read the whole file, then Write the corrected file back. Retrying does not make the anchor unique. replace_all, or sed (a command-line find-and-replace) run through Bash, changes every copy, including the ones that should stay.
  • ✗ Reading every file up front to "understand the codebase". ✓ Grep for an entry point, Read it, follow its imports, Grep again. Reading everything fills the context with files the question never touches.
  • ✗ One Grep for the function name, then declaring the callers found. ✓ List the wrapper module's exported names first, then Grep for each. Aliases and wrapping functions hide callers from a single search.

2.5.8 Put it together: explore a codebase and make one change

You now have every piece of Dana's afternoon: the six tools, and two habits, following one lead at a time and listing a wrapper's exports before searching. The quickest way to make the rules stick is to run them on a tiny codebase and then make a tool fail on purpose.

The same tools and habits carry into the domains ahead. Path-specific rules (3.3) use glob patterns such as **/*.test.tsx to decide which files a set of team conventions applies to. Some explorations are too big for one context window. Managing context in large codebases (5.4) hands a question such as "trace the refund flow" to a subagent, a helper agent with its own context window, so only its findings come back.

Key takeaways

  • ✓ One job per tool: Grep searches file contents, Glob matches file paths, Read loads a whole file, Write replaces a whole file, Edit changes one uniquely anchored spot, Bash runs a command.
  • ✓ Content questions (callers of a function, an error message, an import) are Grep; filename questions (**/*.test.tsx) are Glob, never the other way round.
  • ✓ Edit swaps an exact old_string that must appear exactly once, so a one-line change to an existing file is an Edit, not a Write.
  • ✓ When Edit fails because its anchor is not unique, Read the whole file and Write the corrected file back: the reliable fallback, not the default, because it re-sends every line.
  • ✓ Explore incrementally: Grep for an entry point, Read that file, follow its imports and Grep for the names you learn, rather than reading every file up front.
  • ✓ To trace a function through wrapper modules, list every exported name first, then Grep for each; a single search for the original name misses aliased callers.

Check your understanding

4 questions written for this lesson, then one from the CCAR-F question bank on the same topic. Every answer option is explained, including the ones you did not pick. Nothing is stored.

66 CCAR-F questions on Domain 2, free

Every question in the bank is tagged to a domain, so you can drill 66 questions on Tool Design & MCP Integration alone, or sit the full 60-question timed simulator.

Open the CCAR-F question bank → Back to Domain 2 →

The question bank is free. It asks for an account only because the quiz engine has to store answers to score them and show which domains are weak. The questions on this page need nothing.

Sources