Keep a Changelog Your Users Read
27 Aug 2026Most changelogs are commit logs with the noise filtered out. Nobody reads those, because they still read like git log. A changelog that gets read is one that answers a specific question fast: “did the thing that annoyed me get fixed.”
What a changelog is actually for
It’s a support-ticket filter. A user hits a bug, checks the changelog, sees it was fixed two releases ago, and upgrades instead of filing a ticket. If your changelog is empty, out of date, or unreadable, every fixed bug generates a duplicate report anyway. The changelog pays for itself the first week you keep it current.
Format
Use the Keep a Changelog convention: newest release on top, entries grouped under Added, Changed, Fixed, Removed. Skip categories you don’t need — most releases only touch one or two.
## [2.4.0] - 2026-09-10
### Added
- CSV export for the usage report page.
### Fixed
- Webhook retries no longer duplicate events when the endpoint times out.
### Changed
- API rate limit raised from 60 to 300 requests/minute on paid plans.
Link the version header to a diff or tag if your users are developers who’d actually click it. Don’t link it if they’re not — it’s dead weight.
Write for the reader, not for yourself
- “Fixed a bug in the export flow” tells the user nothing. “Fixed: CSV export dropped the last row when more than 10,000 rows were selected” tells them exactly whether this affects them.
- Skip internal refactors, dependency bumps, and test changes. If it didn’t change what the user sees or can do, it doesn’t belong here. That’s what your commit history is for.
- One line per change. If you need three sentences to explain it, it’s not a changelog entry, it’s a blog post — link out to that instead.
- Order entries by what users care about, not by merge order. A breaking change goes first, a typo fix goes last.
Cadence
Update the changelog in the same PR as the change, not as a batch job before release. If you wait until release day to reconstruct what happened, you will forget the small fixes and the changelog will skew toward big, obvious features — which is exactly backwards, since small fixes are what people are searching for.
For a solo project, one changelog file per app, updated per release, is enough. You don’t need a per-repo microservice changelog until you actually have multiple services shipping independently and users need to know which one changed.
Where to put it
A CHANGELOG.md at the repo root works for developer-facing tools — people expect it there and it renders on GitHub for free. For a product with non-technical users, put it in-app: a small “What’s New” panel behind a bell icon, sourced from the same file if you can manage it, so you’re not maintaining two versions of the truth.
If you maintain both, generate one from the other. Hand-syncing two changelogs is how they drift and stop matching within a month.
Breaking changes get special treatment
Don’t bury a breaking change in a Changed bullet. Put a ### ⚠️ Breaking section at the top of that release, state what breaks, and give the one-line migration. If there’s no simple migration, that’s a sign the change needs a longer deprecation window, not a better changelog entry.
The gotcha
A changelog nobody maintains is worse than no changelog — it trains users to stop checking it, and then even your good entries go unread. If you can’t commit to updating it every release, don’t start one. Ship half the discipline (update it only for breaking changes and major features) rather than none at all.