← Back to Blog

7 Bugs AI Won't Warn You About Before You Ship

2026-02-208 min readSecurity
AI-built apps production readiness

You've spent 48 hours straight coding with an AI assistant. Cursor generated your auth flow, Claude set up your database, and Bolt helped you hook up Stripe. Everything works on localhost. You're feeling like a god. Time to deploy, right?

Hold up.

Here's the thing nobody tells you about AI coding: it gets you about 60% of the way there. The last 40%? That's where every vibe-coded app fails. And AI assistants won't warn you about it because they don't know what production-ready actually means.

Let's talk about the 7 bugs AI won't catch before you ship.

1. Stripe Test Keys in Production

What it is: You forgot to swap pk_test_ and sk_test_ for live keys in production. Those keys end up in your bundle and payment flow.

What happens if you ship it: Customers click pay and nothing happens. No charge. No clear error. Revenue appears to go flat for weeks.

How to fix it: Never hardcode keys in code. Validate env vars at boot and fail fast.

if (process.env.NODE_ENV === 'production' && !process.env.STRIPE_PK?.startsWith('pk_live_')) {
  throw new Error('Stripe live key required in production');
}
Stripe payment form with broken production keys

2. Missing Webhook Signature Verification

What it is: Your /api/webhook/stripe endpoint accepts any POST as a valid event. No signature check, no validation.

What happens if you ship it: Anyone can send fake “subscription activated” events. People get premium access for free until your model is economically broken.

How to fix it: Verify every event with your webhook signing secret.

const signature = request.headers['stripe-signature'];
const event = stripe.webhooks.constructEvent(
  payload,
  signature,
  process.env.STRIPE_WEBHOOK_SECRET
);

3. Exposed .env File

What it is: Your server doesn't block /.env. If someone guesses that path, your secrets are one curl away.

What happens if you ship it: Attackers pull API keys, database passwords, JWT secrets, and begin exploitation immediately.

How to fix it: Block secret file paths at app and edge/server levels.

app.use((req, res, next) => {
  if (req.path === '/.env' && process.env.NODE_ENV === 'production') {
    return res.status(404).end();
  }
  next();
});
Exposed .env file risk illustration

4. No Rate Limiting on Login

What it is: Your auth endpoint accepts unlimited password attempts and does not rate limit or throttle.

What happens if you ship it: Botnets lock out your users, or brute force attacks slowly break credentials.

How to fix it: Add rate limiting middleware and enforce strict attempt budgets.

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,
});
app.post('/api/login', limiter, loginHandler);

5. Default Error Pages Leaking Stack Traces

What it is: You rely on default framework error pages in production.

What happens if you ship it: Stack traces and internal paths leak into the UI, giving attackers a roadmap of your infrastructure.

How to fix it: Use generic production error responses and structured logging on server side.

app.use((err, req, res) => {
  if (process.env.NODE_ENV === 'production') {
    res.status(500).send('Something went wrong.');
  } else {
    console.error(err);
    res.status(500).send(err.stack);
  }
});

6. Missing Security Headers

What it is: Your app has no CSP, no HSTS, no clickjacking protections, and weak browser defenses.

What happens if you ship it: XSS, clickjacking, and downgrade attacks become easier.

How to fix it: Enable a security middleware like Helmet with strict defaults.

import helmet from 'helmet';

app.use(helmet({
  contentSecurityPolicy: true,
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true,
  },
}));
Security headers and protections

7. No Health Endpoint

What it is: Your app has no /healthz or /status endpoint for monitoring.

What happens if you ship it: You discover outages when customers do, not when your systems can.

How to fix it: Expose a simple DB + dependency check endpoint for uptime monitors.

app.get('/healthz', async (req, res) => {
  try {
    await db.query('SELECT 1');
    res.status(200).json({ status: 'healthy' });
  } catch {
    res.status(503).json({ status: 'unhealthy' });
  }
});

The Bigger Problem

These aren't edge cases. These are the common production failures we see in AI-built apps every week.

AI can draft clean features fast. What it can't do is replace your judgment on production readiness.

AI-generated code versus production-ready code

Want to check all 7 automatically?

ShipCheck scans your live app and gives you direct fixes, severity, and where to patch first.

Scan Your App Now