I reviewed a pull request last year that took me 45 minutes to understand. The logic was technically correct. The tests passed. But the code was written as if the author was the only person who would ever read it. Which, clearly, they assumed they would be.
When I finally left my feedback, the developer pushed back: "But it works." And they were right. It did work. That's exactly the problem.
Most articles about junior developer mistakes focus on the obvious stuff: not writing tests, ignoring error handling, pushing to main. Those are real, but most juniors already know about them. The habits I'm talking about are subtler. They're the ones that don't cause immediate failures. They just make everything quietly harder over time.
Treating "It Works" as the Finish Line
This is the one I see most often, and it comes from a genuinely good place: juniors are proud when something works. They should be. But shipping working code is the baseline, not the goal.
Code gets read far more often than it gets written. A function you write today might be touched by three different developers over the next two years, including a future version of yourself who has completely forgotten why you made that decision.
When you stop at "it works," you leave behind a trail of questions: Why is this variable named temp2? Why is this condition inverted? Why does this function do two completely unrelated things?
The discipline to go back and ask "would a stranger understand this in 30 seconds?" is what separates code that works from code that lasts.
Copying Stack Overflow Without Understanding It
There's nothing wrong with Stack Overflow. I still use it constantly. The problem is copy-pasting a solution without understanding what it actually does.
I once watched a junior developer copy a regex pattern for email validation that had a known catastrophic backtracking bug. It worked fine in development. It silently caused timeouts in production for three months before anyone connected the dots.
When you paste code you don't understand, you create a black box in your own codebase. You can't debug it when it breaks. You can't adapt it when requirements change. You can't explain it in a code review.
The fix isn't to stop using Stack Overflow. The fix is to add one extra step: before you paste anything, read it line by line and convince yourself you could rewrite it from scratch if you had to. If you can't, you don't understand it yet.
Over-Engineering Because It Feels More "Professional"
Junior developers often equate complexity with skill. They've heard about design patterns, SOLID principles, and microservices, so they apply them everywhere, whether the problem warrants it or not.
I've seen a simple CRUD feature arrive in a PR with a factory pattern, a strategy pattern, an abstract base class, and three levels of inheritance. For a form that creates blog posts.
Here's the uncomfortable truth: unnecessary abstraction is just as harmful as no abstraction at all. It makes the codebase harder to onboard, harder to debug, and harder to change. The original problem wasn't complex. The solution made it complex.
The right time to add abstraction is when you feel pain from its absence: when you catch yourself duplicating logic in three places, or when a single change ripples across the codebase. Let the pain guide you. Don't preemptively solve problems you don't have yet.
Not Reading Error Messages
This one sounds ridiculous until you watch someone spend 40 minutes debugging an issue while their terminal is displaying the exact file and line number of the problem.
Error messages aren't punishment. They're free documentation. A good error message tells you what broke, where it broke, and sometimes even why. But I've watched junior devs read the first three words of a stack trace, declare "I don't understand this," and immediately jump to Google.
Get into the habit of reading the entire error message before doing anything else. Then read the stack trace from top to bottom. Nine times out of ten, the answer is right there.
TypeError: Cannot read properties of undefined (reading 'map')
at UserList (/src/components/UserList.jsx:14:22)
That's not cryptic. That's line 14, column 22. Something is undefined when you expected an array. Start there.
Asking for Help Too Late (or Too Early)
There's a balance here that most juniors take a long time to find.
On one end: spending three hours stuck on something you could have resolved in 10 minutes with a single question. Nobody respects that kind of silent suffering. It doesn't make you look independent, it makes you look inefficient.
On the other end: asking for help before you've thought about the problem at all. Pinging a senior with "this isn't working" and a screenshot is a quick way to erode goodwill.
A useful rule of thumb: spend 20–30 minutes genuinely trying to solve the problem yourself. Document what you tried and why it didn't work. Then ask, and lead with that documentation. "I've tried X, Y, and Z. X failed because of this, Y because of that. I'm stuck on Z for this reason. What am I missing?"
That question gets answered fast, every time.
Ignoring the Existing Codebase
Juniors often write code as if they're starting from scratch. They build a custom date formatter when the project already has date-fns installed. They write a new API helper when there's already one in /utils/api.js. They introduce a third state management pattern into an app that already has two.
Before you write anything, search the codebase. Your team has already solved a lot of problems. You just haven't found the solutions yet.
# Before writing a new utility, search what already exists
grep -r "formatDate" ./src
grep -r "fetchWithAuth" ./src
Reading the existing code isn't just about avoiding duplication. It tells you how the team thinks, what patterns they prefer, and what decisions they've already made. That context is invaluable, and you'll absorb it faster by reading than by any other method.
Treating Git as a Save Button
Commit messages like fix, wip, stuff, asdfgh are more common than they should be. Git history is documentation. It is often the only way to understand why a change was made, not just what changed.
# This tells your future team nothing:
git commit -m "fix bug"
# This tells your future team everything:
git commit -m "Fix null reference in cart total when discount code is removed
When a discount code was removed from the cart, the discount amount wasn't
being reset to zero before recalculating the total. This caused the total
to appear negative in some edge cases.
Fixes #482"
You don't need to write an essay for every commit. But a one-line summary that explains the what and why takes 30 extra seconds and saves hours of archaeology later.
Assuming the Requirement Is Right
Junior developers tend to take requirements as law and implement them exactly as written. Senior developers push back, politely but consistently.
Requirements come from people who aren't thinking about edge cases, technical constraints, or second-order effects. "Add a button that deletes all user data" sounds straightforward. But what about confirmation dialogs? What about soft delete vs. hard delete? What about cascade effects on related records? What about the legal implications under GDPR?
Implementing exactly what's asked without thinking it through doesn't make you a team player. It makes you a liability.
When you get a requirement, spend five minutes asking: What happens at the edges? What happens when this goes wrong? Is this actually what the user needs, or just what someone thinks the user needs? Ask those questions before you write a single line of code.
Not Owning Their Work End-to-End
This is the most common growth blocker I've seen. A junior submits a PR and considers their job done. The feature gets merged, goes to QA, bugs get filed, and a senior ends up fixing them, sometimes without even looping the original author back in.
Ownership doesn't end at "PR merged." It means following your feature into QA. It means being available when bugs come in. It means reading the error reports from production. It means caring what happens after you ship.
The developers who grow fastest are the ones who treat their work as theirs until it's clearly someone else's. They watch their features in production. They read user feedback. They close the feedback loop themselves.
That habit, more than any technical skill, is what makes someone worth promoting.
Common Patterns Worth Knowing
If you're seeing yourself in any of this, here's the rough order to tackle it:
- Start with readability: write code your teammates can understand without asking you.
- Then own your bugs: follow your work all the way to production.
- Then sharpen your questions: get better at asking the right thing at the right time.
- Then push back on requirements: once you have credibility, use it to improve the product.
- Finally, simplify: strip complexity you added before you needed it.
None of these are about learning a new technology. They're about changing how you think about your work.
Key Takeaways
- "It works" is the starting point, not the ending point.
- Copy code only after you fully understand it.
- Complexity doesn't signal seniority. Clarity does.
- Error messages are your first debugger.
- The 20-minute rule: try first, ask with context.
- Read the existing codebase before writing anything new.
- Git history is documentation. Treat it that way.
- Requirements are suggestions; your job is to improve them.
- Ownership doesn't end at the PR.
The Real Gap Between Junior and Senior
It's not syntax. It's not knowing more frameworks. It's not even system design.
The biggest gap is accountability: the quality, clarity, and longevity of your work. Senior developers have internalized that their job isn't to write code; it's to solve problems in a way that doesn't create new ones.
Every mistake on this list comes down to the same root cause: optimizing for "done" instead of optimizing for "good." The shift from one to the other is uncomfortable, slower in the short term, and absolutely worth it.
What's the mistake you made as a junior that you wish someone had called out sooner? Drop it in the comments. Chances are someone reading this is making it right now.












