# Feature Flags

Feature flags in PostHog enable you to control feature rollouts, run A/B tests, and manage remote configuration without deploying new code. The Node.js SDK supports both local and remote evaluation of feature flags with extensive configuration options.

## Capabilities

### Feature Flag Evaluation

Check if a feature flag is enabled for a specific user.

```typescript { .api }
/**
 * Check if a feature flag is enabled for a specific user
 * @param key - The feature flag key
 * @param distinctId - The user's distinct ID
 * @param options - Optional configuration for flag evaluation
 * @returns Promise that resolves to true if enabled, false if disabled, undefined if not found
 */
async isFeatureEnabled(
  key: string,
  distinctId: string,
  options?: {
    groups?: Record<string, string>
    personProperties?: Record<string, string>
    groupProperties?: Record<string, Record<string, string>>
    onlyEvaluateLocally?: boolean
    sendFeatureFlagEvents?: boolean
    disableGeoip?: boolean
  }
): Promise<boolean | undefined>
```

**Usage Examples:**

```typescript
import { PostHog } from 'posthog-node'

const client = new PostHog('your-api-key', {
  host: 'https://app.posthog.com'
})

// Basic feature flag check
const isEnabled = await client.isFeatureEnabled('new-feature', 'user_123')
if (isEnabled) {
  // Feature is enabled
  console.log('New feature is active')
} else {
  // Feature is disabled
  console.log('New feature is not active')
}

// With groups and properties
const isEnabled = await client.isFeatureEnabled('org-feature', 'user_123', {
  groups: { organization: 'acme-corp' },
  personProperties: { plan: 'enterprise' }
})

// Only evaluate locally (no server call if local evaluation not ready)
const isEnabled = await client.isFeatureEnabled('quick-check', 'user_123', {
  onlyEvaluateLocally: true
})
```

### Get Feature Flag Value

Get the value of a feature flag for multivariate flags or string-based flags.

```typescript { .api }
/**
 * Get the value of a feature flag for a specific user
 * @param key - The feature flag key
 * @param distinctId - The user's distinct ID
 * @param options - Optional configuration for flag evaluation
 * @returns Promise that resolves to the flag value (boolean | string) or undefined
 */
async getFeatureFlag(
  key: string,
  distinctId: string,
  options?: {
    groups?: Record<string, string>
    personProperties?: Record<string, string>
    groupProperties?: Record<string, Record<string, string>>
    onlyEvaluateLocally?: boolean
    sendFeatureFlagEvents?: boolean
    disableGeoip?: boolean
  }
): Promise<FeatureFlagValue | undefined>

type FeatureFlagValue = boolean | string
```

**Usage Examples:**

```typescript
// Basic feature flag check
const flagValue = await client.getFeatureFlag('new-feature', 'user_123')
if (flagValue === 'variant-a') {
  // Show variant A
} else if (flagValue === 'variant-b') {
  // Show variant B
} else {
  // Flag is disabled or not found
}

// With groups and properties for targeted rollout
const flagValue = await client.getFeatureFlag('org-feature', 'user_123', {
  groups: { organization: 'acme-corp' },
  personProperties: { plan: 'enterprise' },
  groupProperties: { organization: { tier: 'premium' } }
})

// Only evaluate locally
const flagValue = await client.getFeatureFlag('local-flag', 'user_123', {
  onlyEvaluateLocally: true
})

// Disable feature flag events
const flagValue = await client.getFeatureFlag('silent-flag', 'user_123', {
  sendFeatureFlagEvents: false // Don't send $feature_flag_called event
})
```

### Get Feature Flag Payload

Get the JSON payload attached to a feature flag.

```typescript { .api }
/**
 * Get the payload for a feature flag
 * @param key - The feature flag key
 * @param distinctId - The user's distinct ID
 * @param matchValue - Optional match value to get payload for specific variant
 * @param options - Optional configuration for flag evaluation
 * @returns Promise that resolves to the flag payload or undefined
 */
async getFeatureFlagPayload(
  key: string,
  distinctId: string,
  matchValue?: FeatureFlagValue,
  options?: {
    groups?: Record<string, string>
    personProperties?: Record<string, string>
    groupProperties?: Record<string, Record<string, string>>
    onlyEvaluateLocally?: boolean
    sendFeatureFlagEvents?: boolean
    disableGeoip?: boolean
  }
): Promise<JsonType | undefined>

type JsonType = string | number | boolean | null | JsonType[] | { [key: string]: JsonType }
```

