π Pull Requests and Code Reviews
A pull request is more than a merge button. It's a proposal, a discussion thread, a quality checkpoint, and a permanent record of why a change was made β all in one. This lesson shows you how to author pull requests that are easy to review and how to review others' code thoughtfully and kindly.
π― Learning Objectives
By the end of this lesson, you will be able to:
- Describe the anatomy and lifecycle of a pull request from draft to merge
- Author small, focused PRs with descriptions that explain the what, why, and how
- Conduct a structured code review covering correctness, design, security, and tests
- Give constructive feedback that improves the code without bruising the author
- Wire up automated checks so routine issues are caught before a human looks
Estimated Time: 35β45 minutes β’ Difficulty: BeginnerβIntermediate
Hands-on: Open a real pull request and perform a structured review of a peer's change.
In This Lesson
Why Pull Requests Exist
A pull request (PR) asks a project to pull the changes on your branch into another branch β usually main. But its real value is everything that happens before the merge: teammates read the diff, ask questions, catch bugs, and leave a paper trail future developers can read.
π‘ A useful analogy: A pull request is like submitting an essay for peer review before publication. You don't just slide your draft onto the printing press β you circulate it, colleagues mark it up, you revise, and only the approved version goes out. The comments left behind also explain the reasoning long after the ink dries.
Pull requests give a team four things at once: quality control (a second set of eyes), knowledge sharing (reviewers learn the codebase), mentorship (juniors learn from seniors), and documentation (the discussion is preserved forever).
Anatomy & Lifecycle
Every PR compares a source branch (your changes, also called head) against a target branch (where you want them to land, also called base). Around that core it gathers a title, a description, the list of commits, the changed files, review comments, status checks, labels, and linked issues.
The lifecycle
A PR moves through a predictable set of states. Understanding them tells you exactly what needs to happen next:
- Creation β opened as a draft (work in progress) or ready for review.
- Review β reviewers read the code and leave feedback.
- Revision β the author addresses feedback with new commits.
- Approval β reviewers approve once satisfied.
- Merge β the change lands in the target branch.
- Cleanup β the source branch is deleted.
π Draft pull requests
A draft PR is explicitly marked "not ready to merge." Use it to get early feedback on your direction, to kick off CI checks while you keep working, and to reserve the PR number β all without the risk of an accidental merge.
Reviewing Code Well
A good review is systematic, not a random scroll through the diff. Work from the outside in: understand the context, grasp the big picture, then examine details.
- Read the description and any linked issue β know what the change is trying to do.
- Scan the overall shape β does the architecture fit the codebase before you nitpick lines?
- Read line by line for correctness, edge cases, and clarity.
- Check the tests β do they cover the change, including error paths?
- Confirm the docs reflect any user-facing change.
- Summarize your findings and choose an outcome.
What to look for
Prioritize in this order β correctness and security matter far more than a misplaced space:
| Priority | Question to ask |
|---|---|
| 1. Correctness | Does it do what it claims, including edge cases? |
| 2. Security | Is user input validated? Any injected secrets or new vulnerabilities? |
| 3. Design | Is it well-structured and maintainable? Any needless complexity? |
| 4. Tests | Is there adequate, meaningful coverage? |
| 5. Readability | Are names clear? Would a newcomer follow it? |
| 6. Style | Does it match project conventions? (Best left to linters.) |
GitHub's review outcomes
When you click Review changes, you choose one of three verdicts:
- Comment β general feedback, no explicit verdict.
- Approve β you're happy for this to merge.
- Request changes β something must be fixed before merging.
Use the suggestion feature (the pencil icon on a line comment) to propose exact replacement code; the author can accept it with one click.
Constructive Feedback
The tone of your feedback decides whether it improves the code or starts a fight. The golden rule: review the code, not the coder. Assume good intent, be specific, and explain the why behind each suggestion.
| Instead of⦠| Try⦠|
|---|---|
| "This code is a mess." | "This might read more clearly if we extracted it into a helper β what do you think?" |
| "Why would you do it this way?" | "I'm curious about the reasoning behind this approach." |
| "You forgot the empty case." | "It looks like we'd hit an error when the input is empty β should we guard against that?" |
| "This is wrong." | "I think there may be a bug here because the counter never resets." |
A well-structured review balances praise with suggestions and separates must-fix from nice-to-have:
### Strengths
- Token generation is clean and well tested.
- Nice job isolating the auth logic in its own middleware.
### Suggestions (blocking)
- The login route should rate-limit attempts to prevent brute forcing.
### Nits (non-blocking)
- Consider renaming `d` to `expiresAt` for clarity.
### Question
- Why the 24-hour token lifetime? Would a shorter one hurt UX much?
π‘ Mentor through reviews
A review is a teaching moment. When you suggest a pattern β say, replacing nested ifs with early returns β show a tiny before/after and link to a reference. The author learns the principle, not just the one-off fix.
// Instead of deeply nested conditionals:
function processUser(user) {
if (user) {
if (user.isActive) {
if (user.hasPermission) {
return true;
}
}
}
return false;
}
// Prefer early returns β the "happy path" reads top to bottom:
function processUser(user) {
if (!user) return false;
if (!user.isActive) return false;
if (!user.hasPermission) return false;
return true;
}
Automating the Boring Parts
Humans should spend their attention on design and logic, not on catching a missing semicolon. Let automated status checks handle the mechanical review so people can focus on judgment.
Run checks on every PR
A GitHub Actions workflow can lint, test, and scan every pull request automatically:
name: Code Quality Checks
on:
pull_request:
branches: [ main ]
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm test
Then make those checks required: in Settings β Branches, add a branch-protection rule for main and enable "Require status checks to pass before merging." Now a PR simply cannot merge while tests are red.
π Helpful review bots
Dependabot opens PRs to update vulnerable dependencies. Codecov reports how a change affects test coverage. CodeQL (GitHub's built-in scanner) flags security issues. Each handles a slice of review no human should have to do by hand.
β The right balance
Automate the mundane β formatting, style, obvious bugs β and reserve human review for the things machines can't judge: is this the right design? does it solve the real problem? Automation enhances human review; it never replaces it.
Hands-on Exercise
ποΈ Open a PR and review one
Objective: Experience both sides of the workflow β authoring a reviewable PR and performing a structured review.
Instructions:
- In a shared or practice repository, create a branch and make one small, focused change:
git checkout -b feature/add-greeting # edit a file... git add . git commit -m "Add greeting helper with tests" git push -u origin feature/add-greeting - On GitHub, click Compare & pull request. Write a description covering what, why, and how, and link an issue with
Closes #N. - Ask a peer (or a second account) to review it. As the reviewer, work through the six-step process: context β big picture β line by line β tests β docs β summary.
- Leave at least one line comment and one suggestion. Phrase both as questions or team-framed suggestions, not commands.
- As the author, address the feedback with a follow-up commit, resolve the threads, and request a re-review. Merge once approved, then delete the branch.
π‘ Hint
Keep the change genuinely small β a single function plus its test is ideal. The point is to practice the process, and a tiny diff lets you focus on writing a clean description and giving thoughtful feedback rather than wrestling with a huge review.
β What success looks like
The PR has a description that answers a reviewer's questions up front, at least one resolved review comment, a follow-up commit that addresses feedback, an "Approved" review, and a clean merge that auto-closes the linked issue. The feedback you gave reads as helpful, not harsh.
π― Quick Quiz
Question 1: Why should pull requests be kept small and focused?
Question 2: When giving review feedback, which approach is most constructive?
Question 3: What is the best division of labor between automated checks and human reviewers?
Best Practices
β Do
- Rebase on the target branch and run tests before opening the PR.
- Write a description that explains what, why, and how β link the related issue.
- Review promptly (aim for within one business day) so work keeps flowing.
- Praise good work; acknowledge improvements over time.
β οΈ Don't
- Don't bundle unrelated fixes or a big refactor into a feature PR.
- Don't rubber-stamp "LGTM" without actually reading the diff and tests.
- Don't let disagreements fester in comment threads β move complex debates to a quick call.
Summary & Quiz
π Key Takeaways
- A pull request is a proposal plus a discussion plus a quality checkpoint plus a permanent record.
- PRs move through a predictable lifecycle: draft β open β review β revise β approve β merge.
- Keep PRs small and focused and write descriptions that answer questions before they're asked.
- Review systematically and prioritize correctness and security over style.
- Give feedback that reviews the code, not the coder, and let automation handle the mundane checks.
π Further Reading
- GitHub Docs β Pull Requests
- Google Engineering Practices β Code Review
- Conventional Comments
- Conventional Commits
π What's Next?
You've seen automated checks running on a pull request. Next we'll go under the hood of that automation and learn to build workflows from scratch with GitHub Actions.
π Great work!
Reviewing well is a superpower β it makes the whole team better, one PR at a time.