database-schema-design · v2.0.0 · 2026-04-14 · sha256 2080f8ca3cd2e459

database-schema-design v2.0.0A

Immutable. This exact content is served forever at /api/v1/blob/2080f8ca3cd2e459.

---
name: database-schema-design
description: >
  Design or refactor application data models and schema evolution plans for relational
  and document databases. Use when the user needs entity boundaries, table/collection
  shape, cardinality, constraints, indexes, naming, multi-tenant or audit patterns,
  migration sequencing, or rollout-safe schema changes before or alongside backend
  implementation. Not for API contract design, auth setup, published docs, or backend
  test planning.
allowed-tools: Bash Read Write Edit Glob Grep
compatibility: >
  Best for backend, product, and fullstack work in web apps and APIs where schema
  choices must balance developer ergonomics, query patterns, integrity rules, and
  production-safe migrations across PostgreSQL, MySQL, SQLite, MongoDB, and similar
  stacks.
license: MIT
metadata:
  version: "2.0.0"
  modernization: 2026-04-14
  tags: database, schema-design, migrations, indexing, constraints, relational, nosql, backend
  platforms: Claude, ChatGPT, Gemini
---

# Database Schema Design

Use this skill when the main job is **choosing and evolving the storage model for a real product**, not dumping generic SQL examples.

`database-schema-design` owns the storage-design layer for:
- choosing relational vs document-heavy vs hybrid data shapes
- turning domain entities into tables, collections, join models, and ownership boundaries
- deciding keys, uniqueness, cardinality, nullability, lifecycle state, and deletion/audit rules
- planning indexes, partitioning, and read-vs-write tradeoffs
- sequencing safe schema evolution so migrations, backfills, and cleanup do not get hand-waved
- producing a design packet that implementation, auth, API, and testing work can share

Read these support docs before handling larger or riskier work:
- [references/storage-decision-matrix.md](references/storage-decision-matrix.md)
- [references/schema-review-checklist.md](references/schema-review-checklist.md)

## When to use this skill
- Design a new database schema for a product feature, service, admin app, marketplace, or internal tool
- Refactor an existing schema that has naming drift, weak constraints, poor cardinality modeling, or missing indexes
- Decide between normalized relational tables, document/JSON fields, event-style append models, or a deliberate hybrid
- Plan multi-tenant, soft-delete, audit-log, status-history, or entitlement-related storage patterns
- Review whether a migration is safe, reversible enough, and staged realistically for production traffic
- Choose indexes and data-access shape based on actual query patterns instead of guesswork
- Produce a storage-model packet before backend implementation starts or while a risky backend change is being designed

## When not to use this skill
- **The main job is REST/GraphQL interface shape, endpoint naming, versioning, or error semantics** → use `api-design`
- **The main job is product-auth provider choice, sessions/JWTs, login methods, or auth-owned user/org boundaries** → use `authentication-setup`
- **The main job is developer-facing docs, quickstarts, or example-heavy API/database documentation** → use `api-documentation`
- **The main job is backend regression coverage, contract tests, migration-test strategy, or CI-vs-local verification** → use `backend-testing`
- **The main job is broader security hardening such as secret handling, CSRF, cookie flags, or OWASP controls** → use `security-best-practices`
- The request has no concrete domain, access pattern, or lifecycle context yet; in that case define the open questions instead of pretending the schema is settled

## Instructions

### Step 1: Classify the storage-design job
Normalize the request before naming tables.

```yaml
schema_design_profile:
  domain: unknown
  workload_shape: oltp | analytics | event-log | content-heavy | mixed | unknown
  data_model_lane: relational | document-heavy | hybrid | unknown
  tenancy: single-tenant | shared-tenant | isolated-tenant | mixed | unknown
  change_type: greenfield | incremental | migration | cleanup | scale-fix
  durability_needs: basic | transactional | audit-heavy | compliance-sensitive | unknown
  traffic_shape: read-heavy | write-heavy | mixed | bursty | unknown
  scale_notes: small | moderate | large | unknown
  main_risks: integrity | performance | migration-safety | data-lifecycle | unclear
```

