Skip to content
Back to skills

Openfeature Sdk Dev

ASecurity

Develop OpenFeature SDK implementations from the specification. Use when implementing the OpenFeature spec in a new language, extending existing SDKs with new features, building custom providers, or contributing to official SDK repositories.

  • 8 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added June 2, 2026
developmentjavascripttypescriptpythonrustgojavarubyswiftkotlinc++

Works with

  • cli
  • api

Security analysis

A100/100

Scanned June 2, 2026

npx -y skills add aRustyDev/agents --skill openfeature-sdk-dev --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Openfeature Sdk Dev?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Openfeature Sdk Dev
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/arustydev-openfeature-sdk-dev/badge)](https://www.skillsdirectory.com/skills/arustydev-openfeature-sdk-dev)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: openfeature-sdk-dev
description: Develop OpenFeature SDK implementations from the specification. Use when implementing the OpenFeature spec in a new language, extending existing SDKs with new features, building custom providers, or contributing to official SDK repositories.
---

# OpenFeature SDK Development

> **Note**: This skill extends patterns from `meta-sdk-patterns-eng`.
> See that skill for foundational SDK patterns (architecture, error handling,
> configuration, testing strategies, packaging).

Guide for implementing OpenFeature SDKs from the specification. This skill covers building SDK implementations, not using existing SDKs (see `openfeature-eng` for usage).

## When to Use This Skill

- Implementing OpenFeature in a new programming language
- Contributing to official OpenFeature SDK repositories
- Building custom providers from scratch
- Extending existing SDKs with new features
- Understanding SDK architecture and compliance requirements

## Specification Overview

OpenFeature uses RFC 2119 keywords (MUST, SHOULD, MAY) to define normative requirements. Implementations achieve compliance by satisfying all mandatory clauses.

### Stability Levels

| Level | Description | Breaking Change Policy |
|-------|-------------|------------------------|
| **Experimental** | Testing features, may change anytime | None |
| **Hardening** | Production-ready, TSC consensus for changes | Requires consensus |
| **Stable** | Battle-tested, major version protection | Major version only |

## Core Architecture

### Component Hierarchy

```
┌─────────────────────────────────────────────────────────────┐
│                     OpenFeature API                         │
│  (Global Singleton - manages providers, hooks, context)     │
├─────────────────────────────────────────────────────────────┤
│                        Client(s)                            │
│  (Domain-scoped flag evaluation interface)                  │
├─────────────────────────────────────────────────────────────┤
│                       Provider(s)                           │
│  (Translates evaluation to flag management system)          │
├─────────────────────────────────────────────────────────────┤
│                         Hooks                               │
│  (Lifecycle middleware: before/after/error/finally)         │
└─────────────────────────────────────────────────────────────┘
```

### SDK Paradigms

**Dynamic-Context (Server-Side)**
- Context passed per evaluation call
- Stateless evaluation
- Multi-tenant support

**Static-Context (Client-Side)**
- Context set once, cached
- Requires reconciliation on context change
- Single-user/device focus

## Type Definitions

### Flag Value Types

```typescript
// Core value types
type FlagValue = boolean | string | number | Structure;

// Structure: Language-idiomatic structured data (object, dict, map)
interface Structure {
  [key: string]: boolean | string | number | DateTime | Structure;
}
```

### Resolution Details

```typescript
interface ResolutionDetails<T extends FlagValue> {
  value: T;                    // Required: resolved flag value
  variant?: string;            // Optional: string identifier for value
  reason?: ResolutionReason;   // Optional: semantic cause
  errorCode?: ErrorCode;       // Optional: null/falsy on success
  errorMessage?: string;       // Optional: additional error detail
  flagMetadata?: FlagMetadata; // Optional: arbitrary metadata
}

type ResolutionReason =
  | 'STATIC'           // Statically configured value
  | 'DEFAULT'          // Default value returned
  | 'TARGETING_MATCH'  // Targeting rule matched
  | 'SPLIT'            // Percentage/split allocation
  | 'CACHED'           // Cached value returned
  | 'DISABLED'         // Flag disabled
  | 'UNKNOWN'          // Reason unknown
  | 'STALE'            // Stale cached value
  | 'ERROR'            // Error occurred
  | string;            // Custom reasons allowed
```

### Error Codes

```typescript
enum ErrorCode {
  PROVIDER_NOT_READY = 'PROVIDER_NOT_READY',
  FLAG_NOT_FOUND = 'FLAG_NOT_FOUND',
  PARSE_ERROR = 'PARSE_ERROR',
  TYPE_MISMATCH = 'TYPE_MISMATCH',
  TARGETING_KEY_MISSING = 'TARGETING_KEY_MISSING',
  INVALID_CONTEXT = 'INVALID_CONTEXT',
  PROVIDER_FATAL = 'PROVIDER_FATAL',
  GENERAL = 'GENERAL'
}
```

### Provider Status

```typescript
enum ProviderStatus {
  NOT_READY = 'NOT_READY',     // Initial state, not yet initialized
  READY = 'READY',             // Initialization complete
  ERROR = 'ERROR',             // Temporary error, may recover
  STALE = 'STALE',             // Using cached/stale data
  FATAL = 'FATAL',             // Unrecoverable error
  RECONCILING = 'RECONCILING'  // Static-context only: context change in progress
}
```

## API Implementation

### Global Singleton (Requirement 1.1.1)

The API SHOULD exist as a global singleton, even with multiple API versions present.

```typescript
// TypeScript example structure
class OpenFeatureAPI {
  private static instance: OpenFeatureAPI;
  private providers: Map<string, FeatureProvider> = new Map();
  private hooks: Hook[] = [];
  private globalContext: EvaluationContext = {};

  static getInstance(): OpenFeatureAPI {
    if (!OpenFeatureAPI.instance) {
      OpenFeatureAPI.instance = new OpenFeatureAPI();
    }
    return OpenFeatureAPI.instance;
  }
}
```

### Provider Management

```typescript
interface OpenFeatureAPI {
  // Requirement 1.1.2: Set default provider
  setProvider(provider: FeatureProvider): void;

  // Requirement 1.1.2: Set provider and await initialization
  setProviderAndWait(provider: FeatureProvider): Promise<void>;

  // Requirement 1.1.3: Bind provider to domain
  setProvider(domain: string, provider: FeatureProvider): void;

  // Requirement 1.1.5: Get provider metadata
  getProviderMetadata(domain?: string): ProviderMetadata;

  // Requirement 1.6.1: Shutdown all providers
  shutdown(): Promise<void>;
}
```

**Provider Lifecycle Rules:**
- MUST invoke `initialize` on newly registered provider before resolution
- MUST call `shutdown` on previously registered provider when replaced
- Requirement 1.6.2: Shutdown MUST reset all API state

### Hook Management

```typescript
interface OpenFeatureAPI {
  // Requirement 1.1.4: Add hooks (append to collection)
  addHooks(...hooks: Hook[]): void;

  // Clear hooks
  clearHooks(): void;
}
```

### Client Creation

```typescript
interface OpenFeatureAPI {
  // Requirement 1.1.6: Create client with optional domain
  getClient(domain?: string): Client;
}
```

**Requirement 1.1.7**: Client creation MUST NOT throw or abnormally terminate.

## Client Implementation

### Client Interface

```typescript
interface Client {
  // Requirement 1.2.2: Immutable domain field
  readonly domain?: string;

  // Client-level hooks
  addHooks(...hooks: Hook[]): void;

  // Client-level context (dynamic paradigm)
  setContext(context: EvaluationContext): void;
}
```

### Flag Evaluation Methods

**Dynamic-Context Paradigm (Requirement 1.3.1.1)**

```typescript
interface Client {
  getBooleanValue(
    flagKey: string,
    defaultValue: boolean,
    context?: EvaluationContext,
    options?: EvaluationOptions
  ): boolean;

  getStringValue(
    flagKey: string,
    defaultValue: string,
    context?: EvaluationContext,
    options?: EvaluationOptions
  ): string;

  getNumberValue(
    flagKey: string,
    defaultValue: number,
    context?: EvaluationContext,
    options?: EvaluationOptions
  ): number;

  getObjectValue<T extends Structure>(
    flagKey: string,
    defaultValue: T,
    context?: EvaluationContext,
    options?: EvaluationOptions
  ): T;
}
```

**Static-Context Paradigm (Requirement 1.3.2.1)**

Context parameter omitted; set at API/client level.

```typescript
interface StaticContextClient {
  getBooleanValue(
    flagKey: string,
    defaultValue: boolean,
    options?: EvaluationOptions
  ): boolean;
  // ... same pattern for other types
}
```

**Requirement 1.3.3.1**: Languages with separate integer/float types SHOULD provide distinct methods.

### Detailed Evaluation Methods

Return `EvaluationDetails` with supplementary metadata.

```typescript
interface EvaluationDetails<T extends FlagValue> extends ResolutionDetails<T> {
  flagKey: string;  // Requirement 1.4.5: Must contain passed flag key
}

interface Client {
  getBooleanDetails(
    flagKey: string,
    defaultValue: boolean,
    context?: EvaluationContext,
    options?: EvaluationOptions
  ): EvaluationDetails<boolean>;

  // ... same pattern for string, number, object
}
```

### Type Safety (Requirement 1.3.4)

If provider returns wrong type, return default value:

```typescript
function evaluateFlag<T>(flagKey: string, defaultValue: T): T {
  const result = provider.resolve(flagKey, defaultValue);

  // Type mismatch → return default
  if (typeof result.value !== typeof defaultValue) {
    return defaultValue;
  }

  return result.value as T;
}
```

### Error Handling (Requirement 1.4.10)

**CRITICAL**: Methods MUST NOT throw exceptions. Always return default value on error.

```typescript
function getBooleanValue(flagKey: string, defaultValue: boolean): boolean {
  try {
    const details = provider.resolveBooleanValue(flagKey, defaultValue, context);
    return details.value;
  } catch (error) {
    // Log via hooks, not direct logging (Requirement 1.4.11)
    return defaultValue;
  }
}
```

### Evaluation Options

```typescript
interface EvaluationOptions {
  // Requirement 1.5.1: Additional hooks for this evaluation
  hooks?: Hook[];

  // Hook hints passed to all hooks
  hookHints?: HookHints;
}
```

## Provider Implementation

### Provider Interface

```typescript
interface FeatureProvider {
  // Requirement 2.1.1: Metadata with name field
  readonly metadata: ProviderMetadata;

  // Requirement 2.4.1: Optional initialization
  initialize?(context: EvaluationContext): Promise<void>;

  // Requirement 2.5.1: Optional shutdown
  shutdown?(): Promise<void>;

  // Requirement 2.6.1: Optional context change handler (static paradigm)
  onContextChange?(
    oldContext: EvaluationContext,
    newContext: EvaluationContext
  ): Promise<void>;

  // Requirement 2.3.1: Optional provider hooks
  hooks?: Hook[];

  // Requirement 2.2.1: Resolution methods
  resolveBooleanValue(
    flagKey: string,
    defaultValue: boolean,
    context: EvaluationContext
  ): ResolutionDetails<boolean>;

  resolveStringValue(
    flagKey: string,
    defaultValue: string,
    context: EvaluationContext
  ): ResolutionDetails<string>;

  resolveNumberValue(
    flagKey: string,
    defaultValue: number,
    context: EvaluationContext
  ): ResolutionDetails<number>;

  resolveObjectValue<T extends Structure>(
    flagKey: string,
    defaultValue: T,
    context: EvaluationContext
  ): ResolutionDetails<T>;
}

interface ProviderMetadata {
  name: string;
}
```

### Provider Lifecycle States

```
                    ┌──────────────────┐
                    │    NOT_READY     │
                    │  (Initial State) │
                    └────────┬─────────┘
                             │ initialize()
                    ┌────────┴─────────┐
          success   │                  │ failure
        ┌───────────┤                  ├───────────┐
        ▼           │                  │           ▼
┌───────────┐       │                  │    ┌───────────┐
│   READY   │◄──────┘                  └────│   ERROR   │
└─────┬─────┘                               └─────┬─────┘
      │                                           │
      │ config change / recovery                  │ recovery
      │ ┌─────────┐                               │
      ├─┤  STALE  │◄──────────────────────────────┘
      │ └─────────┘
      │
      │ shutdown()
      ▼
┌───────────┐
│ NOT_READY │
└───────────┘

┌───────────┐
│   FATAL   │  (Unrecoverable - no transitions out)
└───────────┘
```

### Resolution Details Requirements

```typescript
function resolveValue<T>(flagKey: string, defaultValue: T): ResolutionDetails<T> {
  // Requirement 2.2.3: Value field MUST contain resolved value
  // Requirement 2.2.4: Variant SHOULD be set if available
  // Requirement 2.2.5: Reason SHOULD indicate semantic cause
  // Requirement 2.2.6: Error code MUST be null/falsy on success
  // Requirement 2.2.9: Flag metadata SHOULD be populated

  return {
    value: resolvedValue,
    variant: 'variant-a',
    reason: 'TARGETING_MATCH',
    errorCode: undefined,
    flagMetadata: { version: '1.0.0' }
  };
}
```

### Error Handling in Providers

```typescript
function resolveValue(flagKey: string): ResolutionDetails {
  try {
    // Normal resolution
    return { value: resolved, reason: 'TARGETING_MATCH' };
  } catch (error) {
    // Requirement 2.2.7: Indicate errors with error codes
    // Requirement 2.3.3: May include error message
    return {
      value: defaultValue,
      reason: 'ERROR',
      errorCode: 'GENERAL',
      errorMessage: error.message
    };
  }
}
```

### Provider Shutdown (Requirement 2.5.2-2.5.3)

```typescript
class MyProvider implements FeatureProvider {
  private initialized = false;

  async shutdown(): Promise<void> {
    if (!this.initialized) return; // Idempotent

    // Clean up resources
    await this.connection?.close();
    this.initialized = false;

    // Requirement 2.5.2: Revert to uninitialized state
  }
}
```

## Evaluation Context

### Context Structure

```typescript
interface EvaluationContext {
  // Requirement 3.1.1: Optional targeting key
  targetingKey?: string;

  // Requirement 3.1.2: Custom fields
  [key: string]: boolean | string | number | DateTime | Structure | undefined;
}
```

### Context Merging (Requirement 3.2.3)

Precedence order (highest wins):
1. Before hooks (highest)
2. Invocation context
3. Client context
4. Transaction context
5. API (global) context (lowest)

```typescript
function mergeContext(...contexts: EvaluationContext[]): EvaluationContext {
  // Later contexts override earlier ones
  return contexts.reduce((merged, ctx) => ({
    ...merged,
    ...ctx
  }), {});
}

// Usage in evaluation
const finalContext = mergeContext(
  api.getContext(),           // Lowest precedence
  transactionContext,
  client.getContext(),
  invocationContext,
  beforeHookContext           // Highest precedence
);
```

## Hooks Implementation

### Hook Interface

```typescript
interface Hook {
  // At least one stage required
  before?(context: HookContext, hints: HookHints): EvaluationContext | void;
  after?(context: HookContext, details: EvaluationDetails, hints: HookHints): void;
  error?(context: HookContext, error: Error, hints: HookHints): void;
  finally?(context: HookContext, hints: HookHints): void;
}

interface HookContext {
  readonly flagKey: string;
  readonly flagValueType: FlagValueType;
  readonly defaultValue: FlagValue;
  readonly evaluationContext: EvaluationContext;
  readonly clientMetadata: ClientMetadata;
  readonly providerMetadata: ProviderMetadata;

  // Mutable: allows inter-stage communication
  hookData: Record<string, unknown>;
}

interface HookHints {
  readonly [key: string]: boolean | string | number | DateTime | Structure;
}
```

### Hook Execution Order

```
API Hooks ──┬── before() ────────────────────────────────────┐
            │                                                 │
Client Hooks┼── before() ───────────────────────────────────┐│
            │                                                ││
Invocation ─┼── before() ──────────────────────────────────┐││
Hooks       │                                               │││
            │                                               │││
Provider ───┼── before() ─────────────────────────────────┐││││
Hooks       │                                              │││││
            │          ┌─────────────────┐                 │││││
            │          │  FLAG RESOLVE   │                 │││││
            │          └─────────────────┘                 │││││
            │                                              │││││
Provider ───┼── after() ──────────────────────────────────┘││││
Hooks       │                                               ││││
            │                                               ││││
Invocation ─┼── after() ───────────────────────────────────┘│││
Hooks       │                                                │││
            │                                                │││
Client Hooks┼── after() ────────────────────────────────────┘││
            │                                                 ││
API Hooks ──┴── after() ─────────────────────────────────────┘│
                                                               │
            finally() executes in same reverse order ◄─────────┘
```

### Hook Error Handling

```typescript
async function executeHooks(hooks: Hook[], stage: string): Promise<void> {
  for (const hook of hooks) {
    try {
      await hook[stage]?.();
    } catch (error) {
      // Hooks MUST NOT propagate exceptions
      // Error hooks still execute
      // Finally hooks always execute
      if (stage !== 'error' && stage !== 'finally') {
        await executeErrorHooks(hooks, error);
      }
    }
  }
}
```

### Before Hook Context Modification

```typescript
// Dynamic-context paradigm only
function beforeHook(context: HookContext): EvaluationContext {
  // Can return modified context (merged with highest precedence)
  return {
    ...context.evaluationContext,
    additionalKey: 'added-by-hook'
  };
}
```

## Events Implementation

### Event Types

```typescript
enum ProviderEvent {
  PROVIDER_READY = 'PROVIDER_READY',
  PROVIDER_ERROR = 'PROVIDER_ERROR',
  PROVIDER_FATAL = 'PROVIDER_FATAL',
  PROVIDER_STALE = 'PROVIDER_STALE',
  PROVIDER_CONFIGURATION_CHANGED = 'PROVIDER_CONFIGURATION_CHANGED',
  PROVIDER_RECONCILING = 'PROVIDER_RECONCILING',        // Static-context only
  PROVIDER_CONTEXT_CHANGED = 'PROVIDER_CONTEXT_CHANGED' // Static-context only
}

interface EventDetails {
  providerName: string;
  errorCode?: ErrorCode;
  errorMessage?: string;
  flagsChanged?: string[];
  metadata?: Record<string, unknown>;
}

type EventHandler = (details: EventDetails) => void;
```

### Event Handler Registration

```typescript
interface EventEmitter {
  // Requirement 5.2.1: API-level handlers
  addHandler(event: ProviderEvent, handler: EventHandler): void;

  // Requirement 5.2.2: Client-level handlers
  addHandler(event: ProviderEvent, handler: EventHandler): void;

  // Requirement 5.2.7: Handler removal
  removeHandler(event: ProviderEvent, handler: EventHandler): void;
}
```

### Event Emission Rules

```typescript
class ProviderWrapper {
  private handlers: Map<ProviderEvent, EventHandler[]> = new Map();

  async initialize(): Promise<void> {
    try {
      await this.provider.initialize?.(this.context);
      // Requirement 5.3.1: Emit READY on success
      this.emit(ProviderEvent.PROVIDER_READY);
    } catch (error) {
      // Requirement 5.3.2: Emit ERROR on failure
      this.emit(ProviderEvent.PROVIDER_ERROR, {
        errorCode: 'GENERAL',
        errorMessage: error.message
      });
    }
  }

  private emit(event: ProviderEvent, details?: Partial<EventDetails>): void {
    const handlers = this.handlers.get(event) ?? [];
    for (const handler of handlers) {
      try {
        handler({ providerName: this.provider.metadata.name, ...details });
      } catch {
        // Requirement 5.2.5: Handler errors don't prevent other handlers
      }
    }
  }
}
```

### Deferred Handler Execution (Requirement 5.3.3)

```typescript
function addHandler(event: ProviderEvent, handler: EventHandler): void {
  this.handlers.get(event)?.push(handler);

  // If provider already in target state, execute immediately
  if (event === ProviderEvent.PROVIDER_READY && this.status === 'READY') {
    handler({ providerName: this.provider.metadata.name });
  }
}
```

## Provider Status Management

### Status Tracking (Requirement 1.7.1)

```typescript
class ProviderManager {
  private status: ProviderStatus = ProviderStatus.NOT_READY;

  getStatus(): ProviderStatus {
    return this.status;
  }

  async initialize(): Promise<void> {
    try {
      await this.provider.initialize?.(this.context);
      // Requirement 1.7.3: READY on normal initialization
      this.status = ProviderStatus.READY;
    } catch (error) {
      if (error.code === 'PROVIDER_FATAL') {
        // Requirement 1.7.5: FATAL on fatal error
        this.status = ProviderStatus.FATAL;
      } else {
        // Requirement 1.7.4: ERROR on abnormal termination
        this.status = ProviderStatus.ERROR;
      }
    }
  }
}
```

### Evaluation During Non-Ready States

```typescript
function evaluate(flagKey: string, defaultValue: boolean): EvaluationDetails<boolean> {
  // Requirement 1.7.6: Return error code if NOT_READY
  if (this.status === ProviderStatus.NOT_READY) {
    return {
      value: defaultValue,
      flagKey,
      reason: 'ERROR',
      errorCode: ErrorCode.PROVIDER_NOT_READY
    };
  }

  // Requirement 1.7.7: Return error code if FATAL
  if (this.status === ProviderStatus.FATAL) {
    return {
      value: defaultValue,
      flagKey,
      reason: 'ERROR',
      errorCode: ErrorCode.PROVIDER_FATAL
    };
  }

  // Normal evaluation
  return this.provider.resolveBooleanValue(flagKey, defaultValue, this.context);
}
```

## Testing Requirements

### Gherkin Test Suites

The specification includes Gherkin test suites for compliance testing:

```gherkin
Feature: Flag Evaluation

Scenario: Boolean flag evaluation returns expected value
  Given a provider is registered
  When a boolean flag "my-flag" is evaluated with default "false"
  Then the returned value should be "true"
  And the variant should be "on"
  And the reason should be "TARGETING_MATCH"

Scenario: Evaluation returns default on provider error
  Given a provider is registered that throws errors
  When a boolean flag "error-flag" is evaluated with default "false"
  Then the returned value should be "false"
  And the error code should be "GENERAL"
```

### Compliance Testing Structure

```typescript
// Test categories to implement
describe('Flag Evaluation API', () => {
  describe('Singleton behavior', () => {
    it('should return same instance', () => {
      expect(OpenFeature.getInstance()).toBe(OpenFeature.getInstance());
    });
  });

  describe('Provider management', () => {
    it('should call initialize on provider registration');
    it('should call shutdown on previous provider when replaced');
  });

  describe('Flag evaluation', () => {
    it('should return correct type for boolean flags');
    it('should return default value on type mismatch');
    it('should never throw exceptions');
  });

  describe('Hooks', () => {
    it('should execute in correct order');
    it('should not propagate hook errors');
  });

  describe('Events', () => {
    it('should emit PROVIDER_READY on successful initialization');
    it('should execute deferred handlers immediately');
  });
});
```

## SDK Implementation Checklist

### Phase 1: Core Types

- [ ] Define FlagValue union type
- [ ] Define ResolutionDetails structure
- [ ] Define EvaluationDetails structure
- [ ] Implement ErrorCode enum
- [ ] Implement ProviderStatus enum
- [ ] Implement ResolutionReason type
- [ ] Define EvaluationContext interface
- [ ] Define FlagMetadata type

### Phase 2: Provider Interface

- [ ] Define FeatureProvider interface
- [ ] Define ProviderMetadata interface
- [ ] Implement NoOpProvider (for testing/defaults)
- [ ] Support initialize lifecycle method
- [ ] Support shutdown lifecycle method
- [ ] Support onContextChange (static paradigm)

### Phase 3: API Implementation

- [ ] Implement global singleton
- [ ] Implement provider registration
- [ ] Implement domain-scoped providers
- [ ] Implement hook registration
- [ ] Implement context management
- [ ] Implement shutdown

### Phase 4: Client Implementation

- [ ] Implement client creation (non-throwing)
- [ ] Implement value methods (boolean, string, number, object)
- [ ] Implement details methods
- [ ] Implement client-level hooks
- [ ] Implement client-level context
- [ ] Ensure type safety

### Phase 5: Hooks

- [ ] Define Hook interface
- [ ] Define HookContext interface
- [ ] Define HookHints interface
- [ ] Implement hook execution order
- [ ] Implement error isolation
- [ ] Implement finally stage guarantee
- [ ] Support context modification (dynamic)

### Phase 6: Events

- [ ] Define ProviderEvent enum
- [ ] Define EventDetails interface
- [ ] Implement event emitter
- [ ] Implement handler registration
- [ ] Implement deferred execution
- [ ] Ensure handler error isolation

### Phase 7: Testing

- [ ] Unit tests for all components
- [ ] Integration tests with providers
- [ ] Gherkin compliance tests
- [ ] Edge case coverage
- [ ] Concurrency testing (if applicable)

---

## Server SDK Implementation

Server SDKs use the **dynamic-context paradigm** where evaluation context is passed per request. This is the most common pattern for backend services.

### Key Characteristics

| Aspect | Server SDK Behavior |
|--------|---------------------|
| Context | Passed per evaluation call |
| State | Stateless between evaluations |
| Concurrency | Multi-threaded, concurrent evaluations |
| Users | Multi-tenant (many users per instance) |
| Lifecycle | Long-running process |

### Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                    Server Application                        │
├─────────────────────────────────────────────────────────────┤
│  Request 1          Request 2          Request 3            │
│  ┌─────────┐        ┌─────────┐        ┌─────────┐          │
│  │Context A│        │Context B│        │Context C│          │
│  └────┬────┘        └────┬────┘        └────┬────┘          │
│       │                  │                  │                │
│       └──────────────────┼──────────────────┘                │
│                          ▼                                   │
│                 ┌─────────────────┐                          │
│                 │ OpenFeature API │ (Singleton)              │
│                 └────────┬────────┘                          │
│                          ▼                                   │
│                 ┌─────────────────┐                          │
│                 │    Provider     │ (Shared, thread-safe)    │
│                 └─────────────────┘                          │
└─────────────────────────────────────────────────────────────┘
```

### Server SDK Requirements

```typescript
// Dynamic-context evaluation signature
interface ServerClient {
  getBooleanValue(
    flagKey: string,
    defaultValue: boolean,
    context: EvaluationContext,  // Required per-call
    options?: EvaluationOptions
  ): boolean;
}

// Context passed with each request
app.get('/api/feature', (req, res) => {
  const context: EvaluationContext = {
    targetingKey: req.user.id,
    email: req.user.email,
    plan: req.user.subscription,
    // Request-specific context
  };

  const enabled = client.getBooleanValue('new-feature', false, context);
  res.json({ enabled });
});
```

### Server-Specific Considerations

**Thread Safety**
```typescript
// Provider must be thread-safe
class ThreadSafeProvider implements FeatureProvider {
  private cache: Map<string, FlagValue> = new Map();
  private mutex = new Mutex();

  async resolveBooleanValue(
    flagKey: string,
    defaultValue: boolean,
    context: EvaluationContext
  ): Promise<ResolutionDetails<boolean>> {
    // Use mutex for cache access in multi-threaded environments
    return this.mutex.runExclusive(async () => {
      // Resolution logic
    });
  }
}
```

**Connection Pooling**
```go
// Go - Reuse HTTP connections
type Provider struct {
    client *http.Client
}

func NewProvider() *Provider {
    return &Provider{
        client: &http.Client{
            Transport: &http.Transport{
                MaxIdleConns:        100,
                MaxIdleConnsPerHost: 100,
                IdleConnTimeout:     90 * time.Second,
            },
        },
    }
}
```

**Request-Scoped Context Propagation**
```python
# Python - Using contextvars for request context
from contextvars import ContextVar

request_context: ContextVar[EvaluationContext] = ContextVar('request_context')

@app.middleware("http")
async def add_context(request: Request, call_next):
    context = EvaluationContext(
        targeting_key=request.user.id,
        attributes={"path": request.url.path}
    )
    token = request_context.set(context)
    try:
        response = await call_next(request)
    finally:
        request_context.reset(token)
    return response
```

### Server SDK Checklist

- [ ] Thread-safe provider implementation
- [ ] Connection pooling for external calls
- [ ] Request context propagation
- [ ] Graceful shutdown with in-flight request handling
- [ ] Metrics/telemetry per evaluation
- [ ] Bulk evaluation support (optional)
- [ ] Caching strategy for high-throughput

---

## Client SDK Implementation

Client SDKs use the **static-context paradigm** where context is set once and cached. This pattern is used for mobile apps, browser applications, and edge devices.

### Key Characteristics

| Aspect | Client SDK Behavior |
|--------|---------------------|
| Context | Set once, cached until changed |
| State | Stateful, maintains flag cache |
| Concurrency | Single-threaded (usually) |
| Users | Single user/device per instance |
| Lifecycle | Application lifecycle (may be short-lived) |

### Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                    Client Application                        │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌──────────────────────────────────────────────────────┐   │
│  │                  EvaluationContext                    │   │
│  │  (Set once: user ID, device, app version, etc.)      │   │
│  └──────────────────────┬───────────────────────────────┘   │
│                         │                                    │
│                         ▼                                    │
│  ┌──────────────────────────────────────────────────────┐   │
│  │               OpenFeature API                         │   │
│  │  ┌────────────────────────────────────────────────┐  │   │
│  │  │              Flag Cache                         │  │   │
│  │  │  (Evaluated flags stored locally)              │  │   │
│  │  └────────────────────────────────────────────────┘  │   │
│  └──────────────────────┬───────────────────────────────┘   │
│                         │                                    │
│                         ▼                                    │
│  ┌──────────────────────────────────────────────────────┐   │
│  │                    Provider                           │   │
│  │  (Syncs with server, manages local cache)            │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
└─────────────────────────────────────────────────────────────┘
```

### Client SDK Requirements

```typescript
// Static-context evaluation signature (no context parameter)
interface ClientSdkClient {
  getBooleanValue(
    flagKey: string,
    defaultValue: boolean,
    options?: EvaluationOptions
  ): boolean;  // Synchronous - uses cached values
}

// Context set at initialization or on user change
OpenFeature.setContext({
  targetingKey: user.id,
  email: user.email,
  deviceType: 'mobile',
  appVersion: '2.1.0',
});

// Evaluations use cached context
const enabled = client.getBooleanValue('new-feature', false);
```

### Client-Specific Events

```typescript
// Additional events for client SDKs
enum ClientProviderEvent {
  // Standard events
  PROVIDER_READY = 'PROVIDER_READY',
  PROVIDER_ERROR = 'PROVIDER_ERROR',
  PROVIDER_STALE = 'PROVIDER_STALE',

  // Client-specific events
  PROVIDER_RECONCILING = 'PROVIDER_RECONCILING',      // Context change in progress
  PROVIDER_CONTEXT_CHANGED = 'PROVIDER_CONTEXT_CHANGED', // Context change complete
}

// Listen for context reconciliation
client.addHandler(ProviderEvent.PROVIDER_CONTEXT_CHANGED, () => {
  // Re-render UI with new flag values
  refreshUI();
});
```

### Context Reconciliation

When context changes, client SDKs must reconcile cached values:

```typescript
interface ClientProvider extends FeatureProvider {
  // Called when context changes
  onContextChange(
    oldContext: EvaluationContext,
    newContext: EvaluationContext
  ): Promise<void>;
}

class MyClientProvider implements ClientProvider {
  private cache: Map<string, ResolutionDetails<FlagValue>> = new Map();

  async onContextChange(
    oldContext: EvaluationContext,
    newContext: EvaluationContext
  ): Promise<void> {
    // Emit RECONCILING event
    this.emit(ProviderEvent.PROVIDER_RECONCILING);

    try {
      // Fetch new flag values for new context
      const newFlags = await this.fetchFlags(newContext);
      this.cache = new Map(newFlags);

      // Emit CONTEXT_CHANGED event
      this.emit(ProviderEvent.PROVIDER_CONTEXT_CHANGED);
    } catch (error) {
      this.emit(ProviderEvent.PROVIDER_ERROR, { error });
    }
  }
}
```

### Platform-Specific Patterns

**Mobile (iOS/Android)**
```swift
// Swift - iOS lifecycle handling
class OpenFeatureManager {
    func applicationDidBecomeActive() {
        // Refresh flags when app becomes active
        provider.refresh()
    }

    func applicationDidEnterBackground() {
        // Persist cache before backgrounding
        provider.persistCache()
    }
}
```

```kotlin
// Kotlin - Android lifecycle
class FeatureFlagViewModel : ViewModel() {
    private val client = OpenFeature.getClient()

    init {
        // Observe provider events
        OpenFeature.addHandler(ProviderEvent.PROVIDER_READY) {
            refreshFlags()
        }
    }

    override fun onCleared() {
        // Cleanup when ViewModel is destroyed
        OpenFeature.shutdown()
    }
}
```

**Browser/Web**
```typescript
// Handle page visibility changes
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') {
    // Refresh flags when tab becomes visible
    provider.refresh();
  }
});

// Handle online/offline
window.addEventListener('online', () => {
  provider.reconnect();
});

window.addEventListener('offline', () => {
  // Use cached values, emit STALE if needed
  provider.setStatus(ProviderStatus.STALE);
});
```

**Edge/Embedded**
```rust
// Rust - Resource-constrained environments
pub struct EdgeProvider {
    cache: HashMap<String, FlagValue>,
    max_cache_size: usize,
    storage: Box<dyn PersistentStorage>,
}

impl EdgeProvider {
    pub fn new(max_cache_size: usize) -> Self {
        Self {
            cache: HashMap::with_capacity(max_cache_size),
            max_cache_size,
            storage: Box::new(FileStorage::new("/data/flags")),
        }
    }

    // Load from persistent storage on init
    pub async fn initialize(&mut self) -> Result<(), Error> {
        self.cache = self.storage.load().await?;
        Ok(())
    }
}
```

### Offline Support

```typescript
interface OfflineCapableProvider extends ClientProvider {
  // Check if operating offline
  isOffline(): boolean;

  // Get cached value (works offline)
  getCachedValue<T>(flagKey: string): T | undefined;

  // Queue context changes for when online
  queueContextChange(context: EvaluationContext): void;
}

class OfflineProvider implements OfflineCapableProvider {
  private pendingContext: EvaluationContext | null = null;

  async onContextChange(old: EvaluationContext, new_: EvaluationContext) {
    if (this.isOffline()) {
      this.pendingContext = new_;
      return; // Will reconcile when back online
    }
    await this.reconcile(new_);
  }

  async onOnline() {
    if (this.pendingContext) {
      await this.reconcile(this.pendingContext);
      this.pendingContext = null;
    }
  }
}
```

### Client SDK Checklist

- [ ] Static-context evaluation methods (no context param)
- [ ] Context reconciliation (`onContextChange`)
- [ ] RECONCILING and CONTEXT_CHANGED events
- [ ] Local flag cache
- [ ] Persistent storage for offline support
- [ ] Application lifecycle handling
- [ ] Network state awareness (online/offline)
- [ ] Background refresh strategies
- [ ] Memory-efficient caching (size limits)
- [ ] Synchronous evaluation from cache

---

## Language-Specific Considerations

### Static vs Dynamic Typing

**Statically Typed (Go, Rust, Java, TypeScript)**
- Use generics for type-safe evaluation
- Compile-time type checking preferred
- Requirement 2.2.8.1: Support generic resolution details

**Dynamically Typed (Python, Ruby, JavaScript)**
- Runtime type checking in evaluation
- Duck typing for providers
- Clear documentation of expected types

### Concurrency Models

**Single-threaded (JavaScript/Browser)**
- Async/await for provider operations
- Event loop considerations
- Requirement 1.4.12: Provide async mechanisms

**Multi-threaded (Go, Rust, Java)**
- Thread-safe singleton implementation
- Concurrent provider access
- Lock-free where possible

### Error Handling Idioms

**Exceptions (Java, Python, C#)**
- Catch and suppress in evaluation
- Return defaults on any exception

**Result Types (Rust, Go)**
- Map errors to default values
- Error details in resolution structure

### Memory Management

**Garbage Collected (Java, Go, Python)**
- Standard cleanup in shutdown

**Manual/RAII (Rust, C++)**
- Implement Drop/destructor
- Consider Rc/Arc for shared providers

## References

- [OpenFeature Specification](https://openfeature.dev/specification/)
- [Flag Evaluation API](https://openfeature.dev/specification/sections/flag-evaluation)
- [Provider Interface](https://openfeature.dev/specification/sections/providers)
- [Hooks](https://openfeature.dev/specification/sections/hooks)
- [Evaluation Context](https://openfeature.dev/specification/sections/evaluation-context)
- [Events](https://openfeature.dev/specification/sections/events)
- [Types and Data Structures](https://openfeature.dev/specification/types)
- [Gherkin Test Suites](https://openfeature.dev/specification/appendix-b)
- [Official SDK Repositories](https://github.com/open-feature)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…