Skip to main content
← All Articles Β· Β· 10 min read

Feature Stores Explained: Why Your ML Team Needs One 2026

Feature stores guide 2026: what they are, offline vs online store, point-in-time correctness, training-serving skew prevention, Feast vs Tecton vs Hopsworks vs Vertex AI Feature Store.

feature storeFeastMLOpsfeature engineeringtraining-serving skewML infrastructure

Every ML team eventually hits the same wall: the model works in the notebook, fails in production, and nobody can figure out why. Half the time, the answer is training-serving skewβ€”the features used during training are computed differently from the features the model sees at inference time.

Feature stores solve this problem, and several others. Here’s what they are, when you need one, and how to pick the right one.


The Problem Feature Stores Solve

Problem 1: Training-Serving Skew

Imagine a fraud detection model. During training, the data science team computes a feature: β€œnumber of transactions in the last 24 hours.” They write:

# Training (notebook)
df["txn_count_24h"] = df.groupby("user_id")["transaction_id"].transform(
    lambda x: x.rolling("24h").count()
)

The ML engineer then implements this for production:

# Production (serving code)
def get_txn_count_24h(user_id: str) -> int:
    result = db.execute(
        "SELECT COUNT(*) FROM transactions WHERE user_id = ? AND created_at > NOW() - INTERVAL 1 DAY",
        user_id
    )
    return result[0][0]

Subtle differences in timezone handling, whether β€œ24 hours” means 86400 seconds or a calendar day, or how NULL transactions are countedβ€”any of these cause skew. The model was never tested on the features it actually receives.

Problem 2: Feature Duplication

Team A computes β€œuser lifetime value” for a churn model. Team B computes β€œuser lifetime value” for a recommendation model. They implement it slightly differently. Now you have two LTV definitions, two pipelines, two potential sources of drift, and no single source of truth.

Problem 3: Point-in-Time Correctness

When training a model, you need to join labels to features using the feature values that existed at the time of the eventβ€”not today’s values. If a user churned in January and you join today’s features to that label, you’re leaking future information into training.

Event: User churned on Jan 15
Wrong: Join Jan 15 label β†’ March 1 feature values (data leakage)
Right: Join Jan 15 label β†’ Jan 14 feature values (point-in-time correct)

Problem 4: Feature Discovery

New team member joins. What features exist? Where are they computed? Are they reliable? Without a feature registry, the answer is β€œgrep through 47 notebooks and hope.”


Feature Store Architecture

A feature store has two components:

Offline store: Historical feature values for training. Low-latency writes acceptable. Usually backed by a data warehouse (BigQuery, Snowflake, S3+Hive).

Online store: Current feature values for low-latency inference. Must serve in under 10ms. Usually backed by Redis, DynamoDB, or Cassandra.

Materialization: The process of reading from the offline store and writing current values to the online store. Runs on a schedule (every 15 minutes, hourly, etc.).

Data Sources (Kafka, Postgres, S3)
        ↓
Feature Pipelines (Spark, Flink, dbt)
        ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           Feature Store            β”‚
β”‚                                    β”‚
β”‚  Offline Store      Online Store   β”‚
β”‚  (BigQuery/S3)  ←→  (Redis)        β”‚
β”‚                                    β”‚
β”‚  Feature Registry (definitions)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        ↓                ↓
  Training Jobs    Inference Service

Feast: Open-Source Feature Store Setup

Feast is the most widely deployed open-source feature store. It’s infrastructure-agnostic and works with your existing data stack.

Installation and Configuration

pip install feast[redis,postgres]
feast init my_feature_store
cd my_feature_store
# feature_store.yaml
project: my_ml_project
registry: data/registry.db
provider: local

offline_store:
  type: file  # Use spark or bigquery in production

online_store:
  type: redis
  connection_string: "redis://localhost:6379"

Defining Feature Views

# features/user_features.py
from datetime import timedelta
from feast import (
    Entity,
    Feature,
    FeatureView,
    Field,
    FileSource,
    ValueType,
)
from feast.types import Float64, Int64, String

# Define the entity
user = Entity(
    name="user_id",
    value_type=ValueType.STRING,
    description="User identifier",
)

# Data source (CSV, Parquet, BigQuery table, etc.)
user_stats_source = FileSource(
    path="data/user_stats.parquet",
    timestamp_field="event_timestamp",
)

# Feature view: a group of related features
user_stats_fv = FeatureView(
    name="user_statistics",
    entities=["user_id"],
    ttl=timedelta(days=7),         # How long features stay valid
    schema=[
        Field(name="total_transactions_30d", dtype=Int64),
        Field(name="avg_transaction_amount", dtype=Float64),
        Field(name="account_age_days", dtype=Int64),
        Field(name="preferred_category", dtype=String),
        Field(name="lifetime_value", dtype=Float64),
    ],
    source=user_stats_source,
)

Materializing Features (Offline β†’ Online)

# Materialize features from historical range
feast materialize 2026-01-01T00:00:00 2026-04-02T00:00:00

# Materialize only new data (incremental)
feast materialize-incremental 2026-04-02T00:00:00

Retrieving Features for Training (Point-in-Time Correct)

from feast import FeatureStore
import pandas as pd

store = FeatureStore(repo_path=".")

