datawarehouse.blog logo

Kimball in Real Life: What 6 Months on a Refactored Lakehouse Taught Me

The theory held up. The surprises were somewhere else. Honest lessons from rebuilding business reports on a brand new Kimball model, from grain bugs to reconciling against legacy reports.

Pratik Mane
November 12, 2026 · 11 min read

Back in April I wrote about data warehousing fundamentals and Kimball dimensional modeling. Facts, dimensions, grain, star schemas. All the textbook stuff.

Then I went quiet for a while. Not because I stopped learning, but because I was learning too much at once.

For the last six months or so, my team has been living inside a refactor. We took an older reporting setup (a mix of a traditional database, a lot of transformation logic hidden inside report-level queries, and some "don't touch this" dataflows) and rebuilt it as a proper Kimball-style warehouse on a lakehouse. Bronze, silver, gold. Clean fact tables. Conformed dimensions. Data coming in from several different source systems.

I was in it end to end. The early deployments, improving our shared framework repo, building ingestion pipelines, and modeling the fact and dimension tables. Later, because I had worked with Power BI before, I also picked up the last mile: rebuilding business reports on top of the new model and making sure the numbers matched the old reports.

That last part sounds simple. It was not.

This post is not a tutorial. It's more like notes I wish someone had handed me on day one. I've kept it generic on purpose, so no company details, but every point here came from something that actually happened.

1. The theory held up. Honestly, better than I expected.

Let me start with the good news, because a lot of "lessons learned" posts are just complaints.

The core Kimball ideas worked. When a report was built on a fact table with a clear grain and proper dimensions, it was faster to build, easier to explain, and much easier to debug than the old version.

A few things I really started appreciating:

So if you're wondering whether Kimball is "outdated" in the lakehouse era, my answer after six months is no. The storage changed. The modeling thinking didn't need to.

The surprises were somewhere else.

2. "Matching the legacy report" is a much bigger job than it sounds

Every rebuilt report had the same acceptance test: does it match the old one?

On paper, that's fair. The business trusts the old numbers. If the new numbers are different, they won't trust the new platform.

In practice, I learned three things.

The legacy report is not always right. A few times we found a difference, dug in for hours, and realised the old report had a bug that had been there for years. A filter that dropped some rows, or a join that counted something twice. Now you have an awkward conversation: "the new number is different, and the new number is correct." You need proof for that, not just confidence.

The legacy logic is often hidden. Old reports had business rules buried inside report queries, calculated columns, or one-off transformation steps. Nobody documented them because nobody remembered adding them. Rebuilding a report meant doing a bit of archaeology first.

"Close enough" needs a definition. Is a 0.01% difference fine? What about a 2% difference on one product line that cancels out at total level? We had to agree upfront on what "matching" means, or we'd chase decimals forever.

What I do now before touching any report:

  1. Write down every filter, rule and exception the old report applies, even the weird ones.
  2. Agree with the business user on what "matching" means for this report.
  3. Accept that some differences will be explained rather than fixed, and write those explanations down.

3. Grain is where almost every bug lived

If I had to pick one word from the Kimball book that saved me the most time, it's grain.

Grain means: what does one row in this table represent? One order line? One invoice? One customer per month?

Almost every reconciliation problem I hit came down to a grain mismatch somewhere. A few patterns kept repeating.

Joining two facts at different grains. Say you have sales at order-line level and budget at product-per-month level. Join them directly and the budget gets repeated for every order line. Your budget total is suddenly 40 times bigger than it should be. Nothing errors out. The number is just quietly wrong.

Joining sales at order line grain to budget at month grain repeats the budget on every row

budget_vs_sales.sql
-- Looks harmless. Isn't.
SELECT
    s.product_key,
    SUM(s.sales_amount)  AS sales,
    SUM(b.budget_amount) AS budget   -- budget is repeated for every sales row
FROM fct_sales s
JOIN fct_budget b
  ON s.product_key = b.product_key
 AND s.month_key   = b.month_key
GROUP BY s.product_key;

The fix is to bring both facts to the same grain first, and then combine them. Or keep them separate and let the semantic layer combine them through shared dimensions. That second option is what Kimball calls drilling across, and it's usually the cleaner one.

A dimension that doesn't cover every row in the fact. We had a dimension built at a grain that skipped some combinations that did exist in the facts. Those fact rows had nowhere to point, so they fell out of any visual that used that dimension. Totals looked fine. Breakdowns didn't add up.

The report visual changing the grain. This one surprised me. The table was right, the measure was right, but the visual grouped by a column that made the measure calculate at a different level than the old report did. Same data, different answer, because of how the visual sliced it.

My habit now: whenever a number looks wrong, the first question is "what is the grain of every table and visual involved here?" It solves more than half the issues before I even open the data.

4. Small details in the source data wreck big totals

The bigger bugs were easier to find. The small ones took the longest.

A few examples, kept generic:

None of these are modeling problems. They're business rules, and Kimball can't guess them for you. The best place to handle them is once, in the transformation layer, with a clear column name. For example, a signed_amount column next to the raw amount, or an is_internal flag instead of a hidden filter.

5. Multiple source systems: conformed dimensions are harder than they look

Our platform pulls from several source systems. Different ERPs, different eras, different ideas of what a "product" or a "customer" is.

Kimball says: build conformed dimensions so that every fact shares the same customer, product and date. Great idea. Here's what that looks like in real life:

One customer dimension fed by several source systems and shared by many fact tables

What worked for us:

That last point is honestly the strongest argument for doing Kimball properly. You don't feel the benefit on day one. You feel it the day something upstream changes.

6. Bridge tables and "it depends" relationships

Textbook star schemas are clean: fact in the middle, dimensions around it, one-to-many everywhere.

Real business questions are messier. One order linked to several invoices. One invoice covering several orders. One customer belonging to more than one group.

That's where bridge tables come in, and I used more of them than I expected. Some honest notes:

Kimball is a set of good defaults, not a religion.

7. My reconciliation routine (the thing I'd actually reuse)

After doing this for many reports, I settled into a routine. It's boring, and it works.

Reconciliation routine: grand total, slice one dimension, one record, one sentence cause, fix or explain

  1. Start at the grand total. Pick one measure and one period. Do the totals match? If yes, go wider. If no, stop here.
  2. Slice one dimension at a time. By month, then by site or division, then by product group. Find where the gap lives before asking why.
  3. Go down to a single record. Pick one order or invoice that's different. Trace it from source to bronze, silver, gold, and then the report.
  4. Write down the cause in one sentence. "Credit notes from source X have the wrong sign." If you can't say it in one sentence, you haven't found it yet.
  5. Decide: fix or explain. Some gaps get fixed in the pipeline. Some get documented as expected differences. Both are valid outcomes.

Step 2 is the one people skip, and it's where most of the time is saved. Jumping straight from "total is wrong" to "let me check every row" is how you lose a whole day.

8. Things I'd do differently from day one

If I started this refactor again tomorrow:

Wrapping up

Six months in, here's my honest summary.

Kimball dimensional modeling still works, and it works well on a lakehouse. The star schema, conformed dimensions and clear grain made our reports simpler and our platform easier to change.

But the hard part of a refactor isn't the modeling. It's the translation: taking years of undocumented business rules, quirks and exceptions from the old world and making them explicit in the new one. The model gives you a clean place to put that logic. It doesn't find the logic for you.

If you're about to start a similar migration, my one piece of advice is this: respect the grain, and respect the old report, but don't blindly trust either.

I'll try to write more regularly from here. Next, I'm planning a deeper post on that reconciliation routine, with a worked example.

If you've been through something similar, I'd love to hear what surprised you.

Pratik Mane

Pratik Mane

Senior Data Engineer @ Telefonica Tech | Pune, IN

Specializing in enterprise data engineering, Microsoft Azure, Databricks, and dimensional modeling. Passionate about building scalable analytical systems and sharing practical architecture patterns.

Keep Reading