<< All versions
Skill v1.0.0
currentAutomated scan100/100foryourhealth111-pixel/vibe-skills/data-quality-frameworks
──Details
PublishedSeptember 28, 2026 at 04:11 PM
Content Hashsha256:867330322634d54c...
Git SHAddcaa2affca9
──Files
Files (1 file, 15.6 KB)
SKILL.md15.6 KBactive
SKILL.md · 589 lines · 15.6 KB
version: "1.0.0" name: data-quality-frameworks description: Implement data quality validation with Great Expectations, dbt tests, and data contracts. Use when building data quality pipelines, implementing validation rules, or establishing data contracts.
Data Quality Frameworks
Production patterns for implementing data quality with Great Expectations, dbt tests, and data contracts to ensure reliable data pipelines.
When to Use This Skill
- Implementing data quality checks in pipelines
- Setting up Great Expectations validation
- Building comprehensive dbt test suites
- Establishing data contracts between teams
- Monitoring data quality metrics
- Automating data validation in CI/CD
Core Concepts
1. Data Quality Dimensions
| Dimension | Description | Example Check | |
|---|---|---|---|
| Completeness | No missing values | expect_column_values_to_not_be_null | |
| Uniqueness | No duplicates | expect_column_values_to_be_unique | |
| Validity | Values in expected range | expect_column_values_to_be_in_set | |
| Accuracy | Data matches reality | Cross-reference validation | |
| Consistency | No contradictions | expect_column_pair_values_A_to_be_greater_than_B | |
| Timeliness | Data is recent | expect_column_max_to_be_between |
2. Testing Pyramid for Data
/\/ \ Integration Tests (cross-table)/────\/ \ Unit Tests (single column)/────────\/ \ Schema Tests (structure)/────────────\
Quick Start
Great Expectations Setup
bash
# Installpip install great_expectations# Initialize projectgreat_expectations init# Create datasourcegreat_expectations datasource new
python
# great_expectations/checkpoints/daily_validation.ymlimport great_expectations as gx# Create contextcontext = gx.get_context()# Create expectation suitesuite = context.add_expectation_suite("orders_suite")# Add expectationssuite.add_expectation(gx.expectations.ExpectColumnValuesToNotBeNull(column="order_id"))suite.add_expectation(gx.expectations.ExpectColumnValuesToBeUnique(column="order_id"))# Validateresults = context.run_checkpoint(checkpoint_name="daily_orders")
Patterns
Pattern 1: Great Expectations Suite
python
# expectations/orders_suite.pyimport great_expectations as gxfrom great_expectations.core import ExpectationSuitefrom great_expectations.core.expectation_configuration import ExpectationConfigurationdef build_orders_suite() -> ExpectationSuite:"""Build comprehensive orders expectation suite"""suite = ExpectationSuite(expectation_suite_name="orders_suite")# Schema expectationssuite.add_expectation(ExpectationConfiguration(expectation_type="expect_table_columns_to_match_set",kwargs={"column_set": ["order_id", "customer_id", "amount", "status", "created_at"],"exact_match": False # Allow additional columns}))# Primary keysuite.add_expectation(ExpectationConfiguration(expectation_type="expect_column_values_to_not_be_null",kwargs={"column": "order_id"}))suite.add_expectation(ExpectationConfiguration(expectation_type="expect_column_values_to_be_unique",kwargs={"column": "order_id"}))# Foreign keysuite.add_expectation(ExpectationConfiguration(expectation_type="expect_column_values_to_not_be_null",kwargs={"column": "customer_id"}))# Categorical valuessuite.add_expectation(ExpectationConfiguration(expectation_type="expect_column_values_to_be_in_set",kwargs={"column": "status","value_set": ["pending", "processing", "shipped", "delivered", "cancelled"]}))# Numeric rangessuite.add_expectation(ExpectationConfiguration(expectation_type="expect_column_values_to_be_between",kwargs={"column": "amount","min_value": 0,"max_value": 100000,"strict_min": True # amount > 0}))# Date validitysuite.add_expectation(ExpectationConfiguration(expectation_type="expect_column_values_to_be_dateutil_parseable",kwargs={"column": "created_at"}))# Freshness - data should be recentsuite.add_expectation(ExpectationConfiguration(expectation_type="expect_column_max_to_be_between",kwargs={"column": "created_at","min_value": {"$PARAMETER": "now - timedelta(days=1)"},"max_value": {"$PARAMETER": "now"}}))# Row count sanitysuite.add_expectation(ExpectationConfiguration(expectation_type="expect_table_row_count_to_be_between",kwargs={"min_value": 1000, # Expect at least 1000 rows"max_value": 10000000}))# Statistical expectationssuite.add_expectation(ExpectationConfiguration(expectation_type="expect_column_mean_to_be_between",kwargs={"column": "amount","min_value": 50,"max_value": 500}))return suite
Pattern 2: Great Expectations Checkpoint
yaml
# great_expectations/checkpoints/orders_checkpoint.ymlname: orders_checkpointconfig_version: 1.0class_name: Checkpointrun_name_template: "%Y%m%d-%H%M%S-orders-validation"validations:- batch_request:datasource_name: warehousedata_connector_name: default_inferred_data_connector_namedata_asset_name: ordersdata_connector_query:index: -1 # Latest batchexpectation_suite_name: orders_suiteaction_list:- name: store_validation_resultaction:class_name: StoreValidationResultAction- name: store_evaluation_parametersaction:class_name: StoreEvaluationParametersAction- name: update_data_docsaction:class_name: UpdateDataDocsAction# Slack notification on failure- name: send_slack_notificationaction:class_name: SlackNotificationActionslack_webhook: ${SLACK_WEBHOOK}notify_on: failurerenderer:module_name: great_expectations.render.renderer.slack_rendererclass_name: SlackRenderer
python
# Run checkpointimport great_expectations as gxcontext = gx.get_context()result = context.run_checkpoint(checkpoint_name="orders_checkpoint")if not result.success:failed_expectations = [r for r in result.run_results.values()if not r.success]raise ValueError(f"Data quality check failed: {failed_expectations}")
Pattern 3: dbt Data Tests
yaml
# models/marts/core/_core__models.ymlversion: 2models:- name: fct_ordersdescription: Order fact tabletests:# Table-level tests- dbt_utils.recency:datepart: dayfield: created_atinterval: 1- dbt_utils.at_least_one- dbt_utils.expression_is_true:expression: "total_amount >= 0"columns:- name: order_iddescription: Primary keytests:- unique- not_null- name: customer_iddescription: Foreign key to dim_customerstests:- not_null- relationships:to: ref('dim_customers')field: customer_id- name: order_statustests:- accepted_values:values: ['pending', 'processing', 'shipped', 'delivered', 'cancelled']- name: total_amounttests:- not_null- dbt_utils.expression_is_true:expression: ">= 0"- name: created_attests:- not_null- dbt_utils.expression_is_true:expression: "<= current_timestamp"- name: dim_customerscolumns:- name: customer_idtests:- unique- not_null- name: emailtests:- unique- not_null# Custom regex test- dbt_utils.expression_is_true:expression: "email ~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'"
Pattern 4: Custom dbt Tests
sql
-- tests/generic/test_row_count_in_range.sql{% test row_count_in_range(model, min_count, max_count) %}with row_count as (select count(*) as cnt from {{ model }})select cntfrom row_countwhere cnt < {{ min_count }} or cnt > {{ max_count }}{% endtest %}-- Usage in schema.yml:-- tests:-- - row_count_in_range:-- min_count: 1000-- max_count: 10000000
sql
-- tests/generic/test_sequential_values.sql{% test sequential_values(model, column_name, interval=1) %}with lagged as (select{{ column_name }},lag({{ column_name }}) over (order by {{ column_name }}) as prev_valuefrom {{ model }})select *from laggedwhere {{ column_name }} - prev_value != {{ interval }}and prev_value is not null{% endtest %}
sql
-- tests/singular/assert_orders_customers_match.sql-- Singular test: specific business rulewith orders_customers as (select distinct customer_id from {{ ref('fct_orders') }}),dim_customers as (select customer_id from {{ ref('dim_customers') }}),orphaned_orders as (select o.customer_idfrom orders_customers oleft join dim_customers c using (customer_id)where c.customer_id is null)select * from orphaned_orders-- Test passes if this returns 0 rows
Pattern 5: Data Contracts
yaml
# contracts/orders_contract.yamlapiVersion: datacontract.com/v1.0.0kind: DataContractmetadata:name: ordersversion: 1.0.0owner: data-platform-teamcontact: data-team@company.cominfo:title: Orders Data Contractdescription: Contract for order event data from the ecommerce platformpurpose: Analytics, reporting, and ML featuresservers:production:type: snowflakeaccount: company.us-east-1database: ANALYTICSschema: COREterms:usage: Internal analytics onlylimitations: PII must not be exposed in downstream martsbilling: Charged per query TB scannedschema:type: objectproperties:order_id:type: stringformat: uuiddescription: Unique order identifierrequired: trueunique: truepii: falsecustomer_id:type: stringformat: uuiddescription: Customer identifierrequired: truepii: truepiiClassification: indirecttotal_amount:type: numberminimum: 0maximum: 100000description: Order total in USDcreated_at:type: stringformat: date-timedescription: Order creation timestamprequired: truestatus:type: stringenum: [pending, processing, shipped, delivered, cancelled]description: Current order statusquality:type: SodaCLspecification:checks for orders:- row_count > 0- missing_count(order_id) = 0- duplicate_count(order_id) = 0- invalid_count(status) = 0:valid values: [pending, processing, shipped, delivered, cancelled]- freshness(created_at) < 24hsla:availability: 99.9%freshness: 1 hourlatency: 5 minutes
Pattern 6: Automated Quality Pipeline
python
# quality_pipeline.pyfrom dataclasses import dataclassfrom typing import List, Dict, Anyimport great_expectations as gxfrom datetime import datetime@dataclassclass QualityResult:table: strpassed: booltotal_expectations: intfailed_expectations: intdetails: List[Dict[str, Any]]timestamp: datetimeclass DataQualityPipeline:"""Orchestrate data quality checks across tables"""def __init__(self, context: gx.DataContext):self.context = contextself.results: List[QualityResult] = []def validate_table(self, table: str, suite: str) -> QualityResult:"""Validate a single table against expectation suite"""checkpoint_config = {"name": f"{table}_validation","config_version": 1.0,"class_name": "Checkpoint","validations": [{"batch_request": {"datasource_name": "warehouse","data_asset_name": table,},"expectation_suite_name": suite,}],}result = self.context.run_checkpoint(**checkpoint_config)# Parse resultsvalidation_result = list(result.run_results.values())[0]results = validation_result.resultsfailed = [r for r in results if not r.success]return QualityResult(table=table,passed=result.success,total_expectations=len(results),failed_expectations=len(failed),details=[{"expectation": r.expectation_config.expectation_type,"success": r.success,"observed_value": r.result.get("observed_value"),} for r in results],timestamp=datetime.now())def run_all(self, tables: Dict[str, str]) -> Dict[str, QualityResult]:"""Run validation for all tables"""results = {}for table, suite in tables.items():print(f"Validating {table}...")results[table] = self.validate_table(table, suite)return resultsdef generate_report(self, results: Dict[str, QualityResult]) -> str:"""Generate quality report"""report = ["# Data Quality Report", f"Generated: {datetime.now()}", ""]total_passed = sum(1 for r in results.values() if r.passed)total_tables = len(results)report.append(f"## Summary: {total_passed}/{total_tables} tables passed")report.append("")for table, result in results.items():status = "✅" if result.passed else "❌"report.append(f"### {status} {table}")report.append(f"- Expectations: {result.total_expectations}")report.append(f"- Failed: {result.failed_expectations}")if not result.passed:report.append("- Failed checks:")for detail in result.details:if not detail["success"]:report.append(f" - {detail['expectation']}: {detail['observed_value']}")report.append("")return "\n".join(report)# Usagecontext = gx.get_context()pipeline = DataQualityPipeline(context)tables_to_validate = {"orders": "orders_suite","customers": "customers_suite","products": "products_suite",}results = pipeline.run_all(tables_to_validate)report = pipeline.generate_report(results)# Fail pipeline if any table failedif not all(r.passed for r in results.values()):print(report)raise ValueError("Data quality checks failed!")
Best Practices
Do's
- Test early - Validate source data before transformations
- Test incrementally - Add tests as you find issues
- Document expectations - Clear descriptions for each test
- Alert on failures - Integrate with monitoring
- Version contracts - Track schema changes
Don'ts
- Don't test everything - Focus on critical columns
- Don't ignore warnings - They often precede failures
- Don't skip freshness - Stale data is bad data
- Don't hardcode thresholds - Use dynamic baselines
- Don't test in isolation - Test relationships too