/Intelligenza artificiale/Architetture software/di Gianluca Epifani
Un MVP a costo zero: cosa chiedere a Claude quando sei un PM senza team
Premessa
Negli ultimi mesi mi è capitato più volte di parlare con product manager e founder che hanno un’idea chiara, un gruppo di potenziali utenti a portata di mano e nessuno che scriva codice. Fino a poco tempo fa la risposta era “trova uno sviluppatore” o “fai un prototipo in un tool no-code e vediamo”. Oggi la risposta è diversa: un assistente come Claude Code scrive il codice, e lo scrive bene. Ma non decide l’architettura per te, o meglio: la decide, a ogni prompt, in modo diverso, e il risultato è un’applicazione che funziona in demo e si rompe, o inizia a costare, al centesimo utente.
Questo post è il mio tentativo di dare a un PM due cose: un’architettura fissa, noiosa e gratuita su cui costruire un MVP, e la sequenza di cose da chiedere all’assistente, nell’ordine in cui vanno chieste. Non è un tutorial di programmazione. È il briefing che darei a un collega non tecnico che vuole mettere un prodotto davanti a utenti veri entro un mese, senza una fattura cloud.
Prima di aprire Claude: l’architettura è decisa
Il primo errore è aprire l’assistente e scrivere “fammi un’app per…”. L’assistente sceglierà uno stack, poi un altro quando aggiungi una funzione, poi proporrà un server perché “serve per l’autenticazione”, poi un database perché “serve per i dati”. Ognuna di queste scelte è ragionevole da sola; insieme producono qualcosa che qualcuno deve far girare, aggiornare e pagare.
Per un MVP io fisso lo stack prima, e lo fisso così:
- Front-end a rendering lato client (CSR): una single page application in React o Vue, costruita con Vite, pubblicata come file statici. Nessun server applicativo. Nessun rendering lato server: un prodotto dietro login non ha bisogno di essere indicizzato dai motori di ricerca, e la landing page, se serve, è una pagina statica a parte.
- Firebase Authentication per il login: Google, email e password, eventualmente link via email. Niente tabelle utenti scritte a mano, niente password da custodire.
- Firestore come unico database, letto e scritto direttamente dal browser, con le security rules come unico “back-end”. È il punto che fa storcere il naso agli sviluppatori e che per un MVP è la scelta giusta: il server che non esiste non costa, non va aggiornato e non va monitorato.
- Firebase Hosting per i file statici, con un dominio gratuito del tipo
nome.web.appall’inizio e il proprio dominio, certificato compreso, quando serve: anche questo senza costi. - GitHub per il codice e GitHub Actions per la CI: a ogni pull request lint, test e un’anteprima pubblicata su un URL temporaneo; a ogni merge su
main, deploy in produzione. - Niente funzioni serverless, niente servizi esterni a pagamento, niente chiavi segrete nel progetto. Se una funzione ha bisogno di una di queste tre cose, non è nell’MVP.
I piani gratuiti, al momento in cui scrivo, bastano abbondantemente: Firestore concede decine di migliaia di letture e scritture al giorno e un gigabyte di dati, l’autenticazione è gratuita (SMS a parte), l’hosting ha una quota giornaliera di traffico generosa per un’app, GitHub Actions include abbastanza minuti al mese per decine di deploy. E c’è un dettaglio che per un PM vale più delle cifre: il piano gratuito di Firebase non chiede una carta di credito e, se l’hosting supera la quota, il sito viene sospeso fino al mese successivo invece di generare una fattura. Un MVP che va troppo bene si ferma per qualche giorno; non svuota un conto. Il primo centesimo di cloud arriverà quando l’MVP avrà dimostrato qualcosa, ed è esattamente il momento giusto per spenderlo.
Cosa chiedere, e in che ordine
L’assistente lavora bene quando ha un contesto stabile e un compito alla volta. La sequenza che segue è pensata per dargli entrambe le cose.
0. Il brief, in un file che resta
Prima di qualunque codice, chiedete di scrivere insieme un file CLAUDE.md alla radice del progetto: l’assistente lo legge all’inizio di ogni sessione, e tutto ciò che ci scrivete non dovrà essere ripetuto. Il contenuto minimo:
# <Nome del prodotto>
## Cos'è e per chi
Due frasi. Chi è l'utente, quale problema risolve.
## I tre casi d'uso dell'MVP
1. ...
2. ...
3. ...
Tutto ciò che non è in questa lista NON è nell'MVP: se te lo chiedo,
ricordamelo prima di farlo.
## Architettura (fissa, non proporre alternative)
- SPA React + TypeScript con Vite, rendering lato client, file statici.
- Firebase Auth (Google, email). Firestore letto/scritto dal browser.
- Security rules in `firestore.rules`, testate con l'emulatore.
- Firebase Hosting; CI con GitHub Actions (PR: lint+test+anteprima;
main: deploy).
- Nessun server, nessuna cloud function, nessuna chiave segreta nel
codice o nel browser. La configurazione Firebase è pubblica per
progetto.
## Regole di lavoro
- Prima di ogni funzionalità: proponi un piano in 5-10 righe e aspetta
il mio ok.
- Ogni funzionalità è una pull request: interfaccia, dati, rules e test
insieme.
- Spiega ogni scelta non ovvia in tre righe nella descrizione della PR.
- Se qualcosa richiede denaro, un servizio esterno o un server, fermati
e dimmelo.
L’ultima riga è la più importante. Un PM non legge il codice, ma può leggere “questa cosa richiede un piano a pagamento” e decidere.
1. Il modello dei dati e le rules, insieme
“Dati i tre casi d’uso, proponi le collezioni Firestore, i documenti, i campi, e per ciascuno chi può leggerlo e chi può scriverlo. Sfida il modello: cosa succede con mille utenti? Quali letture costano di più? Poi scrivi le security rules e i test che le verificano con l’emulatore.”
Il risultato va letto, anche da chi non è tecnico, su un punto solo: la tabella “chi può fare cosa” corrisponde a quello che ho in testa? Le rules che ne escono assomigliano a questo:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// tutto negato, salvo quanto segue
match /{document=**} {
allow read, write: if false;
}
// ogni utente vede e modifica solo i propri dati
match /users/{uid}/{document=**} {
allow read, write: if request.auth != null && request.auth.uid == uid;
}
// i progetti si leggono se si è membri, si creano se ci si mette
// fra i membri, non si cancellano mai dal browser
match /projects/{id} {
allow read: if request.auth != null
&& request.auth.uid in resource.data.members;
allow create: if request.auth != null
&& request.auth.uid in request.resource.data.members;
allow update: if request.auth != null
&& request.auth.uid in resource.data.members;
allow delete: if false;
}
}
}
La prima regola nega tutto. Le altre aprono il minimo. Chiedete che resti così: ogni volta che l’assistente propone allow read: if true per “semplificare”, è il momento di dire no.
2. Lo scheletro e la CI, prima della prima funzionalità
“Crea il progetto: Vite, React, TypeScript, routing, inizializzazione Firebase, pagina di login, guardia sulle pagine riservate, emulatori Firebase per lavorare in locale senza toccare il progetto vero. Poi il workflow GitHub Actions.”
Si fa prima di qualunque schermata perché, quando esiste, ogni funzionalità successiva nasce già testata e pubblicata. Il workflow è corto:
name: CI
on:
pull_request:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, cache: npm }
- run: npm ci
- run: npm run lint
- run: npm test
# le rules si provano contro l'emulatore, non contro il database vero
- run: npx firebase emulators:exec --only firestore "npm run test:rules"
- run: npm run build
- name: Anteprima della pull request
if: github.event_name == 'pull_request'
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
expires: 7d
- name: Deploy in produzione
if: github.ref == 'refs/heads/main'
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
channelId: live
L’unico segreto di tutto il sistema è l’account di servizio che permette ad Actions di pubblicare. Lo genera e lo incolla il PM, nei secret del repository, dalla propria tastiera: non va passato all’assistente, non va messo in un file, non va in chat. L’assistente scrive il workflow che lo usa; chi lo possiede è un’altra persona. Vale per qualsiasi credenziale, oggi e dopo.
3. Una funzionalità alla volta, verticale
“Caso d’uso 1. Piano, poi: schermata, lettura e scrittura dei dati, rules aggiornate, test. Alla fine fammela provare sugli emulatori.”
Una fetta verticale per volta: non “tutte le schermate” e poi “tutti i dati”. Ogni PR è una cosa che un utente potrebbe fare da capo a fondo. La revisione del PM non è sul codice ma sul prodotto: aprite l’anteprima pubblicata dalla CI, fate quello che farebbe un utente, scrivete cosa non va. È il modo in cui un PM ha sempre lavorato con un team; cambia solo chi scrive il codice e quanto in fretta torna la versione corretta.
Due richieste che vale la pena fare ogni volta: “cosa succede se l’utente non è loggato?” e “cosa succede se la rete cade a metà?”. L’assistente sa gestire entrambi i casi, ma lo fa più volentieri se glielo chiedete.
4. Prima dei primi utenti
Quando i tre casi d’uso funzionano, un’ultima serie di richieste, tutte a costo zero:
- Analytics degli eventi che contano, non pagine viste: “registrazione completata”, “primo progetto creato”, “invito accettato”. Sono i numeri con cui deciderete se l’MVP ha funzionato, quindi vanno scelti dal PM, non dall’assistente.
- Un interruttore generale: un documento di configurazione in Firestore, letto all’avvio, con un flag
maintenancee un messaggio. Se qualcosa va male alle undici di sera, spegnete l’app da una console invece di fare un deploy. - Export dei dati: uno script che scarica le collezioni in JSON. È il backup, e un giorno sarà la migrazione.
- Un modo per ricevere feedback dentro l’app, anche solo un campo di testo che scrive in una collezione
feedback. - Un
READMEper chi arriva dopo: come si avvia in locale, come si fa il deploy, dove stanno le rules. Fatelo scrivere all’assistente e fatelo leggere a qualcuno che non ha mai visto il progetto.
Checklist
Da stampare, se serve.
- [ ]
CLAUDE.mdcon i tre casi d’uso, l’architettura fissa e le regole di lavoro. - [ ] Modello dei dati con la tabella “chi può fare cosa”, approvata dal PM.
- [ ]
firestore.rulesche nega tutto per default, con test sull’emulatore. - [ ] Progetto Vite + React/Vue + TypeScript, login, emulatori in locale.
- [ ] GitHub Actions: lint, test, rules, build, anteprima per PR, deploy da
main. - [ ] Account di servizio nei secret del repository, incollato dal PM.
- [ ] Tre funzionalità verticali, una PR ciascuna, provate sull’anteprima.
- [ ] Eventi di analytics scelti dal PM, interruttore di manutenzione, export dei dati, feedback in app, README.
- [ ] Un gruppo di utenti veri e una data per decidere.
Concludo
Un MVP costruito così è fatto per essere confermato o buttato, non per durare, e conviene sapere in partenza dove si ferma. Si ferma quando serve logica che non può girare nel browser: pagamenti, invio di email, chiamate a servizi con una chiave segreta, lavori pianificati. Lì entra un pezzo di server, una cloud function sul piano a consumo o un Worker su un’altra piattaforma, ed è di solito il primo costo di cloud, che arriva insieme al primo euro di ricavo. Si ferma anche se il prodotto vive di report e classifiche, perché Firestore non fa join e aggrega poco, e se i contenuti devono finire sui motori di ricerca, perché il rendering lato client non è adatto a un catalogo pubblico anche se basta per una landing. E cambia natura quando entrano altre persone sul codice: a quel punto il CLAUDE.md diventa il documento d’architettura e le decisioni prese “perché gratis” vanno rilette una per una.
C’è però un caso in cui questa architettura va benissimo anche per durare: le web app a uso personale. Uno statino che tiene traccia dei task e delle ore lavorate, un’agenda, un diario degli allenamenti, strumenti per una persona o per una famiglia che nessun piano gratuito vedrà mai saturare, e che possono crescere fino a funzioni non banali come l’integrazione con altri sistemi. Il punto delicato, in quel caso, sono le credenziali delle integrazioni: non vanno nel progetto, e Firebase non offre una cassaforte per i segreti lato client (quella che ha, Secret Manager, serve alle cloud function). La strada è una maschera di configurazione nell’app, da cui ogni utente salva le proprie chiavi in un documento Firestore che le rules rendono leggibile e scrivibile solo a lui, e che l’assistente non deve mai vedere. Conviene sapere due cose prima di chiedere a Claude di costruirla: la chiave resta in chiaro per chi amministra il progetto Firebase, il che per un’app personale è accettabile perché quella persona siete voi, e la chiamata verso l’altro sistema parte dal browser, quindi funziona solo con servizi che accettano richieste da un’origine web. Quando l’integrazione è con un servizio Google, il modo più pulito è non avere alcun segreto da custodire: il login Google di Firebase può chiedere i permessi aggiuntivi, per esempio sul calendario, e usare un token temporaneo che non si salva da nessuna parte.
Niente di tutto questo è un motivo per non partire così. Il coach AI di cui ho scritto è nato come prototipo in un linguaggio e con uno stack diversi da quelli con cui è andato in produzione: il prototipo ha fatto il suo lavoro, cioè dimostrare che valeva la pena riscriverlo.
Quanto al costo, zero euro di cloud è una cifra onesta per i primi mesi. Quello che si paga davvero sono l’abbonamento all’assistente e il tempo del PM: qualche sera per il brief e il modello dei dati, poi un’ora al giorno di revisione del prodotto. È meno di quanto costa spiegare l’idea a uno sviluppatore esterno, ed è tempo speso sulla cosa che un PM sa fare meglio, cioè decidere cosa deve fare il prodotto, e cosa no.