Skip to content

Claude Code - Risk Assessment & Teaching Complexity Analysis

Coding Conventions

Naming Conventions

  • Files: PascalCase for classes/components (BashTool.tsx, QueryEngine.ts), camelCase for utilities (processUserInput.ts)
  • Directories: PascalCase for tool/component directories (AgentTool/, FileEditTool/), camelCase for utility modules (settings/, permissions/)
  • Types: PascalCase, often with descriptive suffixes (ToolUseContext, PermissionDecisionReason, TaskStateBase)
  • Functions: camelCase, verb-first (buildTool(), findToolByName(), getSystemPrompt())
  • Constants: SCREAMING_SNAKE_CASE (REMOTE_SAFE_COMMANDS, INTERNAL_ONLY_COMMANDS)

File Organization Patterns

  • Each tool: XxxTool.ts(x) (core) + prompt.ts (model description) + UI.tsx (render) + constants.ts
  • Each command: sub-directory with index.ts or command-name.ts
  • Root-level files serve as "barrel" re-exports or top-level orchestrators
  • types/ directory breaks circular import cycles

TypeScript Patterns

  • Zod v4 for runtime schema validation (tool inputs, settings)
  • Discriminated unions for Command (3 variants), PermissionDecisionReason (9 variants), TaskType (7 variants)
  • Branded types for SessionId, AgentId via ids.ts
  • DeepImmutable<> wrapper for AppState to enforce immutable updates
  • AsyncGenerator for streaming data flow (query(), submitMessage())
  • feature() from bun:bundle for compile-time dead code elimination
  • memoize from lodash-es for caching expensive computations

Key Design Patterns

1. Builder/Factory Pattern: buildTool()

  • Location: src/Tool.ts
  • Every tool is constructed via buildTool(def), which merges defaults for optional methods
  • The ToolDef type defines what implementors must/may provide; Tool is the fully-resolved type

2. Dependency Injection: ToolUseContext

  • Location: src/Tool.ts
  • A 40+ field context object passed through the entire tool call chain as function arguments
  • Avoids global state; different modes (REPL/SDK/sub-agent) provide different field subsets
  • This is NOT a traditional DI container; it's a large "environment" bag

3. AsyncGenerator/Coroutine Pattern

  • Location: src/query.ts, src/QueryEngine.ts
  • query() and submitMessage() are both AsyncGenerator functions
  • Enables backpressure-aware streaming consumption
  • Natural fit for "produce events as they happen" model

4. Observer/Pub-Sub Pattern

  • Location: src/state/store.ts
  • 35-line Zustand-style store: createStore<T>() -> { getState, setState, subscribe }
  • React integration via useSyncExternalStore in src/state/AppState.tsx

5. Command Pattern (Discriminated Union)

  • Location: src/types/command.ts
  • Three command variants: PromptCommand, LocalCommand, LocalJSXCommand
  • Dispatched by type field in the command registry

6. Strategy Pattern: Permission Handlers

  • Location: src/hooks/useCanUseTool.tsx
  • Three-way handler chain: coordinator -> swarm worker -> interactive
  • Selection based on runtime context (is coordinator? is swarm worker? is interactive?)

7. Decorator Pattern

  • Location: src/QueryEngine.ts
  • wrappedCanUseTool wraps the original canUseTool to append permission denial records

8. Plugin/Extension Pattern

  • Location: src/skills/, src/plugins/, src/services/mcp/
  • Skills: Markdown files with YAML frontmatter -> dynamic Command generation
  • Plugins: Structured packages providing MCP servers, slash commands, hooks
  • MCP: External tool servers connected via stdio/SSE/HTTP/WebSocket

Complexity Hotspots (Teaching Difficulty Ranking)

Critical Complexity (Require Dedicated Chapters)

1. src/main.tsx — The God Function

  • ~4000+ lines in a single file
  • Contains ALL CLI argument parsing, ALL run mode branches, ALL precondition validation
  • Teaching approach: Draw a mode branch tree first, then deep-dive per-branch

2. src/query.ts — The Agentic Loop

  • while(true) loop with 7 continue paths
  • Implicit state machine: auto-compact, max-token recovery, reactive compact, etc.
  • No explicit state machine definition; trigger conditions scattered across conditionals

3. src/tools/BashTool/bashPermissions.ts — 2600-line Permission Engine

  • Wildcard matching, command parsing, speculative classifier
  • The single most complex file in the tools/ directory

