Feature Flags: A Practical Guide for Developers

You’ve built a new feature. It’s tested, reviewed, and ready to ship. But you’re nervous—what if something goes wrong in production? What if users hate it?

Feature flags let you deploy code without exposing it to users, then gradually roll it out while monitoring for problems. In this guide, we’ll learn how to implement and use feature flags effectively.

Похожее
string::substr in C++: Extracting Parts of Strings
C++ offers various methods to work with strings. Among them, the substr function stands out as a way to extract specific portions of a string. In this article, we'll dive into the substr function, see how it works, and explore its numerous applications.
go

What are feature flags?

A feature flag (also called feature toggle) is a conditional that controls whether a piece of code runs:

if (featureFlags.get('new-checkout')) {
  showNewCheckout()
} else {
  showOldCheckout()
}

The flag’s value is stored externally (in a config file, database, or remote service), so you can change it without deploying code.

This simple concept enables powerful workflows:

  • Dark launches: Deploy code that’s hidden from users
  • Gradual rollouts: Enable features for 1%, then 10%, then 100%
  • Kill switches: Instantly disable broken features
  • A/B testing: Show different variants to different users

Types of feature flags

Boolean flags

The simplest type—on or off:

if (flags.get('dark-mode-enabled')) {
  applyDarkTheme()
}
Похожее
vector::size in C++ (With Examples)
In C++, the std::vector container is a dynamic array that can resize itself automatically. A common need is to determine how many elements a vector currently holds. Enter the size function! This article will break down how the size function works, show you some practical examples, and give insights into related features of vectors.
go

Percentage rollouts

Enable a feature for a percentage of users:

// 10% of users see the new feature
if (flags.get('new-pricing', { context: { userId: user.id } })) {
  showNewPricing()
}

The flag system uses the user ID to deterministically decide whether this user is in the 10%. The same user always gets the same result.

User targeting

Enable features for specific users or groups:

// Only premium users get this feature
if (flags.get('advanced-analytics', { context: { plan: user.plan } })) {
  showAdvancedAnalytics()
}

Or target specific users by ID:

// Beta testers only
if (flags.get('experimental-feature', { context: { userId: user.id } })) {
  showExperimentalFeature()
}

Which type of feature flag would you use to test a new checkout flow with 5% of users?

Boolean flag
Percentage rollout
User targeting by ID
Environment variable

Implementing feature flags

Похожее
vector::push_back in C++: Adding Elements to a Vector
C++ offers various ways to manipulate data. The push_back function is a popular way to add elements to a vector. In this article, we'll delve deep into this function, exploring how and when to use it, and discuss some intriguing aspects related to it.
go

Simple in-memory implementation

For small projects, you can start with a simple object:

const FEATURE_FLAGS = {
  'new-checkout': false,
  'dark-mode': true,
  'experimental-api': false,
}

function isEnabled(flagName) {
  return FEATURE_FLAGS[flagName] ?? false
}

This works, but changes require code changes and redeployment.

File-based configuration

Store flags in a JSON file:

import { readFileSync } from 'fs'

function loadFlags() {
  const content = readFileSync('flags.json', 'utf-8')
  return JSON.parse(content)
}

function isEnabled(flagName) {
  const flags = loadFlags()
  return flags[flagName] ?? false
}

Better—you can change flags without changing code. But you still need to restart the application to pick up changes.

Remote configuration

For production systems, use a remote configuration service that pushes updates in real-time:

import { Replane } from '@replanejs/sdk'

interface Configs {
  'new-checkout': boolean
  'advanced-analytics': boolean
}

const replane = new Replane<Configs>()

await replane.connect({
  sdkKey: process.env.REPLANE_SDK_KEY!,
  baseUrl: 'https://cloud.replane.dev',
})

// Flags update in real-time, no restart needed
function isEnabled(flagName: keyof Configs, context?: Record<string, unknown>): boolean {
  if (context) {
    return replane.get(flagName, { context })
  }
  return replane.get(flagName)
}

With remote configuration, you can change flags in a dashboard and have changes take effect across all servers in seconds.

Похожее
vector::erase in C++ (With Examples)
C++ offers several ways to manage elements in its containers. The erase function is a popular way to remove elements from the vector. In this article, we'll dive deep into using this function, understand its intricacies, and look at practical examples.
go

Percentage rollouts in detail

Percentage rollouts need to be deterministic—the same user should always get the same result. Here’s how to implement this:

import { createHash } from 'crypto'

function isInRollout(userId: string, flagName: string, percentage: number): boolean {
  // Create a hash of userId + flagName
  const key = `${userId}:${flagName}`
  const hash = createHash('md5').update(key).digest('hex')

  // Convert first 8 hex chars to number, then to 0-100 range
  const bucket = parseInt(hash.substring(0, 8), 16) % 100

  return bucket < percentage
}

Using it:

// 10% rollout of new-checkout
if (isInRollout(user.id, 'new-checkout', 10)) {
  showNewCheckout()
} else {
  showOldCheckout()
}

