guides

Is My Cursor Project Ready to Ship? The Complete Checklist

You built it with Cursor. It works locally. But is it ready for real users? This comprehensive pre-launch checklist covers security, testing, error handling, deploy config, and everything else Cursor won't remind you about.

FinishKit Team7 min read

Cursor is the best AI coding assistant on the market. It hit $1B ARR faster than any B2B company in history, and for good reason: it genuinely makes experienced developers faster and helps newer developers write code they couldn't write alone.

But Cursor has a fundamental characteristic that creates a specific kind of risk: it does what you ask it to do. It doesn't audit your project. It doesn't flag what's missing. It doesn't tell you that your API routes lack authentication or that you have zero tests for your payment flow. It's an incredible assistant, but it's not a reviewer.

This means that the quality of a Cursor-built project depends entirely on whether the developer knows what to ask for. If you know to request input validation, you'll get good input validation. If you don't think about it, it won't exist.

This checklist covers everything you should verify before shipping a Cursor-built project. Think of it as the review that Cursor itself doesn't do.

Security

Authentication and Authorization

  • Every protected route requires authentication. Check that middleware or route guards are in place. A single unprotected endpoint can expose your entire database.
// Verify every API route checks for auth
export async function GET(request: Request) {
  const userId = request.headers.get('x-supabase-user-id');
  if (!userId) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 });
  }
  // ...
}
  • Authorization is checked on every data access. Authentication tells you who the user is. Authorization tells you what they can access. Make sure users can only access their own data.

  • Admin routes are protected. If you have admin functionality, verify that non-admin users can't access it even if they know the URL.

  • OAuth redirect URLs are restricted. If using OAuth providers, ensure redirect URLs are explicitly whitelisted, not wildcarded.

Data Security

  • No secrets in client-side code. Search your entire codebase:
grep -rn "sk_\|secret\|password\|private_key\|service_role" src/ app/ --include="*.ts" --include="*.tsx" --include="*.js"
  • Environment variables are properly separated. Only NEXT_PUBLIC_* vars should be accessible in the browser. Everything else must be server-only.

  • Database queries are parameterized. If you're writing raw SQL anywhere, ensure you're using parameterized queries to prevent SQL injection.

  • User input is sanitized before rendering. If you're rendering user-generated content, ensure it's sanitized to prevent XSS:

// If using dangerouslySetInnerHTML, make sure input is sanitized
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userContent);
  • File uploads are validated. Check file type, size, and content. Don't trust file extensions alone.

Infrastructure Security

  • Security headers are configured. HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy.

  • Rate limiting is in place for authentication endpoints, public API routes, and any endpoint that sends emails or notifications.

  • CORS is restricted to your own domains, not set to *.

Error Handling

  • Every API call has error handling. No await fetch(...) without a try/catch or .catch():
// Every data fetch should handle failures
async function loadProjects() {
  try {
    const res = await fetch('/api/projects');
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } catch (error) {
    toast.error('Failed to load projects');
    return [];
  }
}
  • Error boundaries exist at the application root and around critical sections. A crash in one component shouldn't take down the whole page.

  • Loading states are implemented. Every async operation should show a loading indicator. Buttons should be disabled while their action is processing.

  • Empty states are handled. What does the page look like when there's no data? "No projects yet" is better than a blank screen.

  • Network failures are handled gracefully. What happens when the user is offline? When the API is slow? When a request times out?

  • Error messages are user-friendly. Users should never see raw error objects, stack traces, or technical error codes.

Testing

  • Authentication flows are tested. Sign up, log in, log out, password reset, and token expiration.

  • Core business logic has tests. Whatever your app's primary purpose is, the critical paths should be tested:

// Test your core business logic
describe('createProject', () => {
  it('creates a project for authenticated user', async () => {
    const project = await createProject({
      name: 'Test Project',
      userId: 'user-123',
    });
    expect(project.name).toBe('Test Project');
    expect(project.userId).toBe('user-123');
  });
 
  it('rejects creation without authentication', async () => {
    await expect(createProject({ name: 'Test' }))
      .rejects.toThrow('Unauthorized');
  });
});
  • Payment flows are tested (if applicable). Checkout, webhook handling, subscription changes, and cancellation.

  • The build succeeds without ignoreBuildErrors. If your next.config.js has typescript: { ignoreBuildErrors: true } or eslint: { ignoreDuringBuilds: true }, fix the underlying errors instead.

Check your next.config.js or next.config.ts for ignoreBuildErrors. Cursor sometimes suggests adding this to "fix" build failures. It doesn't fix anything; it hides problems that will bite you in production.

Deploy Configuration

  • All environment variables are documented and set in your hosting provider. Use a .env.example file to track what's needed.

  • Database migrations are tracked. Your database schema should be reproducible from migration files, not from manual SQL execution in a dashboard.

  • Build output is optimized. Run npm run build and check for warnings. Address large bundle sizes, missing dependencies, and build errors.

  • Static assets are optimized. Images should be properly sized and compressed. Use Next.js Image component or equivalent for automatic optimization.

  • Caching headers are configured for static assets (CSS, JS, images). Dynamic API routes should have appropriate cache-control headers.

Performance

  • No N+1 queries. If you're loading a list of items and then making a separate query for each item's details, combine them into a single query.

  • Large lists are paginated. Don't load 10,000 records into the browser. Implement cursor-based or offset pagination.

  • Images are lazy-loaded below the fold. Above-the-fold images should be eagerly loaded.

  • Bundle size is reasonable. Check your build output. If your JavaScript bundle is over 500KB, investigate what's causing the bloat.

SEO and Meta Tags

  • Every page has a unique title and description. Not just the homepage, every publicly accessible page.

  • Open Graph tags are set for social sharing. When someone shares your URL on Twitter or LinkedIn, it should show a proper preview.

  • Favicon and app icons are configured. A missing favicon is a small thing that makes your app look unfinished.

  • Sitemap is generated for publicly accessible pages.

  • robots.txt is configured. Ensure search engines can crawl your public pages and can't access your dashboard or admin routes.

Monitoring

  • Error tracking is set up. Sentry, LogRocket, or equivalent. You need to know when errors happen in production without waiting for user reports.

  • Analytics are configured. You need to know if people are actually using your app and where they're dropping off.

  • Uptime monitoring is configured. Use a service like BetterUptime or UptimeRobot to alert you when your app goes down.

The Meta-Problem

This checklist has over 40 items. That's a lot to check manually, especially when you're eager to ship. And this is exactly why production readiness gaps persist: not because developers don't know about them, but because the checklist is long and the pressure to ship is real.

Cursor helped you build fast. But speed in building doesn't automatically translate to readiness for production. The patterns we see across AI-built apps are consistent: the build is excellent, the finishing is incomplete.

FinishKit automates this entire checklist. Point it at your repo, and it scans for security gaps, missing error handling, test coverage, deploy configuration issues, and more. You get a prioritized Finish Plan that tells you exactly what to fix, in order of importance. Try a free scan.

You built something real with Cursor. Don't let it fail because of the things you forgot to ask for.