SQL vs NoSQL
This is the most over-argued decision in our field, and most of the argument is noise. “SQL is legacy.” “NoSQL doesn’t scale your team.” Both camps are defending a tool as if it were a tribe. Strip the tribalism away and you find a genuine engineering fork — but the fork is not “relational versus not.” It’s how do you read and write your data, and how much do you need the database to guarantee correctness for you?
What does each side buy us, and what does it cost? That’s the only question that matters.
What “SQL” actually gives you
Section titled “What “SQL” actually gives you”The relational model (Postgres, MySQL, SQL Server, and friends) is built on four pillars that have survived fifty years for good reason:
- A schema. Every row in a table has the same shape, declared up front and enforced on write. A
column is an
INTEGERor it’s rejected. This is a constraint, and constraints are a feature: the database refuses to store nonsense. - Joins. Data is split into normalized tables (no duplication), and you reassemble it at read time with relational joins. You store each fact once and combine facts on demand.
- ACID transactions. A group of changes either all happen or none do, and concurrent users don’t trip over each other (see Transactions & ACID).
- A declarative query language. You say what you want; the query planner figures out how. Add an index years later and old queries get faster with zero code changes.
The cost: the schema is rigid (changing it on a billion-row table is a real operation), and the classic relational model was designed to scale up (a bigger box) more naturally than out (many boxes). Joins and strong transactions are hard to keep cheap once data is spread across machines — which is exactly the wall that birthed NoSQL.
What “NoSQL” actually gives you
Section titled “What “NoSQL” actually gives you”“NoSQL” is an unfortunate umbrella covering four quite different shapes:
Document {nested JSON per record} MongoDB, Couchbase Key-Value key → opaque blob Redis, DynamoDB, Riak Wide-Column rows with flexible columns Cassandra, HBase, Bigtable Graph nodes + edges Neo4j, NeptuneWhat most of them trade for is the same thing: they relax the relational guarantees in exchange for flexibility and horizontal scale.
- Flexible / no enforced schema. Each document can have different fields. Great when your data is genuinely heterogeneous or evolving fast; the responsibility for shape just moves into your application code instead of the database.
- Built to scale out. Many were designed from day one to be partitioned across hundreds of nodes, often with tunable, weaker consistency so that no single coordinator becomes a bottleneck.
- Access-pattern-shaped storage. A document store lets you keep an entire aggregate (an order plus its line items) as one record, so a common read is a single fast lookup — no join.
The cost is the mirror image of SQL’s gifts: you often denormalize (store data multiple times), you give up cross-record transactions and rich ad-hoc joins, and you must know your queries before you model your data, because changing access patterns later can mean re-storing everything.
The real axis: access patterns + consistency
Section titled “The real axis: access patterns + consistency”Here is the reframe that cuts through the dogma. Don’t ask “which technology.” Ask two questions:
- What are my access patterns? Do I read whole self-contained objects by key (document/KV shines), or do I slice and recombine data along many dimensions with queries I can’t predict (relational shines)? Are my relationships the point — friend-of-a-friend, fraud rings — (graph shines)?
- How much consistency do I need? Must a transfer debit one account and credit another atomically, or never (strong, relational — see Consensus)? Or is “the like count is right within a second or two” perfectly fine (eventual consistency, common in NoSQL)?
When each one wins
Section titled “When each one wins”| Situation | Lean toward |
|---|---|
| Money, inventory, anything that must balance | SQL (strong ACID) |
| Unpredictable, ad-hoc queries and reporting | SQL (joins + planner) |
| Massive write volume, simple key lookups | NoSQL (KV / wide-column) |
| Self-contained documents read as a unit | NoSQL (document) |
| Relationship traversal is the core feature | NoSQL (graph) |
| Strict, slowly-changing record shape | SQL (enforced schema) |
| Rapidly evolving or heterogeneous records | NoSQL (flexible schema) |
Under the hood — the line is blurrier than the labels suggest
Section titled “Under the hood — the line is blurrier than the labels suggest”The “relational vs not” fork is real, but the engines themselves have spent a decade eroding it — which is more evidence that the label was never the deciding axis. Postgres has shipped a binary JSON type (JSONB) since 2014: you can store schemaless documents, index inside them, and query their fields, getting much of a document store’s flexibility without leaving a fully relational, ACID database. Coming the other way, MongoDB added multi-document ACID transactions in version 4.0 (2018) — buying back a guarantee that “NoSQL gives up transactions” once treated as definitional.
The takeaway isn’t “the categories collapsed.” Postgres still isn’t built to shard the way Cassandra is, and Mongo’s transactions carry a real cost you pay per use. It’s that you should judge the specific engine’s behavior against your access patterns and consistency needs, not the family it’s filed under. This page’s fork is about guarantees and scaling shape; the brand on the box is a weak proxy for either.
The thread
Section titled “The thread”A database’s job is to store your data and hand it back the way you need it. SQL hands you strong guarantees and query flexibility at the price of rigidity and harder horizontal scale; NoSQL hands you flexibility and out-the-box scale at the price of those guarantees and the burden of knowing your queries upfront. The decision isn’t ideological — it’s a clear-eyed match between how your application reads and writes and how much correctness you need the database to enforce for you. For the wider menu of engines this fork sits inside, see the Databases field guide.
The architect’s lens
Section titled “The architect’s lens”The fork itself is a decision you make once and live with — so strip the tribalism and run it through the five questions:
- Why does it exist? Because the relational model’s strong guarantees (schema, joins, ACID) are hard to keep cheap once data spreads across many machines. The NoSQL families emerged to relax those guarantees in exchange for flexibility and out-the-box horizontal scale.
- What problem does it solve? Matching the engine to how you read and write and how much correctness you need the database to enforce. It’s not a tribe — it’s an access-pattern plus consistency decision.
- What are the trade-offs? SQL buys schema enforcement, ad-hoc joins, ACID, and a declarative planner — at the price of rigidity and harder scale-out. NoSQL buys flexible schema, native partitioning, and access-pattern-shaped storage — at the price of denormalization, lost cross-record transactions, and having to know your queries up front.
- When should I avoid NoSQL? For most new applications. A single relational database is the honest default: flexible for unknown future queries, honest via constraints, and it scales further on one tuned box than people assume. Reach for NoSQL only for a specific, demonstrated access-pattern or scale problem — polyglot persistence is a destination, not a starting line.
- What breaks if I choose wrong? Pick NoSQL prematurely and you forfeit ACID and joins you later
need, and you’re locked into queries you must have known up front; pick relational for a genuine
scale-out firehose and joins plus strong transactions get prohibitively expensive across machines.
(Note the labels blur — Postgres
JSONB, Mongo ACID — so judge the specific engine, not the family.)
Check your understanding
Section titled “Check your understanding”- Name the four pillars of the relational model and the cost each one carries.
- Why is “NoSQL” a misleading category? List the four sub-types and what each is good at.
- Restate the SQL-vs-NoSQL decision as a question about access patterns and consistency.
- Explain schema-on-write vs schema-on-read. Why is “there is always a schema” true even for a schemaless database?
- Why is a single relational database often the right first choice, and what signal tells you it’s time to add or switch to NoSQL?
Show answers
- Schema (enforced shape on write — costs rigidity, hard to change on a billion-row table); joins (reassemble normalized data at read time — cost gets expensive once data is spread across machines); ACID transactions (all-or-nothing, isolated — cost is coordination/performance); declarative query language (you say what, the planner figures out how — at the cost that the classic model scales up more naturally than out).
- “NoSQL” is misleading because it lumps four quite different shapes under one negative label. The sub-types: document (nested JSON aggregates read as a unit), key-value (massive-volume simple key lookups), wide-column (rows with flexible columns, big write volume), and graph (relationship traversal as the core feature).
- Don’t ask “which technology” — ask: what are my access patterns (read whole objects by key, or slice and recombine along unpredictable dimensions, or traverse relationships?) and how much consistency do I need (must writes be strongly atomic, or is “right within a second or two” fine?). The answers point you at the engine; the label follows.
- Schema-on-write validates structure as data goes in (SQL); schema-on-read lets data in freely and imposes structure in your code when it comes out (many NoSQL). “There is always a schema” because data always has a shape your readers depend on — the only question is where it’s enforced: in the database, or in every reader.
- A single relational database is flexible enough for unknown future queries, keeps you honest with constraints, and scales further on one tuned box than people assume. The signal to add or switch: a specific access pattern or scale problem the relational model is demonstrably bad at — not a preemptive guess. Polyglot persistence is a destination, not a starting line.