diff --git a/config/_default/menus/main.en.yaml b/config/_default/menus/main.en.yaml index 2cbf178c35d..4a33f1c794a 100644 --- a/config/_default/menus/main.en.yaml +++ b/config/_default/menus/main.en.yaml @@ -5617,21 +5617,51 @@ menu: parent: software_delivery_heading identifier: feature_flags weight: 80000 - - name: Setup - url: feature_flags/setup + - name: Client SDKs + url: feature_flags/client parent: feature_flags - identifier: feature_flags_setup + identifier: feature_flags_client weight: 1 - name: Android and Android TV - url: feature_flags/setup/android - parent: feature_flags_setup - identifier: feature_flags_setup_android + url: feature_flags/client/android + parent: feature_flags_client + identifier: feature_flags_client_android weight: 101 - name: iOS and tvOS - url: feature_flags/setup/ios - parent: feature_flags_setup - identifier: feature_flags_setup_ios + url: feature_flags/client/ios + parent: feature_flags_client + identifier: feature_flags_client_ios weight: 102 + - name: Server SDKs + url: feature_flags/server + parent: feature_flags + identifier: feature_flags_server + weight: 2 + - name: Go + url: feature_flags/server/go + parent: feature_flags_server + identifier: feature_flags_server_go + weight: 201 + - name: Java + url: feature_flags/server/java + parent: feature_flags_server + identifier: feature_flags_server_java + weight: 202 + - name: Node.js + url: feature_flags/server/nodejs + parent: feature_flags_server + identifier: feature_flags_server_nodejs + weight: 203 + - name: Python + url: feature_flags/server/python + parent: feature_flags_server + identifier: feature_flags_server_python + weight: 204 + - name: Ruby + url: feature_flags/server/ruby + parent: feature_flags_server + identifier: feature_flags_server_ruby + weight: 205 - name: MCP Server url: feature_flags/feature_flag_mcp_server parent: feature_flags diff --git a/content/en/feature_flags/_index.md b/content/en/feature_flags/_index.md index b9ae3b306ab..f223d3bf2cc 100644 --- a/content/en/feature_flags/_index.md +++ b/content/en/feature_flags/_index.md @@ -5,9 +5,12 @@ further_reading: - link: "/getting_started/feature_flags/" tag: "Documentation" text: "Getting started with Feature Flags" -- link: "/feature_flags/setup/" +- link: "/feature_flags/client/" tag: "Documentation" - text: "Set up Feature Flags for your applications" + text: "Set up Feature Flags for client-side applications" +- link: "/feature_flags/server/" + tag: "Documentation" + text: "Set up Feature Flags for server-side applications" - link: "/feature_flags/guide/migrate_from_statsig" tag: "Guide" text: "Migrate Your Feature Flags from Statsig" diff --git a/content/en/feature_flags/setup/_index.md b/content/en/feature_flags/client/_index.md similarity index 69% rename from content/en/feature_flags/setup/_index.md rename to content/en/feature_flags/client/_index.md index ebe60724c0b..74d20ac29ab 100644 --- a/content/en/feature_flags/setup/_index.md +++ b/content/en/feature_flags/client/_index.md @@ -1,6 +1,8 @@ --- -title: Set Up Feature Flags -description: Set up Datadog Feature Flags for your mobile applications. +title: Client-Side Feature Flags +description: Set up Datadog Feature Flags for client-side applications. +aliases: + - /feature_flags/setup/ further_reading: - link: "/feature_flags/" tag: "Documentation" @@ -8,6 +10,9 @@ further_reading: - link: "/getting_started/feature_flags/" tag: "Documentation" text: "Getting Started with Feature Flags" +- link: "feature_flags/server/" + tag: "Documentation" + text: "Server-Side Feature Flags" --- {{< callout url="http://datadoghq.com/product-preview/feature-flags/" >}} @@ -18,7 +23,7 @@ Feature Flags are in Preview. Complete the form to request access. Set up Datadog Feature Flags for your applications. Follow the platform-specific guides below to integrate feature flags into your application and start collecting feature flag data: -{{< partial name="feature_flags/feature_flags_setup.html" >}} +{{< partial name="feature_flags/feature_flags_client.html" >}} ## Further reading diff --git a/content/en/feature_flags/setup/android.md b/content/en/feature_flags/client/android.md similarity index 98% rename from content/en/feature_flags/setup/android.md rename to content/en/feature_flags/client/android.md index dac6fd1dd0c..8290ba519b1 100644 --- a/content/en/feature_flags/setup/android.md +++ b/content/en/feature_flags/client/android.md @@ -1,10 +1,12 @@ --- title: Android and Android TV Feature Flags description: Set up Datadog Feature Flags for Android and Android TV applications. +aliases: + - /feature_flags/setup/android/ further_reading: -- link: "/feature_flags/setup/" +- link: "/feature_flags/client/" tag: "Documentation" - text: "Feature Flags Setup" + text: "Client-Side Feature Flags" - link: "/real_user_monitoring/android/" tag: "Documentation" text: "Android and Android TV Monitoring" diff --git a/content/en/feature_flags/setup/ios.md b/content/en/feature_flags/client/ios.md similarity index 99% rename from content/en/feature_flags/setup/ios.md rename to content/en/feature_flags/client/ios.md index b1f37d76550..a3fd53e8ba4 100644 --- a/content/en/feature_flags/setup/ios.md +++ b/content/en/feature_flags/client/ios.md @@ -1,10 +1,12 @@ --- title: iOS and tvOS Feature Flags description: Set up Datadog Feature Flags for iOS and tvOS applications. +aliases: + - /feature_flags/setup/ios/ further_reading: -- link: "/feature_flags/setup/" +- link: "/feature_flags/client/" tag: "Documentation" - text: "Feature Flags Setup" + text: "Client-Side Feature Flags" - link: "/real_user_monitoring/ios/" tag: "Documentation" text: "iOS and tvOS Monitoring" diff --git a/content/en/feature_flags/server/_index.md b/content/en/feature_flags/server/_index.md new file mode 100644 index 00000000000..67c69550648 --- /dev/null +++ b/content/en/feature_flags/server/_index.md @@ -0,0 +1,72 @@ +--- +title: Server-Side Feature Flags +description: Set up Datadog Feature Flags for server-side applications. +further_reading: +- link: "/feature_flags/client/" + tag: "Documentation" + text: "Client-Side Feature Flags" +- link: "/remote_configuration/" + tag: "Documentation" + text: "Remote Configuration" +--- + +{{< callout url="http://datadoghq.com/product-preview/feature-flags/" >}} +Feature Flags are in Preview. Complete the form to request access. +{{< /callout >}} + +## Overview + +Datadog Feature Flags for server-side applications allow you to remotely control feature availability, run experiments, and roll out new functionality with confidence. Server-side SDKs integrate with the Datadog APM tracer and use Remote Configuration to receive flag updates in real time. + +This guide covers the common setup required for all server-side SDKs, including Agent configuration and application environment variables. Select your language or framework to view SDK-specific setup instructions: + +{{< partial name="feature_flags/feature_flags_server.html" >}} + +## Prerequisites + +Before setting up server-side feature flags, ensure you have: + +- **Datadog Agent 7.55 or later** installed and running +- **Datadog API key** configured +- **APM tracing** enabled in your application + +## Agent configuration + +Server-side feature flags use [Remote Configuration][1] to deliver flag configurations to your application. Enable Remote Configuration in your Datadog Agent by setting `DD_REMOTE_CONFIGURATION_ENABLED=true` or adding `remote_configuration.enabled: true` to your `datadog.yaml`. + +See the [Remote Configuration documentation][1] for detailed setup instructions across different deployment environments. + +### Polling interval + +The Agent polls Datadog for configuration updates at a configurable interval. This interval determines the average time between making a flag change in the UI and the change becoming available to your application. + +{{< code-block lang="bash" >}} +# Optional: Configure polling interval (default: 60s) +DD_REMOTE_CONFIGURATION_REFRESH_INTERVAL=10s +{{< /code-block >}} + +[1]: /remote_configuration + +## Application configuration + +Configure your application with the standard Datadog environment variables. These are common across all server-side SDKs: + +{{< code-block lang="bash" >}} +# Required: Service identification +DD_SERVICE= +DD_ENV= +DD_VERSION= + +# Agent connection (if not using default localhost:8126) +DD_AGENT_HOST=localhost +DD_TRACE_AGENT_PORT=8126 + +# Enable Remote Configuration in the tracer +DD_REMOTE_CONFIG_ENABLED=true +{{< /code-block >}} + +
Some SDKs require additional experimental flags to enable feature flagging. See the SDK-specific documentation for details.
+ +## Further reading + +{{< partial name="whats-next/whats-next.html" >}} diff --git a/content/en/feature_flags/server/go.md b/content/en/feature_flags/server/go.md new file mode 100644 index 00000000000..635e06a24a1 --- /dev/null +++ b/content/en/feature_flags/server/go.md @@ -0,0 +1,279 @@ +--- +title: Go Feature Flags +description: Set up Datadog Feature Flags for Go applications. +further_reading: +- link: "/feature_flags/server/" + tag: "Documentation" + text: "Server-Side Feature Flags" +- link: "/tracing/trace_collection/dd_libraries/go/" + tag: "Documentation" + text: "Go Tracing" +--- + +{{< callout url="http://datadoghq.com/product-preview/feature-flags/" >}} +Feature Flags are in Preview. Complete the form to request access. +{{< /callout >}} + +## Overview + +This page describes how to instrument your Go application with the Datadog Feature Flags SDK. The Go SDK integrates with [OpenFeature][1], an open standard for feature flag management, and uses the Datadog tracer's Remote Configuration to receive flag updates in real time. + +This guide explains how to install and enable the SDK, create an OpenFeature client, and evaluate feature flags in your application. + +## Prerequisites + +Before setting up the Go Feature Flags SDK, ensure you have: + +- **Datadog Agent** with [Remote Configuration][2] enabled +- **Datadog Go tracer** `dd-trace-go` version 2.4.0 or later + +Set the following environment variables: + +{{< code-block lang="bash" >}} +# Required: Enable the feature flags provider +DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true + +# Required: Service identification +DD_SERVICE= +DD_ENV= +{{< /code-block >}} + +## Installation + +Install the Datadog OpenFeature provider package: + +{{< code-block lang="bash" >}} +go get github.com/DataDog/dd-trace-go/v2/openfeature +{{< /code-block >}} + +You also need the OpenFeature Go SDK: + +{{< code-block lang="bash" >}} +go get github.com/open-feature/go-sdk/openfeature +{{< /code-block >}} + +## Initialize the SDK + +Start the Datadog tracer and register the Datadog OpenFeature provider. The tracer must be started first because it enables Remote Configuration, which delivers flag configurations to your application. + +### Blocking initialization + +Use `SetProviderAndWait` to block evaluation until the initial flag configuration is received. This ensures flags are ready before your application starts handling requests. + +{{< code-block lang="go" >}} +package main + +import ( + "log" + + "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer" + ddopenfeature "github.com/DataDog/dd-trace-go/v2/openfeature" + "github.com/open-feature/go-sdk/openfeature" +) + +func main() { + // Start the Datadog tracer (enables Remote Config) + tracer.Start() + defer tracer.Stop() + + // Create the Datadog OpenFeature provider + provider, err := ddopenfeature.NewDatadogProvider(ddopenfeature.ProviderConfig{}) + if err != nil { + log.Fatalf("Failed to create provider: %v", err) + } + defer provider.Shutdown() + + // Register the provider and wait for initialization (default 30s timeout) + if err := openfeature.SetProviderAndWait(provider); err != nil { + log.Fatalf("Failed to set provider: %v", err) + } + + // Create the OpenFeature client + client := openfeature.NewClient("my-service") + + // Your application code here +} +{{< /code-block >}} + +To specify a custom timeout, use `SetProviderAndWaitWithContext`: + +{{< code-block lang="go" >}} +ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) +defer cancel() + +if err := openfeature.SetProviderAndWaitWithContext(ctx, provider); err != nil { + log.Fatalf("Failed to set provider: %v", err) +} +{{< /code-block >}} + +### Non-blocking initialization + +Use `SetProvider` to register the provider without waiting. Flag evaluations return default values until the configuration is received. + +{{< code-block lang="go" >}} +package main + +import ( + "log" + + "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer" + ddopenfeature "github.com/DataDog/dd-trace-go/v2/openfeature" + "github.com/open-feature/go-sdk/openfeature" +) + +func main() { + // Start the Datadog tracer (enables Remote Config) + tracer.Start() + defer tracer.Stop() + + // Create the Datadog OpenFeature provider + provider, err := ddopenfeature.NewDatadogProvider(ddopenfeature.ProviderConfig{}) + if err != nil { + log.Fatalf("Failed to create provider: %v", err) + } + defer provider.Shutdown() + + // Register the provider without waiting + openfeature.SetProvider(provider) + + // Create the OpenFeature client + client := openfeature.NewClient("my-service") + + // Your application code here + // Flag evaluations return defaults until configuration is received +} +{{< /code-block >}} + +## Create a client + +Create an OpenFeature client to evaluate flags. You can create multiple clients with different names for different parts of your application: + +{{< code-block lang="go" >}} +// Create a client for your application +client := openfeature.NewClient("my-service") +{{< /code-block >}} + +## Set the evaluation context + +Define an evaluation context that identifies the user or entity for flag targeting. The evaluation context includes attributes used to determine which flag variations should be returned: + +{{< code-block lang="go" >}} +evalCtx := openfeature.NewEvaluationContext( + "user-123", // Targeting key (typically user ID) + map[string]interface{}{ + "email": "user@example.com", + "country": "US", + "tier": "premium", + "age": 25, + }, +) +{{< /code-block >}} + +The targeting key is used for consistent traffic distribution (percentage rollouts). Additional attributes enable targeting rules, such as "enable for users in the US" or "enable for premium tier users" in the example above. + +## Evaluate flags + +After setting up the provider and creating a client, you can evaluate flags throughout your application. Flag evaluation is local and fast—the SDK uses locally cached configuration data, so no network requests occur during evaluation. + +Each flag is identified by a key (a unique string) and can be evaluated with a typed method that returns a value of the expected type. If the flag doesn't exist or cannot be evaluated, the SDK returns the provided default value. + +### Boolean flags + +Use `BooleanValue` for flags that represent on/off or true/false conditions: + +{{< code-block lang="go" >}} +ctx := context.Background() + +enabled, err := client.BooleanValue(ctx, "new-checkout-flow", false, evalCtx) +if err != nil { + log.Printf("Error evaluating flag: %v", err) +} + +if enabled { + showNewCheckout() +} else { + showLegacyCheckout() +} +{{< /code-block >}} + +### String flags + +Use `StringValue` for flags that select between multiple variants or configuration strings: + +{{< code-block lang="go" >}} +theme, err := client.StringValue(ctx, "ui-theme", "light", evalCtx) +if err != nil { + log.Printf("Error evaluating flag: %v", err) +} + +switch theme { +case "dark": + setDarkTheme() +case "light": + setLightTheme() +default: + setLightTheme() +} +{{< /code-block >}} + +### Numeric flags + +For numeric flags, use `IntValue` or `FloatValue`. These are appropriate when a feature depends on a numeric parameter such as a limit, percentage, or multiplier: + +{{< code-block lang="go" >}} +maxItems, err := client.IntValue(ctx, "cart-max-items", 20, evalCtx) +if err != nil { + log.Printf("Error evaluating flag: %v", err) +} + +discountRate, err := client.FloatValue(ctx, "discount-rate", 0.0, evalCtx) +if err != nil { + log.Printf("Error evaluating flag: %v", err) +} +{{< /code-block >}} + +### Object flags + +For structured data, use `ObjectValue`. This returns a value that can be type-asserted to maps or other complex types: + +{{< code-block lang="go" >}} +config, err := client.ObjectValue(ctx, "feature-config", map[string]interface{}{ + "maxRetries": 3, + "timeout": 30, +}, evalCtx) +if err != nil { + log.Printf("Error evaluating flag: %v", err) +} + +// Type assert to access the configuration +if configMap, ok := config.(map[string]interface{}); ok { + maxRetries := configMap["maxRetries"] + timeout := configMap["timeout"] + // Use configuration values +} +{{< /code-block >}} + +### Flag evaluation details + +When you need more than just the flag value, use the `*ValueDetails` methods. These return both the evaluated value and metadata explaining the evaluation: + +{{< code-block lang="go" >}} +details, err := client.BooleanValueDetails(ctx, "new-feature", false, evalCtx) +if err != nil { + log.Printf("Error evaluating flag: %v", err) +} + +fmt.Printf("Value: %v\n", details.Value) +fmt.Printf("Variant: %s\n", details.Variant) +fmt.Printf("Reason: %s\n", details.Reason) +fmt.Printf("Error: %v\n", details.Error()) +{{< /code-block >}} + +Flag details help you debug evaluation behavior and understand why a user received a given value. + +[1]: https://openfeature.dev/ +[2]: /agent/remote_config/ + +## Further reading + +{{< partial name="whats-next/whats-next.html" >}} diff --git a/content/en/feature_flags/server/java.md b/content/en/feature_flags/server/java.md new file mode 100644 index 00000000000..2e3412ae6f0 --- /dev/null +++ b/content/en/feature_flags/server/java.md @@ -0,0 +1,621 @@ +--- +title: Java Feature Flags +description: Set up Datadog Feature Flags for Java applications. +further_reading: +- link: "/feature_flags/server/" + tag: "Documentation" + text: "Server-Side Feature Flags" +- link: "/tracing/trace_collection/automatic_instrumentation/dd_libraries/java/" + tag: "Documentation" + text: "Java APM and Distributed Tracing" +--- + +{{< callout url="http://datadoghq.com/product-preview/feature-flags/" >}} +Feature Flags are in Preview. Complete the form to request access. +{{< /callout >}} + +
Java Feature Flags support is experimental and requires enabling an experimental flag in the tracer. See the Configuration section for details.
+ +## Overview + +This page describes how to instrument a Java application with the Datadog Feature Flags SDK. Datadog feature flags provide a unified way to remotely control feature availability in your app, experiment safely, and deliver new experiences with confidence. + +The Java SDK integrates feature flags directly into the Datadog APM tracer and implements the [OpenFeature](https://openfeature.dev/) standard for maximum flexibility and compatibility. + +
If you're using Datadog APM and your application already has the Datadog Java tracer and Remote Configuration enabled, skip to Initialize the OpenFeature provider. You only need to add the OpenFeature dependencies and initialize the provider.
+ +## Compatibility requirements + +The Datadog Feature Flags SDK for Java requires: +- **Java 11 or higher** +- **Datadog Java APM Tracer**: Version **1.57.0** or later +- **OpenFeature SDK**: Version **1.18.2** or later +- **Datadog Agent**: Version **7.x or later** with Remote Configuration enabled +- **Datadog API Key**: Required for Remote Configuration + +For a full list of Datadog's Java version and framework support, read [Compatibility Requirements](/tracing/trace_collection/compatibility/java/). + +## Getting started + +Before you begin, make sure you've already [installed and configured the Agent](/tracing/trace_collection/automatic_instrumentation/dd_libraries/java/#install-and-configure-the-agent). + +## Installation + +Feature flagging is integrated into the Datadog Java APM tracer. You need the tracer JAR and the OpenFeature SDK dependencies. + +The `dd-java-agent-feature-flagging-bootstrap` JAR contains shared interfaces that enable the Datadog tracer (running in the bootstrap classloader) to communicate with the OpenFeature provider (running in the application classloader). This is a standard pattern for Java agents. Both JARs are required for feature flags to work. + +{{< tabs >}} +{{% tab "Gradle (Groovy)" %}} +Add the following dependencies to your `build.gradle`: + +{{< code-block lang="groovy" filename="build.gradle" >}} +dependencies { + // Datadog Java tracer (includes feature flagging) + implementation 'com.datadoghq:dd-trace-api:X.X.X' + + // OpenFeature SDK for flag evaluation + implementation 'dev.openfeature:sdk:1.18.2' + + // Datadog OpenFeature Provider + implementation 'com.datadoghq:dd-openfeature:X.X.X' + + // Datadog Feature Flagging Bootstrap (required) + implementation 'com.datadoghq:dd-java-agent-feature-flagging-bootstrap:X.X.X' +} +{{< /code-block >}} +{{% /tab %}} + +{{% tab "Gradle (Kotlin)" %}} +Add the following dependencies to your `build.gradle.kts`: + +{{< code-block lang="kotlin" filename="build.gradle.kts" >}} +dependencies { + // Datadog Java tracer (includes feature flagging) + implementation("com.datadoghq:dd-trace-api:X.X.X") + + // OpenFeature SDK for flag evaluation + implementation("dev.openfeature:sdk:1.18.2") + + // Datadog OpenFeature Provider + implementation("com.datadoghq:dd-openfeature:X.X.X") + + // Datadog Feature Flagging Bootstrap (required) + implementation("com.datadoghq:dd-java-agent-feature-flagging-bootstrap:X.X.X") +} +{{< /code-block >}} +{{% /tab %}} + +{{% tab "Maven" %}} +Add the following dependencies to your `pom.xml`: + +{{< code-block lang="xml" filename="pom.xml" >}} + + + + com.datadoghq + dd-trace-api + X.X.X + + + + + dev.openfeature + sdk + 1.18.2 + + + + + com.datadoghq + dd-openfeature + X.X.X + + + + + com.datadoghq + dd-java-agent-feature-flagging-bootstrap + X.X.X + + +{{< /code-block >}} +{{% /tab %}} +{{< /tabs >}} + +## Configuration + +If your Datadog Agent already has Remote Configuration enabled for other features (like Dynamic Instrumentation or Application Security), you can skip the Agent configuration and go directly to [Application configuration](#application-configuration). + +### Agent configuration + +Configure your Datadog Agent to enable Remote Configuration: + +{{< code-block lang="yaml" filename="datadog.yaml" >}} +# Enable Remote Configuration +remote_configuration: + enabled: true + +# Set your API key +api_key: +{{< /code-block >}} + +### Application configuration + +If your application already runs with `-javaagent:dd-java-agent.jar` and has Remote Configuration enabled (`DD_REMOTE_CONFIG_ENABLED=true`), you only need to add the experimental feature flag (`DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true`). Skip the tracer download and JVM configuration steps. + +Configure your Java application with the required environment variables or system properties: + +{{< tabs >}} +{{% tab "Environment Variables" %}} +{{< code-block lang="bash" >}} +# Required: Enable Remote Configuration in the tracer +export DD_REMOTE_CONFIG_ENABLED=true + +# Required: Enable experimental feature flagging support +export DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true + +# Required: Your Datadog API key +export DD_API_KEY= + +# Required: Service name +export DD_SERVICE= + +# Required: Environment (e.g., prod, staging, dev) +export DD_ENV= + +# Optional: Version +export DD_VERSION= + +# Start your application with the tracer +java -javaagent:path/to/dd-java-agent.jar -jar your-application.jar +{{< /code-block >}} +{{% /tab %}} + +{{% tab "System Properties" %}} +{{< code-block lang="bash" >}} +java -javaagent:path/to/dd-java-agent.jar \ + -Ddd.remote.config.enabled=true \ + -Ddd.experimental.flagging.provider.enabled=true \ + -Ddd.api.key= \ + -Ddd.service= \ + -Ddd.env= \ + -Ddd.version= \ + -jar your-application.jar +{{< /code-block >}} +{{% /tab %}} +{{< /tabs >}} + +The Datadog feature flagging system starts automatically when the tracer is initialized with both Remote Configuration and the experimental flagging provider enabled. No additional initialization code is required in your application. + +
Feature flagging requires both DD_REMOTE_CONFIG_ENABLED=true and DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true. Without the experimental flag, the feature flagging system does not start and the Provider returns the programmatic default.
+ +### Add the Java tracer to the JVM + +For instructions on how to add the `-javaagent` argument to your application server or framework, see [Add the Java Tracer to the JVM](/tracing/trace_collection/automatic_instrumentation/dd_libraries/java/#add-the-java-tracer-to-the-jvm). + +Make sure to include the feature flagging configuration flags: +- `-Ddd.remote.config.enabled=true` +- `-Ddd.experimental.flagging.provider.enabled=true` + +## Initialize the OpenFeature provider + +Initialize the Datadog OpenFeature provider in your application startup code. The provider connects to the feature flagging system running in the Datadog tracer. + +{{< code-block lang="java" >}} +import dev.openfeature.sdk.OpenFeatureAPI; +import dev.openfeature.sdk.Client; +import datadog.trace.api.openfeature.Provider; +import dev.openfeature.sdk.exceptions.ProviderNotReadyError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class App { + private static final Logger logger = LoggerFactory.getLogger(App.class); + private static Client client; + + public static void main(String[] args) throws Exception { + // Initialize the Datadog provider + logger.info("Initializing Datadog OpenFeature Provider..."); + OpenFeatureAPI api = OpenFeatureAPI.getInstance(); + + try { + // Set provider and wait for initial configuration (recommended) + api.setProviderAndWait(new Provider()); + client = api.getClient("my-app"); + logger.info("OpenFeature provider initialized successfully"); + } catch (ProviderNotReadyError e) { + // Optional: Handle gracefully - app will use default flag values + logger.warn("Provider not ready (no tracer/configuration available), continuing with defaults", e); + client = api.getClient("my-app"); + logger.info("App will use default flag values until provider is ready"); + } + + // Your application code here + } +} +{{< /code-block >}} + +Use `setProviderAndWait()` to block evaluation until the initial flag configuration is received from Remote Configuration. This ensures flags are ready before the application starts serving traffic. The default timeout is 30 seconds. + +`ProviderNotReadyError` is an OpenFeature SDK exception thrown when the provider times out during initialization. Catching it allows the application to start with default flag values if Remote Configuration is unavailable. If not caught, the exception propagates and may prevent application startup. Handle this based on your availability requirements. + +### Asynchronous initialization + +For non-blocking initialization, use `setProvider()` and listen for provider events: + +{{< code-block lang="java" >}} +import dev.openfeature.sdk.ProviderEvent; + +OpenFeatureAPI api = OpenFeatureAPI.getInstance(); +Client client = api.getClient(); + +// Listen for provider state changes +client.on(ProviderEvent.PROVIDER_READY, (event) -> { + logger.info("Feature flags ready!"); +}); + +client.on(ProviderEvent.PROVIDER_ERROR, (event) -> { + logger.error("Provider error: {}", event.getMessage()); +}); + +client.on(ProviderEvent.PROVIDER_STALE, (event) -> { + logger.warn("Provider configuration is stale"); +}); + +// Set provider asynchronously +api.setProvider(new Provider()); +{{< /code-block >}} + +## Set the evaluation context + +The evaluation context defines the subject (user, device, session) for flag evaluation. It determines which flag variations are returned based on targeting rules. + +{{< code-block lang="java" >}} +import dev.openfeature.sdk.EvaluationContext; +import dev.openfeature.sdk.MutableContext; + +// Create an evaluation context with a targeting key and attributes +EvaluationContext context = new MutableContext("user-123") + .add("email", "user@example.com") + .add("tier", "premium"); + +// Use the context for flag evaluations (see next section) +{{< /code-block >}} + +The `targetingKey` (for example, `user-123`) is the primary identifier used for consistent flag evaluations and percentage-based rollouts. It's typically a user ID, session ID, or device ID. + +## Evaluate flags + +Evaluate feature flags using the OpenFeature client. All flag types are supported: Boolean, string, integer, double, and object. + +{{< tabs >}} +{{% tab "Boolean" %}} +{{< code-block lang="java" >}} +// Simple Boolean evaluation +boolean enabled = client.getBooleanValue("checkout.new", false, context); + +if (enabled) { + // New checkout flow +} else { + // Old checkout flow +} + +// Get detailed evaluation result +import dev.openfeature.sdk.FlagEvaluationDetails; + +FlagEvaluationDetails details = + client.getBooleanDetails("checkout.new", false, context); + +logger.info("Value: {}", details.getValue()); +logger.info("Variant: {}", details.getVariant()); +logger.info("Reason: {}", details.getReason()); +{{< /code-block >}} +{{% /tab %}} + +{{% tab "String" %}} +{{< code-block lang="java" >}} +// Evaluate string flags (e.g., UI themes, API endpoints) +String theme = client.getStringValue("ui.theme", "light", context); + +String apiEndpoint = client.getStringValue( + "payment.api.endpoint", + "https://api.example.com/v1", + context +); +{{< /code-block >}} +{{% /tab %}} + +{{% tab "Number" %}} +{{< code-block lang="java" >}} +// Integer flags (e.g., limits, quotas) +int maxRetries = client.getIntegerValue("retries.max", 3, context); + +// Double flags (e.g., thresholds, rates) +double discountRate = client.getDoubleValue("pricing.discount.rate", 0.0, context); +{{< /code-block >}} +{{% /tab %}} + +{{% tab "Object" %}} +{{< code-block lang="java" >}} +import dev.openfeature.sdk.Value; + +// Evaluate object/JSON flags for complex configuration +Value config = client.getObjectValue("ui.config", new Value(), context); + +// Access structured data +if (config.isStructure()) { + Value timeout = config.asStructure().getValue("timeout"); + Value endpoint = config.asStructure().getValue("endpoint"); +} +{{< /code-block >}} +{{% /tab %}} +{{< /tabs >}} + +## Error handling + +The OpenFeature SDK uses a default value pattern. If evaluation fails for any reason, the default value you provide is returned. + +{{< code-block lang="java" >}} +import dev.openfeature.sdk.ErrorCode; + +// Check evaluation details for errors +FlagEvaluationDetails details = + client.getBooleanDetails("checkout.new", false, context); + +if (details.getErrorCode() != null) { + switch (details.getErrorCode()) { + case FLAG_NOT_FOUND: + logger.warn("Flag does not exist: {}", "checkout.new"); + break; + case PROVIDER_NOT_READY: + logger.warn("Provider not initialized yet"); + break; + case TARGETING_KEY_MISSING: + logger.warn("Evaluation context missing targeting key"); + break; + case TYPE_MISMATCH: + logger.error("Flag value type doesn't match requested type"); + break; + default: + logger.error("Evaluation error for flag: {}", "checkout.new", details.getErrorCode()); + } +} +{{< /code-block >}} + +### Common error codes + +| Error Code | Description | Resolution | +|------------|-------------|------------| +| `PROVIDER_NOT_READY` | Initial configuration not received | Wait for provider initialization or use `setProviderAndWait()` | +| `FLAG_NOT_FOUND` | Flag doesn't exist in configuration | Check flag key or create flag in Datadog UI | +| `TARGETING_KEY_MISSING` | No targeting key in evaluation context | Provide a targeting key when creating context | +| `TYPE_MISMATCH` | Flag value can't be converted to requested type | Use correct evaluation method for flag type | +| `INVALID_CONTEXT` | Evaluation context is null | Provide a valid evaluation context | + +## Advanced configuration + +### Custom initialization timeout + +Configure how long the provider waits for initial configuration: + +{{< code-block lang="java" >}} +import datadog.trace.api.openfeature.Provider; +import java.util.concurrent.TimeUnit; + +Provider.Options options = new Provider.Options() + .initTimeout(10, TimeUnit.SECONDS); + +api.setProviderAndWait(new Provider(options)); +{{< /code-block >}} + +### Configuration change events + +Listen for configuration updates from Remote Configuration: + +{{< code-block lang="java" >}} +import dev.openfeature.sdk.ProviderEvent; + +client.on(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, (event) -> { + logger.info("Flag configuration updated: {}", event.getMessage()); + // Optionally re-evaluate flags or trigger cache refresh +}); +{{< /code-block >}} + +`PROVIDER_CONFIGURATION_CHANGED` is an optional OpenFeature event. Check the Datadog provider documentation to verify this event is supported in your version. + +### Multiple clients + +Use named clients to organize context and flags by domain or team: + +{{< code-block lang="java" >}} +// Named clients share the same provider instance but can have different contexts +Client checkoutClient = api.getClient("checkout"); +Client analyticsClient = api.getClient("analytics"); + +// Each client can have its own evaluation context +EvaluationContext checkoutContext = new MutableContext("session-abc"); +EvaluationContext analyticsContext = new MutableContext("user-123"); + +boolean newCheckout = checkoutClient.getBooleanValue( + "checkout.ui.new", false, checkoutContext +); + +boolean enhancedAnalytics = analyticsClient.getBooleanValue( + "analytics.enhanced", false, analyticsContext +); +{{< /code-block >}} + +The `Provider` instance is shared globally. Client names are for organizational purposes only and don't create separate provider instances. All clients use the same underlying Datadog provider and flag configurations. + +## Best practices + +### Initialize early +Initialize the OpenFeature provider as early as possible in your application lifecycle (for example, in `main()` or application startup). This ensures flags are ready before business logic executes. + +### Use meaningful default values +Always provide sensible default values that maintain safe behavior if flag evaluation fails: + +{{< code-block lang="java" >}} +// Good: Safe default that maintains current behavior +boolean useNewAlgorithm = client.getBooleanValue("algorithm.new", false, context); + +// Good: Conservative default for limits +int rateLimit = client.getIntegerValue("rate.limit", 100, context); +{{< /code-block >}} + +### Create context once +Create the evaluation context once per request/user/session and reuse it for all flag evaluations: + +{{< code-block lang="java" >}} +// In a web filter or request handler +EvaluationContext userContext = new MutableContext(userId) + .add("email", user.getEmail()) + .add("tier", user.getTier()); + +// Reuse context for all flags in this request +boolean featureA = client.getBooleanValue("feature.a", false, userContext); +boolean featureB = client.getBooleanValue("feature.b", false, userContext); +{{< /code-block >}} + +Rebuilding the evaluation context for every flag evaluation adds unnecessary overhead. Create the context once at the start of the request lifecycle, then pass it to all subsequent flag evaluations. + +### Handle initialization failures (optional) +Consider handling initialization failures if your application can function with default flag values: + +{{< code-block lang="java" >}} +try { + api.setProviderAndWait(new Provider()); +} catch (ProviderNotReadyError e) { + // Log error and continue with defaults + logger.warn("Feature flags not ready, using defaults", e); + // Application will use default values for all flags +} +{{< /code-block >}} + +If feature flags are critical for your application to function, let the exception propagate to prevent startup. + +### Use consistent targeting keys +Use consistent, stable identifiers as targeting keys: +- **Good**: User IDs, session IDs, device IDs +- **Avoid**: Timestamps, random values, frequently changing IDs + +### Monitor flag evaluation +Use the detailed evaluation results for logging and debugging: + +{{< code-block lang="java" >}} +FlagEvaluationDetails details = + client.getBooleanDetails("feature.critical", false, context); + +logger.info("Flag: {} | Value: {} | Variant: {} | Reason: {}", + "feature.critical", + details.getValue(), + details.getVariant(), + details.getReason() +); +{{< /code-block >}} + +## Troubleshooting + +### Provider not ready + +**Problem**: `PROVIDER_NOT_READY` errors when evaluating flags + +**Common Causes**: +1. **Experimental flag not enabled**: Feature flagging is disabled by default +2. **Agent not ready**: Application started before Agent was fully initialized +3. **No flags configured**: No flags published to your service/environment combination +4. **Agent Remote Configuration disabled**: Agent not configured for Remote Configuration + +**Solutions**: +1. **Enable experimental feature**: + ```bash + export DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true + ``` +2. **Verify feature flagging system started** in application logs: + ``` + [dd.trace] Feature Flagging system starting + [dd.trace] Feature Flagging system started + ``` +3. **Ensure Agent is ready** before app starts (use health checks in Docker/Kubernetes) +4. **Check EVP Proxy discovered** in logs: + ``` + discovered ... evpProxyEndpoint=evp_proxy/v4/ configEndpoint=v0.7/config + ``` +5. **Wait for Remote Configuration sync** (can take 30-60 seconds after publishing flags) +6. **Verify flags are published** in Datadog UI to the correct service and environment + +### ClassNotFoundException or NoClassDefFoundError + +**Problem**: Application fails to start with `ClassNotFoundException` for Datadog classes like `datadog.trace.api.featureflag.FeatureFlaggingGateway` + +**Cause**: Missing the bootstrap JAR dependency. The bootstrap module contains shared interfaces that allow the Datadog tracer (running in the bootstrap classloader) to communicate with the OpenFeature provider (running in the application classloader). Without it, the two components cannot interact. + +**Solutions**: +1. **Add the bootstrap JAR** to your dependencies: + ```xml + + com.datadoghq + dd-java-agent-feature-flagging-bootstrap + X.X.X + + ``` +2. **Verify both dependencies are included** in your build: + - `dd-openfeature` (the OpenFeature provider) + - `dd-java-agent-feature-flagging-bootstrap` (the bootstrap module) +3. **Check the classpath** includes both JARs in your runtime configuration + +### Feature flagging system not starting + +**Problem**: No "Feature Flagging system starting" messages in logs + +**Cause**: Experimental flag not enabled in tracer + +**Solution**: +Add `-Ddd.experimental.flagging.provider.enabled=true` to your Java command or set `DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true` + +### EVP proxy not available error + +**Problem**: Logs show "EVP Proxy not available" or "agent does not support EVP proxy" + +**Cause**: Application started before Agent was fully initialized + +**Solutions**: +1. **Add Agent health check** in orchestration (Docker Compose, Kubernetes) +2. **Add startup delay** to application +3. **Retry logic**: Implement retry on provider initialization failure +4. **Upgrade Agent**: Ensure using Agent 7.x or later with EVP Proxy support + +### Flags not updating + +**Problem**: Flag configuration changes aren't reflected in the application + +**Solutions**: +1. Check Remote Configuration is enabled on both Agent and application +2. Verify Agent can connect to Datadog backend +3. Check application logs for "No configuration changes" or "Configuration received" +4. Ensure flags are published (not saved as drafts) in the Datadog UI +5. Verify service and environment tags match between app and flag targeting + +### Type mismatch errors + +**Problem**: `TYPE_MISMATCH` errors when evaluating flags + +**Solutions**: +1. Verify the flag type in Datadog UI matches the evaluation method +2. Use correct method: `getBooleanValue()`, `getStringValue()`, `getIntegerValue()`, `getDoubleValue()` +3. Check flag configuration for correct value types + +### No exposures in Datadog + +**Problem**: Flag evaluations aren't appearing in Datadog UI + +**Solutions**: +1. Verify the flag's allocation has `doLog=true` configured +2. Check Datadog Agent is receiving exposure events +3. Verify `DD_API_KEY` is correct +4. Check Agent logs for exposure upload errors + +## Further reading + +{{< partial name="whats-next/whats-next.html" >}} diff --git a/content/en/feature_flags/server/nodejs.md b/content/en/feature_flags/server/nodejs.md new file mode 100644 index 00000000000..00ce3d63719 --- /dev/null +++ b/content/en/feature_flags/server/nodejs.md @@ -0,0 +1,216 @@ +--- +title: Node.js Feature Flags +description: Set up Datadog Feature Flags for Node.js applications. +further_reading: +- link: "/feature_flags/server/" + tag: "Documentation" + text: "Server-Side Feature Flags" +- link: "/tracing/trace_collection/dd_libraries/nodejs/" + tag: "Documentation" + text: "Node.js Tracing" +- link: "/tracing/" + tag: "Documentation" + text: "Learn about Application Performance Monitoring (APM)" +--- + +{{< callout url="http://datadoghq.com/product-preview/feature-flags/" >}} +Feature Flags are in Preview. Complete the form to request access. +{{< /callout >}} + +## Overview + +This page describes how to instrument your Node.js application with the Datadog Feature Flags SDK. + +## Installing and initializing + +Feature Flagging is provided by Application Performance Monitoring (APM). To integrate APM into your application with feature flagging support, install `dd-trace` and enable Remote Configuration with the `flaggingProvider` option as shown below. See [Tracing Node.js Applications][1] for detailed APM installation instructions. + +```shell +npm install dd-trace @openfeature/server-sdk +``` + +```javascript +import { OpenFeature } from '@openfeature/server-sdk' +import tracer from 'dd-trace'; + +tracer.init({ + experimental: { + flaggingProvider: { + enabled: true, + } + } +}); + +OpenFeature.setProvider(tracer.openfeature); +``` + +### Accepting default values before initialization + +When you call `setProvider` without waiting, the client returns default values until Remote Configuration loads in the background. This approach keeps your application responsive during startup but may serve defaults for early requests. + +```javascript +OpenFeature.setProvider(tracer.openfeature); +const client = OpenFeature.getClient(); + +app.get('/my-endpoint', (req, res) => { + const value = client.getBooleanValue('my-flag', false); + if (value) { + res.send('feature enabled!'); + } else { + res.send('feature disabled!'); + } +}); +``` + +### Waiting for provider initialization + +Use `setProviderAndWait` to ensure the provider fully initializes before evaluating flags. This guarantees that flag evaluations use actual configuration data rather than defaults, at the cost of delaying requests during initialization. + +```javascript +const initializationPromise = OpenFeature.setProviderAndWait(tracer.openfeature); +const client = OpenFeature.getClient(); + +app.get('/my-endpoint', async (req, res) => { + await initializationPromise; + + OpenFeature.setContext({ + userID: req.session?.userID, + companyID: req.session?.companyID + }); + + const value = client.getBooleanValue('my-flag', false); + if (value) { + res.send('feature enabled!'); + } else { + res.send('feature disabled!'); + } +}); +``` + +## Set the evaluation context + +Define who or what the flag evaluation applies to using an `EvaluationContext`. The evaluation context can include user or session information used to determine which flag variations should be returned. Call the `OpenFeature.setContext` method before evaluating flags to ensure proper targeting. + +## Evaluate flags + +After creating the `OpenFeature` client as described in the [Installing and initializing](#installing-and-initializing) section, you can start reading flag values throughout your app. Flag evaluation uses locally cached data, so no network requests occur when evaluating flags. + +Each flag is identified by a _key_ (a unique string) and can be evaluated with a _typed getter_ that returns a value of the expected type. If the flag doesn't exist or cannot be evaluated, the SDK returns the provided default value. + +### Boolean flags + +Use `getBooleanValue()` for flags that represent on/off or true/false conditions. Optionally set the context for specific targeting rules. + +```javascript +OpenFeature.setContext({ + userID: req.session?.userID, + companyID: req.session?.companyID +}); + +const isNewCheckoutEnabled = client.getBooleanValue( + 'new-checkout-flow', // flag key + false, // default value +); + +if (isNewCheckoutEnabled) { + showNewCheckoutFlow(); +} else { + showLegacyCheckout(); +} +``` + +### String flags + +Use `getStringValue()` for flags that select between multiple variants or configuration strings. For example: + +```javascript +OpenFeature.setContext({ + userID: req.session?.userID, + companyID: req.session?.companyID +}); + +const theme = client.getStringValue( + 'ui-theme', // flag key + 'light', // default value +); + +switch (theme) { + case 'light': + setLightTheme(); + break; + case 'dark': + setDarkTheme(); + break; + case 'blue': + setBlueTheme(); + break; + default: + setLightTheme(); +} +``` + +### Number flags + +For number flags, use `getNumberValue()`. This is appropriate when a feature depends on a numeric parameter such as a limit, percentage, or multiplier: + +```javascript +OpenFeature.setContext({ + userID: req.session?.userID, + companyID: req.session?.companyID +}); + +const maxItems = client.getNumberValue( + 'max-cart-items', // flag key + 20, // default value +); + +const priceMultiplier = client.getNumberValue( + 'pricing-multiplier', // flag key + 1.3, // default value +); +``` + +### Object flags + +For structured JSON data, use `getObjectValue()`. This method returns an `object`, which can represent primitives, arrays, or dictionaries. Object flags are useful for Remote Configuration scenarios where multiple properties need to be provided together. + +```javascript +OpenFeature.setContext({ + userID: req.session?.userID, + companyID: req.session?.companyID +}); + +const defaultConfig = { + color: '#00A3FF', + fontSize: 14, +}; +const config = client.getObjectValue('ui-config', defaultConfig); +``` + +### Flag evaluation details + +When you need more than just the flag value, use the `getDetails` functions. These methods return both the evaluated value and metadata explaining the evaluation: + +* `getBooleanDetails() -> EvaluationDetails` +* `getStringDetails() -> EvaluationDetails` +* `getNumberDetails() -> EvaluationDetails` +* `getObjectDetails() -> EvaluationDetails` + +For example: + +```javascript +const details = client.getStringDetails('paywall-layout', 'control'); + +console.log(details.value); // Evaluated value (for example: "A", "B", or "control") +console.log(details.variant); // Variant name, if applicable +console.log(details.reason); // Description of why this value was chosen (for example: "TARGETING_MATCH") +console.log(details.errorCode); // The error that occurred during evaluation, if any +console.log(details.errorMessage); // A more detailed message of the error that occurred, if any +console.log(details.flagMetadata); // Additional information about the evaluation +``` + +## Further reading + +{{< partial name="whats-next/whats-next.html" >}} + +[1]: /tracing/trace_collection/automatic_instrumentation/dd_libraries/nodejs/ \ No newline at end of file diff --git a/content/en/feature_flags/server/python.md b/content/en/feature_flags/server/python.md new file mode 100644 index 00000000000..e35fc7ce389 --- /dev/null +++ b/content/en/feature_flags/server/python.md @@ -0,0 +1,247 @@ +--- +title: Python Feature Flags +description: Set up Datadog Feature Flags for Python applications. +further_reading: +- link: "/feature_flags/server/" + tag: "Documentation" + text: "Server-Side Feature Flags" +- link: "/tracing/trace_collection/dd_libraries/python/" + tag: "Documentation" + text: "Python Tracing" +--- + +{{< callout url="http://datadoghq.com/product-preview/feature-flags/" >}} +Feature Flags are in Preview. Complete the form to request access. +{{< /callout >}} + +## Overview + +This page describes how to instrument your Python application with the Datadog Feature Flags SDK. The Python SDK integrates with [OpenFeature][1], an open standard for feature flag management, and uses the Datadog tracer's Remote Configuration to receive flag updates in real time. + +This guide explains how to install and enable the SDK, create an OpenFeature client, and evaluate feature flags in your application. + +## Prerequisites + +Before setting up the Python Feature Flags SDK, ensure you have: + +- **Datadog Agent** with [Remote Configuration][2] enabled +- **Datadog Python tracer** `ddtrace` version 3.19.0 or later +- **OpenFeature Python SDK** `openfeature-sdk` version 0.5.0 or later + +Set the following environment variables: + +{{< code-block lang="bash" >}} +# Required: Enable the feature flags provider +export DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true + +# Required: Service identification +export DD_SERVICE= +export DD_ENV= +{{< /code-block >}} + +## Installation + +Install the Datadog Python tracer and OpenFeature SDK: + +{{< code-block lang="bash" >}} +pip install ddtrace openfeature-sdk +{{< /code-block >}} + +Or add them to your `requirements.txt`: + +{{< code-block lang="text" filename="requirements.txt" >}} +ddtrace>=3.19.0 +openfeature-sdk>=0.5.0 +{{< /code-block >}} + +## Initialize the SDK + +Register the Datadog OpenFeature provider with the OpenFeature API. The provider connects to the Datadog tracer's Remote Configuration system to receive flag configurations. + +{{< code-block lang="python" >}} +from openfeature import api +from ddtrace.openfeature import DataDogProvider + +# Create and register the Datadog provider +provider = DataDogProvider() +api.set_provider(provider) + +# Create an OpenFeature client +client = api.get_client() + +# Your application code here +{{< /code-block >}} + +## Set the evaluation context + +Define an evaluation context that identifies the user or entity for flag targeting. The evaluation context includes attributes used to determine which flag variations should be returned: + +{{< code-block lang="python" >}} +from openfeature.evaluation_context import EvaluationContext + +eval_ctx = EvaluationContext( + targeting_key="user-123", # Targeting key (typically user ID) + attributes={ + "email": "user@example.com", + "country": "US", + "tier": "premium", + "age": 25 + } +) +{{< /code-block >}} + +The targeting key is used for consistent traffic distribution (percentage rollouts). Additional attributes enable targeting rules, such as "enable for users in the US" or "enable for premium tier users" in the example above. + +## Evaluate flags + +After setting up the provider and creating a client, you can evaluate flags throughout your application. Flag evaluation is local and fast—the SDK uses locally cached configuration data, so no network requests occur during evaluation. + +Each flag is identified by a key (a unique string) and can be evaluated with a typed method that returns a value of the expected type. If the flag doesn't exist or cannot be evaluated, the SDK returns the provided default value. + +### Boolean flags + +Use `get_boolean_value` for flags that represent on/off or true/false conditions: + +{{< code-block lang="python" >}} +enabled = client.get_boolean_value("new-checkout-flow", False, eval_ctx) + +if enabled: + show_new_checkout() +else: + show_legacy_checkout() +{{< /code-block >}} + +### String flags + +Use `get_string_value` for flags that select between multiple variants or configuration strings: + +{{< code-block lang="python" >}} +theme = client.get_string_value("ui-theme", "light", eval_ctx) + +if theme == "dark": + set_dark_theme() +elif theme == "light": + set_light_theme() +else: + set_light_theme() +{{< /code-block >}} + +### Numeric flags + +For numeric flags, use `get_integer_value` or `get_float_value`. These are appropriate when a feature depends on a numeric parameter such as a limit, percentage, or multiplier: + +{{< code-block lang="python" >}} +max_items = client.get_integer_value("cart-max-items", 20, eval_ctx) + +discount_rate = client.get_float_value("discount-rate", 0.0, eval_ctx) +{{< /code-block >}} + +### Object flags + +For structured data, use `get_object_value`. This returns a dictionary with complex configuration: + +{{< code-block lang="python" >}} +config = client.get_object_value("feature-config", { + "maxRetries": 3, + "timeout": 30 +}, eval_ctx) + +max_retries = config.get("maxRetries", 3) +timeout = config.get("timeout", 30) +{{< /code-block >}} + +### Flag evaluation details + +When you need more than just the flag value, use the `*_details` methods. These return both the evaluated value and metadata explaining the evaluation: + +{{< code-block lang="python" >}} +details = client.get_boolean_details("new-feature", False, eval_ctx) + +print(f"Value: {details.value}") +print(f"Variant: {details.variant}") +print(f"Reason: {details.reason}") +print(f"Error Code: {details.error_code}") +print(f"Error Message: {details.error_message}") +{{< /code-block >}} + +Flag details help you debug evaluation behavior and understand why a user received a given value. + +### Evaluation without context + +You can evaluate flags without providing an evaluation context. This is useful for global flags that don't require user-specific targeting: + +{{< code-block lang="python" >}} +# Global feature flag - no context needed +maintenance_mode = client.get_boolean_value("maintenance-mode", False) + +if maintenance_mode: + return "Service temporarily unavailable" +{{< /code-block >}} + +## Waiting for provider initialization + +By default, the provider initializes asynchronously and flag evaluations return default values until the first Remote Configuration payload is received. If your application requires flags to be ready before handling requests, you can wait for the provider to initialize using event handlers: + +{{< code-block lang="python" >}} +import threading +from openfeature import api +from openfeature.event import ProviderEvent +from ddtrace.openfeature import DataDogProvider + +# Create an event to wait for readiness +ready_event = threading.Event() + +def on_ready(event_details): + ready_event.set() + +# Register event handler +api.add_handler(ProviderEvent.PROVIDER_READY, on_ready) + +# Set provider +provider = DataDogProvider() +api.set_provider(provider) + +# Wait for provider to be ready (with optional timeout) +if ready_event.wait(timeout=30): + print("Provider is ready") +else: + print("Provider initialization timed out") + +# Create client and evaluate flags +client = api.get_client() +{{< /code-block >}} + +
Waiting for provider initialization requires OpenFeature SDK 0.7.0 or later. Most applications don't need to wait for initialization, as flag evaluations work immediately with default values.
+ +## Cleanup + +When your application exits, shut down the OpenFeature API to clean up resources: + +{{< code-block lang="python" >}} +api.shutdown() +{{< /code-block >}} + +## Troubleshooting + +### Provider not enabled + +If you receive warnings about the provider not being enabled, ensure `DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true` is set in your environment: + +{{< code-block lang="bash" >}} +export DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true +{{< /code-block >}} + +### Remote Configuration not working + +Verify the following to ensure that Remote Configuration is working: +- Datadog Agent is version 7.55 or later +- Remote Configuration is enabled on the Agent +- `DD_SERVICE` and `DD_ENV` environment variables are set +- The tracer can communicate with the Agent + +[1]: https://openfeature.dev/ +[2]: /agent/remote_config/ + +## Further reading + +{{< partial name="whats-next/whats-next.html" >}} diff --git a/content/en/feature_flags/server/ruby.md b/content/en/feature_flags/server/ruby.md new file mode 100644 index 00000000000..f9e040a258b --- /dev/null +++ b/content/en/feature_flags/server/ruby.md @@ -0,0 +1,237 @@ +--- +title: Ruby Feature Flags +description: Set up Datadog Feature Flags for Ruby applications. +further_reading: +- link: "/feature_flags/server/" + tag: "Documentation" + text: "Server-Side Feature Flags" +- link: "/tracing/trace_collection/automatic_instrumentation/dd_libraries/ruby/" + tag: "Documentation" + text: "Ruby Tracing" +- link: "/tracing/" + tag: "Documentation" + text: "Learn about Application Performance Monitoring (APM)" +--- + +{{< callout url="http://datadoghq.com/product-preview/feature-flags/" >}} +Feature Flags are in Preview. Complete the form to request access. +{{< /callout >}} + +## Overview + +This page describes how to instrument your Ruby application with the Datadog Feature Flags SDK. + +## Prerequisites + +Before setting up the Ruby Feature Flags SDK, ensure you have: + +- **Datadog Agent** with [Remote Configuration][1] enabled +- **Datadog Ruby tracer** `datadog` version 2.23.0 or later +- **OpenFeature Ruby SDK** `openfeature-sdk` version 0.4.1 or later +- **Service and environment configured** - Feature flags are targeted by service and environment + +## Installing and initializing + +Feature Flagging is provided by Application Performance Monitoring (APM). To integrate APM into your application with feature flagging support, install the required gems and configure Remote Configuration with OpenFeature support. + +```shell +gem install ddtrace openfeature-sdk +``` + +```ruby +require 'datadog' +require 'open_feature/sdk' +require 'datadog/open_feature/provider' + +# Configure Datadog with feature flagging enabled +Datadog.configure do |config| + config.remote.enabled = true + config.open_feature.enabled = true +end + +# Configure OpenFeature SDK with Datadog provider +OpenFeature::SDK.configure do |config| + config.set_provider(Datadog::OpenFeature::Provider.new) +end + +# Create OpenFeature client +client = OpenFeature::SDK.build_client +``` + +The client returns default values until Remote Configuration loads in the background. This approach keeps your application responsive during startup but may serve defaults for early requests. + +## Set the evaluation context + +Define an evaluation context that identifies the user or entity for flag targeting. The evaluation context includes attributes used to determine which flag variations should be returned: + +```ruby +context = OpenFeature::SDK::EvaluationContext.new( + targetingKey: 'user-123', # Targeting key (typically user ID) + email: 'user@example.com', + country: 'US', + tier: 'premium', + age: 25 +) +``` + +The targeting key is used for consistent traffic distribution (percentage rollouts). Additional attributes enable targeting rules, such as "enable for users in the US" or "enable for premium tier users" in the example above. + +## Evaluate flags + +After creating the `OpenFeature` client, you can start reading flag values throughout your app. Flag evaluation uses locally cached data, so no network requests occur when evaluating flags. + +Each flag is identified by a unique string _key_. Flags are evaluated using typed methods that return values matching the expected type. The SDK returns the default value if a flag doesn't exist or cannot be evaluated. + +### Boolean flags + +Use `fetch_boolean_value()` for flags that represent on/off or true/false conditions: + +```ruby +enabled = client.fetch_boolean_value( + flag_key: 'new-checkout-flow', + default_value: false, + evaluation_context: context +) + +if enabled + show_new_checkout +else + show_legacy_checkout +end +``` + +### String flags + +Use `fetch_string_value()` for flags that select between multiple variants or configuration strings: + +```ruby +theme = client.fetch_string_value( + flag_key: 'ui-theme', + default_value: 'light', + evaluation_context: context +) + +case theme +when 'dark' + set_dark_theme +when 'light' + set_light_theme +else + set_light_theme +end +``` + +### Number flags + +For numeric flags, use `fetch_integer_value()` or `fetch_float_value()`. Ruby also provides `fetch_number_value()`, which returns the appropriate type based on the default value. These methods are appropriate when a feature depends on a numeric parameter such as a limit, percentage, or multiplier: + +```ruby +max_items = client.fetch_integer_value( + flag_key: 'cart-max-items', + default_value: 20, + evaluation_context: context +) + +discount_rate = client.fetch_float_value( + flag_key: 'discount-rate', + default_value: 0.0, + evaluation_context: context +) + +# Generic number method (type based on default) +batch_size = client.fetch_number_value( + flag_key: 'batch-size', + default_value: 100, # Returns integer + evaluation_context: context +) +``` + +### Object flags + +For structured data, use `fetch_object_value()`. This method returns a hash. Object flags are useful for Remote Configuration scenarios where multiple properties need to be provided together. + +```ruby +config = client.fetch_object_value( + flag_key: 'feature-config', + default_value: { + 'maxRetries' => 3, + 'timeout' => 30 + }, + evaluation_context: context +) + +max_retries = config['maxRetries'] || 3 +timeout = config['timeout'] || 30 +``` + +### Flag evaluation details + +When you need more than the flag value, use the `fetch__details` methods. These methods return both the evaluated value and metadata explaining the evaluation: + +```ruby +details = client.fetch_boolean_details( + flag_key: 'new-feature', + default_value: false, + evaluation_context: context +) + +puts "Value: #{details.value}" +puts "Variant: #{details.variant}" +puts "Reason: #{details.reason}" +puts "Error Code: #{details.error_code}" +puts "Error Message: #{details.error_message}" +``` + +Flag details help you debug evaluation behavior and understand why a user received a given value. + +## Evaluation without context + +You can evaluate flags without providing an evaluation context. This is useful for global flags that don't require user-specific targeting: + +```ruby +# Global feature flag - no context needed +maintenance_mode = client.fetch_boolean_value( + flag_key: 'maintenance-mode', + default_value: false +) + +if maintenance_mode + halt 503, { error: 'Service temporarily unavailable' }.to_json +end +``` + +## Troubleshooting + +### Feature flags always return default values + +If feature flags unexpectedly always return default values, check the following: + +- Verify Remote Configuration is enabled in your Datadog Agent configuration +- Ensure the service and environment are configured (either through `DD_SERVICE` and `DD_ENV` environment variables or `config.service` and `config.env` in Ruby) +- Check that `config.remote.enabled = true` and `config.open_feature.enabled = true` are set in your Ruby application's Datadog configuration +- Verify the `datadog` gem version includes OpenFeature support (2.23.0 or later) + +### Remote Configuration connection issues + +Check the Datadog tracer logs for Remote Configuration status: + +```ruby +# Enable startup and debug logging +Datadog.configure do |config| + config.diagnostics.startup_logs.enabled = true + config.diagnostics.debug = true + config.remote.enabled = true + config.open_feature.enabled = true +end +``` + +Look for messages about: +- Remote Configuration worker starting +- Feature flags configuration being received +- OpenFeature component initialization + +## Further reading + +{{< partial name="whats-next/whats-next.html" >}} + +[1]: /agent/remote_config/ \ No newline at end of file diff --git a/layouts/partials/feature_flags/feature_flags_setup.html b/layouts/partials/feature_flags/feature_flags_client.html similarity index 83% rename from layouts/partials/feature_flags/feature_flags_setup.html rename to layouts/partials/feature_flags/feature_flags_client.html index c4f9b4cf6de..0242ff75633 100644 --- a/layouts/partials/feature_flags/feature_flags_setup.html +++ b/layouts/partials/feature_flags/feature_flags_client.html @@ -3,28 +3,28 @@