Blog — Advancing Analytics

After the Standing Ovation: The Topology of Productionising Built-On Databricks

Written by Ust Oldfield | 23/10/2026, 10:36

A while ago I wrote about a Built-On Databricks prototype: a multi-tenant analytics product with Databricks doing the heavy lifting underneath and deliberately hidden from view. What I hadn't expected was how much of the persuading the prototype did on its own. Showing it to a room tends to end with people nodding, and once or twice with something not far off a standing ovation.

And then the room sits down, the applause stops, and someone asks the question the demo was always going to provoke: this is lovely, but how do we get it into production, and how does it hold up when we have not one customer but forty?

Underneath, that is a question about topology: where things live and how they find each other. It has no single answer, only a handful of choices and an order in which you are advised to make them. Those choices cluster in three places: where the data lives, where the interface lives, and the seam between the two.

(A small plug: I wrote a whitepaper about turning data into a revenue stream using Built-On Databricks, which you can download here.)

Where We're Going, There Is No Workspace

Let me start with the data, and with a small heresy. In the prototype, everything sat in a single Databricks workspace, exactly as the PWAF guidance suggests, and for one tenant that was sensible. The moment you have several, the workspace stops being the thing you think it is.

A workspace is a shared room for people to do collaborative data work in. But in a Built-On product the customer's users never see it; they see your product, and all that collaborative furniture sits unused. What you are left with is something narrower: a container that does four jobs:

  • It holds the compute

  • It draws a quota boundary

  • It defines an identity scope

  • And it draws a security perimeter

That is not really a workspace any more. It is a runtime container with a quota, an identity, and a perimeter, and since I need a word for it that isn't "workspace", and naming things is half the battle (the other half being everyone disagreeing with the name you chose), I am going to call it a service envelope. If the Databricks product team would like to give it a proper name, I will defer happily.

Why does this matter? Because the data plane is where the platform's limits bite first, and Genie is usually the first to complain. Its constraints show up in the envelope, but its value is decided upstream, which is an argument I have made at length elsewhere. At small scale you don't feel the tension. At larger scale you find the envelope holds a finite amount of Genie throughput, serving concurrency, warehouse capacity and vector-search headroom. 

The cheapest place to begin is a single shared envelope, the tenants kept apart by Unity Catalog. This works well right up until the busiest tenant starts eating into the throughput the quietest one is paying for, at which point you learn two things: that "we're isolated by catalog" does not satisfy a procurement team, and that the per-resource limits were never catalog-aware to begin with.

From there you have two options. You can pool, running several envelopes and spreading tenants across them with a sticky assignment, which shares the load without multiplying your operational burden customer by customer. Or you can dedicate, giving a customer an envelope of their own, which buys the isolation a procurement team will sign off on, at the cost of every envelope carrying its own Terraform, its own Service Principals, its own monitoring and its own support requirements.

 

You can dodge that decision by making a different one, which is to tier: standard customers into a shared pool, premium customers into dedicated envelopes, with the price list and the architecture describing the same diagram. For once the commercial and technical models are arguing about the same thing rather than past each other. It is also where the architecture becomes a question of margin, because a dedicated envelope carries a cost floor that a shared pool spreads across everyone. Where you draw the tier line is therefore where you decide which customers pay for their own infrastructure and which are subsidised.

One last thing the data plane will press on you, and it is not soft: Data Residency. Sign a customer with data-residency obligations and you are into regional pools, and because the Unity Catalog metastore is scoped per region per account, this is an architectural boundary rather than a configuration flag.

You Can't Share Memory With Us

The presentation side has a well-trodden ladder, and the question is how far up do you climb before a customer obliges you.

Stage zero is the shared, vendor-branded instance: one application, your brand, every tenant inside it. That is the prototype, and there is no shame in it. 

Stage one themes that instance per tenant, with their logo, palette and a custom domain behind a reverse proxy. Theming is cheap, a separate instance per customer is not.

Stage two is a dedicated instance per customer on your infrastructure. The reason is that some customers will insist on not having their application processes sharing memory with their competitors', and will pay for the separation.

Stage three, embedding your analytics as a widget or iframe or SDK inside the customer's own portal, is less a rung and more a fork in the road: some customers don't want another application to log into, they want the analytics inside the one they already have. Treating that as a separate product rather than a deployment mode clarifies the conversation.

Stage four is bring-your-own-cloud (BYOC): your application as a managed container inside the customer's cloud, your control plane still handling releases, configuration and telemetry. It is the rung that most mirrors what Databricks does to you, the control plane staying a service while the data plane sits in the customer's account. 

