materialization lineage (2)

materialization lineage (2)


Rule: max_materialization_lineage

Manifest Rule

max_materialization_lineage details
This rule limits the length of consecutive non-persisted materializations in a model's lineage. It walks up the DAG via parent_map, counting consecutive ancestors whose materialization is in the included_materializations set (default: view and ephemeral). The walk stops at non-model parents (sources, seeds, etc.) and at models with a materialization not in the included set.

When a node has multiple parents (diamond patterns), the longest chain is used.

Models whose own materialization is not in the included set are skipped entirely.


Configuration

  • type: Must be max_materialization_lineage.
  • max: (optional) Maximum number of consecutive non-persisted models allowed in the lineage (including the current model). Defaults to 4.
  • included_materializations: (optional) List of materializations considered non-persisted. Only chains of these materializations are counted.
    • Default: ["view", "ephemeral"]
    • Options: table, view, incremental, ephemeral, materialized_view, or any custom materialization string.
  • applies_to: (optional) List of dbt object types this rule checks.
    • Default: ["models"]
    • Options: models
Common Rule Config
  • name: Human-readable name of the rule.
  • severity: "error" (fail) or "warning" (warn only).
    • (optional, defaults to "error" if not specified)
  • description: Human-readable explanation of the rule.
  • category: Override the default rule category. Included in structured output (JSON, CSV, NDJSON) but not in the CLI table. Each rule has a built-in default (e.g. documentation, naming, testing, governance, structure, performance).
    • (optional, defaults to the rule type's built-in category)
  • includes: List of patterns to explicitly include for this rule. See Includes & Excludes for pattern syntax and examples.
  • excludes: List of patterns to explicitly exclude from this rule. See Includes & Excludes for pattern syntax and examples.
  • model_materializations: Filter models by materialization type. Only applies when applies_to includes models.
    • (optional, if not specified all materializations are included)
    • Built-in types: table, view, incremental, ephemeral, materialized_view. Custom materializations are also supported.
    • Example: ["table", "incremental"]

Example Config

manifest_tests:
  - name: "no_long_view_chains"
    type: "max_materialization_lineage"
    max: 4
    description: "Limit consecutive non-persisted materializations in lineage"
    # included_materializations: ["view", "ephemeral"]  (optional, default)
    # severity: "warning"  (optional)
    # includes: ["models/marts/*"]
    # excludes: ["models/staging/*"]

  # Stricter limit for marts
  - name: "marts_short_chains"
    type: "max_materialization_lineage"
    max: 2
    includes: ["models/marts"]
    severity: "error"
[[manifest_tests]]
name = "no_long_view_chains"
type = "max_materialization_lineage"
max = 4
description = "Limit consecutive non-persisted materializations in lineage"
# included_materializations = ["view", "ephemeral"]  # (optional, default)
# severity = "warning"  # (optional)
# includes = ["models/marts/*"]
# excludes = ["models/staging/*"]

# Stricter limit for marts
[[manifest_tests]]
name = "marts_short_chains"
type = "max_materialization_lineage"
max = 2
includes = ["models/marts"]
severity = "error"
[[tool.dbtective.manifest_tests]]
name = "no_long_view_chains"
type = "max_materialization_lineage"
max = 4
description = "Limit consecutive non-persisted materializations in lineage"
# included_materializations = ["view", "ephemeral"]  # (optional, default)
# severity = "warning"  # (optional)
# includes = ["models/marts/*"]
# excludes = ["models/staging/*"]

# Stricter limit for marts
[[tool.dbtective.manifest_tests]]
name = "marts_short_chains"
type = "max_materialization_lineage"
max = 2
includes = ["models/marts"]
severity = "error"
Example
  graph LR
  raw_orders["source: raw.orders"] --> stg_orders["stg_orders (view)"]
  stg_orders --> int_order_items["int_order_items (view)"]
  int_order_items --> int_orders["int_orders (view)"]
  int_orders --> fct_orders["fct_orders (view)"]

The rule walks up the parent_map from manifest.json. For example:

"parent_map": {
  "model.project.fct_orders": ["model.project.int_orders"],
  "model.project.int_orders": ["model.project.int_order_items"],
  "model.project.int_order_items": ["model.project.stg_orders"],
  "model.project.stg_orders": ["source.project.raw.orders"]
}

With all four models materialized as view and max: 3, the fct_orders model would fail because it has a chain of 4 consecutive views (stg_orders → int_order_items → int_orders → fct_orders), which exceeds the limit of 3.

If int_orders were materialized as table, the chain at fct_orders would be just 1 (only itself), because the table breaks the chain.


Rule: exposure_parents_materialized

Manifest Rule

exposure_parents_materialized details
This rule ensures that every direct model parent of an exposure uses a persisted materialization. Exposures (dashboards, ML models, applications) that depend on non-persisted models (views, ephemeral) can suffer from poor query performance and stale data.

Only model parents are checked — sources and seeds are skipped since they don’t have configurable materializations.

One violation is produced per non-compliant parent.


Configuration

  • type: Must be exposure_parents_materialized.
  • allowed_materializations: (optional) List of materializations considered persisted/acceptable for exposure parents.
    • Default: ["table", "incremental", "materialized_view"]
    • Options: table, view, incremental, ephemeral, materialized_view, or any custom materialization string.
  • applies_to: (optional) List of dbt object types this rule checks.
    • Default: ["exposures"]
    • Options: exposures
Common Rule Config
  • name: Human-readable name of the rule.
  • severity: "error" (fail) or "warning" (warn only).
    • (optional, defaults to "error" if not specified)
  • description: Human-readable explanation of the rule.
  • category: Override the default rule category. Included in structured output (JSON, CSV, NDJSON) but not in the CLI table. Each rule has a built-in default (e.g. documentation, naming, testing, governance, structure, performance).
    • (optional, defaults to the rule type's built-in category)
  • includes: List of patterns to explicitly include for this rule. See Includes & Excludes for pattern syntax and examples.
  • excludes: List of patterns to explicitly exclude from this rule. See Includes & Excludes for pattern syntax and examples.
  • model_materializations: Filter models by materialization type. Only applies when applies_to includes models.
    • (optional, if not specified all materializations are included)
    • Built-in types: table, view, incremental, ephemeral, materialized_view. Custom materializations are also supported.
    • Example: ["table", "incremental"]

Example Config

manifest_tests:
  - name: "exposure_parents_must_be_materialized"
    type: "exposure_parents_materialized"
    description: "Exposure parents must use persisted materializations"
    severity: "error"
    # allowed_materializations: ["table", "incremental", "materialized_view"]  (optional, default)

  # Allow views for internal dashboards
  - name: "internal_exposure_parents"
    type: "exposure_parents_materialized"
    allowed_materializations:
      ["table", "incremental", "materialized_view", "view"]
    includes: ["models/internal/*"]
    severity: "warning"
[[manifest_tests]]
name = "exposure_parents_must_be_materialized"
type = "exposure_parents_materialized"
description = "Exposure parents must use persisted materializations"
severity = "error"
# allowed_materializations = ["table", "incremental", "materialized_view"]  # (optional, default)

# Allow views for internal dashboards
[[manifest_tests]]
name = "internal_exposure_parents"
type = "exposure_parents_materialized"
allowed_materializations = ["table", "incremental", "materialized_view", "view"]
includes = ["models/internal/*"]
severity = "warning"
[[tool.dbtective.manifest_tests]]
name = "exposure_parents_must_be_materialized"
type = "exposure_parents_materialized"
description = "Exposure parents must use persisted materializations"
severity = "error"
# allowed_materializations = ["table", "incremental", "materialized_view"]  # (optional, default)

# Allow views for internal dashboards
[[tool.dbtective.manifest_tests]]
name = "internal_exposure_parents"
type = "exposure_parents_materialized"
allowed_materializations = ["table", "incremental", "materialized_view", "view"]
includes = ["models/internal/*"]
severity = "warning"
Example exposure

Given an exposure in your dbt project:

  graph LR
  fct_revenue["fct_revenue (table) ✅"] --> dashboard["weekly_revenue_dashboard (exposure)"]
  dim_dates["dim_dates (view) ❌"] --> dashboard
exposures:
  - name: weekly_revenue_dashboard
    type: dashboard
    owner:
      name: Analytics Team
    depends_on:
      - ref('fct_revenue')
      - ref('dim_dates')

If fct_revenue is materialized as table but dim_dates is materialized as view, the rule would produce one violation for dim_dates (assuming the default allowed_materializations).