B2B SaaS product development

Multi-tenant SaaS is one decision you make early and pay for slowly.

Everything expensive about a B2B SaaS product traces back to three choices: how you isolate tenants, how you handle enterprise identity, and how usage becomes an invoice. Get them wrong and you find out in the quarter a serious customer asks for SAML and a security review. This page is what we’d build, and why.

Pressure-test your architecture

The tenancy decision

Four isolation models, judged on what they cost you in year three.

Judge them on four axes rather than on elegance: blast radius when something goes wrong, behaviour under a noisy neighbour, whether you can restore a single tenant to a point in time, and what a schema migration costs once you have N tenants instead of five.

Shared schema, tenant_id column, filtering in application code

Unacceptable in 2026

Every table carries a tenant_id and every query is supposed to filter on it. Blast radius is the entire customer base, and the failure mode is one forgotten WHERE clause in a reporting endpoint written at 2am. Nothing enforces the boundary below your ORM, so every customer’s safety rests on code review catching a missing predicate, forever. Cheapest to run, and the reason most cross-tenant leak postmortems read the same way.

Shared schema with Postgres row level security

Correct for almost everyone under Series A

Same physical layout, but the filter moves into the database as a policy the application can’t forget. Blast radius shrinks to a policy bug rather than a query bug, and you can test a policy bug in a way you can’t test an absent WHERE clause. Noisy neighbours still share a buffer cache and a pool, and per-tenant point-in-time restore is hard because one tenant’s rows are interleaved with everyone else’s. Migrations are one ALTER TABLE.

Schema per tenant

A middle position that ages badly

Isolation is real and per-tenant restore becomes tractable. The cost lands on migrations: at 2,000 schemas a single column addition is 2,000 DDL statements, catalogue bloat starts to hurt autovacuum and planning, and pooling gets awkward because search_path is session state. Online schema change tooling stops being optional the day a migration can’t finish inside a deploy window.

Database per tenant

A commercial decision, not an engineering default

The cleanest possible answer to an enterprise security questionnaire, the cleanest per-tenant restore, and complete freedom from noisy neighbours. It also runs roughly two to three times the infrastructure and operational cost of a shared model, and every migration becomes a fleet orchestration problem with partial-failure states. Build this when a named customer is paying for it, or when a regulator or data-residency rule leaves you no choice.

Row level security

Put the boundary somewhere a developer cannot forget it.

Postgres row level security turns tenant scoping from a convention into a constraint. Two details decide whether it works. A USING clause filters the rows a statement is allowed to see, so it governs SELECT, UPDATE and DELETE. A WITH CHECK clause governs the rows a statement is allowed to write. Declare only USINGand you have built a system where a tenant cannot read another tenant’s row but can happily insert one stamped with their neighbour’s identifier. That’s a real bug, it’s quiet, and it usually surfaces as corrupted data rather than as a security alert.

The second detail is boring and costs more money: every policy predicate needs a supporting index. Postgres appends the policy to your query, so a tenant filter without a leading index on the tenant column turns a list endpoint into a sequential scan across every customer’s rows. Fast in staging with four tenants, over at four hundred. Composite indexes generally need the tenant column first, and you check plans under the policy rather than without it.

Then there’s the question of how the database learns which tenant is asking. A session variable set per request is simple and works anywhere, but it’s session state, and if you run transaction-mode connection pooling that state doesn’t belong to your request for longer than a statement. Set it inside the transaction and let it die with the transaction, never on a checked-out connection. The alternative is signed claims carried in a JWT and read directly by the policy, which removes the session-state problem and replaces it with token lifetime and revocation questions. Both are defensible. Choosing one by accident isn’t. We work through this in detail on our Supabase and Postgres RLS work.

Two operational requirements quietly eliminate options. Migrations across thousands of schemas need online change tooling and a resumable runner, because a fleet migration will fail halfway at some point and you’ll need to know which tenants are on which version. And per-tenant point-in-time restore, which enterprise contracts increasingly ask for by name, is close to trivial with a database per tenant and painful in a shared schema, where restoring one customer means extracting their rows from a full-cluster snapshot into a staging instance and replaying them without touching anyone else. If that requirement is real, decide it before you write the first migration.

