Data Engineering & Databases · ELT/ETL

ETL vs ELT: The Decision That Shapes Your Stack

Choose based on compute, governance, and team workflow.

Reading time: ~8–12 min
Level: All levels
Updated:

“ETL vs ELT” sounds like a vocabulary question. It’s not. It’s a where-do-we-run-compute decision that affects cost, security, speed of iteration, and who owns transformations (data engineering vs analytics). This guide gives you a practical framework, a few “default” patterns, and the pitfalls that quietly make pipelines fragile.


Quickstart

If you need to choose ETL vs ELT fast, start here. These steps are designed to produce a decision you can defend in a design review (and revisit later without regret).

1) Answer the 3 questions that matter

  • Where should compute happen? source/ingest layer vs warehouse/lakehouse
  • Where must governance happen? before landing vs after landing (with strict controls)
  • Who will write transformations? engineers (Python) vs analysts (SQL/dbt) vs mixed

Your “right answer” is mostly determined by these constraints—not by what’s trendy.

2) Use a safe default (then refine)

  • Cloud warehouse + analytics team: start with ELT (raw → staging → marts)
  • Strict PII + pre-landing rules: lean ETL (transform/mask before storage)
  • Hybrid reality: do ETL for ingestion & compliance, then ELT for modeling

Most real stacks end up hybrid. The trick is making the boundary explicit.

3) Decide your “raw” contract

The biggest operational difference isn’t ETL vs ELT—it’s whether “raw” is a trusted asset or a dumping ground.

  • Define naming + partitioning (date, source, region)
  • Track lineage (source → raw → staging → marts)
  • Restrict access: raw is not a playground
  • Document what raw contains (schemas drift!)

4) Add one quality gate today

One strict check beats ten dashboards. Pick the highest-value “breaks the business” rule.

  • Uniqueness for primary keys (orders, users, events)
  • Not-null for critical fields (timestamps, IDs)
  • Freshness for tables that feed dashboards/ops
  • Row-count deltas (catch missing loads)
Practical definition

ETL is “transform before you load into the analytics store.”
ELT is “load first, transform inside the analytics store.”
Everything else is implementation detail: tooling, layers, and governance.

Overview

ETL and ELT solve the same job—turn messy source data into trustworthy analytics tables—but they do it in a different order. That order changes cost, latency, security posture, and how teams collaborate.

What this post covers

  • What ETL and ELT really mean (and what they don’t)
  • A decision framework: compute, governance, and workflow
  • Reference architectures (ETL, ELT, and hybrid)
  • Implementation steps + example snippets
  • Common mistakes that cause “pipeline drama”

Why the choice shapes your stack

What changes ETL bias ELT bias
Compute location Outside the warehouse (pipelines, Spark, jobs) Inside the warehouse/lakehouse (SQL, dbt)
Data landing Only curated/filtered data lands Raw data lands quickly (then curated)
Iteration speed Slower changes (code deploys, backfills harder) Faster changes (SQL models, easy backfills)
Governance Strong “before storage” controls Strong “after storage” controls + access policies

One more clarification: “ELT” does not mean “no engineering.” You still need orchestration, testing, access controls, cost guardrails, and incident response. ELT just shifts where the heavy lifting runs.

Core concepts

The simplest mental model is: ETL vs ELT is a “compute placement” decision. To make that decision well, you need a few shared definitions.

ETL and ELT in one sentence each

ETL (Extract → Transform → Load)

Transform data before loading it into the analytics store, usually in an ingestion pipeline (batch jobs, Spark, Python services, streaming processors).

  • Great when you must mask/filter early
  • Great when sources are huge/complex
  • Often heavier ops (jobs, clusters, deploys)

ELT (Extract → Load → Transform)

Land raw data fast, then transform it inside the warehouse/lakehouse (SQL-first workflows).

  • Great for analytics iteration speed
  • Great when the warehouse is your compute engine
  • Requires strong governance on “raw” data

The layers most teams converge on

Regardless of ETL or ELT, most healthy stacks end up with layers. Names vary, but the intent is consistent: keep raw data available for reproducibility, keep “staging” clean for reuse, and keep marts optimized for end users.

A practical layering model

Layer What it’s for Typical rules
Raw (landing) Reproducibility + audit trail + backfills Immutable-ish, access restricted, schema may drift
Staging Standardize types, dedupe, basic cleanup One model per source table, consistent naming, stable keys
Marts Business-facing metrics and dimensions Documented definitions, tested KPIs, curated access

