Experimentation functions

FeatureQL provides deterministic functions for A/B testing, feature flags, and gradual rollouts that ensure consistent assignment across distributed systems.

HASH01()

HASH01 provides consistent entity assignment to experiment variants using stable hashing. It returns a deterministic value between 0 and 1 based on the input string.

Syntax

HASH01(key: VARCHAR) -> DOUBLE
sql

Parameters

  • key: String to hash, typically entity ID concatenated with a salt

Returns

Double precision value between 0.0 and 1.0

Usage patterns

The function enables multiple experimentation patterns:

  • Holdout groups: Reserve a percentage of users for control
  • Feature toggles: Combine with business logic for targeted rollouts
  • Statistical exposure: Assign users to experiment variants
  • Multi variant tests: Chain conditions for A/B/C/n tests

Key principles:

  • Always use a salt to ensure independent assignment across experiments
  • The same input always produces the same output (deterministic)
  • Distribution is uniform across the 0 to 1 range

GRADUAL_ROLLOUT()

GRADUAL_ROLLOUT enables controlled, time based feature deployment with deterministic assignment. Unlike simple percentage rollouts, it smoothly transitions from 0% to 100% coverage over a defined time window.

Syntax

GRADUAL_ROLLOUT(
    key: VARCHAR,
    time_ref: TIMESTAMP,
    time_0pc: TIMESTAMP,
    time_100pc: TIMESTAMP,
    power: DOUBLE = 1.0,
    chunks: BIGINT = 10
) -> BOOLEAN
sql

Parameters

  • key: Stable identifier (entity ID + salt for independence)
  • time_ref: Current time to evaluate (typically NOW() or event timestamp)
  • time_0pc: Start time when 0% are enabled
  • time_100pc: End time when 100% are enabled
  • power: Shape of rollout curve (1.0 = linear, 2.0 = quadratic, etc.)
  • chunks: Number of discrete steps in the rollout

Returns

Boolean indicating whether the entity is enabled at the given time

Linear rollout example

Quadratic rollout example

Using power = 2.0 creates a slower initial rollout that accelerates over time:

Rollout characteristics

The function guarantees:

  • Monotonic progression: Once an entity is enabled, it stays enabled
  • Deterministic assignment: Same inputs always yield same results
  • Chunk based transitions: Entities move in groups for smoother metrics
  • Customizable velocity: Power parameter controls rollout acceleration

Best practices

Salt management

-- Use unique salts per experiment/rollout
CONST EXPERIMENT_A_SALT := 'exp_a_2024_q1'
CONST ROLLOUT_FEATURE_X_SALT := 'feature_x_rollout_v2'

-- Combine entity ID with salt
HASH01(USER_ID::VARCHAR || EXPERIMENT_A_SALT)
sql

Combining conditions

-- Layer multiple criteria for precise targeting
EXPOSED :=
    NOT IN_HOLDOUT                  -- Not in global holdout
    AND IS_ELIGIBLE                 -- Meets business criteria
    AND IN_EXPERIMENT_POPULATION    -- Statistically assigned (for experiments)
    AND IN_ROLLED_OUT_POPULATION    -- Time based rollout active (for minimizing risk of bugs at start)
sql

Rollout strategies

Conservative rollout (power > 1):

  • Start slow, accelerate later
  • Good for high risk features
  • More time to detect issues early

Aggressive rollout (power < 1):

  • Start fast, decelerate later
  • Good for low risk features
  • Quickly reaches significant coverage

Stepped rollout (small number of chunks):

  • Same % exposure for long periods of time
  • Peak of new bugs at discrete points in time
  • Better for continuous monitoring

Common patterns

Multi variant experiment

WITH
    HASH_VALUE := HASH01(USER_ID::VARCHAR || 'experiment_v3'),
SELECT
    VARIANT := CASE
        WHEN HASH_VALUE < 0.22 THEN 'variant_a'
        WHEN HASH_VALUE < 0.44 THEN 'variant_b'
        WHEN HASH_VALUE < 0.66 THEN 'variant_c'
        ELSE 'control'
    END
sql

Nested experiments

-- Run sub experiment within main experiment population
IN_MAIN_EXPERIMENT := HASH01(USER_ID::VARCHAR || 'main') < 0.5
IN_SUB_VARIANT :=
    IN_MAIN_EXPERIMENT
    AND HASH01(USER_ID::VARCHAR || 'sub') < 0.2
sql

Sequenced experiments

-- Run experiment on the same population as phase 1 + New users (keep the same hashing key)
IN_EXPERIMENT_PHASE1 := HASH01(USER_ID::VARCHAR || 'experiment_v3') < 0.2
IN_EXPERIMENT_PHASE2 := HASH01(USER_ID::VARCHAR || 'experiment_v3') < 0.4
sql
Last update at: 2025/10/13 10:23:46