.NET Coding Standards
Comprehensive coding standards for .NET projects following Clean Architecture principles.
Customization
This skill uses placeholders for company-specific values. Replace these throughout:
| Placeholder | Description | Example |
|---|
{CompanyName} | Your company/organization name | Acme, Contoso |
{ProjectName} | Your project name | OrderApi, PaymentService |
{email} | Support/contact email | [email protected] |
{domain} | Your company domain | example.com |
π New Developer Quick Start
- Clone the repo and run
dotnet restore
- Read the project's README.md (every project has one)
- Check you have .NET 10 SDK installed
- Run
dotnet build β must complete with zero warnings
- Run
dotnet test β must have 80%+ coverage
- Before committing: Run the PR Review Checklist
First task? Start with the New Solution Checklist.
Quick Reference
| Requirement | Rule |
|---|
| Copyright | Every .cs file starts with the copyright header |
| Namespaces | File-scoped: {CompanyName}.[ProjectName].[Layer].[Feature] |
| Documentation | All public members require XML docs |
| Null safety | ?? throw new ArgumentNullException(nameof(param)) in constructors |
| Field access | Always use this. prefix for instance members |
| Build rule | Zero warnings allowed (TreatWarningsAsErrors=true) |
| Framework | .NET 10, nullable reference types enabled |
| StyleCop | Centralized config, NO pragma suppressions allowed |
| C# version | C# 13 features enabled (with .NET 10) |
| Testing | All functional classes must have unit tests |
| Coverage | Minimum 80% code coverage required |
| README | Every solution must have comprehensive README.md |
| ConfigureAwait | Use ConfigureAwait(false) in library/infrastructure code |
| Logging | Serilog in all host processes (overrides Microsoft logging) |
| Exceptions | Sentry for exception tracking in all host processes |
β Critical Rules (Non-Negotiable)
Never Use Pragma Suppressions
// β FORBIDDEN
#pragma warning disable SA1600
public class MyClass { }
#pragma warning restore SA1600
// β
CORRECT - Fix the actual issue
/// <summary>
/// Provides functionality for processing data.
/// </summary>
public class MyClass { }
Never Skip Unit Tests for Functional Classes
Every class with business logic MUST have corresponding unit tests with 80%+ coverage.
Never Use Microsoft.Extensions.Logging Config in Hosts
Serilog configuration takes precedence. See Serilog Standards.
π Detailed Documentation
By Project Type
Standards & Patterns
Templates (Copy-Paste Ready)
Checklists
IDE Snippets
Common Mistakes to Avoid
Logging
| Mistake | Why It's Wrong | Correct Approach |
|---|
logger.LogInformation($"User {userId}") | Loses structure, poor performance | logger.LogInformation("User {UserId}", userId) |
Using ILogger directly in class library | Couples to implementation | Use ILogger<T> from abstractions |
catch (Exception) { } | Swallows errors silently | Log and rethrow, or handle specifically |
| Microsoft logging config in appsettings | Ignored when Serilog is used | Use Serilog section only |
Async/Await
| Mistake | Why It's Wrong | Correct Approach |
|---|
Task.Run(() => AsyncMethod()) | Wastes thread pool thread | await AsyncMethod() |
.Result or .Wait() | Deadlock risk | Always await |
Missing ConfigureAwait(false) in library | Captures unnecessary sync context | Add ConfigureAwait(false) |
ConfigureAwait(false) in controller | Loses HttpContext | Don't use in controllers |
Missing Async suffix | Naming inconsistency | All async methods end with Async |
Testing
| Mistake | Why It's Wrong | Correct Approach |
|---|
| No tests for service class | Untested business logic | Every functional class needs tests |
| Testing implementation details | Brittle tests | Test behavior, not internals |
[Fact] public void Test1() | Unclear purpose | [Fact] public void MethodName_Scenario_ExpectedResult() |
| Not testing exception cases | Incomplete coverage | Test happy path AND error paths |
StyleCop
| Mistake | Why It's Wrong | Correct Approach |
|---|
#pragma warning disable | Hides technical debt | Fix the actual issue |
_fieldName | Non-standard for this codebase | this.fieldName |
| Missing XML docs | SA1600 violation | Document all public members |
| Block-scoped namespace | SA1513 violation | Use file-scoped namespace X; |
Decision Flowcharts
Should I Use ConfigureAwait(false)?
Is this code in a Controller, Razor Page, or Blazor component?
βββ YES β Do NOT use ConfigureAwait(false)
βββ NO β Does this code access HttpContext, User, or UI elements?
βββ YES β Do NOT use ConfigureAwait(false)
βββ NO β USE ConfigureAwait(false) β
What Log Level Should I Use?
Is this an unhandled exception or app crash?
βββ YES β LogCritical (Fatal)
βββ NO β Did an operation fail?
βββ YES β LogError
βββ NO β Is something unexpected but handled?
βββ YES β LogWarning
βββ NO β Is this a significant business event?
βββ YES β LogInformation
βββ NO β Is this useful for debugging?
βββ YES β LogDebug
βββ NO β LogTrace (or don't log)
Do I Need a Unit Test for This Class?
Does this class contain business logic or behavior?
βββ YES β Unit test REQUIRED β
βββ NO β Is this a DTO, model, or configuration class?
βββ YES β Unit test optional (data-only)
βββ NO β Is this auto-generated code?
βββ YES β Unit test not needed
βββ NO β Is this startup/Program.cs?
βββ YES β Integration test instead
βββ NO β Probably needs a unit test β
Package Versions
| Package | Minimum | Recommended | Notes |
|---|
| .NET SDK | 10.0 | 10.0 | Required |
| Serilog | 4.0.0 | 4.0.0 | |
| Serilog.AspNetCore | 8.0.0 | 8.0.0 | For Web API |
| Sentry | 4.0.0 | 4.3.0 | |
| Sentry.Serilog | 4.0.0 | 4.3.0 | |
| StyleCop.Analyzers | 1.2.0-beta.556 | 1.2.0-beta.556 | Beta required for C#13 support |
| FluentAssertions | 6.0.0 | 6.12.0 | |
| Moq | 4.18.0 | 4.20.70 | |
| xUnit | 2.6.0 | 2.6.4 | |
| Coverlet | 6.0.0 | 6.0.0 | |
Quick Commands
# Build with zero warnings
dotnet build --warnaserrors
# Run all tests with coverage
dotnet test --collect:"XPlat Code Coverage" --settings coverlet.runsettings
# Generate HTML coverage report
reportgenerator -reports:./TestResults/**/coverage.cobertura.xml -targetdir:./coverage -reporttypes:Html
# Find StyleCop violations
dotnet build 2>&1 | grep -E "SA[0-9]{4}"
# Find pragma suppressions (should return nothing)
grep -r "#pragma warning disable" --include="*.cs" src/
# Find missing ConfigureAwait in Infrastructure
grep -r "await.*;" --include="*.cs" src/Infrastructure/ | grep -v "ConfigureAwait(false)"
# Find Microsoft logging config (should not exist in hosts)
grep -r '"Logging"' --include="appsettings*.json" src/
Exception Process
If you genuinely cannot follow a standard (e.g., third-party library conflict):
- Document the exception in code:
// STANDARDS-EXCEPTION: [reason]
- Create a GitHub issue linking to the code
- Get tech lead approval
- Add to project's
docs/EXCEPTIONS.md file
Never use pragma suppressions as an escape hatch.
IDE Setup
Visual Studio 2022
- Tools β Options β Text Editor β C# β Code Style
- Import
.editorconfig from solution root
- Enable "Run code analysis on build"
JetBrains Rider
- Settings β Editor β Code Style β C#
- Import from
.editorconfig
- Enable StyleCop inspections in Inspections settings
VS Code
- Install C# Dev Kit extension
- Install "EditorConfig for VS Code" extension
- Add to settings.json:
"omnisharp.enableEditorConfigSupport": true
Glossary
| Term | Definition |
|---|
| Aggregate | A cluster of domain objects treated as a single unit with a root entity |
| Breadcrumb | Sentry term for contextual events leading up to an error |
| CanBeNull | JetBrains annotation indicating a parameter or return value may be null |
| Clean Architecture | Layered architecture with Domain at center, dependencies pointing inward |
| ConfigureAwait(false) | Tells async/await not to capture the synchronization context |
| ContractAnnotation | JetBrains annotation for specifying method behavior based on input/output nullability |
| CQRS | Command Query Responsibility Segregation β separating read and write operations |
| Domain Event | A record of something significant that happened in the domain |
| Host Process | Executable project (API, Console, Worker) that runs the application |
| MediatR | In-process messaging library for implementing CQRS pattern |
| NotNull | JetBrains annotation indicating a parameter or return value cannot be null |
| NRT | Nullable Reference Types β C# compiler feature for null safety |
| Pure | JetBrains annotation marking methods with no side effects |
| Result Pattern | Alternative to exceptions for expected failures, returning success/failure with value |
| Structured Logging | Logging with named placeholders {PropertyName} that preserve data types |
| SUT | System Under Test β the class being tested in a unit test |
| Value Object | Immutable object defined by its attributes rather than identity |
Changelog
- 2.4.0 (2026-02-20): Audit & update β added globs/triggers to frontmatter, updated C# 12β13 references, fixed StyleCop version notes, added Seq apiKey guidance
- 2.3.0 (2026-01-11): Added Nullability & Annotations standards (JetBrains.Annotations)
- 2.2.0 (2026-01-11): Added Clean Architecture, CQRS/MediatR, Domain Design, Error Handling standards
- 2.1.0 (2026-01-10): Added Serilog, Sentry, ConfigureAwait standards; multi-file structure
- 2.0.0 (2025-12-01): Extended to multiple project types (Console, Worker, Blazor)
- 1.0.0 (2025-10-15): Initial Web API standards