Skip to main content
Article complete

Get one like this every Tuesday at 7 PM IST.

codewithmukesh
Back to blog
claude dotnet 16 min read Lesson 9/27 New

I Built a Claude Code Skill That Scaffolds My .NET Architecture

Teach Claude Code your conventions once. Build a custom skill that scaffolds a full vertical slice in .NET 10 - endpoint, handler, validator, EF config, and test - your way.

Teach Claude Code your conventions once. Build a custom skill that scaffolds a full vertical slice in .NET 10 - endpoint, handler, validator, EF config, and test - your way.

claude dotnet

claude-code claude-code-skills dotnet-10 vertical-slice-architecture scaffolding minimal-apis code-generation ai-coding-assistant claude-md ef-core-10 fluent-validation developer-productivity anthropic slash-commands convention-over-configuration aspnet-core feature-folders agentic-coding

Mukesh Murugan
Mukesh Murugan
Software Engineer
Chapter 09 of 27
View course

Claude Code for .NET Developers

From dotnet new to docker push - REST, EF Core 10, auth, caching, Clean Architecture, observability. 27 hands-on lessons, source on GitHub.

For the first few weeks I used Claude Code on a real .NET API, I corrected the same things every single session. It scaffolded a controller when the project uses minimal APIs. It wired up Swashbuckle when I had moved to Scalar. It dumped everything into a Services folder when the codebase is organized by feature. The code worked, but it was never my code, and I was spending more time reshaping it than I would have spent writing it.

So I stopped correcting and started teaching. I wrote my conventions into a custom Claude Code skill once, and now /scaffold-feature Products generates a complete vertical slice - endpoint, handler, validator, response, EF Core configuration, and an integration test - structured exactly the way I would have typed it by hand.

In this article I will build that skill from an empty folder. The .NET work is the hero here: a clean vertical slice in .NET 10. Claude Code is just the tool that learns to reproduce it. Let’s get into it.

What is a Claude Code skill, in one paragraph?

A Claude Code skill is a folder in your .claude/ directory containing a SKILL.md file that teaches Claude a repeatable workflow you can trigger with a slash command. To make it scaffold your architecture, you write your conventions and file templates into that SKILL.md, drop in reference templates if you want exact shapes, and invoke it with something like /scaffold-feature Products. Claude reads the skill, follows your instructions, and generates code that matches your project instead of a generic tutorial. I tested everything here on .NET 10.0.4 with Claude Code running Claude Opus.

That is the whole idea. Skills follow the open Agent Skills standard and are documented in Claude Code’s official Skills guide; the rest of this article is about doing it well for a .NET codebase.

Why generic AI output never matches your codebase

Out of the box, a coding model generates the statistically average .NET code. The average .NET tutorial on the internet uses controllers, Swagger, a three-folder layered structure, and MediatR. So that is what you get, even if your project made deliberate decisions to do none of those things.

The pain is not that the output is wrong. It compiles. The pain is that it is subtly off in twenty small ways, and you become a full-time reviewer correcting drift:

  • A controller instead of a minimal API endpoint.
  • services.AddSwaggerGen() when you standardized on Scalar.
  • A flat Services/ and Models/ split instead of feature folders.
  • A handler with no CancellationToken.
  • Data annotations on the entity instead of an IEntityTypeConfiguration.

Each fix is thirty seconds. Across a feature it is twenty minutes, and across a week it is the reason the tool feels like it is fighting you. The fix is to move your conventions out of your head and into a place Claude reads every time.

CLAUDE.md vs a skill vs a subagent: which one do I reach for?

Before building a skill, it is worth knowing when a skill is even the right tool. There are three ways to teach Claude Code your conventions, and they are not interchangeable.

ToolWhat it isBest forCost
CLAUDE.mdAlways-loaded project memoryGlobal rules that apply to every response (”.NET 10, Scalar not Swagger, never use MediatR”)Loaded into every prompt - keep it short
SkillOn-demand workflow behind a slash commandA repeatable procedure with steps and templates (“scaffold a full feature slice”)Loaded only when invoked - can be long and detailed
SubagentA separate context with its own toolsHeavy, parallel, or isolated work (“review the whole module in a fresh context”)Spawns a new agent - more tokens, more isolation

My rule: put principles in CLAUDE.md, put procedures in skills, and reach for a subagent only when context isolation actually buys you something. Scaffolding a feature is a procedure with a fixed shape and a dozen small rules, so it belongs in a skill. If I tried to cram the full slice template into CLAUDE.md, it would bloat every single prompt with instructions that are irrelevant 95% of the time. The skill stays out of the way until I call it.

