ahmedallem.
Engineering · 7 min read

Database Per Product: Why I Never Share Databases Across Apps

I run 6 production products and each gets its own database, no exceptions. Here's why a database-per-product strategy beats shared infrastructure.

Ahmed Allem

Ahmed Allem

Founder & CTO · Aviation, AI & Startups

ShareShare
Database Per Product: Why I Never Share Databases Across Apps

I run several production products simultaneously: Aviation Infinity, ClickAi, Babonbo, New Pilot Shop, Want To Be a Pilot, and more in development. Early on, I considered sharing infrastructure between products to save costs and reduce operational overhead. I tried it briefly. It was a mistake I corrected quickly and have never repeated.

Every product gets its own database. No exceptions.

The Temptation of Shared Databases

The argument for sharing databases between products is compelling on paper. You save on hosting costs. You reduce the number of systems to monitor. You can share user data between products (single sign-on, shared profiles). You maintain fewer connection strings and credentials.

For a solo founder running multiple products, these efficiencies seem particularly valuable. Every dollar and every minute counts. Why pay for six database instances when one could handle the load?

I'll tell you why.

The Incident That Changed My Mind

In early 2020, I was running two products on a shared MongoDB instance. The products had separate collections but shared the same database cluster. One evening, I deployed an update to one product that included a new database index. The index build operation consumed significant I/O and memory, which is normal for large collections.

What I didn't anticipate was that the index build would affect the other product sharing the same cluster. During the twenty minutes it took to build the index, the other product experienced elevated latency. Not a full outage, but noticeable degradation. Users saw slow page loads and timeout errors on operations that normally completed in milliseconds.

No users were permanently affected. No data was lost. But the incident crystallized something I'd been abstractly worried about: shared infrastructure creates blast radius problems. An operational decision for one product should never affect another product's users.

The Isolation Principle

After that incident, I established a rule that I've followed ever since: every product gets its own database instance, its own deployment pipeline, and its own monitoring. Products share nothing at the infrastructure level.

This might seem extreme, and for a larger organization with dedicated DevOps teams, it might be unnecessary because they can implement proper resource isolation within shared infrastructure. But as a solo founder, I don't have the bandwidth to implement and maintain sophisticated resource isolation. What I can do is keep things completely separate.

The mental model is simple: if I need to perform maintenance, deploy an update, or debug an issue on one product, I know with certainty that nothing I do will affect any other product. That certainty is worth more than the cost savings of shared infrastructure.

The Cost Reality

People assume database-per-product is expensive. It can be, if you're using traditional managed database services with fixed monthly costs. But with MongoDB Atlas's free and low-tier plans, the incremental cost of an additional database is surprisingly low.

Most of my products don't need high-tier database instances. A product in early stage or with modest traffic runs fine on Atlas's shared tier. Only my highest-traffic products need dedicated clusters. The total cost of running separate databases for all my products is less than running a single high-tier shared instance would be.

The key insight is that database cost should scale with the product's revenue and usage, not with an arbitrary infrastructure sharing strategy. A new product that's generating no revenue can run on a free-tier database. As it grows and generates revenue, upgrading to a dedicated instance is a natural expense.

The Operational Benefits

Beyond blast radius isolation, the database-per-product strategy provides several operational benefits that I've come to rely on.

Independent backup and restore. Each product's database has its own backup schedule and retention policy. If I need to restore data for one product, I can do it without any risk to other products. The restore process is straightforward because there are no shared collections or cross-product references to worry about.

Independent scaling. When Aviation Infinity's traffic spikes during exam season, I can scale its database independently. I'm not paying for capacity that only one product needs across all products.

Clean data boundaries. When a product's data is completely isolated in its own database, there's no temptation to create cross-product queries or shared data structures. This forces clean API boundaries between products. If two products need to share data, they do it through explicit API calls, not database queries. This is architecturally cleaner and easier to reason about.

Simpler security model. Each database has its own credentials, its own access controls, and its own network configuration. A compromised credential for one product doesn't give access to any other product's data.

The Schema Freedom

One underappreciated benefit of database-per-product is schema independence. Each product's data model can evolve at its own pace without any consideration for other products.

Aviation Infinity's data model is optimized for educational content delivery: deeply nested question structures, progress tracking arrays, and study plan documents. ClickAi's data model is optimized for website generation: component trees, design tokens, and content blocks. Babonbo's data model is optimized for marketplace operations: listings, bookings, provider profiles, and delivery logistics.

These data models have nothing in common. Forcing them into a shared database, even with separate collections, would create artificial coupling. MongoDB version upgrades, index strategies, and performance tuning would have to consider all three data models simultaneously. With separate databases, each can be optimized independently.

When Products Need to Talk

The obvious counterargument is: what about when products need to share data? A user who has an account on Aviation Infinity might also use Want To Be a Pilot. Don't they want a single login?

Yes, but shared authentication doesn't require a shared database. I use a common authentication service that handles user identity across products. Each product stores its own user data (preferences, progress, content), but authentication tokens are valid across products. The auth service has its own database, naturally.

When products need to reference each other's data, they do it through API calls. If Want To Be a Pilot needs to show a student's Aviation Infinity progress, it calls Aviation Infinity's API. This is more work than a cross-database query, but it creates explicit contracts between products. I know exactly what data flows between products and can version, cache, and rate-limit those interactions independently.

Practical Implementation

For anyone considering this approach, here's how I implement it practically.

Each product has a separate MongoDB Atlas project. Not just a separate database within the same project, but a completely separate project. This gives maximum isolation at the Atlas level: separate billing, separate users, separate network configurations.

Connection strings are stored as environment variables in each product's Vercel deployment. No shared configuration files, no environment variable inheritance between products.

Monitoring is per-product using Atlas's built-in monitoring for database metrics and Vercel's analytics for application metrics. I have a simple dashboard that shows me the health of all products at a glance, but the underlying data is completely separate.

Backups run on per-product schedules. Products with high write volume back up more frequently than products with mostly read traffic. Each product's backup retention matches its data criticality and regulatory requirements.

The Broader Philosophy

Database-per-product is a specific implementation of a broader philosophy: minimize blast radius. When you're a solo founder, every production incident is a crisis because there's nobody else to handle it. The best way to manage this risk isn't better monitoring or faster incident response, though those help. It's ensuring that any single failure affects only one product.

This principle extends beyond databases. I deploy products to separate Vercel projects. I use separate domain names. I maintain separate Git repositories. Each product is an independent unit that can be deployed, scaled, debugged, and if necessary, taken offline without affecting any other product.

Is this more work? Slightly. Is it more expensive? Marginally. Is it worth it? Absolutely. The peace of mind of knowing that my products are truly independent is invaluable when you're the only person responsible for all of them.