Best Practices
by kpbray
Applies Power BI best practices, BPA rules, and enterprise standards. Use for quality validation, naming conventions, and performance optimization.
Skill Details
Repository Files
3 files in this skill directory
name: best-practices description: "Applies Power BI best practices, BPA rules, and enterprise standards. Use for quality validation, naming conventions, and performance optimization."
Best Practices Skill
This skill helps apply Power BI best practices, Best Practice Analyzer (BPA) rules, and enterprise standards to semantic models and reports.
When to Use This Skill
- Validating model quality against BPA rules
- Applying naming conventions
- Optimizing model performance
- Adding documentation and descriptions
- Enforcing enterprise governance patterns
- Reviewing DAX measures for quality
Best Practice Analyzer (BPA) Overview
BPA rules are organized into categories:
- Performance - Optimization and efficiency rules
- DAX Expressions - Measure and calculation quality
- Formatting - Naming and organization standards
- Maintenance - Housekeeping and cleanup rules
- Error Prevention - Validation and safety rules
Quick Validation Checklist
Model Structure
- All tables have descriptions
- Relationships are one-to-many (no bi-directional unless required)
- Key columns are marked as hidden
- Date table is marked as date table
- No unused columns or tables
DAX Measures
- All measures have descriptions (/// comments)
- DIVIDE() used instead of
/operator - Variables used for repeated expressions
- Measures organized in display folders
- No implicit measures (summarizations)
Naming Conventions
- Tables: PascalCase, singular nouns
- Columns: PascalCase with spaces allowed
- Measures: Business-friendly names
- No special characters or reserved words
Performance
- No calculated columns where measures work
- Integer keys instead of string keys
- Narrow tables preferred (fewer columns)
- Appropriate data types used
Performance Rules
HIGH IMPACT
Avoid bi-directional relationships
Bi-directional relationships cause performance overhead and can create ambiguity.
Instead of:
relationship {{guid}}
fromColumn: TableA.'Key'
toColumn: TableB.'Key'
crossFilteringBehavior: bothDirections
Do this:
relationship {{guid}}
fromColumn: TableA.'Key'
toColumn: TableB.'Key'
// Use DAX CROSSFILTER() in measures when needed
measure 'Filtered Value' =
CALCULATE(
[Base Measure],
CROSSFILTER(TableA[Key], TableB[Key], Both)
)
Use integer keys instead of strings
Integer comparisons are faster than string comparisons.
Instead of:
column 'Product Key'
dataType: string
Do this:
column 'Product Key'
dataType: int64
Minimize calculated columns
Calculated columns consume memory and slow refresh. Use measures when possible.
Instead of calculated column:
column 'Profit Margin' =
DIVIDE(Sales[Profit], Sales[Revenue], 0)
Use a measure:
measure 'Profit Margin' =
DIVIDE(SUM(Sales[Profit]), SUM(Sales[Revenue]), 0)
MEDIUM IMPACT
Remove unused columns
Unused columns waste memory. Delete columns not used in:
- Relationships
- Measures
- Visuals
- Filters
Avoid high cardinality columns
Columns with many unique values increase model size. Consider:
- Grouping/binning high cardinality data
- Moving detail columns to a separate table
- Using calculated columns only when necessary
Use appropriate data types
| Data | Recommended Type |
|---|---|
| IDs, Keys | int64 |
| Flags | boolean |
| Currency | decimal |
| Percentages | double or calculated in DAX |
| Dates | dateTime |
DAX Rules
REQUIRED
Use DIVIDE instead of /
Bad:
Margin = Sales[Profit] / Sales[Revenue]
Good:
Margin = DIVIDE(Sales[Profit], Sales[Revenue], 0)
Use VAR for repeated expressions
Bad:
Growth % =
(SUM(Sales[Amount]) - CALCULATE(SUM(Sales[Amount]), SAMEPERIODLASTYEAR(Date[Date])))
/ CALCULATE(SUM(Sales[Amount]), SAMEPERIODLASTYEAR(Date[Date]))
Good:
Growth % =
VAR CurrentSales = SUM(Sales[Amount])
VAR PriorSales = CALCULATE(SUM(Sales[Amount]), SAMEPERIODLASTYEAR(Date[Date]))
RETURN
DIVIDE(CurrentSales - PriorSales, PriorSales)
Add descriptions to measures
/// Calculates total sales revenue
/// Excludes returns and cancellations
measure 'Total Sales' =
SUM(Sales[Amount])
RECOMMENDED
Avoid IFERROR with aggregations
IFERROR masks data quality issues.
Bad:
Total = IFERROR(SUM(Sales[Amount]), 0)
Good:
Total = SUM(Sales[Amount])
// Handle blanks explicitly if needed
Total = COALESCE(SUM(Sales[Amount]), 0)
Use REMOVEFILTERS instead of ALL
REMOVEFILTERS is more explicit about intent.
Prefer:
All Sales = CALCULATE([Total Sales], REMOVEFILTERS(Products))
Avoid nested CALCULATE
Deeply nested CALCULATE is hard to maintain.
Bad:
Measure =
CALCULATE(
CALCULATE(
CALCULATE([Base], Filter1),
Filter2
),
Filter3
)
Good:
Measure =
CALCULATE(
[Base],
Filter1,
Filter2,
Filter3
)
Formatting Rules
Table Naming
| Pattern | Example | Use |
|---|---|---|
| PascalCase | SalesOrders |
Standard tables |
| Singular | Customer not Customers |
Dimension tables |
| Prefix dim/fact | dimCustomer, factSales |
Optional, for clarity |
Column Naming
| Pattern | Example |
|---|---|
| PascalCase with spaces | Customer Name |
| Keys end with Key | Product Key |
| IDs end with ID | Transaction ID |
| Dates end with Date | Order Date |
Measure Naming
| Category | Pattern | Example |
|---|---|---|
| Aggregations | Noun phrase | Total Sales |
| Percentages | End with % |
Gross Margin % |
| Ratios | Include units | Sales per Customer |
| Time Intelligence | Include period | Sales YTD, Sales PY |
| Counts | End with Count |
Customer Count |
Display Folders
Organize measures into logical folders:
├── Core Metrics
│ ├── Total Sales
│ ├── Total Cost
│ └── Total Profit
├── Time Intelligence
│ ├── Sales YTD
│ ├── Sales PY
│ └── Sales YoY %
├── Percentages
│ ├── Gross Margin %
│ └── % of Total
└── KPIs
├── Target
└── Achievement
Maintenance Rules
Remove unused objects
Delete these if not used:
- Tables with no relationships or references
- Columns not in visuals, measures, or relationships
- Measures not in visuals or other measures
- Hidden columns not used in calculations
Document everything
Every object should have a description:
/// Customer dimension table
/// Contains customer demographics and segmentation
table Customers
lineageTag: ...
/// Unique identifier for each customer
column 'Customer ID'
...
/// Customer's full name (First + Last)
column 'Customer Name'
...
Use consistent formatting
- TMDL: Use tabs for indentation
- DAX: Use 4 spaces for indentation
- Follow SQLBI DAX formatting conventions
Error Prevention Rules
Validate relationships
Before creating relationships, verify:
- "To" column contains unique values
- Data types match exactly
- Orphan handling is considered
- Filter direction is appropriate
Validate measure references
Before using measures in visuals:
- Test with simple filters
- Verify time intelligence with date table
- Check for circular references
- Validate with EVALUATE in DAX Studio
Test edge cases
Consider these scenarios:
- Empty filter context (grand total)
- Single value context (card visual)
- Multiple selections
- No data (blanks)
Enterprise Governance Patterns
Row-Level Security (RLS)
Define RLS roles in TMDL:
role SalesRep
modelPermission: read
tablePermission Sales = 'Sales'[Sales Rep] = USERPRINCIPALNAME()
Data Sensitivity
Mark sensitive columns:
column 'SSN'
dataType: string
isHidden
// Consider not including in model
Audit Trail
Document model changes:
- Use Git commit messages
- Add change log in model description
- Track measure versions with comments
Data Source Governance
Credential Security
NEVER store credentials in source files:
/// BAD: Hardcoded connection string
expression ConnectionString = "Server=prod.database.com;User=admin;Password=secret123"
/// GOOD: Use parameters without credentials
expression ServerName = "prod.database.com" meta [IsParameterQuery=true, Type="Text"]
Credentials should be:
- Configured in Power BI Service after publish
- Managed via gateway for on-premises sources
- Stored in Azure Key Vault for automated scenarios
Connection Parameters
Use parameters for environment-specific values:
/// Server name parameter (update per environment)
expression ServerName = "dev-server.database.windows.net" meta [IsParameterQuery=true, Type="Text", IsParameterQueryRequired=true]
/// Database name parameter
expression DatabaseName = "SalesDB_Dev" meta [IsParameterQuery=true, Type="Text", IsParameterQueryRequired=true]
Gateway Configuration
For on-premises or VNet data sources:
| Source Location | Gateway Required |
|---|---|
| Cloud (Azure SQL, SharePoint Online) | No |
| On-premises (SQL Server, file share) | Yes |
| VNet (private endpoints) | Yes (VNet gateway) |
| Local files (CSV, Excel) | Yes or use cloud storage |
Refresh Considerations
Import Mode:
- Data copied into model at refresh
- Schedule refreshes in Power BI Service
- Consider incremental refresh for large tables
DirectQuery:
- Live queries to source
- No refresh needed
- Source must be always available
- Performance dependent on source
Hybrid (Composite):
- Mix of Import and DirectQuery
- Use Import for dimensions, DirectQuery for facts
- Requires careful relationship configuration
Incremental Refresh Setup
- Create required parameters in Power Query:
RangeStart = #datetime(2020, 1, 1, 0, 0, 0) meta [IsParameterQuery=true, Type="DateTime"]
RangeEnd = #datetime(2025, 12, 31, 23, 59, 59) meta [IsParameterQuery=true, Type="DateTime"]
- Filter query with these parameters:
Table.SelectRows(Source, each [Date] >= RangeStart and [Date] < RangeEnd)
- Configure refresh policy in Power BI Desktop before publish
Data Source Checklist
- No credentials in source files
- Connection strings use parameters
- Gateway configured for on-premises sources
- Query folding verified for large tables
- Incremental refresh configured where appropriate
- Timeout settings appropriate for data volume
- Error handling in Power Query transformations
- Data source documented in model description
Query Folding Validation
Ensure transformations push to source database:
| Foldable (DO) | Non-Foldable (AVOID) |
|---|---|
Table.SelectRows (simple) |
Table.AddColumn (custom) |
Table.SelectColumns |
Table.Buffer |
Table.Sort |
Custom functions |
Table.Group |
Cross-source joins |
Check folding: Right-click step > "View Native Query"
Validation Workflow
-
After creating tables
- Verify naming conventions
- Check data types
- Add descriptions
-
After adding relationships
- Confirm cardinality
- Verify no bi-directional unless needed
- Check filter flow
-
After writing measures
- Run BPA checks
- Verify DIVIDE usage
- Add descriptions
- Test calculations
-
Before deployment
- Remove unused objects
- Verify all descriptions
- Check performance
- Test with sample data
Integration with Other Skills
After using other skills, apply best practices:
| Skill | Validation Focus |
|---|---|
pbip-project |
File structure, encoding |
semantic-model |
Relationships, data types |
dax |
Measure quality, formatting |
report-visuals |
Visual references, performance |
power-query |
Query folding, data source parameters |
themes |
Accessibility, color contrast |
calculation-groups |
Appropriate use, format strings |
security |
RLS filter efficiency, complete coverage |
deployment |
BPA in CI/CD, secret management |
BPA Rule Reference
See bpa-rules.md for complete rule list.
Naming Convention Reference
See naming-conventions.md for detailed naming guidelines.
Related Skills
Attack Tree Construction
Build comprehensive attack trees to visualize threat paths. Use when mapping attack scenarios, identifying defense gaps, or communicating security risks to stakeholders.
Grafana Dashboards
Create and manage production Grafana dashboards for real-time visualization of system and application metrics. Use when building monitoring dashboards, visualizing metrics, or creating operational observability interfaces.
Matplotlib
Foundational plotting library. Create line plots, scatter, bar, histograms, heatmaps, 3D, subplots, export PNG/PDF/SVG, for scientific visualization and publication figures.
Scientific Visualization
Create publication figures with matplotlib/seaborn/plotly. Multi-panel layouts, error bars, significance markers, colorblind-safe, export PDF/EPS/TIFF, for journal-ready scientific plots.
Seaborn
Statistical visualization. Scatter, box, violin, heatmaps, pair plots, regression, correlation matrices, KDE, faceted plots, for exploratory analysis and publication figures.
Shap
Model interpretability and explainability using SHAP (SHapley Additive exPlanations). Use this skill when explaining machine learning model predictions, computing feature importance, generating SHAP plots (waterfall, beeswarm, bar, scatter, force, heatmap), debugging models, analyzing model bias or fairness, comparing models, or implementing explainable AI. Works with tree-based models (XGBoost, LightGBM, Random Forest), deep learning (TensorFlow, PyTorch), linear models, and any black-box model
Pydeseq2
Differential gene expression analysis (Python DESeq2). Identify DE genes from bulk RNA-seq counts, Wald tests, FDR correction, volcano/MA plots, for RNA-seq analysis.
Query Writing
For writing and executing SQL queries - from simple single-table queries to complex multi-table JOINs and aggregations
Pydeseq2
Differential gene expression analysis (Python DESeq2). Identify DE genes from bulk RNA-seq counts, Wald tests, FDR correction, volcano/MA plots, for RNA-seq analysis.
Scientific Visualization
Meta-skill for publication-ready figures. Use when creating journal submission figures requiring multi-panel layouts, significance annotations, error bars, colorblind-safe palettes, and specific journal formatting (Nature, Science, Cell). Orchestrates matplotlib/seaborn/plotly with publication styles. For quick exploration use seaborn or plotly directly.
