# How to Handle Schema Evolution and Feature Version Upgrades in Feast

> Learn how to manage schema evolution and feature version upgrades in Feast by separating logical definitions from physical storage. Utilize Iceberg or Delta Lake for physical changes and immutable FeatureServices for versioned ...

- Repository: [Feast/feast](https://github.com/feast-dev/feast)
- Tags: how-to-guide
- Published: 2026-03-01

---

**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](https://github.com/feast-dev/feast/blob/master/docs/reference/data-sources/table-formats.md) 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:

```python
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`](https://github.com/feast-dev/feast/blob/main/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:

```python

# 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:

```python
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](https://github.com/feast-dev/feast/blob/master/docs/getting-started/concepts/feature-retrieval.md), 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`](https://github.com/feast-dev/feast/blob/main/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](https://github.com/feast-dev/feast/blob/master/docs/reference/data-sources/table-formats.md).

### 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.