**Usage Examples:**

```typescript
// Get payload for a feature flag
const payload = await client.getFeatureFlagPayload('flag-key', 'user_123')
if (payload) {
  console.log('Flag payload:', payload)
  // payload can be any JSON structure: string, number, boolean, object, array
}

// Get payload with specific match value
const payload = await client.getFeatureFlagPayload('flag-key', 'user_123', 'variant-a')
// This gets the payload specifically for variant-a

// With groups and properties
const payload = await client.getFeatureFlagPayload('org-flag', 'user_123', undefined, {
  groups: { organization: 'acme-corp' },
  personProperties: { plan: 'enterprise' }
})

// Example: Using payload for configuration
const payload = await client.getFeatureFlagPayload('button-config', 'user_123')
if (payload && typeof payload === 'object') {
  const config = payload as { color: string; text: string; size: string }
  console.log(`Button color: ${config.color}`)
}
```

### Get Remote Config Payload

Get remote configuration payload for encrypted feature flags.

```typescript { .api }
/**
 * Get the remote config payload for a feature flag
 * @param flagKey - The feature flag key
 * @returns Promise that resolves to the remote config payload or undefined
 * @throws Error if personal API key is not provided
 */
async getRemoteConfigPayload(flagKey: string): Promise<JsonType | undefined>
```

**Usage Examples:**

```typescript
// Initialize client with personal API key (required)
const client = new PostHog('your-api-key', {
  host: 'https://app.posthog.com',
  personalApiKey: 'phx_...' // Required for remote config
})

// Get remote config payload
const payload = await client.getRemoteConfigPayload('flag-key')
if (payload) {
  console.log('Remote config payload:', payload)
}

// Example: Using remote config for sensitive settings
const dbConfig = await client.getRemoteConfigPayload('database-config')
if (dbConfig && typeof dbConfig === 'object') {
  const config = dbConfig as { host: string; port: number; name: string }
  // Use encrypted config values
  connectToDatabase(config)
}
```

### Get All Flags

Get all feature flag values for a specific user.

```typescript { .api }
/**
 * Get all feature flag values for a specific user
 * @param distinctId - The user's distinct ID
 * @param options - Optional configuration for flag evaluation
 * @returns Promise that resolves to a record of flag keys and their values
 */
async getAllFlags(
  distinctId: string,
  options?: {
    groups?: Record<string, string>
    personProperties?: Record<string, string>
    groupProperties?: Record<string, Record<string, string>>
    onlyEvaluateLocally?: boolean
    disableGeoip?: boolean
    flagKeys?: string[]
  }
): Promise<Record<string, FeatureFlagValue>>
```

**Usage Examples:**

```typescript
// Get all flags for a user
const allFlags = await client.getAllFlags('user_123')
console.log('User flags:', allFlags)
// Output: { 'flag-1': 'variant-a', 'flag-2': false, 'flag-3': 'variant-b' }

// Check multiple flags efficiently
if (allFlags['new-ui']) {
  // Show new UI
}
if (allFlags['experimental-feature'] === 'test') {
  // Enable test mode
}

// With specific flag keys (only evaluate these flags)
const specificFlags = await client.getAllFlags('user_123', {
  flagKeys: ['flag-1', 'flag-2', 'flag-3']
})

// With groups and properties
const orgFlags = await client.getAllFlags('user_123', {
  groups: { organization: 'acme-corp' },
  personProperties: { plan: 'enterprise' },
  groupProperties: { organization: { employees: 500 } }
})

// Only use local evaluation
const localFlags = await client.getAllFlags('user_123', {
  onlyEvaluateLocally: true
})
```

### Get All Flags and Payloads

Get all feature flag values and their payloads for a specific user.

