Ring deployments
Feature management system requirements, Part 3
We are using LaunchDarkly as our feature management system and everything in this article is reflecting that.
Feature flag evaluation mechanism
To understand the ring deployments, we need to explain how the feature management system decides what varian of a feature flag should be returned to the user.
User context
Every user is represented by a user context. Here is an example of a user context that we send to the feature management system whenever we request a value of a feature flag.
{
"key: "1"
"email": "ondrej.k@something.com"
"customerId": "112233",
"iOSAppVersion": "iOS",
"appVersion": "10.1.2"
}
We can define as many attributes as we need. We should only include what we will need later to release a feature. Here is a list of good candidates:
- key — is what identifies the user, it should be your internal user ID, never a random ID because you are billed based on monthly active users (MAU). Make sure you mock the LaunchDarkly client in your tests or use a static ID, random generated IDs can consume all MAU!
- company ID — in case you are building a product for companies and you want to release features one by one to the customers as they are ready for it (e.g. sometimes, customers want to communicate the features to their users, provide training, do the marketing before releasing a big feature)
- app client type and app version— The client type to identify if a client is iOS, Android, or Web. The app version is useful when you want to release a feature from a specific version of the app or when you want to use different, e.g. REST APIs, for different versions — when you are in the middle of a migration to the new API endpoints.
Feature flag rules
When we ask for the variant of a feature flag, we pass the user context together with the name of a feature flag to the feature management system. The feature management system then applies the rules and returns the value that is evaluated based on the provided user context.
let flagName = "is-new-account-view-enabled"let userContext = {
"key: "1"
"email": "ondrej.k@something.com"
"customerId": "112233",
"iOSAppVersion": "iOS",
"appVersion": "10.1.2"
}let isNewAccountViewEnabled = client.variation(flagName, context)
Example
Let’s imagine we are working on a new feature to redesign an account view. We are going to make a new feature flag named is-new-account-view-enabled
. The feature is going to return false
or true
. By default, we want to serve false
because we are still working on it and we don’t want it to be visible. But we want the feature flag to be evaluated with true
value for the engineering team.
We can deploy this new feature to production whenever we feel like it is ready and try how the early version works in the real environment.
The ring deployments
A ring is defined by a segment, which is a set of selected users or defined rules. It is up to the product team to figure out what segments are useful and should exist.
Here are a few segment examples and naming conventions we use.
Customer segments
The rule for the segment is something like this customerId isOneOf "112233"
. This approach creates a human-readable alias, segment, so you don’t need to work with, e.g. generated UUIDs, in your feature flags (if you do, it gets messy very quickly).
The naming convention is Customer — {customerName}
. For example, Customer — Supermegaevil Corp
.
User segments
We found that it is useful when every product team or group of users has its segment. The first benefit, they can isolate the work and don’t interrupt the other teams. The second benefit, we can put our leadership team or beta users into their group.
The naming convention is Users — {groupName}
. For example Users — Leadership
, Users — Internal beta group
, Users — Frontend Team
, or Users — QA
.
Feature segments
Sometimes (it is rare but it happens) we need to define a specific group for a specific feature, then we use this segment type.
The naming convention is Feature — {featureName}
. For example Feature — Custom UI
.
Ring deployment benefits
Here are the main benefits as we see them based on our experience:
- the team decides who is getting a feature first
- it is possible to do testing in production with different groups, like beta testers (UX researchers might appreciate this)
- it is possible to try out the new or updated feature in production using real data
- we can compare the new behavior with previous behavior with a small group before going all-in
- we can turn off a feature for the segment (ring) we rolled it out in case it is not working well