How-To
Data Pipeline ETL MLOps
How to Build Scalable AI Data Pipelines
Step-by-step guide to building production-ready data pipelines for machine learning.
Emma Richardson
4 min read
Data pipelines are the foundation of machine learning systems. This guide shows you how to build scalable, maintainable pipelines.
Architecture Overview
A complete data pipeline includes:
Data Sources → Ingestion → Processing → Validation → Storage → ML Model
Step 1: Define Data Requirements
Document what your model needs:
Data Requirements:
- Source: User activity logs
- Format: JSON
- Volume: 1M records/day
- Latency: Real-time
- Schema:
- user_id: string
- action: enum
- timestamp: datetime
- metadata: object
Step 2: Implement Data Ingestion
From Databases
import pandas as pd
from sqlalchemy import create_engine
# Connect to database
engine = create_engine('postgresql://user:password@localhost/dbname')
# Read data
df = pd.read_sql_query('SELECT * FROM events', engine)
From APIs
import requests
def fetch_from_api(url, params):
response = requests.get(url, params=params)
data = response.json()
return data
From Files
# Read CSV
df = pd.read_csv('data.csv')
# Read JSON
df = pd.read_json('data.jsonl', lines=True)
# Read Parquet
df = pd.read_parquet('data.parquet')
Step 3: Data Processing
Cleaning
def clean_data(df):
# Remove duplicates
df = df.drop_duplicates()
# Handle missing values
df = df.fillna(method='forward_fill')
# Remove outliers
Q1 = df['value'].quantile(0.25)
Q3 = df['value'].quantile(0.75)
IQR = Q3 - Q1
df = df[(df['value'] > (Q1 - 1.5 * IQR)) & (df['value'] < (Q3 + 1.5 * IQR))]
return df
Transformation
def transform_data(df):
# Convert types
df['timestamp'] = pd.to_datetime(df['timestamp'])
# Create features
df['hour'] = df['timestamp'].dt.hour
df['day_of_week'] = df['timestamp'].dt.dayofweek
# Normalize
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df[['value']] = scaler.fit_transform(df[['value']])
return df
Step 4: Data Validation
def validate_data(df):
# Check schema
assert set(df.columns) == EXPECTED_COLUMNS
# Check data types
for col, dtype in EXPECTED_TYPES.items():
assert df[col].dtype == dtype
# Check for missing values
assert df.isnull().sum().sum() == 0
# Check value ranges
assert (df['value'] >= MIN_VALUE).all()
assert (df['value'] <= MAX_VALUE).all()
return True
Step 5: Store Processed Data
# Store in Parquet (efficient)
df.to_parquet('processed_data.parquet')
# Store in database
df.to_sql('processed_events', engine, if_exists='append')
# Store in data warehouse
df.to_csv('s3://bucket/data/processed_data.csv')
Step 6: Orchestrate Pipeline
Using Apache Airflow:
from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime
dag = DAG(
'data_pipeline',
default_args={'owner': 'ml_team'},
schedule_interval='@daily',
start_date=datetime(2026, 1, 1)
)
ingest_task = PythonOperator(
task_id='ingest_data',
python_callable=fetch_from_source,
dag=dag
)
process_task = PythonOperator(
task_id='process_data',
python_callable=process_data,
dag=dag
)
validate_task = PythonOperator(
task_id='validate_data',
python_callable=validate_data,
dag=dag
)
ingest_task >> process_task >> validate_task
Step 7: Monitor Pipeline Health
class PipelineMonitor:
def __init__(self):
self.metrics = {}
def record_metric(self, name, value):
self.metrics[name] = value
def check_health(self):
# Check data freshness
assert age_of_data < MAX_AGE
# Check data completeness
assert completeness > MIN_COMPLETENESS
# Check data quality
assert quality_score > MIN_QUALITY
Tools and Technologies
| Tool | Purpose | Use Case |
|---|---|---|
| Airflow | Orchestration | Complex workflows |
| Prefect | Orchestration | Modern/flexible |
| dbt | Transformation | SQL-based |
| Apache Spark | Processing | Large-scale |
| Pandas | Processing | Medium-scale |
Best Practices
- Idempotency: Pipeline can be re-run safely
- Monitoring: Track pipeline health continuously
- Error Handling: Graceful failures with alerts
- Documentation: Clear data lineage and ownership
- Version Control: Track schema changes
- Testing: Validate data at each step
- Incremental Processing: Only process new data
Scalability Considerations
- Volume: Use distributed systems (Spark, Dask)
- Velocity: Stream processing (Kafka, Flink)
- Variety: Handle multiple data formats
- Cost: Use cost-effective storage (S3, GCS)
Common Pitfalls
- No monitoring: Can’t detect data quality issues
- Hard-coded paths: Breaks with environment changes
- No error handling: Pipeline fails silently
- No data versioning: Can’t reproduce results
Conclusion
Well-designed data pipelines are essential for successful ML systems. Invest in proper architecture, monitoring, and tooling to ensure reliable data flow.