← Back to the section

When you start a new project, sooner or later the question comes up: which database should you use? People usually pick it either out of habit ("we always use PG") or by fashion ("MongoDB is NoSQL, and NoSQL is modern"). Both approaches lead to problems.

Let's look at how these two databases really differ, and how to figure out which one fits your task.

Different databases for different tasks

PostgreSQL is a relational database. Data is stored in tables with a strict schema: every column has a type, constraints (NOT NULL, FOREIGN KEY), and rows from different tables can be combined with a JOIN.

MongoDB is a document store. Data is stored in collections of documents, where each document is a JSON object. The schema is flexible: two documents in the same collection can have a different set of fields.

Neither is better than the other overall. They solve different tasks.

When your data is interconnected

Imagine an online store: an order belongs to a customer, the customer has an address, the order contains products, and products have categories and stock levels. These entities constantly reference each other.

For data like this, PostgreSQL is a better fit. JOINs between tables are its native operation. Foreign keys guarantee that you won't end up with an order pointing to a customer who doesn't exist. NOT NULL and CHECK catch errors before anything is written to the database.

-- Relationships via foreign keys — PG guarantees integrity
CREATE TABLE orders (
    id          BIGSERIAL PRIMARY KEY,
    customer_id BIGINT REFERENCES customers(id),
    created_at  TIMESTAMP NOT NULL
);

MongoDB is a better fit when data is read as a whole, as a single document. A user profile with all its settings, an order with all its line items, an event with arbitrary attributes. If the typical query is "give me this whole object", the document model wins: everything is stored together and read in a single lookup.

When the data structure is unstable

In the past, you designed the schema before designing the database — and that was the rule. If a field shows up later, you write a migration, and all the documents get updated.

Sometimes that's inconvenient. A product catalog is a good example: a jacket has a size and a material, a TV has a screen size and a resolution, a book has an author and an ISBN. If it's all one table, you either end up with dozens of columns (most of them empty) or you move the attributes out into a separate key–value table.

MongoDB handles this more easily: each document stores only its own fields.

// Jacket
{ "name": "Winter jacket", "size": "L", "material": "polyester" }

// TV
{ "name": "TV 55", "diagonal": 55, "resolution": "4K" }

This difference has established names: PostgreSQL follows schema-on-write (the database checks every row against an explicit schema at write time), MongoDB — schema-on-read (the structure is implicit, and the reading code interprets it). The "schemalessness" of a document database is an illusion: the schema has not gone anywhere, it has just moved from the database into application code.

Another word from the same toolbox is locality: a document is stored as one contiguous chunk, so if the data is read as a whole (a profile, a product card), a single read fetches everything at once — where a relational schema would need several queries or a multi-way join. The flip side: the database usually loads and rewrites the whole document even when you need a small fragment, so large, frequently appended documents eat the advantage away.

But there's an important caveat: if the schema changes chaotically, that's not a reason to use MongoDB. It's a signal that the domain is poorly designed. MongoDB's flexibility does not replace data modeling.

Transactions: when an operation must either complete fully or not at all

Transferring money between accounts is the classic example: debiting one account and crediting another must happen as a single action. If something goes wrong in the middle — roll back both operations.

PostgreSQL has supported full ACID transactions from the very beginning. A transaction across several tables is a routine operation.

MongoDB added support for multi-document transactions in version 4.0, but they carry more overhead than in PG. If most of your operations require transactions across several documents, that's a signal the schema would have been a better fit for a relational database from the start.

Data volume and scaling

As long as there isn't much data (up to a few hundred gigabytes on a single server), both databases work equally well. The difference shows up as you grow.

PostgreSQL does great on a single powerful server. Table partitioning (splitting by date or by a key range) solves most performance problems. Horizontal scaling across several servers is possible, but it requires a separate tool (Citus).

MongoDB was designed for horizontal scaling from the start. A sharded cluster is the standard operating mode for large clusters. If it's clear up front that you'll have tens of terabytes and more than one server, MongoDB simplifies the infrastructure.

PostgreSQL + jsonb: the third option people often forget

Between "pure relational" and "pure document" there's a middle option: PostgreSQL has a jsonb type that lets you store arbitrary JSON right in a column, build indexes on it, and search inside the JSON.

CREATE TABLE product (
    id          BIGSERIAL PRIMARY KEY,
    category_id BIGINT REFERENCES category(id),
    name        TEXT NOT NULL,
    price       NUMERIC(10,2) NOT NULL,
    attributes  JSONB NOT NULL DEFAULT '{}'::jsonb
);

-- GIN index for fast search by JSON content
CREATE INDEX product_attributes_gin ON product USING GIN (attributes);

-- Find red products
SELECT * FROM product WHERE attributes @> '{"color": "red"}';

This gives you flexible attributes (like in MongoDB) while keeping relational relationships, foreign keys, and transactions. If the task sounds like "we need flexible attributes, but the data is interconnected" — try jsonb before moving to MongoDB.

What each database is good at

TaskBetter choice
CRUD service with a clear schema and relationshipsPostgreSQL
Billing, accounting, financePostgreSQL
Product catalog with varied attributesMongoDB or PG + jsonb
User profile with nested dataMongoDB or PG + jsonb
Event feed / logsMongoDB, ClickHouse
Geo dataPostgreSQL + PostGIS
Full-text search with filtersOpenSearch / Elasticsearch
Cache, sessionsRedis
Analytics and aggregatesClickHouse / DuckDB

The operational side

PostgreSQL is a mature tool with decades of practice behind it. Migrations via Flyway or Liquibase, monitoring through pg_stat_* and pgAdmin, backups and replication — all of it is well studied and battle-tested.

MongoDB requires a different set of skills: indexes are built by different rules, sharding is a separate engineering discipline, and backing up a sharded cluster is non-trivial. If there's no one on the team with real MongoDB production experience, that's a risk when the first serious problem hits.

Common mistakes when choosing

"MongoDB — because the schema might change." A flexible schema doesn't remove the need to design your data. A year later, if nobody wrote migrations, nobody knows which documents in the collection are valid.

"Let's use both for flexibility." Two sets of migrations, two monitoring setups, two backups, and the risk of data getting out of sync. You take on two databases when the nature of the tasks is genuinely different: for example, transactional data in PG and a growing event log in MongoDB.

"PG + jsonb will always replace MongoDB." With deep nesting (several levels of arrays inside the JSON), jsonb queries become less readable and slower than the equivalent in MongoDB. For a truly document-shaped model, MongoDB is more convenient as the primary tool.

"MongoDB doesn't need migrations." It does. A new required field, a rename, a type change — those are the same migrations, it's just that in MongoDB nobody writes them by convention, and that creates problems later.

In short

  • PostgreSQL is better when data is interconnected and you need JOINs, transactions, and foreign keys.
  • MongoDB is better when data is read as whole documents, the schema is heterogeneous, and the scale implies several servers.
  • PostgreSQL + jsonb is the middle option: the flexibility of JSON while keeping relational guarantees.
  • Choosing by fashion ("NoSQL is trendy") or by habit ("we always use PG") leads to problems.
  • MongoDB supports transactions, but they're more expensive — if you need transactions constantly, you probably need PG.
  • The team's operational maturity matters: MongoDB requires specific knowledge.
  • Two databases in one service are justified only when the nature of the tasks is genuinely different.

Further reading

  • The PostgreSQL section — ACID, replication, partitioning.
  • The MongoDB section — replica set, sharded cluster, document modeling.
  • Monolith, modular monolith, or microservices — another architectural choice.