Code version best practices with clean commit formats

Angular Nov 25, 2022

Ever wondered how repositories like angular, react,... etc manage their workflow process like version upgrades and generating changelogs for each release. They use a few tools like semantic-release/release-please to parse the commit history to read the changelog and publish a new version(Major/Minor/Patch) based on SemVer guidelines. This is made possible by having commits structured and clean, following commit guidelines such as conventional commits.

In this article, we'll learn how Halodoc has set up the repo infra to make it seamless to work with automated tools.

Angular v14 release changelog - https://github.com/angular/angular/releases/tag/14.0.0
Angular commit history - https://github.com/angular/angular/commits

There are hundreds of active repositories in Halodoc managed by several teams. With the deadlines approaching, devs sometimes give random commit messages such as fix, version bump, etc. commit messages like these are not helpful as devs will not be able to understand and correlate without looking at the code. It becomes prevalent when you have a repository managed by many teams with developers consistently adding new features/squashing bugs/enhancements. A good commit message should be short, concise, and gives an outline of what was the impact on the software application.

To ensure that the commits are linted properly, the team must adhere to commit guidelines set by the organization. We've found that angular commit convention guidelines are good to get started. To use these guidelines, a few open source tools like conventionalcommits, commitlint, commitizen, and husky make it easy for integration with repositories.

What is Conventional Commit?

The Conventional Commits specification is a lightweight convention on top of commit messages. It provides an easy set of rules for creating an explicit commit history; which makes it easier to write automated tools such as semantic-release to add changelog and publish versions on top. This convention aligns with SemVer, by describing the features, fixes, and breaking changes made in commit messages.

Commits is a specification for adding human and machine-readable meaning to commit messages.

The commit message should be structured as follows:

conventional commits base structure

type describes the category of your change & can be one of

scope describes the module affected by the change, and can be project specific
subject describes impact after the change, avoid repeating information from type and scope.

Few examples of good commits

Commit message with description and breaking change footer
Commit message with scope and ! to draw attention to breaking change
Commit message with multi-paragraph body and multiple footers

Why Use Conventional Commits

  • Automatically generating CHANGELOGs.
  • Automatically determining a semantic version bump (based on the types of commits landed).
  • Communicating the nature of changes to teammates, the public, and other stakeholders.
  • Triggering build and publish processes.
  • Making it easier for people to contribute to your projects, by allowing them to explore a more structured commit history.

What is Commitlint?

Commitlint checks if our commit messages adhere to a certain commit format. It's like a linter that verifies if our commits are following the guidelines, and will show an error if the messages are not according to the formats.

commitlint helps your team adhere to a commit convention. By supporting npm-installed configurations it makes sharing of commit conventions easy.

What is Commitizen?

It provides a GUI for the user to add type, scope, message, description, Breaking Changes, and Closing Issues.

Commitizen is a tool designed for teams. Its main purpose is to define a standard way of committing rules and communicating it (using the cli provided by commitizen). The reasoning behind it is that it is easier to read, and enforces writing descriptive commits.

What is Husky?

Husky improves your commits and more 🐶 woof!

You can use it to lint your commit messages, run tests, lint code, etc... when you commit or push. Husky supports all Git hooks.

Modern native git hooks made easy

We'll use the above three tools to improve the commit experience for devs.

Installation and configuring the tools

Installing commitlint cli & config-conventional as dev dependencies.

@commitlint/config-conventional is a shareable commitlint config enforcing conventional commits guidelines.

this installation can be simplified as below

Configuring commitlint

  • Create commitlint.config.js file and extend @commitlint/config-conventional
commitlint.config.js
  • other file formats such as .commitlintrc.js, .commitlintrc, .commitlintrc.json, .commitlintrc.yml can also be used.

Configuring pre-commit hook with Husky:

Install husky as a dev dependency with npm

Installing husky as dev dependency

