bryan@devbox ~ % cat post.md

Claude Certified Architect Exam: Domain 3 — Claude Code

Domain 3 of the Claude Certified Architect exam is Claude Code, and it’s probably my favorite of the five. I spent about six months between Cursor and Claude Code building a meal-planning platform a little over a year ago, and I’ve kept learning how to use Claude Code effectively ever since. If you haven’t already, start with Domain 1: Agentic Architecture and Domain 2: Tools & MCP before diving into this one. This domain covers the Claude Code architecture, CLAUDE.md, defining commands, skills and subagents, and running Claude Code in a CI/CD pipeline.

Architecture

Claude Code is very different from IDE plugins or autocomplete tools. It runs in the terminal and operates semi-autonomously based on the direction you give it. It behaves agentically by default, looping through planning, acting, and observing steps before prompting you for the next one.

As with any other LLM system, Claude Code has a finite context window. Conversation history, tool calls, and the whole CLAUDE.md chain fill that window (more on CLAUDE.md below). When the window fills up, compaction runs to summarize the important bits of history and drop what it decides isn’t. If you tell Claude Code to remember something — or if you have auto memory enabled — it commits that information to a separate memory markdown file that gets referenced in future sessions (and also takes up context window space). Anything not committed to a memory file doesn’t follow you between sessions. Memory files are just text documents, so you can read or edit them yourself between sessions if you want to.

Permissions

Claude Code can be dangerous with unfettered access to your computer, so defining permissions for what it can and can’t do matters. Permissions are defined in JSON files, layered in this order of authority, lowest to highest:

  • Managed — controlled at the organization level, not editable by the user
  • User — a settings.json file at the user level, applies across all projects
  • Project — part of the repo, applies to anyone working in that project
  • Local — a combination of command-line arguments and a project-level JSON file that isn’t committed to the repo

Later layers override earlier ones, with one exception: deny always overrides allow, regardless of where the rule lives. Tools can also be restricted using PreToolUse hooks, defined in the same settings file, but a hook can apply logic rather than just enforcing a static allow/deny rule.

Tools

The main tools Claude Code uses are Read, Edit, Grep, Glob, Write, Bash, and Agent (this isn’t the complete list — just the ones you’ll run into constantly):

  • Read — reads files, whether that’s text, images, or PDFs
  • Write and Edit — similar but distinct: Write overwrites the entire file, Edit makes targeted changes
  • Glob and Grep — finding files and searching file contents, respectively
  • Bash — can do just about anything: delete files or directories, execute programs, call APIs. This is exactly why robust permissions matter so much
  • WebSearch — augments Claude Code’s knowledge or pulls current information needed to complete the task at hand
  • Agent — lets Claude Code function as a multi-agent orchestrator, spawning subagents for independent tasks that benefit from context isolation

Project Onboarding

The /init command analyzes the current project and documents it in a CLAUDE.md file. A well-structured project is more likely to get an accurate result from this than a messy one, but it’s still worth reviewing the output to make sure Claude Code didn’t miss anything. Accurate findings depend on up-to-date README files and config files like package.json or composer.json. The command captures the language(s), framework, and directory structure of the project, and attempts to describe the conventions it finds in the code — along with other key elements like the package name and entry points.

It’s important that the CLAUDE.md file captures everything, especially entry points, commands, and build steps — but it also needs to stay concise. Anything above 200 lines becomes unwieldy and gets processed unpredictably. Beyond the CLAUDE.md file itself, it’s worth reviewing your settings.json to make sure access to keys and secrets is explicitly prohibited.

Context Management

Since the context window is finite, managing what gets added to it matters. If your repository has large files or directories — node_modules, vendor folders, that kind of thing — you can use project-level settings to exclude access to them. Files that are gitignored are generally ignored by Claude Code too, unless you explicitly mention them. Any large binaries or auto-generated files should be explicitly restricted in settings. Compaction happens automatically once the context window fills up, but you can also run /compact yourself if you’d rather do it on your own schedule.

