Testland
Browse all skills & agents

feature-flag-test-matrix-reference

Pure-reference catalog of feature-flag test matrix design. Defines the flag-state combinatorics problem (N flags × M variants × K user-segments = N×M×K test cases), the canonical coverage strategies (pairwise interaction coverage; default-only smoke; full matrix; risk-driven matrix), the kill-switch + percentage-rollout test patterns, and the relationship between flags + experiments (flags toggle behaviour; experiments measure outcome). Use when designing the flag-test surface for a new project or auditing existing flag-test coverage.

Install with skills.sh (any agent)

npx skills add testland/qa --skill feature-flag-test-matrix-reference
View source

feature-flag-test-matrix-reference

Overview

A codebase with N feature flags, each having M variants, and users in K segments, has N × M × K possible flag-state-segment combinations. At realistic numbers (50 flags, 2 variants each, 5 segments) that's 500 - and at 50 flags with 3 variants and 10 segments, it's 1500. Testing every combination is infeasible, so the matrix has to be sampled deliberately rather than enumerated.

This skill is a pure reference consumed by the SDK-test + coverage-builder skills.

When to use

  • Designing the test surface for a new flag-heavy product.
  • Auditing existing flag-test coverage - are critical combinations covered?
  • PR review of a new flag - does it create a coverage gap?
  • Investigating an "only happens with flag X + flag Y on" incident.

How to use

  1. Inventory the flags: list every flag, its variant count (M), and the user segments (K) it targets.
  2. Size the problem: compute N × M × K to confirm full enumeration is infeasible and sampling is required.
  3. Discover interactions: mark flags as inert (independent) or known-interacting (auth + permissions, billing + plan-tier), pulling from a risk register where one exists.
  4. Pick a coverage strategy from the five below - default-only smoke, per-flag isolation, pairwise, full matrix, or risk-driven - matching the flag set's risk.
  5. Add the special flag-state categories (kill-switch, percentage-rollout, sticky-assignment, default-on-error) as their own tests.
  6. Layer the resulting cases across unit, integration, E2E, and production-smoke.
  7. Hand the matrix to the platform SDK skill (launchdarkly-testing, unleash-testing, flagsmith-testing, growthbook-testing) to implement each case.

The combinatorics

VariableTypical scale
Total flags in codebase20-500
Variants per flag2 (most), 3-5 (experiments), 10+ (multivariate)
User segments5-20 (free, paid, enterprise, internal, beta, etc.)
Combinatorial totalQuickly enters thousands

Insight: most flag combinations are inert (independent). Only a small subset interact - the test matrix should target interactions.

Five coverage strategies

1. Default-only smoke

Test only the default-value combination ("all flags off" or "all flags at default"). Fast but misses everything.

Use when: flag-heavy codebase where defaults change rarely.

2. Per-flag isolation

For each flag, test default + each variant in isolation. N × M tests; ignores interactions.

Use when: flags are mostly independent (UI tweaks, language strings, low-risk).

3. Pairwise interaction

Test every pair of flags (combinatorial 2-way coverage). Per NIST SP 800-142 on combinatorial testing, pairwise catches ~67% of real defects with O(N²) combinations.

Use when: flags are known-interacting (auth + permissions, billing + plan-tier).

Implementation: tools like pict (Microsoft) generate the pairwise matrix from a flag inventory.

4. Full matrix

Every combination. N^M tests for boolean flags.

Use when: small (≤10) flag count with strong interaction; financial / regulatory paths.

5. Risk-driven

Custom matrix targeting (flag, segment) cells with known risk (per a risk register per risk-matrix in the qa-process plugin).

Use when: any non-trivial codebase. Best in practice.

Special flag-state test categories

CategoryTest
Kill-switchSetting flag → off must halt the feature within N seconds (cache TTL)
Percentage rolloutFlag at 10% → ~10% of users in 'on' bucket; SDK assignment stable per user
Targeted rolloutTargeting region=EU → only EU users get treatment
Sticky assignmentSame user → same variant across sessions and re-launches
Override hierarchyUser-specific override > segment override > default
Default-on-errorSDK fails / network down → default value returned
Fast-deactivateToggle flag off → live users see new state on next evaluation

These are per-platform behaviours (LaunchDarkly, Unleash, Flagsmith, GrowthBook implement them differently); test per platform per the SDK skills.

Flag-test layering

Tests should run at multiple layers:

LayerCoverage
UnitResolver / handler logic gated on flag value (mock SDK)
IntegrationSDK + handler together (test SDK against local-eval / fixture)
E2EReal flag toggle → real user sees the change
Production smokeAfter flag change → assert expected behaviour live

Flag-experiment distinction

Per ab-test-validity-checklist (in the qa-experimentation plugin):

FlagExperiment
Toggles behaviourMeasures outcome
Boolean / multi-variantMulti-arm with metrics
Test the behaviour changeTest the assignment + outcome correlation
Ship decision on engineering judgmentShip decision on statistical result

A feature flag can power an experiment (the flag is the allocation mechanism), but tests are layered: flag tests verify correct behaviour per variant; experiment tests verify assignment + analytics.

Worked example

