The blueprint phase
Designing a schema is drawing the blueprint before pouring concrete. A good blueprint saves months of renovation later. A bad one means re-routing plumbing after the residents have moved in.
Six habits that keep schemas honest
- Normalise first; denormalise on measured pain. Every entity in its own table; foreign keys connecting them. Denormalisation (computed totals, duplicated names) is a performance optimisation, not a starting point.
- snake_case everywhere:
users,order_items,created_at. Plural tables, singular columns,table_idfor foreign keys. Consistency beats taste. - Always include
created_atandupdated_atasTIMESTAMPTZ NOT NULL DEFAULT now(). Add a trigger that bumpsupdated_aton every UPDATE. You will need this for debugging, syncing, and audit. Add it now or regret it later. - NOT NULL by default; allow NULL by exception.
- Constraints are documentation — every CHECK, UNIQUE, and FK encodes a rule the application can rely on.
- Soft delete instead of hard delete when audit trails matter — add
deleted_at TIMESTAMPTZand exclude rows with non-null deleted_at from queries. (Don't soft-delete by default; pick when it's worth it.)
The auto-bumping updated_at trigger
Worth memorising — you'll write this trigger on most tables.