Skip to main content

Feature Flags Developer Guide

A practical guide for developers working with feature flags in the Next.js monorepo.

Overview

Feature flags enable controlled feature rollouts, A/B testing, and runtime configuration without deploying new code. Our system integrates the Vercel Flags SDK with LaunchDarkly for powerful, flexible feature management.

Key Benefits:

  • Gradual Rollouts: Deploy features to specific user segments
  • A/B Testing: Test different implementations simultaneously
  • Emergency Toggles: Disable problematic features instantly
  • Parallel Development: Multiple teams work independently

Key Resources:


Quick Start Checklist

Before implementing a feature flag:

  • Jira ticket created with proper labels (_FF prefix and _app prefix)
  • Feature flag naming, taken from a Jira ticket label, follows conventions
  • Determine if flag is static (build-time) or dynamic (runtime)

Understanding Feature Flag Types

Static vs Dynamic Flags

Choose the right type for your use case:

TypeUse CaseEvaluationExample
StaticPages generated at build time or ISRBuild-time via evaluateStaticFlag()Content pages, marketing pages, stories
DynamicRuntime features and API endpointsRuntime via evaluateDynamicFlag()Search results, API responses, webhooks
When to Use Which
  • Static: Most visible UI changes on pages that use Static Site Generation (SSG) or Incremental Static Regeneration (ISR)
  • Dynamic: Features that must respond to runtime conditions like search queries, user actions, or API calls
Platform Files Exception

Some changes (e.g., next.config.js, build configuration) don't support feature flags. In these cases, implement your fix directly without flag wrapping.


Flag Creation Process

Prerequisites

Required Information:

  1. Jira Ticket ID: e.g., CMS-69342 from a Jira ticket URL, for example: https://jira.schwab.com/browse/CMS-69342
  2. Jira Labels (added by team lead or automated process):
    • Feature Flag name: Starts with FF_ (e.g., FF_Fix_Something_PUB)
    • Application name: Starts with app_ (e.g., app_www.schwab.com)

Feature Flag Labels

Feature Flag Naming Convention

Feature flags follow a naming pattern: FF_<Feature>_<Audience>

Examples:

  • FF_Fix_Something_PUB → becomes fixSomethingPub in code
  • FF_Release_Edu_Search → becomes releaseEduSearch in code

The formatFeatureFlagName function automatically converts labels to camelCase.

Creating Feature Flags

Two CLI commands create feature flags automatically:

Use this when starting fresh work on a Jira ticket.

schwab checkout CMS-69342

What it does:

  1. Checks if you're already on a branch for this ticket
  2. Prompts to create/checkout an appropriate branch
  3. Detects feature flags from Jira labels
  4. Offers to create feature flag files automatically
  5. Internally calls schwab pullflags if needed

Interactive Prompts:

  • Select branch from list (use arrow keys + spacebar)
  • Confirm feature flag creation (if flags detected in Jira labels)

Option 2: schwab pullflags

Use this when:

  • You've already created a feature branch
  • New flags were added to your Jira ticket
  • You need to reset/recreate a flag file
schwab pullflags CMS-69342

What it does:

  • Scans Jira ticket for _FF labels
  • Creates missing flag files in the appropriate app directory
  • Updates existing flags if needed

Generated Flag File Structure

After running either command, your flag file will be created:

Location: /home/nextjs-web/apps/<app-name>/src/flags/flags/<flagName>.ts

Example: For FF_Fix_Something_PUB in www.schwab.com:

/home/nextjs-web/apps/www.schwab.com/src/flags/flags/fixSomethingPub.ts
import { declareFlag } from '@schwab/utilities/flags';

export const fixSomethingPub = declareFlag({
key: 'fixSomethingPub',
defaultValue: false,
description: 'Fix: EDU SomethingToFix PUB',
});

export default fixSomethingPub;
Default Value

Always leave defaultValue: false when committing. This ensures the flag is off by default in all environments.

Verification

Verify your flag was created successfully:

