/Docs

Migrate from Unleash to A vs B

Unleash is an open-source feature management platform with a simple toggle model and gradual rollout strategies. A vs B covers the same feature toggle surface — boolean flags, gradual rollouts, user targeting — and adds typed string/number/JSON values, a built-in A/B testing engine, holdouts, and a formal event-tracking API. If you self-host Unleash, the migration also moves you to A vs B's managed platform. Your toggle names, strategy configurations, and gradual rollout percentages do not transfer automatically — you will recreate them in the A vs B dashboard — but your evaluation call sites are a close one-to-one swap.

Concept mapping

UnleashA vs B
Toggle / feature flagFeature flag (recreate in A vs B dashboard)
unleash.isEnabled('flag-key', ctx)client.getBoolFlag('flag-key', false).isEnabled()
unleash.getVariant('flag-key', ctx)client.getFlag('flag-key', default).variationKey is the variant name
Variant.nameFlag<T>.variationKey
Variant.enabledFlag<T>.isEnabled()
Variant.payload.valueFlag<T>.value
IToggle (toggle entity)Flag<T> evaluation result
Unleash context (userId, sessionId, remoteAddress, properties)EvalContextcontext.key is the primary bucketing ID
Activation strategy (gradualRollout, userWithId, etc.)Audience rule + traffic allocation on the flag rule in the dashboard
Custom strategyCustom audience condition (attribute-based rule)
Unleash ProxyA vs B CDN datafile delivery — no proxy to self-host
No built-in event trackingclient.track(eventKey, { value?, properties? })
No holdout conceptFlagDatafileHoldout — available on all plans
No multi-contextMultiContext — new capability gained on migration
unleash.destroy()server.close()

Client construction

Unleash's Node SDK connects to the Unleash server (or proxy) via a URL and app name. A vs B connects to its CDN using a per-environment SDK key.

Unleash — Node (unleash-client)
ts
1import { initialize } from 'unleash-client';
2
3const unleash = initialize({
4 url: 'https://unleash.example.com/api',
5 appName: 'my-app',
6 customHeaders: { Authorization: 'YOUR_API_TOKEN' },
7});
8
9unleash.on('synchronized', () => {
10 // Client is ready
11});
A vs B — Node
ts
1import { AvsbServer } from '@avsbhq/node';
2
3const server = new AvsbServer({ sdkKey: process.env.AVSB_SDK_KEY! });
4const result = await server.onReady();
5// result.success → fully initialized from network
6// result.degraded → running on defaults; polling continues
Unleash — Browser (@unleash/proxy-client-react)
ts
1import { FlagProvider } from '@unleash/proxy-client-react';
2
3const config = {
4 url: 'https://unleash-proxy.example.com/proxy',
5 clientKey: 'proxy-client-secret',
6 appName: 'my-app',
7};
8
9<FlagProvider config={config}>
10 <App />
11</FlagProvider>
A vs B — React
ts
1import { AvsbProvider } from '@avsbhq/react';
2
3<AvsbProvider
4 sdkKey="sdk_production_abc123"
5 context={{ kind: 'user', key: 'u_123' }}
6>
7 <App />
8</AvsbProvider>
Info
Unleash requires a self-hosted Unleash Proxy for browser SDK clients (to avoid leaking all flag definitions). A vs B ships per-environment datafiles from its own CDN — no proxy to host or maintain.

Identity model

Unleash accepts a context object on each evaluation call. The context has fixed fields (userId, sessionId, remoteAddress) plus a properties map for custom attributes. A vs B uses a flat EvalContext where all targeting attributes live at the top level under a typed kind.

Unleash
ts
1const ctx = {
2 userId: 'u_123',
3 sessionId: 'sess_abc',
4 remoteAddress: '192.168.1.1',
5 properties: {
6 plan: 'pro',
7 country: 'US',
8 },
9};
10const enabled = unleash.isEnabled('show_banner', ctx);
A vs B
ts
1// Flatten userId, sessionId, and properties into a single EvalContext
2const ctx = {
3 kind: 'user',
4 key: 'u_123', // was userId
5 sessionId: 'sess_abc',
6 remoteAddress: '192.168.1.1',
7 plan: 'pro', // was properties.plan
8 country: 'US', // was properties.country
9};
10
11// Server — per call
12server.forUser(ctx).getBoolFlag('show_banner', false);
13
14// Browser — bound at construction, updated via mutations
15client.identify(ctx);
16client.updateAttributes({ plan: 'enterprise' });
17
18// Anonymous → identified stitching at sign-up
19await client.alias(
20 { kind: 'user', key: 'anon_xyz' },
21 { kind: 'user', key: 'u_123' }
22);

Flag evaluation

Unleash has two evaluation methods: isEnabled (boolean) and getVariant (variant with optional payload). A vs B maps both into the Flag<T> envelope.

