Core Concepts

Understanding the fundamental concepts behind feature flags will help you use Rollgate effectively.

Feature Flags

A feature flag (also called feature toggle) is a mechanism that allows you to enable or disable functionality in your application without deploying new code.

Each flag has a unique key (like new-checkout) and a value that can be boolean, string, number, or JSON.

Common use cases:

  • Gradual rollouts: Release to 10% of users, then 50%, then 100%
  • A/B testing: Show different variants to different users
  • Kill switches: Instantly disable problematic features
  • Beta access: Enable features only for beta testers

Flag Types

Rollgate supports four types of feature flags, allowing you to return different kinds of values:

TypeUse CaseExample Value
booleanSimple on/off togglestrue / false
stringText values, variant names, colors"variant-b"
numberNumeric settings, limits, thresholds42
jsonComplex configurations, objects{"theme": "dark"}

When to use each type

  • Boolean: Feature toggles, kill switches, simple A/B tests
  • String: Theme names, button text, variant identifiers for multivariate tests
  • Number: Rate limits, retry counts, timeout values, pagination sizes
  • JSON: Complex configurations, feature settings with multiple options

Data Model

Rollgate organizes resources in a clear hierarchy. Understanding this structure helps you set up your projects and environments correctly.

Organization (top-level tenant)
├── Team Members      — Users with roles (owner, admin, member)
├── Audit Log         — All actions across the org
├── Billing           — Subscription and plan limits
└── Project           — Groups related flags
    ├── Feature Flags — Defined at project level
    ├── Webhooks      — Notifications for flag changes
    └── Environment   — Isolated context (production, staging, dev)
        ├── API Keys  — Client/Server keys for this environment
        └── Flag States — Per-environment configuration

Key scoping rules

  • Flags are defined once per project but have separate states per environment
  • API Keys are scoped to a single environment — each SDK instance connects to one environment
  • Webhooks are scoped to a project and fire for all environments within it
  • Team Members are at the organization level — a member can access all projects
  • Audit Log records all actions at the organization level, with retention based on your plan

Projects

A projectgroups related flags together. Typically you have one project per application (e.g., "Web App", "Mobile App", "API").

Each project contains its own flags and webhooks. Flags are defined at the project level but their state (enabled/disabled, rollout percentage, rules) is configured independently per environment.

Environments

An environment represents an isolated context within a project — typically production, staging, and development.

Each environment has its own API keys and flag states. This means you can enable a flag in staging for testing while keeping it disabled in production.

Why environments matter

  • Test flag changes safely before going to production
  • Use different rollout percentages per environment
  • Separate API keys prevent accidental cross-environment access
  • Each environment can have its own targeting rules

API Keys

API keys are scoped to an environment, not to a project. Each environment has its own set of keys. Rollgate uses two types:

Client Keys

Prefix: rg_client_

  • ✓ Safe to expose in frontend code
  • ✓ Read-only access to evaluated flags
  • ✓ Use in React, Vue, mobile apps

Server Keys

Prefix: rg_server_

  • ✗ Never expose in client code
  • ✓ Read-only access to flags with full ruleset
  • ✓ Use in Node.js, Python, Go backends

Both key types provide read-only SDK access. Server keys receive the complete ruleset (rules, segments, targeting) for local evaluation, while client keys receive only pre-evaluated flag values. Management operations (creating flags, editing rules) require session authentication via the dashboard — not API keys. This is consistent with how LaunchDarkly, Flagsmith, and Unleash handle key separation: the server verifies the key type and returns different data accordingly.

Evaluation

Flag evaluation is the process of determining whether a flag is enabled for a specific user or context. Rollgate evaluates flags in strict priority order - the first matching condition wins.

Evaluation Priority Order

PriorityStrategyConditionResult
1Flag Disabledenabled = falseFALSE
2Target UsersUser ID in target listTRUE
3Targeting RulesUser matches a ruleRule's rollout %
4Global RolloutNo rule matches (fallback)Flag's rollout %

