The founder's guide to reviewing vibe-coded apps

How to audit, secure and ship an AI-generated codebase: the five-minute Supabase check, what a proper review covers, who to hire and how long it takes.

12 min read

Updated

In this article · 10 sections

I was halfway through an interview process for a standard full-stack engineering role at a company here in Prague, so I took a look at their recently launched product as part of my research. They'd built the low-code app quickly to capture a market opportunity.

But within five minutes, I had access to their entire database: over 2,000 customer records. Names, emails, payment information, chat messages. Everything.

I reported it because I was concerned and wanted to help, but they got defensive. Really defensive. Instead of thanking me for the heads-up, they treated me like I'd done something wrong.

It happens more often than you'd think, but the most serious version of the problem takes five minutes to check for yourself.

The five-minute database check

Before you hire anyone to review your vibe-coded app, check for the most critical issue yourself. It takes five minutes and zero technical skill.

Check your own app

If your app uses Supabase (and most vibe-coded apps do), check what an attacker could reach:

  1. Open your app in Chrome and press F12 (or right-click and choose Inspect).
  2. Go to the Network tab.
  3. Use your app normally: log in, click around, load some data.
  4. Filter the requests by typing "supabase". You're looking for requests to URLs like xxxxx.supabase.co.
  5. Click any Supabase request and find the request headers. Look for one called apikey. On older projects it's a long string starting with eyJ; on newer ones it starts with sb_publishable_.
  6. Copy that key. It's your public key (Supabase calls the new version the publishable key, and the legacy one is the anon key). It's supposed to be public, so that's not the problem. The problem is what that key can reach.
  7. Test what's exposed. Take your Supabase URL and that key to Supabase Schema, paste them in and click Fetch. You'll see every table in your database. Try querying each table directly. If row-level security isn't configured properly, you'll get back all the data, not just the current user's.

What the schema tells you

Red flagWhy it matters
Tables with no relationshipsOrphaned data that's probably insecure
No created_at or updated_at columnsNo audit trail
Sensitive data stored as plain textPasswords and API keys readable by anyone who gets in
No user_id foreign key on user-owned dataNo way to restrict access
Tables named test, temp or backupForgotten development artefacts

The good signs are clear naming conventions, proper UUID primary keys, defined foreign key relationships and timestamp columns for an audit trail.

What vibe coding actually produces

When you build with AI tools (Cursor, Lovable, Bolt, Replit, v0), you're asking an AI to generate code from your prompts. But the AI doesn't understand your business logic, security requirements or scaling needs. It understands patterns from training data, so it optimises for code that works today rather than code that can be maintained.

The result is code that works until it doesn't, often hiding problems under the hood:

  • Inconsistent patterns: each prompt might generate code following different conventions
  • Security gaps: API keys in frontend code, missing authentication checks, overly permissive database rules
  • No error handling: the happy path works, edge cases crash
  • Performance landmines: queries that work with 10 users but break with 1,000
  • Accumulated complexity: features stacked on features with no refactoring

None of it feels like spaghetti while you're building because every feature works and users are happy. But each addition sits on a more fragile foundation, so complexity multiplies until one day you can't ship any more. Nested conditionals pile up:

// What AI generates: works today, breaks tomorrow
if (user.type === "premium") {
  if (feature === "export") {
    if (format === "pdf") {
      // 50 more nested ifs...
    }
  }
}
// What production needs: a pattern that scales
const exportStrategy = exportStrategies[user.tier][format];
return exportStrategy.execute(data);

These issues are fixable, but you need to know what you're dealing with.

The review checklist

A proper review of a vibe-coded app covers different ground from a typical code review. Much of it maps onto the OWASP Top 10, where broken access control sits at number one.

Authentication and authorisation

CheckWhat to look for
Auth implementationIs Supabase Auth (or similar) properly configured?
Route protectionAre authenticated routes actually protected?
Role-based accessIf you have admin features, are they properly gated?
Token handlingAre JWTs stored securely (not localStorage for sensitive apps)?
Session managementDo sessions expire appropriately?

Database security (RLS)

Row-level security is where most vibe-coded apps fail. In Supabase's own words, a table in an exposed schema without RLS is readable and writable by any role with a grant on it.

CheckWhat to look for
RLS enabledIs RLS actually turned on for each table?
Policy coverageDo policies exist for SELECT, INSERT, UPDATE and DELETE?
Policy logicCan users only access their own data?
Admin bypassIs there a proper service role pattern for admin functions?

API keys and environment variables

CheckWhat to look for
Key exposureAre API keys hardcoded in frontend code?
Environment separationDifferent keys for development, staging and production?
Secret handlingSensitive keys only on the backend or in serverless functions?
Rate limitingAny protection against API abuse?

Frontend and input security

CheckWhat to look for
Input validationAre user inputs sanitised?
InjectionAre database queries parameterised, so input can't become SQL?
XSS protectionCan users inject scripts through form fields?
CORS configurationIs it properly restricted, or wide open (*)?
Error messagesDo errors expose internal details to users?

Code quality

