Toolmingo
Guides15 min read

Data Engineer Roadmap 2025 (Complete Guide)

The complete data engineer roadmap for 2025 — SQL, Python, Spark, Kafka, dbt, Airflow, cloud data platforms, and DataOps. Know exactly what to learn and in what order.

A data engineer builds the infrastructure that moves, transforms, and stores data so analysts and data scientists can use it. This roadmap shows you exactly what to learn, in what order, and realistic timelines to go from beginner to job-ready data engineer in 2025.

At a Glance

Phase Skills Timeline
1. Foundations Linux, Git, Python basics Months 1–2
2. SQL & Databases Advanced SQL, data modeling, indexing Months 2–3
3. Python for Data pandas, PySpark, scripting ETL Months 3–4
4. Data Warehousing Snowflake, BigQuery, Redshift, star schema Months 4–5
5. ETL / ELT Pipelines batch processing, dbt, Airbyte Months 5–6
6. Orchestration Airflow, Prefect, Dagster Months 6–7
7. Big Data & Spark HDFS, Spark DataFrames, Spark SQL Months 7–8
8. Streaming Kafka, Flink, Spark Streaming Months 8–10
9. Cloud Platforms AWS/GCP/Azure data services Months 10–12
10. DataOps data quality, observability, CI/CD for pipelines Months 12–14

Phase 1 — Foundations (Months 1–2)

Before touching data pipelines, get comfortable with the command line, version control, and the basics of Python.

Linux & Shell

Data engineers spend a lot of time in terminals — SSH-ing into servers, inspecting logs, running scripts. Learn:

  • File system navigation (ls, cd, find, grep)
  • File manipulation (cp, mv, rm, cat, head, tail)
  • Process management (ps, kill, top, nohup)
  • Text processing (awk, sed, cut, sort, uniq, wc)
  • Shell scripting (for loops, conditionals, cron for scheduling)

Git

All pipeline code lives in Git. Learn:

git init / clone / add / commit / push / pull
git branch / checkout / merge / rebase
git log --oneline --graph

Use Conventional Commits (feat:, fix:, chore:) from day one.

Python Basics

You don't need to be a Python expert yet — just comfortable:

  • Variables, types, control flow, functions
  • List/dict comprehensions
  • File I/O (open, csv, json)
  • Exception handling (try/except)
  • Virtual environments (venv, pip, or uv)

Phase 2 — SQL & Databases (Months 2–3)

SQL is the most important skill for a data engineer. You need to be fluent, not just functional.

Core SQL

-- Aggregations + GROUP BY
SELECT customer_id, SUM(amount) AS total, COUNT(*) AS orders
FROM orders
WHERE created_at >= '2025-01-01'
GROUP BY customer_id
HAVING SUM(amount) > 1000
ORDER BY total DESC;

-- JOINs
SELECT o.id, c.name, o.amount
FROM orders o
JOIN customers c ON o.customer_id = c.id
LEFT JOIN refunds r ON o.id = r.order_id
WHERE r.id IS NULL; -- orders with no refund

Window Functions (essential for analytics)

-- Ranking + running total
SELECT
  date,
  revenue,
  SUM(revenue) OVER (ORDER BY date) AS running_total,
  LAG(revenue, 1) OVER (ORDER BY date) AS prev_day,
  ROUND(100.0 * (revenue - LAG(revenue, 1) OVER (ORDER BY date))
        / LAG(revenue, 1) OVER (ORDER BY date), 2) AS day_over_day_pct
FROM daily_revenue;

Data Modeling

Model Description Use Case
Star Schema fact table + dimension tables data warehouses, BI
Snowflake Schema normalized dimensions complex hierarchies
One Big Table (OBT) denormalized wide table analytics, Spark
Data Vault hub/link/satellite auditable, historical

Database Internals to Understand

  • Indexes (B-tree, hash, composite) — know when to add them
  • Query execution plans (EXPLAIN ANALYZE in PostgreSQL)
  • ACID transactions and isolation levels
  • N+1 problem and how to avoid it

Phase 3 — Python for Data Engineering (Months 3–4)

Now go deeper with Python focused on data tasks.

pandas for Data Manipulation

import pandas as pd

df = pd.read_csv("orders.csv", parse_dates=["created_at"])

# Clean
df = df.dropna(subset=["customer_id"])
df["amount"] = pd.to_numeric(df["amount"], errors="coerce").fillna(0)

