Migrating the Elixir SDK for Featurevisor v4
- Minimal migration path
- Breaking changes
- Root condition reasonBreaking
- Datafile update resultBreaking
- Module callbacksBreaking
- Detailed evaluation fieldsBreaking
- Number getterBreaking
- Language differences
- OpenFeature
- Migration checklist
Upgrade Elixir applications to SDK 2.0.0 for Featurevisor v4 support.
Follow the shared migration order: prepare canonical prerequisite datafiles, upgrade consumers, then upgrade the project CLI. Before publishing new runtime fields, follow Adopt new syntax safely.
Convert Groups before upgrading
This SDK silently ignores legacy feature ranges, with no compatibility handling or warning. Convert Groups to flat list requirements while still on the previous compatible SDK and v3 CLI, then publish all datafiles, caches and bundled copies before upgrading. Unconverted files can change cohorts or enable features without a variation. Old cohorts are not guaranteed. Follow Groups removed.
Minimal migration path#
Most Elixir application code does not change. The following APIs were already available in the previous SDK line and keep the same role:
Featurevisor.create_featurevisor/1andFeaturevisor.set_datafile/3Featurevisor.enabled?/4,Featurevisor.get_variation/4, and feature variable gettersFeaturevisor.get_global_variable/4and its typed getters- the corresponding detailed
evaluate_*functions set_sticky_features/3,set_sticky_variables/3, and child instancesget_feature_evaluations/4andget_global_variable_evaluations/4- module lifecycle, diagnostics, and the existing split sticky events
Elixir already used canonical sticky APIs and separate feature and global variable evaluation helpers in the previous SDK line. Do not replace them with JavaScript compatibility names that never existed in this SDK.
If an application already uses before_evaluation and after_evaluation and does not inspect renamed evaluation struct fields, its source migration can be limited to updating the packages and testing its datafiles.
See the Elixir SDK guide for the complete current API.
Breaking changes#
Root condition reason Breaking#
An unmet root gate reports conditions_unmet for feature flags and global variables, replacing the v3 required reason for feature flags and required_features_unmet for global variables. This applies to attribute, segment, feature and variable leaves in root conditions. Update callbacks, analytics and test expectations. Variation and feature variable evaluations retain their disabled reasons. See the shared migration guidance.
unmet_requirement = evaluation.reason == :requiredunmet_requirement = evaluation.reason == :conditions_unmetDatafile update result Breaking#
Featurevisor.set_datafile/3 now returns true for accepted updates, including unchanged content, and false for rejected updates or closed instances. Replace matches on :ok and {:error, reason} with boolean handling. Rejection details remain available through diagnostics, and the previous valid datafile is retained.
case Featurevisor.set_datafile(f, datafile) do :ok -> IO.puts("Datafile accepted") {:error, reason} -> IO.inspect(reason, label: "Datafile rejected")endif Featurevisor.set_datafile(f, datafile) do IO.puts("Datafile accepted")else IO.puts("Datafile rejected. Check diagnostics.")endUse if to handle a rejected update without failing a pattern match. Merge remains the default. The acceptance result does not report whether any evaluated value changed.
Module callbacks Breaking#
The deprecated feature only before and after fields are removed from Featurevisor.Module. before_evaluation and after_evaluation were already available in the previous SDK line, so applications can adopt them before the breaking upgrade.
module = %Featurevisor.Module{ name: "feature_context", before: fn options -> %{options | context: Map.put(options.context, "country", "nl")} end, bucket_value: fn options -> options.bucket_value end, after: fn evaluation, _options -> evaluation end}f = Featurevisor.create_featurevisor(%{ datafile: datafile, modules: [module]})module = %Featurevisor.Module{ name: "feature_context", before_evaluation: fn options -> if Map.has_key?(options, :feature_key) do %{options | context: Map.put(options.context, "country", "nl")} else options end end, bucket_value: fn options -> options.bucket_value end, after_evaluation: fn evaluation, _options -> evaluation end}f = Featurevisor.create_featurevisor(%{ datafile: datafile, modules: [module]})The canonical callbacks cover feature and global variable evaluations, including prerequisite evaluations. If callback logic is feature specific, check Map.get(options, :feature_key) before transforming the options or result. setup, bucket_key, bucket_value, and close are unchanged.
If modules currently define both callback families, preserve their intended ordering while consolidating them. On the previous SDK line, all legacy before callbacks run before all before_evaluation callbacks, while all after_evaluation callbacks run before all legacy after callbacks. After migration, the SDK runs only canonical callbacks in module registration order.
Detailed evaluation fields Breaking#
Rename the legacy sticky metadata field when application code inspects %Featurevisor.Evaluation{}:
evaluation = Featurevisor.evaluate_flag(f, "checkout", context)sticky_feature = evaluation.stickyrequirements = evaluation.required_featuresevaluation = Featurevisor.evaluate_flag(f, "checkout", context)sticky_feature = evaluation.sticky_featurerequirements = evaluation.conditionsconditions now holds the failed root gate expression. Use evaluation.reason == :sticky to identify a sticky result. sticky_feature is optional metadata and is not present for every sticky result.
Accumulating global variable evaluations add variable_override_paths, listing every applied authored path in execution order. Existing singular override fields still describe the final match. A failed variable prerequisite exposes conditions and the reason :conditions_unmet.
Number getter Breaking#
Variables that hold numbers have the type number, so the typed getter is now get_variable_number/5. It reads number variables and the double variables of datafiles built before v4. get_variable_double/5 and get_global_variable_double/4 were removed. Rename calls to get_variable_number/5 and get_global_variable_number/4, which return the same values.
threshold = Featurevisor.get_global_variable_double(f, "freeShippingThreshold", context)threshold = Featurevisor.get_global_variable_number(f, "freeShippingThreshold", context)Language differences#
Datafile input remains straightforward#
Elixir maps and lists retain the distinction between JSON objects and arrays. Both existing initialization forms remain valid:
datafile = File.read!("datafile.json")f = Featurevisor.create_featurevisor(%{datafile: datafile})datafile = "datafile.json" |> File.read!() |> Jason.decode!()f = Featurevisor.create_featurevisor(%{datafile: datafile})No alternative reader or wrapper type is required for v4. Keep generated datafile keys as strings when passing decoded maps.
OpenFeature #
The Elixir repository publishes featurevisor and the separate featurevisor_openfeature Hex package together. Upgrade both packages to 2.0.0. If FeaturevisorOpenFeature.Provider receives an existing f, that instance must use the matching base SDK.
The provider constructor, key mapping, and resolver usage remain unchanged. The updated provider maps :conditions_unmet to OpenFeature's disabled reason. Keeping provider and core releases aligned also preserves datafile update notifications.
See Elixir OpenFeature usage and the shared OpenFeature provider guide.
Migration checklist#
Required for the migration#
- Prepare and deploy canonical
conditionsdatafiles before changing the runtime. - Replace module
beforeandafterfields withbefore_evaluationandafter_evaluation. - Rename
evaluation.stickytoevaluation.sticky_featurewhere used. - Rename
get_variable_doubleandget_global_variable_doublecalls to their_numberforms. - Upgrade
featurevisorandfeaturevisor_openfeaturetogether when applicable. - Test feature, variation, feature variable, global variable, sticky, child, and module behaviour.
- Upgrade the project CLI last, review mutation order and date and regex comparisons, and keep a compatible rollback datafile.
Only if adopting optional features#
Follow Adopt new syntax safely before publishing new datafile syntax. Upgrade every CLI runner before adding checks or scenarios.

