TL;DR

We've deployed 30+ headless projects in 18 months at Social Animal. Roughly a third of our recent intake is rescue work from designer-led builds that shipped demos but broke in production. Michael Nervegna's "Claude Code for Designers" guide on Substack is legitimately good for prototyping -- but it stops before Row Level Security, auth token refresh, database abstraction choices, deploy rollback, Content Security Policy, and admin authentication. This article covers those gaps. If you're shipping to production, you need them.

Table of Contents

What Is Claude Code and Why Are Designers Using It?

Claude Code is Anthropic's terminal-based coding tool that launched in early 2025. It runs as @anthropic-ai/claude-code and requires Claude Pro ($20/month) or Team ($30/seat/month) with API access. You write, edit, and debug code through natural language in your terminal.

Designers are using it because it closes the gap between "I designed this in Figma" and "this is a working Next.js app." Unlike v0 or Bolt, Claude Code operates on your actual file system. It reads your project structure, modifies files, runs your dev server, and iterates on error output. For someone who understands component hierarchy but doesn't want to memorize TypeScript generics, that's genuinely useful.

Nervegna positions it as a tool that lets designers "think in systems" rather than syntax. We agree. Where we diverge is on what happens after the first npm run dev succeeds.

What Does Nervegna's Guide Get Right?

Nervegna nails three things most AI coding tutorials miss.

First: project context. He recommends feeding Claude Code a CLAUDE.md file with your project conventions, tech stack, and design tokens. We've seen Claude Code generate Tailwind utilities against a project using CSS Modules because nobody told it the conventions. Establishing context before writing code is the right approach.

Second: the iterative loop. Prompt, review output, course-correct, repeat. He doesn't treat Claude Code as a "describe and ship" button. He treats it as a pairing partner that needs oversight. That's the correct mental model.

Third: starting small. Build a single component or page before attempting full application scaffolding. We've seen designers prompt "build me a full SaaS dashboard with auth, billing, and admin" in one message. The result is always a mess. Nervegna's incremental approach avoids this.

Where Nervegna's guide serves designers well is the 0-to-prototype phase. The problem is that prototypes become production code, and that's where the gaps matter.

Where Does the Guide Stop Short?

Nervegna's piece delivers for the prototyping phase. It doesn't address concerns that become urgent when you connect a real database, real users, and real deployment infrastructure.

What's missing:

  1. Row Level Security (RLS) -- Supabase projects generated by Claude Code almost never have correct RLS policies
  2. Auth handoff -- the gap between Supabase Auth in development and production flows with token refresh, session management, and redirect handling
  3. Database abstraction decisions -- when to use Supabase client directly vs. Prisma or Drizzle ORM
  4. Deploy rollback strategy -- what happens when a Claude Code commit breaks production
  5. Content Security Policy -- especially for Next.js Image with proxied/external assets
  6. Admin authentication -- when you need role-based access beyond simple user auth

Let's take each one.

Row Level Security Gotchas That Claude Won't Catch

Row Level Security is Supabase's mechanism for ensuring database queries only return rows the requesting user is authorized to see. When you create a table in Supabase, RLS is disabled by default. Any authenticated user -- or in some configurations, any anonymous request -- can read every row.

When Claude Code scaffolds a Supabase project, it creates tables and writes client-side queries. It will sometimes add RLS policies if you ask. But the policies it generates are often wrong in subtle ways.

Common RLS Mistakes in AI-Generated Code

Mistake What Happens How to Fix
No RLS enabled at all Any authenticated user reads all data ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
Policy uses auth.uid() but table has no user_id column Policy compiles but matches zero rows, blocking all access Add user_id UUID REFERENCES auth.users(id) and populate it
SELECT policy exists but no INSERT/UPDATE/DELETE policies Users can read but not write their own data Create policies for each operation separately
Policy uses auth.role() = 'authenticated' only Any logged-in user can see all rows, not just their own Add auth.uid() = user_id condition
Service role key used in client-side code RLS is bypassed entirely Never expose SUPABASE_SERVICE_ROLE_KEY in client bundles

