CHANGELOG.md format, explained line by line
There's no formal changelog standard, but most projects follow Keep a Changelog, whose 2.0.0 revision came out in June 2026, and the tools that read changelogs expect its conventions. Here's the format line by line, a stricter variant, and the mistakes that make a changelog hard to read for people and parsers alike.
Updated Last checked 8 min readOpen-source libraries and packages
The short answer
- Name the file
CHANGELOG.md, open it with a short preamble, and say which versioning scheme you use. - Give each release a
## [1.4.0] - 2026-10-02heading: the exact version, then the date as YYYY-MM-DD. Newest first, with## [Unreleased]on top. - Group entries under
### Added,Changed,Deprecated,Removed,FixedandSecurity. Six types, on purpose. - Mark breaking changes with a Breaking: prefix where they are (usually under Changed or Removed), and link to a migration guide when the steps are long.
- End the file with link references so each version heading links to its compare view, and mark pulled releases
[YANKED]instead of deleting them.
A complete CHANGELOG.md
A Keep a Changelog 2.0.0 file with an Unreleased section, a breaking change, a deprecation, a yanked release and link references at the bottom. Copy it and replace the entries with your own.
# 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
- `parseStream()` for large inputs.
## [2.0.0] - 2026-11-03
### Changed
- **Breaking:** `timeout` is now in milliseconds, not seconds.
Replace `timeout: 30` with `timeout: 30_000`.
### Removed
- **Breaking:** `parseLegacy()`, deprecated since 1.4.0. Use `parse()`.
## [1.4.0] - 2026-10-02
### Deprecated
- `parseLegacy()`: use `parse()` instead. It will be removed in 2.0.0.
### Fixed
- Files with Windows (CRLF) line endings now parse.
## [1.3.1] - 2026-09-14 [YANKED]
### Fixed
- Error positions point at the right line.
## [1.3.0] - 2026-09-01
### Added
- Error messages include the line and column of the problem.
[Unreleased]: https://github.com/OWNER/REPO/compare/v2.0.0...HEAD
[2.0.0]: https://github.com/OWNER/REPO/compare/v1.4.0...v2.0.0
[1.4.0]: https://github.com/OWNER/REPO/compare/v1.3.1...v1.4.0
[1.3.1]: https://github.com/OWNER/REPO/compare/v1.3.0...v1.3.1
[1.3.0]: https://github.com/OWNER/REPO/releases/tag/v1.3.0Versions, dates and functions are placeholders. OWNER/REPO is your repository; any host with tag and compare URLs works.
Free tool · no sign-up
Can a coding agent read your changelog?
Paste your repository, a GitHub release or a CHANGELOG.md link. The free converter shows every version, breaking change, migration step and deprecation it can extract, as JSON plus an llms.txt summary.
No AI and nothing stored. Or paste a CHANGELOG.md instead
01The file: name, place and preamble
Keep a Changelog 2.0.0 is direct: “Name it CHANGELOG.md.” Put it in the repository root, where people and tools look first. Open with a # Changelog heading and the short preamble from the template, which says what the file is, which format you follow and which versioning scheme you use. Keep a Changelog suggests pinning its link to the version you follow, so it stays accurate as the guidance changes.
Then make it findable from where people install your package. npm renders your README on the package page, so link the changelog from the README; for Python packages, add a Changelog entry to [project.urls] in pyproject.toml. The npm and PyPI guides cover both.
02Version headings and dates
Each release gets a second-level heading with the version in square brackets and the release date: ## [1.4.0] - 2026-10-02. The brackets make the version a Markdown reference link (see below). List the latest version first, and use YYYY-MM-DD dates: Keep a Changelog says the format “orders from the largest unit to the smallest, avoids the confusion of regional date formats, and is an ISO standard.”
| Heading | What's wrong |
|---|---|
## 1.4.0 (10/02/2026) | Is that 10 February or 2 October? Regional dates are ambiguous; use 2026-10-02. |
## Version 1.4 - October | Doesn't match the published version, and has no full date. |
## [1.4.0] | No date, so readers can't tell how old a release is. |
### 1.4.0 - 2026-10-02 | Same level as the change-type headings, so readers and parsers can't tell versions from sections. |
## [1.4.0] - 2026-10-02 | Correct: exact version, ISO date, linkable. |
Use the version exactly as published to the registry and tagged in git, so a reader, a script or an agent can match the heading to the tag. You don't have to use Semantic Versioning, but say which scheme you use; Semantic Versioning and changelogs explains what each number promises if you do. A version may open with a sentence or two summarizing the release before its typed sections; that's optional.
03The six types of change
Under each version, group entries by type, in the order Keep a Changelog lists them, and leave out types with nothing under them. Keep a Changelog keeps it to six “on purpose”: what kind of change it is goes in the type, why it matters goes in the wording.
| Heading | Keep a Changelog's definition | Example |
|---|---|---|
| Added | “for new features” | parseStream() for large inputs. |
| Changed | “for changes in existing functionality” | Error messages include line and column. |
| Deprecated | “for soon-to-be removed features” | parseLegacy(): use parse(). Removal planned in 2.0.0. |
| Removed | “for now removed features” | The my-lib/legacy entry point. |
| Fixed | “for bug fixes” | CRLF line endings now parse. |
| Security | “for vulnerabilities” | Lead with the CVE identifier when there is one, then link the advisory. |
Unsure between Fixed and Changed? Keep a Changelog's test: “ask whether the old behavior was a bug. If it was, use Fixed.” Dependency updates aren't a type: describe their effect under the right type if users notice it, and leave them out if not.
04Breaking changes and migration steps
Keep a Changelog 2.0.0 says breaking changes “usually go under Changed or Removed” with a short Breaking: marker, kept “with the type of change they are” rather than collected into a separate section, so anyone scanning Changed or Removed sees them in place.
### Changed
- **Breaking:** `timeout` is now in milliseconds, not seconds.
Replace `timeout: 30` with `timeout: 30_000`.A one-line upgrade note can sit in the entry. When the steps are substantial, link to a migration guide or the release notes instead of spelling them out in the changelog. Release notes and announcements can still gather breaks under their own heading, like the GitHub release in the Markdown release notes templates; in CHANGELOG.md, keep the marker on the entry. Either way, every break needs a migration step. How to document breaking changes covers what to write.
Deprecations come first: “mark it Deprecated in one release, and only Removed in a later one”, and say which version will remove it. See how to write deprecation notices.
05Unreleased, yanked releases and link references
- Unreleased. “Keep an Unreleased section at the top to collect upcoming changes.” When you cut a release, rename it to the new version in both the heading and its link, then add a fresh, empty Unreleased section.
- Yanked. A release pulled because of a serious bug or security issue stays listed, marked like
## [1.3.1] - 2026-09-14 [YANKED]. Keep a Changelog: “List it; do not hide it.” - Link references. Resolve each bracketed version once at the bottom of the file, pointing it at a comparison with the version before.
[Unreleased]compares the latest tag to HEAD, and the oldest version links to its tag. - Corrections. You can fix a forgotten entry or an unrecorded breaking change after a release; Keep a Changelog suggests noting the date you updated the entry.
The changelog can be corrected; the release can't. Semantic Versioning: “Once a versioned package has been released, the contents of that version MUST NOT be modified.” Fix code in a new version and say so in its entry.
06Keep a Changelog vs Common Changelog
Common Changelog is “adapted from and a stricter subset of Keep a Changelog”. Pick one and name it in your preamble.
| Keep a Changelog 2.0.0 | Common Changelog | |
|---|---|---|
| File name | “Name it CHANGELOG.md” | “Filename must be CHANGELOG.md” |
| Change types | Added, Changed, Deprecated, Removed, Fixed, Security | Four, in order: Changed, Added, Removed, Fixed |
| Unreleased section | Yes, at the top | No |
| References | Optional; link when it helps | “Changes must reference relevant commits”, and should reference issues or pull requests |
| Version links | A compare view against the previous version | On GitHub, the GitHub release with the same content |
| Release summary | An optional sentence or two before the typed sections | One single-sentence notice per release, for example a link to an upgrade guide |
07Mistakes that break readers and parsers
- Pasting the git log. Keep a Changelog: “Do not use a list of commits as a changelog. It is full of noise.” Draft from commits if you like, then edit; see how to generate a changelog from git.
- One long list per version with no change types, so breaking changes hide among fixes.
- Version headings that don't match the published versions or git tags.
- Dates in regional formats, or no dates at all.
- Recording some changes but not others. Readers treat a changelog as the full picture.
- Inventing new types (Improved, Internal, Dependencies) that readers and tools don't expect.
A quick test: paste the file into the free changelog-to-JSON converter. It's a deterministic parser, not AI, so if a version, a breaking change or a deprecation doesn't show up in its output, the heading or wording is probably the problem.
Real projects that do this well
Public release notes worth copying from. Each link opens the project's own page.
- Keep a Changelog's own CHANGELOG.md The project that defines the format, following it: Unreleased on top, ISO dates, Breaking markers in place and compare links at the bottom.
- React's CHANGELOG.md A large project's variation: one heading per version with a written-out date, and changes grouped by package under each release.
Before and after
Too vague
## Changes (Oct 2) - lots of fixes, new timeout option, removed old stuff
Specific and actionable
## [1.4.0] - 2026-10-02 · Deprecated: parseLegacy(), use parse(); it will be removed in 2.0.0. Fixed: files with Windows (CRLF) line endings now parse.
Fictional example: use your own verified names, versions and behavior.
LogPulse
Generate release notes with LogPulse
LogPulse is a hosted changelog that people and coding agents can both read. Here's exactly what it can do for the steps above, and what it can't.
Check your changelog, free
Paste a CHANGELOG.md or a GitHub releases link. A deterministic parser (no AI, nothing stored) shows the versions, breaking changes, migration steps and deprecations an agent can extract.
Draft notes from GitHub
Connect a repository: merged pull requests, commits to your default branch and published GitHub releases collect in an inbox. AI drafts a note in the tone you pick, listing breaking changes and deprecations only when the changes clearly show them. Nothing is published until you approve it. 3 AI drafts on Free; unlimited on Open source and Pro. Pro opens soon: .
Publish once, read everywhere
Approved notes go to a hosted changelog and an in-app widget, to email subscribers on Open source and Pro (Pro opens soon), and to a JSON feed and MCP server coding agents can query. CI and agents can publish with an API key.
What it doesn't do: LogPulse doesn't tag releases, bump versions, publish to npm or PyPI, or write CHANGELOG.md back to your repository; keep your release tooling for those. AI drafts can miss context, so review them like a pull request.
Before you publish
- The file is called CHANGELOG.md and sits in the repository root
- The preamble names the format version and your versioning scheme
- Every version heading matches the published version and has a YYYY-MM-DD date
- Entries use the six change types, and breaking changes carry a Breaking: marker
- An Unreleased section sits on top, and link references sit at the bottom
- Pulled releases are marked [YANKED], not deleted
Common questions
Is there an official CHANGELOG.md standard?
Not a formal one. Keep a Changelog is the most widely used set of conventions (version 2.0.0 since June 2026), and Common Changelog is a stricter subset of it. Following either, and saying which in your preamble, is what makes a changelog predictable for readers and tools.
What date format should a changelog use?
YYYY-MM-DD, as in 2026-10-02. It orders year, month and day from largest to smallest, can't be misread the way 10/02/2026 can, and is an ISO standard.
Where do breaking changes go in Keep a Changelog?
Under the type they are, usually Changed or Removed, with a Breaking: prefix so they stand out. Keep a Changelog 2.0.0 advises against collecting them in a separate section. Put a short upgrade note in the entry, or link a migration guide when it's long.
How do monorepos keep a changelog?
Unrelated projects in one repository each keep their own. For one product split into packages, Keep a Changelog suggests a changelog per component plus one central changelog that summarizes what each release means.
Sources
Facts last checked against these sources on .
Read next
Release notes templates in Markdown
Free Markdown release notes templates for a CHANGELOG.md entry, a GitHub release, a major version with migration steps, a security fix and a monorepo.
Read the guide Open-source libraries and packagesKeep a Changelog vs GitHub Releases
CHANGELOG.md or GitHub Releases? See them side by side, when to use each, and how to keep both in sync with release.yml. Includes a CHANGELOG.md skeleton.
Read the guide Breaking changes, deprecations and versioningSemantic versioning cheat sheet for changelogs
Which change needs a major, minor or patch bump? A cheat sheet of 12 common changes with the changelog line for each, plus 0.x and prerelease rules.
Read the guide Open-source libraries and packagesChangelog vs release notes: what's the difference?
A changelog is the full, versioned history of notable changes; release notes tell the story of one release. Definitions, real examples and when to keep both.
Read the guideMore guides: open-source libraries and packages
- How to write release notes for open-source libraries
- How to generate a changelog from git commits
- How to write release notes from commits (Conventional Commits)
- semantic-release vs release-please vs Changesets (and git-cliff)
- How to keep a changelog for an npm package
- How to keep a changelog for a Python package on PyPI
Tools and resources
LogPulse
Changelogs coding agents can upgrade from.
Publish versions, breaking changes, migration steps and deprecations once. People get a widget and hosted changelog; agents get a JSON feed and MCP server. Free for open source.