This topic explains how to adapt code that currently uses a 4.x version of the Go server-side SDK to use version 5.0 or later.
Before you migrate to 5.0, update to the latest 4.x version. Some of the changes that are mandatory in 5.0 were originally added in a 4.x version and made optional.
If you update to the latest 4.x version, deprecation warnings appear in areas of your code that need to be changed for 5.0. You can update them at your own pace while still using 4.x, rather than migrating everything simultaneously.
To learn more about updating to the latest 4.x version, visit the SDK’s GitHub repository.
The minimum Go version for LaunchDarkly SDK 5.0 is 1.14.
LaunchDarkly no longer supports lower Go versions, as stated in the End of Life policy.
Modules are supported in all modern versions of Go, and are now the standard way to write Go code. However, not all Go users use Go modules.
To support as many users as possible, we designed the 5.x SDK for use with or without modules. It has a go.mod file so it is recognized as a module, but if you use an older dependency management system like dep or govendor, the 5.x SDK will still work.
This is why we have continued to use the gopkg.in redirection service for the SDK’s import paths. Go modules with major versions greater than 1 must normally have an import path suffix like /v5, which would not work with dep and govendor. Paths that use gopkg.in are exempt from that rule.
Starting with the next major version, 6.0.0, you will only be able to use the Go SDK from module-based code.
As described below, the SDK is now made up of multiple repositories. If you do not use modules and you have pinned your dependency versions, when you update the SDK to a new version you should also update any other github.com/launchdarkly or gopkg.in/launchdarkly dependencies that were previously pinned. This is not required if you use modules, because the SDK can specify its own preferred dependency versions.
The package structure of the SDK has changed. Some packages have been added in the main repository, and other packages are now in related repositories.
There were two reasons for this. First, the main package go-server-sdk.v4 was cluttered with many things that are not necessary for normal usage of the SDK. Moving things out of this package produced a clearer basic API.
Second, there are some internal parts of the Go SDK code that are also used by other LaunchDarkly components, such as the Relay Proxy. The fact that those implementation details were visible within the main SDK package meant that they could not be changed in any backward-incompatible way without releasing a new major version of the SDK, even though application code was unlikely to be using them.
This was potentially confusing for developers, and required LaunchDarkly to maintain support for many obsolete types and methods.
The full package schema is now as follows:
gopkg.in/launchdarkly/go-sdk-server.v5
Config, LDClient, and FeatureFlagsState./interfaces: types like DataStore that are only used if you are writing a custom component, as well as interfaces for advanced features like FlagTracker./ldcomponents: configuration builders for specific areas of SDK functionality, such as configuring streaming options or logging options. To learn more, read Understanding changes to SDK configuration.gopkg.in/launchdarkly/go-sdk-common.v2
/ldlog: the SDK’s logging abstraction (formerly in go-server-sdk.v4/ldlog)./ldreason: the EvaluationDetail and EvaluationReason types (formerly in go-server-sdk.v4)./lduser: User, UserBuilder, and related methods (formerly in go-server-sdk.v4)./ldvalue: the universal JSON value type Value, and related types like OptionalString.gopkg.in/launchdarkly/go-sdk-events.v1, gopkg.in/launchdarkly/go-server-sdk-evaluation.v1: these packages are not used directly by application code.
The redis, ldconsul, and lddynamodb packages are now in separate repositories. To learn more, read Understanding changes to the data store.
Previously, the Config struct had fields for all of the SDK’s configuration options. An empty Config{} struct was not a valid configuration. Applications had to first copy from ld.DefaultConfig and then make any desired changes.
Now, most of the properties are grouped into areas of functionality, each of which has its own builder class that is only available if you are using that functionality. The Config struct has a much smaller number of fields, because most of its options are contained within one of the builders.
For example, if you use streaming mode, there are options you can set to control streaming mode, and if you use polling mode, you use a different builder that does not have the streaming options. Similarly, if you have disabled analytics events, the event-related options are not accessible.
ld.DefaultConfig no longer exists. An empty Config{} is now a valid configuration, with the same default behavior that was previously provided by ld.DefaultConfig.
The basic areas of functionality are “data source” (polling, streaming, or file data), “data store” (memory or database), events, logging, and networking.
The data source is the mechanism that the SDK uses to receive feature flag updates. Typically this is a streaming connection, but you can also configure the SDK to use polling or other sources.
For each data source type, there is a factory method whose name ends in DataSource. These methods give you a builder object with methods for whatever options are appropriate. Store that object in Config.DataSource.
Here is an example:
Streaming mode is the default. Unlike the earlier model, it is no longer possible to construct a meaningless configuration such as “use streaming mode, but set the polling interval to 1 minute” or “disable events, but set the flush interval to 10 seconds.”
Another data source option is the file-based data source. To learn more, read the API documentation for ldfiledata.DataSource().
As before, the default data store is a simple in-memory cache.
If you want to use a persistent datastore database integration, you must call a DataStore factory method for that integration. This gives you a builder object with whatever options are appropriate for that database. Next, pass the object to ldcomponents.PersistentDataStore(), a wrapper that provides caching options that are not specific to any one database but are built into the SDK. Put this into Config.DataStore.
In the 4.x API, the constructors for these integrations took a variable number of arguments representing the various options you could use. For example, packagename.NewFeatureStore(packagename.OptionA(value), packagename.OptionB(value)).
The 5.0 API uses a more concise builder pattern instead, with one builder method for each option. For example, packagename.DataStore().OptionA(value).OptionB(value).
Also, the integrations for Redis, DynamoDB, and Consul are no longer bundled in the main SDK project. To learn more, read Understanding changes to add-on integration packages.
To learn more, read Persistent data stores.
The following examples use the Redis integration:
Analytics events are enabled by default. To customize their behavior, call ldcomponents.SendEvents() to get a builder object with event-related options, and then put that object in Config.Events.
To completely disable events, set Config.Events to ldcomponents.NoEvents().
Here’s how:
You can no longer construct a meaningless configuration like “disable events, but set the flush interval to 10 seconds.”
The SDK still uses the ldlog logging API, although that package is now in gopkg.in/launchdarkly/go-sdk-common.v2 rather than gopkg.in/launchdarkly/go-server-sdk.v4.
However, for basic logging configuration, such as specifying the minimum log level or disabling logging, you no longer need to interact directly with the ldlog.Loggers type. Instead, there is a new Logging property in Config which you can set to either ldcomponents.NoLogging() or to a configuration builder that you get from ldcomponents.Logging().
Here is an example:
Options in this category affect how the SDK communicates with LaunchDarkly over HTTP/HTTPS, including connection timeout, proxy servers, and more. If you need to customize these, call ldcomponents.HTTPConfiguration() to get a builder object, configure this builder, and then put that object in Config.HTTP.
Here’s how:
Before Go SDK 4.16, the way to define user properties was to set the fields of the User struct directly. Because the fields had pointer types, this was somewhat inconvenient. Also, the mutability of the User struct and the Custom map inside it could cause concurrency problems.
Version 4.16 added the UserBuilder type as a safer and more convenient way to define user properties. As of version 5.0, this is the only way, if you need to use the full set of properties. The NewUser and NewAnonymousUser constructors still exist for simple users. The User struct no longer has any exported fields and cannot be modified by application code after it is built. It now has getter methods like GetName() if you need to inspect existing property values.
The 5.0 User and UserBuilder types, and the NewUser(), NewAnonymousUser(), and NewUserBuilder() functions, are now in gopkg.in/launchdarkly/go-sdk-common.v2/lduser instead of gopkg.in/launchdarkly/go-server-sdk.v4.
Here is an example:
Before Go SDK 4.16, if you wanted to describe a value that could be of any valid JSON type, you would use Go’s universal value type interface{}.
For example, LDClient.JsonVariation() returned that type, and also took a default value parameter of that type.
However, there were two problems with interface{}:
Version 4.16 added the immutable class ldvalue.Value as a replacement for interface{}, along with a LDClient.JSONVariation() method that uses LDValue, and UserBuilder methods for setting user attributes of any JSON type.
As of version 5.0, these are the only options. interface{} is no longer used in the public API.
The Value type for the 5.0 SDK is now in gopkg.in/launchdarkly/go-sdk-common.v2/ldvalue instead of gopkg.in/launchdarkly/go-sdk-common.v1/ldvalue.
Here is an example:
The EvaluationDetail and EvaluationReason types were formerly in gopkg.in/launchdarkly/go-server-sdk.v4 and are now in gopkg.in/launchdarkly/go-sdk-common.v2/ldreason.
We have modified them in two ways:
EvaluationReason is now a struct rather than an interface. Instead of casting it to another type such as EvaluationReasonRuleMatch to get specific properties, it provides getter methods for those properties.VariationIndex property of EvaluationDetail was previously a pointer of type *int, which would be nil if evaluation failed. This was unsafe due to mutability and the potential for nil pointer panics. It now uses the immutable type ldvalue.OptionalInt instead, which allows an “undefined” value to be represented without using pointers.Here is an example:
The AllFlagsState method of LDClient, which returns a snapshot of flag values and metadata, has been changed as follows:
FeatureFlagsState and was in the main SDK package. It is now called AllFlags and is in the new package go-server-sdk.v5/interfaces/flagstate. Moving it out of the main package helps to streamline the API, and also allows it to be referenced from the interfaces in the interfaces package without causing an import cycle.AllFlags are slightly different than the methods of the earlier FeatureFlagsState, although most applications will not need to use these. There is also a builder for constructing instances of it in test code.AllFlagsState are now in the flagstate package, and have been renamed to OptionClientSideOnly, OptionDetailsOnlyForTrackedFlags, and OptionWithReasons.The Consul, DynamoDB, and Redis integrations are no longer bundled in the main SDK project. Instead, they are maintained in separate repositories: github.com/launchdarkly/go-server-sdk-redis-redigo, github.com/launchdarkly/go-server-sdk-dynamodb, and github.com/launchdarkly/ldconsul.
The new import paths for those packages are the same as the repository locations: github.com/launchdarkly/go-server-sdk-redis-redigo, etc.
Also, the configuration syntax has changed. To learn more about configuration details, read Understanding changes to the data store.
There are two ways you can configure the SDK to use the Relay Proxy.
You can use it in:
The syntax for configuring these has changed in version 5.x.
Most applications use either the default in-memory storage or one of the database integrations provided by LaunchDarkly (Consul, DynamoDB, Redis), but the data store interface (formerly called “feature store”) has always been public so developers can write their own integrations.
Starting in Go SDK 5.0, this model has been changed to make it easier to implement database integrations. The basic concepts are the same. The SDK defines its own “data kinds,” such as feature flags and user segments. The data store must provide a way to add and update items of any of these kinds without knowing anything about their properties except the key and version.
The main changes are:
The interface for “data source” components that receive feature flag data, either from LaunchDarkly or from some other source, such as a file, has also changed slightly to use the new data store model.
To learn more, read the API documentation for the interfaces.DataStore, interfaces.PersistentDataStore, and interfaces.DataSource interfaces. You may also want to review the SDK’s source code for one of the LaunchDarkly database integrations, such as the ldredis package, to learn how it changed from the previous major version.
All types and methods that were marked as deprecated in the last 4.x release have been removed from the 5.0 release.
If you were using these with a recent version previously, you should already have received deprecation warnings at compile time, with suggestions about how to replace them.
The 5.0 release also includes new features as described in the release notes.
These include:
LDClient.GetFlagTracker().LDClient.GetDataSourceStatusProvider().LDClient.GetDataStoreStatusProvider().LDClient.WithEventsDisabled().LDConfig.Logging and ldcomponents.LoggingConfigurationBuilder.ldtestdata package to set flag values or rules programmatically, as an alternative to using ldfiledata.To learn more, read the API documentation for these classes and methods.