agentleFS
Sign inSign up

dotnet-claude-kit / rules

codewithmukesh/dotnet-claude-kit/.cursor/rules/dotnet-rules.md

Auto-generated from the .claude/rules/ directory in dotnet-claude-kit. Do not edit this file directly. Update the individual rule files in .claude/rules/ and regenerate.

Cursor rule691 starsChanged 2 months ago
  • Reads credentials
# .NET Development Rules (Cursor IDE)

> **Auto-generated** from the `.claude/rules/` directory in dotnet-claude-kit.
> Do not edit this file directly. Update the individual rule files in `.claude/rules/` and regenerate.

---

## 1. C# Coding Style

### File Organization
- File-scoped namespaces always. Block-scoped namespaces waste indentation for zero benefit.
- One type per file. File name must match the type name exactly.
- Order members: constants, fields, constructors, properties, public methods, private methods.

### Type Declarations
- Primary constructors for DI injection. Eliminates boilerplate field assignments.
```csharp
// DO
public sealed class OrderService(IDbContext db, TimeProvider clock) { }

// DON'T
public class OrderService
{
    private readonly IDbContext _db;
    public OrderService(IDbContext db) { _db = db; }
}
```

- Records for DTOs and value objects. Immutability, value equality, and `with` expressions for free.
```csharp
public sealed record CreateOrderRequest(string ProductId, int Quantity);
public sealed record Money(decimal Amount, string Currency);
```

- `sealed` on classes not designed for inheritance. Enables JIT devirtualization and communicates intent.
- `internal` by default, `public` only when needed. Minimize the public API surface.

### Expressions and Patterns
- Collection expressions over constructor calls.
```csharp
List<int> ids = [1, 2, 3];  // DO
var ids = new List<int> { 1, 2, 3 };  // DON'T
```

- Pattern matching over if-else chains. Switch expressions and `is` patterns are more readable and exhaustiveness-checked.
- `var` for obvious types, explicit types when clarity matters.
- Async suffix on all async methods (`GetOrderAsync`, not `GetOrder`).
- PascalCase for public members, types, namespaces, methods. camelCase for local variables and parameters.
- No `_` prefix on private fields when using primary constructors.

---

## 2. Architecture

- **Never assume an architecture** -- use the architecture-advisor skill. Ask about team size, domain complexity, and deployment model before recommending Clean Architecture, VSA, DDD, or Modular Monolith.
- **No repository pattern over EF Core.** `DbContext` is already a Unit of Work + Repository. Wrapping it adds indirection with no value.
```csharp
// DO -- inject DbContext directly
public sealed class OrderService(AppDbContext db)
{
    public Task<Order?> GetAsync(Guid id, CancellationToken ct) =>
        db.Orders.FindAsync([id], ct).AsTask();
}

// DON'T -- generic repository wrapping EF
public interface IRepository<T> { Task<T?> GetByIdAsync(Guid id); }
```

- **Every endpoint group gets its own file implementing `IEndpointGroup`.** Never define endpoints in Program.cs. Use `app.MapEndpoints()` for auto-discovery. Program.cs never changes when adding endpoints.
- **Feature folders over layer folders.** Vertical slices keep related code together, reducing cross-cutting file changes.
- **Dependency direction is inward.** Domain depends on nothing. Application depends on Domain. Infrastructure depends on Application. Presentation depends on Application. Never reverse.
- **Module boundaries enforced through project references.** Use integration events or shared contracts for cross-module communication.
- **Shared kernel contains only contracts, never business logic.** Interfaces, DTOs, and integration events only.

---

## 3. Security

