// LOADING
// LOADING
// LOADING_ARTICLE
The single biggest mistake developers coming from relational databases make with MongoDB is trying to normalize everything. The single biggest mistake developers new to MongoDB make is embedding everything. Good schema design is about matching your data model to your access patterns, not to a generic normal form.
MongoDB stores data as BSON documents. A document can contain nested documents and arrays — that is embedding. Alternatively, you can store a reference (usually an ObjectId) to a document in a different collection — that is referencing.
$lookup aggregation or a second query. Updates to the referenced document are reflected everywhere immediately without duplication.Neither approach is universally better. The right choice depends on cardinality, update frequency, and query shape.
Embed when:
js// Embedded one-to-few: post with tags and metadata always read together { _id: ObjectId("..."), title: "MongoDB Indexing", slug: "mongodb-indexing-make-queries-fast", tags: ["mongodb", "performance"], meta: { readingTime: 6, wordCount: 1050 }, publishedAt: ISODate("2026-01-15T00:00:00Z") }
The risk: if the embedded array grows without bound — for example, a post with thousands of comments — the document grows indefinitely. MongoDB documents are capped at 16 MB, but long before that limit you will suffer large document scans and slow reads.
Reference when:
Category that many products reference should be its own collection.js// Referenced: post references its author by ObjectId // posts collection { _id: ObjectId("abc"), title: "Aggregation by Example", authorId: ObjectId("xyz"), // reference to authors collection publishedAt: ISODate("2026-02-01T00:00:00Z") } // authors collection { _id: ObjectId("xyz"), name: "Manikanta Ketha", email: "m@example.com" }
Joining requires a $lookup in the aggregation pipeline, which adds a pipeline stage but gives you the full author document at read time.
A hybrid worth knowing: store the foreign key and a small subset of frequently needed fields from the related document.
js// Order embeds a snapshot of the product at purchase time // This avoids a join for display and preserves the price at the time of sale { _id: ObjectId("order1"), userId: ObjectId("user1"), items: [ { productId: ObjectId("prod1"), // reference for updates name: "Widget Pro", // snapshot price: 29.99 // snapshot at purchase time } ] }
This pattern is common in e-commerce: you need the product reference for linking, but the order must preserve the price and name at the moment of purchase regardless of future product changes.
For high-volume append-only data (sensor readings, click events), embedding into time-bucketed documents reduces index size and improves range query performance:
js// Instead of one document per reading, bucket by hour { sensorId: "sensor-42", hour: ISODate("2026-06-01T14:00:00Z"), readings: [ { ts: ISODate("2026-06-01T14:00:05Z"), value: 21.3 }, { ts: ISODate("2026-06-01T14:00:10Z"), value: 21.4 } // ... up to ~200 readings per document ], count: 2 }
This reduces the number of documents and index entries by orders of magnitude compared to one document per reading. MongoDB's native time series collections automate this pattern if you are on MongoDB 5.0+.
Embedding duplicates data. If the same data lives in two places, a write must update both or you get inconsistency. Reference if the data must always reflect the current state everywhere. Embed if the data is a snapshot (like that order line item) or if the duplication is intentional for performance.
When you do need consistency across documents, multi-document transactions are the tool — but they add overhead and should be used selectively.
For application code that queries these collections, reading explain() output will confirm whether your schema enables efficient index use. A well-modeled schema and a well-chosen index go hand in hand — neither compensates for a bad decision on the other. You can explore how these patterns fit into a full backend in the projects section.
Embed for locality and atomicity; reference for flexibility and independence. Most real applications use both patterns in the same database — the key is applying each one deliberately, driven by actual query patterns rather than habit from relational modeling or an assumption that denormalization is always the MongoDB way.