Learn / Databases, past the CRUD layer

SQL vs NoSQL, the honest version

Lesson 8 of 37 · 8 min read ·

What it is

"NoSQL" is not one thing. It covers document stores (MongoDB), key-value stores (Redis, DynamoDB), wide-column stores (Cassandra) and graph databases (Neo4j). They share only what they are not.

The useful comparison is not SQL vs NoSQL. It is: what does your access pattern look like, and which engine is shaped like it?

The honest defaults

Start relational. Postgres is the correct default for most applications, and the burden of proof is on anything else. It gives you constraints, transactions, joins, JSON columns when you want schema flexibility, full-text search, and thirty years of operational knowledge you can search for at 2 a.m.

Reach for something else when you have a specific reason:

  • Redis — you need sub-millisecond reads of ephemeral data: sessions, rate-limit counters, caches, queues. Not your source of truth.
  • A document store — your entities are genuinely self-contained documents, read and written whole, with a shape that varies per record. Event payloads, CMS content, product catalogues with wildly different attributes.
  • A wide-column store — you have write volumes a single primary cannot take and query patterns known in advance. This is a real but rare threshold.
  • A search engine (Elasticsearch, Typesense) — you need relevance ranking, fuzzy matching and faceting. This sits beside your database, not instead of it.

What you actually trade

The pitch is "flexible schema". The reality is that the schema still exists — it moved into your application code, where nothing enforces it. Six months in, your users collection has three generations of shape, half the documents have phoneNumber and half have phone, and every read path carries defensive checks.

You also trade:

  • Joins. You denormalise instead, which means writing the same fact in several places and keeping them in sync yourself.
  • Constraints. No foreign keys means orphaned references are now possible, and eventually actual.
  • Multi-document transactions. Available in modern MongoDB, but with caveats and costs.

Meanwhile "SQL doesn't scale" is largely folklore at the scale you are likely operating at. A single well-indexed Postgres instance handles tens of thousands of reads per second and terabytes of data. You will hit product problems long before you hit that ceiling.

The one question that decides it

How do you read this data?

If you almost always fetch one document by id and use all of it — a document store is a genuinely good fit. If you slice the same data six different ways, aggregate it, and join it to three other things — you want a relational database, and choosing otherwise means reimplementing joins in application code.

Gotchas

  • "We might need to scale" is not a reason. Design for the scale you have plus one order of magnitude.
  • Polyglot persistence has a real cost: two backup strategies, two failure modes, two sets of expertise.
  • Postgres JSONB gives you document flexibility inside a relational database, indexes included. It often removes the reason to switch.
  • Denormalised data drifts. Decide up front which copy is authoritative.

Prove you know it

Take your current main entity and write down its five most common queries. Then say which storage engine each one is best served by. If they disagree, you have found the real architectural question — and probably an argument for keeping the source of truth relational with a cache or search index alongside.

Go deeper