CheckWhat to look for
Consistent patternsThe same approach throughout, or chaos?
Error handlingTry/catch blocks, error boundaries?
Type safetyTypeScript types actually defined, or any everywhere?
Dead codeUnused components, commented-out blocks?

Auditing Supabase from the dashboard

If you're hiring a reviewer, expect them to check Supabase's security settings:

  1. RLS status. Open the Table Editor and check each table's RLS indicator. If RLS shows as disabled, that's bad.
  2. Policies. Go to Authentication, then Policies. Every table should have policies defined, and they should reference auth.uid() to restrict rows to the current user.
  3. Function permissions. Go to Database, then Functions, and look for SECURITY DEFINER functions. They run with elevated privileges, so a poorly configured one can return rows the caller isn't allowed to read. Each needs careful review.
  4. Storage buckets. Go to Storage and check the bucket policies. Are uploaded files public when they shouldn't be?

Three issues AI-generated code keeps shipping

1. The "works locally" problem. AI tools often generate code that works in development but exposes secrets in production:

// Bad: API key in frontend
const stripe = new Stripe('sk_live_xxxxx')

// Good: key only on the server
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)

2. Missing server-side validation. Just because the frontend validates doesn't mean the API does. Check that every API endpoint validates its input, that database constraints exist as a last line of defence, and that no business logic relies solely on frontend checks.

3. Over-privileged database access. Check whether the app uses the service role key (now called the secret key) from the frontend. It shouldn't: that key bypasses row-level security entirely.

Production readiness

Whether you're preparing to launch or already live, check your app's production readiness.

Environment and operations

ItemProduction requirement
Environment variablesAll secrets in env vars, not code
Build configurationProduction builds enabled (no dev mode)
Error trackingSentry or similar configured
LoggingStructured logs for debugging
MonitoringUptime and performance monitoring in place
DeploymentsAutomated, with a tested rollback procedure
IncidentsA plan for who does what when something breaks

Performance baseline

CheckWhy it matters
Database indexesSlow queries will kill you at scale
Image optimisationLarge images destroy load times
Bundle sizeAI tools often import entire libraries
Caching and CDNStatic assets and expensive operations should be cached
Background jobsSlow work belongs in a queue, not the request
API response timeAim for under 200ms

Backup and recovery

RequirementStatus needed
Database backupsAutomated daily backups
Point-in-time recoveryAbility to restore to a specific moment
Backup testingActually tested restoring from a backup

On Supabase, daily backups come with the paid plans, so free projects need their own exports. Point-in-time recovery is an add-on.

Developer experience

CheckWhat to look for
Local developmentCan a new developer run it locally?
TypeScriptStrict mode on
TestsAn automated test suite
FormattingPrettier and ESLint, enforced by Git hooks
StructureA clear folder structure and documented components

Getting from prototype to production

It's time to move when you have paying users, performance problems or developers to onboard. The prototype that got you there won't get you further, so the work runs in roughly this order.

  1. Triage. Audit the codebase for critical vulnerabilities, find the breaking points and performance bottlenecks, map feature dependencies and technical debt, and prioritise fixes by user impact.
  2. Foundations. Establish a proper project structure (a monorepo when appropriate), consistent patterns across the codebase, essential infrastructure (error handling, logging, monitoring), and shared utilities and components.
  3. Rules for the AI. Instead of letting AI generate whatever it wants, write strict guidelines it has to follow: one way to fetch data, one place for state, one API client with retry logic, feature folders, no inline styles. Enforce them with pre-commit hooks: no direct API calls in components, no conditionals nested more than three deep, TypeScript strict mode.
  4. Pull the backend out of the frontend. Most no-code platforms create frontend-heavy applications with business logic scattered through components. Move that logic into a proper API layer (Hono suits lightweight needs) with clear data contracts, then into services, before adding database indexing, queues for background jobs, caching and rate limiting.
  5. Handoff. Write the documentation any developer can pick up: an architecture overview, the reasoning behind decisions, pattern examples and anti-patterns, recorded walkthroughs and an onboarding checklist.

Payment logic belongs in a service:

// Before: logic scattered across components
const handlePayment = () => {
  // 200 lines of payment logic in a React component
};

// After: a service does the work
const handlePayment = () => {
  return paymentService.process(order);
};

It's tempting to keep building features in the no-code tool while engineers rebuild underneath it. I tried exactly that on Snacker.ai, but it cost more than it saved. From Lovable to production is the post-mortem on why. The tools still earn their place for the right work:

Keep using no-code forUse production engineering for
Rapid prototypingCore business logic
UI and UX iterationsPerformance-critical paths
Marketing pagesSecurity-sensitive features
Admin interfacesComplex integrations
Internal toolsScalable infrastructure

Who to hire

You need a senior full-stack developer with specific experience.

Must-haves:

  • 5+ years of professional development experience
  • Production Supabase experience (or the equivalent: Firebase, Postgres)
  • A security background: has performed security audits before
  • AI-code experience: has cleaned up vibe-coded apps before

