Multi-Tenant SaaS
Tenant boundaries enforced everywhere: data, jobs, files, caches and reports.
- Data isolation
- Tenant context
- Membership
- Noisy neighbours
I've worked across SaaS products from hands-on engineering through technical leadership and delivery. My approach to SaaS development focuses on product structure, tenancy, user management, permissions, billing, infrastructure, maintainability and the realities of running software in production.
Multi-tenancy · Permissions · Billing · Production delivery
How I think about it
SaaS architecture should reflect the business model. Tenancy, permissions, plan limits, billing and data ownership are product architecture decisions, not features that should be bolted on later.
Key experience
Scope
Deep dive
Most SaaS products don't struggle because the framework couldn't scale. They struggle because boundaries were never drawn: billing logic leaks into controllers, tenant checks live in the UI, and every new feature has to understand everything else.
Early on I keep the architecture deliberately plain — a well-structured monolith with clear modules for accounts, billing, the core product domain and integrations. That is easier to change than premature services, and it keeps data ownership obvious: each module owns its tables and exposes behaviour, not raw data.
Request
web, API or job
Identity & tenant
who, and for which organization
Access & plan rules
permissions, limits
Domain logic
the product itself
Tenant-scoped data
database, files, queues
Single-tenant setups — a separate instance or database per customer — make sense when customers need hard isolation, their own release schedule or specific data residency. They cost more to run: every migration, deployment and alert is multiplied by the number of customers.
Shared infrastructure with a tenant identifier on every row is the right default for most B2B SaaS. The risk then moves into the code: a missing scope is a data leak. So tenant scoping has to be automatic — applied by default in the data layer and covered by tests — not something each developer remembers.
Multi-tenancy is not only a database decision. It affects authentication, authorization, billing, reporting, background jobs, caching, file storage and support tooling.
I model the organization as the customer and the user as a person who can belong to several organizations. Treating “user = account” is the shortcut most products regret the first time a customer wants to invite a colleague, or an agency manages several clients.
Membership is its own entity, with a role, a status and who invited whom. Invitations are tokens tied to an email and an organization, with an expiry, and they must handle the invitee signing up with a different address. The current tenant is resolved once per request and passed explicitly — not inferred deep inside a query.
Roles are for people; permissions are for code. Checking role === 'admin' in fifty places turns every new role into a refactor. I check permissions — “can manage billing”, “can invite members” — and map roles to permissions in one place.
Organization-level roles such as owner, admin and member cover most products. Resource-level rules — who can edit this project — live in policies next to the domain and are enforced on the server for every request and API call. The UI only hides what the server would reject anyway.
Plans are product rules, so they belong in data and code the product owns — not in scattered conditionals or a payment provider's dashboard. A plan defines features (on or off), limits (quotas) and sometimes defaults inherited from a base plan; the application asks one question through a single entitlement check: can this organization do this, right now?
Limits are enforced server-side at the moment of the action, including in background jobs and the API. Decide early what happens at the limit — block, warn or allow overage — because changing that later is a support and pricing problem, not just a code change.
The payment provider is the source of truth for billing events: payments, failures, renewals, cancellations. The application is the source of truth for access: what an organization can use right now. Mixing the two produces the classic bugs — premium features after a failed payment, or lost access because a webhook arrived twice.
I treat a subscription as a state machine driven by verified, idempotent webhooks — trialing, active, past due, cancelled — with a way to reconcile missed events. Upgrades usually apply immediately; downgrades at the end of the period, and the product has to define what happens to data above the new plan's limits.
Payment provider
source of billing events
Webhook handler
verified, idempotent
Subscription state
trialing, active, past due…
Entitlements
features and limits
Product access
source of access rules
Signup is the easy part. Trust is won or lost in the lifecycle around it: email verification, password resets that expire, email changes confirmed from both the old and new address, sessions invalidated when a password changes, and accounts that can be disabled without destroying data.
Deletion deserves design up front: what happens to an organization when its owner leaves, which records must be retained, and what can be removed on request. Leaving this to the first support ticket usually means manual database work.
Anything slow, external or retryable belongs in a queue: emails, exports, file processing, outgoing webhooks, third-party syncs. The request accepts the work and returns; the job does it and reports its status.
Jobs fail, so I design for it: retries with backoff, idempotency so a retried job doesn't send two invoices, visible failures, and tenant context passed explicitly into every job. A queue nobody monitors is a place where work silently disappears.
Internal APIs serve your own frontend and can evolve with it. Public APIs are contracts: they need versioning, consistent errors, pagination and rate limits.
Each third party — payments, email, storage, analytics — sits behind a small integration layer: one place for credentials, timeouts, retries and mapping their errors to yours. Inbound webhooks are verified, stored and processed asynchronously, so a slow handler never triggers a provider's retry storm.
Files belong in object storage, not on the application server. Uploads go directly to storage through short-lived signed URLs where possible; the database stores metadata and ownership; and the application checks access before issuing a signed download link.
Processing — thumbnails, parsing, exports — runs asynchronously. Generated files get an owner, an expiry and a predictable path that includes the tenant, which keeps both access rules and clean-up simple.
Every SaaS needs an internal admin, and it's usually built too late. Support needs to find a customer, see their plan and usage, apply an override, resend an invitation and understand what happened — without a developer running database queries.
Admin actions are permission-checked and audited like any other action. An audit log of who changed what, and when, pays for itself the first time a customer asks why something changed.
Shipping SaaS is continuous: separate environments, releases small enough to reason about, migrations that are safe to run while the previous version is still serving traffic, and a clear idea of how to roll back.
In production I want to know about failures before customers do: structured logs with tenant and request context, error tracking, queue health, and a few product-level signals such as failed jobs and failed payments. After launch, the roadmap is shaped by what real users actually do.
In SaaS, accessibility is part of product quality — and increasingly part of procurement. Building shared components accessibly once — forms, dialogs, tables, menus — is far cheaper than auditing and patching every screen later. Delivering an accessibility SaaS platform makes that very concrete: the product has to meet the standard it helps others reach.
AI earns its place in a SaaS product when it removes real work inside an existing workflow — classifying, extracting, summarising, drafting. It sits behind the same permissions, plan limits and audit trail as any other feature, with validated outputs and a cost that fits the pricing model.
In practice
Real products
These patterns come from products I've built, led or contributed to — a multi-tenant collaboration platform, an accessibility SaaS, a school management system and a portfolio builder.
Enterprise SaaS
Led development of a multi-tenant, multi-user platform: organizations and users, file collaboration, APIs, database design and frontend integration.
Accessibility Technology / SaaS
Large accessibility SaaS where I lead cross-functional delivery, technical decisions and production releases.
School Management SaaS / EdTech
School management SaaS where role-based access, payments, multi-language support and day-to-day administration are core product concerns.
SaaS / Portfolio Builder
Founder-led portfolio SaaS where product structure, architecture and engineering decisions are mine end to end.
Tenant boundaries enforced everywhere: data, jobs, files, caches and reports.
Predictable API contracts: validation, errors, authorization, idempotency and webhooks.
Maintainable backends: structure, Eloquent, queues, authorization and SaaS patterns.
Workload-first cloud architecture: serverless, storage, data, security and cost.
Design and build SaaS products from MVP to production, including architecture, user management, subscriptions, integrations and cloud infrastructure.
Learn moreBuild reliable web platforms, SaaS products, dashboards, APIs, and internal tools with modern full-stack technologies.
Learn moreDeploy, optimize, and maintain reliable cloud systems using AWS services and practical cloud architecture.
Learn moreWhether it's an architecture decision, a delivery problem or something specific to SaaS Development, I can help you find a practical path forward.
What happens next
Share your goal
A few lines about the product, problem, or audit need.
Get a quick read
A practical reply on the best technical path forward.
Move forward
A clear scope, estimate, or concrete next step.