Mastering pull requests. How to prepare and review.

Intro
In my opinion, the topic of reviewing and creating pull requests (PR) doesn’t receive the attention it deserves. We weren’t taught this in university, and in many companies, it often seems like something self-evident. Some companies have their handbooks, but discussions rarely extend beyond office walls. Everyone understands it’s important, but not many people discuss why or how to do it effectively. I would have loved to read something like this when I was a junior developer.
This article is great for beginners, but more experienced developers may also find something useful.
I originally started writing this note for myself as a reminder of what to do and what to avoid when creating and reviewing pull requests. Then, I shared it with my team, and finally, I decided it was worth spending a bit more time to write a full article, perhaps it would be helpful to others.
Interestingly, both soft and hard skills come into play when creating and reviewing pull requests, highlighting the importance of improving both. It is, in fact, a cross-disciplinary process that requires a responsible approach.
Benefits
Some might argue that nowadays, everything can be automated. Before creating a PR, you can run static analysis, unit tests, automated tests, and even have an AI assistant reviewing the PR. Sure, this is possible, and in some cases, it may even be justified. So why bother?
For me, I see the following reasons, which I divide into soft and hard skills:
Soft skills:
Learning how to give constructive feedback.
Learning how to accept feedback and different perspectives.
Learning how to resolve conflicts.
Building horizontal relationships within the team from a psychological perspective.
Hard skills:
Reducing the risk of errors in the project.
Maintaining code consistency and readability.
Learning (both personally and as a team).
Improving shared understanding of the project.
Additionally, this benefits the business. Time spent on reviews saves time that might later be spent debugging, finding regressions, and fixing production errors. As we all know, the later a bug is found, the more expensive it becomes.
Soft skills
While I want to focus on technical aspects in this article, let’s briefly touch on other important factors.
Software development is, most often, a team effort. Building a strong team directly impacts the success of a product, and forming horizontal connections is an integral part of that process. How does code review fit into this?
Consider these questions:
How do you comment in an acceptable tone? And what tone is acceptable in a given situation?
How do you provide feedback that fosters learning?
How do you consider different cultures and genders when giving feedback?
How do you avoid being or appearing overly critical during a review?
How should you react if you feel that a comment is toxic?
How do you resolve conflicts of interest?
These are things you learn with experience. Try reading materials on team psychology, conflict management, and working in multicultural teams. Maybe someone can share good resources in the comments?
It’s essential to understand that we are all human, and we all make mistakes — and that’s okay! For example, a review on a day when you woke up on the wrong side of the bed will likely be very different.
That’s why I stick to these core principles:
Set your ego aside and show empathy.
Focus on the product.
Be as objective as possible.
If you’re tired or too emotional, take a break and continue later.
Choose a convenient time for the review.
If needed, split the review into multiple rounds.
How to criticise properly? (Advice from psychologist E. Sigitova)
Signs of good criticism:
It comes from a “side-by-side” perspective with the author
It is respectful, acknowledging the effort and/or result of another person’s work.
It motivates to change.
It sparks insights about improvements.
It doesn't affect self-esteem. In an ideal world, it doesn’t even touch it, as there's no need to hurt someone’s ego to make a point.
How to criticise effectively:
Start with something positive and give sincere praise. Find something genuinely good about the work and explain why it matters.
Set aside negative emotions so as not to trigger defensive reactions. Don’t suppress feelings completely, just put them on hold.
Use warm reinforcement, positive body language, and a smile, if your criticism is verbal. If written, maintain a friendly tone within the limits of text.
When addressing issues, use “I” statements instead of indicative sentences. For example, say, “I don’t agree” instead of “You’re wrong.” This way you will avoid an accusatory tone.
Be as specific as possible about what needs to be changed or improved. The more details, the better.
Focus on behaviour, product, or work output rather than the person.
Put yourself in the position of the recipient. Consider the effort they’ve put in and any constraints they’ve faced.
Don’t overload with too many points at once. Stick to two or three key points initially, the rest can be added if there is a dialogue.
Offer ready solutions and advices where possible. Even a rough idea is better than nothing.
End on a positive note and, again, praise sincerely. The better you praise at the beginning and at the end, the better the middle part will be perceived.
I believe this topic deserves a separate article, so let’s get back to the technical aspects.
Practices for authors
Commits
"A journey of a thousand miles begins with a single step."
Similarly, everything starts with a commit message. I like using the "Conventional Commits" approach to write structured and meaningful commit messages. This makes it easier to navigate through commit history and find needed information.
Atomicity
Make life easier for both yourself and reviewers by ensuring that changes are limited to the scope of the task. Avoid mixing bug fixes with feature development.
This requires effort at the task decomposition stage. If a task is too large, consider creating a separate branch and splitting it into multiple smaller PRs. If splitting isn't an option, leave instructions in the PR description to help reviewers check the changes efficiently.
Remember, you write code once, but others will read, use, and modify it hundreds or thousands of times. Focus on writing clear, maintainable code within the scope of a single task or refactoring effort.
Before creating a PR
If possible, run static analysis and tests beforehand to avoid burdening reviewers with trivial issues.
After finishing a task, take a break, then review your own changes as if you were the reviewer. You may spot areas for improvement or simplification. This demonstrates professionalism and respect for your colleagues' time.
PR description
A well-written PR description helps reviewers get up to speed. Links to the relevant task, design documents, and supporting information can be valuable. If UI changes are involved, a short video may be helpful.
Describing complex code areas can also be useful, but if you find yourself doing this often, reconsider the code’s clarity, perhaps it should be refactored for better readability. Even just thinking about the description of the PR can be valuable and provide an opportunity to look at the proposed solution from a different perspective.
Sometimes it may be useful to attach test steps so that the reviewer can run and check the project themself.
Your code is no longer your's
Once code enters the repository, it no longer belongs to you. It belongs to the company, and the whole team is equally responsible for it. Don’t take feedback personally, and understand that your code may be changed over time.
This mindset would have saved me a lot of nerves early in my career. Without code reviews, developers often drift toward their own styles and architectural preferences. Reviews help broaden perspectives and find the best solutions through compromise.
Resolving Comments
Code review isn’t just about fixing mistakes, it’s a collaboration process that ensures code quality and team alignment.
Here are some best practices for handling comments on your code:
Respond to comments as quickly as possible.
Discuss with your team who is responsible for resolving comments — the author or the reviewer.
If you disagree with a comment, communicate your reasoning instead of simply dismissing it.
In case of conflicting opinions, back up your position with data and references, or suggest alternative solutions that work for both sides.
If a comment cannot be resolved within the scope of the current PR, create a separate task and address it later.
Consider all comments, even minor ones. Code review is the first test of your code’s clarity and maintainability. Pay special attention to comments about readability and comprehension. Your code will be read many times, so making it understandable is crucial.
Useful Metrics
Both authors and managers can benefit from tracking key metrics such as:
MTM (Mean Time to Merge): Measures the average time it takes for a pull request to be merged.
TTM (Time to Market): Tracks how long it takes for a feature or fix to reach the end user.
These metrics help identify bottlenecks in the code review process and enable the team to focus on delivering value to the users more efficiently.
MTM
A "good" Mean Time to Merge (MTM) varies depending on the team's size, project complexity, and workflow. However, some general benchmarks can help:
Small teams or startups: 1-2 days
Mid-sized teams: 2-4 days
Large teams or enterprise projects: 4-7 days
If your MTM is consistently over a week, it may indicate inefficiencies, such as slow reviews, unclear requirements, or overcomplicated PRs. And vice versa, if PRs are merged too quickly (within hours), it could mean reviews aren’t thorough enough.
Ways to Improve MTM:
Keep PRs small & focused
Set expectations on review timelines — define team norms (e.g., "PRs should be reviewed within 24 hours").
Automate checks before review — use CI/CD pipelines to catch basic issues before a human review.
Encourage early feedback — draft PRs or "work-in-progress" PRs allow reviewers to spot issues before the final submission.
Improve team availability for reviews — a dedicated "review time" in daily workflows helps avoid long delays.
TTM
Reflects how quickly a feature or fix reaches the end user. A low TTM is great for business, but cutting corners on code quality leads to long-term technical debt. Here’s how to balance speed and quality:
Strategies for optimising TTM:
Testing — catch issues earlier in the development cycle through unit tests, integration tests, and pre-merge automated checks.
Feature flags — deploy code behind feature flags, allowing for gradual rollouts and quick rollbacks if needed.
Parallel workflows — Encourage parallelisation where possible (e.g., frontend and backend development happening simultaneously).
Ensure smooth collaboration between developers, testers, and product managers to prevent bottlenecks.
Invest in developer tooling — faster builds, better CI/CD pipelines, and local development environments improve efficiency.
Practices for reviewers
Context
Sometimes, reviewing a pull request requires understanding the associated task and design to assess whether the implementation is correct. Don’t overlook the task description, especially if the PR itself lacks sufficient details.
If necessary, you can ask the author to record a short video demonstrating the implementation (particularly for UI-related changes). However, the ideal approach is to check out the branch and run the project yourself.
Comments
Try to complete your review in one sitting. This makes it easier for the author to address all comments at once and notify you when changes are ready, rather than waiting for multiple rounds of feedback. Otherwise, communicate clearly to the author that the review will take more time.
When leaving comments:
Be polite and professional. If you provide criticism, do it constructively (see the earlier section on proper criticism).
Don’t forget to highlight the positive aspects of the implementation.
Ask questions if something is unclear. It’s better to clarify than to assume the code is incorrect.
Avoid thinking “How would I write this?” If you want to share an alternative approach, mark it as a [Nitpick]. A reviewer’s role is not to rewrite the code but to ensure readability, maintainability, and correctness. Suggest alternatives only when they significantly improve these aspects.
Be mindful of whether you're offering necessary feedback or simply nitpicking.
Be as specific as possible. If you suggest a change, provide a concrete solution.
Share educational resources when relevant, especially if a mistake indicates a knowledge gap.
Offer feedback in an objective and open manner. Code review is the first test of how understandable and maintainable the code is. If something is unclear, it’s worth discussing with the author.
Keep in mind that developers often seek simplicity rather than perfection. While simplifications can be beneficial, they may also negatively impact the project in the long run. Strive to find a balance and encourage authors to write high-quality code that aligns with your team's standards. (https://refactoring.guru/ could help)
Remember to mark your comment as [Blocking] (e.g. A security vulnerability; code that breaks existing functionality; performance bottlenecks) or [Non-blocking] (Stylistic preferences; alternative ways to write a function; minor refactoring suggestions)
Reviewer fatigue and how to avoid it
In fast-moving teams, developers often review multiple PRs daily, leading to “reviewer fatigue.” This can reduce the effectiveness of reviews and cause important details to be overlooked. Here’s how to prevent it:
Time management: allocate specific time slots for reviews instead of doing them sporadically throughout the day.
Batch reviews: if possible, group smaller PRs together to handle them efficiently.
Limit review rounds: encourage authors to self-review before submitting a PR to minimise back-and-forth.
Rotate reviewers: if one person is constantly reviewing PRs, distribute the workload across the team.
Use automation tools: let static analysis, linters, and CI pipelines catch trivial mistakes before they reach human reviewers.
Asynchronous code reviews in distributed teams
PR reviews in a team, which is spread across different time zones, can slow development. Here’s how to make async reviews more efficient:
Time zone awareness: agree with your manager and team that you'll review PRs from authors who have overlapping working hours with you.
Detailed PR descriptions: encourage the team to write clear PR descriptions that can reduce unnecessary back-and-forth.
Teammates can open draft PRs early for preliminary feedback.
Summarise discussions: if an in-depth discussion happens on Slack or a call, summarise key points in the PR comments.
Who Should Review?
Different teams have different approaches to code reviews, but here you can see some common patterns:
2 Reviewers = 1 Lead + 1 Developer
3 Reviewers = 1 Lead + 1 Developer + 1 QA
2 Reviewers = 1 Feature/Directory Owner or Expert + 1 Developer
The ideal composition depends on the team structure, codebase complexity, and the criticality of the changes. In some cases, a QA engineer’s involvement can be crucial, especially when changes affect user-facing functionality. Meanwhile, having a domain expert (someone deeply familiar with a specific feature or directory) can help maintain architectural consistency.
Review Process
It’s beneficial when the reviewer and author approach the code from different perspectives. This often leads to valuable discussions about architecture, maintainability, and readability.
If a reviewer cannot check the code promptly, they should notify the author instead of leaving them waiting. This helps the author manage their time better and work on other tasks in the meantime.
If a pull request is too large, try breaking it down into logical sections and reviewing them in parts. It can be helpful to discuss the PR structure with the author to ensure you’re understanding it correctly. A well-organised PR is easier to review and reduces the risk of missing critical issues.
A good reviewer doesn’t just check for syntax errors or coding style violations. They also evaluate the impact on the product as a whole. A great reviewer balances both technical and product perspectives.
Check-list
Quick reference
Criteria | Questions to consider |
Problem solving | Does the code solve the specified task — and only that task? Is the PR description clear? Have you checked that business requirements haven't changed since the task was created. |
Functionality | Does the code behave as intended? Does it improve the user experience or business logic? |
Documentation | Have the changes been documented properly? Are API updates reflected in the documentation? |
Comments | Are comments added where necessary? Are they clear, useful, and concise? |
Code design | Does the implementation align with the team's architectural guidelines? |
Complexity | Could the code be simplified? Will another developer understand and reuse it easily? Isn't there an over-engineering (is the code too complex for the problem it solves)? |
Refactoring | Does the code need refactoring? (e.g., overly large classes or methods) |
Unit tests | Have tests been written? Do they cover key logic and edge cases? |
SDK/API usage | Are SDKs/APIs integrated correctly and following best practices? |
Security | Have potential security risks been considered (e.g., input validation, data handling)? |
Performance | Is there an opportunity to optimise performance without sacrificing readability? Have profiling tools been used? Was it documented? |
Naming | Are variable, method, and class names descriptive and consistent? |
Style | Does the code follow the team’s coding conventions and formatting guidelines? |
Detailed
Implementation
Does the code behave as expected and improve the user story?
Does it follow the project's architecture and design principles (SOLID)?
Does it follow the project's naming, packaging, and style conventions?
Is the code modular, at the correct abstraction level, and free from obsolete sections?
Can the solution be simplified, improved for readability, maintainability, performance, or security?
Can we employ more idiomatic /* paste your language or framework */?
Is there existing code with similar functionality that could be reused?
Are dependencies properly injected (if using DI frameworks)?
Are all happy paths and edge cases tested?
Are the static code analysis metrics acceptable?
Does obfuscated code work properly?
Error handling
Is error handling done the correct way?
Are network issues (slow/no connection) handled correctly?
[Mobile] What happens when the device is rotated (or any other config is changed)?
[Mobile] What happens if required permissions are denied?
What happens when there are edge cases in data?
Are error messages clear, user-friendly, and logged properly?
Are unexpected inputs or external effects handled properly?
Should any logging or debugging information be added or removed?
Tests
Is the new code testable? Is it covered by the unit, UI tests?
Are tests well structured (including correct packaging) and maintainable?
Can modified or added classes be easily mocked?
Should any existing tests be updated or removed?
Do the existing tests reasonably cover the code change?
Are there any test cases, input, or edge cases that should be tested additionally?
Is code coverage sufficient?
Dependencies
Is the new dependency essential? Could its functionality be implemented in-house?
[Mobile] Does it significantly increase app size?
Is the library actively maintained and widely adopted?
Does a version bump introduce breaking changes?
If the project uses any obfuscator, does the obfuscated build work as expected?
Does the dependency have any known vulnerabilities?
UI, UX, Usability, Accessibility [Mobile, Web, Desktop]
Do code changes impact UI, and if so, does the UI match the design specs?
Are the correct UI components and styles used?
How does the UI perform on different screen sizes (small, tablet, foldable)?
Is the proposed solution well-designed from a UX perspective?
Is the proposed solution accessible?
Is the code designed for easy localisation?
Are global formats considered (text direction, Date - Time, currency, etc)?
Performance
Are there any performance issues or inefficiencies?
Are threads/dispatchers used correctly?
Are coroutine scopes used correctly?
[Android] Is baseline profile updated?
[Android] Are unnecessary recompositions, overdraw, or state reads happening?
[Mobile] Are battery drain risks minimised?
Are API requests and responses optimised (caching, reduced calls)?
Will this change negatively impact performance?
Can performance be optimised further without impacting readability?
Is memory management handled effectively?
Are algorithms and data structures optimally selected?
Code readability
Is the code easy to understand for other developers?
Are variable names, methods, classes clear for other developers?
Are functions and classes well-organised and single-purpose?
Which parts were confusing to you and why?
Well-written code doesn't require comments, but:
Is the code well-commented explaining "why" rather than "what"?
Could some comments re removed or changed?
Is there any commented out code? -> Remove it
[Android] Are resource annotations (@StringRes, @DrawableRes, @ColorInt, etc) used correctly?
Can readability be improved by smaller methods and other refactoring techniques? (https://refactoring.guru/refactoring/techniques)
[Mobile] Is the data flow understandable and adheres data -> domain -> ui direction?
Security and data privacy
Does the code introduce any security vulnerabilities?
Does this change user data in a way that might raise privacy concerns?
Are authorisation and authentication properly implemented?
Is user input validated, sanitised, and escaped?
Is sensitive data securely handled, stored?
Does this code reveal any secrets (keys, passwords, usernames, etc)? Is this info displayed securely?
Is retrieved data checked for security issues? (https, certificates)
Are OWASP practices followed?
Outro
A well-structured code review process is not just about finding bugs, it ensures code is maintainable, scalable, secure, and user-friendly.
When reviewing code, consider both the immediate impact and long-term maintainability. Balance functionality, simplicity, and efficiency, while maintaining clear communication with the author.
I'd be more than happy if some teams would reduce technical debt, improve collaboration, and ensure high-quality software delivery by following this checklist.
Interested in Mobile development? Subscribe to my Telegram channel
Appreciate the insights? You can support me here ;)
Subscribe to my newsletter
Read articles from Iamushev Igor directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
