SQL, NoSQL, and why PostgreSQL became the default database anyway

James Okonkwo

James Okonkwo

September 18, 2026

SQL, NoSQL, and why PostgreSQL became the default database anyway

I spent the 2010s being told SQL was the past. Mongo would scale. Cassandra would scale. Dynamo would free us from migrations. The companies I actually worked at ended the decade with a Postgres primary and a graveyard of “polyglot persistence” slides.

Postgres did not win because NoSQL was fake. NoSQL taught us what we were pretending not to need: flexible documents, cheap horizontal stories, a cache that looked like a database. Postgres absorbed enough of that — JSONB, decent full text, logical replication, a million extensions — that opening a second database became a decision I had to defend, not a default I got praised for.

This is the default I use in 2026, the NoSQL lessons I kept, and the cases I will still leave.

What the NoSQL era actually taught

It taught us that a rigid schema can be a lie when the product is still hunting. Early Mongo was a way to ship a document you did not understand yet. The cost arrived when you needed a join you had denied yourself, or a unique constraint you had implemented in the application and forgotten in the second writer.

It taught us that operational simplicity is a feature. A Dynamo table with a clear access pattern is simpler than a Postgres you have not indexed. A Redis that is allowed to vanish is simpler than a Redis you are treating as a source of truth. The era’s crime was not new tools. It was pretending every problem was a new tool.

It taught us that SQL’s pain was often our pain: migrations we feared, ORMs we worshipped, a primary we would not tune. People did not hate relations. They hated a DBA process and a Hibernate graph. When we put JSONB in Postgres and a migration tool in CI, a lot of the hate left.

It taught us about access patterns. Dynamo’s “if you cannot name the query, you cannot have the table” is a better design review than “we will add an index later.” I still steal that sentence. I steal it for Postgres. I do not steal a Dynamo bill for a 40-request-per-minute admin app.

Why Postgres became the default anyway

One process. One backup story. One set of roles. One language for the questions finance will ask. Constraints that live in the database so a second writer cannot “forget.” Transactions that mean I can take a payment authorization and a ledger row together without inventing a saga on day one.

JSONB covered the “I do not know the shape” case well enough. Not as well as a dedicated document store for huge, varied blobs. Well enough that I stop opening Mongo for a user-preferences object.

The ecosystem closed the rest of the gap. Logical replication. Foreign data wrappers I mostly do not use. pgvector when someone wants to look modern. Citus or a bigger box when I actually have a scale problem. Managed Postgres from RDS, Cloud SQL, Neon, a vendor I can yell at. Hiring: I can find people who have been burned by Postgres. I can find fewer people who have been burned by the third document store of the decade and will admit it.

MySQL is still a default in some houses. SQL Server in others. I am not a language nationalist. I am a “how many backup tools does this team operate” nationalist. Postgres won in the shops I inhabit because it was good enough at being several databases while remaining one.

A quiet office desk with a database schema printout and a coffee cup

The defaults I actually run

A primary Postgres. A pooler. Migrations in the same PR as the code. Constraints for the invariants I can name. JSONB for the junk drawer I can lose or re-derive. Redis only if I can describe the day it is empty. A queue that is not the database if the queue is a product — Kafka, SQS — or a SKIP LOCKED table if the queue is a modest worker and I want one restore drill.

Search: Postgres FTS until it is not. Then Meilisearch or whatever we already operate. I do not start Elasticsearch because a blog said search. I have operated that cluster. I remember the heap.

Cache: the HTTP edge for public GETs. A materialized view or a table for a dashboard that can be a minute late. Redis if the key is obvious and the miss is cheap.

That shape has carried every product team I have led since 2020. The exceptions have names. They are not a personality.

When I still leave Postgres

A true key-value access pattern at a scale where the bill or the p99 says so. Dynamo, or a cache with a DB behind it. I leave when I have the pattern, not when I have a conference talk.

A workload that is a warehouse. Analytics belongs in a warehouse or a column store. I will not make the primary the warehouse. I have watched a BI tool take the primary down. That is not a NoSQL lesson. That is a manners lesson.

A queue that must survive the database being the thing that is sad. If Postgres is down, I may still want to accept an event. Then the queue is not a table.

A document pile that is huge, varied, and not relational. A CMS blob store. An object store plus metadata in Postgres is usually the adult version. Mongo if the team already has it and the queries are document-shaped. I will not introduce Mongo to look flexible.

When the company already has a default I would be a fool to fight. SQL Server in a Microsoft house. I will write T-SQL and I will not run a shadow Postgres as a hobby.

Two whiteboards labeled SQL and document store with most arrows pointing at one cylinder

A Mongo I do not regret, and one I do

The one I do not regret was an event dump from devices that sent a different JSON blob every firmware version. We put the blob in object storage and a thin metadata row in Postgres. We tried Mongo first. The queries we needed were “devices in this fleet that have not checked in,” which is a relation. Mongo made that query a scan and a story. The regret lasted a quarter. The metadata table lasted years.

The Mongo I do regret was a session store we opened because “Postgres should not do sessions.” It could. We already had it. We now had a second backup and a Friday where sessions vanished because a replica set member was “temporary.” Temporary became the architecture. I would put sessions in Postgres or in Redis with a TTL I can explain. I would not open a document database to avoid a table.

Those two stories are the NoSQL era in miniature. One real document pile, handled without pretending it was a product database. One fashionable second system that taught me to ask for the query first.

What I tell teams that want a new database this quarter

Name the query the current Postgres cannot do, after an index and a schema change. If you cannot name it, you want a feeling. I do not fund feelings with a second restore drill.

If you can name it — “we need a graph hop,” “we need a 10TB append-only log,” “we need search that is not FTS” — we will buy that tool and we will write down who is on-call for it. A database without an owner is a weekend.

The NoSQL era did not kill SQL. It taught Postgres what to absorb and taught me to stop opening a new database for every noun. I still leave when the access pattern or the failure domain is honestly different. I do not leave because the last decade’s slides said I should have left already.

SQL is the language of questions. Postgres is the default place I ask them. Everything else is a specialist I hire when the question is a specialist’s question. That is the whole default. It survived the decade that was supposed to end it. I expect it to survive the next one, unless we invent a new way to forget about constraints. We probably will. I will still start the next app on Postgres, and I will still make you name the query before we leave.

If you are choosing this week, choose the restore you can run. Choose the constraint you can put in the database. Choose the hiring pool you have. Those three have pointed me at Postgres more often than any architecture book pointed me at a zoo. The zoo is still there if you need it. I visit. I do not live there. And if someone tells you SQL is over again in 2027, ask them which query they could not write, and whether they tried an index. I have asked. I will ask again. The answer is usually a feeling, and feelings are a poor reason to split the backup.

More articles for you