Evaluation Examples

#FlagRolloutTarget UsersRulesUserResultReason
1OFF100%--anyFALSEFlag disabled (P1)
2ON0%["user-1"]-user-1TRUETarget user (P2)
3ON0%["user-1"]-user-2FALSE0% rollout (P4)
4ON100%--anyTRUE100% rollout (P4)
5ON100%-Rule: 0%matchesFALSERule 0% (P3)
6ON100%-Rule: 100%matchesTRUERule 100% (P3)
7ON100%-Rule: 100%no matchTRUEGlobal 100% (P4)
7bON0%-Rule: 100%no matchFALSEGlobal 0% (P4)
8ON0%["user-1"]Rule: 0%user-1, matchesTRUETarget wins (P2)

Key Points

  • Target users always win - Explicit user targeting overrides all rules
  • First rule wins - Rules are evaluated top-to-bottom, first match decides
  • No match = Global rollout - When no rule matches, fall through to global rollout percentage
  • Use 0% global rollout for exclusion - Set global rollout to 0% if you want non-matching users to get FALSE
  • Consistent hashing - Same user always gets same result for percentage rollouts

Consistent Hashing

When using percentage rollout (either global or rule-specific), Rollgate uses a hash of the flag ID + user ID to determine if a user is in the rollout. This ensures the same user always gets the same result for a given flag, even across different sessions or devices.

Example:A rule with 50% rollout will consistently return true for the same user every time. If user "abc123" gets true on the first evaluation, they will always get true for that rule.

Targeting Rules

Targeting rules enable you to show features to specific user segments based on their attributes like country, plan, email domain, or any custom property.

How Rules Work

  • First-match-wins - Rules are evaluated top-to-bottom, the first matching rule decides the outcome
  • Conditions within a rule use AND logic - ALL conditions must match
  • Rule rollout - Each rule has its own rollout % (0-100%), applied when that rule matches
  • Fallback - If no rule matches, global rollout is used

Example: Italian Pro Users

Scenario: You want to enable a new feature only for users who are from Italy AND have a Pro or Growth plan.

// Rule: "Italian Pro Users"
// Conditions (AND):
//   - country equals "IT"
//   - plan in "pro,growth"

// User: { country: "IT", plan: "pro" }
// ✓ country = "IT"? YES
// ✓ plan in "pro,growth"? YES
// Result: flag = true

Available Operators

OperatorDescriptionExample
equalsExact match (case-insensitive)country = "IT"
containsString contains valueemail contains "@company"
inValue in comma-separated listplan in "pro,growth"
gt, gte, lt, lteNumeric comparisonsage gte 18
regexRegular expression matchemail regex ".*@company\\.com$"
semver_gtSemantic version comparisonapp_version semver_gt "2.0.0"

See GET /api/v1/operators for the full list with descriptions.

Common Use Cases

Here are practical examples showing how to configure flags for common scenarios.

Use Case 1: Premium Feature for Pro Users Only

Goal: Show a feature only to users with plan=pro or plan=enterprise. Everyone else should NOT see it.

Flag: premium-analytics
├── Rule 1: plan IN "pro,enterprise" → rollout 100%
└── Global Rollout: 0%  ← non-matching users get FALSE

| User              | Result  | Reason           |
|-------------------|---------|------------------|
| { plan: "pro" }   | TRUE    | Rule 1 matches   |
| { plan: "free" }  | FALSE   | Global 0%        |
| { }               | FALSE   | Global 0%        |

Use Case 2: Gradual Rollout with Beta Testers First

Goal: Roll out a new feature starting with beta testers (100%), then internal team (100%), then gradually to everyone else (start at 10%).

Flag: new-checkout
├── Rule 1: beta_tester EQUALS true → rollout 100%
├── Rule 2: email ENDS_WITH "@company.com" → rollout 100%
└── Global Rollout: 10%  ← everyone else gets 10% chance

