tools & reviews

When to reach for Postgres, MySQL, or MongoDB

The right default is Postgres. Not because MySQL and MongoDB are bad tools, but because when you have no specific reason to pick something else, Postgres is the choice you’re least likely to curse two years later. That’s the short version. The longer version is where the tradeoffs live, and it’s also what a good interviewer is probing when they ask why you’d put a piece of data in one store over another.

The mental model most people carry is out of date. “SQL versus NoSQL” was a real fork in 2012. Today Postgres and MySQL both store and index JSON, MongoDB has had multi-document ACID transactions since version 4.0 in 2018, and all three run comfortably into the terabytes on a single well-tuned cluster. The decision that matters now is about access patterns, how much your schema changes under you, and which failure modes you’re willing to run in production.

The relational-vs-document split stopped being the deciding line

Postgres treats JSON as a first-class citizen through its jsonb type. You can index a nested field with a GIN index and query it with containment operators, and Postgres 17 added JSON_TABLE so you can shred a JSON blob into rows and run normal SQL against it. That means a lot of “we need a document database” requirements are satisfied by a relational database that also happens to give you joins and foreign keys when you want them.

-- Postgres: index a JSON field and query it like a real column
CREATE INDEX idx_prefs ON users USING gin (preferences jsonb_path_ops);
SELECT id FROM users WHERE preferences @> '{"beta": true}';

MySQL has a JSON type too, but it’s the weaker of the two. There’s no direct equivalent to a GIN index on the document; the usual pattern is to pull a value out into a generated column and put a functional index on that. It works, and plenty of production systems run on it, but it’s fiddly and it doesn’t cover the range of query shapes that Postgres handles without ceremony. Independent JSON benchmarks tend to show Postgres several times faster than MySQL on indexed nested lookups, which lines up with how the two engines store the data.

MongoDB stores documents natively as BSON, so there’s no impedance mismatch at all: what your application object looks like is what sits on disk. Secondary and compound indexes behave the way you’d expect, and querying deeply nested structures feels natural rather than bolted on. If your data is genuinely document-shaped, this is a real ergonomic win, not marketing.

Where MySQL still earns the pick

MySQL’s advantage is boring in the best way. It is everywhere, its InnoDB storage engine is battle-tested, and binary-log replication from a primary to its replicas is about as well-understood as any operational pattern in the industry. Every managed provider runs it, every hosting platform has a one-click option, and the pool of engineers who can debug a slow query or a replication lag spike at 2 a.m. is enormous.

For a read-heavy web application backed by a team that already runs MySQL well, “migrate to Postgres” is rarely worth the cost on greenfield merits alone. The place MySQL bites you later is exactly the JSON story above, and its historically looser defaults around things like implicit type coercion. Turn on strict mode, know your storage engine, and it’s a perfectly good relational database.

What MongoDB is actually good at

MongoDB fits when the data is document-shaped and the shape varies per record. A product catalog where a laptop, a paperback, and a bag of coffee each carry a wildly different set of attributes is a genuine document problem, and modeling it as one flexible collection beats a pile of sparse columns or an entity-attribute-value table in a relational store. Writes scale horizontally through built-in sharding, and a replica set gives you automatic failover: if the primary dies, the members elect a new one without a human in the loop.

The caveats are real and worth saying out loud in an interview. Multi-document transactions exist, but they carry overhead, default to a 60-second limit, and behave best inside a single replica set rather than across shards. The classic failure mode is a team that reaches for Mongo because “it scales,” then rebuilds joins and cross-document consistency in application code, which is slower and buggier than the database would have been. Pick it for the document model, not to dodge SQL.

Decision axis PostgreSQL 17 / 18 MySQL 8.4 LTS MongoDB 8.0
Data model Relational with strong jsonb Relational with a weaker JSON type Native documents (BSON)
JSON querying and indexing GIN/GiST indexes, containment operators, JSON_TABLE Generated columns plus functional indexes; awkward First-class; secondary and compound indexes on any field
Multi-record ACID transactions Full, no practical limits on scope or duration Full via InnoDB Supported since 4.0; ~60s default cap, best within one replica set
Horizontal write scaling Read replicas, table partitioning, sharding via the Citus extension Read replicas; sharding via app logic or Vitess Built-in sharding across the cluster
Replication model WAL streaming plus logical (selective tables, cross-version) Binary-log replication, primary to replicas Replica sets with automatic primary election
Sweet spot Correctness-sensitive apps, mixed relational and JSON, geo and vector via extensions Read-heavy web apps, teams already fluent in it Document-shaped data with variable schema and heavy write volume

The scaling argument that keeps being wrong

“Relational databases don’t scale” is the most durable myth in this whole debate, and it costs teams real money. Postgres runs read replicas, partitions large tables by range or hash, and shards across nodes with Citus when you truly outgrow one box. Companies serve very large workloads on a single Postgres primary with replicas well before sharding enters the conversation. The right time to shard is when you have measured a specific ceiling you’re about to hit, not on the first architecture diagram.

MongoDB’s sharding being built in is a genuine convenience, but built in is not free. Once your data spans shards, cross-shard transactions get limited, your query planner has more ways to surprise you, and the operational surface grows. You’ve traded a scaling problem you might have had for a distributed-systems problem you definitely now have. That trade is sometimes right. It’s rarely right on day one.

How the choice shows up in a system design round

The question usually arrives dressed up as a feature. “We’re building a news feed.” “We’re storing user profiles and their activity.” “We need to log a few billion events a day.” Then the interviewer waits to see whether you reach for a database because it fits the access pattern or because you memorized that one of them is web-scale.

The answer they want starts with the data, not the product. What are the read and write patterns? How strong does consistency need to be for this particular table? How much write throughput, and is it bursty? Does the schema change per record or stay uniform? Only after those does the store fall out. A payments ledger wants Postgres and real transactions. A catalog of heterogeneous items with per-category fields is a fair document case. A firehose of append-only telemetry might want a time-series or column store that isn’t any of these three, and saying so scores points.

A few of the prompts that get used to separate reasoning from recall:

  • “You have a write-heavy event stream at 50,000 inserts a second. Postgres or MongoDB, and what makes you sure?”
  • “Your documents change shape per customer. How would you model that in Postgres without a schema migration every week?”
  • “When would you actually shard, and what specifically breaks the moment you do?”

Notice that none of these have a single correct database as the answer. They have a correct reasoning path, and the strongest candidates name the tradeoff they’re accepting rather than pretending there isn’t one. “I’d use Postgres with a jsonb column and a GIN index, and I’d revisit if the write rate crosses what a partitioned primary can hold” beats “MongoDB, because it’s flexible” every time.

If you want a rule you can defend under pressure: use Postgres unless you can name the specific property that makes it the wrong tool. Use MySQL when your organization already runs it fluently or you want the simplest replication story on the planet. Use MongoDB when the data is truly document-shaped and modeling it relationally would mean fighting the grain of your own domain. The engineers who get burned aren’t usually the ones who picked the wrong database. They’re the ones who picked it for a reason they couldn’t say out loud.

newsletter

What's actually being asked right now

Interview patterns & comp trends, straight to your inbox.

No spam. Unsubscribe anytime.

newsletter

What's actually being asked right now

Interview patterns & comp trends, straight to your inbox.

No spam. Unsubscribe anytime.

1972 Soviet postage stamp commemorating the Mars 2 probe

worth a read

Mars For The Rest of Us — a weekly-or-more deep dive on the technical side of Mars exploration: rocket propulsion, microbiology, mission architecture, and everything in between. Written by Maciej Ceglowski.

Read it on Substack →
Scroll to Top