CRITICAL: All builds must complete without warnings. The project enforces this through:
-
VS Code tasks
CSharpier Format→.Net Build→.Net Format.Net Formatmust pass with--verify-no-changesbefore commit- Command:
dotnet format style --verify-no-changes --severity=info --verbosity=detailed
-
Analyzer configuration
<AnalysisLevel>latest-all</AnalysisLevel><EnableNETAnalyzers>true</EnableNETAnalyzers>- Analyzer severity is
suggestion, but all warnings must be addressed
-
Husky.Net pre-commit hooks
- Automated checks run before commits
Available VS Code tasks (use via run_task tool):
.Net Build: Build with diagnostic verbosity.Net Format: Verify formatting and style (must pass)CSharpier Format: Auto-format code with CSharpier.Net Tool Update: Update dotnet toolsHusky.Net Run: Run pre-commit hooks manually
-
CSharpier: Primary code formatter
- Run before committing:
dotnet csharpier format --log-level=debug .
- Run before committing:
-
dotnet format: Style verification
- Verify no changes:
dotnet format style --verify-no-changes --severity=info --verbosity=detailed
- Verify no changes:
-
Husky.Net: Git hooks for automated checks
- Installed as a local dotnet tool (via
dotnet tool restore) - Install Git hooks locally with
dotnet husky install - Pre-commit hooks run formatting and style checks
- Installed as a local dotnet tool (via
-
Other tools
dotnet-outdated-tool: Dependency update checks- Nerdbank.GitVersioning: Version management
- Required VS Code extensions: CSharpier, markdownlint, CSpell
- VS Code settings: Use the workspace settings without overrides
- Linting: All
.mdfiles must be linted with the VS Codemarkdownlintextension (local only; no CI) - Zero warnings: Markdown linting must be error and warning free
- CSpell: All spelling checks must be error free using the CSpell VS Code integration
- Accepted spellings: Words must be correctly spelled in US or UK English
- Allowed exceptions: Project-specific terms must be added to the workspace CSpell config
Note: Code snippets are illustrative examples only. Replace namespaces/types to match your project.
-
File-scoped namespaces
namespace Example.Project.Library;
-
Nullable reference types: Enabled (
<Nullable>enable</Nullable>)- Use nullable annotations appropriately
- Use
requiredfor mandatory properties
-
Modern C# features: Prefer modern language constructs
- Primary constructors when appropriate
- Top-level statements for console apps
- Pattern matching over traditional checks
- Collection expressions when types loosely match
- Extension methods using
extension()syntax - Implicit object creation when type is apparent
- Range and index operators
-
Expression-bodied members: Use for applicable members
- Methods, properties, accessors, operators, lambdas, local functions
-
varkeyword: Do NOT usevar(always use explicit types)// Correct int count = 42; string name = "test"; // Incorrect var count = 42; var name = "test";
-
Private fields: underscore prefix with camelCase
private readonly HttpClient _httpClient; private int _counter;
-
Static fields:
s_prefix with camelCaseprivate static int s_instanceCount;
-
Constants: PascalCase
private const int MaxRetries = 3;
-
Global usings: Use
GlobalUsings.csfor common namespacesglobal using System; global using System.Net.Http; global using System.Threading.Tasks; global using Serilog;
-
Usings placement: Outside namespace, sorted with
Systemdirectives firstusing System.CommandLine; using System.Runtime.CompilerServices; using Example.Project.Library; namespace Example.Project.Console;
-
Braces: Allman style
public void Method() { if (condition) { // code } }
-
Indentation
- C# files: 4 spaces
- XML/csproj files: 2 spaces
- YAML files: 2 spaces
- JSON files: 4 spaces
-
Line endings
- C#, XML, YAML, JSON, Windows scripts: CRLF
- Linux scripts (
.sh): LF
-
#region: Do not use regions. Prefer logical file/folder/namespace organization. -
Member ordering (StyleCop SA1201): const → static readonly → static fields → instance readonly fields → instance fields → constructors → public (events → properties → indexers → methods → operators) → non-public in same order → nested types
-
XML documentation
<GenerateDocumentationFile>true</GenerateDocumentationFile>- Missing XML comments for public APIs are suppressed (
.editorconfig) - Must document all public surfaces.
- Single-line summaries, additional details in remarks, document input parameters, returns values, exceptions, and add crefs
/// <summary> /// Example of a single line summary. /// </summary> /// <remarks> /// Additional important details about usage. /// Multiple lines if needed. /// </remarks> /// <param name="category"> /// The quote category to request /// </param> /// <param name="cancellationToken"> /// A <see cref="System.Threading.CancellationToken"/> that can be used to cancel the request. /// </param> /// <returns> /// A <see cref="string"/> containing the quote text. /// </returns> /// <exception cref="System.ArgumentException"> /// Thrown when <paramref name="category"/> is not a supported value. /// </exception> public async Task<string> GetQuoteOfTheDayAsync(string category, CancellationToken cancellationToken) {}
-
Code analysis suppressions
- Do not use
#pragmasections to disable analyzers - For one-off cases, use suppression attributes with justifications
- For project-wide suppressions, add rules to
.editorconfig
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Design", "CA1034:Nested types should not be visible", Justification = "https://github.com/dotnet/sdk/issues/51681" )]
- Do not use
-
Serilog logging: Use structured logging
logger.Error(exception, "{Function}", function);
-
Library log configuration: Libraries must expose logging configuration
- Provide options or settings to supply an
ILoggerFactoryand/orILogger - Offer a global fallback logger for static usage when needed
- Provide options or settings to supply an
-
CallerMemberName: Use for automatic function name tracking
public bool LogAndPropagate( Exception exception, [CallerMemberName] string function = "unknown" )
-
Logger extensions: Use
Extensions.csfor logger and other extension methodsextension(ILogger logger) { public bool LogAndPropagate(Exception exception, ...) { } }
-
Exceptions: Do not swallow exceptions; log and rethrow or translate to a domain-specific exception
- Guard clauses: Prefer early returns for validation and error handling
- Async all the way: Avoid blocking calls (
.Result,.Wait()); useasync/await - Cancellation tokens: Accept
CancellationTokenas the last parameter and pass it through - ConfigureAwait: In library code, use
ConfigureAwait(false)unless context is required- Do not call
ConfigureAwait(false)in xUnit tests (see xUnit1030)
- Do not call
- Disposables: Use
await usingfor async disposables; preferusingdeclarations - LINQ vs loops: Use LINQ for clarity, loops for hot paths or allocations
- HTTP: Reuse
HttpClientvia factory; avoid per-request instantiation - Collections: Prefer
IReadOnlyList<T>/IReadOnlyCollection<T>for public APIs - Immutability: Prefer immutable records; use init-only setters when records are not suitable; prefer immutable or frozen collections for read-only data
- Exceptions as control flow: Avoid using exceptions for expected flow
- Sealing classes: Seal classes that are not designed for inheritance
- Read-only data: Use immutable or frozen collections for read-only data sets
- Lazy initialization: Use
Lazy<T>for static, thread-safe instantiation (e.g., logger factory, HTTP factory)
-
Framework: xUnit with AwesomeAssertions
[Fact] public void MethodName_Scenario_ExpectedBehavior() { // Arrange int expected = 42; // Act int actual = GetValue(); // Assert actual.Should().Be(expected); }
-
Organization: Arrange-Act-Assert pattern
-
Naming: Descriptive names with underscores
-
Theory tests: Use
[Theory]with[InlineData]
-
Target framework: .NET 10.0 (
<TargetFramework>net10.0</TargetFramework>) -
AOT compatibility
<IsAotCompatible>true</IsAotCompatible><VerifyReferenceAotCompatibility>true</VerifyReferenceAotCompatibility>
-
Assembly information
- Use semantic versioning
- Include SourceLink:
<PublishRepositoryUrl>true</PublishRepositoryUrl> - Embed untracked sources:
<EmbedUntrackedSources>true</EmbedUntrackedSources>
-
Internal visibility: Use
InternalsVisibleTofor test and benchmark access<ItemGroup> <InternalsVisibleTo Include="Benchmarks" /> <InternalsVisibleTo Include="Tests" /> </ItemGroup>
- Code reviews: All changes go through pull requests