Junior Developer's Code Review Survival Guide: For Both Reviewers and Reviewees
That fear when your first code review lands. The notification pops up and your stomach drops. "Is my code terrible?", "Will they judge me for not knowing this?", "Why are there so many comments?"
Here's the thing: code review is collaboration, not combat. The reviewer was once nervous too. Everyone wants the same thing — better code. I wish I'd understood that earlier.
This is what I've learned as both a reviewee and, later, as a reviewer.
Part 1: As a Reviewee — How to Write Review-Friendly PRs
A PR is a Letter
Think of opening a PR not as dropping code on someone, but as writing a letter to a colleague. Context-free code dumps force the reviewer to start with "what even is this?" A friendly PR description cuts review time in half.
A good PR description includes:
## Summary
Added user profile image upload feature
## Why This Approach
- Chose S3 over Cloudinary: already on AWS, reduces costs
- 5MB file size limit: average profile images are under 2MB
## How to Test
1. Log in, navigate to /profile
2. Click the upload button
3. Select an image under 5MB → expect success message
4. Select an image over 5MB → expect error message
## Screenshots
[Before] / [After]
## Related Issues
Closes #123
This feels like overhead but reviewers love it — they can dive straight into reviewing logic instead of reverse-engineering intent.
Smaller PRs, Faster Reviews
Would you rather check out 100 items at a supermarket or 5 items at a convenience store? Code review works the same way.
500-line PRs are brutal. Attention fragments, issues get missed, and reviewers feel the pull of rubber-stamp approving.
Practical PR size guidelines:
| PR Size | Lines Changed | Review Time | Recommendation |
|---|
| Small | ~100 lines | 10–20 min | Best |
| Medium | 100–300 lines | 30–60 min | Fine |
| Large | 300–500 lines | 1–2 hours | Caution |
| XL | 500+ lines | Half a day+ | Avoid |
For big features, split into multiple PRs: "refactor PR", "interface change PR", "implementation PR".
Self-Review Before Opening
Before hitting "Create PR", read your own diff as a reviewer would. On GitHub's diff view, check:
// Ask yourself these before submitting:
// 1. Do variable names communicate intent?
const d = new Date(); // bad
const createdAt = new Date(); // good
// 2. Does each function do one thing?
function processUserAndSendEmail(user: User) {
// Doing two things → split into two functions
}
// 3. Are error cases handled?
async function fetchUser(id: string) {
const user = await db.users.findById(id);
return user; // what if user is null?
}
// 4. Are there tests?
// 5. Did you leave any console.log or debug code?
Self-review catches obvious mistakes upfront. That frees your reviewer to focus on real logic issues.
Don't Get Defensive About Comments
Receiving review comments triggers defensiveness. "Why is this a problem?", "You don't understand my intent." That's natural — but pause before reacting.
Response patterns that work:
Reviewer: "This function seems to be doing too much. Feels like an SRP violation."
Bad response:
"It works fine though, I think this is acceptable."
Good response:
"You're right — looking at it again, it's both parsing the file and
saving to DB. Splitting those makes sense. I'll fix it."
Or when you genuinely disagree:
"Could you explain a bit more about why this is an SRP violation?
I thought these were related enough to group together, but I might
be missing something."
Asking for clarification is always better than silent resistance.
Part 2: As a Reviewer — How to Give Constructive Feedback
Review the Code, Not the Person
The most common code review mistake is criticizing the person instead of the code.
Criticizing the person (bad):
"Why did you make this so complicated? I can't follow this at all."
"This suggests you don't know the basics."
"I wouldn't have written it this way."
Criticizing the code (good):
"This logic is hard to follow as written.
Extracting an intermediate variable might make it clearer."
"A factory function here would make this easier to test."
"There might be a cleaner approach — what if we changed this to...?"
Feedback should always be about the code. "This code could be improved" not "you made a mistake."
Label Your Comments by Priority
When every comment carries the same weight, the reviewee doesn't know what to fix first. Use prefixes:
[BLOCKING] — must fix before merge
"[BLOCKING] User input is being interpolated directly into the SQL query.
This creates an SQL injection vulnerability. Use a prepared statement."
[NIT] — minor style issue, optional
"[NIT] `data` could be `userData` for a bit more clarity."
[QUESTION] — genuine curiosity, not criticism
"[QUESTION] Why setTimeout here? Just wondering if there's a
specific timing issue this is solving."
[SUGGESTION] — improvement idea, not required
"[SUGGESTION] Extracting this into a custom hook would make it
reusable elsewhere."
Reviewees can prioritize [BLOCKING], then get to [NIT] when time allows.
Praise Is Also Code Review
Reviews don't have to be purely problem-hunting. When you see good code, say so.
"This error handling is really clean — I'm going to borrow this pattern."
"The function name here is so clear I didn't need to read the implementation."
"These test cases are thorough. Future maintainers will thank you."
Teams that build praise into review culture receive critical feedback much more graciously.
Part 3: Review Checklists
Reviewee Checklist Before Opening PR
## Before Opening PR
### Code Quality
- [ ] Self-reviewed the diff
- [ ] Removed console.log, debugger, debug artifacts
- [ ] Comments explain "why" not "what"
- [ ] Function/variable names communicate intent
- [ ] No duplicated code
### Functionality
- [ ] Error cases handled
- [ ] Edge cases considered
- [ ] No performance issues (N+1 queries, memory leaks)
### Tests
- [ ] Tests added for new functionality
- [ ] All existing tests pass
- [ ] Tests actually test meaningful behavior
### The PR Itself
- [ ] Description clearly explains what and why
- [ ] PR size is reviewable (~300 lines or less)
- [ ] Screenshots or demo added for UI changes
- [ ] Related issue linked
Reviewer Checklist
## Code Review Checklist
### Security
- [ ] SQL injection vulnerabilities
- [ ] XSS possibilities
- [ ] Sensitive data exposure (hardcoded API keys, passwords)
- [ ] Proper auth/permission checks
### Logic
- [ ] Business logic matches requirements
- [ ] Sufficient error handling
- [ ] Edge cases addressed
### Design
- [ ] No Single Responsibility Principle violations
- [ ] Appropriate abstraction level
- [ ] Blast radius of future changes is contained
### Performance
- [ ] No unnecessary computation
- [ ] Appropriate caching
- [ ] Database query efficiency
### Tests
- [ ] Important cases are tested
- [ ] Tests can actually fail
- [ ] Tests are fast and deterministic
Part 4: Real Scenarios
Scenario 1: You Encounter Ninja Code
// Reviewer receives this:
const r = u.map(x => x.a > 0 ? {...x, b: x.b * 1.1} : x)
.filter(y => y.c)
.reduce((acc, z) => acc + z.b, 0);
Good review comment:
It took me a while to figure out what this does.
Renaming the variables would make this self-documenting:
const totalActiveRevenue = users
.map(user => user.isEligible
? { ...user, revenue: user.revenue * 1.1 }
: user
)
.filter(user => user.isActive)
.reduce((total, user) => total + user.revenue, 0);
Same logic, reads like prose now.
Scenario 2: You Spot a Performance Issue
// N+1 query spotted in review
async function getPostsWithAuthors(postIds: string[]) {
const posts = await db.posts.findMany({ where: { id: { in: postIds } } });
// Problem: separate query for each post
const postsWithAuthors = await Promise.all(
posts.map(async (post) => {
const author = await db.users.findById(post.authorId); // runs N times!
return { ...post, author };
})
);
return postsWithAuthors;
}
Review comment:
[BLOCKING] This has an N+1 query issue — 100 posts means 101 DB queries.
Using `include` resolves this in one query:
const posts = await db.posts.findMany({
where: { id: { in: postIds } },
include: { author: true } // one query total!
});
Alternatively, batch the authorId lookups into a single IN query.
Scenario 3: You Receive a Vague Comment
Reviewer leaves:
"This seems off."
No idea what's wrong or how to fix it. Response:
"Could you be a bit more specific about what seems off?
Knowing which part and what you'd suggest would help a lot —
happy to refactor once I understand the concern :)"
Asking for specifics is not pushback. It's necessary.
Part 5: Building an Async Review Culture
Set Team Review SLAs
PRs left waiting for days kill development flow. Agree on SLAs upfront:
Team Review SLA Example:
- Small PR (under 100 lines): first review within 24 hours
- Normal PR (100–300 lines): first review within 48 hours
- Large PR (300+ lines): first review within 72 hours
- Emergency hotfix: within 2 hours
When primary reviewer is unavailable:
- Designate a backup reviewer
- Surface blocking PRs in standups
Standardize Comment Tone as a Team
Every team has a different review culture — some direct, some softer. Whichever style fits your team, consistency is what matters.
## Code Review Conventions
### Comment Prefixes
- [BLOCKING]: must fix before merge
- [NIT]: minor style, optional
- [QUESTION]: genuine question, not criticism
- [SUGGESTION]: improvement idea, optional
- [PRAISE]: recognizing good work
### Ground Rules
1. Critique the code, not the person
2. Never "change this" without explaining why
3. Include a suggested fix when possible
4. Praise good work explicitly
Closing: Code Review Is a Team Sport
The anxiety around code review is real and normal. Having your work evaluated by a peer is never comfortable. But over time, you start to feel how a strong review culture accelerates the whole team.
Teams with healthy review cultures:
- Catch bugs before they reach production
- Share knowledge naturally across the team
- Junior devs absorb senior thinking by reading reviews
- The codebase maintains consistent quality
It starts uncomfortable on both sides. Give it time. The team's collective skill compounds — and that's code review's real value.