Data Quality with Great Expectations

Data EngineeringAnalytics
Share on LinkedIn

Bad data reaching the CEO dashboard hurts more than a failed Airflow task nobody notices until Monday. Great Expectations gives data teams vocabulary for "this column should look like X" — executable, documented, and shareable with people who don't read SQL.

Core concepts

Term Meaning
Expectation Single assertion (expect_column_values_to_not_be_null)
Expectation suite Collection of expectations for a dataset
Batch Data slice validated (table, query result, file)
Checkpoint Runs a suite against a batch with actions
Data Docs Generated HTML site showing results and profiling
import great_expectations as gx

context = gx.get_context()

suite = context.suites.add(
    gx.ExpectationSuite(name="orders_suite")
)
suite.add_expectation(
    gx.expectations.ExpectColumnValuesToNotBeNull(column="order_id")
)
suite.add_expectation(
    gx.expectations.ExpectColumnValuesToBeBetween(
        column="total_cents", min_value=0, max_value=10_000_000
    )
)

Suites live in git — review expectation changes like code.

Checkpoints in CI and production

checkpoint = context.checkpoints.add(
    gx.Checkpoint(
        name="orders_daily",
        validations=[
            {
                "batch_request": {
                    "datasource_name": "snowflake",
                    "data_asset_name": "analytics.fct_orders",
                },
                "expectation_suite_name": "orders_suite",
            }
        ],
        actions=[
            {"name": "store_validation_result"},
            {"name": "update_data_docs"},
        ],
    )
)
result = checkpoint.run()
if not result.success:
    raise RuntimeError("Orders validation failed")

Wire checkpoint failure to PagerDuty for gold-tier assets; Slack warning for silver.

Profiling to bootstrap suites

GX profilers scan sample batches and suggest expectations — uniqueness ratios, value ranges, regex patterns for emails. Don't accept blindly; analysts confirm business rules. Profiling catches schema drift early when a new enum value appears.

Run profiling on staging after source schema changes; diff suggested expectations in PR.

Custom expectations

When built-ins aren't enough:

from great_expectations.expectations import BatchExpectation

class ExpectOrderTotalMatchesLines(BatchExpectation):
    metric_dependencies = ("column.sum",)
    # validate sum(line_items) == order_total per order_id

Custom expectations package domain logic — "refund amount never exceeds original charge" — reusable across checkpoints.

GX + dbt together

Reasonable split:

Orchestrate GX checkpoint before dbt run in Dagster/Airflow. If raw fails, skip warehouse spend.

GX 1.0+ integrates with Fluent API and cloud-hosted GX; self-hosted OSS core remains viable for many teams.

Operational maturity

Track validation success rate over time — flapping expectations indicate bad thresholds, not bad data. Version suites; tie checkpoint names to SLAs in catalog metadata.

Avoid expectation sprawl: 200 weak checks dilute signal. Tier blocking vs warning:

# Conceptual policy
gold_tables:
  blocking: [not_null on keys, referential integrity, row count bounds]
silver_tables:
  blocking: [not_null on keys]
  warning: [distribution drift]

Common pitfalls

Testing production directly without sampling on huge tables — use LIMIT batches or warehouse sampling. Expectations on volatile columns without wide bounds. No owner for fixing failures — route alerts to dataset owner from catalog.

Expectation tiers and SLAs

Not every table deserves the same rigor. Tier expectations by business impact:

Tier Examples Blocking expectations Alert channel
Gold Revenue facts, payment ledger Keys, referential integrity, row count bounds, business rules PagerDuty
Silver Product analytics, user events Keys, not-null on critical columns Slack #data-alerts
Bronze Raw landing, vendor dumps Schema presence, row count > 0 Email digest

Gold failures halt downstream pipelines. Silver failures warn but allow dbt to proceed with flagged models. Bronze failures log for investigation.

Document tier in your data catalog — dataset owners know their SLA without asking.

Schema drift detection

The most valuable expectations aren't business rules — they're schema change detectors:

# Auto-generated from last successful profile
suite.add_expectation(
    ExpectTableColumnCountToEqual(value=42)
)
suite.add_expectation(
    ExpectColumnToExist(column="customer_id")
)
suite.add_expectation(
    ExpectColumnValuesToBeInSet(
        column="status",
        value_set=["pending", "confirmed", "shipped", "delivered"],
    )
)

When a source adds status = 'cancelled' or drops a column, expectations fail before bad data propagates through five dbt models. Run schema expectations on raw landing zone immediately after ingest.

Integration with orchestration

Wire GX into your pipeline as a gate, not an afterthought:

# Airflow DAG pattern
with DAG("daily_orders") as dag:
    ingest = PythonOperator(task_id="ingest", ...)
    validate_raw = PythonOperator(
        task_id="validate_raw",
        python_callable=run_gx_checkpoint,
        op_kwargs={"checkpoint": "raw_orders_checkpoint"},
    )
    dbt_run = BashOperator(task_id="dbt_run", ...)
    validate_mart = PythonOperator(
        task_id="validate_mart",
        python_callable=run_gx_checkpoint,
        op_kwargs={"checkpoint": "fct_orders_checkpoint"},
    )

    ingest >> validate_raw >> dbt_run >> validate_mart

If validate_raw fails, dbt never runs — you don't transform garbage. If validate_mart fails, BI refresh is blocked but raw data is intact for debugging.

Trending validation results over time

Single pass/fail is noisy. Store validation results and trend:

GX Cloud and self-hosted metadata stores support this. Review monthly with dataset owners — adjust bounds or fix upstream.

Failure modes

Production checklist

Resources

Frequently asked questions

What does Great Expectations do?

Great Expectations (GX) lets you define expectations — assertions about your data like 'column email must be unique' or 'order_total must be between 0 and 100000'. Checkpoints run suites against batches and produce pass/fail results plus HTML Data Docs for stakeholders.

How is Great Expectations different from dbt tests?

dbt tests run in-warehouse on models during dbt build — tight integration, SQL-native. GX supports multiple execution engines, profiling-driven expectation generation, richer documentation, and validation of raw landing-zone files before dbt touches them. Many teams use both: GX on ingest, dbt on transforms.

Where should data quality checks run in the pipeline?

As close to the failure point as possible — validate raw ingest before propagation, validate marts before BI refresh. Fail fast on blocking issues; warn on drift. Store results in a metadata store for trending, not just pass/fail logs.

Hiring a senior Android / Flutter engineer?

I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.

Get in touch →