feat: add evaluate_flags() API for single-call flag evaluation#136
Closed
feat: add evaluate_flags() API for single-call flag evaluation#136
Conversation
Add `Client#evaluate_flags(distinct_id, …)` returning a `FeatureFlagEvaluations` snapshot, and a `flags:` option on `capture` so a single `/flags` call can power both flag branching and event enrichment per request. The snapshot exposes `is_enabled`, `get_flag`, `get_flag_payload`, plus `only_accessed` / `only([keys])` filter helpers. `flag_keys:` scopes the underlying `/flags` request itself. `is_enabled` and `get_flag` fire `$feature_flag_called` events with full metadata (id, version, reason, request_id), deduped through the existing per-distinct_id cache. The legacy single-flag dedup + capture is extracted into `_capture_feature_flag_called_if_needed` and shared between the existing `get_feature_flag` path and the snapshot's access-recording. Existing `is_feature_enabled`, `get_feature_flag`, `get_feature_flag_payload`, and `capture(send_feature_flags:)` continue to work unchanged; deprecation is a follow-up minor. Generated-By: PostHog Code Task-Id: fe67a5bd-7d33-4568-9148-39d181660a5a
The keyword arg `flag_keys:` triggered Ruby 2.7's hash-to-kwargs conversion on the existing `get_flags(distinct_id, groups, person_properties, group_properties)` callsite, eating the trailing `group_properties` hash as kwargs and raising `ArgumentError: unknown keyword: :company` for any non-empty value. Positional default avoids the conversion entirely and keeps the existing internal calls source-compatible across Ruby 2.7 → 3.x. Generated-By: PostHog Code Task-Id: fe67a5bd-7d33-4568-9148-39d181660a5a
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.
Problem
Today every flag check on the Ruby SDK fires its own
/flagsrequest, andcapture(send_feature_flags: true)silently fires another. Flag values can drift between the branching call and the captured event, andsend_feature_flagsbloats event properties on high-volume events. The/flagsv4 response carries rich metadata (id,version,reason,request_id) that is parsed intoFeatureFlagobjects today and then thrown away on the$feature_flag_calledevent — silently degrading experiment exposure tracking.Changes
Phase 1 of the Server SDK Feature Flag Evaluations RFC, mirroring
posthog-js#3476andposthog-python#539:A single
/flagsrequest powers both branching and event enrichment.is_enabledandget_flagfire\$feature_flag_calledevents (deduped through the existing per-distinct_id cache) with full metadata (\$feature_flag_id,\$feature_flag_version,\$feature_flag_reason,\$feature_flag_request_id).get_flag_payloaddoes not record access or fire an event.Two layers of scoping
flag_keys:option onevaluate_flags): scopes the/flagsrequest itself viaflag_keys_to_evaluate.only_accessed/only([keys])): narrows which flags get attached to a captured event without re-fetching.only_accessedfalls back to all flags with a warning if invoked before any access;onlywarns on missing keys. Filtered clones do not back-propagate access to the parent.Local evaluation
Transparent: locally-resolved flags are tagged with
locally_evaluated: true, reason\"Evaluated locally\", and\$feature_flag_definitions_loaded_at(added to the poller in this PR) on emitted events.Internals
The legacy single-flag path and the new snapshot share a new
_capture_feature_flag_called_if_needed(distinct_id:, key:, response:, properties:, groups:, disable_geoip:)helper that owns dedup-key construction, the per-distinct_id sent-flags cache, and the\$feature_flag_calledcapture call. Both paths dedupe identically.A new
feature_flags_log_warnings:client option (defaulttrue) silences filter-helper warnings.Pre-flight answers
Client#get_feature_flag. Phase 1 extracts_capture_feature_flag_called_if_needed. Cache:@distinct_id_has_sent_flag_calls(SizeLimitedHash, max 50k)./flagsresponse handler:FeatureFlagsPoller#get_flagsalready returns{flags: {key => FeatureFlag}, requestId, evaluatedAt, …}. Reused as-is./api/feature_flag/local_evaluation. This PR addsflag_definitions_loaded_atto the poller.attrswith symbol keys.:flagsslots in alongside:send_feature_flagsand takes precedence.lib/posthog/feature_flag_evaluations.rbdefiningPostHog::FeatureFlagEvaluations.Phase 2 (separate follow-up minor)
These methods will be flagged with deprecation warnings in a follow-up PR:
is_feature_enabledget_feature_flagget_feature_flag_payloadcapture(send_feature_flags:)This PR adds no runtime deprecation warnings.
Tests
17 new examples in
spec/posthog/feature_flag_evaluations_spec.rbcovering: remote-evaluation snapshot shape, single-call request, dedup, full metadata on events,\$feature_flag_error: \"flag_missing\"for unknown flags,get_flag_payloadnot firing,flag_keysround-trip, empty-distinct_id safety, all four filter behaviors, capture integration without extra/flagscall, precedence oversend_feature_flags, and local-evaluation tagging including\$feature_flag_definitions_loaded_at.Full suite: 176 examples, 1 pre-existing transport-spec failure unrelated to this PR. Rubocop clean.
Created with PostHog Code