Skip to main content
Back to blog

Why OpenFeature Matters: The End of Vendor Lock-in for Feature Flags

OpenFeature is an open standard for feature flag evaluation. Learn how it prevents vendor lock-in, enables multi-provider setups, and why it's the future of feature flag management.

Ben EbsworthFebruary 25, 20267 min read
OpenFeaturestandardsopen-sourcefeature-flags

Why OpenFeature Matters: The End of Vendor Lock-in for Feature Flags

Feature flag platforms have traditionally been black boxes. Each vendor provides its own SDK, its own API, its own evaluation model. Switch providers and you rewrite every flag evaluation call in your codebase. This is vendor lock-in, and it's been the status quo for over a decade.

OpenFeature changes this. It's a CNCF (Cloud Native Computing Foundation) project that defines a standard API for feature flag evaluation. Think of it as what OpenTelemetry did for observability — a vendor-neutral interface that lets you swap implementations without changing application code.

The Problem OpenFeature Solves

Consider a typical feature flag integration without OpenFeature:

// Vendor A's SDK
import { VendorAClient } from '@vendor-a/sdk';
const client = new VendorAClient('api-key');
const value = client.getBoolVariation('my-flag', user, false);
 
// Two years later, switching to Vendor B...
// You must find and replace EVERY call across your entire codebase
import { VendorBClient } from '@vendor-b/sdk';
const client = new VendorBClient('different-api-key');
const value = client.isEnabled('my-flag', { user });  // Different API!

This isn't a hypothetical problem. Teams regularly outgrow their initial flag platform — pricing changes, feature gaps emerge, or organizational requirements shift. Without a standard API, migration means touching every file that evaluates a flag.

How OpenFeature Works

OpenFeature defines three core concepts:

1. The Evaluation API

A standard interface for evaluating flags. The same code works regardless of which provider backs it:

import { OpenFeature } from '@openfeature/server-sdk';
 
const client = OpenFeature.getClient();
 
// These calls work with ANY OpenFeature-compatible provider
const enabled = await client.getBooleanValue('dark-mode', false);
const variant = await client.getStringValue('checkout-flow', 'control');
const limit = await client.getNumberValue('rate-limit', 1000);

2. Providers

Providers are the adapters between the OpenFeature API and your chosen flag platform. Swap the provider, keep your evaluation code:

// Today: using Flaggr
import { FlaggrProvider } from '@flaggr/openfeature-provider';
OpenFeature.setProvider(new FlaggrProvider({ endpoint: 'https://flaggr.dev' }));
 
// Tomorrow: switch to another provider
// Change ONE line. All evaluation code stays the same.
import { AnotherProvider } from '@another/openfeature-provider';
OpenFeature.setProvider(new AnotherProvider({ apiKey: 'key' }));

3. Evaluation Context

A standardized way to pass targeting attributes (user ID, email, plan, location) that all providers understand:

const context: EvaluationContext = {
  targetingKey: user.id,
  email: user.email,
  plan: user.subscription.plan,
  country: user.geo.country,
};
 
const value = await client.getBooleanValue('premium-feature', false, context);

Why This Matters for Your Architecture

Multi-Provider Support

Large organizations often need multiple flag sources. Your platform team might manage infrastructure flags in one system while product teams use another for experiments. OpenFeature supports this natively through named clients:

// Different providers for different domains
const infraClient = OpenFeature.getClient('infrastructure');
const productClient = OpenFeature.getClient('product');
 
OpenFeature.setProvider('infrastructure', new ConfigProvider());
OpenFeature.setProvider('product', new FlaggrProvider());

This multi-provider capability is particularly powerful during migrations. If you are moving from one flag platform to another, you can run both providers simultaneously. New flags go into the new provider while existing flags continue to evaluate against the old one. There is no big-bang cutover required.

You can also use named providers to separate concerns architecturally. A platform team might manage infrastructure-level flags (rate limits, circuit breakers, maintenance mode) in one provider, while product teams manage feature rollouts and experiments in another. Each team owns their provider configuration independently, reducing coordination overhead.

// Platform team: infrastructure flags from a config file
OpenFeature.setProvider('platform', new FileProvider({ path: '/etc/flags/platform.json' }));
 
// Product team: experiment flags from Flaggr
OpenFeature.setProvider('product', new FlaggrProvider({
  endpoint: 'https://flaggr.dev',
  serviceId: 'web-app',
}));
 