The enterprise wall

SSO and SCIM aren’t features. They’re the price of the logo.

B2B SaaS budgets die at the enterprise-readiness wall. A deal reaches procurement, security review asks for single sign-on, directory-driven deprovisioning, an audit trail and granular roles, and a team that built none of it loses a quarter. Building the seams for it during the MVP costs comparatively little.

On identity, SAML 2.0 and OIDC are not interchangeable. OIDC is cleaner, JSON-native and far easier to implement, and it’s what you want when the customer will let you have it. SAML is XML, signature-canonicalisation-sensitive, and still what large enterprises run, so a B2B product that wants those customers supports both. Within SAML, service-provider-initiated flows begin at your login screen and are straightforward. Identity-provider initiated flows begin in the customer’s app portal, arrive unsolicited with no request to correlate against, and are where replay and audience-confusion vulnerabilities live. Support them because customers demand them, and validate them properly.

Just-in-time provisioning creates the user on first successful login, which feels like the whole answer and isn’t. It has nothing to say about the employee who left: their directory account is disabled, your record is not, and their session or API token outlives their employment. SCIM 2.0 is the actual answer, because the directory pushes deprovisioning to you rather than leaving you to infer it. JIT also drifts, since attributes only refresh when someone happens to log in, so a role change in the IdP can go months without reaching you. Domain-based auto-join deserves its own warning. Letting anyone with a matching email domain join an existing tenant is convenient, and it’s a real escalation path when the domain is a shared consumer provider or the company was acquired. And supporting Okta well tells you almost nothing about how the next customer’s Entra ID or Ping deployment will behave.

Authorisation deserves the same seriousness. Role-based access covers most products. Attribute-based rules earn their place when access depends on record state rather than user identity, and relationship-based models are what you need once permissions inherit down a hierarchy of workspaces, folders and documents. Wherever the model lands, the check belongs at the query layer rather than the route layer: a guard on an endpoint protects one path, a predicate in the data layer protects every path, including the export job someone adds next year. Cross-tenant admin impersonation is the last piece. Support will need it, it’s the most dangerous capability in the product, and unless you log every impersonated session with the operator, the tenant, the reason and the duration, it becomes the incident.

SOC 2, without pretence

We hold no certification. The architecture half is still ours.

EdgeBrain Studios is not SOC 2 certified and has not been through an audit. No agency can hand you a report anyway. A licensed CPA firm issues SOC 2 to your company, against controls your company operates. You bring the auditor, the policies, the onboarding and offboarding process, the vendor register and the risk assessment. A compliance platform will handle most of the paperwork side of that.

What an engineering partner can own is the half of the Trust Services Criteria that turns into architecture and cannot be bolted on afterwards. Immutable audit logging with an actor, a resource, a before-and-after and a timestamp on every state change, written in the same transaction as the change so the log cannot drift from reality. Access review evidence that’s queryable, because an auditor asking who had admin on this tenant in March is a database question if you modelled it that way and a forensic exercise if you didn’t. Change management traceability linking a deployed commit to a reviewed pull request. And an honest encryption scope: transport and at-rest encryption are the easy claims, key custody and rotation generate the follow-up questions.

The pattern is the same in every direction. Retrofitting audit logging into a product that has been mutating rows in place for two years is one of the most expensive pieces of work in B2B SaaS, and emitting events from the first week avoids all of it. If you’re weighing that kind of retrofit against starting again, our guide to judging a rewrite against a refactor covers how we make that call.

Metering and money

Never bill from a counter you updated in place.

Usage metering should be an append-only event stream with an idempotency key on every event, aggregated into invoice lines and reconciled against your billing provider on a schedule. You can’t explain an incremented counter. When a customer disputes a bill, and they will, an event log lets you show them the timestamped records behind the number. Retries, replays and a late webhook all break counters silently.

