MySQL ist immer noch ein solider Standard
- mysql
- prisma
- databases
- infrastructure
Jedes neue Projekt löst dieselbe Debatte aus. Postgres oder MySQL? SQLite? Neon? Hier ist, warum ich immer wieder zu MySQL 8.4 und Prisma 7 greife und es nie bereue.
Es gibt ein bestimmtes Entwicklerritual: Du öffnest ein leeres Repository, brauchst eine Datenbank, und plötzlich sagen dir drei Leute im Internet, dass deine Wahl falsch ist. MySQL ist Legacy. SQLite skaliert nicht. Postgres ist die einzig ernsthafte Option. Neon ist die Zukunft. Ich beobachte diese Schleife seit Jahren. Bei NearYou habe ich MySQL 8.4 mit Prisma 7 und dem mariadb-Adapter gewählt. Es funktioniert einwandfrei. Hier ist die vollständige Begründung.
Langweiligkeit ist ein Feature
MySQL betreibt Produktionsworkloads seit 1995. Das sind drei Jahrzehnte an Randfällen, Fehlerszenarien und Korrekturen, die in die Codebasis eingeflossen sind. Wenn nachts um 2 Uhr etwas kaputt geht, findest du einen Stack-Overflow-Thread aus dem Jahr 2014, der das Problem bereits gelöst hat. Das ist keine Kleinigkeit. Neue Datenbanken sind in Blogbeiträgen aufregend und in der Produktion erschöpfend.
MySQL 8.4 ist ein LTS-Release. Bugfixes erscheinen nach einem vorhersehbaren Zeitplan. Die Storage Engine ist InnoDB, die dir Row-Level-Locking, Fremdschlüssel-Enforcement und ACID-Transaktionen standardmäßig bietet. Du musst nichts davon konfigurieren. Es ist aktiv. Der Optimierer behandelt die meisten Abfragepläne korrekt ohne Hints. Die Replikation ist ausgereift. Wenn du später Read Replicas benötigst, fügst du sie hinzu, ohne den Anwendungscode zu ändern.
Die beste Datenbank für ein neues Projekt ist jene, über die du nie wieder nachdenken musst.

UTF8MB4 von Anfang an
Ein echter historischer MySQL-Fehler war der utf8-Zeichensatz, der eigentlich eine 3-Byte-Kodierung war, die die meisten CJK-Zeichen und alle Emojis nicht speichern konnte. Diese Kodierung heißt jetzt utf8mb3 und ist veraltet. Der Ersatz ist utf8mb4, das echtes 4-Byte-UTF-8 ist und alles speichert: Arabisch, Hebräisch, Japanisch, Koreanisch, Thailändisch und ja, jedes Emoji, das deine Nutzer dir schicken werden.
NearYou hat Nutzer in Südtirol, daher kommen Inhalte auf Deutsch, Italienisch, Ladinisch und Rumänisch an. Zeichensatz und Kollation einmalig auf Datenbankebene festzulegen bedeutet, dass du auf Anwendungsebene nie mehr darüber nachdenken musst.
Ein Container neben der Anwendung
Für die Entwicklung und für kleine Produktionsdeployments läuft MySQL in einem Docker-Container auf demselben Host wie die Anwendung. Die Netzwerklatenz sinkt auf unter eine Millisekunde. Es gibt keinen verwalteten Dienst, der dir Leerlaufzeit in Rechnung stellt. Das Setup sind vier Zeilen Docker Compose.
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:Der Healthcheck ist wichtig. Ohne ihn kann der App-Container starten, bevor MySQL seine Init-Sequenz abgeschlossen hat, und der Connection Pool schlägt bei der ersten Abfrage fehl. depends_on mit condition: service_healthy löst das sauber.
Prisma 7: Schema, Migrationen, Typen
Prisma ist der Grund, warum dieser Stack wirklich angenehm zu benutzen ist. Du schreibst eine einzige Schema-Datei. Prisma generiert SQL-Migrationen daraus, führt sie gegen die Datenbank aus und generiert einen TypeScript-Client mit Typen, die genau zu jedem Modell passen. Keine Laufzeit-Typüberprüfungen. Keine handgeschriebenen SQL-Strings. Keine ORM-Magie, die stillschweigend N+1-Abfragen ausführt.
Prisma 7 hat erstklassige Unterstützung für den mariadb-Provider hinzugefügt, der MySQL 8.4 mit besserer Kompatibilität als der Legacy-Provider mysql für bestimmte Randfälle rund um Datetime-Präzision und JSON-Handling abdeckt. Bei NearYou verwenden wir ihn explizit.
// 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
}Sobald das Schema an Ort und Stelle ist, erledigen zwei Befehle alles.
# 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 generateDie Migrationsdatei ist SQL. Sie lebt in der Versionskontrolle. Du kannst sie lesen, in einem PR überprüfen, manuell zurückrollen, wenn etwas schiefläuft. Es gibt keinen versteckten Zustand. Der Befehl prisma migrate deploy führt ausstehende Migrationen in CI oder beim Serverstart aus. Der gesamte Ablauf ist vorhersehbar.
Wie ein KI-Agent mit diesem Stack arbeitet
Ich verwende Claude Code als KI-Coding-Agent bei NearYou. Der Agent schreibt Prisma-Schemata, generiert Migrationen, fragt die Datenbank über den Client ab und behebt Verbindungsprobleme. Damit das zuverlässig über Sitzungen hinweg funktioniert, benötigt der Agent persistentes Gedächtnis der genauen Einrichtung: welcher Provider, welche MySQL-Version, welche Konventionen wir befolgen, welche Fallstricke uns bereits getroffen haben.
Ich löse dies mit einer projektbezogenen AGENTS.md-Datei, die der Agent bei jeder Sitzung liest. Es ist keine Dokumentation für Menschen. Es ist eine kompakte, präzise Kontextdatei, die dem Agenten mitteilt, wie er sich mit diesem spezifischen Stack verhalten soll. Der Agent muss nicht neu entdecken, dass wir mariadb als Prisma-Provider verwenden, dass Migrationen in prisma/migrations/ liegen oder dass das DSN-Format für unser Docker-Compose-Setup den Servicenamen db als Hostname verwendet.