Permission Modes

There are four permission modes: default (also called “manual”), plan, accept-edits, and bypass-permissions. Only the first three are meant to be toggled while Claude Code is running. Bypass-permissions is fairly dangerous and should be reserved for sandboxed or CI contexts — it’s set via a CLI flag or a settings file configuration rather than an in-session toggle.

There’s also “auto mode,” toggled with Shift+Tab, which behaves less like a fourth permission mode and more like a behavioral bias layered on top of the others: it uses a classifier to auto-approve safe actions, with a meaningfully higher threshold before it stops to prompt you than accept-edits mode has.

The CLAUDE.md File

Similar to the settings configuration, there’s never just one CLAUDE.md. It exists to provide persistent instructions, loaded at four levels, broadest to narrowest:

  • Enterprise/managed — lives in a system path, applies across the organization
  • User — lives in the user’s profile, applies across that user’s projects
  • Project — lives at the root of a project, committed to the repo
  • Local — a CLAUDE.local.md that’s both project- and machine-specific, not committed

You can also add CLAUDE.md files inside individual directories, for instructions that only apply to files within that directory. Unlike settings, there’s no real precedence hierarchy here beyond load order — the files are loaded starting with the enterprise level and ending at the local project scope, then effectively concatenated together. Anthropic’s answer to conflicting instructions across files is, more or less: don’t do that.

Skills

Skills are essentially modular prompt engineering. A skill defines a discrete process that gets triggered by typing / followed by the skill name. Skills can be defined at the project level or the user level (available across multiple projects) — either way, they live in .claude/skills under the user profile or the project root. Skills can take arguments passed inline when you trigger them, or the skill can prompt you for them interactively.

Naming matters: it should be easy to tell what a skill does just from its name, and the standard convention is (verb)-(noun). Structure matters too. A well-built skill should clearly define the role, task, output format, and constraints it’s operating under — Claude won’t refuse to run a skill missing any of these, but results will be noticeably worse without them.

Similar to tools, an agent like Claude Code can choose to use a particular skill when the task at hand calls for it. The skill’s frontmatter contains its name and description, and Claude uses that description to decide when to reach for a given skill. Other fields that can appear in frontmatter include argument-hint, allowed-tools, disallowed-tools, model, and metadata, among others. Frontmatter isn’t strictly required, but it’s worth including — it’s the entire mechanism Claude uses to know the skill exists and when to use it.

Subagents

A subagent is an autonomous worker that handles a complex task independently, configured to use a narrow set of skills, tools, context, and permissions. Claude Code can spawn a built-in subagent when it decides a task needs one, but explicitly defining tightly scoped subagents ahead of time is far more efficient. A subagent needs three things at minimum: the task, the necessary context, and the expected output format.

Delegation isn’t free — there’s overhead in latency and coordination complexity, so it’s worth deciding whether that cost is justified before defining a subagent rather than just handling something inline. Beyond a standard output schema, subagents also need to be configured to return explicit error information on failure, along with whatever context would help diagnose it.

CI/CD in Action

Hooks are useful in a CI/CD context in a few specific ways:

  • PostToolUse — linting, auto-formatting, audit logging
  • PreToolUse — a safety net to block destructive actions before they run
  • SessionEnd — generating a session summary log

You can also run Claude Code in non-interactive mode, using the -p flag, in scenarios where you can hand it a tightly defined task with a narrow toolset, triggered by an event: a build step, generating release notes when a PR opens, running tests for pre-merge verification. In non-interactive mode, you can also define the output format, which matters when something downstream in the pipeline needs to parse the result rather than just read it.

Claude Agent SDK

The Agent SDK lets you embed Claude Code’s functionality directly inside applications and scripts without running the CLI itself. Every feature of Claude Code is available through it — hooks, subagents, MCP support, skills, plugins, all of it — just accessed as a library in your own code instead of a terminal session.


Next up: Domain 4, Prompt Engineering.