We've seen that last one -- service role key in client code -- in three separate designer-built projects this year. Claude Code will sometimes use the service role key because it "works" and doesn't throw permission errors. That key bypasses all RLS. It belongs in server-side code only (API routes, server actions, edge functions). Never in a use client component.

Nervegna doesn't cover RLS in his guide, which is understandable. But if you're following his workflow and connecting to Supabase, check every table manually. Run this in the Supabase SQL editor:

SELECT schemaname, tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public';

If rowsecurity is false for any table that holds user data, stop and fix it before deploying.

Auth Handoff: The Gap Between Prototype and Production

Auth is the most common failure point in designer-led builds. Not because the initial setup is wrong -- Supabase Auth is easy to scaffold -- but because production auth involves edge cases that don't surface during local development.

Nervegna's guide focuses on getting things working locally. Here's what breaks when you deploy:

Token Refresh Failures

Supabase Auth tokens expire after 3600 seconds (1 hour) by default. The Supabase client handles refresh automatically -- in theory. In practice, if your Next.js middleware or server components create a new Supabase client on every request without passing the existing session, you'll get intermittent 401 errors after the first hour.

With Next.js App Router, you need @supabase/ssr (version 0.5.x as of February 2025) and proper cookie handling in middleware. Claude Code often generates the older @supabase/auth-helpers-nextjs package, which is deprecated. Check your package.json.

Redirect URI Mismatches

OAuth providers (Google, GitHub) require exact redirect URIs. Claude Code configures these for localhost:3000. When you deploy to Vercel or Netlify, you need to add your production URL and any preview deployment URLs to both the OAuth provider's console and Supabase's Auth settings under "Redirect URLs." This takes 5 minutes but blocks 100% of OAuth logins if you miss it.

The Session Sync Problem

In Next.js 14+, you have server components, client components, middleware, API routes, and server actions -- all potentially needing the current user session. The Supabase client must be created differently in each context. Claude Code often creates a single createClient() utility and uses it everywhere, which causes hydration mismatches and stale sessions.

You need at minimum three client creation functions:

  • createBrowserClient() for client components
  • createServerClient() for server components and server actions
  • createMiddlewareClient() for middleware

This is documented in Supabase's SSR guide but Claude Code doesn't consistently generate it correctly.

Supabase vs Prisma: Which Should Claude Code Generate For?

Nervegna doesn't address this decision, and it matters more than most designers realize.

Supabase client (@supabase/supabase-js) queries the database through Supabase's PostgREST API. It's convenient, requires no schema definition files, and works directly with RLS. But it gives you no type safety beyond what Supabase generates, and it tightly couples your application to Supabase's infrastructure.

Prisma ORM (currently v6.x) connects directly to PostgreSQL. It gives you a schema file (schema.prisma), generated TypeScript types, migrations, and database-agnostic queries. But it doesn't respect RLS policies (it connects as a privileged user), and it requires a build step to generate the client.

Drizzle ORM (v0.36.x) is a lighter alternative with SQL-like syntax and better edge runtime support.

Decision Matrix

Factor Supabase Client Prisma Drizzle
RLS support Native Must implement in app layer Must implement in app layer
Type safety Generated via CLI Generated from schema Schema-as-code
Edge runtime compatible Yes Limited (Prisma Accelerate required) Yes
Learning curve for designers Low Medium Medium
Vendor lock-in High (Supabase) Low Low
Claude Code generation quality Good Good Inconsistent

Our recommendation: if you're building a prototype or will always run on Supabase, use the Supabase client. If you need to move off Supabase or want strict schema-driven development, use Drizzle. We've moved away from Prisma for new projects due to edge runtime limitations, though Prisma Accelerate ($0 for 60K queries/month, then $49/month) has improved this.

Tell Claude Code your decision explicitly in your CLAUDE.md file. If you don't, it will mix approaches -- sometimes querying via Supabase client, sometimes via an ORM -- and you'll end up with two different data access patterns in the same project.

Deploy Rollback: What Happens When Your AI-Generated Code Breaks

Nervegna's guide walks through building features iteratively. It doesn't address what happens when a Claude Code session generates a commit that passes local dev but breaks in production.

