Skip to main content
Back to Blog
Launch23 min read

How to Ship a Vibe-Coded App: The Complete 2026 Guide

Step-by-step guide to taking your AI-built app from working prototype to production. Covers security, error handling, database hardening, deployment, monitoring, and launch.

Share:

Your app works. You built it with Cursor, Lovable, Bolt, or Replit in a weekend and you're ready to put it in front of real users. The question is whether it's ready for them.

The honest answer is probably not. Not because you did anything wrong — the AI tools got you to a working prototype, which is exactly what they're designed to do. But a working prototype and a production application are different things, and the gap between them is real — but it's fixable, and worth closing before you launch.

This guide covers every step between "it works" and "it's live." Six phases, in order. Each one builds on the one before it. Skip ahead if you've already handled a phase, but don't skip a phase entirely — the issues compound.

If you want the checklist version, our five-phase production checklist is the condensed reference. This guide is the expanded walkthrough — the why behind every step, with enough detail to actually follow it.

Phase 1: Security first

Security comes first because everything else is irrelevant if your users' data is exposed. This isn't theoretical. In our audit of 50 vibe-coded apps, 94% had at least one critical security vulnerability. The same issues show up in nearly every AI-generated codebase, regardless of which tool built it.

Don't leave security for later. If someone scrapes your users' data on launch day, you're done. No recovery from that.

Move secrets server-side

Open your .env file and check every variable. Anything prefixed with NEXT_PUBLIC_ or VITE_ is visible to every user who opens browser dev tools. The only keys that should be client-side are public keys explicitly designed for browser use — Supabase's anon key, Stripe's publishable key, analytics IDs.

Everything else — database URLs, API secrets, service tokens — must move to server-side code. That means API routes, server actions, or edge functions. Not React components.

This is the single most common vulnerability in vibe-coded apps. In our audits, 86% of apps had at least one secret exposed client-side. The pattern is always the same: the AI generates environment variables with NEXT_PUBLIC_ prefixes because that's the fastest way to make the code work, and the developer doesn't know the prefix has security implications.

How to check: Open your deployed app in a browser. Open dev tools. Go to Sources and search for any of your API keys. If you find them, they're exposed. Also search the JavaScript bundle for strings like "sk_", "secret", "password", and your database host URL.

Add input validation

Every form, every API endpoint, every server action that accepts user input needs validation. Client-side validation is for user experience — it gives instant feedback. Server-side validation is for security — it's the actual gate.

Use a schema library like Zod. Define the shape once and validate on both sides:

  • Maximum field lengths (prevent someone submitting a 500,000-character string)
  • Expected formats (email addresses, URLs, dates)
  • Required vs optional fields
  • Type checking (strings where you expect strings, numbers where you expect numbers)
  • Sanitisation of HTML and special characters to prevent XSS

The reason this matters isn't just theoretical injection attacks. Without input validation, a malicious user can submit enormous payloads that crash your server, inject script tags that run in other users' browsers, or send malformed data that corrupts your database. 82% of the apps we've audited have no server-side validation at all.

Enable database security

If you're using Supabase, check Row Level Security (RLS) on every table. This is the most common vulnerability in Lovable apps, but it affects all tools. RLS disabled means any authenticated user can read or modify any other user's data. That's not a hypothetical — it means User A can see User B's private data, edit their records, or delete their account.

For each table:

  1. Enable RLS in the Supabase dashboard or via SQL: ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
  2. Add a SELECT policy that checks auth.uid() against an owner column
  3. Add separate INSERT, UPDATE, and DELETE policies — don't use a single policy for all operations
  4. Test by logging in as User A and attempting to access User B's data via the browser console
  5. Check that insert operations automatically set the user_id column to the authenticated user

Don't rely on the application code to restrict access. RLS is enforced at the database level, which means it works even if your application code has bugs.

Implement rate limiting

Every public-facing endpoint needs rate limiting. Without it, a single bot can drain your email quota, brute-force your login page, or rack up thousands of pounds in API costs.

Standard limits for a new app:

  • Form submissions: 10 per IP per day
  • Authentication attempts: 5 per IP per hour
  • API endpoints: 60 per IP per minute
  • File uploads: 5 per IP per hour

Upstash Redis with a sliding window algorithm is the standard approach for serverless apps. The free tier is sufficient for most early-stage applications. If you're on Express or a custom server, use express-rate-limit with a Redis store.

Configure security headers