4. src/ink/ — Custom React Reconciler

  • Complete TUI framework: reconciler, Yoga WASM layout, ANSI rendering, terminal I/O
  • ~85 files implementing a full React renderer targeting terminal output
  • Requires deep React internals knowledge to understand

5. src/screens/REPL.tsx — The Interactive Session

  • ~3000+ lines orchestrating all UI interactions
  • Manages message display, user input, permission dialogs, task panels, teammate views

High Complexity (Substantial Explanation Needed)

6. src/hooks/useCanUseTool.tsx — Permission Decision Tree

  • Multi-layer async race: coordinator -> swarm -> speculative classifier (2s timeout) -> interactive dialog
  • Promise.race with timeout, multiple fallback paths
  • Note: File is React Compiler output, harder to read than source

7. src/QueryEngine.ts::submitMessage() — ~900 lines

  • Single async generator function containing transcript persistence, budget checks, snip compaction, message dispatch
  • Multiple interleaved concerns in one function

8. src/bootstrap/state.ts — Global Mutable Singleton

  • 80+ getter/setter functions, implicit temporal coupling
  • Some getters return uninitialized values before init() completes

9. src/Tool.ts::ToolUseContext — 40+ Field Context Object

  • Optional fields mean different modes provide different subsets
  • No type-level enforcement of which fields exist in which mode

Medium Complexity (Moderate Explanation)

10. src/commands.ts — Feature Flag Conditional Loading

  • feature('FLAG') ? require('./module') : null pattern breaks static analysis
  • 70+ commands with dynamic skill/plugin merging

11. src/services/mcp/ — MCP Client Management

  • 4 transport types (stdio, SSE, HTTP, WebSocket)
  • OAuth authentication, tool/resource enumeration

12. src/services/compact/ — Context Window Management

  • Auto-compact, micro-compact, snip strategies
  • Critical for understanding how Claude Code handles long conversations

Teaching-Relevant "Aha Moments"

These are the non-obvious architectural decisions that will create the biggest learning impact:

  1. The AsyncGenerator Pipeline: Why query() is a generator, not a callback/promise — it enables natural backpressure and streaming without complex event emitter wiring

  2. ToolUseContext as "Everything Bag": Why a single 40-field object instead of 10 smaller interfaces — pragmatic trade-off for a system where most tools need most context

  3. The 35-Line Store: How src/state/store.ts achieves Zustand-like functionality in 35 lines, and why the team chose this over a library

  4. buildTool() Factory: How a single factory function with TypeScript generics ensures every tool implements the full Tool interface while making most methods optional

  5. Compile-Time Feature Flags: How bun:bundle's feature() enables dead code elimination at build time while GrowthBook provides runtime overrides

  6. Permission Decision Cascade: The 9-variant PermissionDecisionReason union that traces exactly why every permission decision was made — a lesson in auditability

  7. Recursive Agent Architecture: How AgentTool calls query() recursively to create sub-agents, sharing some context (file cache) while isolating others (message history)

  8. Custom React Reconciler for Terminal: Why Anthropic built their own Ink fork instead of using the npm package — performance and control needs for a production CLI tool

Risks for Teaching Guide Creation

Content Organization Risks

R1: main.tsx is too large to explain linearly

  • Mitigation: Break into "mode branches" and explain each as a separate path through the function

R2: React Compiler output files are unreadable

  • Affected: AppState.tsx, useCanUseTool.tsx
  • Mitigation: Explain the pre-compilation logic; reference source maps embedded in file footers

R3: Circular dependency patterns

  • The types/ directory exists specifically to break cycles
  • Teaching must explain why certain types live in types/ vs their "natural" module

R4: Feature flag dead code

  • Readers may be confused by code blocks wrapped in feature('FLAG') that are never reachable in open-source builds
  • Mitigation: Explain the DCE system early, mark feature-gated code clearly

Scope Risks

R5: utils/ is 564 files — cannot cover exhaustively

  • Strategy: Cover key sub-areas (bash/, permissions/, settings/, swarm/, model/) and provide a directory map for the rest

R6: components/ is 389 files — UI coverage needs scoping

  • Strategy: Focus on message rendering pipeline, permission dialogs, and the design system; treat rest as reference

Built for learners who want to read Claude Code like a real system.