# List all flags in your app
ls /home/nextjs-web/apps/www.schwab.com/src/flags/flags/

# Check flag content
cat /home/nextjs-web/apps/www.schwab.com/src/flags/flags/fixSomethingPub.ts

Implementing Feature Flags

Finding Your Feature Flag

From Jira Labels:

  • Flag label: _FF_Fix_Something_PUB → variable name: fixSomethingPub
  • App label: _app_www.schwab.com → location: /home/nextjs-web/apps/www.schwab.com/src/flags/flags/

File location pattern:

/home/nextjs-web/apps/<app-name>/src/flags/flags/<flagNameInCamelCase>.ts

Variable Naming Convention

Pattern: Prefix flag name with flag when using in code

Flag File NameVariable Name in Code
fixSomethingPub.tsflagFixSomethingPub
releaseEduSearch.tsflagReleaseEduSearch
featureCheckBotIdModes.tsflagFeatureCheckBotIdModes

Implementation Pattern Overview

Feature flags create conditional code paths:

// Pseudo-code pattern
if (flagFeatureName === true) {
// New feature code
} else {
// Existing code (kept for rollback)
}
Code Coexistence

Unlike typical development, both old and new code remain in the codebase. The old code provides a rollback path if issues arise.

Static Feature Flags (Build-Time)

When to Use

Use static flags for:

  • Pages generated at build time (SSG)
  • Incremental Static Regeneration (ISR) pages
  • Most visible UI components
  • Content pages, stories, marketing pages

Basic Implementation

Step 1: Import Required Functions

Page Component
import { evaluateStaticFlag } from '@schwab/utilities/flags';
import { allFlags } from '../../../flags'; // Adjust path as needed

Step 2: Evaluate Flags in Component

Simple Example
export default async function Page({ params }: { params: Promise<Params> }): Promise<JSX.Element> {
const { code } = await params;

const [flagFixSomethingPub] = await Promise.all([
evaluateStaticFlag(allFlags, code, 'fixSomethingPub'),
]);

// Use the flag...
}

Step 3: Multiple Flags (Common Pattern)

Multiple Flags
const [
flagTealiumTracking,
flagEduSearch,
flagFixSomethingPub,
] = await Promise.all([
evaluateStaticFlag(allFlags, code, 'tealiumTrackanalyticsFunction'),
evaluateStaticFlag<boolean>(allFlags, code, 'releaseEduSearch'),
evaluateStaticFlag(allFlags, code, 'fixSomethingPub'),
]);
Type Safety

Use TypeScript generics for non-boolean flags:

evaluateStaticFlag<string>(allFlags, code, 'featureCheckBotIdModes')

Getting code and flags Parameters

Scenario A: Parameters Already Available

Some components already receive flags and code as props:

Content Type Components:

  • /packages/ui/src/content-types/PatternLandingPage/pattern-landing-page.tsx
  • /packages/ui/src/content-types/Story/story.tsx
  • /packages/ui/src/content-types/Collection/collection.tsx
  • /packages/ui/src/content-types/Topic/topic.tsx
  • /packages/ui/src/content-types/Person/person.tsx
Using Existing Props
export default async function SomeContentType({
flags, // Already available
code, // Already available
// ... other props
}: TComponentProps): Promise<JSX.Element> {
const [flagFixSomethingPub] = await Promise.all([
evaluateStaticFlag(flags, code, 'fixSomethingPub'),
]);
}

Scenario B: Top-Level Page Component

For Next.js page components, extract code from params:

Top-Level Page
type Params = {
code: string;
alias: string[];
};

export default async function Page({ params }: { params: Promise<Params> }): Promise<JSX.Element> {
const { code } = await params;

const [flagFixSomethingPub] = await Promise.all([
evaluateStaticFlag(allFlags, code, 'fixSomethingPub'),
]);
}

Real-World Example: Conditional Rendering

