designing-schemas

star 1

Design normalized PostgreSQL schemas from tech specs, with proper constraints, naming conventions, and standard columns.

7a336e6e By 7a336e6e schedule Updated 2/6/2026

name: Designing Schemas description: Design normalized PostgreSQL schemas from tech specs, with proper constraints, naming conventions, and standard columns.

Goal

Translate data entities from a tech spec into well-structured PostgreSQL tables that follow normalization rules, enforce data integrity through constraints, and use consistent naming conventions.

When to Use

  • Starting a new feature that requires new database tables
  • Refactoring an existing schema to fix normalization issues
  • Reviewing a proposed data model before writing migrations
  • Adding relationships between existing entities

Instructions

1. Identify Entities and Relationships

Read the tech spec and extract all data entities, their attributes, and the relationships between them. Map each entity to a table and each relationship to a foreign key or join table.

2. Apply Normalization

Normalize to Third Normal Form (3NF) by default. Every non-key column must depend on the key, the whole key, and nothing but the key. Denormalization is acceptable only with explicit justification (e.g., read-heavy access patterns with measured performance impact).

3. Define Primary Keys

Prefer UUID for distributed systems or BIGSERIAL for single-database deployments. Every table must have a column named id as its primary key.

4. Add Standard Columns

Every table must include these columns:

  • id — primary key
  • created_at TIMESTAMPTZ NOT NULL DEFAULT now()
  • updated_at TIMESTAMPTZ NOT NULL DEFAULT now()

5. Define Constraints and Indexes

Add foreign keys for all relationships. Add NOT NULL to every column unless there is a clear reason for allowing nulls. Add CHECK constraints for value boundaries. Create indexes on all foreign key columns.

6. Example Schema

CREATE TABLE users (
    id            BIGSERIAL PRIMARY KEY,
    email         VARCHAR(255) NOT NULL,
    password_hash VARCHAR(128) NOT NULL,
    display_name  VARCHAR(100) NOT NULL,
    created_at    TIMESTAMPTZ  NOT NULL DEFAULT now(),
    updated_at    TIMESTAMPTZ  NOT NULL DEFAULT now(),

    CONSTRAINT uq_users_email UNIQUE (email),
    CONSTRAINT ck_users_email_format CHECK (email ~* '^.+@.+\..+$')
);

CREATE INDEX ix_users_email ON users (email);
CREATE TABLE task_assignments (
    id         BIGSERIAL   PRIMARY KEY,
    user_id    BIGINT      NOT NULL REFERENCES users (id) ON DELETE CASCADE,
    task_id    BIGINT      NOT NULL REFERENCES tasks (id) ON DELETE CASCADE,
    assigned_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),

    CONSTRAINT uq_task_assignments_user_task UNIQUE (user_id, task_id)
);

CREATE INDEX ix_task_assignments_user_id ON task_assignments (user_id);
CREATE INDEX ix_task_assignments_task_id ON task_assignments (task_id);

7. Follow Naming Conventions

Refer to the naming conventions reference for all naming decisions. Use snake_case throughout. Table names are plural, column names are singular.

Constraints

✅ Do

  • Use NOT NULL by default on every column; allow NULL only with explicit justification
  • Add CHECK constraints for value ranges and formats
  • Define indexes on all foreign key columns
  • Include id, created_at, and updated_at on every table
  • Use TIMESTAMPTZ for all timestamp columns
  • Add ON DELETE behavior (CASCADE, SET NULL, or RESTRICT) to every foreign key
  • Document denormalization decisions with comments in the SQL

❌ Don't

  • Use VARCHAR without a length limit; always specify a max length
  • Skip foreign key constraints for convenience
  • Use reserved SQL words (user, order, group) as table or column names
  • Create circular foreign key dependencies
  • Use floating point types for monetary values; use NUMERIC instead
  • Add surrogate keys to pure join tables unless they need independent identity

Output Format

Produce a SQL file containing all CREATE TABLE, CREATE INDEX, and ALTER TABLE statements. Group related tables together. Include comments explaining non-obvious design decisions. End with a summary listing all tables and their relationships.

Dependencies

Install via CLI
npx skills add https://github.com/7a336e6e/skills --skill designing-schemas
Repository Details
star Stars 1
call_split Forks 2
navigation Branch main
article Path SKILL.md
More from Creator