| User                          | Result  | Reason              |
|-------------------------------|---------|---------------------|
| { beta_tester: true }         | TRUE    | Rule 1 matches      |
| { email: "[email protected]" }  | TRUE    | Rule 2 matches      |
| { email: "[email protected]" }   | ~10%    | Global 10% rollout  |

Increase global rollout over time: 10% → 25% → 50% → 100%

Use Case 3: Block Feature for Specific Country

Goal: Enable a feature for everyone EXCEPT users in Germany (due to legal restrictions).

Flag: gdpr-analytics
├── Rule 1: country EQUALS "DE" → rollout 0%  ← block Germany
└── Global Rollout: 100%  ← everyone else gets it

| User               | Result  | Reason              |
|--------------------|---------|---------------------|
| { country: "DE" }  | FALSE   | Rule 1 blocks       |
| { country: "IT" }  | TRUE    | Global 100%         |
| { country: "US" }  | TRUE    | Global 100%         |

Use Case 4: A/B Test with 50/50 Split

Goal: Show new pricing page to 50% of users for A/B testing. Same user always sees the same variant (consistent).

Flag: new-pricing-page
├── (no rules)
└── Global Rollout: 50%

| User                | Result    | Reason                    |
|---------------------|-----------|---------------------------|
| { id: "user-123" }  | TRUE      | Hash puts them in 50%     |
| { id: "user-456" }  | FALSE     | Hash puts them out        |
| { id: "user-123" }  | TRUE      | Same user = same result   |

Note: Consistent hashing ensures user-123 always gets TRUE.

Use Case 5: VIP Access Override

Goal: Feature is disabled globally, but specific VIP users should always have access.

Flag: early-access-feature
├── Target Users: ["vip-user-1", "vip-user-2"]  ← always TRUE
├── (no rules)
└── Global Rollout: 0%

| User                  | Result  | Reason                |
|-----------------------|---------|----------------------|
| { id: "vip-user-1" }  | TRUE    | In target users list |
| { id: "vip-user-2" }  | TRUE    | In target users list |
| { id: "normal-user" } | FALSE   | Global 0%            |

Note: Target users override everything, even 0% rollout.

Pro tip:When designing flag rules, start by asking: "What should happen to users who don't match any rule?" Set your global rollout accordingly (0% for exclusive features, 100% for opt-out patterns).

Real-time Updates

Rollgate supports two methods for keeping flags in sync:

SSE (Server-Sent Events)

  • ✓ Instant updates (milliseconds)
  • ✓ Single persistent connection
  • ✓ Lower server load
  • ✗ May be blocked by some proxies

Polling

  • ✓ Works everywhere
  • ✓ Simpler to debug
  • ✓ Default mode (recommended)
  • ✗ Slight delay (configurable interval)

Scheduled Changes

Scheduled changes let you plan flag updates in advance. Set a specific date and time for a flag to be enabled or disabled automatically.

How it works:

A background scheduler checks every minute for flags that need to be toggled. When the scheduled time arrives, the flag state changes automatically - no manual intervention required.

Example: E-commerce Black Friday Campaign

Scenario: You run an e-commerce store and want to show a special Black Friday banner with 40% off. The sale runs from November 29th at midnight to December 2nd at 11:59 PM.

Problem: Without scheduled changes, someone needs to be awake at midnight to enable the flag, and remember to disable it after the sale ends. Human error, timezone confusion, and forgotten tasks can cause the banner to appear too early, too late, or never disappear.

Solution: Schedule the flag in advance:

POST /api/v1/flags/:flagId/environments/:envId/schedule
{
  "enable_at": "2026-11-29T00:00:00Z",
  "disable_at": "2026-12-02T23:59:00Z"
}

Result: The banner appears automatically at midnight on Black Friday and disappears when the sale ends. Your team can enjoy the holiday without worrying about manual toggles.

Example: Planned Maintenance Window

