Pull Requests Make Code Worse
Introduction#
Pull requests are the most common form of code review in the software engineering industry. However, I do not believe that pull requests actually help teams with private repositories to build better software. Let’s walk through what pull requests are, what they do well, and where they fail.
Origins of Pull Requests#
Modern pull requests were first introduced in 2008. Chris Wanstrath posted about it on the github blog the next day. They were originally designed for public repositories, based on the idea that any open source code had two groups of people involved. There were trusted contributors and the general public (untrusted). The trusted contributors needed a way to moderate content submitted to their public repositories by the general public. The pull request allowed the trusted contributors to review the code, interact with the general contributors (in the form of comments), and then approve or reject the proposed change. The popularity of pull requests today is a testament to how effective the solution became.
Internal Team Code Review Adoption#
It didn’t take long for development teams to adopt pull requests as a code review solution. The UI features of the pull request makes it easy to understand what has changed in the code. The approval process can enforce the need for another engineer to review the code as part of the development process. As of 2023, it seems almost universal that a team’s code review process uses pull requests.
The Implicit Distrust of Pull Requests#
Pull requests were originally a tool for trusted contributors of a public repository to control the flow of code from the untrusted. However, on a development team building in a private repository, who are the trusted? Perhaps the harder hitting question is, who are the untrusted? If a team has people in the untrusted category, then there are deeper problems within the team. Pull requests are not solving them.
This means that healthy internal teams working on private repositories don’t need pull requests for their originally intended purpose. They would only be used then for their utility to effectively facilitate a code review. So, the question now becomes, how effective is a pull request at facilitating a code review?
Are Pull Requests Effective Code Reviews#
In order to assess a pull request’s effectiveness, we need some meaningful metrics to measure it by. I believe that an ideal code review process has three characteristics:
- Quick Feedback
- Improved Code Quality
- Minimal Effort Level
Let’s consider how using the pull request as our code review mechanism compares against these metrics.
Quick Feedback#
A pull request is asynchronous by design. What happens to the author of the code and the reviewers once a pull request has been created? The reviewers are already working on their own tasks. The distraction of processing a pull request is significant. So reviewers tend to wait until a natural break occurs in their work before processing the pull request. This may take a considerable amount of time. Many companies suggest that a 4-hour response time on a pull request is acceptable.
During this 4-hour time period, what is the author of the code doing? They hopefully aren’t just waiting. They are likely onto the next task, mentally off-loading all of the contextual information of the details of their pull request. When a reviewer finally provides feedback, the author now needs to switch context back from their new task to their pull request. This is sometimes repeated via several rounds of pull-request communication - each round taking up to 4 hours to complete. This process does not meet my definition of quick feedback.
Improved Code Quality#
Reviewers are required to read the submitted pull-request code and to assess if any of it should be refactored. However, in order to make this assessment properly, they must understand what the original problem was. More than that, they should know about decisions made in meetings and conversations of which they may not have been a part. All of these factors form the context of how the code they are reviewing came to be. If a reviewer lacks any of that information, it will reduce their ability to give proper insight in improving the code.
Let’s say a reviewer does find a mistake in the code. If their feedback addresses something fundamental, the author may have to spend hours or even days on refactoring. Sometimes the cost of redoing the work is so high that the author and reviewer agree that they can’t currently afford the time to fix it. They simply leave the poor code ‘as is.’ This happens because the pull request occurs at the conclusion of development. The feedback was too late to catch the poor code, and it gets into the main branch anyway. Code quality suffers instead of improving.
Minimal Effort Level#
Pull requests take effort. The need for a pull request implies that you are in a feature branch. You’ve had to invest time into keeping this feature branch up-to-date with the main branch for however long it has lived. You may also have had to resolve merge conflicts with the feature branch. After all of that, the pull request requires attention from you while others are reviewing it, pulling your concentration away from work that you have moved onto. The effort level continues to ratchet up as the reviewers struggle to grasp the context of changes in code they may be seeing for the first time. This steals time and effort from their own tasks. The same flaws that thwart quick feedback when using pull requests for code review also maximize effort output.
Pair Programming: A Better Code Review Option#
If pull requests are not an effective pull request then what is? Pair Programming is the best code review process around. You get immediate feedback from your partner as you are coding together. This gives us the quick feedback we were looking for. The review is happening while you are solving the problem with your partner. This gives us minimal effort. Finally, Code produced from pair programming has shown to yield less bugs and higher code quality then 2 developers working independently. This give us our third metric.
Because the code review is already taken care of, this removes the need for the pull requests. This means that we no longer have a need for feature branches. Allowing the team to have a trunk based workflow. This further reduces the effort of managing feature branches.
Conclusion#
Pull requests are an excellent tool for public repositories. That does not make them the silver bullet for all code reviews. Each team should take what they read above and assess for themselves what value their pull requests are giving them and if there is a better way to achieve their goals. For me and my teams, pair programming makes us faster, more reliable and the teams are happier.
1. Quick Feedback#
Code quality depends on tight feedback loops. When you make a mistake, finding out immediately means you can fix it while the context is fresh. Finding out days later means relearning context and rebuilding focus.
Pull requests create delayed feedback:
- Code is written
- PR is opened
- Reviewer gets notified (maybe)
- Reviewer finds time to review (next day at best)
- Reviewer reads the code and writes comments
- Author gets notified and context-switches back
- Author makes changes and responds
- Cycle repeats for multiple rounds
This process can take days. By the time feedback arrives, the author is on other work and has lost context. Reintegrating feedback means relearning why the decision was made.
Pair programming provides feedback in seconds: “Wait, what about…?” Discussions are completed and decisions are made before code is committed.
2. Code Quality#
Pull request review is optimized for visibility and approval, not quality. Reviewers apply surface-level critique:
- Did the tests pass?
- Does the code match the style guide?
- Are there obvious bugs?
Deeper questions aren’t asked:
- Is this the best design?
- Does this integrate well with adjacent systems?
- Can this be simplified?
- What about edge cases?
These questions require deep thinking, which reviewers skip because they’re context-switching from other work. The review takes 10 minutes instead of 30 because attention is divided.
Pair programming creates space for this deeper thinking because both parties are focused on the problem together. Design trade-offs are discussed before implementation rather than critiqued after.
3. Effort Required#
Effort is measured not by how long review takes, but by total effort across developer and reviewer.
Pull requests create effort from:
- Author describing the change (PR description, comments explaining decisions)
- Reviewer loading context (reading PR, understanding problem, reviewing code)
- Communication overhead (multiple rounds of comments, clarifications)
- Author relearning context for each round of feedback
Total effort is high because each person involves multiple context-switches.
Pairing is effort-intensive while it’s happening (two people focused on one problem) but creates no additional communication overhead. The moment pairing ends, knowledge is shared and decisions are settled.
Why Pull Requests Feel Productive#
Pull requests feel productive because of visibility. You can see what everyone is working on, when reviews are blocked, when PRs are approved. Managers like this.
But visibility isn’t productivity. Three developers waiting for code review approval looks productive (you can see the work), but it’s actually idle capacity.
The Secondary Cost: Communication Friction#
Pull request reviews often include critical feedback. The developer reading that feedback has a choice:
- Accept the criticism and implement the suggested change
- Defend their approach and explain why the suggestion doesn’t apply
When this happens in a PR, written explanations create friction. The reviewer feels dismissed if the author disagrees. The author feels attacked if the criticism seems harsh. Social dynamics create defensive communication.
Pair programming has the same critical feedback, but it’s immediate and conversational. A question like “Why use a factory here?” is just conversation in pairing. In a PR, the same question feels like criticism.
What Gets Lost#
Design Discussion#
When you pair, you discuss designs before they’re coded. “Should we use a strategy pattern or inheritance?” gets debated at the whiteboard. By the time implementation starts, design is settled.
Pull request review debates design after implementation. The author has invested in a direction; suggesting alternatives feels like rejecting their work. So alternatives don’t get suggested. Bad designs make it through.
Knowledge Transfer#
When two people pair, both understand the resulting code. If a new developer joins or someone gets sick, two people can explain the code.
With pull requests, the author understands the code, the reviewer has a passing acquaintance with it. Knowledge lives with one person.
Iteration Speed#
Because feedback is delayed, fixing a problem means multiple back-and-forth rounds. What could be resolved in seconds of pairing takes hours across email.
The Business Consequence#
The business consequence is clear in the three metrics:
- Quick feedback: Pull requests are slow
- Code quality: Pull requests produce surface-level review
- Effort: Pull requests require more total effort
Most teams accept this because pull requests feel professional and provide visibility. But the trade-off is real.
When Pull Requests Work#
Pull requests aren’t always bad. They work well for:
- Open source where you need integration decisions from maintainers
- Distributed teams with large time zone differences
- Large organizations where visibility into change is mandatory
- Code that needs formal approval for compliance reasons
These are legitimate constraints. But many teams use pull requests out of habit, not because the constraints require it.
Alternatives#
Trunk-Based Development#
Merge directly to main branch after pairing or simple review. This requires strong testing practices and CI/CD, but it eliminates PR process overhead.
Pair Programming#
Real-time collaboration provides feedback and knowledge transfer that async review can’t.
Code Review Without Pull Requests#
Some teams use synchronous code review: peer over shoulder before commit, or scheduled review sessions. This provides feedback faster than PR review.
Hybrid Approach#
Pair on high-risk changes, use simple PR review for straightforward changes. This balances overhead with safety.
The Argument in Three Metrics#
Pull request code review feels safer than pairing or trunk-based development, but the evidence doesn’t support this:
| Metric | Pull Requests | Pairing | Trunk + Simple Review |
|---|---|---|---|
| Quick Feedback | Hours/Days | Seconds | Minutes |
| Code Quality | Surface-level | Deep discussion | Risk-based focus |
| Total Effort | High | High/moment | Low |
Pull requests don’t win on any of the metrics that drive code quality. They win on visibility and process formality.
Conclusion#
Pull requests are a tool, not a best practice. They solve specific problems (open source integration, compliance visibility) but create new problems (delayed feedback, communication friction) that teams accept as unavoidable.
They’re not unavoidable. Many high-performing teams use pairing for complex work and direct merge with strong CI/CD for simple changes. They get faster feedback, better knowledge transfer, and code that’s easier to maintain.
If your team is stuck in slow PR cycles, consider experimenting with pairing or trunk-based development. You might find that the process feels less formal but produces better outcomes.
The goal isn’t process; it’s quality and velocity. Pull requests are one mechanism; don’t mistake the mechanism for the goal.