Every SaaS product has to answer the tenancy question, usually earlier than the team feels ready to answer it permanently. The good news: there are a small number of well-understood models, each with clear trade-offs, and the right choice depends on requirements you likely already know.
The three common models
Shared schema, shared tables. Every tenant's data lives in the same tables, distinguished by a tenant ID column. This is the fastest to build initially and the most efficient for infrastructure at small-to-medium scale, but it puts the entire burden of tenant isolation on application code — every single query must filter by tenant ID correctly, with no exceptions.
Shared database, separate schemas. Each tenant gets their own schema within a shared database. This adds a layer of isolation enforced at the database level, at the cost of more operational complexity — migrations need to run across every tenant schema, and connection management gets more involved.
Separate databases per tenant. The strongest isolation, often required for specific compliance or contractual requirements, at the highest operational cost — you're now managing N databases instead of one, and cross-tenant reporting or analytics becomes meaningfully harder.
The isolation enforcement question matters more than the model
Regardless of which model you choose, the more important decision is where isolation gets enforced. Relying entirely on application code to remember to filter every query by tenant ID is fragile — a single missed filter in a new feature is a data leak. PostgreSQL's row-level security lets you enforce tenant isolation at the database layer itself, so even a buggy query can't return another tenant's data. We default to this for shared-schema architectures specifically because it removes an entire class of bug from being possible, rather than relying on code review to catch it every time.
Permissions don't stay simple
Early-stage SaaS products often start with a flat permission model — you're either in the account or you're not. This breaks down quickly once customers have more than a few users: someone needs read-only access, someone needs to manage billing but not user data, an external contractor needs scoped access to one project. Designing a role-based (or better, capability-based) permission system from early on is meaningfully cheaper than retrofitting one after customers are already depending on the flat model.
Billing complexity arrives faster than expected
"We'll just charge a flat monthly fee" survives contact with real customers for a surprisingly short time. Usage-based components, seat-based pricing, annual contracts with custom terms, and mid-cycle plan changes all show up faster than founders expect, and each one interacts with the others in ways that are easy to get wrong — proration on a downgrade combined with a usage overage combined with a failed payment retry is not a hypothetical edge case, it's a Tuesday.
Build the internal tools alongside the product
The tenancy, billing, and permissions decisions above all eventually surface in tooling your own team needs — an internal admin panel to help a customer who's locked out, visibility into which tenants are approaching plan limits, tools to debug a billing discrepancy. Teams that treat this as "we'll build it later" usually end up debugging production issues directly against the database, which is a worse position to be in than building the tooling proactively.
None of these decisions need to be perfect on day one. But they should be deliberate — made with an understanding of the trade-offs, not defaulted into because it was the fastest way to ship the first version.