+1 (726) 227-2971

LookML Refinements vs extends: Structuring a Multi-Team Looker Project

Most LookML projects start clean and end up as one giant views/ folder where nobody dares change a file because four Explores and a dozen dashboards depend on it. Refinements (+view_name) and extends are the two language features that fix this, and they solve different problems. Used together they let several teams work in one Looker project without stepping on each other, and they make Looker Marketplace blocks and dbt-generated views safe to customize.

This tutorial covers when to reach for each, the gotchas that bite teams in production, and a folder layout that scales.

extends: build a new object from an old one

extends creates a new object that inherits everything from one or more parents. The parent still exists and is still usable on its own.

# views/base/orders_base.view.lkml
view: orders_base {
  sql_table_name: analytics.orders ;;

  dimension: id {
    primary_key: yes
    type: number
    sql: ${TABLE}.id ;;
  }

  dimension_group: created {
    type: time
    timeframes: [raw, date, week, month, quarter, year]
    sql: ${TABLE}.created_at ;;
  }

  measure: count {
    type: count
  }

  measure: total_revenue {
    type: sum
    sql: ${TABLE}.amount ;;
    value_format_name: usd
  }
}
# views/finance/orders_finance.view.lkml
include: "/views/base/orders_base.view.lkml"

view: orders_finance {
  extends: [orders_base]

  measure: recognized_revenue {
    type: sum
    sql: ${TABLE}.amount ;;
    filters: [status: "shipped,delivered"]
    value_format_name: usd
  }
}

Finance now has its own view with its own measure, and the marketing team's Explores keep using orders_base untouched. Two rules worth internalising:

  • Extension is by name. If the child defines total_revenue, the child's version wins completely; it does not merge with the parent's.
  • Order matters with multiple parents. In extends: [a, b], b overrides a. Keep the list to one or two entries; longer chains become impossible to reason about.

Mark a view or Explore as extension: required when it is an abstract base that should never be used directly:

view: dates_base {
  extension: required
  ...
}

Looker will then refuse to let anyone Explore it, which is exactly what you want for scaffolding views.

Refinements: change an object in place

A refinement (view: +orders) modifies the existing object everywhere it is used. Nothing new is created, no include juggling in Explores, no renaming.

# views/refinements/orders_labels.view.lkml
include: "/views/base/orders.view.lkml"

view: +orders {
  dimension: id {
    label: "Order Number"
  }

  measure: total_revenue {
    description: "Gross merchandise value, pre-refunds."
  }

  measure: aov {
    type: number
    sql: ${total_revenue} / NULLIF(${count}, 0) ;;
    value_format_name: usd
  }
}

Three properties make refinements different from extends:

  1. Fields merge instead of replacing. In the example above, total_revenue keeps its type and sql from the original view and only gains a description. This is the single most useful behaviour of refinements: you can add a label, description, hidden, or group_label to an existing field without copying its SQL.
  2. They apply globally, to every Explore that already references the view, in include order.
  3. They compose. You can refine the same view in several files; Looker applies them in the order the files are included, so the last one wins on a conflict.

Refinements also work on Explores, which is how you add a join or an access_filter to an Explore you did not write:

explore: +orders {
  access_filter: {
    field: orders.region
    user_attribute: region
  }

  join: support_tickets {
    sql_on: ${orders.id} = ${support_tickets.order_id} ;;
    relationship: one_to_many
  }
}

Choosing between them

SituationUse
Add labels, descriptions, or hide fields on a generated or Marketplace viewRefinement
One team needs extra measures that others should not seeextends into a team-specific view
Add row-level security to an Explore defined in another fileRefinement on the Explore
Build several near-identical views over similar tables (regional clones)extends from an extension: required base
Patch a dbt- or block-generated file that will be regeneratedRefinement (never edit the generated file)

The rule of thumb: refine when everybody should see the change, extend when only one consumer should.

Gotchas we see in audits

  • Refinement not included. A refinement only applies if the file is included in the model. Include your refinement files in the model file, after the base views, and use include: "/views/refinements/*.view.lkml" so new files are picked up automatically.
  • Include order surprises. Two refinements setting the same label silently resolve by order. Keep one refinement file per concern (labels, security, team measures) rather than one per developer.
  • extends copies drift. A team view that extends a base and then redefines total_revenue will not inherit a later fix to the base. Redefine as little as possible.
  • final: yes on a view or Explore blocks further refinement. Useful on a certified, governed model; frustrating if applied casually.
  • Set names. Refinements can add fields to a view, but those fields do not automatically appear in an Explore's fields: list if that list is explicit. Update the set, or use refinements on the Explore too.
  • Testing. Add a LookML data test alongside each refinement that changes SQL, and let CI catch the regression; see CI for LookML.

A folder layout that scales

views/
  base/            # generated or hand-written, one per table, no labels
  refinements/     # global labels, descriptions, security
  teams/
    finance/       # extends + team-only measures
    marketing/
explores/
  core.explore.lkml
  finance.explore.lkml
models/
  core.model.lkml  # includes base, then refinements, then explores

With this layout, a dbt or Marketplace regeneration only ever touches views/base/. Everything the business cares about, naming, security, and team-specific metrics, lives in files that regeneration never overwrites. Combined with a git branch per developer and pull request review, it is the difference between a project that survives three years and one that gets rebuilt.

If your LookML project has outgrown its folder structure and you want help refactoring it without breaking dashboards, our LookML architects do exactly this kind of work. Get in touch.