Release Notes Template
A release notes template that communicates changes clearly to users, operators, and stakeholders with categories, upgrade instructions, and known issues.
Use this template to communicate what changed, why it matters, and what users need to do. Pair it with the Changelog Template for ongoing tracking and the Post-Deployment Checklist for verification.
Overview
Release notes are the primary communication channel between the people who build software and the people who use it. I’ve shipped releases where the notes were a pasted git log --oneline — the support tickets that followed cost more time than writing the notes would have. Good release notes answer three questions: what changed, why it matters, and what the reader needs to do. Bad ones dump a commit list and leave users to figure it out.
This template covers:
- Structured sections — highlights, breaking changes, new capabilities, fixes, security
- Audience targeting — different tones for users, operators, and developers
- Upgrade instructions — explicit steps, not assumptions
- Known issues — transparency about what isn’t fixed yet
- Semver alignment — matching notes to version bump type
Release notes and changelogs serve different purposes: a changelog is the running history maintained in the repo, while release notes are a user-facing artifact written for a specific version. Most teams write the release notes first, then roll them into CHANGELOG.md.
Template
# Release vX.Y.Z — [Release Title]
**Release Date:** YYYY-MM-DD
**Status:** [draft / published / deprecated]
## Highlights
2-3 sentences summarizing the most important changes for users.
## Breaking Changes
- **Change:** Description of what changed
- **Impact:** Who is affected and how
- **Migration:** Exact steps to adapt
## New Capabilities
- **Capability name** — short description and link to docs
## Improvements
- **Area** — what got better and by how much
## Bug Fixes
- **Issue #123** — description of the fixed bug
## Security
- **CVE-YYYY-NNNN** — severity and fix summary
## Deprecations
- **Capability X** — will be removed in vX.Y+2.0; use Capability Y instead
## Upgrade Instructions
1. Step 1
2. Step 2
3. Verify with: `command to check`
## Known Issues
- **Issue #456** — workaround available, fix targeted for next release
## Full Changelog
Link to full commit diff or changelog file.
Filled Example
# Release v2.5.0 — CSV Export & Performance Improvements
**Release Date:** 2026-07-15
**Status:** published
## Highlights
Added CSV export for analytics dashboards and improved dashboard load time
by 40%. Fixed a bug where date ranges crossing month boundaries produced
empty exports.
## Breaking Changes
- **Change:** `/api/v1/reports` now returns a `metadata` object instead of flat fields
- **Impact:** Any client parsing the response as flat JSON will break
- **Migration:** Update response parsing to read from `metadata`. See migration guide.
## New Capabilities
- **CSV Export** — export any dashboard chart as CSV. See [docs](/recipes/export-csv-excel/).
- **Saved Filters** — save and name dashboard filter presets for reuse.
## Improvements
- **Dashboard load time** — reduced from 3.2s to 1.9s (p95) by lazy-loading charts
- **API rate limits** — increased from 100 to 200 req/min for Pro tier
## Bug Fixes
- **#234** — CSV export downloads empty file when date range crosses a month boundary
- **#231** — saved filter names truncate at 20 characters
- **#228** — dashboard crashes when switching from table to bar chart with 0 rows
## Security
- **CVE-2026-1234** — XSS in chart tooltip rendering (High). Fixed by escaping
user input in tooltip templates.
## Deprecations
- **`/api/v1/reports` flat response format** — removed in v3.0.0. Use `metadata`.
See [API Deprecation Notice](/docs/api-deprecation-notice-template/).
## Upgrade Instructions
1. Update the SDK: `npm install @stackpractices/sdk@2.5.0`
2. If using `/api/v1/reports`, update response parsing (see migration guide)
3. Verify: `curl -H "Authorization: Bearer $TOKEN" https://api.example.com/v1/reports | jq .metadata`
## Known Issues
- **#240** — CSV export fails for charts with more than 10,000 rows.
Workaround: narrow the date range. Fix targeted for v2.5.1.
## Release Metrics
- Commits: 47 · Contributors: 6 · Issues closed: 12 · PRs merged: 31
## Full Changelog
https://github.com/example/repo/compare/v2.4.1...v2.5.0
Audience-Specific Versions
| Audience | Focus | Tone |
|---|---|---|
| End users | New capabilities and bug fixes that affect their workflow | Friendly, benefit-oriented |
| Operators | Breaking changes, upgrade steps, security fixes | Precise, action-oriented |
| Developers | API changes, deprecations, library updates | Technical, detailed |
You don’t need three documents — one release note can serve all three if you order sections so each audience finds their part first. Breaking changes and upgrade instructions lead; developer-only details (dependency bumps, internal refactorings) can live in the linked changelog.
The same change, three voices
Take a breaking change — the /api/v1/reports response format switch — and notice how the same fact lands differently per audience:
- End user: “Reports now include metadata like creation date and filters used. No action needed — your exports keep working.”
- Operator: “
/api/v1/reportsreturns ametadataobject instead of flat fields. Update response parsing before v3.0.0; old format deprecated, removal date announced below.” - Developer: “
GET /api/v1/reports— response schema changed.metadataobject wrapscreated_at,filters,row_count. Flat fields removed in v3.0.0. Migration guide linked.”
Same change, same release — three entry points into the same document.
Semver Alignment
| Version bump | When to use | Release notes focus |
|---|---|---|
| MAJOR (X.0.0) | Breaking changes | Migration guide, breaking changes section first |
| MINOR (X.Y.0) | New functionality, backward-compatible | New capabilities, improvements |
| PATCH (X.Y.Z) | Bug fixes, backward-compatible | Bug fixes, security patches |
A mismatch between the bump and the notes erodes trust fast. If your notes list a breaking change, the version must be a MAJOR — shipping it as a PATCH trains users to fear every update.
What Works
- Write release notes before the release — they’re a forcing function for final review. I’ve caught undocumented breaking changes just by trying to write the highlights paragraph.
- Highlight breaking changes first — users scan for things that might break them.
- Include upgrade instructions — even for “no action required” releases, say so explicitly.
- Link to documentation — every new capability should have a corresponding docs page. Use the API Documentation Template for reference.
- Include verification commands — give users a way to confirm the upgrade worked.
- Date every release — ISO 8601 format (
YYYY-MM-DD) for consistency. - Link to the full diff — let developers dig into commit-level details if they want.
- Quantify improvements — “40% faster” means something; “various improvements” means nothing.
- Keep a running draft during the sprint — entries written at merge time read better than entries reconstructed at release time.
- Write the breaking-changes section even when it’s empty — “None in this release” is information.
Common Mistakes
- Listing every commit — users don’t care about “refactor helper function”.
- Omitting breaking changes — this breaks trust and causes incidents. See the Incident Postmortem Template for recovery procedures.
- No upgrade instructions — users waste time guessing the migration path. See the Environment Setup Guide for configuration context.
- Skipping known issues — transparency builds trust; hiding problems destroys it.
- Vague descriptions like “various improvements” — name the improvement and quantify it.
- No link to a migration guide for breaking changes — users need a step-by-step path.
- Publishing notes that only the author understands — if the person on support can’t paraphrase it, rewrite it.
From Git Log to Release Note
The hardest part isn’t the template — it’s turning raw commits into something a user cares about. Here’s what the translation looks like:
| Commit message | Release note entry |
|---|---|
feat(reports): add csv exporter | CSV Export — export any dashboard chart as CSV from the share menu |
fix: date boundary bug in export | #234 — CSV export downloaded an empty file when the date range crossed a month boundary |
perf: lazy load chart components | Dashboard load time — reduced from 3.2s to 1.9s (p95) by lazy-loading charts |
chore: bump express to 4.21 | (omit — no user-visible change) |
refactor: extract report service | (omit — internal change) |
feat(api)!: wrap response in metadata | Breaking: /api/v1/reports now returns a metadata object — update parsing before v3.0.0 |
fix(security): escape tooltip input | CVE-2026-1234 — XSS in chart tooltips (High); update to v2.5.0 |
The rule: every entry gets a name a user would search for, a benefit or symptom they recognize, and a number (issue ID, metric, version) that makes it verifiable. Internal work stays in the changelog.
A practical triage: read each merged PR title once and tag it user-facing, internal, or needs migration. If you can’t tell which from the title, it’s probably internal — or the PR title needs a rewrite, which is its own signal worth feeding back to the team.
Variants
Automated release notes (release-please, semantic-release)
Tools like release-please and semantic-release generate release notes from conventional commit messages. Use commit prefixes (feat:, fix:, BREAKING CHANGE:) and the tool auto-generates categorized notes. Review and edit the output before publishing — automated notes are a starting point, not a finished product.
Internal service release notes
For internal services, add deployment context: which environments were updated, rollback procedure, feature flags enabled/disabled, and links to monitoring dashboards. Operators need to know what changed in their environment, not just what changed in the code. A useful internal header adds: Deployed to: staging 2026-07-14, production 2026-07-15 · Rollback: revert image tag to v2.4.1 · Flags: csv-export on for Pro tier · Dashboard: link to the release's Grafana panel.
Hotfix release notes
Hotfixes need a shorter format: “Fixed [bug] that caused [symptom]. Deployed to [environment]. Monitoring for [metric]. Rollback: [command].” No highlights or new capabilities — just the fix and the operational context. A real one looks like: “Fixed null-pointer crash on checkout when a discount code expired mid-session. Deployed to production at 14:30 UTC. Monitoring checkout error rate. Rollback: helm rollback checkout 41.”
How to Adopt This Template
Start small: pick the template sections that apply to your next release and delete the rest. An empty “Security” section in a patch release isn’t required — the skeleton is meant to be pruned, not filled blindly.
The sequence that works in practice:
- Enable conventional commits in CI (commitlint or a PR title check) so automation has something to parse.
- Draft notes from commits a week before release — release-please does this automatically; otherwise copy the git log and sort entries into the template sections.
- Assign an owner per release: one person rewrites the draft for the target audience and prunes internal-only commits.
- Review breaking changes and security entries with whoever owns the affected surface — they confirm the migration steps actually work.
- Publish where users look — GitHub Releases for developer audiences, the docs site for operators, in-app notices for breaking changes.
After two or three releases the draft step collapses: conventional commits plus a template means most entries arrive pre-sorted, and the owner’s job shrinks to rewriting highlights and confirming migration steps.
A realistic first month looks like this: week one, turn on commitlint and copy this template into the repo; week two, publish your first notes with it — expect the rewrite pass to take an hour; weeks three and four, tighten the filters as chore commits slip through and adjust which sections you keep.
Troubleshooting
- Nobody reads the release notes: they’re probably written for the wrong audience. Lead with the breaking changes and what the reader must do; move the feature list below. Also check where they live — a
CHANGELOG.mdin the repo won’t reach users who never open the repository. - Release notes lag the release by days: draft from commits during the sprint, not after the tag. If you’re using release-please, the draft PR already exists — the remaining work is rewriting highlights, which takes minutes.
- Notes and GitHub Releases are out of sync: pick one source of truth. Publish release notes on the Releases page or docs site first, then roll them into
CHANGELOG.md— never maintain both independently. - Automated notes are full of chore commits: tighten the conventional commit rules in CI so internal work uses
chore:/ci:/refactor:prefixes that the generator filters out. - A breaking change slipped into a patch release: acknowledge it immediately in the notes and the known issues, publish the migration path, and bump your release process so the breaking-change check happens before tagging.
- The notes read like internal documentation: full of service names, ticket IDs, and jargon users don’t know. Rewrite each entry around what the user sees in the UI or API, and move internal details to the changelog link.
- Translations lag behind the English notes: publish in the primary language first and flag the translated notes with the release date — stale translated notes that show an older version as “latest” erode trust faster than missing translations.
Release Day Checklist
- Release notes drafted and reviewed for the target audience
- Breaking changes have migration steps verified by the affected owner
- Security fixes include CVE numbers and severity
- Upgrade instructions tested on a clean environment
- Version bump matches the notes’ content (semver check)
- Published on the channel users actually read
- Entries rolled into
CHANGELOG.md - Known issues listed with workarounds and target fix versions
Production Notes
- Announce breaking changes through more than one channel — release notes alone won’t reach users who never visit the Releases page. In-app banners and email work better for migrations.
- Keep a “no action required” line for routine releases — silence makes users suspicious that instructions are missing, not that they’re unnecessary.
- Watch support volume after publishing — a spike in “how do I upgrade” tickets means the instructions section needs work, not that users are careless.
- Rotate who writes the notes — the same person tends to write for themselves. Rotating the author keeps the language honest.
- Archive every published note — when a customer reports a regression three versions later, you’ll want to know exactly what they were told.
Key Takeaways
- Write for the reader’s upgrade decision: what changed, whether it affects them, and what to do — in that order.
- Quantify everything you can: “3.2s to 1.9s” beats “faster”; “47 commits” beats “lots of work”.
- Automate the sorting, not the writing: let conventional commits categorize, then let a human decide what matters.
- Transparency about known issues is cheaper than the support tickets that arrive when users discover them alone.
Further Reading
- Keep a Changelog — the reference format behind
CHANGELOG.mdconventions - Semantic Versioning 2.0.0 — the version-number rules your notes should align with
- release-please documentation — automated release note generation from conventional commits
- Companion resources — downloadable template, filled example, and hotfix format
Common Production Pitfalls
- Leaving required sections blank instead of removing them — an empty “Security” section reads like a bug.
- Writing notes once and never updating them when the release scope changes late in the sprint.
- Storing release notes where users don’t look — a wiki page nobody bookmarks loses to a Releases page every time.
- Copying the previous release’s notes and forgetting to remove stale sections.
- Skipping the “no action required” line, which makes users hunt for instructions that don’t exist.
- Publishing without a review pass from whoever owns the affected surface — migration steps that don’t work are worse than none.
Frequently Asked Questions
How detailed should release notes be for internal services?
Internal services need the same structure but with additional deployment context: which environments were updated, rollback procedure, and links to monitoring dashboards. See the CI/CD Pipeline Guide for deployment automation.
Should I include CVE numbers for security fixes?
Yes, always. Security-conscious users need to cross-reference with their vulnerability scanners. Include the CVE, severity score, and a brief description of the attack vector.
Do patch releases (0.0.X) need full release notes?
Yes, but they can be shorter. A patch release note needs: "Fixed [bug] that caused [symptom]. Upgrade recommended. No breaking changes."
Should release notes go in the repository or on a website?
Both. Keep a CHANGELOG.md in the repository for developers browsing the code. Publish release notes on a website or mailing list for users who don't read git repos. The website version can be more user-friendly; the CHANGELOG can be more technical.
How do I communicate release notes to users who don't read docs?
Use multiple channels: in-app notifications for breaking changes, email for major releases, and a Slack/Discord announcement for community projects. Don't rely on a single channel — users miss things.
What if a release introduces a regression?
Acknowledge it quickly. Add the regression to "Known Issues" with a workaround if available and a target fix version. If the regression is severe, pull the release and re-publish once fixed — a fast, honest correction builds more trust than silence.
How do we decide what to include in release notes?
Include anything a user can see or must act on: new features, visible improvements, bug fixes, breaking changes, and security fixes. Skip internal refactors, dependency bumps without behavior changes, and typo fixes — those belong in the changelog.
How often should we publish release notes?
With every release. Continuous deployers can batch into weekly or per-sprint notes; semver projects publish per version. Keep one format so users learn where to scan.
How do we automate release notes generation?
Use conventional commits and a tool like release-please, changesets, or semantic-release to draft categorized notes. Automate the sorting; keep a human for the highlights and migration steps.
Related Resources
Changelog Template
A structured changelog template following Keep a Changelog conventions for tracking project releases.
DocBug Report Template (Markdown + Examples)
A structured bug report template to help teams reproduce, triage, and resolve defects faster — with a filled example and a GitHub issue form variant.
DocPost-Deployment Verification Checklist Template
A checklist template for verifying deployments: health checks, smoke tests, metric validation, and rollback readiness before declaring all-clear.
GuideCI/CD Pipeline Guide
A practical guide to building CI/CD pipelines with GitHub Actions, testing, deployment strategies, and rollback procedures.
DocRollout Communication Template
A template for release notes and stakeholder updates during capability rollouts.