I have noticed a trend recently of library authors abandoning their CHANGELOG.md files, in favour of Github Releases.

Example of Github repository changelog.md stating that "Release notes are now stored in Github Releases"

For a library author, the choice is understandable. A Github Release message contains much the same information as a changelog, so why duplicate effort and risk having no source of truth by maintaining both? But there’s a pretty big problem with this: Github Releases today sucks UX wise for your library’s users.

TLDR? Get the best of both worlds by generating your changelog from your Releases with rhysd/changelog-from-release!

First off, let me clarify what I use changelogs for, and what I expect of them. By far the most common use case I have for a changelog is when upgrading a library, to understand what significant (to me) changes have occurred between the version I’m on and the version I’m upgrading to.

Pretty simple stuff, but let’s see how the difficulty of completing this task differs between Github Releases and a CHANGELOG.md file.

Github Releases

We navigate to our repository’s Releases page, where we can see the 10 most recently released versions. Most of them are patches, so we only get one minor release on the whole page.

This isn’t the version we’re upgrading from though, obviously. It’s ok though, there’s a search bar, so we can just search for my exact version! Except…

Nope, the Releases search bar behaviour is… unintuitive. Passing the exact Release tag string doesn’t guarantee it comes up first, or even on the first page. Adding quotation marks doesn’t either. It turns out the magic incantation is tag:blah:

Great! We have the version we’re running now. Now we just need to page forwards through the newer versions. Except, that is not possible from this view:

So I guess we’ll have to return to the index page, and page backwards from the latest version all the way to the version we are currently on after all. In batches of 10. Fun.

Well this is awful. Oh, at least there’s a &page=X query param. We can use that to skip back and forth through pages homing in on the page featuring our version. We go to page=3 and the last version is too new. On page=8 the first version is too old. How about page=5? Last version too new again… Somehow we have gone from upgrading a library to executing a human powered binary search 🤢.

Eventually we find the right page, and can now start paging forwards through the newer versions, studying the associated notes. Even now though we run into an annoying behaviour we need to be aware of. Since releases are ordered chronologically by publish date there may be releases “between” versions in the list that do not apply to us because they are not present in the version we are running, because they are from another branch:

Mercifully it is possible to suppress pre-releases with prerelease:false in the search UI. However, this doesn’t solve the issue where older major versions are maintained alongside newer versions as is common for bigger dependencies like languages and frameworks. In those cases, you just have to skip past these versions.

All in all, quite the ordeal to complete a seemingly simple task. So how does this modern clean solution compare to the ancient archaic text file approach?


We navigate to our repository’s CHANGELOG.md in its root directory, where we can see all versions ever. Next, we Ctrl+F for our version, type its name into the browser find in page UI, and we are instantly scrolled down to it. If you prefer mice to keyboards, we also have a list of jump links to every version.

It’s not uncommon to have the entire repository’s history in this single page. React for example has 11 years of history on this one page.

And to see changes between the upgraded version and ours? Scroll up. That’s it 😄. By virtue of being branch specific, changelogs sidestep the aforementioned pre-release problem, because the repository’s trunk branch changelog only contains changes that have landed on the trunk. If you are a pre-release tester, the release branch’s changelog has what you need.

I think the UX chasm between these two workflows is obvious, and that is why I am surprised and concerned to see CHANGELOG.md falling out of fashion. So what’s to be done? Well on the off chance you are a Github employee, please, improve this experience! If Releases are to replace changelogs, as I think they reasonably could, their UX needs to be better than changelogs, not worse. A UI to see changelogs between releases X and Y would be a game changer.

However, in the much likelier event you are a repo maintainer that’s all in on Github Releases, please consider maintaining a CHANGELOG.md also. If that’s too much effort consider generating a CHANGELOG.md from your existing Github Releases. Thanks to rhysd/changelog-from-release this is a pretty simple thing to do. It can even be configured to ignore certain releases, enabling you to e.g. exclude pre-releases from your trunk changelog, but include them on your pre-release branch changelog.

Thanks for reading, and above all, much love to all open source maintainers out there! ❤️

PS I used to have Disqus comments on but going forward I’m disabling them as they are full of really tasteless ads. Please discuss on HN / Reddit / Twitter X instead.