learn-web3-with-phoebe / Practitioner session 2 of 10
Learn Web3 with Phoebe · Practitioner session 2 of 10

The indexer is the product

Session 1 ended on an honest admission: raw, the rows answer almost nothing anybody asks. Closing that distance is a pipeline, and most of it is a pipeline you have built a hundred times before. Four stages, decode, normalise, enrich, aggregate, and only the first one is strange. Tonight is the shape of that pipeline, the three properties it has that no ordinary ingestion has to survive, and a measured demonstration of why two competent teams reading the same rows will publish different numbers and both be right.

🔧 Practitioner track Data engineers who would have to build this Anyone reconciling two teams' figures 45 min
0-3 · Welcome 3-17 · The four stages 17-30 · Three abnormal properties 30-45 · Build-along and Q&A
Part 0

Larkfield calls it a connector, and it is not a connector

The work between a raw record and a table somebody can query has a name in most organisations and the name is usually wrong. It gets scoped as a connector, estimated as a fortnight, and assigned to whoever is free. What it actually is, is the layer that decides what happened, and every figure Larkfield ever reports is downstream of it. Tonight is that layer taken seriously: its four stages, the one stage that carries all the judgement, and the three ways it differs from every pipeline on the team's existing roadmap.

Live = presented in the 45 minutes Self-study = the full depth, after class 🔍 Every diagram zooms

★ What you walk out with tonight. The four stages named in order, with a clear view of which one is ordinary engineering and which one is your organisation's opinion written as code. The three abnormal properties stated precisely enough to put in a design document. And a measured result from Larkfield's month: four defensible definitions of the same metric, four different numbers, and a spread you can read off the page.

LiveWhy this is the session data engineers recognise fastest4 min▶

Most of what follows is not new. Reading an immutable upstream, writing idempotent loads, handling records that arrive out of order, keeping a table restatable, testing a transformation: all of that is ordinary craft, all of it applies here unchanged, and it lives in learn-data-engineering-with-phoebe rather than being repeated tonight. If you already build pipelines you are most of the way there, which is worth saying because the surrounding conversation tends to imply otherwise.

What is left after subtracting the ordinary craft is small, and it is the whole of the difficulty. One stage with no equivalent in a normal pipeline, and three properties of the upstream that break specific habits. Tonight is that residue, stated as precisely as possible so it can be designed for rather than discovered.

Real world

Larkfield scoped its indexer as a two week connector and shipped it in nine. Seven of those weeks were not engineering. They were a series of arguments about what counted as a reward: whether a reward that was issued and never collected had happened, whether a reward moved between two of the company's own addresses was one, and whether a batch that credited forty addresses at once was one event or forty. Each argument was settled in a meeting and then written into the decoder. That is the shape of the work, and no estimate that calls it a connector will ever be right.

Self-studyThe words that keep coming back4 min▶
  • Indexer. The layer that turns raw chain records into tables somebody can query. Four stages, and the subject of tonight.
  • Decode. Turning a payload into a named event with typed fields, against the definition the deploying code published. The only stage with no ordinary equivalent.
  • Normalise. Folding several record shapes into the one table your team agreed on.
  • Enrich. Attaching what you know that the chain does not, from your own systems, clearly marked as yours.
  • Aggregate. Counts, cohorts, time series, and the figure somebody puts in a pack.
  • Retraction. The upstream withdrawing blocks whose rows you already loaded and reported. Measured in session 6.
  • Definition drift. A new contract deployed with a different shape, silently making your decode wrong.
  • Idempotency, late-arriving data, restatement. General pipeline craft, applied here unchanged. It lives in learn-data-engineering-with-phoebe.
Part 1 · covers the pipeline

Decode, normalise, enrich, aggregate

14 min live

Four stages in a fixed order, and three of them will be familiar to the point of boredom. The fourth, which is the first, is where the entire character of the thing lives. Draw the four out and the reason an indexer takes nine weeks rather than two becomes visible immediately: only one stage is engineering work, and the other three depend on it having been right.

