From Git Commits to Release Notes
A commit says a change happened. A release note says what it means — and the only way to bridge them is to ask.
Here is a commit message from a real codebase:
Refactored auth module to consolidate token validation logic and reduce redundancy in middleware chain.
Here is what the release note for that change should say:
Authentication is now faster. API requests that previously required multiple validation steps complete in a single pass, reducing latency on secure endpoints by approximately 30ms.
These describe the same change. One is written for developers who maintain the code. The other is written for developers who use the product. The translation between them is a core technical writing skill, and it’s not as hard as it looks once you know what to look for.
Most refactors never get this far. They change nothing a user can see or measure, and they belong in the changelog. This one gets a release note because it made something faster in a way a customer would notice. That test decides every entry you write: does anything the user does or experiences change?
What makes the note work? The 30ms figure isn’t in the commit message. It isn’t anywhere in the commit. The writer got it by asking the developer what the change actually did for users. That’s your job, right there in one line. Commit messages record that a change happened. They almost never say what it means, and the only way to find out is to ask.
Changelogs vs. Release Notes
These terms get conflated, but they serve different purposes.
A changelog is a chronological record of every change to a codebase, primarily a tool for developers. It can be auto-generated from commit messages. It doesn’t need to be readable by end users; it needs to be complete for developers to read and understand.
Release notes are communications to users about what changed and what those changes mean for them. They’re selective, covering only changes that affect how users interact with the product.
Plenty of teams publish both. The changelog lives in the repository. The release notes go to customers.
Translating Technical Changes
The translation from commit message to release note requires answering one question: what does this change mean for the person using the product?
A refactored authentication module means faster login and API calls. A bug fix to the file upload handler means uploads no longer fail when the filename contains special characters. A new endpoint at /v1/webhooks/bulk means users can register several webhooks in one call instead of one call per webhook. In each case, the behavior that changed is the important part, and the code path that changed is not.
When in doubt, go back to the test: does anything the user does or experiences change? If yes, the change should be included in the release notes. If no, it doesn’t.
Getting the “What It Means”
The 30ms in that opening example is the important point, and it’s the part no tool can produce, because it doesn’t exist in the commit. It exists in the head of the engineer who made the change. Getting it out is an interview skill, and it’s what you add to the task.
Before you ask anyone, read the commit message. The pull request usually says more than the commit message, and the ticket the PR closes often states the problem in user terms: “customers on large accounts report the dashboard taking eight seconds to load.” That’s your release note’s before-state, already written by someone else. The design doc, if there is one, carries the why. A lot of “asking the developer” is really finding the answer they already wrote down.
When you do ask, ask concrete questions the engineer can answer in a sentence:
What can a user do now that they couldn’t before?
What would a user have noticed going wrong before this fix?
Is there a number: faster by how much, from what to what?
Does anyone have to change their own code because of this change?
That last question is how you catch breaking changes before your users do. Push on the number question. “Reduces latency by about 30ms” earns user trust in a way “improves performance” won’t. By the way, if there isn’t a measured number, don’t invent one. “Faster” you can verify is always better than a figure you can’t.
Timing matters here. Catch the change at the pull request, while it’s fresh, not at release time when the engineer has moved on to three other things and has to reconstruct what they did a month ago. The writers who produce good release notes are usually the ones reading PRs as they surface.
Structuring a Release Note
A well-structured release note has three sections, each handling a different category of change. They’re listed here in the order users read them, not the order you’ll write them.
Breaking changes. Changes that will cause previously working user workflows to fail. These get their own section and go first. For any breaking change, include what broke, what users need to do to update their integration, and a link to migration documentation.
New features, or What’s new. Capabilities that didn’t exist before. Lead with what the user can now do, not with the feature name.
Fixes. Issues that affected users and have now been resolved. Describe the symptom that was occurring, not the code change that resolved it.
Breaking changes are where inadequate release notes cause real damage. A user who hits one without warning and without migration guidance will blame the documentation before they blame the product team, and they’ll be at least partially right.
Bad Note, Good Note
The translation is easier to see in pairs. On the left, what the commit or a lazy writer produces. On the right, what the user needs.
The pattern across all four: the bad version names the code that changed; the good version names the experience that changed. When you can’t describe the experience, that’s usually the signal to ask a developer or to leave the entry out.
Automating Safely
Automated pipelines that parse commit messages and PR descriptions can produce a usable draft, but only if those commits and descriptions were written with the expectation that a user would read them.
If your team consistently uses conventional commit formatting, automation can separate the categories. Types like feat:, fix:, perf:, and chore: sort the entries. Breaking changes are marked with a ! immediately before the colon (feat!:), a BREAKING CHANGE: footer, or both. The ! is a machine-readable signal the tooling keys on to populate the section your users read first. Know how your team marks them, because a breaking change nobody flagged is one your users might miss. If commit messages are informal and inconsistent, automated parsing produces incomplete or misleading output at best.
Your role in an automated pipeline is to audit the output. Confirm that every breaking change is documented, rewrite descriptions that are too technical, and add the context that commit messages don’t contain, such as why a change was made or what the migration path is.
Tools That Generate Release Notes
If your team automates this, there are some tools you might use. They fall into two layers.
The first layer turns commit history into a changelog. git-cliff and conventional-changelog read your commit messages and produce a formatted changelog file. Both work best with conventional commits, though git-cliff will parse other formats if you configure its regex rules. That covers the changelog well. It doesn’t write a release note because it can only reformat what the commits already say.
The second layer automates the release itself, and the tools in it differ mainly in how much room they leave for a human.
release-please, Google’s release automation tool (also packaged as a GitHub Action), keeps a running “release pull request” that assembles the next version number and changelog from conventional commits. You review that PR and merge it to cut a release, which leaves a natural place to edit. It handles versioning and the GitHub release, but not publication to package managers.
semantic-release automates versioning, notes, and publishing. It’s built to run without a human in the loop, so the release note is whatever the commits produced. If this is your team’s tool, insert yourself before publishing to review and edit the release notes.
Changesets, common in JavaScript monorepos, take the opposite approach: each contributor writes a short, human-readable note alongside the PR that makes the change. It’s the only tool here that captures the meaning of a change while it’s fresh, from the person who made it, which is the argument this whole article is making.
GitHub’s own auto-generated release notes are the zero-setup option. Out of the box, you get a list of merged pull requests, a list of contributors, and a link to the full changelog. Add a .github/release.yml and you can sort those PRs into labeled categories, including a Breaking Changes section. Either way, the output is exactly as good as your PR titles and labels, and no better.
Every one of these produces a draft, and the draft is only as good as the commit and PR hygiene behind it. None of them writes the “what it means for the user” line on its own.
Where AI Fits, and Where It Won’t
An AI model is well suited to the first pass over a pile of commits. Cluster related changes and group them by feature, rewrite fix: null check in upload handler into a plausible user-facing sentence, flag the commits that look like breaking changes. On a release with 200 commits, that grouping and rewriting save valuable time.
The limit is the same as the tools' limit. A model cannot measure impact it cannot see. Hand it perf: optimize dashboard query and it will write “dashboards are now significantly faster”. It doesn’t know the number is eight seconds down to one, because the number isn’t in the commit. It can’t know why a change was made or what migration a breaking change requires unless that context is in what you feed it. So it fills the gap with confident, generic language, which is precisely the language a good release note avoids.
As a context owner, this is your job. Your work on an AI-drafted release note is the same as your work on an auto-generated one: supply the context the source lacks, replace invented impact with measured impact or cut it, and confirm every breaking change is real and documented. You own whether what ships is true.
The Archive Is Documentation
Release notes accumulate value over time, but only if you can find the old ones. A user trying to understand why a behavior changed in a version they upgraded to six months ago needs the release note that documented it. A documentation site that shows only the current release’s notes and makes everything older inaccessible is creating a support problem for anyone who upgrades across multiple versions. The archive is part of the documentation. If management only wants the current release notes published, this is the argument for keeping all of them up.
Further Reading
Conventional Commits — conventionalcommits.org — the commit-message convention that enables reliable automation; the spec is short and worth reading in full.
Keep a Changelog — keepachangelog.com — the widely adopted format for human-readable changelogs, with a clear statement of the changelog-vs-release-note distinction this article draws.
Semantic Versioning — semver.org — how version numbers signal breaking vs. non-breaking changes; the vocabulary your breaking-changes section depends on.
release-please — github.com/googleapis/release-please — Google’s release automation Action; the README is a good, concrete look at how a “release PR” workflow actually runs.
Changesets — github.com/changesets/changesets — the author-the-note-at-PR-time model; the clearest tooling embodiment of “capture the meaning while it’s fresh.”
Docs for Developers: An Engineer’s Field Guide to Technical Writing — Bhatti, Corleissen, Lambourne, Nunez, Waterhouse — practical coverage of developer-facing documentation, including how release communication fits the larger docs system.
Write the Docs — release notes topic — writethedocs.org — the technical-writing community’s shared guidance and discussion; a good place to see how other teams handle release notes in practice.
What You Can Do
Find three recent commit messages from any public GitHub repository.
For each one, write the user-facing release note entry it would produce. Post your originals and your translations.
Next: Code Samples and Code Comments — the rules for writing code samples that actually work, and how to write comments that explain why rather than what.

