Inherited a Power BI Model? Document It First

TOOLS Published 10 mins read By Leon Wei
Inherited a Power BI Model? Document It First cover image

Quick summary

Summarize this blog with AI

When you inherit a Power BI report with no documentation, do not start by renaming or deleting things. First snapshot the numbers people rely on. Then use DAX query view to list every table, column, measure, and relationship, and to find which measures depend on which. Finally, search the saved report files to see which fields the visuals actually use. Only then clean up, one change at a time, checking the snapshot after each change.

This workflow uses features built into Power BI Desktop: DAX query view, the INFO DAX functions, and Power BI project (PBIP) files. The queries use function and column names from Microsoft's current reference documentation, but they were not executed against a model for this guide. Run them on a copy first. The INFO functions need full access to the model: open the original Desktop file or a copy of it, not a report that is live connected to a published semantic model.

Work on a copy and record where it came from

Save a copy of the file before you open anything else, and keep the original untouched. Write down where the published version lives: workspace, semantic model name, refresh schedule, and the owner listed in the service. Those facts are often the only documentation that exists.

Check which kind of file you have. If the lower-right corner of Report view says it is live connected to a semantic model, the model lives elsewhere. You can still query it, but you cannot see measure formulas, and dependency queries will not run. Get the semantic model's own file, or ask a workspace admin to download it.

Snapshot the numbers people rely on

Before changing anything, record a baseline you can compare against after every cleanup step. Ask the report's users which three to five numbers they actually use, then capture them as a DAX query.

The fastest way to capture an existing visual exactly is Performance Analyzer. In Report view, open Optimize > Performance Analyzer, select Start recording and Refresh visuals, expand a visual, and choose to run its query in DAX query view. Power BI opens the visual's own query in a new tab. Run it, use the grid's Copy button, and paste the result into a text file named with today's date.

For measures without a convenient visual, write a summary query yourself. Replace the names with the ones in your model:

EVALUATE
    SUMMARIZECOLUMNS (
        'Date'[Year],
        "Revenue", [Revenue],
        "Orders", [Orders],
        "Margin %", [Margin %]
    )
ORDER BY 'Date'[Year]

Rename the query tab Baseline. DAX query view saves its tabs inside the Desktop file, so the baseline query travels with the model. Rerun it after each change; the results should match your saved copy unless you intended to change a number. To turn the baseline into proper known-answer tests, see testing Power BI measures with known answers.

Inventory the model with INFO.VIEW functions

The INFO.VIEW functions return the model's own metadata as tables with readable names. Open DAX query view and run each of these in its own tab:

EVALUATE INFO.VIEW.TABLES()

EVALUATE INFO.VIEW.COLUMNS()

EVALUATE INFO.VIEW.RELATIONSHIPS()

Use the tables list to find import tables, calculated tables, and hidden helper tables. Use the relationships list to note each relationship's direction, cardinality, and whether it is active. Bidirectional and inactive relationships are where surprising numbers usually come from, so record them first.

For measures, select the documented columns that matter for a handover:

EVALUATE
    SELECTCOLUMNS (
        INFO.VIEW.MEASURES (),
        "Home table", [Table],
        "Measure", [Name],
        "Folder", [DisplayFolder],
        "Hidden", [IsHidden],
        "State", [State],
        "Description", [Description],
        "DAX formula", [Expression]
    )
ORDER BY [Home table], [Measure]

Copy the results into a spreadsheet; this becomes the measure register for your handover document. Look first at the State column, which shows measures in an error state, then at measures with no description, names such as Measure 3 or Sales v2 FINAL, and pairs of measures with nearly identical formulas. Microsoft's INFO functions reference lists every column these functions return.

If the model has hundreds of measures, Quick queries > Define all measures in the model in the Data pane writes every formula into one query tab, which is easier to search with Ctrl+F than a grid.

Find which measures depend on which

INFO.CALCDEPENDENCY lists each calculated object and every object it references. It runs only in DAX query view, requires write permission on the model, and does not run when live connected. This query shows what a single measure is built from:

EVALUATE
    SELECTCOLUMNS (
        FILTER ( INFO.CALCDEPENDENCY (), [OBJECT] = "Revenue" ),
        "Object type", [OBJECT_TYPE],
        "Object", [OBJECT],
        "Uses type", [REFERENCED_OBJECT_TYPE],
        "Uses table", [REFERENCED_TABLE],
        "Uses", [REFERENCED_OBJECT]
    )

Reverse the filter to [REFERENCED_OBJECT] = "Revenue" to see everything built on top of Revenue. That reverse list is what breaks if you change it.

To list measures that no other model object references, compare the measure register with the dependency list:

DEFINE
    VAR Referenced =
        DISTINCT (
            SELECTCOLUMNS (
                FILTER ( INFO.CALCDEPENDENCY (), [REFERENCED_OBJECT_TYPE] = "MEASURE" ),
                "Measure", [REFERENCED_OBJECT]
            )
        )
    VAR AllMeasures =
        SELECTCOLUMNS ( INFO.VIEW.MEASURES (), "Home table", [Table], "Measure", [Name] )

EVALUATE
    FILTER ( AllMeasures, NOT ( [Measure] IN Referenced ) )
ORDER BY [Home table], [Measure]

Treat the result as a list of candidates, not unused measures. A measure that no other measure references may still be used by a visual, a filter, a bookmark, another report connected to the published semantic model, or someone's Excel workbook. The next two sections check the visible cases. Run EVALUATE INFO.CALCDEPENDENCY() on its own first and look at the values in REFERENCED_OBJECT_TYPE, so the filter matches the labels your model actually returns.

Find which fields the report uses

