C# Conventions (kalamos-wide)
These conventions apply to all kalamos-managed C# projects unless overridden by a project-level SKILL.md.
Style
- C# 9 minimum. Use
varwhen type is obvious from RHS; use explicit type when it isn’t. - Async by default. Methods doing I/O return
Task<T>. SuffixAsync. Don’t mix sync and async code paths in the same callsite. - Null safety. Reference-type params are non-null unless explicitly nullable. Validate at the boundary; don’t propagate nulls through internal logic.
- No
varin public APIs — use explicit types in public method signatures and return values. - Filename = type name. One public type per file. Filename matches
PascalCaseof the type.
Project structure
- Multi-project solutions: group by feature/concern, not by layer.
Templates/,Generators/,Tools/are domain folders.Models/,Services/,Controllers/flat-by-layer is an anti-pattern. - No
Helpers/folder. Anything generic enough to live inHelpers/should be in a domain-specific class. If you find yourself wanting to write aHelpers.cs, name the actual concept it represents.
Build
- MSBuild via
dotnet buildCLI — never invokemsbuild.exedirectly from agent runs (different resolution rules; brittle under containerization). - Always
-maxcpucount:2 -nodeReuse:false— see[[Builder-MSBuild-Zombie-Reuse]]. - PackageReference, never packages.config. Repos using legacy
packages.configneed migration before any new work.
Test
- xUnit + Moq is the kalamos default. NUnit and MSTest are accepted only in legacy projects awaiting migration.
[Trait("Category", "Integration")]marks integration tests. Defaultdotnet testinvocation filters with--filter Category!=Integrationunless an integration run is explicitly requested.- AAA structure: Arrange / Act / Assert in every test, with blank lines between. Don’t squeeze them onto one line.
Forbidden
- Don’t suppress warnings without justification.
<NoWarn>requires a comment explaining why and a follow-up issue. - Don’t catch
Exceptionwithout rethrowing or logging. Barecatch (Exception) { }is a code-review block. - Don’t use
DateTime.Nowin tests. Inject anIClockabstraction or useDateTimeOffset.UtcNow. - Don’t
Task.Resultor.Wait()in async code paths. Deadlocks. Useawait.
See also
[[CSProj-Duplicate-PackageReference-Trap]][[NuGet-v3-vs-v6-Resolution]][[Builder-MSBuild-Zombie-Reuse]]