- **Never hardcode secrets in source code.** Use `dotnet user-secrets` for local dev, Azure Key Vault or env vars for deployed environments.
- **Never commit `.env`, `appsettings.Development.json` with real credentials, or `credentials.json`.** Add to `.gitignore`.
- **Validate all external input at system boundaries.** Use FluentValidation or built-in validation attributes.
- **Parameterized queries always.** Never string-concatenate SQL. EF Core interpolation is parameterized and safe.
```csharp
// DO (safe)
db.Database.SqlQuery<Order>($"SELECT * FROM Orders WHERE Id = {id}");

// DON'T (SQL injection)
db.Database.ExecuteSqlRaw("SELECT * FROM Orders WHERE Id = '" + id + "'");
```

- **Explicit `[Authorize]` or `[AllowAnonymous]` on every endpoint.** Ambiguous auth is a security hole.
- **HTTPS everywhere.** Enforce HSTS in production, redirect HTTP to HTTPS.
- **Use Data Protection API for encrypting data at rest.** Never roll your own encryption.
- **CORS: explicit origins only, never wildcard in production.**
- **Do not log PII at Information level or below.** Production log aggregators are broadly accessible.

---

## 4. Testing

- **Integration tests first.** `WebApplicationFactory` + Testcontainers to test real HTTP pipelines against real databases.
- **No in-memory database for testing.** `UseInMemoryDatabase` has different behavior from real providers. Use Testcontainers.
```csharp
// DO -- real PostgreSQL via Testcontainers
public sealed class DatabaseFixture : IAsyncLifetime
{
    private readonly PostgreSqlContainer _container = new PostgreSqlBuilder().Build();
    public string ConnectionString => _container.GetConnectionString();
    public Task InitializeAsync() => _container.StartAsync();
    public Task DisposeAsync() => _container.DisposeAsync().AsTask();
}
```

- **AAA pattern** with clear separation: Arrange, Act, Assert separated by blank lines.
- **One assertion concept per test.** Separate behaviors need separate tests.
- **Test naming: `MethodName_Scenario_ExpectedResult`.** Clear, searchable, self-documenting.
- **Shared fixtures for expensive setup.** Database containers, HTTP servers shared via `IClassFixture<T>` or `ICollectionFixture<T>`.
- **No mocking frameworks for things you own.** Use real or test implementations. Reserve mocks for third-party boundaries.
- **Test behavior, not implementation details.** Assert on observable outcomes (HTTP response, database state, published event).

---

## 5. Performance

- **Always propagate `CancellationToken` through the call chain.** Dropped tokens mean cancelled requests continue burning resources.
```csharp
// DO
public Task<Order?> GetOrderAsync(Guid id, CancellationToken ct) =>
    db.Orders.FirstOrDefaultAsync(o => o.Id == id, ct);

// DON'T -- token silently ignored
public Task<Order?> GetOrderAsync(Guid id, CancellationToken ct) =>
    db.Orders.FirstOrDefaultAsync(o => o.Id == id);
```

- **Async all the way.** No `.Result` or `.Wait()`. Synchronously blocking on async code causes thread pool starvation and deadlocks.
- **`TimeProvider` over `DateTime.Now` / `DateTime.UtcNow`.** Injectable and testable.
```csharp
// DO
public sealed class AuditService(TimeProvider clock)
{
    public DateTimeOffset Now => clock.GetUtcNow();
}
```

- **`IHttpClientFactory` over `new HttpClient()`.** Direct instantiation causes socket exhaustion under load.
- **`HybridCache` over `IMemoryCache` / `IDistributedCache`.** Stampede protection, L1+L2, tag-based invalidation out of the box.
- **Compiled queries for hot-path EF Core queries.** Skips expression tree translation on every call.
- **`ArrayPool<T>` / `MemoryPool<T>` for buffer-heavy operations.** Avoids GC pressure from frequent large allocations.
- **`ValueTask<T>` over `Task<T>` for high-throughput paths that often complete synchronously.**

---

## 6. Error Handling

