8 tips for writing User-Defined Functions in DAX

In a previous article, we introduced you to DAX functions. DAX functions (or user-defined functions; UDFs) give you a way to centralize and re-use logic and values in your model, which makes it easier and quicker to develop and make changes to DAX code. DAX functions have a lot of potential benefits and use-cases, but also some considerations to keep in mind when you make them.  

In this article, we share some simple tips to write better DAX functions for use in your semantic models and reports, based on our experience so far. 

Tips-for-writing-better-DAX-functions-in-a-semantic-model
Tip #1: Be careful with parameter types and evaluation

Functions are an archetypal DAX feature, because they are simple, yet not easy. Specifically, the type hints for function parameters are a more nuanced concept that take some reading, thinking, and practice to get right. For simple scenarios, type hints are optional. However, more advanced scenarios require type hints to control how a function will behave. Using the wrong type hints can produce incorrect results. 

As a reminder, here’s a review of the anatomy of a function. Type hints go after the definition of the parameter, and include Type, SubType, and ParameterMode: 

The-anatomy-of-a-DAX-function

Type hints are optional, but in some cases, you need to declare the right type hints. If you do not, then you will get incorrect results. 

First, consider the following simple function that has no parameter type hints. It simply compares two numbers or measures (with the parameters actual and target) and produces the relative comparison in percent as the result. 

Example-of-a-function-that-will-return-the-correct-result

This function requires no type hints; it uses the default Type (SCALAR) and ParameterMode (VAL) with no explicit SubType declared (although it is implicitly handled). The parameters actual and target are scalar values, which means that you can provide a measure reference or a scalar number, and both will work as expected. However, when you want to modify the filter context, things get trickier. 

To illustrate with an example, consider the following example of a simple function that is intended to compute values only for certain billing document types. Such a function would be useful, because it centralizes business logic. In this example, the business might only want to report sales for certain document types, excluding specific adjustments or intercompany invoices: 

Example-of-a-function-that-will-return-the-wrong-result
However, the function does not work as intended. This is evident when we test it in a DAX query, checking how many invoice lines we have by billing document type. As you see, we still have invoice lines for document types other than “F2” or “L2”, which we should be filtering out… but we’re not. Why is that?
Evaluating-the-function-produces-incorrect-results

What is happening here is that the parameter uses the default Type ANYVAL and the default ParameterMode VAL. This means that the parameter (if it’s an expression, or a reference to an expression) is evaluated before being passed into the function body. Basically, the parameter is working like a variable; it’s arriving without the possibility to further modify its filter context. In other words, the CALCULATE does nothing. This is called eager evaluation. 

To get the function to work as expected, you must use a different type hint; either a Type of ANYREF or adding a ParameterMode of EXPR (with or without SCALAR) in this case. Using a Type SCALAR or a SubType STRING without EXPR produces the incorrect result.  

Adding-anyref-type-or-expr-parameter-mode-fixes-the-function

By adding EXPR, the parameter is evaluated differently. Now, it’s evaluated with lazy evaluation. This means it is evaluated in the function body, when the function is called. Now, you can modify the filter context. 

Note that Tabular Editor knows this, and so it will warn you if you are using the wrong type hints. This makes it easier for you to make the choice when you build your functions. 

Tabular-Editor-will-automatically-warn-you-if-you-use-wrong-parameters

The example illustrates the importance of understanding parameter types and evaluation when you write functions. In summary, there are some scenarios where this is important: 

  • When you want to change or control evaluation context, you need to use type hints that will result in lazy evaluation, such as adding EXPR ParameterMode. 
  • When precision is important, you need to declare the parameter SubType. 
  • When you expect or want to handle only a certain value type, you should declare the Type and/or SubType. 

We only talked about a simple example of the first scenario, here. In future articles, we’ll discuss more complex examples, as well as examples of the other two scenarios. 

Tip #2: Follow good naming conventions for functions and parameters

This might seem like a generic tip that applies to your entire semantic model (and it is). In general, the same rules apply that your functions should use consistent, clear, and concise names that describe what they do and apply consistent casing (like CamelCase, snake_case, and others). However, naming conventions have some special relevance for DAX functions for the following reasons: 

  • Function names appear in autocomplete when writing DAX. 
  • Function names appear in DAX code once they’re being called. 
  • Functions are new technical object types that might require different naming conventions from traditional object types like tables, columns, and measures that are exposed to the business. 
  • Functions have parameters, which appear in code assistance when you use a function. 

