Skip to lesson

AnthropicClaude Certified Architect – Foundations2026-102.5 Select and apply built-in tools (Read, Write, Edit, Bash, Grep, Glob) effectively

Follow exported names to indirect callers

Completed by the knowledge check

Learning objective

Trace code usage incrementally across imported modules, exported aliases, and wrappers rather than loading the entire codebase.

Core idea

A code generation agent needs a path through the code that explains the requested behavior. It does not need every file before it can ask the next useful question. Start from a known function, route, or error message, search for its location, read the relevant module, and follow the imports and exported names that connect that location to callers.

One textual search cannot establish every usage of an implementation. A module can re-export a function under another name, and a wrapper can call it behind its own public interface. You must discover those names and search for each one. The investigation grows from evidence, with each read producing the next search target rather than an invitation to load the whole repository.

How it works

Start from one behavioral question

Choose an entry point that connects directly to the request. If customers see “Refund exceeds captured amount,” use Grep.pattern to search that text. If the task names applyRefund, search that identifier. Use output_mode: "content" when you need a statement and location rather than only a filename. Search-tool scoping and availability are covered in 2.5.1.

Use Read.file_path on the relevant module, with offset and limit where a range is enough. Determine which definition emits the error, which conditions lead to it, and which imported functions participate in that path. Follow an import because it answers the next question about this behavior, not because every dependency deserves immediate expansion.

An import is a lead, not a complete explanation. A refund implementation might import both a capture lookup and a ledger writer. If the question concerns why refunds exceed available capture, the lookup is relevant; if it concerns missing ledger entries, the writer is relevant. Decide from the observed branch and the requested outcome instead of following an arbitrary directory order.

Follow names across module interfaces

Search for the implementation’s name to locate importers, calls, and re-exporting modules. Read a re-exporting index.ts rather than assuming it contains no behavior worth checking. Identify every exported name in that module that forwards or wraps the target function before searching for downstream users.

Consider these illustrative relationships in a billing package:

Observed module relationshipName to search nextWhy it matters
refunds.ts defines applyRefundapplyRefundFinds direct mentions and modules exposing it
index.ts exports applyRefund as processRefundprocessRefundConsumers can use the exported alias
index.ts exports refundOrder, which calls applyRefundrefundOrderConsumers call a distinct wrapper function
A consumer imports processRefund as issueRefundissueRefund within that consumerThe imported function has another local name

A re-exported alias and a wrapper are different. The alias supplies another name for the target. The wrapper has its own body and can add validation, supply defaults, or transform arguments before calling the target. Read that body to determine whether its callers exercise the behavior you are tracing. Do not describe every wrapper as an interchangeable alias.

Search each discovered public name across the relevant codebase, then read the matching import and use together. If another module forwards a name again, repeat the same step for that module’s exports. A name is meaningful together with its module origin: two packages can contain an unrelated processRefund.

Keep the trace bounded and evidential

Treat search results as candidate edges in the trace. A comment mentioning refundOrder is not a call. A test importing it demonstrates a test dependency, not a production entry point. A file that imports an alias establishes a dependency, while reading the call establishes how the dependency is used.

At each step, retain the source path, the observed imported or exported name, and the statement connecting it to the next module. This lets you explain the path from caller to implementation without carrying every file into the final answer. If a branch remains unresolved, report the missing link instead of upgrading a plausible route into a verified one.

Anthropic’s best practices identify unscoped investigation as “the infinite exploration”: reading hundreds of files fills context. The context window includes conversation messages, file reads, and command output, and performance degrades as it fills. Bound the investigation by the behavior and verification criteria. A broad Glob followed by reading every source file defeats that boundary even if each individual read succeeds.

Stop when the discovered paths answer the requested question and the relevant verification can be selected. That is different from claiming a complete runtime call graph from text matching. If a discovered wrapper introduces another name or branch relevant to the requirement, continue; if an unrelated import does not affect the requested behavior, leave it outside the trace. Each read produces the next search target until the discovered paths answer the question.

