PostgreSQL vs MongoDB: What Two Years of Pain Taught Me
I've heard the debate a thousand times: "just use Postgres" vs "Mongo scales better". Both camps are wrong in interesting ways. After running both in production (sometimes at the same time, which is its own kind of pain), here's my honest take.
The Real Difference Isn't Documents vs Tables
People frame this as a philosophical debate about data modeling. It's not. The real difference is when your schema becomes clear to you.
- Postgres: Your schema is defined upfront. It's rigid. This is a feature, not a bug — it forces you to think about your data model before you've painted yourself into a corner.
- MongoDB: Your schema is enforced by the application. This feels flexible until you have six versions of the same field name in production and no idea which documents have which shape.
Where Mongo Actually Wins
I'm not here to bash MongoDB. There are real scenarios where it's the better choice:
1. Truly variable schemas. If you're storing user-uploaded form data where every form has different fields, document storage is genuinely natural.
2. High write throughput on denormalized data. MongoDB's write performance on a single collection with no joins can be faster because there's nothing to lock.
3. Rapid early iteration. When you're in the "I don't know what my data looks like yet" phase, Mongo's flexibility lets you move fast.
{
"_id": "abc123",
"type": "user_action",
"payload": {
"action": "click",
"element": "#cta-button",
"timestamp": 1706000000
}
}
That kind of heterogeneous event data is genuinely comfortable in Mongo.
Where Postgres Wins (Most of the Time)
1. Joins exist for a reason. The moment you need data from two collections in MongoDB, you're either denormalizing everything (duplicating data, consistency nightmare) or using $lookup (which is just a worse JOIN).
2. Transactions are real. Mongo added multi-document transactions, but they're slower and more complex to use than Postgres transactions.
3. The query planner is genius. Postgres's query planner has been refined for decades. I've seen it rewrite a query I wrote badly into something optimal I wouldn't have thought of.
4. JSON support. Here's the kicker: Postgres has jsonb columns. You can store documents inside a relational database and query them with operators. It's the best of both worlds when you need it.
SELECT user_id, metadata->>'plan' as plan
FROM users
WHERE metadata @> '{"verified": true}'
AND created_at > NOW() - INTERVAL '30 days';
My Actual Decision Framework
| Situation | My Choice |
|---|---|
| Standard web app with relations | Postgres |
| Event/log ingestion pipeline | Mongo (or ClickHouse) |
| Don't know schema yet | Start Postgres, migrate later |
| Geospatial queries | Postgres (PostGIS) |
| Full-text search is primary concern | Postgres (or Elasticsearch) |
| Need to move fast, schema unclear | Mongo, with the explicit plan to revisit |
The Lesson
Most "Mongo vs Postgres" debates are really "I'm making a decision I can't easily reverse and I'm scared." Pick Postgres by default. If you hit a genuine need for document storage, you'll know it — it won't be a guess.