MongoDB Associate Developer study materials
MongoDB Associate Developer study resources

MongoDB Associate Developer common mistakes

MongoDB Associate Developer · Understand the traps and how to avoid them.

Mistake 1

Calling updateOne() or updateMany() to 'create or update' a record and then treating a result of matchedCount: 0, modifiedCount: 0, upsertedId: null as a driver error, or wrapping it in retry logic.

Developers coming from SQL UPSERT/MERGE habits or from ORMs assume an update creates a missing document automatically. It does not: an update with no matching filter matches nothing and silently does nothing, so the code 'succeeds' while no document is written.

What to do instead

Pass { upsert: true } in the options object, and treat the returned upsertedId (non-null) as the signal that an insert happened; check matchedCount/modifiedCount/upsertedCount explicitly rather than assuming a write occurred.

Mistake 2

Using the return value of findOneAndUpdate() as the post-update document, then rendering stale field values (for example the old status or old counter) in the application layer.

findOneAndUpdate returns the document as it was before the modification unless told otherwise. Because the return value is non-null and looks like the same shape the caller expects, the bug is invisible until someone compares a field the update changed.

What to do instead

Set returnDocument: 'after' (Node.js driver 4.x+) or the legacy returnNewDocument: true, and include the projection option when only specific fields are needed.

Mistake 3

Building a compound index by listing fields in the order they appear in the query filter, or in whichever order the fields are declared in the application's schema/model.

Candidates know which fields to index but not that field order determines whether the index can serve equality matches, the sort, and the range predicate simultaneously. The index is created without error, so nothing signals that it is a poor fit until explain() shows a SORT stage or a scan.

What to do instead

Order keys Equality, Sort, Range (ESR): equality-matched fields first, then the sort field(s) in the sort direction used by the query, then range fields. Verify with explain('executionStats') that the plan has no blocking SORT stage and totalDocsExamined is close to nReturned.

Mistake 4

Placing $match on a field after a $group, $project, or $unwind stage and expecting the same performance as matching before it, then concluding 'the index isn't being used' when the query runs slowly.

Indexes only serve predicates applied to collection documents. Once a stage has reshaped or grouped the stream, subsequent filters run in memory against the transformed pipeline output, so the earlier stages still read every input document.

What to do instead

Push filters as early as possible: put the selectivity-reducing $match as the first stage, before any reshaping stage, and use $sort immediately after an indexed $match so the sort can be index-backed. Use $match again after $unwind only when the predicate genuinely depends on the unwound values.

Mistake 5

Writing a $lookup whose 'as' output field is then tested directly with $match or passed to $group without an $unwind, and getting counts of 1 for every document or missing matches.

$lookup always writes an array into the 'as' field, even when at most one foreign document can match or when it finds nothing (an empty array). Comparing that array field to a scalar or grouping by it produces the wrong cardinality rather than an error.

What to do instead

Add { $unwind: { path: '$as Field', preserveNullAndEmptyArrays: true } } when you need one output document per matched foreign document, and remember to index the foreignField on the joined collection so the per-document lookups are not collection scans.

Mistake 6

Modeling one-to-many relationships by embedding an array that grows without a bound (all comments on a post, all events for a user, all followers), then hitting write errors or degraded performance in production.

The schema looks correct and works with the small test data set used during development. The 16 MB BSON document limit, document growth/rewrite cost, and index entries on large arrays only become a problem once the array is populated at realistic volume.

What to do instead

Apply the subset pattern (embed only the most recent N items, keep the full set in a separate collection) or the bucket pattern, or reference from the 'many' side with a parent id field. Base the decision on whether the array is bounded by the application's rules, not on the sample data.

Mistake 7

Starting a multi-document transaction but passing the session only to the first operation, or writing and reading inside a transaction without any session at all.

Operations outside the session execute outside the transaction, so they are neither rolled back nor visible to the transactional reads. The code compiles and often appears to work because the operations still succeed individually.

What to do instead

Pass the session in the options of every read and write in the block, and run the block through session.withTransaction(), which retries on TransientTransactionError and UnknownTransactionCommitResult. Also confirm the deployment is a replica set or sharded cluster: transactions are not available on a standalone server.

Mistake 8

Constructing a new MongoClient (and calling connect()) inside a request handler, a Lambda invocation, or a helper that each data-access function calls.

It looks like the safe, self-contained way to get a connection, and it works in manual testing at low volume. Each client opens its own connection pool and monitoring connections, so under load the application exhausts connections and pays handshake and topology-discovery cost per request.

What to do instead

Create one MongoClient for the application's lifetime (module-level or injected singleton), connect once at startup, and reuse it; drivers are thread-safe and pool connections internally. Close it only on application shutdown.

Mistake 9

Debugging an Atlas connection failure by rotating the database user's password or rewriting the connection string, when the error is an authenticationTimeout or server-selection timeout caused by the client's IP not being on the project's IP access list.

The error surfaces as a connection or auth problem, and the developer assumes credentials. The access-list check happens before user authentication, so wrong password and blocked IP look similar, and the Atlas UI separates the two settings in different places.

What to do instead

Check the cluster's IP Access List first and add the client's egress IP (or 0.0.0.0/0 only for throwaway testing), then verify the connection string uses the mongodb+srv:// form for an Atlas cluster with the correct database user, authSource=admin, and TLS enabled by default.

Mistake 10

Sorting documents by _id (or omitting sort entirely and trusting the natural order) to get 'newest first', assuming ObjectId values are strictly increasing in the order documents were inserted.

ObjectId embeds a 4-byte timestamp with one-second granularity plus a per-process random value and counter, so documents inserted in the same second by different clients do not sort in true insertion order, and find() without an explicit sort has no guaranteed order at all.

What to do instead

Add an explicit field that captures creation time (a Date, or a Date stored alongside the _id) and sort on it with an index to support the sort; never rely on natural order or on _id ordering as a substitute for a timestamp.