For this reason, you want to make some special considerations about naming your DAX functions. Here are some examples: 

  • Consider a hierarchical “namespace”-like naming convention, such as something like [CategoryName].[SubCategoryName].[FunctionName] (for an example see the below image). This can keep your functions organized in the TOM Explorer (or data pane) and easier to find in autocomplete when you use them. It also helps you avoid potential conflicts with new DAX functions that will be introduced in the future. 
    • Note that there are reserved keywords (like calculate, value, and percent) which you can’t use as names or parts of names (like Calculate.Pareto). Tabular Editor warns you if you’ll run into one of these scenarios. 
  • Ensure that the names describe clearly what the function does. Ideally, someone looking at the function reference in the code and understand its purpose and output. 
  • Likewise, consider clear parameter names that adhere to the formula or function body. Some examples are below: 
    • measure_reference as a parameter name for a function that expects a measure. 
    • K as a parameter name for Kelvin in a function that converts temperature in Kelvin to Celsius. 
  • Consider a different casing for parameters than your variables. For instance, you can declare parameters with snake_case and variables with _CamelCase. This is just a style choice, admittedly, and has no real functional consequences 😊 
  • Check also the naming conventions available at sqlbi docs, which includes recommendations like prefixes for local vs. model-independent functions. 

In summary, make sure that you follow standard naming convention practices for functions and parameters, but consider the specific requirements for functions, too. 

Tip #3: Add comments and descriptions

Like naming conventions, comments and descriptions are important in your semantic model overall to help document it for both humans and AI tools. However, again, functions are special, because comments and descriptions are how client tools like Tabular Editor and Power BI Desktop will annotate your functions with code assistance. These annotations are instrumental to help you and others use your functions. It is not just a good documentation practice for hygienic models, but also for increasing adoption of the actual functions, themselves. 

Here’s an example of a simple function, showing where these comments should go:

Comments-in-a-function-are-functionally-relevant

As you can appreciate, the comments and thus the tooltips make the function much easier to use and its parameters to understand. In this simple example it might seem superfluous, but when your functions become more complex, this is essential.  

Consider the following example with a function that configures a dynamic format string: 

Multi-line-comments-with-examples-are-sometimes-pragmatic

The previous example is more complex, with five different parameters which have non-obvious inputs. In this case, without the parameter tooltips (which we refer to as calltips), the function would be very hard to use. Adding the multi-line comments is essential and logical to make sure you get the right results, and it is also easy to do (either yourself or with AI). 

Note that in Power BI Desktop, parameter tips do not yet work. Power BI Desktop takes this syntax from the function description, and does not (yet) support parameter descriptions in code assistance. 

Tip #4: Keep it simple, keep it lean

Just because you can make a function doesn’t mean that you should. Functions are powerful and interesting, but they can overcomplicate your model by adding additional calculation layers that do not need to be there. Like everything, functions ultimately offer you additional options and choices to address specific problems. 

An example of a function that does not make sense is a time intelligence function that reuses the existing simple time intelligence logic: 

Example-of-a-function-that-is-unnecessary-in-your-model

In this example, the function is adding unnecessary complexity. It only slightly simplifies an already simple calculation. Furthermore, if you are only using this function a few times in a model, then it has very little real benefit. 

In contrast, consider a scenario where you have custom time intelligence logic. An example might be when you have monthly budgets and forecasts, but are required to report this by workday. In this scenario, a function that encapsulates this logic makes sense. 

Example-of-a-function-for-customized-month-to-date-logic-applied-to-monthly-targets

Note that this example has logic that is quite specific to the semantic model in which it’s used. It also hard-codes various column, table, and measure references rather than parameterizing them. That is completely fine. The purpose of this function is to centralize and re-use this custom, month-to-date logic; these references don’t need to be parameterized, and doing so would over-complicate the function and make it less convenient to use. 

This is what it looks like when you over-parameterize your functions: 

Example-of-a-function-thats-over-parameterized-and-inconvenient

This level of parameterization is excessive and unnecessary, unless you will share it with others (such as via DaxLib). To emphasize: you should find functions that work best for your model and your scenarios. Don’t worry about the specific niche business logic or hard-coded object references, and focus on how you can make your models more efficient, more performant, and easier to maintain. 

As a summary, here’s some examples of situations where you might not use DAX functions: 

  • Calculation logic or code that is already simple. 
  • Calculation logic or code that you will only use once, or a few times. Here, the function is adding unnecessary objects and code. 
  • You are less experienced with semantic modelling or working with others that are less experienced. Here, the function is creating complexity and making it harder to understand or make changes to the model. 
  • Situations with functions that are over-composed with multiple nested layers (functions calling functions calling functions) which makes it too difficult to understand or use. 
  • Situations that require functions which are overcomplicated, with excessive parameterization, which try to handle too many different scenarios. 

In summary, functions are useful. However, they should only be used in situations where they make sense. Like other advanced objects such as calculation groups or parameters, overuse or abuse of functions introduces complexity and could cause new problems. 