Conditional Component Rendering
export default async function StoryPage({ params }: { params: Promise<Params> }): Promise<JSX.Element> {
const { code } = await params;

const [flagNewStoryHeader] = await Promise.all([
evaluateStaticFlag(allFlags, code, 'experimentEduNewStoryHeader'),
]);

return (
<Layout>
{flagNewStoryHeader ? (
<NewStoryHeader {...headerProps} />
) : (
<LegacyStoryHeader {...headerProps} />
)}
<StoryContent {...contentProps} />
</Layout>
);
}

Real-World Example: Feature Modification

Modifying Feature Behavior
export default async function BranchLocator({ params }: { params: Promise<Params> }): Promise<JSX.Element> {
const { code } = await params;

const [flagGeocodingSuggestions] = await Promise.all([
evaluateStaticFlag(allFlags, code, 'releaseBranchGeocodingSuggestions'),
]);

const searchConfig = {
...baseConfig,
enableSuggestions: flagGeocodingSuggestions,
maxResults: flagGeocodingSuggestions ? 10 : 5,
};

return <BranchSearch config={searchConfig} />;
}

Dynamic Feature Flags (Runtime)

When to Use

Use dynamic flags for:

  • API routes and endpoints
  • Server actions
  • Search and filter operations
  • Runtime-evaluated features
  • Webhook handlers

Basic Implementation

Step 1: Import Required Functions

API Route or Runtime Component
import { evaluateDynamicFlag } from '@schwab/utilities/flags';
import { allFlags } from '../../../../flags'; // Adjust path as needed

Step 2: Evaluate Flags at Runtime

Simple Dynamic Flag
export async function GET(request: Request) {
const [flagApiOptimization] = await Promise.all([
evaluateDynamicFlag(allFlags, 'featureApiOptimization'),
]);

if (flagApiOptimization) {
return optimizedApiHandler(request);
}
return legacyApiHandler(request);
}

Real-World Example: API Route

apps/meganav-mfe/src/app/api/output/json/functions/getMfeFeatureFlags.ts
export async function getMfeFeatureFlags(): Promise<Record<string, boolean>> {
const [
nextjsDrupalRevalidate,
featureMeganavFooter,
meganavMfeDisableSearchUi,
] = await Promise.all([
evaluateDynamicFlag(allFlags, 'nextjsDrupalRevalidate'),
evaluateDynamicFlag(allFlags, 'featureMeganavFooter'),
evaluateDynamicFlag(allFlags, 'meganavMfeDisableSearchUi'),
]);

return {
nextjsDrupalRevalidate,
featureMeganavFooter,
meganavMfeDisableSearchUi,
};
}

Real-World Example: Dynamic URL Construction

Runtime URL Modification
export async function revalidateContent() {
const [flagNewApiDomain] = await Promise.all([
evaluateDynamicFlag(allFlags, 'featureNewApiDomain'),
]);

const hostDomain = process.env.NEXT_API_PROD_DOMAIN;
const apiDomain = flagNewApiDomain
? hostDomain.replace('api', 'api-new')
: hostDomain;

const apiUrl = `https://${apiDomain}/api/revalidate`;

return fetch(apiUrl, { method: 'POST' });
}

Passing Flags to Child Components

The Challenge

Feature flags must be evaluated at the top level (Page component) to work with Vercel Toolbar and LaunchDarkly. When your changes are in a child component, you need to pass flags down the component tree.

Anti-Pattern: Individual Flag Props

Don't do this (doesn't scale):

Poor scalability
interface IChildProps {
existingVar1: number;
existingVar2: string;
flagFixSomething: boolean;
flagNewFeature: boolean;
// Adding more flags requires interface changes
}

Do this (scalable):

Scalable pattern using Record
import { TFeatureFlag } from '@schwab/schema/global/FeatureFlag';

interface IChildProps {
existingVar1: number;
existingVar2: string;
featureFlags?: TFeatureFlag; // Extensible
}