Unleash
ts
1// Boolean toggle
2const showBanner = unleash.isEnabled('show_banner', ctx);
3
4// Variant toggle
5const variant = unleash.getVariant('homepage_theme', ctx);
6// variant.name → 'blue' | 'green' | 'disabled'
7// variant.enabled → true/false
8// variant.payload → { type: 'string', value: 'blue' } | undefined
9const theme = variant.enabled ? variant.payload?.value ?? 'default' : 'default';
A vs B
ts
1// Boolean flag
2const showBanner = server.forUser(ctx).getBoolFlag('show_banner', false).isEnabled();
3
4// String variant — variationKey is the variant name
5const themeFlag = server.forUser(ctx).getStringFlag('homepage_theme', 'default');
6const theme = themeFlag.value; // 'blue' | 'green' | 'default'
7const variantName = themeFlag.variationKey; // 'blue' | 'green' | null
8
9// Full envelope
10const flag = server.forUser(ctx).getStringFlag('homepage_theme', 'default');
11flag.isEnabled() // true if a rule matched and value is truthy
12flag.source // 'rule' | 'default' | 'holdout' | 'sticky' | ...
13flag.ruleId // matched rule id or null
14flag.reasons // string[]
Tip
Unleash's variant.payload.type field declares whether the payload is a string, number, or JSON. In A vs B, use the appropriate typed evaluator (getStringFlag, getNumberFlag, getJsonFlag<T>) and the type inference is enforced at compile time.

Tracking events

Unleash does not have a built-in event tracking API for metric collection. A vs B adds full event tracking, which is required when you attach metric targets to A/B test rules.

A vs B — event tracking (new capability)
ts
1// Browser
2client.track('purchase_completed', {
3 value: 49.99,
4 properties: { orderId: 'ord_99', plan: 'pro' },
5});
6
7// Server
8server.track('purchase_completed', {
9 context: { kind: 'user', key: 'u_123' },
10 value: 49.99,
11 properties: { orderId: 'ord_99' },
12});

Multi-context

Unleash targets on a single context. A vs B adds multi-context targeting as a new capability on migration — useful when you want to bucket by organization, device, or any other entity type in addition to the user.

A vs B — multi-context (new capability)
ts
1import type { MultiContext } from '@avsbhq/core';
2
3const ctx: MultiContext = {
4 kind: 'multi',
5 user: { kind: 'user', key: 'u_123', plan: 'pro' },
6 organization: { kind: 'organization', key: 'org_42', tier: 'enterprise' },
7};
8server.forUser(ctx).getBoolFlag('enterprise_feature', false);

Streaming updates

Unleash uses a polling model to refresh toggle definitions. A vs B uses the same configurable polling approach with optional SSE push for the browser.

Unleash
ts
1unleash.on('update', () => {
2 // Toggle definitions refreshed
3});
A vs B
ts
1client.on('configUpdate', ({ publishedAt, reason }) => {
2 // reason: 'poll' | 'stream' | 'manual'
3});
4
5client.on('flagChange', ({ flagKey, previousValue, newValue }) => {
6 // A specific flag value changed
7});

Bootstrap / SSR

Unleash's proxy-based architecture can serve bootstrap values via the proxy API. A vs B uses a FlagDatafile fetched on the server and passed as bootstrap to the client provider.

A vs B — SSR bootstrap
ts
1import { fetchDatafile } from '@avsbhq/node/server';
2const datafile = await fetchDatafile(process.env.AVSB_SDK_KEY!);
3
4<AvsbProvider sdkKey="..." context={ctx} bootstrap={datafile ?? undefined}>
5 {children}
6</AvsbProvider>

Holdouts

Unleash does not have a holdout concept. A vs B includes holdouts on all plans. After migration, create a holdout in the dashboard and associate flags with it to maintain a clean control group across concurrent experiments — held-out users receive source: 'holdout' in the evaluation result and are automatically served the control variation.

Cleanup

Unleash
ts
1unleash.destroy();
A vs B
ts
1await server.close(); // flushes events, stops polling

Testing

Unleash — test setup
ts
1import { createTestUnleashInstance } from 'unleash-client/test';
2
3// NOTE: verify before publish — Unleash test utility API subject to change in upstream SDK
4const { client, repo } = createTestUnleashInstance();
5repo.setToggle('show_banner', true);
A vs B — mock client
ts
1import { createMockClient } from '@avsbhq/test';
2
3const mock = createMockClient({
4 flags: {
5 show_banner: true,
6 homepage_theme: 'blue',
7 },
8});

Cutover checklist

1

Recreate toggles in A vs B dashboard

Unleash toggle definitions do not auto-import. Recreate each toggle as a flag in the A vs B dashboard, configuring targeting rules and rollout percentages to match your existing Unleash strategies.
2

Remove Unleash packages

Uninstall unleash-client, @unleash/proxy-client-react, and any related packages.
3

Shut down Unleash Proxy (if self-hosted)

Once the migration is complete and traffic has shifted, decommission your Unleash Proxy instance. A vs B serves datafiles from its own CDN.
4

Install A vs B packages

Install @avsbhq/node, @avsbhq/browser, and @avsbhq/react as needed.
5

Migrate context construction

Flatten Unleash's context fields (userId, sessionId, remoteAddress, properties.*) into a flat EvalContext: rename userId → key, add kind: 'user', and move properties.* to top-level attributes.
6

Replace isEnabled calls

Replace unleash.isEnabled(key, ctx) with server.forUser(ctx).getBoolFlag(key, false).isEnabled().
7

Replace getVariant calls

Replace unleash.getVariant(key, ctx) with the appropriate typed evaluator. Map variant.name → flag.variationKey, variant.payload.value → flag.value.
8

Add event tracking

Add client.track(eventKey, { value, properties }) at conversion points you want to measure in A/B tests.
9

Update tests

Replace Unleash test utilities with createMockClient from @avsbhq/test.
10

Verify and deploy

Run npm run build and npx tsc --noEmit. Confirm flag evaluations in a staging environment before promoting to production traffic.