The model cannot see the report's visuals. To search them, save the report as a Power BI project: File > Save as and choose the Power BI project (*.pbip) file type. Save it to a short local folder path: Power BI Desktop cannot save a project directly to OneDrive or SharePoint, and long paths can exceed Windows' default path limit. Power BI writes a folder for the report and a folder for the semantic model. In the current Power BI enhanced report format (PBIR), which is now the default, each visual is a separate visual.json file under the report folder's definition\pages\ folder, and each page's filters are in its page.json.

Search that folder for each candidate measure's exact name in quotes. In VS Code, use Find in Files on the report folder. On macOS or Linux, this lists the files that mention a measure called Revenue LY old:

grep -rl '"Revenue LY old"' "Sales Report.Report/definition"

On Windows PowerShell, the equivalent is Get-ChildItem "Sales Report.Report\definition" -Recurse -Filter *.json | Select-String -SimpleMatch '"Revenue LY old"' -List. Open each match to confirm it is a field reference rather than, for example, a title that happens to contain the same words. To go from a visual on screen to its file, enable Copy object names when right clicking on report objects under File > Options and settings > Report settings, right-click the visual, choose Copy object name, and search for that name. Microsoft documents the full structure in the PBIP report folder reference.

The report files can include data values, such as a filter on a specific customer name. Treat the folder as confidential, and check before committing it to a shared repository.

Check what lives outside the file

Three kinds of dependency are invisible from a single Desktop file:

  • Other reports on the same semantic model. In the Power BI service, open the semantic model's lineage or impact analysis to see the reports and workspaces that use it. A measure no visual in your file uses may be central to someone else's report.
  • Row-level security. Open Modeling > Manage roles and read each role's filter expressions before changing the tables and columns they use.
  • Excel and other tools. Analyze in Excel workbooks and other client tools query the model directly. Ask the users; the file cannot tell you.

Trace one important number end to end

Pick the most important number on the report and trace it all the way to the source. This exercise teaches you more about the model than any inventory:

  1. Visual to measure. Select the visual and note the measure and the filters applied by the page, the visual, and any slicers.
  2. Measure to formula. Right-click the measure in DAX query view's Data pane and choose Quick queries > Define with references and evaluate. Power BI writes the measure and every measure it uses into one query.
  3. Formula to columns and relationships. Note each column the formulas use, and which relationships connect those tables to the visual's filters.
  4. Columns to Power Query. Open Transform data, select the table, and read the applied steps, or open the Advanced Editor. Note filters, merges, and type changes that affect the measure's inputs.
  5. Power Query to source. Record the source system, database, and table or file path, plus any parameters.

Then reproduce the number from the source independently, for example with a SQL query using the same filters. If the two disagree, you have found either a bug or an undocumented business rule; the metric reconciliation playbook helps you tell which. If the source is a database nobody documented either, exploring an unfamiliar database covers that side.

Write a one-page handover document

Keep it short enough that the next person will read it, and store it next to the file:

  • Purpose and audience: the decisions the report supports and who uses it.
  • Location and refresh: workspace, semantic model, schedule, gateway, and the credentials it depends on.
  • Sources: each source system and what it supplies.
  • Model map: fact tables and their grain, dimensions, and relationships, noting inactive and bidirectional ones.
  • Key measures: the five to ten measures that matter, each with a plain-language definition and a business owner.
  • Security: roles and who belongs to them.
  • Known issues and open questions: what you could not explain yet.
  • Change log: date, change, reason, and the baseline check that confirmed it.

Put definitions inside the model too. In DAX query view, a /// comment directly above a MEASURE in a DEFINE block becomes the measure's description when you choose Update model with changes, and users then see it as a tooltip in the Data pane.

Clean up safely, one change at a time

With the inventory, dependencies, report usage, and baseline in hand, change the model in small steps:

  1. Add descriptions and display folders first. They change no numbers and make everything after easier.
  2. Hide before deleting. Hide a candidate measure, publish, and wait at least one full reporting cycle. If nobody reports a broken visual or workbook, delete it in a later release.
  3. Rename deliberately. Power BI Desktop updates references to a renamed measure inside the same model and report, but other reports and Excel workbooks that use the published semantic model can break. Check the impact analysis first, and rename in a scheduled release.
  4. Merge duplicates only after proving they are equal. Evaluate both measures side by side in a query across the dimensions people filter by. If any row differs, the measures encode different rules; find out which one is right before removing either.
  5. Rerun the baseline after every step. If a number changes and you did not intend it, undo the step before doing anything else.

FAQ

Can I use these queries if I only have viewer access to the semantic model?

Partly. INFO functions require semantic model admin permissions, INFO.CALCDEPENDENCY needs write permission, and formulas are hidden when you are live connected. With viewer and build permissions you can still live connect and query measure results, which is enough for a baseline. For the inventory, ask the owner or a workspace admin for access or for the file.

Should I put the INFO.VIEW queries in the model as calculated tables?

You can. The INFO.VIEW functions, unlike the other INFO functions, can be used in calculated tables, so a hidden Model documentation table can keep the measure list current on every refresh. Keep it hidden from report users, and remember that it reflects only what the model contains, not how the report uses it.

The report has 300 measures. Where do I start?

Start with the numbers people use, not with the measure list. Trace those, document them, and protect them with the baseline. Many of the remaining measures will turn out to be intermediate steps or experiments. Clean those up gradually with the hide-then-delete approach, and you will not break something you did not know mattered.

Interview Prep

Begin Your SQL, Python, and R Journey

Master 230 interview-style coding questions and build the data skills needed for analyst, scientist, and engineering roles.

Related Articles

All Articles