中文 | English
Reading a line of code is easy. Understanding why it was written that way is the real lesson.
This document mirrors the Chinese philosophy guide and keeps the same evidence points so English readers can follow the same learning path.
- Reversibility First
- Generator As The Core Abstraction
- A Three-Layer Permission Model
- Errors Do Not Need To Surface Immediately
- Cache Stability Is A First-Class Concern
- Memory Is An Index, Not A Dump
- The Coordinator Never Peeks
- Global State Must Stay Small
- A Tool Is Schema Plus Function
- Numeric IDs Beat String Labels
Source evidence: system prompt guidance in constants/prompts.ts
Claude Code optimizes for operations that are safe to undo. The more destructive or wide-reaching an action is, the more likely the agent should pause and confirm first.
This is not just a UX choice. It is an architecture choice:
- local, reversible actions can often proceed
- shared or hard-to-undo actions should stop for confirmation
- the cost of one missed confirmation is much higher than the cost of one extra confirmation
When you build your own agent, this principle should outrank feature completeness.
Source evidence: query.ts exports query() and defines queryLoop()
Claude Code uses async generators because they solve three problems at once:
- streaming output
- cancellation
- composition through
yield*
This is why nested agent flows can forward events all the way to the UI without inventing a second event bus. The design is less about syntax and more about keeping the runtime model simple.
Source evidence: tools/BashTool/bashPermissions.ts, tools/BashTool/bashSecurity.ts
Command execution is guarded by three different layers:
- semantic safety checks
- allow/deny rule matching
- an explicit user confirmation fallback
This layering matters because it separates:
- obviously dangerous commands
- repeatable user policy
- everything ambiguous
The result is safer than “always ask” and less annoying than “ask for everything.”
Source evidence: query.ts, especially isWithheldMaxOutputTokens()
Some errors are temporarily withheld so the runtime can attempt recovery before exposing failure to the outside world.
The key insight is operational:
- if a transient failure can be recovered internally, surfacing it too early may cause upstream callers to abort the whole session
- recovery first, reporting second, often produces a more stable system
This is one of the most instructive patterns in the codebase because it is counterintuitive but pragmatic.
Source evidence: constants/prompts.ts, QueryEngine.ts, context utilities
Claude Code is designed around prompt-cache friendliness:
- a static/dynamic boundary keeps the stable prefix identical
- important session settings are snapshotted once
- sticky latches avoid changing cache-breaking headers mid-session
The lesson is larger than Claude Code itself: once prompt caching matters economically, architecture starts to bend around cache stability.
Source evidence: memdir/memdir.ts, memory prompts, MEMORY.md
Persistent memory is intentionally split into:
- a tiny index file that is always loaded
- content files that are loaded only when relevant
This keeps memory lightweight, searchable, and less likely to rot. Claude Code stores what cannot be cheaply rediscovered elsewhere, not everything that happened.
Source evidence: coordinator/coordinatorMode.ts, AgentTool, SyntheticOutputTool
In coordinator mode, the parent agent does not inspect a worker’s internal state. It waits for a structured output boundary instead.
That preserves:
- isolation
- replaceability
- simpler orchestration logic
The analogy to processes is useful: good boundaries matter more than raw visibility.
Source evidence: bootstrap/state.ts
The file literally says:
// DO NOT ADD MORE STATE HERE - BE JUDICIOUS WITH GLOBAL STATEThat comment expresses a discipline, not just a style preference. Claude Code keeps message history, UI state, and tool-use context out of the global singleton on purpose. The payoff is easier reasoning and lower coupling.
Source evidence: Tool.ts, tools/*/
The model sees the schema and description. The runtime owns the implementation. Keeping those separate has three benefits:
- the model never sees local implementation details
- implementations can change without retraining the model’s mental contract
- descriptions become part of the API surface for tool quality
This is one of the cleanest design ideas in the project and worth copying directly.
Source evidence: tools/BashTool/bashSecurity.ts, BASH_SECURITY_CHECK_IDS
Security checks use numeric IDs instead of verbose strings. That improves:
- telemetry efficiency
- privacy and log hygiene
- long-term stability across renames
It is a small detail with a very strong engineering smell: the code was written by people who expect the system to operate at scale.
Claude Code repeatedly makes the same kinds of decisions:
- optimize for reversibility
- prefer recovery over noisy failure
- keep boundaries explicit
- treat cache hit rate as architecture
- make abstractions compose cleanly
Those are the real lessons in the source. The syntax is secondary.
For guided reading, continue with:
- 源码导图(中文)
- Source Map (English)
docs/layers/for per-layer deep dives