Hier ist der tatsächliche Datenbankabschnitt aus der AGENTS.md von NearYou. Er ist kurz. Er ist präzise. Jede Zeile verdient ihren Platz.
## 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.Mit dieser Datei fragt der Agent nie, welchen Provider er verwenden soll, rät nie das DSN-Format und wiederholt nie einen Fehler, den wir bereits dokumentiert haben. Er liest die Datei, verinnerlich die Einschränkungen und bedient den Stack beim ersten Versuch korrekt. Die AGENTS.md wird zum Langzeitgedächtnis, das Kontextfenster-Resets überlebt.
Die eine Regel: echte, getestete Backups
Alles oben Genannte ist komfortabel. Das einzige Nicht-Verhandelbare sind Backups. Nicht ein Backup-Cron, von dem du annimmst, dass er funktioniert. Tatsächlich getestete Wiederherstellungen.
- Führe
mysqldump --single-transaction --routines --triggerstäglich als Minimum aus. Das Flag--single-transactionvermeidet Tabellensperren bei InnoDB. - Versende den Dump sofort an einen externen Ort. Backups auf demselben Host sind keine Backups, sie sind ein falsches Sicherheitsgefühl.
- Teste die Wiederherstellung monatlich. Starte einen frischen Container, leite den Dump ein, überprüfe die Zeilenanzahl in kritischen Tabellen.
- Wenn du bei einem verwalteten Host (Railway, PlanetScale, Render) bist, prüfe, ob ihre Point-in-Time-Recovery tatsächlich deine Datenaufbewahrungsanforderungen abdeckt. Nimm nichts als selbstverständlich an.
- Prisma-Migrationen sind kein Backup. Sie erstellen das Schema neu, aber keine Daten.
Das Urteil
MySQL 8.4 mit Prisma 7 und dem mariadb-Adapter ist nicht glamourös. Es hat kein glänzendes neues Paper, das man zitieren könnte. Es hat dreißig Jahre Produktionshärtung, ein ausgereiftes Ökosystem, ausgezeichnete Werkzeuge und null Überraschungen im Betrieb. Für NearYou ist es die richtige Wahl. Für die meisten neuen Projekte ist es ein vollkommen vertretbarer Standard. Wähle es, konfiguriere utf8mb4 von Tag eins an, schreibe eine echte AGENTS.md, damit dein KI-Agent die Einrichtung in- und auswendig kennt, und stelle sicher, dass deine Backups getestet sind. Dann hör auf, über die Datenbank nachzudenken, und baue das Produkt.