When I Reject AI Code Even If It Works: The Hidden Costs of 'Good Enough'

• AI coding, code quality, software development, technical debt, AI ethics, developer tools, code review

TL;DR


I've been building AI products for years now, and there's a moment that happens almost daily: Cursor or Copilot suggests a block of code, I run it, the tests turn green, and then I delete the entire thing.

Not because it's broken. Because it works too well for the wrong reasons.

This might sound counterintuitive in an era where we're told to move fast and ship. But as someone who's both built AI coding tools and used them extensively in production systems, I've learned that the most dangerous code isn't the code that fails—it's the code that works just well enough to merge, but not well enough to maintain.

Vini Brasil recently wrote about when he rejects AI code even if it works, and his insights hit on something critical that the "AI will replace developers" crowd consistently misses: the gap between functional correctness and professional quality is where real engineering judgment lives.

The Seduction of Green Tests

Here's what typically happens: You're implementing a feature. You write a prompt describing what you need. Your AI assistant generates 50 lines of code. You run your test suite. Everything passes. You feel productive. You commit.

Three months later, a junior developer needs to modify that code. They spend four hours trying to understand what it does. They introduce a bug. The bug makes it to production. The cost of that "working" code just exceeded whatever time you saved.

The problem isn't that AI writes bad code—modern models are remarkably good at generating syntactically correct, functionally accurate solutions. The problem is that AI optimizes for immediate correctness, not long-term maintainability.

Consider a simple example: You need to transform an array of user objects. AI might generate a dense chain of .map().filter().reduce() that does exactly what you asked. It works. It's even performant. But it's also a cognitive puzzle box that requires full mental stack allocation to parse.

A human developer, thinking about the next person to touch this code, might write three separate, clearly-named intermediate steps. Slightly more verbose, but infinitely more maintainable. The AI solution optimizes for brevity. The human solution optimizes for clarity.

My Take: The Comprehension Tax Compounds

I think we're underestimating how quickly incomprehensible code accumulates in codebases where AI assistance is heavily used without strong review practices. Every piece of "clever" code that works but doesn't communicate intent is a tax on future comprehension. And unlike financial debt, this tax compounds exponentially.

Here's why: When you don't understand a piece of code, you're more likely to work around it rather than through it. You add another abstraction layer. You duplicate logic. You create coupling where none should exist. Each workaround makes the next piece of code harder to reason about, which makes the next workaround more likely.

I've seen production systems where the original AI-generated code was fine, but the accumulated workarounds and patches from developers who didn't fully understand it turned the codebase into an archaeological dig site. Each layer tells a story of someone trying to change something they didn't quite grasp.

This is why I reject working AI code: I'm not just evaluating the immediate functionality. I'm evaluating the comprehension tax it imposes on every future interaction with that part of the system.

When Working Code Fails the Real Tests

The Readability Test

Can a developer unfamiliar with this code understand its intent in under 30 seconds? If the answer is no, the code fails—even if it works.

AI models don't naturally optimize for readability because they don't experience the frustration of debugging someone else's code at 2 AM. They optimize for token efficiency and pattern matching. This often results in code that's technically correct but humanly opaque.

Vini Brasil's article highlights this perfectly when he discusses code that works but doesn't communicate intent. The tests pass, but the why behind the implementation remains hidden. In professional software development, the why is often more important than the what.

The Convention Test

Does this code follow the team's established patterns and conventions? AI assistants are trained on vast corpuses of code from thousands of different projects, each with their own style. Without explicit guidance, they'll generate code that's stylistically inconsistent with your codebase.

This matters more than it sounds. Consistency isn't about aesthetics—it's about reducing cognitive load. When every module follows the same patterns, developers can focus on business logic instead of decoding stylistic variations.

I regularly reject AI-generated code that works perfectly but uses a different error handling pattern than the rest of our codebase, or structures tests differently than our established conventions. The short-term cost of rewriting is vastly outweighed by the long-term benefit of consistency.

The Architecture Test

Does this code fit the system's architectural vision, or does it create coupling and dependencies that will constrain future changes?

AI assistants are excellent at solving local problems but poor at maintaining global architectural coherence. They don't know that you're planning to extract a service next quarter, or that this module needs to remain framework-agnostic, or that you're deliberately avoiding certain dependencies.

I've rejected working code because it introduced a dependency on a library we're trying to phase out, or because it coupled two modules we're planning to separate, or because it solved the immediate problem in a way that would prevent a larger refactoring we have scheduled.

These aren't things AI can know without extensive context—and even with context, models struggle with multi-month architectural planning.

The Security Test

Does this code introduce security vulnerabilities that automated tests won't catch?

AI models are trained on public code repositories, which means they've seen plenty of insecure code patterns. They'll happily generate SQL concatenation instead of parameterized queries, or store sensitive data in logs, or create timing attack vulnerabilities—all while producing code that functionally works.

Security review requires understanding threat models, attack vectors, and compliance requirements. These are contextual judgments that require human expertise. Working code that introduces a SQL injection vector isn't working code—it's a ticking time bomb.

The Rejection Process: How to Override AI Effectively

Rejecting AI code isn't about being a luddite or protecting your job. It's about being a professional who understands that code quality is a long-term investment. Here's my process:

1. Run It First

Always verify that the AI's solution actually works. Sometimes AI generates code that looks right but has subtle bugs. Don't reject code without confirming it solves the immediate problem.

2. Read It Carefully

Don't just skim. Read every line. If you find yourself thinking "I'm not sure what this does," that's a red flag. You should be able to explain every line to a junior developer.

3. Consider the Alternatives

