Design a Feature Store

A model needs the same number at 3am training time and at the 50-millisecond serving moment — the user's 7-day click count had better mean the same thing in both. Build up from every team hand-rolling features in their service to a dual store fed by one definition: a columnar offline store holding years of history for point-in-time-correct training, a key-value online store holding the latest vector for 50K QPS under 50ms p99. Train/serve skew, the time-travel join, streaming freshness, and drift — drawn, computed, and animated.

System design · AI / ML. The source ↗

A free, interactive, animated visual explainer of Design a Feature Store — built to be understood, not skimmed.

Questions

What is a feature store and what problem does it solve?
A feature store is the data system that sits between raw data and machine-learning models, making the same feature values available in two very different access patterns: huge historical reads for training, and single-row low-latency lookups for online inference. Databricks defines it as "an ML-specific data system that: Runs data pipelines that transform raw data into feature values, Stores and manages the feature data itself, and Serves feature data consistently for training and inference purposes." The problem it solves is consistency: a feature like a user's 7-day click count must have the same definition and the same value whether a model is training on last year's data or scoring a live request, or the model quietly rots. It also lets features be defined once and reused across many models.
What is train-serve skew (training-serving skew)?
Train-serve skew is when the feature values a model was trained on differ from the values it sees in production — so a model that looked great offline degrades live, silently. It has two classic causes. The first is two code paths: features computed by a batch job for training and re-computed by hand in the request path for serving, which drift apart in rounding, time windows, or logic. The second is temporal leakage: joining training labels against the latest feature value instead of the value as of the label's timestamp. As Databricks puts it, "The definitions of features used to train a model must exactly match the features provided in online serving. When they don't match, training-serving skew is introduced, which can cause catastrophic and hard-to-debug model performance problems." A feature store fixes it by computing each feature once, from one definition, and feeding both stores.
What is point-in-time correctness in a feature store?
Point-in-time correctness means that when you build a training set, each label row is joined to the feature values as they were at that label's timestamp — never a value that only became known later. Feast's historical retrieval, "for each entity row, it retrieves the latest feature values with a timestamp at or before the entity row's event_timestamp." A naive join on the latest value leaks the future into training: the model learns from information it will not have at prediction time, scores beautifully offline, and fails live. This is why offline stores keep the full time-series history of every feature and the training join is an as-of (time-travel) join rather than a plain lookup.
Why does a feature store have separate offline and online stores?
Because training and serving have opposite access patterns. Training reads months or years of history across millions of entities in big scans, tolerant of latency — a columnar lakehouse or warehouse is the right shape. Serving needs one entity's freshest feature vector in single-digit milliseconds at tens of thousands of requests per second — a key-value store holding only the latest value per entity is the right shape. Feast describes exactly this split: an offline store "for historical feature extraction used in model training" and an online store "for serving features at low-latency in production systems and applications." One feature definition feeds both; materialization copies the latest values from the offline store to the online store so the two never disagree.
How does a feature store serve features at 50K QPS under 50ms?
The online store is a key-value cache with a contract: it holds one precomputed feature vector per entity, keyed by entity id, so a serving lookup is a single fast read rather than a fresh computation. Databricks describes it as delivering "a single vector of features at a time made up of the freshest feature values … through a high-performance API backed by a low-latency database." The engineering is the same as any hot read path — sharding, replication, and hot-key handling — plus a freshness contract: streaming or batch materialization keeps the online values fresh to a chosen tier (seconds to a day), and the tighter the freshness SLA, the more write amplification you pay to keep every entity's vector current.

Related explainers