Deployment & Ops
Hosting & Deployment
Platform Selection
Pick a host that matches your stack and your tolerance for surprise bills.
Best for Next.js. Instant previews. Git push to deploy.
- Free (Hobby): 100GB bandwidth, no build-minute allowance
- Pro: $20/seat/mo (each additional seat is $20/mo)
- Granular overage billing for bandwidth and functions
Best for full-stack with Database. Usage-based pricing.
- One-time $5 trial credit (Hobby is a $5/mo paid plan)
- Typical Next.js app: $8-15/mo
- Built-in PostgreSQL, Redis, cron jobs
Unlimited bandwidth. 100K requests/day free. Built-in DDoS protection.
- Free: unlimited sites, unlimited bandwidth
- Workers: 100K req/day free, $5/mo for 10M
- D1 database, R2 storage (no egress fees)
Free tier is now credit-based (~300 credits, roughly 15GB). Good for static sites and JAMstack.
Good for StaticDeploy Docker containers globally. Pay-as-you-go (the old free-VM allowance was removed; new users get a short trial).
Docker / Global EdgeSelf-hosted Heroku on your own VPS ($5/mo Hetzner).
Advanced / Self-HostIn March 2024 a developer woke up to a $104,000 Netlify bill after a DDoS attack repeatedly requested a single ~3.44 MB file, racking up roughly 190 TB of bandwidth. Netlify ultimately waived the charge, but you can't count on that. Lesson: Prefer platforms with traffic protection and spending caps (Cloudflare has both by default). Set up billing alerts before you deploy.
Branch Protection Rules
- Require PR reviews: No direct pushes to
main. All changes go through pull requests. - Require status checks: PRs can't merge until CI tests pass.
- Require linear history: Force rebase/squash merges for clean git log.
Rollback Procedures
- Vercel: Every deploy gets a permanent URL. Click "Promote to Production" on any previous deployment.
- Netlify: One-click rollback in the Deploys dashboard.
- Railway/Render:
git revertthe problematic commit and push.
Vercel/Netlify create a new URL for every commit. If production breaks, you can instantly rollback to the previous deployment. Test every PR with a unique preview URL before merging.
Monitoring & Analytics
Monitoring
Know when things break before your users tell you.
Error tracking. 5K errors/mo free. Seer AI Debugger auto-diagnoses root causes with 94.5% accuracy. MCP Server integration for Cursor.
Must HaveSession replay. Watch exactly what users did before a bug occurred. See their clicks, scrolls, and console errors.
DebuggingLog aggregation and incident management. Status pages. Better UI than Datadog at a fraction of the cost.
LogsChecks if your site is up every 5 minutes. Sends alerts via email, Slack, or SMS. 50 monitors free.
UptimeAnalytics
Free: 1M events/month. Paid plans scale with usage.
- Combines product analytics, session replay, feature flags, and A/B testing into a single platform
- Self-hostable for complete data ownership and privacy compliance
- Official React and React Native SDKs available
- Setup time: roughly 10 minutes
$9/month for 10K pageviews. Straightforward, transparent pricing.
- Privacy-friendly and GDPR-compliant out of the box with no cookies required
- Tracking script weighs roughly 1KB compared to Google Analytics at roughly 45KB
- No consent banner needed in most EU jurisdictions
- Setup time: roughly 2 minutes (single script tag)
Free with generous limits on data collection and reporting.
- Most feature-rich analytics platform, but all data is sent to Google servers
- Requires a cookie consent banner to comply with EU regulations
- Configuration can be complex for accurate event and conversion tracking
- Setup time: roughly 10 minutes
Errors: Sentry (catches bugs you didn't know existed). Analytics: Plausible (privacy-friendly, simple) or PostHog (more features, self-hostable). Uptime: UptimeRobot (get alerted when your site goes down). Total cost: $0-$40/month depending on tier.
Payments & Billing
Anyone can send a fake POST request to your webhook endpoint. You MUST verify the Stripe-Signature header using the signing secret.
Webhook Verification Pattern
// Correct: Verify signature with raw body
const event = stripe.webhooks.constructEvent(
req.rawBody, // Must be raw, not parsed
req.headers['stripe-signature'],
process.env.STRIPE_WEBHOOK_SECRET
);
If you use Express with express.json() before your webhook route, the body gets parsed and signature verification fails. Use express.raw() for the webhook endpoint specifically.
Idempotency
Webhooks can arrive 2+ times. Check event.id in your DB before processing to prevent double charges. Store processed event IDs and skip duplicates.
const existing = await db.webhookEvent.findUnique({
where: { stripeEventId: event.id }
});
if (existing) return; // Already processed
Test Mode Keys
Use sk_test_... for dev. sk_live_... for prod. Never mix them.
4242424242424242Success4000000000009995Declined4000002500003155Requires 3D SecureStripe Test-to-Live Checklist
Switching from test mode to live mode is one of the most common launch failures. Follow this checklist exactly.
sk_test_ and pk_test_ to sk_live_ and pk_live_ in environment variablesSubscription State Machine
Handle all subscription states, not just "active."
activeFull access
trialingFree trial period
past_duePayment failed, retrying
canceledAccess until period end
unpaidAll retries exhausted
Smart Retries: Stripe's intelligent retry logic recovers 15-25% more failed payments than fixed schedules. Enable it in the Stripe dashboard under Billing settings. Card Account Updater automatically refreshes expired cards for existing subscriptions, which industry estimates suggest can cut hard declines by up to 30-50%.
Dunning Configuration: Configure up to 4 reminder emails per invoice before marking as unpaid. Each email is another chance to recover revenue. Businesses using Stripe's full dunning setup recover significantly more than basic email reminders alone.
Centralized Pricing Config
Keep all pricing logic in a single file. Never scatter price IDs across components.
// lib/pricing.ts - Single source of truth
export const PLANS = {
free: {
name: "Free",
price: 0,
limits: { projects: 3, storage: "500MB" },
},
pro: {
name: "Pro",
price: 29,
stripePriceId: process.env.STRIPE_PRO_PRICE_ID!,
limits: { projects: 50, storage: "50GB" },
},
} as const;
// Use everywhere:
if (user.plan === "free" &&
projectCount >= PLANS.free.limits.projects)
return "Upgrade to Pro";
Why this matters: AI scatters hardcoded price IDs like price_1234abc across your codebase. When you change plans, you miss half of them. Centralizing means one file to update for pricing changes.
Subscription Cancellation Flow
AI never implements cancellation. Users get trapped. App stores reject your app.
User Clicks Cancel
Show a confirmation with what they'll lose
Grace Period
Access continues until billing period ends
Downgrade
Revert to free tier, keep data intact
Win-Back Email
Send 7 days later with a discount offer
EU law now requires an easy "cancel contract" button so ending a subscription is as simple as starting one (the withdrawal-button requirement applies from June 2026). In the US, the FTC's "click-to-cancel" rule was vacated by a federal appeals court in 2025, though the agency has moved to revive a similar rule. Either way, design cancellation to be as easy as signup: if users sign up in 2 clicks, cancellation shouldn't require calling support or navigating 5 pages.
Payment Platforms Compared
| Platform | Fees | Best For | Key Feature |
|---|---|---|---|
| Stripe | 2.9% + $0.30 | Most apps | Best API, docs, and AI training data |
| LemonSqueezy | 5% + $0.50 | Solo developers | Merchant of Record (handles tax/VAT) |
| Paddle | 5% + $0.50 | B2B SaaS | Merchant of Record, no tax headaches |
| Polar | 5% + $0.50 | Open source | Merchant of Record, built for developers |
Merchant of Record (MoR): LemonSqueezy and Paddle handle sales tax and VAT globally so you don't have to. Higher fees but zero tax compliance work. Choose MoR if you sell to international consumers.
LemonSqueezy was acquired by Stripe in July 2024. Some users have reported errors and delayed payouts since the acquisition, creating uncertainty about its future. Evaluate carefully before committing.
Revenue Threshold: At $50K/month revenue, the fee difference between MoR (5% + $0.50) and Stripe (2.9% + $0.30) exceeds $1,000/month. Start with MoR for simplicity, migrate to Stripe as revenue scales.
Operations & Incident Response
The Emergency Handbook
When something breaks, work the runbook. Don't improvise.
- 1Check hosting provider status page (Vercel, Supabase, etc.).
- 2Check deployment logs in your dashboard.
- 3Verify database connectivity (Supabase dashboard).
- 4Check third-party API status (Stripe, OpenAI).
- 5Action: Paste error logs into AI with "My app is down. Here are the logs. Diagnose."
- 6Communicate immediately: Tell your users "We're aware and investigating" even before you have answers. Silence destroys trust faster than downtime.
- 1Thank the reporter immediately.
- 2Assess severity: Is user data exposed?
- 3Supabase: Check Row Level Security (RLS) policies immediately.
- 4Patch critical issues immediately (take app offline if needed).
- 5Document everything for potential GDPR disclosure (72h window).
- 1Identify source in billing dashboard (Compute? Database? Storage?).
- 2Set hard spending limits immediately.
- 3Add rate limiting to all external API calls.
- 4Contact support and request one-time forgiveness (often works for first-time incidents).
When to Hire a Developer
Most vibe-coded apps reach an inflection point around $100K MRR where professional engineering becomes necessary. Signs: bugs take longer to fix than features take to build, every change breaks something else, or scaling issues are costing you customers.
Green Flags in Candidates
- Wants to improve the existing codebase incrementally
- Asks about tests and CI/CD first
- Comfortable with AI-assisted development
Red Flags in Candidates
- Wants to rewrite everything from scratch
- Dismisses AI coding tools entirely
- Proposes switching to microservices immediately
Use Repomix to generate AI-readable codebase summaries. Run it before hiring to create a comprehensive technical overview that new developers can reference. Paste the output into an AI chat and ask it to generate onboarding docs.
Must-Have VS Code Extensions
Error LensInline error highlighting - see issues without hoveringPretty TypeScript ErrorsHuman-readable TypeScript error messagesESLintAuto-fix code quality issues on saveTailwind IntelliSenseAutocomplete for Tailwind classesPrismaSchema syntax highlighting and autocompletionREST ClientTest API endpoints directly from VS CodeBrowser DevTools Tricks
Skills every vibe coder should have, even without deep technical knowledge.
Look for red errors after any user action. Copy the full error and paste it into your AI tool for diagnosis.
Filter by "Fetch/XHR" to see API calls. Red entries are failed requests. Click to see the full error response body.
Check Local Storage, Session Storage, and Cookies. Security check: search for any API keys or tokens stored here that shouldn't be client-visible.
Record a user interaction to identify slow JavaScript execution and long tasks blocking the UI.
Domain Registrars
Where you buy your domain matters. Pricing transparency and renewal costs vary significantly.
Best overall value. Registration and renewal prices are identical, so no surprise markup at renewal time. Includes free WHOIS privacy, SSL certificates, and email forwarding. Consistently well reviewed by users for pricing transparency.
Charges exactly at wholesale cost with zero markup. Includes enterprise DNS, DNSSEC, and DDoS protection. Requires Cloudflare nameservers and supports fewer TLDs than competitors.
Attractive initial pricing (e.g., $8.88 first year for .com), but renewal prices jump significantly (e.g., $16/year). Watch for pre-checked upsell boxes at checkout. Solid 24/7 support.
Never buy domains from your hosting provider. They're usually overpriced and complicate migration if you switch hosts. Keep domain registration separate from hosting.
npm audit and pip audit Decoded
Your dependencies have known vulnerabilities. These tools tell you which ones and how severe they are.
npm audit checks every package in your project against a database of known security vulnerabilities and reports what it finds.
| Severity | Meaning | Action |
|---|---|---|
| Critical | Remote code execution possible | Fix immediately, same day |
| High | Significant damage if exploited | Fix within 1-2 days |
| Moderate | Exploitable under certain conditions | Plan to fix within 1-2 weeks |
| Low | Minor, unlikely to be exploited | Fix at convenience |
# View the vulnerability report
npm audit
# Apply safe, non-breaking patches only
npm audit fix
# Apply ALL fixes including breaking changes (use carefully)
npm audit fix --force
# Python equivalent
pip audit
Run npm audit weekly. Fix Critical and High severity issues immediately. Moderate issues can wait for the next sprint. Always run npm audit fix --dry-run before using --force to preview what will change.
Dependabot vs Renovate
Automated tools that create pull requests when your dependencies have updates available.
| Feature | Dependabot | Renovate |
|---|---|---|
| Setup | Built into GitHub, enable with clicks | Requires GitHub App install + config file |
| PR Grouping | Basic | Advanced (group by type, scope, etc.) |
| Auto-merge | With config | With config |
| Customization | Limited | Highly configurable |
| Best For | Most projects | Large monorepos |
Recommendation: Just use Dependabot. It's free, built into GitHub, and requires zero setup beyond a config file. Enable it in your repo's Settings under Security. Renovate is more powerful but overkill for most vibe-coded projects. An empirical study of Dependabot found that automated dependency updates measurably reduce how long projects stay exposed to known vulnerabilities.
Scaling Basics
Do not optimize for 100K users when you have 100. Ship first, scale when the pain is real.
| Stage | Users | What to Add |
|---|---|---|
| 0-100 | Just shipping | Nothing. Focus on building. |
| 100-1K | Fixing N+1 queries | Database indexes, connection pooling |
| 1K-10K | Adding infrastructure | CDN, caching (Redis), background jobs |
| 10K+ | Real engineering | Read replicas, auto-scaling, load balancing |
Redis caching stores frequently accessed data in memory instead of hitting your database every time. CDN setup (Cloudflare's free tier works) distributes static files worldwide. Database indexing works like a book's table of contents, add indexes on columns you frequently search, filter, or sort by.
Feature Flags at the Simplest Level
Deploy code to production but keep new features hidden until you're ready to launch them.
The simplest approach uses an environment variable. No libraries needed.
# In your .env file
NEXT_PUBLIC_FEATURE_NEW_DASHBOARD=true
// In your component
{
process.env.NEXT_PUBLIC_FEATURE_NEW_DASHBOARD === 'true' &&
<NewDashboard />
}
This costs nothing but requires a redeploy to toggle. When you need real-time toggling without redeploying, consider these options:
Open source. Free up to 50K requests/month. Best for vibe coders who need runtime toggling.
Enterprise-grade. Starts at $12/month. Overkill for most indie apps but powerful for A/B testing and gradual rollouts.
Database Migrations Golden Rule
Always use migration files (Prisma Migrate, Supabase CLI migrations). Make the change in your migration tool, test locally, test on staging, then deploy to production.
- Dropping columns before migrating data to a new location. This causes permanent data loss.
- Renaming columns without updating all queries that reference the old name. Everything breaks silently.
- Changing column types without a data conversion step. Existing data may be truncated or lost.
- Running
prisma migrate reseton production. This deletes all data.
Add new column with a default value
Backfill data from old column to new
Remove old column after verification
Mobile Deployment
Expo Ecosystem Overview
Expo is the React Native framework that makes mobile development accessible to vibe coders.
Expo releases SDK updates on a quarterly cadence, tracking every other React Native release. The New Architecture (Fabric renderer, TurboModules) has been enabled by default since SDK 52, meaning all new projects get modern rendering performance out of the box.
- expo-router: File-based routing (every file in /app becomes a route)
- expo-notifications: Push notification handling for iOS and Android
- expo-camera: Camera access with barcode scanning
- expo-image: Performant image component with caching
- SDK 52: New Architecture default, stable expo-video, WinterCG Fetch API
- SDK 53: New Architecture default for all projects (not just new ones)
- SDK 54: Precompiled XCFrameworks cut clean iOS build times significantly
EAS Build, Submit, and Update
Expo Application Services (EAS) handles cloud builds, store uploads, and over-the-air updates.
EAS BuildCloud-based builds without Xcode or Android Studio locally
15 iOS + 15 Android builds/month. Low priority queue (60-120+ min wait). 45-minute build timeout.
$225 build credit included. High priority queue (significantly faster). 2 concurrent builds. 50K update MAUs.
EAS SubmitUpload builds directly to App Store and Google Play
# Submit to iOS App Store
npx eas submit --platform ios
# One-command build + sign + submit to TestFlight
npx testflight
Works from Windows and Linux (no Mac required for cloud builds). iOS submissions appear in TestFlight after 10-15 minutes of Apple processing.
EAS UpdateOver-the-air JS updates without app store review
Push bug fixes in minutes instead of waiting for app store review. The critical constraint: OTA updates can only change JavaScript and assets. You can't add native libraries, change permissions, modify bundle identifiers, or update native dependencies through OTA. Those require a full rebuild and store submission.
Expo Go is a playground for quick prototyping only. Expo's own documentation states it's "not useful for building production-grade projects." It's positioned for students and learners.
Development builds are required for: push notifications, custom native modules, production-accurate testing, and custom splash screens/icons.
# Build locally for iOS
npx expo run:ios
# Or build in the cloud via EAS
npx eas build --profile development
Never ship an app that only works in Expo Go. Once built, development builds only need rebuilding when native code or configuration changes.
App Store Submission Timelines
Build buffer time into every launch plan. First submissions take longer.
- Typical review: 24-48 hours
- First submission: Can take up to 7 days (or longer)
- Submit Tuesday or Wednesday for shortest queue times
- Holiday shutdown (Dec 20-26) causes additional delays
- Standard review: 24-72 hours
- Complex categories (fintech, healthcare): 4-7 days
- Sometimes approved same day
- New developer accounts get extra scrutiny
Buffer 3-5 business days before any launch date. For first-time submissions, budget 1-2 weeks for Apple (including a likely rejection and resubmission) and 3-7 days for Google Play.
Developer Program Costs
- Required for App Store distribution and TestFlight
- Auto-renewable subscription
- Small Business Program: 15% commission (down from 30%) if under $1M/year
- Organization accounts require a DUNS number
- No annual fee
- 15% commission on first $1M in earnings, 30% above that
- New organization accounts now also require DUNS numbers
Hidden costs: Apple requires a Mac for Xcode (iOS development is Mac-only for local builds), both stores take 15-30% commission on in-app purchases, and you need a privacy policy URL before submitting.
A DUNS Number (nine-digit business identifier from Dun & Bradstreet) is required for enrolling as an organization on both Apple and Google platforms. Your business must be a legal entity.
6 days (US companies)
4-8 weeks
$229 for 1-2 business days
Start this process EARLY. It can block your entire launch. If you're a solo builder, enroll as an individual ($99/year) and skip the DUNS process entirely. Your personal name will appear as the developer.
Top 10 App Store Rejection Reasons
Apple rejected approximately 25% of submissions in 2024. These are the rejections that hit vibe-coded apps hardest.
- 1
App Completeness (Guideline 2.1): Crashes, broken links, placeholder content. Guideline 2.1 accounts for over 40% of App Store rejections. AI-generated code often handles only the "happy path" and crashes on edge cases.
- 2
Minimum Functionality (Guideline 4.2): Apps that are essentially a WebView wrapper get rejected. Apple requires native navigation, push notifications, offline support, and device-specific features.
- 3
In-App Purchase (Guideline 3.1.1): Using Stripe for digital goods instead of Apple's StoreKit. In the US, after the 2025 Epic v. Apple ruling, apps may link out to external payment for digital goods (Apple may still charge a commission on those external-link purchases). Outside that US carve-out, Apple requires StoreKit and takes 15-30%. Physical goods can always use external payment.
- 4
Spam/Copycat (Guideline 4.3): Too similar to existing apps without meaningful differentiation. AI tools generate similar-looking apps by design. Apple's automated systems detect duplicate code structures.
- 5
Data Collection (Guideline 5.1.1): Missing or inaccurate privacy policy. You need the privacy policy both in App Store Connect metadata and inside the app itself.
- 6
Sign in with Apple: Required if you offer any third-party social login (Google, Facebook, etc.). Either add Sign in with Apple or remove all social logins.
- 7
Permission Strings (ITMS-90683): Must explain why you need camera, location, or microphone access. Not "This app requires camera access" but "This app needs camera access to let you take profile photos."
- 8
App Tracking Transparency: Must show ATT prompt before any tracking. If the user denies tracking, you must stop all tracking immediately. No nagging or manipulation.
- 9
Missing Demo Account: Reviewers need working login credentials to test your app. Provide them in App Store Connect under App Review Information.
- 10
Account Deletion: Users must be able to delete their account from within the app. Linking to a website or email doesn't count. AI-generated auth flows almost never include this.
TestFlight: Internal vs External Testing
Use internal testing for your team, external for beta users.
| Feature | Internal Testing | External Testing |
|---|---|---|
| Tester Limit | Up to 100 (must be team members) | Up to 10,000 (anyone with a link) |
| Apple Review | Not required, instant distribution | Required for first build of each version (1-2 days) |
| Build Expiration | 90 days | 90 days |
| Best For | Your team, fast iteration | Beta programs, wider user feedback |
Always start with internal testing for zero-delay feedback. The 100-tester limit is generous for most indie scenarios. For wider testing, use external TestFlight with a public link where testers can join anonymously.
Push Notifications Setup
Requires a development build. Push notifications don't work in Expo Go.
Expo handles both FCM (Android) and APNs (iOS) configuration, abstracting away most of the platform-specific complexity. However, several common issues trip up developers.
- Missing FCM credentials in your project configuration
- APNs certificate expired or misconfigured
- User didn't grant notification permission (handle gracefully)
- Sending to an invalid or stale push token
- Test on real devices (simulators don't support push notifications reliably)
- Use development builds, not Expo Go
- Verify tokens are fresh before sending
- Handle the case where users deny permission without crashing