export function ChildComponent({
existingVar1,
existingVar2,
featureFlags,
}: IChildProps): JSX.Element {
// Access flags
const isNewFeatureEnabled = featureFlags?.flagNewFeature;

return (
// Component JSX
);
}

Complete Example

Parent Component (Page Level)
import { TFeatureFlag } from '@schwab/schema/global/FeatureFlag';
import { evaluateStaticFlag } from '@schwab/utilities/flags';
import { allFlags } from '../../../flags';

export default async function ParentPage({ params }: { params: Promise<Params> }): Promise<JSX.Element> {
const { code } = await params;

const [
flagFixSomething,
flagNewFeature,
flagExperiment,
] = await Promise.all([
evaluateStaticFlag(allFlags, code, 'fixSomethingPub'),
evaluateStaticFlag(allFlags, code, 'releaseNewFeature'),
evaluateStaticFlag(allFlags, code, 'experimentAbTest'),
]);

// Create flag record
const flaglist: TFeatureFlag = {
flagFixSomething,
flagNewFeature,
flagExperiment,
};

return (
<ChildComponent
existingVar1={42}
existingVar2="data"
featureFlags={flaglist}
/>
);
}
Child Component
import { TFeatureFlag } from '@schwab/schema/global/FeatureFlag';

interface IChildProps {
existingVar1: number;
existingVar2: string;
featureFlags?: TFeatureFlag;
}

export function ChildComponent({
existingVar1,
existingVar2,
featureFlags,
}: IChildProps): JSX.Element {
return (
<div>
{featureFlags?.flagNewFeature ? (
<NewImplementation />
) : (
<LegacyImplementation />
)}
</div>
);
}

Viewing Features

Once you've implemented feature flags in your code, you'll want to toggle them on and off to see the results. There are four methods to control feature flags during development and testing.

Method 1: Manual Flag Override (Quick Testing)

The fastest way to test your feature flag locally.

Option A: Modify in Code (Temporary)

Directly override the flag value after evaluation:

Direct Override
const [tealiumTrackanalyticsFunction, flagFixSomethingPub] = await Promise.all([
evaluateStaticFlag(flags, code, 'tealiumTrackanalyticsFunction'),
evaluateStaticFlag(flags, code, 'fixSomethingPub'),
]);
flagFixSomethingPub = true; // Override for testing
Remember to Remove

Remove this override before committing your code!

Option B: Change Default Value (Cleaner)

Modify the flag definition file directly:

Location: /home/nextjs-web/apps/www.schwab.com/src/flags/flags/fixSomethingPub.ts

Flag Definition File
export const fixSomethingPub = declareFlag({
key: 'fixSomethingPub',
defaultValue: true, // Changed from false for testing
description: 'Fix: EDU SomethingToFix PUB',
});

The development server will automatically detect the change and update.

Important

ALWAYS change the value back to false before committing your code.

Method 2: Vercel Toolbar Component (Development Mode)

The Vercel Toolbar provides a visual interface to toggle flags in real-time.

Availability

Currently available in:

  • www.schwab.com

The toolbar is automatically loaded in development mode via the layout file:

Location: /home/nextjs-web/apps/www.schwab.com/src/app/[code]/layout.tsx

Vercel Toolbar Integration
import { VercelToolbar } from '@vercel/toolbar/next';
...
const shouldInjectToolbar = process.env.NODE_ENV === 'development';
...
{shouldInjectToolbar && <VercelToolbar />}

Usage Steps

  1. Start Development Server
cd /home/nextjs-web/apps/www.schwab.com
pnpm dev
  1. Open App in Browser

