AI Dev Tools

AI Code: 6 Production Bugs You'll Encounter

AI code generators are a powerful new force in software development, but shipping their output into production is a minefield. Here are six pitfalls you're bound to hit.

AI Code Bugs: 6 Common Traps for Production Apps [Field Guide]

A non-tech founder, panicked, pinged at 11 PM on a Tuesday. Her app’s waitlist, built with AI, had exploded on Product Hunt, racking up thousands of signups. By dinner, the dashboard was a blank white screen. Nothing had been deployed. Nothing changed. She couldn’t even log into her own admin panel.

This isn’t a one-off horror story; it’s becoming a recurring nightmare for developers embracing AI coding assistants. I’ve seen this exact failure mode surface at least a dozen times in the past eight months. Different founders, different AI tools — Bolt, Cursor, Claude Code, you name it — but the same handful of critical blind spots. The code the AI churns out? Often technically sound. But production? That’s a whole different beast, one where the AI’s limited context is brutally exposed.

This is your field guide to those six gaping holes. If you’ve shipped, or are about to ship, anything dreamed up by an AI coder, you’ll nod along to at least three of these.

Here’s the fundamental disconnect: an AI coding model sees the code it’s writing, right here, right now. It doesn’t see your DNS records, your carefully crafted environment variables, your server’s cold-start latency, your database connection limits, the creeping passage of time across different timezones, or, critically, the fact that your so-called ‘production’ environment is actually your personal laptop running ngrok.

Models are trained on a massive corpus where ‘it works locally’ is the ultimate arbiter of success. For a side project tinkered with on a single machine, that’s often true. But for a live application, with real users, traversing real networks, across actual timezones, that assumption shatters. The AI isn’t lying, mind you. It just lacks production telemetry. You’re the one who has to stitch that in.

So, let’s dissect these six recurring patterns. Each one is a personal discovery, a bug I’ve wrestled out of a ‘vibe-coded’ codebase in the last year.

The Wildcard API Key Fiasco

Roughly seven out of every twelve AI-assisted applications I’ve audited have at least one API key lying naked in the client-side code. Why? Because the AI was told, ‘Call the Stripe API from this React form.’ It dutifully complied, completely oblivious to the fact that this key is now broadcast to every browser on Earth via the ‘View Source’ option.

The giveaway: you grep your dist/ or .next/ build output for the first few characters of your sensitive key, and there it is.

# Run this against your built output BEFORE deploying
cd dist && grep -r "sk_live_" . || echo "Clean"
cd .next && grep -r "sk_live_" . || echo "Clean"

The fix is elegantly simple: every secret-laden API call must travel through a backend route. Your client-side code sends a request to your server. Your server, armed with the secret securely stashed in an environment variable, makes the call to the third-party service. Even for a simple static site, a tiny serverless function or a single Express route can handle this. It costs next to nothing and takes about fifteen minutes.

If you take away only one thing from this entire post, make it this: Leaked Stripe keys, leaked OpenAI keys, leaked Resend keys. These aren’t just theoretical vulnerabilities; they translate directly into real financial losses and a devastating erosion of customer trust. I’ve seen one founder rack up an eye-watering $1,800 in OpenAI charges in just eleven hours because a leaked key was scraped from a GitHub mirror.

The ‘Select Star’ Avalanche

The AI model churns out something like SELECT * FROM users WHERE org_id = ?. In development, with your four test users, it works perfectly. But in production? When your largest customer boasts eighty thousand users? That single query can return eighty thousand rows across the wire, flooding into your Node.js process, being converted into JSON, and then broadcast back over the network. Your API server’s memory usage goes through the roof. Your database connection pool becomes utterly exhausted. And suddenly, your dashboard displays that dreaded blank white screen.

This is, by an enormous margin, the most frequent Lovable/Bolt failure I encounter.

The solution? Paginate absolutely everything and impose a hard upper limit on every single query. Set a sensible default limit—fifty is usually fine for most applications—enforce it at the query layer, and only allow the client to request more data in explicit, chunked segments.

// Bad (what the AI likely wrote)
const users = await db.query('SELECT * FROM users WHERE org_id = $1', [orgId]);

// Better
const limit = Math.min(parseInt(req.query.limit) || 50, 200); // Enforce a max limit of 200
const offset = parseInt(req.query.offset) || 0;
const users = await db.query(
  'SELECT * FROM users WHERE org_id = $1 ORDER BY id LIMIT $2 OFFSET $3',
  [orgId, limit, offset]
);

While you’re at it, add an index to the column you’re filtering by. Most AI-generated applications sport precisely zero indexes beyond the primary keys automatically created by the ORM.

The CORS Catastrophe: Wildcard Woes

Every AI model I’ve interacted with, when presented with a ‘CORS error’ query, will reliably suggest setting Access-Control-Allow-Origin: *. It certainly removes the error message. It also, crucially, flings your API wide open to every single website on the public internet. This becomes a significant problem the moment you introduce session cookies or authentication tokens into the mix.

The truly correct approach is to whitelist your own domain (along with your staging domain, and localhost:3000 for local development).

```javascript // Express example const allowedOrigins = [ ‘https://yourapp.com’, ‘https://staging.yourapp.com’, ‘http://localhost:3000’, ];

app.use(cors({ origin: (origin, callback) => { // Allow requests with no origin (like mobile apps or curl requests) if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error(‘Not allowed by CORS’)); } }


🧬 Related Insights

Alex Rivera
Written by

Developer tools reporter covering SDKs, APIs, frameworks, and the everyday tools engineers depend on.

Worth sharing?

Get the best Developer Tools stories of the week in your inbox — no noise, no spam.

Originally reported by dev.to

Stay in the loop

The week's most important stories from DevTools Feed, delivered once a week.