```typescript { .api }
/**
 * Get all feature flag values and payloads for a specific user
 * @param distinctId - The user's distinct ID
 * @param options - Optional configuration for flag evaluation
 * @returns Promise that resolves to flags and payloads
 */
async getAllFlagsAndPayloads(
  distinctId: string,
  options?: {
    groups?: Record<string, string>
    personProperties?: Record<string, string>
    groupProperties?: Record<string, Record<string, string>>
    onlyEvaluateLocally?: boolean
    disableGeoip?: boolean
    flagKeys?: string[]
  }
): Promise<PostHogFlagsAndPayloadsResponse>

interface PostHogFlagsAndPayloadsResponse {
  featureFlags: Record<string, FeatureFlagValue>
  featureFlagPayloads: Record<string, JsonType>
}
```

**Usage Examples:**

```typescript
// Get all flags and payloads for a user
const result = await client.getAllFlagsAndPayloads('user_123')
console.log('Flags:', result.featureFlags)
console.log('Payloads:', result.featureFlagPayloads)

// Use flags and payloads together
if (result.featureFlags['new-button']) {
  const config = result.featureFlagPayloads['new-button']
  if (config && typeof config === 'object') {
    renderButton(config as { color: string; text: string })
  }
}

// With specific flag keys
const result = await client.getAllFlagsAndPayloads('user_123', {
  flagKeys: ['flag-1', 'flag-2']
})

// Only evaluate locally
const result = await client.getAllFlagsAndPayloads('user_123', {
  onlyEvaluateLocally: true
})

// With groups for organization-level flags
const result = await client.getAllFlagsAndPayloads('user_123', {
  groups: { organization: 'acme-corp', team: 'engineering' }
})
```

### Local Evaluation Status

Check if local evaluation is ready for feature flags.

```typescript { .api }
/**
 * Check if local evaluation of feature flags is ready
 * @returns true if local evaluation is ready, false otherwise
 */
isLocalEvaluationReady(): boolean
```

**Usage Examples:**

```typescript
// Check if ready
if (client.isLocalEvaluationReady()) {
  // Local evaluation is ready, can evaluate flags locally
  const flag = await client.getFeatureFlag('flag-key', 'user_123')
} else {
  // Local evaluation not ready, will use remote evaluation
  const flag = await client.getFeatureFlag('flag-key', 'user_123')
}

// Use in conditional logic
const evaluationMode = client.isLocalEvaluationReady() ? 'local' : 'remote'
console.log(`Using ${evaluationMode} evaluation`)
```

### Wait for Local Evaluation

Wait for local evaluation to be ready with optional timeout.

```typescript { .api }
/**
 * Wait for local evaluation of feature flags to be ready
 * @param timeoutMs - Timeout in milliseconds (default: 30000)
 * @returns Promise that resolves to true if ready, false if timed out
 */
async waitForLocalEvaluationReady(timeoutMs?: number): Promise<boolean>
```

**Usage Examples:**

```typescript
// Wait for local evaluation
const isReady = await client.waitForLocalEvaluationReady()
if (isReady) {
  console.log('Local evaluation is ready')
  // Now safe to use flags with onlyEvaluateLocally: true
} else {
  console.log('Local evaluation timed out')
}

// Wait with custom timeout
const isReady = await client.waitForLocalEvaluationReady(10000) // 10 seconds
if (isReady) {
  // Proceed with local evaluation
}

// Use at application startup
async function initializeApp() {
  const client = new PostHog('your-api-key', {
    host: 'https://app.posthog.com',
    personalApiKey: 'phx_...'
  })

  // Wait for flags to be ready before serving requests
  await client.waitForLocalEvaluationReady(5000)

  // Start server
  app.listen(3000)
}
```

### Reload Feature Flags

Force reload feature flag definitions from the server.

```typescript { .api }
/**
 * Reload feature flag definitions from the server for local evaluation
 * @returns Promise that resolves when flags are reloaded
 */
async reloadFeatureFlags(): Promise<void>
```

**Usage Examples:**

