A UK manufacturer rang us in February asking for a Power BI Health Check. Their executive dashboard had stopped refreshing inside its scheduled window. An in-house team had built the model two years earlier, everyone who needed to had signed it off, and it ran cleanly until the dataset crossed some invisible threshold and something began to slip. By the time we arrived, the morning briefing was running on yesterday’s numbers.
Six hours into the Health Check we had found three issues. None of them new. None of them visible from the report itself. All three of them already sitting on the internal list we keep of the patterns we see almost every time.
We have run more than 200 Power BI Health Checks since 2018, across financial services, retail, public sector, manufacturing, and healthcare. Different industries, different team sizes, different model complexities. The same three issues come up almost every time. This piece walks through what they are, why they recur, and what we recommend.
1. Auto date/time hierarchies left on
What it looks like
Open File > Options > Current File > Data Load in Power BI Desktop and find the Time intelligence section. If “Auto date/time” is ticked, Power BI silently generates a hidden date table behind every date column in the model. We have audited models that contain fourteen date columns. The model is carrying fourteen hidden date hierarchies, each one indexing every day from the start of the data to the end of next year.

Power BI Desktop > File > Options > Current File > Data Load > Time intelligence. The toggle is on by default.
You will not see these tables in the model view. You will see them if you open the model in DAX Studio or Tabular Editor, or if you look at the .pbix file size and wonder why a model with twelve million rows of fact data weighs four gigabytes.
Why it matters
On the manufacturer’s model, switching auto date/time off and substituting a single date dimension cut the .pbix from 1.8 GB to 640 MB. Refresh time fell from forty-one minutes to twelve. The dashboard met its SLA again the following Monday.
The cost is rarely about disk space, though. It is about the VertiPaq engine doing more work than it needs to during refresh, and about the model carrying compression overhead that buys it nothing. On a Premium capacity, that overhead costs money. On a Pro tenant, it costs you users who close the dashboard and open Excel.
What we recommend
Auto date/time is on by default in every new Power BI Desktop install, and that default has not changed. So the first move is to turn it off at the file level (File > Options > Current File > Data Load > Time intelligence) for every new model. For existing models, turn it off in the same place and replace it with a single, marked date dimension. If you need to keep auto date/time on for one specific dataset, which is occasionally the right call for a sandbox or experimentation model, make that an explicit decision rather than an inherited default.
2. Bidirectional relationships used as a workaround
What it looks like
In the model view, any double-headed arrow between two tables. Sometimes one. Sometimes seven. Each one is a sign that someone, at some point, found a filter that would not propagate the direction they needed, switched the relationship to bidirectional, watched the visual update correctly, and moved on.

An anonymised model from a recent Health Check. The double-headed arrows are bidirectional relationships. Seven of them, in this case.
The visual updated correctly because bidirectional cross-filtering brute-forces the engine into propagating filters in directions it would not otherwise go. It also creates relationships that mask missing bridge tables, ambiguous join paths, and assumptions that fall apart the moment someone adds a new measure.
Why it matters
Bidirectional relationships are the single most common cause of the “this visual is taking too long to load” warning we see in Performance Analyzer. On a retail client’s sales model, removing four bidirectional relationships and replacing two of them with bridge tables took a slicer-driven page from nine seconds to under two. The page had not changed; the user could not tell us what we had done, only that it now responded the moment they clicked. That is the kind of fix that earns a Health Check its fee, because it is invisible until someone measures it.

