The Feature-Branch Workflow on GitHub

A recipe for collaborative projects using Git and GitHub

Jonathan Manera
5 min readJul 16, 2022
Photo by Rubaitul Azad on Unsplash

In case you are considering adopting a workflow that allows all members of your team to seamlessly integrate their changes. The Feature Branch Workflow not only fulfills these purposes, but also is one of the easiest workflows to implement and understand.

The Main Branch

Unlike other workflows where you have multiple long-lived branches (such as Gitflow), the Feature Branch Workflow consists in a single long-lived branch known as the “main” branch. Developers create new short-lived branches every time they start working on a new feature.

Feature Branches

Feature branches should have descriptive names based on the team’s naming convention (e.g., feature/userstory-01) and must be integrated into the main branch via pull-requests.

Each pull request is merged into a single commit in which all commits from the feature branch are “squashed.” This is important because the main branch holds the official project history and does not care about individual commits.

As soon as the merge is complete, the feature branch is usually deleted.

Trunk-Based Development

Another important aspect of having a single long-lived branch is that it helps teams iterate quickly and implement CI/CD.

Trunk-based development is a common practice among DevOps teams, requiring developers to merge short-lived branches into a central “trunk” or main branch.

General Settings

If you already have a GitHub repository, go to Settings and select “General”.

In the “Pull Requests” section, disable the options “Allow merge commits” and “Allow rebase merging”. Make sure “Allow squash merging” is selected, as shown in the image below.

In the “Allow squash merging” section, you can select the option “Default to pull request title” to use PR titles as comments for squashed commits.

You might also want to consider enabling “Automatically delete head branches” to remove branches that have already been merged.

Adding Collaborators

Go to “Collaborators” and select “Add People”.

If the repository is in your personal account, you have two permission levels: the repository owner and collaborators.

If the repository is within an organization, once your collaborators accept the invitation, make sure everyone except you has write access (you should be admin). For further information about organization roles see: Repository roles for an organization.

Rules

Create branch protection rules to reinforce the workflow.

Go to “Branches” and select “Add rule.”

First, you need to add a pattern. Just set the name as “main”.

Enable the “Require approvals” option so that one or more reviewers must approve open pull-requests before merging.

I personally like to enable “Allow force pushes” for everyone with push access to main, just in case.

Finally, select “Create.”

Pull Request Templates

It would be a good idea to add a pull-request template to your repositories, for collaborators to fill in with useful information for the reviewers.

First, you need a markdown file named pull_request_template.md. Feel free to use the template below as a reference:

Now, you have three options:

  1. To make the template visible and store the file in the repository’s root directory.
  2. To make the template visible and store the file in the repository’s docs directory.
  3. To make the template not visible and store the file in a hidden directory named .github.

Naming Conventions

As a team, you need to have an easy set of rules for naming your branches, pull-requests and commits.

For branches

Use a pattern like {type_of_change}/{issue_id}_{description} .

Examples:

  • feature/ISSUE1_my_new_endpoint
  • bugfix/ISSUE2_business_logic_correction
  • hotfix/ISSUE3_security_issue
  • tests/ISSUE4_my_new_test
  • refactor/ISSUE5_memory_optimization

For commits and PRs

Use a pattern similar to the one for branches, e.g., [{issue_id}] {description}.

It is expected that this convention will be used for the squashed commit in main, as shown in the example below.

Once the PR is approved, select “Confirm squash and merge”:

Git Tags

Git tags are used to mark a specific commit in the project history.

Release versions can be labeled with tags. A common practice is to prefix version names with the letter v. Based on the Semantic Versioning specification, a version name should have the following pattern: v{MAJOR}.{MINOR}.

  • MAJOR for major incompatible changes (breaking changes).
  • MINOR for new functionalities (backwards compatible changes).

Version control is particularly important, since we use a single long-lived branch and must be able to separate stable, production-reliable code from code that is not yet ready. Because of that, we have two kinds of tags: release tags and pre-release tags.

Release tags

This tag tells us that this code is ready for production and that you can rely on it.

Pre-release tags

Also known as “release candidates,” this tag may be used for testing purposes. Typically, the tag name indicates the next release version followed by the release candidate version, e.g., v1.0-rc1, v1.0-rc2, v1.0-rc3.

Don’t forget to select “Set as a pre-release” when you create pre-release tags!

Summary

In this article, we have discussed the Feature Branch Workflow. This workflow helps organize teams, centralizes a unique project history, promotes collaboration between team members through pull requests, allows a quick iteration, and makes it easy to implement CI/CD.

Thanks for reading. I hope this was helpful!

The example code is available on GitHub.

--

--

Jonathan Manera

If you wish to make a Java app from scratch, you must first invent the universe.