Labs4Change

Free Guide

LookML Best Practices Guide + AI Skill

The patterns we've refined across 200+ Looker implementations and 16,000+ trained engineers. Plus a drop-in AI skill that enforces them automatically.

What's Inside

This guide covers the 10 patterns that separate production-quality LookML from the mess most teams end up with. Every rule comes from real audits — not theory.


1. Project Structure

Organize with clear separation of concerns. Auto-generated views, refinements, derived tables, and explores each get their own folder.

project/
├── views/
│   ├── _autogen/              # Auto-generated — NEVER modify
│   ├── _refined/              # Refinements with business logic
│   └── derived_tables/        # NDTs, SQL DTs, PDTs
├── explores/                  # Explore definitions (separate from views)
├── models/
├── tests/                     # Data tests
└── dashboards/

One view per file. File name matches view name. Auto-generated views are read-only. All business logic lives in refinement files.

2. Auto-Generated Views

Only use ${TABLE}.column_name syntax. No labels, descriptions, measures, or business logic.

view: orders {
  sql_table_name: `project.dataset.orders` ;;

  dimension: order_id {
    type: number
    sql: ${TABLE}.order_id ;;
  }
}

When the schema changes upstream, you can regenerate this file without losing any work.

3. Refinements Over Extends

Use the + prefix to add business logic without touching originals:

include: "/views/_autogen/orders.view.lkml"

view: +orders {
  dimension: order_id {
    primary_key: yes
    description: "Unique identifier for each order."
  }

  measure: total_revenue {
    type: sum
    sql: ${amount} ;;
    description: "Sum of all order amounts in USD."
    value_format_name: usd
  }
}

Refinements let you layer business logic on top of auto-generated views. The original file stays clean and regenerable.

4. The Substitution Operator

This is the most common source of bugs in LookML. The rules are simple but almost always violated:

ContextSyntaxExample
Database column (autogen only)${TABLE}.columnsql: ${TABLE}.order_id ;;
Same-view field reference${field_name}sql: ${amount} * ${quantity} ;;
Cross-view field reference${view.field}sql_on: ${orders.user_id} = ${users.id} ;;

Never use ${TABLE}.column outside auto-generated views. Always use ${field_name} within the same view.

5. Every Field Needs a Description

Every description should answer three questions:

  1. What is this? — Business meaning
  2. What are the possible values? — Enums, nulls, edge cases
  3. How should I use this? — Guidance for the user
dimension: is_first_purchase {
  type: yesno
  description: "Yes if this is the customer's earliest order by created date.
    Useful for new customer acquisition analysis.
    Returns No for customers whose only order was cancelled."
}

Descriptions also feed Looker's Gemini AI for natural language querying — better descriptions means better AI answers.

6. Derived Tables

  • Prefer Native Derived Tables (NDTs) over SQL-based
  • Always define a primary key in derived table views
  • Use datagroup_trigger (not persist_for) for PDTs
  • For complex transformations — build in dbt instead

7. Naming Conventions

ObjectConventionExample
View filessnake_case, match view nameorders.view.lkml
Refined view filesappend _rorders_r.view.lkml
Dimensionssnake_case, business-friendlycustomer_name
Measuresverb + nountotal_revenue
Yesno dimensionsprefix is_ or has_is_first_purchase
Dimension groupsno "date" or "time" suffixcreated, shipped
Primary keysentity + _idorder_id

8. Primary Keys & Joins

  • Every view must have a primary key. No exceptions.
  • Always specify relationship on every join.
  • Hide composite/technical primary keys.
  • Wrong relationships = wrong measures (silent data errors).

9. Caching With Datagroups

Always use datagroups over persist_for. Align cache invalidation with your ETL schedule.

datagroup: daily_etl {
  max_cache_age: "24 hours"
  sql_trigger: SELECT MAX(updated_at) FROM orders ;;
}

explore: orders {
  persist_with: daily_etl
}

10. Data Tests

Run tests before every deploy to production:

test: pk_not_null {
  explore_source: orders {
    column: order_id { field: orders.order_id }
  }
  assert: {
    expression: ${order_id} != null ;;
  }
}

The AI Skill

The SKILL.md file encodes all 10 rules above into a format that AI coding assistants understand. Drop it into your project and your AI will enforce these patterns every time it writes LookML.

Works with:

  • Claude Code — place in your project root or .claude/skills/
  • Cursor — place in project root
  • GitHub Copilot — reference in your prompt context

Go Deeper

This guide covers the foundation. For deep dives on specific topics, read:

Need a professional LookML audit?

We've audited hundreds of Looker instances. Book a free 30-minute call and we'll review your project structure, explores, and permissions.

Book a Free LookML Audit