Skip to the page
Tecyfy

System design interview question · hard

You are building analytics for a SaaS product with 2,000 customer companies. Each customer gets dashboards and can query their own data, and must never see another customer's. Design the data layer.

The short answerChoose the isolation model by what each tenant needs, then enforce it in the platform, not in each query.

The full answer

Choose the isolation model by what each tenant needs, then enforce it in the platform, not in each query. For most SaaS analytics, keep shared tables with a tenant_id on every row and attach a row filter that the platform applies to every query; give large or regulated customers their own schema or catalog. Map users to tenants in one table, test the boundary continuously, and never rely on a dashboard's WHERE clause for security.

1. Three isolation models

ModelHowFits
Shared tables + row filterOne table, tenant_id column, a filter function applied at query timeMany small tenants; easiest to operate and change
Schema or catalog per tenantSame tables copied per tenant, grants per schemaLarge tenants, custom retention, contractual separation
Separate workspace or accountFully separate platformStrict regulation or data residency

With 2,000 tenants, a schema each means 2,000 copies of every table and every pipeline change applied 2,000 times. A common answer is shared tables for most, with the option to move a large customer to its own catalog.

2. Enforce it in the platform

CREATE FUNCTION gov.tenant_filter(t STRING)
RETURN EXISTS (
  SELECT 1 FROM gov.user_tenants u
  WHERE u.user_email = current_user() AND u.tenant_id = t
) OR is_account_group_member('platform_admins');

ALTER TABLE analytics.orders SET ROW FILTER gov.tenant_filter ON (tenant_id);
  • Every query, from a dashboard, a notebook or an API, gets the filter. A forgotten WHERE cannot leak data.
  • Keep the function simple and fast: it runs for every query. Where a lookup is slow, give each tenant's users a group and test group membership instead.
  • Across many tables, apply the same rule through attribute-based policies on tagged tables, instead of setting it table by table.
  • Column masks hide sensitive columns (emails, card numbers) even inside a tenant, for users without the right role.

3. Identity and mapping

  • One user_tenants table is the source of truth, fed from the product's account system, with an owner and an audit trail.
  • Service accounts for embedded dashboards act on behalf of one tenant at a time, with the tenant taken from the authenticated session, never from a URL parameter.

4. The pipelines

  • tenant_id is required (not null) in every silver and gold table, and checked by expectations.
  • Aggregates are computed per tenant. Cross-tenant tables (benchmarks, internal reporting) live in a separate schema with no customer access.
  • Caches and extracts are keyed by tenant.

5. Prove it

Automated tests log in as a user of tenant A and query every exposed table, expecting zero rows from tenant B. Alert on any table without a filter or policy, and review access logs for queries returning several tenants.

What interviewers want: weighing shared, per-schema and per-account isolation, platform-enforced row filters rather than query discipline, a single user-to-tenant mapping, tenant IDs enforced through the pipelines, column masks, and continuous cross-tenant tests.

Last updated 16 October 2026