Skip to content

Commit 5210356

Browse files
watsonrochdev
authored andcommitted
docs: add AGENTS.md and update CONTRIBUTING.md (#7109)
Add comprehensive developer guide (AGENTS.md) and enhance CONTRIBUTING.md with detailed documentation covering: - Project setup, structure, and overview - Testing workflows (unit tests, integration tests, plugin tests) - Code style and linting standards (import ordering, ECMAScript/Node.js APIs) - Performance and memory considerations (async/await restrictions, array iteration) - Development workflow best practices (backportability, refactoring principles) - How to add new configuration options and instrumentations - Debugging, logging, and error handling patterns The AGENTS.md file follows the agents.md spec and provides a quick reference for common development tasks and established best practices to help both AI coding assistants and human contributors navigate the codebase effectively. Includes CLAUDE.md symlink for compatibility with Claude-based tools. This reduces onboarding friction, enforces consistency, prevents common mistakes, and improves code quality by making best practices discoverable.
1 parent 4eea2d1 commit 5210356

File tree

4 files changed

+556
-21
lines changed

4 files changed

+556
-21
lines changed

.vscode/settings.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,16 @@
22
// Use the workspace version of TypeScript instead of VSCode's bundled version
33
"typescript.tsdk": "node_modules/typescript/lib",
44
"typescript.enablePromptUseWorkspaceTsdk": true,
5+
"cSpell.words": [
6+
"aerospike",
7+
"appsec",
8+
"backportability",
9+
"kafkajs",
10+
"llmobs",
11+
"microbenchmarks",
12+
"oracledb",
13+
"rabbitmq",
14+
"rspack",
15+
"sirun"
16+
],
517
}

