Feature flags
The feature flags utility provides a simple rule engine to define when one or multiple features should be enabled depending on the input.
Info
We currently only support AppConfig using freeform configuration profile.
Terminology¶
Feature flags are used to modify behaviour without changing the application's code. These flags can be static or dynamic.
Static flags. Indicates something is simply on
or off
, for example TRACER_ENABLED=True
.
Dynamic flags. Indicates something can have varying states, for example enable a list of premium features for customer X not Y.
Tip
You can use Parameters utility for static flags while this utility can do both static and dynamic feature flags.
Warning
Be mindful that feature flags can increase the complexity of your application over time; use them sparingly.
If you want to learn more about feature flags, their variations and trade-offs, check these articles:
- Feature Toggles (aka Feature Flags) - Pete Hodgson
- AWS Lambda Feature Toggles Made Simple - Ran Isenberg
- Feature Flags Getting Started - CloudBees
Note
AWS AppConfig requires two API calls to fetch configuration for the first time. You can improve latency by consolidating your feature settings in a single Configuration.
Key features¶
- Define simple feature flags to dynamically decide when to enable a feature
- Fetch one or all feature flags enabled for a given application context
- Support for static feature flags to simply turn on/off a feature without rules
Getting started¶
IAM Permissions¶
Your Lambda function IAM Role must have appconfig:GetLatestConfiguration
and appconfig:StartConfigurationSession
IAM permissions before using this feature.
Required resources¶
By default, this utility provides AWS AppConfig as a configuration store.
The following sample infrastructure will be used throughout this documentation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
Evaluating a single feature flag¶
To get started, you'd need to initialize AppConfigStore
and FeatureFlags
. Then call FeatureFlags
evaluate
method to fetch, validate, and evaluate your feature.
The evaluate
method supports two optional parameters:
- context: Value to be evaluated against each rule defined for the given feature
- default: Sentinel value to use in case we experience any issues with our store, or feature doesn't exist
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
1 2 3 4 5 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Static flags¶
We have a static flag named ten_percent_off_campaign
. Meaning, there are no conditional rules, it's either ON or OFF for all customers.
In this case, we could omit the context
parameter and simply evaluate whether we should apply the 10% discount.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
1 2 3 4 5 |
|
Getting all enabled features¶
As you might have noticed, each evaluate
call means an API call to the Store and the more features you have the more costly this becomes.
You can use get_enabled_features
method for scenarios where you need a list of all enabled features according to the input context.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
1 2 3 4 5 6 7 8 9 10 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
Beyond boolean feature flags¶
When is this useful?
You might have a list of features to unlock for premium customers, unlock a specific set of features for admin users, etc.
Feature flags can return any JSON values when boolean_type
parameter is set to false
. These can be dictionaries, list, string, integers, etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
1 2 3 4 5 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Time based feature flags¶
Feature flags can also return enabled features based on time or datetime ranges. This allows you to have features that are only enabled on certain days of the week, certain time intervals or between certain calendar dates.
Use cases:
- Enable maintenance mode during a weekend
- Disable support/chat feature after working hours
- Launch a new feature on a specific date and time
You can also have features enabled only at certain times of the day for premium tier customers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
1 2 3 4 5 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
You can also have features enabled only at certain times of the day.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
You can also have features enabled only at specific days, for example: enable christmas sale discount during specific dates.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
How should I use timezones?
You can use any IANA time zone (as originally specified in PEP 615) as part of your rules definition. Powertools takes care of converting and calculate the correct timestamps for you.
When using SCHEDULE_BETWEEN_DATETIME_RANGE
, use timestamps without timezone information, and
specify the timezone manually. This way, you'll avoid hitting problems with day light savings.
Advanced¶
Adjusting in-memory cache¶
By default, we cache configuration retrieved from the Store for 5 seconds for performance and reliability reasons.
You can override max_age
parameter when instantiating the store.
1 2 3 4 5 6 7 8 |
|
Getting fetched configuration¶
When is this useful?
You might have application configuration in addition to feature flags in your store.
This means you don't need to make another call only to fetch app configuration.
You can access the configuration fetched from the store via get_raw_configuration
property within the store instance.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Schema¶
This utility expects a certain schema to be stored as JSON within AWS AppConfig.
Features¶
A feature can simply have its name and a default
value. This is either on or off, also known as a static flag.
minimal_schema.json | |
---|---|
1 2 3 4 5 6 7 8 9 |
|
If you need more control and want to provide context such as user group, permissions, location, etc., you need to add rules to your feature flag configuration.
Rules¶
When adding rules
to a feature, they must contain:
- A rule name as a key
when_match
boolean or JSON value that should be used when conditions match- A list of
conditions
for evaluation
feature_with_rules.json | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
You can have multiple rules with different names. The rule engine will return the first result when_match
of the matching rule configuration, or default
value when none of the rules apply.
Conditions¶
The conditions
block is a list of conditions that contain action
, key
, and value
keys:
conditions.json | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
The action
configuration can have the following values, where the expressions a
is the key
and b
is the value
above:
Action | Equivalent expression |
---|---|
EQUALS | lambda a, b: a == b |
NOT_EQUALS | lambda a, b: a != b |
KEY_GREATER_THAN_VALUE | lambda a, b: a > b |
KEY_GREATER_THAN_OR_EQUAL_VALUE | lambda a, b: a >= b |
KEY_LESS_THAN_VALUE | lambda a, b: a < b |
KEY_LESS_THAN_OR_EQUAL_VALUE | lambda a, b: a <= b |
STARTSWITH | lambda a, b: a.startswith(b) |
ENDSWITH | lambda a, b: a.endswith(b) |
KEY_IN_VALUE | lambda a, b: a in b |
KEY_NOT_IN_VALUE | lambda a, b: a not in b |
VALUE_IN_KEY | lambda a, b: b in a |
VALUE_NOT_IN_KEY | lambda a, b: b not in a |
SCHEDULE_BETWEEN_TIME_RANGE | lambda a, b: time(a).start <= b <= time(a).end |
SCHEDULE_BETWEEN_DATETIME_RANGE | lambda a, b: datetime(a).start <= b <= datetime(b).end |
SCHEDULE_BETWEEN_DAYS_OF_WEEK | lambda a, b: day_of_week(a) in b |
Info
The **key**
and **value**
will be compared to the input from the **context**
parameter.
Time based keys
For time based keys, we provide a list of predefined keys. These will automatically get converted to the corresponding timestamp on each invocation of your Lambda function.
Key | Meaning |
---|---|
CURRENT_TIME | The current time, 24 hour format (HH:mm) |
CURRENT_DATETIME | The current datetime (ISO8601) |
CURRENT_DAY_OF_WEEK | The current day of the week (Monday-Sunday) |
If not specified, the timezone used for calculations will be UTC.
For multiple conditions, we will evaluate the list of conditions as a logical AND
, so all conditions needs to match to return when_match
value.
Rule engine flowchart¶
Now that you've seen all properties of a feature flag schema, this flowchart describes how the rule engine decides what value to return.
Envelope¶
There are scenarios where you might want to include feature flags as part of an existing application configuration.
For this to work, you need to use a JMESPath expression via the envelope
parameter to extract that key as the feature flags configuration.
1 2 3 4 5 6 7 8 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Built-in store provider¶
Info
For GA, you'll be able to bring your own store.
AppConfig¶
AppConfig store provider fetches any JSON document from AWS AppConfig.
These are the available options for further customization.
Parameter | Default | Description |
---|---|---|
environment | "" |
AWS AppConfig Environment, e.g. test |
application | "" |
AWS AppConfig Application |
name | "" |
AWS AppConfig Configuration name |
envelope | None |
JMESPath expression to use to extract feature flags configuration from AWS AppConfig configuration |
max_age | 5 |
Number of seconds to cache feature flags configuration fetched from AWS AppConfig |
sdk_config | None |
Botocore Config object |
jmespath_options | None |
For advanced use cases when you want to bring your own JMESPath functions |
logger | logging.Logger |
Logger to use for debug. You can optionally supply an instance of Powertools Logger. |
AppConfigStore sample | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Testing your code¶
You can unit test your feature flags locally and independently without setting up AWS AppConfig.
AppConfigStore
only fetches a JSON document with a specific schema. This allows you to mock the response and use it to verify the rule evaluation.
Warning
This excerpt relies on pytest
and pytest-mock
dependencies.
Unit testing feature flags | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
Feature flags vs Parameters vs env vars¶
Method | When to use | Requires new deployment on changes | Supported services |
---|---|---|---|
Environment variables | Simple configuration that will rarely if ever change, because changing it requires a Lambda function deployment. | Yes | Lambda |
Parameters utility | Access to secrets, or fetch parameters in different formats from AWS System Manager Parameter Store or Amazon DynamoDB. | No | Parameter Store, DynamoDB, Secrets Manager, AppConfig |
Feature flags utility | Rule engine to define when one or multiple features should be enabled depending on the input. | No | AppConfig |
Deprecation list when GA¶
Breaking change | Recommendation |
---|---|
IN RuleAction |
Use KEY_IN_VALUE instead |
NOT_IN RuleAction |
Use KEY_NOT_IN_VALUE instead |
get_enabled_features |
Return type changes from List[str] to Dict[str, Any] . New return will contain a list of features enabled and their values. List of enabled features will be in enabled_features key to keep ease of assertion we have in Beta. |
boolean_type Schema |
This might not be necessary anymore before we go GA. We will return either the default value when there are no rules as well as when_match value. This will simplify on-boarding if we can keep the same set of validations already offered. |