LogPulse / guides

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-02 heading: the exact version, then the date as YYYY-MM-DD. Newest first, with ## [Unreleased] on top.
  • Group entries under ### Added, Changed, Deprecated, Removed, Fixed and Security. 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.md
# 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.0

Versions, 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.”

HeadingWhat'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 - OctoberDoesn'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-02Same level as the change-type headings, so readers and parsers can't tell versions from sections.
## [1.4.0] - 2026-10-02Correct: 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.

HeadingKeep a Changelog's definitionExample
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.

A breaking change, 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.0Common Changelog
File name“Name it CHANGELOG.md”“Filename must be CHANGELOG.md”
Change typesAdded, Changed, Deprecated, Removed, Fixed, SecurityFour, in order: Changed, Added, Removed, Fixed
Unreleased sectionYes, at the topNo
ReferencesOptional; link when it helps“Changes must reference relevant commits”, and should reference issues or pull requests
Version linksA compare view against the previous versionOn GitHub, the GitHub release with the same content
Release summaryAn optional sentence or two before the typed sectionsOne 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.

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.

  1. 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.

    Open the converter

  2. 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: .

    Free for open source

  3. 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.

    Read the docs

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 .

More guides: open-source libraries and packages

All release notes guides

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.