This is Part 1 of the "Agentic Engineering for .NET Teams" series. Part 2 covers context engineering, Part 3 covers sharing state across sessions and agents, and Part 4 covers function calling in C#.
GitHub Copilot suggests the next line, and a chat assistant answers questions about code you paste into it. Claude Code works differently: it reads your repository, proposes a plan, writes files, runs dotnet build, reads the compiler errors, fixes them, runs dotnet test and iterates on the failures, all from your terminal. Understanding that loop is what this article is about.
Claude Code launched as a research preview in February 2025 and became generally available in May 2025. It now runs in the terminal, a desktop app, VS Code, JetBrains IDEs and Slack. Adoption has moved quickly: a JetBrains Research survey of more than 15,000 developers, published in August 2026, puts Claude Code at 39% use at work, up from 18% in the company's January survey. The reason is architectural rather than cosmetic, because this is an agent with filesystem and shell access, not an autocomplete.
A rough mental model helps. Copilot completes code inside the file you are editing. Chat tools brainstorm and explain, but they cannot touch your repo. Cursor does multi-file edits inside a VS Code style editor. Claude Code is an agent that starts from the terminal: it searches your tree, plans, edits several files, runs shell commands and corrects itself based on the real output.
The building blocks it exposes are CLAUDE.md, skills, subagents, slash commands, hooks and MCP servers, and plugins bundle several of those into one installable unit. You do not need all of them on the first day, but knowing they exist stops you from treating the tool as a smarter autocomplete.
In February 2026, a year after he coined "vibe coding", Andrej Karpathy proposed the name "agentic engineering" for the professional version of this work: agents do much of the typing, and people keep the oversight. That is also the idea behind this series.
The recommended install is the native installer, which updates itself in the background. Homebrew and WinGet also work, and the npm package is still available. All of them install the same native binary, so claude does not depend on Node.js at runtime.
curl -fsSL https://claude.ai/install.sh | bash
claude --version
Claude Code is included with a paid Claude subscription. There is a 5-hour usage limit plus weekly limits, shared across claude.ai, Claude Code and the desktop app, and how fast you use them depends on the model and on how much context each request carries. Run /usage inside a session to see where you stand.
This is the most important part of the setup, and it is the part most teams write once and then forget. Anthropic's own guidance is to keep each CLAUDE.md under 200 lines, because a longer file costs more context and is followed less reliably. Instructions that only matter for part of the codebase can go into path-scoped rule files under .claude/rules/, which load only when Claude works with matching files.
Two files are worth separating. AGENTS.md is an open format that several coding agents read, so it is a good home for things every tool should know: target framework, coding conventions, forbidden APIs and architectural rules. CLAUDE.md is specific to Claude Code and holds workflow instructions. Since v2.1.277, Claude Code can read AGENTS.md directly, but by default only when the repository has no CLAUDE.md. If you keep both, import one into the other with a line such as @AGENTS.md, so both always load.
A practical structure for a Clean Architecture ASP.NET Core service:
@AGENTS.md
## What this repo is
ASP.NET Core Web API, Clean Architecture. Target: net10.0.
Conventions and forbidden APIs are in AGENTS.md (imported above).
## Build and test commands
- Build: dotnet build src/MyService.slnx
- Test: dotnet test src/MyService.slnx --no-build
- Format: dotnet format src/MyService.slnx
## Workflow
- Use plan mode for any change touching src/Billing/ or src/Domain/
- Always add or update xUnit tests in the same PR as domain changes
- Prefer record types for value objects
- Do not use MediatR pipeline behaviours for cross-cutting concerns;
use middleware instead (see ADR-004)
The solution file ends in .slnx because that is what dotnet new sln creates from the .NET 10 SDK onwards; older repositories may still have a .sln, and both work with the commands above.
A structure that works well is what, why and how: what the project is, why certain conventions exist, and how to run the workflow. Claude can also update the file for you, so "add this rule to CLAUDE.md" works in conversation. The old # shortcut for quick memory entries was removed in December 2025, so asking Claude directly is now the way to do it. If you want to see what actually loaded into a session, /context shows the memory files alongside everything else in the context window.
Hooks are what turn Claude Code from a tool that writes code into one that also checks it. A PostToolUse hook runs after a tool call succeeds, so a hook on Edit and Write can build the solution after every change. When the hook exits with code 2, Claude sees its stderr and fixes the problem before moving on.
Add the hook to .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/dotnet-build.sh" }
]
}
]
}
}
And the script it calls:
#!/usr/bin/env bash
input=$(cat)
case "$input" in
*'.cs"'*|*'.csproj"'*) ;;
*) exit 0 ;;
esac
cd "$CLAUDE_PROJECT_DIR" || exit 0
if ! out=$(dotnet build src/MyService.slnx --no-restore -v q -nologo 2>&1); then
echo "$out" | grep -E "error [A-Z]+[0-9]+" | sort -u | head -20 >&2
exit 2
fi
The script reads the hook input from stdin, skips edits to files that cannot break the build, and passes only the compiler errors back to Claude. A full build after every edit can be slow on a large solution, so many teams point the hook at the nearest project instead, or run the tests from a Stop hook once Claude has finished a turn.
For any non-trivial change, start in plan mode. In plan mode Claude reads the relevant code and proposes changes without editing your files. You review the plan, push back on anything wrong, and only then let it act.
Running example: adding a PATCH /orders/{id}/status endpoint to an existing ASP.NET Core service, with request validation and integration tests.
claude --permission-mode plan "Add a PATCH /orders/{id}/status endpoint.
- Accept { status: string } body. Validate against the OrderStatus enum.
- Return 400 if the transition is invalid (see Domain/OrderStateMachine.cs).
- Add FluentValidation validator.
- Add integration tests in tests/MyService.IntegrationTests using the
existing WebApplicationFactory<Program> fixture."
Inside a running session, /plan followed by the same description does the same thing. Claude reads OrderStateMachine.cs, the existing endpoint pattern, the test fixture and the validator convention, then produces a numbered plan. If step 3 is wrong, for example because it adds a MediatR handler that your CLAUDE.md forbids, you correct it before a single file changes.
Once you approve, Claude acts. It edits files, the PostToolUse hook builds the solution, Claude reads any compiler errors and fixes them, then it runs dotnet test and works through the failures. How many rounds that takes depends on the change and on how clear the plan was.
For changes that touch dozens of files, such as a rename or a layer extraction, start the session in its own git worktree with --worktree:
claude --worktree rename-customer-to-client \
"Rename the Customer domain entity to Client across the whole solution.
Update all references, namespaces, tests, and migration names."
The worktree is created under .claude/worktrees/rename-customer-to-client/ on a new branch named worktree-rename-customer-to-client, so your main working tree stays untouched until you review and merge.
A few patterns matter in practice.
Scope tasks tightly. "Refactor the service layer" is too broad. "Extract OrderFulfillmentService from OrderService and move the three shipping-related methods" is something Claude can act on. The plan step helps here, because a vague task produces a vague plan.
Reference files explicitly. Writing @src/Billing/InvoiceService.cs in your prompt puts that file's content into the conversation straight away. Claude will find related files by searching the repository, but naming the starting points saves it the search.
Review diffs before moving on. /rewind can roll the conversation and the code back to an earlier point, because Claude Code checkpoints the files it edits with its own tools. It does not track files that only shell commands changed, so a git diff before you commit is still the habit that protects you. Claude Code produces the code, and you own the result.
Know when to intervene. If the plan heads somewhere you do not want, for example towards an abstraction you have no use for, interrupt and type your correction. Claude takes it into account and replans. Treating the loop as a black box is where teams get into trouble.
Review before merging. /code-review checks the current diff for correctness bugs inside your session. For security-sensitive code, such as a billing or auth module, /code-review ultra (also available as /ultrareview) runs a deeper multi-agent review in a cloud sandbox. Pro and Max plans include 3 free ultra runs, after which it uses usage credits.
The permission model has changed twice this year, and both changes matter for teams.
In v2.1.200 (3 July 2026), the mode that asks before every action was renamed Manual in the CLI, VS Code and JetBrains. Its config value is still default, and manual is accepted as an alias, so --permission-mode manual and "defaultMode": "manual" both work. In Manual mode, Claude reads files freely but asks before edits, shell commands and network requests.
In v2.1.283 (25 September 2026), auto mode became the built-in starting mode for interactive terminal and VS Code sessions. In auto mode a second model, the classifier, reviews actions instead of you. If your team wants every action approved by a person, set "defaultMode": "default" (or "manual") in settings rather than relying on the built-in default.
Do not rely on permission mode alone to enforce hard rules. A permission mode is a session-wide setting for how much supervision you want. Rules that must always hold belong in permission rules and hooks: deny rules apply in every mode, including bypassPermissions, and a PreToolUse hook runs before the tool does, so it can block the call. A PostToolUse hook, by contrast, runs after the tool has already done its work, which makes it right for checking and wrong for blocking.
Clarifying questions now wait for you. Also in v2.1.200, the AskUserQuestion dialog stopped continuing on its own after a timeout. That is safer for supervised work. If a workflow of yours relied on the old timeout to keep going unattended, you can opt back into an idle timeout through /config.
For organisations, managed settings sit above every other settings level, so no user or project file can override them. They can be delivered as a managed-settings.json file in a system directory, through MDM, or as server-managed settings from the Claude admin console. That is where you enforce organisation-wide rules: blocked reads, disabled bypass mode and required hooks.
Keep secrets out of context. Claude Code does not hide .env or appsettings.*.json files from its own file tools by default. Add Read deny rules for them in your settings, and keep real secrets in environment variables or a secret store rather than in files inside the repository:
{
"permissions": {
"deny": ["Read(.env)", "Read(appsettings.*.json)"]
}
}
Everything above runs within a single Claude Code session. Part 2 of this series looks at context engineering: what Claude actually sees in its context window, and how to get the right information in front of it for a .NET codebase. Part 3 moves beyond one session to memory, MCP servers, subagents and handoffs between agents. Part 4 builds the function calling loop in C# with the Anthropic SDK.
The gains from Claude Code come from the feedback loop more than from clever prompts. Set that loop up deliberately: a CLAUDE.md with your real conventions, hooks that run dotnet build and your tests, plan mode for anything non-trivial, and a habit of reviewing the diff before it is merged.