Why we reach for Postgres first (and second)
Most products do not need a new database. They need someone who knows Postgres well enough to stop reaching for new ones. Here is our default, and the few times we break it.
There is a phase in every young product where someone suggests a new database. A document store because the schema “keeps changing.” A vector DB because there’s an AI feature. A separate time-series database because there are metrics. A graph database because two tables have a foreign key.
Our default answer is the same almost every time: use Postgres. Then, if you still have a problem, use Postgres differently.
The case is almost always overstated
The usual argument for a new datastore is a feature you could get out of Postgres with a little less fashion and a lot less operational risk.
- “Our schema keeps changing.” That’s what
jsonbcolumns and migrations are for. You get flexible documents and the option to add constraints later when the shape settles. - “We need full-text search.” Postgres has had it for years.
tsvectorplus a GIN index handles more traffic than most products will ever see. - “We’re adding semantic search.”
pgvectorstores embeddings and does approximate nearest-neighbour search in the same database as the rest of your data. - “We need a queue.”
SELECT ... FOR UPDATE SKIP LOCKEDis a perfectly good queue until you’re genuinely at scale.
None of these are clever tricks. They’re documented, boring features that have been load-bearing in production for a decade.
What you’re actually trading when you add a datastore
Every new database in your stack is not one new thing. It’s:
- A new failure mode in production
- A new backup and restore story you have to test
- A new set of access controls and secrets
- A new consistency boundary — now you have data in two places that can disagree
- A new thing every future engineer has to learn before they can be useful
That last one is the quiet killer. A second datastore doesn’t just cost you ops time. It raises the floor on what “competent here” means, which makes hiring and onboarding slower forever.
When we do break the rule
Boring is a default, not a religion. We’ve happily added other stores when the problem genuinely outgrew Postgres:
- Redis when we need a real cache or ephemeral state with sub-millisecond reads, and losing it on restart is fine.
- A dedicated search cluster (Elasticsearch/OpenSearch) when search is a primary product surface with relevance tuning, not a
WHERE title ILIKE. - An object store (S3 and friends) for blobs, because blobs do not belong in a relational database. Ever.
- A real analytics warehouse (BigQuery, Snowflake, ClickHouse) once analytical queries start fighting your transactional workload for resources.
Notice the pattern: we add a store when the workload is genuinely different in kind, not when it’s the same workload wearing a trendier hat.
The boring superpower is knowing one thing deeply
The teams that move fastest aren’t the ones with the most exotic stacks. They’re the ones where everyone knows the primary datastore cold — how it locks, how it plans queries, how EXPLAIN ANALYZE reads, what a missing index looks like under load.
You get that depth by staying on one thing long enough to learn its edges. Every new database resets that clock.
So: Postgres first. Postgres second. And when you do reach for something else, do it because the workload demanded it — not because a conference talk did.