Communitygithub.com

Prisma Patterns

Prisma ORM-Muster für TypeScript-Backends – Schema-Design, Query-Optimierung, Transaktionen, Paginierung und kritische Fallstricke wie updateMany, das nur eine Anzahl statt Datensätze zurückgibt, $transaction-Zeitüberschreitungen, migrate dev, das die DB zurücksetzt, @updatedAt, das bei Bulk-Schreibvorgängen übersprungen wird, und Verbindungserschöpfung in serverlosen Umgebungen. Verwenden Sie dies beim Schreiben eines Prisma-Schemas oder einer Query oder beim Debuggen von Transaktionen, Migrationen oder serverlosen Verbindungslimits.

Was ist Prisma Patterns?

Diese Skill bietet eine umfassende Sammlung von Produktionsmustern und nicht offensichtlichen Fallstricken für die Verwendung des Prisma ORM in TypeScript-Backends. Sie behandelt kritische Themen wie Schema-Design mit ID-Strategien, Schema-Standardwerte und Datenbankindizes. Ein besonderer Schwerpunkt liegt auf der Aufklärung über häufige Fehler, wie z.B. dass `updateMany` keine Datensätze, sondern nur eine Anzahl zurückgibt, dass die interaktive Transaktion nach 5 Sekunden standardmäßig abbricht, und dass `migrate dev` bei Schema-Abweichungen die gesamte Datenbank zurücksetzen kann. Die Skill ist plattformagnostisch und für jede Umgebung geeignet, in der Prisma mit TypeScript verwendet wird, einschließlich Next.js, Express, Fastify und anderer Node.js-Frameworks. Sie enthält klare Codebeispiele für Cursor-Paginierung, Soft Deletes, Fehlerbehandlung und serverlose Verbindungspooling-Optimierung. Anti-Patterns werden detailliert erklärt, um teure Debugging-Sitzungen zu vermeiden, insbesondere in serverlosen Umgebungen wie Vercel oder AWS Lambda, wo Verbindungslimits kritisch sind.

Funktioniert mit~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/prisma-patterns

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht Prisma Patterns?

Produktionsmuster und nicht offensichtliche Fallstricke für Prisma ORM in TypeScript-Backends.

Überprüfen Sie Ihre Version, bevor Sie Muster anwenden. Die Prisma-API-Oberfläche hat sich über die großen Versionen hinweg weiterentwickelt:

npx prisma --version

Bemerkenswerte API-Unterschiede zwischen den Versionen:

  • relationJoins kann Beziehungen über JOIN statt separater Abfragen laden, kann aber bei großen 1:N-Beziehungen oder tiefem include zu einer Zeilenexplosion führen – benchmarken Sie beide Ansätze
  • omit Feldmodifikator und die prisma.$extends Client Extensions API wurden hinzugefügt
  • Neuere Installationen: Das Paket kann prisma anstelle von @prisma/client heißen; PrismaClient erfordert möglicherweise einen Treiberadapter (z.B. @prisma/adapter-pg); datasource.url kann sich in prisma.config.ts anstelle von schema.prisma befinden
  • CLI-Befehle (migrate dev, migrate deploy, generate) sind versionsübergreifend unverändert

Wann aktivieren

  • Entwerfen oder Ändern von Prisma-Schema-Modellen und -Beziehungen
  • Schreiben von Abfragen, Transaktionen oder Paginierungslogik
  • Verwendung von updateMany, deleteMany oder einer anderen Bulk-Operation
  • Ausführen oder Planen von Datenbankmigrationen
  • Bereitstellen in serverlosen Umgebungen (Vercel, Lambda, Cloudflare Workers)
  • Implementieren von Soft Delete oder Multi-Tenant-Zeilenfilterung

Kernkonzepte

ID-Strategie

StrategieVerwenden, wennVermeiden, wenn
@default(cuid())Standardwahl – URL-sicher, sortierbar, keine KollisionenSequenzielle IDs für externe Systeme benötigt
@default(uuid())Interoperabilität mit Nicht-Prisma-Systemen erforderlichTabellen mit vielen Schreibvorgängen (zufällige UUIDs fragmentieren B-Tree-Indizes)
@default(autoincrement())Interne Join-Tabellen, Audit-LogsÖffentlich zugängliche IDs (gibt Datensatzanzahl preis)