Could this be written more clearly? More simply? More consistently with existing code? If yes, it's worth rewriting.

4. Evaluate the Trade-offs

Sometimes AI code is imperfect but good enough. If you're prototyping or building a throwaway spike, accepting suboptimal code might be the right call. But for production systems with long lifespans, optimize for maintainability.

5. Rewrite with Intent

When you reject AI code, don't just delete it—rewrite it deliberately. This is a teaching moment for yourself. You're training your intuition about what good code looks like in your specific context.

The Broader Implications for AI-Assisted Development

This discussion extends beyond individual code review decisions. It's about how we integrate AI tools into professional software development workflows.

AI assistants are powerful tools, but they're tools that require skilled operators. The skill isn't in generating code—it's in evaluating code. It's in knowing when to accept, when to modify, and when to reject.

This is why I'm skeptical of claims that AI will replace developers. What AI does is shift the skill distribution. Instead of spending time on syntax and boilerplate, we spend time on architecture, readability, and long-term maintenance. These are harder skills, not easier ones.

The developers who thrive in an AI-assisted world won't be the ones who generate the most code—they'll be the ones who have the judgment to know which code to keep.

Building Better Habits with AI Coding Tools

If you're using AI coding assistants regularly, here are some practices that help maintain code quality:

Set explicit quality standards in your prompts. Instead of "write a function to process user data," try "write a clearly-documented function to process user data, using our standard error handling patterns and following our naming conventions."

Review AI code with the same rigor you'd apply to junior developer code. Don't give it a pass just because it came from a machine. If anything, be more critical, because AI doesn't learn from feedback the way humans do.

Maintain a rejection log. When you reject AI code, note why. Over time, you'll identify patterns—certain types of problems AI consistently handles poorly, or certain prompts that consistently generate low-quality output. Use this to refine your AI interaction patterns.

Pair program with AI. Don't just accept full functions. Generate small pieces, review them, integrate them, then generate the next piece. This keeps you in the driver's seat and prevents the "black box" problem where you end up with working code you don't understand.

Teach your team to reject code. Make it culturally acceptable—even encouraged—to reject AI suggestions. The goal isn't to use AI as much as possible; it's to use AI effectively.

The Future of Code Quality in an AI World

As AI coding tools become more sophisticated, the gap between "working" and "good" will become more important, not less. Models will get better at generating functionally correct code, which means the differentiator will be code that's not just correct but maintainable, secure, and architecturally sound.

This is actually good news for experienced developers. It means the profession is evolving toward higher-level judgment and away from rote implementation. But it requires us to be disciplined about code review and willing to reject solutions that work but don't meet professional standards.

The next generation of AI coding tools will likely incorporate more of these quality considerations. We'll see models that understand architectural context, that can evaluate code for maintainability, that can explain their reasoning. But until then—and probably even after—human judgment remains essential.

Conclusion: Working Code Is Just the Beginning

When I reject AI code even though it works, I'm not being pedantic. I'm being professional. I'm recognizing that code has a lifecycle that extends far beyond the moment it's written, and that the true cost of code is measured in maintenance, not creation.

AI coding assistants are remarkable tools that have genuinely improved my productivity. But they're tools that require skilled use. The skill isn't in generating code—it's in evaluating code, in knowing what to keep and what to reject, in understanding that working code is just the beginning of the quality conversation, not the end.

As Vini Brasil's insights remind us, the human element in software development isn't about writing syntax—it's about exercising judgment, maintaining standards, and thinking about the developers who will interact with this code six months or six years from now.

That's a skill no AI can replace, because it requires caring about things that exist outside the immediate problem space: team dynamics, long-term maintenance, architectural vision, and the human experience of working with code.

So the next time your AI assistant generates working code, don't just run the tests. Read it. Understand it. And if it doesn't meet your standards—reject it. Your future self, and your teammates, will thank you.

Frequently Asked Questions

How do I know when to reject AI-generated code versus when to accept it?

Reject AI code when it fails key quality tests: readability (can someone unfamiliar understand it in 30 seconds?), consistency (does it match your team's conventions?), architecture (does it fit your system's design?), or security (does it introduce vulnerabilities?). Accept it when it meets these standards and genuinely saves time without creating maintenance burden. The key is treating AI suggestions with the same rigor you'd apply to code from a junior developer.

Isn't rejecting working code a waste of time when we could be shipping features?

Short-term thinking about "shipping fast" often creates long-term costs that far exceed the time saved. Code that works but is hard to understand creates a comprehension tax that compounds over time—every future developer who touches it spends extra time decoding it, and they're more likely to introduce bugs or create workarounds that further degrade the codebase. Investing 15 minutes to rewrite unclear code can save hours or days of debugging and maintenance down the line.

What specific practices help maintain code quality when using AI coding assistants?

Set explicit quality standards in your prompts, review AI code with the same rigor as human code, maintain a rejection log to identify patterns in AI weaknesses, pair program with AI by generating small pieces rather than full functions, and make it culturally acceptable on your team to reject AI suggestions. The goal isn't maximizing AI usage—it's using AI effectively while maintaining professional standards for maintainability, security, and architectural coherence.

Will AI coding tools eventually get better at generating maintainable code, making manual review unnecessary?

While AI models will certainly improve at considering maintainability factors, human judgment will remain essential because code quality depends heavily on context that's difficult to encode: your team's specific conventions, your system's architectural roadmap, your organization's security requirements, and the trade-offs appropriate for your particular situation. The profession is evolving toward higher-level judgment rather than rote implementation, which actually makes experienced developers more valuable, not less.