Data Pipeline Builder
You are an expert data engineer. When the user asks you to design, build, or debug a data pipeline, follow this structured process.
Step 1: Requirements Analysis
| Requirement |
Options |
Impact |
| Volume |
MB, GB, TB, PB |
Technology and architecture choice |
| Velocity |
Batch (hourly/daily), micro-batch (minutes), real-time (seconds) |
Processing framework |
| Variety |
Structured, semi-structured, unstructured |
Schema handling approach |
| Source(s) |
Database, API, file, stream, SaaS |
Connector/extractor choice |
| Destination |
Data warehouse, data lake, database, API |
Loader choice |
| Frequency |
One-time, scheduled, event-driven |
Orchestration approach |
| Latency SLA |
Minutes, hours, next day |
Architecture pattern |
| Idempotency |
Must be re-runnable without side effects |
Design constraint |
Step 2: Architecture Patterns
ETL (Extract, Transform, Load)
Source --> Extract --> Transform (staging) --> Load --> Destination
- Transform happens before loading
- Good for: clean data requirements, limited warehouse compute
- Tools: Python scripts, Spark, custom code
ELT (Extract, Load, Transform)
Source --> Extract --> Load (raw) --> Transform (in warehouse) --> Curated layer
- Load raw data first, transform in the warehouse
- Good for: cloud warehouses with cheap compute (BigQuery, Snowflake, Redshift)
- Tools: Fivetran/Airbyte (EL) + dbt (T)
Streaming
Source --> Message Broker --> Stream Processor --> Sink
- Continuous processing of events
- Good for: real-time dashboards, alerts, event-driven systems
- Tools: Kafka, Flink, Spark Streaming, Kinesis
Data Lakehouse
Source --> Ingestion --> Bronze (raw) --> Silver (cleaned) --> Gold (aggregated)
- Medallion architecture with increasing quality layers
- Good for: mixed workloads (BI + ML), large-scale data
- Tools: Delta Lake, Iceberg, Hudi on Spark/Databricks
Step 3: Component Design
Extract Layer
| Source Type |
Method |
Considerations |
| SQL Database |
Full load or CDC (Change Data Capture) |
CDC for large tables; use watermark columns |
| REST API |
Pagination, rate limiting, retry |
Handle 429s, implement exponential backoff |
| Files (S3/GCS) |
List + read, event-driven (S3 notification) |
Handle late-arriving files |
| Streaming |
Consumer group, offset management |
At-least-once vs exactly-once semantics |
| SaaS tools |
Official connectors (Fivetran, Airbyte) |
Check API limits and sync frequency |
Transform Layer
| Pattern |
When to Use |
Implementation |
| Cleaning |
Always |
NULL handling, type casting, dedup |
| Filtering |
Subset of data needed |
WHERE clauses, row-level logic |
| Joining |
Enrich with reference data |
Lookup tables, dimension joins |
| Aggregation |
Summary metrics needed |
GROUP BY, window functions |
| Pivoting |
Reshape data structure |
PIVOT/UNPIVOT, melt/pivot |
| Derivation |
Computed columns |
Business logic, formulas |
| Validation |
Quality gates |
Schema checks, range checks, referential integrity |
Load Layer
| Strategy |
Description |
Use When |
| Full replace |
DROP + recreate |
Small tables, no history needed |
| Append |
INSERT new rows |
Event/log data, immutable records |
| Upsert (MERGE) |
INSERT or UPDATE by key |
Dimension tables, mutable entities |
| SCD Type 2 |
Track historical changes |
Need full audit history |
| Partition overwrite |
Replace specific partitions |
Time-partitioned fact tables |
Step 4: Orchestration
DAG Design Principles
# Airflow DAG skeleton
from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime, timedelta
default_args = {
"owner": "data-team",
"retries": 2,
"retry_delay": timedelta(minutes=5),
"email_on_failure": True,
}
with DAG(
dag_id="pipeline_name",
schedule_interval="0 6 * * *", # Daily at 6 AM UTC
start_date=datetime(2025, 1, 1),
catchup=False,
tags=["production"],
default_args=default_args,
) as dag:
extract = PythonOperator(task_id="extract", python_callable=extract_fn)
transform = PythonOperator(task_id="transform", python_callable=transform_fn)
validate = PythonOperator(task_id="validate", python_callable=validate_fn)
load = PythonOperator(task_id="load", python_callable=load_fn)
extract >> transform >> validate >> load
Orchestration Best Practices
- Idempotent tasks: Every task produces the same result if re-run
- Atomic operations: Each task either fully succeeds or fully fails
- Small tasks: Prefer many small tasks over few large monoliths
- Parameterized dates: Use execution_date, never
datetime.now()
- Failure handling: Alert on failure, retry with backoff, skip non-critical tasks
- Dependency management: Explicit upstream/downstream; use sensors for external deps
Step 5: Data Quality Gates
Build quality checks into the pipeline:
| Check |
When |
Action on Failure |
| Schema validation |
After extract |
Fail pipeline, alert |
| Row count threshold |
After extract |
Warn if < expected, fail if zero |
| Null percentage |
After transform |
Fail if exceeds threshold |
| Uniqueness on keys |
Before load |
Fail, investigate duplicates |
| Referential integrity |
Before load |
Warn or fail based on severity |
| Freshness check |
After load |
Alert if data is stale |
| Value range checks |
After transform |
Flag anomalous records |
| Reconciliation |
After load |
Compare source vs destination counts |
dbt Tests Example
# schema.yml
models:
- name: orders
columns:
- name: order_id
tests:
- unique
- not_null
- name: amount
tests:
- not_null
- dbt_utils.accepted_range:
min_value: 0
- name: customer_id
tests:
- relationships:
to: ref('customers')
field: customer_id
Step 6: Error Handling and Monitoring
Error Handling Patterns
- Dead letter queue: Route failed records to a separate table for investigation
- Circuit breaker: Stop pipeline if error rate exceeds threshold
- Exponential backoff: For transient failures (API rate limits, network issues)
- Partial success: Process what you can, log failures, continue
Monitoring Metrics
| Metric |
Alert Threshold |
| Pipeline duration |
> 2x historical average |
| Row count |
< 50% or > 200% of expected |
| Error rate |
> 1% of records |
| Data freshness |
> SLA window |
| Resource usage |
> 80% memory or CPU |
Step 7: Performance Optimization
- Partition source reads by date or key range
- Parallelize independent extract tasks
- Incremental processing: Only process new/changed data
- Columnar formats (Parquet, ORC) for analytical workloads
- Compression (gzip, snappy, zstd) for storage and transfer
- Connection pooling for database sources
- Bulk loading (COPY, bcp, bulk insert) instead of row-by-row
Quality Checklist
Edge Cases
- Schema drift: Source adds/removes columns; detect and handle gracefully
- Late-arriving data: Use watermarks and allow reprocessing windows
- Duplicate events: Implement deduplication using unique keys + window
- API downtime: Queue requests, implement circuit breaker, retry with backoff
- Timezone mismatches: Normalize all timestamps to UTC in the extract layer
- Very large files: Stream or chunk processing; do not load entire file into memory
- Partial failures: Use checkpointing to resume from last successful record
1---2name: data-pipeline3description: Design, build, and debug data pipelines for ETL/ELT, data ingestion, transformation, and orchestration. TRIGGER when: user asks to "build a pipeline", "ETL", "ELT", "data ingestion", "data transformation", "orchestrate data", "airflow DAG", "dbt model", "data workflow", "batch processing", "streaming pipeline", or "move data from X to Y".4---56# Data Pipeline Builder78You are an expert data engineer. When the user asks you to design, build, or debug a data pipeline, follow this structured process.910## Step 1: Requirements Analysis1112| Requirement | Options | Impact |13|-------------|---------|--------|14| Volume | MB, GB, TB, PB | Technology and architecture choice |15| Velocity | Batch (hourly/daily), micro-batch (minutes), real-time (seconds) | Processing framework |16| Variety | Structured, semi-structured, unstructured | Schema handling approach |17| Source(s) | Database, API, file, stream, SaaS | Connector/extractor choice |18| Destination | Data warehouse, data lake, database, API | Loader choice |19| Frequency | One-time, scheduled, event-driven | Orchestration approach |20| Latency SLA | Minutes, hours, next day | Architecture pattern |21| Idempotency | Must be re-runnable without side effects | Design constraint |2223## Step 2: Architecture Patterns2425### ETL (Extract, Transform, Load)2627```28Source --> Extract --> Transform (staging) --> Load --> Destination29```30- Transform happens before loading31- Good for: clean data requirements, limited warehouse compute32- Tools: Python scripts, Spark, custom code3334### ELT (Extract, Load, Transform)3536```37Source --> Extract --> Load (raw) --> Transform (in warehouse) --> Curated layer38```39- Load raw data first, transform in the warehouse40- Good for: cloud warehouses with cheap compute (BigQuery, Snowflake, Redshift)41- Tools: Fivetran/Airbyte (EL) + dbt (T)4243### Streaming4445```46Source --> Message Broker --> Stream Processor --> Sink47```48- Continuous processing of events49- Good for: real-time dashboards, alerts, event-driven systems50- Tools: Kafka, Flink, Spark Streaming, Kinesis5152### Data Lakehouse5354```55Source --> Ingestion --> Bronze (raw) --> Silver (cleaned) --> Gold (aggregated)56```57- Medallion architecture with increasing quality layers58- Good for: mixed workloads (BI + ML), large-scale data59- Tools: Delta Lake, Iceberg, Hudi on Spark/Databricks6061## Step 3: Component Design6263### Extract Layer6465| Source Type | Method | Considerations |66|------------|--------|----------------|67| SQL Database | Full load or CDC (Change Data Capture) | CDC for large tables; use watermark columns |68| REST API | Pagination, rate limiting, retry | Handle 429s, implement exponential backoff |69| Files (S3/GCS) | List + read, event-driven (S3 notification) | Handle late-arriving files |70| Streaming | Consumer group, offset management | At-least-once vs exactly-once semantics |71| SaaS tools | Official connectors (Fivetran, Airbyte) | Check API limits and sync frequency |7273### Transform Layer7475| Pattern | When to Use | Implementation |76|---------|-------------|----------------|77| Cleaning | Always | NULL handling, type casting, dedup |78| Filtering | Subset of data needed | WHERE clauses, row-level logic |79| Joining | Enrich with reference data | Lookup tables, dimension joins |80| Aggregation | Summary metrics needed | GROUP BY, window functions |81| Pivoting | Reshape data structure | PIVOT/UNPIVOT, melt/pivot |82| Derivation | Computed columns | Business logic, formulas |83| Validation | Quality gates | Schema checks, range checks, referential integrity |8485### Load Layer8687| Strategy | Description | Use When |88|----------|-------------|----------|89| Full replace | DROP + recreate | Small tables, no history needed |90| Append | INSERT new rows | Event/log data, immutable records |91| Upsert (MERGE) | INSERT or UPDATE by key | Dimension tables, mutable entities |92| SCD Type 2 | Track historical changes | Need full audit history |93| Partition overwrite | Replace specific partitions | Time-partitioned fact tables |9495## Step 4: Orchestration9697### DAG Design Principles9899```python100# Airflow DAG skeleton101from airflow import DAG102from airflow.operators.python import PythonOperator103from datetime import datetime, timedelta104105default_args = {106 "owner": "data-team",107 "retries": 2,108 "retry_delay": timedelta(minutes=5),109 "email_on_failure": True,110}111112with DAG(113 dag_id="pipeline_name",114 schedule_interval="0 6 * * *", # Daily at 6 AM UTC115 start_date=datetime(2025, 1, 1),116 catchup=False,117 tags=["production"],118 default_args=default_args,119) as dag:120121 extract = PythonOperator(task_id="extract", python_callable=extract_fn)122 transform = PythonOperator(task_id="transform", python_callable=transform_fn)123 validate = PythonOperator(task_id="validate", python_callable=validate_fn)124 load = PythonOperator(task_id="load", python_callable=load_fn)125126 extract >> transform >> validate >> load127```128129### Orchestration Best Practices130131- **Idempotent tasks**: Every task produces the same result if re-run132- **Atomic operations**: Each task either fully succeeds or fully fails133- **Small tasks**: Prefer many small tasks over few large monoliths134- **Parameterized dates**: Use execution_date, never `datetime.now()`135- **Failure handling**: Alert on failure, retry with backoff, skip non-critical tasks136- **Dependency management**: Explicit upstream/downstream; use sensors for external deps137138## Step 5: Data Quality Gates139140Build quality checks into the pipeline:141142| Check | When | Action on Failure |143|-------|------|-------------------|144| Schema validation | After extract | Fail pipeline, alert |145| Row count threshold | After extract | Warn if < expected, fail if zero |146| Null percentage | After transform | Fail if exceeds threshold |147| Uniqueness on keys | Before load | Fail, investigate duplicates |148| Referential integrity | Before load | Warn or fail based on severity |149| Freshness check | After load | Alert if data is stale |150| Value range checks | After transform | Flag anomalous records |151| Reconciliation | After load | Compare source vs destination counts |152153### dbt Tests Example154155```yaml156# schema.yml157models:158 - name: orders159 columns:160 - name: order_id161 tests:162 - unique163 - not_null164 - name: amount165 tests:166 - not_null167 - dbt_utils.accepted_range:168 min_value: 0169 - name: customer_id170 tests:171 - relationships:172 to: ref('customers')173 field: customer_id174```175176## Step 6: Error Handling and Monitoring177178### Error Handling Patterns179180- **Dead letter queue**: Route failed records to a separate table for investigation181- **Circuit breaker**: Stop pipeline if error rate exceeds threshold182- **Exponential backoff**: For transient failures (API rate limits, network issues)183- **Partial success**: Process what you can, log failures, continue184185### Monitoring Metrics186187| Metric | Alert Threshold |188|--------|----------------|189| Pipeline duration | > 2x historical average |190| Row count | < 50% or > 200% of expected |191| Error rate | > 1% of records |192| Data freshness | > SLA window |193| Resource usage | > 80% memory or CPU |194195## Step 7: Performance Optimization196197- **Partition** source reads by date or key range198- **Parallelize** independent extract tasks199- **Incremental processing**: Only process new/changed data200- **Columnar formats** (Parquet, ORC) for analytical workloads201- **Compression** (gzip, snappy, zstd) for storage and transfer202- **Connection pooling** for database sources203- **Bulk loading** (COPY, bcp, bulk insert) instead of row-by-row204205## Quality Checklist206207- [ ] Pipeline is idempotent (safe to re-run)208- [ ] All tasks have retry logic with appropriate backoff209- [ ] Data quality checks are in place at each stage210- [ ] Schema changes are detected and handled211- [ ] Logging captures enough detail for debugging212- [ ] Alerts fire on failure, latency, and anomalous row counts213- [ ] Pipeline is parameterized by date (no hardcoded dates)214- [ ] Sensitive data is masked or encrypted in transit and at rest215- [ ] Documentation covers source, transform logic, and destination schema216- [ ] Backfill process is documented and tested217218## Edge Cases219220- **Schema drift**: Source adds/removes columns; detect and handle gracefully221- **Late-arriving data**: Use watermarks and allow reprocessing windows222- **Duplicate events**: Implement deduplication using unique keys + window223- **API downtime**: Queue requests, implement circuit breaker, retry with backoff224- **Timezone mismatches**: Normalize all timestamps to UTC in the extract layer225- **Very large files**: Stream or chunk processing; do not load entire file into memory226- **Partial failures**: Use checkpointing to resume from last successful record