Three good reasons to reach for JSONB
- Genuinely variable shape: event payloads from many sources, audit logs, third-party API snapshots.
- Sparse, optional fields: 50 possible attributes, any given row uses 5-10 of them.
- Bag of settings or flags: user preferences where the set of keys evolves over time.
Three bad reasons (don't)
- "To avoid schema migrations." The migration cost is real, but the schema-less cost is bigger — you push the validation problem onto every consumer of the data.
- "Because it's flexible." Flexibility is fine; uncontrolled flexibility is technical debt.
- "Because the ORM made it easy." The ORM doesn't run queries against your data — your future self does.
The hybrid approach
The right pattern is usually: stable fields in real columns (with NOT NULL, CHECK, FK), variable bits in a JSONB sidecar. You get the integrity of relational + the flexibility of JSON, with no compromise on either.