Schema-Standardwerte

model User {
  id        String    @id @default(cuid())
  email     String    @unique  // @unique erstellt bereits einen Index – kein @@index nötig
  name      String
  role      Role      @default(USER)
  posts     Post[]
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt
  deletedAt DateTime?

  @@index([createdAt])
  @@index([deletedAt, createdAt]) // zusammengesetzt für Soft-Delete + Sortierabfragen
}
  • Fügen Sie @@index für jeden Fremdschlüssel und jede Spalte hinzu, die in WHERE oder ORDER BY verwendet wird.
  • Deklarieren Sie deletedAt DateTime? von Anfang an, wenn Soft Delete absehbar ist – das spätere Hinzufügen erfordert eine Migration auf einer Live-Tabelle.
  • updatedAt @updatedAt wird von Prisma automatisch nur bei update und upsert gesetzt (siehe Anti-Patterns für die Bulk-Update-Falle).

include vs. select

includeselect
RückgabeAlle Skalar-Felder + angegebene BeziehungenNur angegebene Felder
Verwenden, wennSie die meisten Felder plus eine Beziehung benötigenHeiße Pfade, große Tabellen, Überabruf vermeiden
LeistungKann bei breiten Tabellen zu Überabruf führenMinimale Nutzlast, schneller bei großen Datensätzen
Prisma 5 HinweisVerwendet standardmäßig JOIN (relationJoins)Gleich
// include – alle Spalten + Beziehung
const user = await prisma.user.findUnique({
  where: { id },
  include: { posts: { select: { id: true, title: true } } },
});

// select – explizite Positivliste
const user = await prisma.user.findUnique({
  where: { id },
  select: { id: true, email: true, name: true },
});

Geben Sie niemals rohe Prisma-Entitäten aus API-Antworten zurück – mappen Sie auf Antwort-DTOs, um die exponierten Felder zu kontrollieren:

// SCHLECHT: gibt passwordHash, deletedAt, interne Felder preis
return await prisma.user.findUniqueOrThrow({ where: { id } });

// GUT: explizites DTO-Mapping
const user = await prisma.user.findUniqueOrThrow({ where: { id } });
return { id: user.id, name: user.name, email: user.email };

Auswahl der Transaktionsform

SituationVerwenden
Unabhängige Operationen, keine AbhängigkeitenArray-Form
Späterer Schritt hängt von früherem Ergebnis abInteraktive Form
Externe Aufrufe (E-Mail, HTTP) beteiligtAußerhalb der gesamten Transaktion
// Array-Form – in einem Roundtrip gebündelt
const [user, post] = await prisma.$transaction([
  prisma.user.update({ where: { id }, data: { name } }),
  prisma.post.create({ data: { title, authorId: id } }),
]);

// Interaktive Form – nur tx-Client verwenden, niemals den äußeren prisma-Client
const post = await prisma.$transaction(async (tx) => {
  const user = await tx.user.findUniqueOrThrow({ where: { id } });
  if (user.role !== 'ADMIN') throw new Error('Forbidden');
  return tx.post.create({ data: { title, authorId: user.id } });
});

PrismaClient Singleton

Jede PrismaClient-Instanz öffnet ihren eigenen Verbindungspool. Einmal instanziieren.

// lib/prisma.ts

// Option A – adapterbasierte Initialisierung (erforderlich bei neueren Prisma-Installationen)
import { PrismaClient } from '@prisma/client'; // oder der generierte Client-Pfad für Ihr Setup
import { PrismaPg } from '@prisma/adapter-pg';

function createPrismaClient() {
  const adapter = new PrismaPg({
    connectionString: process.env.DATABASE_URL!,
  });
  return new PrismaClient({
    adapter,
    log: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],
  });
}

const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };

export const prisma = globalForPrisma.prisma ?? createPrismaClient();

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;

// Option B – direkte Initialisierung (ältere Installationen, kein Adapter nötig)
// import { PrismaClient } from '@prisma/client';
// export const prisma = globalForPrisma.prisma ?? new PrismaClient({ ... });