The pricing model then reaches back into the data model. Seat pricing needs a defensible definition of an active seat and proration that survives mid-cycle changes. Usage pricing needs accurate, near-real-time aggregation plus soft limits and alerts, because the fastest way to lose a customer is a surprise invoice. Hybrid pricing needs both, plus entitlements that live in data rather than in conditionals scattered through the codebase, so a plan change is a row update instead of a deploy. Trials add expiry, grace periods and a conversion path that doesn’t lose work. The plumbing side of this is on our Stripe integration page, and the MVP cost estimator will give you a rough figure for the build around it.

One more, because it’s new enough that most architectures haven’t caught up. If your product has an AI feature over customer data, tenancy has to reach into the vector layer too. A single shared embedding index with tenant metadata attached is a cross-tenant leak waiting to happen, because approximate nearest neighbour search returns semantic neighbours first and applies your filter second, and any gap between those two steps is an exposure. Use separate namespaces, collections or indexes per tenant, and scope retrieval per tenant before generation. Our AI automation and RAG engineering and AI consulting practice exist for exactly these decisions.

When not to hire us

Some of these products should not be built at all.

If you’re building an internal tool that happens to serve several departments, you don’t have a multi-tenant SaaS problem and you shouldn’t pay for one. Departments aren’t tenants. Use one database, real roles, and skip every word above.

If your idea sits in a well-served horizontal category, buy the incumbent. Applicant tracking, help desk, expense management, e-signature, basic CRM: all solved at a price no custom build reaches. The test is whether your differentiation lives in the workflow or only in the interface. Interface dissatisfaction is not a business case, and a configured off-the-shelf product with an integration layer on top is usually the right answer.

And if you haven’t validated demand yet, a multi-tenant platform is premature. A single-tenant instance for your first three design-partner customers will teach you more, cost a fraction, and can be migrated later at a known price. Don’t let an architecture article talk you into infrastructure your revenue doesn’t justify. Our writeups on what an MVP actually costs and choosing between fixed price and time and materials are the plain versions of that conversation.

Budget and timeline

What this costs, before you email us.

Published bands in USD. Most projects land between $18,000 and $45,000, with the larger platform engagements above that. Our minimum on build work is $7,500, and the architecture sprint sits below it and is credited back against the build.

Architecture and readiness sprint

from $3,500

1 to 2 weeks

Tenancy model chosen and written down with its consequences, an RLS policy design with the indexes it needs, an identity plan, and a migration path if you’re already live on application-level filtering. You leave with a document and a working proof rather than a recommendation deck, and the fee is credited in full against a build that starts within 30 days.

SaaS MVP build

from $24,000

8 to 12 weeks

Tenancy, auth, roles, billing, and one core product loop, live. Built so the enterprise-readiness work later is an addition rather than a rewrite: audit events emitted from day one, identity abstracted behind an interface, entitlements read from data rather than hard-coded.

Platform engagement

from $28,000

12 weeks and up

Multi-tenant platform work with the enterprise surface included: SAML and OIDC single sign-on, SCIM provisioning, an immutable audit log, role-based access enforced at the data layer, and metering reconciled against your billing provider.

Ongoing engineering

from $2,500 per month

rolling, 30 days notice

The work that arrives after launch: the second identity provider that behaves nothing like the first, migrations across a growing tenant fleet, and the security questionnaire that lands mid-quarter with a deadline attached.

The bands above are estimates, shown in US dollars and drawn from comparable work already delivered, because no two projects carry the same scope. Your own quote is not an estimate: it is fixed in writing after scoping, before kickoff, and it does not move.

Three things move a quote inside those bands: how many identity providers you support on day one, whether metering has to be near-real-time or can settle nightly, and whether you’re starting clean or migrating a live product off application-level tenant filtering. That last one is the largest, because it’s a data migration with a security deadline attached. Products with a mobile client scope separately under our mobile app development work, and the web application layer sits with our Next.js web development practice. You can see how we document builds across our published case studies.

Send us your tenancy model.

One paragraph on what you’re building, how tenants are separated today, and the largest customer you want to close. We reply with the architectural risks we can see, a scope and a price, inside two working days.