This happens more often than you'd expect. Claude Code can modify 15 files in a single prompt response. If you commit that as one unit and deploy, rolling back means reverting all 15 changes -- even if 13 were fine.

A Practical Rollback Strategy

1. Commit after each logical change, not each Claude Code session. If Claude modifies auth, UI, and database schema in one session, make three separate commits.

2. Use Vercel's instant rollback. If you're deploying on Vercel, every deployment is immutable. You can roll back to any previous deployment in under 10 seconds from the dashboard. Netlify offers the same.

3. Never run database migrations from Claude Code directly. If Claude generates a migration file, review it manually before running npx supabase db push or npx prisma migrate deploy. A dropped column isn't something you can roll back with a git revert.

4. Tag known-good states. Before starting a Claude Code session that touches critical paths (auth, payments, data models), create a git tag: git tag pre-auth-refactor. If things go wrong, git reset --hard pre-auth-refactor gets you back.

5. Preview deployments are mandatory, not optional. Vercel and Netlify both create preview deployments for every PR. Don't merge to main without clicking through the preview. Claude Code can generate code that works locally but fails in production due to missing environment variables, edge runtime incompatibilities, or CSP violations.

Content Security Policy for Proxied Assets

This is niche but it bites hard when you deploy a designer-built site.

Next.js's <Image> component proxies external images through /_next/image by default. This is great for optimization. But if you have a Content Security Policy header (and you should), you need to explicitly allow the domains your images come from.

Claude Code will set up next.config.js with remotePatterns for image optimization but won't add CSP headers. When you deploy behind Vercel's security headers or add your own via middleware, external images break silently -- they load locally (where CSP is often lax) but fail in production.

Here's the minimum you need in your middleware or next.config.js headers:

// middleware.ts
const cspHeader = `
  default-src 'self';
  script-src 'self' 'unsafe-eval' 'unsafe-inline';
  style-src 'self' 'unsafe-inline';
  img-src 'self' blob: data: https://your-supabase-project.supabase.co https://your-cdn.com;
  font-src 'self';
  connect-src 'self' https://your-supabase-project.supabase.co;
  frame-ancestors 'none';
`;

Replace 'unsafe-eval' and 'unsafe-inline' with nonces for production hardening. The point is: if Claude Code pulls in images from Unsplash, Supabase Storage, or any external CDN, and you don't add those domains to your CSP img-src directive, you'll get broken images in production with zero console errors in development.

When Do You Actually Need Admin Auth?

Nervegna's guide covers basic user authentication. Many designer-built projects need an admin interface -- a way for the site owner or content team to manage data without touching the database directly.

The question is: when do you need admin auth as a separate concern vs. just using Supabase's dashboard?

You DON'T Need Custom Admin Auth When:

  • You're the only person managing content
  • Your client is comfortable using Supabase's Table Editor
  • The project has fewer than 3 content types
  • Updates happen less than once per week

You DO Need Custom Admin Auth When:

  • Non-technical team members need to manage content
  • You need approval workflows or draft/publish states
  • The project has role-based access (editor vs. admin vs. viewer)
  • You're managing user-generated content that requires moderation

If you need admin auth, the simplest approach is a role column on your profiles table (which mirrors auth.users) with an enum: 'user' | 'editor' | 'admin'. Then add RLS policies that check this role:

CREATE POLICY "Admins can do anything"
ON public.posts
FOR ALL
USING (
  EXISTS (
    SELECT 1 FROM public.profiles
    WHERE profiles.id = auth.uid()
    AND profiles.role = 'admin'
  )
);

Claude Code can generate this if you prompt it specifically. Without the prompt, it will default to simple auth.uid() = user_id policies that don't account for admin access. You'll end up with an admin who can't see other users' content.

Nervegna's workflow of defining requirements in a CLAUDE.md file would catch this -- if you think to include role-based access in your requirements. Add it to the file before you start building.

Before You Push to Prod: The Checklist

This is what we use at Social Animal before deploying any project built or significantly assisted by AI coding tools.

