Code Review Tips for Git Teams
Code review is the highest-leverage practice available to a software team. Studies consistently show that code review catches more defects per hour of effort than any form of testing. But beyond defect detection, review is where knowledge spreads across the team, where architectural decisions are validated, and where junior developers learn from seniors. Done well, code review is not a bottleneck — it is an accelerant.
Why Code Review Matters
Benefit | How it works |
|---|---|
Defect detection | A second pair of eyes catches bugs, edge cases, and security issues the author missed |
Knowledge sharing | Reviewers learn about features they did not build; authors learn best practices |
Consistency | Reviews enforce style, architecture patterns, and conventions across the codebase |
Onboarding | New team members learn the codebase faster through reviewing code |
Accountability | Knowing your code will be read raises the standard of work |
Documentation | PR descriptions and comments create a record of why decisions were made |
Reviewing the Diff: Understand Context
The worst review habit is opening a PR and scrolling through the diff without understanding what the change is supposed to do. Before reading a single line of code:
Read the PR title and description completely.
Click through to the linked issue to understand the original problem.
Look at the files changed list — get a mental map of the scope before diving in.
Check the PR size: if it is >500 lines, consider asking the author to split it.
Understand the testing approach before reading the implementation.
When reading the code, ask: "If I did not know this PR existed, would this code be understandable?" Not just "does this change work" but "will the team be able to maintain this in six months?"
Using git diff to Understand the Full PR
See the full diff between main and a feature branch
# Three-dot diff: shows what changed on feature-branch since it diverged from main git diff main...feature/user-search # This is equivalent to what GitHub shows in the PR diff tab # Diff a specific file only git diff main...feature/user-search -- src/api/users.ts # See which files changed and how many lines git diff --stat main...feature/user-search # src/api/users.ts | 89 +++++++++++++++++++++++++ # src/api/users.test.ts | 134 +++++++++++++++++++++++++++++++++++++++ # 2 files changed, 223 insertions(+)
Compare two released versions (useful for reviewing between releases)
# All changes between v1.9.4 and v2.0.0 git diff v1.9.4..v2.0.0 # Only files that changed git diff --name-only v1.9.4..v2.0.0 # Summary stats git diff --stat v1.9.4..v2.0.0
Reviewing One Commit at a Time
For PRs with many commits, reviewing the full diff can be overwhelming. Instead, walk through the commits chronologically — each one should represent a logical, self-contained step.
Review commits one at a time
# List commits on the feature branch not yet in main git log main..feature/user-search --oneline # def5678 test(api): add tests for user search # abc1234 feat(api): add user search endpoint # Review the first commit (the foundation) git show abc1234 # Shows the full diff for just that commit # Review the second commit (tests built on the foundation) git show def5678 # Or use git log -p to see all commits with their diffs in one scroll git log -p main..feature/user-search
GitHub Suggestions Feature
When you want to propose a specific code change, use GitHub's Suggested Changes feature instead of writing a text comment. The author sees your exact proposed code and can apply it with one click.
Writing a GitHub suggestion in a review comment
In the review comment box, use the suggestion code block: ```suggestion const label = isActive ? 'Active' : 'Inactive' ``` This replaces the highlighted lines with your suggestion. The author can click "Commit suggestion" to apply it without leaving GitHub. Your change appears in the commit history with attribution.
When to Comment, Request Changes, or Approve
Action | When to use | Effect |
|---|---|---|
Comment | Questions, praise, optional suggestions | No approval status change |
Request Changes | Blocking issues that MUST be fixed before merge | Blocks merge until re-reviewed |
Approve | All blocking concerns resolved, code is ready to merge | Satisfies the approval requirement |
Reserve "Request Changes" for real blockers — security issues, correctness bugs, missing tests for critical paths, or significant design problems. Using "Request Changes" for style preferences or optional improvements is a review anti-pattern that creates unnecessary friction.
Review Checklist
Use a mental checklist when reviewing. Not every point applies to every PR, but running through this list takes only a few minutes and catches most issues:
Correctness
Does the code do what the PR description says it does?
Are edge cases handled? (empty inputs, null values, zero, very large numbers)
Are error cases handled? (network failure, database error, invalid input)
Are race conditions possible if this code runs concurrently?
Is the logic correct for all code paths (if/else branches, loop termination)?
Tests
Are new tests provided for new behaviour?
Do the tests actually test the right thing, or are they testing implementation details?
Do tests cover the error paths, not just the happy path?
Would a future refactor break these tests even if behaviour is correct?
Security
Is user input validated and sanitised before use?
Are SQL queries parameterised (no string interpolation into queries)?
Are secrets or credentials accidentally included?
Are authorisation checks in place (can any user call this endpoint, or only the right user)?
Is sensitive data logged or exposed in error messages?
Performance
Is there an N+1 query problem (one database query per loop iteration)?
Are indexes being used for the new queries?
Is expensive computation happening on every render/request when it could be cached?
Are large files or images being loaded unnecessarily?
Readability and Maintainability
Are variable and function names clear?
Is complex logic commented with the "why", not just the "what"?
Are functions short and single-purpose?
Is there code that could be reused but is duplicated instead?
Does the code follow the existing patterns and conventions of the codebase?
Documentation
Is the README updated if user-facing behaviour changed?
Are new API endpoints documented?
Are JSDoc/docstring comments added for public functions?
Is the CHANGELOG updated for user-visible changes?
Pair Programming as an Alternative
Asynchronous code review is not the only option. For high-stakes changes, complex algorithms, or difficult debugging sessions, pair programming can be more effective:
Approach | Best for | Drawback |
|---|---|---|
Async PR review | Most changes, remote teams, parallel work | Latency, context loss |
Pair programming | Complex features, onboarding, knowledge transfer | Requires scheduling, more time intensive |
Pre-merge pair review | High-risk changes, final check | Can be a bottleneck |
Code Review Anti-Patterns to Avoid
Anti-pattern | What it looks like | Better approach |
|---|---|---|
Rubber stamping | LGTM with no actual review | Read the code; write at least one substantive comment |
Bikeshedding | Long debate over naming or formatting | Automate formatting; accept reasonable alternatives |
Review bombing | Dozens of nit comments on a good PR | Pick the top 2-3 comments; let the rest go |
Holding PRs hostage | Blocking merge for weeks on non-critical changes | Time-box reviews; approve with optional follow-up |
Scope creep | "While you are in here, also fix X" | Open a new issue for the additional change |
Personal attacks | "This is terrible code" | Always address the code, never the person |
Drive-by reviewing | Commenting on a PR without context, then disappearing | Either commit to the full review or don't start |
Invisible comments | Leaving comments without requesting changes, then blocking on them | Use Request Changes when something is blocking |