Stage five, full self-hosting, where the customer holds the artefact and you ship releases, is the territory of anyone whose security posture forbids vendor processes outright. 

Don't build either of the last two until a real customer with a real signature asks, because the support burden is considerable and speculative work here tends to be rebuilt the moment the genuine requirement arrives.

You Shall Not Pass (Without an Asserted Identity)

This is the plane I find most neglected, because it sits between two teams and is less visible than either the data or the interface. It is also where most of the commercial flexibility lives, and most of the avoidable pain. Four things live in the seam: cardinality, authentication, networking, and observability. 

The first is cardinality. One instance of your application can serve many envelopes, routing each request to the right one by who has logged in. One envelope can sit behind many interfaces, a branded portal, an embedded widget, an internal admin console, all on the same backend. Weld "one interface, one envelope" into your routing layer and you will be unpicking it the first time a customer wants both the portal and the widget, so treat the tenant-to-envelope mapping as configuration, not something set in concrete at deployment.

The second is authentication, and I want to correct something I have implied myself in the past. In a Built-On product the end user has no Databricks account and is never provisioned into Unity Catalog; they sign in through their organisation's identity provider, and every call into Databricks goes through a per-tenant Service Principal held server-side. That is the SSO-SPN pattern I described in the earlier post. It is tempting to treat the Service Principal as a starting point you later upgrade to on-behalf-of-user authentication, with Unity Catalog enforcing each real human's permissions and naming them in the audit log. For Built-On, that upgrade is a mirage. 

Unity Catalog can only enforce on a principal it knows, and your end users are, by design, principals it has never heard of. The only way to make it enforce per human is to provision your customers' users into the customer's Databricks account and federate their identity provider into it, and the moment you do, you have stopped doing Built-On.

So if the Service Principal is both the floor and the ceiling at the Unity Catalog boundary, where does per-user control come from? It comes from your application.

For row-level enforcement per human, your application asserts the verified user's identity into the query session, and your Unity Catalog row filters and column masks read from that asserted context rather than the platform's own notion of the current user. The consequence is that your application becomes the trust boundary: assert the wrong user and Unity Catalog will faithfully enforce the wrong thing, so the code that sets that context deserves a lot of scrutiny. Which human ran which query is then reconstructed above Unity Catalog, by tagging each query with the end user's identity so their name lands in the query history even though the query ran as the Service Principal. Enforcement happens at the envelope's edge, and attribution is reassembled inside the application. It is less elegant than the on-behalf-of story, but it is the one that is true here.

This is why authentication design is a decision to make early.

The third thing is network posture, a ladder you climb on demand: public endpoints, IP allowlists, frontend PrivateLink into the envelope, then fully private with backend PrivateLink and a Network Connectivity Configuration. 

The fourth, usually left until it is urgent, is observability across the seam. When a customer says their dashboard is slow you want to follow one action from the user's session through the routing layer to the envelope, to the warehouse or serving endpoint, to the query plan, and back. That is a tracing problem with a tenancy dimension, and if your traces don't carry the tenant and the envelope as first-class properties from the start, you will spend your first year in production diagnosing by intuition and apology. The instrumentation costs little up front and saves you that miserable first year. 

Know When to Build 'Em, Know When to Defer 'Em

If the prototype has earned its applause and someone is asking about production, the decisions worth committing to early are the ones painful to reverse. The first is the tenancy model, and in particular whether you intend to tier, because the tier line is the margin line and the price list will follow the architecture whether you planned it to or not. The second is the authentication posture, because so much downstream is shaped by it. The third is the regional strategy, because the metastore boundary makes it a hard split rather than a soft one. 

Design, but don't yet build, BYOC and dedicated hosting. Have the architecture sketched so you can show a customer something credible when they ask, but resist building on spec; the requirements vary enough between customers that speculative work tends to be thrown away when the real version arrives.

Defer entirely, until there is a named customer with a signature: the air-gapped and fully sovereign deployments. They are real markets, but not your first, and the cost of getting them wrong with no one to anchor them to makes patience the cheaper option.

Going to production is a sequence of topology choices, made deliberately and in a sensible order, that fit the customer in front of you while leaving room for the ones you haven't met. The prototype is always easy. The hard work is everything between the standing ovation and the signed contract, and if you are somewhere in the middle of it, we're here to help.

I wrote a whitepaper going into more depth on turning data into a revenue stream using Built-On Databricks; you can download it here.