# Transform
monthly = (
    df.groupby(df["created_at"].dt.to_period("M"))
    .agg(revenue=("amount", "sum"), orders=("id", "count"))
    .reset_index()
)
print(monthly)

Writing ETL Scripts

A simple extract-transform-load script pattern:

import psycopg2, requests, json

def extract():
    resp = requests.get("https://api.example.com/orders", timeout=30)
    resp.raise_for_status()
    return resp.json()

def transform(records):
    return [
        {
            "order_id": r["id"],
            "customer_id": r["customer"]["id"],
            "amount": float(r["total"]),
        }
        for r in records
        if r.get("status") == "completed"
    ]

def load(rows, conn_str):
    with psycopg2.connect(conn_str) as conn, conn.cursor() as cur:
        cur.executemany(
            "INSERT INTO orders (order_id, customer_id, amount) VALUES (%s, %s, %s) ON CONFLICT DO NOTHING",
            [(r["order_id"], r["customer_id"], r["amount"]) for r in rows],
        )

if __name__ == "__main__":
    raw = extract()
    clean = transform(raw)
    load(clean, "postgresql://user:pass@localhost/db")

Key Libraries

Library Purpose
pandas DataFrame manipulation
polars Rust-backed fast DataFrames
psycopg2 / sqlalchemy PostgreSQL connectivity
boto3 AWS SDK for Python
google-cloud-bigquery BigQuery client
pyspark Spark DataFrames in Python
great_expectations data quality checks
duckdb embedded OLAP SQL engine

Phase 4 — Data Warehousing (Months 4–5)

Data warehouses store analytical data at scale, separate from operational databases.

Core Concepts

  • OLTP vs OLAP: transactional (row-oriented, many small writes) vs analytical (column-oriented, large scans)
  • Columnar storage: Parquet, ORC — compress well, fast for aggregations
  • Partitioning: slice tables by date/region to prune scans
  • Clustering / Z-ordering: co-locate related rows for faster filtering

Major Data Warehouses

Platform Cloud Strengths
Snowflake AWS/GCP/Azure easy scale, time travel, data sharing
BigQuery GCP serverless, pay-per-query, ML integration
Redshift AWS Postgres-compatible, RA3 nodes
Databricks Lakehouse AWS/GCP/Azure Spark + Delta Lake, unified platform
Azure Synapse Azure integrated with Azure ecosystem
DuckDB local / embedded zero-config, fast, great for dev

Star Schema Example

-- Dimension table
CREATE TABLE dim_customer (
    customer_sk   SERIAL PRIMARY KEY,
    customer_id   TEXT NOT NULL,
    name          TEXT,
    country       TEXT,
    valid_from    DATE,
    valid_to      DATE
);

-- Fact table
CREATE TABLE fact_orders (
    order_id      TEXT PRIMARY KEY,
    customer_sk   INT REFERENCES dim_customer(customer_sk),
    date_sk       INT,          -- FK to dim_date
    product_sk    INT,          -- FK to dim_product
    quantity      INT,
    amount        NUMERIC(12,2)
);

Phase 5 — ETL / ELT with dbt (Months 5–6)

Modern data engineering favors ELT (extract → load raw → transform in warehouse) over traditional ETL.

dbt (Data Build Tool)

dbt lets you write SQL SELECT statements; it handles materialisation, dependency resolution, and documentation.

-- models/staging/stg_orders.sql
{{ config(materialized='view') }}

SELECT
    id            AS order_id,
    customer_id,
    CAST(total AS NUMERIC) AS amount,
    status,
    created_at::DATE AS order_date
FROM {{ source('raw', 'orders') }}
WHERE status != 'cancelled'
-- models/marts/fct_monthly_revenue.sql
{{ config(materialized='table', partition_by={'field': 'month', 'data_type': 'date'}) }}

SELECT
    DATE_TRUNC('month', order_date) AS month,
    COUNT(*)                         AS orders,
    SUM(amount)                      AS revenue
FROM {{ ref('stg_orders') }}
GROUP BY 1

dbt Core Concepts

Concept Description
Sources raw tables in warehouse
Staging models 1-to-1 clean + rename
Intermediate models joins, business logic
Mart models final analytics-ready tables
Seeds CSV lookup tables checked into Git
Tests not_null, unique, accepted_values, custom
Snapshots SCD Type 2 history tracking

Ingestion Tools

Tool Type Use Case
Airbyte open-source EL 300+ connectors
Fivetran managed EL enterprise, low maintenance
Stitch managed EL simpler, cheaper
Kafka Connect streaming EL real-time sources
custom scripts Python/SQL APIs, legacy sources

