In short
Apps generated by AI tools fail in the same handful of ways, and the most serious failure can be confirmed in five minutes by someone with no technical skill.
The worst problem takes five minutes to check
Before you pay anyone to review an AI-built app, find the worst of it yourself. It takes five minutes and zero technical skill.
Check your own app
If your app uses Supabase, and most apps generated this way do, that is where to start.
- Open your app in Chrome and press F12, or right-click and choose Inspect.
- Go to the Network tab.
- Use the app as a normal user: log in, move between a few screens, load some records.
- Type "supabase" into the request filter. You are looking for calls to an address such as
xxxxx.supabase.co. - Open any of those requests and read the request headers. One is called
apikey. On older projects it is a long string beginning witheyJ; on newer ones it begins withsb_publishable_. - Copy that value. It is your public key, which Supabase calls the publishable key in its newer form and the anon key in the old one. Being visible is by design, so the key itself is not the fault. What it can reach is.
- Test the reach. Take your Supabase URL and the key to Supabase Schema, paste both in and press Fetch. Every table in your database appears. Query them one at a time. Where row-level security is missing or wrongly written, you get the entire table back rather than the signed-in user's rows.
What the table list tells you about the build
The shape of a database is a quick report on how carefully the app was made.
| Red flag | Why it matters |
|---|---|
| Tables with no relationships | Orphaned data that's probably insecure |
No created_at or updated_at columns | No audit trail |
| Sensitive data stored as plain text | Passwords and API keys readable by anyone who gets in |
No user_id foreign key on user-owned data | No way to restrict access |
Tables named test, temp or backup | Forgotten development artefacts |
Reassuring signs run the other way: names that mean something, UUID primary keys, foreign keys that genuinely join tables, and timestamp columns for an audit trail.
What an AI tool actually hands you
Prompts to Cursor, Lovable, Bolt, Replit or v0 produce working software. But the model holds no picture of your business rules, your security obligations or your scaling needs. It has patterns from training data, so it writes code that works today rather than code another person can maintain.
What comes out works until it does not, with the weaknesses sitting under the surface:
- Inconsistent patterns: each prompt can land on its own conventions, so the app ends up with several ways of doing the same thing
- Security gaps: API keys inside frontend code, missing authentication checks, database rules that permit far too much
- No error handling: the happy path works and the edge cases crash
- Performance landmines: queries that are fine with 10 users and fall over at 1,000
- Accumulated complexity: features stacked on features with nothing refactored along the way
None of it feels like spaghetti while you are still building, because every feature works and users are happy. Each addition rests on a weaker foundation, though, and the complexity compounds until the day you cannot ship anything further. Nested conditionals are the visible version:
// 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);
Every one of those faults is fixable. The work starts with knowing precisely what you have.
The review checklist a real reviewer works through
A review of an AI-built app covers different ground from an ordinary code review. Much of it follows the OWASP Top 10, where broken access control sits at number one.
Can the wrong people get in
Access comes first because it is where the data and the money sit.
| Check | What to look for |
|---|---|
| Auth implementation | Is Supabase Auth (or similar) properly configured? |
| Route protection | Are authenticated routes actually protected? |
| Role-based access | If you have admin features, are they properly gated? |
| Token handling | Are JWTs stored securely (not localStorage for sensitive apps)? |
| Session management | Do sessions expire appropriately? |
Database rules, where most of these apps fail
Row-level security is the usual point of collapse. In Supabase's own words, a table in an exposed schema without RLS is readable and writable by any role that holds a grant on it.
| Check | What to look for |
|---|---|
| RLS enabled | Is RLS actually turned on for each table? |
| Policy coverage | Do policies exist for SELECT, INSERT, UPDATE and DELETE? |
| Policy logic | Can users only access their own data? |
| Admin bypass | Is there a proper service role pattern for admin functions? |
Keys and secrets
| Check | What to look for |
|---|---|
| Key exposure | Are API keys hardcoded in frontend code? |
| Environment separation | Different keys for development, staging and production? |
| Secret handling | Sensitive keys only on the backend or in serverless functions? |
| Rate limiting | Any protection against API abuse? |
What users are allowed to send in
| Check | What to look for |
|---|---|
| Input validation | Are user inputs sanitised? |
| Injection | Are database queries parameterised, so input can't become SQL? |
| XSS protection | Can users inject scripts through form fields? |
| CORS configuration | Is it properly restricted, or wide open (*)? |
| Error messages | Do errors expose internal details to users? |
Whether anyone else can work on it
| Check | What to look for |
|---|---|
| Consistent patterns | The same approach throughout, or chaos? |
| Error handling | Try/catch blocks, error boundaries? |
| Type safety | TypeScript types actually defined, or any everywhere? |
| Dead code | Unused components, commented-out blocks? |
What a reviewer should open in your Supabase dashboard
If you are paying someone, these four screens are where the money shows up.
- RLS status. In the Table Editor, check the RLS indicator on every table. Disabled is a finding.
- Policies. Go to Authentication, then Policies. Every table should have policies defined, and they should reference
auth.uid()so rows are limited to the current user. - Function permissions. Go to Database, then Functions, and look for anything marked
SECURITY DEFINER. Those run with elevated privileges, and a badly configured one can return rows the caller is not allowed to read. Each needs reading carefully. - Storage buckets. Go to Storage and check the bucket policies. Uploaded files that are public when they should not be are the same mistake in a different place.
Three faults AI-generated code keeps shipping
1. The works-locally leak. Code that behaves in development and 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. Validation that only exists in the browser. Frontend checks are a courtesy to the user, not a control. Every API endpoint has to validate its own input, database constraints need to exist as a last line of defence, and no business rule should live only in the client.
3. Database access with too much power. Check whether the app uses the service role key, now called the secret key, anywhere in the frontend. It should not: that key bypasses row-level security entirely.
Is it ready to run a business on
Live or about to launch, the same questions apply.
Running it day to day
| Item | Production requirement |
|---|---|
| Environment variables | All secrets in env vars, not code |
| Build configuration | Production builds enabled (no dev mode) |
| Error tracking | Sentry or similar configured |
| Logging | Structured logs for debugging |
| Monitoring | Uptime and performance monitoring in place |
| Deployments | Automated, with a tested rollback procedure |
| Incidents | A plan for who does what when something breaks |
Speed under real load
| Check | Why it matters |
|---|---|
| Database indexes | Slow queries will kill you at scale |
| Image optimisation | Large images destroy load times |
| Bundle size | AI tools often import entire libraries |
| Caching and CDN | Static assets and expensive operations should be cached |
| Background jobs | Slow work belongs in a queue, not the request |
| API response time | Aim for under 200ms |
Backups and getting your data back
| Requirement | Status needed |
|---|---|
| Database backups | Automated daily backups |
| Point-in-time recovery | Ability to restore to a specific moment |
| Backup testing | Actually tested restoring from a backup |
On Supabase, daily backups come with the paid plans, so a free project needs exports of its own. Point-in-time recovery is an add-on.
Whether another developer could pick it up
| Check | What to look for |
|---|---|
| Local development | Can a new developer run it locally? |
| TypeScript | Strict mode on |
| Tests | An automated test suite |
| Formatting | Prettier and ESLint, enforced by Git hooks |
| Structure | A clear folder structure and documented components |
The order the work goes in, from prototype to production
The moment to move is when you have paying users, performance problems, or developers to bring on. The prototype that got you there will not carry you further, so the work tends to run in this order.
- Triage. Audit the codebase for critical vulnerabilities, find the breaking points and performance bottlenecks, map feature dependencies and technical debt, and rank the fixes by user impact.
- Foundations. Establish a proper project structure, a monorepo where that fits, consistent patterns across the codebase, the essential infrastructure for error handling, logging and monitoring, and shared utilities and components.
- Rules for the AI. Rather than letting the tool generate whatever it likes, 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.
- Pull the backend out of the frontend. Most no-code platforms produce frontend-heavy applications with business logic scattered through the components. Move that logic into a proper API layer, with Hono for lightweight needs, behind clear data contracts, then into services, before adding database indexing, queues for background jobs, caching and rate limiting.
- Handoff. Write the documentation any developer can pick up: an architecture overview, the reasoning behind the decisions, pattern examples and anti-patterns, recorded walkthroughs and an onboarding checklist.
Payment logic belongs in a service, not a component:
// 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 is tempting to keep building features in the no-code tool while engineers rebuild underneath it. I tried exactly that on Snacker.ai and it cost more than it saved. The tools still earn their place for the right work:
| Keep using no-code for | Use production engineering for |
|---|---|
| Rapid prototyping | Core business logic |
| UI and UX iterations | Performance-critical paths |
| Marketing pages | Security-sensitive features |
| Admin interfaces | Complex integrations |
| Internal tools | Scalable infrastructure |
Who to hire for this
You want a senior full-stack developer who has done this specific job before, not a generalist learning at your expense.
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 that separate the real ones
On technical skill:
- "Walk me through how you would audit RLS policies in a Supabase project."
- "Which three security issues do you see most often in AI-generated code?"
- "What do you do when you find API keys exposed in the frontend?"
On experience:
- "Tell me about a vibe-coded app you have reviewed. What did you find?"
- "What does your process for a code security audit look like?"
- "How do you decide which issue gets fixed first?"
On communication:
- "How would you explain a critical security issue to a founder who is not technical?"
- "What documentation would I get at the end of the review?"
Signs to walk away from
- Cannot explain their process clearly
- Wants to rewrite everything straight away
- Dismissive of AI-generated code with no nuance
- No specific Supabase or database security experience
- Cannot provide references from similar work
What it takes in days and in hours
| Phase | Duration | What happens |
|---|---|---|
| Initial assessment | 1 day | Schema review, high-level code scan, critical issues identified |
| Deep audit | 2 to 3 days | Full code review, security testing, documentation |
| Fix critical issues | 1 to 2 days | Patch immediate security problems |
| Full remediation | 1 to 2 weeks | Complete fixes, testing, deployment prep |
A review with the critical fixes normally lands at three to five days. The hours run like this:
| Scope | Hours |
|---|---|
| Schema and security audit only | 8 to 12 |
| Full code review and documentation | 20 to 30 |
| Review and fix critical issues | 30 to 40 |
| Complete remediation and deployment | 60 to 80 |
A quote far below those hours means inexperience or corner-cutting. A quote far above them may be padding for a rebuild.
What should land on your desk
- An executive summary: the top issues in plain English
- A security report: every vulnerability with a severity rating
- A code quality assessment: patterns, technical debt, maintainability
- A prioritised fix list: what to fix first, second and third
- A deployment checklist: what's needed before and for production
- A recommendation: rebuild or remediate
Rebuild it or fix what is there
Sometimes the answer is to start fresh.
| Remediate when | Rebuild when |
|---|---|
| The core architecture is sound | No consistent patterns exist |
| The issues are fixable in under two weeks | The security issues are fundamental, not just missing pieces |
| The business logic is correct, just insecure | Adding 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 verdict either way. If it is a rebuild, ask for the reasons in business terms rather than a flat statement that the code is bad.
Where to start this week
- Do the five-minute check: open dev tools, find your Supabase key, see what is exposed.
- Write down what you know: the features, the integrations, and anything you have already noticed going wrong.
- Find the right person: a senior developer with production database, security and AI-code experience.
- Get the audit: three to five days for a thorough one.
- Make the call: remediate or rebuild, on the evidence.
If you would rather not read the code yourself, tell me what you have built and where it runs and I will say what I would look at first.
The aim is production readiness, not perfection. A good reviewer will tell you what good enough looks like at your stage and get you there efficiently.
Frequently asked questions
- Can I run the five-minute check without any technical skill?
- Yes. Open the app in Chrome, press F12, use the app normally, filter the network requests by supabase and copy the apikey value. Paste that key and your Supabase URL into Supabase Schema and press Fetch to see every table and what comes back.
- How long does a proper review of an AI-built app take?
- A review with critical fixes takes three to five days. The first assessment is one day, the deep audit two to three days, and patching the immediate security problems one to two days, with full remediation running one to two weeks.
- Should I rebuild the app or fix what is there?
- Remediate when the core architecture is sound and the issues are fixable in under two weeks. Rebuild when no consistent patterns exist, when the security problems are fundamental rather than missing pieces, or when remediation would cost more than a new build.
- What should I receive at the end of an audit?
- An executive summary in plain English, a security report with severity ratings, a code quality assessment, a prioritised fix list, a deployment checklist and a recommendation on rebuild or remediate. Ask for the rebuild case to be argued in business terms.
- Who is the right person to hire?
- A senior full-stack developer with 5+ years of experience, production Supabase or Postgres work, a security background and previous experience cleaning up AI-generated apps. Ask for references from similar reviews and walk away from anyone who wants to rewrite everything immediately.
Sources
- API keys | Supabase DocsSupabase, checked 18 October 2026
- Row Level Security | Supabase DocsSupabase, checked 18 October 2026
- Database Functions | Supabase DocsSupabase, checked 18 October 2026
- Database Backups | Supabase DocsSupabase, checked 18 October 2026
- OWASP Top 10:2025OWASP Foundation, checked 18 October 2026
- Welcome to Lovable | Lovable DocumentationLovable, checked 18 October 2026
- Bolt AI builder: Websites, apps & prototypesStackBlitz, checked 18 October 2026