Feature lifecycle

Every feature carries a status that says how much you should trust it: DEV, BETA, ACTIVE, DEPRECATED, or HARD_DEPRECATED. The interesting part is that status is not a label — the registry enforces it across the dependency graph, so a stable feature can never quietly be built on something half-finished.

This page follows one small feature and its consumer from first draft to retirement, and shows what the registry refuses along the way.

Lifecycle is a managed-registry capability. The local engine rejects status mutations and may expose STATUS as NULL.

Everything starts in DEV

Create two features — a basket value and a tax-inclusive version that depends on it.

FeatureQL
CREATE OR REPLACE FEATURES AS
PERSIST
    fm.tutorials.lifecycle.basket_value := 120,
    fm.tutorials.lifecycle.basket_value_with_tax := fm.tutorials.lifecycle.basket_value
    * 12
    / 10
;
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.LIFECYCLE.BASKET_VALUECREATEDFeature created as not exists
FM.TUTORIALS.LIFECYCLE.BASKET_VALUE_WITH_TAXCREATEDFeature created as not exists

Nothing was said about status, so both are DEV: usable, changeable, and not a promise to anyone.

CREATE OR REPLACE of a formula also resets the replaced feature to DEV and re-checks the same transition table and dependency matrix as ALTER FEATURE … SET STATUS. That is why an ACTIVE or BETA feature cannot be edited in place — a formula change is a new review, not a silent hotfix. Demote first, or introduce a replacement and retire the old name.

FeatureQL
SHOW FEATURES (COLUMNS (NAME, STATUS)) WHERE NAME LIKE 'FM.TUTORIALS.LIFECYCLE.%';
Result
NAME VARCHARSTATUS VARCHAR
FM.TUTORIALS.LIFECYCLE.BASKET_VALUEDEV
FM.TUTORIALS.LIFECYCLE.BASKET_VALUE_WITH_TAXDEV

Status is independent of descriptive metadata, custom attributes, and runtime quality assertions. An attribute named point_of_contact or pager_duty is documentation; it changes nothing about lifecycle behavior.

Promote from the bottom up

BASKET_VALUE_WITH_TAX looks ready for production, so try promoting it with ALTER FEATURE … SET STATUS.

FeatureQL
ALTER FEATURE FM.TUTORIALS.LIFECYCLE.BASKET_VALUE_WITH_TAX SET STATUS = 'ACTIVE';
Result
ERROR UE/LIFECYCLE-DEPENDENCY-FORBIDDEN

The registry refuses, and the reason is the whole point of the feature: an ACTIVE feature may only depend on other ACTIVE features, and its dependency is still DEV. Promotion travels upward through the graph — dependencies first.

Promote both together and the pair is consistent, so it passes. ALTER FEATURES takes an explicit name list; mutation never accepts WHERE or LIKE.

FeatureQL
ALTER FEATURES FM.TUTORIALS.LIFECYCLE.BASKET_VALUE, FM.TUTORIALS.LIFECYCLE.BASKET_VALUE_WITH_TAX
SET STATUS = 'ACTIVE';
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.LIFECYCLE.BASKET_VALUEALTEREDFeature successfully altered
FM.TUTORIALS.LIFECYCLE.BASKET_VALUE_WITH_TAXALTEREDFeature successfully altered

BETA sits between the two: production-ready but still experimental, so it may depend on BETA and ACTIVE features and may be consumed by DEV and BETA ones. Use it when consumers should align with the point of contact before relying on the feature.

Retirement works the same way, in reverse

Months later the tax logic moves elsewhere and you want to deprecate BASKET_VALUE. Its consumer is still ACTIVE.

FeatureQL
ALTER FEATURE FM.TUTORIALS.LIFECYCLE.BASKET_VALUE SET STATUS = 'DEPRECATED';
Result
ERROR UE/LIFECYCLE-DEPENDENCY-FORBIDDEN

Same rule, other direction: an ACTIVE feature may not depend on a DEPRECATED one. Retire the consumer along with it.

FeatureQL
ALTER FEATURES FM.TUTORIALS.LIFECYCLE.BASKET_VALUE, FM.TUTORIALS.LIFECYCLE.BASKET_VALUE_WITH_TAX
SET STATUS = 'DEPRECATED';
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.LIFECYCLE.BASKET_VALUEALTEREDFeature successfully altered
FM.TUTORIALS.LIFECYCLE.BASKET_VALUE_WITH_TAXALTEREDFeature successfully altered

DEPRECATED is a warning, not a wall — existing queries keep working.

FeatureQL
RETURN
    fm.tutorials.lifecycle.basket_value_with_tax
;
Result
FM.TUTORIALS.LIFECYCLE.BASKET_VALUE_WITH_TAX DOUBLE
144

What it does stop is new work built on top of it. A fresh feature is created as DEV, and DEV may only depend on DEV, BETA, or ACTIVE.

FeatureQL
CREATE OR REPLACE FEATURE FM.TUTORIALS.LIFECYCLE.BASKET_VALUE_ROUNDED AS
ROUND(FM.TUTORIALS.LIFECYCLE.BASKET_VALUE_WITH_TAX);
Result
ERROR UE/LIFECYCLE-DEPENDENCY-FORBIDDEN

HARD_DEPRECATED is the wall

When the feature is genuinely gone, mark it HARD_DEPRECATED.

FeatureQL
ALTER FEATURES FM.TUTORIALS.LIFECYCLE.BASKET_VALUE, FM.TUTORIALS.LIFECYCLE.BASKET_VALUE_WITH_TAX
SET STATUS = 'HARD_DEPRECATED';
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.LIFECYCLE.BASKET_VALUEALTEREDFeature successfully altered
FM.TUTORIALS.LIFECYCLE.BASKET_VALUE_WITH_TAXALTEREDFeature successfully altered

Now every query that references it fails at compile time, which is how you find the last stragglers before deleting anything.

FeatureQL
RETURN
    fm.tutorials.lifecycle.basket_value_with_tax
;
Result
ERROR UE/LIFECYCLE-HARD-DEPRECATED

You cannot hard-deprecate a feature that is still referenced by a PREPARED_STATEMENT — remove the serving artifact first. Metadata edits such as SET DESCRIPTION still work on hard-deprecated features, so you can leave a note pointing at the replacement.

The rules in full

StatusProduction readyWhat it means
DEVNoDefault on create. In development, not ready for production.
BETAYesProduction-ready but experimental. Can change at any time — align with the point of contact before using.
ACTIVEYesStable features in production.
DEPRECATEDYesWill be removed in the future. Cannot be used by new feature definitions that the dependency matrix forbids.
HARD_DEPRECATEDYesRemoved. Queries that reference it fail at compile time. Cannot be set while the feature is referenced by a PREPARED_STATEMENT.

Both errors above come from one matrix, read in the two directions. A feature with status X can depend on:

Status XCan use features with status
DEVDEV, BETA, ACTIVE
BETABETA, ACTIVE
ACTIVEACTIVE
DEPRECATEDACTIVE, DEPRECATED
HARD_DEPRECATEDHARD_DEPRECATED

And a feature with status X can be depended on by:

Status XCan be used by features with status
DEVDEV
BETADEV, BETA
ACTIVEDEV, BETA, ACTIVE, DEPRECATED
DEPRECATEDACTIVE, DEPRECATED
HARD_DEPRECATEDHARD_DEPRECATED

The rule applies to the dependency graph only — not to catalog position, namespace, or custom attributes.

Next