feat(flags): minimize $feature_flag_called events for non-experiment flags#166
Open
haacked wants to merge 2 commits into
Open
feat(flags): minimize $feature_flag_called events for non-experiment flags#166haacked wants to merge 2 commits into
haacked wants to merge 2 commits into
Conversation
Emit a minimal, allowlisted $feature_flag_called event when the /flags response carries the top-level minimalFlagCalledEvents gate and the evaluated flag reports has_experiment: false. Any missing signal (gate absent or unparseable, has_experiment unknown, experiment-linked flag) keeps the full legacy event shape. The gate is stored on each FeatureFlags.Result so events fired later from an Evaluations snapshot respect it. Minimal events are assembled the same way capture/3 would (context, then global properties) and then projected onto the allowlist, so context tags and customer global properties are stripped while $groups and $process_person_profile survive when set. Generated-By: PostHog Code Task-Id: ffe402fd-d75c-4043-8e5d-d2fe513cac6f
Contributor
posthog-elixir Compliance ReportDate: 2026-07-18 04:26:45 UTC ✅ All Tests Passed!46/46 tests passed Capture Tests✅ 29/29 tests passed View Details
Feature_Flags Tests✅ 17/17 tests passed View Details
|
haacked
marked this pull request as ready for review
July 18, 2026 04:28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
💡 Motivation and Context
$feature_flag_calledevents carry the full enriched properties (event context, global properties, system context) on every flag call, even for flags not linked to an experiment. This PR trims those events to a strict allowlist iff the server-controlled gate is on (top-levelminimalFlagCalledEventsin the v2/flagsresponse) and the flag'shas_experimentis exactlyfalse. Any missing signal (gate absent or unparseable,has_experimentunknown, experiment-linked flag) sends the full legacy shape unchanged.Server-gated because rolling this out unconditionally could break existing insights; announcement/comms come before enablement. The goal is to establish
$feature_flag_calledas a special system event.Part of a cross-SDK rollout; reference implementation and fuller context: PostHog/posthog-python#748.
How it works in this SDK
/flagsremote path applies: the gate is read from the top-levelminimalFlagCalledEventsfield of the v2 response.PostHog.FeatureFlags.Result(alongside the other response-level fields likerequest_idanderrors_while_computing), so events fired later from anEvaluationssnapshot respect it per flag.capture/3+bare_capture/4) and then projected onto the allowlist viaMap.takewith a dual atom/string key allowlist. Kept: flag identity and evaluation metadata ($feature_flag,$feature_flag_response,$feature_flag_has_experiment,$feature_flag_id,$feature_flag_version,$feature_flag_reason,$feature_flag_request_id,$feature_flag_evaluated_at,$feature_flag_errorwhen set),$groups,$process_person_profile,$lib,$lib_version, and$is_server. Everything else is stripped, including the$feature/<key>property, context tags, and customer global properties.before_sendstill runs after the projection and may re-inflate the event — the accepted customer escape hatch; the SDK itself adds nothing after the allowlist.capture_prepared/4that skips re-enrichment;bare_capture/4behavior is unchanged and the public API snapshot is untouched.💚 How did you test it?
mix test— 345 tests, 0 failures (18 integration-tagged excluded)mix format --check-formatted— cleanmix credo --strict— 0 issuesmix compile --warnings-as-errors— cleanmix posthog.public_api --check— snapshot up to dateNote: run locally on Elixir 1.18.3 / OTP 27.3.3 (
.tool-versionspins 1.20.0-otp-29, which wasn't installed locally); CI runs the pinned toolchain.Coverage added:
false/ unparseable ("yes") → full; gated buthas_experimentmissing → full.$is_server: true,$lib,$lib_version, and$feature_flag_errorwhen the response signals errors), so any extra or missing property fails the test.$groups/$process_person_profilesurvive the allowlist while string-keyed junk is stripped (pins the string half of the dual allowlist).Evaluationssnapshot flow: per-flag gate behavior from one snapshot (minimal for the non-experiment flag, full for the experiment flag) plus the missing-flag full-shape fallback.A minor changeset is included (
.sampo/changesets/mellow-ibex-sointu.md).📝 Checklist
If releasing new changes
sampo addto generate a changeset fileCreated with PostHog Code