Coding conventions¶
Code style¶
.editorconfig is the source of truth. The high-level rules:
- 4 spaces, no tabs.
- LF line endings.
- File-scoped namespaces (no curly-braced namespaces).
usingdirectives outside the namespace.- Prefer
varonly when the type is obvious from the right-hand side. - Public APIs require XML doc comments; internals do not.
recordfor value types;classfor entities and services.- Async methods end in
Async; cancellation tokens are the last parameter.
Naming¶
| Kind | Style | Example |
|---|---|---|
| Class / record | PascalCase | RolloutService |
| Method | PascalCase | StartAsync |
| Property | PascalCase | Status |
| Field (private) | _camelCase | _repository |
| Constant | PascalCase | DefaultTimeout |
| Local | camelCase | rolloutId |
Bounded-context rules¶
See Bounded contexts. The rules are non-negotiable:
Ampora.OpAmp.Coreknows no Collector concepts.Ampora.Collector.Configknows no OpAMP concepts.Ampora.Fleetis the only place the two meet.Ampora.Webconsumes application services, never repositories.
A code-review check exists for the imports.
Commits¶
Common types: feat, fix, docs, chore, refactor, test, perf. The footer is for BREAKING CHANGE: notes and Closes #... references.
Commit examples¶
feat(rollout): canary step-up schedule
Implements ADR-011. Adds dwell-time-between-steps for percentage rollouts.
Closes #321
fix(opamp): reject frames over MaxMessageBytes
We were silently accepting oversized frames and OOMing on large agent
populations. Reject with ServerErrorResponse and bump
ampora_opamp_frames_rejected_total{reason="oversize"}.
Pull request title¶
Same conventional format as the commit. CI lints the title on every push.
Code review¶
Two-reviewer minimum on master (one for refactors that move files without changing behaviour). Reviewers are responsible for:
- correctness against the relevant ADR (link from PR description),
- test coverage for the new behaviour,
- audit-log row for any state change,
- docs updated for any user-visible change.
A self-merged PR is a process bug.
Tests¶
- Unit tests live next to the project they test.
- Integration tests live in
tests/Ampora.Integration.Tests/. - Test names describe the scenario in plain English:
RolloutPause_WhenGateFires_RecordsAuditEventAndPausesBatch. - Arrange / Act / Assert as separate code blocks; do not collapse into one-liners.
See Testing for the test taxonomy.
Logging¶
- Structured logs only. No string interpolation in log messages — use
_logger.LogInformation("Rollout {RolloutId} entered state {State}", id, state). - Levels:
Informationfor normal lifecycle events,Warningfor recoverable,Errorfor unhandled. - No PII in messages. Tenant IDs and entity IDs are fine; user names and IP addresses are not (those go in audit, which is controlled).
Async¶
- All I/O is async. No
.Result, no.Wait(). - Pass
CancellationTokenfrom the call site; do not capture ambient ones. - Background services derive from
BackgroundService; they respectstoppingToken.
Database access¶
- All DB access goes through
AmporaDbContext. - Long queries belong on the repository / service classes, not in controllers / endpoints.
- New columns require a migration (
dotnet ef migrations add ...). - New JSONB columns require explicit indexes if they will be queried.