Phase 6 — Orchestration (Months 6–7)

Orchestration tools schedule and monitor data pipelines, handle retries, and manage dependencies between tasks.

Apache Airflow

Airflow uses DAGs (Directed Acyclic Graphs) written in Python.

from airflow import DAG
from airflow.operators.python import PythonOperator
from airflow.providers.postgres.operators.postgres import PostgresOperator
from datetime import datetime, timedelta

default_args = {"retries": 2, "retry_delay": timedelta(minutes=5)}

with DAG(
    "daily_orders_pipeline",
    schedule_interval="0 6 * * *",   # 06:00 UTC daily
    start_date=datetime(2025, 1, 1),
    catchup=False,
    default_args=default_args,
) as dag:

    extract = PythonOperator(
        task_id="extract_orders",
        python_callable=extract_orders_from_api,
    )

    load = PostgresOperator(
        task_id="load_to_staging",
        sql="sql/load_staging_orders.sql",
        postgres_conn_id="warehouse",
    )

    transform = BashOperator(
        task_id="dbt_run",
        bash_command="dbt run --select marts.fct_monthly_revenue",
    )

    extract >> load >> transform

Orchestration Comparison

Tool Language Strengths Best For
Airflow Python mature, large ecosystem complex DAGs, enterprise
Prefect Python modern API, dynamic workflows developer-friendly
Dagster Python asset-centric, strong typing data asset management
dbt Cloud SQL built-in scheduling for dbt dbt-first shops
Mage Python UI-first, Jupyter-like quick start, small teams

Phase 7 — Big Data & Apache Spark (Months 7–8)

When data exceeds a single machine (typically 10GB+), you need distributed computing.

Spark Concepts

  • RDD → low-level distributed collection (avoid in new code)
  • DataFrame → structured, SQL-like API (use this)
  • Lazy evaluation → transformations build a plan; actions execute it
  • Partitions → units of parallelism; aim for 100–200MB per partition

PySpark Example

from pyspark.sql import SparkSession
from pyspark.sql import functions as F

spark = SparkSession.builder.appName("OrdersETL").getOrCreate()

df = spark.read.parquet("s3://data-lake/raw/orders/")

result = (
    df
    .filter(F.col("status") == "completed")
    .withColumn("month", F.date_trunc("month", F.col("created_at")))
    .groupBy("month")
    .agg(
        F.sum("amount").alias("revenue"),
        F.count("*").alias("orders"),
    )
    .orderBy("month")
)

result.write.mode("overwrite").parquet("s3://data-lake/processed/monthly_revenue/")

Spark Performance Tips

Tip Why
Avoid collect() on large DataFrames OOM on driver
Use filter / select early reduce shuffle data
broadcast() small dimension tables avoid shuffle joins
Persist intermediate DFs (cache()) avoid recomputation
Tune spark.sql.shuffle.partitions default 200 is often wrong
Use Delta Lake / Iceberg ACID, schema evolution, time travel

Phase 8 — Streaming Data (Months 8–10)

Batch processing is great for nightly jobs, but many use cases need near-real-time data.

Apache Kafka

Kafka is the industry-standard event streaming platform.

Concept Description
Topic named stream of events
Partition parallel lane within a topic
Producer app that writes events
Consumer app that reads events
Consumer Group multiple consumers sharing work
Offset position of a message in a partition
Broker Kafka server node
Replication copies of partitions across brokers
from confluent_kafka import Producer, Consumer

# Producer
p = Producer({"bootstrap.servers": "localhost:9092"})
p.produce("orders", key="order-123", value='{"amount": 99.99}')
p.flush()

# Consumer
c = Consumer({
    "bootstrap.servers": "localhost:9092",
    "group.id": "order-processor",
    "auto.offset.reset": "earliest",
})
c.subscribe(["orders"])
while True:
    msg = c.poll(1.0)
    if msg and not msg.error():
        print(f"Received: {msg.value().decode()}")

Streaming Frameworks

Framework Language Strengths
Spark Structured Streaming Python/Scala unified batch+streaming, SQL API
Apache Flink Python/Java/Scala true stream, low latency, stateful
Kafka Streams Java lightweight, no cluster needed
AWS Kinesis Data Analytics SQL managed Flink on AWS

Phase 9 — Cloud Data Platforms (Months 10–12)

Most data engineering happens in the cloud. Pick one cloud to go deep on first.

AWS Data Stack

