Data quality management is the set of checks, contracts, and processes that keep bad data from reaching the people who trust it. A pipeline that runs green every night but loads duplicated orders or null customer IDs is worse than one that fails loudly - the silent corruption gets baked into reports before anyone notices. Quality work makes the failure visible at the point it enters the warehouse.

The six dimensions

Quality is usually broken into measurable dimensions so a vague "the data looks wrong" becomes a specific, testable assertion.

DimensionQuestion it answersExample check
CompletenessAre required values present?customer_id IS NOT NULL
AccuracyDoes it match reality?order total = sum of line items
ConsistencyDo related values agree?ship_date ≥ order_date
TimelinessIs it fresh enough?max(loaded_at) within 2 hours
UniquenessAny unintended duplicates?one row per order_id
ValidityDoes it fit the allowed format/range?status in known enum

Testing in the pipeline

The checks belong inside the pipeline, not in a separate audit after the fact. Three common tools express the same dimensions in different ways:

  • dbt tests - declarative assertions in YAML next to the model, run as part of the transform step.
  • Great Expectations - Python "expectation suites" that validate a dataframe or table and emit data docs.
  • Soda - checks written in SodaCL/YAML, often pointed at the warehouse on a schedule.
# dbt schema.yml
models:
  - name: stg_orders
    columns:
      - name: order_id
        tests: [unique, not_null]
      - name: status
        tests:
          - accepted_values:
              values: ['pending','shipped','cancelled']

Quarantine vs. fail-fast, and SLAs

When a check fails you have two responses. Fail-fast stops the run so nothing bad propagates - right for the kind of breakage where partial data is dangerous (a broken revenue table). Quarantine routes the offending rows to a side table and lets the clean rows through, so one malformed record doesn't block the other 99.99%. Quarantined rows are then triaged like the dead-letter records in error handling. The choice should be explicit per check, not accidental.

Quality targets get written down as SLAs and SLOs: an SLO might be "99.5% of orders complete and valid"; the SLA is the commitment to consumers - e.g. "daily sales table fresh by 07:00, or the data team is paged." These pair with the timeliness dimension and with checks applied to CDC streams, where out-of-order events can violate consistency.

Frequently asked questions

Quarantine or fail-fast - which is the default? Fail-fast for breakage where partial loads corrupt downstream numbers; quarantine for row-level defects where the clean majority should still flow. Decide per check.

dbt tests vs. Great Expectations vs. Soda? dbt tests live with the model and run in the dbt graph; Great Expectations gives rich Python suites and data docs; Soda targets scheduled checks against the warehouse. They overlap - pick by where your transforms already live.

What's the difference between an SLA and an SLO? The SLO is the internal target (e.g. 99.5% valid rows); the SLA is the external commitment to consumers, with consequences when missed.

Why test inside the pipeline instead of auditing later? In-pipeline checks catch defects before they reach reports, and can fail or quarantine the load; a later audit only tells you the bad data already shipped.