Contributions are welcome. Pull requests for bug fixes are welcome, but before submitting new features or changes to current functionality, please open an issue and discuss your ideas or propose the changes you wish to make first. After a resolution is reached, a PR can be submitted for review.
See docs/repo_structure.md for the full directory layout, module overview, feature flags, and internal architecture.
See docs/add_configurations.md for how to add new configuration options.
- MSRV: 1.87.0 (set via
rust-versioninCargo.toml) - Formatting: pinned
nightly-2026-04-07toolchain
Install the required toolchains and components:
rustup install nightly-2026-04-07
rustup component add rustfmt --toolchain nightly-2026-04-07
rustup component add clippy --toolchain nightly-2026-04-07
rustup install 1.87.0
rustup component add clippy --toolchain 1.87.0Formatting uses cargo fmt with the pinned nightly-2026-04-07 toolchain to ensure consistent
output across environments:
rustup run nightly-2026-04-07 cargo fmt -p <modified crate>To check without modifying files (as CI does):
rustup run nightly-2026-04-07 cargo fmt --all -- --checkClippy is run across every feature combination using cargo-hack v0.6.42:
cargo hack --each-feature clippy -- -D warningsInstall cargo-hack with:
cargo install cargo-hack@0.6.42Shell scripts are linted with shellcheck v0.11.0. To run locally:
find . -name '*.sh' -print0 | xargs -0 shellcheckMarkdown files are linted with rumdl v0.1.25. To run locally:
rumdl check .Install rumdl v0.1.25 with:
cargo install rumdl@0.1.25# Format code
rustup run nightly-2026-04-07 cargo fmt --all
# Lint Rust (all feature combinations)
cargo hack --each-feature clippy -- -D warnings
# Run tests
cargo nextest run --workspace --locked -E '!test(integration_tests::)'
cargo test --workspace --doc --locked
# Lint shell scripts and markdown
find . -name '*.sh' -print0 | xargs -0 shellcheck
rumdl check .
# Update licence file if dependencies changed
./scripts/update_license_3rdparty.sh- Prefer
impl TraitoverBox<dyn Trait>when possible - Avoid code duplication as much as possible
- Document all public symbols
- Avoid
usedeclarations (imports) inside functions. Place them at the top of the file, unless they are needed for tests only, in which case they should be placed at the top of themod testsblock. - Keep functions simple and short (less than 100 lines).
- Keep indent level under 3 inside of functions, ideally under 2. Resort to early return / early break pattern rather than having complex logic inside of conditional blocks.
- Prefer
&stroverStringin function parameters - Use
Result<T, E>for fallible operations, not panics - Avoid possible panics like
unwraporexpectin non-test code. If there's a reasonable default, considerunwrap_or/unwrap_or_else. - Prefer
Arc::clone(x)overx.clone()whenxis anArc - Prefer
usestatements likeuse std::sync::Arcrather than fully qualifying types every time they are used (e.g.std::sync::Arc) - Avoid eager combinators (
ok_or,unwrap_or,map_or) when the fallback allocates or does meaningful work. Consider usingok_or_else,unwrap_or_else, ormap_or_elseinstead. - Avoid
#[allow(clippy::too_many_arguments)]. Instead, try to simplify function signatures, introduce abstractions / structs, or as a last resort refactor to use a dedicated struct for parameters. - Avoid adding dead code with
#[allow(clippy::dead_code)]. Prefer deleting the code unless it will be used in subsequent work. - Avoid shadowing names in the prelude, like
Result,None, andIterator. For instance, preferanyhow::Result<T>instead ofuse anyhow::Result. - Prefer iterator patterns instead of manual loops, where convenient. For instance, use
findinstead of a loop with an earlyreturn, and usecollectinstead of.pushin a loop.
- Bug fixes must include non-regression tests
- All functional changes require at least 1 test
- Tests are run with
cargo nextest run --workspace --locked -E '!test(integration_tests::)' - Doc-tests are run separately with
cargo test --workspace --doc --locked(nextest does not support doc-tests) - Integration tests (
integration_tests::filter) require Docker — they spin up theghcr.io/datadog/dd-apm-test-agent/ddapm-test-agentcontainer automatically. Run them withcargo nextest run --workspace --locked -E 'test(integration_tests::)'. In CI they only execute on Linux.
Always use git commands when adding, moving, or removing tracked files:
- New file: run
git add <file>to stage it before writing content to it - Move/rename: use
git mv <old> <new>instead ofmv - Delete: use
git rm <file>instead ofrm
All pull requests must follow the Conventional Commits specification. The PR title is linted automatically on every push. Format:
type(scope): short description
Common types: feat, fix, chore, refactor, docs, test, perf, ci. Append ! after
the type/scope for breaking changes (e.g. chore(api)!: ...). The scope is optional but
encouraged.
If referencing an internal ticket, place it at the end of the description inside [], e.g.:
feat(config): add visibility reporting [APMAPI-1693]
When opening a pull request, please open it as a draft to not auto-assign reviewers before the pull request is in a reviewable state.
If any code in the PR was contributed by an AI agent, apply the ai generated label to the PR.
When adding or updating dependencies, you must update the LICENSE-3rdparty.csv file to reflect
these changes. This file is checked by our CI pipeline to ensure all dependencies are properly
documented.
To update the license file:
- Run
./scripts/update_license_3rdparty.sh(requires Docker or local tool install) - Review the changes to
LICENSE-3rdparty.csv - Commit the updated file
The script uses Docker to ensure the generated file matches our CI environment, avoiding
platform-specific differences. Otherwise the GH action will generate a correct
LICENSE-3rdparty.csv file artifact on failure, which you can download and add to your branch.
When reviewing Rust code, pay attention to:
- Error handling: Proper use of
?operator, meaningful error messages - Ownership: Avoid unnecessary clones, prefer borrowing
- Unsafe code: Minimize and document all
unsafeblocks