A CFO gets different revenue figures from two dashboards, even though both pointed at the same Iceberg table. This isn't always because the data drifted, but rather, because the two dashboards point to different engines. "Recognized revenue" was defined in a SQL view in each engine, and the definitions quietly drifted or contained a difference in how each engine handles nulls in a CASE expression. No error, no alert. Just two different answers from the same source of truth, and no clear owner for the reconciliation.
The series breaks down multi-engine lakehouse architectures into three interoperability dimensions: data, business logic and governance. Solving for interoperability requires ecosystem solutions that are vendor-neutral and provide customers with optionality at each of these dimensions. Apache Iceberg™ for data interoperability (and some minor efforts to address business logic interoperability) and Apache Polaris™ for governance interoperability have emerged as widely adopted open source projects with diverse communities. For business logic interoperability holistically, however, no open source project has reached the same level of adoption as those two, though progress is being made with Apache Ossie (incubating), formerly known as Open Semantic Interchange (OSI), which has seen vendor participation more than double in the past year.
While business logic doesn't carry the same stakes as governance interoperability, where gaps can mean compliance violations and data breaches, it's still where organizations are most likely to get caught off-guard as they plan their lakehouse and AI deployments. These gaps are mostly not acceptable in an enterprise environment. The good news is that viable workarounds exist today, and for most organizations, those workarounds are enough to architect around the gaps while the ecosystem catches up.
What a multi-engine lakehouse is really doing is unbundling a data warehouse. The data interoperability deep dive covers the storage layer of that unbundling, which is in reasonable shape. But a data warehouse/data processing system was never just storage; it was also business logic entities like views, user-defined functions (UDFs), stored procedures and metric definitions that turned raw tables into business answers.
Once you decouple compute engines from a previously tightly coupled compute engine and storage engine, all that business logic needs to either live somewhere securely accessible and engine-agnostic or be replicated independently in every engine that accesses the data. This overview provides a deep dive into existing mechanisms for managing business logic in multi-engine lakehouse environments and practical guidance for how to architect with today's solutions as you look to the future when interoperable business logic is fully solved.
Where we are today
There is no complete, widely adopted standard for sharing business logic across engines in production. Not for lack of attempts or progress: Iceberg Views, Iceberg UDFs, Substrait and Ossie all exist in various stages of maturity. None have achieved the cross-engine adoption needed to be a reliable architectural dependency today. For anyone making production architecture decisions right now, the practical effect is the same as having no standard at all, though the seeds being planted should factor into long-term planning.
It's worth framing the scope of this problem accurately. The majority of query volume in most data platforms hits tables directly: SELECT against Iceberg tables where data interoperability (covered in the data interoperability deep dive) is sufficient. Business logic interoperability matters specifically for the workloads that route through or include views, UDFs, metric definitions and stored procedures. While that may be a smaller share of total query volume, it's where the highest-stakes business answers live. The CFO's revenue dashboard, the regulatory report, the executive KPI that drives a board decision: Those are the workloads that encode business logic, and those are the ones affected by the gaps described in this post.
The consequence is straightforward. Any metric, view, UDF or transformation logic defined in one engine must be manually redefined in every other engine that accesses the same data. There's no synchronization mechanism and no feedback loop that tells you when definitions have drifted. This isn't a hypothetical problem. Companies like Airbnb and LinkedIn have built entire internal frameworks to address it: Minerva at Airbnb for metric consistency and Coral at LinkedIn for cross-engine SQL translation. But those organizations have large dedicated engineering teams that can sustain these systems indefinitely. For most organizations, that's not a realistic path; the ongoing maintenance burden alone tends to outweigh the initial build cost.
For governance interoperability, tools like Immuta and Privacera exist to centralize policy definitions and push them to multiple engines. No equivalent exists for business logic. Semantic layer platforms like Cube and AtScale come closest: You define metrics once, and they generate engine-specific SQL at query time. But consumers must query through the tool for the consistency guarantee to hold; anything that bypasses it doesn't benefit, and the "sit-in-the-middle" architecture runs into the same scaling and single-point-of-failure problems that have historically limited data virtualization, even when they are distributed systems. Transformation tools like dbt and SQLMesh take a different approach. They generate per-engine SQL at build time from a single model definition, which is closer to "define once, deploy to many," but they only cover the transformations managed through the tool. We cover both in detail later in this post.
The problem gets worse in decentralized organizations where different business units build their own logic independently on shared data sets, each internally consistent but unaware when their definitions diverge from each other.
The pragmatic answer for today: For high-stakes business logic (for example, finance, regulatory and executive dashboards), either physicalize the logic via a pipeline that computes the result of the logic and writes the results of that computation to a table so all engines inherently get the same answer, or centralize authoritative definitions into a single, enterprise-ready engine capable of handling highly concurrent workloads. Multi-engine reads on shared Iceberg tables are reasonable for ad hoc analysis; multi-engine definitions of "recognized revenue" are not, unless the maintenance burden is worth it for a given definition. This isn't a retreat from the lakehouse vision. Your data is still in Iceberg, portable and open. You're just being deliberate about where authoritative logic lives while the ecosystem catches up.
Here's a summary table of what we'll go through in this blog:
| Mechanism | What it covers | State today | Pragmatic guidance |
|---|---|---|---|
| Iceberg Views | Named SQL queries stored in the catalog; supports multiple dialect representations | Spec live; Spark/Trino ship single-dialect support; multi-dialect authoring limited to Java API; no official transpilation stance or tool | Use as a catalog-level registry; don't rely on for cross-engine consistency without verification; physicalize or designate a single authoritative engine for high-stakes logic |
| Iceberg UDFs | SQL UDF metadata format in the catalog; same architecture as views | Spec published; no major engine has shipped support | Treat as engine-locked for now; route critical UDF workloads through a single engine |
| Apache Ossie (incubating) | Metric and dimension definitions; YAML-based, vendor-neutral; AI context support | 50+ orgs in working group; spec live under Apache 2.0; converters in progress; incubation underway at the Apache Software Foundation | Most credible path for metric consistency long-term; in the meantime, centralize authoritative definitions in a single engine or semantic layer |
| dbt Semantic Models / SQLMesh | Metric and transformation definitions as code; generate per-engine SQL at build time | Mature; widely deployed | Useful for managing consistency within transformations they own; doesn't address behavioral divergence at query time; secondary to architectural mitigations |
| sqlglot | SQL transpilation across 30+ dialects; handles null ordering, type casting, function semantics | Mature open source; actively maintained | Use for migrations; covers a lot of semantic ground, but not all; note that you take on operations and accountability |
| Substrait | Cross-language spec for serializing relational algebra | DuckDB, Arrow, DataFusion support; not much support outside of those | Right long-term concept; the project isn't very mature from an adoption perspective, so don't treat it as a production dependency until the engines you use actually adopt it |
| Stored procedures | Engine-proprietary procedural languages; no shared abstraction exists | No community spec; no portability mechanism; no clear path | Accept as engine-locked; route those workloads through a single engine; refactor to declarative SQL or externalize the logic to a shared tool |
What exists but doesn't fully solve the business logic problem
Before evaluating the tools and specs the community has built, it's worth establishing why cross-engine business logic consistency is hard for the workloads that depend on it, which is certainly not all workloads. The behavioral differences across SQL engines are the root cause; everything that follows in this section is an attempt to work around them.
Cross-engine behavioral differences
The most fundamental challenge for business logic interoperability isn't that engines use different SQL syntax; syntax differences are well-understood and tools like sqlglot or custom tooling can translate between them. The harder problem is that engines with identical syntax produce different results because they implement SQL semantics differently. These aren't bugs; each engine is internally consistent. The problem is that "correct" is engine-specific, and two engines can both be right by their own standards while producing different answers from the same query on the same data. These divergences exist regardless of the underlying table format; they're properties of the compute engines, not the storage layer.
A few concrete examples:
ORDER BYwith null values: Spark defaults to nulls first for ascending order; DuckDB and Trino default to nulls last. A query that sorts users bylast_login_dateascending to find inactive accounts will place users who have never logged in (null) at the top on Spark and at the bottom on DuckDB or Trino, changing which users appear in the first page of results.- Type casting: Spark's behavior depends on whether ANSI mode is enabled; with ANSI mode off (the historical default; Spark 3.x defaults to ANSI mode off; Spark 4.0 changed this default, though many production deployments remain on 3.x),
CAST('a' AS INT)silently returns null, while DuckDB and Trino throw an error on invalid casts by default (both provideTRY_CASTas the null-returning alternative). A pipeline that casts user input and filters on the result will silently include rows with null values on Spark (ANSI off) that would have been excluded on DuckDB or Trino. - Timestamp handling: The timezone that an engine assumes when resolving timestamp values adds another layer. How timezone resolution interacts with session configuration and how daily or weekly aggregations behave across DST boundaries all vary.
These differences tend to surface in production on edge cases rather than in testing on clean data.

