Blog

Problems with MongoDB

Key Takeaways

  • MongoDB’s disadvantages are about modeling and operational trade-offs—not an inherent lack of acknowledged writes or transactions.
  • A single-document write is atomic. Multiple independent writes don’t become one atomic workflow unless you use a transaction.
  • MongoDB supports database-enforced schema validation, but document validation doesn’t replace relationship management or schema migrations.
  • Evaluate your access patterns, integrity requirements, indexes, and failure behavior before choosing MongoDB.

This article is about potential pitfalls, not an argument that MongoDB is inherently inferior. The main MongoDB disadvantages are the design work around cross-document integrity, schema evolution, and distributed read/write behavior. Its flexible document model can help applications evolve quickly, but flexibility isn’t free.

Problems with Reliability: Understand the Actual Guarantees

The older claim that MongoDB writes proceed without confirmation by default is wrong. Ordinary driver writes wait for acknowledgment according to the effective write concern. An asynchronous JavaScript API doesn’t make the underlying write unacknowledged.

Write concern, read concern, and read preference are different controls

These settings answer different questions:

ControlWhat it determinesPotential pitfall
Write concernWhat acknowledgment a write requires.Acknowledgment from the primary alone isn’t the same as majority replication.
Read concernWhich data a read may observe, such as majority-committed data or a transaction snapshot.Durable data isn’t necessarily the newest data.
Read preferenceWhere reads run; the default is the primary.Reads routed to secondaries can return stale data.

For write concern, w: 0 requests no acknowledgment; w: 1 requests acknowledgment from the primary in a replica set. w: "majority" requires the deployment’s calculated majority acknowledgment. Journaling records changes in an on-disk recovery log; the server’s j: true option requests journal acknowledgment.

Most deployments use an implicit majority write concern, but certain replica-set topologies containing arbiters use w: 1. Explicit deployment or client settings can change the effective behavior, so inspect your configuration rather than assuming a universal default.

A majority-acknowledged write doesn’t mean every secondary has applied it. For read-your-writes guarantees across replica-set members, use a causally consistent session with majority read and write concerns, following MongoDB’s read consistency rules.

Single-document atomicity doesn’t cover an entire workflow

Atomic means all-or-nothing within a defined boundary. MongoDB guarantees atomic writes to a single document, including changes to multiple fields. Outside a transaction, updateMany() updates each document atomically, but the entire operation isn’t atomic.

One document:       [update stock + reserved]       one atomic unit

Independent writes: [decrement stock] --> [insert order]
                     can succeed          can fail

Transaction:        [decrement stock + insert order]
                     commit both or abort both

Suppose you decrement inventory and then insert an order. If the application crashes between those independent writes, inventory can change without an order being created. That’s a missing transaction boundary, not a consequence of using async/await.

MongoDB supports multi-document transactions on replica sets and sharded clusters, including across collections. The comparison with relational databases should use equivalent boundaries: PostgreSQL transactions and MySQL InnoDB transactions also require the intended multi-statement unit to be grouped together.

Example: an atomic update versus a multi-document transaction

The following script makes an independent stock correction, then uses a transaction to decrement stock and create an order together. It uses the official mongodb Node.js driver and its session transaction API.

Use a supported Node.js runtime and a disposable database on a replica set or sharded cluster—not a standalone server. Set MONGODB_URI in your environment, save the script as integrity.mjs, and run:

npm install mongodb
node integrity.mjs
import { MongoClient, ObjectId } from "mongodb";

const uri = process.env.MONGODB_URI;
if (!uri) throw new Error("Set MONGODB_URI before running this script");

const writeConcern = {
  w: "majority",
  journal: true,
  wtimeoutMS: 5000,
};
const client = new MongoClient(uri, { writeConcern });

