-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature Flag Spec for integration libraries #10459
Comments
cc: @liyiy @alexkim205 |
You might want to have a look at the |
Local evaluation for server side librariesFlag definition loadingOn initialization, the library makes one request:
This request returns an object with two keys:
Experience continuityIf a flag has experience continuity enabled, then it's never evaluated locally. Active flagsConsider this case today: Thus, when local evaluating, get non-active flags as well from Debug loggingTo make it easier for developers to debug when a flag is locally vs remotely evaluated, log this.
Person and Group propertiesFor local evaluation, we expect the client to provide person and group properties alongside any This changes the interface from:
to
For the flag in question, if all required property keys are present, and the operator is not In every other case, we fall back to a Note: Whenever we fallback to Handling CohortsFor cohorts, we fall back to Hash IdentifiersIf flag evaluation conditions above are met, we then determine if rollout percentage conditions are met. To do this, we generate a hash as follows: ### Non-multivariate flags The hash key is
and the value is
divided by the largest float, That is, the first 16 characters of a sha1 hash, parsed as an integer in base 16. If this value is less than or equal to the rollout %, then the flag evaluates to True. Multivariate flagsFor multivariate flags, the value is calculated the same way. The only two differences are the hash key, and which variant is assigned based on rollout %s. The hash key is
That is, there is a salt added to the key, called To determine which variant the flag belongs to, we generate a variant lookup table that looks like so:
then to compute the variant, if Test CoverageSince it's easy to make mistakes here, we also want a consistent test suite across all libraries. Here are some expected tests and the subsequent results: General Recommendations
Consistency TestsThese tests ensure that the hashes are computed correctly. These take the result from the server, and ensure that client libs match these results. Every library must have 2 of these tests, which replicates the server tests here.
Fallback test
Default value TestsWe expect 2 tests here as well.
Single Property Matching TestsFor each property type, ensure that the results are what we'd expect. 8 tests (one per operator) Property Group Matching TestsFeature flag definitions look something like:
Here, all groups are OR'ed, while all properties in the arrays are AND'ed. Have a complex test to test all permutations here. And some special edge cases:
Person and Group property matching1 test each for simple person and group property matching. Experience continuity testIf flag has continuity enabled, never try to calculate locally. Get All flags
Memory ManagementFor long running libraries, the caching we do to check if we've sent a This event follows atleast once semantics, so it's fine if we send the event again for a single user, so just clear the cache when it reaches a certain size. Python library has this at 100,000 people right now. Special Edge cases
|
To enable client-side libs to not wait for flags to parse, we can have an option to initialise the library with a distinctID, and corresponding flags. To make this seamless though, our server side libs need another method: Spec looks like this: Only for server-side libraries. Function signature is: It computes locally what it can, and then sends a request to The final return type is a dict of flag_key and values. |
After playing around a bit, I'd like to add a few more things ( and noticed a few more missing things):
And one more questionable thing: |
Default results parameter in the spec has been confusing users, specially around when it applies. So:
|
All of our client and server side libraries should support experience continuity and multivariate feature flags. This is the spec for functions we'd want to see:
Client Side Spec
A mechanism to send a POST request to
/decide/?v=2
with the following data:The response will contain an object at
response['featureFlags']
with the flags that are enabled for this user, and the variant value, if any. The distinct ID should be already available from existing methods implemented in this lib.The response should be cached and refreshed periodically (for simple flags only. Doesn't apply (yet) to multivariate support.
Experience continuity: New feature to "persist flag across authentication steps"
Whenever an
$identify
call is made for the first time for an anonymous user, we also want to send$anon_distinct_id
as part of the decide request. See the posthog-js implementationA method
getFeatureFlag
This method takes a feature flag key as an input and returns the variant value if multivariate flag, or otherwise a boolean indicating if the given flag is on or off for the user based on the response from
/decide
. See posthog-js.Whenever this method is called, we also send a
$feature_flag_called
event.A method
isFeatureEnabled
This method takes a feature flag key as an input and returns a boolean indicating if the given flag is on or off for the user based on the response from
/decide
. See posthog-js.Whenever this method is called, we also send a
$feature_flag_called
event.Set feature flag values on capture
Whenever an event is captured, the library also sets the following two properties on every event:
$feature/<feature-flag-name>
, for each feature flag, with the value as the output ofgetFeatureFlag
.$active_feature_flags
, which is a list of all active feature flags.Server Side Spec
On the server-side, the template to follow is
posthog-python
. The biggest difference is that server side libraries are stateless. We don't store distinctIDs, nor feature flags, because the same instance is often called for multiple users, depending on what requests are coming in.Requests to
/decide/?v=2
to get feature flags, the API contract is the same.No experience continuity, since there's no anon_distinct_ids
A method
getFeatureFlag
, same as client side libraries, which also sends a$feature_flag_called
event.A method
isFeatureEnabled
, same as client side libraries, which also sends a$feature_flag_called
event.An option
send_feature_flags
on capture calls, which gets all feature flags for a given distinct ID, and adds them to the event, just like in client side libraries. See this PRNew addition: See comment #10459 (comment)
The text was updated successfully, but these errors were encountered: