Why this matters
BI code reviews ensure your dashboards, semantic models, SQL models, and ETL changes are correct, reliable, and understandable. In real BI work you will:
- Review SQL/dbt model diffs that can change row counts, grain, and business logic.
- Check Power BI/Tableau measures and calculated fields for correctness under filters and time periods.
- Validate schema and column contract changes that affect downstream reports and APIs.
- Protect performance and costs by spotting inefficient queries and visuals.
- Keep governance intact: PII handling, documentation, tests, and rollout plans.
Concept explained simply
A BI code review is a short, structured check before merging a change. You look for: correctness, clarity, impact, and safety. Think of it as a pre-flight checklist for data changes.
Mental model: The Data Change Risk Pyramid
- Top (highest risk): Grain and join logic changes — can silently change metrics.
- Middle: Filters, calculations (SQL/DAX), incremental logic, data types.
- Base: Naming, docs, tests, performance, and rollout plan.
Review from top to base. If the top is wrong, nothing else matters.
What to look for in a BI code review (checklist)
- Business intent clear? What decision does this change support?
- Grain stable? Any joins/aggregations changing row counts?
- Filters/where clauses: could they drop valid rows or create bias?
- Column contracts: added/removed/renamed columns documented and communicated?
- Tests: added/updated (schema, not null, unique, relationships, metric assertions)?
- Performance: query predicates, indexes, partitions, materialization reviewed?
- Incremental models: safe backfill plan, idempotency, late-arriving data handling?
- Security/PII: masking, access, row-level security verified?
- Visuals/measures: totals vs. subtotals, filter context, time intelligence sanity checked?
- Docs and lineage: updated descriptions, impact noted, rollout and rollback steps included.
Quick PR description template you can copy
Title: [Component]: [Short business intent]
Intent: What business question this solves.
Scope: Models/Measures changed, tables touched.
Risk: Grain changes? Column contract changes? Performance impact?
Validation: Row-count/metric diffs, sample screenshots, failing/passing tests.
Rollout: Backfill plan, schedule impact, owner to monitor.
Rollback: Steps to revert safely.
Worked examples
Example 1: SQL join change (LEFT to INNER)
Diff:
-- before
SELECT c.customer_id, o.order_id, o.amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
-- after
SELECT c.customer_id, o.order_id, o.amount
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
AND o.status <> 'CANCELLED'
What to check:
- Row count for customers with no orders will drop to zero.
- Business logic: is excluding cancelled orders intended for all downstream metrics?
- Tests: add assertion that total customers remains stable if needed, and metric tests for revenue.
- Performance: predicate on orders may improve performance; verify indexes.
Approval condition: A clear note that customer-level KPIs exclude no-order customers now, with test coverage and communication to report owners.
Example 2: DAX measure adjustment
-- before
Total Sales := SUM(FactSales[Amount])
-- after
Total Sales := CALCULATE(
SUM(FactSales[Amount]),
FactSales[IsReturn] = FALSE()
)
What to check:
- Filter context: ensure returns are correctly flagged and excluded in all visuals.
- Totals vs details: verify grand total equals sum of visible categories under slicers.
- Time intelligence: confirm that month-to-date and year-to-date measures still align.
- Docs: document that returns are excluded from Total Sales.
Approval condition: screenshots or numbers before/after on a known month, plus a measure test comparing to finance baseline.
Example 3: dbt incremental model adding a new column
-- change: add discount_rate as NUMERIC and recalc net_amount
SELECT
order_id,
amount,
COALESCE(discount_rate, 0) AS discount_rate,
amount * (1 - COALESCE(discount_rate, 0)) AS net_amount
FROM {{ ref('stg_orders') }}
What to check:
- Backfill plan: full-refresh or safe backfill to populate discount_rate for historical rows.
- Data type: numeric precision/scale adequate and consistent with consumers.
- Tests: not null (if required), accepted values (0–1), and metric parity checks.
- Downstream: update any reports using net_amount.
Approval condition: documented backfill, added tests, communication to report owners, and comparison of total net_amount vs historical baseline.
How to run a good BI PR (step-by-step)
Exercises
These mirror the interactive exercises below. Do them in your own repo or a sandbox project.
Exercise 1: Review a SQL PR with potential grain change (ex1)
Instructions:
- Given a PR changing a LEFT JOIN to INNER JOIN and adding a WHERE filter, write your review comments.
- Include: grain/row-count risks, tests to add, validation queries, performance notes, and a rollback plan.
- [ ] Identify impact on row counts and customers with no orders
- [ ] Propose at least 2 automated tests
- [ ] Provide a simple before/after validation query
- [ ] Note performance considerations
- [ ] Describe rollback steps
Exercise 2: Review a DAX measure change (ex2)
Instructions:
- Given a measure that now excludes returns, list the validation plan and edge cases.
- Include screenshots or sample numbers you would capture and how you would explain the change to stakeholders.
- [ ] Verify totals vs categories under filters
- [ ] Test a month with known returns
- [ ] Compare with finance baseline
- [ ] Update measure description
- [ ] Define a post-merge monitor metric
Common mistakes and self-check
- Approving with no data diff: Always include a before/after table or metric sample.
- Ignoring grain: Ask "Did the number of rows per key change?" If yes, dig deeper.
- Untested filters: Add tests for not null, accepted values, relationships.
- Silent contract breaks: Document column changes and notify downstream owners.
- Performance blind spots: Check predicates on partition columns and limit scans.
- No rollback: Specify exact revert steps and data restore plan if backfill is involved.
Practical projects
- Create a PR template for BI with sections for intent, impact, validation, rollout, and rollback. Use it in two sample PRs.
- Build a small dbt project with one incremental model; submit a PR that changes logic and demonstrate safe backfill and tests.
- Design a Power BI (or Tableau) mock report and open a PR changing one key measure; include screenshots and a validation notebook or SQL.
Learning path
- Start: Learn basic Git flow (feature branches, PRs, reviews).
- Next: Practice SQL/data modeling reviews (joins, grain, tests).
- Then: Review semantic model/report changes (measures, filters, RLS).
- Advance: Performance reviews and cost-awareness; rollout strategies.
Who this is for
- BI Developers and Analysts maintaining dashboards and models.
- Analytics Engineers adding or changing data transformations.
- Team leads who want consistent, low-risk BI deployments.
Prerequisites
- Basic Git (clone, branch, commit, push, PR).
- Intermediate SQL; familiarity with a BI tool (Power BI/Tableau/Looker) and, optionally, dbt.
- Understanding of your team’s data tests and deployment process.
Mini challenge
Pick one recent BI change you made. Open a new PR that only improves the PR description, adds two tests, and defines a rollback plan. Ask a teammate to review it and note what changed in the quality of the discussion.
Next steps
- Adopt the checklist above in your next three PRs.
- Add one automated test per high-risk change (grain or filter) going forward.
- Run the quick test below to check your understanding. Everyone can take it; only logged-in users get saved progress.