Navigate to your local development URL (typically http://localhost:3000)

  1. Locate the Vercel Toolbar Widget

Look for the widget in the bottom-right corner of your browser:

Vercel Toolbar Widget

  1. Select Feature Flags

Click the widget to open the toolbar interface:

Choosing Feature Flag Selector

  1. Toggle Flags

Use the slider to change flag values from false to true (or vice versa):

Turning Flags On/Off

  1. Apply Changes

Click the "Apply" button (lower right corner) to activate your changes.

Troubleshooting

If the Vercel Toolbar widget disappears after clicking Apply:

  • Refresh your browser window
  • Scroll the page slightly
  • The widget should reappear

How It Works

After clicking Apply, the feature flag changes take effect within seconds. The page will reload with the new flag configuration automatically.

Method 3: Vercel Chrome Extension (Cross-App Support)

For a more robust solution that works across all apps and environments, install the Vercel browser extension.

Installation

Google Chrome:

Microsoft Edge:

Advantages

  • Works in all apps (not just www.schwab.com)
  • Available across different environments
  • Same interface as the Vercel Toolbar component
  • Persists across browser sessions

Once installed, the extension provides the same toolbar widget interface shown in Method 2.

Method 4: LaunchDarkly (Integration Testing)

Use LaunchDarkly to test flags in preview deployments and production-like environments.

Prerequisites

This method requires:

  • Code committed and pushed to repository
  • Vercel preview deployment created
  • LaunchDarkly access (see permissions guide)

Step 1: Commit Your Changes

schwab commit

Interactive prompts:

  • Replace previous commit? → Your choice (yes to amend, no to create new)
  • Type of change? → Usually "patch"
  • Commit message? → e.g., "testing feature flag"
  • URL to demonstrate? → Not needed for testing, can skip

Step 2: Push to Remote

schwab push

Interactive prompts:

  • Create pull request? → No
  • Ready for review? → No

Wait for build: Watch the terminal output. Look for "NextJS Build Succeeded" message.

Sync Wait Time

After pushing, wait 5-10 minutes for:

  • Vercel to create preview deployment
  • Feature flag to sync with LaunchDarkly

Step 3: Find Preview Deployment

Visit the Vercel deployments page: https://vercel.com/charles-schwab/www.schwab.com/deployments?environment=preview

Your deployment should be at the top of the list. Search by:

  • Feature flag name
  • Ticket number
  • Commit message

Step 4: Toggle in LaunchDarkly

  1. Login to LaunchDarkly

    Navigate to: https://app.launchdarkly.com

    Access Required

    If you cannot login or switch flags, request permissions via the LaunchDarkly Process Flow documentation.

  2. Find Your Flag

    The landing page shows a list of all feature flags. Locate yours by name.

  3. Select Environment

    Choose "Preview" environment from the dropdown.

  4. Toggle Flag State

    Use the toggle switch to turn the flag on/off.

  5. Test Preview Deployment

    Visit your preview deployment URL and verify the flag behavior.

LaunchDarkly Interface

The LaunchDarkly dashboard allows you to:

  • Toggle flags for specific environments
  • Target specific users or user segments
  • Set percentage rollouts
  • View flag evaluation metrics
  • Configure flag rules and variations

Comparison of Methods

MethodSpeedEnvironmentsAppsSetup Required
Manual OverrideInstantLocal onlyAllNone
Vercel ToolbarInstantLocal devLimitedAuto (if enabled)
Chrome ExtensionInstantLocal devAllExtension install
LaunchDarkly5-10 min syncAll (Preview, Prod)AllCommit + Push
  1. Initial Development: Use Manual Override for quick iteration
  2. Component Testing: Use Vercel Toolbar/Extension for visual testing
  3. Integration Testing: Use LaunchDarkly for preview environment testing
  4. Production Rollout: Use LaunchDarkly for gradual rollout and monitoring

Testing With Feature Flags

Local Development Testing

Method 1: Manual Flag Override (Fastest)

Temporarily change the default value in your flag file:

apps/www.schwab.com/src/flags/flags/fixSomethingPub.ts
export const fixSomethingPub = declareFlag({
key: 'fixSomethingPub',
defaultValue: true, // Changed for testing
description: 'Fix: EDU SomethingToFix PUB',
});
Remember to Revert

ALWAYS change back to defaultValue: false before committing!

For apps with Vercel Toolbar enabled (e.g., www.schwab.com):

  1. Start development server:
cd /home/nextjs-web/apps/www.schwab.com
pnpm dev
  1. Open your app in browser (e.g., http://localhost:3000)
  2. Click Vercel Toolbar widget (bottom-right)
  3. Toggle feature flags on/off
  4. Click "Apply" to see changes
Troubleshooting

If toolbar disappears after clicking Apply, refresh the page and scroll slightly.

Method 3: Vercel Chrome Extension

Install for broader coverage across all apps:

Unit Testing with Mocks

Use the Mockery class from @schwab/test:

Feature Flag Mocking
import { Mockery } from '@schwab/test';

describe('MyComponent with feature flags', () => {
beforeEach(() => {
// Mock static flags
Mockery.mockEvaluateStaticFlag({
fixSomethingPub: true,
releaseNewFeature: false,
});
});

it('renders new feature when flag is enabled', async () => {
const component = await MyComponent({ params: mockParams });
expect(component).toContain('New Feature');
});
});
Dynamic Flag Mocking
import { Mockery } from '@schwab/test';

describe('API Route with dynamic flags', () => {
beforeEach(() => {
// Mock dynamic flags
Mockery.mockEvaluateDynamicFlagImplementation('on');
});

it('uses optimized endpoint when flag is on', async () => {
const response = await GET(mockRequest);
expect(response.url).toContain('api-new');
});
});

Integration Testing with LaunchDarkly

Prerequisites

  1. Code committed and pushed
  2. Preview deployment created
  3. LaunchDarkly access

Steps

1. Commit and Push

schwab commit
# Follow prompts - type: patch, message: "testing flag"

schwab push
# No to PR, No to ready for review
# Wait for "NextJS Build Succeeded"

2. Wait for Deployment

  • Integration engine triggers Vercel preview deployment
  • Flag syncs to LaunchDarkly (usually 2-5 minutes)

3. Find Preview Deployment

Visit: https://vercel.com/charles-schwab/www.schwab.com/deployments?environment=preview

Search for your metadata (flag name, ticket number)

4. Toggle in LaunchDarkly

  1. Login: https://app.launchdarkly.com
  2. Find your flag in the list
  3. Select "Preview" environment
  4. Toggle on/off
  5. Test your preview deployment

Environment Variables

Local Configuration

Edit .env.local in your app directory:

Example: /home/nextjs-web/apps/www.schwab.com/.env.local
NEXT_PUBLIC_API_URL=https://api.schwab.com
LAUNCHDARKLY_SDK_KEY=sdk-key-here

Build-Time Variables

For variables needed at build time:

1. Add to turbo.json

/home/nextjs-web/turbo.json
{
"globalEnv": [
"NEXT_PUBLIC_API_URL",
"LAUNCHDARKLY_SDK_KEY"
]
}

2. Add to Vercel Dashboard

  1. Go to Vercel Environment Variables
  2. Select correct app from top menu
  3. Add variable name and value
  4. Choose appropriate environments

3. Test Locally

vercel env pull --environment=development --cwd /home/nextjs-web/apps/www.schwab.com

Common Patterns and Best Practices

1. Feature Flag Lifecycle

2. Error Handling

Always provide fallbacks:

Defensive Flag Usage
export default async function Page({ params }: { params: Promise<Params> }): Promise<JSX.Element> {
const { code } = await params;

try {
const [flagNewFeature] = await Promise.all([
evaluateStaticFlag(allFlags, code, 'releaseNewFeature'),
]);

// Use flag with fallback
const useNewFeature = flagNewFeature ?? false;

return useNewFeature ? <NewFeature /> : <LegacyFeature />;
} catch (error) {
console.error('Flag evaluation failed:', error);
// Always fallback to safe default
return <LegacyFeature />;
}
}

3. Performance Optimization

Evaluate all flags in parallel:

Parallel Flag Evaluation
// ✅ Good: Parallel evaluation
const [flag1, flag2, flag3] = await Promise.all([
evaluateStaticFlag(allFlags, code, 'flag1'),
evaluateStaticFlag(allFlags, code, 'flag2'),
evaluateStaticFlag(allFlags, code, 'flag3'),
]);

// ❌ Bad: Sequential evaluation (slow)
const flag1 = await evaluateStaticFlag(allFlags, code, 'flag1');
const flag2 = await evaluateStaticFlag(allFlags, code, 'flag2');
const flag3 = await evaluateStaticFlag(allFlags, code, 'flag3');

4. Type Safety

Use TypeScript generics for non-boolean flags:

Type-Safe Flags
// String flag
const mode = await evaluateStaticFlag<string>(
allFlags,
code,
'featureCheckBotIdModes'
);

// Number flag
const limit = await evaluateStaticFlag<number>(
allFlags,
code,
'featureResultsLimit'
);

// Boolean flag (default, no generic needed)
const enabled = await evaluateStaticFlag(
allFlags,
code,
'releaseNewFeature'
);

Troubleshooting

Flag Not Showing in Vercel Toolbar

Symptoms: Flag doesn't appear in toolbar widget

Solutions:

  1. Verify flag file exists in src/flags/flags/
  2. Check flag is exported in src/flags/index.ts (should be automatic)
  3. Restart dev server: pnpm dev
  4. Clear browser cache and reload
  5. Verify <VercelToolbar> component is loaded (check layout.tsx)

Flag Not Syncing to LaunchDarkly

Symptoms: Flag doesn't appear in LaunchDarkly dashboard

Solutions:

  1. Wait 5-10 minutes after push (sync is not instant)
  2. Verify build succeeded in Vercel dashboard
  3. Check environment variables are set correctly
  4. Confirm LaunchDarkly permissions (contact team lead)

Flag Value Not Changing

Symptoms: Toggling flag has no effect

Solutions:

  1. Click "Apply" button in Vercel Toolbar
  2. Refresh browser page
  3. Check flag is being evaluated (not hardcoded)
  4. Verify correct flag name (case-sensitive)
  5. Check for caching issues (disable browser cache)

TypeScript Errors

Symptoms: Type errors when using flags

Solutions:

  1. Import correct types:
    import { TFeatureFlag } from '@schwab/schema/global/FeatureFlag';
  2. Use generics for non-boolean flags:
    evaluateStaticFlag<string>(allFlags, code, 'flagName')
  3. Provide fallback values:
    const flag = await evaluateStaticFlag(allFlags, code, 'flagName') ?? false;

Build Failures

Symptoms: Build fails after adding flag

Solutions:

  1. Verify flag file syntax (check for typos)
  2. Ensure defaultValue is set
  3. Check imports are correct
  4. Run local build: pnpm build
  5. Check for circular dependencies

Quick Reference

Import Statements

// Static flags
import { evaluateStaticFlag } from '@schwab/utilities/flags';
import { allFlags } from '../../../flags';

// Dynamic flags
import { evaluateDynamicFlag } from '@schwab/utilities/flags';
import { allFlags } from '../../../../flags';

// Types
import { TFeatureFlag } from '@schwab/schema/global/FeatureFlag';

// Testing
import { Mockery } from '@schwab/test';

Common Commands

# Create/checkout with flags
schwab checkout CMS-12345

# Pull flags for existing branch
schwab pullflags CMS-12345

# Start dev server
pnpm dev

# Commit and push
schwab commit
schwab push

# Pull environment variables
vercel env pull --environment=development --cwd /home/nextjs-web/apps/www.schwab.com

File Locations

ItemLocation
Flag definitions/apps/<app>/src/flags/flags/
Flag index/apps/<app>/src/flags/index.ts
Environment variables/apps/<app>/.env.local
Utility functions/packages/utilities/src/flags/index.ts
Type definitions/packages/schema/src/global/FeatureFlag.ts
Test utilities/packages/test/src/mock/base/Mockery.ts

See Also