Visual walkthrough — Feature stores
Step 1 — The two kinds of facts on a timeline
WHAT. Draw a single horizontal line: time, flowing left to right. On it we place two very different kinds of dots.
WHY. Everything in machine learning training data is one of these two things, and confusing them is the root of every leakage bug. So we separate them visually before touching any math.
PICTURE. Look at the figure.
- The orange dots are feature observations — moments where we recorded a number about the user (their balance, their click count). Each carries a value and the timestamp it was recorded at.
- The plum dot is the label event — the moment something we care about happened (a transaction we later labelled "fraud / not fraud"). Its timestamp is (the subscript = "event").

Step 2 — The question a training row must answer
WHAT. We want to build one row of training data for the label event. That row asks: "Given only what we knew right before , could a model have predicted the label?"
WHY. A model in production only ever sees the past. If our training row secretly contains the future, we teach the model to rely on information it will never have when it matters. That is leakage, and it is exactly the skew disaster the parent note warns about.
PICTURE. Drop a vertical "now" wall at . Everything left of the wall is knowable; everything right of it is the future.

Step 3 — Forbid the future (the constraint )
WHAT. Cross out every orange dot that sits to the right of the wall.
WHY. A feature recorded after the event could not have been known when the event happened. Using it is cheating with tomorrow's newspaper. In symbols: we only keep observations with Read left to right: "observation time is at or before the event time." The (not ) allows a feature recorded at the exact instant of the event — that's fine, it's still not the future.
PICTURE. The greyed-out dot on the right is gone. We're left with a shorter list of allowed dots.

Step 4 — Among the survivors, pick the freshest ()
WHAT. Of the dots that survived Step 3, choose the one closest to the wall — the largest .
WHY. All survivors were knowable, but they disagree (balance was $100 at 09:00, $40 at 10:30). Which is "the" feature value? The most recent one, because that's literally what a live system would have looked up at : the latest known value. Newer = more informative.
To name "the position of the freshest allowed dot", we need one tool.
PICTURE. The winning dot glows; a bracket measures its distance to the wall as the smallest gap among survivors.

Step 5 — Read off the value
WHAT. Take the value stored at the winning dot:
WHY. told us which dot; now we simply read its recorded value. That is the number that goes into the training row — the same number a live lookup would have returned at . This equality is why the skew vanishes: training uses the exact value serving would.
PICTURE. An arrow drops from the glowing dot down to the training-row cell being filled.

Step 6 — Edge case: TTL (stale features must expire)
WHAT. Add a second wall, a distance (the time-to-live) to the left of . Now a dot is allowed only if it lands in the narrow band between the two walls:
- ::: the oldest a feature may be before we distrust it.
- ::: how long a value stays "fresh enough" (e.g. 2 minutes).
WHY. A value that survived Step 3 might still be ancient. A three-month-old "recent activity" number lies to the model. TTL forces us to treat it as missing rather than trust a fossil.
PICTURE. Two walls form a window. Dots outside the window (too old) are struck through just like future dots were.

Step 7 — Degenerate case: nothing on the left at all
WHAT. What if every observation is in the future, or there are no observations? Then Step 3's fence keeps zero dots, and over an empty set has no answer.
WHY. This is common for a brand-new user (no history yet) or a very early event. The join must not crash or silently pick a future value.
PICTURE. An empty left region; the training cell is filled with a labelled default / null, and a note reminds you the model must learn to handle it.

The one-picture summary
Everything above is one timeline, one wall (two with TTL), one glowing winner. This figure compresses all seven steps: the future-fence, the freshness pick, the TTL band, and the value read-off.

Recall Feynman: retell the whole walkthrough
Picture a timeline. Some orange dots are moments we wrote down a fact about the user. One plum dot is the moment something happened that we want to predict. Now hammer a fence post at that plum moment: nothing to the right counts, because that's the future we didn't know yet. Of the dots on the left, grab the one nearest the fence — that's the freshest thing we honestly knew. Read the number on it; that number goes into our training row, and it's exactly what a live system would have looked up. If we're strict about freshness, add a second fence a little to the left (TTL): only dots between the two fences count, so a fossil value can't sneak in. And if no dot lives in the allowed zone — new user, no history — we don't fake it; we write "null" and make the model deal with it. That's the entire point-in-time join: look only at what you truly knew, and take the most recent piece of it.
Prerequisites worth a look: Feature engineering, Data versioning, Batch vs streaming pipelines; the online store this feeds is usually Redis or DynamoDB.