In this article ⏷

One Model, Every Runtime

Same Entities, Native Code For SSIS, ADF, Databricks, And Fabric

July 8, 2026

A platform migration usually means a rewrite. Your warehouse runs on SSIS, the mandate comes down to move to Fabric or Databricks, and someone re-derives every staging load, every dimension merge, every key from scratch in a new dialect. The model in your head did not change. The runtime did. But the work treats them as the same thing, so you pay for both.

BimlFlex separates the two. The entity definitions, the relationships, the load patterns, the change-tracking rules: that is the model, and it lives in metadata. The runtime is a setting. When you switch the target, BimlFlex routes the same model through a different emitter and writes code native to that platform. The migration becomes a regeneration.

This post is about that portability of the runtime. The companion piece on whether the generated keys stay identical across platforms is its own story, covered in keys that reconcile across SQL Server, Fabric, and Databricks. Here the question is narrower and more structural: what does "same model, different runtime" actually mean in the code you receive?

Two axes, not one

People talk about "the target" as a single choice. In BimlFlex it is two, and keeping them separate is what makes the model reusable.

The first axis is the integration template: how the pipeline is orchestrated and where the compute runs. The product ships five.

  • SSIS (SQL Server Integration Services)
  • ADF (Azure Data Factory)
  • DBR (Azure Databricks)
  • FDF (Fabric Data Factory)
  • FBR (Fabric Lakehouse)

The second axis is the target system: the database or warehouse the code lands in and whose dialect it must speak. That list is longer.

  • SQL Server, Azure SQL Database, Azure Synapse Analytics
  • Snowflake, Databricks, Delta Lake
  • Fabric SQL Database, Fabric Data Warehouse, Fabric Lakehouse
  • Oracle, Teradata, PostgreSQL, MySQL

A model is a set of entities and rules. A deployment is that model crossed with a template and a target system. The same entities can produce an ADF pipeline writing to Synapse, a Databricks notebook writing Delta, or a Fabric Lakehouse load, because orchestration and dialect are configuration, not content. This is the same split that makes Fabric automation worth the trouble even when the platform already promises unification: the consistency lives in the model, not in any one runtime.

The emitter does the translating

Generation is not a single template with the target name pasted in. BimlFlex dispatches on what the project is. An SSIS project produces SSIS packages. A Data Factory project produces ADF or Fabric Data Factory pipelines. A Databricks project produces notebooks. A Fabric project produces Lakehouse or Warehouse artifacts. Each path is a distinct emitter that knows the idioms, the file formats, and the constraints of its runtime.

That is why you do not hand-wire the difference between, say, an SSIS data flow and a Databricks notebook. The model says "stage this source, then load this hub." The SSIS emitter renders that as a package; the Databricks emitter renders it as notebook cells against your cluster. Same instruction, two deliverables, both native. The point of breaking the scale ceiling is exactly this: the model scales because the per-runtime translation is generated, not typed.

Where it gets specific: hash SQL per dialect

The clearest place to see the emitter earning its keep is the hash key expression. Every platform can compute a hash, but none of them spell it the same way. A naive port would write SQL Server syntax everywhere and break on the first non-SQL-Server target. BimlFlex writes each one in the target's own dialect.

On SQL Server and its relatives, including Azure SQL Database, Synapse, and Fabric SQL Database, the generated expression is HASHBYTES over an NVARCHAR(MAX) cast:

UPPER(CONVERT(CHAR(40), HASHBYTES('SHA1', CONVERT(NVARCHAR(MAX), @@this)), 2)) 

Fabric Data Warehouse gets HASHBYTES too, but over a VARCHAR(MAX) cast, because that is what the warehouse expects:

UPPER(CONVERT(CHAR(40), HASHBYTES('SHA1', CONVERT(VARCHAR(MAX), @@this)), 2)) 

Databricks does not have HASHBYTES. The generated expression uses Spark SQL functions instead:

SHA2(@@this, 256) 

Snowflake has its own hash family, and the emitter writes that one:

SHA2(@@this, 256) 

Fabric Lakehouse gets its own pattern again. Five families of target, and the same conceptual instruction, "hash this business key," comes out as five different valid expressions. You did not write any of them. You set the target system and the hash algorithm, and the emitter resolved the rest. If you had hand-coded the SQL Server version and then "moved" to Databricks, every one of those HASHBYTES calls would be a compile error waiting for you.

A model worth owning

The reason this matters beyond convenience is ownership. When the runtime is a setting, you are not married to it. A team that built on SSIS years ago can target Fabric today without re-litigating the model, because the model was never written in SSIS to begin with. It was written in metadata, and SSIS was one rendering of it.

That is the difference between an asset and a rental, which is the whole argument in building an asset instead of renting a liability. Code locked to one runtime is a liability the day that runtime stops being the right choice. A model that regenerates to whichever runtime you pick keeps its value as the platform landscape shifts under it, and it shifts often: the data lake, platform, and fabric choices that look settled this year rarely stay settled.

Switching targets will never be free. There is a deployment to stand up, connections to point at the new platform, a load to validate. But the part that used to dominate the cost, re-deriving the model in a new dialect by hand, is the part BimlFlex deletes. You change the target, you regenerate, you get native code for the new runtime. The model stays exactly where it was.