Scenario:Your payment provider is performing maintenance on Saturday from 2 AM to 6 AM. You want to show a "Payments temporarily unavailable" message and disable the checkout button during this window.

Solution: Create a payment-maintenance flag and schedule it to be enabled only during the maintenance window:

// Schedule maintenance mode
POST /api/v1/flags/:flagId/environments/:envId/schedule
{
  "enable_at": "2026-01-18T02:00:00Z",
  "disable_at": "2026-01-18T06:00:00Z"
}

In your code:

const isMaintenanceMode = useFlag('payment-maintenance', false);

if (isMaintenanceMode) {
  return <MaintenanceBanner message="Payments back at 6 AM" />;
}
return <CheckoutButton />;

1-Click Rollback

Every flag change is recorded in the change history. If something goes wrong, you can instantly revert to the previous state with one click.

How it works:

Every time a flag is toggled, the previous state (enabled, rollout percentage, target users) is saved to the history. The rollback restores the exact previous configuration in milliseconds.

Example: Broken Feature in Production

Scenario: You enabled the new-checkoutflag for 100% of users. Within 5 minutes, your error monitoring (Sentry, Datadog) shows a spike in checkout failures. Users are complaining they can't complete purchases.

Problem: Finding and fixing the bug could take hours. Every minute costs you sales and customer trust.

Solution: One API call to rollback:

POST /api/v1/flags/:flagId/environments/:envId/rollback

// Response:
{
  "message": "Rollback successful",
  "enabled": false,
  "rollout_percentage": 50,
  "rolled_back_to": "2026-01-07T14:00:00Z"
}

Result: The flag instantly reverts to the previous state (disabled, or 50% rollout). Users see the old checkout, errors stop, and your team can debug without pressure.

Example: Accidental Change by Team Member

Scenario: A new team member accidentally disabled thepayment-gateway flag in production while exploring the dashboard. The flag was at 100% rollout with a specific list of beta tester user IDs.

Problem:Re-enabling the flag is easy, but what was the rollout percentage? What were the target user IDs? Without history, you'd need to guess or check the code.

Solution: Check history and rollback:

// View what changed
GET /api/v1/flags/:flagId/environments/:envId/history

// Rollback restores EVERYTHING: enabled, percentage, target users
POST /api/v1/flags/:flagId/environments/:envId/rollback

Result: The exact previous configuration is restored, including the 100% rollout and beta tester user IDs. No guessing, no mistakes.

Safety net: Even accidental changes can be undone immediately, minimizing the impact of human errors. History is kept for the last 20 changes per flag.

Circuit Breaker

The SDK includes a built-in circuit breaker that protects your application if Rollgate becomes unavailable.

How it works:

  • Closed: Normal operation, all requests go to Rollgate API
  • Open: After 5 consecutive failures, the circuit opens and all requests use cached flag values
  • Half-open: After 30 seconds, one test request is sent. If successful, circuit closes; if not, it stays open

Example: API Outage During Peak Traffic

Scenario:It's Black Friday and your site has 50,000 concurrent users. Rollgate API becomes temporarily unreachable due to a network issue.

Without circuit breaker: Every page load waits for API timeout (5 seconds), your site becomes unusable, users leave, you lose sales.

With circuit breaker:After 5 failed requests, the circuit opens. All subsequent flag checks return instantly from cache. Your site stays fast, users don't notice anything.

// Monitor circuit state in your health endpoint
app.get('/health', (req, res) => {
  const circuit = rollgate.getCircuitState();
  res.json({
    status: circuit === 'open' ? 'degraded' : 'healthy',
    circuit: circuit,
    flagsCached: true
  });
});

// Alert when circuit opens
rollgate.on('circuit-open', () => {
  alertOps('Rollgate circuit breaker opened - using cached flags');
});

Result: Your app gracefully degrades. Features controlled by flags continue working with the last known values. When Rollgate recovers, the circuit closes and fresh data flows again.