Skip to content
LogluneStart free

Changelog: What to Record and How to Write One

By Loglune

A human-readable release changelog groups dated version changes by category and preserves older entries.
What a changelog records for each version
Version and date
## [1.2.0] - 2026-10-06
Change groups
Added, Changed, Deprecated, Removed, Fixed, Security
Flag versions
Each flag change, the version it shipped in, and notes added later

A changelog is a file that lists the notable changes made in each version of a software project, with the newest version first, each version labeled with its number and release date, and the changes grouped by type such as Added, Changed, Fixed, and Removed. Most projects keep it as CHANGELOG.md in the root of the repository. A changelog is written for people: it is a curated summary of what changed between releases, not a dump of commit messages.

To write a changelog, keep an Unreleased section at the top, add an entry there each time a notable change merges, and at release time rename that section to the new version and date. This guide is for maintainers and developers who keep a changelog for a library, an API, or a product. It covers the format, the 6 change categories, an example, the writing steps, how to handle yanked releases and corrections, and how to keep the same kind of record for changes shipped behind feature flags, with output recorded in Loglune on October 6, 2026. It does not cover patch notes for games or customer-facing announcements, which are a job for release notes.

Reproduce a customer’s flag state

Start free

What is a changelog?

Keep a Changelog, an open changelog convention, defines a changelog as a file with “a curated, chronologically ordered list of notable changes” for each version of a project. Its purpose is to let users and contributors see exactly what changed between each release.

There are 2 kinds of changelog. A curated changelog is written so readers can understand it, usually organized into bug fixes and new features. A generated changelog lists every change, such as the commit list that a version control system like Git produces, and many maintainers find that list unhelpful to readers.

The file name varies by project. Keep a Changelog recommends CHANGELOG.md, and projects also use CHANGES, HISTORY, NEWS, or RELEASES. The GNU Automake convention is ChangeLog. Hosted products often publish the same kind of record as a web page, such as the GitHub Changelog, with entries headed by date.

Changelog vs release notes vs version history

A changelog, release notes, and version history all record change, and they differ in who writes them and for whom.

Changelog Release notes Version history
Scope Every notable change, all versions, in one file One release Every change the system recorded
Written by Maintainers, curated Product or docs team, written for users Nobody: generated by Git, a package registry, or a document editor
Reader Developers, integrators, technical users Customers and end users Developers investigating a specific change
Tone Neutral and factual Explains the benefit and who gets it Raw entries

Featurebase sums up the difference as curation: version history is generated, a changelog is curated, and release notes are marketed. The Loglune guide to release notes covers how to turn one release’s changelog entries into plain-language notes for customers.

What goes into a changelog entry

Each version in a changelog is one entry, and each entry has 3 parts.

  1. A heading with the version and date: For example ## [1.2.0] - 2026-10-06. The version links to the release or to a compare view of the two tags.
  2. Change groups: A third-level heading for each type of change that occurred, such as ### Added or ### Fixed, followed by a bulleted list. Empty groups are left out.
  3. One line per change: Each line describes the change for the reader and links to the pull request, issue, or commit that has the details.

The 6 change categories

Keep a Changelog groups changes into 6 types:

Category Use it for
Added New features
Changed Changes in existing functionality
Deprecated Features that will be removed in a future version
Removed Features removed in this version
Fixed Bug fixes
Security Fixed vulnerabilities

Common Changelog, a stricter style guide adapted from Keep a Changelog, allows 4 categories in this order: Changed, Added, Removed, Fixed. It also requires each change to start with a present-tense verb in the imperative mood (“Add”, “Fix”, “Bump”), each change to reference its commit, and breaking changes to carry a bold Breaking: prefix and be listed first.

How versions and dates are labeled

Versions in a changelog normally follow Semantic Versioning: a major version (2.0.0) for breaking changes, a minor version (1.4.0) for new backward-compatible functionality, and a patch version (1.3.2) for backward-compatible bug fixes. The changelog should say whether the project follows Semantic Versioning.

Dates use the ISO 8601 format, YYYY-MM-DD. Keep a Changelog recommends it because it orders units from largest to smallest and does not overlap with regional formats that swap the month and day. Common Changelog makes the format mandatory and requires the version in each heading to match a Git tag.

An [Unreleased] section sits above the newest version. Changes are logged there as they merge, and at release time the section is renamed to the new version and date. Readers can also see what is coming in the next release.