If you experience the error "could not determine executable to run" when committing your code, you need to reinstall husky this command or remove your node_modules and reinstall node

create a prepare step to enable husky after installation

Husky script initialization

This will ensure that husky is configured properly after the manual installation of husky or npm i

we want to run a pre-commit hook that will lint our commit message according to the conventional commit guidelines. To enable that, create a pre-commit file by running this command.

With this commitlint installation is complete.

whenever we commit with git commit -m "<text>", our configured pre-commit hook runs to make sure that commit message is following the conventional commit guidelines.

Let's see a quick demo

We get the above error and help when the commit message is violating the commit guidelines. To fix it, we can change the commit message to

// is added for demonstration.

Lets's use another tool called commitizen that works hand in hand with commitlint and gives us a nice GUI to define the impact of commit.

Commitizen Installation & setup

Let's install commitizen first with npm as a dev dependency.

Now we need to install cz-conventional-changelog part of the commitizen family. Prompts for conventional changelog standard.

or we can use commitizen adaptor provided by commitlint called   @commitlint/cz-commitlint

There are a lot of adaptors provided by commitizen team, that are ready to use for teams with different requirements, such as enforcing the commit to include jira id, time spent, workflow status, and comments with this adaptor.

we'll use the default adaptor supported by commitizen available from commitlint that works for most teams.

Install @commitlint/cz-commitlint as dev dependency.

Now add the below script to package.json file

Now configure commitizen

If you've installed cz-conventional-changelog instead of @commitlint/cz-commitlint, the configuration would look like this

Commitizen installation & setup is now complete. Now we can run the following command

commitizen will provide us with different steps to choose type, add scope, description, body, footer, and issues. Let's see it in action.

Using commitlint with CI tools

Although we've set up commitlint and commitizen to lint commit messages, it's possible to bypass them locally. we can add a step in our CI/CD workflow to validate commit messages.

In this example, we are going to be using Gitlab Pipeline but there are official guides for Travis CI, Circle CI, and Github Actions as well.

Conclusion

We've configured tools to lint commit messages. These tools will now help the team to keep the commits structured and clean.

Next steps...

We can use tools like release-please or semantic-release to automate the whole package release workflow : determining the next version number, generating the release notes, and publishing the package.

It does so by parsing your git history, looking for Conventional Commit messages

References

Angular commit guidelines - https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit
Conventional Commits - https://www.conventionalcommits.org/en/v1.0.0/
Commitlint - https://commitlint.js.org/#/
@commitlint/config-conventional - https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional
Commitizen - https://commitizen.github.io/cz-cli/
semantic-release - https://github.com/semantic-release/semantic-release
release-please - https://github.com/googleapis/release-please

Join us

Scalability, reliability, and maintainability are the three pillars that govern what we build at Halodoc Tech. We are actively looking for engineers at all levels and if solving hard problems with challenging requirements is your forte, please reach out to us with your resumé at careers.india@halodoc.com

About Halodoc

Halodoc is the number 1 all around Healthcare application in Indonesia. Our mission is to simplify and bring quality healthcare across Indonesia, from Sabang to Merauke. We connect 20,000+ doctors with patients in need through our Tele-consultation service. We partner with 3500+ pharmacies in 100+ cities to bring medicine to your doorstep. We've also partnered with Indonesia's largest lab provider to provide lab home services, and to top it off we have recently launched a premium appointment service that partners with 500+ hospitals that allow patients to book a doctor appointment inside our application. We are extremely fortunate to be trusted by our investors, such as the Bill & Melinda Gates Foundation, Singtel, UOB Ventures, Allianz, GoJek, Astra, Temasek and many more. We recently closed our Series C round and In total have raised around USD$180 million for our mission. Our team works tirelessly to make sure that we create the best healthcare solution personalised for all of our patient's needs, and are continuously on a path to simplify healthcare for Indonesia.

Chaitanya S

Web Engineer