Performance Analyzer on the same page before and after the fix. Total query duration on the right.
Performance is only the visible half of the cost. Bidirectional relationships are also the most common cause of ambiguity errors that surface months after a model goes live, when someone adds a measure that crosses the wrong filter path and Power BI flags the calculation as ambiguous. The error is technically correct. The model has been wrong for months, and the new measure is simply the first thing to ask a question the broken filter path could not answer.
Why it keeps happening
Bidirectional cross-filtering is a one-click change in the modelling pane, and it usually fixes the immediate symptom on the spot. The underlying cause, which is almost always a missing bridge table or a many-to-many that the data layer should have resolved, requires more thought and does not show its payoff on the screen in front of you.
Microsoft’s documentation now warns against the pattern in several places, but those warnings live deep inside longer modelling articles. The user never sees them at the moment of the click, which is the only moment that would change the decision.
What we recommend
Treat every bidirectional relationship as a flag, not a fix. If your model has more than one, write down what each one is doing and what would break if it were single-directional. In almost every case, the right answer is a bridge table, a measure that uses CROSSFILTER inside its calculation, or a remodel of the underlying schema.
3. Calculated columns doing the work of measures
What it looks like
A model with thirty, forty, sometimes more than a hundred calculated columns. Sales margin as a column. Year-on-year change as a column. Customer lifetime value as a column. The engine computes each one row by row during refresh, stores it uncompressed (or close to it) in the model, and gains nothing at query time because every source refresh recomputes the column anyway.

VertiPaq Analyzer in DAX Studio, sorted by column size. Calculated columns in the top ten are the ones we focus on first.
The giveaway is the calculation icon next to the column name in the field list. The cost stays hidden until you look at the storage tab in DAX Studio and find that a third of the model size has gone to columns that should have been measures.
Why it matters
Calculated columns sit at the wrong end of every Power BI performance trade-off. They bloat the model. Their values are often unique or near-unique per row, so compression is poor. Visuals cannot reuse them the way measures can. Refresh time grows with every column added, because the engine recomputes each one whenever the source refreshes. And the work happens at refresh, when the VertiPaq engine could do it faster at query time through a measure.
On a financial services client’s general ledger model, converting twenty-three calculated columns into measures reduced model size by 38% and brought refresh time down by nine minutes. The measures themselves added negligible query time, because the engine aggregates at speed.
Why it keeps happening
Calculated columns feel intuitive to anyone who has spent time in Excel. They look like spreadsheet formulas. They produce a column you can see, drag, and filter. Measures, by contrast, only exist when something asks for them. So new Power BI developers learn calculated columns first, build their first three or four models around them, and never revisit the choice.
Where you actually want a calculated column
There are cases where a calculated column is the right tool, and they are worth naming so the rule does not harden into dogma. You want one when the value participates in a relationship, when users will filter or group on it directly, or when it feeds a slicer a measure cannot. Everything else should default to a measure. To find the columns that broke that rule, sort your model’s columns in DAX Studio by storage size and walk down the list, asking of each whether it could be a measure instead. Most of them can be.
What to do before you bring anyone in
Three checks you can run against your own model this afternoon:
- Open File > Options > Current File > Data Load. If “Auto date/time” is on, turn it off, then check each existing model in the tenant.
- Open the model view. Count the bidirectional relationships. For each one, write down what would break if it were single-directional. Any one you cannot answer is a flag.
- Open DAX Studio against your largest model. Run VertiPaq Analyzer. Sort columns by size. If calculated columns appear in your top ten, you have at least one conversation worth having.
If any of these return more than you expected, you have somewhere to start.
Why the Power BI Health Check still finds these three
These three issues have been in our top three for six years. Power BI has changed in that time, the licences have changed, and Microsoft has rebranded the products more than once. The underlying patterns have not changed.
Part of the reason is that Microsoft ships defaults that are friendly to a first-time user and costly to a mature one, and Power BI’s user base has matured faster than the defaults have. Part of it is that the path from “I built a working dashboard” to “I built a healthy dashboard” is not a path most teams know they need to walk. The dashboard works. The questions get answered. The cost shows up later, usually as a refresh that started missing its window.
Some of the harder decisions sit upstream of the model itself, in where data preparation lives and what the pipeline architecture should look like. We have written separately about whether you need a silver layer in Power BI, which is often the next conversation after a Health Check, when the answer to “fix the model” turns out to be “the model is the wrong place to fix it.”
A Power BI Health Check is one way to catch the cost early, particularly where these three patterns sit underneath thirty more that an afternoon’s self-check will not surface. Running the three checks above against your own model, honestly, is the cheaper place to start.


