Skip to content

Add perf-review skill for pre-PR hot-path performance review#11912

Open
dougqh wants to merge 1 commit into
masterfrom
dougqh/perf-review-skill
Open

Add perf-review skill for pre-PR hot-path performance review#11912
dougqh wants to merge 1 commit into
masterfrom
dougqh/perf-review-skill

Conversation

@dougqh

@dougqh dougqh commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What

Adds a read-only /perf-review Claude Code skill (.claude/skills/perf-review/) that reviews a diff / branch / PR for tracer performance overhead using the performance rubric, and notes the pre-PR performance review under AGENTS.md's PR conventions (alongside /techdebt).

The skill flags hot-path allocation, unbounded memory, repeated work, escaping objects, native-boundary crossings, and JVM-specific pitfalls (escape analysis, JNI / virtual-thread pinning, backtracking-regex ReDoS, varargs/boxing hashing, String.format, ByteBuddy-Advice anti-patterns).

Posture: do-no-harm / assume-hot, advisory (never blocks a merge), precision over recall (silent when unsure), and verify-don't-verdict (every finding routes to Benchmark → Profile → Improve → Guard). It is read-only — it reports ranked findings and never edits code.

Layout

  • SKILL.md — posture, workflow, a PR-style output template, and a compact check index.
  • references/guide.md — the narrative "how": severity model, hotness rubric, categories with examples.
  • references/checks.md — the confidence/severity cost-model: 7 universal checks + Java addendum (J1–J11) + ByteBuddy-Advice fix idioms.
  • references/example-review.md — a worked review to calibrate output.

Usage

Run /perf-review before opening a PR. It computes the branch diff against the upstream merge-base (or reviews specific files you point it at) and reports findings with confidence, severity, the reachability path, and a verify-with suggestion.

Validation

Back-tested against a curated perf-review corpus of merged/closed PRs with known expected verdicts — reproduced the expected precision (false-positive traps stay silent), recall (real regressions flagged), and nuance (severity-regime demotion, resolve-via-sink, correctness-lens routing).

Notes

  • No tracer runtime code changes — this adds a dev-workflow skill (markdown) plus one AGENTS.md line. tag: no release notes.

🤖 Generated with Claude Code

@dougqh dougqh added type: documentation tag: no release notes Changes to exclude from release notes comp: tooling Build & Tooling tag: ai generated Largely based on code generated by an AI or LLM labels Jul 10, 2026
@datadog-prod-us1-6

datadog-prod-us1-6 Bot commented Jul 10, 2026