try {
  await client.connect();
  const db = client.db("stackchief_integrity_demo");
  const inventory = db.collection("inventory");
  const orders = db.collection("orders");
  await orders.createIndex({ requestId: 1 }, { unique: true });

  const { insertedId: sku } = await inventory.insertOne({ stock: 2 });

  // An independent stock correction: one document, one atomic update.
  const adjustment = await inventory.updateOne(
    { _id: sku, stock: { $gte: 1 } },
    { $inc: { stock: -1 } },
  );
  if (adjustment.matchedCount !== 1) {
    throw new Error("Stock correction failed");
  }

  const requestId = new ObjectId(); // Stable across callback retries.
  const session = client.startSession();
  try {
    await session.withTransaction(async () => {
      const result = await inventory.updateOne(
        { _id: sku, stock: { $gte: 1 } },
        { $inc: { stock: -1 } },
        { session },
      );
      if (result.matchedCount !== 1) {
        throw new Error("Insufficient stock");
      }
      await orders.insertOne(
        { requestId, sku, quantity: 1 },
        { session },
      );
    }, {
      readConcern: { level: "snapshot" },
      writeConcern,
      readPreference: "primary",
    });
    console.log("Order committed");
  } finally {
    await session.endSession();
  }
} catch (error) {
  console.error("Demo failed:", error);
  process.exitCode = 1;
} finally {
  await client.close();
}

The conditional stock filter and decrement execute together, avoiding a separate read-then-write race. Checking matchedCount matters: an update that matches nothing isn’t automatically an application error.

The driver uses journal: true for the server’s journaling option. Here, wtimeoutMS: 5000 limits the replication-acknowledgment wait to five seconds; it isn’t an overall request deadline. A write-concern timeout doesn’t undo a successful write.

Failover and retries still need application design

During replica-set failover, operations can fail while the driver looks for a new primary. Official drivers enable retryable writes by default on supported deployments, but only eligible operations receive that protection. It doesn’t make an arbitrary business workflow retry-safe.

Make retryable workflows idempotent: repeating a request should not apply its business effect twice. Use a stable request identifier, an appropriate unique index, and explicit handling of previously completed requests. The demo’s generated identifier survives transaction callback retries, but a real API must preserve the identifier across repeated application requests.

Transactions also have costs. Long-running transactions can increase contention and storage-cache pressure, and transactions are subject to runtime limits. Keep them short and measure conflict rates and latency under load; MongoDB’s transaction production considerations describe these constraints.

Challenges with a Flexible Schema

MongoDB is flexible-schema, not incapable of enforcing a schema. Without a collection validator, documents can have different fields and types. That helps staged application changes, but consumers still need a clear data contract.

What actually happens when user.email is missing?

For an existing JavaScript object without an email property, user.email normally evaluates to undefined. The property access itself doesn’t throw; calling email.trim() afterward would throw a TypeError.

const user = { name: "Sam" };
const email = user.email; // undefined, not an exception

if (typeof email === "string" && email.trim() !== "") {
  console.log(email.trim());
} else {
  console.error("Missing or invalid email");
}

This isn’t unique to MongoDB. API payloads, query projections, legacy records, and nullable SQL columns can all leave application code without a usable string. Define which fields are required and enforce that contract at the appropriate boundaries.

Enforce document rules with native schema validation

MongoDB supports collection validators, including $jsonSchema validation. In mongosh, create a new collection with an explicit policy:

db.getSiblingDB("stackchief_integrity_demo").createCollection("users", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["email"],
      properties: {
        email: { bsonType: "string", minLength: 1 },
      },
    },
  },
  validationLevel: "strict",
  validationAction: "error",
});

This rejects ordinary inserts and updates that produce a document without a non-empty email string. It doesn’t verify email deliverability or reject whitespace-only strings. With access control enabled, appropriately privileged writers can bypass this validation.

Document validation also doesn’t enforce email uniqueness or ensure a referenced customer exists. Use a suitable MongoDB unique index for uniqueness, and design coordinated application logic for cross-document invariants—rules that must remain true across records.

Schema evolution still needs a migration plan

Changing application code doesn’t rewrite stored documents. MongoDB’s schema versioning pattern lets old and new document shapes coexist, but readers, writers, and indexes must account for those differences.

  1. Deploy readers that understand both shapes; add a schemaVersion field when it makes the distinction clearer.
  2. Update every writer, including imports and maintenance jobs.
  3. Backfill existing documents in bounded, restartable batches, guarding against concurrent changes.
  4. Verify the migration before tightening validation and removing compatibility code.

