Writing changelog fragments
SkillDev toolsUse when a change in this repo needs a customer-facing changelog entry — e.g. "add a changelog entry" — or when an approved PR that changes user-visible behavior in lib/, ext/, or docs/GettingStarted.md is about to merge.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Writing changelog fragments skill
What this skill tells your AI
The instructions your AI receives, as published by datadog/dd-trace-rb in .claude/skills/write-changelog/SKILL.md and read by ahel’s review.
READ unreleased/README.md FIRST — it is the human-facing description of
the fragment system and its fields. This skill is the agent-facing
checklist.
A fragment may be written as soon as the PR exists, but its grounding is only final when the diff is: re-ground the message against the final diff when the PR is complete and approved, right before merging — grounding and triage see the complete change only there.
The customer
Every message targets one reader: a Ruby developer using this gem in
their application, scanning CHANGELOG.md to decide whether an upgrade
affects them. They read for what changes in their application — what
they observe, do, or get. The exact version they run is their context.
Core principles
- ALWAYS write for the customer, not the diff
- ALWAYS lead with the customer effect; supporting detail follows
- ALWAYS scope who is affected — versions, platforms, triggers, workloads — whenever the scope decides it
- NEVER narrate the implementation Scope names what the customer runs; narrative names what the code does
Grounding
Read the diff and triage first; the answers drive every later step:
-
Effect: what does this PR make true when it lands? If the effect arrives only in a later change (groundwork, flag plumbed but off), there is nothing to write yet
-
Count: one change or several distinct effects? Several → ASK the user to confirm the split before writing several fragments; NEVER bundle several effects into one
<!-- Bad: two effects bundled into one fragment --> Add support for Bundler deployment mode (`bundle install --deployment`) and report UI-oriented injection results. <!-- Good: one effect stated whole; the PR's other effects each get their own fragment --> Add support for Bundler deployment mode (`bundle install --deployment`). -
Product: which product each effect belongs to — by effect, not code location (a core fix to a profiler crash is Profiling); diffuse core-wide →
Core. Closed list:ReleasePrep::Fragment::PRODUCTS(tasks/lib/release_prep/fragment.rb); the integration name (Redis) goes in themessage, not theproduct -
Type:
Added(new capability),Changed(behavior change),Fixed(bug fix); performance changes areChanged, orFixedwhen they restore performance an earlier version had
Then keep every written claim grounded:
- Verify identifiers (settings, classes, methods, env vars, gems) against the hunks, not recalled conventions
- Verify versions against the diff (gemspec, Matrixfile, CI), not ecosystem memory
- Measured numbers: the diff, its benchmark output, or the author's reported result — a benchmark's output often lives only in the PR; no source there, and the claim stays directional
- Verify behavior against the diff's tests — no test, no behavioral claim
- A reproducible defect with no test: ASK the PR to add the test; the behavioral claim waits for it
- A PR that merges without the test grounds its entry in what inspection establishes — the claim caps there, and the review thread notes the missing test
- When a deterministic test cannot reproduce the defect — it depends on timing or uncontrolled external state — verification falls back to inspection: the change the diff adds is the evidence, and the message claims no more than it establishes
- ALWAYS drop or weaken a claim that traces to nothing; NEVER hedge it
Deciding
- ALWAYS add a fragment for new features, behavior changes, and customer-affecting bug fixes
- NEVER add a fragment for internal refactors, test-only, CI/tooling, or
docs outside
docs/GettingStarted.md - SHOULD add when unsure — a reviewer can delete an entry; a missing one leaves customers unaware
Creating the fragment
-
ALWAYS write against a real PR number; with no PR yet, open a draft first — nothing checks the number mechanically
-
ALWAYS update the existing fragment for this effect; NEVER add a second one for the same effect. Fragments for other effects stay untouched
-
ALWAYS generate the scaffold with
bundle exec rake unreleased:new; NEVER create a blank file by handThe placeholders name what each field needs; filled-in references live in
unreleased/examples/. -
ALWAYS set
authorto the external contributor's@-prefixed GitHub handle; NEVER setauthorfor a Datadog contributor
The floor and the standard
bundle exec rake unreleased:lint and unreleased:vale enforce a basic
mechanical floor and it will keep expanding. The floor never
proves an entry good: passing it proves the entry checkable. Every rule
below is the standard, stated on its own terms; some overlap the floor,
none lean on it.
Writing the message
The rules follow in drafting order. When the structure will not fit the caps, keep the customer effect, its scope, and any access point or escape hatch — the compressible rest is the explanatory detail, never the actionable.
-
Structure by type — the reader's question differs:
-
Fixed: ALWAYS name the symptom they recognize, then the trigger they can perform or observe (a call they make, a setting they use); when nothing is observable, bottom out at what the evidence establishes, and NEVER below it, into internal sequencing<!-- Bad: the trigger named, but no symptom to recognize --> Fix `Process.spawn` when passing an environment Hash. <!-- Good: the symptom they recognize + the trigger they perform --> Fix `TypeError` from `Process.spawn` when passing an environment Hash. -
Added: ALWAYS name the capability, then the access point — the setting/API that gets it for them<!-- Bad: the capability stated, but no access point to enable it --> Show class and module names in profiler stack frames (`Foo::Bar#baz` instead of `baz`), making hot methods easier to identify. <!-- Good: capability + example + why it helps + the exact access point --> Show class and module names in profiler stack frames (`Foo::Bar#baz` instead of `baz`), making hot methods easier to identify; enable it with `DD_PROFILING_EXPERIMENTAL_SHOW_CLASSES_ENABLED=true`. -
Changed: ALWAYS name the new behavior and the escape hatch when one exists; SHOULD follow with why it matters<!-- Bad: the new behavior and why, but no way back --> Move the gem's diagnostic logs from stdout to stderr, so stdout stays clean for application output. <!-- Good: new behavior + why it matters + escape hatch --> Move the gem's diagnostic logs from stdout to stderr, so stdout stays clean for application output; restore the old default with `c.logger.instance = Datadog::Core::Logger.new($stdout)` in `Datadog.configure`.
-
-
ALWAYS state what changed, in customer terms; NEVER code-review jargon ("refactored", "cleaned up") or internal file names.
<!-- Bad: code-review terms lead — jargon, file name, the customer claim buried at the end --> Set `Tracing::Metadata::Ext::TAG_KIND` on spans in the `ActiveRecord` `sql` event handler (`events/sql.rb`) to fix missing peer tags for database queries. <!-- Good: customer framing, grounded, code spans --> Fix missing peer tags for database queries traced through `ActiveRecord`. -
ALWAYS open with a present-tense imperative verb (Add, Fix, Support, Improve, ...); NEVER the past tense ("Fixed a crash..."), process-speak ("This PR fixes..."), or subject-first prose ("The gem now supports...", "Also fixes...") — the entry states what the new version does for the customer, not a report of work done
<!-- Bad: the past tense reads as a report of work done, not what it does --> This PR fixes missing peer tags for database queries traced through `ActiveRecord`. <!-- Good: the present-tense imperative opens the entry --> Fix missing peer tags for database queries traced through `ActiveRecord`. -
ALWAYS end the message with terminal punctuation — the entry is a sentence, not a fragment
-
ALWAYS open on the customer's observable delta; the mechanism, scope, and numbers follow it
<!-- Bad: the mechanism opens; the customer's delta never leads --> Enforce a process-wide rate limit across all probes: with multiple probes set, they can emit less than their individual limits allow — combined output is capped at 20 snapshots/s and 5000 log events/s per process. <!-- Good: the customer's observable delta leads, the numbers follow --> Cap probe output process-wide: with multiple probes set, they can emit less than their individual limits allow — combined output is capped at 20 snapshots/s and 5000 log events/s per process. -
ALWAYS make the CamelCase code-span call: span what names the code the customer runs (
ActiveRecord), leave product names bare in prose (Bundler) -
NEVER repeat the product verbatim — with product
AppSec, "Add AppSec detection..." says it twice; lowercase technical phrasing ("GC profiling") is fine<!-- Bad: the product said twice --> Add AppSec detection of response splitting. <!-- Good: the product already renders beside the entry --> Add detection of response splitting. -
ALWAYS name exact versions and platforms when they decide who is affected — NEVER vague quantifiers ("recent", "newer")
<!-- Bad: vague version --> Disable live heap size profiling on recent Ruby versions due to incompatibility. <!-- Good: exact version --> Disable live heap size profiling on Ruby 4.0 due to incompatibility. -
SHOULD back performance claims with measured numbers from the PR's own evidence (Grounding); a claim with no measured number there stays directional
<!-- Bad: the vague quantifier stands in for the directional claim --> Reduce profiler overhead significantly for applications with many idle or blocked threads by skipping samples that would carry no new information; skipped threads are still reported each period. <!-- Good: directional with scope — the PR's evidence carries no measured number --> Reduce profiler overhead for applications with many idle or blocked threads by skipping samples that would carry no new information; skipped threads are still reported each period.
Finishing loop
Run the three steps in order; ANY revision restarts the loop from step 1. Done when a pass makes no revision:
bundle exec rake unreleased:lintandbundle exec rake unreleased:vale— fix every reported violationbundle exec rake unreleased:render— check the rendered entry against every Core principle and every Writing rule in turn, and revise; done when each one is accounted for- Re-run the Grounding triage against the final diff — effect, count, product, and type, then every identifier, version, number, and behavioral claim traced to a specific hunk, benchmark output, or test; drop or weaken each one that traces to nothing. A count that grew means a new effect: the ASK re-fires and it gets its own fragment
Signals
- GitHub stars
- 416
- Forks
- 417
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
write-changelog- Source
- github.com/datadog/dd-trace-rb