Database & Security

  • RLS is enabled on every table in public schema
  • RLS policies exist for SELECT, INSERT, UPDATE, and DELETE on each table
  • SUPABASE_SERVICE_ROLE_KEY is only used in server-side code (grep your codebase: grep -r "SERVICE_ROLE" --include="*.ts" --include="*.tsx")
  • No Supabase client is created with the service role key in any file containing 'use client'
  • Database migrations have been reviewed manually
  • Foreign key constraints exist where expected
  • Indexes exist on columns used in WHERE clauses and RLS policies

Authentication

  • @supabase/ssr is used (not deprecated @supabase/auth-helpers-nextjs)
  • Separate client creation functions exist for browser, server, and middleware contexts
  • OAuth redirect URIs are configured for production domain AND preview deployment domains
  • Token refresh is tested (set a short expiry temporarily and verify sessions survive)
  • Protected routes redirect to login when session is absent
  • Logout clears all cookies and server-side session state

Admin & Roles

  • If admin features exist, role checks happen at the RLS level (not just UI-level hiding)
  • Admin routes are protected by middleware, not just conditional rendering
  • Default role for new users is the least privileged role

Deployment & Rollback

  • Environment variables are set in deployment platform
  • A known-good git tag exists from before the last major AI-assisted changes
  • Preview deployment has been tested by clicking through core flows
  • Vercel/Netlify instant rollback is understood and documented for the team
  • Database backups are enabled (Supabase Pro plan includes daily backups for $25/month)

Content Security & Assets

  • CSP headers include all external image domains in img-src
  • CSP headers include Supabase project URL in connect-src
  • next.config.js remotePatterns matches CSP img-src domains
  • Fonts are self-hosted or their CDN is in font-src
  • No mixed content (HTTP resources on HTTPS pages)

Code Quality

  • TypeScript strict mode is enabled ("strict": true in tsconfig.json)
  • No @ts-ignore or any types that Claude Code added to suppress errors
  • npm run build passes without warnings (not just npm run dev)
  • Error boundaries exist for client components that fetch data
  • Loading and error states exist for async operations

Performance

  • Images use Next.js <Image> component with width and height or fill
  • No client-side data fetching for data that could be fetched in server components
  • Bundle size has been checked (npx next@latest build shows route sizes)
  • Lighthouse score is above 90 for Performance (run on the preview deployment, not localhost)

This checklist isn't exhaustive. It's the minimum for a project that touches Supabase, Next.js, and AI-generated code.

FAQ

Is Claude Code good enough for designers to build production apps?

Claude Code is excellent for generating working code from design intent. But production readiness requires security, auth, and infrastructure knowledge that the tool doesn't provide unprompted. Pair it with a checklist and code review from someone with backend experience.

Does Nervegna's guide work for projects beyond prototypes?

Nervegna's workflow -- context-first prompting, incremental building, iterative review -- scales well. The gap is in production concerns like RLS, auth edge cases, and deployment strategy. His approach is sound; it needs supplementation for anything user-facing.

Should I use Supabase or Prisma with Claude Code?

Use Supabase's client library if you want RLS enforcement at the database level and you're committed to the Supabase platform. Use Drizzle ORM if you want database portability and edge runtime compatibility. We've moved away from Prisma for new projects due to edge limitations.

How do I prevent Claude Code from using the Supabase service role key in client code?

Add an explicit rule to your CLAUDE.md file: "Never use SUPABASE_SERVICE_ROLE_KEY in client components. Only use it in server actions, API routes, or middleware." Claude Code respects project-level instructions when they're stated clearly.

What's the cheapest way to deploy a Claude Code-generated Next.js app?

Vercel's free Hobby plan supports one production deployment per project. Supabase's free tier includes 2 projects with 500MB database and 1GB file storage. Total cost: $0/month for low-traffic sites. Expect to move to Vercel Pro ($20/month) and Supabase Pro ($25/month) once you have real users.

How often does Claude Code generate code with security issues?

In our experience, roughly 40% of Claude Code sessions involving database operations produce code with at least one RLS gap. It's not malicious -- the tool optimizes for "working" code, and RLS violations don't produce errors. They just expose data silently. Always audit manually.