Nice-to-haves: experience with your specific stack (Next.js, React and so on), DevOps and deployment experience, and time spent with early-stage startups.

Questions to ask candidates

To verify technical skill:

  1. "Walk me through how you'd audit RLS policies in Supabase."
  2. "What are the top three security issues you see in AI-generated code?"
  3. "How would you handle finding API keys exposed in the frontend?"

To verify experience:

  1. "Tell me about a vibe-coded app you've reviewed. What did you find?"
  2. "What's your process for a code security audit?"
  3. "How do you prioritise which issues to fix first?"

To check communication:

  1. "How would you explain a critical security issue to a non-technical founder?"
  2. "What documentation would you provide after the review?"

Red flags in candidates

  • Can't explain their process clearly
  • Wants to rewrite everything immediately
  • Dismissive of AI-generated code without nuance
  • No specific Supabase or database security experience
  • Can't provide references from similar work

How long it takes

PhaseDurationWhat happens
Initial assessment1 daySchema review, high-level code scan, critical issues identified
Deep audit2 to 3 daysFull code review, security testing, documentation
Fix critical issues1 to 2 daysPatch immediate security problems
Full remediation1 to 2 weeksComplete fixes, testing, deployment prep

A review and critical fixes take three to five days. Hours vary by scope:

ScopeHours
Schema and security audit only8 to 12
Full code review and documentation20 to 30
Review and fix critical issues30 to 40
Complete remediation and deployment60 to 80

If someone quotes far less, they're either inexperienced or planning to cut corners. If they quote far more, they may be padding for a full rebuild.

What you should receive

  1. An executive summary: the top issues in plain English
  2. A security report: every vulnerability with a severity rating
  3. A code quality assessment: patterns, technical debt, maintainability
  4. A prioritised fix list: what to fix first, second and third
  5. A deployment checklist: what's needed before and for production
  6. A recommendation: rebuild or remediate

Rebuild or remediate

Sometimes the answer is to start fresh.

Remediate whenRebuild when
The core architecture is soundNo consistent patterns exist
The issues are fixable in under two weeksThe security issues are fundamental, not just missing pieces
The business logic is correct, just insecureAdding features takes three times longer than it should
You're under time pressure and the app is "mostly there"The developer estimates remediation will cost more than a new build

A senior developer should give you an honest assessment. If they recommend a rebuild, ask them to explain why in business terms, not just "the code is bad".

Where to start

  1. Do the five-minute check: open dev tools, find your Supabase key, see what's exposed.
  2. Write down what you know: features, integrations and any issues you've noticed.
  3. Find the right person: choose a senior developer with production database, security and AI-code experience.
  4. Get the audit: three to five days for a thorough review.
  5. Make the call: remediate or rebuild, based on the findings.

If the call is a rebuild and you'd rather hand it over, that's what Build is: ten business days and £20,000 fixed for one agreed outcome.

The goal is production readiness, not perfection. A good reviewer will help you understand what "good enough" looks like for your stage and get you there efficiently.

Research and sources

Checked 2026-10-18. Next review: 2027-04-18. Listings and availability can change.

  1. API keys | Supabase Docs

    Supabase · Checked 2026-10-18 · Primary source

    Publishable keys (sb_publishable_) replace the legacy anon key and are meant to be public; secret keys replace the legacy service_role key and bypass Row Level Security.

  2. Row Level Security | Supabase Docs

    Supabase · Checked 2026-10-18 · Primary source

    A table in an exposed schema without RLS is readable and writable by any role with a grant on it, and auth.uid() returns the ID of the requesting user for use in policies.

  3. Database Functions | Supabase Docs

    Supabase · Checked 2026-10-18 · Primary source

    A security definer function can return rows the caller isn't allowed to read.

  4. Database Backups | Supabase Docs

    Supabase · Checked 2026-10-18 · Primary source

    Supabase backs up Pro, Team and Enterprise projects daily, offers Point-in-Time Recovery for more frequent backups, and recommends free projects export their own data.

  5. OWASP Top 10:2025

    OWASP Foundation · Checked 2026-10-18 · Primary source

    The OWASP Top 10:2025 ranks Broken Access Control as A01, the most serious web application security risk.

  6. Welcome to Lovable | Lovable Documentation

    Lovable · Checked 2026-10-18 · Primary source

    Lovable is an AI app builder, one of the tools vibe-coded apps are made with.

  7. Bolt AI builder: Websites, apps & prototypes

    StackBlitz · Checked 2026-10-18 · Primary source

    Bolt is an AI builder for websites, apps and prototypes, one of the tools vibe-coded apps are made with.

People and organisations mentioned

What are you building or trying to learn?

Built your app with Lovable, Bolt or Cursor and not sure what it exposes? Tell me what you're building, and I'll tell you whether it needs fixing or rebuilding.

Build or learn with me
Doug SilkstoneFounder, builder & operator

I build and run businesses with a team of me plus AI agents. These articles draw on the methods I use across product, brand, design, outreach, sales, growth, support and operations, and the work I do with other businesses.

About me · More articles by Doug