Stop doing pull requests
Now that we’ve managed to convince everyone and their grandma to use pull requests, it’s time to take a step back, see what we have really accomplished and ask: is it really what we want?
I am old enough to have been using Git version control many years before GitHub. GitHub offers a free and convenient way to host your version-controlled projects. This was integral in popularizing Git as the default version control it is today. GitHub is currently the largest code hosting service in the world with over 3 million users.
Typical flow for a development project nowadays runs along these lines: a developer creates new code and makes changes to existing code and then pushes those changes to a new branch of Git repository.
Pull requests let you tell others about these changes on GitHub. Once a pull request is opened, you can discuss and review the potential changes with collaborators and add follow-up commits before your changes are merged into the base branch.
What’s the problem?
“Pull requests were invented to gate-keep access to open source projects.” - Dave Farley
GitHub started as the place to host open source projects. Open source projects are typically owned by a single developer or a small team. At the same time, popular projects can have hundreds or thousands of contributors. Here, a contributor means a developer who has sent the project a change or addition to the codebase. For example, it might be a fix to a problem they have had when using the project. There is no way that the owners of the project know or trust all their contributors. So they needed a way to get these contributions and review and discuss them before accepting or rejecting them. This is completely understandable for a distributed open source project.
What is not understandable is that now most teams that are co-located, internal to a company who know and trust each other, is to find themselves following this process designed for the complete opposite situation.
So what’s the problem with this approach? Instead of spoken interaction, we default to mostly one-way, text-based communication. It is taxing for the reviewer to internalize the context from pull request discussion and possibly a large set of code. It is very painful for a developer to complete coding a thing in half an hour, have it built, tested, linted in a few minutes and then wait hours or days for other team members to finally review the code. I’ve seen multiple team leads turned to pull request review automatons, with no time left for them to do anything else. I’ve been to so many retrospectives where the main complaint is how it might take a full week before people get their PR reviewed.
Band-Aid solutions
As pretty much every team I have worked with has struggled with pull requests and the associated code reviews, I have also seen and suggested lots of small things to improve the situations.
One Artificial Intelligence team I worked with settled for agreeing that different things require different levels of reviewing. Trivial things like new tests, dependency upgrades, documentation changes etc, were fine the merge without a human reviewer. Other things were accepted with a single review and, for example, challenging changes to algorithms were expected to get two reviews before merging.
On other teams, we have tried agreeing on things like “you must do X number of reviews before starting a new task” or “start each day with code reviews before continuing your own work”. We have tried mobbing code reviews at the end of the week for those pull requests that are stuck awaiting review.
I have also had lengthy discussions on what you should and should not focus on in code reviews or how to write better pull request descriptions to make reviews faster or easier.
All of the above helps to improve the pull request based process. But honestly it also feels like rearranging the deck chairs on the Titanic. It’s all just a bit too little.
Intermission
Before I continue, I want to emphasize that automation is not linked to the idea of pull requests. I highly encourage every team to fully automate everything they can in their software process. This means that new code and changes are always automatically linted, formatted, built, tested, packaged and so on. It is common to have these actions be triggered by pull requests, but they can just as well be triggered by push events to a branch. Results are easy to push to a team's Slack channel for example. For the fastest feedback, they can also be run locally on push or commit hooks. So when I say stop using pull requests, this does not in any way encourage giving up automation.
The real issues and solutions
With a pull request-based process, internal teams suffer from two main problems: impaired communication and interruptions to flow.
With impaired communication I mean that written pull request description is never as efficient in getting an idea or solution described as live dialogue between humans. It is much easier to focus on explaining your ideas when the listener is free to ask questions and guide the dialog to what he finds challenging or complex.
Before the time of GitHub and pull requests, I enjoyed working with a team where we would not only do pair programming but when working alone, we would do reviews by showing the changes commit-by-commit to a coworker, explain what was done and why. Just recently in a startup with a distributed team, we avoided traditional pull request reviews and instead did a quick voice call or video meeting and showed the code and the reasoning behind it. With both of these teams, we never had tasks stuck waiting for review and the interruptions to others workflows were very short.
To quote Dr W. Edwards Deming, “Inspection is too late. The quality, good or bad, is already in the product.” The more we can move the collaboration to the beginning of the task rather than when the code is complete, the more we can improve quality, reduce frustration and reduce rework.
With pull requests, there is always the battle between the author wanting feedback as fast as possible and the reviewer wanting his work to be interrupted as little as possible.
There is only one way of working that eliminates all the above mentioned challenges. That way is pair programming. No reviews are needed when you always have someone reviewing your code as you write it. Collaboration happens from the start so no surprise disagreements await at the end. No interruptions are needed as the pair can complete the task on their own.
I’ve seen even huge companies like Thoughtworks require all code to be done with pair programming. To me this feels unnecessary. On the other hand, I would love to see teams use pair programming much more often than I’ve seen it so far. Most people will probably enjoy it more than they expect and the strongest opponents of pair programming have likely never practiced it.
To solve many of the above problems and still not work with pair programming full time, I’ve found it really helpful to pair at the beginning of the task. For example, define a shared API, integration point or class structure together. This will greatly reduce the effort needed for a review or rework later on.
Summary
Most of the programming that happens in the world now is not open source but software development done internally in companies. But most of these internal teams use a heavy process designed for highly distributed teams that do not know or trust each other. As software developers, I feel we must look for better solutions. I believe that by increasing the amount and frequency of live communication, pair or mob programming and collaboration that happens earlier during the development, we can make the formal pull request review process trivial and eventually redundant.
Janne Sinivirta is a hands-on architect, agilist, programming language nerd, kickboxer, mountain biker, proud father of two and Principal DevOps Consultant at Polar Squad.