Verwenden Sie Option A, wenn Ihre Prisma-Installation ein adapter-Argument im PrismaClient-Konstruktor erfordert. Verwenden Sie Option B, wenn new PrismaClient() ohne Argumente funktioniert. Lassen Sie den Compiler entscheiden, was richtig ist.

Das globalThis-Muster verhindert doppelte Instanzen während des Hot Reloads (Next.js, nodemon, ts-node-dev).

N+1-Problem

Das Laden von Beziehungen innerhalb einer Schleife führt zu einer Abfrage pro Zeile.

// SCHLECHT: N+1 – eine zusätzliche Abfrage pro Benutzer
const users = await prisma.user.findMany();
for (const user of users) {
  const posts = await prisma.post.findMany({ where: { authorId: user.id } });
}

// GUT: einzelne Abfrage
const users = await prisma.user.findMany({ include: { posts: true } });

Mit Prisma 5+ relationJoins verwendet die include-Form einen einzelnen JOIN. Bei großen 1:N-Mengen kann dies die Ergebnisgröße erhöhen – benchmarken Sie beide Ansätze, wenn die Beziehung viele Zeilen pro übergeordnetem Element zurückgeben kann.

Codebeispiele

Cursor-Paginierung (bevorzugt für Feeds und große Datensätze)

async function getPosts(cursor?: string, limit = 20) {
  const items = await prisma.post.findMany({
    where: { published: true },
    orderBy: [
      { createdAt: 'desc' },
      { id: 'desc' }, // sekundäre Sortierung verhindert instabile Paginierung bei doppelten Zeitstempeln
    ],
    take: limit + 1,
    ...(cursor && { cursor: { id: cursor }, skip: 1 }),
  });

  const hasNextPage = items.length > limit;
  if (hasNextPage) items.pop();

  return { items, nextCursor: hasNextPage ? items[items.length - 1].id : null };
}

limit + 1 abrufen und das letzte Element entfernen – kanonischer Weg, um hasNextPage ohne eine zusätzliche Count-Abfrage zu erkennen. Fügen Sie immer ein eindeutiges Feld (z.B. id) als sekundäres orderBy ein, um instabile Paginierung zu verhindern, wenn mehrere Zeilen denselben Zeitstempel haben. Verwenden Sie Offset-Paginierung nur, wenn Benutzer zu beliebigen Seiten springen müssen (Admin-Tabellen).

Soft Delete

// Immer explizit filtern – nicht auf Middleware verlassen (verbirgt Verhalten, schwer zu debuggen)
const activeUsers = await prisma.user.findMany({ where: { deletedAt: null } });

await prisma.user.update({ where: { id }, data: { deletedAt: new Date() } });
await prisma.user.update({ where: { id }, data: { deletedAt: null } }); // wiederherstellen

Fehlerbehandlung

import { Prisma } from '@prisma/client'; // oder der generierte Client-Pfad für Ihr Setup

try {
  await prisma.user.create({ data: { email } });
} catch (e) {
  if (e instanceof Prisma.PrismaClientKnownRequestError) {
    if (e.code === 'P2002') throw new ConflictError('E-Mail existiert bereits');
    if (e.code === 'P2025') throw new NotFoundError('Datensatz nicht gefunden');
    if (e.code === 'P2003') throw new BadRequestError('Referenzierter Datensatz existiert nicht');
  }
  throw e;
}

Häufige Codes: P2002 Unique-Verletzung · P2025 nicht gefunden · P2003 Fremdschlüsselverletzung.

Fangen Sie Fehler an der Service-Grenze ab und übersetzen Sie sie in Domain-Fehler. Geben Sie niemals rohe Prisma-Nachrichten an API-Konsumenten weiter.

Verbindungspool – Serverlos

Betten Sie Verbindungsparameter direkt in DATABASE_URL ein – Zeichenkettenverkettung bricht, wenn die URL bereits Abfrageparameter enthält (z.B. ?schema=public):

# .env – bevorzugt: Parameter in die URL einbetten
DATABASE_URL="postgresql://user:pass@host/db?connection_limit=1&pool_timeout=20"

