Introduction
The Power BI & Fabric ecosystem continues to evolve, making models more powerful, reusable, and easier to maintain. One of the most interesting recent additions that might reshape how BI teams design semantic models is User-Defined Functions (UDFs).
Rather than duplicating the same logic across multiple measures, UDFs let DAX developers define business logic once, parameterize it, and reuse it wherever needed. This significantly reduces maintenance effort and moves DAX toward a more modular, governed, and professional development model.
What is it
User-Defined Functions encapsulate a calculation into a reusable function with parameters, which can be scalars, columns, measures, or expressions. Once defined, they behave just like any other built-in DAX function: you can call them inside other functions and measures anywhere in the model.
Compared to calculation groups, UDFs offer more flexibility because they accept parameters and can be packaged as reusable libraries across different models (more on this later). The result is a development approach with less duplication, more composability, stronger governance, and easier standardization.
Our element61 perspective
We see DAX UDFs as a major upgrade in semantic modeling. The benefits go beyond “write once”:
- Reusability (patterns, not just formulas): Encode entire calculation patterns once (with options for fiscal vs. calendar, formatting) and apply them consistently across every semantic model.
- Maintainability with guardrails: Define behavior once (e.g., denominator-zero rules, filter scope, BLANK handling, capping/extreme filtering) in the UDF, and every dependent measure inherits it. This eliminates drift between teams and models.
- Modularity & composability: Break down complex logic into small, testable building blocks you can stack (e.g., seasonality → YOY → FX → mix-effect).
- Fewer artifacts to maintain: Combine UDFs with Field Parameters or Calculation Groups to avoid a “measure explosion.” You can keep a minimal set of base measures and apply governed transformations dynamically.
For star-schema projects with evolving business rules, UDFs accelerate delivery, reduce defects, and raise governance, especially across multiple teams where you want repeatable patterns without re-writing DAX.
Putting this into practice with a simple example
To create a UDF, you’ll need to either navigate to the Dax Query view or edit your model via the TMDL view. In our example below, we chose to use the DAX Query view rather than the TMDL editor.
Example 1 – Simplifying Year-over-Year Growth
Before UDFs (duplicated pattern)
Sales YOY % =
DIVIDE(
[Total Sales] - CALCULATE([Total Sales], DATEADD('Date'[Date], -1, YEAR)),
CALCULATE([Total Sales], DATEADD('Date'[Date], -1, YEAR))
)
Without UDFs, this same pattern had to be copied for every KPI (Quantity, Profit, Cost, Headcount, …).
Each copy then needed manual tweaks for edge cases (e.g., handling prev=0, missing dates, fiscal vs. calendar). Over time, this led to drift and inconsistencies across models and reports.
With UDFs: Define a calculation pattern once
First, create a generic function for percent change. To do so, jump into the Dax Query view & write:
DEFINE
FUNCTION PercentChange = (currMeasure, prevMeasure) =>
DIVIDE(currMeasure - prevMeasure, prevMeasure)
Now the YOY measure becomes much simpler, cleaner, and more consistent over different measures:
Sales YOY Growth =
PercentChange([Total Sales], CALCULATE([Total Sales], DATEADD('DimDate'[Date], -1, YEAR)))
Result
- One definition: The percent-change logic lives once as a UDF and can be called from measures, calculation group items, and other UDFs as a single source of truth.
- Policy as code: Encode org-wide rules (e.g., prev=0 ⇒ BLANK, BLANK handling, clamping, fiscal vs. calendar) inside the UDF. When policy changes, you update one function, and everyone inherits the change.
- Composable building blocks: Layer UDFs (e.g., FX normalization -> governed YOY -> winsorization) to implement complex KPIs without nesting “DAX spaghetti.”
While this YOY example is intentionally simple, the pattern scales to the hard stuff: multi-currency conversion, allocation rules, service-level KPIs with caps, cohort/retention logic, seasonality adjustments…
Why not just a calculation group
- Different purpose: Calc groups apply a transformation at query time (SELECTEDMEASURE()), but they’re not reusable, callable functions. UDFs define the governed logic you can reuse anywhere (measures, calculated columns, other UDFs, and yes, also inside calc groups, more on this below).
- Real parameterization: UDFs accept parameters (including references), so you can pass options and guardrails explicitly (e.g., clamp thresholds, fiscal/calendar switch, FX mode) rather than scattering variants across calc items.
- Portable libraries & governance: Package UDFs as a corporate KPI library and reuse across models/clients. This reduces divergence, simplifies code review, and makes audits easier.
- Best practice is both: Put the canonical logic in a UDF, and invoke it from a calc group (or a single “Selected Metric” measure). You get a dynamic application without measure proliferation, plus centralized, testable logic.
Bottom line: calc groups solve the runtime application, UDFs solve the standardized definition. Together, they eliminate duplication, enforce policy, and keep complex semantics consistent across your estate.
Go further: Reduce manual work with Field Parameters, Calculation groups and UDFs
You can eliminate measure sprawl and keep logic governed by combining a field parameter (to pick the KPI), a UDF (to encode policy), and a calculation group (to apply it at runtime).
Example:
- Field parameter: add your base measures (e.g., [Total Sales], [Total Quantity], [Profit]) to a field parameter and use it in a slicer/selector on the report
- UDF: canonical percent change
- Calculation group: apply at query time
Create a calc group (e.g., Time Comparison) with two items:- Base measure (selectedmeasure())
- YOY
YoY =
VAR Prev =
CALCULATE(SELECTEDMEASURE(), DATEADD('DimDate'[Date], -1, YEAR))
RETURN
PercentChange(SELECTEDMEASURE(), Prev)
Result: one calc item returns YOY for whatever KPI the user picks via the field parameter. No cloning [Sales YOY %], [Quantity YOY %], [Profit YOY %], etc. The UDF centralizes the math/policies; the calc group handles the runtime application.
Of course, to be entirely complete, we would want to include formatting options in those measures to force % to show properly.
Why this matters: YOY math doesn’t change often, but policies do (BLANK handling, caps, fiscal alignment). Encoding those once and applying them via parameters gives you consistency, not just “shorter code.”
What we want next
While DAX UDFs are already a huge step forward, a few missing pieces would truly make them a first-class development experience in Power BI and Fabric:
- UDF Library Manager (tenant & workspace):
Instead of copy-pasting functions from one model to another, we’d love to see a central catalog of certified, versioned UDFs where people could access company-wide UDFs - Service authoring:
Today, you can only create or edit UDFs in Desktop through the DAX Query/TMDL view. The ability to author and maintain UDFs directly in the Power BI Service would accelerate collaboration. - Better IntelliSense & documentation:
While the current documentation available directly in the formula bar is serviceable, it is not very extensive. This would help developers quickly understand and reuse existing functions. - Function-level formatting metadata:
Currently, formatting must be applied at the measure or calc-group level. It would be a welcome improvement if UDFs could declare their own format strings so that any measure or calc item calling them automatically inherits the proper percentage, currency, or decimal formatting.
Conclusion
User-Defined Functions in DAX are a meaningful step forward. They push Power BI toward modular, governed semantics and pair well with Field Parameters and Calculation Groups to cut duplication and enforce policy.
But this isn’t a magic wand. Today, portability is still manual, authoring is Desktop-centric, formatting rides on the end-user, and there’s no first-party library manager yet.
Our stance is pragmatic: use UDFs where they remove friction and codify policy, not everywhere. Keep base measures lean, centralize guardrails in a small UDF set, and document choices.
If Microsoft delivers library management, service authoring, and function-level formatting, UDFs will move from “powerful” to “foundational”. Until then, they’re a sharp tool, and you should use them deliberately.