Back to blog
Changelog distribution9 min readPublished September 10, 2026

Changelog format: three conventions, three real examples, and how to pick one

Keep a Changelog, Common Changelog and conventional-changelog produce three different CHANGELOG.md files. Three complete changelog examples, a decision table matching convention to project type, and the rule for what belongs in the file at all.

What this solves

Someone is about to create or rewrite CHANGELOG.md and wants to see what the file actually looks like, which convention to follow, and what belongs in it.

How S2P helps

You leave with a CHANGELOG.md structure you can paste in, a convention picked on purpose rather than by accident, and a rule for deciding whether a change belongs in the file.

Key takeaways

  • Three conventions cover almost every CHANGELOG.md in the wild: Keep a Changelog, Common Changelog, and the output conventional-changelog generates from Conventional Commits.
  • Keep a Changelog 2.0.0 shipped on 2026-06-07 and did not change the format: the same six change types, the same YYYY-MM-DD dates, the same Unreleased and YANKED markers.
  • Common Changelog is a stricter subset: four categories instead of six, no Unreleased section, and a required commit or pull request reference on every entry.
  • A generated changelog is raw material, not a finished one, because a commit message and a changelog entry are written for different readers.
  • The hard part is not the format. It is deciding which changes are notable enough to appear in the file at all.

Section 1

The three conventions, side by side

Almost every CHANGELOG.md follows one of three conventions. Each section below carries a complete changelog example you can paste.

Keep a Changelog defines a changelog as a curated, chronologically ordered list of the notable changes for each version of a project, kept as a file in the repository. Common Changelog adds one prerequisite: every released version has a corresponding git tag. The benefit: a reader decides whether to upgrade without reading your diff.

The three are not variations on formatting. They disagree about who writes an entry and when. Keep a Changelog and Common Changelog assume a person sits down at release time and writes for readers. conventional-changelog assumes a tool reads structured commit messages and emits the file.

That also answers what the different types of changelogs are. Inside the file, the types are the change categories. Outside it, the split is between the repository changelog, the release notes for one version, and a hosted product changelog page. The release notes versus changelog post covers that split.

Keep a Changelog vs Common Changelog vs conventional-changelog output

RuleKeep a Changelog 2.0.0Common Changelogconventional-changelog output
Change categoriesSix, fixed: Added, Changed, Deprecated, Removed, Fixed, SecurityFour, in this order: Changed, Added, Removed, FixedNamed for commit types: Features, Bug Fixes, and similar
Version heading## [1.2.0] - 2026-08-28## VERSION - DATE, with the version linked to the release## [1.2.0](compare URL) (2026-08-28)
Date formatYYYY-MM-DD (ISO 8601)YYYY-MM-DD (ISO 8601)YYYY-MM-DD, in parentheses after the version
Unreleased sectionRecommended, kept at the topDeliberately removedNot produced
Breaking changesA Breaking marker on the entry, inside Changed or RemovedA bold Breaking prefix, sorted first within its categoryA separate BREAKING CHANGES section
Commit and issue referencesOptional, as reference-style links at the bottomRequired: every change references a commitAutomatic: a commit hash and issue link per entry
Author creditOptional, and a plain name travels better than a handleRequired, unless the project has one contributorNot present in the default preset output
Yanked releasesA YANKED marker in the headingA one-sentence notice under the headingNot represented
Who writes an entryA person, at release timeA person, at release time, with the whole release in viewA tool, from commit messages

Section 2

Keep a Changelog: the default

The convention most repositories follow and most tools can read. Six change types, one Unreleased section, ISO dates.

Version 2.0.0 shipped on 2026-06-07. Its own release entry states that the six change types, YYYY-MM-DD dates, and the Unreleased and YANKED markers are all unchanged, so an existing file stays valid; only the guidance around the format changed. The site root still serves 1.1.0 as its canonical page, so both numbers are in circulation.

The six types are Added for new features, Changed for changes in existing functionality, Deprecated for soon-to-be removed features, Removed for now removed features, Fixed for bug fixes, and Security for vulnerabilities. There are only six on purpose: the 2.0.0 guidance is explicit that Dependencies is not a type of change, and that a faster JSON parser belongs under Changed, not a new Performance section.

If the old behaviour was a bug, use Fixed; if it was intentional and you are changing it, use Changed. Lead a Security entry with its CVE identifier so security tools can match it to the advisory.

  • Changelogs are for humans, not machines.
  • Every version should have an entry.
  • Group changes of the same type.
  • Make versions and sections linkable.
  • List the latest version first.
  • Show the release date of each version.
  • Note which versioning scheme you use.
  • Write plainly, because many readers are not native speakers.

CHANGELOG.md (Keep a Changelog 2.0.0)

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/2.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- `--dry-run` flag for the `deploy` command.

## [2.0.0] - 2026-08-28

Drops the legacy config loader. Files written for 1.x keep working through the shim added in 1.9.

### Changed

- **Breaking:** `parse()` returns a result object instead of raising.

### Removed

- **Breaking:** Node.js 18 support.

### Fixed

- Off-by-one in the pagination cursor.

### Security

- CVE-2026-12345: out-of-bounds read when parsing a malformed header.

## [1.9.0] - 2026-07-14

### Added

- `theme` option for terminal output.

### Deprecated

- The `color` option. Use `theme` instead; `color` is removed in 2.0.0.

## [1.8.3] - 2026-06-02 [YANKED]

### Fixed

- Nothing usable: this build corrupted the on-disk index and was pulled.

[Unreleased]: https://github.com/your/project/compare/v2.0.0...HEAD
[2.0.0]: https://github.com/your/project/compare/v1.9.0...v2.0.0
[1.9.0]: https://github.com/your/project/compare/v1.8.3...v1.9.0
[1.8.3]: https://github.com/your/project/releases/tag/v1.8.3

Section 3

Common Changelog: the strict subset

Built for libraries whose changelog gets read out of context, in someone else's dependency bump pull request.

Common Changelog is a style guide adapted from, and a stricter subset of, Keep a Changelog. Four categories replace six: Changed, Added, Removed and Fixed, in that order. Deprecated and Security are gone, and a deprecation is listed under Changed.

In their place come stricter rules. Every change must reference the relevant commit, and should reference the ticket or pull request when one exists, written after the change on the same line, in parentheses. Author names follow the references. Breaking changes carry a bold Breaking prefix and sort first within their category. Entries use the imperative mood and must be self-describing, as if no category heading existed: write Add write() method, not write() method.

There is no Unreleased section: a contributor cannot add the reference to their own change, because it only exists after the fact. Yanked releases get a one-sentence notice rather than a YANKED tag, which the spec concedes is more generic but unparsable. The cost: every entry is hand written at release time, by someone holding the whole release in their head.

CHANGELOG.md (Common Changelog, excerpt from Level/level)

# Changelog

## [10.0.0] - 2025-04-20

_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md)._

### Changed