Four stages in order, and only the first one is unfamiliar 1 · DECODE Bytes into a named event with typed fields, against a definition you chose. Wrong here and every number downstream is wrong. 2 · NORMALISE Several record shapes folded into the one table your team agreed on. Wrong here and two things that are one event stay two. 3 · ENRICH What you know that the chain does not, marked clearly as yours. Wrong here and you have invented an attribute. 4 · AGGREGATE Counts, cohorts, series, and the figure somebody puts in a pack. Wrong here and it is the ordinary kind of bug. INSIDE STAGE 1 · WHY THE DECODE HOLDS THE DOMAIN KNOWLEDGE ONE PAYLOAD bytes, identical FOUR JUDGEMENTS, NOT FOUR PARSES 1 · Which published definition do these bytes match? 2 · What is this event called in our own vocabulary? 3 · Does a failed attempt count as an event at all? 4 · Is a reward moved between two of our own addresses still a reward? TEAM A's TABLE reward_issued TEAM B's TABLE transfer Same bytes. Different tables. No bug. The decode is not a parsing step with a domain expert standing nearby. The decode is the domain knowledge, as code. Stages 2, 3 and 4 are ordinary transformation work and the craft for them already exists. Stage 1 is where your organisation's answer to "what happened" gets written down, usually by one engineer, usually in a hurry, and almost never reviewed as policy. Every reconciliation argument you will ever have with another team resolves to a difference inside that box.
🔍 Click to zoom - the four questions in the amber box have no technically correct answer. They have a house answer, and it belongs in a document.
LiveThe four stages, and which one is the product4 min▶

Decode takes a payload and produces a named event with typed fields. It needs the definition that the deploying code published, and it needs a decision about which family of records you are even looking for. That is the unfamiliar stage.

Normalise folds several record shapes into the one table your team agreed on. It is the same work as reconciling three source systems into a conformed dimension, and the same failure modes apply.

Enrich attaches what you know and the chain does not: your own product's mapping of an address to a campaign, your own labels, your own categories. This is the only stage where private data enters, and every field it adds should be marked as yours rather than the chain's, because a reader six months later will assume every column came from the source.

Aggregate produces the counts and cohorts. It is completely ordinary, and it is where everybody looks first when a number is wrong, which is almost never where the number went wrong.

Say the four out loud in order a few times. The value is not the taxonomy, it is that when a figure is disputed you can ask which stage the dispute is in, and the answer is nearly always stage one.

LiveThe decode is a judgement, not a parse4 min▶

Look again at the four questions in the amber box. Not one of them has a technically correct answer. Whether an uncollected reward happened, whether an internal movement is a reward, whether a batch of forty is one event or forty, whether a failed attempt is an event at all: these are questions about what Larkfield means, and the decoder is where the answer is recorded. Usually by one engineer, usually under time pressure, usually without anyone calling it policy.

This is why the decode deserves review by the people who own the definition rather than only by the people who own the code. A pull request that changes a threshold in an aggregation is an engineering change. A pull request that changes which payloads are recognised as a reward is a change to what the company reports, and it will move the number without any dashboard showing a deploy.

The practical move is small and it is worth insisting on: write the four answers down in prose, keep them beside the decoder, version them together, and put the prose in front of whoever will be defending the number. If that document does not exist, the definition still exists. It is just only readable by someone who can read the decoder.

LiveEnrich is the stage that quietly ruins provenance3 min▶

Enrichment is the least discussed stage and it carries a specific risk here that it does not carry elsewhere. The whole claimed advantage of working from a public ledger is that a stranger can recompute your figure from the same inputs. The moment stage three attaches a column from your own systems, that property is gone for every metric downstream of that column, and nothing in the table says so.

The fix is naming rather than architecture. Prefix enriched columns, put them in a separate table, or mark them in the model documentation, but make it impossible to read a wide table and not know which columns a stranger could reproduce. Teams that skip this end up publishing figures that look publicly verifiable and are not, which is worse than publishing a figure that is clearly internal.

Self-studyThe three stages that are just a pipeline4 min▶