- **Result pattern for expected failures** (not found, validation, conflict). Exceptions are expensive and hide control flow.
- **Do not use try-catch for flow control.** If you can predict the failure, return a Result.
- **Typed error codes** in your Result type: `NotFound`, `Validation`, `Conflict`, `Unauthorized`.
- **ProblemDetails (RFC 9457) for all HTTP error responses.** Industry standard format.
- **No bare `Exception` catch** unless at the application boundary (middleware/top-level handler).
- **`IExceptionHandler` middleware** for unhandled exceptions. Centralizes error logging and ProblemDetails conversion.
- **No catch-and-rethrow without adding context.** Either handle it or let it propagate.
- **Validate at system boundaries only.** Internal code should trust validated data.

| Scenario | Approach |
|---|---|
| User input invalid | Result with Validation error |
| Entity not found | Result with NotFound error |
| Unhandled crash | IExceptionHandler middleware |
| External API failure | Catch specific exception, return Result |
| Concurrent update | Result with Conflict error |

---

## 7. Git Workflow

- **Conventional commit prefixes:** `feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `chore:`.
- **Commit body explains "why", not "what".** The diff shows what changed.
- **No vague messages** like "fix bug" or "update code".
- **Branch naming:** `feature/`, `fix/`, `refactor/` prefixes.
- **Atomic commits.** One logical change per commit. Feature and its tests belong together.
- **Never force-push to main or master.**
- **Never skip pre-commit hooks** with `--no-verify`.
- **Run verification before creating a PR.** `/verify` or `dotnet build` + `dotnet test`.
- **Keep PRs focused on a single concern.** Split large changes into stacked PRs.

---

## 8. Agent & Tool Usage

- **MCP tools before file reading.** Use `find_symbol`, `find_references`, `get_public_api`, `get_type_hierarchy` before reading source files.
- **`get_project_graph` before structural changes.** Understand the dependency tree first.
- **`get_diagnostics` after modifications.** Faster and more structured than parsing `dotnet build` output.
- **Do not read entire files to find a single method.** Use `find_symbol` first.
- **Subagents for parallel research and independent tasks.** One task per subagent for focused execution.
- **Route to specialist agents** for domain-specific work (see AGENTS.md routing table).
- **Sonnet for routine tasks** (formatting, simple refactors, test generation, boilerplate).
- **Opus for complex architecture decisions** and multi-system analysis.
- **Fable for the highest-stakes work** (greenfield architecture for long-lived systems, problems that resisted Opus).
- **Use model aliases** (`fable`, `opus`, `sonnet`, `haiku`), never pinned version IDs — aliases track the latest version.
- **Load relevant skills before starting work.** Check AGENTS.md skill maps.

---

## 9. Hooks

- **Auto-accept post-edit format hooks.** They enforce consistent style automatically.
- **Do not revert or undo** formatting changes applied by hooks.
- **Never skip pre-commit hooks.** Investigate and fix the root cause when a hook blocks a commit.
- **Pipe test output through `hooks/post-test-analyze.sh`** when running test workflows, and act on its summary.
- **Do not interfere with hook configuration.** See `hooks/README.md` for which scripts run automatically vs manually.
- **Wait for post-scaffold-restore** to complete after `.csproj` changes before building.

| Hook | Correct Response |
|---|---|
| Post-edit format | Accept the changes |
| Pre-commit failure | Fix the issue, commit again |
| Post-test-analyze | Read and act on insights |
| Post-scaffold-restore | Wait for completion before building |

---

## 10. Package Management

- **Never hardcode NuGet package versions from memory.** Training data contains outdated 8.x/9.x versions.
- **Run `dotnet add package <name>` without `--version`** to pull the latest stable release automatically.
- **Microsoft.* packages for .NET 10 must use 10.x versions** (EF Core, Extensions, ASP.NET Core).
- **Use `Directory.Packages.props`** for multi-project solutions to centralize version pins.
- **Never downgrade** a package already in the project unless explicitly asked.
- **Prefer release versions** over preview/RC unless the project targets preview features.
- If unsure about the latest version, suggest `dotnet package search <name>` or checking NuGet.org.

Discussion

Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.

Posts are public.Sign in to post

No one has posted yet. Be the first.