Chalk home page
Docs
SDK
CLI
  1. Features
  2. Versioning

What are feature versions

Feature versions allow you to manage a feature as its definition changes over time. You can reference versions of a feature when querying, in resolvers, in migrations, and anywhere you use features!

The ability to maintain multiple versions of a feature is especially useful as your use cases evolve and the definition of a feature changes. This might be because your old feature had a bug, or maybe you want to improve the feature to be even more useful.

Changing the definition of a feature can have unexpected side effects, especially when there might be many consumers of a feature, some of which you may be unaware of. Feature versions allow you to update the feature definition without worrying that you might affect these consumers unknowingly.

Try it out yourself with this tutorial.


Definition

To define a versioned feature, supply the version keyword argument to the feature function. This allows you to continue adding versions without affecting past versions.

from chalk.features import features, feature

@features
class Trial:
    id: int
    code: str = feature(version=2)

Querying

You can refer to a specific version of a feature using the @ operator. You can query versioned features from any client, including the CLI, Python, etc.

$ chalk query --in trial.id=1 --out trial.code@2
from chalk.client import ChalkClient
from src.models import Trial

client = ChalkClient()
result = client.query(
    input={Trial.id: 1},
    output=[Trial.code@2]
)

Default versions

When you define a versioned feature, that feature always has a default version. When you reference a versioned feature without the @ operator, you reference the default version. The default version can be controlled by adding the default_version keyword argument to the feature function. If no default_version is provided, the default version is 1. It is best practice to leave the default as 1 until all consumers have been updated to use a newer version.

from chalk.features import features, feature

@features
class Trial:
    id: int
    code: str = feature(version=2, default_version=2)

Deprecating features

As you migrate to newer feature versions or phase out old features, you can mark them as deprecated to signal that they should no longer be used. This helps teams understand which features to avoid and provides a clear migration path.

You can deprecate features that aren’t versioned to indicate they should be phased out:

from chalk.features import features, feature

@features
class User:
    id: str
    # Mark the old feature as deprecated
    legacy_risk_score: float = feature(
        deprecated=True,
        description="Use risk_score_v2 instead"
    )

    # The new feature that should be used instead
    risk_score_v2: float

Deprecating specific versions

When using multiple versions of a feature, you can mark specific versions as deprecated:

from chalk.features import features, feature
from chalk import _

@features
class User:
    id: str
    score: int
    banned: bool = feature(versions={
        1: feature(
            deprecated=True,
            description="Prior thresholds we used",
            expression=_.score > 900,
        ),
        2: feature(
            description="New cutoff released by Monica's team",
            expression=_.score > 300 | (_.score < 100 & _.id == "admin"),
        ),
    })

When a deprecated feature is used in queries or resolvers, Chalk will log warnings to help you track and eliminate usage of deprecated features across your codebase. This is particularly useful during gradual migrations when you need to maintain backward compatibility while encouraging adoption of new features.

To monitor the migration progress and identify which deprecated features can be safely removed, you can use the Chalk web interface to sort features by usage and see when each feature was last queried. This helps you make data-driven decisions about when it’s safe to fully retire deprecated features.


Resolvers

Resolvers also reference versioned features with @. Resolvers can take versions of a feature as input, return them as output, or both. Chalk will always generate the right version of a feature when chaining resolvers that require versioned features.

Requesting versioned features

from chalk.features import online

@online
def detect_trial_failure(code: Trial.code) -> Trial.failed:
    # Default version is 1.
    return code in {'failed', 'err'}

@online
def detect_trial_failure_v2(code: Trial.code@2) -> Trial.failed:
    # Version 2 is specifically requested.
    return code in {'FAILED', 'ERR'}

Producing versioned features

@online
def extract_trial_code_v1(d: Trial.raw_data) -> Trial.code @ 1:
    return d['code']

@online
def extract_trial_code_v2(d: Trial.raw_data) -> Trial.code @ 2:
    return d['data']['code']
-- resolves: Trial@2
-- source: pg_trial

select id, code from trial_table;

Limitations

Only scalar features can be versioned. Other kinds of features cannot be versioned. These include:

  • has_one and has_many
  • FeatureTime
  • Windowed
  • Primary