What Is a Changelog? Format, Template and Real Examples
Shayan, Founder19 min read

What is a changelog? A list of the notable changes to a piece of software, newest first, with a date or a version number on each entry. The definitions agree on that much. Where they part is the format, and most of the articles that rank for the question describe one that almost nobody uses.
Take 34 open-source projects, from React and Rust to curl and SQLite, and 20 product changelog pages, from Shopify and Intercom to Buttondown and Render. One of the 34 follows the Keep a Changelog format in full. One of the 20 uses the label "Fixed". The rest of this post is what they do instead, the two conventions worth copying, a template for each, and the examples to learn from, as the 54 stood on October 31, 2026.
What is a changelog?
The GNU Coding Standards, which tell GNU maintainers how to lay out a project, say to "keep a change log to describe all the changes made to program source files", and give the reason: "so that people investigating bugs in the future will know about the changes that might have introduced the bug." That changelog was for the next programmer, and it lived in a file called ChangeLog next to the code. GNU keeps a second file, NEWS, for "a list of user-visible changes worth mentioning". The split between a record of every change and a list of the changes a user would notice is already there.
The definition most people quote today is from Keep a Changelog, a one-page convention Olivier Lacan has maintained since 2014: "A changelog is a file which contains a curated, chronologically ordered list of notable changes for each version of a project." Its version 2.0.0, published on June 7, 2026, drops "a file which contains" from that sentence and otherwise barely changes it, though its FAQ still keeps the changelog "in one file in the repository", and the site still sends visitors to 1.1.0 by default. The pages that rank for the term use the word for both the file and a product's updates page, and so does this guide.
Wikipedia's article is looser and in one way more useful: "A changelog is a list of changes made to software that has been revised over time such as a codebase or a product." It then says a changelog "can be a curated record" or "a generated report that includes all changes without regard to the ease of consuming or understanding." Both exist. Keep a Changelog 2.0.0's position on the second is that "a raw commit history is not a changelog."
The product pages define the thing by its contents. Buttondown's is "a day-by-day log of everything we ship". Zapier's is "new features, improvements, and changes to Zapier, newest first". Plausible's says "Plausible is updated multiple times per week. Here we list the major new features and improvements." None of the 20 taglines mentions versions.
Put together: a changelog is kept for the people who use the software, lists only the changes they would notice, puts the newest at the top, dates every entry, and is written by a person.
Changelog or release notes?
The two words name the same page on most product sites, and the distinction is in the guide to release notes. The short version, in Keep a Changelog 2.0.0's words: "The changelog is the source; release notes are drawn from it and shaped for the announcement." Go's site shows the split in practice. Its release history page says "This page summarizes the changes between official stable releases of Go. The change log has the full details", and each major version has a separate release notes page on top. Python does the same with its "What's New" pages, which it calls essays, and its full changelog underneath.
Several projects use the words interchangeably. Laravel's file is named CHANGELOG.md and titled "Release Notes for 13.x". Intercom's title tag is "Intercom Changelog & Release Notes | Product Updates".
The two kinds of changelog
Every changelog is one of two things, and the format follows from which.
The developer changelog travels with the code. Of the 34 open-source projects, 23 keep theirs as a file in the repository, usually CHANGELOG.md, sometimes RELEASES.md (Rust), Releases.md (Deno), NEWS.rst (pip) or one AsciiDoc file per version (Git). Seven keep it on a website instead: Go, Python, Django, PostgreSQL, curl, SQLite and VS Code. One, Next.js, keeps it only on GitHub Releases, and three more, TypeScript, Bun and Home Assistant, pair a GitHub Release with a blog post and have no file at all. Its reader is someone upgrading a dependency who wants to know what breaks, what is deprecated, and which pull request did it.
The product changelog lives on the product's site for its customers. Of the 20 product pages, 17 call the page "Changelog" in the title tag, Discord's developer docs call it "Change Log", Webflow's heading is "Updates" and Plausible's title is "What's New". Eighteen of the 20 have changelog or change-log in the URL, Plausible included. "Release notes" is the main name on none of them. Its reader is a customer who wants to know what changed since they last looked, and whether the thing they asked for has shipped.
The same product often keeps both. Sentry's page puts SDK version numbers in its entry titles; Render's lists the current CLI and SDK versions at the top of the product page; Paddle's lives on its developer docs subdomain and still reads like a product page. If you write software other people build on, you probably need the file and the page, and they will not say the same things.
The changelog format
Four things are in every changelog worth the name, before any convention:
- Newest first. Keep a Changelog lists it among its principles, and none of the 54 does otherwise.
- A date on every entry. Twenty-seven of the 34 developer changelogs date their releases, and all 20 product pages date their entries. The seven undated files are listed in the examples to avoid.
- One line per change, starting with what changed. Tailwind: "Support
--watch --poll[=ms]in@tailwindcss/cliwhen filesystem events are unreliable or unavailable". Buttondown: "Stopped asking you to confirm deleting a nearly empty draft (thanks, Marcin!)". Shopify: "Variant management now lives in the product page and introduces other improvements across the page." - Some way to tell the kind of change apart, whether by a group heading, a label or the first word of the line.
Beyond those four there are two conventions, and then what the product pages do.
Keep a Changelog
The convention most ranking articles describe. One file, CHANGELOG.md. A fixed preamble. An Unreleased section at the top that collects changes before they ship. Then one section per version, newest first, headed ## [1.2.0] - 2026-10-31 with the date in YYYY-MM-DD, and under it the changes grouped into six types: Added, Changed, Deprecated, Removed, Fixed and Security. Version headings link to the diff between that version and the one before. A pulled release is marked [YANKED].
Version 2.0.0 keeps all of that and adds rules for the cases 1.1.0 left open. The line between two of the groups: "Fixed: the behavior was wrong, and is now correct. Changed: the behavior worked as intended, and now works differently." Breaking changes get a Breaking: marker on the entry, inside its type, instead of a section of their own. A security entry with a CVE leads with the identifier. Dependency bumps are not a type: "If it matters to your users, describe its effect under the right type. If it does not, leave it out." And there will be no seventh type: "What kind of change it is goes in the type; why it matters goes in the wording of the entry, not in a new type."
Here is what it looks like in a real file. This is the top of Tailwind CSS's CHANGELOG.md on October 31, 2026, cut to the first two entries in each group:
# 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.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Add `@tailwindcss/turbopack` package to run Tailwind CSS with Next.js ([20367](https://github.com/tailwindlabs/tailwindcss/pull/20367))
### Fixed
- Ensure watch mode detects changes to symlinked `@source` files whose real paths aren't otherwise scanned ([#20356](https://github.com/tailwindlabs/tailwindcss/pull/20356))
- Ensure custom variants using `@scope` wrap the generated utilities instead of nesting inside them ([#20369](https://github.com/tailwindlabs/tailwindcss/pull/20369))Tailwind is the only one of the 34 that does the whole thing: the preamble, Unreleased, the typed groups, ISO dates, a pull request on every line and the compare links at the bottom. Five more do part of it. Mastodon uses the groups and even copies the opening sentence, but has no Unreleased section. Laravel and esbuild keep an Unreleased section with no groups, Terraform keeps one with groups of its own, and Python's full changelog has a "Python next" section that serves the same purpose. The other 28 follow none of it. The format most articles present as standard is used in full by one project in 34, and when you adopt it you are choosing it.
The GNU ChangeLog
The older convention, still in the GNU Coding Standards. A header line with the date, the author's name and email, then an indented description, then one line per changed file and function, starting with an asterisk. From the standard's own examples, drawn from Emacs:
2019-06-15 Paul Eggert <eggert@cs.ucla.edu>
Port to platforms where tputs is in libtinfow
* configure.ac (tputs_library): Also try tinfow, ncursesw (Bug#33977).You will not want this for a product. It is a record of which functions changed, kept for the next person who debugs the code. The standard itself now says the header line "is needed only in a separate ChangeLog file, not when the change logs are kept in a VCS". None of the 34 projects uses it. The question it answers, which change might have introduced this bug, is still the one a developer changelog has to answer.
What product pages do instead
None of the 20 product changelogs uses Keep a Changelog's six groups, and most do not group by type at all. Eight put a type label on each entry, and the labels do not agree: "New" on five pages, "Improved" on three, "Beta" on three, then "Feature", "Security", "Improvement", "New feature", "Changed", "Update", "Enhancement", "Fixed", "Deprecated" and "GA" on one or two each. Four tag entries by product area only (Paddle, Buttondown, Discord, Netlify). Six do not group at all, and two, Lemon Squeezy and Neon, put a "Fixes & improvements" section inside each entry. Render is the only one of the 20 that uses "Fixed" or "Deprecated" as a label, and Fixed appears ten times there against 148 entries marked New or Improved.
A customer wants the headline, a sentence on what it does for them, a screenshot, and a link to the docs. So the product changelog entry has become a small post: a title, a date, a paragraph or two, an image, a type or area label, and usually its own URL. Twelve of the 20 give each entry its own page. Two, Railway and Neon, publish one page per week instead, and Railway numbers them ("Changelog #0314").
The format that does both jobs at once is the one Buttondown runs: one line per change, one group per day, each line starting with a verb that says what kind of change it is, and the customer who reported the problem thanked in the line. Its October 30, 2026 entry begins:
Stopped asking you to confirm deleting a nearly empty draft (thanks, Marcin!)
Fixed confirmed duplicate emails getting blocked when sent (thanks, Etienne!)
Fixed extra space around captioned images in emails (thanks, Davis and Sacha!)
That is a Keep a Changelog entry with the group heading folded into the verb, and it is one of the clearest pages of the 20.
Changelog templates to copy
Two templates, one for each kind. Both are in Markdown, so they paste into a repository file or a changelog tool's editor as they are.
CHANGELOG.md template
The Keep a Changelog shape, with its 2.0.0 rules written in as placeholder text you replace. Six groups, in the order the convention lists them; leave out any group with nothing in it.
# 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
- New capability, described by what it lets the user do ([#123](https://github.com/org/repo/pull/123))
## [1.2.0] - 2026-10-31
One or two sentences on the theme of the release, if it has one. Optional.
### Added
- New capability, described by what it lets the user do ([#118](https://github.com/org/repo/pull/118))
### Changed
- **Breaking:** behaviour that worked as intended and now works differently, with what to do about it or a link to the migration guide ([#115](https://github.com/org/repo/pull/115))
- Non-breaking change to existing behaviour ([#116](https://github.com/org/repo/pull/116))
### Deprecated
- Feature that still works and will be removed in 2.0, with its replacement ([#110](https://github.com/org/repo/pull/110))
### Removed
- Feature removed in this version, deprecated since 1.0 ([#105](https://github.com/org/repo/pull/105))
### Fixed
- Behaviour that was wrong and is now correct, said from the user's side ([#120](https://github.com/org/repo/pull/120))
### Security
- CVE-2026-00000: what was exploitable and in which versions, leading with the identifier ([#121](https://github.com/org/repo/pull/121))
## [1.1.0] - 2026-09-12
### Added
- ...
[Unreleased]: https://github.com/org/repo/compare/v1.2.0...HEAD
[1.2.0]: https://github.com/org/repo/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/org/repo/compare/v1.0.0...v1.1.0The compare links at the bottom are what make the version headings clickable. Linking each line to the pull request, issue or commit is something 27 of the 34 projects do, and it is the single most useful habit, because the reader who needs more detail has somewhere to go. The optional summary sentence under the version heading is new in 2.0.0. If your project ships continuously and has no version numbers, 2.0.0 says to keep dated entries under Unreleased.
Product changelog entry template
One entry on a public changelog page. The fields are the ones the clearest product pages share: Render's type label, Zapier's "What changed" list and availability line, Shopify's link to the help doc.
## Title that says what the user can now do
**New**
October 31, 2026
One paragraph: what changed, in the reader's terms, and what it replaces or fixes.
If it needs a screenshot, it goes here.
**What changed**
- First visible difference
- Second visible difference
**Availability:** every plan, or the plans it is on. Where to turn it on, if it is off by default.
[Read the docs →](https://example.com/docs/the-feature)
Built from [this request](https://feedback.example.com/p/the-request)Label it with one of a short fixed set: New, Improved, Fixed, and Deprecated or Security when they apply. The last line, the link back to the request the change came from, is the one a feedback board makes possible, so the people who asked can see it shipped. There are six templates of this shape, from a feature release to App Store text, each with a real filled example, in the release notes template post.
Changelog examples worth copying
From the 54, the ones to study, and what each does well.
Tailwind CSS (CHANGELOG.md). The full Keep a Changelog format with nothing skipped, and the entries say what the user will see: "Don't warn about Angular's ::ng-deep and :host-context() when optimizing CSS."
Django (release notes index). One page per version with the date at the top and, for security releases, a first sentence that counts the issues by severity and then a section per CVE before the bug fixes. Its 6.1.2 page opens: "Django 6.1.2 fixes one security issue with severity “low”, three security issues with severity “moderate”, and provides a fix for an insufficient security mitigation in 6.1."
Node.js (CHANGELOG_V26.md). A short "Notable Changes" list written by the releaser sits above the full commit list generated by a tool, so you get the curated version and the complete one, in that order. Its 26.13.0 entry leads with: "This release marks the transition of Node.js 26.x into Long Term Support (LTS) with the codename 'Lithium'."
PostgreSQL (release 18.6). The 18.6 release opens with a "Migration to Version 18.6" section that says whether you need a dump and restore and which indexes to rebuild, before any change is listed.
Ruff (CHANGELOG.md). Groups that fit the project: "Breaking changes", "Stabilization", "Preview features", "Bug fixes", "Performance", and a contributors list. Ruff never uses the major version number, and the file says so.
Render (changelog). A type label on every product entry from a fixed set of six, including Fixed, Security and Deprecated. The current CLI and SDK versions are listed at the top, each entry has a page, and the feed comes in RSS, Atom and JSON.
Zapier (changelog). Every entry carries a type, the product it touches, which plans have it, a "What changed" list and an "Availability" paragraph. It is the only one of the 20 with a plan availability line.
Shopify (changelog.shopify.com). A one-sentence summary per entry on the index, a type and an area on each, and a page per entry that links the help article.
Neon (docs changelog). One dated page per week with a "Fixes & improvements" section, code samples where a change is an API, links to the docs, and both an RSS feed and an email subscription with the line "No spam, guaranteed."
Buttondown (changelog). Covered above. 1,676 entries, one line each, 22 category filters with counts, and the customers who reported the bugs thanked by name. All of it is one page: 2.2 megabytes of HTML with 1,695 image tags and no pagination.
Changelog examples to avoid
ESLint's CHANGELOG.md is 1.1 megabytes of commit lines with hashes and no grouping, and the bot commits sit between the fixes: "docs: Update README (GitHub Actions Bot)" is an entry. It is the "commit log diffs" Keep a Changelog warns about. The tool generated the file and nobody curates it.
Terraform keeps the changelog on main for the next version only. On October 31 it held "1.18.0 (Unreleased)" and some experiments. The released 1.17.1 is in a different file on the v1.17 branch, so the file a visitor lands on never lists the version they are running.
Kubernetes opens each version with about sixty lines of binary and container image download tables before the first change, and the versions carry no date. One change in 1.37.2 is listed twice under different kinds, as "Kubernetes is now built with Go 1.26.10" under Dependency and "Kubernetes is now built using Go 1.26.10" under Feature.
Rails has no version headings and no dates on main; entries are separated only by the author's name in italics. The released versions are in one file per framework component per stable branch.
React's file was one release behind. GitHub Releases had 19.3.1, published October 14; CHANGELOG.md still started at 19.3.0 more than two weeks later.
Lemon Squeezy's page had, on October 31, 2026, a newest entry from August 18, 2025, no feed, no per-entry pages, and several entries with an empty "Fixes & Improvements" heading. Miro's is titled "Weekly Update" and its newest entry was June 22, 2026. Asana's what's-new page, outside the 20, is organised by quarter, headed "Explore The Quarterly Release: Q3 2026", and no entry on it carries a date.
Intercom labels entries "New" and "New feature" side by side with no visible difference between them.
Seven of the 34 developer changelogs carry no date in the file at all: Svelte, Astro, Prettier, esbuild, Kubernetes, Git, whose release notes files are dated only by the tag, and Rails on main.
Can you generate a changelog?
Eighteen of the 34 developer changelogs are generated in some form, so the practical answer is yes, and the useful question is from what.
Eight are built from commit messages or pull request titles: Vue and Vite with conventional-changelog, git-cliff with itself, ESLint with its release tool, Laravel from a GitHub Action over merged PRs, Next.js and Deno with their own scripts, Ruff with a tool that sorts PRs by label. These produce the ESLint problem unless the project is disciplined about commit messages, and Vue's and Vite's are: the subject of a Vue commit, compat: expose _setupProxy for Vue 2.7 compiled script setup, is a readable line because the commit was written to be one.
Eight more are built from fragments a person writes when they open the pull request: Svelte and Astro with changesets, pip and pytest with towncrier, Terraform with changie, Python with blurb, Kubernetes from a release-note block in each PR description, and Prettier from one file per PR in a changelog_unreleased folder. The tool only assembles. The sentence a user reads was written by the person who made the change, at the moment they understood it best.
Two do both. Node.js's releaser writes the "Notable Changes" by hand over a generated commit list, and curl's maintainer runs a script over the commits and then edits the result. Sixteen are written by hand, including React, Rust, Go, Django, PostgreSQL, SQLite, Git and VS Code.
Keep a Changelog 2.0.0 has a paragraph on the newest way to generate one, with a language model: "it is now easy to publish a fluent changelog that no one has read." It gives a brief worth copying into your prompt: "summarize notable, user-facing changes; do not paste a git log; sort each change into one of the six types; explain the reason in the text; mark breaking changes; and remove anything not worth reading. Then read the result before anyone else does."
Draft the changelog from what you shipped.
Juno, the assistant in UserJot's changelog editor, lists the requests you completed since the last post, reads them and their comments, and writes the draft with the title, tags and linked requests filled in. You read it before it goes out. Free to start.
How to write the changelog entry
The lines in the good examples above have four things in common. They start with the change. They name what the reader will see. They are one sentence. And where there is more to say, they link to it.
Keep a Changelog 2.0.0's sorting rule settles most of the editing. If the behaviour was wrong and is now right, it is Fixed. If it worked as designed and now works differently, it is Changed, and if the reader has to do something about it, it is marked Breaking. If it is new, it is Added; if it is going away, Deprecated; if it is gone, Removed; if it closes a vulnerability, Security. "Keeping a changelog is partly an act of restraint", the spec says, and ESLint's file above is what the absence of it looks like.
I wrote more about the sentence itself, with before and after examples, in the guide to writing changelogs people read.
Where the changelog lives
For a developer changelog the answer is settled: a file in the repository, which 23 of the 34 do, usually CHANGELOG.md in the root, with GitHub Releases pointing at it. Keep a Changelog's objection to Releases-only is portability: "these releases don't travel with the repository and follow you to another host". Next.js is the one project that keeps its changelog only there.
For a product changelog, the 20 pages mostly agree:
| What the page has | Pages of 20 |
|---|---|
changelog or change-log in the URL | 18 |
| A page per entry | 12, plus 2 with a page per week |
| An RSS or Atom feed | 15 linked from the page, 2 more that exist but are not linked |
| An email subscription | 3 (Paddle, Ghost, Neon) |
| A link to the docs from the entry | 13 |
| An entry in October 2026 | 17 |
Webflow and Miro have a feed and do not link it; Lemon Squeezy, Zapier and Plausible have none. A page per entry gives each change a URL to share and search engines something to index. Email is rare: three of the 20.
From the request to the changelog
The link in the product template, from the entry back to the request it answers, is the one a feedback board makes possible. On UserJot the request sits on your public board with its votes, moves across the roadmap as its status changes, and every status change emails everyone who posted, voted or commented on it. When you publish the changelog post, it lists the requests it came from, and Juno, the editor assistant, writes the draft from the requests you completed since the last post. The page lives at /updates on your board, with its own URL per post, and the same posts show inside your app through the widget. Publishing is on every plan, including Free. The setup is in Publish updates.
Frequently asked questions
What is a CHANGELOG.md file?
A Markdown file, usually named CHANGELOG.md and kept in the root of a repository, that lists the notable changes in each released version of the software, newest first. Keep a Changelog recommends the name and says some projects use HISTORY, NEWS or RELEASES instead; Rust's is RELEASES.md and pip's is NEWS.rst. Of 34 widely used open-source projects, 23 keep the changelog as a file in the repository, under one name or another.
What is the difference between a changelog and release notes?
On most product sites, nothing: of 20 product changelog pages, 17 use "Changelog" in the title and none uses "Release notes" as its main name, and each entry on them is both. Where a project keeps the two apart, as Go and Python do, the changelog is the full record of every notable change and the release notes are the write-up for one version, drawn from it. The release notes guide covers the write-up side.
What is the standard changelog format?
There is no standard, and Keep a Changelog says so on its own FAQ. Its convention is the closest thing: an Unreleased section, one section per version headed with the number and an ISO date, and changes grouped as Added, Changed, Deprecated, Removed, Fixed and Security. One of 34 open-source projects follows it in full and five in part. The older GNU ChangeLog format records the date, author and changed functions, and is described in the GNU Coding Standards.
Should a changelog be newest first?
Yes. Keep a Changelog lists "the latest version comes first" among its principles, and none of the 54 changelogs in this post does otherwise. The reader is almost always looking for what changed most recently.
Does a changelog need version numbers?
No. A developer changelog usually has them because the software is installed by version. A product changelog usually does not; none of the 20 product pages mentions versions in its tagline, and most date their entries instead. Keep a Changelog 2.0.0 covers the case directly: "Some projects release continuously and have no version numbers. A changelog still helps: keep dated entries under Unreleased."
Is a git log a changelog?
No. A commit records a step in the source code, often one of several for a single change, and a commit history includes merges, reverts, bot commits and internal work. A changelog entry records the difference a user notices, written for them. Tools can assemble a changelog from commits, and eight of the 34 projects do that, but the result reads well only when the commit messages were written to be read, as Vue's and Vite's are.
What does "Unreleased" mean in a changelog?
The section at the top of a CHANGELOG.md where changes go as they merge, before a version is cut. It shows readers what the next release will contain, and at release time it is renamed to the new version number and date and a fresh empty Unreleased section is started above it. Tailwind, Laravel, esbuild and Terraform all keep one.
How often should a changelog be updated?
Every time something a user would notice ships, which for a versioned library means every release and for a SaaS product means as often as you deploy visible changes. Buttondown adds lines most days; Railway and Neon publish a page a week. A changelog whose newest entry is more than a year old, like Lemon Squeezy's on October 31, reads as abandoned whether or not the product is.