SQL is where most of the business logic portability discussion focuses, but it's not the only language in play. UDFs can be written in Python, Java or Scala with no cross-engine portability mechanism; stored procedures depend on engine-proprietary procedural languages with no equivalents elsewhere. Both are covered in dedicated sections later in this post.
The governance deep dive covers fine-grained access control (FGAC) enforcement approaches in detail. The primitive to carry forward from there is that SQL behavioral divergence can affect who sees what data, not just whether a dashboard number is right. One consequence that extends beyond analytics: FGAC policies (row filters, column masks) are typically SQL expressions. For simple predicates on standard operators (<, >=, !=), all major engines execute them the same way. But filters involving type casting, null handling or function calls enter the territory where engines can produce different results, meaning the security boundary itself varies by engine. A row filter that evaluates differently on Engine B than Engine A means users may see different data depending on which engine they query through, with no error to flag the discrepancy.
Iceberg views
With that behavioral variance as context, let's look at what the Iceberg community has built to share business logic across engines. The Iceberg view spec defines a metadata object for storing named SQL queries in the catalog alongside tables. Each view version contains one or more representations: a {type: "sql", sql: "...", dialect: "..."} tuple that records the SQL text and the dialect it was written in. A view can store multiple representations for different dialects, so in principle an engine can look for a representation matching its own dialect. The view is versioned, discoverable via the REST Catalog API and appears as a table-like object to downstream consumers.
// simplified — the full spec includes schema, default catalog/namespace, and other fields
{
"version-id": 2,
"representations": [
{ "type": "sql", "sql": "SELECT date_add(order_date, 30) ...", "dialect": "spark" },
{ "type": "sql", "sql": "SELECT date_add('day', 30, order_date) ...", "dialect": "trino" }
],
...
}Iceberg views are a real step forward from having no shared view mechanism at all. It also stops well short of solving the problem. The spec is a metadata container: It stores what you give it, with no translation layer, no behavioral normalization and no mechanism to verify that multiple dialect representations actually produce the same results. If you store a Spark SQL representation and a Trino SQL representation of the same view, keeping them semantically equivalent as both the underlying data and the business logic evolve is entirely your responsibility.
The deeper issue, covered in detail in the cross-engine behavioral differences section above, is that even identical SQL text can produce different results across engines. The behavioral divergences described there (null ordering, type casting, timestamp handling) all apply directly to Iceberg views. A view stored in one dialect and executed by another engine is subject to every one of those differences.
The community is actively developing an Iceberg materialized view spec with the same metadata-container architecture, specifying a SQL dialect, as well as adding a data dimension to the problem. The materialized output is a physical Iceberg table, so you inherit both the business logic and data interoperability issues.
The community is actively working on closing these gaps. The Iceberg view spec already supports storing multiple dialect representations per view, but today that's practically only accessible via the Java ViewBuilder API. Engines like Spark and Trino only write a single dialect through SQL. Making multi-dialect authoring easier via PyIceberg and engine-level SQL support is an ongoing discussion, but the conversation in the community has evolved. The community has also recently merged a structured expressions spec that represents value expressions as structured signatures rather than dialect-specific SQL text, which is covered in the "Open problems and active work" section below.
Pragmatic guidance
Iceberg views are worth using as a catalog-level registry for view definitions; having view metadata in the catalog rather than scattered across engine-specific catalogs is a genuine organizational improvement. But don't rely on them for cross-engine consistency without additional discipline. If a view encodes high-stakes business logic, either: (a) physicalize it as a table so all engines read the same precomputed result; or (b) designate one engine as the authoritative source for that view, and treat the Iceberg view as documentation rather than an executable contract across engines. If you do maintain multiple dialect representations, build cross-engine regression tests that compare results on representative data and run them on a schedule; the drift is silent and gradual.
Iceberg UDFs
The Iceberg community has also specified a SQL UDF metadata format that follows the same architectural pattern as views: Store the function body, its SQL dialect, input/output types and versioning metadata in the catalog. Like views, UDFs can store multiple representations for different dialects. The spec is published, but no major engine has shipped support yet; this is earlier in its lifecycle than Iceberg views.
The more structural challenge is that the Iceberg UDF spec only covers SQL UDFs. In practice, many UDFs that encode critical business logic are written in Python, Java or Scala. SQL UDFs have the same dialect divergence problem as SQL views: The function body is SQL text, and the same behavioral differences (type casting, null handling, function semantics) apply. Non-SQL UDFs add another dimension. Python UDFs have relatively broad support across engines (Snowpark, Spark, Trino, DuckDB all support them), though the registration mechanisms and runtime environments differ. But other languages are less portable. Scala UDFs, for example, have no equivalent in Trino or DuckDB. For non-SQL UDFs to become portable via the Iceberg spec, the spec would first need to support non-SQL languages as representation types, and the community would need to converge on a common invocation contract across engines. That's a harder problem than SQL dialect translation, though in many cases the core function logic is already portable; it's the wiring around it (type declarations, serialization, available libraries) that isn't.
Pragmatic guidance
Treat UDFs as engine-locked for now. If a UDF encodes critical business logic, that's one of the stronger arguments for routing that workload through a single engine rather than attempting cross-engine portability. Where necessary, physicalize the logic via a pipeline that writes results to a table so all engines inherently get the same answer.
Semantic layer and Apache Ossie (incubating)
Before covering Ossie specifically, it's worth disambiguating a term: Semantic layer. Semantic layer means different things to different people. Some use it to mean a metric definitions layer. Some use it to mean a BI modeling layer (LookML, Cube, AtScale). Some use it to mean the full stack of business logic on top of raw tables. For this post, the functional pieces that a "semantic layer" typically refers to are covered individually: View-based transformations are covered in the Iceberg views section above; access policies are covered in the governance deep dive; and metric and dimension definitions are covered below. What matters is the specific interoperability problem for each, not the umbrella term.
Ossie is the most significant initiative addressing semantic layer definition portability. With contributions since its inception from Snowflake, alongside Salesforce, dbt Labs and others in 2025, the working group has grown to include more than 50 organizations: Databricks, Cube, AtScale, Dremio, Starburst, Collibra, Oracle and many others have joined since founding. The breadth of participation signals genuine industry alignment, not a single-vendor initiative.
The spec is live under Apache 2.0 licensing: a declarative YAML-based standard that defines semantic models, data sets, metrics, dimensions, relationships and AI context in a vendor-neutral format. Metric expressions support dialect annotations, so a metric can carry its calculation in ANSI SQL (or other dialects) alongside the semantic metadata. The initiative has a public roadmap with active working groups developing metric semantics (aggregation, grain, cumulative metrics), catalog integration (including an Apache Polaris converter) and ontology/conceptual interoperability. Future roadmap efforts cover a semantic query language and reference engine, and AI-native context for grounded query generation. Converters for Snowflake, dbt, Apache Polaris and Salesforce exist. Snowflake, for one, already offers bidirectional support: the SYSTEM$CREATE_SEMANTIC_VIEW_FROM_OSSIE_YAML stored procedure (in public preview at time of publication) builds a native semantic view from an Ossie model, and the SYSTEM$READ_OSSIE_YAML_FROM_SEMANTIC_VIEW function (public preview at publication time) exports an existing semantic view back to Ossie YAML, so a definition can round-trip between the open spec and a governed engine object. The initiative has already transitioned to foundation-led governance as an incubating project in the Apache Software Foundation.
What Ossie covers directly addresses the CFO-gets-different-numbers scenario from the current state assessment: If "recognized revenue" is defined once in an Ossie model and consumed by every tool in the stack, the definition doesn't drift. That's the highest-value slice of business logic interoperability.
What Ossie does not cover today: UDFs, stored procedures, procedural logic and transformation logic. Metrics are important, arguably the highest-value category, but they're one category. Organizations that adopt Ossie will have metric consistency across tools but still face the drift problem for everything else discussed in this post. The scope limitation is deliberate and appropriate for a first initiative; it's a step in the right direction that should be taken into account when planning your architecture.
Pragmatic guidance
Ossie defines the standard, but a standard still has to be implemented and governed somewhere, and that requirement holds, no matter whose tools you use: Metric and dimension definitions need to live as first-class, governed objects, defined once at the source, so an AI agent asked for "ARR" resolves against a single authoritative definition instead of grabbing whichever revenue field it lands on, and every consumer, BI tool or agent, inherits that one definition rather than reimplementing it. You can build this yourself, but it means standing up and maintaining a governed definition store that every engine and tool resolves against, and keeping it in sync as definitions change. If you'd rather not, Snowflake provides this capability out of the box through Horizon Context. Definitions live as native, governed semantic objects and because Snowflake semantic views are easily round-tripped to and from Ossie YAML (see stored procedure and function above), adopting it now doesn't wall you off from the open standard as it matures.
For organizations operating across multiple engines or prioritizing cross-vendor portability long-term, Apache Ossie (incubating) is the most promising path and worth tracking closely given the breadth of industry participation.
In the meantime, the pragmatic approach remains the same as the broader guidance in this post: Centralize authoritative metric definitions in a single engine or semantic layer, and treat other consumers as downstream, or physicalize the logic. dbt and SQLMesh can also help here by managing metric definitions as code and generating per-engine SQL from a single source, though they're limited to the transformations they manage and don't solve behavioral divergence. For organizations evaluating semantic layer platforms (Cube, AtScale and others in the Ossie working group), watch for native Ossie spec support as a signal of long-term portability.
Open problems and active work
The previous section covered what exists today and where workarounds are needed. This section covers the problems that don't have shipped solutions yet: Some have active community work with real momentum (transpilation, intermediate representations); some are recognized gaps with no clear path forward (stored procedures); and some are lower-priority known issues that will likely be the last to get addressed (the remaining long tail).
Transpilation and intermediate representations
The long-term solution to SQL behavioral divergence is likely some form of canonical representation. Instead of maintaining N copies of the same logic in N dialects, store one version and adapt it at query time or build time. Two approaches are being pursued: syntactic transpilation (translate SQL text between dialects) and intermediate representations (store logic in an engine-agnostic format that gets compiled to engine-specific SQL).