```typescript
// Force reload of feature flags
await client.reloadFeatureFlags()
console.log('Feature flags reloaded')

// Reload before checking a specific flag
await client.reloadFeatureFlags()
const flag = await client.getFeatureFlag('flag-key', 'user_123')

// Use in a scheduled job
setInterval(async () => {
  await client.reloadFeatureFlags()
  console.log('Flags refreshed')
}, 60000) // Refresh every minute

// Reload on demand (e.g., webhook endpoint)
app.post('/webhooks/flags-updated', async (req, res) => {
  await client.reloadFeatureFlags()
  res.json({ success: true })
})
```

## Local vs Remote Evaluation

PostHog feature flags can be evaluated in two ways: locally or remotely. Understanding the difference is crucial for optimal performance.

### Local Evaluation

Local evaluation evaluates feature flags within your application using cached flag definitions. This is faster and reduces API calls.

**Requirements:**
- Personal API key must be provided in initialization
- Flag definitions are fetched and cached periodically

**Benefits:**
- Near-instant flag evaluation (no network call)
- Reduced load on PostHog servers
- Works offline with cached flags
- Lower latency for feature checks

**Setup:**

```typescript
const client = new PostHog('your-api-key', {
  host: 'https://app.posthog.com',
  personalApiKey: 'phx_...', // Required for local evaluation
  enableLocalEvaluation: true, // Default: true
  featureFlagsPollingInterval: 30000 // Default: 30 seconds (minimum: 100ms)
})

// Wait for flags to load
await client.waitForLocalEvaluationReady()

// Now flags are evaluated locally
const flag = await client.getFeatureFlag('flag-key', 'user_123')
```

**Example:**

```typescript
// Initialize with local evaluation
const client = new PostHog('your-api-key', {
  host: 'https://app.posthog.com',
  personalApiKey: 'phx_...',
  featureFlagsPollingInterval: 60000 // Poll every 60 seconds
})

// Check readiness
console.log('Local eval ready:', client.isLocalEvaluationReady())

// Wait for ready state
const ready = await client.waitForLocalEvaluationReady(5000)
if (ready) {
  // Fast local evaluation
  const flag = await client.getFeatureFlag('my-flag', 'user_123')
}
```

### Remote Evaluation

Remote evaluation makes an API call to PostHog servers for each flag check. This ensures you always have the latest flag values but is slower.

**Use When:**
- Personal API key not available
- Need real-time flag updates
- Flag depends on server-side user properties
- GeoIP-based targeting is required

**Setup:**

```typescript
// Without personal API key, uses remote evaluation
const client = new PostHog('your-api-key', {
  host: 'https://app.posthog.com'
})

// Each call makes an API request
const flag = await client.getFeatureFlag('flag-key', 'user_123')
```

**Example:**

```typescript
// Remote evaluation with user properties
const flag = await client.getFeatureFlag('premium-feature', 'user_123', {
  personProperties: {
    email: 'user@example.com',
    plan: 'premium'
  }
})

// Remote evaluation with GeoIP
const flag = await client.getFeatureFlag('region-feature', 'user_123', {
  disableGeoip: false // Allow GeoIP-based targeting
})
```

### Hybrid Approach

You can combine both approaches for optimal performance:

```typescript
const client = new PostHog('your-api-key', {
  host: 'https://app.posthog.com',
  personalApiKey: 'phx_...'
})

// Try local first, fallback to remote if needed
async function getFlag(key: string, userId: string) {
  if (client.isLocalEvaluationReady()) {
    // Use local evaluation
    return await client.getFeatureFlag(key, userId, {
      onlyEvaluateLocally: false // Allow fallback to remote
    })
  } else {
    // Use remote evaluation
    return await client.getFeatureFlag(key, userId)
  }
}
```

## Feature Flag Options

All feature flag methods support a rich set of options for advanced use cases.

### Groups

Associate the flag check with groups (e.g., organizations, teams).

```typescript
const flag = await client.getFeatureFlag('org-feature', 'user_123', {
  groups: {
    organization: 'acme-corp',
    team: 'engineering'
  }
})
```

**Use Cases:**
- Organization-level feature rollouts
- Team-based access control
- Multi-tenant applications

### Person Properties

Provide user properties for local evaluation without making additional API calls.

