A changelog is a maintained, cumulative record of meaningful project changes, not a raw commit feed.
A changelog is a cumulative, human-readable record of meaningful changes in a project. It helps users, contributors, package maintainers, and downstream teams understand what changed across versions without reading every commit.
A useful changelog is not generated noise. It groups changes by version and type, keeps an unreleased section for work in progress, and records changes that matter to people using or maintaining the software.
Changelog and release notes have separate roles
Release notes explain one release in a user-facing narrative. A changelog preserves the structured history across releases. The two can link to each other, but they should not be identical.
The changelog is where a user can compare versions, see when a fix landed, confirm whether an API was removed, or understand which release introduced a change. Release notes can be more selective and explanatory.
If a project only has raw commits, users must infer importance from terse messages. A maintained changelog saves that effort and reduces repeated questions.
A maintainable structure
Keep a Changelog popularized a practical structure: an Unreleased section, version headings with dates, and change categories such as Added, Changed, Deprecated, Removed, Fixed, and Security.
That pattern works because it answers several questions quickly:
- Which version included this change?
- Was the change a feature, fix, removal, or security note?
- Is unreleased work already known?
- Which changes affect users rather than only maintainers?
- Where can readers find the linked issue, pull request, or release?
The exact categories can be adapted, but consistency matters. If the same kind of change moves between categories every release, readers stop trusting the structure.
What belongs in each category
Use categories to clarify impact:
| Category | Use when | Example | Avoid |
|---|---|---|---|
| Added | New user-visible behavior exists | Added CSV export for filtered reports | Added internal helper class |
| Changed | Existing behavior is different | Changed default cache location | Improved stuff |
| Deprecated | Feature still exists but should be replaced | Deprecated legacy config key | Removing without warning |
| Removed | Feature, platform, API, or file is gone | Removed Python 3.8 support | Hiding removals in fixes |
| Fixed | Defect is corrected | Fixed crash when importing empty files | Fixed bugs |
| Security | Publicly disclosed security-relevant change | Updated parser after advisory link | Unverified vulnerability claims |
Not every commit belongs. Refactors, dependency bumps, formatting, build-script cleanup, and test-only changes may belong only when they affect users, packagers, contributors, or security posture.
Work from an unreleased section
An unreleased section prevents maintainers from trying to reconstruct release history at the last minute. Add entries as meaningful changes merge, then move them under a version heading when the release is cut.
This habit improves accuracy. The author of a pull request usually knows whether the change is Added, Changed, Fixed, or Removed. Waiting until release day makes the changelog dependent on memory.
The unreleased section should still be edited before release. Combine duplicates, remove internal details, clarify user impact, and ensure breaking changes are obvious.
Automation needs human editing
Automation can collect merged pull requests, issue numbers, labels, contributors, and commit messages. It can help maintainers avoid missed entries. It should not replace editorial judgment.
Commit messages are written for maintainers, not always for users. Labels can be wrong. Pull request titles can be too technical or too vague. A generated list may also overemphasize internal work while hiding a migration risk.
Use automation as input, then edit for meaning. The changelog should tell users what changed, not merely prove that work happened.
Traceability without clutter
Links can make a changelog more useful, but too many links can turn it into a noisy index. Link entries when the issue, pull request, advisory, migration guide, or release page adds detail that a reader may need.
For a small fix, one clear sentence may be enough. For a breaking change, security note, format change, or removed platform, link to the deeper explanation. If a changelog entry contains only issue numbers, readers have to open every link to understand the release.
Traceability is strongest when the visible text still stands on its own. A reader should understand the change before deciding whether to open the link.
Corrections and historical accuracy
Sometimes old changelog entries need correction. A version date may be wrong, a change may have been categorized incorrectly, or a security note may need a safer public link after disclosure.
Correct factual errors directly, but avoid rewriting old entries merely to match a new writing style. The changelog is a historical record. Large retroactive rewrites can make it harder for users and downstream packagers to compare what was said at release time.
When a correction materially changes user understanding, note the correction near the affected entry or in the next release notes. Quietly fixing a typo is different from changing whether a release contained a breaking change.
Changelog hygiene
Changelogs become hard to use when they mix styles, skip dates, change category names, or bury breaking changes under small fixes. They also lose value when old entries are rewritten without reason.
Good hygiene includes:
- Keep version headings and dates consistent.
- Link versions to release tags when useful.
- Link issue or pull request numbers only when the title explains the change.
- Put breaking changes where readers will notice them.
- Keep security details aligned with official public disclosure.
- Preserve history unless correcting a factual error.
The expected outcome is a history that a user can scan before upgrading and a maintainer can maintain without heroic cleanup.
Before cutting a release
Before publishing a new version, review the changelog for:
- entries in the right version section;
- clear user-facing wording;
- removed, deprecated, or breaking behavior;
- security entries that match public advisories or project notes;
- issue links that add context;
- release date and version number consistency;
- no raw commit dumps or duplicate entries.
A changelog does not need to be long. It needs to be accurate, consistent, and written for the people who must understand the release history.