- PostgreSQL
- MongoDB
- Databases
- Opinion
Postgres until proven otherwise
I run both MongoDB and PostgreSQL in production. My default flipped years ago, and the reason isn't performance — it's that schemas are promises, and Postgres makes you keep them.
Every project starts with the same fork in the road, and I've taken both paths enough times to have a default: Postgres, until the data proves otherwise. Not because MongoDB is bad — it isn't, and I run it happily in production — but because of what each database does to your future self.
Schemas are promises
A schema is not bureaucracy; it is a promise about what shape the data will be when you read it back. The question is never whether your data has a schema — it always does — but who enforces it.
- In Postgres, the database enforces it. Bad writes fail loudly, today.
- In Mongo, your application enforces it. Bad writes succeed silently, and fail three months later in a report nobody can explain.
Document validation in Mongo narrows this gap, and libraries like Zod or Mongoose narrow it further. But an enforcement layer you must remember to apply is a different guarantee than one you cannot forget.
Where Mongo genuinely wins
The honest list, from production experience — not the marketing one:
- Documents that are truly documents. CMS page trees, form builders, event payloads — data that arrives and leaves as one nested blob, where joining was never the point.
- Shapes you don't control. Webhook payloads and third-party syncs, where the upstream changes without asking you.
- Horizontal scale as a first-class citizen — if you concretely know you're heading past what one big Postgres box serves. Most of us aren't.
Meanwhile jsonb quietly ended the old dichotomy: my default stack is
relational tables for the entities that relate — users, orders, permissions
— with a jsonb column where a document honestly is a document. One
database, both models, real transactions.
Relational for the data you promise, jsonb for the data you tolerate.
The tell
Here's the test I apply to any "which database?" debate: describe your data out loud. If the sentence contains "belongs to," "references," or "the total across" — that's a relational sentence, and fighting it in a document store means reimplementing joins in application code, badly, forever. If the sentence is "we store what we receive and show it back" — that's a document sentence, and normalizing it into fifteen tables is ceremony.