Ask or infer:
1. What product/domain entities exist, and which ones truly own the workflow?
2. What are the highest-value reads, writes, filters, joins, aggregates, and retention rules?
3. Is the task greenfield design, incremental change, or repairing drift in a live system?
4. Which constraints are real business rules versus implementation convenience?
5. What databases, ORMs, or platform constraints already exist?

### Step 2: Choose the data-model lane deliberately
Do not default to a database style because it is fashionable.

#### Prefer relational-first when
- data integrity, transactions, and clear entity relationships matter most
- the product has strong cardinality rules and shared business invariants
- reporting, joining, and consistency matter more than free-form document flexibility
- the team needs constraints and indexes to carry real business meaning

#### Prefer document-heavy when
- entity shape varies a lot across records
- nesting is natural and most reads happen around one aggregate document
- the team accepts weaker cross-document integrity and more application-owned invariants
- the product values ingestion flexibility more than relational rigor

#### Prefer hybrid when
- transactional core data is relational but some secondary metadata is naturally JSON/document-shaped
- the team can explain which fields are query-critical and which are flexible payloads
- there is a clear reason not to force every attribute into first-class columns

State the reason for the chosen lane. “Because the stack already has it” is useful context, not a full design rationale.

### Step 3: Model entities, ownership, and lifecycle
Turn the domain into explicit storage boundaries.

For each core entity or collection, define:
- purpose and ownership boundary
- stable identifier strategy
- required vs optional attributes
- lifecycle states and timestamps
- who references it and at what cardinality
- whether deletion should be hard, soft, archived, or evented
- whether tenant/org ownership, audit fields, or history tables are required

Watch for classic modeling traps:
- mixing lookup/reference data with user-generated mutable state
- hiding many-to-many relationships inside ad hoc arrays or comma-separated fields
- collapsing status history into a single overwritten status when history matters
- storing every variable field as JSON without a query/access plan
- letting auth/profile/org membership data blur into unrelated product entities

### Step 4: Design integrity rules and access patterns together
A schema is only as good as the invariants it enforces.

For relational lanes, define:
- primary keys and foreign keys
- uniqueness rules
- nullability and defaults
- check constraints and enum/state rules
- join tables and ownership direction

For document-heavy lanes, define:
- aggregate/document boundary
- embedded vs referenced substructures
- per-document uniqueness assumptions
- fields that still need app-level invariants or background reconciliation

For either lane, explicitly list:
- hottest reads and filters
- write amplification risks
- query patterns that justify indexes
- fields that need full-text, partial, composite, or time-based indexing
- places where denormalization is intentional rather than accidental

If you cannot name the main reads/writes, the indexing advice is probably fake.

### Step 5: Plan schema evolution, not just the end state
The design is incomplete if rollout safety is missing.

For incremental or live-system changes, define:
- what changes are additive, backfill-driven, destructive, or compatibility-sensitive
- whether the migration should be expand-and-contract
- when constraints/indexes are added relative to application deploys
- whether data backfill, dual write, or shadow read phases are needed
- what the rollback or stop condition looks like
- which old fields/tables can only be removed after traffic and job cleanup

Good defaults:
- add new columns/tables before switching reads/writes
- backfill before enforcing new non-null or uniqueness rules when possible
- treat destructive renames/drops as staged work, not one-step bravery
- call out long-running backfills, lock risk, and index build cost explicitly

### Step 6: Produce the storage design packet
Pick the lightest artifact that still gives downstream work a trustworthy map.

Recommended artifacts:
- **entity/table design memo** for early architecture work
- **schema review packet** for PRD/spec/ADR-driven changes
- **migration rollout plan** for live-system evolution
- **ERD + decisions table** when collaboration and review are the main need

Minimum packet:
- workload and data-model lane with rationale
- entity / collection map
- key integrity rules and lifecycle decisions
- index/access-pattern notes
- migration or rollout plan if the system is live
- open questions and risk notes
- handoffs to adjacent skills

### Step 7: Review for boundary quality and future pain
Before finalizing, check:
- does the model encode real business rules or just mirror current code objects?
- are lifecycle, retention, audit, and deletion rules explicit enough?
- are indexes justified by named queries, not superstition?
- did you accidentally turn API shape, auth setup, or testing strategy into part of this skill?
- is the migration path believable for a live system?
- will another engineer understand what is normalized, denormalized, or intentionally flexible?

