⚡DEFAULT
Value supplied when INSERT omits the column
DEFAULT participates in row construction: if an INSERT does not name a column (or names it but passes DEFAULT), the engine substitutes the configured expression, literals, NOW(), gen_random_uuid(), sequence nextval, etc. Changing a default does not rewrite historical rows; only new inserts see the new expression unless you run an UPDATE.
Timestamp default + insert omitting created_at
CREATE TABLE audit.events (
event_id BIGSERIAL PRIMARY KEY,
payload JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
INSERT INTO audit.events (payload) VALUES ('{"k":1}'::jsonb)
RETURNING event_id, created_at;| event_id | created_at |
|---|---|
| 90012 | 2026-04-12 10:01:02+00 |
DEFAULT 0 for “unknown quantity”
Downstream averages treat imputed zeros as measurements, control charts and regulated submissions drift.
NULL + NOT NULL policy, or a separate is_estimated flag
Reserve defaults for true neutral baselines (created_at, version = 1); encode missing science data as NULL with explicit handling in analytics contracts.
📦Bulk load paths
COPY, CSV loaders, and ORMs may bypass defaults unless configured.
When ETL supplies explicit NULL or empty strings, defaults do not rescue you, validate staging files against the same rules as OLTP inserts.
Volatile defaults
Random/UUID defaults per row are fine when intended; document whether the same statement should generate distinct values for multi-row inserts on your engine.
API contracts
If clients must see defaulted fields in responses, return them explicitly from queries or projections, do not assume serializers infer DB defaults.
⚡ Advanced / dialect note
ALTER COLUMN SET DEFAULT updates metadata for future rows only; pair with a one-time UPDATE … WHERE col IS NULL when tightening semantics on existing data.Documentation
For every DEFAULT that encodes policy (not just NOW()), add a one-line comment tying it to the ticket or ADR so future migrations know whether it is safe to change.
TL;DR
A DEFAULT supplies a column value when an INSERT omits that column (or explicitly inserts DEFAULT). It is metadata evaluated at insert time, changing a default does not rewrite existing rows. Use constants or simple functions like CURRENT_TIMESTAMP; complex policy belongs in application code or triggers if defaults would hide business rules.