◐ ai-teacher

🔀 Pipelines & Orchestration · Chapter 2

Anatomy of a pipeline: extract, land, model, serve

Every data pipeline you will ever build is the same four boxes. Engineers who can name the box a bug lives in fix it in ten minutes; the rest re-run everything and hope.

Study this lesson free in the app →

The basics

A data pipeline moves data from where it's created to where it's used, changing its shape on the way. Almost every pipeline has four stages: extract (pull from the source), land (store a raw copy, untouched), model (clean and reshape into useful tables) and serve (hand it to dashboards, ML models or other apps).

The problem it solves: source systems — the ride-booking Postgres, the payment gateway's API, the GPS event stream — are built to run the business, not to answer questions. Querying them directly for analytics is slow, risky (you can slow down bookings) and inconsistent. A pipeline copies the data out once and reshapes it for questions.

The mental model: a restaurant kitchen. Ingredients are bought (extract), stored in the cold room as delivered (land), prepped and cooked (model), and plated for the table (serve). If a dish tastes wrong, you check each station in order — you don't rebuy the vegetables first.

Words you must know 5

Source system
where data is born (app DB, API, event stream)
Landing zone / raw / bronze
the untouched copy
Staging
lightly cleaned, one-to-one with the source
Mart
business-ready tables for a team or use case
Idempotent
re-running gives the same result

Explained

Monday 9 am: Meghalaya Cabs' revenue dashboard shows ₹0 for Tura.

Is the source down? Did the extract skip Tura? Did the landing job write an empty file? Did a model filter it out? Is the dashboard cached?

Four stages, four places to look. Knowing the anatomy turns panic into a checklist.

The wedding caterer

A caterer for a 600-guest Khasi wedding doesn't cook at the market.

Extract: buy rice, pork, vegetables from three suppliers — on their schedule, without blocking their shop.

Land: everything goes into the store room exactly as delivered, labelled with the date and supplier. Nobody chops anything yet.

Model: wash, cut, cook — one recipe at a time, with a taster checking each pot.

Serve: plate it the way each table wants it: kids' table, elders' table, vegetarian table.

When the jadoh is too salty, you check the pot, not the market. And because the raw rice is still in the store room, you can cook it again.

Meghalaya Cabs, end to end

An orchestrator (Airflow, Dagster) runs the stages in order; data-quality checks sit between them.

Each stage, precisely

Extract — get data out without hurting the source. Full extract (small tables) or incremental (watermark or CDC). Read from a replica, page through APIs, respect rate limits.

Land — write the extract as received to cheap storage, partitioned by ingestion date, never modified. Schema drift and bad data are visible here, not hidden. This is your replay button.

Model — turn raw into trustworthy tables: cast types, dedupe, standardise ('SHG' and 'Shillong' → one city), join, apply business rules ("a completed ride has a fare > 0"). Layered as staging (1:1 with source, cleaned) → intermediate → marts (facts and dimensions). Usually SQL in the warehouse via dbt.

Serve — deliver in the shape each consumer needs: aggregated tables for BI, point lookups for apps, feature tables for ML, pushes back into operational tools (reverse ETL). Each has its own freshness SLA.

The skeleton in Python

def extract(day):
    # incremental, from a read replica — never the primary
    return replica.query(
        "SELECT * FROM rides WHERE updated_at >= %s AND updated_at < %s",
        [day, day + timedelta(days=1)])

def land(rows, day):
    # raw, untouched, partitioned by ingestion date
    path = f"s3://mc-lake/raw/rides/ingest_date={day}/part-0.json"
    s3.put(path, to_json_lines(rows))   # overwrite same path = idempotent
    return path

def model(day):
    # SQL in the warehouse: staging → marts (usually dbt)
    warehouse.run("dbt build --select +fact_rides")

def serve(day):
    refresh_dashboard("city_revenue")
    publish_features("driver_daily_stats", day)

Each stage takes the day it's processing as input and writes to a deterministic place — so any stage can be re-run alone.

Why the raw copy pays for itself

14 March. A model change maps the city code TUR to null instead of Tura. Nobody notices for 9 days.