```typescript
const flag = await client.getFeatureFlag('premium-feature', 'user_123', {
  personProperties: {
    plan: 'enterprise',
    role: 'admin',
    email: 'user@example.com'
  }
})
```

**Use Cases:**
- Local evaluation with targeting rules
- Avoiding additional API calls for user data
- Performance optimization

### Group Properties

Provide group properties for local evaluation.

```typescript
const flag = await client.getFeatureFlag('org-feature', 'user_123', {
  groups: { organization: 'acme-corp' },
  groupProperties: {
    organization: {
      tier: 'enterprise',
      employees: 500,
      region: 'us-west'
    }
  }
})
```

**Use Cases:**
- Organization-level targeting
- Multi-tenant feature rollouts
- Local evaluation with group rules

### Only Evaluate Locally

Force local evaluation only, never fall back to remote.

```typescript
const flag = await client.getFeatureFlag('flag-key', 'user_123', {
  onlyEvaluateLocally: true
})

// If local evaluation not ready or flag requires server evaluation,
// returns undefined instead of making API call
```

**Use Cases:**
- Performance-critical paths
- Offline operation
- Reducing API calls

### Send Feature Flag Events

Control whether `$feature_flag_called` events are sent.

```typescript
const flag = await client.getFeatureFlag('flag-key', 'user_123', {
  sendFeatureFlagEvents: false // Don't send events
})
```

**Use Cases:**
- Reducing event volume
- Flags checked frequently (e.g., every request)
- Internal/system flags

### Disable GeoIP

Disable GeoIP-based targeting for the flag check.

```typescript
const flag = await client.getFeatureFlag('flag-key', 'user_123', {
  disableGeoip: true
})
```

**Use Cases:**
- Privacy compliance
- Known user location
- Testing

### Flag Keys

Specify which flags to evaluate (for `getAllFlags` and `getAllFlagsAndPayloads`).

```typescript
const flags = await client.getAllFlags('user_123', {
  flagKeys: ['flag-1', 'flag-2', 'flag-3']
})

// Only returns values for specified flags
```

**Use Cases:**
- Performance optimization
- Loading specific feature sets
- Reducing payload size

## Common Patterns

### A/B Testing

Run A/B tests with multivariate flags and payloads.

```typescript
// Define flag with variants in PostHog UI:
// - variant-a (50%)
// - variant-b (50%)

async function renderPage(userId: string) {
  const variant = await client.getFeatureFlag('homepage-test', userId)

  switch (variant) {
    case 'variant-a':
      return renderVariantA()
    case 'variant-b':
      return renderVariantB()
    default:
      return renderDefault()
  }
}

// Track conversion
async function trackPurchase(userId: string, amount: number) {
  client.capture({
    distinctId: userId,
    event: 'purchase',
    properties: { amount }
  })
}
```

### Feature Rollout

Gradually roll out features to users.

```typescript
async function showNewFeature(userId: string) {
  const isEnabled = await client.isFeatureEnabled('new-feature', userId)

  if (isEnabled) {
    return renderNewFeature()
  } else {
    return renderOldFeature()
  }
}

// In PostHog UI, gradually increase rollout percentage:
// 5% -> 10% -> 25% -> 50% -> 100%
```

### Experiments

Run experiments with payload-based configuration.

```typescript
// Configure in PostHog UI with payloads:
// variant-a: { color: "blue", cta: "Buy Now" }
// variant-b: { color: "green", cta: "Get Started" }

async function renderCTA(userId: string) {
  const payload = await client.getFeatureFlagPayload('cta-experiment', userId)

  if (payload && typeof payload === 'object') {
    const config = payload as { color: string; cta: string }
    return `<button style="background: ${config.color}">${config.cta}</button>`
  }

  // Default
  return '<button>Learn More</button>'
}
```

### Remote Configuration

Use feature flags as remote configuration.

```typescript
// Configure in PostHog UI with payload:
// { apiUrl: "https://api.example.com", timeout: 5000, retries: 3 }

async function getApiConfig() {
  const config = await client.getRemoteConfigPayload('api-config')

  if (config && typeof config === 'object') {
    return config as {
      apiUrl: string
      timeout: number
      retries: number
    }
  }

  // Default config
  return {
    apiUrl: 'https://api.example.com',
    timeout: 3000,
    retries: 1
  }
}

// Use config
const config = await getApiConfig()
const response = await fetch(config.apiUrl, {
  timeout: config.timeout
})
```

