Alec Brunelle Alec is a web developer who loves to work in all areas of the stack. Currently hacking on GraphQL services at Unity Technologies.

Guide to version management with changesets

5 min read 1554

Changesets Logo

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:

What is the role of changesets?

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

Sample scenario: Handling codebase changes

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:

Pet Store Monorepo
The pet store monorepo.

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.

Without changesets

After a recent release, an external developer opens a pull request to the monorepo package ordering to add a new feature:

GitHub Pull Request Containing New Utility Function
GitHub pull request containing the new utility function.

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:

Comment by a Repository's Maintainer
A comment by the repository’s maintainer.

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.


More great articles from LogRocket:


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

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:

Changeset File Pull Request
The “changeset” file inside the 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.

Automating changesets

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:

Changesets GitHub Bot Comment
changesets GitHub Bot commenting on our example pull request.

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:

Action Failing Due to Lack of a Changeset
An action failing when a contributor doesn’t add a changeset.

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.

Conclusion

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.

Alec Brunelle Alec is a web developer who loves to work in all areas of the stack. Currently hacking on GraphQL services at Unity Technologies.

Leave a Reply