Skip to content

Feature Experimentation

intermediate

Traditional software releases are binary — a feature is either deployed to everyone or deployed to no one. This creates a coupling between deploying code and releasing features that introduces risk. A single broken feature in a deploy means rolling back everything.

Feature flags decouple deployment from release. You deploy code continuously, but features are hidden behind flags that you control remotely. Turn a flag on for 1% of users, monitor metrics, then gradually increase to 100%. If something breaks, turn off the flag instantly — no code rollback needed.

Feature Experimentation builds on this foundation by adding measurement. You do not just release features — you experiment with them, measuring which variations drive the best outcomes.

Feature Experimentation operates through SDKs embedded in your application code. When your code needs to decide whether to show a feature, it calls the SDK, which evaluates targeting rules and returns a decision.

Basic feature flag check
csharp
using OptimizelySDK;

var optimizely = OptimizelyFactory.NewDefaultInstance(sdkKey);
var user = optimizely.CreateUserContext("user-123");

// Check if feature is enabled for this user
var decision = user.Decide("new_checkout_flow");

if (decision.Enabled)
{
    // Show new checkout
    var buttonColor = decision.Variables["button_color"];
    RenderNewCheckout(buttonColor);
}
else
{
    // Show existing checkout
    RenderCurrentCheckout();
}
javascript
import { createInstance } from '@optimizely/optimizely-sdk';

const optimizely = createInstance({ sdkKey: 'YOUR_SDK_KEY' });
await optimizely.onReady();

const user = optimizely.createUserContext('user-123', {
  plan: 'enterprise',
  country: 'US',
});

const decision = user.decide('new_checkout_flow');

if (decision.enabled) {
  const buttonColor = decision.variables.button_color;
  renderNewCheckout(buttonColor);
} else {
  renderCurrentCheckout();
}
python
from optimizely import optimizely

client = optimizely.Optimizely(sdk_key='YOUR_SDK_KEY')
user = client.create_user_context('user-123', {
    'plan': 'enterprise',
    'country': 'US',
})

decision = user.decide('new_checkout_flow')

if decision.enabled:
    button_color = decision.variables['button_color']
    render_new_checkout(button_color)
else:
    render_current_checkout()

A feature flag is a named toggle with optional variables. It has three components:

ComponentWhat it doesExample
KeyUnique identifiernew_checkout_flow
VariationsDifferent configurationscontrol (old) vs treatment (new)
VariablesConfigurable values per variationbutton_color: "green", show_trust_badges: true

Variables are the key to moving beyond simple on/off toggles. Instead of just enabling a feature, you can configure how it behaves — what color, what copy, what algorithm — and vary those configurations across experiment variations.

You control who sees a feature using audiences defined by user attributes:

Targeting with user attributes
javascript
// Pass user attributes when creating context
const user = optimizely.createUserContext('user-456', {
  plan: 'enterprise',
  country: 'DE',
  employee_count: 500,
  signed_up: '2024-01-15',
});

// The SDK evaluates targeting rules server-side
// and returns a decision based on these attributes
const decision = user.decide('advanced_analytics');

Audience rules are defined in the Optimizely UI (not in code). This means product managers can change targeting without a deploy — for example, expanding a feature from “enterprise US customers” to “all enterprise customers” is a UI change, not a code change.

Rollouts let you release a feature gradually:

  1. 1% traffic — Smoke test with a small population
  2. 10% traffic — Monitor error rates and performance
  3. 50% traffic — Validate at scale
  4. 100% traffic — Full release

At each stage, you can monitor metrics and halt the rollout if problems appear. The SDK ensures consistent bucketing — a user who was in the 1% group stays in it at 10%, 50%, and 100%.

Running an experiment on a feature flag adds measurement. Instead of just rolling out and hoping for the best, you split traffic between variations and measure which one performs better.

Experiment setup:

  1. Define the feature flag with variations
  2. Create an experiment targeting the flag
  3. Set primary and secondary metrics
  4. Allocate traffic (e.g., 50/50 between control and treatment)
  5. Launch and wait for statistical significance

When running multiple experiments simultaneously, you need to ensure they do not interfere with each other. Mutual exclusion groups guarantee that a user is only in one experiment from the group at a time.

The SDK is designed for performance and reliability:

AspectHow it works
InitializationSDK downloads a datafile (JSON configuration) at startup
Decision makingAll decisions are made locally — no network call per decision
Datafile updatesBackground polling or webhook-triggered updates
Offline fallbackIf the CDN is unreachable, the SDK uses the last cached datafile
Event trackingEvents are batched and sent asynchronously

This architecture means feature flag checks add sub-millisecond latency to your application. The SDK does not call home for every decision.

PlatformPackageUse case
JavaScript (Browser)@optimizely/optimizely-sdkClient-side web apps
JavaScript (Node.js)@optimizely/optimizely-sdkServer-side Node apps
React@optimizely/react-sdkReact components with hooks
C# (.NET)Optimizely.SDK.NET applications
Pythonoptimizely-sdkPython backends
Javacom.optimizely.ab:core-apiJava applications
Gogithub.com/optimizely/go-sdkGo services
Rubyoptimizely-sdkRuby applications
PHPoptimizely/php-sdkPHP applications
SwiftOptimizelySwiftiOS apps
Androidcom.optimizely.ab:android-sdkAndroid apps

Use Feature Experimentation when:

  • You need server-side control over feature visibility
  • You want to reduce deployment risk with progressive rollouts
  • Your experiment involves backend logic (algorithms, pricing, APIs)
  • You need cross-platform consistency (web + mobile + API)
  • You want instant kill switches for new features
  • Your team follows trunk-based development with continuous deployment