DataDog/datadog-agent
Patterns and conventions
The conventions a contributor needs in their head before opening the editor.
Use dda inv, never raw go
The single most important rule, repeated throughout AGENTS.md:
This project uses extensive custom Go build tags. Most source files are ignored by the standard Go toolchain unless the correct tags are passed.
Direct invocation:
| Don't | Do |
|---|---|
go build ./... |
dda inv agent.build (or another *.build task) |
go test ./pkg/... |
dda inv test --targets=./pkg/... |
go mod tidy |
dda inv tidy |
go vet ./... |
dda inv linter.go |
golangci-lint run |
dda inv linter.go |
If you only want a smoke test that the change compiles, build the smallest binary that includes the path you touched (dda inv dogstatsd.build, dda inv trace-agent.build, dda inv system-probe.build).
Build tags
Build tags drive compilation. The full computation lives in tasks/build_tags.py. A few you'll meet often:
| Tag | Effect |
|---|---|
kubeapiserver |
Compiles in the Kubernetes API server plumbing (Cluster Agent and Cluster Checks). |
containerd |
containerd integration. |
docker |
Docker integration. |
cri |
CRI runtime integration. |
kubelet |
Kubelet client. |
linux_bpf |
eBPF implementation files (Linux-only). |
python |
Python check runtime. |
jmx |
JMX-Fetch bridge. |
serverless |
Stripped-down build for the serverless flavor. |
iot |
Stripped-down IoT Agent build. |
clusterchecks |
Cluster check dispatcher logic. |
secrets |
The secret backend executor. |
ec2, gce, azure, cloudfoundry |
Cloud-provider-specific integrations. |
Always start a new file with explicit constraints when a feature is platform- or tag-specific:
//go:build linux && linux_bpf
// +build linux,linux_bpf
package mythingMismatched tags between source and tests are a common cause of "works on my machine."
Component framework patterns
Every new piece of functionality should be added as a component under comp/. Read Components framework for the longer explanation.
Conventions:
- Layout:
comp/<bundle>/<component>/def/,<component>/impl/,<component>/fx/, optionally<component>/mock/. - Component interfaces go in
def/. Implementations go inimpl/. Fx wiring goes infx/. - Bundles (
comp/<bundle>/bundle.go) collect related components into a singlefx.Option. - Mocks (
<component>/mock/) are mandatory for components used in tests. - Mocks must be registered with
mockeryconfiguration in.mockery.yaml.
A component's interface should be:
- Small. Focused on the one capability the component provides.
- Stable. Other components depend on it; breaking changes are expensive.
- Testable. Returning concrete types or interfaces other code can mock.
When migrating from pkg/ to comp/, the typical pattern is:
- Create
comp/<bundle>/<component>/def/, copy or define the interface. - Create
comp/<bundle>/<component>/impl/. The first version often delegates to the existingpkg/code. - Create
comp/<bundle>/<component>/fx/with the Fx options. - Update binaries (
cmd/<binary>/subcommands/run/) to depend on the component instead of callingpkg/directly. - Eventually move the implementation into
impl/and delete thepkg/package.
Error handling
Use
fmt.Errorf("…: %w", err)for wrapping. Bareerrreturns lose context.Reserve
panicfor truly unrecoverable situations. The Agent's job is to keep running even when subsystems fail.Functions exposed to UIs and APIs must degrade gracefully when a dependency isn't ready. From
AGENTS.md:Components initialize in stages — some dependencies may not be ready when others start. Functions exposed to UIs or APIs should return safe defaults when a dependency is unavailable, not propagate errors or panic.
Concurrency and lifecycle
Most components implement an explicit Start() / Stop(). Conventions:
- Spawn goroutines from
Start(), signal them via context cancellation inStop(). - Channels owned by the component are closed by the goroutine that writes to them, after they are confident no more writes will happen.
- Send-on-closed-channel races at shutdown are the most common bug class. Tests must exercise the shutdown path.
golang.org/x/sync/errgroupandgolang.org/x/sync/singleflightare commonly used in the codebase.
The relevant AGENTS.md section is reproduced verbatim:
The agent runs many concurrent goroutines with explicit
Start()/Stop()lifecycles. The most common bugs are send-on-closed-channel during shutdown and goroutine leaks. Changes that introduce goroutines or modify component lifecycle should have tests exercising startup and graceful shutdown.
Logging
Conventions in docs/public/guidelines/conventions/logging.md and surfaced via pkg/util/log/ and comp/core/log/. Highlights:
- Use the package's bound logger (
log.Debug,log.Info,log.Warn,log.Error). - Don't log secrets. The flare scrubber redacts known patterns, but logs are not flares — they may go to disk regardless.
- Prefer one log line per event with structured fields, rather than multi-line free-form output.
- Per-package loggers (e.g.,
pkg/security/seclog/,pkg/trace/log/) exist for backwards compatibility; new code should use the canonical logger.
Configuration
- Configuration goes through
pkg/config/setup/(legacy) andcomp/core/config/(component framework). - Every config key should have a default in
pkg/config/setup/config.go(or its delegated callers) and a corresponding YAML key in the exampledatadog.yaml. - Environment variables follow the
DD_<UPPERCASE_KEY>pattern automatically; you don't add them by hand. - New runtime-changeable settings go through
comp/core/settings/so they appear inagent configandagent config set. - The
.claude/skills/create-config-field/skill walks through the full procedure for adding a new config field.
Platform-specific code
- Use
*_linux.go,*_windows.go,*_darwin.gofilenames for OS-specific implementations. - Stub the same symbols for unsupported platforms so packages still compile.
- Take Windows seriously. The
.claude/skills/, the build system (packages/agent/windows/), and the install scripts all have Windows variants. Drift between Linux and Windows is one of the most common bug classes.
Documentation in code
- Every exported symbol should have a doc comment.
- The component framework auto-generates
comp/README.mdfrom package docs (dda inv collector.generate). Keep package summaries up to date. - README files inside
pkg/andcomp/(e.g.,pkg/aggregator/README.md,pkg/trace/README.md,comp/dogstatsd/README.md) are the canonical reference for those subsystems. AGENTS.mdfiles are scoped to a directory tree; keep their guidance accurate.
Naming and style
- Use
gofmt(enforced bydda inv linter.go). - Package names are lowercase, no underscores. Exception: generated
*_string.gofiles for stringer. - Test packages are typically the same as the package under test (white-box). Black-box tests use
<pkg>_test. - File names use lowercase with underscores when needed (
time_sampler.go). - For new directories, mirror the existing style of the parent. The repo is large and consistency matters more than clever organization.
License headers
Every Go file starts with the standard header:
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.The tasks/licenses.py task validates this. eBPF C code uses GPLv2; rtloader uses Apache.
Per-area AGENTS.md
Several subtrees have their own AGENTS.md files that refine the repo-wide conventions:
bazel/AGENTS.md— Bazel rules and conventions.test/e2e-framework/AGENTS.md,test/fakeintake/AGENTS.md— E2E and fakeintake.pkg/collector/corechecks/ebpf/AGENTS.md— eBPF-based core checks.- And many more across
pkg/andcomp/.
Read the local AGENTS.md before making changes in an unfamiliar subtree. The root AGENTS.md explicitly asks contributors and AI agents to update these files when they go stale.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.