Service Role
S3 data lake storage (Parquet, Delta)
Glue serverless Spark ETL + Data Catalog
Redshift columnar data warehouse
Kinesis managed event streaming
EMR managed Hadoop/Spark cluster
Athena serverless SQL on S3
Lake Formation data lake governance
Step Functions pipeline orchestration

GCP Data Stack

Service Role
Cloud Storage (GCS) data lake
BigQuery serverless data warehouse
Dataflow managed Apache Beam (batch + streaming)
Pub/Sub managed event messaging
Dataproc managed Spark/Hadoop
Looker BI and data exploration

Azure Data Stack

Service Role
Azure Data Lake Storage (ADLS) data lake
Azure Synapse Analytics warehouse + Spark
Azure Data Factory ETL orchestration
Event Hubs managed Kafka-compatible streaming
HDInsight managed Hadoop/Spark
Azure Databricks Spark + Delta Lake

File Formats to Know

Format Type Strengths
Parquet columnar fast reads, great compression
ORC columnar Hive-optimised, good compression
Avro row schema evolution, Kafka serialisation
Delta Lake columnar + ACID Spark-native, time travel, upserts
Apache Iceberg columnar + ACID multi-engine, hidden partitioning

Phase 10 — DataOps (Months 12–14)

DataOps applies DevOps principles to data pipelines: testing, CI/CD, monitoring, and data quality.

Data Quality with Great Expectations

import great_expectations as ge

df = ge.read_csv("orders.csv")

df.expect_column_values_to_not_be_null("order_id")
df.expect_column_values_to_be_unique("order_id")
df.expect_column_values_to_be_between("amount", min_value=0, max_value=100_000)
df.expect_column_values_to_be_in_set("status", ["completed", "pending", "refunded"])

results = df.validate()
print(results["success"])  # True/False

dbt Tests

# models/staging/schema.yml
models:
  - name: stg_orders
    columns:
      - name: order_id
        tests: [not_null, unique]
      - name: amount
        tests:
          - not_null
          - dbt_utils.accepted_range:
              min_value: 0
      - name: status
        tests:
          - accepted_values:
              values: [completed, pending, refunded]

CI/CD for Data Pipelines

# .github/workflows/dbt-ci.yml
name: dbt CI
on: [pull_request]
jobs:
  dbt-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with: {python-version: "3.12"}
      - run: pip install dbt-bigquery
      - run: dbt deps
      - run: dbt compile
      - run: dbt test --select state:modified+
        env:
          DBT_ENV_PROFILES_DIR: .

Observability

Tool Purpose
Monte Carlo automated data observability
Soda Core open-source data quality checks
dbt artifacts lineage, test results
Airflow metrics DAG duration, failure rates
Prometheus + Grafana Spark / Kafka metrics
OpenLineage cross-tool data lineage

Full Technology Map

┌─────────────────────────────────────────────────────────────────┐
│                    DATA ENGINEER TECH MAP                       │
├──────────────┬──────────────────────────────────────────────────┤
│ Foundations  │ Linux · Git · Python · SQL                       │
├──────────────┼──────────────────────────────────────────────────┤
│ Storage      │ PostgreSQL · S3/GCS/ADLS · Delta/Iceberg/Parquet │
├──────────────┼──────────────────────────────────────────────────┤
│ Warehousing  │ Snowflake · BigQuery · Redshift · Databricks     │
├──────────────┼──────────────────────────────────────────────────┤
│ Ingestion    │ Airbyte · Fivetran · Kafka Connect · Custom      │
├──────────────┼──────────────────────────────────────────────────┤
│ Transform    │ dbt · PySpark · pandas · polars · SQL            │
├──────────────┼──────────────────────────────────────────────────┤
│ Orchestration│ Airflow · Prefect · Dagster · dbt Cloud          │
├──────────────┼──────────────────────────────────────────────────┤
│ Batch        │ Spark · Glue · Dataflow · EMR                    │
├──────────────┼──────────────────────────────────────────────────┤
│ Streaming    │ Kafka · Flink · Spark Streaming · Kinesis        │
├──────────────┼──────────────────────────────────────────────────┤
│ DataOps      │ Great Expectations · dbt tests · OpenLineage     │
├──────────────┼──────────────────────────────────────────────────┤
│ Cloud        │ AWS (Glue/Redshift/EMR) · GCP · Azure            │
└──────────────┴──────────────────────────────────────────────────┘

Realistic Timeline

