ZeroToShip

Docs/Extra

// extra

Lavorare con l'AI sul codice

Il motivo per cui un boilerplate fa risparmiare tempo a un assistente AI non è che il codice è già scritto — è che le convenzioni sono già scritte. Nella root del repo c'è .cursorrules: un file di testo che Cursor carica ad ogni prompt, e che dice al modello com'è fatto questo progetto prima che tocchi un solo file. Accanto c'è CLAUDE_INSTRUCTIONS.md, la versione per Claude Code.

1. Cosa c'è dentro .cursorrules

Non sono quattro righe di buoni propositi. Il file apre con lo stack — Next.js 15 App Router, TypeScript 5.9, Supabase con Row Level Security, Stripe con webhook, Tailwind 4.1 con DaisyUI 5, Resend — e poi scende nel concreto con sezioni su convenzioni TypeScript, naming e struttura file, architettura dei componenti, ordine degli import, route handler, database, autenticazione, Stripe, variabili d'ambiente, gestione errori, performance, sicurezza e messaggi di commit.

La parte di struttura è quella che evita all'AI di inventarsi cartelle nuove:

.cursorrules
bash
### File Naming & Structure
- Use PascalCase for component names: ButtonCheckout
- API routes: /app/api/[feature]/route.ts
- Components: /components/ComponentName.tsx
- Pages: /app/[route]/page.tsx
- Types: /types/[feature].ts
- Utils/Libs: /libs/[utility].ts

2. Le regole che cambiano davvero l'output

La maggior parte dei modelli ha in testa più codice Next.js 14 che 15, e Tailwind 3 che 4. Sono esattamente le due cose su cui .cursorrules insiste di più, perché sono quelle che producono codice che sembra giusto e non compila.

Su Next 15 diverse API sono diventate asincrone, e il file lo dice a chiare lettere:

.cursorrules
typescript
### Async APIs
- headers() and cookies() now return Promises - always use await
- params in dynamic routes are now Promises - use await params

export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
}

Stessa storia per il client Supabase lato server: in questo repo createClient() di libs/supabase/server.ts è asincrono, quindi va sempre atteso. Il file lo ripete in tre sezioni diverse — server component, client component, route handler — perché è l'errore che l'AI fa più spesso.

.cursorrules
typescript
// Server: await required
const supabase = await createClient();

// Client component: no await
"use client";
const supabase = createClient();

Su Tailwind, la regola è che non esiste più un tailwind.config.js: la configurazione è CSS-first dentro app/globals.css, con @import "tailwindcss", il blocco @theme per i token e @utility per le classi custom. Se il tuo assistente prova a crearti un file di config JS, è perché non ha letto questa sezione.

3. La lista “Do Not”

In fondo al file c'è un elenco di divieti secchi. Questi valgono più di dieci paragrafi di stile, perché un modello li tratta come vincoli:

  • niente any se non è davvero inevitabile, e niente aggiramenti dello strict mode;
  • niente valori di configurazione hardcodati — vanno in config.ts o nelle variabili d'ambiente;
  • mai dimenticare await sulle API asincrone di Next 15;
  • mai saltare la verifica della firma sui webhook Stripe;
  • mai esporre la service role key di Supabase lato client;
  • mai dimenticare le policy RLS quando si crea una tabella;
  • niente componenti senza interfaccia TypeScript per le props, e niente stati di loading ed errore lasciati fuori.

Una nota onesta su una di queste regole: il file dice anche don't use inline styles, prefer TailwindCSS classes, ma buona parte dei componenti della landing usa style={{ background: "var(--cream)" }} di proposito, per leggere i token del tema definiti in globals.css. Se la tua AI ti riscrive quegli stili in classi Tailwind “per rispettare le regole”, ora sai perché — e sai che in quel punto la regola va ignorata.

4. CLAUDE_INSTRUCTIONS.md

Stessa sostanza, formato diverso. CLAUDE_INSTRUCTIONS.md è costruito quasi tutto per coppie sbagliato/giusto: mostra il pattern Next 14 e accanto quello Next 15, il client Supabase senza await e quello con await, il config Tailwind in JS e quello in CSS. Per un modello è la forma più efficace, perché rende esplicito l'errore da non fare invece di descrivere solo il comportamento corretto.

Contiene anche due cose che .cursorrules non ha: un albero delle cartelle del progetto, e una sezione finale su come verificare il lavoro.

CLAUDE_INSTRUCTIONS.md
bash
### Common Build Errors to Watch For:
1. Missing await on async Next.js 15 APIs
2. Incorrect params typing in dynamic routes
3. Client/Server component boundary issues
4. Missing environment variables
5. Tailwind CSS configuration errors

Un dettaglio pratico: Claude Code per convenzione cerca un file chiamato CLAUDE.md, e qui il file si chiama CLAUDE_INSTRUCTIONS.md. Se vuoi che venga letto in automatico, copialo o rinominalo:

terminal
bash
cp CLAUDE_INSTRUCTIONS.md CLAUDE.md

Altrimenti citalo tu nel prompt la prima volta: “leggi CLAUDE_INSTRUCTIONS.md prima di iniziare”.

5. Come scrivere i prompt su questo repo

Siccome il contesto ce l'ha già, il prompt più efficace non spiega il progetto: nomina i file. Un modello che sa già cosa contiene libs/api.ts non deve indovinare come si chiama il client axios.

prompt
bash
Crea /api/newsletter sullo stesso schema di app/api/lead/route.ts:
valida body.email, salva su Supabase, ritorna { error } sui fallimenti.
Poi un client component che lo chiama con apiClient da libs/api.ts.

Aggiungi un terzo piano "Team" in config.ts sotto stripe.plans,
con isFeatured false, e verifica che <Pricing /> lo renderizzi senza modifiche.

Nel webhook Stripe, dentro checkout.session.completed,
manda una email di benvenuto con sendEmail() di libs/resend.ts.

Quando invece stai lavorando sul look, punta ai token e alle primitive: “usa i token di app/globals.css e WobbleBorder da components/drawn” ti restituisce un componente coerente col resto della landing, invece di un blocco DaisyUI blu che stona con tutto.

6. Tieni i due file aggiornati

Questa è la parte che si dimentica. .cursorrules e CLAUDE_INSTRUCTIONS.md non sono documentazione decorativa: sono la fonte di verità che l'AI legge prima di scrivere. Se sostituisci Resend con un altro provider di email, o aggiungi una cartella services/ con una tua convenzione, e non aggiorni quei file, il modello continuerà a produrre codice per il progetto di prima. Ogni volta che cambi una convenzione, cambiala anche lì.

E una regola di igiene che vale sempre, a prescindere dal modello: dopo una sessione in cui l'AI ha toccato più file, lancia il build prima di committare. Gli errori della lista al passo 4 vengono fuori tutti lì.

terminal
bash
npm run build

Se ti serve un ripasso su cosa deve esserci nel .env.local prima che il build passi, è in Configurare le variabili.