Feature
Etsy's Feature flagging API used for operational rampups and A/B testing.
Install / Use
/learn @etsy/FeatureREADME
Feature is no longer actively maintained and is no longer in sync with the version used internally at Etsy.
Feature API
Etsy's Feature flagging API used for operational rampups and A/B testing.
The Feature API is how we selectively enable and disable features at a very fine grain as well as enabling features for a percentage of users for operational ramp-ups and for A/B tests. A feature can be completely enabled, completely disabled, or something in between and can comprise a number of related variants.
For features that are not completely enabled or disabled, we log every time we check whether a feature is enabled and include the result, including what variant was selected, in the events we fire.
The two main API entry points are:
Feature::isEnabled('my_feature')
which returns true when my_feature is enabled and, for multi-variant
features:
Feature::variant('my_feature')
which returns the name of the particular variant which should be used.
The single argument to each of these methods is the name of the feature to test.
A typical use of Feature::isEnabled for a single-variant feature
would look something like this:
if (Feature::isEnabled('my_feature')) {
// do stuff
}
For a multi-variant feature, within the block guarded by the
Feature::isEnabled check, we can determine the appropriate code to
run for each variant with something like this:
if (Feature::isEnabled('my_feature')) {
switch (Feature::variant('my_feature')) {
case 'foo':
// do stuff appropriate for the foo variant
break;
case 'bar':
// do stuff appropriate for the bar variant
break;
}
}
It is an error (and will be logged as such) to ask for the variant of
a feature that is not enabled. So the calls to variant should always
be guarded by an Feature::isEnabled check.
The API also provides two other pairs of methods that will be used much less frequently:
Feature::isEnabledFor('my_feature', $user)
Feature::variantFor('my_feature', $user)
and
Feature::isEnabledBucketingBy('my_feature', $bucketingID)
Feature::variantBucketingBy('my_feature', $bucketingID)
These methods exist only to support a couple very specific use-cases:
when we want to enable or disable a feature based not on the user
making the request but on some other user or when we want to bucket a
percentage of executions based on something entirely other than a
user.) The canonical case for the former, at Etsy, is if we wanted to
change something about how we deal with listings and instead of
enabling the feature for only some users but for all listings those
users see, but instead we want to enable it for all users but for only
some of the listings. Then we could use isEnabledFor and
variantFor and pass in the user object representing the owner of the
listing. That would also allow us to enable the feature for specific
listing owners. The bucketingBy methods serve a similar purpose
except when there either is no relevant user or where we don't want to
always put the same user in the same bucket. Thus if we wanted to
enable a certain feature for 10% of all listings displayed,
independent of both the user making the request and the user who owned
the listing, we could use isEnabledBucketingBy with the listing id
as the bucketing ID.
In general it is much more likely you want to use the plain old
isEnabled and variant methods.
For Smarty templates, where static methods can’t readily be called,
there is an object, $feature, wired up in Tpl.php that exposes the
same four methods as the Feature API but as instance methods, for
instance:
{% if $feature->isEnabled("my_feature") %}
Configuration cookbook
There are a number of common configurations so before I explain the complete syntax of the feature configuration stanzas, here are some of the more common cases along with the most concise way to write the configuration.
A totally enabled feature:
$server_config['foo'] = 'on';
A totally disabled feature:
$server_config['foo'] = 'off';
Feature with winning variant turned on for everyone
$server_config['foo'] = 'blue_background';
Feature enabled only for admins:
$server_config['foo'] = array('admin' => 'on');
Single-variant feature ramped up to 1% of users.
$server_config['foo'] = array('enabled' => 1);
Multi-variant feature ramped up to 1% of users for each variant.
$server_config['foo'] = array(
'enabled' => array(
'blue_background' => 1,
'orange_background' => 1,
'pink_background' => 1,
),
);
Enabled for a single specific user.
$server_config['foo'] = array('users' => 'fred');
Enabled for a few specific users.
$server_config['foo'] = array(
'users' => array('fred', 'barney', 'wilma', 'betty'),
);
Enabled for a specific group
$server_config['foo'] = array('groups' => 1234);
Enabled for 10% of regular users and all admin.
$server_config['foo'] = array(
'enabled' => 10,
'admin' => 'on',
);
Feature ramped up to 1% of requests, bucketing at random rather than by user
$server_config['foo'] = array(
'enabled' => 1,
'bucketing' => 'random',
);
Single-variant feature in 50/50 A/B test
$server_config['foo'] = array('enabled' => 50);
Multi-variant feature in A/B test with 20% of users seeing each variant (and 40% left in control group).
$server_config['foo'] = array(
'enabled' => array(
'blue_background' => 20,
'orange_background' => 20,
'pink_background' => 20,
),
);
New feature intended only to be enabled by adding ?features=foo to a URL
$server_config['foo'] = array('enabled' => 0);
This is kind of a funny edge case. It could also be written:
$server_config['foo'] = array();
since a missing 'enabled' is defaulted to 0.
Configuration details
Each feature’s config stanza controls when the feature is enabled and what variant should be used when it is.
Leaving aside a few shorthands that will be explained in a moment, the
value of a feature config stanza is an array with a number of special
keys, the most important of which is 'enabled'.
In its full form, the value of the 'enabled' property is either the
string 'off', meaning the feature is entirely disabled, any other
string, meaning the named variant is enabled for all requests, or an
array whose keys are names of variants and whose values are the
percentage of requests that should see each variant.
As a shorthand to support the common case of a feature with only one
variant, 'enabled' can also be specified as a percentage from 0 to
100 which is equivalent to specifying an array with the variant name
'on' and the given percentage.
The next four most important properties of a feature config stanza
specify a particular variant that special classes of users should see:
'admin', 'internal', 'users', and 'groups'.
The 'admin' and 'internal' properties, if present, should name a
variant that should be shown for all admin users or all internal
requests. For single-variant features this name will almost always be
'on'. (Technically you could also specify 'off' to turn off a
feature for admin users or internal requests that would be otherwise
enabled. But that would be weird.) For multi-variant features it can
be any of the variants mentioned in the 'enabled' array.
The 'users' and 'groups' variants provide a mapping from variant
names to lists of users or numeric group ids. In the fully specified
case, the value will be an array whose keys are the names of variants
and whose values are lists of user names or group ids, as appropriate.
As a shorthand, if the list of user names or group ids is a single
element it can be specified with just the name or id. And as a further
shorthand, in the configuration of a single-variant feature, the value
of the 'users' or 'groups' property can simply be the value that
should be assigned to the 'on' variant. So using both shorthands,
these are equivalent:
$server_config['foo'] => array('users' => array('on' => array('fred')));
and:
$server_config['foo'] => array('users' => 'fred');
None of these four properties have any effect if 'enabled' is a
string since in those cases the feature is considered either entirely
enabled or disabled. They can, however, enable a variant of a feature
if no 'enabled' value is provided or if the variant’s percentage is
0.
On the other hand, when an array 'enabled' value is specified, as an
aid to detecting typos, the variant names used in the 'admin',
'internal', 'users', and 'groups' properties must also be keys
in the 'enabled' array. So if any variants are specified via
'enabled', they should all be, even if their percentage is set to 0.
The two remaining feature config properties are 'bucketing' and
'public_url_override'. Bucketing specifies how users are bucketed
when a feature is enabled for only a percentage of users. The default
value, 'uaid', causes bucketing via the UAID cookie which means a
user will be in the same bucket regardless of whether they are signed
in or not.
The bucketing value 'user', causes bucketing to be based on the
signed-in user id. Currently we fall back to bucketing by UAID if the
user is not signed in but this is problematic since it means that a
user can switch buckets if they sign in or out. (We may change the
behavior of this bucketing scheme to simply disable the feature for
users who are not signed in.)
Finally the bucketing value 'random', causes each request to be
bucketed independently meaning that the same user will be in different
buckets on different requests. This is typically used for features
that should have no user-visible effects but where we want to ramp up
somethin
Related Skills
node-connect
336.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
82.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
336.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
82.8kCommit, push, and open a PR