### Targeted Feature Access

Enable features based on user properties and groups.

```typescript
async function hasAccessToFeature(userId: string, userPlan: string, orgId: string) {
  const hasAccess = await client.isFeatureEnabled('premium-feature', userId, {
    personProperties: { plan: userPlan },
    groups: { organization: orgId }
  })

  return hasAccess
}

// Usage
const canAccess = await hasAccessToFeature('user_123', 'enterprise', 'org_456')
if (canAccess) {
  showPremiumFeature()
}
```

### Bootstrap Application with Flags

Load all flags at application startup.

```typescript
async function bootstrapApp() {
  const client = new PostHog('your-api-key', {
    host: 'https://app.posthog.com',
    personalApiKey: 'phx_...'
  })

  // Wait for flags to load
  const ready = await client.waitForLocalEvaluationReady(5000)

  if (!ready) {
    console.warn('Flags not ready, using defaults')
  }

  // Get all flags for current user
  const flags = await client.getAllFlags('current-user')

  // Store in app state
  app.state.featureFlags = flags

  // Start app
  app.listen(3000)
}
```

### Feature Flag with Events

Send feature flag values with events automatically.

```typescript
// Capture event with feature flags
client.capture({
  distinctId: 'user_123',
  event: 'button_clicked',
  properties: { button_id: 'cta' },
  sendFeatureFlags: true // Include all active flags
})

// With specific flags
client.capture({
  distinctId: 'user_123',
  event: 'page_viewed',
  properties: { page: '/pricing' },
  sendFeatureFlags: {
    flagKeys: ['pricing-test', 'new-plans']
  }
})

// Only use local evaluation for flags
client.capture({
  distinctId: 'user_123',
  event: 'checkout_started',
  sendFeatureFlags: {
    onlyEvaluateLocally: true
  }
})
```

## Best Practices

### 1. Use Local Evaluation

Always provide a personal API key for better performance:

```typescript
// Good
const client = new PostHog('your-api-key', {
  host: 'https://app.posthog.com',
  personalApiKey: 'phx_...' // Enable local evaluation
})

// Not optimal (remote evaluation only)
const client = new PostHog('your-api-key', {
  host: 'https://app.posthog.com'
})
```

### 2. Wait for Local Evaluation at Startup

Ensure flags are ready before serving requests:

```typescript
async function startServer() {
  const client = new PostHog('your-api-key', {
    host: 'https://app.posthog.com',
    personalApiKey: 'phx_...'
  })

  // Wait for flags (with timeout)
  await client.waitForLocalEvaluationReady(5000)

  app.listen(3000)
  console.log('Server started with feature flags loaded')
}
```

### 3. Cache Flag Values Appropriately

For frequently checked flags, cache the value per request or session:

```typescript
class FeatureFlagCache {
  private cache: Map<string, { value: any; timestamp: number }> = new Map()
  private ttl = 60000 // 1 minute

  async getFlag(key: string, userId: string): Promise<any> {
    const cacheKey = `${key}:${userId}`
    const cached = this.cache.get(cacheKey)

    if (cached && Date.now() - cached.timestamp < this.ttl) {
      return cached.value
    }

    const value = await client.getFeatureFlag(key, userId)
    this.cache.set(cacheKey, { value, timestamp: Date.now() })

    return value
  }
}
```

### 4. Provide Properties for Better Targeting

Pass person and group properties for accurate local evaluation:

```typescript
// Good - properties included
const flag = await client.getFeatureFlag('feature', 'user_123', {
  personProperties: { plan: 'premium', email: 'user@example.com' },
  groups: { organization: 'acme' },
  groupProperties: { organization: { tier: 'enterprise' } }
})

// Not optimal - may require server evaluation
const flag = await client.getFeatureFlag('feature', 'user_123')
```

### 5. Use Groups for Organization Features

For B2B applications, use groups for organization-level flags:

