docs/internal/feature-flags/super-groups.md
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.
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:
new-dashboard$feature_enrollment/new-dashboard (only because it's in an active stage)$feature_enrollment/new-dashboard to "true" on their personIf 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.
Super groups are stored in the feature flag's filters JSON field alongside regular conditions:
{
"groups": [
{
"properties": [...],
"rollout_percentage": 50
}
],
"super_groups": [
{
"properties": [
{
"key": "$feature_enrollment/new-dashboard",
"type": "person",
"operator": "exact",
"value": ["true"]
}
],
"rollout_percentage": 100
}
]
}
Super groups use the same FeatureFlagGroupType as regular conditions:
interface FeatureFlagGroupType {
properties?: AnyPropertyFilter[]
rollout_percentage?: number | null
variant?: string | null
users_affected?: number
sort_key?: string | null
description?: string | null
}
"Super groups" suggests:
What it actually is:
$feature_enrollment/{flag_key} propertiesA clearer name would be enrollment_condition or early_access_gate.
Using FeatureFlagGroupType implies you can:
$feature_enrollment/* makes sense)exact with ["true"] is meaningful)The Django code that creates super groups always produces exactly one property, and only for features in ActiveStage (Alpha, Beta, GA):
# 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.
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:
| Concept | What it means |
|---|---|
| PostHog groups | Company/account-level entities for B2B analytics |
| Feature flag groups | Release condition sets (targeting rules) |
| Super groups | Early access enrollment checks |
Three unrelated uses of "group" in feature flags.
The serializer and API enforce constraints that the type doesn't express:
These constraints live in validation code, not the type system, making the valid configurations unclear from the schema alone.
A dedicated type that reflects what super groups actually do:
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:
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.
| Component | Path |
|---|---|
| Python evaluation | posthog/models/feature_flag/flag_matching.py |
| Rust evaluation | rust/feature-flags/src/flags/flag_matching.rs |
| Early access API | products/early_access_features/backend/api.py |
| Frontend types | frontend/src/types.ts |
| Frontend logic | frontend/src/scenes/feature-flags/featureFlagReleaseConditionsLogic.ts |