Upgrading packages is often one of the most frustrating aspects of developing software. Vague release notes can bog down and confuse the process.
Package maintainers use a pattern called Semantic Versioning (semver) to describe changes in new versions. semver tells consuming applications how to handle updates. If a project is small, maintainers have an easy time choosing the semver type for a release. Performing other tasks, like writing release notes, are also quick for small projects. But, this type of work can become time consuming as projects get larger.
For example, Jest is a large monorepo with packages that depend on each other, are public, and are consumed individually. With pull requests merging frequently, Jest maintainers often have a tough time figuring out what gets shipped in a package release.
Maintainers need to track merging changes, document how apps should upgrade, update packages within the repository, and publish packages. changesets is one of the most prominent tools available designed to make version management and changelogs within monorepos easier.
In this article, we’ll introduce changesets and review an example to better illustrate the benefits of using changesets for version management.
Let’s get started!
Jump ahead:
Without using a tool, maintainers can provide checklists in pull request descriptions to remind contributors to add details to their changes. Maintainers aim to encourage many contributions and want to make them seamless and easy. The goal of this approach is to ease the maintainer’s burden and lessen their workload.
This strategy works except when reviewers miss things. In those instances, the maintainer still has work to do. When multiple pull requests are merged, the maintainer must merge everything into a single release/document. They could have very little context about the pull requests, since there could be weeks between a merge and a package release.
changesets is a tool that helps with version management inside monorepos. It provides a CLI interface for contributors to describe their changes in a pull request alongside the semver bump type. These bits of information are called, aptly, “changesets”.
The tool is used to perform versioning, which includes consuming all of the “changesets” since the last release, determining the maximum semver bump type, updating the changelog, and updating the appropriate internal packages. repo
To demonstrate the capabilities and advantages of using changesets, let’s take a look at how different changes to a codebase are handled both with and without changesets.
For our example, we’ll consider an open source ecommerce store platform that is specifically made for pet stores. The team behind the application is having a hard time organizing the code, and they want to make parts of the codebase more usable for different applications.
They decide that creating a monorepo will be a suitable solution. So, they split up the pieces of code that they consider to be generic into different packages:
Packages, like ordering
, become adopted outside the codebase. This particular package makes the lives of other developers easier by handling the ordering of items in their stores. During the course of the next week, developers on the core team and outside contributors make changes to the package.
An internal developer adds a new feature that makes it easy to update the stock of an item:
// Updates the stock of an item in the database export function updateStock(item, quantity) {}
This change requires a minor semver bump, as apps or packages that get this update can safely upgrade with no breaking changes to any existing APIs.
This is not the only change that occurs. An external contributor reports a critical bug in which the sku
cannot be entered into the database when items are being added. As a result, the sku
is added as a required field on the addItem
function:
// old export function addItem(item) {} // new export function addItem(item, sku) {}
This change requires a major semver bump, as apps or packages must make code changes to upgrade to this new version safely.
Let’s see how these two changes can be released, both without changesets and with changesets.
After a recent release, an external developer opens a pull request to the monorepo package ordering
to add a new feature:
The repository’s maintainer is pleased but notices that the contributor didn’t update the release notes in the pull request. This is outlined in the guidelines, but contributors sometimes miss it in their haste to make changes in a timely manner.
The maintainer asks the contributor to add the notes:
This change, as well as others, get merged, and the maintainer proceeds to release the package a few weeks later.
As part of this process, the maintainer needs to figure out the semver bump type. They also need to look over every pull request to see if there were any breaking changes, feature additions, or bug fixes.
In this case, the maintainer finds the sku
addition to the addItem
change and decides a full version bump is needed. As part of the release, they need to find every package that depends on the ordering
package, like the pet-store
app, and bump it from v1.0.0 to v2.0.0. The maintainer is tasked with upgrading the app to comply with the changes.
This process takes a while since the contributor did not provide much written documentation. Next, the maintainer must create a changelog entry, by again going through all the pull requests merged since the last release.
This effort, possibly alongside other tasks, is cumbersome, prone to errors, and makes the maintainer’s job tedious.
With changesets, this process looks a bit different.
When a contributor makes a change, they run yarn changeset
and create a “changeset”. They describe the new updateStock
function, how consuming packages can use it, which package was affected by the change, and the semver bump type. Then, they push the change and open a pull request:
The maintainer simply reviews the pull request and merges it; no questions asked! The “changeset” file contains everything that is required to make the release process easy.
Afterward, additional pull requests come in, including one with a major version bump. This is due to the sku
argument addition to addItem
:
--- "changesets-package-ordering": major --- Add sku argument to addItem function The `sku` argument was added to the `addItem` function. It is a required argument due to changing business needs. Example usage: ...
Now the maintainer is ready to release the next version of the ordering
package. To do this, all they need to do is run the yarn changeset version
command.
This command removes all of the local “changeset” files, creates the changelog entry, and automatically bumps the version of the ordering
package in the package itself and in the package dependents, like the pet-store
app:
## 2.0.0 ### Major Changes - ea13cc5: Add sku argument to addItem function The `sku` argument was added to the `addItem` function. It is a required argument due to changing business needs. ### Minor Changes - 7034f8a: Add the updateStock utility function The `updateStock` utility function is used by applications to update the stock of an item.
Since the contributor had to provide detailed documentation for their breaking change, the maintainer has an easy time upgrading the pet-store
app to comply with the changes.
With the help of changesets, the maintainer is able to easily and quickly create a release and update apps across the monorepo in a standardized and reproducible way.
We want to ensure that the correct steps are consistently followed by both contributors and maintainers. This is where automations can make working with changesets even easier.
For example, we can ensure that contributors always create a “changeset” by installing the changesets GitHub Bot. This bot will comment on pull requests when a “changeset” is missing, as shown below:
Another way to ensure that no pull requests can be merged without a “changeset” is to run the yarn changeset status
command inside of a GitHub Action. If a contributor has forgotten to add a “changeset” the action will fail:
When a new version is ready to be created, maintainers can further automate this step by utilizing the changesets
GitHub Action. This action will run the yarn changeset version
command mentioned earlier and create a pull request with all of the file changes it creates.
Using the changesets package eliminates many of the steps that make the release process a burden for maintainers. With changesets, contributors are reminded to make detailed notes about their changes and maintainers can easily create new versions of packages using a single CLI command. High-quality, recurring package releases also facilitate the upgrade process for consumers.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.