A changelog is a curated, chronologically ordered list of notable changes for each version of a project. It is written for humans, not for machines. It groups the same kinds of changes under the same headings. It links every version. It puts the latest version first. It includes the release date. It tells the reader whether the project follows semantic versioning. That is the working definition. The rest of this post is the template, the realistic examples, the mistakes the cloud guides skip, and the workflow that makes the changelog a habit instead of a chore.
The cloud guides all converge on the same format. The format is right. The format is not the hard part. The hard part is the workflow that makes the changelog a habit, the four realistic examples that show what the format looks like at different stages of a project, and the mistake that turns the changelog from a useful record into a Friday afternoon chore the team quietly stops maintaining.
Table of contents
- The short version
- The working template
- The four section headings, in the order the team should use them
- Example 1: a small project at launch
- Example 2: a project that has hit product-market fit
- Example 3: a project that is scaling
- Example 4: a project that has just had an incident
- The seven things the changelog guides skip
- The mistake that turns a changelog into a chore
- The workflow that makes the changelog a habit
- FAQ
The short version
A changelog is a file at the root of the project called CHANGELOG.md. The file has a reverse-chronological list of versions, with a date and a set of grouped changes under each version. The changes are grouped into five headings — Added, Changed, Deprecated, Removed, Fixed, and Security — and the team picks the headings that fit the release. The latest version is at the top. The unreleased version is at the very top, above all the others. The unreleased section is where the team writes the changes as the team makes them, and the team promotes the unreleased section into a real version on release day.
The template is right. The hard part is the workflow. The hard part is the habit.
The working template
# 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
- (in progress) — what is shipping in the next release
### Changed
- (in progress) — what is changing in the next release
### Fixed
- (in progress) — what is being fixed in the next release
## [1.4.0] - 2026-06-09
### Added
- New feature description.
- Another new feature description, with a link to the docs: https://example.com/docs/feature
### Changed
- Description of what changed and why.
### Fixed
- Description of what was broken and how it was fixed.
### Security
- Description of the vulnerability, the impact, and the fix.
## [1.3.0] - 2026-05-26
### Added
- New feature description.
### Fixed
- Description of the bug fix.
## [1.2.0] - 2026-05-12 — [YANKED]
### Fixed
- This release was yanked because of a regression in the auth flow.
- The fix is included in 1.2.1.
## [1.1.0] - 2026-04-28
### Added
- New feature description.
The template is the skeleton. The five headings are the menu. The team picks the headings that fit the release, the team writes the changes as the team makes them, and the team promotes the unreleased section into a real version on release day. The [YANKED] tag is loud on purpose — it is the way the team tells the reader the release was pulled, and the team should use the tag every time a release is pulled, not just sometimes.
The link to the Keep a Changelog convention at the top of the file is a courtesy to the reader. The reader can read the convention, understand the format, and know that the changelog is following a known pattern. The link to the Semantic Versioning spec is the same courtesy. The team is telling the reader the project follows a known versioning scheme, and the reader does not have to guess.
The four section headings, in the order the team should use them
The team should use the headings in the order the headings appear in the template. The order is the order the reader scans, and the reader scans the headings from top to bottom. The reader who is looking for a new feature scans the Added heading. The reader who is looking for a breaking change scans the Changed heading. The reader who is looking for a fix scans the Fixed heading.
Added. New features. The team added something the reader did not have before. The reader who is interested in new features scans this heading first.
Changed. Changes to existing features. The team changed something the reader has been using, and the change is not a bug fix. The reader who is upgrading wants to know what changed.
Deprecated. Features that are going away. The team is telling the reader the feature is still working, but the team is going to remove it in a future release, and the reader should start planning the migration. The reader who is upgrading wants to know what is going to break.
Removed. Features that are gone. The team removed a feature the reader was using, and the reader needs to know.
Fixed. Bug fixes. The team fixed something that was broken. The reader who was hitting the bug wants to know it is fixed.
Security. Vulnerability fixes. The team fixed a security issue, and the reader needs to know, and the team should describe the vulnerability, the impact, and the fix.
The team should use every heading that fits the release. The team should not invent a new heading. The reader who is upgrading has mental model for the six headings, and the team should respect the mental model.
Example 1: a small project at launch
The first three versions of a small project. The project is a single-service application with a Postgres database and a static site. The team is three developers. The changelog is short. The changelog is the right size for a project at launch.
# 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
- Email notification when a new feature is published.
## [0.3.0] - 2026-06-02
### Added
- Project search, with full-text search across titles and descriptions.
- Saved searches, with a "new results" badge.
### Fixed
- Login form no longer submits twice when the user double-clicks the button.
## [0.2.0] - 2026-05-19
### Added
- Two-factor authentication, with TOTP codes.
- Account-level audit log of login attempts and password changes.
## [0.1.0] - 2026-05-05
### Added
- Initial public release.
- Project creation, editing, and archiving.
- Team membership, with three roles: owner, editor, viewer.
- Slack integration, with notifications for project changes.
The example is short on purpose. The team at launch is not adding twelve features a week. The team is adding one feature, fixing one bug, and shipping one version. The changelog is the right size for the cadence. The team that writes a 30-line changelog entry for a one-feature release is the team that is going to dread writing the next changelog entry.
The semantic versioning scheme is right for a project at launch. The team is in 0.x.x, the team bumps the minor version for new features, the team bumps the patch version for bug fixes, and the team is not promising API stability yet. The 1.0.0 release is the release where the team commits to the API, and the 1.0.0 release is the release where the changelog starts promising deprecation cycles.
Example 2: a project that has hit product-market fit
A version from a project that has been in production for a year. The project is a multi-service application with a Postgres database, a Redis cache, a worker, a cron job, and a static site. The team is twelve developers. The changelog is longer. The changelog is the right size for a project at product-market fit.
# 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 import of records from a CSV file, with progress and error reporting.
- Webhook signature verification, with the signing secret in the project settings.
### Changed
- Default retention for the audit log extended from 30 days to 90 days.
## [2.7.0] - 2026-06-04
### Added
- Custom roles, with up to ten permissions per role and a default-deny policy.
- Per-role audit log of permission changes.
### Changed
- The API rate limit is now per-token instead of per-IP, with a default of 600 requests per minute.
- The webhook retry policy is now exponential backoff with a maximum of 24 hours.
### Deprecated
- The v1 API is deprecated and will be removed on 2027-01-01. The migration guide is at https://example.com/docs/v1-to-v2.
### Fixed
- The "export to CSV" button no longer hangs on projects with more than 10,000 records.
- The audit log no longer shows duplicate entries for the same action.
### Security
- Fixed a stored XSS vulnerability in the project description field. The fix is in the project description renderer; existing descriptions are sanitized on next render. CVE-2026-XXXX.
The example is longer. The team is shipping more features, fixing more bugs, and deprecating more APIs. The deprecation entry is the part the cloud guides skip. The deprecation entry is the part that tells the reader the v1 API is going away on a specific date, and the deprecation entry is the part that gives the reader a migration guide. The deprecation entry is the part that turns a breaking change into a planned migration.
The security entry is the other part the cloud guides skip. The security entry describes the vulnerability, the impact, and the fix. The security entry is the part that tells the reader the project is being honest about the security work, and the security entry is the part the reader is going to scan for when the reader is upgrading a production system.
Example 3: a project that is scaling
A version from a project that has been in production for three years. The project is a multi-region, multi-service application with a Postgres database, a Redis cache, a worker fleet, a cron job, a static site, and a private network. The team is forty developers across three time zones. The changelog is longer still. The changelog is the right size for a project that is scaling.
# 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
- Read replicas in the EU region, with a 5-second replication lag SLO.
- Per-project encryption keys, with a key rotation API.
## [5.2.0] - 2026-06-07
### Added
- Private network peering with the customer's VPC, with a self-service peering request flow.
- Per-region failover for the worker fleet, with a 60-second RTO.
### Changed
- The default database connection pool size is now 50 per service, with a 100-connection cap.
- The static site CDN now serves from 18 PoPs instead of 6, with a 50ms p95 latency SLO.
### Deprecated
- The legacy v1 webhook format is deprecated and will be removed on 2027-06-01. The migration guide is at https://example.com/docs/v1-webhook-migration.
### Removed
- The `GET /api/v1/projects/:id/secrets` endpoint was removed in this release. Use the new `GET /api/v2/projects/:id/credentials` endpoint instead.
### Fixed
- The worker fleet no longer drops events during a region failover.
- The cron job runner no longer double-executes jobs after a clock skew of more than 5 seconds.
### Security
- Fixed a privilege escalation vulnerability in the role assignment API. The fix is in the permission check; existing role assignments are not affected. CVE-2026-XXXX.
The example is longer. The team is shipping infrastructure changes, not just features. The Added section is the part the cloud guides skip. The Added section is the part that tells the reader the project is shipping the kind of infrastructure the reader’s project is going to need when the reader’s project scales.
The Removed section is the other part the cloud guides skip. The Removed section tells the reader the v1 endpoint is gone, not deprecated, not going away, gone. The Removed section is the part that turns a breaking change into a forced migration, and the Removed section is the part that tells the reader the team is committed to the v2 API.
The semantic versioning scheme is right for a project that is scaling. The team is in 5.x.x, the team bumps the minor version for new features, the team bumps the major version for breaking changes, and the team is promising API stability across minor versions. The 5.0.0 release was the release where the team committed to the v2 API, and the team is now in the 5.x line, and the team will move to 6.0.0 when the next breaking change ships.
Example 4: a project that has just had an incident
A patch version from a project that has just had a security incident. The project is a single-service application with a Postgres database. The team is six developers. The changelog is short, but the changelog entry is the most important entry the team has ever written.
# 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).
## [3.1.1] - 2026-06-09
### Security
- Fixed a SQL injection vulnerability in the search endpoint. The vulnerability was reported by an external security researcher on 2026-06-07 and fixed in this release. The fix is in the search query builder; existing search queries are not affected. CVE-2026-XXXX. A post-mortem is at https://example.com/blog/2026-06-09-search-endpoint-postmortem.
## [3.1.0] - 2026-06-02
### Added
- Saved searches, with a "new results" badge.
The example is short. The example is the most important entry the team has ever written. The security entry describes the vulnerability, the impact, the fix, the date the vulnerability was reported, the date the fix shipped, the CVE number, and a link to the post-mortem. The security entry is the part that tells the reader the team is being honest about the security work, and the security entry is the part the reader is going to scan for when the reader is upgrading a production system.
The patch version is right for a security fix. The team is in 3.1.x, the team bumps the patch version for a security fix that does not change the API, the minor version stays the same, and the major version stays the same. The patch version is the part the cloud guides skip. The patch version is the part that tells the reader the fix is a drop-in replacement, and the patch version is the part the reader is going to upgrade to immediately.
The seven things the changelog guides skip
The cloud guides all converge on the same format. The format is right. The guides skip the seven things that turn the changelog from a useful record into a habit the team maintains.
The unreleased section is the heart of the workflow. The unreleased section is at the top of the file. The unreleased section is where the team writes the changes as the team makes them. The unreleased section is not a thing the team writes on release day. The unreleased section is a thing the team writes every time a pull request merges. The unreleased section is the part that makes the changelog a habit, not a chore.
The link to the post-mortem belongs in the security entry. A security fix is incomplete without a post-mortem. The post-mortem is the part that tells the reader what happened, what the team learned, and what the team is going to do next. The link to the post-mortem belongs in the security entry, not in a separate blog post the reader has to find. The link is the part that makes the security entry a complete story.
The [YANKED] tag is loud on purpose. A release that is pulled is a release the reader needs to know about. The [YANKED] tag is in the version header, the tag is loud, and the tag tells the reader the release was pulled, why, and what the reader should install instead. The team that uses the [YANKED] tag is the team the reader trusts.
The migration guide belongs in the deprecation entry. A deprecation is incomplete without a migration guide. The migration guide is the part that tells the reader how to move from the deprecated API to the new API. The link to the migration guide belongs in the deprecation entry, not in a separate blog post the reader has to find. The link is the part that makes the deprecation entry a complete story.
The contributor credit belongs at the bottom of the version, not in the heading. A version that credits the contributors is a version the team is proud of. The credit belongs at the bottom of the version, after the last section, in a small line. The credit is the part the reader is going to scan for when the reader is looking for a familiar name.
The changelog is not the commit log. A commit log is a record of every change the team made. A changelog is a curated list of the notable changes. The team that pastes the commit log into the changelog is the team that has a changelog nobody reads. The team that curates the changelog is the team that has a changelog the reader trusts.
The changelog is not the release notes. Release notes are written for the customer. The changelog is written for the developer. The two are different audiences, the two are different formats, and the two are different files. The team that has one is the team that has a half-useful artifact. The team that has both is the team that ships the project on a cadence the customer and the developer can both plan around.
The mistake that turns a changelog into a chore
The mistake is treating the changelog as a release-day event. The team that writes the changelog on release day is the team that has a 30-line changelog entry for a one-feature release, a 30-minute meeting to write the changelog, and a release day that is two hours longer than it should be. The team that writes the changelog on release day is the team that is going to quietly stop maintaining the changelog, and the team that quietly stops maintaining the changelog is the team that has a project the reader cannot trust.
The mistake is the workflow. The workflow that fixes the mistake is the workflow that makes the changelog a habit. The workflow is the next section.
The workflow that makes the changelog a habit
A short, opinionated workflow. The workflow is the part the cloud guides skip. The workflow is the part that turns the changelog from a release-day event into a habit the team maintains.
The unreleased section is the default branch’s working file. The team merges a pull request, the team updates the unreleased section, the team commits the unreleased section in the same pull request. The unreleased section is part of every feature, every bug fix, every refactor. The unreleased section is the part the team updates every time the team ships a change.
The unreleased section is reviewed in the pull request. The reviewer reads the unreleased section entry, the reviewer comments on the entry if the entry is unclear, and the reviewer approves the pull request with the unreleased section entry as part of the change. The unreleased section is not a thing the team writes after the reviewer approves. The unreleased section is a thing the team writes before the reviewer approves.
The release is a separate pull request. The team cuts a release, the team opens a pull request that moves the unreleased section into a real version, the team updates the version number, the team updates the date, the team merges the pull request, and the team ships the release. The release pull request is small, the release pull request is reviewable, and the release pull request is the part that turns the unreleased section into a real version.
The release happens on a schedule. The team picks a cadence — weekly, biweekly, per sprint — and the team cuts a release on the cadence. The cadence is the part the cloud guides skip. The cadence is the part that turns the changelog from a release-day event into a habit. The team that has a cadence is the team that has a changelog. The team that does not have a cadence is the team that has a release-day chore.
The cadence is the deadline for the unreleased section. The unreleased section is the list of changes that are going to ship in the next release. The unreleased section is the part the team is going to scan on release day. The unreleased section is the part that tells the reader what is shipping, and the unreleased section is the part the team is going to be honest about.
The workflow is five sentences. The workflow is the part that makes the changelog a habit. The workflow is the part the cloud guides skip. The team that adopts the workflow is the team that has a changelog the reader trusts.
FAQ
What is the best changelog format?
The best changelog format is the format the reader can scan. The reader is going to scan the latest version, the reader is going to scan the section headings, and the reader is going to scan the first line of each entry. The format that makes the scan work is the format that puts the latest version first, groups the changes by section, and uses the six section headings. The format is the Keep a Changelog convention, and the convention is the convention the cloud guides converge on.
What is the difference between a changelog and release notes?
A changelog is written for the developer. A release note is written for the customer. The developer wants to know what changed, when, and why. The customer wants to know what is new, what is better, and what is fixed. The two are different audiences, the two are different formats, and the two are different files. The team that has both is the team that ships the project on a cadence the customer and the developer can both plan around.
What is the difference between a changelog and a commit log?
A changelog is a curated list of the notable changes. A commit log is a record of every change. The commit log is full of noise — merge commits, refactors, dependency updates, internal cleanup. The changelog is the part of the commit log the reader is going to read. The team that pastes the commit log into the changelog is the team that has a changelog nobody reads. The team that curates the changelog is the team that has a changelog the reader trusts.
How often should I update a changelog?
Update the unreleased section every time a pull request merges. The unreleased section is the part the team updates as the team makes the changes. Cut a release on a schedule — weekly, biweekly, per sprint — and the release is the part the team uses to move the unreleased section into a real version. The team that updates the changelog on a cadence is the team that has a changelog. The team that updates the changelog on release day is the team that has a release-day chore.
What is the difference between semantic versioning and a changelog?
Semantic versioning is the scheme the team uses to assign version numbers. A changelog is the file the team uses to record the changes. The two are independent. The team can have a changelog without semantic versioning, and the team can have semantic versioning without a changelog. The team that has both is the team that has a project the reader can reason about. The team that has neither is the team that has a project the reader cannot.
What is the best place to publish a changelog?
The best place to publish a changelog is the place the reader is going to look for it. The reader is going to look for the changelog at the root of the repository, in a file called CHANGELOG.md. The reader is also going to look for the changelog on the project’s website, on the project’s documentation site, and in the project’s release notes. The team that publishes the changelog in all four places is the team that has a project the reader trusts. The team that publishes the changelog in one place is the team that has a project the reader has to hunt for.