Skip to content
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

[Do not merge yet] Update documentation for feature flags and feature launch process. #5390

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions wiki/Guidelines-for-launching-new-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
## Table of Contents

- [Why do we need feature flags?](#why-do-we-need-feature-flags)
- [When to use feature flags?](#when-to-use-feature-flags)
- [How to use feature flags?](#how-to-use-feature-flags)
- [Development stage](#development-stage)
- [Pre-launch testing stage](#pre-launch-testing-stage)
- [Post-launch testing](#post-launch-testing)

When developing a new feature, you might want to limit the scope of the feature so that it's only enabled when certain criteria are met (e.g. only enabled in dev environment). In these cases, you can use the feature gating system to gate the enabling of the features with a feature flag.

## Why do we need feature flags?

Feature flags make the development process much easier by letting developers safely introduce new features to users. They also give us the ability to quickly turn off features if they cause issues after being launched.

Feature flags are handy for hiding features that are still in the development or testing phase. This way, developers can gradually add the feature to the code and only activate it when it's fully ready for everyone to use. If something goes wrong with the feature, feature flags make it easy to turn it off without undoing all the changes. This is especially helpful for big or complex features.

Moreover, feature flags help separate the release of the feature from the release of the app itself. This means we can update the app's behavior, when the new feature is ready to launch, with fewer chances of things going wrong.


## When to use feature flags?

Feature flags should be used when you are working on a feature whose scale makes it hard to implement all at once, or for more experimental features that are likely to cause breakages. Essentially, feature flags are a must for features that are not yet ready for production/fully-tested at the time that they're merged into develop.


## How to use feature flags?

Suppose you are working on a large scale user-facing feature that will take 1 or more PRs to fully implement. In such a case, please use feature flags to gate your feature appropriately by carefully following the steps listed below:


### Development Stage

1. First, create a PR on Web (following [these instructions](https://github.com/oppia/oppia/wiki/Launching-new-features#follow-the-steps-below-to-add-a-new-feature-flag)) that introduces the feature flag, so that it can be used by Android. The name of the feature flag must start with `android_` and the feature flag should be in DEV stage.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to this change, but we haven't raised any PRs on web for our new feature flags(AFAIK).


1. While waiting for that PR to be merged, create a PR on Android following the instructions in [Platform Parameters & Feature Flags](https://github.com/oppia/oppia-android/wiki/Platform-Parameters-&-Feature-Flags) that introduces your feature and adds a new feature flag that is meant to be used with this new feature. The feature flag should be placed in the DEV stage and disabled by default, so that it cannot accidentally be turned on in environments for which it is not yet ready (like the alpha/beta/GA release channels). Every single user-facing aspect of the feature -- whether frontend or backend -- must be gated behind the feature flag (both in the first PR, and all the following ones as well). This is to ensure that the feature is not visible to the user until it is fully implemented and ready for production.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The feature flag should be placed in the DEV stage and disabled by default

We don't have actual classifications for DEV stage on android, but we do disable feature flags by default.


1. The PR in the previous step must be merged before any subsequent Android PRs are merged. This is to ensure that the feature flag is available in the codebase for it to be used in the following PRs.


### Pre-launch testing stage

Testing is crucial to ensure that the app's core functionalities function properly. This early identification of major bugs and stability issues saves time and resources in the long run. We use the testing (pre-alpha) version of the app for this.

1. When your feature is fully implemented and ready for testing, submit a follow-up PR on Web to move your feature to the TEST stage.

1. Once that PR is merged, fill in [this form](https://forms.gle/rUJaHJSpRGemtGDp6) to ask the Web server admins to update the feature flag's stage to TEST on the Web server. (This will make the feature available in both the testing and alpha channels of the Android app.)

1. While waiting for the above two requests to be completed, fill in the form to [request a new feature test](https://forms.gle/Kf3993v15srUTu4k7) to request a new feature test. This will send a new request to the Android release team.

1. Work with the Android release team to organize a test of your feature. This will entail the following:

- The release team will deploy a new testing version of the app with the feature flag for your feature set to True. (This is currently done through a compile-time flag, but will later switch to using the Web feature flag defined in step 1 above.)

- The release team will send instructions to the feature testers for how to test the feature. Feature testers will use this to conduct the testing, by filling in the [feature review template](https://docs.google.com/document/d/1Uj5XFzDjthBI0ze-sgYZxD8qs8nTxsZso4Rd--DDffs/edit?usp=sharing).

1. As the feature owner, you should analyze and address any feedback received from testers. This includes bug reports, suggestions for improvement, and overall impressions of the feature.

- If the feature testing surfaces blocking issues that need to be fixed prior to the feature’s release, you must work on fixing the highlighted issues before proceeding further. (Any issue that the feature owner, PM, or Android team lead considers blocking should be regarded as such.) You can request a re-test once all the testing feedback is addressed.

1. If the testing results are positive and the feature is approved by the QA team (i.e. it’s been fully tested, and all issues filed have been fixed + verified), your feature is ready for launch! Create a PR on the Web codebase that moves the feature flag to the PROD stage, allowing it to be enabled/disabled in production and have its min-version set (by the release coordinator(s)). **NOTE: When opening this PR, include a link to the testing doc or other proof that the feature has been approved for release.** While this PR is open, confirm (through discussion) with the Android QA team that the new CUJs for this feature have been added to the overall CUJs used for testing Android releases in general.

- Once this PR is merged, send a ["feature-flag flip request"](https://forms.gle/rUJaHJSpRGemtGDp6) to the release coordinators to turn on the feature in production by adding a rule in the /release-coordinator page.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I initially read this, I was confused by

by adding a rule in the /release-coordinator page.

I thought it meant, "I, the feature owner, would need to do that".

I think ommiting that sentence fragment will remove confusion but still convey the message.


- We recommend filling in [this form](https://goo.gl/forms/sNBWrW03fS6dBWEp1) to announce your feature to the public once it's launched!


## Post-launch testing

We need to do some cleanup after the feature is successfully running in production:

1. Once the feature is confirmed to be functioning as intended in production (at least 10% of the 7DA user base is on a version of the app that has the feature enabled) by the product team, please do the following, in order:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is worth clarifying how to get to this point, e.g. check in 4 weeks after the release(or is the 7 days the actual time after release to check in?), or otherwise file a tracking issue(Which we currently do but can fall through the cracks with no specific follow up guidelines. Especially since the clean up is not typically done by the original feature owner).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could have a new issue template specifically for Post-launch cleanup, that ensures all the steps needed for clean up are captured.


- Make sure that the feature is ready to be made permanent. To do this, confirm with the PMs that no users have reported issues with it, and that no regressions have been detected via Android Play Store / Firebase analytics or general user feedback.

- The PMs should also fill in this [post-launch review template](https://docs.google.com/document/d/1DifFAe3oRzjmVPh2fEllfAky4n0QMAXVQc3Y580qkr8/edit).

1. Once you have confirmation that the feature can be made permanent, create a PR in the Android codebase to remove all remaining references to the feature flag from the codebase (for example, in all the `if` blocks you created to gate the feature).

1. Finally, merge one last Web PR to move the feature flag to the "deprecated" stage (one of the stages listed in `core/domain/platform_feature_list.py`, meant for flags that are no longer in use).
44 changes: 27 additions & 17 deletions wiki/Platform-Parameters-&-Feature-Flags.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
## Table of Contents

- [Introduction](#introduction)
- [How to create a Platform Parameter](#how-to-create-a-platform-parameter)
- [How to consume a Platform Parameter](#how-to-consume-a-platform-parameter)
- [How to write tests related Platform Parameter](#how-to-write-tests-related-platform-parameter)
- [How to create a Feature Flag or Platform Parameter](#how-to-create-a-feature-flag-or-platform-parameter)
- [How to consume a Feature Flag or Platform Parameter](#how-to-consume-a-feature-flag-or-platform-parameter)
- [How to write tests related to Platform Parameters](#how-to-write-tests-related-to-platform-parameters)
- [1. We actually don't test for platform parameter(s)](#1-we-actually-dont-test-for-platform-parameters)
- [2. We test for different values of platform parameter(s)](#2-we-test-for-different-values-of-platform-parameters)
- [2. We test for different values of platform parameter(s)](#2-we-test-for-different-values-of-platform-parameters)

## Introduction
With a large scale system like Oppia, we sometimes have features that contain several points of integration in the codebase, and/or require additional data priming or migrations ahead of the feature being released. These features often span multiple releases and thus require feature flags to gate integration points to ensure that the feature is not partially released ahead of schedule. Moreover, these features often require migrations which need to be run in specific releases due to new versions being made in irreversible data structures (e.g. explorations).
With a large scale system like Oppia, we sometimes have features that contain several points of integration in the codebase, and/or require additional data priming or migrations ahead of the feature being released. These features often span multiple releases and thus require **feature flags** to gate integration points to ensure that the feature is not partially released ahead of schedule. Moreover, these features often require migrations which need to be run in specific releases due to new versions being made in irreversible data structures (e.g. explorations). In order to release these types of features smoothly, we need to be able to put these features behind feature flags that are enabled in specific builds (compile-time) and can be enabled dynamically (at runtime).

In order to release these types of features in a smooth manner, we need to be able to put these features behind feature flags that are enabled in specific builds (compile-time) and can be enabled dynamically (at runtime). Thus it actually involves introducing what are called platform parameters. These are parameters that can be one of several data types (e.g. strings, integers, booleans). We use boolean types for gating features as described above, but the other parameters are essential in order to ensure the app is reasonably configurable for many different circumstances (include deprecations).
Through the platform parameter system in Oppia Android, we can model both feature flags and a more general version called platform parameters:

- **Feature flags** have boolean values. We use these for turning features on or off, as described above. (If a feature can't be done by the time of PR check-in, it should be behind a feature flag.) Please also see the wiki page [Guidelines for launching new features](https://github.com/oppia/oppia-android/wiki/Guidelines-for-launching-new-features) for more information on releasing new features using feature flags. Note that any new Android feature flags will also need to be implemented on Web so that they can be configured remotely.

- **Platform parameters** can have one of several data types (e.g. strings, integers, booleans). These are used to ensure the app is configurable for different circumstances. They should be used for configurable values that need to be changed at runtime without a re-release of the app, or for different app flavours for experimentation. Any new platform parameters need to be approved by the Android tech lead (e.g. Ben).


## How to create a Feature Flag or Platform Parameter

**Note:** Most of this section describes the implementation of platform parameters. Feature flags are a special case of platform parameters, and can be implemented in a similar way.

## How to create a Platform Parameter
1. Create the Constants
- If the Platform Parameter you intend to create is related to a particular feature, so first check that do there exist a file in the `utility\src\main\java\org\oppia\android\util\platformparameter` which contains other Platform Parameters related to the same feature. If there is no such then create a new Kotlin file along with its name corresponding to the feature.
- New feature flags should go in [FeatureFlagConstants.kt](https://github.com/oppia/oppia-android/blob/develop/utility/src/main/java/org/oppia/android/util/platformparameter/FeatureFlagConstants.kt).
- **Note:** Previously, platform parameters and feature flags were mixed together in `utility/src/main/java/org/oppia/android/util/platformparameter`. We are now planning to handle feature flags and platform parameters separately.
- After searching/making a "constants" file related to a feature, we need to define three things:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe replace with:

In the PlatformParameterConstants.kt or FeatureFlagConstants.kt file as appropriate,

Since we do not create any new files besides the two.

1. Qualifier Annotation which will help us to distinguish our Platform Parameter from others.

<br>

```
Expand All @@ -29,8 +38,8 @@ In order to release these types of features in a smooth manner, we need to be ab
annotation class SyncUpWorkerTimePeriodInHours
```

2. The name of the Platform Parameter in String format
2. The name of the Platform Parameter in String format

<br>

```
Expand All @@ -54,7 +63,7 @@ In order to release these types of features in a smooth manner, we need to be ab
```

2. Provide your Platform Parameter
- For providing your Platform Parameter in the App, we need to first make a @Provides annotated method in the `PlatformParameterModule(domain\src\main\java\org\oppia\android\domain\platformparameter\PlatformParameterModule.kt)`
- For providing your Platform Parameter in the App, we need to first make a @Provides annotated method in the `PlatformParameterModule(domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterModule.kt)`
- The return type for this @Provides annotated method will be equal to either `PlatformPrameterValue\<String\>`, `PlatformPrameterValue\<Integer\>` Or `PlatformPrameterValue\<Boolean\>` depending on the data type of the Platform Parameter you intend to create. Any other type will cause the platform parameter sync to fail. For eg- here we provide `SyncUpTimePeriodInHours` platform parameter, which is actually of integer type.

<br>
Expand Down Expand Up @@ -83,7 +92,8 @@ Note: If the Platform Parameter that you are creating will only be a Compile Tim
- Note that permission will be required before accessing the Feature Gating console in the Oppia backend.


## How to consume a Platform Parameter
## How to consume a Feature Flag or Platform Parameter

To consume a Platform Parameter in any file, we need to inject the specific `PlatformParameterValue\<T\>` instance along with the Qualifier Annotation defined for that Parameter. For eg - we are injecting the `SyncUpTimePeriodInHours` platform parameter in `PlatformParameterSyncUpWorkManagerInitializer`

```
Expand All @@ -99,11 +109,11 @@ class PlatformParameterSyncUpWorkManagerInitializer @Inject constructor(
}
```

## How to write tests related Platform Parameter
Before writing a test we must understand the purpose of the platform parameter in our class/classes (that needs to be tested). After verifying this we can divide testing procedures into following groups -
## How to write tests related to Platform Parameters
Before writing a test we must understand the purpose of the platform parameter in our class/classes (that needs to be tested). After verifying this we can divide testing procedures into following groups -

### 1. We actually don't test for platform parameter(s)
We just need specific platform parameter(s) in the dagger graph because our class needs it, but our test cases are not actually verifying the behaviour of class based on different values of the platform parameter. These are the simplest cases to write tests for. We will only need to create a `TestModule` inside the Test class and then include this into the @Component for the `TestApplicationComponent`. For eg -
We just need specific platform parameter(s) in the dagger graph because our class needs it, but our test cases are not actually verifying the behaviour of class based on different values of the platform parameter. These are the simplest cases to write tests for. We will only need to create a `TestModule` inside the Test class and then include this into the @Component for the `TestApplicationComponent`. For eg -

```
@Module
Expand Down Expand Up @@ -131,7 +141,7 @@ interface TestApplicationComponent {
```

### 2. We test for different values of platform parameter(s)
We need to test the behaviour of the target class/classes based on different values of the platform parameter. Same platform parameter can have different values because of the difference between its compile-time/default and runtime/server value. To test for this case we can set up a fake singleton class and provide the seed values that we want to be injected into target classes. For eg -
We need to test the behaviour of the target class/classes based on different values of the platform parameter. Same platform parameter can have different values because of the difference between its compile-time/default and runtime/server value. To test for this case we can set up a fake singleton class and provide the seed values that we want to be injected into target classes. For eg -
```
@Test
fun testSyncUpWorker_checkIfServerValueOfSyncUpTimePeriodIsUsed(){
Expand Down
Loading
Loading