Free Tier Experience
The free tier is your acquisition funnel. If it sucks, nobody upgrades.
| Severity | Check | Status |
|---|---|---|
| Critical | Free users can sign up and access the core feature without payment | ⬜ |
| Critical | Free tier provides real standalone value (not a crippled product) | ⬜ |
| Critical | Free tier limits are clearly communicated before the user hits them | ⬜ |
| Warning | Upgrade prompts appear at natural friction points, not random popups | ⬜ |
| Warning | Free user can understand the value of Pro from the upgrade prompt | ⬜ |
| Info | Free users see social proof or testimonials on upgrade page | ⬜ |
Free tier smoke test:
- Create a new account (don't use your admin account)
- Complete the core workflow as a free user
- Hit at least one free tier limit and verify the upgrade prompt
- Verify that free users cannot access Pro-only features via URL manipulation
Payments & Billing
Payment bugs lose you money and trust. Test thoroughly.
| Severity | Check | Status |
|---|---|---|
| Critical | Stripe is in LIVE mode (not test mode) for production | ⬜ |
| Critical | Checkout flow completes successfully with a real payment method | ⬜ |
| Critical | Webhook handler verifies Stripe signature before processing | ⬜ |
| Critical | checkout.session.completed webhook updates user plan to "pro" | ⬜ |
| Critical | customer.subscription.deleted webhook reverts user plan to "free" | ⬜ |
| Warning | invoice.paid webhook handles recurring payments correctly | ⬜ |
| Warning | invoice.payment_failed webhook handles failed payments (notify user) | ⬜ |
| Warning | Customer portal link works — user can manage/cancel subscription | ⬜ |
| Warning | Duplicate webhook events are handled idempotently (no double-upgrades) | ⬜ |
| Info | Pricing page clearly shows what's included in each plan | ⬜ |
| Info | Receipt emails are sent (Stripe handles this by default) | ⬜ |
Payment smoke test:
- Complete a real checkout (use Stripe's $0.50 test product if needed)
- Verify user plan updated in database after successful payment
- Cancel subscription via Stripe dashboard, verify plan reverted
- Check Stripe dashboard for webhook delivery — any failures?
Security
One security bug can kill your product. Check every item.
| Severity | Check | Status |
|---|---|---|
| Critical | All API keys and secrets are in environment variables, not in code | ⬜ |
| Critical | .env / .env.local is in .gitignore (not committed to repo) | ⬜ |
| Critical | Stripe webhook signatures verified via stripe.webhooks.constructEvent() | ⬜ |
| Critical | Clerk/auth webhook signatures verified via svix or equivalent | ⬜ |
| Critical | Every mutation validates input types with framework validators (Convex v, Zod, etc.) | ⬜ |
| Critical | No raw user input passed directly into AI system prompts | ⬜ |
| Warning | Rate limiting on AI/expensive operations: max ___ requests/minute/user | ⬜ |
| Warning | Rate limiting on auth endpoints: max ___ attempts/minute/IP | ⬜ |
| Warning | CORS configured correctly (only allows your domain) | ⬜ |
| Warning | No sensitive data logged to console or error tracking | ⬜ |
| Warning | File uploads (if any) validated for type and size | ⬜ |
| Info | Content Security Policy headers set | ⬜ |
| Info | HTTPS enforced (HTTP redirects to HTTPS) | ⬜ |
Security smoke test:
- Search codebase for hardcoded API keys:
grep -r "sk_" --include="*.ts" - Verify
.envis not in git history:git log --all --diff-filter=A -- .env - Open browser dev tools → Network tab → verify no API keys in client-side requests
- Try submitting a form with malicious input (XSS payload, SQL injection) — verify it's sanitized
SEO & Performance
If search engines can't find you, you don't exist.
| Severity | Check | Status |
|---|---|---|
| Warning | Every page has a unique tag | ⬜ |
| Warning | Every page has a unique | ⬜ |
| Warning | Open Graph tags set for social sharing (og:title, og:description, og:image) | ⬜ |
| Warning | Sitemap.xml exists, is valid, and lists all public pages | ⬜ |
| Warning | Robots.txt exists and allows search engine crawling of public pages | ⬜ |
| Warning | Page load time under 3 seconds on mobile (test with PageSpeed Insights) | ⬜ |
| Warning | All pages are mobile responsive — test at 375px width | ⬜ |
| Info | Images have descriptive alt text | ⬜ |
| Info | Structured data (JSON-LD) added for key pages | ⬜ |
| Info | Canonical URLs set to prevent duplicate content | ⬜ |
| Info | 404 page returns proper 404 status code (not 200) | ⬜ |
SEO smoke test:
- Google "site:yourdomain.com" — are your pages indexed?
- Paste your URL into Twitter/LinkedIn — does the preview card look right?
- Run Google PageSpeed Insights on your landing page — score above 80?
- Check sitemap.xml in browser — does it list all public pages?
Error Handling & UX
Users who hit errors and get no feedback will leave and never come back.
| Severity | Check | Status |
|---|---|---|
| Critical | No unhandled promise rejections or uncaught errors in production | ⬜ |
| Warning | Loading skeletons shown during all data fetching (never a blank screen) | ⬜ |
| Warning | Error messages are user-friendly (not raw stack traces or API errors) | ⬜ |
| Warning | Empty states shown when no data exists (not blank white space) | ⬜ |
| Warning | Form validation shows clear inline error messages | ⬜ |
| Warning | Toast notifications confirm async actions (create, update, delete) | ⬜ |
| Warning | 404 page exists and matches the app's design | ⬜ |
| Info | Undo/confirmation for destructive actions (delete project, cancel subscription) | ⬜ |
| Info | Keyboard navigation works for core workflows | ⬜ |
| Info | Error monitoring service configured (Sentry, LogRocket, etc.) | ⬜ |
UX smoke test:
- Disable JavaScript — does the page show a reasonable fallback?
- Throttle network to "Slow 3G" in dev tools — do loading states appear?
- Submit every form with empty fields — do validation errors appear?
- Open the app on your phone — does everything fit?
Code Quality
Technical debt you ship on day 1 compounds forever.
| Severity | Check | Status |
|---|---|---|
| Critical | npm run build (or equivalent) passes with zero errors | ⬜ |
| Warning | npm run lint passes with zero warnings | ⬜ |
| Warning | No console.log statements in production code | ⬜ |
| Warning | No hardcoded URLs — all URLs use environment variables or constants | ⬜ |
| Warning | No any types in TypeScript (use unknown and narrow) | ⬜ |
| Warning | No @ts-ignore or @ts-expect-error comments | ⬜ |
| Warning | Environment variables documented in .env.example (without values) | ⬜ |
| Info | No unused imports or variables | ⬜ |
| Info | No commented-out code blocks | ⬜ |
| Info | No TODO/FIXME/HACK comments left in production code | ⬜ |
Code quality smoke test:
- Run
npm run build && npm run lint— both pass clean? - Search for console.log:
grep -r "console.log" --include=".ts" --include=".tsx" src/ - Search for ts-ignore:
grep -r "ts-ignore\|ts-expect-error" --include=".ts" --include=".tsx" - Search for TODOs:
grep -r "TODO\|FIXME\|HACK" --include=".ts" --include=".tsx"
Pre-Deploy Checklist
Final checks before the deploy button.
| Severity | Check | Status |
|---|---|---|
| Critical | All environment variables set in production (not just local) | ⬜ |
| Critical | Domain DNS configured and propagated | ⬜ |
| Critical | SSL certificate active (HTTPS working) | ⬜ |
| Critical | Stripe in live mode with live API keys | ⬜ |
| Critical | Auth provider configured for production domain (not localhost) | ⬜ |
| Warning | Webhook URLs point to production domain (not localhost or ngrok) | ⬜ |
| Warning | Error monitoring configured for production | ⬜ |
| Warning | Database backups configured (if self-hosted) | ⬜ |
| Info | Analytics/tracking installed (Plausible, PostHog, etc.) | ⬜ |
| Info | Custom error pages deployed (404, 500) | ⬜ |
Action Items
Track every failed item here. Fix all Critical and Warning items before launch.
Critical Fixes (must fix before launch)
| Item | Section | Description | Status |
|---|---|---|---|
| ⬜ | |||
| ⬜ | |||
| ⬜ |
Warning Fixes (should fix before launch)
| Item | Section | Description | Status |
|---|---|---|---|
| ⬜ | |||
| ⬜ | |||
| ⬜ |
Info Fixes (fix after launch)
| Item | Section | Description | Status |
|---|---|---|---|
| ⬜ | |||
| ⬜ | |||
| ⬜ |
Want an AI to run these checks and generate a fix list? Try Build a Startup →