It is worth being explicit about how much of this is ordinary, because a session that dwells only on the strange parts leaves people thinking they need a new discipline. They do not.

Idempotent loads, watermarking, late-arriving records, incremental models, tests on uniqueness and referential integrity, a restatable published layer, documented lineage, contract tests between your own layers: all of that is exactly what you already do, all of it applies, and none of it changes shape because the upstream happens to be a ledger. That craft lives in learn-data-engineering-with-phoebe and there is no version of it that is special here.

Two habits transfer with a small twist. First, your uniqueness tests should be written against the grain you chose in session 1, and the grain should be in the table name, because both grains will exist in the warehouse. Second, your freshness checks cannot be written against a promise, because there is no promise. They have to be written against a baseline you measured, which makes them a little weaker and considerably more honest.

Part 2 · covers what makes it abnormal

Three properties, and two teams who will never agree

13 min live

Subtract the ordinary craft and three things are left over. Each one breaks a specific habit, each one is worth a line in a design document, and only the first of the three is usually anticipated. Then the consequence that follows from all three at once: two teams, the same rows, the same month, the same question, and different answers with no bug anywhere.

Subtract the ordinary craft and exactly three things are left 1 · THE SOURCE CAN RETRACT HISTORY YOU ALREADY LOADED BLOCKS AS THEY ARRIVED, AND WHAT YOU LOADED n-4 n-3 n-2 n-1 n these three were replaced their rows are still in your tables No other upstream does this to you. A normal source sends a late row, a correction or a restatement, and it tells you. This one withdraws blocks that were already read, already loaded and already reported on, and sends no signal at all when it does. Measured in session 6. Designed against in session 7. 2 · THE SCHEMA BELONGS TO CODE YOU DID NOT WRITE definition v1 your decoder right rows definition v2 decoder, unchanged wrong rows The deploy happened. The dashed path is the notice you never get. Your decode is a pinned interpretation. It is right until a different definition is deployed, and that arrives with no version bump you can subscribe to, no deprecation window and nobody to notify you. The first symptom is a metric bending in a direction nobody ordered, which is why the baseline is the alarm. 3 · ONE BUSINESS EVENT, MANY EXPRESSIONS IN THE ROWS issued directly issued in a batch of 40 claimed by the user A REWARD REACHED A USER Folding all three in is a judgement, not a parse. The business calls these one thing. The rows call them three, and a fourth appears the quarter a new contract ships. Nothing in the data says the three belong together. A team that folds all three in and a team that counts only the first are both right, and will never reconcile.
🔍 Click to zoom - the third band is the one that gets missed in design. The first two are upstream behaviour; the third is a vocabulary problem wearing a schema costume.
One set of rows, four defensible readings of the same question THE SAME ROWS One month. 4,000 transactions. About 1,000 addresses. Identical for everyone who reads them. Addresses credited by a reward this month The widest reading, and the easiest to compute NOT PEOPLE Addresses that ever spent Activity rather than receipt, which is a real distinction NOT RECEIPT Addresses that spent more than once Repeat activity, which many teams mean by active NOT EVERYONE ACTIVE Holders after co-spend clustering The nearest thing to a party count, and session 3 builds it NOT A POINT ESTIMATE All four counts are computed live in the box below, from the same generator every session uses. Four teams, four numbers, no bug anywhere. The number is a property of the definition, not a property of the chain. Which is why reconciling with another team starts by comparing decoders and definitions, and never by comparing totals.
🔍 Click to zoom - nothing here is a data quality problem. Four teams doing careful work produce four numbers, and the gap is in the words.
Live Four definitions, one month, computed in this page. Change the month and watch the spread rather than the values.

...

...
LiveRetraction is the property with no analogue4 min▶

Every pipeline handles data that is late, data that is wrong and data that gets restated. The habit that covers all three is the same: accept that the past can change, make the load idempotent, keep the published layer restatable. That habit is necessary here and it is not sufficient, because it assumes somebody tells you.

