How to Handle Schema Evolution and Feature Version Upgrades in Feast

Feast handles schema evolution by separating logical feature definitions from physical storage, using table formats like Iceberg or Delta Lake for physical changes and immutable FeatureServices for versioned model consumption.

Feast is a feature store that decouples feature definitions from data storage, enabling teams to evolve schemas without breaking existing models. When working with the feast-dev/feast repository, you manage changes through table formats for physical storage and versioned FeatureService objects for logical consumption.

Understanding Schema Evolution in Feast

Schema evolution in Feast operates at two distinct layers: the physical storage layer managed by table formats, and the logical feature layer defined in FeatureView objects.

Physical Schema Changes via Table Formats

Feast delegates physical schema evolution to underlying table formats such as Apache Iceberg, Delta Lake, and Apache Hudi. These formats provide ACID transactions, time-travel, and native schema evolution capabilities.

When you alter the source table using the table format's native API (for example, ALTER TABLE prod.driver_stats ADD COLUMN new_speed DOUBLE in Iceberg), Feast does not require metadata changes. The FeatureView continues pointing to the same table location, and subsequent reads automatically recognize the new columns. This approach is documented in the table formats reference within the Feast repository.

Logical Schema Updates with FeatureViews

When new physical columns must be exposed as features, you update the logical schema by modifying the FeatureView definition in your Python feature definitions file.

Add the new field to the schema parameter and re-apply the registry:

from feast import FeatureView, Entity, Field
from feast.types import Float64, Int64

driver_stats_fv = FeatureView(
    name="driver_stats",
    entities=[Entity(name="driver_id", dtype=Int64)],
    schema=[
        Field(name="conv_rate", dtype=Float64),
        Field(name="new_speed", dtype=Float64),   # newly added feature

    ],
    source=...,
)

Running feast apply registers the updated FeatureView without affecting existing FeatureService objects that reference previous versions, ensuring backward compatibility.

Implementing Feature Version Upgrades

Feast treats FeatureService objects as immutable contracts between feature definitions and model versions. Each model version should consume a dedicated FeatureService to guarantee reproducible training and serving.

Creating Immutable FeatureServices

According to the implementation in sdk/python/feast/feature_service.py, a FeatureService groups specific FeatureView versions into a named, versioned collection. Once a FeatureService is used by a model, it should not be modified; instead, create a new service with a distinct name:


# sdk/python/example_feature_service.py

from feast import FeatureService
from driver_hourly_stats_fv import driver_hourly_stats_fv
from driver_daily_stats_fv import driver_daily_stats_fv

# New feature service for model version 2

driver_activity_v2 = FeatureService(
    name="driver_activity_v2",
    features=[driver_hourly_stats_fv, driver_daily_stats_fv],
)

Register the new service using feast apply, which pushes the definition to the registry without altering existing services.

Versioned Retrieval for Training and Serving

Retrieve features using the specific FeatureService name to ensure the correct schema version is used for both historical training data and online inference:

store = FeatureStore(".")
fs_v2 = store.get_feature_service("driver_activity_v2")

# Historical training data

train_df = store.get_historical_features(
    entity_df=entity_df,
    features=fs_v2,
).to_df()

# Online inference

online_resp = store.get_online_features(
    features=fs_v2,
    entity_rows=[{"driver_id": 123}],
)

As documented in the feature retrieval concepts guide, this approach guarantees that models trained against driver_activity_v2 will always receive the exact feature set expected, even as newer versions are introduced.

Summary

  • Physical schema evolution is handled by underlying table formats (Iceberg, Delta Lake, Hudi) that support ACID schema changes without requiring Feast metadata updates.
  • Logical schema updates require modifying FeatureView definitions and running feast apply, which creates new registry entries while preserving existing FeatureService references.
  • Feature versioning relies on immutable FeatureService objects defined in sdk/python/feast/feature_service.py, where each model version consumes a dedicated service (e.g., driver_activity_v2).
  • Backward compatibility is maintained because existing FeatureService instances continue referencing the specific FeatureView versions they were defined with, allowing old and new model versions to coexist.

Frequently Asked Questions

How does Feast handle breaking schema changes?

Feast prevents breaking changes by treating FeatureService objects as immutable once they are used by models. When you introduce a breaking change—such as removing a column from a source table—you create a new FeatureView or modify the existing one and define a new FeatureService with a distinct name. Existing models continue using the old service name, ensuring uninterrupted serving while new models adopt the updated schema.

Can I rollback to a previous feature version?

Yes, rollback is supported because the Feast registry retains all historical FeatureService definitions. If a new feature version introduces issues, you can revert your model configuration to reference the previous FeatureService name (e.g., changing from driver_activity_v2 back to driver_activity_v1). Since each service pins specific FeatureView versions, this immediately restores the previous feature set without data migration.

What table formats support schema evolution?

Feast integrates with ACID table formats that natively support schema evolution, specifically Apache Iceberg, Delta Lake, and Apache Hudi. These formats allow you to execute ALTER TABLE operations (adding, dropping, or renaming columns) while maintaining historical data consistency. Feast reads from these tables without requiring metadata changes, as documented in the table formats reference.

Do I need to retrain models when upgrading feature versions?

Retraining is not strictly required for the upgrade process itself, but it is strongly recommended to ensure model accuracy. When you create a new FeatureService for an upgraded feature set, you should generate new training data using store.get_historical_features(features=fs_v2) and retrain your model against this data. This ensures the model learns the relationships between the new or modified features and the target variable, preventing serving skew between training and inference.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →