Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Design and build production-grade data pipelines, ETL/ELT workflows, data warehouses, and streaming architectures. Use when the user asks about data pipelines, ETL/ELT, dbt models, Airflow DAGs, Kafka, data warehouses (Snowflake, BigQuery, Redshift), data quality, schema design for analytics, event streaming, or says "build a pipeline", "transform this data", or "set up a data warehouse".
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 115% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 175% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 186% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 206% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 212% | 0% |
Approach every data pipeline as a senior engineer who has been paged at 3am because a downstream dashboard showed zero rows. Data pipelines fail silently. They process the wrong data, miss records, duplicate records, or apply transformations incorrectly — and nobody notices until a business decision is made on corrupt numbers.
Your job is not to move data from A to B. Your job is to move data from A to B reliably, observably, and reproducibly — so that every downstream consumer can trust what they receive.
Before designing any pipeline:
| Pattern | When to use | When to avoid | |---------|------------|---------------| | Batch ETL | Daily/hourly reports, historical loads, large volume transforms, cost-sensitive workloads | When freshness < 15 min is required | | ELT (load raw, transform in warehouse) | When the warehouse has compute power (BigQuery, Snowflake, Redshift), source data is complex to transform before loading | When raw data contains PII that must not land in the warehouse | | Streaming (Kafka, Kinesis, Pub/Sub) | Real-time dashboards, fraud detection, event-driven microservices, continuous ML scoring | When batch is sufficient — streaming adds operational complexity | | CDC (Change Data Capture) | Syncing operational databases to analytics without full re-scans | Source DB doesn't support binlog/WAL, or change volume is too high | | Lambda architecture | When both real-time and historical accuracy are needed | Most cases — it is complex to maintain two processing paths | | Kappa architecture | Real-time-first, reprocess from log when needed | When the event log cannot be replayed |
Rules:
pythondef extract_orders(source_client, watermark: datetime) -> Iterator[dict]: """ Extract orders modified after watermark. Yields records one page at a time to avoid memory issues. """ cursor = None while True: response = source_client.get_orders( modified_after=watermark.isoformat(), cursor=cursor, page_size=500, ) for record in response.data: validate_schema(record, ORDER_SCHEMA) # fail fast on schema mismatch yield record if not response.has_more: break cursor = response.next_cursor
Rules:
pythondef transform_order(raw: dict) -> dict: """ Apply business rules to raw order records. Business rules: - Amount stored as integer cents (prevents floating point rounding errors) - status mapped to internal enum (source uses legacy codes: 'O'=open, 'C'=closed) - customer_id prefixed with 'cust_' for consistency with user service IDs """ return { "id": raw["order_id"], "customer_id": f"cust_{raw['customer_id']}", "amount_cents": round(float(raw["amount"]) * 100), # float->cents "status": ORDER_STATUS_MAP[raw["status_code"]], # documented mapping "created_at": parse_iso8601(raw["created_at"]), "updated_at": parse_iso8601(raw["updated_at"]), } # Every transformation has a corresponding test def test_transform_order_converts_amount_to_cents(): raw = create_raw_order(amount="19.99") result = transform_order(raw) assert result["amount_cents"] == 1999 def test_transform_order_maps_legacy_status_code(): raw = create_raw_order(status_code="O") result = transform_order(raw) assert result["status"] == "OPEN"
Rules:
sql-- Atomic swap pattern: load to staging, validate, swap BEGIN; -- 1. Load new data into staging INSERT INTO orders_staging SELECT * FROM orders_new_batch; -- 2. Run quality checks -- (run in application layer; rollback if any fail) -- 3. Upsert from staging to production INSERT INTO orders (id, customer_id, amount_cents, status, created_at, updated_at) SELECT id, customer_id, amount_cents, status, created_at, updated_at FROM orders_staging ON CONFLICT (id) DO UPDATE SET amount_cents = EXCLUDED.amount_cents, status = EXCLUDED.status, updated_at = EXCLUDED.updated_at; -- 4. Truncate staging TRUNCATE orders_staging; COMMIT;
dbt_project/
├── models/
│ ├── staging/ # 1:1 with source tables, minimal transform, rename columns
│ │ └── stg_orders.sql
│ ├── intermediate/ # Business logic joins, not exposed to end users
│ │ └── int_orders_enriched.sql
│ └── marts/ # Final analytics tables exposed to BI tools
│ ├── finance/
│ │ └── fct_revenue.sql
│ └── product/
│ └── fct_user_activity.sql
├── tests/
│ └── generic/ # Custom generic tests
├── macros/ # Reusable SQL macros
├── seeds/ # Static reference data CSVs
└── sources/ # Source declarations
└── sources.ymlsql-- models/staging/stg_orders.sql -- Staging model: 1:1 with source table, only renames and type casts {{ config( materialized = 'view', tags = ['staging', 'orders'] ) }} with source as ( select * from {{ source('ecommerce', 'raw_orders') }} ), renamed as ( select order_id as order_id, customer_id as customer_id, cast(amount as numeric) as amount, status_code as status_code, cast(created_at as timestamp) as created_at from source ) select * from renamed
yaml# models/staging/schema.yml version: 2 models: - name: stg_orders description: "Staged orders from the ecommerce source system" columns: - name: order_id description: "Unique order identifier" tests: - unique - not_null - name: customer_id tests: - not_null - relationships: to: ref('stg_customers') field: customer_id - name: status_code tests: - accepted_values: values: ['O', 'C', 'P', 'R'] - name: amount tests: - not_null - dbt_utils.accepted_range: min_value: 0
dbt rules:
unique and not_null tests on primary keysref() for inter-model dependencies — never hardcode schema.tablesource() for raw source tables — never hardcode source schemadescription — required, not optionalpythonfrom airflow import DAG from airflow.operators.python import PythonOperator from airflow.utils.dates import days_ago from datetime import timedelta # DAG-level defaults — applied to all tasks unless overridden DEFAULT_ARGS = { "owner": "data-engineering", "depends_on_past": False, # Don't block on previous run failures "retries": 3, # Retry transient failures "retry_delay": timedelta(minutes=5), "retry_exponential_backoff": True, "email_on_failure": True, "email": ["data-alerts@company.com"], } with DAG( dag_id="orders_pipeline", description="Extract orders from source, load to warehouse, transform with dbt", schedule_interval="0 * * * *", # Hourly start_date=days_ago(1), catchup=False, # Don't backfill missed runs automatically default_args=DEFAULT_ARGS, tags=["orders", "finance"], ) as dag: extract = PythonOperator( task_id="extract_orders", python_callable=extract_orders_task, # Each task is idempotent — safe to retry ) load = PythonOperator( task_id="load_to_staging", python_callable=load_orders_task, ) quality_check = PythonOperator( task_id="run_data_quality_checks", python_callable=run_quality_checks_task, ) transform = BashOperator( task_id="dbt_transform", bash_command="dbt run --select tag:orders --target prod", ) # Task dependencies define the DAG extract >> load >> quality_check >> transform
Airflow rules:
catchup=False by default — explicit backfill only when neededretries and retry_delay on every taskData quality checks run at three points in every pipeline:
pythonclass DataQualityCheck: """Run data quality checks against a dataframe. Fail loudly.""" def check_no_nulls(self, df, column: str): null_count = df[column].isnull().sum() assert null_count == 0, f"Column {column} has {null_count} nulls — expected 0" def check_unique(self, df, column: str): dupe_count = df[column].duplicated().sum() assert dupe_count == 0, f"Column {column} has {dupe_count} duplicates — expected unique" def check_row_count(self, df, min_rows: int, max_rows: int = None): count = len(df) assert count >= min_rows, f"Got {count} rows, expected at least {min_rows}" if max_rows: assert count <= max_rows, f"Got {count} rows, expected at most {max_rows}" def check_no_future_dates(self, df, column: str): future = df[df[column] > pd.Timestamp.now()] assert len(future) == 0, f"Column {column} has {len(future)} future dates" def check_referential_integrity(self, df, fk_col: str, ref_df, pk_col: str): orphans = ~df[fk_col].isin(ref_df[pk_col]) count = orphans.sum() assert count == 0, f"{count} records in {fk_col} have no match in reference"
Quality checks that must exist on every pipeline:
pythonfrom confluent_kafka import Consumer, KafkaError import json def consume_events(topic: str, group_id: str, processor): """ Consume events from Kafka with at-least-once semantics. Offset is committed only after successful processing + downstream write. This means an event may be processed twice on failure — processor must be idempotent. """ consumer = Consumer({ "bootstrap.servers": KAFKA_BROKERS, "group.id": group_id, "auto.offset.reset": "earliest", "enable.auto.commit": False, # Manual commit after processing }) consumer.subscribe([topic]) while True: msg = consumer.poll(timeout=1.0) if msg is None: continue if msg.error(): if msg.error().code() == KafkaError._PARTITION_EOF: continue raise KafkaException(msg.error()) try: event = json.loads(msg.value()) validate_schema(event, EVENT_SCHEMA) # fail fast on bad schema processor(event) # must be idempotent consumer.commit(asynchronous=False) # commit only on success except SchemaValidationError as e: dead_letter(msg, reason=str(e)) # route to DLQ, don't crash consumer.commit(asynchronous=False) except Exception as e: log.error(f"Processing failed: {e}", exc_info=True) # Do not commit — message will be redelivered
Kafka rules:
sql-- Fact table: one row per event, foreign keys to dimensions, measures CREATE TABLE fct_orders ( order_id VARCHAR PRIMARY KEY, customer_key INTEGER REFERENCES dim_customers(customer_key), product_key INTEGER REFERENCES dim_products(product_key), date_key INTEGER REFERENCES dim_date(date_key), amount_cents BIGINT NOT NULL, quantity INTEGER NOT NULL, created_at TIMESTAMP NOT NULL ) PARTITION BY RANGE (created_at); -- partition by date for query performance -- Dimension table: descriptive attributes, slowly changing CREATE TABLE dim_customers ( customer_key SERIAL PRIMARY KEY, -- surrogate key customer_id VARCHAR NOT NULL, -- natural/business key name VARCHAR, email VARCHAR, tier VARCHAR, valid_from DATE NOT NULL, valid_to DATE, -- NULL = current record (SCD Type 2) is_current BOOLEAN DEFAULT TRUE );
Warehouse design rules:
Every pipeline in production must have:
max(updated_at) is older than the SLAA data pipeline is production-ready when:
Other measured skills in the registry, with their headline benchmark lift.