# Training data: entity + label + event timestamp
entity_df = pd.DataFrame({
    "user_id": ["user_001", "user_002", "user_003"],
    "event_timestamp": ["2026-01-15", "2026-02-01", "2026-03-10"],
    "churned": [1, 0, 1],  # Labels
})

# Fetch historical features at each event_timestamp
# This is the point-in-time correct join
training_df = store.get_historical_features(
    entity_df=entity_df,
    features=[
        "user_statistics:total_transactions_30d",
        "user_statistics:avg_transaction_amount",
        "user_statistics:lifetime_value",
        "user_statistics:account_age_days",
    ],
).to_df()

print(training_df)
# user_id | event_timestamp | churned | total_transactions_30d | ...
# user_001 | 2026-01-15      |    1    |           12           | ...

Feast automatically joins features at the correct timestamp β€” no manual window logic needed.

Retrieving Features for Online Inference

# Serving (production inference)
online_features = store.get_online_features(
    features=[
        "user_statistics:total_transactions_30d",
        "user_statistics:avg_transaction_amount",
        "user_statistics:lifetime_value",
    ],
    entity_rows=[{"user_id": "user_001"}],
).to_dict()

# Returns latest materialized values from Redis
# Latency: ~2-5ms
prediction = model.predict(online_features)

The same feature definitions used for training are used for serving. Skew eliminated.


Feature Store Comparison

Feature StoreTypeHostingOnline StoreStrengthsWeaknesses
FeastOpen sourceSelf-hostedRedis, DynamoDBFree, flexible, active communityManual infra setup, limited governance
TectonManagedCloudDynamoDB, RedisEnterprise support, streaming featuresExpensive ($), vendor lock-in
HopsworksOpen source + managedBothRonDB (MySQL)Full ML platform, feature monitoringHeavy, complex deployment
Vertex AI Feature StoreManagedGCP onlyBigtableNative GCP integrationGCP lock-in, limited customization
Databricks Feature StoreManagedDatabricksDelta + RedisUnity Catalog integrationDatabricks lock-in
SageMaker Feature StoreManagedAWS onlyDynamoDBNative AWS integrationAWS lock-in, limited flexibility

Decision criteria:

  • Existing GCP stack: Vertex AI Feature Store
  • Existing Databricks: Databricks Feature Store
  • AWS + need flexibility: Feast on AWS (S3 offline + DynamoDB online)
  • Multi-cloud or on-prem: Feast or Hopsworks
  • Enterprise budget, need SLA: Tecton

When Do You Actually Need a Feature Store?

Feature stores add overhead. A solo data scientist or a team of 2 doing ad-hoc work does not need one. Here are the thresholds:

SignalIndicator You Need a Feature Store
Team size3+ data scientists computing overlapping features
Model count5+ models in production
Skew incidentsAny production issue caused by feature mismatch
Feature complexityAggregations over time windows (last 7d, 30d, 90d)
Compliance needAudit trail required for feature computation
Latency requirementsOnline inference < 100ms with fresh features
Data freshnessFeatures need to update within minutes of raw data changing

If you have fewer than 3 of these signals, a well-documented dbt pipeline with a Redis cache for serving features is sufficient and much simpler.

If you have 3+, a feature store pays for itself in reduced debugging time within 3–6 months.


The Feature Registry: Making Features Discoverable

The registry is the metadata layer. Every feature definition includes:

user_stats_fv = FeatureView(
    name="user_statistics",
    entities=["user_id"],
    ttl=timedelta(days=7),
    schema=[
        Field(
            name="lifetime_value",
            dtype=Float64,
            description="Total revenue from user since account creation",
            tags={
                "owner": "data-team",
                "pii": "false",
                "sla": "hourly-materialization",
                "version": "2",
            }
        ),
    ],
    source=user_stats_source,
    tags={"domain": "user", "status": "production"},
)
# Discover available features
feast feature-views list

# Show feature details
feast feature-views describe user_statistics

New team members can browse the registry, understand what exists, and reuse features rather than recompute them.


Common Pitfalls

Materializing too infrequently: A fraud model that needs β€œtransactions in last 5 minutes” cannot use a feature materialized hourly. Design your materialization frequency around your model’s freshness requirements.

Using Feast for streaming without a streaming platform: Feast’s batch materialization is solid; streaming features require Kafka + Flink and add significant complexity. Batch-first is the right starting point.

Tight coupling to a single feature store vendor: Define feature computation logic in dbt or Spark independently of the feature store. The store should be a serving layer, not the computation layer.

Skipping feature monitoring: A feature store ensures consistency, but features can still degrade (upstream null rates increase, aggregation windows produce unexpected values). Monitor feature statistics post-materialization just as you would monitor model predictions.

Feature stores are plumbing β€” boring, important plumbing. The teams that invest in them early find that their model iteration speed doubles, because engineers spend time on new models instead of debugging why lifetime_value in training differs from lifetime_value in production.

Free Download

50 CLI Commands That Save Hours Every Week

The exact Git shortcuts, file search tricks, Docker commands, and productivity hacks that senior developers use daily. Compiled from 5+ years of shipping production code.

  • Git mastery (10 cmds)
  • File & search wizardry
  • Docker & DevOps tricks
  • Process & network tools
  • Productivity boosters
  • macOS & Linux install guide

No spam. 1 email/week max. Unsubscribe anytime.

Found this guide useful? Check out our free developer tools.