# How Experiment Flags Control Feature Rollouts in the Roo Code Extension

> Learn how experiment flags control feature rollouts in Roo Code. Discover the three-layer architecture: flag definitions, runtime checks, and VS Code state persistence for seamless feature management.

- Repository: [Roo Code/Roo-Code](https://github.com/RooCodeInc/Roo-Code)
- Tags: how-to-guide
- Published: 2026-04-26

---

**Roo Code utilizes a centralized experiment flag system in [`src/shared/experiments.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/experiments.ts) that gates features through a three-layer architecture: flag definitions, runtime checks via `experiments.isEnabled()`, and VS Code state persistence.**

The Roo Code VS Code extension implements a lightweight yet robust mechanism for rolling out new capabilities gradually. By leveraging experiment flags defined in the shared configuration, developers can safely deploy optional functionality without risking stability for all users. This system allows specific tools and UI components to check whether a feature should be active based on merged user preferences and default settings.

## The Three-Piece Architecture of Roo Code's Experiment System

The experiment flag implementation consists of three interconnected components that manage the complete lifecycle from definition to runtime evaluation.

First, **`EXPERIMENT_IDS`** and **`experimentConfigsMap`** in [`src/shared/experiments.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/experiments.ts) enumerate available flags and establish default disabled states. Second, the **`experiments.isEnabled()`** helper function provides the runtime logic for checking flag status. Third, individual tools and components perform **feature checks** by calling this helper with the current state to determine execution paths.

## Defining Experiment Flags in src/shared/experiments.ts

All experiment flag definitions reside in [`src/shared/experiments.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/experiments.ts), which exports both the identifier constants and their default configurations.

### The EXPERIMENT_IDS Enum

The system uses a const assertion to define unique string identifiers for each experiment:

```typescript
export const EXPERIMENT_IDS = {
  PREVENT_FOCUS_DISRUPTION: "preventFocusDisruption",
  IMAGE_GENERATION:        "imageGeneration",
  RUN_SLASH_COMMAND:      "runSlashCommand",
  CUSTOM_TOOLS:           "customTools",
} as const satisfies Record<string, ExperimentId>;

```

This structure ensures type safety while providing human-readable keys for developers.

### Default Configuration with experimentConfigsMap

Each flag maps to a configuration object specifying its default enabled state. As implemented in the Roo Code source, all flags default to `false`:

```typescript
export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
  PREVENT_FOCUS_DISRUPTION: { enabled: false },
  IMAGE_GENERATION:        { enabled: false },
  RUN_SLASH_COMMAND:      { enabled: false },
  CUSTOM_TOOLS:           { enabled: false },
};

```

### Generating Fallback Values

The system converts the configuration map into a flat record for runtime lookups using `experimentDefault`:

```typescript
export const experimentDefault = Object.fromEntries(
  Object.entries(experimentConfigsMap).map(([_, config]) => [
    EXPERIMENT_IDS[_ as keyof typeof EXPERIMENT_IDS] as ExperimentId,
    config.enabled,
  ]),
) as Record<ExperimentId, boolean>;

```

This creates a fallback object where missing user preferences default to the safe, disabled state.

## Checking Feature Flags at Runtime

The `experiments` namespace exposes two utilities for interacting with flags. The `isEnabled` method handles the critical logic of merging user state with defaults:

```typescript
export const experiments = {
  get: (id: ExperimentKey) => experimentConfigsMap[id],
  isEnabled: (experimentsConfig: Experiments, id: ExperimentId) =>
    experimentsConfig[id] ?? experimentDefault[id],
} as const;

```

When `experiments.isEnabled()` is called, it returns the user's stored preference if available, or falls back to the default configuration. This boolean determines whether gated features execute or return early with error messages.

## Real-World Implementation Examples

The Roo Code extension demonstrates consistent patterns for gating functionality across different tool types.

### Gating UI Behavior: The Prevent Focus Disruption Flag

In [`src/core/tools/WriteToFileTool.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/tools/WriteToFileTool.ts) (lines 1006-1010), the `PREVENT_FOCUS_DISRUPTION` flag controls how diff views are presented:

```typescript
const isPreventFocusDisruptionEnabled = experiments.isEnabled(
  state?.experiments ?? {},
  EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION,
);

```

