Headlessly

Experiment to Rollout

Run an A/B test, analyze results, progressively roll out the winner via feature flags.

The experiment-to-rollout workflow connects Experimentation and Analytics. An Experiment tests a hypothesis, Metrics track results, and FeatureFlags manage progressive rollout of the winning variant.

Full Flow

import { Experiment, FeatureFlag } from '@headlessly/experiments'
import { Event, Metric } from '@headlessly/analytics'

// 1. Create experiment
const experiment = await Experiment.create({
  name: 'Onboarding Flow V2',
  hypothesis: 'Simplified onboarding increases activation by 15%',
  variants: [
    { key: 'control', weight: 50 },
    { key: 'simplified', weight: 50 },
  ],
  metric: 'activation_rate',
  targetSampleSize: 1000,
})

// 2. Start the experiment
await Experiment.start({ id: experiment.$id })

// 3. Track variant events
await Event.create({
  type: 'onboarding.completed',
  properties: {
    experiment: experiment.$id,
    variant: 'simplified',
    duration: 45,
  },
})

// 4. Conclude when significant
await Experiment.conclude({
  id: experiment.$id,
  winner: 'simplified',
  confidence: 0.97,
  lift: 0.18,
})

// 5. Progressive rollout via feature flag
Experiment.concluded(async (experiment, $) => {
  const flag = await $.FeatureFlag.create({
    key: 'onboarding-v2',
    description: experiment.name,
    variant: experiment.winner,
    rolloutPercent: 10,
  })
  await $.FeatureFlag.enable({ id: flag.$id })
})

Step-by-Step Breakdown

Step 1: Create the Experiment

An Experiment defines variants, the target metric, and the sample size needed for statistical significance.

import { Experiment } from '@headlessly/experiments'

const experiment = await Experiment.create({
  name: 'Onboarding Flow V2',
  hypothesis: 'Simplified onboarding increases activation by 15%',
  variants: [
    { key: 'control', weight: 50 },
    { key: 'simplified', weight: 50 },
  ],
  metric: 'activation_rate',
  targetSampleSize: 1000,
})
// experiment.$id => 'experiment_xN4yKpTm'

Step 2: Start the Experiment

Starting activates variant assignment. Users hitting the experiment are bucketed into a variant.

await Experiment.start({ id: 'experiment_xN4yKpTm' })
// experiment.status => 'Running'

Step 3: Track Events per Variant

Events record user actions and tag them with the experiment and variant for analysis.

import { Event } from '@headlessly/analytics'

await Event.create({
  type: 'onboarding.completed',
  properties: {
    experiment: 'experiment_xN4yKpTm',
    variant: 'simplified',
    duration: 45,
  },
})

Step 4: Conclude the Experiment

When enough data is collected, conclude the experiment with the winning variant, confidence level, and observed lift.

await Experiment.conclude({
  id: 'experiment_xN4yKpTm',
  winner: 'simplified',
  confidence: 0.97,
  lift: 0.18,
})
// experiment.status => 'Concluded'

Step 5: Create and Enable Feature Flag

The Experiment.concluded after-hook creates a FeatureFlag for progressive rollout of the winning variant.

import { FeatureFlag } from '@headlessly/experiments'

Experiment.concluded(async (experiment, $) => {
  const flag = await $.FeatureFlag.create({
    key: 'onboarding-v2',
    description: experiment.name,
    variant: experiment.winner,
    rolloutPercent: 10,
  })
  await $.FeatureFlag.enable({ id: flag.$id })
})
// flag.$id => 'featureflag_mR7vPxQn'

Step 6: Progressive Rollout

Gradually increase the rollout percentage as confidence grows.

// Week 1: 10%
await FeatureFlag.rollout({ id: 'featureflag_mR7vPxQn', percent: 10 })

// Week 2: 50%
await FeatureFlag.rollout({ id: 'featureflag_mR7vPxQn', percent: 50 })

// Week 3: 100%
await FeatureFlag.rollout({ id: 'featureflag_mR7vPxQn', percent: 100 })

Event Flow

StepEventPayload
Experiment createdexperiment.created{ $id: 'experiment_xN4yKpTm', variants: 2 }
Experiment startedexperiment.started{ status: 'Running' }
Variant eventevent.created{ type: 'onboarding.completed', variant: 'simplified' }
Experiment concludedexperiment.concluded{ winner: 'simplified', confidence: 0.97 }
Flag createdfeatureflag.created{ key: 'onboarding-v2', rolloutPercent: 10 }
Flag enabledfeatureflag.enabled{ $id: 'featureflag_mR7vPxQn' }
Rollout increasedfeatureflag.rolledout{ percent: 50 }

MCP Example

An agent can manage the full experiment lifecycle:

{
  "tool": "do",
  "input": "Create an experiment called 'Onboarding Flow V2' testing whether simplified onboarding increases activation by 15%, with 50/50 variant split and 1000 sample size target"
}
{
  "tool": "search",
  "input": { "type": "Experiment", "filter": { "status": "Running" } }
}
{
  "tool": "do",
  "input": "Conclude experiment_xN4yKpTm with 'simplified' as winner at 97% confidence and 18% lift, then create a feature flag and roll out to 10%"
}

Automation Pattern

Automate the full experiment-to-rollout pipeline with a staged rollout schedule:

Experiment.concluded(async (experiment, $) => {
  if (experiment.confidence < 0.95) return // Only roll out high-confidence winners

  const flag = await $.FeatureFlag.create({
    key: experiment.name.toLowerCase().replace(/\s+/g, '-'),
    description: `Winner from ${experiment.name}: ${experiment.winner} (${(experiment.lift * 100).toFixed(0)}% lift)`,
    variant: experiment.winner,
    rolloutPercent: 0,
  })
  await $.FeatureFlag.enable({ id: flag.$id })

  // Schedule progressive rollout
  await $.Workflow.create({
    name: `Rollout: ${flag.key}`,
    trigger: { type: 'schedule', cron: '0 9 * * MON' },
    steps: [
      { action: 'FeatureFlag.rollout', id: flag.$id, percent: 10, delay: '0w' },
      { action: 'FeatureFlag.rollout', id: flag.$id, percent: 50, delay: '1w' },
      { action: 'FeatureFlag.rollout', id: flag.$id, percent: 100, delay: '2w' },
    ],
  })

  await $.Metric.record({
    name: 'experiments.concluded',
    value: 1,
    dimensions: { winner: experiment.winner, lift: experiment.lift },
  })
})

Experimentation and Analytics share the same graph. Experiments produce events, events feed metrics, metrics inform conclusions, conclusions trigger feature flags, and feature flags roll out progressively -- all connected through verb conjugation and after-hooks. No separate experimentation platform, no manual flag management.

On this page