Releases the Easy Way
Published: 2022-05-02 13:54:29

![Clash of the Titans, Release the Kraken](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/agpdbelv3ph39ti9gzkl.gif)

[Semantic versioning](https://semver.org/) and [changelogs](https://www.freecodecamp.org/news/a-beginners-guide-to-git-what-is-a-changelog-and-how-to-generate-it/) are the lingua franca of software releases. They provide an easy means to communicate what’s changed in a piece of software and what developers and users can expect when upgrading. Despite their importance, maintaining a changelog and semantic version for your software manually can be tedious and error prone.

Good developers are 1) careful and 2) [lazy](https://medium.com/giant-machines/embrace-your-laziness-automatically-convert-word-documents-into-terms-conditions-pages-a6e0574e9dc9), so thankfully there are a variety of tools you can use to quickly, easily, and painlessly semantically version your project and generate a changelog. This post will walk you through adding some of these tools to your repository.

## TL;DR on Semantic Versioning

[Semantic versioning](https://semver.org/) follows a simple, three-number pattern: `Major.Minor.Patch` (e.g., `1.0.0`, `2.1.3`). Each number tells developers and users what has changed within your project.

The first–or major–number denotes big architectural changes or breaking API changes. It tells developers that upgrading to this version might require some extra effort on their part to make their project compatible. Here’s where you communicate that you’ve changed a route, tweaked some method names, or now require the latest version of Node.

The minor number means that you’ve added new features that are backwards compatible. For example, you might increment the minor number if you’ve added a feature for converting images into PDFs or just added a new route without changing any existing ones.

The patch number denotes bug fixes.

Each individual release, even if it includes changes from all three categories above, increments only one of the major, minor, or patch version numbers. Preference is given to major, minor, and patch changes, in that order. So two big API changes and ten new features won’t bump your version from `1.0.0` to `3.10.0`. Rather, your version will simply jump to `2.0.0`. And twelve new features and a bug fix after that will bump your version to `2.1.0` not `2.12.1`.

## About Your Sloppy Commit Messages…


![Schitts Creek, I'm don't think I'm ready to commit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eb1k6r12r5najig4kfrx.gif)

Okay, so where do you start if you want to update your project to better handle semantic versioning and changelogs? First on the chopping block are your lazy commit messages. Unfortunately, those two-word commits will have to change. But not by much!

Most semantic versioning tools are built on the concept of [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/), which provide an easy structure for commit messages and take the form of `type(optional scope): description`. They hinge on the idea of commit types. The two main types are:

* `feat`, which denotes a new feature and will increment the minor number
* `fix`, which is used for bug fixes and will increment the patch number

Semantic versioning tools will review the types present in your commit history and update the version number according to the standards we discussed above. So ten commits with a `feat` type won’t increment the minor number ten times.

Some tools include other commit types that allow for more descriptive commits but won’t update the version number at all:

* `build`: for changes to the build process
* `ci`: denotes CI/CD updates
* `docs`: for changes to the documentation
* `refactor`: updates to the source code that don’t add new features or fix any bugs
* `test`: updates to unit, integration, or end-to-end tests
* `perf`: for performance improvements
* `style`: updates to white space, semicolons, ESLint issues, etc.

The scope (which is optional and added in parentheses after the type) is a short message that quickly communicates what was updated in your commit. It’s usually a one- or two-word noun describing the affected section of your codebase. You _don’t_ need to list every single file affected here.

Your description is a short sentence describing what changes were made.

Here’s what all that looks like in practice:

* fix(endpoints): update user sorting method
* feat(checkout): add save cart feature
* ci: add github actions secrets to pipeline
* test: update cypress tests
* refactor(checkout): tweak typescript types for save cart feature

Lastly, the major number isn’t incremented via a type. Rather, most semantic release tools rely on the text `BREAKING CHANGE:` followed by a description of the breaking change in the body or footer of any commit. Regardless of the type, including this text _will_ increment your major number, so be sure to use it wisely.

All of these commits will be compiled into a changelog automatically by the tools described below. Easy!

## This Just Sounds Like _More_ Work

Stick with me here. Remember what I said about good developers being careful and lazy? This little bit of structure to your commits serves that purpose. Now you can add some tools to your project that will leverage these conventional commits and make your life so much easier!

You’ll be utilizing a few tools here to handle semantic versioning, conventional commits, linting, and changelogs:

* [Commitizen](https://www.npmjs.com/package/commitizen) (for conventional commits)
* [Husky](https://www.npmjs.com/package/husky) with [@commitlint/cli](https://www.npmjs.com/package/@commitlint/cli) (to lint your commit messages)
* [Standard Version](https://www.npmjs.com/package/standard-version) (to handle semantic versioning)

## Making Conventional Commits Easier

First up, let’s add Commitizen. Commitizen provides an easy interface for structuring your conventional commits. Sure, you can write these by hand, but where’s the fun in that?

![Commitizen screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cstfvmhcgpj7dw5ixyaf.png)

Install Commitizen globally by running `npm install -g commitizen`. Then, you’ll need to initialize your project with a Commitizen adapter. Go to your repository in your terminal and run `commitizen init cz-conventional-changelog –save-dev –save-exact`. This will install the [cz-conventional-changelog](https://github.com/commitizen/cz-conventional-changelog) package as a dev dependency and update your package.json file.

Once you’ve done this set up, you can open the interface just by running `git cz`. You can also install the [VS Code Commitizen extension](https://marketplace.visualstudio.com/items?itemName=KnisterPeter.vscode-commitizen). Neat!

## Linting Your Commits

Linting–while annoying–is a developer’s best friend. We all make mistakes, but it would be awful if your semantic versioning was thrown off just because you forgot to add a `feat` type to your commit message.

Fortunately, you can use [Husky](https://www.npmjs.com/package/husky) and [@commitlint/cli](https://www.npmjs.com/package/@commitlint/cli) to lint your message before a commit is made. First, install the packages you’ll need:

```
npm install –save-dev husky @commitlint/{config-conventional,cli}
```

After that, you’ll need to initialize Husky’s Git hooks in your repository with `npx husky install`. This will add a `.husky` folder to the root of your project. Then, add the Commitlint hook with `npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'`. You’ll also need to run `echo "module.exports = {extends: ['@commitlint/config-conventional']};" > commitlint.config.js` to create a configuration file.

Now you’ll be notified if you try to make a commit whose message doesn’t meet the Conventional Commit standards! Even better, you can configure your linting by updating your config file with some [rules](https://github.com/conventional-changelog/commitlint/blob/master/docs/reference-rules.md)!

![Commitizen linting errors](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/seelu2ec92ybai0i6yod.png)

## Versioning and Changelogs

Now for the coup de grace! You’ll be using Standard Version to tie all this work together and easily tackle your versioning and changelogs. To do this, you’ll need to install the `standard-version` dependency and then add a script to your package.json:

```
npm install –save-dev standard-version
```
```
{

  “scripts”: {

    “release”: “standard-version”

  }

}
```

Now, when you run `npm run release`, Standard Version will bump the version number in your package.json file. It will also generate a changelog, like so:

```
# Changelog

## 1.1.0 (2022-05-01)

### Features

* added a really cool feature!

### Bug Fixes

* fixed an annoying production bug!
```

To see what changes will be made without actually committing anything, you can use the dry-run mode: `npm run release – –dry-run`.

## It’s a Piece of Cake!

We’ve gone over semantic versioning, conventional commits and why they matter, installed a linter to yell at you, and added a bunch of tools to your project. This definitely is a lot! And if you’re not used to working in this manner, it might indeed be overwhelming at first, especially when it comes to generating easier commits.

I guarantee you, though, that this will save your team much more time in the long run! On our own projects, these tools have allowed us to pull away from the manual generation of changelogs for stakeholders and engineers. And if you’re working on an open-source project, all of this is even more critical. After all, your users rely on accuracy in your logs and version numbers.

So what are you waiting for? Get installing!