
Retail Data Chaos: How We Found Our Way Out ( When spreadsheets fail and databases multiply, where do you turn? )
The Problem: Drowning in Retail Data
Picture this: You're managing data for a growing retail chain. Store after store opens New York, San Francisco, Los Angeles each with its own MySQL database. Each database has the same schema: orders, customers, products, order_items. Simple enough, right?
Wrong.
Fast forward six months. Your CEO wants a single dashboard showing daily sales across all stores. Your marketing team needs customer analytics that span regions. Your finance team is manually combining spreadsheets from fifteen different databases every quarter.
The Daily Struggles
- Custom Python scripts break every time a store schema drifts
- SQL queries embedded in code strings are impossible to review or test
- No one knows which transformation ran last or if it succeeded
- Data quality issues surface only when executives spot them in reports
- Adding a new store means copying and modifying dozens of scripts
Sound familiar? This was us. We tried band-aids: more Python scripts, scheduled jobs that emailed error logs no one read, shared Google Sheets that became "sources of truth" through sheer desperation.
The Search for Something Better
We knew we needed a real solution. Not another hack, but a framework that could grow with us. We looked at the usual suspects:
Airflow and Prefect were tempting for orchestration. But they're just schedulers they didn't solve our core problem of managing SQL transformations, testing data quality, or documenting our data models.
Traditional ETL tools like SSIS and Informatica felt heavy. GUI-driven workflows, license fees, and the inability to version control our logic in Git made them feel like the wrong era's solution.
Custom Python ETL was what we already had and it was the problem, not the solution. SQL buried in string templates, no easy way to understand dependencies, and tribal knowledge locked in individual developers' heads.
Then someone mentioned dbt.
What Is dbt, Really?
Here'sthe pitch that won us over: dbt (data build tool) treats SQL transformations as code. You write SELECT statements normal SQL and dbt handles everything else:
Dependencies - Reference models with ref(), and dbt automatically figures out what to run first
Materialization - Choose whether each model becomes a view or table without writing DDL
Testing - Define data quality checks right alongside your models
Documentation - Auto-generated docs with lineage graphs showing how everything connects
But here's what sealed the deal for our retail scenario: dbt is SQL-first. Our team already knew SQL inside and out. We didn't need to learn a new language or paradigm. We just needed better tools for what we were already doing.
Why dbt Won for Multi-Tenant Retail
Our situation had a specific challenge: many source databases(one per retail store) feeding into one centralized analytics warehouse. Each store had the same schema structure, but we needed to process them dynamically
dbt's variable system made this possible. Instead of hardcoding schema names, we could parameterize them:
The Magic: We write our models once using variables like var('source_schema') and var('target_schema'). At runtime, we pass in which store to process: dbt run --vars "{source_schema: retail_store_ny, target_schema: analytics_retail}"
This meant one dbt project could handle all our stores. No copy-paste. No drift. Just clean, reusable SQL.
The Architecture That Saved Us
We adopted a three-layer structure that became our north star:
Staging Layer
One model persource table. Clean up column names, cast data types, but stay close to the source. Think of it as your data's first impression in the warehouse you want it presentable but still recognizable.
Intermediate Layer
This is where business logic lives. Join orders with customers. Enrich order items with product details. Calculate derived metrics. These models are the work horses that transform raw data into meaningful insights.
Marts Layer
The final products: clean, aggregated, analytics-ready tables. mart_daily_store_sales becomes the single source of truth for revenue reporting. mart_customer_lifetime_value powers marketing campaigns.
This structure did something magical: it made our data pipeline understandable. A new team member could look at the project structure and immediately grasp the flow from raw data to business insights.
The First Win
We started with a pilot: the orders table from our New York store. Three models: stg_orders, int_orders_enriched , and mart_daily_store_sales. With tests to ensure order IDs were unique and revenue calculations were correct.
The first successful dbt run felt like magic. Green checkmarks cascading down the terminal. Tests passing. Documentation generating automatically. Data flowing from retail_store_ny into analytics_retail with complete lineage tracked.
Then we ran it again for San Francisco. Same code, different source_schema variable. Worked perfectly.
That's when we knew we'd found our answer.
Coming Up Next
We've chosen our tool and built our foundation. But a retail data warehouse isn't complete until we solve three critical challenges: keeping data fresh, handling explosive table growth, and preserving history. In Episode 2, we'll tackle observability, incremental processing, and snapshots.
We've chosen our tool and built our foundation. But a retail data warehouse isn't complete until we solve three critical challenges: keeping data fresh, handling explosive table growth, and preserving history. In Episode 2, we'll tackle observability, incremental processing, and snapshots.
The dbt Migration Chronicles · Episode 1 of 4
Written for data teams navigating the journey from chaos to clarity

Build an Automated Image Compression Script with Sharp and SVGO
Compress images from the terminal with a Node.js script powered by Sharp and SVGO a safe, two-step workflow that keeps your site fast without bloating your repo.
Read More
The Right Way to Migrate from MySQL to AWS Aurora DSQL
Migrating a production database is one of the highest-risk changes you can make to an application. Moving from MySQL to AWS Aurora DSQL raises the stakes further...
Read More
From Memory Nightmare to Serverless: Bundling Files into a ZIP with AWS Lambda
A straightforward 'download all these files as one ZIP' request that worked perfectly on my laptop and... fell over the first day it met real production load. Here's the debugging story, the scaling options I ruled out, and why AWS Lambda was the right answer.
Read More
Clean Code vs. Overengineering: Where Should Developers Draw the Line?
Clean code reduces unnecessary complexity; overengineering invents it. A practical guide to using context, evidence, and the cost of change to know when to stop adding abstractions...
Read More
Kafka vs RabbitMQ vs AWS EventBridge: Choosing the Right Architecture Based on Business Requirements
Compare Kafka, RabbitMQ, and AWS EventBridge based on scalability, routing, event streaming, replay, infrastructure, and business requirements to choose the right architecture...
Read More
AI Integrations for QA Engineers
Learn how QA engineers can connect AI with Jira, GitHub, Slack, Notion and other tools to improve testing, bug tracking, reporting and QA productivity...
Read More
The Ultimate Guide to Amazon SES Setup with GoDaddy DNS
Learn how to set up Amazon SES with GoDaddy DNS. Complete step-by-step guide covering Easy DKIM, SPF, DMARC, custom MAIL FROM, and exiting the SES Sandbox...
Read More
AWS DevOps Agent Setup Guide with EC2
Learn how to set up AWS DevOps Agent with EC2, CloudWatch, IAM, and Agent Spaces for AI-assisted monitoring, incident investigation, and root-cause analysis...
Read More
Multi-Tenancy Patterns in DynamoDB: Silo, Pool, and Bridge Models
If you've already made the jump from a relational database to DynamoDB see our guide on moving relational data from SQL to DynamoDB...
Read MoreReady to Work With Us?
Most engagements start with a 20-minute conversation. No pitch, no pressure - just an honest discussion about what you're building and whether we're the right fit.