Back to Posthog

Super groups

docs/internal/feature-flags/super-groups.md

1.43.16.7 KB
Original Source

Super groups

What they are

Super groups are a feature flag evaluation mechanism that controls enrollment in early access features. Despite the name suggesting "groups of users," they're actually a single person-level property check that determines whether someone has opted into an early access feature.

When a super group condition matches, it takes precedence over all regular feature flag conditions—hence "super." The evaluation returns immediately without checking other conditions.

What they're used for

Super groups power the early access feature (EAF) system. When users opt into an early access feature through the UI, PostHog sets a person property like $feature_enrollment/my-feature with the value "true" (stored as a string, not a boolean). The super group condition checks for this property.

Super groups are only added to feature flags for early access features in active stages: Alpha, Beta, and General Availability. The Concept stage allows users to register interest, but does not add super groups – the feature flag remains disabled for opted-in users until the feature is promoted to an active stage.

Example flow:

  1. Product team creates an early access feature "New Dashboard" in Alpha stage linked to feature flag new-dashboard
  2. PostHog automatically adds a super group to the flag checking $feature_enrollment/new-dashboard (only because it's in an active stage)
  3. User opts in via the early access features UI
  4. PostHog sets $feature_enrollment/new-dashboard to "true" on their person
  5. On next flag evaluation, super group matches → user sees the feature
  6. Regular rollout conditions are never checked for this user

If the feature were in Concept stage instead, steps 3-4 would still occur (user can register interest), but step 2 would not – no super group would be added, so the feature flag would remain disabled.

Note: The condition uses ["true"] (an array) because property filters in PostHog always expect array values for the exact operator, even for single values.

This allows opted-in users to see features regardless of percentage rollouts, geography targeting, or other conditions on the flag.

How they are stored

Super groups are stored in the feature flag's filters JSON field alongside regular conditions:

json
{
  "groups": [
    {
      "properties": [...],
      "rollout_percentage": 50
    }
  ],
  "super_groups": [
    {
      "properties": [
        {
          "key": "$feature_enrollment/new-dashboard",
          "type": "person",
          "operator": "exact",
          "value": ["true"]
        }
      ],
      "rollout_percentage": 100
    }
  ]
}

Data type used

Super groups use the same FeatureFlagGroupType as regular conditions:

typescript
interface FeatureFlagGroupType {
  properties?: AnyPropertyFilter[]
  rollout_percentage?: number | null
  variant?: string | null
  users_affected?: number
  sort_key?: string | null
  description?: string | null
}

Why this causes confusion

The name doesn't match the purpose

"Super groups" suggests:

  • A group-based targeting concept (like "premium users" or "beta testers")
  • Something related to PostHog's group analytics feature
  • A collection of users

What it actually is:

  • A single person property check
  • Specifically for $feature_enrollment/{flag_key} properties
  • An enrollment gate, not a targeting mechanism

A clearer name would be enrollment_condition or early_access_gate.

The type allows more than is supported

Using FeatureFlagGroupType implies you can:

  • Add multiple properties (in practice, there's never more than one)
  • Use any property key (only $feature_enrollment/* makes sense)
  • Use different operators (only exact with ["true"] is meaningful)
  • Set different rollout percentages (it's always 100% for enrolled users)

The Django code that creates super groups always produces exactly one property, and only for features in ActiveStage (Alpha, Beta, GA):

python
# From products/early_access_features/backend/api.py
super_conditions = lambda feature_flag_key: [
    {
        "properties": [
            {
                "key": f"$feature_enrollment/{feature_flag_key}",
                "type": "person",
                "operator": "exact",
                "value": ["true"],
            },
        ],
        "rollout_percentage": 100,
    },
]

While the type system allows an array of properties, this is the only code path that creates super groups, so there's always exactly one property in practice.

It's not group-based at all

This is the most confusing aspect. PostHog has a separate "groups" feature for company-level / account-level analytics. Super groups have nothing to do with that:

ConceptWhat it means
PostHog groupsCompany/account-level entities for B2B analytics
Feature flag groupsRelease condition sets (targeting rules)
Super groupsEarly access enrollment checks

Three unrelated uses of "group" in feature flags.

Constraints not enforced by the type

The serializer and API enforce constraints that the type doesn't express:

  • Early access features can't attach to group-based flags
  • Early access features can't have multivariate variants
  • Super groups only support person properties

These constraints live in validation code, not the type system, making the valid configurations unclear from the schema alone.

A clearer alternative would be

A dedicated type that reflects what super groups actually do:

typescript
interface EarlyAccessEnrollment {
  featureFlagKey: string // The key checked in $feature_enrollment/{key}
  enabled: boolean // Always true when present
}

Or even simpler, a boolean flag on the feature flag model:

python
class FeatureFlag(models.Model):
    has_early_access_enrollment = models.BooleanField(default=False)

The enrollment property key can be derived from the flag key, eliminating the need for a separate structure.

Key files

ComponentPath
Python evaluationposthog/models/feature_flag/flag_matching.py
Rust evaluationrust/feature-flags/src/flags/flag_matching.rs
Early access APIproducts/early_access_features/backend/api.py
Frontend typesfrontend/src/types.ts
Frontend logicfrontend/src/scenes/feature-flags/featureFlagReleaseConditionsLogic.ts