| name | db-schema |
| description | Apply when designing database schemas, writing migrations, or reviewing table structure. Covers naming, keys, indexes, constraints, nullability, and migration safety. |
| license | MIT |
| version | 1.0.0 |
| tokens_target | 1900 |
| triggers | database schema, migration, table design |
| loads_after | |
| supersedes |
Sub-Skill: Database Schema Design
Purpose: Prevent data-integrity bugs, race conditions, and irreversible migrations by enforcing database-level constraints and safe migration patterns before code reaches production.
Rules
Naming Conventions
- Snake case everywhere. Always use
snake_casefor table names, column names, and index names; never use camelCase or PascalCase in SQL identifiers. - Plural table names. Always name tables in the plural (
users,orders,line_items); singular names cause inconsistency across ORMs and query builders. - Consistent FK column naming. Always name foreign-key columns
<referenced_table_singular>_id(e.g.,user_id,order_id) so the reference is self-documenting.
Primary Keys
- Surrogate primary key. Always define an explicit surrogate primary key (
id) on every table; never rely on natural keys as the sole PK unless the domain guarantees immutability. - UUID vs serial. Prefer
uuidPKs for distributed systems or public-facing IDs; preferbigserial/bigint GENERATED ALWAYS AS IDENTITYfor high-insert internal tables where sort order matters.
Foreign Keys & Referential Integrity
- Declare FK constraints. Always declare
FOREIGN KEYconstraints in the schema; never rely on application-layer join logic to enforce referential integrity. - Explicit ON DELETE behaviour. Always specify
ON DELETEbehaviour (CASCADE,SET NULL, orRESTRICT) on every FK; omitting it defaults toRESTRICT, which can cause surprising errors at runtime. - Missing FK is a data-integrity bug. Never ship a column that references another table without a corresponding FK constraint — orphaned rows accumulate silently. Reference: ERR-2026-022
Indexes
- Index every FK column. Always add an index on every foreign-key column; unindexed FKs cause full-table scans on joins and cascade operations.
- Covering indexes for hot queries. Use covering indexes (include all columns in the
SELECT) for read-heavy queries rather than adding redundant columns to the base table. - Unique constraints at DB level. Always enforce uniqueness at the database level rather than relying on application-layer deduplication; race conditions between concurrent writes bypass app checks. Reference: ERR-2026-022
Nullability & Defaults
- Explicit NOT NULL. Always mark columns
NOT NULLunlessNULLcarries distinct semantic meaning (unknown vs. absent); nullable columns complicate query logic and ORM mapping. - Meaningful defaults. Always supply a
DEFAULTfor columns that have a sensible zero-value (0,'',false,now()); avoid forcing callers to supply values the DB can compute.
Timestamps & Soft Delete
- Standard audit columns. Always include
created_at TIMESTAMPTZ NOT NULL DEFAULT now()andupdated_at TIMESTAMPTZ NOT NULL DEFAULT now()on every table; omitting them makes debugging and auditing impossible. - Soft delete with deleted_at. Prefer
deleted_at TIMESTAMPTZover hard deletes for user-facing entities; ensure all queries filterWHERE deleted_at IS NULLor use a view/partial index.
Enum Columns
- DB-native enum or check constraint. Always enforce enum-like columns with a
CHECKconstraint or a nativeENUMtype; never store unconstrained strings and validate only in the application.
Migrations
- Up and down migrations. Always write both an
upand adownmigration; a rollback path is mandatory before any migration reaches production. - Idempotent migrations. Always guard DDL statements with
IF NOT EXISTS/IF EXISTSso re-running a migration does not error; this is required for blue-green and multi-replica deployments. - Non-blocking index creation. Use
CREATE INDEX CONCURRENTLY(Postgres) or equivalent for indexes on large tables; blocking index creation causes downtime.
Constraints Over App Logic
- Check constraints for invariants. Always encode business invariants (e.g.,
price > 0,quantity >= 0) asCHECKconstraints; application-layer validation is bypassed by direct DB writes, scripts, and migrations.
See also
skills/code-quality/SKILL.md— general code-quality rules that apply to migration scriptsskills/review-deployment/SKILL.md— deployment checklist that includes migration safety gates