My onboarding peer-mentor at Trello described a good pull request as telling a story. In practice this meant that you would edit and order the commits after you were done so that the reviewer could go commit-by-commit and understand the change you made in steps.
So, for example, let’s say you were working on a feature. In your second commit, you introduce a bug, but then in your fifth commit, you find and fix that bug. Instead of just PR’ing that, you would edit the commits so that the bug was never introduced.
This is analogous to sending a document with superfluous text deleted, not crossed out. If you don’t edit the commits, you will waste the reviewers time because they might see the error in the second commit, make a comment, and then have to go back and amend their comment in the fifth. If you did this a lot, they might not even finish reviews before rejecting them (which is what I suggest you do to PRs with obvious problems).
I like the story frame, but I have started to think of a PR as more of an argument of its own correctness. I am trying to teach the reviewer the details of the problem and convince them through evidence that the new code is correct.
In a PR, I might start by introducing a unit test into the area you intend to change. Then, to make things clearer, I might commit a small refactoring (that isolates the change). It’s now possible to add more tests and possibly a failing one that shows what my intended fix will address. My small code clean-up commits are in service of that argument. Without them, it’s hard to tell if my fix won’t break something else. With them, the fix feels like a natural and inevitable conclusion.
Like a philosophical argument, I will anticipate and address the cases the reviewer might think of. But it’s not enough to handle a case in the code, your whole PR needs to make it clear that you anticipated and addressed it (with tests, comments, screenshots or any other evidence you can think of).
But the most important reviewer to convince is myself, of course, and doing the work to write the argument gives me confidence that my code is correct.