Effectively using Git in a distributed team

December 28, 2020

Conventions with distributed teams

Working on big web projects with distributed teams is not easy at all, especially if your colleagues are located in different timezones.

In order to provide best practices and common workflow, conventions and code reviews are fundamental tools every team needs to consolidate and improve the quality of the code.

When talking about CI/CD workflows, you know that having well described pull requests containing structured commits with a good message is one of the keys to maintain large codebases and ensuring successful releases.

Writing good pull requests description is one of the most tedious jobs a developer has to do, but it helps the stakeholders and the team members in the long run.

In parallel, each commit should describe the intent and the scope of the change; its message should include why the changes were introduced, explaining the main aspects of the enhancement.

As you can imagine, maintaining these types of conventions is a big challenge for every Tech Lead of the project: the team can certainly write pages of documentation, forcing developers to be compliant with everything described but, we know that this is not enough.

Luckily, nowadays we have a variety of amazing tools we can use to automate workflows and processes avoiding human mistakes.

Setting up good commit messages

As mentioned before, a commit message should explain what was changed and why: it's not necessary a deeper explanation on how the change was implemented.
Unfortunately, there's not a universal way to write commits and so each team defines specific rules for their needs.

One awesome tool to potentially solve the issue is the adoption of Conventional Commits to "provide an easy set of rules for creating an explicit commit history".

In the example below, you can see the difference before/after adopting this convention:

# UNUSEFUL COMMIT MESSAGES 7b68940 fix tests 3cd9776 add tests 13e4267 update conditions 722a73f updates # MEANINGFUL COMMIT MESSAGES 0aa0e60 feat(IT-1040): round pic on Author Card 9328c40 feat(IT-1125): create serviceworker af0376f fix(IT-1007): fix images lazy-load observer 9cade3a hot(IT-1025): remove typo on `sitemap.xml`

As you can see, these commits are composed with a defined structure <type>(scope): <description> that should contain up to 50 characters.

If you work with Agile tools like Jira, the scope can be the unique identifier of a ticket: you can use it to easily group and filter all the commits related to a specific feature.

You can also have an optional body, providing additional contextual information about the code changes: it must begin one blank line after the description. A commit body is free-form and should consist of any number of newline-separated paragraphs. Each line can contain up to 72 chars.

Why do you have characters limitation? Well, in Github, you get a warning if the commit subject text is more than 50 characters, plus, if the message is more than 72 characters, it may be complex reading it with your terminal.

Another benefit is that you could automatically generate CHANGELOGs when your branch will be released, just using commit messages.

Validate the commit message

You might ask yourself: how can we ensure that commit message conventions have been respected by every dev?
You can use a custom commit template that helps ensure good commit detail by forcing to follow a structured outline.

Inside the git root folder, you need to create a file called .gitmessage that contains the template that is used when you run git commit.

This is how the template looks like:

# Subject: <type>(jira-ticket-ID): <description> # Types are [feat|fix|hot] # Respect the spacing # No more than 50 chars. #### 50 chars is here: # # Body: Explain *what* and *why* (not *how*). # Markdown syntax is allowed # Leave one blank line at the beginning # Wrap at 72 chars. ################################## which is here: # #

Then you need to associate the template file for your current repository with
git config commit.template .gitmessage

You can use CommitLint and Husky npm libraries to validate the commit message during pre-commit git-hook.

After installing both libraries, you need to crate a file called commitlint.config.js to override the default CommitLint configuration. Below, you can find an example based on the rules described before:

module.exports = { extends: [ "@commitlint/config-conventional" ], rules: { 'body-empty': [1, 'never'], 'body-leading-blank': [1, 'always'], 'body-max-line-length': [2, 'always', 72], 'header-max-length': [2, 'always', 50], 'scope-case': [2, 'always', 'upper-case'], 'scope-empty': [2, 'never'], 'subject-case': [ 2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case'] ], 'subject-empty': [2, 'never'], 'subject-full-stop': [2, 'never', '.'], 'type-case': [2, 'always', 'lower-case'], 'type-empty': [2, 'never'], 'type-enum': [ 2, 'always', [ 'feat', 'fix', 'hot' ] ] } };

In this example, a commit is valid if it respects these rules:

  • type can’t be empty
  • type must be lowercase
  • type can be feat | fix | hot
  • scope can’t be empty
  • scope must be uppercase
  • subject can’t be empty
  • subject doesn't contain a final period
  • header (main line) can’t contain more than 50 chars
  • body can be empty (as a warning)
  • body max line-length must contain less/or 72 chars
  • body must begin one blank line after the main line
  • ...feel free to explore all the rules in the example

You also need to update package.json file to run the commit validation task on each pre-commit git hook using Husky adding these few lines:

"husky": { "skipCI": true, "hooks": { "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" } }

Every time you try to commit something, the validation task will be performed: your CLI will output errors/warnings or nothing if the commit message is ok.
FYI, until you have errors, your staged files are not committed.

Given we could now use our commits to update the CHANGELOG, you can rebase interactively your feature branch before pushing it to origin. This gives you the opportunity to squash insignificant commits, delete obsolete ones, and make sure everything else is in order before merging it into the main project history.

Code review and pull request

Pull requests are a mechanism for developers to notify the team that they have completed a feature that is ready to be validated before releasing it (depending on your Git workflow).

If you’re on a team with 50+ developers, building lots of applications and microservices where your teammates have to look at the all changes you made, validate them, and ensuring the new code won’t break anything, is a big problem and time-consuming activity.
You should always respect the time of your colleagues, saving them from spending it reverse-engineering your code.

As mentioned at the beginning of this post, a pull request is an open discussion for the proposed feature. If there are any problems with the changes, teammates can post feedback in the pull request or even tweak the feature by pushing follow-up commits.
Other than that, code review is probably the main entry point to improve the skills of the entire team, discussing possible unexplored solutions, fixing mistakes, or just add some valuable feedback.

In general, you should write the pull request text imagining your reviewer doesn't know anything about the functionality you have implemented.
For example, you can provide a short description, a link to the ticket on your tracking tool, deeply describe what you changed and why, and show how to test the feature on your localhost so other teammates can debug your code during the review process.
You can also attach some screenshots just copying and pasting images if you use Github: this is really helpful to visually identify the changes while working on frontend tickets.

Again, we need some conventions and some automatic tools to facilitate developers during this task: we can introduce the pull request template.

Pull request template

When you add a pull request template to your repository, other teammates will automatically see the template's contents in the pull request body.

To make it visible in the repository's root directory, you need to create a new file called pull_request_template.md, setting up all the sections you need using markdown syntax, and providing default examples.

This is an example of a pull request template that works well but, keep into consideration that you can find hundreds of other examples online if needed:

## Short description of the feature A few sentences describing the overall goals of the pull request's commits. ## List of changes proposed in this PR * * ## How to test the feature locally Outline the steps to test or reproduce the PR here. * URL: * ENV: ## Related PRs List related PRs against other branches: branch | PR ------ | ------ other_pr_develop | [link to PR]() ## Todos - [ ] Tests - [ ] Documentation ## Impacted Areas in Application List general components of the application that this PR will affect: *

Finally, try to keep pull requests as small as possible by splitting out unrelated changes and submitting multiple pull requests per time: keep in mind that if the size is overwhelming to the reviewer, a big pull request is likely to get less time and attention.