G.STANCUTA
Pubblicato · 2026 · 05 · 227 min di lettura

MySQL è ancora un default eccellente

  • mysql
  • prisma
  • databases
  • infrastructure

Ogni nuovo progetto scatena lo stesso dibattito. Postgres o MySQL? SQLite? Neon? Ecco perché continuo a scegliere MySQL 8.4 con Prisma 7 senza alcun rimpianto.

C'è un certo rituale dello sviluppatore: apri un repository vuoto, hai bisogno di un database, e improvvisamente tre persone su internet ti dicono che la tua scelta è sbagliata. MySQL è legacy. SQLite non scala. Postgres è l'unica opzione seria. Neon è il futuro. Ho assistito a questo ciclo per anni. Su NearYou ho scelto MySQL 8.4 con Prisma 7 e l'adapter mariadb. Funziona perfettamente. Ecco il ragionamento completo.

La noia è una funzionalità

MySQL fa girare carichi di lavoro in produzione dal 1995. Sono tre decenni di casi limite, modalità di errore e correzioni integrate nel codice sorgente. Quando qualcosa si rompe alle 2 di notte, trovi un thread di Stack Overflow del 2014 che ha già risolto il problema. Non è una cosa da poco. I database nuovi sono eccitanti nei post del blog ed estenuanti in produzione.

MySQL 8.4 è una release LTS. Le correzioni dei bug arrivano con una cadenza prevedibile. Il motore di archiviazione è InnoDB, che offre locking a livello di riga, applicazione delle chiavi esterne e transazioni ACID per impostazione predefinita. Non hai bisogno di configurare nulla di tutto questo. È già attivo. L'ottimizzatore gestisce la maggior parte dei piani di query correttamente senza suggerimenti. La storia della replica è matura. Se hai bisogno di repliche di lettura in seguito, le aggiungi senza modificare il codice dell'applicazione.

Il miglior database per un nuovo progetto è quello a cui non devi mai più pensare.

Diagramma isometrico del container MySQL accanto a un container applicativo con Prisma come layer di connessione
Lo stack completo: container applicativo, client Prisma, MySQL 8.4 in un container sidecar.

UTF8MB4 fin dall'inizio

Un errore storico genuino di MySQL è stato il charset utf8, che era in realtà una codifica a 3 byte incapace di memorizzare la maggior parte dei caratteri CJK e tutte le emoji. Quella codifica si chiama ora utf8mb3 ed è deprecata. Il sostituto è utf8mb4, che è il vero UTF-8 a 4 byte e memorizza tutto: arabo, ebraico, giapponese, coreano, tailandese e, sì, ogni emoji che i tuoi utenti ti invieranno.

NearYou ha utenti in Alto Adige, quindi i contenuti arrivano in tedesco, italiano, ladino e rumeno. Impostare charset e collation una volta sola a livello di database significa non doverci più pensare a livello applicativo.

Un container accanto all'applicazione

Per lo sviluppo e per i deployment in produzione di piccole dimensioni, MySQL gira in un container Docker sullo stesso host dell'applicazione. La latenza di rete scende a meno di un millisecondo. Non c'è un servizio gestito che ti fattura il tempo di inattività. La configurazione è quattro righe di Docker Compose.

yaml
services:
  db:
    image: mysql:8.4
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: nearyou
      MYSQL_USER: app
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
    command: >
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_unicode_ci
      --default-authentication-plugin=caching_sha2_password
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 5

  app:
    build: .
    depends_on:
      db:
        condition: service_healthy
    environment:
      DATABASE_URL: mysql://app:${DB_PASSWORD}@db:3306/nearyou

volumes:
  db_data:

L'healthcheck è importante. Senza di esso, il container dell'applicazione può avviarsi prima che MySQL completi la sua sequenza di inizializzazione e il connection pool esplode alla prima query. depends_on con condition: service_healthy risolve il problema in modo pulito.

Prisma 7: schema, migrazioni, tipi

Prisma è il motivo per cui questo stack è genuinamente piacevole con cui lavorare. Scrivi un unico file di schema. Prisma genera le migrazioni SQL da esso, le esegue sul database e genera un client TypeScript con tipi che corrispondono esattamente a ogni modello. Nessuna asserzione di tipo a runtime. Nessuna stringa SQL scritta a mano. Nessuna magia ORM che esegue silenziosamente query N+1.

Prisma 7 ha aggiunto il supporto di prima classe per il provider mariadb, che copre MySQL 8.4 con una compatibilità migliore rispetto al legacy provider mysql per certi casi limite riguardanti la precisione dei datetime e la gestione di JSON. Su NearYou lo usiamo esplicitamente.

prisma
// schema.prisma
generator client {
  provider        = "prisma-client-js"
  previewFeatures = []
}

datasource db {
  provider = "mariadb"
  url      = env("DATABASE_URL")
}

model Place {
  id          String    @id @default(cuid())
  name        String    @db.VarChar(255)
  description String?   @db.Text
  lat         Decimal   @db.Decimal(10, 8)
  lng         Decimal   @db.Decimal(11, 8)
  category    Category
  ownerId     String
  owner       User      @relation(fields: [ownerId], references: [id], onDelete: Cascade)
  events      Event[]
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt

  @@index([lat, lng])
  @@index([ownerId])
}

model User {
  id        String   @id @default(cuid())
  email     String   @unique @db.VarChar(320)
  name      String   @db.VarChar(255)
  places    Place[]
  createdAt DateTime @default(now())
}

enum Category {
  RESTAURANT
  CAFE
  SHOP
  EVENT_VENUE
  OUTDOOR
  OTHER
}

