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 var when type is obvious from RHS; use explicit type when it isn’t.
  • Async by default. Methods doing I/O return Task<T>. Suffix Async. 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 var in public APIs — use explicit types in public method signatures and return values.
  • Filename = type name. One public type per file. Filename matches PascalCase of 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 in Helpers/ should be in a domain-specific class. If you find yourself wanting to write a Helpers.cs, name the actual concept it represents.

Build

  • MSBuild via dotnet build CLI — never invoke msbuild.exe directly 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.config need 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. Default dotnet test invocation filters with --filter Category!=Integration unless 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 Exception without rethrowing or logging. Bare catch (Exception) { } is a code-review block.
  • Don’t use DateTime.Now in tests. Inject an IClock abstraction or use DateTimeOffset.UtcNow.
  • Don’t Task.Result or .Wait() in async code paths. Deadlocks. Use await.

See also

  • [[CSProj-Duplicate-PackageReference-Trap]]
  • [[NuGet-v3-vs-v6-Resolution]]
  • [[Builder-MSBuild-Zombie-Reuse]]