AGENTS.md

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
# AGENTS.md
2+
3+
## Prerequisites
4+
5+
- Node.js >= 18
6+
- yarn 1.x
7+
- Docker + docker-compose (for running service dependencies in tests)
8+
9+
## Setup
10+
11+
- Install dependencies: `yarn install`
12+
13+
**Note:** This project uses yarn, not npm. Always use `yarn` commands instead of `npm` commands.
14+
15+
## Project Overview
16+
17+
dd-trace is the Datadog client library for Node.js.
18+
19+
**Key Directories:**
20+
- `packages/dd-trace/` - Main library (APM, profiling, debugger, appsec, llmobs, CI visibility)
21+
- `packages/datadog-core/` - Async context storage, shared utilities
22+
- `packages/datadog-instrumentations/` - Instrumentation implementations
23+
- `packages/datadog-plugin-*/` - 100+ plugins for third-party integrations
24+
- `integration-tests/` - E2E integration tests
25+
- `benchmark/` - Performance benchmarks
26+
27+
## Testing Instructions
28+
29+
### Testing Workflow
30+
31+
When developing a feature or fixing a bug:
32+
33+
1. Start with individual test files to verify things work
34+
2. Run component tests: `yarn test:<component>` (e.g., `yarn test:debugger`, `yarn test:appsec`)
35+
3. Run integration tests: `yarn test:integration:<component>` (e.g., `yarn test:integration:debugger`)
36+
37+
### Running Individual Tests
38+
39+
**IMPORTANT**: Never run `yarn test` directly. Use `mocha` or `tap` directly on test files.
40+
41+
**Mocha unit tests:**
42+
```bash
43+
./node_modules/.bin/mocha -r "packages/dd-trace/test/setup/mocha.js" path/to/test.spec.js
44+
```
45+
46+
**Tap unit tests:**
47+
```bash
48+
./node_modules/.bin/tap path/to/test.spec.js
49+
```
50+
51+
**Integration tests:**
52+
```bash
53+
./node_modules/.bin/mocha --timeout 60000 -r "packages/dd-trace/test/setup/core.js" path/to/test.spec.js
54+
```
55+
56+
**Target specific tests:**
57+
- Add `--grep "test name pattern"` flag
58+
59+
**Enable debug logging:**
60+
- Prefix with `DD_TRACE_DEBUG=true`
61+
62+
**Note**: New tests should be written using mocha, not tap. Existing tap tests use mocha-style `describe` and `it` blocks.
63+
64+
### Plugin Tests
65+
66+
**Use `PLUGINS` env var:**
67+
```bash
68+
PLUGINS="amqplib" yarn test:plugins
69+
PLUGINS="amqplib|bluebird" yarn test:plugins # pipe-delimited for multiple
70+
./node_modules/.bin/mocha -r "packages/dd-trace/test/setup/mocha.js" packages/datadog-plugin-amqplib/test/index.spec.js
71+
```
72+
73+
**With external services** (check `.github/workflows/apm-integrations.yml` for `SERVICES`):
74+
```bash
75+
export SERVICES="rabbitmq" PLUGINS="amqplib"
76+
docker compose up -d $SERVICES
77+
yarn services && yarn test:plugins
78+
```
79+
80+
**ARM64 incompatible:** `aerospike`, `couchbase`, `grpc`, `oracledb`
81+
82+
### Test Coverage
83+
84+
```bash
85+
./node_modules/.bin/nyc --include "packages/dd-trace/src/debugger/**/*.js" \
86+
./node_modules/.bin/mocha -r "packages/dd-trace/test/setup/mocha.js" \
87+
"packages/dd-trace/test/debugger/**/*.spec.js"
88+
```
89+
90+
**Philosophy:**
91+
- Integration tests (running in sandboxes) don't count towards nyc coverage metrics
92+
- Don't add redundant unit tests solely to improve coverage numbers
93+
- Focus on covering important production code paths with whichever test type makes sense
94+
95+
### Test Assertions
96+
97+
Use `node:assert/strict` for standard assertions. For partial deep object checks, use `assertObjectContains` from `integration-tests/helpers/index.js`:
98+
99+
```js
100+
const assert = require('node:assert/strict')
101+
const { assertObjectContains } = require('../helpers')
102+
103+
assert.equal(actual, expected)
104+
assertObjectContains(response, { status: 200, body: { user: { name: 'Alice' } } })
105+
```
106+
107+
### Time-Based Testing
108+
109+
**Never rely on actual time passing in unit tests.** Use sinon's fake timers to mock time and make tests deterministic and fast.
110+
111+
## Code Style & Linting
112+
113+
### Linting & Naming
114+
- Lint: `yarn lint` / `yarn lint:fix`
115+
- Files: kebab-case
116+
- JSDoc: TypeScript-compatible syntax (`@param {string}`, `@returns {Promise<void>}`, `@typedef`)
117+
118+
### Import Ordering
119+
120+
Separate groups with empty line, sort alphabetically within each:
121+
1. Node.js core modules (with `node:` prefix)
122+
2. Third-party modules
123+
3. Internal imports (by path proximity, then alpha)
124+
125+
Use destructuring for utility modules when appropriate.
126+
127+
```js
128+
const fs = require('node:fs')
129+
const path = require('node:path')
130+
131+
const express = require('express')
132+
133+
const { myConf } = require('./config')
134+
const log = require('../log')
135+
```
136+
137+
### ECMAScript and Node.js API Standards
138+
139+
**Target Node.js 18.0.0 compatibility:**
140+
- Use modern JS features supported by Node.js (e.g., optional chaining `?.`, nullish coalescing `??`)
141+
- Guard newer APIs with version checks using [`version.js`](./version.js):
142+
```js
143+
const { NODE_MAJOR } = require('./version')
144+
if (NODE_MAJOR >= 20) { /* Use Node.js 20+ API */ }
145+
```
146+
- **Prefix Node.js core modules with `node:`** (e.g., `require('node:assert')`)
147+
148+
### Performance and Memory
149+
150+
**CRITICAL: Tracer runs in application hot paths - every operation counts.**
151+
152+
**Async/Await:**
153+
- Do NOT use `async/await` or promises in production code (npm package)
154+
- Allowed ONLY in: test files, worker threads (e.g., `packages/dd-trace/src/debugger/devtools_client/`)
155+
- Use callbacks or synchronous patterns instead
156+
157+
**Memory:**
158+
- Minimize allocations in frequently-called paths
159+
- Avoid unnecessary objects, closures, arrays
160+
- Reuse objects and buffers
161+
- Minimize GC pressure
162+
163+
#### Array Iteration
164+
165+
**Prefer `for-of`, `for`, `while` loops over functional methods (`map()`, `forEach()`, `filter()`):**
166+
- Avoid `items.forEach(item => process(item))` → use `for (const item of items) { process(item) }`
167+
- Avoid chaining `items.filter(...).map(...)` → use single loop with conditional push
168+
- Functional methods create closures and intermediate arrays
169+
170+
**Functional methods acceptable in:**
171+
- Test files
172+
- Non-hot-path code where readability benefits
173+
- One-time initialization code
174+
175+
**Loop selection:**
176+
- `for-of` - Simple iteration
177+
- `for` with index - Need index or better performance in hot paths
178+
- `while` - Custom iteration logic
179+
180+
### Debugging and Logging
181+
182+
Use `log` module (`packages/dd-trace/src/log/index.js`) with printf-style formatting (not template strings):
183+
184+
```js
185+
const log = require('../log')
186+
log.debug('Value: %s', someValue) // printf-style
187+
log.debug(() => `Expensive: ${expensive()}`) // callback for expensive ops
188+
log.error('Error: %s', msg, err) // error as last arg
189+
```
190+
191+
Enable: `DD_TRACE_DEBUG=true DD_TRACE_LOG_LEVEL=info node app.js`
192+
Levels: `trace`, `debug`, `info`, `warn`, `error`
193+
194+
### Error Handling
195+
196+
**Never crash user apps:** Catch/log errors (`log.error()`/`log.warn()`), resume or disable plugin/subsystem
197+
Avoid try/catch in hot paths - validate inputs early
198+
199+
## Development Workflow
200+
201+
### Core Principles
202+
- **Search first**: Check for existing utilities/patterns before creating new code
203+
- **Small PRs**: Break large efforts into incremental, reviewable changes
204+
- **Descriptive code**: Self-documenting with verbs in function names; comment when needed
205+
- **Readable formatting**: Empty lines for grouping, split complex objects, extract variables
206+
- **Avoid large refactors**: Iterative changes, gradual pattern introduction
207+
- **Test changes**: Test logic (not mocks), failure cases, edge cases - always update tests
208+
209+
### Always Consider Backportability
210+
211+
**We always backport `master` to older versions.**
212+
- Keep breaking changes to a minimum
213+
- Don't use language/runtime features that are too new
214+
- **Guard breaking changes with version checks** using [`version.js`](./version.js):
215+
```js
216+
const { DD_MAJOR } = require('./version')
217+
if (DD_MAJOR >= 6) {
218+
// New behavior for v6+
219+
} else {
220+
// Old behavior for v5 and earlier
221+
}
222+
```
223+
224+
## Adding New Configuration Options
225+
226+
1. **Add default value** in `packages/dd-trace/src/config_defaults.js`
227+
2. **Map environment variable** in `packages/dd-trace/src/config.js` (`#applyEnvironment()` method)
228+
3. **Add TypeScript definitions** in `index.d.ts`
229+
4. **Add to telemetry name mapping** (if applicable) in `packages/dd-trace/src/telemetry/telemetry.js`
230+
5. **Update** `packages/dd-trace/src/supported-configurations.json`
231+
6. **Document** in `docs/API.md` (non-internal/experimental options only)
232+
7. **Add tests** in `packages/dd-trace/test/config.spec.js`
233+
234+
**Naming Convention:** Size/time-based config options should have unit suffixes (e.g., `timeoutMs`, `maxBytes`, `intervalSeconds`).
235+
236+
## Adding New Instrumentation
237+
238+
**New instrumentations go in `packages/datadog-instrumentations/`.** The instrumentation system uses diagnostic channels for communication.
239+
240+
Many integrations have corresponding plugins in `packages/datadog-plugin-*/` that work with the instrumentation layer.
241+
242+
### What Are Plugins?
243+
244+
Plugins are modular code components in `packages/datadog-plugin-*/` directories that:
245+
- Subscribe to diagnostic channels to receive instrumentation events
246+
- Handle APM tracing logic (spans, metadata, error tracking)
247+
- Manage feature-specific logic (e.g., code origin tracking, LLM observability)
248+
249+
**Plugin Base Classes:**
250+
- **`Plugin`** - Base class with diagnostic channel subscription, storage binding, enable/disable lifecycle. Use for non-tracing functionality.
251+
- **`TracingPlugin`** - Extends `Plugin` with APM tracing helpers (`startSpan()`, automatic trace events, `activeSpan` getter). Use for plugins creating trace spans.
252+
- **`CompositePlugin`** - Extends `Plugin` to compose multiple sub-plugins. Use when one integration needs multiple feature plugins (e.g., `express` combines tracing and code origin plugins).
253+
254+
**Plugin Loading:**
255+
- Plugins load lazily when application `require()`s the corresponding library
256+
- Disable with `DD_TRACE_DISABLED_PLUGINS` or `DD_TRACE_<PLUGIN>_ENABLED=false`
257+
- Test framework plugins only load when Test Optimization mode (`isCiVisibility`) is enabled
258+
259+
**When to Create a New Plugin:**
260+
1. Adding support for a new third-party library/framework
261+
2. Adding a new product feature that integrates with existing libraries (use `CompositePlugin`)
262+
263+
### Creating a New Plugin
264+
265+
```bash
266+
mkdir -p packages/datadog-plugin-<name>/{src,test}
267+
cp packages/datadog-plugin-kafkajs/src/index.js packages/datadog-plugin-<name>/src/
268+
```
269+
270+
Edit `src/index.js`, create `test/index.spec.js`, then register in:
271+
`packages/dd-trace/src/plugins/index.js`, `index.d.ts`, `docs/test.ts`, `docs/API.md`, `.github/workflows/apm-integrations.yml`
272+
273+
## Pull Requests and CI
274+
275+
### Commit Messages
276+
277+
Conventional format: `type(scope): description`
278+
Types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `ci`
279+
Example: `feat(appsec): add new WAF rule`
280+
281+
### PR Requirements
282+
283+
- Use template from `.github/pull_request_template.md`
284+
- Label: `semver-patch` (fixes only), `semver-minor` (new features), `semver-major` (breaking)
285+
- **All tests must pass - all-green policy, no exceptions**
286+
287+
## Vendoring Dependencies
288+
289+
Using rspack: Run `yarn` in `vendor/` to install/bundle dependencies → `packages/node_modules/`
290+
(Some deps excluded, e.g., `@opentelemetry/api`)

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AGENTS.md

0 commit comments

Comments
 (0)