A billing service has 3 boolean flags - new_checkout, annual_discount, tax_engine_v2 - across 3 segments (free, paid, enterprise). The team knows annual_discount and tax_engine_v2 interact: a discount changes the taxable amount.

  1. Inventory: 3 flags, 2 variants each, 3 segments; the full flag-state-by-segment cross-product is too large to enumerate.
  2. Interaction discovery marks the annual_discount × tax_engine_v2 pair as known-interacting; new_checkout is a UI change treated as independent.
  3. Strategy: risk-driven. Per-flag isolation covers new_checkout; a pairwise cell covers (annual_discount on, tax_engine_v2 on) on the paid and enterprise segments where money moves.
  4. Special categories: a kill-switch test asserts toggling tax_engine_v2 off reverts to v1 within the cache TTL; a sticky-assignment test asserts the annual_discount percentage rollout keeps each user in the same bucket across sessions.
  5. Layering: unit tests exercise the tax resolver per flag value; an E2E test toggles tax_engine_v2 and asserts an enterprise invoice total.

Result: a handful of targeted cases instead of the full cross-product, with the discount × tax interaction covered explicitly.

Anti-patterns

Anti-patternWhy it failsFix
Test only default-value pathMisses every flag-on casePer-flag isolation minimum
Mock the SDK to return constantMisses targeting / rollout logicLocal-eval mode or fixture-based SDK
Same test for every flag combinationSlow; flaky; opaque failuresPer-combination assertion logs
No kill-switch testProduction incident has no rehearsed responseTest deactivation latency
Don't test percentage-rollout sticky-assignmentRollout produces non-deterministic UXPer ab-test-validity-checklist
Tests assume flag-on defaultReal default-off behaviour untested in CITest both paths
No cleanup test for removed flagsStale-flag accumulates per flag-removal-runbook-authorPeriodic stale-flag audit
Pairwise without flag-interaction discoverySome pairs spuriously interactCouple with risk-register input

Limitations

  • Pairwise misses 3-way+ interactions. Some real-world bugs need 3-way coverage.
  • Real-world matrices have ordering effects. Flag A enabled THEN flag B may differ from B then A; test ordering needs separate coverage.
  • Coverage tooling lags. PICT / ACTS exist but integration with flag platforms is bespoke.
  • Stale flags pollute the matrix. Cleanup pairs with flag-removal-runbook-author.

References

Related skills

flag-removal-runbook-author

Workflow-driven skill that builds the runbook for safely removing a feature flag from the codebase + the flag platform. Walks through: pre-removal verification (flag fully rolled out, no usage variance in evaluations, dependent code paths identified), the code-removal steps (delete the if-branches, simplify, restore types), the platform-side removal (archive in LaunchDarkly / Unleash / Flagsmith / GrowthBook), the verification post-removal, and the rollback plan. Use when removing a flag that has finished its mission (rollout-complete, experiment-shipped, kill-switch retired).

flag-state-coverage-builder

Workflow-driven skill that builds a flag-state coverage matrix from the project's flag inventory and risk register. Walks through: inventorying flags (grep for flag-evaluation calls), classifying each (boolean / multi-variant / kill-switch / experiment), choosing the coverage strategy (per-flag-isolation / pairwise / full / risk-driven per feature-flag-test-matrix-reference), generating the test matrix (PICT for pairwise; manual for risk-driven), and emitting test skeletons. Use when introducing flag-test coverage to a new codebase or when a flag-related incident exposes a coverage gap.

flagsmith-testing

Wraps Flagsmith server-side SDK testing patterns (feature flags / feature toggles) so tests run without calling the Flagsmith API: offline mode with LocalFileHandler + a downloaded environment.json snapshot, local-evaluation mode (no per-request network), and default_flag_handler for per-feature mocked fallbacks, via the get_environment_flags / get_identity_flags evaluation paths. Use when writing feature-flag tests for code that uses Flagsmith, mocking flag values, or testing feature toggles offline in CI.

growthbook-testing

Wraps GrowthBook Node SDK testing patterns: GrowthBookClient initialization with direct payload (initSync; no network), isOn / getFeatureValue / evalFeature, scoped instances (createScopedInstance) for per-request user context, inline experiment (runInlineExperiment) tests, and tracking-callback assertion patterns. Use when writing tests for code using GrowthBook for flags + experiments.

killswitch-test-author

Workflow-driven skill that authors the four test categories specific to kill-switch (ops-toggle) flags: switch-OFF graceful degradation, fail-static default when the flag service is unreachable, latency budget for the kill decision, and no-data-corruption mid-flight. Distinct from flag-state-coverage-builder (which builds a full coverage matrix across all flag types) and feature-flag-test-matrix-reference (which catalogs patterns without producing tests). Use when a kill-switch flag exists in the codebase and needs dedicated, production-incident-rehearsing tests authored for it.

launchdarkly-testing

Wraps LaunchDarkly server-side SDK testing patterns: TestData data source for hermetic tests (no network), file-based data source for fixture-driven tests, flag override patterns (TestData.update for per-test flag values), and assignment-integrity tests. Use when writing tests for code that uses LaunchDarkly flags.

openfeature-sdk-testing

Wraps OpenFeature (CNCF vendor-neutral SDK abstraction) testing patterns: the InMemoryProvider for hermetic tests without network calls, provider registration via OpenFeature.setProvider, the getBooleanValue/getBooleanDetails evaluation API with EvaluationDetails (value, variant, reason, errorCode), hooks for evaluation side-effects, and evaluation context for targeting-rule tests. Covers TypeScript, Java, and Python SDKs. Use when writing tests for code that resolves feature flags through the OpenFeature SDK regardless of the underlying flag management platform.

unleash-testing

Wraps Unleash (Open Source / SaaS) SDK testing patterns: bootstrap with a static toggles array (no network), the test mode (disableMetrics + disablePolling), the custom strategy testing pattern (implement a Strategy class + assert isEnabled), and assignment-integrity tests. Use when writing tests for code that uses Unleash for feature flags.