The Art of Code Review: What Senior Engineers Actually Look For
Code reviews are not about catching typos. Here is what experienced engineers focus on and why it makes teams dramatically more effective.
On this page
Beyond Syntax
Junior developers often think code review is about catching bugs. Missing semicolons, wrong variable names, off-by-one errors. Automated tools handle most of that. The real value of code review lies elsewhere.
Senior engineers review code for three things that linters cannot catch: correctness of approach, long-term maintainability, and knowledge sharing.
What to Look For
1. Does It Solve the Right Problem?
The most expensive bug is a correct implementation of the wrong solution. Before reading a single line of code, understand:
- What problem is this PR solving?
- Is this the simplest approach that could work?
- Are there edge cases the author has not considered?
A ten-line fix to the right problem is worth more than a hundred-line implementation of the wrong one.
2. Failure Modes
Every system has a happy path and many unhappy ones. For each code change, ask:
- What happens when the database is slow?
- What happens when the input is unexpectedly large?
- What happens when this is called concurrently?
- What happens when a downstream service is unavailable?
// This looks correct but fails under concurrency
async function getOrCreate(key: string): Promise<Entity> {
const existing = await db.find(key);
if (existing) return existing;
return await db.create({ key, value: defaultValue });
}
// Two concurrent calls can both pass the find check
// and both try to create, causing a duplicate key errorThe fix is often simple — a unique constraint, an upsert, or a distributed lock — but only if someone asks the question during review.
3. Abstractions
Bad abstractions are worse than no abstractions. Watch for:
- Premature abstraction: Creating interfaces or base classes for things that only have one implementation
- Wrong boundaries: Abstractions that group things that change independently, or split things that change together
- Leaky abstractions: When the caller needs to know implementation details to use the API correctly
The best test: can you explain what this abstraction represents in one sentence without using the word "and"?
4. Naming
Good names are documentation you cannot skip. Look for:
- Functions that do what they say:
processData()tells you nothing;calculateMonthlyRevenue()tells you everything - Boolean names that read as questions:
isValid,hasPermission,shouldRetry - Consistent vocabulary: If the codebase says "user," do not introduce "account" for the same concept
5. Test Quality
Tests that pass are not necessarily good tests. Check:
- Do they test behavior or implementation? (Behavior survives refactoring; implementation does not)
- Are the assertions specific enough? (
expect(result).toBeDefined()proves nothing) - Do they cover the edge cases identified in the failure mode analysis?
- Can you understand what the code should do by reading only the tests?
// Bad: tests implementation
test("calls database.save", async () => {
await createUser(userData);
expect(database.save).toHaveBeenCalledWith(userData);
});
// Good: tests behavior
test("creates a user with the given email", async () => {
const user = await createUser({ email: "[email protected]" });
expect(user.email).toBe("[email protected]");
expect(await findUser(user.id)).not.toBeNull();
});How to Give Feedback
Be Specific
Not: "This could be better." But: "This query fetches all columns but only uses three. Selecting specific columns would reduce memory usage and network transfer."
Distinguish Must-Fix from Nice-to-Have
Use a consistent prefix:
- Blocking: Must be addressed before merge
- Suggestion: Would improve the code but not required
- Nit: Trivial style preference, take it or leave it
- Question: Genuine curiosity, not a change request
Ask Questions
"Why did you choose X over Y?" is more productive than "You should use Y." The author might have context you lack. Questions also teach the reviewer.
Acknowledge Good Work
When you see something clever, elegant, or well-tested, say so. Positive feedback reinforces good practices and makes the process less adversarial.
The Meta-Game
The best code reviewers are not the ones who find the most bugs. They are the ones who:
- Raise the bar gradually: Each review makes the codebase slightly better
- Spread knowledge: After reviewing their PRs, team members internalize the patterns
- Build trust: Consistent, fair, specific feedback creates psychological safety
- Catch systemic issues: Individual PRs reveal patterns — if every PR has the same problem, the team needs a convention, not more review comments
Code review is not a gate. It is a conversation. The goal is not to prove you are smarter than the author. The goal is to ship better software, together.
Recommended for you
Understanding Transformer Architecture: The Engine Behind Modern AI
A deep dive into the transformer architecture that powers GPT, BERT, and virtually every modern language model — explained with clarity.
System Design: Building a Real-Time Notification Service at Scale
How to architect a notification system that handles millions of events per second — from WebSockets to message queues to delivery guarantees.
RAG Is Not Just Vector Search: Building Production Retrieval Systems
Why naive RAG implementations fail in production and how to build retrieval-augmented generation systems that actually work reliably.
Why Your Microservices Are Slower Than a Monolith (And How to Fix It)
Microservices promise scalability and team autonomy. But without careful design, they deliver latency, complexity, and debugging nightmares.