Una volta che lo schema è al suo posto, due comandi gestiscono tutto.

bash
# Create and apply a migration (give it a descriptive name)
npx prisma migrate dev --name add-place-category-index

# Regenerate the TypeScript client after any schema change
npx prisma generate

Il file di migrazione è SQL. Vive nel controllo di versione. Puoi leggerlo, revisionarlo in una PR, eseguirne il rollback manualmente se qualcosa va storto. Non c'è uno stato nascosto. Il comando prisma migrate deploy esegue le migrazioni pendenti in CI o all'avvio del server. L'intero flusso è prevedibile.

Come un agente AI lavora con questo stack

Uso Claude Code come agente AI di programmazione su NearYou. L'agente scrive schemi Prisma, genera migrazioni, interroga il database tramite il client e risolve i problemi di connessione. Perché questo funzioni in modo affidabile tra le sessioni, l'agente ha bisogno di una memoria persistente della configurazione esatta: quale provider, quale versione di MySQL, quali convenzioni seguiamo, quali insidie ci hanno già colpito.

Risolvo questo con un file AGENTS.md a livello di progetto che l'agente legge a ogni sessione. Non è documentazione per gli esseri umani. È un file di contesto compatto e preciso che dice all'agente come comportarsi con questo stack specifico. L'agente non ha bisogno di riscoprire che usiamo mariadb come provider Prisma, o che le migrazioni risiedono in prisma/migrations/, o che il formato DSN per la nostra configurazione Docker Compose usa il nome del servizio db come hostname.

Diagramma schematico di un agente AI che legge un file di memoria markdown ed esegue comandi di migrazione del database
L'agente legge AGENTS.md all'avvio della sessione, poi agisce su di esso in modo affidabile per l'intera sessione.

Ecco la sezione database effettiva dal AGENTS.md di NearYou. È breve. È precisa. Ogni riga guadagna il suo posto.

md
## Database (MySQL 8.4 + Prisma 7)

Provider: `mariadb` (not mysql — use this in schema.prisma)
DSN env var: `DATABASE_URL`
DSN format: `mysql://app:<pass>@db:3306/nearyou`
Schema file: `prisma/schema.prisma`
Migrations dir: `prisma/migrations/`

### Commands
- Dev migration: `npx prisma migrate dev --name <descriptive-name>`
- Deploy migrations: `npx prisma migrate deploy`
- Regenerate client: `npx prisma generate`
- Open Studio: `npx prisma studio`
- Reset dev DB: `npx prisma migrate reset` (DESTROYS DATA)

### Conventions
- All string PKs use cuid() not uuid() — smaller index footprint.
- All money values use Decimal, never Float.
- lat/lng use Decimal(10,8) and Decimal(11,8) respectively.
- Compound index on [lat, lng] on every Place-like model.
- onDelete: Cascade on all user-owned relations.

### Gotchas
- MySQL 8.4 default auth is caching_sha2_password. Old clients fail without upgrading.
- utf8mb4_unicode_ci is case-insensitive. Use utf8mb4_bin for case-sensitive fields like tokens.
- JSON columns in MySQL 8.4 do not support partial updates via Prisma — read, merge, write.
- Never run migrate reset in production. It drops and recreates all tables.

Con questo file presente, l'agente non chiede mai quale provider usare, non indovina mai il formato DSN e non ripete mai un errore che abbiamo già documentato. Legge il file, interiorizza i vincoli e opera correttamente con lo stack al primo tentativo. Il file AGENTS.md diventa la memoria a lungo termine che sopravvive ai reset della finestra di contesto.

L'unica regola: backup reali e testati

Tutto quanto sopra è confortevole. L'unica cosa non negoziabile sono i backup. Non un cron di backup che presumi funzioni. Restore effettivamente testati.

  • Esegui mysqldump --single-transaction --routines --triggers giornalmente come minimo. Il flag --single-transaction evita i lock sulle tabelle con InnoDB.
  • Spedisci il dump fuori sede immediatamente. I backup sullo stesso host non sono backup, sono un falso senso di sicurezza.
  • Testa il restore mensilmente. Avvia un container nuovo, immetti il dump, verifica il conteggio delle righe nelle tabelle critiche.
  • Se sei su un host gestito (Railway, PlanetScale, Render), verifica se il loro point-in-time recovery copre effettivamente i tuoi requisiti di conservazione dei dati. Non dare nulla per scontato.
  • Le migrazioni Prisma non sono un backup. Ricreano lo schema ma non i dati.

Il verdetto

MySQL 8.4 con Prisma 7 e l'adapter mariadb non è glamour. Non ha un paper nuovo e brillante da citare. Ha trent'anni di hardening in produzione, un ecosistema maturo, ottimi strumenti e zero sorprese alla scala. Per NearYou è la scelta giusta. Per la maggior parte dei nuovi progetti è un default completamente difendibile. Sceglilo, configura utf8mb4 dal primo giorno, scrivi un vero AGENTS.md affinché il tuo agente AI conosca la configurazione a fondo, e assicurati che i tuoi backup siano testati. Poi smetti di pensare al database e costruisci il prodotto.

Portfolio · Cartiglio
Disegnato da
G. STANCUTA
Disciplina
AI & AUTOMATION
Luogo
MORTER · SÜDTIROL
Stato
Disponibile
Lingue
IT · EN · RO · DE+
Stack
PLOI · HETZNER
Revisione
REV 2026.A
2026

© 2026 Gabriel Stancuta · jumpinotech.com — Progettato con l'AI, costruito per funzionare da solo.