Each read produces the next search target until the discovered paths answer the questionStarts by searching an entry point and reading the relevant module, then identifies every exported name that forwards or wraps the target and searches each discovered public name. Where a result introduces another relevant name or branch, the trace loops back to read that module. An unrelated import is left outside the trace, and once the question is answered the trace stops by selecting the relevant verification.continueunrelated importquestion answeredSearch an entrypointRead the relevantmoduleIdentify everyexported nameSearch eachdiscovered publicnameAnother relevantname or branch?Leave it outsidethe traceSelect the relevantverification

Key distinctions and trade-offs

Implementation name versus consumer name: applyRefund can disappear from downstream source once a package exposes processRefund. Search the exported name after reading the re-export; repeating the original search cannot reveal a name it does not contain.

Alias versus wrapper: Both can conceal indirect usage from one search, but only a wrapper introduces a separate function body to inspect. Preserving wrapper argument handling can matter as much as changing the implementation.

Local understanding versus whole-codebase loading: A focused path uses less context and lets you inspect each relevant branch carefully. Its limitation is explicit scope: the findings must say which behavior and module paths were investigated. Smaller reads do not justify larger claims.

Search versus resolution: Grep searches text. A matching identifier can belong to a different module, and an imported alias can change the spelling at the call site. Reading the import and call together supplies the relationship that a bare identifier list lacks.

When active, the LSP tool uses a running language server to find symbol references, jump to definitions, and trace call hierarchies. It requires a code intelligence plugin for the language and an installed server binary; it stays inactive in cloud sessions because plugin language servers do not start there. Use it for symbol resolution; exported-name searches remain the exam guide’s assessed method.

Architecture / decision scenario

A Claude Code agent must add a required refund reason without breaking request handlers or scheduled jobs. It begins with Grep for the refund error text and reads refunds.ts, where applyRefund checks the captured amount and invokes a ledger writer.

Searching applyRefund finds a direct request handler and billing/index.ts. The agent reads that module and lists two exposed routes to the implementation: the re-export processRefund and the wrapper refundOrder. It searches both names before concluding that the direct request handler is the only affected caller.

The refundOrder search reveals a reconciliation job. Reading the wrapper shows that it constructs the argument object sent to applyRefund, so adding the required reason means examining that construction as well as the job. A processRefund importer renames it locally to issueRefund; reading that import tells the agent which local calls to inspect.

The agent follows the relevant ledger import to check where the reason is recorded, then selects the request-handler and job tests reached by those paths. It can explain implementation, public alias, wrapper, local alias, and downstream effect using a small set of observed files. It does not load every billing module to establish those connections or label unrelated identifier matches as affected callers.

Common exam traps

  • Searching the original implementation name once and declaring every caller found.
  • Skipping a re-exporting index.ts because it looks like a directory manifest.
  • Searching only one of several exported names that expose the behavior.
  • Treating a wrapper function as a pure alias without reading its argument handling.
  • Ignoring a renamed import at the consumer’s call site.
  • Reading every file before defining the behavior to trace.
  • Turning a scoped textual investigation into a claim of complete runtime reachability.

Where the exam guide differs

Note:

The exam guide’s incremental trace assumes Grep. Claude Code omits it from the default tool set on macOS, Linux, and WSL, searching through Bash with grep instead. Restore it through --tools, through --allowedTools naming either Grep or Glob, or through a subagent tools list that includes Grep and omits Bash; see 2.5.1 for restoration scope. The method remains the same: find an entry point, read relevant code, follow imports, and search the exported names.

What to remember

Search an entry point, read its relevant code, follow imports, identify exported aliases and wrappers, and search each discovered name. Repeat only where the evidence opens another relevant path. Preserve the module relationships and unresolved gaps so the resulting explanation is both compact and defensible.

Knowledge check

References