The feature is done. The tests pass, QA signed off, and customers can use it. So why does the senior reviewer still ask for changes? Because "it works" only proves the code is correct today, for the inputs you tried. Good code also has to survive the next developer, the next requirement, the next unexpected input, and the next production incident. Code can work perfectly and still be bad code.
Working Is the Minimum, Not the Goal
Every piece of code is read many more times than it is written, and changed many more times than anyone expects. The real cost of software isn't writing the first version; it is everything after. Code that only "works" pushes that cost onto whoever touches it next, often you, six months later, at 2 AM during an outage.
Sign 1: Nobody Can Understand It Quickly
Names like d, tmp2, processData, or flag force readers to reverse-engineer intent. Compare:
// Works, but what does it do?
if (u.t === 2 && d > 30) p = p * 0.9;
// Works, and explains itself
const isPremium = user.tier === Tier.PREMIUM;
if (isPremium && daysSinceSignup > 30) {
price = applyLoyaltyDiscount(price);
}If a reviewer needs you to explain the code verbally, the code isn't finished. Clear names, small functions, and obvious control flow are part of the feature.
Sign 2: One Change Breaks Three Other Things
Working code often hides tight coupling: a function that updates the database, sends an email, formats a response, and logs analytics all at once, or modules that reach into each other's internals. It works until a requirement changes. Then a small edit ripples across the system. If you recognize this, our list of 10 signs your code structure is becoming unmaintainable goes deeper.
Sign 3: It Only Handles the Happy Path
The demo used a valid email, a positive amount, and a fast network. Production will send empty strings, duplicate requests, time-outs, null values, and Unicode names. Code that ignores these cases "works" right up to the first real user. Validate input at the boundaries, handle errors explicitly instead of swallowing them, and decide what should happen on failure.
Sign 4: It Can't Be Tested
If the only way to test a function is to spin up the whole app, hit a real database, and call a third-party API, the design is the problem. Hard-coded dependencies, hidden global state, and methods that do too much make testing painful, so tests don't get written, so the next change is risky. Code that is easy to test is usually also easy to understand and change.
Sign 5: It's Clever Instead of Clear
A one-line chain of nested ternaries, a regex nobody can read, or a "smart" generic abstraction for a single use case may work and even impress. But clever code is expensive to maintain and easy to break. Optimize for the reader. We compared the trade-offs in Clean Code vs Clever Code: What Actually Works in Production.
Sign 6: It's Duplicated Everywhere
Copying a block of validation or pricing logic into five places works perfectly until the rule changes and someone updates four of them. Duplicated business rules are bugs waiting to happen. Put each rule in one place. At the same time, don't force abstraction on code that only looks similar but changes for different reasons.
Sign 7: You Can't See What It's Doing in Production
When something goes wrong, code with no useful logs, no error context, and no metrics turns a ten-minute fix into a day of guessing. Log meaningful events with identifiers, report errors with context, and add metrics for important operations. Observability is part of working software, not an extra.
Sign 8: It Works Only at Today's Scale
Loading every record into memory, running a query inside a loop, or calling an external API on every page view works with 100 rows and falls over with 100,000. You don't need to optimize everything up front, but avoid obvious traps like N+1 queries and unbounded lists, and know where your limits are.
Sign 9: The Intent Isn't Recorded Anywhere
Strange code sometimes exists for good reasons: a workaround for a vendor bug, a legal rule, a performance fix. Without a short comment explaining why, the next developer will "clean it up" and reintroduce the problem. Comment the why, not the what, and keep commit messages meaningful.
How to Move From Working to Good
- After it works, reread it as if you were a new team member. Rename anything unclear.
- Split functions that do more than one job.
- Add tests for edge cases and failure paths, not just the happy path.
- Remove duplication of business rules.
- Add logging and error handling where failures would be hard to diagnose.
- Ask for review and treat feedback as part of the work, not as criticism.
This habit of finishing the job properly is one of the clearest markers separating experience levels, as discussed in The Difference Between Junior and Senior Developer Code.
Conclusion
"It works" is where good code starts, not where it ends. Code that is readable, loosely coupled, tested, robust against bad input, observable, and honest about its intent keeps working as the system and team change. Take the extra hour after the feature works to make it good. It is the cheapest hour you will ever spend on that code.