Changelog example

The example below follows the Keep a Changelog format for a fictional library, with links at the bottom so each version heading points to a compare view.

# 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/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Bulk export for saved reports (#212)

## [1.2.1] - 2026-10-06

### Fixed
- Bulk export no longer times out for accounts with more than 10,000 rows (#209)

## [1.2.0] - 2026-09-28

### Added
- Dark mode for the dashboard (#198)

### Changed
- Reports now load the last-viewed workspace by default (#201)

### Deprecated
- The `/v1/export` endpoint. Use `/v2/export`; `/v1/export` will be removed in 2.0.0 (#203)

## [1.1.0] - 2026-09-02

### Added
- Slack notifications for failed exports (#185)

[Unreleased]: https://github.com/acme/reports/compare/v1.2.1...HEAD
[1.2.1]: https://github.com/acme/reports/compare/v1.2.0...v1.2.1
[1.2.0]: https://github.com/acme/reports/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/acme/reports/releases/tag/v1.1.0

How to write a changelog in 6 steps

  1. Create the file: Add CHANGELOG.md to the repository root with a # Changelog heading, a line saying which format and versioning scheme it follows, and an empty ## [Unreleased] section.
  2. Log changes as they merge: Add a line under Unreleased in the same pull request as the change, so the entry is written by the person who knows what changed. Reconstructing a month of work at release time is how entries get lost.
  3. Generate a draft when you need one: For an existing project, list the commits between two tags with git log (git log --oneline v1.1.0..v1.2.0) and rewrite them. Tools such as git-cliff and release-please generate changelog drafts from Git history when commits follow the Conventional Commits format, and GitHub can generate release notes for a tag from merged pull requests. Whichever source you use, the draft still goes through step 4.
  4. Remove noise and rewrite: Drop dotfile changes, dev-only dependency bumps, and formatting changes. Keep refactors that can change behavior and changes to supported runtimes. Merge commits that are one change, and leave out commits that cancel each other, because the entry describes the difference between two releases.
  5. Group and order: Put each line under its category, list breaking changes first, and keep deprecations and removals visible. Keep a Changelog notes that it “should be painfully clear when something will break.”
  6. Release: Rename Unreleased to the version and date, add the compare link, create the Git tag, and start a new empty Unreleased section. Common Changelog asks that the changelog entry be committed before the tag is created.

Yanked releases and rewriting a changelog

A yanked release is a version that was pulled because of a serious bug or security issue. Keep a Changelog says yanked versions belong in the changelog, marked in the heading so readers notice: ## [0.0.5] - 2014-12-13 [YANKED]. Common Changelog keeps the entry and its list of changes and adds a one-sentence notice under the heading that explains the status, for example that the release was never published to npm because of a security issue.

Both conventions say you can rewrite a changelog. Keep a Changelog gives the example of discovering a breaking change that the notes for a version forgot to mention. Common Changelog describes the changelog as a historical record that answers questions like “When did X change?”, which is the reason to fix it. Amoeboids adds the other half of the rule under “Preserve Version History”: do not overwrite or delete older entries, because the complete trail is what helps with troubleshooting and audits. In practice, corrections are added to an entry, and entries are not removed.

Changelog mistakes to avoid

  • Dumping the commit log: Keep a Changelog calls commit log diffs “full of noise”: merge commits, obscure titles, and documentation changes. A commit documents a step in the code; a changelog entry documents the notable difference, often across several commits.
  • Missing dates or versions: An entry without a version and date cannot answer “which release changed this?”
  • Inconsistent entries: A changelog that records only some important changes misleads readers who treat it as complete.
  • Hiding deprecations: Users upgrading need to see what will break before it breaks.
  • Mixing audiences: Internal implementation notes belong in commits or docs; customer marketing belongs in release notes.

Changelog entries for changes shipped behind feature flags

A changelog ties each change to a version and a Git tag. When a team ships behind feature flags, that tie breaks for the changes users notice. The code for bulk export can ship in 1.2.0 while a flag keeps it off, and the day a teammate switches the flag on is the day behavior changes. No tag, commit, or changelog entry records that moment. If bulk export then fails and someone switches the flag off again, the Git history shows nothing at all.

The same rules the changelog conventions apply to code releases apply to flag changes: every change belongs to a numbered, dated version; older versions stay as they were; a pulled release keeps its entry with a notice; and a correction is added instead of overwriting the record.

Loglune records flag changes this way. Each flag edit is appended as a change event to an append-only log, the pattern known as event sourcing. A publish turns the current draft into an immutable version with a number that only increases, and stores an optional note of up to 200 characters and a report ID in the same write. A note added later is appended with its author and time, and leaves the version, its original note, and its evaluations unchanged. A rollback does not repoint to the old version: it appends edits that bring the draft back to version N and immediately publishes a new version with the same contents as N. The diff between two versions and the audit of change events, each tagged with the version that first published it, are read operations and are not billed. The audit log is part of the Team Standard plan (pricing).

What the output looks like

We ran the shipped control route on a local test database on October 6, 2026, with editorial example flags. The run created a dark-mode flag and published version 1, switched dark-mode on and added a bulk-export flag set to off and published version 2, switched bulk-export on and published version 3, added a note to version 3, and rolled back to version 2. Each publish carried a changelog-style note.

Reading each version returned its note, and reading version 3 also returned the note added after it shipped:

"label": "1.2.0 Changed bulk-export default to on",
"reportId": "CHG-120",
"notes": [
  {
    "actor": "dev@example.com",
    "at": "2026-10-06T10:00:08.000Z",
    "note": "Bulk export timed out for large accounts; rolled back in version 4",
    "reportId": "INC-311"
  }
]

Version 3 kept its original note, "1.2.0 Changed bulk-export default to on", and its published state, bulk-export on. The later note sits next to it, which is the Common Changelog notice for a pulled release, recorded by the system instead of by editing a file. The rollback produced version 4, published at "2026-10-06T10:00:09.000Z" with the note "1.2.1 Rolled back to version 2: bulk-export off", and with bulk-export off and dark-mode on, the same flag state as version 2.

The audit listed 5 change events, each with the version that first published it:

seq at targetKey change publishedIn
1 2026-10-06T10:00:01.000Z dark-mode create, default off 1
2 2026-10-06T10:00:03.000Z dark-mode default off to on 2
3 2026-10-06T10:00:04.000Z bulk-export create, default off 2
4 2026-10-06T10:00:06.000Z bulk-export default off to on 3
5 2026-10-06T10:00:09.000Z bulk-export default on to off (rollback) 4

The diff between version 3 and version 4 returned "change": "changed" for bulk-export only, from "defaultVariationId": "on" to "defaultVariationId": "off", together with the publish watch finding for the same pair:

"findings": [
  {
    "kind": "defaultFlip",
    "flagKey": "bulk-export",
    "before": "on",
    "after": "off",
    "severity": "high"
  }
]

Read as a changelog, the 4 versions map to entries the way a CHANGELOG.md would: version 1 added dark-mode, version 2 changed its default and added bulk-export, version 3 changed bulk-export, and version 4 is the rollback, with version 3 still on record and carrying its notice. Like a changelog that answers “When did X change?”, the audit row for each flag change answers which version shipped it and when.

FAQ

Is it “changelog” or “change log”?

Both mean the same thing. “Changelog” as one word is the common spelling, and the file is named CHANGELOG.md. Keep a Changelog switched from “change log” to “changelog” in version 1.0.0 of its guide because it is the common usage.

Which changelog tool should you use?

The right tool depends on the reader. For a developer-facing CHANGELOG.md in a repository, Git-based generators such as git-cliff and release-please fit, because they work from commit history. For a customer-facing changelog page with widgets and email, Amoeboids lists hosted tools such as Headway, AnnounceKit, Olvy, and Canny. For changes made through feature flags rather than code, the record has to come from the flag system’s versions.

Keeping a changelog readers can trust

A changelog works when every release has a dated, versioned entry, changes are grouped by type and written for people, deprecations and breaking changes are easy to find, and older entries are corrected without being erased. Start with CHANGELOG.md and an Unreleased section, log changes as they merge, and cut the entry at release time. For changes that reach users through feature flags, keep the same record in published flag versions: a numbered version for each set of changes, notes that are added and never overwritten, and a rollback that ships as a new version.

Keep a changelog of every flag change

Loglune publishes each set of flag changes as an immutable, numbered version with an optional note and report ID. A note added after the fact is appended and leaves the version and its original note unchanged. A rollback publishes a new version with the same contents as an earlier one. The diff between two published versions is a read operation and is not billed. The Free plan costs $0 for 1 seat.