Read next

Anatomy of a Claude Code Session - What Happens Under the Hood

How CLAUDE.md, rules, and skills actually load into context. Useful background before you start writing your own skill.

The .NET architecture I am encoding: a vertical slice

A skill can only reproduce a structure you can describe precisely. So before writing the skill, I need the target output nailed down. I use vertical slice architecture: each feature is a self-contained folder holding everything that feature needs, instead of spreading one feature across Controllers, Services, Repositories, and Models.

Here is the slice I want generated for a CreateProduct feature:

src/Api/ 4 dirs · 7 files
src/Api/
Features/
Products/
CreateProduct/
CreateProductEndpoint.cs
CreateProductCommand.cs
CreateProductHandler.cs
CreateProductValidator.cs
CreateProductResponse.cs
Product.cs
ProductConfiguration.cs

The endpoint is a minimal API, not a controller. It validates, calls a plain handler, and returns typed results:

public static class CreateProductEndpoint
{
public static void MapCreateProduct(this IEndpointRouteBuilder app)
{
app.MapPost("/products", Handle)
.WithName("CreateProduct")
.WithTags("Products");
}
private static async Task<Results<Created<CreateProductResponse>, ValidationProblem>> Handle(
CreateProductCommand command,
CreateProductHandler handler,
IValidator<CreateProductCommand> validator,
CancellationToken cancellationToken)
{
var validation = await validator.ValidateAsync(command, cancellationToken);
if (!validation.IsValid)
{
return TypedResults.ValidationProblem(validation.ToDictionary());
}
var response = await handler.HandleAsync(command, cancellationToken);
return TypedResults.Created($"/products/{response.Id}", response);
}
}

The handler is a plain class invoked directly. No MediatR, no pipeline indirection - just a constructor-injected DbContext and a method:

public sealed class CreateProductHandler(AppDbContext db)
{
public async Task<CreateProductResponse> HandleAsync(
CreateProductCommand command,
CancellationToken cancellationToken)
{
var product = new Product
{
Id = Guid.CreateVersion7(),
Name = command.Name,
Price = command.Price
};
db.Products.Add(product);
await db.SaveChangesAsync(cancellationToken);
return new CreateProductResponse(product.Id, product.Name, product.Price);
}
}

The command, response, and validator are small and predictable:

public sealed record CreateProductCommand(string Name, decimal Price);
public sealed record CreateProductResponse(Guid Id, string Name, decimal Price);
public sealed class CreateProductValidator : AbstractValidator<CreateProductCommand>
{
public CreateProductValidator()
{
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
RuleFor(x => x.Price).GreaterThan(0);
}
}

This is the shape every feature follows. Because it is so consistent, it is exactly the kind of thing a skill can generate reliably.

One piece the folder layout hides: how the endpoint actually reaches the app. Each endpoint exposes its own Map* extension, so wiring a feature is a single line - no controller for the framework to discover, no central registration block to edit:

Program.cs
app.MapCreateProduct();
// app.MapGetProduct(); app.MapListProducts(); ... as Products grows

That one line is the only change outside the feature folder, which is the entire point of a slice: a feature gets added, never threaded back through four shared directories. Registering it is an explicit step in the skill, so the scaffold comes out wired and building - not just generated and left for you to hook up.

Read next

CQRS in ASP.NET Core Without MediatR

Why I invoke handlers directly instead of routing everything through a mediator - the reasoning behind the handler shape above.

Building the skill, step by step

A project skill lives at .claude/skills/<name>/SKILL.md. Personal skills that follow you across every repo live at ~/.claude/skills/<name>/SKILL.md. Since these conventions are project-specific, I put it in the repo so the whole team gets it.

Step 1: Create the skill folder

Terminal window
mkdir -p .claude/skills/scaffold-feature/references

The references subfolder will hold the literal code templates so the main SKILL.md stays readable.

Step 2: Write SKILL.md

The frontmatter is the contract. The skill’s folder name is what becomes the slash command (/scaffold-feature) - the name field is the display label shown in skill listings, so keep it matching the folder. The description is how Claude decides when the skill is relevant on its own. A vague description means the skill never fires when you want it. Be specific.