Governance: where “raw” becomes dangerous

ELT teams sometimes discover the hard way that “raw” is not a neutral place. If raw contains PII or sensitive fields, you need a clear policy: what lands, who can read it, and how it is retained. ETL moves this policy earlier; ELT can still be safe, but only if you enforce controls in the warehouse/lakehouse.

The most common ELT failure mode

Loading everything “because we might need it” and then letting everyone query raw. That creates compliance risk, metric chaos, and surprise bills. If you do ELT, treat raw as a controlled zone.

Workflow: who writes transformations and how they ship

ETL tends to concentrate transformation logic in code (Python/Spark) owned by data engineering. ELT tends to move transformation logic into SQL models owned by analytics engineering (often with code review and CI). Your team structure matters: ELT works best when SQL models are treated like software.

The decision you’re really making

ETL vs ELT is not “which is modern.” It’s “what is our default engine for data work—pipelines or warehouse compute?” Pick the engine you can operate reliably.

Step-by-step

This guide is intentionally “tool-agnostic.” Whether you use Airflow, Dagster, Prefect, dbt, Spark, or a managed ELT tool, the decision framework stays the same.

Step 1 — Map your constraints (the part people skip)

  • Latency: daily batch, hourly, near-real-time?
  • Data volume: can the warehouse handle transforms without surprise cost?
  • Governance: can raw data land as-is, or must it be filtered/masked first?
  • Skill set: SQL-first team, Python-first team, or mixed?
  • Change frequency: will business logic change weekly (ELT-friendly) or rarely?

Step 2 — Choose a baseline architecture (ETL, ELT, or hybrid)

ELT baseline (common in modern warehouses)

  • Extract + load raw into raw schema
  • Transform in-warehouse into staging
  • Build marts for BI/metrics
  • Use code review + CI for SQL models

ETL baseline (common in regulated / heavy processing)

  • Extract into a controlled processing environment
  • Transform: mask PII, standardize, validate, enrich
  • Load only trusted outputs into the analytics store
  • Keep raw snapshots in a secure archive if required
Hybrid is normal

A clean hybrid split looks like this: ETL for ingestion policies (PII filtering, schema validation, dedupe) and ELT for analytics modeling (staging + marts). Don’t fight it—document it.

Step 3 — Define your transformation boundaries

The biggest architecture mistakes happen when “transformations” are a grab bag. Make a boundary decision: which transformations must happen before landing, and which are safe to do after landing?

A practical boundary rule

Transform type Best home Why
PII masking / tokenization ETL (pre-landing) or controlled raw zone Reduces risk exposure and access surface
Type casting, dedupe, rename ELT staging Fast iteration, easy backfills, transparent SQL
Business logic (metrics) ELT marts Versioned definitions, shared KPIs
Heavy joins / ML feature prep Depends Warehouse is great if cost is controlled; Spark is great for extreme scale

Step 4 — Implement the “ELT loop” (load fast, transform safely)

If you choose ELT, your daily reliability comes from repeatable runs, consistent environment configuration, and a habit of testing. Here’s a minimal command flow that many teams adopt when using SQL-first transformations.

# Minimal ELT run pattern (example using dbt + a warehouse adapter)
# 1) Load raw data with your ingestion tool (not shown here)
# 2) Transform + test in the warehouse

python -m venv .venv
source .venv/bin/activate
pip install dbt-core dbt-bigquery

# Initialize project once, then run daily
dbt init analytics
cd analytics

# Verify connections + config
dbt debug

# Build transformations (staging + marts)
dbt run --select tag:daily

# Quality gates (fail the job if critical tests fail)
dbt test --select tag:daily
Why this works

The “ELT loop” works when transformation code is treated like software: version control, code review, repeatable runs, and tests that break the build when the data breaks.

Step 5 — Write staging models that standardize reality

Staging is where you pay down chaos: schema drift, late arriving records, duplicates, weird timestamps, inconsistent IDs. Keep staging boring and deterministic. Push business logic to marts.

-- Example staging model (BigQuery Standard SQL style)
-- Goal: dedupe to latest record, standardize types, and keep naming consistent.