sqlglot is an open source Python SQL parser and transpiler with support for 30+ dialects. It handles function name differences, quoting conventions, type mappings and many syntax transformations, and it emits warnings rather than silently producing wrong SQL when it can't translate cleanly. sqlglot goes beyond syntax in several important cases:
- Transpiling
ORDER BY col ASCfrom Spark to DuckDB correctly adds an explicitNULLS FIRSTclause to preserve Spark's null ordering default CAST('a' AS INT)from Spark (where invalid casts return null by default) becomesTRY_CASTon DuckDB (whereCASTthrows)CONCATcalls get rewritten to preserve each engine's null propagation behavior
These are genuine semantic translations, not just keyword swaps. That said, there are categories of divergence that no syntactic transpiler can address: decimal precision behavior (the same division on the same types produces different precision across engines), division-by-zero semantics (null vs. infinity vs. error) and implicit-type coercion rules that are embedded in engine runtimes rather than expressed in SQL text. For migrations, build-time SQL generation and reducing the manual work needed to stand up multi-dialect pipelines, sqlglot is a useful tool in that space. But it doesn't eliminate the need to test cross-engine equivalence for critical business logic. For high-stakes business logic, transpilation is a tool on the path to a good architecture, not a substitute for one. Authoritative definitions still need to live somewhere deliberate: a single engine or physicalized as tables.
LinkedIn's Coral takes a different approach. It is a Java-based SQL translation and rewrite engine that uses an intermediate representation (based on Apache Calcite's relational algebra) to translate between HiveQL, Spark SQL and Trino dialects. LinkedIn operates it in production across its data platform, demonstrating that IR-based translation works at large data volumes for a specific set of dialects, particularly when maintained by a large engineering organization. The limitation, however, is that the supported dialect set is narrow, and extending it requires deep familiarity with Calcite's framework. While Coral is open source, it's not a turnkey solution for most teams.
The Substrait project is working toward a broader version of this concept: a cross-language specification for serializing relational algebra that any engine could produce or consume. If business logic were stored as Substrait plans rather than SQL text, dialect differences would be handled by engine-specific adapters rather than text translation. In practice, adoption has been uneven. Apache Arrow's Acero and DataFusion (as a producer and consumer), and Ibis (as a producer) have native Substrait support, but the major query engines that dominate Iceberg lakehouse deployments don't have native Substrait integration. Until the engines you actually use support it, Substrait is a promising architecture rather than a production dependency.
Within Iceberg itself, the recently merged expressions spec (Iceberg PR #16652) is a narrower take on the same idea. Instead of raw SQL text, it represents expressions as standardized expression signatures. The structure is engine-agnostic, but the spec deliberately stops there. It doesn't say how the defined expressions actually behave, leaving that to each engine. It standardizes the shape of an expression, not its implementation behavior. That makes it foundational plumbing (scalar expressions, not full queries) that view and UDF definitions could eventually build on, but not a fix for the behavioral divergence described above.
A fourth, semantic-layer-level variant is on Ossie's roadmap: a reference engine plus an Ossie-to-SQL compiler and a cross-implementation conformance suite, so a single semantic model definition could be compiled to each engine's dialect with consistent interpretation. It is scoped to Ossie-modeled semantic queries (not arbitrary SQL, views or UDFs) and is on the roadmap rather than shipped, but it applies the same canonical-representation idea at the semantic layer.
There's also an accountability problem that applies to all transpilation and IR approaches. In a single-engine world, when a query produces the wrong answer, there's one vendor to call. In a multi-engine world with a translation layer in between, a wrong result could be the fault of the source engine, the target engine or the translation layer. Debugging cross-engine result differences becomes a cross-vendor support problem: slow to resolve, ambiguous in ownership and costly when it surfaces in a downstream business decision. The governance deep dive covers why this accountability gap is particularly consequential for security policies.
Pragmatic guidance
sqlglot covers more ground than most teams expect: Use it for migrations, build-time SQL generation and as a first pass when standing up multi-dialect pipelines. For the divergences it handles (null ordering, type casting, null propagation in functions), it genuinely reduces the manual work. For the divergences it can't (precision, division-by-zero, implicit coercion), cross-engine regression tests on representative data are the backstop. Watch Substrait adoption across the specific engines in your architecture; it's a sound long-term concept but not yet a dependency you can rely on. For critical business logic, the architectural guidance from the current state assessment still applies: Centralize authoritative definitions in a single engine or physicalize the results rather than depending entirely on a translation layer.
Stored procedures and procedural logic
Unlike views, UDFs and metrics, stored procedures don't have an Iceberg spec equivalent, a community proposal or even a widely discussed path forward. This isn't an oversight; it's a reflection of how deeply engine-coupled stored procedures are. Views and UDFs are at least SQL expressions that can be stored and potentially translated. Stored procedures are full programs: loops, conditionals, exception handling, transaction control, cursor management, dynamic SQL and calls to engine-specific system functions, all written in engine-proprietary procedural languages (PL/pgSQL, Snowflake Scripting, Spark's programmatic APIs). There's no shared abstraction because there's no shared surface area to abstract over.
In a single-engine world, stored procedures are where critical multi-step business workflows live: end-of-month financial close processes, data quality enforcement routines, complex ETL orchestration, conditional transformation logic that can't be expressed as a single SQL statement. In a multi-engine world, these are entirely engine-locked with no portability path. Transpilation tools like sqlglot operate on SQL expressions, not procedural control flow. Intermediate representations (IRs), like Substrait, model relational algebra, not imperative logic. Even if every other category of business logic described in this post achieved full cross-engine portability tomorrow, stored procedures would remain tied to the engine they were written for.
Stored procedures are also where complex governance logic sometimes lives: multi-step anonymization workflows, conditional access decisions that go beyond what a single-row filter expression can handle, audit logging routines, to name a few. When that logic is engine-locked, the compliance implications extend beyond analytics consistency into security posture; the governance deep dive covers this in detail.
Pragmatic guidance
Accept that stored procedures are engine-locked and plan accordingly. If a critical business workflow lives in a stored procedure, that's one of the strongest arguments for routing that workload through a single engine. Where possible, refactor procedural logic into declarative SQL (which has a portability path, even if an imperfect one) or externalize the orchestration layer to tools like Airflow, Dagster or Prefect, which coordinate workflow steps without depending on engine-specific procedural languages.
The remaining long tail
Even if views, UDFs, metrics and stored procedures all had cross-engine solutions tomorrow, a set of lower-priority business logic categories would remain engine-specific. Triggers and event-driven logic have no portability mechanism and no community proposal addressing them. Scheduled tasks and job orchestration defined inside an engine (as opposed to external orchestrators like Airflow) are tied to that engine's scheduler semantics. Materialized view refresh logic and maintenance schedules are engine-specific by definition. Session state and context functions used in business logic (CURRENT_USER(), CURRENT_ROLE(), session variables) return engine-specific values with no cross-engine equivalence guarantee, which matters when those functions appear in access control policies or audit logic. None of these are typically the source of the "two dashboards, different numbers" problem, which is why they're lower priority than the categories covered earlier. But they are worth acknowledging: Any architecture assessment that only accounts for views, UDFs and metrics will undercount the total surface area of engine-coupled business logic.
Diagnostic questions
If you're evaluating or already operating a multi-engine lakehouse, these are the questions worth asking before you're in too deep and discover the answers the hard way:
- For any business logic you can't physicalize as an output table (active users, recognized revenue, churn), how do you detect when definitions diverge across engines, and what's your reconciliation process?
- Which engine's business logic definition is the explicit, documented system of record for high-stakes metrics (finance, regulatory reporting, executive dashboards) — and is that decision enforced architecturally or just assumed?
- When two engines produce different results from the same Iceberg table, what is your escalation path? If neither engine vendor can reproduce the issue independently and a translation layer sits in between, have you mapped that cross-vendor accountability chain before you're in production — and do you know who owns the debugging when an Iceberg view produces a wrong result due to dialect differences?
- When you need to update a business logic definition (for example, how "churn" or "MRR" is calculated), what is your process for deploying that change consistently across all engines at the same time — and what's your rollback plan if one engine fails validation after another has already gone live?
- Have you audited the SQL behavioral differences between your engines, including null ordering, type casting behavior (ANSI mode, TRY_CAST availability) and timestamp resolution?
- How does each engine in your architecture handle timestamp storage, timezone resolution and DST transitions — and have you specifically tested daily and weekly aggregations that cross a DST boundary on a global data set?
Conclusion
Business logic interoperability is the least mature of the three pillars, but it's not a dead end. Most query volume in a typical data platform hits tables directly, where data interoperability is sufficient; the gaps described in this post affect a smaller but disproportionately high-stakes slice of workloads. The building blocks are in progress: Iceberg views and UDFs provide catalog-level metadata containers, Apache Ossie (incubating) is converging on metric definition portability with broad industry participation, and transpilation tools cover a good amount of ground.
The problem is that none of these are sufficient on their own today, and the pragmatic path remains architectural: Physicalize high-stakes logic as tables; keep authoritative logic definitions in a single engine, where the cons of physicalization outweigh the pros; and treat cross-engine logic consistency as something you verify rather than assume. The data is portable, but the business logic isn't yet. Treat that as a known constraint, and architect around it deliberately.
To learn more, check out An Architect's Guide to Multi-Engine Lakehouses: What's Solved and What Isn't, which is a brief summary of all three pillars, and the other deep investigations into data interoperability and governance interoperability.


