Let Kody automatically check quality, security and performance in every pull request—cloud or self-hosted, in under 2 minutes.
A focused checklist to review Go code for readability, security and performance, helping you ship reliable and production-ready pull requests.
Here’s what this checklist helps you review before approving a pull request.
✅ Code & Design Quality – idiomatic Go style, clear structure, and maintainable logic
✅ Security & Performance – validated inputs, safe crypto/TLS, efficient concurrency and scalable execution
✅ Reliability & Observability – robust error handling, structured logging, metrics and tracing for production readiness
✅ Data & Integration – safe migrations and transactions, stable APIs, and clean configuration management
Use these points as a starting guide to automate code reviews with your agent.
Names follow Go conventions (mixedCaps/PascalCase; no underscores; package = short, lower).
Files, packages, and directories are cohesive; no “kitchen-sink” packages.
One top concept per file; large files split logically.
Functions are small, single-purpose; parameters < ~5; return values are meaningful.
Avoid cleverness; prefer straightforward, readable code.
Comments explain why, not what; exported identifiers have complete doc comments.
Keep imports minimal and grouped; standard, third-party, local order; goimports
clean.
Avoid long parameter lists and boolean flags; prefer options/config structs.
Consistent error message style (lowercase, no punctuation
), actionable context included.
No dead code/ commented-out blocks/ TODOs without linked issues.
Prefer range
loops; correct use of value vs pointer iteration (no address of loop variable bug).
Avoid interface{} when generics or concrete types suffice.
Prefer zero values over explicit initialization where idiomatic.
Use time.Duration
for time, not naked ints; units correct.
Use defer
for cleanup close to acquisition; mindful of defer cost in hot paths.
Avoid panic in library code; reserve panic for truly unrecoverable programmer errors.
Use context.Context
as first param when operations are cancelable/timeout-bound.
Avoid embedding types when it leaks APIs unintentionally; prefer explicit forwarding when needed.
Understand copy vs reference semantics (slices, maps, channels, strings).
Use errors.Is/As
for error matching; sentinel errors exported appropriately.
Choose pointer vs value receivers intentionally (mutability, size, nil-safety, interface satisfaction).
Generic functions/types used where they improve clarity/safety; constraints precise.
Interfaces are small and consumer-owned; avoid large “god” interfaces.
Do not export types or methods unnecessarily; keep API surface minimal.
Validate inputs at boundaries; invariants enforced internally.
Avoid nil
maps/slices if appended/assigned into; ensure init paths are clear.
Return concrete types; accept interfaces for flexibility (DI via interface).
Use time.Time
vs *time.Time
consciously; zero-time semantics documented.
Goroutine lifecycles are bounded; no leaks on cancel/error/return.
All goroutines respect context
cancellation; pass context down call chain.
Channel usage clear: directions (chan<-
, <-chan
) and buffering justified.
No send/receive on closed channels; close occurs on producer side only.
select
used to avoid blocking; default cases avoided unless truly needed.
Shared data guarded (mutex, RWMutex) or confined to a goroutine; no data races.
Avoid using WaitGroup
incorrectly (no copying, Add
before goroutine start).
Avoid busy loops; backoff/timers/tickers are cleaned up; time.Ticker.Stop()
called.
Avoid unbounded concurrency; use worker pools/semaphores where needed.
No context.TODO
in production paths; use context.Background()
only at roots.
Appropriate data structures (map vs slice vs set/bitmap) for access patterns.
Minimize allocations in hot paths; reuse buffers (bytes.Buffer
, sync.Pool
) when justified.
Avoid copying large structs; pass pointers where appropriate.
Pre-allocate slices with correct capacity; avoid repeated reallocation.
Avoid reflect
/interface{}
on critical paths; avoid fmt
in tight loops.
String/[]byte conversions minimized (use strings.Builder
, bytes
).
Profiling guides optimizations; no premature micro-optimizations.
Streaming I/O preferred over loading whole files/blobs into memory.
Consider time.AfterFunc
vs creating many timers; reuse time.Timer
where needed.
Errors handled explicitly; no silent ignores (_ = x
without reason).
Wrap errors with context (fmt.Errorf("doing X: %w", err)
); avoid losing stack/context.
Distinguish expected vs exceptional errors; no log-spam or double-logging.
Logging is structured; keys consistent; no PII/secrets in logs.
Metrics cover key paths (latency, throughput, errors, saturation).
Tracing spans propagate context; attributes/events capture useful detail.
Health/readiness endpoints reflect actual dependencies; include dependency checks.
Backoff with jitter for retries; retry only idempotent operations.
Circuit breakers/timeouts used for remote calls; deadlines respected.
Input validation and canonicalization performed; reject by default.
Use parameterized queries; no string-built SQL; migrations reviewed.
Secrets never hardcoded; loaded from secure store; redacted in logs.
Crypto uses crypto/*
primitives correctly; no home-rolled crypto.
TLS verified (server name, roots); certificate pinning/rotation considered.
HTTP handlers defend against common vulns (CSRF where stateful, XSS, SSRF).
http.Server
timeouts set (ReadHeaderTimeout
, ReadTimeout
, WriteTimeout
, IdleTimeout
).
Validation on JSON/YAML/proto decoding; DisallowUnknownFields
where helpful.
File operations sandboxed; path traversal prevented; safe temp files (os.CreateTemp
).
Authorization checks centralized and enforced; least privilege for OS/network/DB.
Use golang.org/x/*
security updates; dependencies scanned and pinned.
Idempotency keys for retried external operations.
Sagas/outbox or other patterns used for cross-service consistency.
Time handling is UTC internally; time zones only at the edges.
Resource cleanup on shutdown (listeners, tickers, goroutines) via context
.
Disk and network failures simulated/considered; exponential backoff with jitter.
At-least/at-most/exactly-once semantics documented and enforced where relevant.
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.
Pyramid balanced: fast unit tests, focused integration tests, a few E2E.
Tests deterministic; no sleeps except with require.Eventually
-style helpers.
Table-driven tests with subtests; edge cases and error paths covered.
Fakes over mocks when possible; interfaces small and testable.
Concurrency tests include race detector (-race
) in CI.
Golden files stable and human-reviewable; regeneration process documented.
Benchmarks for hotspots; regression tests for perf bugs.
Property-based tests (where valuable) validate invariants.
Coverage thresholds meaningful; critical packages near 80–90% where feasible.
go vet
, golangci-lint
(curated linters) clean; false positives suppressed locally with comments.
gofmt
/goimports
enforced; ineffassign
, misspell
, errcheck
addressed.
Security linters (gosec, vulncheck) run; findings triaged.
-race
run in CI for tests and selected integration paths.
Build tags documented and covered in CI matrix.
Generated code checked in only when appropriate; // Code generated … DO NOT EDIT.
present.
HTTP clients reuse http.Client
with tuned timeouts; no per-request client creation.
Servers set sane limits (body size, headers, concurrency); gzip/deflate handled safely.
JSON encoding uses omitempty
wisely; numbers decoded safely (no precision loss).
gRPC: context deadlines, interceptors for logging/metrics, message size limits.
Versioning strategy defined (semver for APIs); backwards compatibility policy enforced.
OpenAPI/Protobuf specs source-of-truth; handlers adhere to contracts; validation enforced.
Idempotent methods use correct HTTP verbs; GET
is safe; proper status codes returned.
Pagination, filtering, sorting implemented consistently and safely.
Transactions used correctly; isolation levels chosen and documented.
Migrations reversible and tested; DB schema in version control.
Indexes align with query patterns; query plans reviewed for hotspots.
Scan/encode errors handled; sql.Null*
/custom nullable types used correctly.
For ORMs, N+1 avoided; eager vs lazy loading explicit; batch operations considered.
Serialization formats stable; forward/backward compatibility verified.
Data retention/TTL policies applied; PII lifecycle respected.
Clear boundaries between packages (domain, transport, persistence, app).
Dependency direction points inward; no cycles; internal packages used properly.
Public APIs minimal; internal details hidden; adapters thin.
Configuration isolated; no global mutable state; DI via constructors.
Feature modules cohesive; avoid cross-package reach-through.
Docs/README per package explain purpose and constraints.
Prefer composition over inheritance; small structs with embedded behavior where appropriate.
Functional options for constructors to avoid long parameter lists.
Adapter/Facade to isolate external deps; repository pattern when it simplifies testing.
Worker pool / pipeline patterns for concurrency; channels used judiciously.
Strategy/state patterns via interfaces or function types when it improves clarity.
Avoid over-engineering; patterns earn their keep.
Module name stable; go.mod
tidy; go.sum
committed.
Minimal dependencies; avoid heavy frameworks; choose well-maintained libs.
Replace directives used only for dev; not left in production.
Versions pinned (no v0.0.0-…
unless necessary); updates tracked.
Internal forks documented; license compliance verified.
Reproducible builds; -trimpath
/-buildvcs
considered; version/commit injected via -ldflags
.
Multi-arch builds if needed; CGO usage explicit; static linking when appropriate.
Makefile/Taskfile with common targets (test
, lint
, build
, bench
, docker
).
CI matrix covers OS/arch/tags; caches used appropriately.
Artifacts signed/checksummed; SBOM generated if required.
Startup and shutdown hooks implemented; graceful termination tested.
Single source for config; precedence defined (env > file > defaults).
Validation on load; fail fast on invalid config.
Secrets via environment/secret store; never in repo.
Feature flags typed, centralized, and evaluated efficiently; defaults safe.
Config/watchers shut down cleanly; dynamic reload documented if supported.
Health/readiness/startup probes correct; dependencies gated behind readiness.
Structured logs suitable for aggregation; correlation IDs propagated.
Resource usage predictable; memory/CPU limits respected under load.
Graceful shutdown within Kubernetes terminationGracePeriodSeconds
.
Containers run as non-root; least privileges; fs read-only when possible.
Config via env/Downward API; no baked secrets; 12-factor friendly.
Clear commands, flags, and help; sensible defaults; examples provided.
Errors actionable and concise; exit codes meaningful.
Do not prompt in non-interactive contexts; --yes
/--force
patterns consistent.
Output formats support (text/json); stable for scripting.
Shell completion/man pages generated when applicable.
unsafe
, reflect
, or atomic
used only with strong justification and benchmarks.
Memory alignment and lifetime rules respected; no pointer aliasing violations.
CGO boundaries minimized; error handling and resource ownership clear.
Sync/atomic operations paired correctly; no torn reads/writes.
Build tags for experimental code isolated and documented.
Let Kody automatically check quality, security and performance in every pull request—cloud or self-hosted, in under 2 minutes.