Copy link
Copy Markdown

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 56.93% (-0.11%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 218da4a | Docs | Datadog PR Page | Give us feedback!

@dd-octo-sts

dd-octo-sts Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

Suite Status
Startup 🟢 pass

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 13.99 s 14.01 s [-0.8%; +0.5%] (no difference)
startup:insecure-bank:tracing:Agent 12.95 s 13.08 s [-1.7%; -0.3%] (maybe better)
startup:petclinic:appsec:Agent 17.48 s 16.59 s [+0.9%; +9.8%] (maybe worse)
startup:petclinic:iast:Agent 17.47 s 17.58 s [-1.4%; +0.2%] (no difference)
startup:petclinic:profiling:Agent 17.46 s 17.21 s [-0.2%; +3.1%] (no difference)
startup:petclinic:sca:Agent 17.56 s 17.53 s [-0.8%; +1.1%] (no difference)
startup:petclinic:tracing:Agent 16.59 s 16.76 s [-2.1%; +0.1%] (no difference)

Commit: 218da4a5 · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

Adds a read-only `/perf-review` Claude Code skill under
.claude/skills/perf-review/ that reviews a diff / branch / PR for tracer
performance overhead using the perf rubric: 7 universal checks + the Java
addendum (J1-J11) + ByteBuddy-Advice fix idioms, with a precision-first,
advisory, verify-don't-verdict posture (do-no-harm / assume-hot).

- SKILL.md: posture, workflow, PR-style output template, and a compact
  check index (pointers into the references).
- references/guide.md: the narrative "how" (severity, hotness, categories).
- references/checks.md: the confidence/severity cost-model + Java addendum.
- references/example-review.md: a worked review to calibrate output.

Also adds a "Review Guidelines" section to AGENTS.md that points at the
skill for the pre-PR performance review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dougqh dougqh force-pushed the dougqh/perf-review-skill branch from 050d46d to 218da4a Compare July 10, 2026 17:47
@dougqh dougqh marked this pull request as ready for review July 10, 2026 17:59
@dougqh dougqh requested a review from a team as a code owner July 10, 2026 17:59
@dougqh dougqh requested a review from PerfectSlayer July 10, 2026 17:59

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 218da4a51f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

container kill. Flag aggressively.

**Raw unbounded cache.** No size or byte bound, keyed by data that grows with load (URL, SQL,
resource names). Fix: `DDCache.newFixedSizeWeightedCache(n, weigher, maxBytes)`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Point cache fixes at DDCaches

This fix path names DDCache.newFixedSizeWeightedCache, but DDCache is only the cache interface; the static factory lives on datadog.trace.api.cache.DDCaches (internal-api/src/main/java/datadog/trace/api/cache/DDCaches.java). When /perf-review flags a SEV-1 unbounded-cache issue, following this guidance would produce an uncompilable recommendation or patch instead of the intended DDCaches.newFixedSizeWeightedCache(...) fix.

Useful? React with 👍 / 👎.


**Mis-sized collections.** `ArrayList` grows 1.5×; `HashMap` doubles and rehashes. Both pay
allocation + copy on growth. Fix: pre-size accurately at construction. Note: `new HashMap<>(n)`
still rehashes at 75% fill — use `HashMap.newHashMap(n)` (JDK 19+) for accurate pre-sizing.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid Java 19 APIs in default fix guidance

The skill is meant to review normal dd-trace-java production changes, whose shared Gradle config still targets Java 8 (gradle/java_no_deps.gradle), so this unconditional fix is unsafe outside explicitly newer source sets: HashMap.newHashMap(int) was added in JDK 19 and will not compile in most modules. A reviewer following this guidance on hot code would suggest an unusable API; prefer a Java 8-compatible capacity calculation/helper unless the target source set is known to be 19+.

Useful? React with 👍 / 👎.


- **J7 — `substring`/slice → `SubSequence` zero-copy view** *(refines #1, #7)*. `String.substring`/`subSequence` allocates a fresh backing array per call. On a hot parse path (headers, tags, query strings, SQL/DBM, propagation) where the slice is **transient** — compared (`equals`/`startsWith`/`contains`/`indexOf`), parsed, or appended, then discarded — a `SubSequence` (offset+length view) is zero-copy and EA-elided **iff** the consumer takes a `CharSequence`/range (else the boundary `toString()` erases the win → add the overload or skip). **flag-as-measure** for the transient case (EA-dependent). The retention trap is **flag-with-confidence**: a `SubSequence` stored in a field/tag/collection/cache pins its *entire* backing String — a small window over a large string is a net memory loss — so a retained view must be materialized or `compact()`'d. Discriminator = transient (view, measure) vs retained (must detach).
- **J8 — Backtracking regex on external input → RE2J / bounded input** *(distinct from #2 compile-per-call)*. `java.util.regex` backtracks → exponential worst-case (ReDoS) on adversarial input — a CPU / tail-latency / DoS hazard, **not** an allocation one. Flag the **conjunction**: (a) input is user/external-controllable AND (b) the pattern is backtracking-prone (nested/overlapping quantifiers, `(a+)+`, unanchored `.*` around a quantified group). **flag-with-confidence** when both hold — SEV-2 (tail latency), **SEV-1** on a per-request AppSec/security-scan path (IAST Reporter / WAF run regex on untrusted input every request). Fix: RE2J (`com.google.re2j`, guaranteed linear; no backrefs/lookaround), anchor/de-nest, or hard-cap input length.
- **J9 — `Objects.hash(...)` varargs / boxing hash on a hot path → `HashingUtils`** *(refines #1)*. `Objects.hash(a, b, …)` is varargs — it allocates an `Object[]` per call and **boxes every primitive** arg; `Arrays.hashCode` and hand-rolled `31*h + …` combines that box also qualify. Per-span tag/key building, or the `hashCode()` of a hot value object, → a guaranteed per-call allocation + boxing. Fix: `datadog.trace.util.HashingUtils` — primitive `hash(long/int/boolean/char/…)` overloads (no boxing), `hash(Object,Object)` and `hash(int,int)` combiners (no array); for >2 fields fold pairwise through `hash(int,int)` (there is no varargs form, by design). flag-with-confidence (array + boxing are mechanism-determined) — SEV-2/3.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't flag allocation-free hash combines

This rule groups Objects.hash with Arrays.hashCode and hand-rolled 31*h + … combines, then describes the whole set as guaranteed allocation/boxing. That is only true for varargs/boxing forms; a hot hashCode() using 31 * h + Long.hashCode(x) or an existing primitive array is allocation-free (and HashingUtils itself uses 31-based combines), so /perf-review would produce false-positive perf findings and recommend replacing already-correct code.

Useful? React with 👍 / 👎.

- **`@Advice.AllArguments()` — deterministic lint.** Materializes a new `Object[]` boxing all arguments on every advised call; always escapes, EA cannot elide it. Fix: `@Advice.Argument(value=N)` for the one argument and type needed.
- **`@Advice.SkipOn(OnDefaultValue.class)` + cached boolean — the preferred feature-flag pattern.** Compute a `static final boolean` once, return it from `@Advice.OnMethodEnter`, suppress exit advice when disabled. Residual cost: one JIT-hoistable field read per call. Recommend this whenever a `Config.get()` shows up in advice.
- **`@Advice.Local` — prefer over `ThreadLocal`.** Carries per-invocation state from `OnMethodEnter` to `OnMethodExit` with no map lookup.
- **`@Advice.Origin` String allocation.** Injects class/method name as a `String` parameter — allocates per invocation at a non-static site. Prefer a `static final String` constant.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop treating string origins as allocations

This flags every @Advice.Origin(...) String as a per-invocation allocation, but Byte Buddy injects origin strings as constants; the per-read recreation warning applies to reflective Method/Constructor origins, not string patterns. dd-trace-java has many hot advices intentionally passing #m strings, so this rule would create false positives and push reviewers toward static constants that cannot represent multi-method advice cleanly.

Useful? React with 👍 / 👎.

@datadog-prod-us1-6 datadog-prod-us1-6 Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Datadog Autotest: PASS

More details

This PR adds a read-only performance-review skill and documentation for pre-PR hot-path analysis. No tracer code changes. All cited utilities (HashingUtils, Strings, Hashtable, RE2J) are verified to exist; "coming" items are properly marked. One documentation inconsistency: example-review.md embeds reachability information as prose rather than the structured bullet-point format the template requires, which could confuse reviewers on output format consistency.

Was this helpful? React 👍 or 👎

Open Bits AI session

🤖 Datadog Autotest · Commit 218da4a · What is Autotest? · Any feedback? Reach out in #autotest

Comment thread AGENTS.md

## Review Guidelines

Before marking a PR ready, run the applicable reviews below over the branch changes (the diff since the merge-base with `master`). These are **advisory, precision-first** checks — they surface high-severity issues early and are not merge gates. The linked guidelines are the tool-agnostic source of truth; in Claude Code each is also runnable as a `/`-skill.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect we'll want to scrub the Claude-specific references, but I'll let others help me out here.

@bm1549 bm1549 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this is a solid use of the progressive disclosure pattern and the AGENTS.md pointer to the skill file will likely get the Codex PR reviewer's attention, although it's not fully deterministic. A future iteration can use something like datadog-agent's agentic code review workflow, but that can be a follow-up task

Mostly left comments on the way the skill is structured, rather than the contents of the skill itself. Once these comments are resolved (and Codex's ones are either solved or dismissed), I think this is good to merge

Comment thread AGENTS.md

## Review Guidelines

Before marking a PR ready, run the applicable reviews below over the branch changes (the diff since the merge-base with `master`). These are **advisory, precision-first** checks — they surface high-severity issues early and are not merge gates. The linked guidelines are the tool-agnostic source of truth; in Claude Code each is also runnable as a `/`-skill.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can drop this - the agents won't need it

Suggested change
Before marking a PR ready, run the applicable reviews below over the branch changes (the diff since the merge-base with `master`). These are **advisory, precision-first** checks — they surface high-severity issues early and are not merge gates. The linked guidelines are the tool-agnostic source of truth; in Claude Code each is also runnable as a `/`-skill.
Before marking a PR ready, run the applicable reviews below over the branch changes (the diff since the merge-base with `master`). These are **advisory, precision-first** checks — they surface high-severity issues early and are not merge gates. The linked guidelines are the tool-agnostic source of truth.

Comment thread AGENTS.md

### Performance Review

Follow the performance-review guidelines in [.claude/skills/perf-review/SKILL.md](.claude/skills/perf-review/SKILL.md) — the do-no-harm / assume-hot posture, the hot-path checks (universal + Java J1–J11 + ByteBuddy-Advice idioms), and the confidence/severity model, with the detailed rubric in its `references/`. Scope: hot-path allocation, unbounded memory, repeated work, escaping objects, native-boundary crossings, and JVM-specific pitfalls. In Claude Code, run `/perf-review`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest moving the skill directory into .agents/skills/, as it's symlinked to .claude/skills/ anyways

Past that, you don't need to tell the agent that it's invocable as a skill - it can infer it from the frontmatter

Suggested change
Follow the performance-review guidelines in [.claude/skills/perf-review/SKILL.md](.claude/skills/perf-review/SKILL.md) — the do-no-harm / assume-hot posture, the hot-path checks (universal + Java J1–J11 + ByteBuddy-Advice idioms), and the confidence/severity model, with the detailed rubric in its `references/`. Scope: hot-path allocation, unbounded memory, repeated work, escaping objects, native-boundary crossings, and JVM-specific pitfalls. In Claude Code, run `/perf-review`.
Follow the performance-review guidelines in [.agents/skills/perf-review/SKILL.md](.agents/skills/perf-review/SKILL.md) — the do-no-harm / assume-hot posture, the hot-path checks (universal + Java J1–J11 + ByteBuddy-Advice idioms), and the confidence/severity model, with the detailed rubric in its `references/`. Scope: hot-path allocation, unbounded memory, repeated work, escaping objects, native-boundary crossings, and JVM-specific pitfalls.

rubric. Use whenever the user wants a performance / overhead / hot-path review,
asks to check a diff or PR for allocation / GC / memory / latency / startup cost,
mentions the "perf rubric" or the "do no harm / assume hot" tracer posture, or is
about to open a PR touching span lifecycle, tag maps, serialization/encoding,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

about to open a PR

While I'm all for this, I'm not sure this is what you intended

about to open a PR touching span lifecycle, tag maps, serialization/encoding,
decorators, propagation, or instrumentation — even if they just say "review this
for perf" without naming the rubric. Advisory and READ-ONLY: it reports ranked,
verify-first findings; it never blocks a merge and never edits code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need to say it doesn't block a merge to the agent

Suggested change
verify-first findings; it never blocks a merge and never edits code.
verify-first findings; it never edits code.

Review the current branch's changes for performance overhead in the dd-trace-java
tracer, using the tracer performance rubric bundled in `references/`. This is a
**low-friction advisory nudge**, not a gate: it reports findings and stops. It
never edits code and never blocks a merge.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
never edits code and never blocks a merge.
never edits code.

span lifecycle (create / setTag / finish), tag-map ops, serialization/encoding, the
metrics/stats path, decorators, propagation (header read/write).

**Cold only with positive evidence:** one-time init, startup-only path, a genuinely

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't necessarily need to grow the scope of this skill, but startup is something we should consider as "hot" too. No need to change anything since this skill won't regress startup, but something to keep in mind for the next performance skill

- **Small, focused diff** (one method, a handful of files): report *every* genuinely
high-confidence finding, ranked by severity. A tight diff with four real allocation
smells should list all four (as the worked example does).
- **Large PR:** lead with the 1–2 highest-severity findings and note that lower-severity

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more can't hurt 😁

Suggested change
- **Large PR:** lead with the 1–2 highest-severity findings and note that lower-severity
- **Large PR:** lead with the 1–3 highest-severity findings and note that lower-severity

## Output format

Follow this structure (see `references/example-review.md` for a full worked instance —
PR #11903, Bucket4j). Showing your suppressed lookalikes and what you cleared is not

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would not recommend having the agent pull a PR just for a format check

Suggested change
PR #11903, Bucket4j). Showing your suppressed lookalikes and what you cleared is not
). Showing your suppressed lookalikes and what you cleared is not

Follow this structure (see `references/example-review.md` for a full worked instance —
PR #11903, Bucket4j). Showing your suppressed lookalikes and what you cleared is not
filler: it demonstrates the precision that makes the findings trustworthy.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not positive if this will apply to the Codex reviewer or not, but it can't hurt to try

Suggested change
When providing suggestions as code review comments, prefix the comments with "perf: "

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: tooling Build & Tooling tag: ai generated Largely based on code generated by an AI or LLM tag: no release notes Changes to exclude from release notes type: documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants