Data Quality
What This Does
Implements data quality checks, schema validation, and monitoring for data pipelines and warehouses. Catches data issues before they propagate — missing values, schema drift, distribution shifts, freshness violations, and uniqueness constraints. Covers dbt tests, Great Expectations, Soda, and custom validation patterns.
Instructions
Define quality dimensions. For each dataset, establish expectations:
| Dimension |
Question |
Example Check |
| Completeness |
Are required fields populated? |
NOT NULL on critical columns |
| Uniqueness |
Are IDs truly unique? |
No duplicates in primary key |
| Validity |
Are values within acceptable ranges? |
Status in ('active', 'inactive', 'pending') |
| Consistency |
Do related values agree? |
Order total = sum of line items |
| Freshness |
Is data up to date? |
Last record within 24 hours |
| Volume |
Is the expected amount of data present? |
Row count within 20% of previous run |
| Accuracy |
Does the data match reality? |
Spot-check against source systems |
Implement with dbt tests (recommended for warehouse data).
# models/staging/stg_orders.yml
version: 2
models:
- name: stg_orders
description: Cleaned orders from raw source
columns:
- name: order_id
description: Unique order identifier
tests:
- unique
- not_null
- name: customer_id
description: Foreign key to customers
tests:
- not_null
- relationships:
to: ref('stg_customers')
field: customer_id
- name: status
description: Order status
tests:
- accepted_values:
values: ['pending', 'active', 'completed', 'cancelled']
- name: total_amount
description: Order total in cents
tests:
- not_null
- dbt_utils.expression_is_true:
expression: ">= 0"
-- tests/assert_orders_freshness.sql
-- Custom test: fail if no orders in the last 24 hours
select count(*) as failures
from {{ ref('stg_orders') }}
having max(created_at) < current_timestamp - interval '24 hours'
-- tests/assert_revenue_not_anomalous.sql
-- Custom test: fail if daily revenue deviates > 50% from 7-day average
with daily as (
select
date_trunc('day', order_date) as day,
sum(total_amount) as revenue
from {{ ref('stg_orders') }}
where order_date >= current_date - interval '8 days'
group by 1
),
stats as (
select
avg(revenue) as avg_revenue,
stddev(revenue) as std_revenue
from daily
where day < current_date
)
select count(*) as failures
from daily, stats
where daily.day = current_date
and abs(daily.revenue - stats.avg_revenue) > stats.avg_revenue * 0.5
Implement with Great Expectations (for Python pipelines).
import great_expectations as gx
context = gx.get_context()
# Define expectations
suite = context.add_expectation_suite("orders_quality")
validator = context.get_validator(
batch_request=batch_request,
expectation_suite_name="orders_quality"
)
# Column-level expectations
validator.expect_column_values_to_not_be_null("order_id")
validator.expect_column_values_to_be_unique("order_id")
validator.expect_column_values_to_be_in_set(
"status", ["pending", "active", "completed", "cancelled"]
)
validator.expect_column_values_to_be_between(
"total_amount", min_value=0, max_value=1000000
)
# Table-level expectations
validator.expect_table_row_count_to_be_between(
min_value=1000, max_value=100000
)
# Run validation
results = validator.validate()
Schema enforcement. Catch schema drift before it breaks pipelines:
# dbt source freshness + schema tests
sources:
- name: raw
database: raw_db
freshness:
warn_after: {count: 12, period: hour}
error_after: {count: 24, period: hour}
loaded_at_field: _loaded_at
tables:
- name: orders
columns:
- name: id
tests:
- not_null
- unique
Data quality monitoring dashboard. Track over time:
- Test pass/fail rates per model
- Freshness SLA adherence
- Row count anomalies
- Null rate trends per column
- Schema change events
Set up alerting. When quality checks fail:
- Critical failures (uniqueness, freshness): block pipeline, alert immediately
- Warning failures (volume anomalies): alert but continue
- Info failures (minor validation): log for review
- Route alerts to Slack/PagerDuty based on severity
Output Format
# Data Quality Setup: {Dataset/Pipeline}
## Quality Rules
| Rule | Dimension | Severity | Column/Table |
|------|-----------|----------|-------------|
| {description} | {completeness/uniqueness/etc.} | {critical/warning/info} | {target} |
## dbt Tests
{YAML configuration for model tests}
## Custom Tests
{SQL or Python test definitions}
## Freshness SLAs
| Source | Warn After | Error After |
|--------|-----------|-------------|
| {source} | {duration} | {duration} |
## Monitoring
{Dashboard queries and alerting configuration}
## Incident Response
{What to do when quality checks fail}
Tips
- Start with the basics: not_null, unique, and accepted_values catch most real-world issues
- dbt tests run as SQL queries — they're fast and integrate naturally with warehouse workflows
- Freshness checks are the highest-ROI quality check — stale data causes the most business impact
- Volume anomaly detection (row count vs expected) catches upstream failures that freshness misses
- Don't block pipelines on warnings — alert and continue. Block only on critical failures.
- Schema contracts (dbt contracts) enforce column types and prevent schema drift in production
- Great Expectations generates data documentation automatically — useful for data governance
1---2name: data-quality3description: Data validation, schema enforcement, quality monitoring, and anomaly detection for data pipelines and warehouses.4---56# Data Quality78## What This Does910Implements data quality checks, schema validation, and monitoring for data pipelines and warehouses. Catches data issues before they propagate — missing values, schema drift, distribution shifts, freshness violations, and uniqueness constraints. Covers dbt tests, Great Expectations, Soda, and custom validation patterns.1112## Instructions13141. **Define quality dimensions.** For each dataset, establish expectations:1516 | Dimension | Question | Example Check |17 |-----------|----------|---------------|18 | Completeness | Are required fields populated? | `NOT NULL` on critical columns |19 | Uniqueness | Are IDs truly unique? | No duplicates in primary key |20 | Validity | Are values within acceptable ranges? | Status in ('active', 'inactive', 'pending') |21 | Consistency | Do related values agree? | Order total = sum of line items |22 | Freshness | Is data up to date? | Last record within 24 hours |23 | Volume | Is the expected amount of data present? | Row count within 20% of previous run |24 | Accuracy | Does the data match reality? | Spot-check against source systems |25262. **Implement with dbt tests (recommended for warehouse data).**2728 ```yaml29 # models/staging/stg_orders.yml30 version: 23132 models:33 - name: stg_orders34 description: Cleaned orders from raw source35 columns:36 - name: order_id37 description: Unique order identifier38 tests:39 - unique40 - not_null4142 - name: customer_id43 description: Foreign key to customers44 tests:45 - not_null46 - relationships:47 to: ref('stg_customers')48 field: customer_id4950 - name: status51 description: Order status52 tests:53 - accepted_values:54 values: ['pending', 'active', 'completed', 'cancelled']5556 - name: total_amount57 description: Order total in cents58 tests:59 - not_null60 - dbt_utils.expression_is_true:61 expression: ">= 0"62 ```6364 ```sql65 -- tests/assert_orders_freshness.sql66 -- Custom test: fail if no orders in the last 24 hours67 select count(*) as failures68 from {{ ref('stg_orders') }}69 having max(created_at) < current_timestamp - interval '24 hours'70 ```7172 ```sql73 -- tests/assert_revenue_not_anomalous.sql74 -- Custom test: fail if daily revenue deviates > 50% from 7-day average75 with daily as (76 select77 date_trunc('day', order_date) as day,78 sum(total_amount) as revenue79 from {{ ref('stg_orders') }}80 where order_date >= current_date - interval '8 days'81 group by 182 ),83 stats as (84 select85 avg(revenue) as avg_revenue,86 stddev(revenue) as std_revenue87 from daily88 where day < current_date89 )90 select count(*) as failures91 from daily, stats92 where daily.day = current_date93 and abs(daily.revenue - stats.avg_revenue) > stats.avg_revenue * 0.594 ```95963. **Implement with Great Expectations (for Python pipelines).**9798 ```python99 import great_expectations as gx100101 context = gx.get_context()102103 # Define expectations104 suite = context.add_expectation_suite("orders_quality")105106 validator = context.get_validator(107 batch_request=batch_request,108 expectation_suite_name="orders_quality"109 )110111 # Column-level expectations112 validator.expect_column_values_to_not_be_null("order_id")113 validator.expect_column_values_to_be_unique("order_id")114 validator.expect_column_values_to_be_in_set(115 "status", ["pending", "active", "completed", "cancelled"]116 )117 validator.expect_column_values_to_be_between(118 "total_amount", min_value=0, max_value=1000000119 )120121 # Table-level expectations122 validator.expect_table_row_count_to_be_between(123 min_value=1000, max_value=100000124 )125126 # Run validation127 results = validator.validate()128 ```1291304. **Schema enforcement.** Catch schema drift before it breaks pipelines:131 ```yaml132 # dbt source freshness + schema tests133 sources:134 - name: raw135 database: raw_db136 freshness:137 warn_after: {count: 12, period: hour}138 error_after: {count: 24, period: hour}139 loaded_at_field: _loaded_at140 tables:141 - name: orders142 columns:143 - name: id144 tests:145 - not_null146 - unique147 ```1481495. **Data quality monitoring dashboard.** Track over time:150 - Test pass/fail rates per model151 - Freshness SLA adherence152 - Row count anomalies153 - Null rate trends per column154 - Schema change events1551566. **Set up alerting.** When quality checks fail:157 - Critical failures (uniqueness, freshness): block pipeline, alert immediately158 - Warning failures (volume anomalies): alert but continue159 - Info failures (minor validation): log for review160 - Route alerts to Slack/PagerDuty based on severity161162## Output Format163164```markdown165# Data Quality Setup: {Dataset/Pipeline}166167## Quality Rules168| Rule | Dimension | Severity | Column/Table |169|------|-----------|----------|-------------|170| {description} | {completeness/uniqueness/etc.} | {critical/warning/info} | {target} |171172## dbt Tests173{YAML configuration for model tests}174175## Custom Tests176{SQL or Python test definitions}177178## Freshness SLAs179| Source | Warn After | Error After |180|--------|-----------|-------------|181| {source} | {duration} | {duration} |182183## Monitoring184{Dashboard queries and alerting configuration}185186## Incident Response187{What to do when quality checks fail}188```189190## Tips191192- Start with the basics: not_null, unique, and accepted_values catch most real-world issues193- dbt tests run as SQL queries — they're fast and integrate naturally with warehouse workflows194- Freshness checks are the highest-ROI quality check — stale data causes the most business impact195- Volume anomaly detection (row count vs expected) catches upstream failures that freshness misses196- Don't block pipelines on warnings — alert and continue. Block only on critical failures.197- Schema contracts (dbt contracts) enforce column types and prevent schema drift in production198- Great Expectations generates data documentation automatically — useful for data governance