Claude Code ships with six built-in tools that cover the majority of software engineering tasks: Read, Write, Edit, Bash, Grep, and Glob. Knowing which tool to use for a given task, the exact risks each carries, and how to sequence them safely in agentic workflows is both a practical skill and a core exam topic.
A woodworker has many tools: a ruler (measure/inspect), a pencil (mark/create from scratch), a chisel (precise targeted cuts), a power saw (fast but dangerous if misused), a magnifying glass (find a specific grain pattern), and a blueprint search tool (locate the right plank by spec).
Each serves a distinct purpose. Using a power saw where a chisel is needed destroys the work. Using a ruler when you need a chisel wastes time. The expert craftsperson reaches for the right tool for the specific job — never defaulting to the most powerful one just because it’s available.
Claude Code’s six built-in tools map exactly: Read (ruler/measure), Write (pencil/create), Edit (chisel/precise), Bash (power saw/powerful+dangerous), Grep (magnifying glass/find content), Glob (blueprint search/find files).
Reads file contents or lists directory entries. Non-destructive. Foundational for all exploration and understanding tasks.
Creates a new file or completely replaces existing file content. Destructive if the file exists. Use only for new files or full rewrites.
Makes targeted changes to specific lines or text blocks within an existing file. Preserves all unchanged content. Surgical precision tool.
Executes arbitrary shell commands. Extremely powerful — can run tests, install packages, delete files. Carry irreversible potential.
Searches file contents for a pattern across a directory tree. Returns matching lines with context. Non-destructive, read-only.
Finds files matching a path pattern (e.g., **/*.py). Returns matching paths. Non-destructive. Used for file discovery before read or edit.
| Tool | Operation | Destructive? | Scope | Prefer Over |
|---|---|---|---|---|
| Read | File read / dir list | No | Single file or dir | Bash cat / ls |
| Write | Full file create/replace | Yes (overwrites) | Entire file | — |
| Edit | Targeted content change | Partial | Specific lines/blocks | Write for changes to existing files |
| Bash | Shell command execution | Potentially | System-wide | — Not a substitute for other tools |
| Grep | Content pattern search | No | Tree-wide | Bash grep |
| Glob | File path discovery | No | Tree-wide | Bash find |
Read is the safest and most frequently used built-in tool. It provides file content or directory listings without any side effects. It should always precede any Edit or Write operation on an existing file.
| Parameter | Purpose | Example |
|---|---|---|
path | Absolute or relative file/directory path | /project/src/main.py |
start_line | Optional: read from this line (1-indexed) | 50 |
end_line | Optional: read up to this line | 100 |
The canonical agentic pattern is: Glob → Read → Edit/Write. Never edit a file you haven’t read. Reading first ensures you understand current content, avoids overwriting unexpected changes, and gives Claude the accurate line numbers needed for targeted edits. This is the safest sequence and the one tested on the exam.
# Read full file before editing Read: /project/src/payments/process.py # Read only relevant section (large file) Read: /project/src/payments/process.py (lines 45-120) # List directory to understand structure before diving in Read: /project/src/ # Returns: [process.py, models.py, gateway/, state_machine.py, ...]
Write creates or completely replaces a file with new content. It is a destructive operation on existing files. The entire file content is overwritten — there is no merge or partial replacement.
Using Write on an existing file deletes all current content and replaces it with the new content verbatim. If you intended to change only one function but used Write with incomplete content, you’ve lost the rest of the file. Use Edit for changes to existing files. Use Write only for new files or intentional full rewrites.
--- WRONG: Using Write to change a single function --- Write: /project/src/auth.py Content: "def login(user, pwd):\n return True" # ❌ Destroys every other function, import, and class in auth.py --- CORRECT: Use Edit to change a single function --- Edit: /project/src/auth.py OldContent: " return check_password(user, pwd)" NewContent: " return check_password_with_mfa(user, pwd, mfa_token)" # ✅ Only the targeted line changes; everything else preserved --- CORRECT: Write for a new file --- Write: /project/tests/test_auth_mfa.py Content: "import pytest\n\ndef test_mfa_required():\n ..." # ✅ File doesn't exist yet; Write is the right tool
Edit replaces a specific string or line range within an existing file. It preserves everything else, making it the correct default tool for modifying existing files. Two flavors:
The str_replace variant of Edit requires the target string to be unique within the file. If the same string appears multiple times, Edit may fail or apply to the wrong location. Always read the file first to confirm uniqueness of the target. If the target isn’t unique, include more context (surrounding lines) to make it unique.
# Pattern 1: str_replace - targeted text replacement Edit: /project/src/payments/process.py OldContent: | def validate_amount(amount): if amount < 0: raise ValueError("Amount must be positive") NewContent: | def validate_amount(amount: Decimal) -> None: """Validate payment amount. Amount must be in cents as integer.""" if not isinstance(amount, (int, Decimal)): raise TypeError("Amount must be Decimal or int") if amount <= 0: raise ValueError("Amount must be positive (in cents)") # Pattern 2: insert - add new content without replacing Edit: /project/src/payments/process.py InsertAfterLine: 42 Content: | # Added: log all payment attempts for audit trail logger.info(f"Payment attempt: order_id={order_id}, amount={amount}")
Bash runs arbitrary shell commands. It is the most powerful built-in tool — and the most dangerous. Commands execute with the permissions of the Claude Code process and can have irreversible effects on the system.
pytest tests/, npm testruff check ., mypy src/make build, docker build .git status, git diff, git log --oneline -10pip install -r requirements.txtA critical anti-pattern: using Bash: cat file.py instead of Read: file.py, or Bash: grep -r pattern . instead of Grep, or Bash: find . -name "*.py" instead of Glob. The dedicated tools (Read, Grep, Glob) are safer (no shell injection risk), more structured, and produce output Claude can reason about more reliably. Reserve Bash for operations that have no built-in tool equivalent.
| Rule | Rationale |
|---|---|
Never run rm -rf without explicit user confirmation | Irreversible; destroys data permanently |
Prefer read-only Git commands (git diff, git log) before write commands | Understand state before modifying it |
| Avoid piped commands where shell injection is possible | User input in shell pipes can execute arbitrary commands |
| Never combine Bash with untrusted input from tool results | Tool result content could contain shell metacharacters |
| Run tests after edits before declaring completion | Verifies changes didn’t break existing behavior |
Grep searches file contents across a directory tree for a text or regex pattern. It is the standard tool for “where is this used?” questions before refactoring, and for finding configuration values, error messages, or function references.
| Parameter | Purpose | Example |
|---|---|---|
pattern | Text or regex to search for | validate_payment or def \w+_payment |
path | Directory or file to search within | /project/src |
include | File extension filter | *.py |
case_sensitive | Case-sensitive search | false |
Before renaming a function or changing a method signature, always Grep for all its call sites. Knowing the full impact of a change before making it is the hallmark of safe agentic code modification. Pattern: Grep for all usages → Read each caller → Edit each impacted file → Bash to run tests.
# Find all callers of a function before refactoring Grep: pattern="validate_payment" path="/project/src" include="*.py" # Find hardcoded API endpoints before moving to env vars Grep: pattern="https://api.payments.internal" path="/project" include="*.py" # Find all TODO comments in the codebase Grep: pattern="# ?TODO" path="/project/src" include="*.py" # Find all places that import a module about to be renamed Grep: pattern="from payments.process import" path="/project" include="*.py"
Glob finds files matching a path pattern. Unlike Grep (which searches file contents), Glob searches file names and paths. It is the right tool for “where are the files of type X?” before reading or editing them.
# Find all Python test files Glob: pattern="**/test_*.py" path="/project" # Find all YAML config files Glob: pattern="**/*.yml" path="/project" # Find all migration files in the alembic directory Glob: pattern="migrations/versions/*.py" path="/project" # Find all TypeScript files in the frontend src Glob: pattern="**/*.ts" path="/project/frontend/src" # Find all .env files (to check what exists before touching them) Glob: pattern="**/.env*" path="/project"
Glob: finds files by path/name pattern. Use when you need to know which files exist matching a name pattern. Grep: finds files by content pattern. Use when you know the file type but need to find which files contain specific text. These are complementary, not substitutes. Typical combined usage: Glob to get all *.py files, then Grep to find which ones use a specific function.
When Claude faces a task step, the right tool follows from the question being answered:
| Question / Task | Correct Tool | Wrong Tool (Anti-pattern) |
|---|---|---|
| What is in this file? | Read | Bash: cat |
| What files are in this directory? | Read (dir) | Bash: ls |
| Which files match *.test.ts? | Glob | Bash: find |
| Which files contain "validate_payment"? | Grep | Bash: grep -r |
| Change one function in an existing file | Edit | Write (overwrites all) |
| Create a brand-new file | Write | Edit (file doesn't exist) |
| Run tests, lint, or build | Bash | — |
| Run git diff to check changes | Bash | — |
| Insert a new line without replacing | Edit (insert) | Write (full replace) |
| Completely rewrite auto-generated file | Write | Edit (too many changes) |
Skilled agentic use of built-in tools means chaining them in safe, logical sequences. The exam tests whether you know the correct order:
1. Glob: "**/payments*.py" # Discover relevant files 2. Read: payments/process.py # Understand current content 3. Grep: "validate_amount" in src # Find all call sites 4. Edit: payments/process.py # Make targeted change 5. Bash: "pytest tests/payments/" # Verify change didn't break # ✅ Safe: Explore → Read → Search → Edit → Test
1. Read: /project/src/ # Understand project structure 2. Glob: "**/test_*.py" # Find test file conventions 3. Read: tests/test_auth.py # Understand test patterns used 4. Write: src/mfa.py # Create new module (file doesn't exist) 5. Write: tests/test_mfa.py # Create new test file 6. Edit: src/auth.py # Integrate new module into auth 7. Bash: "pytest tests/" # Run full test suite # ✅ Correct order: Read → Glob → Write new → Edit existing → Test
1. Read: /project/ # Top-level structure 2. Read: /project/src/ # Source layout 3. Glob: "**/models.py" # Find all model files 4. Read: src/payments/models.py # Understand payment models 5. Grep: "class Payment" # Find all Payment class usages 6. Bash: "git log --oneline -20" # Review recent changes # ✅ Pure exploration: Read + Glob + Grep + safe Bash only. No edits.
Using Write to make partial changes to existing files. Write replaces the entire content. All code not included in the Write call is permanently deleted. Use Edit for existing file changes.
Running cat, find, or grep via Bash when dedicated tools exist. Dedicated tools are safer (no shell injection), output structured data, and don’t expose the system shell to unnecessary risk.
Calling Edit with a target string from memory or assumption without first reading the file. The actual content may differ — whitespace, indentation, or variable naming can cause the target match to fail or apply to the wrong location.
Running rm -rf, DROP TABLE via bash script, or git reset --hard without explicit human confirmation. These are irreversible; the system must request approval before executing them.
Completing code edits without running the test suite via Bash. Claude Code cannot know if its changes are correct without execution. Always end edit sequences with Bash: pytest or equivalent.
Using Grep to find files by name or Glob to find files by content. Glob = path/name patterns. Grep = content patterns. Swapping them produces no results or unexpected matches and wastes tool calls.
Make Read → Edit an unbreakable habit. Reading first provides accurate line content, confirms the edit target exists, and prevents "file not found" or "target not matched" errors mid-workflow.
Apply Write only when creating files that don’t exist. For all modifications to existing files, use Edit. This preserves all existing content not explicitly changed.
After any Write, Edit, or Bash command that changes state, run tests or read back the result to verify correctness. Autonomous agents must self-verify; they cannot assume success.
Scenario: An agent needs to rename the function check_payment to validate_payment across a Python codebase. What is the correct sequence of built-in tools?
Answer: (1) Grep: search for all occurrences of check_payment across *.py files. (2) Read: read each affected file to understand context. (3) Edit: str_replace in each file to rename. (4) Bash: run pytest to verify no breakage. (5) Grep again: confirm no remaining check_payment references.
Common distractor: “Use Bash: sed -i to do a global replace” — wrong. Using Bash when Edit exists is an anti-pattern. Bash sed has shell injection risk, less structured output, and bypasses Claude Code’s built-in safety guardrails.
When should you use Write instead of Edit for a modification task?
Answer: Never — for modification of an existing file, Edit is always correct. Write is for creating new files or entirely replacing auto-generated content. If you find yourself wanting to Write an existing file with partial content, you’ve chosen the wrong tool.
You have covered all 5 task statements of Domain 2: Tool Design & MCP Integration. Next up: Domain 3 — Claude Code Configuration & Workflows, starting with Task 3.2.