The Feature-Branch Workflow on GitHub
A recipe for collaborative projects using Git and GitHub
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:
- To make the template visible and store the file in the repository’s root directory.
- To make the template visible and store the file in the repository’s
docs
directory. - 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.