```typescript
// Associate user with organization
client.capture({
  distinctId: 'user_123',
  event: 'page_view',
  groups: { organization: 'acme-corp' }
})

// Check organization flag
const hasFeature = await client.isFeatureEnabled('org-feature', 'user_123', {
  groups: { organization: 'acme-corp' }
})
```

### 6. Handle Undefined Gracefully

Always handle the case where a flag is undefined:

```typescript
// Good
const flag = await client.getFeatureFlag('new-feature', 'user_123')
if (flag === 'variant-a') {
  renderVariantA()
} else if (flag === 'variant-b') {
  renderVariantB()
} else {
  // Flag disabled, not found, or error
  renderDefault()
}

// Not safe
const flag = await client.getFeatureFlag('new-feature', 'user_123')
// flag might be undefined!
```

### 7. Disable Events for High-Volume Flags

For flags checked on every request, disable event tracking:

```typescript
// Checked frequently - disable events
const flag = await client.getFeatureFlag('rate-limit-config', userId, {
  sendFeatureFlagEvents: false
})

// Important experiment - keep events
const variant = await client.getFeatureFlag('pricing-test', userId, {
  sendFeatureFlagEvents: true // Default
})
```

### 8. Use getAllFlags for Multiple Checks

If checking multiple flags, use `getAllFlags` for better performance:

```typescript
// Good - single call
const flags = await client.getAllFlags('user_123')
if (flags['feature-a']) { /* ... */ }
if (flags['feature-b']) { /* ... */ }
if (flags['feature-c']) { /* ... */ }

// Not optimal - multiple calls
const featureA = await client.getFeatureFlag('feature-a', 'user_123')
const featureB = await client.getFeatureFlag('feature-b', 'user_123')
const featureC = await client.getFeatureFlag('feature-c', 'user_123')
```

### 9. Reload Flags After Updates

After updating flag definitions, force a reload:

```typescript
// Webhook endpoint for flag updates
app.post('/webhooks/posthog/flags-updated', async (req, res) => {
  await client.reloadFeatureFlags()
  console.log('Feature flags reloaded')
  res.json({ success: true })
})
```

### 10. Monitor Local Evaluation Status

Track when local evaluation is not ready:

```typescript
async function checkFlag(key: string, userId: string) {
  if (!client.isLocalEvaluationReady()) {
    console.warn('Local evaluation not ready, using remote evaluation')
  }

  return await client.getFeatureFlag(key, userId)
}

// Or use events
client._events.on('error', (error) => {
  console.error('Feature flag error:', error)
})

client._events.on('localEvaluationFlagsLoaded', (count) => {
  console.log(`Loaded ${count} feature flags for local evaluation`)
})
```

### 11. Test with Different Variants

Create test utilities for flag variants:

```typescript
// Test utility
class FeatureFlagTestHelper {
  private overrides: Map<string, any> = new Map()

  setFlag(key: string, value: any) {
    this.overrides.set(key, value)
  }

  async getFlag(key: string, userId: string) {
    if (this.overrides.has(key)) {
      return this.overrides.get(key)
    }
    return await client.getFeatureFlag(key, userId)
  }

  clearOverrides() {
    this.overrides.clear()
  }
}

// In tests
const helper = new FeatureFlagTestHelper()
helper.setFlag('new-feature', 'variant-a')
// Test variant-a behavior
```

### 12. Use TypeScript for Type Safety

Define types for flag values and payloads:

```typescript
// Define flag types
type FeatureFlags = {
  'new-ui': boolean
  'pricing-test': 'control' | 'variant-a' | 'variant-b'
  'button-config': { color: string; text: string }
}

// Type-safe wrapper
async function getFlag<K extends keyof FeatureFlags>(
  key: K,
  userId: string
): Promise<FeatureFlags[K] | undefined> {
  return await client.getFeatureFlag(key, userId) as FeatureFlags[K] | undefined
}

// Usage
const uiEnabled = await getFlag('new-ui', 'user_123')
// Type: boolean | undefined

const variant = await getFlag('pricing-test', 'user_123')
// Type: 'control' | 'variant-a' | 'variant-b' | undefined
```