# Mit externem Pooler (PgBouncer, Supabase pooler)
DATABASE_URL="postgresql://user:pass@host/db?pgbouncer=true&connection_limit=1"
// Vercel, AWS Lambda und ähnliche serverlose Laufzeiten:
// Pool auf 1 pro Instanz begrenzen; connection_limit und pool_timeout werden über DATABASE_URL gesteuert

// Adapterbasiertes Setup (wenn Ihre Prisma-Installation einen Adapter erfordert):
import { PrismaClient } from '@prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';

const prisma = new PrismaClient({
  adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
});

// Direktes Setup (wenn Ihre Prisma-Installation keinen Adapter erfordert):
// const prisma = new PrismaClient();

Anti-Patterns

updateMany gibt eine Anzahl zurück, keine Datensätze

// SCHLECHT: Ergebnis ist { count: 2 } – users[0] ist undefined
const users = await prisma.user.updateMany({ where: { role: 'GUEST' }, data: { role: 'USER' } });

// GUT: zuerst IDs erfassen, dann aktualisieren, dann nur die betroffenen Zeilen abrufen
const targets = await prisma.user.findMany({
  where: { role: 'GUEST' },
  select: { id: true },
});
const ids = targets.map((u) => u.id);
await prisma.user.updateMany({ where: { id: { in: ids } }, data: { role: 'USER' } });
const updated = await prisma.user.findMany({ where: { id: { in: ids } } });

Gleiches gilt für deleteMany – gibt { count: n } zurück, niemals die gelöschten Zeilen.

$transaction interaktive Form bricht nach 5 Sekunden ab

// SCHLECHT: externer Aufruf innerhalb der Transaktion überschreitet den Standard von 5s → „Transaction already closed“
await prisma.$transaction(async (tx) => {
  const user = await tx.user.findUniqueOrThrow({ where: { id } });
  await sendWelcomeEmail(user.email); // externer Aufruf
  await tx.user.update({ where: { id }, data: { emailSent: true } });
});

// GUT: externe Aufrufe außerhalb der Transaktion
const user = await prisma.user.findUniqueOrThrow({ where: { id } });
await sendWelcomeEmail(user.email);
await prisma.user.update({ where: { id }, data: { emailSent: true } });

// Nur Timeout erhöhen, wenn die Bulk-Verarbeitung es wirklich erfordert
await prisma.$transaction(async (tx) => { ... }, { timeout: 30_000 });

migrate dev kann die Datenbank zurücksetzen

migrate dev erkennt Schema-Abweichungen und kann zum Zurücksetzen der DB auffordern, wobei alle Daten gelöscht werden.

# NIEMALS auf gemeinsam genutzten Entwicklungs-, Staging- oder Produktionsumgebungen
npx prisma migrate dev --name add_column

# Sicher überall außer bei lokaler Solo-Entwicklung
npx prisma migrate deploy

# Abweichung prüfen, ohne anzuwenden
npx prisma migrate diff \
  --from-migrations ./prisma/migrations \
  --to-schema-datamodel ./prisma/schema.prisma \
  --shadow-database-url "$SHADOW_DATABASE_URL"

Manuelles Bearbeiten einer Migrationsdatei zukünftige Bereitstellungen

Prisma prüft die Prüfsumme jeder Migrationsdatei. Das Bearbeiten nach der Anwendung verursacht P3006 checksum mismatch in jeder Umgebung, in der die ursprüngliche bereits ausgeführt wurde. Erstellen Sie stattdessen eine neue Migration.

Breaking Schema-Änderungen erfordern mehrstufige Migration

Das Hinzufügen von NOT NULL zu einer vorhandenen Spalte oder das Umbenennen einer Spalte in einer einzigen Migration sperrt die Tabelle oder löscht Daten. Verwenden Sie Expand-and-Contract:

# Schritt 1: Migration lokal erstellen, dann bereitstellen
npx prisma migrate dev --name add_new_column   # nur lokal
npx prisma migrate deploy                       # Staging / Produktion
// Schritt 2: Daten nachträglich füllen (in einem Skript oder Migrationsjob ausführen, nicht in der Shell)
await prisma.user.updateMany({ data: { newColumn: derivedValue } });
# Schritt 3: Migration mit NOT NULL-Constraint lokal erstellen, dann bereitstellen
npx prisma migrate dev --name make_new_column_required  # nur lokal
npx prisma migrate deploy                               # Staging / Produktion