// Analytics team: data pipeline flags from a separate Flaggr project
OpenFeature.setProvider('analytics', new FlaggrProvider({
  endpoint: 'https://flaggr.dev',
  serviceId: 'data-pipeline',
}));
 
// Each client resolves against its own provider
const maintenanceMode = await infraClient.getBooleanValue('ops-maintenance', false);
const showNewDashboard = await productClient.getBooleanValue('release-dashboard-v2', false);
const enableNewPipeline = await analyticsClient.getBooleanValue('release-spark-v3', false);

Hooks for Cross-Cutting Concerns

OpenFeature hooks are one of the most powerful features of the specification. They let you add logging, metrics, validation, or transformation to every flag evaluation -- regardless of which provider backs it. Hooks execute at four lifecycle stages: before, after, error, and finally.

const loggingHook: Hook = {
  after: (hookContext, evaluationDetails) => {
    logger.debug('Flag evaluated', {
      flagKey: hookContext.flagKey,
      value: evaluationDetails.value,
      reason: evaluationDetails.reason,
    });
  },
};
 
OpenFeature.addHooks(loggingHook);

Hooks are registered globally (applying to all evaluations) or per-client (applying only to evaluations through that client). Here are some practical hook patterns:

Metrics hook -- record evaluation counts and latency to your observability stack:

const metricsHook: Hook = {
  before: (hookContext) => {
    hookContext.flagMetadata = { startTime: performance.now() };
  },
  after: (hookContext, evaluationDetails) => {
    const duration = performance.now() - hookContext.flagMetadata.startTime;
    metrics.histogram('feature_flag.evaluation_duration_ms', duration, {
      flag_key: hookContext.flagKey,
      provider: hookContext.providerMetadata.name,
    });
    metrics.increment('feature_flag.evaluation_count', 1, {
      flag_key: hookContext.flagKey,
      value: String(evaluationDetails.value),
    });
  },
  error: (hookContext, error) => {
    metrics.increment('feature_flag.evaluation_error', 1, {
      flag_key: hookContext.flagKey,
      error: error.message,
    });
  },
};

Validation hook -- enforce conventions like flag key naming patterns or required context attributes:

const validationHook: Hook = {
  before: (hookContext) => {
    // Enforce naming convention
    if (!/^(release|experiment|ops|permission)-/.test(hookContext.flagKey)) {
      logger.warn(`Flag key "${hookContext.flagKey}" does not follow naming convention`);
    }
    // Require targeting key for non-anonymous evaluations
    if (!hookContext.context.targetingKey) {
      logger.warn(`Evaluation of "${hookContext.flagKey}" without targeting key`);
    }
  },
};

These hooks work with any provider. Switch from Flaggr to another OpenFeature-compatible platform and your logging, metrics, and validation hooks continue to function without any changes. For more on flag naming and lifecycle conventions, see our feature flag best practices guide.

Testing Without Infrastructure

OpenFeature's InMemoryProvider lets you test flag-dependent code without connecting to any backend:

import { InMemoryProvider } from '@openfeature/web-sdk';
 
const testProvider = new InMemoryProvider({
  'dark-mode': {
    defaultVariant: 'on',
    variants: { on: true, off: false },
  },
});
 
OpenFeature.setProvider(testProvider);
// Now all flag evaluations return deterministic values

This is a significant improvement over testing with vendor-specific SDKs, where mocking often requires understanding internal implementation details. With InMemoryProvider, you define the flag state declaratively and the test evaluates against it using the same OpenFeature API your application uses.

For integration tests, you can use the InMemoryProvider to simulate specific scenarios:

describe('Checkout flow', () => {
  it('renders new checkout when flag is enabled', async () => {
    const provider = new InMemoryProvider({
      'release-checkout-v2': {
        defaultVariant: 'on',
        variants: { on: true, off: false },
      },
    });
    OpenFeature.setProvider(provider);
 
    const { getByTestId } = render(<CheckoutPage />);
    expect(getByTestId('new-checkout')).toBeTruthy();
  });
 
  it('renders legacy checkout when flag is disabled', async () => {
    const provider = new InMemoryProvider({
      'release-checkout-v2': {
        defaultVariant: 'off',
        variants: { on: true, off: false },
      },
    });
    OpenFeature.setProvider(provider);
 
    const { getByTestId } = render(<CheckoutPage />);
    expect(getByTestId('legacy-checkout')).toBeTruthy();
  });
});

