Principles of an ETL Pipeline

First lesson of the Simple ETL Pipelines course, free to read: about 15 minutes, 3 exercises and 3 quiz questions. This is the catalogue's base lesson; in the student space, it is rewritten for your profile.

Objectives

  • Describe the extract, transform and load steps and the difference between ETL and ELT
  • Structure a pipeline into testable functions
  • Explain and verify the idempotency of a process

ETL stepsETL and ELTIdempotency

Every Monday, an analyst downloads three exports, cleans them in a spreadsheet and pastes them into a tracking file. One copy-paste mistake, and the executive committee receives wrong figures. An ETL pipeline automates this work in a reliable, reproducible way.

The three steps

  • Extract: read the data where it lives (CSV files, API, production database) without modifying it.
  • Transform: clean, type, normalize, join, aggregate, compute indicators.
  • Load: write the result to the destination (data warehouse, analytical database, file).

ETL or ELT?

In ETL, you transform before loading. In ELT, you first load the raw data into the warehouse, then transform it in SQL directly inside the warehouse. ELT has become widespread with powerful cloud warehouses and tools like dbt: you keep the raw history and can replay the transformations. The principles in this course hold for both: only the steps change place.

Splitting into functions

A readable pipeline is made of small single-responsibility functions:

import io
import pandas as pd

def extraire(texte_csv):
    # reads the source without modifying anything
    return pd.read_csv(io.StringIO(texte_csv))

def transformer(df):
    # pure function: does not modify its input, returns a new DataFrame
    sortie = df.copy()
    sortie["client"] = sortie["client"].str.strip().str.lower()
    sortie = sortie[sortie["montant"] > 0]
    return sortie.groupby("client", as_index=False)["montant"].sum()

def charger(df, destination):
    # fully replaces the contents of the destination
    destination.clear()
    destination.extend(df.to_dict("records"))

source = "client,montant\n Alice ,10\nbob,5\nalice,7\nbob,-2\n"
entrepot = []                       # a list plays the role of the destination here
charger(transformer(extraire(source)), entrepot)
print(entrepot)   # [{'client': 'alice', 'montant': 17}, {'client': 'bob', 'montant': 5}]

A pure function always returns the same result for the same input and changes nothing around it. You can test it with a small DataFrame, with no database or network.

Idempotency

A pipeline will be rerun: after an outage, a fix, a backfill. It must be idempotent: rerunning it on the same data produces the same final state, with no duplicates.

charger(transformer(extraire(source)), entrepot)   # second run
print(len(entrepot))                               # still 2 rows: idempotent

If charger simply appended (extend without clear), the second run would double the data: this is one of the most frequent bugs in production. Idempotent strategies are replacement (of a table or a partition, for example one day) and upsert (insert or update based on a key), covered in lesson 5.

Parameterize rather than hard-code

The processed date, the source path or the connection are parameters: executer_pipeline(date="2026-11-09", source=..., destination=...). You can then replay any day and test with fake data.

Key takeaways

  • Extract without modifying, transform with pure functions, load idempotently.
  • ETL and ELT differ in where the transformation happens.
  • Explicit parameters; test that running twice changes nothing.

Exercises

Solutions and tests stay on the server. In the student space, your code is run and checked, and the tutor gives hints before the solution.

  1. CodeDifficulty 2Python in the browser2 hints

    A mini idempotent pipeline

    Complete transformer(df) (without modifying df: clean client with strip + lower, keep strictly positive amounts, then sum by client with as_index=False, sorted by client) and charger(df, destination) (replace the contents of the destination list with df’s records). The test code runs the pipeline twice.

    import io
    import pandas as pd
    
    def extraire(texte_csv):
        return pd.read_csv(io.StringIO(texte_csv))
    
    def transformer(df):
        pass
    
    def charger(df, destination):
        pass
    
    source = 'client,montant\n Alice ,10\nbob,5\nalice,7\nbob,-2\n'
  2. Multiple choiceDifficulty 21 hint

    ETL or ELT

    Every night, a team loads the application’s raw tables into its cloud warehouse, then builds the analytical tables with SQL queries inside the warehouse. Which approach is this?

    • ETL
    • ELT
    • Neither
    • A manual export
  3. CodeDifficulty 2Python in the browser1 hint

    Detect a non-idempotent load

    Write est_idempotent(etape, entree) that runs etape(entree, destination) twice on the same initially empty destination list and returns True if its contents are identical after the first and the second run. Two load functions are provided for testing.

    def charger_ajout(lignes, destination):
        destination.extend(lignes)
    
    def charger_remplacement(lignes, destination):
        destination[:] = list(lignes)
    
    def est_idempotent(etape, entree):
        pass

End-of-lesson quiz

  1. What does it mean for a pipeline to be idempotent?

    • It is fast
    • Rerunning it on the same data produces the same final state
    • It only reads one source
  2. What is a pure function?

    • A function with no parameters
    • A function that always returns the same result for the same input, with no side effects
    • A function written in SQL
  3. Why pass the processed date as a parameter?

    • To be able to replay any day and to test
    • To speed up the code
    • Because pandas requires it

The rest of the course

  1. Extraction: CSV, JSON and SQL Databases18 min
  2. Transformation: Cleaning, Typing and Safe Joins20 min
  3. Data Quality Checks and Quarantine18 min
  4. Loading: Upsert, Transactions and Incremental Loads20 min
  5. Orchestration, Logging and Reliability18 min

Simple ETL Pipelines · Advanced · 3 h 49 min