Open-Source Wikis

/

Prometheus

/

How to contribute

/

Patterns and conventions

prometheus/prometheus

Patterns and conventions

Conventions repeated across the codebase. Some are encoded in lint rules, some are tribal knowledge from AGENTS.md, and some emerge from reading the code itself.

Code style

The two referenced style guides are:

Local enforcement:

  • gofmt -s (run by make format).
  • golangci-lint with .golangci.yml. Linters enabled include gocritic (notable rule emptyStringTest), errcheck, revive, staticcheck, whitespace, unconvert, usestdlibvars, gocheckcompilerdirectives, loggercheck, perfsprint, and testifylint.
  • //nolint:linter1[,linter2] is allowed but reviewers will push back if the suppression is not justified.

All exported objects must have a doc comment that begins with the object's name and ends with a full stop. All comments start with a capital letter and end with a full stop.

Interface contracts at the interface

When ownership or buffer-reuse semantics matter, document them on the interface, not the implementation. Examples in the codebase:

  • storage.Appender (storage/interface.go) documents that callers must not retain labels.Labels after Append returns; the appender may copy or intern.
  • tsdb.ChunkReader documents that the returned chunk byte slice is valid only until the next call.
  • prompb.Compress/Decompress (util/compression/) — buffers passed in are reused, callers must copy before the next call.

This is a recurring review comment on PRs that introduce new interfaces.

Goroutine lifecycle

Most subsystems own a Run(ctx context.Context, ...) error method that exits when the context is cancelled. The server's main loop in cmd/prometheus/main.go uses oklog/run.Group to compose them: each member registers a Execute and Interrupt function, and the group cancels everything when any member returns.

Subsystems that hold their own goroutines:

  • discovery.Manager.Run / discovery.Manager.ApplyConfig
  • scrape.Manager.Run
  • notifier.Manager.Run
  • rules.Manager.Run (per-group)
  • storage/remote.QueueManager.Start / Stop
  • web.Handler.Run

Reload is implemented by calling ApplyConfig(*config.Config) on each manager, with the new config. A reload is only successful if every ApplyConfig returns nil.

Error handling

  • Wrap with fmt.Errorf("...: %w", err). Avoid errors.Wrap from pkg/errors — not used here.
  • Sentinel errors are var Err... = errors.New(...) at the top of the file. Match with errors.Is. See storage/interface.go and tsdb/head_append.go for examples.
  • Long-lived goroutines log and continue; short-lived helpers return up the stack.
  • Multi-errors use errors.Join (Go 1.20+). The TSDB migrated away from multierror in #17768.

Metrics hygiene

  • Each subsystem owns a *Metrics struct, registered via a constructor newXMetrics(reg prometheus.Registerer). Metrics live with their owner — search for prometheus.NewCounter( in any package to find them.
  • Use the prometheus_ prefix for self-metrics. Subsystem prefix follows: prometheus_tsdb_*, prometheus_scrape_*, prometheus_remote_storage_*, prometheus_rule_*, prometheus_notifications_*, prometheus_sd_*.
  • Exposing label dimensions has a real cost — be conservative. For instance, prometheus_remote_storage_samples_failed_total{remote_name, url, reason} was carefully reviewed before the reason label was added.

Hot-path performance rules

AGENTS.md is explicit:

  • Reuse allocations on hot paths (slices, buffers, label sets).
  • When a buffer is passed across an interface boundary that may retain it, document the contract at the interface and note in commit messages.
  • Performance changes need a benchmark and benchstat before/after. Reviewers may ask for a flame graph if the change is non-trivial.
  • The util/pool/ package wraps sync.Pool with size-class buckets — used heavily by scraping, remote write, and TSDB compaction.
  • The util/zeropool/ package provides a generic sync.Pool with explicit size classes; favour it over raw sync.Pool for typed slices.

Testing patterns

  • Use exported APIs in tests where possible. This both keeps tests close to library users and makes refactoring safer.
  • New test files use testify (require, assert). Older tests use vanilla t.Errorf and t.Fatalf; do not migrate them in unrelated PRs.
  • Time-sensitive tests should use testing.B's context, synctest, or an injectable clock interface, not time.Sleep.
  • The storage/teststorage and tsdb/testutil helpers handle most fixture setup; prefer them over reimplementing.

Configuration parsing

YAML structs use prometheus/common/yaml.in/yaml/v2 and go.yaml.in/yaml/v2. Config validation lives in Validate() methods on each config struct. Defaults are encoded in package-level Default*Config variables and applied in UnmarshalYAML by copying the default first then overlaying the parsed value (config/config.go does this throughout).

When a config field references files (TLS certs, file_sd files), implement discovery.DirectorySetter (or the corresponding interface in your package) so relative paths resolve relative to the config file directory.

Build tags

The codebase uses build tags pervasively:

  • slicelabels / stringlabels / dedupelabels — pick the labels representation. Default is stringlabels. CI runs all three.
  • forcedirectio — TSDB direct-IO mode, exercised in CI.
  • builtinassets — embed UI assets into the binary. Default in production builds.
  • netgo — pure-Go DNS resolver. Default for portable releases.
  • remove_all_sd and enable_<sd>_sd — configurable subset of service discovery (see plugins/).
  • fuzzing — only used by util/fuzzing/corpus_gen.

The configurable SD set is a 3.10 feature (#17736); use it to ship slimmer Prometheus binaries in environments where most SDs are unused.

Commenting

  • Every exported symbol needs a doc comment.
  • Tag work-in-progress with TODO(<name>): <description> (the codebase has ~320 of these).
  • XXX: and HACK: are used very sparingly (less than 10 occurrences).
  • Big design decisions are usually captured in the PR description rather than a // block — search the PR history when unsure.

Documentation parity

Docs live in docs/ (user docs) and documentation/ (operational examples + Jsonnet mixin). Two kinds of CI checks enforce parity:

  • make cli-documentation regenerates the flag docs and is checked by make check-cli-documentation.
  • The OpenAPI golden test under web/api/v1/openapi_golden_test.go ensures the spec matches the API surface.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – Prometheus wiki | Factory