| name | software-csharp-backend |
| description | Applies C# and .NET backend standards. Use when shaping API boundaries, data access, resilience, observability, or security defaults. |
| compatibility | Portable core. Works on Claude Code and Codex. |
| version | 1.1 |
| last_validated | 2026-07-11T00:00:00.000Z |
C# Backend Engineering
Quick Reference
| Decision | Default | Notes |
|---|
| Runtime | .NET 10 + C# 14 (LTS, GA Nov 2025, supported through ~Nov 2028) | .NET 8 LTS ends Nov 10, 2026 — verify current dates at dotnet support policy; treat continued .NET 8 targeting as a tracked migration, not a comfortable steady state |
| API style | Minimal API for new endpoints | Controllers for large CQRS surface with many filters/action results |
| Persistence (SQL, write-heavy) | EF Core | Use Dapper when query shapes are complex and hand-tuned SQL wins |
| Persistence (SQL, query-heavy) | Dapper | Pair with EF Core for write path if aggregates exist |
| Persistence (document) | MongoDB driver | Use when schema variability is genuine, not habitual |
| Background worker | IHostedService / BackgroundService | Add lease, graceful shutdown, and poison-message handling from day one |
| Resilience | Microsoft.Extensions.Resilience (Polly v8) | Standard pipeline: retry → circuit breaker → timeout |
| OpenAPI | Built-in ASP.NET Core OpenAPI (Microsoft.AspNetCore.OpenApi) | Do not add Swashbuckle unless repo already uses it |
Route other tasks:
- NUnit fixture design, WireMock/Testcontainers setup, or flake reduction →
$qa-testing-nunit
nuke/Build.cs, CI target sequencing, or artifact publication → $ops-nuke-cicd
- Legacy
ILogger or Serilog rewrite automation → $dev-structured-logs
When to Use This Skill
- Building or reviewing C# / .NET backend services
- Choosing between ASP.NET Core API styles (controller, minimal API, background worker)
- Implementing data access with EF Core, Dapper, or MongoDB
- Adding resilience, observability, or security baselines to .NET services
- Refactoring .NET backend code or detecting common anti-patterns
When NOT to Use This Skill
Workflow
- Classify the requested change (new feature, refactor, bug fix, review).
- Choose runtime shape before implementation details.
Load
references/scenario-guides.md and, for HTTP services, references/aspnet-core-api-patterns.md.
- Apply language and coding standards.
Load
references/csharp-language-practices.md and references/dotnet-coding-standards.md.
- Confirm architecture and boundaries before editing internals.
Load
references/backend-architecture-principles.md and references/modular-architecture-principles.md.
- Choose persistence and consistency strategy from query/write shape.
Load
references/data-access-patterns.md; if EF Core is selected, load references/efcore-persistence-patterns.md.
- Add resilience behavior for outbound I/O and long-running work.
Load
references/reliability-and-resilience.md and references/resilience-policy-defaults.md.
- Define tests by risk and boundary.
Load
references/testing-practices.md.
- Add logs, traces, metrics, health probes, and operability defaults.
Load
references/observability-standards.md, and for API/runtime deployment defaults load references/runtime-ops-checklist.md. If the service is distributed or cloud-native by design, also load references/scenario-guides.md for the Aspire-oriented profile.
- Validate auth, validation, and secrets handling.
Load
references/security-baseline.md.
- Run feedback loop validation for changed behavior.
For NUKE-based repositories, run
BuildAll, LocalUnitTest, ApiTest (when relevant), and TestAll; use $ops-nuke-cicd for pipeline-target edits.
- Run final review against anti-pattern checklist.
Load
references/code-review-checklist.md.
ASCII Flow
C# backend task
-> Identify API, worker, data access, or service boundary
-> Confirm .NET version, hosting model, DI, and persistence choices
-> Design validation, auth, errors, retries, and observability
-> Implement bounded slice with unit and integration tests
-> Check EF, async, cancellation, and deployment traps
-> Run focused verification and capture follow-up risks
Decision Tree
- If the issue is naming, nullability, exception usage, or async flow, read
references/csharp-language-practices.md.
- If the issue is project layout, DI, configuration, or layering, read
references/dotnet-coding-standards.md.
- If the issue is service boundaries or clean architecture drift, read
references/backend-architecture-principles.md.
- If the issue is module boundaries, composition hosts, or modular-monolith tradeoffs, read
references/modular-architecture-principles.md.
- If the issue is API style, middleware ordering, ProblemDetails, OpenAPI, request timeouts, rate limiting, health/readiness, or graceful shutdown, read
references/aspnet-core-api-patterns.md.
- If the issue is SQL/Mongo/EF access, pagination, transactions, idempotency, or N+1, read
references/data-access-patterns.md.
- If EF Core is selected for relational persistence, also read
references/efcore-persistence-patterns.md.
- If the issue is system profile specific (high-throughput API, event-driven worker, multi-tenant service, distributed/Aspire host), read
references/scenario-guides.md.
- If the issue is retry/timeout/circuit behavior, cancellation propagation, standard resilience handler behavior, or worker reliability, read
references/reliability-and-resilience.md and references/resilience-policy-defaults.md.
- If the issue is container/runtime readiness, startup config validation, HybridCache defaults, or deployment reliability gates, read
references/runtime-ops-checklist.md.
- If the issue is target framework constraints, package/API compatibility, multi-targeting, or migration between
.NET Framework/netstandard2.0 and modern .NET, read references/version-compatibility-notes.md.
- If the issue is flaky tests, fake time control, or weak coverage strategy, read
references/testing-practices.md.
- If the issue is logs/traces/metrics/health checks, read
references/observability-standards.md.
- If the issue is input validation, auth boundaries, secret handling, or secure defaults, read
references/security-baseline.md.
- If the task is reviewing a PR for backend quality risks, read
references/code-review-checklist.md.
- If the task is designing or fixing build-test pipeline loop behavior, use
$ops-nuke-cicd.
Do / Avoid
| Do | Avoid |
|---|
| Keep application services small and explicit about dependencies | Coupling domain logic directly to HTTP, DB driver types, or framework-specific classes |
| Return deterministic domain/application results for expected failures | Swallowing exceptions or replacing root causes with vague error messages |
Pass CancellationToken through every async layer and external call | Letting cancellation stop at the controller while downstream calls continue |
Model options with IOptions<T> validation; fail fast on invalid startup config | Binding configuration directly into services without options validation |
| Choose API style intentionally; keep middleware ordering explicit | Mixing minimal APIs, controllers, and bespoke endpoint frameworks without a clear error-shape policy |
| Use built-in ASP.NET Core OpenAPI and ProblemDetails before adding third-party wrappers | Adding Swashbuckle or MediatR when built-in plumbing is sufficient |
| Keep persistence choices aligned to use-case shape, not team habit | Using retries without timeout and idempotency guarantees |
| Make telemetry and security checks part of definition of done | Shipping endpoints without structured logs, traces, metrics, and health signals |
| Write tests at unit and integration seams separately | Mixing unit and integration concerns in the same test fixture |
| — | Using async void outside event handlers |
| — | Sharing one DbContext instance across concurrent operations/threads |
| — | Captive dependencies (singleton depending on scoped service) |
Known Traps
- Mixing minimal APIs, controllers, and bespoke endpoint frameworks without a clear contract strategy or error-shape policy.
- Letting cancellation stop at the controller boundary while downstream HTTP, EF Core, queue, or cache calls continue running.
- Treating EF Core defaults as safe under load: lazy loading, implicit tracking, and missing query shaping frequently create hidden cost.
- Using retries around non-idempotent handlers, transaction scopes, or third-party calls without dedupe or timeout coordination.
- Shipping background workers without graceful shutdown, lease or lock ownership, or poison-message handling.
- Binding configuration directly into services without options validation, startup failure checks, or explicit secret handling.
- Choosing Native AOT for a service that depends on EF Core, reflection-based JSON, or dynamic plugin loading — EF Core is not fully AOT-compatible today, and unbounded reflection breaks at publish/trim time. Verify each dependency's AOT/trim compatibility before committing, or keep the service on the standard runtime.
- Instantiating
new HttpClient() per call or per request instead of resolving it through IHttpClientFactory/AddHttpClient<T> — this exhausts sockets under load (new HttpClient() disposed per call) or goes DNS-stale (one long-lived static instance) if done by hand.
- Leaving Server GC (the ASP.NET Core default) unexamined in a memory-constrained or high-density container deployment — Server GC sizes heaps to the visible processor count, which can over-allocate memory on small/shared containers. Use
DOTNET_GCHeapHardLimit/DATAS (Dynamic Adaptation to Application Sizes — opt-in on .NET 8, default-on from .NET 9) or switch to Workstation GC for high-density, low-CPU hosting; keep Server GC for dedicated, CPU-bound API pods.
Common Anti-Patterns
- Building
clean architecture layers that are mostly pass-through wrappers with no boundary or policy value.
- Returning exceptions for expected domain outcomes instead of explicit result or error contracts.
- Sharing repository abstractions everywhere even when the real need is a focused query handler or aggregate persistence boundary.
- Hiding cross-cutting behavior inside ad hoc helpers instead of using middleware, filters, options, or resilience handlers deliberately.
- Using integration tests as the only safety net while unit seams, fake time, and deterministic failure cases stay untested.
Navigation
References
Templates
Related Skills
Freshness Protocol
When users ask version-sensitive questions about .NET, C#, or ASP.NET Core, verify current information before answering.
Trigger Conditions
- "What's the latest .NET LTS version?"
- "Should I use EF Core or Dapper?"
- "What's new in ASP.NET Core / C#?"
- "Is [pattern/library] still recommended for .NET?"
How to Freshness-Check
- Start from
data/sources.json (official docs, release notes, support policies).
- Run a targeted web search for the specific .NET component.
- Prefer official Microsoft docs over blogs for versions and support windows.
What to Report
- Current landscape: what is stable and widely used now
- Emerging trends: what is gaining traction (and why)
- Deprecated/declining: what is falling out of favor (and why)
- Recommendation: default choice + 1-2 alternatives, with trade-offs
Fact-Checking
- Known bugs, regressions, framework/compiler/runtime footguns, and version-specific crash or workaround guidance must be verified against current primary web sources before being treated as current fact.
- Use web search/web fetch to verify current external facts, versions, pricing, deadlines, regulations, or platform behavior before final answers.
- Prefer primary sources; report source links and dates for volatile information.
- If web access is unavailable, state the limitation and mark guidance as unverified.
Learnings Loop
Before applying this skill on a non-trivial task, read learnings.consolidated.md in this directory (and learnings.md if present).
After applying it, if you encountered a pattern worth remembering, a mistake worth preventing, or a domain fact that surprised you, append one dated bullet to learnings.md via agents-skills-feedback-loop/scripts/append_learning.py. Do not modify SKILL.md itself.