---
name: scaffold-feature
description: Use when adding a new feature to this .NET API - scaffolds a complete vertical slice (endpoint, command, handler, validator, response, EF configuration, integration test) following this project's conventions. Triggers on "scaffold feature", "add feature", "new endpoint", "create slice".
---
# /scaffold-feature - Scaffold a Vertical Slice
Generate a complete vertical slice for a new feature in this .NET 10 minimal API.
Follow the conventions below exactly. Do not invent alternative structures.
## Conventions (non-negotiable)
- .NET 10, minimal APIs only. Never generate controllers.
- Scalar for API docs, never Swashbuckle or Swagger.
- One feature = one folder under `src/Api/Features/<Entity>/<UseCase>/`.
- Each use case is self-contained: endpoint, command, handler, validator, response.
- Handlers are plain sealed classes invoked directly from the endpoint. No MediatR.
- Every command has a FluentValidation validator.
- Every endpoint and handler takes a CancellationToken and passes it down.
- EF Core 10. Configure entities with IEntityTypeConfiguration, never data annotations.
- Endpoints return Results<...> from TypedResults. Never raw IActionResult.
- Use Guid.CreateVersion7() for entity ids.
## Steps
1. If the entity and use case were not provided, ask for them.
2. Read references/slice-template.md for the exact file shapes.
3. Generate every file under src/Api/Features/<Entity>/<UseCase>/.
4. Register the endpoint by calling its Map* method in the feature's endpoint wiring.
5. Register the validator and handler in dependency injection.
6. Generate an integration test using WebApplicationFactory under tests/.
7. Run `dotnet build` and fix any errors before reporting done.
## Output
Report each file created and the route registered. Do not summarize the code back to me.

Two things make this work. First, the conventions are stated as rules, not suggestions - “never generate controllers” leaves no room for the average-tutorial default to creep in. Second, the steps include verification (dotnet build), so the skill does not hand back code that does not compile.

Step 3: Add the reference template

Claude follows prose well, but for code shape, showing beats telling. I put the literal templates in references/slice-template.md and point the skill at it. This is the same CreateProduct code from earlier, written with <Entity> and <UseCase> placeholders so Claude can substitute the real names. Keeping templates in a separate file means I can tighten the shape without touching the skill logic.

Step 4: Confirm discovery

Claude Code watches the skills directories and picks up a new skill folder within the current session - no restart needed when .claude/skills/ already existed. The one exception: if that directory did not exist when the session started, restart so Claude Code can begin watching it. Once loaded, the skill shows up as /scaffold-feature. If it does not appear, the usual cause is malformed YAML frontmatter - a missing field or a description spanning lines without correct indentation.

Don’t want to write it by hand? Let Claude build the skill

I wrote that SKILL.md by hand because I know exactly how I want the body to read. But you do not have to start from a blank file. Anthropic ships an official skill-creator skill that builds skills for you through a conversation, which is the more pragmatic starting point if this is your first skill. The claude-plugins-official marketplace is available in every Claude Code install, so you just add the plugin:

Terminal window
# Inside a Claude Code session
/plugin install skill-creator@claude-plugins-official

Then describe the skill you want in plain English. The trick is to feed it the same conventions you would have typed by hand - the more specific the prompt, the closer the first draft lands:

/skill-creator
Create a skill called `scaffold-feature` that generates a complete vertical
slice for a new feature in my .NET 10 minimal API.
Each slice is one folder under src/Api/Features/<Entity>/<UseCase>/ containing:
- endpoint
- command
- handler
- validator
- response
- an EF Core IEntityTypeConfiguration
- a WebApplicationFactory integration test
Conventions (non-negotiable):
- Minimal APIs only, never controllers
- Scalar for docs, not Swagger
- Plain sealed handlers, no MediatR
- Every method takes a CancellationToken and passes it down
Finish by running dotnet build and fixing any errors.
How you work with me:
- Before writing a single line, relentlessly ask me questions until we both
reach a shared understanding of every aspect of this skill.
- Never assume anything. If a convention, edge case, or default is ambiguous,
stop and ask rather than guessing.

It does not silently dump a file. It interviews you - edge cases, example inputs, trigger phrases - then drafts the frontmatter and body, and finishes by rewriting the description: field for better auto-invocation. The “never assume anything” line earns its place: it forces skill-creator to ask about the decisions it would otherwise guess at, and guessing is exactly where a generated skill drifts from your architecture. That description-tuning pass is the part I find hardest to get right by hand, so even when I write the body myself, I let skill-creator sharpen the trigger. You review the draft, save it to .claude/skills/scaffold-feature/, and the blank-page problem is gone.

Treat the generated skill as a first draft, not the final word: read every convention line, tighten anything vague, and add the reference template in the next step. The conventions in this article are exactly the kind of detail you paste into that prompt so the draft starts close to your architecture instead of the generic tutorial default. For a deeper walkthrough of the five-phase skill-creator flow, see my guide to Claude Code skills.

Running it: /scaffold-feature Products

With the skill in place, I run:

Terminal window
/scaffold-feature CreateProduct on the Products entity

Claude reads the skill and its reference template, then generates the full slice under src/Api/Features/Products/CreateProduct/: the minimal API endpoint (not a controller), the sealed handler with its CancellationToken, the validator, the records, and an IEntityTypeConfiguration. It adds the app.Map* line, registers the handler and validator in dependency injection, writes a WebApplicationFactory integration test, and runs dotnet build to confirm it compiles.

What used to be twenty minutes of typing plus twenty of correcting is now one command and a quick read of the diff. And the output is consistent - the tenth slice looks exactly like the first, which is what keeps a growing codebase navigable.

My take: when this is worth it, and when it is not

A scaffolding skill pays off the moment you have a repeated, stable structure and more than a handful of features to build. The whole value is consistency at volume. If every feature in your codebase looks the same, a skill encodes that sameness and protects it as the team grows.

It is not worth it for a throwaway prototype, a project whose conventions are still changing weekly, or a tiny API with three endpoints. A skill is a contract, and writing a contract around conventions you have not settled is just churn. Settle the architecture by hand first, build two or three slices the way you like them, and then extract the pattern into a skill. The skill should encode a decision you have already made, never make the decision for you.

Tightening the skill when it drifts

Even with a detailed skill, Claude occasionally drifts - it adds a try/catch you did not ask for, or names a file slightly differently. The fix is the same loop you would use for any instruction: find the gap, close it in the skill, and the next run inherits the correction. Three things kept my skill on the rails:

  1. State conventions as hard rules. “Never generate controllers” works. “Prefer minimal APIs” invites exceptions.
  2. Put exact code in the reference file. Drift drops sharply when Claude has a concrete shape to copy instead of inferring one from prose.
  3. Bake verification into the steps. Ending with dotnet build (and a test run) turns “looks right” into “is right” and catches the small mistakes before you see them.

A skill is a living artifact. Every time you correct the same thing twice, that correction belongs in the SKILL.md, not in your next prompt.

Read next

Swagger Alternatives in .NET - Why I Use Scalar

The API documentation convention the skill enforces, and why Scalar replaced Swashbuckle in my projects.

Skip the boilerplate: dotnet-claude-kit

Building one scaffolding skill is the right way to learn how skills work. But once you have done it, you realize a real .NET project needs a dozen more - one for migrations, one for code review, one for security scans, one per architecture you support. That is a lot of SKILL.md files to write and maintain. I went through that, and then I packaged the result so you do not have to.

dotnet-claude-kit is an open-source Claude Code plugin that ships a complete .NET 10 / C# 14 setup the moment you install it. The /scaffold command it ships is a production-grade version of the exact skill this article builds: it generates a full vertical slice - endpoint, handler, FluentValidation validator, Result pattern, OpenAPI metadata, pagination, and integration tests - across all four supported architectures, not just the one in this guide. Alongside it you get specialist subagents like ef-core-specialist and code-reviewer, and a set of Roslyn-powered MCP tools that let Claude query your solution semantically (find callers, detect circular dependencies, map test coverage) for a fraction of the tokens a full file read costs.

Two commands and you are running:

Terminal window
# Install the Roslyn MCP server
dotnet tool install -g CWM.RoslynNavigator
# Inside a Claude Code session
/plugin marketplace add codewithmukesh/dotnet-claude-kit
/plugin install dotnet-claude-kit
# Generate a project-tuned CLAUDE.md
/dotnet-init

The Kit is also a working reference for everything in this article: every skill in it follows the same convention-as-rules, reference-template, build-to-verify structure I used above. If you want to read well-built skills before writing your own, clone the repo and start with the scaffolding skill.

Free resource Companion download

.NET Claude Kit

Open-source Claude Code companion with 47 skills and 10 specialist agents

Where this goes next

That vertical slice is not just an API pattern - it is the building block of a module. In a modular monolith, each module is a collection of these slices behind a clear boundary, and a scaffolding skill is how you keep every module internally consistent as the system grows. Get the slice right first, and the larger architecture becomes a matter of grouping slices, not reinventing them - which is exactly the foundation a modular monolith is built on.

Key takeaways

  • A Claude Code skill is a SKILL.md file in .claude/skills/<name>/ that teaches Claude a repeatable workflow you trigger with a slash command.
  • Principles go in CLAUDE.md, procedures go in skills. Scaffolding a feature is a procedure, so it belongs in a skill that loads only when invoked.
  • State conventions as non-negotiable rules and back them with a literal reference template - that is what stops generic-tutorial defaults from creeping in.
  • Bake dotnet build into the skill’s steps so it never hands back code that does not compile.
  • Build the pattern by hand first, then extract it. A skill should encode an architecture you have already settled, not decide it for you.

Frequently asked questions

What is a Claude Code skill?

A Claude Code skill is a folder containing a SKILL.md file that teaches Claude a repeatable workflow. The file has YAML frontmatter with a name and description, followed by markdown instructions Claude follows when you invoke the skill with a slash command like /scaffold-feature.

Where do Claude Code skills live?

Project skills live at .claude/skills/<name>/SKILL.md inside the repository, so the whole team shares them. Personal skills live at ~/.claude/skills/<name>/SKILL.md and follow you across every project on your machine.

What is the difference between a CLAUDE.md file and a skill?

CLAUDE.md is always loaded into every prompt and is best for short global rules. A skill loads only when you invoke it, so it can be long and detailed. Use CLAUDE.md for principles that apply to every response and skills for procedures with steps and templates, like scaffolding a feature.

Can a Claude Code skill scaffold a full vertical slice in .NET?

Yes. By writing your conventions and a reference code template into the SKILL.md, the skill can generate a complete vertical slice - endpoint, command, handler, validator, response, EF Core configuration, and an integration test - and then run dotnet build to confirm it compiles.

How do I stop Claude Code from generating controllers instead of minimal APIs?

State it as a hard rule in the skill, such as 'minimal APIs only, never generate controllers,' and provide a literal minimal API endpoint template in a reference file. Phrasing it as a non-negotiable rule rather than a preference keeps the generic-tutorial default from creeping back in.

Does this work with .NET 10?

Yes. Everything here was tested on .NET 10.0.4 with EF Core 10. The generated slice uses minimal APIs, TypedResults, Guid.CreateVersion7 for ids, and IEntityTypeConfiguration for EF mapping, all current .NET 10 patterns.

How do I update a skill when Claude drifts from my conventions?

Treat the skill as a living file. When you correct the same thing twice, add that correction to the SKILL.md as a hard rule or tighten the reference template. The next invocation inherits the fix, so the skill improves over time instead of repeating the mistake.

Troubleshooting

  • The skill does not appear as a slash command. New skill folders are picked up within the session when .claude/skills/ already existed; if you just created that directory, restart so Claude Code starts watching it. The most common cause otherwise is malformed YAML frontmatter - confirm the description is present (it is what drives discovery) and the file is named exactly SKILL.md.
  • The skill fires at the wrong time, or never fires. The description drives relevance matching. If it is vague, Claude cannot tell when to use it. Add concrete trigger phrases like “scaffold feature” and “new endpoint.”
  • Generated code still drifts from your conventions. Move the exact shapes into references/slice-template.md and reference it from the steps. Prose conventions guide, but a literal template is what Claude copies.
  • The build breaks after scaffolding. Make sure the final step of the skill is dotnet build with an instruction to fix errors before reporting done. Without an explicit verification step, the skill will happily return code that does not compile.

Summary

Correcting the same generic output every session is a signal, not a chore. It means your conventions live only in your head, where Claude cannot reach them. Moving them into a custom skill turns Claude Code from a tool that writes average .NET code into one that writes your .NET code - a full vertical slice, consistent every time, verified by a build before it reaches you.

Build the pattern by hand, settle it, then encode it once. After that, /scaffold-feature does the typing and you do the thinking.

Happy Coding :)

View all articles

What's your take?

Push back, share a war story, or ask the obvious question someone else is wondering. I read every comment.

View on GitHub

Weekly .NET tips · free

Newsletter

stay ahead in .NET

One email every Tuesday at 7 PM IST. One topic, deep. The week's articles. No filler.

Tutorials Architecture DevOps AI
Join 9,735 developers · Delivered every Tuesday
Privacy notice 30s read

Cookies, but only the useful ones.

I use cookies to understand which articles get read and which CTAs actually work. No third-party advertising trackers, ever. Read the privacy policy →