G.STANCUTA
Veröffentlicht · 2026 · 05 · 227 Min. Lesezeit

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.

Isometrisches Diagramm eines MySQL-Containers neben einem App-Container mit Prisma als Verbindungsschicht
Der vollständige Stack: App-Container, Prisma-Client, MySQL 8.4 in einem Sidecar-Container.

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.

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:

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.

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
}

Sobald das Schema an Ort und Stelle ist, erledigen zwei Befehle alles.

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

Die 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.

Schematisches Diagramm eines KI-Agenten, der eine Markdown-Gedächtnisdatei liest und Datenbankmigrationsbefehle ausführt
Der Agent liest AGENTS.md beim Sitzungsstart und handelt dann für die gesamte Sitzung zuverlässig danach.

Hier ist der tatsächliche Datenbankabschnitt aus der AGENTS.md von NearYou. Er ist kurz. Er ist präzise. Jede Zeile verdient ihren Platz.

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.

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 --triggers täglich als Minimum aus. Das Flag --single-transaction vermeidet 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.

Portfolio · Schriftfeld
Gezeichnet von
G. STANCUTA
Disziplin
AI & AUTOMATION
Standort
MORTER · SÜDTIROL
Status
Verfügbar
Sprachen
IT · EN · RO · DE+
Stack
PLOI · HETZNER
Revision
REV 2026.A
2026

© 2026 Gabriel Stancuta · jumpinotech.com — Mit KI entworfen, gebaut, um sich selbst zu betreiben.