You click approve because the logic looks roughly right, nothing obvious jumps out, and you don't want to be the person who blocks a senior's PR over something you might have misread. That feeling is normal. It's also how bad habits calcify into a ceiling on your career.
Knowing how to do a good code review isn't just about finding bugs. It's about building a systematic way of reading code that compounds over time. The four habits below are the ones that most reliably keep junior developers from progressing, and each one has a concrete fix.
1. Approving code you mostly understand
The habit looks like this: you read through the diff, the logic seems plausible, and you approve. You didn't trace the full execution path. You didn't ask what happens at the edges. But nothing looked broken, so you moved on.
Juniors do this for understandable reasons. Reading slowly feels like a weakness. Blocking a senior's PR over something you're not sure about feels presumptuous. And if you've never seen what a complete review looks like, you don't know you're stopping short.
The cost is real. Bugs that slip through here are the ones that hit production on a Friday night. Your name is on that approval. That's not a scare tactic, it's just what the audit log shows.
The fix is a two-question rule before you approve anything. What's the worst input this function could receive? What happens then? If you can't answer both questions confidently, you haven't finished the review. This sounds simple, but most developers don't ask it consistently until they've been burned.
Reviewing real pull requests under graded conditions is one of the fastest ways to build that edge-case muscle. Try a free graded review on Goodcatch to see exactly which scenarios you're missing before you miss them in production.
2. Commenting on style instead of substance
Count the comments in your last ten reviews. How many addressed behaviour? How many addressed presentation?
For most junior reviewers, the ratio is lopsided toward presentation. Variable naming, indentation, spacing, comment formatting. These are easy to spot and safe to raise. Nobody argues about a naming suggestion the way they argue about a logic call.
What gets left untouched: off-by-one errors, missing auth checks, race conditions, unhandled promise rejections, SQL injection vectors. These are the things that define a senior reviewer. They require confidence, deeper reading, and a willingness to say "I think this is wrong" rather than "I think this could be named better."
Here's the honest part: Prettier, ESLint, Rubocop, and their equivalents handle most style issues automatically. A reviewer who spends their time flagging what a linter would catch isn't adding much value. The team already has a tool for that.
The fix is a personal triage rule. Before you submit a review, categorise each comment as behavioural or presentational. If every comment you've written is presentational, go back and read for substance. One loop through asking "could this cause incorrect behaviour?" is often enough to surface what you skipped.
Learning to weight substance over style is a progression skill, and it compounds. The developers who advance fastest are the ones who start catching the security issues that junior reviewers most often miss while their peers are still debating variable names.
3. Reviewing the diff instead of the system
You read the changed lines. They look clean. You approve. What you didn't do is trace where that code gets called, what it depends on, or whether it fits into the existing system at all.
Here's a concrete version of this: a junior approves a small helper function that looks tidy in isolation. It duplicates logic that already exists in a service three layers up, written slightly differently. Now there are two implementations that will drift apart over time, and the next person who touches either one won't know which is canonical.
The function was fine. The addition was a problem. But the diff looked clean.
Understanding a change in context requires knowing the system. Juniors often don't have that context yet, and that's fair. The mistake isn't lacking the context, it's not flagging the gap. "I can see this function works, but I'm not sure whether it duplicates existing logic. Worth a second look?" is a useful comment. Approving silently is not.
The practical fix: before reviewing, spend two minutes tracing where the changed code is called from and what it depends on. Ask whether this change could break anything upstream or downstream. You won't always have the full answer, but the two minutes almost always surfaces a question worth asking.
This skill genuinely does improve with exposure to more codebases. Graded practice on varied real-world pull requests accelerates that exposure faster than waiting for it to accumulate on the job, because you get feedback on what you missed rather than discovering it six weeks later.
4. Treating the first pass as the final pass
The first read-through catches surface issues. You understand roughly what the code is trying to do. Nothing obviously explodes. You write a comment or two and submit.
The problem is that security vulnerabilities, subtle logic errors, and missing error handling tend to surface on a slower second read, after you've stopped trying to understand what the code does and started asking whether it actually does it correctly. The first pass and the second pass answer different questions. Running them together means you're only ever answering the first one.
A practical structure that senior reviewers use, often without naming it explicitly:
- First pass: understanding. What is this change trying to do? What's the intended behaviour?
- Second pass: correctness. Does the code actually do that? Are there cases where it doesn't?
- Third pass: risk. What could go wrong, and is it handled? What happens on failure?
The honest trade-off: not every PR warrants three passes. A one-line config change is different from a new authentication flow. The skill is recognising which type of review you're looking at before you start, not after. Getting that call wrong in both directions wastes time, either over-reviewing trivial changes or under-reviewing critical ones.
Senior and lead reviewers aren't necessarily faster. They're more systematic. That system can be learned deliberately. It doesn't have to form slowly on its own through years of scar tissue.
Goodcatch's graded feedback and weak-spot analytics are designed to show you specifically which pass you're currently skipping. If you consistently miss logic bugs but catch security issues, that tells you something. If your edge-case detection is weak but your structural thinking is solid, that tells you something different. Knowing the gap is the first step to closing it.
One more thing worth saying plainly: if you're looking for a shortcut that makes code review feel effortless, this isn't it. These habits require deliberate attention, and they take time to build. What Goodcatch offers is structured repetition with feedback, which is faster than unstructured experience, but it's still work.
For a closer look at how to frame the comments you leave once you've found something real, this guide on giving useful code review feedback covers the communication side without the vagueness.
And if you want to get specific about the logic bugs that most often slip through on the first pass, this breakdown on spotting logic bugs before they hit production is worth reading alongside this one.
Start a free graded review on Goodcatch and find out which habits you're carrying.