If you’re a solo developer working on your own projects your Git workflow is usually simple: You work on the main (or master) branch all day every day.
You probably know that professional developer teams don't work like this. Multiple devs all committing to the main branch can quickly become chaotic. And it’s likely that unreviewed or untested code makes it into production eventually. Danger!
Professional teams use processes and workflows to prevent this from happening. And the most common Git workflow used in developer teams (at least from my experience):
Trunk-Based Development (or more correctly Scaled Trunk-Based Development).
If your goal is to find a job as a professional developer I highly recommend getting used to this workflow upfront. The more you know how to work like a professional the less you’ll be overwhelmed on your first job.
I promise: it’s not very difficult if you know the basics of Git. But there is a lot of glossary around it that might confuse you at first.
Watch the video below to see me walk through one cycle of the workflow. Below the video you can find instruction on how to set up branch protection in your GitHub repository to enforce this workflow and a detailed walkthrough based on screenshots.
This series of articles and the application I'm building are part of my upcoming React Job Simulator. Build a project hands-on like a professional team and gain job-like experience.
Table of Contents
- Trunk-Based Development in a Nutshell
- Branch Protection: Enforce usage of Pull Requests
- A tour through the Trunk-Based Development workflow
- Summary
Trunk-Based Development in a Nutshell
- You check out a new branch from the main branch.
- You commit your code on this branch and push it to the GitHub repo.
- You open a Pull Request (or Merge Request as GitLab calls it).
- Automated tests verify that the application behaves as expected.
- A teammate reviews your code and you adjust it according to the feedback.
- You merge your branch into the main branch via the Pull Request (short PR).
If this is all sounds like Gibberish, no worries. Have a look at my free course where you can learn and practice this workflow.
What the heck is a Pull Request?
I always found the name Pull Request on GitHub confusing. GitLab calls it a Merge Request which is much more descriptive. Basically, a Pull Request is a way to ask for permission to merge your code into the main branch:
“Hey team, can somebody have a look at this code and tell me if it’s any good? I’d like to get it into the main branch so our users can benefit from it.”
You can think of a Pull Request as a feature on top of a Git branch that allows you to get feedback from your teammates. And as mentioned it also allows you to automatically run checks and tests on your code changes before they go to the main branch.
To summarize, Pull Requests are
- a mechanism to gather feedback and thus increase code quality
- a tool to run automation (e.g. tests) on your code to decrease the risk of introducing bugs into production code.
Branch Protection: Enforce usage of Pull Requests
Processes and workflows are great. But people are lazy and look for workarounds. So ideally we want to force everyone on the team to use Pull Requests instead of committing directly to the main branch.
Luckily GitHub has our back with a feature called “branch protection”. To protect the main branch open your repository’s settings on GitHub, select “Branches” in the left menu, and click on the “Add rule” button.
A few notes about the selected rules:
- In a team of developers the option “Require a Pull Request before merge” → “Require approvals” is mostly activated. This way we can enforce that developers review and approve each other’s code. That is one safeguard against new bugs and ideally increases code quality and coherence.
- The option “Require linear history” is not necessary but from my experience many teams use it nowadays. It prevents merge commits on the respective branch. Instead you have to either “Squash and merge” a Pull Request or “Rebase” it. You can see the “Squash and merge” in action including an explanation here in the video.
- The “Include administrators” option is important if you want to enforce the workflow for yourself in your own repositories. Since you’re the administrator the rules wouldn’t apply to you otherwise.
If a developer now creates a commit on the main branch and tries to push it they will see an error message.
A tour through the Trunk-Based Development workflow
Open a Pull Request
git checkout -b migrate-to-styled-components
Now we write our code, commit and push it to the remote repository on GitHub.
git commit -m "Migrate home page to styled-components"
git push origin migrate-to-styled-components
On GitHub, you should now see a banner to create a Pull Request.
Once you click the button you see a form where you can enter a title and description. Next click the “Create Pull Request” button.
Congrats, you opened your first Pull Request! This is what you should see now:
Continuous Integration Pipeline
Did you note the status checks at the bottom of the PR?
This is a really handy feature. You can run scripts like npm run lint
or npm run test
within your Pull Requests to decrease the risk of introducing bugs. This is called a Continuous Integration pipeline. I’ll leave it as a cliffhanger for tomorrow’s article. If you can’t wait you can already watch me set it up in the video.
Code Reviews
In a real-world team, your code is typically reviewed by at least one teammate. This again prevents bugs and helps keep the codebase clean and consistent. A Pull Request is also a great way of discussing your code in case you’re stuck.
So let’s switch to another account with access to the repository. Here is how our imaginary teammate would review your code.
Our teammate can add comments to the code.
Finally, they submit the review.
As the author of the Pull Request, we can now see the comments.
Handling Review Comments
We have two options now: we can update our code according to the comments or start a discussion.
To adjust our code we simply head back to our local machine, change the code, commit and push it. You can see the new commit below the review comments. You can also add a comment and resolve the conversation.
Finally, you can request a new review:
Approving a Pull Request
Once your teammate is pleased they can approve your Pull Request by submitting a review. They might add a useless yet affirmative emoji comment to make you happy.
Merging the Pull Request
Finally, it’s time to merge our Pull Request. Now our code changes will be added to the main branch.
Remember that we set the “Require linear history” option in our branch protection rules? That’s why we see a “Squash and merge” button instead of a simple “Merge” button by default.
Let’s see what happens when we push the button:
And once we press the confirmation button we’re all set.
The history of the main branch
I didn’t explain yet what the “Squash and merge” button does, right? The Pull Request (or our Git branch) contained multiple commits:
When we look at the commit history of our main branch we don’t see these commits anymore. Instead, there’s only a single commit that points to the Pull Request (via the link #6
):
All the commits of our original branch have been squashed into a single commit. The benefit here is that you don’t need to keep the commits in the Pull Request super tidy. For example, commits that are simple fixes during the review process like “Remove unused class name” don’t really need to show up in the history of the main branch.
Updating the local main branch
As the last step (that’s easy to forget) we sync our local main branch with the remote repository. Since the merge happened on GitHub our local machine doesn’t know about these changes in the main branch yet.
git pull origin main
When we work in a team of developers we should actually do this every time we start working on a new branch.
Summary
In this article you learned how to set up a GitHub repository with branch protection to enforce a popular Git workflow called Trunk-Based Development. By now, I hope you're less intimidated by Git & GitHub thanks to the detailed walkthrough.
Top comments (3)
These four tutorials have been a gift to me. I really thank you for spending your time giving us developers this valuable information.
Thanks for the feedback. I'm glad you found it valuable :)
Where I work, we are actually normally two steps away from master/main. We have a Project/Feature branch that acts as a defacto main for the current feature we are building as a team, and individualy, work on a Story branch off project, not only can we not commit to main, we also can't commit to the project without a pull request, and PRs from Project into main are only allowed by a select few. The expectation is that we continually pull from project to local and resolve diffs before committing and raising new PRs. For us main is sacrosanct and never gets touched until code is in production.(Which is a whole another level)