When this flag returns `true`, the tool gathers a unified diff and waits for explicit user approval before writing files, preventing accidental focus switches. When `false`, it uses the original live-edit workflow that opens diff views immediately.

### Blocking Entire Tools: Image Generation and Slash Commands

Some features require complete tool suppression when disabled. In [`src/core/tools/GenerateImageTool.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/tools/GenerateImageTool.ts) (lines 15-30), the `IMAGE_GENERATION` flag blocks execution and surfaces an error message when disabled. Similarly, [`src/core/tools/RunSlashCommandTool.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/tools/RunSlashCommandTool.ts) (lines 4-30) validates the `RUN_SLASH_COMMAND` flag before executing commands, returning errors when the feature is disabled.

This pattern follows the same check structure demonstrated in [`WriteToFileTool.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/WriteToFileTool.ts), where `experiments.isEnabled()` determines whether the tool proceeds or returns early.

### Validating Custom Tool Registration

The `CUSTOM_TOOLS` flag in [`src/core/tools/validateToolUse.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/tools/validateToolUse.ts) (lines 6-20) prevents registration of user-defined tools unless explicitly enabled. Tools call `experiments.isEnabled(state?.experiments ?? {}, EXPERIMENT_IDS.CUSTOM_TOOLS)` and throw descriptive errors when the flag is `false`, ensuring experimental capabilities remain inaccessible until users opt in.

## Persisting User Preferences in VS Code State

User overrides for experiment flags are stored in VS Code's global state under the key `"experiments"`. During initialization in [`src/core/webview/webviewMessageHandler.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/core/webview/webviewMessageHandler.ts) (line 736), the extension merges persisted settings with defaults:

```typescript
...(getGlobalState("experiments") ?? experimentDefault),

```

Users toggle flags through the Experimental Settings UI, which writes updates back to global state. Subsequent tool invocations read these values via the `state?.experiments` object passed to `isEnabled()` checks.

## Summary

- **Roo Code defines experiment flags centrally** in [`src/shared/experiments.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/experiments.ts) using `EXPERIMENT_IDS` and `experimentConfigsMap`, with all features disabled by default for safety.
- **Runtime checks use `experiments.isEnabled()`**, which merges user state from VS Code's global storage with fallback defaults to return a boolean.
- **Tools gate behavior conditionally** by checking flags at execution time, either modifying UI workflows in [`WriteToFileTool.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/WriteToFileTool.ts) or blocking entire features in [`GenerateImageTool.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/GenerateImageTool.ts) and [`validateToolUse.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/validateToolUse.ts).
- **User preferences persist across sessions** via the global state management in [`webviewMessageHandler.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/webviewMessageHandler.ts), allowing gradual feature rollouts without code changes.

## Frequently Asked Questions

### How do I enable an experiment flag in Roo Code?

Navigate to the Experimental Settings section in the Roo Code extension UI and toggle the desired flag. The extension writes this configuration to VS Code's global state under the `"experiments"` key, which is then merged with `experimentDefault` values on the next execution.

### What happens if an experiment flag is not defined in user settings?

When a flag is missing from user state, `experiments.isEnabled()` automatically falls back to the value in `experimentDefault` (defined in [`src/shared/experiments.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/experiments.ts)). Since Roo Code initializes all flags to `false` by default, undefined features remain safely disabled until explicitly activated.

### Where are experiment flags defined in the codebase?

All flag definitions, default configurations, and the `isEnabled` helper function are located in [`src/shared/experiments.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/src/shared/experiments.ts). Individual tool implementations (such as [`WriteToFileTool.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/WriteToFileTool.ts), [`GenerateImageTool.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/GenerateImageTool.ts), and [`validateToolUse.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/validateToolUse.ts)) import these definitions to perform runtime checks.

### Can experiment flags be used for A/B testing in Roo Code?

Yes, the architecture supports A/B testing scenarios. Developers can modify the default values in `experimentConfigsMap` for different user segments, or implement custom logic in [`webviewMessageHandler.ts`](https://github.com/RooCodeInc/Roo-Code/blob/main/webviewMessageHandler.ts) to assign flag values based on user IDs or random distribution, then analyze tool usage patterns through standard telemetry.