Migrating from Vendor-Specific SDKs

If you are currently using a vendor-specific SDK (LaunchDarkly, Split, Unleash, ConfigCat, or any other provider), migrating to OpenFeature follows a well-defined path:

Step 1: Install the OpenFeature SDK alongside your existing SDK.

npm install @openfeature/server-sdk @flaggr/openfeature-provider

Step 2: Create a thin wrapper that maps your existing evaluation calls to OpenFeature.

// Before: vendor-specific
import { LDClient } from 'launchdarkly-node-server-sdk';
const value = await ldClient.variation('my-flag', user, false);
 
// After: OpenFeature standard
import { OpenFeature } from '@openfeature/server-sdk';
const client = OpenFeature.getClient();
const value = await client.getBooleanValue('my-flag', false, {
  targetingKey: user.key,
  email: user.email,
});

Step 3: Migrate flag-by-flag, not all at once. Use the multi-provider capability to run both your old and new providers simultaneously. As you migrate each flag's configuration to the new platform, switch that flag's evaluation to use the new provider's client.

Step 4: Remove the old SDK once all flags are migrated. Because OpenFeature separates the evaluation API from the provider implementation, removing the old provider is a single-line configuration change -- no application code modifications required.

The key insight is that the migration cost is proportional to the number of provider setup points (typically one per service), not the number of flag evaluation points (potentially thousands). This is the core value proposition of adopting a standard.

OpenFeature Protocol: OFREP

Beyond the client SDK standard, OpenFeature defines OFREP — the OpenFeature Remote Evaluation Protocol. This standardizes how flag evaluation requests are made over HTTP, so any OFREP-compatible server works with any OFREP-compatible client.

Flaggr implements OFREP v1 at /api/ofrep/, meaning you can use any OFREP-compatible SDK to evaluate flags — not just Flaggr's own SDK. This is the ultimate decoupling: your evaluation protocol is standardized, not just your client API.

# OFREP evaluation request — works with any OFREP server
curl -X POST https://flaggr.dev/api/ofrep/v1/evaluate/flags/dark-mode \
  -H "Content-Type: application/json" \
  -d '{"context": {"targetingKey": "user-123"}}'

The CNCF Ecosystem

OpenFeature is a CNCF incubating project, which means:

  • Governance — neutral, community-driven development
  • Longevity — backed by the same foundation as Kubernetes, Prometheus, and OpenTelemetry
  • Ecosystem — growing list of providers from major vendors and open-source projects
  • Interoperability — designed to work alongside other CNCF projects

OpenFeature SDKs exist for:

  • TypeScript/JavaScript (server and browser)
  • Go
  • Java
  • Python
  • .NET
  • PHP
  • Ruby
  • Kotlin (Android)
  • Swift (iOS)

How Flaggr Implements OpenFeature

Flaggr was built with OpenFeature compatibility from day one, not bolted on after the fact:

  • Server Provider — full-featured provider for Node.js with SSE streaming for real-time updates
  • Web Provider — browser-optimized provider with cached evaluation and event-driven updates
  • OFREP Endpoint — standards-compliant remote evaluation for any OFREP client
  • Evaluation Context — all targeting rules work with standard OpenFeature context attributes
  • Hooks — built-in OpenTelemetry hooks for automatic evaluation metrics

This means you can adopt Flaggr today and migrate away tomorrow with zero application code changes. We're confident enough in the platform that we don't need lock-in to retain users.

Getting Started with OpenFeature

If you're evaluating feature flag platforms, here's what to look for:

  1. Native OpenFeature support — not a wrapper around a proprietary SDK
  2. OFREP compliance — for protocol-level interoperability
  3. Hook support — for observability and custom evaluation logic
  4. Multi-provider capability — for gradual migration or multi-vendor setups

The investment in OpenFeature pays off the first time you need to change providers, add evaluation monitoring, or support a multi-platform architecture. Start with the standard from day one and never worry about lock-in again.


Flaggr is fully OpenFeature-compatible. See the quick start guide to set up your first flag, or read about provider architecture for advanced configurations.

B
Ben EbsworthCreator of Flaggr

Software engineer building developer tools and infrastructure. Creator of Flaggr, an open-source feature flag platform. Passionate about developer experience, observability, and shipping software safely.