Tip #5: Think outside the box

This might seem like a contradiction to the previous tip. However, “outside the box” is not the same as “complex”. Functions are interesting not just because they let you centralize and encapsulate existing logic, but also because they enable new possibilities. For instance, functions can return either scalar values or tables, which means you can apply them in some interesting places. 

Consider the following example in the SpaceParts model, which has eight different data security roles defined. Each role has a similar filter expression, applied to a different column on a dimension table that corresponds to the role of the employee (like Account Manager in the Customers table). 

Spaceparts-model-roles
You could turn this table expression into a function, parameterizing only the role column. This would both centralize and simplify the logic for row-level security (RLS). All eight roles can use the same function: 
Turn-RLS-filter-expressions-into-a-function-to-centralize-the-logic
The resulting function means that each filter expression is a single line; a single function call: 
RLS-as-a-single-simplified-function
As you can see, functions let you do some cool new things with DAX. Keep it simple, of course, but make sure you think outside the box to find scenarios where functions could help you improve your model. 
Tip #6: Test, test, test

Functions can have implications for the accuracy and performance of your DAX expressions. For performance specifically, functions might improve performance in cases where you have multiple layered measure references. However, they might reduce performance in cases where you overcomplicate or overparameterize the function (such as adding multiple functions or IF expressions to handle different inputs). 

As such, you must test your DAX with queries both before and after using functions, and compare whether the result is better or worse. Then, you can modify your function body to optimize the code further, or opt to not use a function, altogether. This choice will be dependent on your scenario and data.  

Furthermore, when you create semantic models that other people will consume, you can expect that these consumers might use your functions to define their own calculations (in measures that are in composite models, thin reports, or with visual calculations). Users might have a different idea of how a function will work than you as a developer, so you will want to make sure to align and test the function with them before you deploy it. This can help you to avoid assumptions, incorrect results, and upset users! 

Tip #7: Curate a library of functions

Functions are objects scoped to a single model. However, many functions have use-cases in different models, especially within teams and organizations. Even across organizations, there are many DAX patterns which are repeatable and complex to implement from scratch, so they are good candidates for functions. 

As such, you should curate a library of functions that you can apply and re-use across different semantic models. An easy way to do this is just to keep a separate Power BI project (PBIP) or functions.tmdl file that contains all the functions you want to re-use. You can store this in a central repository (like OneDrive or a Git remote repository) to share with other people… or just conveniently access yourself in different projects. 

However, you can also use community libraries like DaxLib, which contain many functions available out-of-the-box to address common scenarios. Tabular Editor can integrate with  DaxLib, so you can add and remove these functions with the click of a button. 

Re-use-DAX-functions-across-semantic-models-with-tabular-editor-3
This is a demonstration of what that feature looks like: 
Demo-DAX Lib Integration
In the future, you’ll be able to quickly add, remove, and modify functions from any library—including private and organizational ones. This is very convenient and can save you a lot of time when you build a semantic model. 
Tip #8: Be careful when using AI to generate functions

Many people are experimenting with using AI tools (like Claude Desktop) or coding agents (like Claude Code) to help them write DAX code. However, for functions, you must be careful. Functions are a new DAX feature released in September 2025, and thus are completely absent in LLM training data. This means that the risk of hallucinations is extremely high. 

To mitigate this risk, you need to provide extensive context to an LLM (in your prompt, or preferably, with instructions and tools in augmented chatbot and agentic scenarios). The Microsoft documentation and articles from SQLBI aren’t sufficient, either, unless you will just be creating a few, simple functions. 

We recommend using the following to help you write functions with help from AI: 

  • Microsoft documentation, obtained either with a WebFetch tool or the official Microsoft Docs MCP server. 
  • Relevant examples that you provide yourself. Ensure that you provide examples that cover multiple scenarios which are relevant and representative of your scenarios. You should ideally refer to a remote repository that has your library of functions so that it can improve over time as you create and use more functions. 
  • Instructions, either baked into the tool (like Claude Desktop Projects) or as a separate markdown (.md) file. The instructions should explain specifically how you expect the LLM to create the functions, and any specific tips or remarks. You should treat this file like a memory and add to it over time to improve your results. 

In summary, if you try to generate functions with vanilla LLMs like ChatGPT or Claude, you’re gonna have a bad time. Make sure you invest time in creating context and examples so that the AI tool understands functions and can generate them for you.  

In conclusion

DAX user-defined functions (UDFs) are a fantastic new addition to semantic models, which unlock a lot of new possibilities. However, there’s some considerations to keep in mind if you want to use them effectively. In this article, we listed a few tips from our experience to help you write better DAX functions. 

Related articles