Team A transformed in flight and stored only modelled tables. The source Postgres has since overwritten updated_at on many rides; the API only returns 7 days of history. Nine days of Tura revenue is gone for good — they reconstruct it from bank settlements over two weeks.

Team B landed raw first. They fix the mapping and re-run model and serve for 14–23 March from the raw zone. 40 minutes.

Landing raw isn't about storage. It's about being able to say "re-run from the 14th" when — not if — the logic is wrong.

At scale

Each stage scales differently. Extract is bound by the source (API rate limits, replica load) — parallelising it can take down production. Model is bound by warehouse compute. Serve is bound by consumer latency.

Contracts at each boundary. With 50 sources, the extract→land boundary needs schema checks and alerts on drift; the model→serve boundary needs tests so a broken mart never reaches a CFO.

Ownership. At scale, source teams own extract quality (data contracts), the platform team owns land and orchestration, analytics engineers own models, and each consumer owns its serve SLA.

Lineage. You need to answer "which dashboards use raw.rides?" before you change it — tools like dbt docs, OpenLineage or a catalog make that a query instead of a guess.

Gotchas

Words to know

Source systemThe operational system where data is created, such as the app database or a payment API.
Landing zoneStorage holding extracted data exactly as received, never modified.
Staging modelA lightly cleaned, typed version of one source table, one-to-one with it.
Data martBusiness-ready tables built for a team or use case.
Reverse ETLPushing modelled warehouse data back into operational tools like a CRM.
LineageThe map of which datasets feed which, used to assess the impact of changes.

In two minutes

Test yourself

1. What is the main purpose of the landing (raw) stage?

  1. Make queries faster for analysts
  2. Keep an untouched copy so any later stage can be replayed when logic changes
  3. Apply business rules early
  4. Delete duplicate records
Show the answer

Keep an untouched copy so any later stage can be replayed when logic changes — Raw data is the only thing you can't recreate once the source changes or expires. Speed and business rules belong to the model and serve stages.

2. Where should you extract rides data from to avoid hurting the booking app?

  1. The primary database during peak hours
  2. The BI dashboard's cache
  3. A read replica, or the change log via CDC
  4. Directly from drivers' phones
Show the answer

A read replica, or the change log via CDC — Heavy analytical scans on the primary compete with bookings. Replicas and CDC take load off the system that runs the business.

3. In a layered model, what is a staging model?

  1. A cleaned, typed version of one source table, kept 1:1 with it
  2. The final dashboard table
  3. A raw untouched file
  4. A table that joins every source together
Show the answer

A cleaned, typed version of one source table, kept 1:1 with it — Staging fixes types and names without changing grain. Marts come later, built on staging.

4. The dashboard shows ₹0 for Tura. The raw zone has Tura rides for that day. Where do you look next?

  1. The source database
  2. The extract job
  3. Re-run the whole pipeline from extract
  4. The model stage — a transformation or filter dropped Tura
Show the answer

The model stage — a transformation or filter dropped Tura — Raw is correct, so extract and land are fine. Walk forward: staging, then marts, then serve. Re-running everything blindly re-hammers the source and may hide the bug.

5. Why should each stage take its processing date as a parameter?

  1. So any stage can be re-run for a specific past day and produce the same result
  2. To make logs prettier
  3. Because Python requires it
  4. So the job runs faster
Show the answer

So any stage can be re-run for a specific past day and produce the same result — Deterministic inputs → deterministic outputs → safe re-runs and backfills. Wall-clock time breaks this.

6. Marketing wants each rider's lifetime spend inside their CRM tool. That's an example of…

  1. Extract
  2. Reverse ETL in the serve stage
  3. Landing
  4. Staging
Show the answer

Reverse ETL in the serve stage — Pushing modelled warehouse data back into an operational tool is reverse ETL — a serving pattern.

There are 9 questions, 3 puzzles and 5 recall cards for this lesson in the app — the recall cards come back on later days, just before you would forget.

Learn it so it stays. Read it, test yourself, then recall it on two different days. The app schedules that for you, free.

Start this lesson →