Pipeline Anatomy: Beginner
One chart, 3 attempts to produce it
A small online kitchenware shop sells pans and knives, along with kettles, through a website that writes every purchase to a Postgres table called orders: around 6,200 rows a day, 2 million in total. The finance lead wants a chart of yesterday's revenue by product category, refreshed before she arrives. Each of the 3 attempts to give it to her exposes 1 of the 3 gaps a pipeline exists to close.
The second attempt moves the work onto a person. An analyst exports yesterday's orders as a CSV at 7:30 and converts the amount column from text to numbers. She drops the 20 or so test orders developers placed from company email addresses, then sums by category and emails the chart. The conversion and the sum are needed because orders holds 1 row per purchase with the amount stored as a string, and the chart needs 1 row per category per day with a decimal total. That's the shape gap: data is recorded in the shape that made the sale fast to write, and read in the shape that makes a question fast to answer, and the 2 shapes differ in almost every real system.
- ▸Load gap: the source system cannot absorb analytical reads without slowing the business function it exists for.
- ▸Shape gap: data is written 1 row per event and read as totals and joins, so it must be reshaped before use.
- ▸Schedule gap: the work recurs on a timer, and only a machine runs a timer without ever forgetting.
What a pipeline is
The support team runs a query at 9:00 every morning against the checkout database to count yesterday's refunds grouped by reason, then pastes the counts into a shared sheet before standup. Which gaps does this task show?
Source
A source is a system that produces data as a side effect of running the business. The shop's Postgres orders table exists so the checkout can record a sale, and the fact that finance wants a chart from it played no part in its design. Other sources: a payments API such as Stripe, a clickstream from the website, log files from a server, a SaaS tool's export endpoint. Like the orders table, every source is shaped for the operational job rather than for analysis, and none can be asked to work harder without risk to the business function it exists for. Both facts shape everything downstream. A pipeline reads from a source and never writes back to one.
Transform
Storage
Consumer
The shop's pipeline with 1 box per role. Roughly 6,200 order rows move left to right once a night, starting at 02:00, and become 8 rows per day in Snowflake.
| Role | Produces or holds | Shop example | How to recognise it |
|---|---|---|---|
| Source | Data as a by-product of business activity | Postgres orders table | Exists for an operational job; the pipeline only reads it |
| Transform | A new shape from an input shape | Python task that cleans and sums | Keeps nothing between runs |
| Storage | The result, ready to be read repeatedly | Snowflake daily_category_revenue | Chosen for the reads that will hit it |
| Consumer | A decision, a screen or an action | Looker chart read at 8:00 | The reason the pipeline exists |
A role is a position in the flow, and the same product plays different roles in different pipelines: Snowflake is storage here and a source for a pipeline that exports finance totals to an accounting system.
The shop adds a second pipeline that reads the Snowflake table daily_category_revenue every night and pushes category totals into NetSuite, the accounting system. In that second pipeline, which role does the Snowflake table play?
The shop's pipeline drawn with its raw copy made visible. The nightly run moves 1 day of orders along every arrow in roughly 22 minutes, and the dashboard reads the last arrow at 8:00.
3 questions to ask of every box
One order, traced along the arrows
| Time on 14 and 15 March | Box | What happens to order 88213 |
|---|---|---|
| 14:32, 14 Mar | orders | Written as 1 row: amount '24.99' stored as text, time in Europe/London |
| 02:03, 15 Mar | extract_orders | Read with the day's other 6,213 rows and written unchanged to Parquet |
| 02:03, 15 Mar | shop-raw | Sits in s3://shop-raw/orders/date=2024-03-14/ with its original values |
| 02:10, 15 Mar | clean_and_sum | Amount cast to 24.99, time shifted to UTC, summed into the kettles row for the day |
| 02:22, 15 Mar | daily_category_revenue | Present only as part of 1 category row: kettles, 1,088 orders, 41,208.40 |
| 08:00, 15 Mar | Revenue dashboard | Chart reads the kettles row among 240 rows for the month |
A diagram shows this flow: web_clicks (Kafka) to parse_events (Flink) to events_raw (S3) to sessionize (Spark) to sessions (BigQuery) to retention_dashboard (Looker). Which boxes still hold data after a run finishes?
An arrow runs from daily_category_revenue (Snowflake) to Revenue dashboard (Looker). What does that arrow say?
Step 1: extract, 02:00 to 02:03
The extract step reads 1 day's window from Postgres and writes the rows unchanged. It produces 6,214 rows in 1 Parquet file of 410 KB at s3://shop-raw/orders/date=2024-03-14/, and holds a connection to the checkout database for 3 minutes at the quietest hour of the day. Nothing is fixed here on purpose: amounts are still text, timestamps are still in the application's zone, and the developers' test orders are still present, because the raw copy is the one thing in the pipeline that matches what the source said that night. Postgres rows change afterwards as refunds and edits land.
| order_id | customer_email | category | amount | created_at |
|---|---|---|---|---|
| 88213 | dana@example.com | kettles | 24.99 | 2024-03-14 14:32:07 |
| 88214 | test@kitchenco.com | pans | 0.01 | 2024-03-14 14:33:50 |
| 88215 | li.wen@example.com | knives | 42.50 | 2024-03-14 14:35:12 |
| 88216 | omar@example.com | pans | 37.00 | 2024-03-14 14:36:44 |
Step 2: transform, 02:05 to 02:20
| order_date | category | orders | revenue |
|---|---|---|---|
| 2024-03-14 | pans | 1,420 | 52,318.60 |
| 2024-03-14 | kettles | 1,088 | 41,208.40 |
| 2024-03-14 | knives | 731 | 30,744.90 |
| 2024-03-14 | bakeware | 905 | 19,872.15 |
4 of the 8 rows the transform produced; the other 4 categories (boards and utensils, storage and glassware) account for the remaining 2,046 orders.
Step 3: load, 02:20 to 02:22
The load step writes the 8 rows into the Snowflake table daily_category_revenue. What it produces is a new state of that table: 8 rows for 14 March alongside every earlier day. It deletes any rows already dated 14 March before inserting, so a rerun after a failure leaves 8 rows for the day rather than 16. A month holds 240 rows and a year 2,920. The run reports success at 02:22, and the on-call engineer sees 1 line in the morning: 3 steps, 6,214 rows in, 8 rows out, 22 minutes.
Step 4: the consumer reads
On the morning of 15 March the chart shows revenue for 13 March and nothing for 14 March. daily_category_revenue holds no rows dated 14 March. The S3 folder date=2024-03-14/ holds a 410 KB Parquet file written at 02:03. Which step failed?
A query
A read replica
A cache
When the pipeline wins
| Tool | Load gap | Shape gap | Schedule gap | Ongoing cost |
|---|---|---|---|---|
| Query on a replica | Closed | For 1 run only | Open | Minutes of an analyst's time |
| Read replica | Closed | Open | Open | A second instance, seconds of lag |
| Cache or materialized view | Closed | Within 1 database | A refresh timer | Staleness equal to the refresh interval |
| Pipeline | Closed | Closed, across systems | Closed | Code, a scheduler and an on-call owner |
- ▸It repeats on a schedule, and the same question comes back with a newer window each time.
- ▸Its reads land on a system that cannot absorb them, or its output must land in a different system.
- ▸Someone downstream makes a decision from the output, so a missed run has a cost.
Four requests reached the shop's data team in the same week: the CEO's one-time order count for an investor deck, support's daily refunds by reason in a sheet before 9:00, a developer's check on whether order 88213 was charged twice, and marketing's list of 2023 kettle buyers. Which request should become a pipeline first?
- Write down which of the 3 gaps a request shows before choosing a tool.
- Point every analytical read at a replica, so the checkout database only ever serves the checkout.
- Answer a question asked once with a query, and save the query in case it is asked again.
- Build a pipeline when the work recurs, reshapes data across systems and feeds a decision.
- Build a pipeline for a one-off question; it will run nightly for nobody.
- Run reporting scans against the production database to save the cost of a replica.
- Treat a cache as a pipeline; it holds 1 answer and cannot move data to another system.
- Let a recurring report live in 1 person's calendar; the first holiday breaks it.
> A regional pharmacy chain with 140 stores runs its point-of-sale on a single SQL Server instance. The head of operations wants a 7:00 report of yesterday's prescription fills per store, the store managers want a live count of today's fills on a wall screen, and an auditor has asked once for every controlled-substance sale in the last quarter.
Why pipelines exist, the 4 roles every one is built from, how to read one, and when a query would do instead.
- Category
- Pipeline Architecture
- Difficulty
- beginner
- Duration
- 20 minutes
- Challenges
- 6 hands-on challenges
Topics covered: Why Pipelines Exist, The Four Roles in Any Pipeline, Reading a Pipeline Left to Right, A First End-to-End Pipeline, When a Pipeline Is Not Needed
Lesson Sections
- Why Pipelines Exist
One chart, 3 attempts to produce it The first attempt runs a report query straight against the checkout database. It returns the right numbers, and it scans 2 million rows on the machine that is also taking payments. Postgres serves a checkout with thousands of reads and writes of 1 row each, so a scan over 2 million rows competes for the same disk and memory. On a busy morning the report pushes checkout latency from 200 milliseconds to 2 seconds while it runs. That is the load gap: the system t
- The Four Roles in Any Pipeline
Every pipeline, from the shop's nightly copy to the systems behind a streaming service's recommendations, is built from the same 4 roles. Naming them turns any pipeline diagram into something readable, because every box in one is playing exactly 1 of them at that point in the flow. Source Transform A transform is a step that reads data in 1 shape and writes it in another. The shop's transform converts amount strings to decimals, drops test orders, moves timestamps to UTC and sums revenue by cate
- Reading a Pipeline Left to Right
A pipeline diagram is read the way data moves through it: left to right, source to consumer, along the arrows. Each arrow means the box at its head reads what the box at its tail wrote, and nothing more. Boxes with no arrow in are sources, and boxes with no arrow out are consumers. Everything between is a transform or storage, and the question that tells those 2 apart is whether the box still holds data after the run ends. This drawing has 6 boxes where the previous section's had 4, and the 4 ro
- A First End-to-End Pipeline (concepts: Batch Processing)
This section runs the shop's pipeline for 14 March from the source through to the consumer that reads the result, with 1 transform and 1 destination in between. Each step produces something that can be opened and counted, and knowing what each one produces is how a broken run is diagnosed in the morning. Step 1: extract, 02:00 to 02:03 Step 2: transform, 02:05 to 02:20 The transform reads the 6,214 raw rows and produces 8: 1 per category for the day. On the way it casts amount to a decimal, drop
- When a Pipeline Is Not Needed
The shop's data team is 2 people, and in 1 week they received 4 requests. A pipeline for each would leave them maintaining jobs whose readers have moved on, so the first decision on any request is whether a pipeline is the cheapest thing that closes the gaps it shows. Most of what looks like pipeline work is covered by 3 cheaper tools. A query A question asked once is answered by a query, run by hand, against a copy of the database rather than the checkout instance. The CEO wants the total numbe