Write idiomatic, nullable and explicit C#. Types model the domain; async does not hide I/O. Web: aspnet-core. Persistence: entity-framework. Tests: csharp-testing.
Tooling
- .NET 10+ SDK in new code; respect
TargetFramework/global.jsonof the repo. - Nullable reference types enabled (
<Nullable>enable</Nullable>). dotnet format,dotnet test, analyzers (TreatWarningsAsErrorsin new code if CI already requires it).- Central Package Management (
Directory.Packages.props) if the repo already uses it.
Organization
src/
MyApp/ # exe ou worker
MyApp.Domain/
MyApp.Infrastructure/
tests/
MyApp.Tests/
- Projects by boundary, not by cosmetic layer.
internalby default;publiconly stable API.- File-scoped namespaces; implicit usings only the SDK ones.
Language
- Records /
record structfor values; classes for identity and lifecycle. - Primary constructors when the type is thin; do not hide too many mutable fields.
- Pattern matching (
is,switchexpressions) instead ofif+ cast. required/initfor contract objects; avoid public setters without an invariant.- Collections:
List<T>,IReadOnlyList<T>,FrozenDictionarywhen immutable and hot. - LINQ for clarity; avoid N+1 in I/O (materialize / batch).
span/Memoryonly in a measured hot path.
public sealed record UserId(Guid Value);
public static string Label(UserStatus status) => status switch
{
UserStatus.Active => "Ativo",
UserStatus.Suspended => "Suspenso",
_ => throw new ArgumentOutOfRangeException(nameof(status)),
};
Nullability and errors
T?explicit; no blind!. Null-forgiving only with a commented local invariant.- Lib APIs:
Result<T>/ domain exceptions — choose one per boundary. ArgumentNullException.ThrowIfNull; validate at the edge.- Do not
catch (Exception)swallowing. PreserveInnerException.
Async
asyncTask/ValueTask to the edge; do not.Result/.Wait().- Pass
CancellationTokenin I/O. Cancel on shutdown. ConfigureAwait(false)in libs; ASP.NET apps do not need it.- Heavy CPU:
Task.Runonly with reason; do not wrap sync blocking.
DI and config (apps)
- Register lifetimes consciously (
Singletonstateless,Scopedper request/UoW). - Options pattern +
IValidateOptions; secrets out of code. - No service locator (
IServiceProvider.GetRequiredServicein the domain).
Anti-patterns
dynamic/objectin new APIsasync void(except UI event handlers)- Assume
HttpContext/DbContextin this skill DateTime.Nowin the domain — inject a clock- Static "helper" classes with state
Integration with other skills
- HTTP:
aspnet-core - ORM:
entity-framework - Tests:
csharp-testing - Realtime:
signalr - HTTP contracts:
api-design
Completion criteria
- Nullable and warnings handled in scope
- Async with CT at I/O boundaries
- Minimal public API; records/enums in the domain
- Tests of the critical path (
csharp-testing) - No
.Result/ Exception swallowing