02 / 06

Discipline

Data Engineering & Database Administration

Two things that usually get separated and should not be. The first is designing how your data is stored, related, secured, and moved: schemas, migrations, the queries that carry the load, and the rules that keep one customer’s information away from another’s. The second is keeping that database alive, which means backups that have actually been restored, performance that does not quietly degrade, and recovery that works on the day it is needed.

Most providers are enthusiastic about the first and vague about the second.

Approach

Positions we hold

How we approach it.

Tenant isolation belongs in the database

If your platform serves multiple customers, the most consequential decision you make is where isolation is enforced. The common answer is the application layer, where every query filters by tenant and correctness depends on every developer remembering the filter, forever. One missed clause in one endpoint is a data breach you find out about from a customer.

We put it in the database. Row-level security means the database refuses to return another tenant’s rows regardless of what the application asks for.

What that costs

Row-level security has a query-planning cost, complicates some administrative operations, and needs care with connection pooling and session context.

We think that is a good trade for removing an entire category of breach, and we would rather say what it costs than present it as free.

Shared schema or per tenant is a decision

Most platforms have whichever model the first developer implemented, and no record of a decision. Shared schema is cheaper to operate, simpler to migrate, and better with many small tenants. Per-tenant databases give real blast-radius isolation, per-customer backup and restore, and a straightforward answer on data residency.

They also multiply migration and operations work by the number of tenants. Pick against the customers you actually expect.

One codebase can serve both

One platform we run routes tenants across SQLite and per-tenant SQL Server from a single codebase and a single release cycle, because different deployments of the same product had genuinely different requirements.

That is not a trick. It follows from treating tenancy as an architecture decision rather than a default.

Migrations are part of the product

A schema change that cannot be applied without downtime is a design problem rather than an operations problem. Migrations are versioned, reviewed, applied through the pipeline, and written to be reversible where reversal is possible and explicitly one-way where it is not.

A backup nobody has restored is a belief

We test restores into an isolated environment on a schedule and record what the restore actually took. Recovery time is a number you want to learn before an incident.

The same applies to indexing. Reports that time out are rarely a hardware problem, and finding the cause is usually a matter of looking rather than guessing.

Where we stop

We are not a data science or analytics practice. We build and run the operational data layer, including the reporting a system needs to explain itself. If you want a warehouse, a modelling layer, and a BI practice on top, we will build the pipelines that feed it and tell you that the analytics work belongs with a specialist.

Stack

What actually runs in production.

SQL Server

Per-tenant SQL Server

Postgres

Row-level security

SQLite

Redis

Entity Framework Core

CQRS

SQL Server Reporting Services

Kafka

Supabase

Firebase

Versioned migrations

Engagements

Typical shapes this takes.

Tenancy design

Choosing and implementing the isolation model, before or during a build. Days to weeks, and an order of magnitude cheaper before launch than after.

Performance and cost work

Finding what actually got slow, and what is driving the bill on a system that has grown.

Database administration on a retainer

Backups, restore testing, index maintenance, recovery planning, capacity.

Deliverables

What you get.

A written tenancy decision

With the trade-offs stated and the reasoning kept.

Migration history under source control

Nobody has to reconstruct what state the database is in.

Isolation policies in the database

Row-level security where isolation matters.

A tested restore

With a recorded recovery time, rather than a screenshot of a backup setting.

Before and after on remedial work

What each query did, what it does now, and why it changed.

Ownership

Schema, migrations and data are yours throughout.

Reading

Related reading.

The strongest seam here is routing tenants across SQLite and per-tenant SQL Server from one codebase, and the case for row-level security over application-layer filtering. Those pieces will be linked here as they are published.