Migrating the Go SDK for Featurevisor v4
- Minimal migration path
- Breaking changes
- Nil slices are nullBreaking
- Root condition reasonBreaking
- Datafile update resultBreaking
- Module callbacksBreaking
- Evaluation details and requirement typesBreaking
- Language differences
- OpenFeature
- Migration checklist
Upgrade Go applications to SDK 4.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.
Update core imports from github.com/featurevisor/featurevisor-go/v3 to github.com/featurevisor/featurevisor-go/v4:
import featurevisor "github.com/featurevisor/featurevisor-go/v3"import featurevisor "github.com/featurevisor/featurevisor-go/v4"go get github.com/featurevisor/featurevisor-go/[email protected]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 application calls do not change. CreateFeaturevisor, IsEnabled, GetVariation, feature variable getters, global variable getters, child instances, canonical sticky methods, and the specific sticky events were already available in the previous SDK line.
If your application already uses BeforeEvaluation and AfterEvaluation, and does not inspect legacy evaluation details or construct legacy requirement types, its source migration can be small:
- Follow the shared migration order and prepare legacy prerequisite datafiles first.
- Upgrade the Go SDK to 4.0.0.
- Update the removed APIs below, then deploy every Go consumer.
- Enable each new datafile capability only after every consumer of that datafile supports it.
For staggered application deployments, follow mixed version deployments. V4 builds emit schemaVersion: "4", so the schema number is not a capability check.
Breaking changes#
Nil slices are null Breaking#
A typed nil slice such as var roles []string represents null, not an empty array. Both positive and negative membership operators return false for it. In particular, notIncludes no longer matches a nil slice. This aligns with Go's JSON encoding and the existing empty behaviour.
isNull now matches typed nil slices, maps and pointers. notNull returns false for them. Both still return false for a missing path, while exists remains true for a present nil value. Nonnil empty containers are not null. Comparisons with literal null and null elements in arrays use the same distinction.
If a user has no roles and should satisfy a condition such as roles notIncludes admin, pass a nonnil empty slice:
var roles []stringcontext := featurevisor.Context{ "roles": roles,}roles := []string{}context := featurevisor.Context{ "roles": roles,}| Context value | includes admin | notIncludes admin | includesAny [admin] | notIncludesAny [admin] | empty | isNull |
|---|---|---|---|---|---|---|
[]string(nil) | false | false | false | false | false | true |
[]string{} | false | true | false | true | true | false |
The same distinction applies to evaluated variable arrays in conditions. Nil candidate slices are also invalid for in, notIn and the array list operators. Reached test checks report invalid array operands as comparison errors. Review slice values in contexts and sticky values before deploying. Do not replace null with an empty array unless it means an empty list in your application.
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.
unmetRequirement := evaluation.Reason == featurevisor.EvaluationReasonRequiredunmetRequirement := evaluation.Reason == featurevisor.EvaluationReasonConditionsUnmetDatafile update result Breaking#
SetDatafile now returns bool: true for accepted updates, including unchanged content, and false for rejected updates or closed instances. Existing calls can ignore it. Update any interfaces or function types that declare the old setter signature. Rejection keeps the previous valid datafile, with details in diagnostics.
f.SetDatafile(datafile)accepted := f.SetDatafile(datafile)if !accepted { println("Datafile rejected. Check diagnostics.")}Module callbacks Breaking#
The previous SDK line exposed both the old feature only callbacks and the canonical callbacks. After migration, the SDK removes Before and After. Existing modules that already implement BeforeEvaluation and AfterEvaluation need no rename.
module := &featurevisor.FeaturevisorModule{ Before: func(options featurevisor.EvaluateOptions) featurevisor.EvaluateOptions { return options }, After: func(evaluation featurevisor.Evaluation, _ featurevisor.EvaluateOptions) featurevisor.Evaluation { return evaluation },}module := &featurevisor.FeaturevisorModule{ BeforeEvaluation: func(options featurevisor.EvaluateOptions) featurevisor.EvaluateOptions { return options }, AfterEvaluation: func(evaluation featurevisor.Evaluation, _ featurevisor.EvaluateOptions) featurevisor.Evaluation { return evaluation },}The canonical callbacks run for feature and global variable evaluations, including prerequisite evaluations. If the old callback was intended only for features, check options.FeatureKey before applying its transformation. An accumulating override program is one evaluation, so callbacks wrap the complete program.
See Go SDK modules.
Evaluation details and requirement types Breaking#
The compatibility fields Evaluation.Required and Evaluation.Sticky are removed. Read their canonical replacements:
requirements := evaluation.Requiredsticky := evaluation.Stickyrequirements := evaluation.Conditionssticky := evaluation.StickyFeatureDetailed evaluations expose unmet root gates in Conditions. Accumulating global variable evaluations also expose VariableOverridePaths. The singular override key, path, and index describe the last matching program step.
Applications that construct typed datafiles directly must replace the old requirement types. Required and RequiredWithVariation are removed. The old RequiredFeature struct becomes the RequiredFeature leaf or group interface, with RequiredFeatureOptions as its object form:
required := []featurevisor.Required{ featurevisor.RequiredFeature{ Feature: "checkout", },}required := []featurevisor.RequiredFeature{ featurevisor.RequiredFeatureOptions{ Feature: "checkout", },}Do not translate legacy { "key": ..., "variation": ... } prerequisite objects at runtime. Rebuild them as canonical { "feature": ..., "variation": ... } entries before upgrading. Schema required property lists inside object variable schemas are unrelated and remain valid.
Language differences#
Unknown dates#
The zero value time.Time{} is the valid instant 0001-01-01T00:00:00Z, not an unknown date. It can match before a modern launch time. Omit the context key or use a nil pointer when the date is unknown. This is not a breaking change. See native date values.
Sticky APIs that stay the same#
The current Go line already uses the canonical public sticky API. Keep FeaturevisorOptions.StickyFeatures, FeaturevisorOptions.StickyVariables, SpawnOptions, SetStickyFeatures, SetStickyVariables, EventNameStickyFeaturesSet, and EventNameStickyVariablesSet.
f := featurevisor.CreateFeaturevisor(featurevisor.FeaturevisorOptions{ Datafile: datafile, StickyFeatures: &stickyFeatures, StickyVariables: &stickyVariables,})f.SetStickyFeatures(nextFeatures, true)f.SetStickyVariables(nextVariables, true)There is no combined sticky event or public SetSticky migration in this SDK. Do not introduce one while updating the application.
OpenFeature #
The Go OpenFeature provider is a separate module. Replace github.com/featurevisor/featurevisor-go/openfeature/v3 imports with github.com/featurevisor/featurevisor-go/openfeature/v4 and upgrade it with core:
go get github.com/featurevisor/featurevisor-go/[email protected]go get github.com/featurevisor/featurevisor-go/openfeature/[email protected]The OpenFeature provider can require a newer Go toolchain than core. Applications using only core do not inherit the provider's Go minimum. Check both module manifests and each consuming application's toolchain before upgrading.
Provider key grammar and the standard getters remain unchanged. A Featurevisor result with reason conditions_unmet maps to the OpenFeature disabled reason without a provider error. Test guarded flags and global variables through your OpenFeature client before publishing variable requirements.
The provider distinguishes a received empty targeting key from an absent one: empty replaces the configured userId or targeting field, while omission preserves it. Go's ordinary upstream NewEvaluationContext("", attributes) omits that empty key during flattening. Put "targetingKey": "" in the attributes when you need the override, and check inherited nonempty keys in the final client context.
See Go SDK OpenFeature integration and the OpenFeature provider guide.
Migration checklist#
Required for the migration#
- Prepare and deploy canonical prerequisite datafiles before upgrading the runtime.
- Use nonnil empty slices where empty list membership is intended, and review nil slices in contexts and sticky values.
- Replace module
BeforeandAftercallbacks when used. - Replace removed evaluation fields and typed requirement declarations when used.
- Keep ordinary getters, canonical sticky APIs, and specific sticky events unchanged.
- Upgrade the separate OpenFeature provider together with its compatible core SDK.
- Check the provider's Go toolchain minimum separately from core's minimum.
- Review mutation order and date and regex comparisons before rebuilding, and keep a prepared v3 datafile for rollback.
Only if adopting optional features#
Follow Adopt new syntax safely before publishing new datafile syntax. Upgrade every CLI runner before adding checks or scenarios.

