MongoDBBest Practices

MongoDB Best Practices

A checklist-style tour of the habits that separate a MongoDB deployment that scales smoothly from one that falls over under load. Most of these are cross-references to deeper pages — this is the "did I remember to" summary.

1. Design Schema for Access Patterns, Not Entities

Start from your application's queries, not from an ER diagram. Ask "what does the most frequent query need to read in one go?" before deciding what to embed and what to reference. See the Embedding vs Referencing and Schema Design Patterns pages.

2. Always Index the Fields You Query On

JS
// If this runs often...
db.orders.find({ customerId: id, status: "shipped" }).sort({ createdAt: -1 })

// ...this index should exist:
db.orders.createIndex({ customerId: 1, status: 1, createdAt: -1 })
Warning
An unindexed query on a large collection means a full collection scan (COLLSCAN) — it gets slower every day as the collection grows. Use .explain("executionStats") to check for COLLSCAN on any query you expect to run often.
3. Follow the ESR Rule for Compound Indexes

Order compound index fields as Equality, Sort, Range — equality-matched fields first, then fields you sort by, then range-filtered fields last. This lets MongoDB use the index for the widest variety of matching queries.

JS
// Query: find shipped orders for a customer, newest first, placed
// in the last 30 days
db.orders.find({
  customerId: id,               // Equality
  status: "shipped",             // Equality
  createdAt: { $gte: thirtyDaysAgo }  // Range
}).sort({ createdAt: -1 })        // Sort

db.orders.createIndex({ customerId: 1, status: 1, createdAt: -1 })
// customerId + status = Equality, createdAt = Sort/Range combined
4. Size Your Connection Pool Deliberately
  • Create one MongoClient per process/application instance and let it manage pooling — never open a new client per request.

  • Set maxPoolSize based on expected concurrency, not an arbitrary large number — an oversized pool just moves the bottleneck to the server's connection limit.

  • In serverless environments, reuse the client across invocations (module-level singleton) instead of connecting on every cold path.

5. Use writeConcern: majority for Durable Writes

JS
db.orders.insertOne(doc, { writeConcern: { w: "majority" } })
// Waits for acknowledgment from a majority of replica set members —
// the write survives a primary failover. See the Read/Write Concerns page.
6. Enable Retryable Writes

Retryable writes (on by default with modern drivers via retryWrites=true in the connection string) automatically retry a write once if it fails due to a transient network error or replica set election — without this, a failover during a write could otherwise surface as an application error for something that would have succeeded on retry.

7. Avoid Unbounded Arrays

An array field with no natural cap (comments, log entries, notifications) will eventually hit the 16MB document limit and cause expensive document rewrites well before that. Cap it, bucket it, or move it to a referenced collection. See One-to-Many and Schema Design Patterns.

8. Project Only What You Need

JS
// Instead of pulling the whole document over the wire...
db.products.find({ category: "electronics" })

// ...limit the payload to what the UI actually renders
db.products.find({ category: "electronics" }, { name: 1, price: 1, thumbnailUrl: 1 })
Tip
Projection reduces network transfer and memory pressure on both the server and the client — it's one of the cheapest wins available on any hot read path.
9. Monitor the Basics

Metric

Why it matters

opcounters (insert/query/update/delete rate)

Baseline load — spot unexpected spikes

connections (current / available)

Approaching the connection limit means requests will start failing

queues / tickets (WiredTiger)

Requests waiting for a storage engine ticket — a sign of I/O contention

replication lag

Secondary falling behind risks stale reads and slow failover

index hit ratio / COLLSCAN count

Rising unindexed scans predict future slowdowns

cache eviction rate (WiredTiger)

Working set no longer fits in RAM — a leading indicator to scale up

10. Security Checklist
  • Enable authentication — never run a production mongod with --noauth or expose an unauthenticated instance to the network.

  • Use least-privilege custom roles per application instead of granting root/dbOwner broadly. See Authorization.

  • Enable TLS for all client and inter-node traffic. See Encryption.

  • Restrict network access with IP allowlists / VPC peering — never bind to 0.0.0.0 on a publicly reachable host without a firewall.

  • Rotate credentials and use a secrets manager, not hardcoded connection strings.

  • Enable encryption at rest, and use Client-Side Field Level Encryption or Queryable Encryption for especially sensitive fields (SSNs, payment data).

  • Keep MongoDB server and driver versions patched — subscribe to MongoDB security advisories.

Note
Every item above links back to a deeper page in this section — treat this as the checklist you run through before shipping a MongoDB-backed feature to production, not as the full explanation.