Pull requests are at the core of using GitHub, regardless of whether we're talking about open-source or private projects. The pull request process not only serves as a gate for code but also creates opportunities for discussion and exchange of knowledge. It also acts as documentation for code decisions.
In this post, I'll talk about a super-specific—yet very valuable—aspect of the pull request process: the different ways in which you can approve a pull request on GitHub.
I'll open with some fundamentals, defining GitHub pull requests and offering a brief explanation about how they work. Then I'll talk about some different ways in which you can approve GitHub pull requests.
By the end of the post, you should know more about some of the options at your disposal and why some represent anti-patterns you should be aware of.
Table of Contents
- What Is a GitHub Pull Request?
- How Does a Pull Request Work?
- How to Approve a Pull Request in GitHub
- Why Are Certain Approval Processes Better?
What Is a GitHub Pull Request?
Let's start with the "what" and "how" of GitHub pull requests (PRs).
A pull request is a functionality popularized by GitHub—but currently offered by its competitors as well—that allows a contributor to request approval of their code changes before it is merged.
The pull request process is a core tenet of how many teams work. It can serve as a protective barrier, guarding the quality of the code. Additionally, PRs create a nice opportunity for reviewing the code, discussions, and exchange of knowledge.
If you use continuous integration and continuous deployment —and you should—PRs represent a nice spot for the implementation of quality gates. Before a branch is merged into the mainline, you have the perfect opportunity for running automated tests and doing all kinds of useful checks.
Finally, pull requests are a great piece of documentation. The discussions that take place while reviewing proposed changes become invaluable historical documents for people in the future who will seek the context and motivation behind certain changes.
How Does a Pull Request Work?
For open-source projects, the pull request process typically works like this:
- You find an open-source project you want to contribute to and fork it (i.e. create a copy of it using GitHub's UI).
- Then you clone the Git repository locally.
- You start working on your task or fix on a new branch—usually created off of main, though different projects might use different workflows.
- At any point, you can push the changes to your own fork of the repository on GitHub.
- When you feel you're done, you go to the original repository's page and use the UI to create a pull request.
- Once the pull request is opened, it cannot be approved by it's creator. You must submit it to a peer for review and approval.
So, for open-source contributions, there will be at least three repositories you must be aware of: the original repo, your GitHub fork, and your local repository. This is called the "fork and pull model."
For private repositories, things are generally a bit more simplified. Instead of having to create your own fork, you'd typically have Git push access to the same repository. You'd then clone this repository.
From this point on, the process would be the same. This is called the "shared repository model," and it's what small open-source teams and private companies use.
How to Approve a Pull Request in GitHub
As it turns out, the GitHub pull request process is quite flexible, accommodating many different styles and workflows. I'll now walk you through several different ways in which you can approve a PR on GitHub.
Approving Without Review
Let's start with the worst possible scenario: when people accept PRs without any kind of review. Except when it comes to the most trivial of changes (e.g., fixing typos or improving the formatting of some class), approving PRs without reviewing them is a terrible anti-pattern.
PRs merged without reviews are signs of serious problems inside a team or organization. PR size might be one of them.
Large pull requests are hard for the reviewers, so many might simply give up and just accept them by default. Lack of team conventions and strong governance models also come to mind—after all, it shouldn't even be possible to merge PRs without review.
Our developer bot, WorkerB, can at least notify you and your team when this happens, and you can set team goals to reduce when this occurs.
If a PR is approved without review, there are ways to deal with that, which include reverting the PR or even performing a review after the fact.
Closing Without Approving
It's also possible for a reviewer to close a PR without approving it. This might sound harsh, but it's actually better for the health of the codebase than blindly approving all PRs.
In the context of open-source projects, maintainers will often close PRs that don't follow the contributing guidelines of the project. They might even use bots to automatically check some requirements and close the PRs that don't adhere to them.
I'd say that, in the context of private projects, outright closing PRs is rare. Typically, at least some discussion will take place before the submissions are either accepted or rejected.
Having Required Reviewers
Using GitHub's branch protection settings, you can configure several different options for your repos. Among them, you can determine that a given number of approvals is needed for PRs to be approved.
For instance, you might want to enforce a rule that the branch needs at least two approving reviews to merge a pull request.
Accepting After Offering Feedback
On GitHub, there are some different forms of feedback you can provide:
- General comments. These are PR-level comments, grouped in the "conversation" tab of the PR page.
- File comments. You can add individual comments to any changed lines of any altered file.
- Review. This is an "official" review, which can contain one or more comments.
As the following image shows, when giving feedback on lines, you can choose to add a single comment or start a review:
When you decide to submit a review, you must provide a comment with one of the following three statuses:
- Comment. This is a comment that doesn't explicitly approve or reject the changes.
- Approve. This feedback closes and accepts the PR.
- Request changes. This type of feedback keeps the PR open while it's not addressed.
To act upon a piece of feedback, the person submitting the code will typically:
- Go back to their local repository
- Perform the necessary changes
- Commit and push those changes
GitHub automatically picks up the changes and updates the PR page.
This style of approving merges typically leads to higher-quality code. Setting thresholds for the number of comments per PR would have similar code quality benefits to setting a rule that a branch needs two approvals to merge a PR.
With LinearB, you can set customized thresholds, and WorkerB will warn your team when a PR with a basic review is about to be merged. Then you can measure your team's review depth over time.
We've found that as review depth increases, your code quality increase, cycle time improves and unplanned interruptions caused by code churn are reduced. All of this allows your devs to spend more time on new features.
Testing the Branch Locally
This is probably the most labor-intensive style of approving PRs. Sometimes the change is so complex or critical that the reviewer feels the need to pull the branch to merge it and test it locally. There might be the need to solve merge or logical conflicts, fix broken tests, or perform other types of corrections.
Personally, I don't like this style. I think the testing and code review ought to be two distinct activities. If the reviewer feels the need to do this intensive form of review, it's a troubling sign.
Why Are Certain Approval Processes Better?
The pull request process is at the core of the software development lifecycle. However, it can derail into anti-patterns if you don't give it the care it deserves.
In this post, I've walked you through some of the styles of review you can adopt when approving pull requests. As you've seen, most of them represent some kind of anti-pattern.
Our research shows that the perfect PR is the one that's small, focused, generates some amount of healthy discussion, and doesn't take too long to be reviewed and merged. If you've discovered that your cycle time is too high and the culprit is your pull requests process, or specifically your PR review time, be wary of how you encourage your team to improve these metrics. It's not merging without review, and it's not superficial, less thoughtful review comments.
We're on a mission to help teams improve their PR process and code quality simultaneously. Interested? Book a demo today! Or check out this presentation from our CEO, Ori Keren, at The DevOps Conference on why we believe PR Size is the most important indicator of development pipeline health.