WITH src AS (
  SELECT
    *
  FROM `raw.shopify_orders`
  QUALIFY
    ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY ingested_at DESC) = 1
),
typed AS (
  SELECT
    SAFE_CAST(order_id AS INT64)            AS order_id,
    SAFE_CAST(customer_id AS INT64)         AS customer_id,
    TIMESTAMP(order_created_at)             AS created_at,
    UPPER(CAST(currency AS STRING))         AS currency,
    SAFE_CAST(total_amount AS NUMERIC)      AS total_amount,
    LOWER(CAST(status AS STRING))           AS status,
    TIMESTAMP(ingested_at)                  AS ingested_at
  FROM src
)
SELECT
  *
FROM typed
WHERE status != 'cancelled';

Staging checklist

  • One staging model per source table
  • Stable primary key (or clear surrogate key strategy)
  • Deterministic dedupe rule (latest by ingestion time)
  • Time zones decided once (and documented)

Gotcha to watch

Dedupe rules are business decisions. If “latest” is wrong for your use case, you’ll get subtle correctness bugs. Decide whether you need “latest event,” “latest update,” or “as-of” snapshots.

Step 6 — Add governance: tests, freshness, and access policy

ELT does not remove governance; it moves it. A good starting point is: freshness (is data on time?), uniqueness (are keys stable?), and not-null checks (are critical fields present?). This example shows a schema/test file pattern that scales well.

version: 2

sources:
  - name: shopify
    schema: raw
    tables:
      - name: orders
        loaded_at_field: ingested_at
        freshness:
          warn_after: {count: 6, period: hour}
          error_after: {count: 24, period: hour}
        columns:
          - name: order_id
            tests:
              - not_null
              - unique
          - name: customer_id
            tests:
              - not_null
          - name: order_created_at
            tests:
              - not_null

models:
  - name: stg_shopify_orders
    columns:
      - name: order_id
        tests:
          - not_null
          - unique
      - name: created_at
        tests:
          - not_null
Cost guardrail (don’t skip)

ELT makes it easy to run expensive queries repeatedly. Add guardrails early: schedule windows, incremental models, query limits/quotas, and review “top costly models” monthly. Otherwise, ELT feels great until the bill arrives.

Step 7 — Decide how you backfill and how you version

Pipelines break. Business logic changes. The only “safe” stack is the one that can reproduce yesterday’s numbers. That means versioning transformation code and keeping enough raw history to re-run transforms when needed.

A simple reproducibility policy

  • Store raw with partitions and retention rules (documented)
  • Tag releases of transformation code (so you can rerun v1 vs v2)
  • Write down the backfill playbook (who triggers, how long, what to validate)
  • Keep a “known good” dashboard snapshot for sanity checks

Common mistakes

Most “ETL vs ELT problems” show up as reliability, trust, or cost problems. Here are the patterns (and the fixes) that consistently move teams from “pipeline drama” to “boring and dependable.”

Mistake 1 — Treating raw as a public sandbox

If everyone queries raw, you get inconsistent metrics, compliance risk, and accidental reprocessing.

  • Fix: restrict raw access; treat staging/marts as the default query surfaces.
  • Fix: document raw retention and what lands (especially PII).

Mistake 2 — Doing business logic in ingestion

Hard-coded business rules in ETL jobs are slow to change and hard to audit.

  • Fix: keep ingestion focused on reliability + minimal normalization.
  • Fix: move metrics and semantic rules to ELT marts (versioned SQL).

Mistake 3 — No “definition of done” for data quality

Without explicit quality gates, you only learn about failures from angry dashboard users.

  • Fix: pick 5–10 critical tests (keys, not-null, freshness) and fail jobs on them.
  • Fix: add row-count anomaly checks for top tables.

Mistake 4 — Confusing “works once” with “operable”

A pipeline that runs manually is not production. It’s a demo.

  • Fix: schedule it, alert on failure, and make re-runs safe (idempotency).
  • Fix: document backfills and ownership (who responds at 2am?).

Mistake 5 — Warehouse compute with no guardrails

ELT can silently become “pay-per-mistake” if models are inefficient or re-run too often.

  • Fix: use incremental strategies, partition pruning, and scheduling windows.
  • Fix: review expensive models monthly and optimize the top offenders first.

Mistake 6 — No versioning, no lineage

If you can’t reproduce a report from last month, you don’t have analytics—you have vibes.

  • Fix: version transformation code and store raw history needed for re-runs.
  • Fix: track lineage: source → raw → staging → marts (even a simple doc helps).