- **Breaking:** upgrade to abstract-level 3 ([#248](https://github.com/Level/level/issues/248)) ([`b27277c`](https://github.com/Level/level/commit/b27277c)) (Vincent Weevers)

## [8.0.1] - 2024-01-27

### Fixed

- Explicitly depend on abstract-level for TypeScript ([#241](https://github.com/Level/level/issues/241)) ([`c501868`](https://github.com/Level/level/commit/c501868)) (Hanxx).

## [7.0.1] - 2021-10-02

### Added

- Document new features ([#207](https://github.com/Level/level/issues/207)) ([`ad8f924`](https://github.com/Level/level/commit/ad8f924)) (Vincent Weevers)

### Fixed

- Bump dependencies to prevent dedupe ([`7083ec6`](https://github.com/Level/level/commit/7083ec6)) (Vincent Weevers)

Section 4

conventional-changelog: the generated file

A tool reads your commit messages and writes the file. The file reads like it.

conventional-changelog generates a changelog from git metadata. Install it alongside a preset and pass the preset name, for example conventional-changelog -p angular, which reads commits since the last matching semver tag and does not overwrite previous changelogs. Its documentation recommends using a commit convention so the tool can tokenize commits. That convention is usually Conventional Commits, whose 1.0.0 specification maps fix to PATCH, feat to MINOR, and any commit carrying BREAKING CHANGE to MAJOR regardless of type.

The output is recognisable, and it is not Keep a Changelog. Version headings are links to a compare diff with the date in parentheses, sections are named Features and Bug Fixes, and breaking changes collect into their own section instead of sitting with the change they break.

Both other conventions argue against generating the file. Keep a Changelog 2.0.0 states that a commit and a changelog entry are written for different people, and that a generated changelog is raw material at best. Common Changelog lists Conventional Commits as an antipattern, because using it still means converting commit messages into a more readable form. The counterargument is short: a generated changelog exists, and an unwritten one does not.

CHANGELOG.md (generated, excerpt from the conventional-changelog repository)

# Changelog

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [8.1.3](https://github.com/conventional-changelog/conventional-changelog/compare/conventional-changelog-v8.1.2...conventional-changelog-v8.1.3) (2026-08-12)

### Bug Fixes

* add conventional-commits-filter to dependencies ([#1536](https://github.com/conventional-changelog/conventional-changelog/issues/1536)) ([43f19df](https://github.com/conventional-changelog/conventional-changelog/commit/43f19dfd521ec46e3a17ff3564b5aebca499d8ea))

## [8.1.0](https://github.com/conventional-changelog/conventional-changelog/compare/conventional-changelog-v8.0.1...conventional-changelog-v8.1.0) (2026-07-09)

### Features

* replace `meow` with `argue-cli` ([#1505](https://github.com/conventional-changelog/conventional-changelog/issues/1505)) ([1ad6177](https://github.com/conventional-changelog/conventional-changelog/commit/1ad6177da0b9da2df4fffc41e0b6caf9b76bf200))

## [8.0.0](https://github.com/conventional-changelog/conventional-changelog/compare/conventional-changelog-v7.2.1...conventional-changelog-v8.0.0) (2026-06-26)

### ⚠ BREAKING CHANGES

* Handlebars template strings and partial files were replaced with render functions.
* packages now require Node.js 22 or newer.

Section 5

Which convention to pick

Match the changelog format to who reads the file and how much writing you will do at release time.

The constraint is effort per release, not aesthetics. Common Changelog produces the best file and asks the most of you. conventional-changelog asks almost nothing and produces the weakest file. Keep a Changelog sits between them, and is the safe answer when you are unsure.

Whichever you pick, declare it in the preamble. Keep a Changelog's guidance is that stating your conventions tells readers, and tools, what to expect. Pin the link to the version you follow so it stays accurate as that page changes. Neither spec needs a changelog tool installed to start.

Convention by project type

Project typeStart withWhyWhat it costs
Public library or SDKCommon ChangelogEntries get read out of context, in dependency bump pull requests on other people's repositoriesEvery entry is hand written and needs a commit or pull request link
Application or CLI with end usersKeep a ChangelogDeprecated and Security are their own types, and users of an installed app act on bothSomeone curates the Unreleased section before every release
Public API or hosted serviceKeep a Changelog, with a Deprecated entry for every planned removalDeprecating in one release and removing in a later one is the only warning integrators getYou must announce a deprecation at least one release before you act on it
Internal serviceconventional-changelog outputNobody outside the team reads it, and a generated log beats an unwritten oneEntries are reworded commit messages, so file quality tracks commit quality
MonorepoOne changelog per component, plus one central summaryReaders should not have to read a dozen component files to understand a releaseTwo levels to keep in sync at release time
Pre-1.0 projectKeep a Changelog, Unreleased section only at firstUnder 0.y.z anything may change at any time, so the file is your only compatibility signalAlmost nothing, and it builds the habit before the first stable release

Section 6

What belongs in the file, and what does not

The specs agree on the format and mostly leave this to you. It decides whether anyone reads the file twice.

Keep a Changelog 2.0.0 frames this as curating rather than accumulating: a changelog records notable changes, which means some changes are not notable. It warns the other way too. A changelog that records only some changes can mislead as much as no changelog, because readers treat it as the full picture.

The test that resolves most cases: would someone who uses the distributed software notice, or need to act? Common Changelog turns that into an exclusion list, and a shorter list of things that look like noise and are not.

Two habits do the most damage. Ignoring deprecations: mark something Deprecated in one release and only Removed in a later one, and say which version will remove it. And pasting a commit list, which records steps in the evolution of source code, not the differences that matter to the people using it.

  • Leave out: dotfile changes, development-only dependency bumps, minor code style changes, formatting changes in documentation.
  • Keep in: refactors, because they may have unintentional side effects.
  • Keep in: changes to supported runtime environments, even when they only show up in dotfiles.
  • Keep in: code style changes that use new language features.
  • Keep in: new documentation, when the feature was previously undocumented.
  • Keep in: every deprecation, removal and breaking change. If you record nothing else, record these.

Section 7

Version headings, dates and the SemVer mapping

The heading carries the version, the date and the diff link. Get the SemVer mapping right and the heading does most of the communicating.

Write the heading as the version number and the date in YYYY-MM-DD form. Keep a Changelog's reasons for ISO order: it runs from the largest unit to the smallest, avoids regional formats where month and day swap places, and is an ISO standard. Common Changelog requires the same format.

The square brackets around the version make it a Markdown reference link, resolved once at the bottom of the file. Point each version at a comparison with the one before it, and Unreleased at HEAD, so every version is tied to its tag and links to the exact diff. Any host exposes tag and comparison URLs, so the pattern survives a move to a different one.

Semantic Versioning 2.0.0 decides which number moves. Under 0.y.z the spec says anything may change at any time and the public API should not be considered stable, which is when a changelog is doing the most work.

Change type to version bump

What changedKeep a Changelog typeSemVer 2.0.0 bump
Backward compatible bug fixFixedPATCH
New backward compatible functionality in the public APIAddedMINOR
Public API functionality marked as deprecatedDeprecatedMINOR
Behaviour changed but still compatibleChangedMINOR, or PATCH when the change is internal only
Backward incompatible change or removalChanged or RemovedMAJOR
Vulnerability fixedSecurityPATCH when the fix is backward compatible, MAJOR when it is not

Section 8

Where to store it, and is changelog one word

Repository root, one file, called CHANGELOG.md. The rest is edge cases.

Common Changelog makes the location a hard requirement: the filename must be CHANGELOG.md and the content must be Markdown starting with a first-level heading. Keep a Changelog allows HISTORY, NEWS or RELEASES but says a predictable name makes it easy to find. There is no special GitHub changelog format: a GitHub changelog is CHANGELOG.md at the repository root, with version headings resolved to GitHub compare URLs at the bottom.

Is changelog one word or two? One word. Keep a Changelog records the decision in its own 1.0.0 entry: start using changelog over change log, since it is the common usage. Write the file name in uppercase and the word itself in lowercase in prose.

In a monorepo, unrelated projects sharing a repository each keep their own file, while one product made of many parts can keep a changelog per component but should also keep one central changelog. A hosted product changelog page, or an app changelog inside the product, is a separate artifact generated from this file rather than replacing it.

Section 9

Keeping it current without automating the wrong half

The maintenance loop is short. The common failure is handing a machine the judgement instead of the mechanics.

Land changes, add the notable ones to Unreleased, and at release time rename Unreleased to the new version in both the heading and its link, add the date, then add a fresh empty Unreleased pointing at HEAD. Common Changelog adds one ordering detail worth copying: commit the changelog entry before you create the git tag, so the tag contains its own entry.

Keep a Changelog 2.0.0 says to use continuous integration for mechanical tasks: moving Unreleased into a dated version, checking formatting, reminding a contributor that a change may need an entry. Do not make a changelog edit a required check on every pull request, because that teaches people to add a line to pass the check, which fills the file with noise.

Because every version sits under a predictable heading, that section is already the draft of its release notes, and a few lines of awk pull it out.

extract-release-notes.sh

#!/usr/bin/env bash
# Print the CHANGELOG.md section for one version, ready to paste into a release.
set -euo pipefail

VERSION="${1:?usage: extract-release-notes.sh 1.2.0}"

awk -v want="$VERSION" '
  /^## / {
    if (found) exit
    head = $0
    gsub(/[][]/, "", head)
    split(head, f, " ")
    if (f[2] == want) { found = 1; next }
  }
  found { print }
  END { if (!found) { print "no entry for " want > "/dev/stderr"; exit 1 } }
' CHANGELOG.md

# ./extract-release-notes.sh 1.2.0 > notes.md

Section 10

After the file is written

The one step that still repeats every release.

Once CHANGELOG.md is the source of truth, the work that repeats every release is turning one version section into posts for wherever your users are. Ship 2 Post watches your repository for releases, tags, merged pull requests and deployments through its GitHub App, drafts channel-native variants in a brand voice you configure, and queues them for one-click approval before anything publishes to any of its 14 destinations. The free plan is one post per UTC day, no credit card.

FAQ

Questions this article answers

What is the standard format for a changelog?

There is no formal standard, and Keep a Changelog says so directly rather than claiming to be one. The de facto format is Keep a Changelog: a CHANGELOG.md at the repository root, a Changelog heading and a short preamble naming your conventions, versions newest first under a heading of version number and ISO date, and entries grouped under Added, Changed, Deprecated, Removed, Fixed and Security.

What should be included in a changelog?

Notable, user-facing changes, grouped by type and by version. Always include deprecations, removals, breaking changes and security fixes. Leave out dotfile edits, development-only dependency bumps and minor code style tweaks. Keep refactors, changes to supported runtime environments, and newly written documentation for a previously undocumented feature, because all three can surprise somebody upgrading even though they look like housekeeping.

Where should a changelog be stored?

In a file named CHANGELOG.md at the root of the repository, written in Markdown. Common Changelog requires that exact filename. Keep a Changelog notes that some projects use HISTORY, NEWS or RELEASES but recommends a predictable name so people can find it. Keeping it in the repository means it travels with your code, unlike release pages that live in a host's database.

Is changelog one word or two?

One word. Keep a Changelog records the switch in its own 1.0.0 changelog entry: start using changelog over change log, since it is the common usage. Write the file name in uppercase as CHANGELOG.md, and the word itself in lowercase when you use it in a sentence. Change log as two words is not wrong, just dated.

How do you decide if a change is significant enough to include in the changelog?

Ask whether someone who uses the distributed software would notice the change or need to act on it. If yes, it goes in. Keep a Changelog frames this as restraint: a changelog records notable changes, so by definition some changes are not notable. It also warns that a changelog recording only some changes misleads, because readers assume it is complete. Trivial out, notable in, consistently.

How to generate changelog automatically?

Conventional changelog generators read structured commit messages to draft the file and pick the next version: conventional-changelog, semantic-release, release-please, Changesets and git-cliff. GitHub also documents generating release notes from a default template. Treat all of it as a first draft: Keep a Changelog 2.0.0 calls a generated changelog raw material at best. The guide to automating release notes on GitHub compares the tools in detail.

Related guides and pages

Where to go next

Hand-picked pages that go deeper on the workflow, channels, and tooling covered above.

Ship 2 Post

Stop writing release posts.

Your engineers already commit. Now those commits become content - in your voice, on every channel.