MongoDB at Scale: Lessons from Managing Multiple Databases
Running MongoDB at scale across multiple products taught me hard lessons about indexing, aggregation pipelines, and schema design.

Every product I build gets its own MongoDB database. Not a shared database with product-specific collections, but a separate database entirely. This decision has operational implications that I've learned to manage over time.
Here's what matters when you're running MongoDB at scale across multiple products.
One Database Per Product
The decision to use separate databases is deliberate:
Isolation. A bad query in one product can't affect another product's performance. A schema migration gone wrong doesn't corrupt another product's data. Each database is an independent system.
Security boundary. Each database has its own credentials. A compromised product doesn't expose other products' data. The blast radius of a security incident is limited.
Operational clarity. Monitoring, backups, and performance analysis are per-product. When a database is slow, I know exactly which product is affected and which queries are responsible.
Independent scaling. A product with heavy read traffic can have its own read replicas without affecting other products' resource allocation.
The tradeoff is management overhead: more databases means more connection strings, more monitoring targets, and more backup configurations. MongoDB Atlas handles most of this overhead through its managed service, making the per-database approach practical for a solo operator.
Indexing Lessons
Every MongoDB performance problem I've encountered was an indexing problem. The solutions are usually simple, but finding the right index requires understanding query patterns.
Index what you query. If you query blog posts by status and sort by publishedAt, create a compound index on { status: 1, publishedAt: -1 }. Not two separate indexes, but one compound index that covers both the filter and the sort.
Read your query plans. MongoDB's .explain() shows whether a query uses an index or scans the entire collection. I check query plans for any query that runs frequently or processes significant data.
Don't over-index. Each index consumes memory and slows writes. A collection with fifteen indexes might have fast reads but slow inserts. Index the queries that matter most and accept sequential scans for rare queries.
TTL indexes for cleanup. For temporary data (session tokens, log entries, cached responses), TTL indexes automatically delete documents after a specified duration. This eliminates the need for cleanup scripts.
Schema Design Patterns
MongoDB's schema flexibility is a double-edged sword. Without discipline, schemas diverge across documents in the same collection, creating maintenance nightmares.
Use Mongoose schemas. Even though MongoDB doesn't enforce schemas, Mongoose does at the application level. Define schemas for every collection. Treat them as contracts that new code must honor.
Embed what you read together. If you always display a blog post with its author name, embed the author name in the post document. Don't create a separate authors collection and join them. The denormalization trades storage for read performance.
Reference what changes independently. If user profiles update frequently and appear in many contexts, reference the user ID rather than embedding the profile. Otherwise, every profile update requires updating every document that embeds it.
Use timestamps everywhere. createdAt and updatedAt on every document. These fields are invaluable for debugging ("when was this data created?"), analytics ("how many users signed up this week?"), and data management ("delete documents older than X").
Aggregation Pipelines
MongoDB's aggregation framework is powerful and under-used. Most developers query with .find() and process data in application code. Aggregation pipelines move the processing to the database, which is faster for large datasets.
Patterns I use regularly:
Group and count. "How many users signed up per day this month?" One aggregation query instead of multiple find queries.
Lookup and join. When I do need to join data from multiple collections, $lookup handles it in the database. Slower than embedding but faster than multiple application-level queries.
Conditional aggregation. "Show me the top-performing questions by subject, but only for questions that have been answered at least 50 times." Complex filtering and grouping in a single pipeline.
The learning curve for aggregation pipelines is steep, but the payoff for data-heavy products is significant. Aviation Infinity's analytics (performance by subject, difficulty distribution, student progress trends) are all powered by aggregation pipelines.
Backup and Recovery
Data loss is the worst possible failure for a product. My backup strategy:
Atlas automated backups. Daily snapshots with point-in-time recovery. This is the baseline. It happens automatically and requires no management.
Application-level exports. Weekly exports of critical collections to JSON files. These exports serve as an independent backup that doesn't depend on Atlas infrastructure.
Migration scripts. Before any schema change, a migration script that can be reversed. Not because I expect to reverse it, but because the ability to reverse makes me more confident about moving forward.
The cost of comprehensive backups is negligible compared to the cost of data loss. I've never needed to restore from a backup in production. I've needed the peace of mind constantly.
What I'd Change
If I started today with current knowledge:
Use TypeScript types derived from Mongoose schemas. Early products had separate TypeScript interfaces and Mongoose schemas that could drift apart. Modern Mongoose with TypeScript generates types from schemas, eliminating this drift.
Adopt MongoDB transactions earlier. For operations that need atomicity across multiple documents (e.g., transferring credits between users), transactions ensure consistency. I initially avoided them due to complexity, but they've become essential for financial operations.
Invest in query monitoring from day one. Atlas's performance monitoring shows slow queries, missing indexes, and collection scan patterns. I should have enabled and checked this from the start instead of discovering performance issues reactively.
MongoDB isn't the right database for every product. But for the products I build (document-heavy, schema-evolving, read-optimized), it's the right choice. And managing it well at scale is a skill that compounds with every product.
Enjoyed this article?
I write about building products, AI, aviation, and the journey of entrepreneurship. Follow along for more.
Keep reading

Getting Started with Allem SDK: React Hooks for AI, Forms & Auth
Allem SDK is a collection of React hooks for AI chat, form validation, authentication, analytics, and utilities. Here is how to install and use it.

Getting Started with Allem UI: React & React Native Components
Allem UI is an accessible component library for React and React Native with 44+ components, dark mode, and Tailwind CSS v4. Here is how to install and use it.

The Agento Suite: Building 6 AI Products in Parallel
In 2026, I launched six AI products across legal tech, travel, healthcare, and developer tools. Here is the architecture and playbook for building in parallel.