How to write the perfect pull request
As maintainer of several open-source packages, I'm frequently handling requests from contributors to incorporate changes into the codebase. This article outlines the steps to making those contributions eminently acceptable. This article assumes the reader is familiar with Distributed Version Control Systems such as Git or Mercurial and the pull request process in a service such as GitHub or Bitbucket. Reference GitHub's Using Pull Requests or Atlassian's Create a Pull Request for background on those topics.
1. Locate or file a ticket describing the issue. First search the history to see if the issue has already been reported. If you cannot find an existing issue, then create one. It's okay if you create a duplicate - the project maintainers can link the tickets, but doing a little research up front will save everyone time.
2. In the ticket, describe the problem as you faced it. Include how you encountered the issue and in what environment. Try to include enough detail to allow another person to replicate your findings. If possible, distill the problem down to a minimal program that triggers the failure. If a minimal program cannot be created, try to characterize the issue such that another reader can understand the failure. Include error messages, stack traces, and other detail in their entirety.
3. Research the project-specific contribution policy. Many projects explicitly advertise a contribution policy as a CONTRIBUTING file in the root of the repository, or in other documents such as README. If you're new to a project or unfamiliar with its nuances, doing a little research up front could save you some time and trouble.
4. Start small. Unless your name already carries widespread reputation within the community, your first contribution is likely indistinguishable from that of any other contributor. If you start with small changes, you can establish a rapport with the project and avoid wasting precious time and effort.
5. Prepare your fork of the code for contribution. You haven't committed any code to your fork of the code yet, but you're about to do so. Because you've already been troubleshooting the problem, you may have already even drafted a fix. Set aside those changes (using your DVCS tools) and work from a clean copy of the code as found with the project.
6. Avoid making stylistic changes. This step, while a non-operation, is important. Try to avoid applying your preferred style (regardless how authoritative that style may seem) to the code. Adopt the style of the project as much as possible. In rare cases, your editor will automatically make some stylistic changes when saving (such as removing trailing whitespace from lines). If that is the case, either disable that feature in your editor or commit the stylistic changes separately and prior to any substantive changes. Doing so will allow the reviewers to accept or reject the stylistic changes separately and not be distracted by them.
7. Write a test to capture the desired behavior. If possible, write a test in the test suite of the target project that captures the failure or intended new behavior. Often, the distilled program from above can be re-purposed to accomplish this goal. Commit this change prior to any substantive changes. This newly-created test will now fail at that revision, proving that the issue is fixed in subsequent commits.
8. Implement your suggested fix. Try to minimally adjust the arrangement of the code. If the change is more than a few lines fix, consider making several incremental commits working toward the ultimate solution. For example, if the fix includes extracting a method from a larger method, consider doing that extraction as a separate commit. Then, each commit describes the contributor's thought process working toward the solution. Most code review tools will allow the reviewer to see the commits in aggregate or separately, so more incremental contributions give the reviewer better visibility without compromising the big picture.
9. When committing your changes in (5) and (6), consider using commit messages that reference the ticket number. Both GitHub and Bitbucket can automatically link commits to a ticket if the commit message properly references the ticket. Follow the example of prior commits in that project to be consistent. Creating these references means you'll get the best attribution for your contribution and means less work for the reviewer. Use casual references for incremental commits and then use phrases like "Fixes #NNN" for the commit that actually fixes the issue.
10. Create the pull request referencing the ticket. The pull request should include a description referencing the ticket it fixes and any other information that will help the reviewer understand your thought process. Highlight any areas that you think might be controversial or incomplete and give additional justification for those areas here, along with any factors that mitigate those concerns.
If you do all of these things while creating your pull request, a reviewer will find it hard not to accept your contribution. Not all of these steps are necessary to accept a contribution. Sometimes a test suite isn't available in a project, or the contribution doesn't justify a test. Perhaps you only have enough experience to write the ticket or the test, but not implement the solution. Use your best judgment and contribute what you can. In the open-source world, good project managers understand that we have limited resources and capabilities and will make the best of any contribution.
Finally, don't be offended if your pull request is rejected. Different projects and their maintainers have different standards for accepting contribution. Hopefully, they will provide constructive criticism that will help you improve your contribution. If not, ask for specific feedback on how you can improve your contributions for the future.
1. Locate or file a ticket describing the issue. First search the history to see if the issue has already been reported. If you cannot find an existing issue, then create one. It's okay if you create a duplicate - the project maintainers can link the tickets, but doing a little research up front will save everyone time.
2. In the ticket, describe the problem as you faced it. Include how you encountered the issue and in what environment. Try to include enough detail to allow another person to replicate your findings. If possible, distill the problem down to a minimal program that triggers the failure. If a minimal program cannot be created, try to characterize the issue such that another reader can understand the failure. Include error messages, stack traces, and other detail in their entirety.
3. Research the project-specific contribution policy. Many projects explicitly advertise a contribution policy as a CONTRIBUTING file in the root of the repository, or in other documents such as README. If you're new to a project or unfamiliar with its nuances, doing a little research up front could save you some time and trouble.
4. Start small. Unless your name already carries widespread reputation within the community, your first contribution is likely indistinguishable from that of any other contributor. If you start with small changes, you can establish a rapport with the project and avoid wasting precious time and effort.
5. Prepare your fork of the code for contribution. You haven't committed any code to your fork of the code yet, but you're about to do so. Because you've already been troubleshooting the problem, you may have already even drafted a fix. Set aside those changes (using your DVCS tools) and work from a clean copy of the code as found with the project.
6. Avoid making stylistic changes. This step, while a non-operation, is important. Try to avoid applying your preferred style (regardless how authoritative that style may seem) to the code. Adopt the style of the project as much as possible. In rare cases, your editor will automatically make some stylistic changes when saving (such as removing trailing whitespace from lines). If that is the case, either disable that feature in your editor or commit the stylistic changes separately and prior to any substantive changes. Doing so will allow the reviewers to accept or reject the stylistic changes separately and not be distracted by them.
7. Write a test to capture the desired behavior. If possible, write a test in the test suite of the target project that captures the failure or intended new behavior. Often, the distilled program from above can be re-purposed to accomplish this goal. Commit this change prior to any substantive changes. This newly-created test will now fail at that revision, proving that the issue is fixed in subsequent commits.
8. Implement your suggested fix. Try to minimally adjust the arrangement of the code. If the change is more than a few lines fix, consider making several incremental commits working toward the ultimate solution. For example, if the fix includes extracting a method from a larger method, consider doing that extraction as a separate commit. Then, each commit describes the contributor's thought process working toward the solution. Most code review tools will allow the reviewer to see the commits in aggregate or separately, so more incremental contributions give the reviewer better visibility without compromising the big picture.
9. When committing your changes in (5) and (6), consider using commit messages that reference the ticket number. Both GitHub and Bitbucket can automatically link commits to a ticket if the commit message properly references the ticket. Follow the example of prior commits in that project to be consistent. Creating these references means you'll get the best attribution for your contribution and means less work for the reviewer. Use casual references for incremental commits and then use phrases like "Fixes #NNN" for the commit that actually fixes the issue.
10. Create the pull request referencing the ticket. The pull request should include a description referencing the ticket it fixes and any other information that will help the reviewer understand your thought process. Highlight any areas that you think might be controversial or incomplete and give additional justification for those areas here, along with any factors that mitigate those concerns.
If you do all of these things while creating your pull request, a reviewer will find it hard not to accept your contribution. Not all of these steps are necessary to accept a contribution. Sometimes a test suite isn't available in a project, or the contribution doesn't justify a test. Perhaps you only have enough experience to write the ticket or the test, but not implement the solution. Use your best judgment and contribute what you can. In the open-source world, good project managers understand that we have limited resources and capabilities and will make the best of any contribution.
Finally, don't be offended if your pull request is rejected. Different projects and their maintainers have different standards for accepting contribution. Hopefully, they will provide constructive criticism that will help you improve your contribution. If not, ask for specific feedback on how you can improve your contributions for the future.
Written on April 18, 2014