Over the last six months at EnactOn, we’ve started noticing a brand-new category of emergency audit requests.
It used to be that when a founder brought us broken software, it was built by a junior freelancer who was clearly in over their head. You’d find messy spaghetti code, but at least someone had sat there and typed it out by hand.
Lately, though, the code looks completely different.
The variable names are pristine. The comments look like they were written by a textbook author. The frontend is built with clean Tailwind components, smooth loaders, and snappy interactive states. On a screen-share demo, the app looks like a million bucks.
Then you look at the backend, and you realize what actually happened:
The agency charged the founder $15,000 for "senior custom development," opened an AI code generator, prompted their way through the backend in four days, and collected the final invoice.
AI coding assistants are incredible tools when an experienced engineer is steering them. But when an agency uses AI to generate an entire backend so they can cut corners, it creates a very specific kind of technical disaster.
Here is what actually breaks under the hood, why demos never catch it, and how to tell if your app was built on a foundation of sand.
1. The Exposed Root Key (How AI Fixes Permission Errors)
If you’ve ever watched someone build an app with an AI tool, you know how it handles friction.
When a human engineer runs into a database permission error, they take the time to configure server roles, environment boundaries, and access policies.
When an AI hits that same permission error, it doesn't care about security. It just wants the red error text in the terminal to disappear. And the fastest way to make the error disappear is to use the master admin key that bypasses all security rules:
// What the AI puts in a frontend component to make the error go away:
import { createClient } from '@supabase/supabase-js'
export const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY! // 🚩 Root bypass key bundled into every user's browser
)
Because the agency developer was just prompting instead of reading the architecture docs, they don't realize that prefixing a secret key with NEXT_PUBLIC_ literally bundles that key into the public JavaScript downloaded by every single visitor.
To the founder clicking around on a staging link, the app works seamlessly.
To anyone who opens Chrome DevTools and searches the network tab, the master key is sitting right there. Anyone with that key can read, modify, or drop the entire database without ever logging in.
2. The Illusion of Security: DISABLE ROW LEVEL SECURITY
In modern serverless databases like Supabase or Postgres, Row-Level Security (RLS) is what stops User A from reading User B's private files.
Writing solid RLS policies takes real thought. You have to write specific rules for every single table: who can insert, who can read, who can delete.
When an agency prompts an AI to scaffold ten database tables at once, the queries almost always fail because the policies conflict with each other.
We recently audited a health-tracking app where the previous dev team had run into this exact wall. Their solution? The agency had simply run this command across all eight tables:
ALTER TABLE user_medical_records DISABLE ROW LEVEL SECURITY;
The app stopped throwing permission errors. The agency marked the sprint as complete.
On a demo call with one test account, everything looked completely normal. But because RLS was turned off and the queries weren't guarded on the server, the entire table of sensitive patient intake logs was completely public to anyone who sent a raw GET request to their API endpoint.
Frontend forms don't protect data. If the database rules aren't enforced at the table level, you don't have security. You just have an unlocked door with a polite sign hanging on it.
3. Fake Scale: The Infinite Loop & Memory Bloat
AI models are trained on millions of simple tutorial projects. And in a tutorial project with five sample rows, you don't worry about connection pooling, rate limiting, or pagination.
This is where the "fake scale" kicks in.
We audited an internal directory app where the search bar felt blazing fast during the founder's initial review. But the second twenty people started using it at the same time, their server CPU shot to 100% and the entire site went down.
When we opened the codebase, we found two classic AI habits:
- Unbounded Data Fetching: Instead of asking the database for the ten specific records on page one, the generated code was fetching all 15,000 directory rows into the server's memory on every single keystroke, then running a JavaScript .filter() in memory.
- Missing Dependency Arrays: The AI had wired a data fetch inside a React useEffect without proper caching or dependency boundaries, causing the frontend to re-fetch the entire database every time the user's cursor hovered over a menu item.
In local testing on a developer's laptop, pulling 15 mock records takes 3 milliseconds. You never notice the problem.
In production with real users, you burn through your entire monthly database connection pool in twenty minutes, and the hosting provider slaps you with a 504 Gateway Timeout.
The Reality of Vibe Coding Development
We don't tell founders to avoid AI. AI has permanently changed the speed at which you can validate an idea, build a proof of concept, and test customer interest.
The problem starts when an agency treats a vibe-coded prototype like production-ready software.
A prototype only needs to survive a demo call with one person clicking the obvious buttons. A real software product has to survive real strangers: people with spotty Wi-Fi, people running script crawlers, and multiple users hitting the same database at the exact same second.
When an agency skips foundational engineering and lets an AI hallucinate the backend, they haven't saved you money. They’ve just taken out an uncollateralized loan on your technical debt that comes due the week you launch.
3 Quick Checks to Run on Your Own App Right Now
If an agency built your MVP using AI and you want to know what you’re actually dealing with before you launch, you don't need a computer science degree to check these three things:
- Check the Network Bundle: Open your app in Chrome, hit Inspect, go to the Network tab, reload the page, and filter by JS. Search inside the files for words like service_role, admin_secret, or your database master password. If they show up in the browser, your database is exposed.
- The RLS Confirmation: Ask your agency to send you a screenshot of your database console showing your active Row-Level Security policies. If every table says "RLS Disabled" or "No Policies Applied," your data is wide open.
- The Incognito Data Test: Open two different browser profiles. Log in as Account A in one, and Account B in the other. Try to access Account A’s private resource ID directly from Account B. If the app serves the data without an unauthorized error, your backend has no real authorization logic.
Has anyone else here had to step in and rescue an "AI-built" backend recently? What was the wildest shortcut you found buried in the code?


Top comments (0)