Add these headers in your framework's configuration (in Next.js, this goes in next.config.ts):

  • Content-Security-Policy — Controls which scripts, styles, and resources can load. Start strict and loosen as needed.
  • Strict-Transport-Security — Forces HTTPS. Set max-age to at least one year.
  • X-Frame-Options — Set to DENY to prevent your app being embedded in iframes (clickjacking protection).
  • X-Content-Type-Options — Set to nosniff to prevent MIME type sniffing.
  • Referrer-Policy — Set to strict-origin-when-cross-origin to control what information is sent in the Referer header.
  • Permissions-Policy — Restrict access to device features like camera, microphone, and geolocation.

For the full security review, work through our 15-point security checklist. It covers everything above plus authentication route protection, CSRF prevention, and more.

Phase 2: Stability and error handling

A prototype handles one user on a good day. Production handles many users on bad days. The difference is error handling.

AI tools build exclusively for the happy path. The feature works when the API returns the right data in the right format at the right time. Production means slow networks, expired tokens, rate-limited third-party APIs, malformed responses, and users doing things you never anticipated. Without error handling, any of these crashes the app. Read why AI-generated apps keep crashing for the most common failure patterns.

Install error boundaries

A single component failing shouldn't crash the entire application. In React, error boundaries catch rendering errors and show a fallback UI instead of a white screen.

At minimum, add:

  • A global error boundary that wraps your entire app — this is your safety net when everything else fails
  • Section-level boundaries around major features (dashboard, settings, data views) — so a failure in one section doesn't bring down the rest
  • A user-friendly error page that offers a way to recover (retry, go home, contact support) — not a stack trace or "Something went wrong"

In Next.js, create an error.tsx file in your app directory for route-level error boundaries, and a global-error.tsx for the root layout. These are built into the framework — you just need to create the files and design the fallback UI.

Handle every data state

For every component that fetches data, implement four states:

  1. Loading — A skeleton screen or spinner while data is being fetched. Skeleton screens are better for perceived performance because they suggest structure rather than emptiness.
  2. Success — The expected UI with data.
  3. Empty — A helpful message when the query returns no results. "No projects yet — create your first one" is better than a blank page.
  4. Error — A clear message when the fetch fails, with a retry button. Include enough information for the user to understand what happened without showing technical details.

AI tools almost always generate only the success state. The other three are where production apps earn trust. A user who sees a clear loading state and a helpful empty state trusts the app. A user who sees a blank page and a console error doesn't come back.

Handle session expiry

What happens when a user's authentication token expires while they're using the app? In most vibe-coded apps, the answer is "it breaks." API calls start returning 401 errors, the UI shows cryptic messages, or the app crashes entirely.

Implement a session refresh mechanism:

  • Detect 401 responses in your API client (use an Axios interceptor or a fetch wrapper)
  • Attempt a silent token refresh using your auth provider's refresh token
  • If the refresh succeeds, retry the original request transparently
  • If the refresh fails, redirect to the login page with a message like "Your session expired — please sign in again"
  • Never show a raw 401 error to the user

Handle third-party failures

Your app probably depends on external services — an email provider, a payment processor, an analytics service, an AI API. Any of these can go down. When they do, the core app should keep working.

Use try/catch around every external call. If analytics fails, log the error silently and keep going. If the email provider is down, queue the email for retry or use Promise.allSettled to handle partial failures. If a non-critical feature fails, degrade gracefully rather than crashing.

The principle is simple: distinguish between critical and non-critical failures. Payment processing failing is critical — show the user an error. Analytics failing is not — log it and move on.

Set up error monitoring

Install Sentry or a similar service. This gives you:

  • Real-time alerts when errors occur in production
  • Stack traces showing exactly where and why something broke
  • User context so you can see which users are affected
  • Release tracking so you know which deployment introduced a bug
  • Session replay (optional) to see exactly what the user did before the error

Don't rely on users reporting errors. Most won't. They'll just leave. Error monitoring means you know about problems within minutes instead of days, and you can fix them before they affect more users.

The free tier of Sentry is sufficient for most new apps. Set it up before you launch, not after you start getting complaints.

Phase 3: Database and performance

Your app works with 10 rows of test data. It needs to work with 10,000 rows of real data and 100 concurrent users.

Add database indexes

Check every query your app makes. Any column used in a WHERE clause, ORDER BY, or JOIN needs an index. Without indexes, every query scans the entire table, getting slower as the data grows.

Common columns to index:

  • user_id (used in almost every query to filter by owner)
  • created_at (used for sorting and date range filters)
  • email (used for login and lookups)
  • status and any type or category column used for filtering
  • Composite indexes for queries that filter on multiple columns together

In Supabase, you can add indexes via the SQL editor:

CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_created_at ON posts(created_at DESC);

Test the impact by running EXPLAIN ANALYZE on your queries before and after adding indexes. You should see "Index Scan" instead of "Seq Scan" in the output.

Implement pagination

If your app displays lists of data, don't fetch everything at once. Implement pagination or infinite scroll with a reasonable page size (20-50 items). This applies to both the API response and the database query — use LIMIT and OFFSET at the database level.

For larger datasets, consider cursor-based pagination (using the last item's ID or timestamp) instead of offset-based pagination. Offset pagination gets slower as the page number increases because the database still has to skip all the preceding rows.

Use connection pooling

If your app is serverless (Vercel, Netlify, Cloudflare Workers), each function invocation opens a new database connection. Without pooling, you'll exhaust connection limits under even moderate load. PostgreSQL's default limit is 100 connections, and serverless functions can burn through that in seconds during a traffic spike.

For Supabase, use the connection pooler URL (port 6543) rather than the direct connection URL (port 5432). For other PostgreSQL providers, use PgBouncer or a similar pooling layer. This is a one-line change — swapping the connection string — that prevents an entire category of production failures.

Optimise expensive queries

Look for these common patterns in AI-generated code:

  • N+1 queries — Fetching a list of items, then making a separate query for each item to get related data. The AI does this because it's the simplest approach. Use joins or batch fetches instead.
  • SELECT * — Fetching every column when you only need three. Only fetch the columns you actually use, especially for tables with large text or JSON columns.
  • Missing pagination — Queries that return unbounded results. Add LIMIT to every query that could return more than a few dozen rows.
  • Redundant queries — The same data fetched multiple times on the same page. Use caching, shared state, or React Query / SWR to deduplicate requests.
  • Unoptimised full-text search — If your app has search, make sure it's using proper text search indexes rather than LIKE '%query%' which scans every row.

Check your bundle size

AI tools add dependencies freely. Run your framework's bundle analyser (@next/bundle-analyzer for Next.js) and look for:

  • Large libraries imported for a single function (moment.js for date formatting, lodash for one utility)
  • Duplicate dependencies (two different icon libraries, multiple HTTP clients)
  • Client-side imports of server-only packages

Remove what you don't need. Replace moment.js with date-fns or native Intl. Replace lodash with individual utility functions. Check if you're importing entire icon libraries when you only use a handful of icons. Every unnecessary kilobyte slows down your initial page load.

Phase 4: Deployment and monitoring

Don't deploy by copying files or running manual commands. Set up a pipeline that handles this automatically and reliably.

Put your code in version control

If your code only exists in Lovable, Bolt, or Replit, export it to a Git repository on GitHub. This gives you:

  • History of every change, so you can see what changed and when
  • The ability to revert if a deployment breaks something
  • A foundation for CI/CD, code review, and collaboration
  • Branch-based workflows where you can test changes before they reach production

Every tool has an export mechanism. Lovable and Bolt can push to GitHub directly. Replit has Git integration. If you're using Cursor, you're likely already in a local repository — push it to a remote.

Set up CI/CD

Every push to your main branch should automatically:

  1. Run linting (catches code style issues and common errors)
  2. Run type checking with tsc --noEmit (catches type mismatches before they reach users)
  3. Run tests (catches regressions)
  4. Build the application (catches build-time errors)
  5. Deploy if everything passes

If any step fails, the deployment stops. This prevents broken code from reaching production.

Vercel, Netlify, and Railway all provide this out of the box when connected to a Git repository. For custom setups, GitHub Actions is free for public repositories and has generous free minutes for private ones.

Configure environments

You need at least two environments:

  • Preview/staging — Deployed from pull requests or feature branches. Used for testing before changes reach production. Every pull request should get its own preview URL.
  • Production — Deployed from the main branch. This is what real users see.

Each environment should have its own set of environment variables. Different API keys, different database URLs, different third-party service configs. Never share secrets between environments. A test payment processed against your production Stripe account is an expensive mistake.

Choose the right hosting

Match your hosting provider to your stack:

  • Vercel — Best for Next.js and other React frameworks. Built-in edge functions, image optimisation, and analytics.
  • Railway — Best for Node.js backends and full-stack apps that need more control over the server.
  • Fly.io — Best for Docker-based deployments and custom setups that need specific runtime environments.
  • Cloudflare Pages — Best for static sites and edge-first architectures.

For tool-specific deployment details, follow the guide for your tool:

Connect your domain

Buy a domain and connect it to your hosting provider. This takes 15 minutes and immediately makes your app look legitimate. Configure:

  • HTTPS enforced (no HTTP access — all traffic encrypted)
  • DNS records pointing to your host (usually an A record or CNAME)
  • www redirect (choose one canonical URL and redirect the other to it)
  • Email forwarding or inbox set up for your domain (hello@yourdomain.com is more trustworthy than a personal Gmail address)

Set up uptime monitoring

Use a free service like UptimeRobot or Better Stack to ping your app every few minutes. Configure alerts to notify you by email, SMS, or Slack when it goes down. You should know about downtime before your users do.

Also monitor your key endpoints — not just the homepage. If your API is down but the landing page still loads, an uptime check on the homepage won't catch it.

Set up basic logging

Your application should log:

  • Errors with full context (what happened, which user, which page, what input caused it)
  • Authentication events (logins, failed attempts, password resets, token refreshes)
  • Important business events (signups, purchases, key feature usage, plan upgrades)
  • Performance metrics (slow queries, long API response times)

Don't log personally identifiable information (emails, passwords, full names, IP addresses) to avoid GDPR compliance issues. Send logs to a searchable service like Axiom, Logtail, or Datadog — not just the console, which disappears when the serverless function terminates.

Phase 5: Launch preparation

The technical foundation is solid. Now prepare the product for real humans.

Build a landing page

Your app needs a front door that isn't a login screen. When someone visits your URL for the first time, they should immediately understand what the app does and why they should care.

A landing page should include:

  • One sentence explaining what the app does — above the fold, no scrolling required
  • Who it's for — Be specific about the target user. "For freelance designers who need to track client feedback" is better than "For everyone"
  • A screenshot or demo — Show the app in action. A 30-second video walkthrough is even better.
  • A clear call to action — Sign up, start free trial, request access. One button, not three.
  • Social proof — Even one testimonial, a user count, or a "Featured on Product Hunt" badge.

This page is often the first thing a potential user sees. It needs to load fast (under 2 seconds), work on mobile, and immediately communicate value. Spend more time on this page than on any feature.

Install analytics

You need data from day one. Without it, you're making decisions based on guesses. At minimum track:

  • Page views and unique visitors (is anyone coming to the site?)
  • Sign-up conversion rate (of people who visit, how many sign up?)
  • Key feature usage (which features are people actually using?)
  • Retention (are people coming back after day one?)
  • Error rates in production (how often are things breaking?)

PostHog, Plausible, or Google Analytics all have free tiers that are more than sufficient at launch. PostHog also includes session replay and feature flags, which become valuable as you grow.

Configure SEO basics

Search is a long-term growth channel, but the basics take an hour to set up and pay off for months:

  • Title tags and meta descriptions on every page — unique, descriptive, under 60 characters for titles and 160 for descriptions
  • Open Graph images for social sharing — so links to your app look good on Twitter, LinkedIn, and Slack
  • A sitemap submitted to Google Search Console — so Google knows what pages exist
  • Canonical URLs to prevent duplicate content issues
  • Mobile-responsive design (Google indexes mobile-first)
  • Structured data (JSON-LD) for rich search results if applicable

Test on real devices

Open your app on a real phone — not browser dev tools pretending to be a phone. Walk through every flow:

  1. Sign up / sign in
  2. Use every core feature
  3. Edit profile / settings
  4. Complete a key action (whatever the app's primary value is)
  5. Log out

Test on both iOS Safari and Android Chrome. These two browsers cover the vast majority of mobile users and they render things differently. Pay particular attention to form inputs (autocomplete, keyboard types), touch targets (buttons need to be at least 44px), and scroll behaviour.

Add legal pages

If you collect any user data — and you almost certainly do — you need:

  • Privacy policy — What data you collect, how you use it, who you share it with, and how users can request deletion. Required under GDPR if you have any UK or EU users.
  • Terms of service — The rules for using your app, liability limitations, and account termination conditions.
  • Cookie consent — If you use analytics or any tracking cookies, you need a consent banner for EU/UK users.

AI tools can draft these, but have a human review them. They need to be accurate for your specific app and jurisdiction.

Set up a feedback channel

Users need a way to reach you when something doesn't work. A simple contact form, a support email address, or even a link to a Google Form. The channel matters less than its existence. In the early days, every piece of user feedback is gold — it tells you what to fix and what to build next.

Phase 6: Go live

The foundation is in place. Time to launch.

Pick a launch date

Having a deadline forces you to stop building and start shipping. Choose a date 1-2 weeks out. Tell someone about it so you're accountable. Write it on a sticky note and put it on your monitor. The date isn't arbitrary — it's the forcing function that prevents endless tinkering.

Prepare launch assets

Before launch day, have these ready:

  • 3-5 screenshots of the app in action (real usage, not empty states)
  • A short demo video (even 30 seconds — a Loom recording works perfectly)
  • A clear description: what it does, who it's for, why it exists (write this before launch day, not on it)
  • A link to your landing page
  • Responses pre-drafted for common questions ("How is this different from X?" "What's your pricing?" "Is my data secure?")

Choose one platform

Don't launch everywhere at once. Pick one platform and put your full effort into it:

  • Product Hunt — Best for consumer and developer tools. Requires preparation — build your profile, get followers, and engage with the community before your launch. See our Product Hunt launch guide for the full strategy.
  • Hacker News — Best for technical products. Write a genuine "Show HN" post. Keep it factual and let the product speak for itself.
  • Reddit — Best if there's an active subreddit for your app's problem domain. Be a genuine community member first.
  • Twitter/X — Best if you've been building in public and have an audience. A thread documenting the journey works well.

Launch day

On the day:

  1. Deploy the final version early in the morning
  2. Verify everything works in production (sign up, core flow, payments if applicable)
  3. Post to your chosen platform
  4. Share with your email list or waitlist
  5. Monitor error tracking and analytics in real time
  6. Respond to every comment, question, and piece of feedback within minutes

The first week

Launch day gets attention. The first week builds momentum:

  • Respond to every user who reaches out — speed matters, personal responses matter more
  • Fix bugs immediately (this is where error monitoring pays off)
  • Track which features people use and which they ignore
  • Write down every piece of feedback — you'll use it to prioritise what to build next
  • Send a follow-up email to everyone who signed up, asking what they think

If nobody shows up, don't panic. Read why your vibe-coded app has zero users for a practical playbook on getting your first 100 users. The absence of users on day one is normal — what matters is what you do about it.

How long does this take?

For a typical vibe-coded app, working through all six phases takes 1-3 weeks of focused effort. The breakdown:

  • Phase 1 (Security): 2-4 days
  • Phase 2 (Stability): 2-3 days
  • Phase 3 (Database): 1-2 days
  • Phase 4 (Deployment): 1-2 days
  • Phase 5 (Launch prep): 2-3 days
  • Phase 6 (Go live): 1 day plus the first week of monitoring

The exact timeline depends on your app's complexity and how many issues the AI tool left behind. A simple Lovable app with a straightforward database might take a week. A complex Cursor-built SaaS with payment processing and multiple user roles might take three weeks.

Use our production checklist alongside this guide to track your progress phase by phase.

The cost

Most apps cost between £500 and £5,000 to get production-ready if you hire help. The range depends on complexity, how many security issues exist, and whether the architecture needs restructuring or just hardening. Simple security fixes on a well-built Lovable app sit at the lower end. Comprehensive overhauls of complex Cursor or Replit projects sit at the upper end.

For a detailed breakdown by tool and approach — including DIY, freelancer, and agency costs — read how much it costs to fix a vibe-coded app.

Getting help

If you want to know exactly where your app stands before committing time or money, request a free audit. We'll review your app and send a prioritised list of what needs fixing — no commitment, no sales pitch. Just an honest assessment of where the gaps are.

If you've been through this guide and want someone to handle the hardening work, tell us about your project. We'll send a scoped proposal within 48 hours with a fixed price and clear deliverables.

Every step in this guide exists because we've done it dozens of times for apps built with Lovable, Cursor, Bolt, Replit, and v0. The gap between "it works" and "it's live" is real. But it's a known gap, with known fixes.

Get articles like this in your inbox

Practical tips on shipping vibe-coded apps. No spam.

Want to know where your app stands?

Get a free 5-point security snapshot from our dev team — no strings attached.