@updatedAt wird bei updateMany nicht ausgelöst

@updatedAt wird automatisch nur bei update und upsert gesetzt. Bulk-Schreibvorgänge lassen es veraltet.

// SCHLECHT: updatedAt bleibt auf seinem alten Wert
await prisma.post.updateMany({ where: { authorId }, data: { published: true } });

// GUT
await prisma.post.updateMany({
  where: { authorId },
  data: { published: true, updatedAt: new Date() },
});

Soft Delete + findUniqueOrThrow gibt gelöschte Datensätze preis

findUniqueOrThrow wirft P2025 nur, wenn die Zeile nicht in der DB existiert. Soft-gelöschte Zeilen existieren noch und werden ohne Fehler zurückgegeben.

findUniqueOrThrow erfordert ein eindeutiges Constraint-Feld in where – das Hinzufügen von deletedAt: null neben id bricht den Typ, da { id, deletedAt } kein zusammengesetztes Unique-Constraint ist. Verwenden Sie stattdessen findFirstOrThrow.

// SCHLECHT: gibt soft-gelöschten Benutzer zurück
const user = await prisma.user.findUniqueOrThrow({ where: { id } });

// SCHLECHT: Prisma-Typfehler – { id, deletedAt } ist kein Unique-Constraint
const user = await prisma.user.findUniqueOrThrow({ where: { id, deletedAt: null } });

// GUT: findFirstOrThrow unterstützt beliebige where-Bedingungen
const user = await prisma.user.findFirstOrThrow({ where: { id, deletedAt: null } });

deleteMany ohne where löscht jede Zeile

// SCHLECHT: löscht die Tabelle stillschweigend
await prisma.post.deleteMany();

// GUT
await prisma.post.deleteMany({ where: { authorId: userId } });

Best Practices

RegelGrund
migrate deploy in CI/CD, migrate dev nur lokalmigrate dev kann die DB bei Abweichungen zurücksetzen
Entitäten auf Antwort-DTOs mappenVerhindert das Preisgeben interner Felder
take + 1 für Cursor-PaginierungErkennt hasNextPage ohne extra Count-Abfrage
Soft-Delete-Filter explizit in jeder Abfrage angebenMiddleware verbirgt das Verhalten und erschwert das Debuggen
Externe Aufrufe aus interaktiven Transaktionen auslagernVermeidet 5-Sekunden-Timeout-Fehler
createdAt als @default(now()) für Default-Werte setzencreatedAt wird bei update nicht durch @updatedAt verwaltet, es sei denn, createdAt @updatedAt ist gesetzt (was nicht sinnvoll ist); createdAt sollte nur @default(now()) sein
Unique-Feld als sekundäres orderBy hinzufügenVerhindert instabile Paginierung bei doppelten Zeitstempeln
Prisma-Version in package.json dokumentierenAPI-Änderungen zwischen Versionen verfolgen
findFirstOrThrow statt findUniqueOrThrow für Soft DeletefindUniqueOrThrow erfordert einen zusammengesetzten Unique-Key; findFirstOrThrow unterstützt beliebige Where-Bedingungen

Zusammenfassung der Versionen

VersionWichtige Änderungen
Prisma 5+Standard-JOINs (relationJoins); omit-Modifikator; Client Extensions
Prisma 6+Adapterbasierte Initialisierung (@prisma/adapter-*); prisma.config.ts; Paket kann prisma statt @prisma/client heißen

So überprüfen Sie Ihre Prisma-Version:

npx prisma --version

So überprüfen Sie, ob Ihre Installation einen Treiberadapter benötigt:

// Wenn der folgende Code einen Fehler wirft, benötigen Sie einen Adapter:
new PrismaClient();

// Wenn nicht (ältere Installation), ist die direkte Initialisierung in Ordnung.

Befolgen Sie diese Muster, um häufige Fallstricke zu vermeiden und Produktionsbereitschaft für Prisma in TypeScript-Backends zu gewährleisten.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Verwandte Skills