The hash ensures:

  • Same user + same flag = same result (deterministic)
  • Different flags give different results (independent)
  • Distribution is roughly uniform

Most feature flag services handle this automatically. For example, with Replane, you configure percentage rollouts in the dashboard and the SDK handles the math.

Why is it important for percentage rollouts to be deterministic?

To save CPU cycles
To reduce network calls
So users get a consistent experience
To comply with regulations

Best practices

Name flags clearly

Use descriptive names that explain what the flag does:

// Good
'enable-new-checkout-flow'
'show-premium-features'
'use-v2-recommendation-engine'

// Bad
'flag1'
'test'
'new_thing'
Похожее
string::size in C++: Measuring String Length
C++ offers a variety of tools to work with strings, and one of the most essential functions is string::size. In this article, we'll dive deep into this function, understand how it measures strings, and explore its real-world applications.
go

Set expiration dates

Feature flags are temporary. When a feature is fully rolled out, remove the flag:

// This flag has been at 100% for 3 months
// Time to remove it and clean up the code
if (flags.get('new-checkout')) {  // DELETE THIS
  showNewCheckout()
} else {
  showOldCheckout()  // DELETE THIS
}

// Becomes simply:
showNewCheckout()

Track flag creation dates and set reminders to clean up.

Use defaults wisely

Always have a safe default in case the flag service is unavailable:

// Good: safe default
const newFeatureEnabled = flags.get('risky-new-feature', { default: false })

// Bad: no default, might throw
const newFeatureEnabled = flags.get('risky-new-feature')

For risky features, the default should be false. For well-established features behind flags, the default might be true.

Log flag evaluations

When debugging, you need to know which flags were active:

function getFlag(name, context) {
  const value = flags.get(name, { context })
  logger.debug(`Flag ${name} = ${value} for context ${JSON.stringify(context)}`)
  return value
}
Похожее
map::count function in C++
C++ offers a range of functions to navigate its containers. One such function, `count`, helps find if a key exists in a `std::map`. Dive in to learn how to use it, when it's handy, and see it in action.
go

Keep flag checks simple

Avoid complex logic around flags:

// Bad: nested flags are confusing
if (flags.get('feature-a')) {
  if (flags.get('feature-b')) {
    doSomething()
  } else if (flags.get('feature-c')) {
    doSomethingElse()
  }
}

// Better: one flag, clear behavior
if (flags.get('feature-a-with-b')) {
  doSomething()
}

Common patterns

Kill switch

A flag that’s normally true but can be turned false to disable a feature:

if (flags.get('payments-enabled', { default: true })) {
  processPayment()
} else {
  showMaintenanceMessage()
}

If payments break at 2am, flip the switch and users see a friendly message instead of errors.

Похожее
strlen in C/C++: string length
The strlen function in C is used to count the number of characters in a string. In this article, we'll look at examples of its usage. Additionally, we'll implement this function ourselves, and wrap up with some exercises for further practice.
go

Ops flags vs release flags

Separate operational flags from release flags:

// Ops flag: controls behavior, long-lived
const rateLimit = flags.get('api-rate-limit')

// Release flag: temporary, remove after rollout
if (flags.get('new-ui-enabled')) {
  showNewUI()
}

Ops flags stay forever. Release flags should be removed once the feature is fully rolled out.

Trunk-based development

Feature flags enable trunk-based development—everyone commits to main, but incomplete features are hidden behind flags:

main ──●──●──●──●──●──●──●──▶
       │  │  │  │  │  │  │
       Feature A (flagged off)
          └──┴──┴──┴──┘
             Feature B (flagged off)
                   └──┘

This avoids long-lived feature branches and merge conflicts.

Tools for feature flags

You can build your own feature flag system, but existing tools save time:

  • Replane — Open-source, self-hosted or cloud, supports JavaScript/Python/.NET
  • LaunchDarkly — Enterprise feature management
  • Unleash — Open-source feature toggles
  • Flagsmith — Open-source with cloud option

For open-source and self-hosting, Replane is a solid choice with real-time updates via SSE and built-in version history.

Похожее
The map::find in C++: Efficiently Locating Elements
C++ boasts a rich collection of libraries, and the map container is among its gems. A key function within this container is map::find, which is essential for locating elements swiftly. In this article, we'll dive into the purpose of this function, explore its mechanics, and run through a practical example.
go

Summary

Feature flags let you:

  • Deploy code without exposing it to users
  • Gradually roll out features to percentages of users
  • Target specific users or groups
  • Instantly disable broken features

Key practices:

  • Name flags clearly
  • Clean up old flags
  • Use safe defaults
  • Log flag evaluations

Exercises

  1. Implement a flag system: Build a simple feature flag class that supports boolean flags and percentage rollouts.

  2. Add targeting rules: Extend your flag system to support rules like “enable for premium users” or “enable for users in Europe”.

  3. Build a flag dashboard: Create a simple web interface where you can view and toggle feature flags.

Discussion

© 2026, codelessons.dev