When we set out to build our internal platform at Deveote, one of the earliest and most consequential architectural decisions was how to implement multi-tenancy. This decision would affect everything from database design to deployment strategy, security model, and pricing flexibility. After evaluating multiple approaches and learning some expensive lessons along the way, we have developed a set of principles that we now apply across all our venture studio projects.
Choosing Your Tenancy Model
There are three primary approaches to multi-tenancy: shared database with shared schema, shared database with separate schemas, and separate databases per tenant. Each has tradeoffs in complexity, isolation, and cost. For most African B2B SaaS products, we recommend starting with a shared database and shared schema approach, using a tenant identifier column on every table. This minimizes infrastructure costs and operational complexity while providing sufficient isolation for early-stage products.
When to Graduate to Separate Databases
As your product matures and you onboard enterprise customers, you may need to offer stronger data isolation guarantees. Regulatory requirements, customer security policies, or performance SLAs may necessitate dedicated database instances for specific tenants. Design your data access layer with this migration in mind from the beginning. We use a repository pattern with a tenant context that can be swapped from shared to dedicated without changing application code.
Row-Level Security
The most critical aspect of multi-tenancy is ensuring that one tenant can never access another tenant's data. In a shared schema model, this means every database query must be scoped to the current tenant. We enforce this at multiple levels: middleware that sets the tenant context from the authenticated session, a data access layer that automatically applies tenant filters to all queries, and database-level row-level security policies as a safety net. This defense-in-depth approach means that a bug in one layer does not compromise tenant isolation.
Testing Tenant Isolation
We run automated tests that specifically attempt to access data across tenant boundaries. These tests create data for tenant A, authenticate as tenant B, and verify that no cross-tenant data leakage occurs. These tests run in our CI pipeline on every commit, because a tenant isolation bug is a company-ending security incident.
Performance Considerations
In a shared database model, a single tenant's heavy usage can affect the performance of all other tenants, a phenomenon known as the noisy neighbor problem. We mitigate this through query timeouts, connection pooling limits per tenant, and rate limiting at the API layer. For computationally expensive operations like report generation, we use background job queues with per-tenant concurrency limits.
Database Indexing Strategy
Every query in a multi-tenant system should include the tenant identifier in its WHERE clause, and your indexes should reflect this. Composite indexes that lead with the tenant ID ensure that query performance does not degrade as the total number of records grows. We regularly review slow query logs and add missing indexes, always ensuring the tenant ID is the leading column.
Tenant Onboarding and Provisioning
The onboarding experience for a new tenant should be seamless and fast. We automate the entire provisioning process: creating the tenant record, seeding default configuration, setting up the initial admin user, and configuring any tenant-specific integrations. This process should complete in under thirty seconds, because first impressions matter.
Conclusion
Multi-tenancy is deceptively simple at the surface but deeply complex in practice. The decisions you make early in your product's life will have lasting consequences for security, performance, and operational complexity. Invest the time to get your tenancy model right, and you will have a foundation that scales with your business rather than constraining it.