Month Focus Milestone
1 Linux, Git, Python basics Automate a file processing task
2 Advanced SQL, data modeling Write complex window function queries
3 Python ETL scripting Build a CLI ETL script with error handling
4 Data warehouse setup Load data into Snowflake or BigQuery
5 dbt basics Transform raw → staging → mart with tests
6 Airflow DAGs Schedule a multi-step pipeline
7 Spark DataFrames Process 10GB CSV with PySpark
8 Kafka basics Produce/consume events, build a streaming pipeline
9 Cloud data services Deploy a full pipeline on AWS or GCP
10 Data quality + CI/CD Add Great Expectations + GitHub Actions
11–12 Portfolio + specialise 3 portfolio projects, pick a track
13–14 Job search Leetcode SQL, system design, interviews

Portfolio Project Ideas

Project Skills Demonstrated Complexity
GitHub Archive pipeline Python, Kafka, Spark, BigQuery Medium
E-commerce dbt project SQL, dbt models, tests, docs Low
Real-time crypto dashboard Kafka, Flink/Spark Streaming, Grafana High
Wikipedia pageview ETL Airflow, Spark, S3, Parquet Medium
Spotify listening history API ingestion, Snowflake, dbt, Looker Low–Medium
NYC Taxi data lakehouse Delta Lake, Databricks, PySpark, dbt Medium–High

Data Engineer Roles & Salary

Role Experience US Salary EU Salary
Junior Data Engineer 0–2 years $90–120k €45–65k
Data Engineer 2–4 years $120–160k €65–90k
Senior Data Engineer 4–7 years $160–200k €90–120k
Staff Data Engineer 7+ years $200–250k €110–145k
Data Engineering Manager 5+ years $180–240k €100–140k
Data Architect 8+ years $180–250k €110–150k

Common Mistakes

Mistake Better Approach
Building pipelines before understanding the data Profile raw data first (df.describe(), SELECT COUNT(*))
Not handling schema drift Use schema enforcement (dbt contract, Avro, Iceberg)
One giant DAG for everything Small, modular DAGs with clear boundaries
Writing transformations outside the warehouse Push computation to the warehouse (ELT over ETL)
Ignoring data quality until prod breaks Add dbt tests + Great Expectations from day one
Reprocessing all history when logic changes Use incremental models and Delta/Iceberg time travel
Storing secrets in code Use environment variables, AWS Secrets Manager, Vault
Not monitoring pipeline SLAs Set up Airflow SLA misses + Slack alerts

Data Engineer vs Related Roles

Dimension Data Engineer Data Scientist Analytics Engineer Data Analyst ML Engineer
Primary output pipelines, data products models, insights dbt models, metrics dashboards, reports ML systems
Main language Python, SQL, Scala Python, R SQL, dbt SQL, Excel, Python Python, C++
Typical tools Spark, Airflow, Kafka scikit-learn, pandas dbt, Looker Tableau, Power BI PyTorch, Kubernetes
Infrastructure designs data systems uses data systems bridges both consumes clean data deploys models
US salary range $120–200k $130–200k $110–170k $80–130k $150–230k

Frequently Asked Questions

Do I need a CS degree to become a data engineer?

No. Many data engineers come from software development, data analysis, or self-taught backgrounds. What matters is demonstrable skill in SQL, Python, and building pipelines. A portfolio of working projects is more persuasive than a degree.

Should I learn Scala or Python for Spark?

Start with Python (PySpark). It has a larger community, easier learning curve, and most new Spark jobs use PySpark. Learn Scala only if your employer or a specific job requires it — it offers better performance but isn't necessary to get hired.

Spark or dbt first?

dbt first. Most data engineering roles start in the warehouse layer (Snowflake/BigQuery + dbt). Spark is needed once data scale exceeds what the warehouse handles well, which may be months or years into a role.

How important is real-time/streaming?

Valuable but not required for most roles. The majority of data engineering work is batch. Get a solid batch foundation, then add streaming. Kafka is by far the most common streaming technology to know.

Which cloud should I learn first?

AWS is the largest market share and most job postings. GCP is best if you plan to use BigQuery (easiest warehouse to start with). Azure is dominant in enterprise/Microsoft shops. Pick based on the jobs you're targeting, or learn BigQuery on GCP first for its low-cost serverless model.

What SQL questions get asked in data engineering interviews?

Expect window functions (ROW_NUMBER, RANK, LAG/LEAD, running totals), complex JOINs, de-duplication, finding the Nth highest value, and writing incremental queries. Practice on real datasets — not just toy examples.

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools