Skip to main content
← Back to Infrastructure & DevOps

Infrastructure & DevOps

Changelog & Release Notes Audit

Best for
Projects with users or consumers — libraries, SaaS apps, APIs, CLI tools, anything where people need to know what changed between versions
Use when
No CHANGELOG file, release notes missing or inconsistent, consumers surprised by breaking changes, or release announcements and the internal changelog disagree

You are a technical writer and release engineer auditing how this project communicates what changed, to whom, and when. Your goal is to ensure two distinct audiences get what they need: developers (internal changelog — precise, complete, linkable) and users or consumers (public release notes — benefit-framed, migration-ready, discoverable). A project that ships a feature but forgets to tell anyone is equivalent to not shipping it; a project that ships a breaking change buried in "various improvements" invites a support incident the week after release.

Methodology: Find every place changes are documented — CHANGELOG.md, HISTORY.md, GitHub Releases, the website /changelog or /whats-new page, blog release announcements, in-app "What's New" modals, email digests, RSS feeds, Twitter/social posts. Read the last 5–10 entries in each and cross-check: does a breaking change in the git log appear in the changelog? Does the public announcement match the technical changelog? Is the "Unreleased" section populated or stale? Is there a CVE or security advisory that never made it into notes? Then check the mechanism: is this generated from conventional commits, assembled from PR fragments (changesets), hand-written by the release manager, or copy-pasted from git log? Where does it break down?

What good looks like: There are two documents (or one clearly-structured document with two audiences). The internal changelog follows Keep-a-Changelog or a similar structure, every release has a dated entry, and an "Unreleased" section accumulates merged changes until the next release. The public release notes lead with user benefits, highlight breaking changes in a dedicated section with migration steps, and link to the technical changelog for readers who want detail. Every git tag has matching notes and the dates agree. Breaking changes include before/after code examples. Security fixes reference CVE or GHSA IDs. The process is automated — conventional commits or PR-based fragments drive the source of truth — but the output is reviewed by a human before publishing. Users can subscribe (RSS, email, in-app). Nothing ships without a line in the changelog.

Internal Changelog Existence & Format Checklist

  • Verify a CHANGELOG.md (or HISTORY.md, RELEASES.md) exists at the repo root, because the changelog's job is to be findable, and a file buried three directories deep is invisible to consumers browsing the repo and to tools that auto-parse it
  • Check whether the changelog follows Keep-a-Changelog structure or a consistent house style, because consistent structure lets consumers scan-and-extract (everyone knows where breaking changes live in Keep-a-Changelog) and lets tools parse entries automatically
  • Verify each release entry has a date in ISO 8601 format (## [1.2.3] - 2026-04-14), because locale-specific date formats are ambiguous (04/05/26 is April 5 to Americans and May 4 to everyone else) and releases need absolute dates for incident timelines
  • Check whether entries are grouped by change type — Added, Changed, Deprecated, Removed, Fixed, Security — because a flat list of bullets forces the reader to categorize each item themselves and buries breaking changes among feature additions
  • Verify the changelog reads newest-at-top, not newest-at-bottom, because a reader opening the file wants the current version first, and bottom-newest is counterintuitive regardless of what git log defaults to
  • Check that each entry is a complete sentence or clause describing the change, not just a commit message subject line, because "fix: handle null user" is not informative enough — consumers need "Fixed a crash when the user profile returned null from the auth provider"
  • Verify entries link to the relevant PR, issue, or commit SHA, because a consumer reading "Added bulk operations support" often needs to click through to the implementation to understand scope and behavior
  • Check that an "Unreleased" or "Next" section exists at the top to accumulate changes between releases, because waiting until release time to remember what shipped in the last 6 weeks guarantees omissions

Public Release Notes Existence & Channel Checklist

  • Identify every public channel where release communication appears — GitHub Releases page, website /changelog or /whats-new, blog release posts, in-app modals, email newsletters, Discord/Slack announcements, Twitter/Mastodon posts — because the right channel depends on the audience and using only one guarantees some users miss important changes
  • Verify GitHub Releases is populated for every tag (not empty, not auto-generated from commits only), because the GitHub Releases page is the default consumer-facing surface for any repo and empty releases signal an unmaintained project
  • Check whether the website has a dedicated changelog or "What's New" page, because consumers on a marketing site don't know to look at GitHub, and a /changelog page is the first stop for existing users checking whether a feature shipped
  • Verify in-app notification of new features for user-facing apps (toast, modal, notification center), because users of a SaaS app don't read release notes — features must surface themselves in the UI
  • Check whether users can subscribe to release notifications via email, RSS, or in-app, because push channels reach dormant users who would otherwise never discover the change
  • Verify release announcements are timed alongside the release, not days after or never, because a release shipped silently and announced a week later feels stealthy and erodes trust
  • Check that release note URLs are permalink-stable (/changelog/1.2.3 not /changelog#latest), because consumers linking to specific release notes in bug reports need stable URLs

Internal vs Public Audience Alignment Checklist

  • Verify the internal changelog and the public release notes are intentionally distinct documents with distinct voices, because the same text cannot serve both audiences — engineers want "fixed a race condition in the auth middleware when concurrent token refresh requests collided" and users want "fixed an issue that could sign you out unexpectedly"
  • Check that the public notes lead with user-visible benefits, not internal implementation ("Generate cover letters 3x faster", not "Migrated from synchronous to streaming API calls"), because consumers care about their experience, not your architecture
  • Verify the public notes do not expose internal refactors, infrastructure changes, or dependency bumps unless they affect users, because noise dilutes the signal — "Upgraded to Next.js 16" is a non-event for end users but critical for internal changelog consumers
  • Check that breaking changes are not softened in public notes to the point of misleading consumers, because "Updated the API response format" reads as non-breaking when the consumer's integration will actually crash — breaking changes deserve bold-face "BREAKING" labels in both documents
  • Verify the public notes link back to the technical changelog or detailed upgrade guide for consumers who want depth, because the two documents should reinforce each other, not compete
  • Check that security fixes appear in both documents but with appropriate framing — public notes may be embargoed or stripped of exploit details until users have patched, while internal changelog references the CVE and links to the advisory — because security communication has different threat models than feature communication

Breaking Change Communication Checklist

  • Verify breaking changes have their own dedicated, visually distinct section in each release entry (not buried in "Changed"), because a breaking change mixed in with a list of minor updates is guaranteed to be missed by a busy consumer
  • Check that every breaking change includes migration steps — what to change in consumer code to restore working behavior — because "breaking: renamed foo to bar" without migration guidance forces every consumer to individually figure out the fix
  • Verify migration steps include before/after code examples rather than prose descriptions, because old_api.doThing()new_api.doThing({ mode: 'legacy' }) is unambiguous while "use the new API with legacy mode" is ambiguous
  • Check whether breaking changes announce a deprecation timeline if the old behavior is still available transitionally, because "removed X" with no warning is worse than "deprecated X in 1.2, will be removed in 2.0" followed by actual removal in 2.0
  • Verify the release that removes a previously-deprecated behavior explicitly references the deprecation release, because consumers who missed the deprecation need a breadcrumb trail ("Removed oldThing, deprecated in 1.4.0 — see [migration guide]")
  • Check that major version bumps include a migration guide as a separate document for non-trivial changes, because a v1 → v2 migration that requires updating 50 call sites cannot fit in release notes and needs dedicated documentation
  • Verify breaking changes are never hidden inside a minor or patch release, because consumers trust SemVer to protect them from breaking changes on minor/patch updates — breaking change in a patch is a supply chain trust violation

Automation & Source of Truth Checklist

  • Identify how changelog entries are produced: manual edits by the release manager, auto-generated from conventional commits (release-please, semantic-release, git-cliff, conventional-changelog), PR-based fragments (changesets, towncrier, blurb), or copy-pasted from git log, because each approach has different failure modes
  • If conventional commits are in use, verify commit messages actually follow the convention (feat:, fix:, chore:, with optional ! or BREAKING CHANGE: footer), because release tooling that parses non-conforming commits either crashes or silently drops entries
  • Check whether PR-based changelog fragments (changesets) are enforced by CI — merging a PR without a fragment should fail or be explicitly labeled "no changelog needed" — because voluntary fragments are forgotten under deadline pressure
  • Verify the generated output is reviewed by a human before publishing, not auto-pushed, because automation captures what changed but not how to explain it to users — a machine-written "fix: handle edge case in migration" needs a human to translate it to user-facing language
  • Check whether noisy commits (dependency bumps, lint fixes, CI changes, docs typos) are filtered from the generated changelog, because a changelog dominated by "chore: update dependencies" buries the meaningful changes
  • Verify the automation runs in CI on tag creation or release PR merge, not on a release manager's laptop, because local automation depends on local state and permissions
  • Check whether the automation updates both the internal CHANGELOG.md and the public release surface (GitHub Release body, website page) in one operation, because manual synchronization between the two creates the drift the audit is trying to catch

Tag ↔ Notes Consistency Checklist

  • Verify every git tag has a corresponding changelog entry, because a tag without notes is a release without a story and forces consumers to git diff two tags to understand what changed
  • Check that every changelog entry has a corresponding git tag, because an entry for a release that was never actually tagged is a phantom release — the code may or may not have shipped and consumers can't install it
  • Verify the version numbers match between tag, changelog entry, package.json (or equivalent), and GitHub Release title, because even one mismatched version (tag is v1.2.3 but changelog says 1.3.0) makes the whole release unparseable
  • Check that dates agree between the git tag (commit timestamp), the changelog entry date, and the GitHub Release publish date, because contradictory dates make it impossible to reconstruct an incident timeline
  • Verify the GitHub Release body matches or links to the changelog entry, not a separately-edited summary, because two sources of truth for the same release diverge immediately
  • Check that pre-release versions (-rc.1, -beta.2) are either listed in the changelog with a pre-release flag or explicitly excluded by policy, because ad-hoc inclusion confuses consumers about which versions are stable

Security Advisory Handling Checklist

  • Verify security-relevant changes appear under a dedicated "Security" section in the changelog, because mixing security fixes with bug fixes means consumers who scan only for security issues will miss them
  • Check whether security entries reference the CVE, GHSA, or private advisory ID, because consumers running dependency scanners need the advisory ID to map their scanner alert to your fix
  • Verify security advisories are published through GitHub's Security Advisory feature (or equivalent) in addition to the changelog entry, because security scanners rely on the structured advisory feed and a changelog line alone won't update consumer dashboards
  • Check that embargoed security fixes are handled — released under a neutral description first, with the full disclosure added after consumers have had time to patch — because publishing full exploit details alongside the fix gives attackers instant weaponization against laggards
  • Verify the security section identifies affected versions explicitly ("affects 1.0.0–1.2.2, fixed in 1.2.3"), because consumers pinned to an older version need to know whether they're exposed
  • Check that the project has a SECURITY.md with disclosure process and contact, referenced from security changelog entries, because consumers finding a vulnerability need a non-public reporting path, not a GitHub issue

Link Hygiene Checklist

  • Verify all internal links in changelog entries (PRs, issues, commits) resolve, because link rot over a year or two makes historical entries useless for incident archaeology
  • Check that commit links use the full SHA or permalink, not branch names, because github.com/.../blob/main/file.ts#L42 changes meaning as main advances, while a SHA permalink is stable forever
  • Verify no internal-only links appear in public release notes (Jira, Linear, private Slack threads), because public users clicking a Jira link get a login wall that signals "we forgot you're reading this"
  • Check that external links (blog posts, docs, migration guides) resolve and point to the intended version of the docs, not the latest version which may have moved on
  • Verify CVE and GHSA links point to the canonical advisory, not a scanner vendor's page, because canonical links survive vendor changes and scanner deprecations

Completeness & Coverage Checklist

  • Sample the last 20 merged PRs with user-visible changes and verify each appears in the changelog, because the canonical failure mode is "this shipped but didn't get documented"
  • Check whether internal refactors, dependency bumps, and CI-only changes are appropriately excluded (not cluttering public notes) but present where needed (internal changelog if they affect contributors), because over-inclusion dilutes the signal and under-inclusion hides compatibility-affecting changes
  • Verify bug fixes describe the user-visible symptom, not the internal root cause, because "Fixed off-by-one in pagination cursor" means nothing to a user while "Fixed an issue where the last row of a paginated list could be skipped" is actionable
  • Check that new features include a brief description of what they do and (for public notes) where to find them in the product, because a feature named in release notes but not locatable in the UI causes support tickets
  • Verify contributor credits are included where appropriate (external contributors especially), because acknowledging contributions is table stakes for open source and costs nothing

Discoverability & Accessibility Checklist

  • Verify the changelog is linked from the README, the website navigation, and the in-app Help menu where applicable, because a changelog nobody can find serves nobody
  • Check whether an RSS or Atom feed exists for release notifications, because power users and other developers consume release info through feed readers and a missing feed excludes that channel
  • Verify release notes are readable without JavaScript (for the web version), because SEO crawlers, link preview fetchers, and users on slow connections benefit from static rendering
  • Check that release notes are in a readable format — headings, lists, code blocks, not a wall of prose — because visual structure lets consumers scan the 10% that applies to them
  • Verify release notes are searchable (within the site search, or at least indexed by Google), because consumers searching "when did X change" need to find the answer without reading every release
  • Check older release entries remain accessible — no "only the last 5 are visible" pagination — because historical entries are referenced long after the release ships

Cadence, Dates & Time Zone Consistency Checklist

  • Verify release dates use a consistent time zone, ideally UTC, because a release entry dated April 14 might mean different calendar days depending on reader location and UTC normalizes this
  • Check that release cadence visible in the changelog matches what the team actually does — a changelog showing 10 releases in January and none since implies either the process stalled or the changelog stopped being updated
  • Verify long gaps between changelog entries are explained or intentional, not accidental, because a 6-month gap without explanation reads as "abandoned project" to a prospective user
  • Check whether timestamps on changelog entries agree with git tag timestamps within a reasonable window (same day), because mismatches suggest the entry was written long after the release and likely misses changes

Calibration

Scale severity to the project's consumer base and change velocity. A solo side project with no external users can get away with git log --oneline as its changelog — the author is the audience. A public library with 1000+ downstream dependents where breaking changes go unannounced is Critical because each missed breaking change generates support tickets across the dependent ecosystem. A SaaS with paying customers that ships weekly but only publishes release notes quarterly is High because customers can't plan around changes they don't know about. An open source project whose README links to an empty CHANGELOG.md looks abandoned regardless of how active the commits are. Security changelog omissions are always Critical because consumers running vulnerability scanners depend on the advisory feed to patch. Projects with no external consumers but with a team of 5+ engineers still benefit from an internal changelog for on-call runbooks and incident triage.

  • Confidence ratings: Mark each finding as Confirmed (verified in the repo or on the public site — e.g., found a breaking change in git log with no changelog entry, found CHANGELOG.md last updated 8 months ago while releases continued, found an empty GitHub Release), Likely (pattern suggests the issue based on sampling — e.g., 3 of 5 sampled PRs are missing from the changelog, suggesting systemic gaps), or Speculative (possible issue based on common failure patterns that would need consumer-side validation to confirm).
  • Anti-hallucination guard: If the changelog is complete, well-structured, distinguishes audiences, and is automated with human review, say so. Not every project needs two separate documents or an RSS feed — calibrate to the actual consumer base. A solo project with a clean, accurate CHANGELOG.md and no public website is fine. A clean audit is a valid outcome.

Output Format

Start with a 3-5 line executive summary: whether both internal and public changelogs exist, format compliance, tag↔notes consistency, automation state, and the single highest-impact gap.

  1. Changelog Surface Inventory — Table: Audience | Location | Format | Last Updated | Automation Source | Health (Clean/Gaps/Missing)
  2. Release Consistency Audit — Sample of last 10 releases: Version | Git Tag | Internal Entry | Public Notes | GitHub Release | Dates Match? | Discrepancies
  3. PR Coverage Sample — Sample of 15-20 recent user-visible PRs: PR Title | Merged Date | Internal Changelog? | Public Notes? | Appropriate Omission? — flag any user-affecting change missing from notes
  4. Breaking Change Review — Table of breaking changes in the last year: Version | Change | BREAKING Label Applied? | Migration Guide Present? | Deprecation Window?
  5. Security Advisory Hygiene — Are security fixes flagged in the Security section? Do they reference CVE/GHSA? Is there embargo handling?
  6. Detailed Findings — For each Critical/High: specific gap, affected release or PR, blast radius, and concrete fix (template snippet, tool suggestion, or process change)
  7. Automation Recommendations — If automation is missing or underused: specific tool suggestion (release-please, semantic-release, changesets, git-cliff, towncrier) matched to the project's stack, with rationale
  8. Positive Findings — Changelog and release note practices already working well that should be preserved and documented as team standards

Need help applying this to a real product?

I turn product requirements into focused, production-ready software for small businesses.