A reliable rule of thumb

If a change requires a code deploy and a backfill, keep it out of ingestion unless it’s required for security/compliance. Put “meaning” (metrics) in versioned transformation layers.

FAQ

Is ELT always better in the cloud?

No. ELT is often a great default because cloud warehouses/lakehouses are strong compute engines and SQL workflows iterate fast. But if you must filter or mask data before it lands (or you have extreme preprocessing needs), ETL—or a hybrid—is a better fit.

Can I combine ETL and ELT without making a mess?

Yes—if you define the boundary. A clean hybrid is: ETL handles ingestion policies (PII masking, validation, dedupe), then ELT handles analytics modeling (staging + marts). The mess happens when business logic is split randomly across both.

Where does dbt fit in ETL vs ELT?

dbt is an ELT transformation layer. It’s designed for in-warehouse transformations (staging and marts), plus testing and documentation. It doesn’t replace ingestion; it replaces a pile of unversioned SQL scripts.

What about data lakes and lakehouse architectures?

You can do ETL or ELT on a lakehouse. The principle is the same: either transform before landing into your analytics tables, or land raw and transform in-place using lakehouse compute (SQL engines, Spark, etc.). What changes is the storage format and the access model.

How should I handle PII with ELT?

Make “raw” a controlled zone. Use strict access controls, keep a masked/curated layer for most users, and document retention. If your compliance requirements demand it, do the masking before landing (ETL) or land to an encrypted/quarantined area.

What’s the fastest way to improve an existing ETL pipeline?

Move business logic out of ingestion. Keep ingestion focused on reliability and minimal normalization, then build a transformation layer (ELT-style) for staging and marts. This usually improves iteration speed and reduces “who changed the number?” incidents.

What’s the biggest cost trap with ELT?

Re-running expensive transforms too often. Fix it with incremental processing, partitioning, and scheduling windows—then monitor the top cost queries/models and optimize the top 20%.

Cheatsheet

Use this as a fast decision and implementation checklist. If you’re in a hurry: pick the row that matches your constraints, then follow the “minimum controls” list.

Decision rules (fast)

If you need… Prefer… Because…
Fast analytics iteration (SQL-first) ELT Transforms are easy to change and backfill
Pre-landing masking/validation ETL Reduces risk; limits what lands
Both (typical reality) Hybrid ETL for policy, ELT for modeling
Heavy preprocessing at extreme scale ETL (or hybrid) Dedicated compute can be cheaper/faster than warehouse SQL

Minimum controls for ELT

  • Raw access restricted (role-based)
  • Staging layer standardized + versioned
  • 5–10 critical tests (unique, not-null, freshness)
  • Incremental models or partition-aware queries
  • Cost guardrails (schedules, quotas, reviews)

Minimum controls for ETL

  • Idempotent loads (safe reruns)
  • Schema validation + dead-letter handling
  • PII policy enforced before load
  • Backfill playbook (documented)
  • Outputs mapped to staging/marts definitions

The “boring pipeline” checklist

  • Every table has an owner (and an on-call plan if it’s critical)
  • Failures are visible (alerts), not discovered by users
  • Definitions are centralized (marts), not duplicated in dashboards
  • Backfills are safe and predictable
  • Costs are monitored and reviewed

Wrap-up

The best ETL vs ELT decision is the one your team can operate reliably. ELT is a strong default when your warehouse is a capable compute engine and your team can treat SQL transformations as software. ETL is a strong default when governance must happen before data lands or when preprocessing is heavy and specialized.

Your next actions (pick one)

  • Today: define your raw policy + add one strict data test
  • This week: implement staging → marts layering with versioned transformations
  • This month: add cost guardrails + backfill playbook + ownership for critical tables
A simple default

If you’re unsure, start with ELT + strong governance: land raw into a restricted zone, build staging and marts with tests, and keep your ingestion layer minimal. Then move only the transformations that truly require pre-landing control into ETL.

Want to go deeper? The related posts below cover transformation best practices, scalable SQL patterns, and lakehouse-style projects.

Quiz

Quick self-check (demo). This quiz is auto-generated for data / engineering / databases.

1) What is the most accurate difference between ETL and ELT?
2) Which situation most strongly favors ETL (or an ETL boundary in a hybrid stack)?
3) In a well-designed ELT stack, what is the primary role of the staging layer?
4) What is a common cost risk in ELT, and a practical mitigation?