I once saw a project that had picked MongoDB on the logic of “no schema, we’ll move fast” — two years later. Every read was defensive: data?.user?.address?.city ?? null. The reason was simple — no two documents had exactly the same shape. The schema hadn’t disappeared; it had just left the database and scattered across every bit of code that reads a document.

”Schemaless” is a misnomer

There is no such thing as schemaless data. Data always has a shape: a user’s email, the line items of an order, the date on an invoice. The question is not whether there is a shape; it is where that shape is defined.

There are two options. Either the schema lives in the database and is enforced by the database, or it lives in the application code and everyone is assumed to comply with it. Saying “schemaless” means choosing the second — not deleting the schema, but moving it somewhere it isn’t enforced.

Where the schema moves

When the database stops enforcing the schema, that work doesn’t vanish; it scatters to these places:

  • To every read. The code can no longer assume the incoming document has the expected fields; it checks every field defensively.
  • To every write. There is no guarantee you wrote the right shape; there is only hope.
  • To scattered validation code. The NOT NULL, type check, and foreign key that the database does in a single line all turn into hand-written checks.
  • To the team’s heads. “That field is sometimes a string, sometimes an array” becomes undocumented tribal knowledge.

A schema is not bureaucracy; it is validation the database does for free on your behalf. Refusing it doesn’t erase the bill; it just charges it to someone else.

The crises you hit

Projects that start with the comfort of “schemaless” sooner or later hit these:

  • Data drift. Over time, ten different versions of a document live in the same collection. A record written in 2023 doesn’t have the same shape as one written in 2026, and no migration ever enforced it.
  • No referential integrity. An order pointing to a user that doesn’t exist isn’t blocked by the database. Orphan records pile up silently.
  • Migration becomes an application job. What a single ALTER TABLE does in the relational world becomes, here, a script that walks millions of documents one by one and has to be batched.

These are invisible in a small project; they blow up at exactly the moment the project grows, the team changes, and no one can answer “why is that field sometimes missing?”

Where NoSQL really is the right answer

This is not an anti-NoSQL piece. NoSQL has real and justified uses:

  • Workloads that exceed the write capacity of a single primary and genuinely need horizontal scale — the hardest breaking point in data-intensive systems.
  • Documents that are genuinely heterogeneous by nature.
  • Specific access patterns — pure key-value, wide-column, graph.

The trap isn’t NoSQL; it’s choosing NoSQL not for one of these real strengths but just because “I don’t want a schema.” Right tool, wrong reason.

If you want flexibility, PostgreSQL already gives it

If the need for “let some fields be flexible” is real, you don’t have to sacrifice all your integrity for it. PostgreSQL’s jsonb column gives you a flexible document field within the integrity of a relational table — as I touched on in is PostgreSQL enough for everything. The fields that are fixed stay as schema, and the part that is genuinely variable lives in jsonb. The best of both, and in a single system.


A schema is not a burden; it is a shield. When you remove it from the database it doesn’t disappear — it just leaves the place where it protected you and moves to a place where it can’t.

There is no such thing as “schemaless”; there is only the question of “who enforces the schema.”