public Task<Order> GetOrderAsync(int id);
public ValueTask SaveChangesAsync(CancellationToken ct);
public Task<Order> GetOrder(int id);
public Task<Order> GetOrderTask(int id);
```text
Exception: Event handlers and interface implementations where the framework does not use the `Async` suffix (e.g.,
ASP.NET Core middleware `InvokeAsync` is already named by the framework).
### Boolean Naming
Prefix booleans with `is`, `has`, `can`, `should`, or similar:
```csharp
public bool IsActive { get; set; }
public bool HasOrders { get; }
public bool CanDelete(Order order);
```csharp
### Collection Naming
Use plural nouns for collections:
```csharp
public IReadOnlyList<Order> Orders { get; }
public Dictionary<string, int> CountsByName { get; }
```csharp
---
## File Organization
### One Type Per File
Each top-level type (class, record, struct, interface, enum) should be in its own file, named exactly as the type.
Nested types stay in the containing type's file.
```text
OrderService.cs -> public class OrderService
IOrderRepository.cs -> public interface IOrderRepository
OrderStatus.cs -> public enum OrderStatus
OrderSummary.cs -> public record OrderSummary
```csharp
### File-Scoped Namespaces
Always use file-scoped namespaces (C# 10+):
```csharp
namespace MyApp.Services;
public class OrderService { }
namespace MyApp.Services
{
public class OrderService { }
}
```text
### Using Directives
Place `using` directives at the top of the file, outside the namespace. With `<ImplicitUsings>enable</ImplicitUsings>`
(default in modern .NET), common namespaces are already imported. Only add explicit `using` statements for namespaces
not covered by implicit usings.
Order of `using` directives:
1. `System.*` namespaces
2. Third-party namespaces
3. Project namespaces
### Directory Structure
Organize by feature or layer, matching namespace hierarchy:
```text
src/MyApp/
Features/
Orders/
OrderService.cs
IOrderRepository.cs
OrderEndpoints.cs
Users/
UserService.cs
Infrastructure/
Persistence/
OrderRepository.cs
```csharp
---
## Code Style
### Braces
Always use braces for control flow, even for single-line bodies:
```csharp
if (order.IsValid)
{
Process(order);
}
if (order.IsValid)
Process(order);
```text
### Expression-Bodied Members
Use expression bodies for single-expression members:
```csharp
public string FullName => $"{FirstName} {LastName}";
public override string ToString() => $"Order #{Id}";
```text
### `var` Usage
Use `var` when the type is obvious from the right-hand side:
```csharp
var orders = new List<Order>();
var customer = GetCustomerById(id);
var name = "Alice";
IOrderRepository repo = serviceProvider.GetRequiredService<IOrderRepository>();
decimal total = CalculateTotal(items);
```text
### Null Handling
Prefer pattern matching over null checks:
```csharp
if (order is not null) { }
if (order is { Status: OrderStatus.Active }) { }
if (order != null) { }
if (order is object) { }
if (!(order is null)) { }
```text
Use null-conditional and null-coalescing operators:
```csharp
var name = customer?.Name ?? "Unknown";
var orders = customer?.Orders ?? [];
items ??= [];
```csharp
### String Handling
Prefer string interpolation over concatenation or `string.Format`:
```csharp
var message = $"Order {orderId} totals {total:C2}";
var json = $$"""
{
"id": {{orderId}},
"name": "{{name}}"
}
""";
var message = string.Format("Order {0} totals {1:C2}", orderId, total);
var message = "Order " + orderId + " totals " + total.ToString("C2");
```text
---
## Access Modifiers
Always specify access modifiers explicitly. Do not rely on defaults:
```csharp
public class OrderService
{
private readonly IOrderRepository _repo;
internal void ProcessBatch() { }
}
class OrderService
{
readonly IOrderRepository _repo;
}
```text
### Modifier Order
Follow the standard order:
```text
access (public/private/protected/internal) -> static -> extern -> new ->
virtual/abstract/override/sealed -> readonly -> volatile -> async -> partial
```text
```csharp
public static readonly int MaxSize = 100;
protected virtual async Task<Order> LoadAsync() => await repo.GetDefaultAsync();
public sealed override string ToString() => Name;
```csharp
---
## Type Design
These conventions implement SOLID and DRY principles at the code level. For comprehensive coverage with anti-patterns
and fixes, see [skill:dotnet-solid-principles].
### Seal Classes by Default
Seal classes that are not designed for inheritance. This improves performance (devirtualization) and communicates
intent:
```csharp
public sealed class OrderService(IOrderRepository repo)
{
}
```text
Only leave classes unsealed when you explicitly design them as base classes.
### Prefer Composition Over Inheritance
```csharp
public sealed class OrderProcessor(IValidator validator, INotifier notifier)
{
public async Task ProcessAsync(Order order)
{
await validator.ValidateAsync(order);
await notifier.NotifyAsync(order);
}
}
public class BaseProcessor { }
public class ValidatingProcessor : BaseProcessor { }
public class NotifyingValidatingProcessor : ValidatingProcessor { }
```text
### Interface Segregation
Keep interfaces focused. Prefer multiple small interfaces over one large one:
```csharp
public interface IOrderReader
{
Task<Order?> GetByIdAsync(int id, CancellationToken ct = default);
Task<IReadOnlyList<Order>> GetAllAsync(CancellationToken ct = default);
}
public interface IOrderWriter
{
Task<Order> CreateAsync(Order order, CancellationToken ct = default);
Task UpdateAsync(Order order, CancellationToken ct = default);
}
public interface IOrderRepository : IOrderReader, IOrderWriter { }
```text
---
## CancellationToken Conventions
Accept `CancellationToken` as the last parameter in async methods. Use `default` as the default value for optional
tokens:
```csharp
public async Task<Order> GetOrderAsync(int id, CancellationToken ct = default)
{
return await _repo.GetByIdAsync(id, ct);
}
```text
Always forward the token to downstream async calls. Never ignore a received `CancellationToken`.
---
## XML Documentation
Add XML docs to public API surfaces. Keep them concise:
```csharp
public Task<Order?> GetByIdAsync(int id, CancellationToken ct = default);
```text
Do not add XML docs to:
- Private or internal members (unless it's a library's `InternalsVisibleTo` API)
- Self-evident members (e.g., `public string Name { get; }`)
- Test methods
---
## Analyzer Enforcement
Configure these analyzers in `Directory.Build.props` or `.editorconfig` to enforce standards automatically:
```xml
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<AnalysisLevel>latest-all</AnalysisLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
```text
Key `.editorconfig` rules for C# style:
```ini
[*.cs]
csharp_style_namespace_declarations = file_scoped:warning
csharp_prefer_braces = true:warning
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_style_require_accessibility_modifiers = always:warning
csharp_style_prefer_pattern_matching = true:suggestion
```csharp
See [skill:dotnet-add-analyzers] for full analyzer configuration.
---
## Knowledge Sources
Conventions in this skill are grounded in publicly available content from:
- **Microsoft Framework Design Guidelines** -- The canonical reference for .NET naming, type design, and API surface
conventions. Source: https:
- **C# Language Design Notes (Mads Torgersen et al.)** -- Design rationale behind C# language features that affect
coding standards. Key decisions relevant to this skill: file-scoped namespaces (reducing nesting for readability),
pattern matching over type checks (expressiveness), `required` members (compile-time initialization safety), and `var`
usage guidelines (readability-first). The language design team explicitly chose these features to reduce ceremony
while maintaining safety. Source: https:
> **Note:** This skill applies publicly documented design rationale. It does not represent or speak for the named
> sources.
## Code Navigation (Serena MCP)
**Primary approach:** Use Serena symbol operations for efficient code navigation:
1. **Find definitions**: `serena_find_symbol` instead of text search
2. **Understand structure**: `serena_get_symbols_overview` for file organization
3. **Track references**: `serena_find_referencing_symbols` for impact analysis
4. **Precise edits**: `serena_replace_symbol_body` for clean modifications
**When to use Serena vs traditional tools:**
- ✅ **Use Serena**: Navigation, refactoring, dependency analysis, precise edits
- ✅ **Use Read/Grep**: Reading full files, pattern matching, simple text operations
- ✅ **Fallback**: If Serena unavailable, traditional tools work fine
**Example workflow:**
```text
# Instead of:
Read: src/Services/OrderService.cs
Grep: "public void ProcessOrder"
# Use:
serena_find_symbol: "OrderService/ProcessOrder"
serena_get_symbols_overview: "src/Services/OrderService.cs"
```
## References
- [Framework Design Guidelines](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/)
- [C# Coding Conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions)
- [C# Identifier Naming Rules](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/identifier-names)
- [.editorconfig .NET]()
- [C# Language Design Notes]()