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],boverridesa. 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:
- Fields merge instead of replacing. In the example above,
total_revenuekeeps itstypeandsqlfrom the original view and only gains a description. This is the single most useful behaviour of refinements: you can add alabel,description,hidden, orgroup_labelto an existing field without copying its SQL. - They apply globally, to every Explore that already references the view, in include order.
- 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
| Situation | Use |
|---|---|
| Add labels, descriptions, or hide fields on a generated or Marketplace view | Refinement |
| One team needs extra measures that others should not see | extends into a team-specific view |
| Add row-level security to an Explore defined in another file | Refinement 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 regenerated | Refinement (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
labelsilently resolve by order. Keep one refinement file per concern (labels, security, team measures) rather than one per developer. extendscopies drift. A team view that extends a base and then redefinestotal_revenuewill not inherit a later fix to the base. Redefine as little as possible.final: yeson 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.