Let Kody automatically check quality, security and performance in every pull request—cloud or self-hosted, in under 2 minutes.
A handy checklist to help you review C# code for quality, security and performance so every pull request is ready for production.
Here’s what this checklist helps you review before approving a pull request.
✅ Code & Design Quality – consistent .NET style, clear structure, modern C# patterns and proper dependency management
✅ Security & Performance – validated inputs, safe deserialization, secrets handling, efficient queries, caching and scalable runtime
✅ Reliability & Observability – robust error handling, structured logging/tracing, metrics, health checks and safe CI/CD rollouts
✅ Data & Integration – versioned APIs, safe migrations and transactions, proper indexing and clean configuration/secrets management
Use these points as a starting guide to automate code reviews with your agent.
Names follow .NET conventions (PascalCase for types/methods, camelCase for locals/parameters, ALL_CAPS for const).
Files and classes are cohesive; no “God” classes; methods are short and intention-revealing.
No dead/commented code; TODOs tracked in issues, not left in source.
Consistent formatting via EditorConfig/formatter (dotnet-format/Rider); no style bikeshedding in PR.
using directives are trimmed, sorted, and file-scoped; no wildcard usings.
Early returns over deep nesting; guard clauses for validation.
Comments/docstrings explain why, not what; public APIs have XML docs where non-obvious.
LINQ pipelines remain readable; extract steps/locals when complex; avoid hidden multiple enumerations.
String handling uses interpolation or StringBuilder in loops; culture/format specified where important.
Nullable Reference Types (NRT) enabled and warnings treated seriously; nullability annotations correct.
Immutability favored: readonly fields/structs, record for value types/DTOs; defensive copies for mutable inputs.
Visibility is minimal; avoid public setters where not required; use init-only setters.
Prefer composition over inheritance; abstract classes only for shared behavior contracts.
Avoid static/global state; inject dependencies; no service locator.
Pattern matching/enhanced switch used where it clarifies logic; avoid deep if/else.
Don’t throw from property getters; keep constructors free of heavy I/O/logic—use factories/builders.
IDisposable implemented correctly; use using/await using for disposable/async-disposable resources.
Public APIs fully typed (no object/dynamic unless necessary); generic constraints declared precisely.
Interfaces model behavior not data; segregation respected (ISP).
IReadOnlyCollection<>/IEnumerable<> returned when mutation not intended.
Use Span<T>/Memory<T>/ReadOnlySpan<T> for hot paths on buffers (when safe).
Contracts documented (pre/postconditions) and validated at boundaries; guard clauses throw specific exceptions.
Avoid null for collections; return empty collections.
All inputs validated and normalized (FluentValidation/DataAnnotations); server-side enforced.
Parameterized queries only (Dapper/EF Core); no string-concat SQL; protect against ORM injection.
XSS: Razor auto-encoding respected; no raw HTML without sanitization.
CSRF: antiforgery tokens on state-changing endpoints (if cookie-based auth).
Authentication via ASP.NET Core auth handlers; authorization policies/voters applied at every endpoint.
No sensitive data in logs; PII redaction; GDPR/LGPD awareness.
Secrets from secret store (Azure Key Vault, AWS Secrets Manager); never in repo or images; rotation plan exists.
Security headers set (HSTS, X-Content-Type-Options, X-Frame-Options/frame-ancestors, CSP where possible).
SSRF mitigations: outbound allow-list; block link-local/metadata IPs; DNS rebinding defenses.
Deserialization safe (System.Text.Json with known types); no BinaryFormatter; no insecure YAML/XML parsers (XXE).
Rate limiting/anti-abuse on auth and expensive endpoints; lockouts for brute force.
Hot paths avoid allocations; use pooling (ArrayPool, ObjectPool) when appropriate; avoid boxing.
Asynchronous I/O (async/await) for network/disk; do not block threads (.Result, .Wait()).
Avoid sync-over-async; configure awaits with ConfigureAwait(false) in libraries.
LINQ vs loops chosen based on profiling; avoid multiple enumerations; ToList() only when needed.
Caching strategy defined (MemoryCache/Distributed cache); keys/TTLs/invalidation documented.
EF Core: no N+1; projection selects; AsNoTracking for read-only; compiled queries when hot.
HTTP client reuse via IHttpClientFactory; timeouts set; connection pooling validated.
JSON serialization uses System.Text.Json with source-gen for hot payloads when beneficial.
GC mode/server GC settings appropriate; thread pool starvation monitored.
Benchmarks (BenchmarkDotNet) for disputed hotspots; optimize after measuring.
Exceptions are specific; no swallow of Exception; catch-wrap-rethrow with context when useful.
Domain vs technical errors distinguished; problem details returned to clients (RFC7807) without leaking internals.
All external calls have timeouts; retries are bounded with jitter and idempotent-only; circuit breaker/bulkhead where needed.
Logging is structured (serilog/MEL) with correlation IDs; no sensitive values.
Metrics (RED/USE) exported (Prometheus/OpenTelemetry); SLIs/SLOs defined.
Tracing with OpenTelemetry spans for inbound/outbound; context propagated.
Health/ready/live endpoints implemented; shutdown is graceful (drain requests, close brokers).
Unit tests deterministic, fast (xUnit/NUnit/MSTest); naming reflects behavior.
Integration tests cover DB/cache/HTTP/brokers (Testcontainers); isolated and parallelizable.
E2E or contract tests for public APIs (OpenAPI validation, Pact for consumer/provider).
Property-based tests (FsCheck) for pure logic/parsers.
Regression tests accompany bug fixes; fail without the fix.
Coverage monitored on critical paths/error handling; quality > raw %.
Static analysis: Roslyn analyzers/StyleCop/FxCop rules; nullable warnings treated as errors where possible.
Mutation testing (Stryker.NET) on critical modules when justified.
Security scans (DevSkim/GitHub CodeQL) in CI; failing gates block merge.
Layered/hexagonal boundaries enforced; no infrastructure bleeding into domain.
Dependency direction respected (core has no refs to UI/infra); abstractions stable, implementations swappable.
Modules small and cohesive; cyclical references avoided.
Feature flags for risky behavior; toggles centralized and typed.
ADRs/design notes for non-trivial changes; rationale captured.
Public contracts versioned; backward compatibility considered.
Patterns reduce complexity/duplication, not add ceremony.
Factories/Builders for complex object creation; no telescoping constructors.
Strategy/Policy to replace if/else pyramids; Mediator for decoupled request handling (with care).
CQRS only where read/write models diverge meaningfully; events have clear ownership and delivery guarantees.
Anti-patterns avoided: service locator, anemic domain (unless choosing transactional script explicitly), God objects.
Project uses SDK-style csproj; Directory.Packages.props or central package mgmt where appropriate.
Deterministic builds; packages.lock.json (NuGet lock) enabled for apps.
Semantic Versioning respected for libraries; package metadata complete.
SBOM generated (e.g., SPDX/CycloneDX); vulnerability scanning configured.
Strong-naming/signing applied where required; assembly attributes correct.
Third-party licenses reviewed; runtime RID assets well defined.
API surface consistent; versioning via URL/header; breaking changes gated.
OpenAPI accurate and generated; request/response models validated.
Consistent error model (problem+json); validation errors structured.
Minimal APIs use typed endpoints/filters properly; Controllers keep thin; business logic in services.
gRPC: deadlines/timeouts set; message sizes and compression configured; auth enforced.
Idempotency keys for unsafe POST; webhook signatures (HMAC + timestamp) verified; retries deduped.
async/await used correctly; no blocking on async; cancellation tokens plumbed end-to-end.
Task lifetimes managed; unobserved exceptions handled; Task.Run only for CPU-bound offload.
Concurrency control with SemaphoreSlim/Channel<T>/Dataflow where suitable; backpressure enforced.
Thread-unsafe members protected; locks minimal and fine-grained; deadlock risks reviewed.
Parallel LINQ/Parallel.ForEachAsync used intentionally with sizing limits.
EF Core: explicit transactions where needed; correct isolation; SaveChanges bounded in scope.
Migrations atomic and forward-only (with rollback plan if necessary); data backfills planned.
Indexes match query patterns; no full scans on hot paths; query plans checked for regressions.
Bulk operations use proper APIs; batching/pagination for large sets.
UTC in storage; DateTimeOffset/Instant where needed; precision/scale defined for money/decimals.
Middleware order correct (exception handling, routing, auth, endpoints).
Model binding safe; validation attributes/FluentValidation applied; custom binders minimal and tested.
Filters used for cross-cutting concerns (authz, caching, transactions) not scattered in controllers.
CORS minimal; compression/caching configured; response buffering considered.
Typed options (IOptions<T>) with validation; fail fast on invalid config.
appsettings.*.json per environment; no environment-specific branches in code.
Secrets from vault/SM; not in repo or images; rotation tested; local dev uses user-secrets.
Feature flags/config centralized; dynamic reload where safe.
CI runs lint/analyzers/tests/security scans; gates block merges; artifacts reproducible.
dotnet publish with correct RID/-c Release; single-file/AOT/trimming considered (and annotations added) where suitable.
Containers minimal, non-root, read-only FS; proper health checks; small attack surface.
Kubernetes: liveness/readiness/startup probes; resource limits/requests; pod disruption budgets.
Rollouts blue/green or canary; fast rollback path; DB compatibility checks in pipeline.
Startup/shutdown hooks drain queues and close pools.
Structured logs (JSON) with correlation/causation IDs; scope enrichers used.
Sampling configured for noisy components; PII redaction in sinks.
Metrics exported (Prometheus/OpenTelemetry) with RED/USE coverage.
Tracing spans for inbound/outbound and DB/cache; baggage/trace context propagated across services.
PII cataloged; access controls audited; least privilege enforced.
Data retention and erasure implemented; subject requests supported; audit trails immutable.
Encryption in transit/at rest; key mgmt documented; regional residency respected.
Third-party processors reviewed; DPAs in place.
CLI/console apps use System.CommandLine/Spectre; helpful --help; exit codes meaningful.
Scripts deterministic and idempotent; cross-platform (PowerShell + Bash); pinned tool versions.
Service README with purpose, dependencies, run/debug instructions, and operability notes.
XML docs for public packages; examples compile; API docs generated (Swashbuckle + annotations).
ADRs for significant choices; upgrade notes/changelog maintained.
Runbooks/on-call playbooks updated for new behaviors and alerts.
IaC (Bicep/Terraform/Pulumi) versioned; reviewable changes; drift detection.
Resiliency patterns: retries, timeouts, circuit breakers, bulkheads; chaos testing where feasible.
Cost budgets/alerts; right-sized SKUs; caching/queueing to reduce load.
Backups tested; RPO/RTO documented; disaster recovery drills scheduled.
Let Kody automatically check quality, security and performance in every pull request—cloud or self-hosted, in under 2 minutes.