What happens here is that blocks you already read get replaced, and their rows stay in your tables describing history the chain no longer has. There is no restatement message, no correction record, no changed row, no failed load and no alert. The rows are simply wrong from a certain moment onwards and nothing in your stack has any reason to think so.

Session 6 measures exactly how much of a warehouse ends up in that state on Larkfield's numbers, and it is more than most people guess. Session 7 takes apart the two defences, which are separate mechanisms that are constantly confused for each other. Tonight the only thing to carry is that this belongs in the design document as a named property of the source, not as an operational surprise.

LiveTwo teams, same rows, different numbers4 min▶

Run the live box through all five months and read the spread rather than the values. Four definitions of one metric, every one of them defensible in a review, and the widest and narrowest are hundreds apart on a population of about a thousand addresses. Nobody made an error. There is no data quality incident to open.

This is the part that surprises people who expect a public dataset to produce a single agreed truth. Shared rows remove the argument about what the data says. They do not touch the argument about what the question means, and that argument turns out to be the bigger one. The chain gives everybody the same inputs and then leaves the definition entirely to you, which means the only way two teams agree is by agreeing on words first.

The operational consequence is worth stating flatly. When your figure does not match another team's figure, do not start by comparing totals, and do not start by looking for a bug. Start by putting the two decoders and the two written definitions side by side. The difference is nearly always visible in the first ten minutes, and it is nearly always a sentence rather than a line of code.

Self-studyWhere serious people disagree5 min▶

One camp says every team must own its own decode. Their argument is that the decode is the business definition, so outsourcing it means outsourcing what your company means by a customer, a reward and an event. A team that adopts somebody else's interpretation inherits every judgement inside it, cannot explain its own number under questioning, and discovers the inherited assumptions only when a regulator or a board member asks the one question the borrowed definition answers differently. On this view the nine weeks Larkfield spent are not waste, they are the cost of knowing what you are reporting.

The other camp says that is an expensive way to produce a number nobody else can compare with. Every team writing its own decode means every team's figure is incomparable with every other team's, which destroys the single genuine advantage of a shared public dataset. Better, on this view, to adopt a published shared definition, contribute corrections to it in the open, and accept that your number will sometimes be wrong in the same way everybody else's is. A figure that is comparable and slightly wrong is more useful than a figure that is bespoke and slightly right, because comparability is the thing you cannot manufacture later.

Both have shipped this and neither is being naive. Notice that they disagree about what the number is for: one camp is optimising for defending it internally, the other for comparing it externally. Notice also that the second position quietly depends on the shared definition being maintained by somebody, and the first quietly depends on your organisation actually writing its judgements down. Ask which of those two conditions holds where you work, and the argument usually answers itself.

Part 3 · build-along

Find your decode, and find out who owns it

15 min hands on

No code. Six steps against a pipeline your team already runs, said out loud in the room. Every one of them is a question about where a definition is written down, because that is the thing this session says is the product.

Name your four stages

Take one pipeline you own and split it into decode, normalise, enrich, aggregate. Some stages will be missing and one will be enormous. Say which one is enormous and whether that is where your incidents come from.

Find the sentence that defines your main metric

Not the SQL, the sentence. Go and find it. If it does not exist, say who would have to write it and how long the argument would take, and notice that the argument is happening anyway, once per disputed number.

Answer the four judgement questions for Larkfield

In the room, decide: does an uncollected reward count, is a movement between two company addresses a reward, is a batch of forty one event or forty, does a failed attempt exist. Two minutes each, no consensus required. Count how many of the four split the room.

Move the month and read the spread

In the live box, step through all five months and write down the widest and narrowest of the four counts each time. Then say which of the four you would have picked by default, and what you would have called it in a dashboard title.

Mark your enriched columns

In one published table, list the columns that came from your own systems rather than the source. Then ask whether a reader could tell. If not, that table is being read as more verifiable than it is.

Write one line for the design document

One sentence naming retraction as a property of the source, in words a reviewer who has never seen a ledger would understand. Bring it to session 6, which puts a percentage on it.

