Skip to content

feat(flags): add $feature_flag_has_experiment to $feature_flag_called events - #165

Merged
haacked merged 2 commits into
mainfrom
haacked/feature-flag-has-experiment
Jul 15, 2026
Merged

feat(flags): add $feature_flag_has_experiment to $feature_flag_called events#165
haacked merged 2 commits into
mainfrom
haacked/feature-flag-has-experiment

Conversation

@haacked

@haacked haacked commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

Part of a cross-SDK effort to shrink $feature_flag_called events. This first phase adds a $feature_flag_has_experiment boolean property to every $feature_flag_called event, reflecting the server's has_experiment signal: metadata.has_experiment in the /flags?v=2 response and has_experiment on /api/feature_flag/local_evaluation flag definitions. When the server does not report the field (older deployments, bootstrapped flags), the property is omitted, so it is tri-state: true, false, or absent (unknown).

This lets ingestion distinguish experiment-linked flag events (which need the full property set for exposure analysis) from the rest, and lets us measure the split before a later phase strips the expensive properties ($feature/<key>, $feature_flag_payload, per-event system metadata) from non-experiment flag events. This PR intentionally minimizes nothing: no properties are removed, no config options are added, and dedupe behavior is unchanged.

Changes

  • has_experiment: boolean() on the Result struct (default false); build_result/3 parses metadata.has_experiment from the /flags?v=2 response with strict boolean coercion (this SDK has no local evaluation).
  • log_feature_flag_usage/4 always adds the property, covering both the legacy single-flag path and the Evaluations snapshot path (missing flags omit the property).
  • The SDK compliance adapter's hand-built $feature_flag_called also includes the property via an extract_has_experiment/2 helper over the response it already parses.
  • Minor sampo changeset (CHANGELOG is generated at release).

💚 How did you test it?

mix test: 336 tests, 0 failures, with new coverage for true / reported-false / absent on both send paths plus the struct default. mix format --check-formatted, mix credo --strict, and mix posthog.public_api --check all clean. Note: ran on Elixir 1.18.3/OTP 27 (the repo pins 1.20/OTP 29, not installed locally; mix.exs requires only ~> 1.17); CI covers the pinned toolchain.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran sampo add to generate a changeset file

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

posthog-elixir Compliance Report

Date: 2026-07-15 23:28:08 UTC
Duration: 117489ms

✅ All Tests Passed!

46/46 tests passed


Capture Tests

29/29 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields 608ms
Format Validation.Event Has Uuid 609ms
Format Validation.Event Has Lib Properties 608ms
Format Validation.Distinct Id Is String 608ms
Format Validation.Token Is Present 608ms
Format Validation.Custom Properties Preserved 608ms
Format Validation.Event Has Timestamp 608ms
Retry Behavior.Retries On 503 5614ms
Retry Behavior.Does Not Retry On 400 2612ms
Retry Behavior.Does Not Retry On 401 2612ms
Retry Behavior.Respects Retry After Header 5614ms
Retry Behavior.Implements Backoff 15612ms
Retry Behavior.Retries On 500 5613ms
Retry Behavior.Retries On 502 5614ms
Retry Behavior.Retries On 504 5615ms
Retry Behavior.Max Retries Respected 15624ms
Deduplication.Generates Unique Uuids 618ms
Deduplication.Preserves Uuid On Retry 5614ms
Deduplication.Preserves Uuid And Timestamp On Retry 10619ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry 5615ms
Deduplication.No Duplicate Events In Batch 615ms
Deduplication.Different Events Have Different Uuids 611ms
Compression.Sends Gzip When Enabled 608ms
Batch Format.Uses Proper Batch Structure 608ms
Batch Format.Flush With No Events Sends Nothing 606ms
Batch Format.Multiple Events Batched Together 613ms
Error Handling.Does Not Retry On 403 2609ms
Error Handling.Does Not Retry On 413 2612ms
Error Handling.Retries On 408 5615ms

Feature_Flags Tests

17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id 609ms
Request Payload.Flags Request Uses V2 Query Param 609ms
Request Payload.Flags Request Hits Flags Path Not Decide 607ms
Request Payload.Flags Request Omits Authorization Header 608ms
Request Payload.Token In Flags Body Matches Init 608ms
Request Payload.Groups Round Trip 608ms
Request Payload.Groups Default To Empty Object 608ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False 608ms
Request Payload.Disable Geoip Omitted Defaults To False 608ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key 608ms
Request Lifecycle.No Flags Request On Init Alone 4ms
Request Lifecycle.No Flags Request On Normal Capture 607ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests 1212ms
Request Lifecycle.Mock Response Value Is Returned To Caller 608ms
Retry Behavior.Retries Flags On 502 911ms
Retry Behavior.Retries Flags On 504 910ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event 1209ms

@haacked haacked changed the title Add $feature_flag_has_experiment to $feature_flag_called events feat(flags): add $feature_flag_has_experiment to $feature_flag_called events Jul 15, 2026
@haacked
haacked marked this pull request as ready for review July 15, 2026 18:29
@haacked
haacked requested a review from a team as a code owner July 15, 2026 18:29
@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown

Reviews (1): Last reviewed commit: "Add $feature_flag_has_experiment to $fea..." | Re-trigger Greptile

@turnipdabeets turnipdabeets left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! get_in + == true keeps it crash-proof and always-boolean, both the legacy and Evaluations paths converge on the same Result field, and the true/false/absent cases are all asserted.

@haacked
haacked merged commit 673ec2c into main Jul 15, 2026
29 checks passed
@haacked
haacked deleted the haacked/feature-flag-has-experiment branch July 15, 2026 23:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants