In this post we will see what a Claude Skill is, how to create one and how to use it.
A Skill in Claude is a specialized capability that enhances Claude’s performance in specific domains by providing detailed instructions, best practices, and context-specific guidelines. Think of it as a expert-level instruction manual that Claude consults before tackling domain-specific tasks.
HOW SKILLS WORK
Skills are stored as markdown (.md) files in specific directories that Claude can access.
When we ask Claude to perform a task that triggers a skill, Claude:
- Detects the relevant skill based on our request
- Reads the skill file using the view tool
- Applies the guidelines from the skill to our task
- Produces output following the best practices defined in the skill
WHAT IS INSIDE A SKILL?
A well-designed skill contains:
- Trigger conditions (when to use the skill)
- Best practices accumulated through trial and error
- Domain-specific workflows and methodologies
- Quality standards and patterns to follow
- Common pitfalls to avoid
- Examples and templates
WHY CREATE A SKILL?
While Claude comes with built-in skills for common tasks (documents, spreadsheets, presentations), custom skills unlock powerful capabilities:
[Codify our team’s standards]
Stop copy and past our coding conventions into every conversation. Embed them once in a skill, and Claude will follow them automatically.
[Ensure consistency]
Every team member gets the same high-quality output following the same patterns and practices.
[Reduce repetition]
Never explain “use CQRS pattern” or “follow SOLID principles” again. The skill handles it.
[Improve code quality]
Leverage accumulated knowledge and proven patterns instead of ‘ad hoc’ responses.
[Specialize Claude]
Transform Claude into an expert in our specific domain whether that’s C# development, React architecture, or DevOps automation.
The Anatomy of a Skill File
A skill is a markdown file with a specific structure:
# Skill Name
## Overview
Brief description of what this skill does and when to use it
## When to Use This Skill
Clear triggers and use cases that activate the skill
## Core Principles
Fundamental guidelines and philosophies to follow
## Step-by-Step Workflow
Detailed instructions for completing tasks
## Best Practices
Proven approaches and quality standards
## Common Pitfalls
Mistakes to avoid with examples
## Examples
Concrete examples demonstrating the skill in action
Let’s build a production-ready skill for C# development that embodies modern best practices. Our goal is to create a skill that ensures:
- Clean, maintainable code following SOLID principles
- Modern C# 12+ features and patterns
- Clean Architecture and CQRS implementation
- Entity Framework best practices
- Proper API design patterns
[csharp_ckill.md]
---
name: csharp-development-excellence
description: Guides creation of high-quality, maintainable C# code following modern best practices, SOLID principles, and proven design patterns
license:
---
# C# Development Excellence Skill
## Overview
This skill guides the creation of high-quality, maintainable C# code following modern best practices, SOLID principles, and proven design patterns. Use this skill for any C# development task including API development, business logic, data access, and application architecture.
## When to Use This Skill
- Writing C# classes, methods, or complete applications
- Implementing business logic or domain models
- Creating APIs (REST, gRPC, minimal APIs)
- Working with Entity Framework or database access
- Implementing design patterns
- Refactoring existing C# code
- Code reviews or architectural decisions
## Core Principles
### 1. SOLID Principles (Non-Negotiable)
- **Single Responsibility**: Each class/method does ONE thing well
- **Open/Closed**: Open for extension, closed for modification
- **Liskov Substitution**: Derived classes must be substitutable for base classes
- **Interface Segregation**: Many specific interfaces > one general interface
- **Dependency Inversion**: Depend on abstractions, not concretions
### 2. Clean Code Standards
- **Meaningful names**: `CalculateMonthlyRevenue()` not `Calc()` or `DoStuff()`
- **Small methods**: 5-15 lines ideally, max 30 lines
- **Shallow nesting**: Max 2-3 levels of indentation
- **Fail fast**: Validate inputs early, throw exceptions clearly
- **No magic numbers**: Use named constants or enums
### 3. Modern C# Features (Embrace C# 12+)
- Record types for DTOs and value objects
- Pattern matching and switch expressions
- Nullable reference types (always enabled)
- Primary constructors
- Collection expressions
- Required members
- File-scoped namespaces
- Global usings
## Architecture Patterns
### Clean Architecture Structure
```
YourProject/
├── Domain/ # Enterprise business rules
│ ├── Entities/ # Core business objects
│ ├── ValueObjects/ # Immutable value objects
│ ├── Enums/
│ └── Exceptions/ # Domain-specific exceptions
├── Application/ # Application business rules
│ ├── Features/ # Feature-based organization
│ │ ├── Users/
│ │ │ ├── Commands/
│ │ │ │ └── CreateUser/
│ │ │ │ ├── CreateUserCommand.cs
│ │ │ │ ├── CreateUserCommandHandler.cs
│ │ │ │ └── CreateUserCommandValidator.cs
│ │ │ └── Queries/
│ │ └── Orders/
│ ├── Interfaces/ # Abstractions for infrastructure
│ └── DTOs/
├── Infrastructure/ # External concerns
│ ├── Persistence/ # EF Core, repositories
│ ├── Identity/ # Authentication/Authorization
│ └── Services/ # External service integrations
└── API/ # Presentation layer
└── Controllers/ # Or Endpoints for minimal APIs
```
### CQRS with MediatR
**Commands** (Write operations):
```csharp
public record CreateUserCommand(
string Email,
string FirstName,
string LastName
) : IRequest<Result<Guid>>;
public class CreateUserCommandHandler
: IRequestHandler<CreateUserCommand, Result<Guid>>
{
private readonly IApplicationDbContext _context;
private readonly IPasswordHasher _passwordHasher;
public CreateUserCommandHandler(
IApplicationDbContext context,
IPasswordHasher passwordHasher)
{
_context = context;
_passwordHasher = passwordHasher;
}
public async Task<Result<Guid>> Handle(
CreateUserCommand request,
CancellationToken cancellationToken)
{
// Validate business rules
if (await _context.Users
.AnyAsync(u => u.Email == request.Email, cancellationToken))
{
return Result<Guid>.Failure("User with this email already exists");
}
// Create entity
var user = User.Create(
request.Email,
request.FirstName,
request.LastName);
// Persist
_context.Users.Add(user);
await _context.SaveChangesAsync(cancellationToken);
return Result<Guid>.Success(user.Id);
}
}
```
**Queries** (Read operations):
```csharp
public record GetUserByIdQuery(Guid UserId) : IRequest<Result<UserDto>>;
public class GetUserByIdQueryHandler
: IRequestHandler<GetUserByIdQuery, Result<UserDto>>
{
private readonly IApplicationDbContext _context;
public GetUserByIdQueryHandler(IApplicationDbContext context)
{
_context = context;
}
public async Task<Result<UserDto>> Handle(
GetUserByIdQuery request,
CancellationToken cancellationToken)
{
var user = await _context.Users
.AsNoTracking()
.Where(u => u.Id == request.UserId)
.Select(u => new UserDto(
u.Id,
u.Email,
u.FirstName,
u.LastName,
u.CreatedAt))
.FirstOrDefaultAsync(cancellationToken);
return user is null
? Result<UserDto>.Failure("User not found")
: Result<UserDto>.Success(user);
}
}
```
## Essential Design Patterns
### 1. Repository Pattern (with Specification)
```csharp
public interface IRepository<T> where T : class
{
Task<T?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<List<T>> ListAsync(CancellationToken cancellationToken = default);
Task<List<T>> ListAsync(ISpecification<T> spec, CancellationToken cancellationToken = default);
Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
}
// Specification pattern for complex queries
public interface ISpecification<T>
{
Expression<Func<T, bool>>? Criteria { get; }
List<Expression<Func<T, object>>> Includes { get; }
Expression<Func<T, object>>? OrderBy { get; }
Expression<Func<T, object>>? OrderByDescending { get; }
}
```
### 2. Result Pattern (No Exceptions for Business Logic)
```csharp
public class Result<T>
{
public bool IsSuccess { get; }
public bool IsFailure => !IsSuccess;
public T? Value { get; }
public string Error { get; }
private Result(bool isSuccess, T? value, string error)
{
IsSuccess = isSuccess;
Value = value;
Error = error;
}
public static Result<T> Success(T value)
=> new(true, value, string.Empty);
public static Result<T> Failure(string error)
=> new(false, default, error);
public TResult Match<TResult>(
Func<T, TResult> onSuccess,
Func<string, TResult> onFailure)
=> IsSuccess ? onSuccess(Value!) : onFailure(Error);
}
```
### 3. Factory Pattern for Complex Object Creation
```csharp
public static class User
{
public static User Create(
string email,
string firstName,
string lastName)
{
// Validation
if (string.IsNullOrWhiteSpace(email))
throw new ArgumentException("Email is required", nameof(email));
if (!email.Contains('@'))
throw new ArgumentException("Invalid email format", nameof(email));
// Business rules
var user = new User
{
Id = Guid.NewGuid(),
Email = email.ToLowerInvariant(),
FirstName = firstName.Trim(),
LastName = lastName.Trim(),
CreatedAt = DateTime.UtcNow,
IsActive = true
};
// Domain events could be added here
// user.AddDomainEvent(new UserCreatedEvent(user));
return user;
}
}
```
## Entity Framework Best Practices
### 1. Configuration Over Attributes
```csharp
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.ToTable("Users");
builder.HasKey(u => u.Id);
builder.Property(u => u.Email)
.IsRequired()
.HasMaxLength(256);
builder.HasIndex(u => u.Email)
.IsUnique();
builder.Property(u => u.FirstName)
.IsRequired()
.HasMaxLength(100);
builder.Property(u => u.LastName)
.IsRequired()
.HasMaxLength(100);
// Value object configuration
builder.OwnsOne(u => u.Address, address =>
{
address.Property(a => a.Street).HasMaxLength(200);
address.Property(a => a.City).HasMaxLength(100);
address.Property(a => a.PostalCode).HasMaxLength(20);
});
}
}
```
### 2. Optimized Queries
```csharp
// BAD: N+1 query problem
var users = await _context.Users.ToListAsync();
foreach (var user in users)
{
var orders = user.Orders.ToList(); // Separate query per user!
}
// GOOD: Eager loading
var users = await _context.Users
.Include(u => u.Orders)
.AsNoTracking() // For read-only scenarios
.ToListAsync();
// GOOD: Projection to DTO (even better)
var userDtos = await _context.Users
.Select(u => new UserWithOrdersDto(
u.Id,
u.Email,
u.Orders.Select(o => new OrderDto(o.Id, o.Total)).ToList()))
.ToListAsync();
```
### 3. Transaction Management
```csharp
public async Task<Result> ProcessOrderAsync(CreateOrderCommand command)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
// Create order
var order = Order.Create(command.UserId, command.Items);
_context.Orders.Add(order);
await _context.SaveChangesAsync();
// Update inventory
foreach (var item in command.Items)
{
await _inventoryService.ReserveStockAsync(
item.ProductId,
item.Quantity);
}
await transaction.CommitAsync();
return Result.Success();
}
catch (Exception ex)
{
await transaction.RollbackAsync();
return Result.Failure($"Order processing failed: {ex.Message}");
}
}
```
## API Development Patterns
### 1. Minimal APIs (Modern Approach)
```csharp
public static class UserEndpoints
{
public static void MapUserEndpoints(this IEndpointRouteBuilder app)
{
var group = app.MapGroup("/api/users")
.WithTags("Users")
.RequireAuthorization();
group.MapPost("/", CreateUserAsync)
.WithName("CreateUser")
.Produces<UserDto>(StatusCodes.Status201Created)
.ProducesValidationProblem();
group.MapGet("/{id:guid}", GetUserByIdAsync)
.WithName("GetUserById")
.Produces<UserDto>()
.Produces(StatusCodes.Status404NotFound);
}
private static async Task<IResult> CreateUserAsync(
CreateUserCommand command,
ISender sender)
{
var result = await sender.Send(command);
return result.Match(
onSuccess: userId => Results.CreatedAtRoute(
"GetUserById",
new { id = userId },
userId),
onFailure: error => Results.BadRequest(new { error }));
}
private static async Task<IResult> GetUserByIdAsync(
Guid id,
ISender sender)
{
var query = new GetUserByIdQuery(id);
var result = await sender.Send(query);
return result.Match(
onSuccess: user => Results.Ok(user),
onFailure: error => Results.NotFound(new { error }));
}
}
```
### 2. Versioning and Swagger
```csharp
// Program.cs
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Restaurant Management API",
Version = "v1",
Description = "Comprehensive restaurant management system"
});
// JWT Authentication
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
});
```
## Code Quality Checklist
Before considering code complete, verify:
### Naming Conventions
- [ ] Classes: `PascalCase` (e.g., `OrderProcessor`)
- [ ] Interfaces: `IPascalCase` (e.g., `IOrderRepository`)
- [ ] Methods: `PascalCase` (e.g., `CalculateTotal()`)
- [ ] Variables/fields: `camelCase` (e.g., `totalAmount`)
- [ ] Constants: `PascalCase` (e.g., `MaxRetryAttempts`)
- [ ] Private fields: `_camelCase` (e.g., `_dbContext`)
### Structure
- [ ] Each class has single responsibility
- [ ] Methods are small (< 30 lines)
- [ ] No deep nesting (< 3 levels)
- [ ] Dependencies injected via constructor
- [ ] Async methods end with `Async` suffix
### Safety
- [ ] Nullable reference types enabled
- [ ] All inputs validated
- [ ] Exceptions are specific and meaningful
- [ ] Resources disposed properly (using/IDisposable)
- [ ] CancellationTokens used in async methods
### Testing Readiness
- [ ] Interfaces defined for all dependencies
- [ ] Business logic separated from infrastructure
- [ ] Pure functions where possible
- [ ] Side effects isolated and explicit
## Common Pitfalls to Avoid
### Don't: God Classes
```csharp
// 3000 lines, does everything
public class OrderService
{
public void CreateOrder() { }
public void ValidateOrder() { }
public void ProcessPayment() { }
public void SendEmail() { }
public void UpdateInventory() { }
public void GenerateInvoice() { }
// ... 50 more methods
}
```
### Do: Single Responsibility
```csharp
public class OrderCreationService { }
public class OrderValidator { }
public class PaymentProcessor { }
public class EmailNotificationService { }
public class InventoryManager { }
public class InvoiceGenerator { }
```
### Don't: Primitive Obsession
```csharp
public class Order
{
public string Email { get; set; } // What validates this?
public decimal Price { get; set; } // What currency?
public string Status { get; set; } // Typos possible
}
```
### Do: Value Objects and Enums
```csharp
public class Order
{
public Email Email { get; set; }
public Money Price { get; set; }
public OrderStatus Status { get; set; }
}
public record Email
{
public string Value { get; }
private Email(string value) => Value = value;
public static Result<Email> Create(string value)
{
if (string.IsNullOrWhiteSpace(value))
return Result<Email>.Failure("Email is required");
if (!value.Contains('@'))
return Result<Email>.Failure("Invalid email format");
return Result<Email>.Success(new Email(value.ToLowerInvariant()));
}
}
public enum OrderStatus
{
Pending = 0,
Confirmed = 1,
Processing = 2,
Shipped = 3,
Delivered = 4,
Cancelled = 5
}
```
### Don't: Leaky Abstractions
```csharp
// Repository returns EF entities directly
public interface IOrderRepository
{
Task<DbSet<Order>> GetOrdersAsync(); // Exposes EF Core!
}
```
### Do: Clean Abstractions
```csharp
public interface IOrderRepository
{
Task<List<Order>> GetOrdersAsync(OrderFilter filter);
Task<Order?> GetByIdAsync(Guid id);
}
```
## Performance Optimization
### 1. Async/Await Best Practices
```csharp
// GOOD: ValueTask for frequently synchronous results
public async ValueTask<User?> GetCachedUserAsync(Guid id)
{
if (_cache.TryGetValue(id, out var user))
return user; // Synchronous path, no Task allocation
return await _repository.GetByIdAsync(id);
}
// GOOD: ConfigureAwait(false) in library code
public async Task<Order> GetOrderAsync(Guid id)
{
var order = await _repository
.GetByIdAsync(id)
.ConfigureAwait(false); // Don't capture context
return order;
}
```
### 2. String Handling
```csharp
// BAD: String concatenation in loops
string result = "";
foreach (var item in items)
{
result += item.ToString(); // Creates new string each iteration!
}
// GOOD: StringBuilder
var sb = new StringBuilder();
foreach (var item in items)
{
sb.Append(item.ToString());
}
string result = sb.ToString();
// BETTER: String interpolation or collection expression
string result = string.Join(", ", items.Select(i => i.ToString()));
```
### 3. LINQ Performance
```csharp
// BAD: Multiple enumerations
var list = GetExpensiveData();
var count = list.Count(); // Enumerates once
var first = list.First(); // Enumerates again!
// GOOD: Materialize once
var list = GetExpensiveData().ToList(); // Single enumeration
var count = list.Count; // O(1) property access
var first = list[0];
```
## Testing Patterns
### Unit Test Structure (AAA Pattern)
```csharp
public class CreateUserCommandHandlerTests
{
[Fact]
public async Task Handle_WithValidCommand_ReturnsSuccessWithUserId()
{
// Arrange
var command = new CreateUserCommand(
"test@example.com",
"John",
"Doe");
var mockContext = new Mock<IApplicationDbContext>();
var mockPasswordHasher = new Mock<IPasswordHasher>();
var handler = new CreateUserCommandHandler(
mockContext.Object,
mockPasswordHasher.Object);
// Act
var result = await handler.Handle(command, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeTrue();
result.Value.Should().NotBeEmpty();
mockContext.Verify(
x => x.SaveChangesAsync(It.IsAny<CancellationToken>()),
Times.Once);
}
}
```
## Final Quality Standards
Every piece of C# code should be:
1. **Readable**: A junior developer should understand it in < 2 minutes
2. **Maintainable**: Changes require minimal code modifications
3. **Testable**: Can be unit tested without complex setup
4. **Performant**: No obvious performance issues
5. **Secure**: No SQL injection, XSS, or security vulnerabilities
6. **Documented**: Complex logic has XML comments
## Resources and References
- **Official C# Docs**: https://learn.microsoft.com/en-us/dotnet/csharp/
- **Clean Architecture**: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
- **Design Patterns**: "Design Patterns: Elements of Reusable Object-Oriented Software" (Gang of Four)
- **EF Core Best Practices**: https://learn.microsoft.com/en-us/ef/core/performance/
---
**Version**: 1.0
**Last Updated**: February 2026
**Maintained By**: Damiano
Finally, we have to upload the file in Claude:






Now that the skill is installed, let’s verify it’s actually being used by Claude.
We’ll ask Claude to create a repository interface and implementation for managing Orders, including support for filtering by date range and customer ID.
What We Should See:
Before Claude generates any code, we’ll notice it automatically reads the C# development skill.