The habit to keep. When two teams disagree on a number, the reflex is to diff the data. Diff the definitions instead, and diff them in prose before diffing them in code. Shared inputs make the arithmetic uncontroversial and leave the vocabulary as the only remaining place for a disagreement to hide, which is exactly where it will be.

Check yourself

Three questions

Two teams index the same rows for the same month and publish active user figures that differ by several hundred. Both pipelines pass their tests. What is the most likely cause?

Shared inputs remove the argument about what the data says and leave the argument about what the question means. Four defensible definitions on Larkfield's month give four different counts with no bug anywhere. Reconciliation starts with the two definitions side by side, not with the two totals.

Which of the three abnormal properties is the one an ordinary pipeline's habits do not cover at all?

Schema changes and multi-shape sources are both familiar problems with existing craft, harder here but recognisable. Retraction is the one with no analogue: rows already loaded silently start describing history the chain no longer has, with no correction record, no failed load and no alert. Session 6 measures it.

An indexer adds a column mapping each address to a marketing campaign, taken from Larkfield's own systems, and publishes it in the same wide table as the decoded chain fields. What has that cost?

Enrichment is ordinary work with one specific cost here. The claimed advantage of public rows is that a stranger can recompute your figure. A private column silently removes that for everything downstream of it, and a wide table that mixes the two is read as more verifiable than it is. The fix is naming, not architecture.

Before session 3

Homework

Twenty minutes, on the definition you went looking for in step 2 and the four answers your room gave in step 3.

Write the decode document that does not exist

Half a page, in prose, for one metric you publish: what counts, what does not, and which of those calls you would lose an argument about. Put it next to the code and see whether anyone objects to it being called policy.

Bring the clustering question

The fourth definition in the live box is holders after co-spend clustering. Write down, before session 3, what you think that rule is and how much of the gap between addresses and people you expect it to close. Session 3 builds the algorithm in code.

Covered tonight

What this session covers

✓The four stages in orderDecode, normalise, enrich, aggregate, and which failure each stage produces.
✓Why the decode holds the domain knowledgeFour judgement questions with no technically correct answer, recorded in code and rarely reviewed as policy.
✓Enrichment and the provenance it spendsPrivate columns remove public recomputability downstream, and nothing in a wide table says which columns those are.
✓The three abnormal propertiesRetraction, a schema owned by deployed code, and one business event with many expressions in the rows.
✓Four definitions, four numbersComputed live on Larkfield's month, all four defensible, and the spread read off the page.
✓How to reconcile with another teamCompare decoders and written definitions first, and totals last.
◐The co-spend clustering ruleNamed tonight as the fourth definition. Session 3 builds it in code with union-find.
◐What retraction costs a warehouseNamed tonight as a property of the source. Session 6 measures the share of rows affected.
◐The two defences against retractionSession 7, where holding rows back and removing them are kept apart.
○Idempotency, late-arriving data and restatementGeneral pipeline craft, and it lives in learn-data-engineering-with-phoebe.
○Hashes, blocks, consensus and chain mechanicsThe prerequisite is learn-blockchain-with-phoebe and it is assumed rather than repeated.
○Named networks, platforms or indexing softwareNone of it can be verified from here and all of it moves faster than a session. Every figure comes from the generator.
○Anything to do with price, markets or holdingsOut of scope by design, and nothing here is advice of any kind.
Take this with you

Cheat sheet

The four stages. Decode, normalise, enrich, aggregate. Only the first has no ordinary equivalent.
The decode is the product. It is your organisation's answer to what happened, written as code, usually by one person in a hurry.
The four judgements. Which definition, what is it called, does a failure count, is an internal move a real event. None has a technical answer.
Property 1, retraction. Rows you loaded describe history the chain no longer has, with no signal of any kind. Session 6 measures it.
Property 2, someone else's schema. A new definition is deployed, your decoder is unchanged, and nothing fails.
Property 3, many expressions. One business event, several record shapes, and a new one each quarter. Folding them in is a judgement.
Four definitions, four numbers. Same rows, same month, no bug. The number is a property of the definition.
The reconciliation rule. Diff the definitions in prose, then the decoders. Diff the totals last, if at all.