Flags as Code

The Flags SDK is conceptually different from the SDKs of most feature flag providers. This page describes the constraints we put in place to lead to a better experience when using flags.

Consistently simple call sides

Using the SDK of a typical feature flag provider looks similar to the code example below, where the SDK is called with the name of the feature flag as a string:

// a typical feature flag SDK, such as OpenFeature in this example
const exampleValue = await client.getBooleanValue(
'exampleFlag',
false
);

Compare this to what it looks like when using a feature flag in the Flags SDK:

// the Flags SDK
const exampleValue = await exampleFlag();`}

Being able to use a feature flag of course requires first declaring it like so:

import { flag } from '@vercel/flags/next';
export const exampleFlag = flag({
key: "example-flag",
defaultValue: false,
decide() {
return false;
}
});

Feature Flags are functions

Turning each feature flag into its own function means the implementation can change without having to touch the call side. It also allows you to use your well-known editor shortcuts like “Find All References” to see if a flag is still in use.

Feature Flags declare their default value

Each feature flag's declaration can contain the default value. This value is used in case the feature flag can not be evaluated. Containing the default value on the declaration means it will be consistent across all evaluations.

Learn more about defaultValue.

Feature Flags declare how their context is established

Traditional feature flag SDKs require passing in the context on the call side, as shown below:

// a typical feature flag SDK, such as OpenFeature in this example
// add a value to the invocation context
const context = {
user: { id: '123' },
};
const boolValue = await client.getBooleanValue(
'boolFlag',
false,
context
);

The downside of this approach is that every call side needs to recreate the evaluation context. If the evaluation context is created differently or not provided, the feature flag may evaluate differently across the codebase.

The Flags SDK does not require the context to be passed in on each invocation. Instead, the context is established when you declare the feature flag.

import { flag } from '@vercel/flags/next';
export const exampleFlag = flag({
key: "example-flag",
identify() {
return { user: { id: '123' } };
},
decide({ entities }) {
return entities.user.id === '123';
}
});

Learn more about identify.

Avoid vendor lock-in

A downside of using the SDK of a specific provider is that it makes it hard to switch to a different feature flag provider at a later point. Often, the provider's SDK becomes deeply integrated into the codebase over time.

The Flags SDK does not lock you into a specific provider. You can switch to a different provider by changing the definition of the feature flag. Switching providers is possible without changing where your feature flag is used.

The Flags SDK further specifically contains an adapter pattern for this, which makes it even easier to swap providers.

import { flag } from '@vercel/flags/next';
import { statsig } from "@flags-sdk/statsig";
export const exampleFlag = flag({
key: "example-flag",
// replace the adapter with a different one
adapter: statsig(),
});

Learn more about adapters.