Under strict validation, changing the validator doesn’t retroactively check or repair existing documents. The collMod command can change collection validation rules, but it isn’t a data migration.

Mongoose helps at the application layer

Mongoose is an optional object-document mapper, or ODM: a library that models database documents in application code. Its validation behavior needs attention too: update validators are off by default and require runValidators: true, with documented limitations.

Mongoose’s transaction helpers wrap driver transactions; MongoDB supplies the underlying guarantees. Mongoose validation can’t protect writes made by another application that doesn’t use those models. Our guide to using Mongoose with MongoDB covers the application-modeling benefits.

MongoDB Disadvantages Depend on Your Data Model

Embedding means storing related data inside one document; referencing means storing identifiers pointing to separate documents. Embedding can reduce reads and keep related updates within one atomic boundary. References avoid copying independently managed entities, but introduce additional retrieval and consistency work.

Neither extreme is free. Duplicated mutable data needs a synchronization policy. An ever-growing embedded array can eventually hit the 16-mebibyte BSON document limit. Start with your MongoDB schema design and access patterns, not a blanket rule to embed everything.

MongoDB vs PostgreSQL: choose by workload

Workload characteristicEvaluation starting point
Bounded related data usually read and updated togetherTest an embedded MongoDB document model.
Many independent entities with strict relationship constraintsEvaluate a relational model first.
Most requests coordinate changes across many documentsCompare transaction-heavy designs directly; don’t assume MongoDB’s flexibility removes coordination costs.
Variable attributes alongside relational integrity requirementsConsider PostgreSQL with relational columns and JSON data, rather than treating flexibility as exclusive to MongoDB.

PostgreSQL offers foreign-key, unique, and not-null constraints, as well as jsonb storage. MongoDB requires application logic to maintain references between documents. Neither database automatically makes arbitrary business rules correct.

Benchmark representative queries with realistic data, concurrency, and durability settings. Include MongoDB indexing in that work: document flexibility doesn’t eliminate query planning or index design.

Production Checklist

  • Document the atomic boundary and recovery strategy for every important workflow.
  • Review effective read/write concerns and read preferences, including failover behavior.
  • Test concurrent updates, lost responses, repeated requests, and transaction retries.
  • Apply validators and indexes deliberately; restrict validation-bypass privileges.
  • Track transaction latency, conflicts, replication lag, document growth, and migration progress.

Conclusion

MongoDB can be a strong choice when documents match how your application reads and changes data. Choose it because that model fits—not because you expect to avoid schemas, transactions, migrations, or consistency decisions.

FAQ

Does MongoDB support ACID transactions?

Yes. MongoDB supports multi-document ACID transactions on replica sets and sharded clusters, including operations across collections. You still need appropriate transaction boundaries, read/write concerns, and error handling.

Are MongoDB writes asynchronous by default?

An asynchronous driver API doesn’t mean writes are unacknowledged. Ordinary writes wait for acknowledgment according to the configured write concern; w: 0 explicitly requests no acknowledgment.

Are MongoDB writes atomic?

A write to one document is atomic, even when it changes multiple fields. Multiple independent writes, or updateMany() outside a transaction, aren’t automatically one atomic unit.

Can MongoDB enforce a schema?

Yes. Collection validators, including $jsonSchema rules, can enforce required fields and types. Enforcement depends on the validation policy and bypass permissions, and document validation doesn’t enforce arbitrary cross-document business rules.

What are the main disadvantages of MongoDB compared with PostgreSQL?

MongoDB requires more application coordination when integrity depends on relationships across documents, and flexible document shapes require deliberate migration practices. PostgreSQL’s relational constraints can simplify relationship-heavy workloads; MongoDB’s embedded model can simplify workloads centered on bounded groups of related data.

Further Reading

Written by Sam Erickson

Software engineer. I write every article on StackChief myself. Author of Mastering the Tech Interview. More about me