Why Your Code Works But Still Gets Rejected in Code Review

# code# developer
Why Your Code Works But Still Gets Rejected in Code ReviewMark

Have you ever submitted a pull request where all the tests pass, the feature works perfectly, and...

Have you ever submitted a pull request where all the tests pass, the feature works perfectly, and yet... your code review comes back with "Request Changes" in bright red? Yeah, me too. And it's frustrating because you're thinking, "But it WORKS!"

Here's the thing: working code is just the minimum bar. It's not the finish line.

Let's talk about the hidden rules of code review that nobody tells you about.

They don't teach the real world in bootcamps. Get 25 years of production-grade experience on my YouTube: 🛠️👉 https://www.youtube.com/@lessonsfromproduction


1. Cognitive Load

Your code isn't just read once. It's read hundreds of times over its lifetime. Every time someone debugs an issue, adds a feature, or tries to understand how the system works, they're reading your code.

Consider this:

d = datetime.now()
y = d.year
m = d.month
dy = d.day
Enter fullscreen mode Exit fullscreen mode

This code works perfectly fine. The tests pass. But every single person who reads it has to mentally decode what each variable means. That's cognitive load.

Now look at the better version:

today = datetime.now()
year = today.year
month = today.month
day = today.day
Enter fullscreen mode Exit fullscreen mode

It's self-documenting. The reader doesn't have to spend mental energy figuring out what's going on — they can immediately understand the intent and move on to the actual logic.

Good code doesn't make you feel smart for writing it. Good code makes the next person feel smart for reading it.


2. Premature Abstraction

Oh boy, have I been guilty of this one. You get excited about design patterns, and suddenly you're building a DataProcessorFactory with a ProcessorRegistry and a Strategy pattern for a feature that literally runs once a day.

There's a principle called YAGNI: You Aren't Gonna Need It.

Abstraction is powerful, but it comes with a cost. Every abstraction layer is another place where bugs can hide. Every interface is another thing future developers need to understand.

The better approach? Start simple. Just write a straightforward function that does the thing. When you find yourself copying and pasting that code for the third time, then abstract it. When you have three real, concrete use cases, then build the framework.

Rule of thumb: Don't abstract until you have at least three examples of the pattern. Two is coincidence. Three is a pattern.


3. Missing the "Why"

Comments should explain WHY, not WHAT. The code already shows what you're doing.

// convert to milliseconds
return seconds * 1000;
Enter fullscreen mode Exit fullscreen mode

That comment adds zero value. The code is literally showing that conversion.

But this?

// API times out after 5 seconds, so we use 4.5s to leave a buffer
return seconds * 4500;
Enter fullscreen mode Exit fullscreen mode

Now you're documenting the business logic. Now you're explaining why this specific number matters.

Same goes for retry logic:

// BAD: "Retry 3 times" is obvious from the code.

// GOOD: Third-party API occasionally fails with 503 errors;
// retrying usually succeeds within 3 attempts.
for (let i = 0; i < 3; i++) { ... }
Enter fullscreen mode Exit fullscreen mode

That tells future maintainers why the retry logic exists and helps them decide if 3 is still the right number six months from now. Context is everything. When you come back to this code later, you won't remember why you made these decisions. Help your future self out.


4. Scope Creep

You opened a PR to fix a button bug. Simple, right? But then you noticed the form validation was messy, so you refactored that. And while you were there, you reorganized the components. Oh, and you updated the styling system too.

Now your "simple bug fix" touches 47 files.

Every additional change makes the review harder. It makes deployment riskier. And if something breaks in production, good luck figuring out which of those 47 file changes caused it.

One PR, one purpose. Fix the button bug. Ship it. Then open a separate PR for the form refactor. Then another for the component reorganization.

Smaller PRs get reviewed faster, deployed more confidently, and reverted more easily if needed.


5. Inconsistent Style

Your code works. But it looks foreign in the codebase because you used Promises when everyone else uses async/await. Or you used snake_case when the team uses camelCase.

I get it — you have preferences. I have mine. But consistency beats personal preference every single time.

The goal is for the entire codebase to look like one person wrote it. When someone is reading through the code, they shouldn't be able to tell where your code starts and someone else's ends.

Match the existing patterns. Follow the team conventions. Use the linter. Your future teammates will thank you.


6. Poor Error Handling

The happy path works perfectly. You tested it manually, it works great. But what about the sad path?

async function updateUser(id, data) {
  const user = await db.find(id);
  user.update(data);
  await user.save();
}
Enter fullscreen mode Exit fullscreen mode

What if the user doesn't exist? What if the save fails? What if the data is invalid?

Production code needs to handle failure gracefully. Ask yourself: what could go wrong here? And then handle those cases explicitly. Fail fast with clear error messages. Don't let invalid data propagate through your system. Don't let a null reference crash your server.

The difference between junior and senior developers often comes down to this:

  • Juniors think about what should happen.
  • Seniors think about what could happen.

7. Magic Numbers

What does 50 mean? What does 300000 mean? What does 0.75 mean?

if (users.length > 50) paginate();
setTimeout(retry, 300000);
if (score > 0.75) approve();
Enter fullscreen mode Exit fullscreen mode

These numbers might make sense to you right now, but they're meaningless to anyone else — and they'll be meaningless to you in three months.

Use named constants:

const MAX_USERS_PER_PAGE = 50;
const RETRY_DELAY_MS = 300_000; // 5 minutes
const SUCCESS_THRESHOLD = 0.75;
Enter fullscreen mode Exit fullscreen mode

MAX_USERS_PER_PAGE tells a story. When the product manager asks you to change the threshold, you want to find it quickly and change it confidently. Named constants make that possible.


8. Testing Shortcuts

Tests pass, but do they actually test anything?

expect(result).toBeTruthy();
Enter fullscreen mode Exit fullscreen mode

This test will pass if the function returns 1, true, "hello", or literally any non-falsy value. It tells you nothing about whether the code is actually working correctly.

Good tests verify specific behavior:

expect(result.email).toBe("user@example.com");
expect(result.name).toBe("Jane Doe");
expect(result.id).toBeDefined();
Enter fullscreen mode Exit fullscreen mode

They document what the code is supposed to do. They give you confidence that when you refactor, you haven't broken anything. Your tests are documentation for future developers — make them count.


The Real Goal of Code Review

Code review isn't really about finding bugs. Modern testing catches most of those.

Code review is about knowledge sharing. It's about maintaining code quality over time. It's about catching edge cases and ensuring long-term maintainability.

When a reviewer asks you to change something, they're not saying your code doesn't work. They're saying "this will be easier to maintain if we do it this way" or "this matches our patterns better" or "future you will thank us for this clarity."

Before you hit submit on your next PR, ask yourself:

  1. Can someone understand this code in six months when the context has faded?
  2. Does it follow the team's conventions and patterns?
  3. Is it as simple as possible, but no simpler?
  4. Does it handle errors gracefully?

Working code is the minimum bar, not the finish line. Great code is code that disappears — code that's so clear and well-structured that future developers don't even notice it. They just understand it and move on.


You're not writing code for computers. Computers don't care if your variables are named x or userAccountBalance. You're writing code for humans.

Write code for the tired developer at 2 AM trying to fix a production bug. Write code for the new team member trying to understand how the system works. Write code for yourself in six months when you've forgotten everything.


What code review feedback frustrates you the most? Drop it in the comments below.

Note: This article was written with AI assistance to help structure and articulate 25+ years of debugging experience. All examples, insights, and methodologies are from real-world experience.