Most Looker projects we are brought into are not greenfield. There is an incumbent BI tool — usually Tableau, sometimes Power BI or Qlik — with several hundred workbooks, a shared extract server, and a group of analysts who have been writing calculated fields for six years. The migration question is never "how do I rebuild this dashboard?" It is "which of these 400 artifacts deserve to exist in a semantic layer, and how do I prove the numbers still match?"
This tutorial is the playbook we use. It assumes you have a Looker instance (Looker Google Cloud core or Looker original) and a cloud warehouse, and that you are migrating from an extract-based tool.
1. Understand what actually changes
Tableau and Power BI are document-centric: logic lives inside the workbook or the PBIX. Two workbooks can define "Active Customer" two different ways and nobody notices until a board meeting. Looker is model-centric: logic lives in LookML, in Git, once, and every Explore and dashboard inherits it.
That is the whole point of the migration, and it drives one rule:
Do not port calculated fields one-for-one. Port business definitions into LookML, then rebuild content on top of them.
A one-for-one port gives you Tableau-in-Looker: thousands of ad hoc measures, no reuse, and the same governance problem in a new tool.
2. Inventory and triage before you write any LookML
Pull usage metadata from the incumbent tool first. For Tableau, the Metadata API gives you workbooks, datasources, and fields; for Power BI, the admin REST API (/admin/groups?$expand=reports,datasets) plus activity events does the same. Join it to view counts over the last 90 days.
Then triage every artifact into four buckets:
| Bucket | Criterion | Action |
|---|---|---|
| Retire | No views in 90 days, or an owner who has left | Delete. Do not migrate. |
| Merge | Near-duplicate of another report | Fold into one Looker dashboard with filters |
| Rebuild | Actively used, clear owner | Model in LookML, rebuild as a Look or LookML dashboard |
| Defer | Niche, heavy custom viz, or a spreadsheet in disguise | Schedule as a Looker delivery or leave in place for now |
In a typical 400-workbook estate, 50–65% lands in Retire or Merge. That number is the single biggest determinant of migration cost, which is why the inventory comes before the modeling.
3. Harvest the metric definitions
For everything in Rebuild, extract the calculated field logic into a plain spreadsheet with four columns: metric name, incumbent formula, owner, and proposed LookML definition. This document is the migration contract — it is what you review with finance, not the dashboards.
Watch for three patterns that do not translate cleanly:
- Table calculations (Tableau
WINDOW_SUM,INDEX(), Power BICALCULATEwith filter context). These are post-aggregation. In Looker they become eithertype: numbermeasures referencing other measures, a derived table that does the window function in SQL, or a table calculation in the Explore. Prefer SQL window functions in a derived table when the result needs to be filterable. - LOD expressions (
{FIXED [Customer]: SUM([Sales])}). These usually indicate a fanout problem that symmetric aggregates solve natively in Looker, or a genuine pre-aggregation that belongs in a PDT. - Row-level filters baked into the workbook. These become
access_filterplus user attributes, not hardcodedsql_always_where.
4. Model the core, not the estate
Start with one subject area — orders, or subscriptions, or tickets — and build it properly:
view: orders {
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, fiscal_year]
sql: ${TABLE}.created_at ;;
}
dimension: status {
type: string
sql: ${TABLE}.status ;;
}
measure: gross_revenue {
label: "Gross Revenue"
description: "Sum of order line totals before refunds. Source of truth: replaces Tableau [Sales $] and Power BI [Total Sales]."
type: sum
sql: ${TABLE}.line_total ;;
value_format_name: usd
}
measure: net_revenue {
label: "Net Revenue"
description: "Gross revenue less refunds. Replaces Tableau [Net Sales]."
type: number
sql: ${gross_revenue} - ${refunds} ;;
value_format_name: usd
}
}
Two habits matter here. First, write a real description on every migrated measure and name the legacy field it replaces — that mapping is what stops analysts from rebuilding the old formula by hand six months later, and it is also what makes the model usable by Gemini and the Conversational Analytics API. Second, use fiscal_year and friends from day one if the business reports on a fiscal calendar; retrofitting that after 60 dashboards exist is miserable.
5. Parity test with real numbers, automatically
The migration dies politically, not technically. It dies when a VP sees $4.1M in Looker and $4.3M in Tableau and stops trusting the project. So build the reconciliation harness before the rebuild, not after.
Export the incumbent tool's numbers for each key metric at a fixed grain (metric × month × region, say) into a warehouse table, migration.parity_baseline. Then assert against it with a LookML data test:
test: net_revenue_matches_legacy {
explore_source: orders {
column: created_month { field: orders.created_month }
column: net_revenue { field: orders.net_revenue }
column: legacy_net_revenue { field: parity_baseline.net_revenue }
filters: [orders.created_date: "12 months"]
}
assert: revenue_within_tolerance {
expression: abs(${net_revenue} - ${legacy_net_revenue}) < 1 ;;
}
}
Run looker validate / Spectacles in CI on every pull request. Now parity is a build status, not an argument. When a test fails, one of three things is true: the warehouse data differs from the extract, the legacy formula was wrong, or your LookML is wrong — and discovering that the legacy formula was wrong is extremely common. Document each resolution in the metric spreadsheet.
A note on timing: extracts are stale by definition. Always compare against an extract refreshed at a known timestamp, and filter both sides to data older than that timestamp, or you will chase phantom variances all week.
6. Rebuild content, then make the old tool read-only
Rebuild in this order: Explores → a handful of curated Looks → dashboards. Use LookML dashboards for anything that must be version-controlled and promoted between instances; use user-defined dashboards for team-owned content that will churn.
For the cutover itself:
- Run both tools in parallel for one full reporting cycle (a month-end close, usually).
- Freeze the incumbent: revoke publish rights, leave view access. New content is created in Looker only.
- Move scheduled deliveries over. This is the step teams forget — half the "users" of the old tool are actually email recipients who never log in. Recreate them as Looker schedules.
- Decommission on a published date, with the extract server snapshot archived.
7. Plan the human side
Tableau analysts are used to dragging any field anywhere. The first week in Looker feels restrictive: they cannot invent a join, and fields they expect are missing from an Explore. Budget for it.
- Run a two-hour Explore workshop per team, using their own data, not demo data.
- Publish a "where did my field go?" mapping page from the metric spreadsheet.
- Give two or three power analysts development access and a branch, with a developer reviewing their pull requests. Converting your loudest Tableau power user into your first LookML contributor is worth more than any training deck.
- Set
conditionally_filteron large Explores so an unfiltered drag-and-drop does not scan a billion rows on day one and sour the "Looker is slow" narrative.
8. A realistic schedule
For a 300–500 artifact estate with a reasonably modeled warehouse:
- Weeks 1–2: inventory, triage, metric harvest
- Weeks 3–6: core LookML model for the top two subject areas, parity harness in CI
- Weeks 7–12: content rebuild, iterating subject area by subject area
- Weeks 13–14: parallel run and cutover
- Ongoing: a named model owner, or the sprawl you just cleaned up returns within a year
If the warehouse is not well modeled — if the incumbent tool was doing the transformation work inside extracts — add a data engineering workstream in front of all of this. Looker will not rescue a schema that only ever existed inside a workbook.
Where this usually goes wrong
- Porting instead of re-modeling. The most expensive mistake, and it is invisible for about four months.
- Skipping the parity harness. Manual spot-checks do not scale past a dozen metrics and do not survive a model change.
- Migrating everything. The retire bucket is a feature. Defend it with usage data.
- No owner after go-live. A semantic layer is a product, not a project.
If you are scoping a Tableau or Power BI to Looker migration and want a second opinion on the inventory, the parity approach, or the LookML structure, get in touch — we do this work with senior Looker developers who have run it from both sides.