CDPG Go style and engineering rules
Status: Implemented as the standard; fleet conformance is partial. gofmt, build, vet, tests, race tests, security checks, and contract checks enforce behavior; prose alone is not a gate.
API and package design
- Package names are short, lowercase, singular where natural, and describe a capability. Avoid
util,common,helpers, andmisc. - File names are lowercase with underscores only when readability improves (
access_request.go). Tests end in_test.go; generated files carry the generator header. - Export the smallest stable surface. Accept interfaces and return concrete types unless substitution is a real consumer need.
- Declare interfaces at the consumer boundary. Keep them narrow enough to fake by hand.
- Constructors validate required dependencies. Functional options are for optional behavior with safe defaults, not required fields.
Preferred:
type GrantStore interface {
ByID(context.Context, string) (Grant, error)
}
func NewService(store GrantStore, authorizer Authorizer) (*Service, error) {
if store == nil || authorizer == nil {
return nil, errors.New("grant service: dependencies are required")
}
return &Service{store: store, authorizer: authorizer}, nil
}
Avoid a package-wide service locator, an interface that mirrors every repository method, or a constructor that succeeds with a nil security dependency.
Context, errors, and telemetry
context.Contextis the first parameter and is never stored in a struct.- Pass the request context to all network, database, lock, and wait operations.
- Create errors with actionable context and wrap the cause with
%w. - Use
errors.Is/errors.As; do not inspect error strings. - Handle an error once. Return it or log it, not both, unless adding an explicit boundary event.
- Use current platform structured logging (
zap) and OpenTelemetry/Prometheus integrations. Do not introduce a second logger into a service. - Metrics describe behavior, not high-cardinality identities. Put IDs in traces or structured logs under the data-handling policy.
Preferred:
widget, err := repo.ByID(ctx, id)
if err != nil {
return Widget{}, fmt.Errorf("get widget %q: %w", id, err)
}
Avoid log.Printf, swallowed errors, raw error text in an HTTP response, or subject/resource IDs as Prometheus labels.
Concurrency and lifecycle
- The creator owns a goroutine’s cancellation and completion.
- Use
errgroup,bootstrap.App.Go,App.Background, orplatform/executor; never fire-and-forget from a handler. - Keep channels directional, close them only from the sender, and document ownership.
- Bound worker concurrency, queue capacity, retries, and per-item time.
- Protect shared state with the simplest appropriate primitive. Do not hold a lock during network I/O.
- Run
go test -race ./...; addgoleakchecks for components that own goroutines.
Database and time
- Parameterize every value. Interpolate only compile-time allowlisted identifiers.
- Put transaction boundaries in application use cases and use
sql.Manager.Do/DoRetry. - Use
SELECT … FOR UPDATEfor conflicting state transitions; use advisory locks or durable leases for singleton ownership. - Map no-row, uniqueness, serialization, and connection errors to stable domain/platform errors.
- Represent nullable database values explicitly. Avoid sentinel strings or zero timestamps.
- Store and compare UTC; inject a clock where time controls a business decision. Serialize RFC 3339 timestamps.
- IDs are opaque. Validate UUID/URN syntax at the edge and never derive tenant authorization from string shape alone.
HTTP, gRPC, JSON, and protobuf
- Use typed platform handlers and declarative routes.
HandleRawis reserved for standards-native or streaming responses. - Keep handlers thin: bind, call, map.
- JSON field names are stable
lowerCamelCase; reject unknown fields where the contract requires strictness. - Protobuf fields are additive; never reuse a number. Reserve deleted fields and names.
- Set request/body limits and timeouts. A stream must be explicitly marked and cancellation-aware.
Configuration and secrets
- Embed
config.Base, apply defaults, bind file then environment, validate at startup. - Environment keys are unprefixed platform keys such as
SERVER_PORTandPOSTGRES_DSN;DX_BOOT_MODEis the explicit operational-role exception. - No secret has a usable default. Secrets come from the deployment secret mechanism, not repository YAML or command examples.
- Never log tokens, credentials, approval codes, private keys, presigned URLs, or unredacted protected attributes.
- Use constant-time comparisons for secret material when a library does not already verify it.
Security
- Authenticate at the edge and verify workload identity again at every service boundary.
- Treat represented subject and workload as separate identities.
- Authorize every object/state transition; role membership alone is not resource access.
- Default deny on missing mapping, invalid decision, unsupported obligation, unavailable required dependency, or expired context.
- Validate all external input, normalize before authorization, parameterize datastore queries, and constrain paths/bucket keys to owned namespaces.
- Use vetted cryptographic and JWT libraries. Pin allowed algorithms, issuer, audience, time bounds, and key source.
Testing and dependencies
- Use table-driven unit tests, explicit fixtures/builders, and fakes at consumer interfaces.
- Prefer integration tests with real PostgreSQL, Redis, RabbitMQ, OpenFGA, Elasticsearch, or S3-compatible infrastructure when adapter semantics matter.
- Fuzz parsers, validators, URNs, route matching, obligation translators, and event decoders.
- Mock behavior you own, not an entire third-party SDK surface.
- Keep dependencies few, maintained, licensed, pinned, scanned, and justified. Shared platform capability wins over a new one-off library.
- Generated code is reproducible in CI and never manually patched.
Required commands
gofmt -w .
go build ./...
go vet ./...
go test ./...
go test -race ./...
govulncheck ./...
Repositories may add stricter linters, OpenAPI/protobuf generation checks, integration suites, and container scans. They may not remove these gates without a recorded exception.