Semantic Layer Best Practices
Synthesized from the dbt Semantic Layer best practices guide.
Core Principles
- Prefer normalization - Let MetricFlow denormalize dynamically for end users rather than pre-building wide tables
- Compute in metrics, not rollups - Define calculations in measures and metrics instead of frozen aggregations
- Start simple - Build simple metrics first before advancing to ratio and derived types
Semantic Model Design
Structure Order
Define components consistently: entities → dimensions → measures
Entities
- Each semantic model needs exactly one primary entity
- Use singular naming (
ordernotorder_id) withexprfor the column reference - Foreign entities enable joins between semantic models
Dimensions
- Always include a primary time dimension when the model has measures
- Use
exprfor computed dimensions (e.g., categorizing by thresholds) - Set granularity at the column level for time dimensions
Measures
- Create measures for quantitative values you'll aggregate
- Use
expr: 1withagg: sumfor counting records - Measures are the building blocks for all metric types
Metric Design
Required Properties
Every metric needs: name, description, label, and type
Type Progression
- Simple - Single measure with optional filters (start here)
- Ratio - Numerator divided by denominator
- Derived - Calculations combining multiple metrics
- Cumulative - Running totals or windowed aggregations
Naming
- Use clear business-friendly labels for downstream tools
- Use double underscores to disambiguate dimensions (
orders__location)
File Organization
Two valid approaches:
- Co-located - Semantic YAML alongside corresponding mart models
- Parallel folder - Dedicated
semantic_models/subfolder withsem_prefixes
Choose based on project scale and team preference.
Development Workflow
# Refresh manifest after changes
dbt parse
# List available dimensions for a metric
dbt sl list dimensions --metrics <metric_name> # dbt Cloud CLI
mf list dimensions --metrics <metric_name> # MetricFlow CLI
# Test metric queries
dbt sl query --metrics <metric_name> --group-by <dimension> # dbt Cloud CLI
mf query --metrics <metric_name> --group-by <dimension> # MetricFlow CLI
What to Avoid
| Anti-pattern | Better approach |
|---|---|
| Building semantic models on dimension-only tables without measures | Only add primary entity for pure dimensional tables |
| Refactoring production code directly | Build in parallel, deprecate gradually |
| Pre-computing rollups in dbt models | Define calculations in metrics |
| Creating multiple time dimension buckets | Set minimum granularity, let MetricFlow handle the rest |
When to Use Marts
Use intermediate marts strategically for:
- Grouping related tables
- Attaching measures to dimensional tables
- Complex joins that benefit from materialization
Build semantic models on staging when source data is already well-structured.