Route next steps clearly:
- `api-design` for interface/contract shape that depends on the data model
- `authentication-setup` for user/org/session/provider ownership decisions
- `backend-testing` for migration verification, repository coverage, or contract/regression checks
- `api-documentation` for published docs/examples once the model is stable enough to explain
- `security-best-practices` for hardening that goes beyond data integrity modeling

## Output format

```markdown
## Storage Design Packet: [System or Feature]

### Design framing
- Domain: [...]
- Workload shape: [OLTP / analytics / content-heavy / mixed]
- Chosen lane: [relational / document-heavy / hybrid]
- Reason: [...]

### Entity / collection map
| Entity | Purpose | Key fields | Relationships / ownership | Lifecycle notes |
|--------|---------|------------|----------------------------|-----------------|
| ... | ... | ... | ... | ... |

### Integrity and access rules
- Required constraints: [...]
- Uniqueness rules: [...]
- Indexes and why: [...]
- Intentional denormalization / JSON usage: [...]

### Migration / rollout plan
- Change type: [greenfield / additive / staged migration / cleanup]
- Sequence: [...]
- Backfill / compatibility notes: [...]
- Removal / cleanup conditions: [...]

### Risks / open questions
- [...]

### Handoffs
- API contract: [...]
- Auth/data ownership: [...]
- Verification/testing: [...]
- Docs: [...]
```

## Examples

### Example 1: SaaS billing and organization model
**Input:** “We need schema help for a B2B SaaS app with users, orgs, memberships, subscriptions, invoices, and audit logs. We use Postgres and need something implementation-ready, not just an ERD.”

**Good response shape:**
- chooses relational-first because org membership, billing, and audit rules carry real integrity constraints
- separates `users`, `organizations`, `memberships`, `subscriptions`, and immutable invoice records clearly
- defines uniqueness and lifecycle rules for invites, membership role changes, and subscription history
- calls out audit/event tables separately from mutable entity state
- routes auth-provider/session details to `authentication-setup`
- includes index suggestions tied to named reads such as org membership lookup, invoice history, and subscription status review

### Example 2: Live migration from flexible JSON to structured fields
**Input:** “Our marketplace has a `products.metadata` JSON blob. Search, filtering, and moderation now depend on stable fields. We need a safe migration plan without breaking production.”

**Good response shape:**
- keeps the design focused on storage boundaries, not API docs
- identifies which metadata fields deserve first-class columns and which can remain JSON
- proposes expand-and-contract: add columns, backfill, dual read/write, then clean up
- notes index timing and backfill risk explicitly
- routes interface changes to `api-design` and migration verification to `backend-testing`

### Example 3: Document-heavy content workflow
**Input:** “We ingest user-authored page blocks with highly variable nested content, but still need account ownership and search.”

**Good response shape:**
- justifies a hybrid or document-heavy lane instead of forcing over-normalization
- separates stable ownership/account structures from flexible content payloads
- names which fields must stay queryable and therefore deserve indexes or extracted columns
- records lifecycle/retention choices for revisions and drafts

## Best practices
1. Start from business invariants and access patterns, not table aesthetics.
2. Treat migration safety as part of schema design for live systems.
3. Use JSON/document flexibility intentionally, not as a way to postpone modeling forever.
4. Name the reads and writes that justify every important index.
5. Separate mutable entity state, history/audit data, and derived/cache data.
6. Keep adjacent handoffs explicit so this skill does not absorb API, auth, docs, or test work.
7. Prefer a durable design packet over giant vendor-specific SQL dumps.

## References
- [Evolutionary Database Design — Martin Fowler](https://martinfowler.com/articles/evodb.html)
- [Prisma Migrate mental model](https://www.prisma.io/docs/orm/prisma-migrate/understanding-prisma-migrate/mental-model)
- [PostgreSQL Data Definition docs](https://www.postgresql.org/docs/current/ddl.html)
- [DBML docs](https://dbml.dbdiagram.io/docs)