Segment
Introduction
To analyze an experiment with data that goes through Segment, your application tells Segment which variation each user saw. You add a handler once, and Segment passes those exposures to your destinations along with the events you already send it.
Nothing passes through ConfigDirector, and ConfigDirector never holds your Segment write key.
What Segment reads
Segment's A/B testing spec names one event, Experiment Viewed, with four properties:
| Property | What you send | Example |
|---|---|---|
experiment_id | The config key | checkout-cta-copy |
experiment_name | The config key | checkout-cta-copy |
variation_id | The variation's ID, which stays the same when it is renamed | 0190b5a2-7c1e-7f3a-9d44-2f6c8a1e5b70 |
variation_name | The variation's name | treatment-a |
Add the handler
Add a handler for the configEvaluated hook where you create the client, in the browser or in Node.js:
import { createClient } from "@configdirector/client-sdk";
import { AnalyticsBrowser } from "@segment/analytics-next";
const analytics = AnalyticsBrowser.load({ writeKey: "YOUR-SEGMENT-WRITE-KEY" });
export const client = createClient("YOUR-CLIENT-SDK-KEY", {
hooks: {
configEvaluated: ({ evaluation }) => {
const variation = evaluation.variation;
if (!variation?.name) return;
analytics.track("Experiment Viewed", {
experiment_id: evaluation.key,
experiment_name: evaluation.key,
variation_id: variation.id,
variation_name: variation.name,
});
},
},
});
await client.initialize({ id: "user-42" });
import { createClient } from "@configdirector/server-sdk";
import { Analytics } from "@segment/analytics-node";
const analytics = new Analytics({ writeKey: "YOUR-SEGMENT-WRITE-KEY" });
export const client = createClient("YOUR-SERVER-SDK-KEY", {
hooks: {
configEvaluated: ({ evaluation }) => {
const variation = evaluation.variation;
const userId = evaluation.context?.id;
if (!variation?.name || !userId) return;
analytics.track({
userId,
event: "Experiment Viewed",
properties: {
experiment_id: evaluation.key,
experiment_name: evaluation.key,
variation_id: variation.id,
variation_name: variation.name,
},
});
},
},
});
await client.initialize();
Only an experiment's evaluations carry a variation, so the check skips every other config, along with the in-code default value. It also skips a variation without a name, so name each one.
In the browser, Segment attaches the exposure to the user you have identified to it. In Node.js, the handler passes the context's id as the userId, so it has to be the ID your other Segment events use. A context without an id is skipped, since it does not take part in the split on a server.
The handler needs version 1.11.0 or later of the JavaScript SDK, or 1.13.0 or later of the Node.js SDK. React, Vue and React Native take the same handler in their hooks. The Experiments guide lists the hook for every other SDK.
With OpenFeature
In an OpenFeature application, a hook does the same job, with the variant as the variation's name. Every config has a variant, not only experiments: on any other config it is ConfigDirector's ID for the value. So attach the hook to the reads of your experiments instead of registering it for every read. A hook does not see the variation's ID, so it sends the name in both properties:
import { OpenFeature, type Hook } from "@openfeature/web-sdk";
import { AnalyticsBrowser } from "@segment/analytics-next";
const analytics = AnalyticsBrowser.load({ writeKey: "YOUR-SEGMENT-WRITE-KEY" });
const reportExposure: Hook = {
after: (hookContext, details) => {
if (!details.variant) return;
analytics.track("Experiment Viewed", {
experiment_id: hookContext.flagKey,
experiment_name: hookContext.flagKey,
variation_id: details.variant,
variation_name: details.variant,
});
},
};
const client = OpenFeature.getClient();
const buttonLabel = client.getStringValue("checkout-cta-copy", "Start free trial", {
hooks: [reportExposure],
});
import { OpenFeature, type Hook } from "@openfeature/server-sdk";
import { Analytics } from "@segment/analytics-node";
const analytics = new Analytics({ writeKey: "YOUR-SEGMENT-WRITE-KEY" });
const reportExposure: Hook = {
after: (hookContext, details) => {
const userId = hookContext.context.targetingKey;
if (!details.variant || !userId) return;
analytics.track({
userId,
event: "Experiment Viewed",
properties: {
experiment_id: hookContext.flagKey,
experiment_name: hookContext.flagKey,
variation_id: details.variant,
variation_name: details.variant,
},
});
},
};
const client = OpenFeature.getClient();
const buttonLabel = await client.getStringValue(
"checkout-cta-copy",
"Start free trial",
{ targetingKey: "user-42" },
{ hooks: [reportExposure] },
);
A variation without a name also has ConfigDirector's ID for the value as its variant, so name each one. The hook needs version 1.11.0 or later of the Web provider, or 1.13.0 or later of the Node.js provider.
When the handler runs
The handler runs once per evaluation, whenever your application reads the config. So read an experiment where the user reaches the change rather than at startup, or users who never see it count as exposed.
A config read on every render sends an event each time. Segment does not reduce them to one per user: it passes each one to your destinations, and each one counts as an API call toward your plan's throughput. How a user is counted from there is up to each destination.
Compare the variations
The outcomes you compare are the events your application already sends to Segment, so there is nothing more to add to your code.
Segment does not compare the variations itself. That happens in the destination you connect, so check which event its experiment feature reads. Some read only their own: Amplitude reads $exposure, which you can send through Segment by using the event from the Amplitude page. Others can be pointed at this one: in Mixpanel, set the exposure event to Experiment Viewed under Settings > Project > Experimentation, with experiment_name and variation_name as its properties.
The handler also runs outside the split: before the run starts, after you ship and when a conditional rule serves a variation. So limit your analysis to the dates of the run, which the Experiment tab lists. A conditional rule above the split can still make the numbers higher than the Exposures card, which counts only the contexts the split served.
Keep each variation's name for the length of the run if your destination compares by variation_name, because a new name would show up there as a new variation.
Chart markers from an analytics integration are not offered for this tool; they are for Amplitude, Mixpanel, Datadog, Grafana and PostHog.
Google Analytics 4
Report experiment exposures to Google Analytics 4 from your application, so you can compare the variations next to the events you already track there.
PostHog
Report experiment exposures to PostHog from your application, so you can compare the variations next to the events you already track there.