Slide 1

Slide 1 text

ڊେͳΞϓϦ։ൃΛࢧ͑Δϑϥά؅ཧज़
 Flag-based feature management for a large scale application Abstract in three lines: 1. Branches are hard to manage in a large application 2. Enables/disables features in a property file 3. Implemented as a Gradle plugin Slides: in English
 Talk: in Japanese

Slide 2

Slide 2 text

Flag-based feature management for a large scale application Munetoshi Ishikawa

Slide 3

Slide 3 text

What I’d Like to Say For a large scale application ‣ Hard to manage lots of feature branches ‣ Hard to conduct “large” refactoring

Slide 4

Slide 4 text

What I’d Like to Say For a large scale application ‣ Hard to manage lots of feature branches ‣ Hard to conduct “large” refactoring Go with feature flags, no more feature branches

Slide 5

Slide 5 text

Topics ‣ Pain of feature branches ‣ Overview of feature flags ‣ Flag implementation ‣ Limitations and considerations

Slide 6

Slide 6 text

Topics ‣ Pain of feature branches ‣ Overview of feature flags ‣ Flag implementation ‣ Limitations and considerations

Slide 7

Slide 7 text

Feature Branch Example: git-flow, GitHub flow main branch (trunk, master, develop…)

Slide 8

Slide 8 text

Feature Branch Example: git-flow, GitHub flow feature branch main branch (trunk, master, develop…)

Slide 9

Slide 9 text

Feature Branch Example: git-flow, GitHub flow feature branch pull request main branch (trunk, master, develop…)

Slide 10

Slide 10 text

Feature Branch Example: git-flow, GitHub flow feature branch pull request main branch (trunk, master, develop…)

Slide 11

Slide 11 text

Case of Large Scale Application Lots of branches for a large scale application main branch feature branch

Slide 12

Slide 12 text

What’s the Problem? Conflicts, conflicts, conflicts… - Commonly used code updated, like an API/library - Project wide refactoring, like package renaming feature development main branch …

Slide 13

Slide 13 text

What’s the Problem? Conflicts, conflicts, conflicts… - Commonly used code updated, like an API/library - Project wide refactoring, like package renaming feature development main branch … refactoring

Slide 14

Slide 14 text

What’s the Problem? Conflicts, conflicts, conflicts… - Commonly used code updated, like an API/library - Project wide refactoring, like package renaming feature development main branch … refactoring Conflict!

Slide 15

Slide 15 text

How to Resolve Conflicts 1/2 Merge refactoring first feature development main branch …

Slide 16

Slide 16 text

How to Resolve Conflicts 1/2 Merge refactoring first feature development main branch … refactoring

Slide 17

Slide 17 text

How to Resolve Conflicts 1/2 Merge refactoring first feature development main branch … refactoring resolve

Slide 18

Slide 18 text

How to Resolve Conflicts 1/2 Merge refactoring first Many branches are required to resolve conflicts feature development main branch … refactoring resolve

Slide 19

Slide 19 text

How to Resolve Conflicts 2/2 Merge feature development first feature development main branch …

Slide 20

Slide 20 text

How to Resolve Conflicts 2/2 Merge feature development first feature development main branch … refactoring

Slide 21

Slide 21 text

How to Resolve Conflicts 2/2 Merge feature development first feature development main branch … refactoring resolve

Slide 22

Slide 22 text

How to Resolve Conflicts 2/2 Merge feature development first feature development main branch … refactoring resolve 1. The conflict becomes large

Slide 23

Slide 23 text

How to Resolve Conflicts 2/2 Merge feature development first feature development main branch … refactoring 1. The conflict becomes large 2. No chance to resolve with “all” branches resolve

Slide 24

Slide 24 text

How to Resolve Conflicts 2/2 Merge feature development first feature development main branch … refactoring 1. The conflict becomes large 2. No chance to resolve with “all” branches resolve Conflict!

Slide 25

Slide 25 text

How to Minimize the Conflicts Apply each “halfway” update to each other - Merge before finishing the implementation - Merge before fixing the release plan

Slide 26

Slide 26 text

How to Minimize the Conflicts Apply each “halfway” update to each other - Merge before finishing the implementation - Merge before fixing the release plan Guard a “halfway” feature by a boolean flag

Slide 27

Slide 27 text

Topics ‣ Pain of feature branches ‣ Overview of feature flags ‣ Flag implementation ‣ Limitations and considerations

Slide 28

Slide 28 text

Feature Flag Concept ‣ Merge any pull request to the main branch main branch pull request

Slide 29

Slide 29 text

Feature Flag Concept ‣ Merge any pull request to the main branch ‣ Enable/disable a feature with a property file main branch pull request flag property file FEATURE_STICKER=ENABLED FEATURE_THEME=DISABLED

Slide 30

Slide 30 text

Feature Flag Usage Example Case1: Replace presentation logic presenter = if (BuildConfig.FEATURE_STICKER) NewPresenter() else OldPresenter()


Slide 31

Slide 31 text

Feature Flag Usage Example Case1: Replace presentation logic presenter = if (BuildConfig.FEATURE_STICKER) NewPresenter() else OldPresenter()
 Case2: Change view visibility button.isVisible = BuildConfig.FEATURE_THEME

Slide 32

Slide 32 text

What a Feature Flag Realizes ‣ Minimizes conflicts ‣ Separates release schedule from the development progress ‣ Disables features if critical issues found on the release branch ‣ Shares the halfway features to other members

Slide 33

Slide 33 text

What a Feature Flag Realizes ‣ Minimizes conflicts ‣ Separates release schedule from the development progress ‣ Disables features if critical issues found on the release branch ‣ Shares the halfway features to other members

Slide 34

Slide 34 text

Conflict Minimization Apply a project wide update to halfway features main branch feature PR

Slide 35

Slide 35 text

Conflict Minimization Apply a project wide update to halfway features main branch feature PR refactoring PR

Slide 36

Slide 36 text

Conflict Minimization Apply a project wide update to halfway features main branch feature PR refactoring PR Conflict

Slide 37

Slide 37 text

Conflict Minimization Apply a project wide update to halfway features - Forget about ongoing refactoring once a pull request is merged main branch feature PR refactoring PR Conflict saved PRs

Slide 38

Slide 38 text

What a Feature Flag Realizes ‣ Minimizes conflicts ‣ Separates release schedule from the development progress ‣ Disables features if critical issues found on the release branch ‣ Shares the halfway features to other members

Slide 39

Slide 39 text

Release schedule Feature PRs can be merged before creating target release branch main branch release branch 1.0 2.0

Slide 40

Slide 40 text

Release schedule Feature PRs can be merged before creating target release branch main branch release branch 1.0 2.0 feature “STICKER” PRs (for 2.0)

Slide 41

Slide 41 text

Release schedule Feature PRs can be merged before creating target release branch main branch release branch 1.0 2.0 feature “STICKER” PRs (for 2.0) STICKER=DISABLED STICKER=ENABLED

Slide 42

Slide 42 text

Release schedule Feature PRs can be merged before creating target release branch main branch release branch 1.0 2.0 feature “STICKER” PRs (for 2.0) STICKER=DISABLED STICKER=ENABLED

Slide 43

Slide 43 text

What a Feature Flag Realizes ‣ Minimizes conflicts ‣ Separates release schedule from the development progress ‣ Disables features if critical issues found on the release branch ‣ Shares the halfway features to other members

Slide 44

Slide 44 text

Feature Disabling 1/2 Hard to revert after resolving conflict main branch release branch feature branch

Slide 45

Slide 45 text

Feature Disabling 1/2 Hard to revert after resolving conflict main branch release branch problem found feature branch

Slide 46

Slide 46 text

Feature Disabling 1/2 Hard to revert after resolving conflict main branch release branch refactoring PR problem found feature branch

Slide 47

Slide 47 text

Feature Disabling 2/2 Just turn off problematic features problem found main branch release branch feature PR refactoring PR

Slide 48

Slide 48 text

Feature Disabling 2/2 Just turn off problematic features STICKER=ENABLED DISABLED → problem found main branch release branch feature PR refactoring PR

Slide 49

Slide 49 text

What a Feature Flag Realizes ‣ Minimizes conflicts ‣ Separates release schedule from the development progress ‣ Disables features if critical issues found on the release branch ‣ Shares the halfway features to other members

Slide 50

Slide 50 text

Halfway Feature Sharing ‣ Code is visible for other developers - For a related or dependent feature development ‣ A feature is deployable for a demo and testing - For project owners, testers, and designers

Slide 51

Slide 51 text

Topics ‣ Pain of feature branches ‣ Overview of feature flag ‣ Flag implementation ‣ Limitations and considerations

Slide 52

Slide 52 text

Implementation Options ‣ Gradle plugin and BuildConfig ‣ Build variants ‣ Annotation processing ‣ Reflection

Slide 53

Slide 53 text

Implementation Options ‣ Gradle plugin and BuildConfig ‣ Build variants ‣ Annotation processing ‣ Reflection

Slide 54

Slide 54 text

Key Idea of Implementation 1. Create a Java property file 2. Write the property content to BuildConfig FEATURE_STICKER=true
 FEATURE_THEME=false feature_flag.properties

Slide 55

Slide 55 text

Key Idea of Implementation 1. Create a Java property file 2. Write the property content to BuildConfig Properties()... .forEach { (key, value) -> variant .buildConfigField("boolean", "$key", "$value") } FeatureFlagPlugin.kt (Gradle plugin)

Slide 56

Slide 56 text

Key Idea of Implementation 1. Create a Java property file 2. Write the property content to BuildConfig public static final boolean FEATURE_STICKER = true; public static final boolean FEATURE_THEME = false; BuildConfig.java (output)

Slide 57

Slide 57 text

Requirements ‣ IDE warnings workaround ‣ Flag control by build variants ‣ Flag control by release version ‣ Runtime flag modification

Slide 58

Slide 58 text

Requirements ‣ IDE warnings workaround ‣ Flag control by build variants ‣ Flag control by release version ‣ Runtime flag modification

Slide 59

Slide 59 text

IDE Warnings Warning for a conditional branch with a boolean constant if (BuildConfig.FEATURE_STICKER) {

Slide 60

Slide 60 text

IDE Warning Workaround Call `Boolean.valueOf(...)` for a debug build 
 val valueString = "Boolean.valueOf($value)" FeatureFlagPlugin.kt (Gradle plugin)

Slide 61

Slide 61 text

IDE Warning Workaround Call `Boolean.valueOf(...)` for a debug build (Use a literal for release to help ProGuard) val isRelease = variant.buildType.name == "release"
 val valueString = if (isRelease) "$value" else "Boolean.valueOf($value)" FeatureFlagPlugin.kt (Gradle plugin)

Slide 62

Slide 62 text

Requirements ‣ IDE warnings workaround ‣ Flag control by build variants ‣ Flag control by release version ‣ Runtime flag modification

Slide 63

Slide 63 text

Flag Control by Build Variants Enable with debug while disable with release # Enabled only for debug FEATURE_STICKER=debug # Enabled for both of debug and release FEATURE_THEME=release feature_flag.properties

Slide 64

Slide 64 text

Implementation of Control by Variants 1/2 const val DEBUG_NAME = "debug" const val RELEASE_NAME = "release" val BUILD_TYPE_TO_FLAG_LIST_MAP = mapOf( DEBUG_NAME to arrayOf(DEBUG_NAME, RELE...), RELEASE_NAME to arrayOf(RELEASE_NAME) ) FeatureFlagPlugin.kt (Gradle plugin)

Slide 65

Slide 65 text

Implementation of Control by Variants 2/2 val buildTypeName = variant.buildType.name FeatureFlagPlugin.kt (Gradle plugin)

Slide 66

Slide 66 text

Implementation of Control by Variants 2/2 val buildTypeName = variant.buildType.name val availableFlagValues = BUILD_TYPE_TO_FLAG_LIST_MAP[buildTypeName] .orEmpty() FeatureFlagPlugin.kt (Gradle plugin)

Slide 67

Slide 67 text

Implementation of Control by Variants 2/2 val buildTypeName = variant.buildType.name val availableFlagValues = BUILD_TYPE_TO_FLAG_LIST_MAP[buildTypeName] .orEmpty() val isEnabled = availableFlagValues.contains(flagValue) // WHERE, flagValue is "debug" or "release" FeatureFlagPlugin.kt (Gradle plugin)

Slide 68

Slide 68 text

Requirements ‣ IDE warnings workaround ‣ Flag control by build variants ‣ Flag control by release version ‣ Runtime flag modification

Slide 69

Slide 69 text

Flag Control by Release Version 1/2 May forget to enable before creating the target release branch main branch release branch 1.0 2.0 feature “STICKER” PRs (for 2.0) STICKER=debug STICKER=release

Slide 70

Slide 70 text

Flag Control by Release Version 1/2 May forget to enable before creating the target release branch main branch release branch 1.0 2.0 feature “STICKER” PRs (for 2.0) STICKER=debug STICKER=release

Slide 71

Slide 71 text

Flag Control by Release Version 2/2 Reserve target release version as soon as release schedule fixed main branch release branch 1.0 2.0 feature “STICKER” PRs (for 2.0) STICKER=debug,release:since_2.0

Slide 72

Slide 72 text

Implementation of Control by Version val tokenizedFlag = flag.split(":since_")
 ... FeatureFlagPlugin.kt (Gradle plugin)

Slide 73

Slide 73 text

Implementation of Control by Version val tokenizedFlag = flag.split(":since_")
 ... val sinceVersion = VersionNumber .parse(tokenizedFlag.getOrNull(1).orEmpty()) FeatureFlagPlugin.kt (Gradle plugin)

Slide 74

Slide 74 text

Implementation of Control by Version val tokenizedFlag = flag.split(":since_")
 ... val sinceVersion = VersionNumber .parse(tokenizedFlag.getOrNull(1).orEmpty()) availableFlagValues.contains(buildTypeName) && sinceVersion <= project.applicationVersion FeatureFlagPlugin.kt (Gradle plugin)

Slide 75

Slide 75 text

Bonus Points for Control by Version Easy to know when we can clean up the flag STICKER=...:since_2.0 main branch 2.0

Slide 76

Slide 76 text

Requirements ‣ IDE warnings workaround ‣ Flag control by build variants ‣ Flag control by release version ‣ Runtime flag modification

Slide 77

Slide 77 text

Runtime Flag Modification Make flags overridable in a debug menu for - Demoing - Debugging - Side-by-side testing

Slide 78

Slide 78 text

Implementation of Flag Modification 1/2 `buildConfigField` outputs `static final` (I don’t like Java’s reflection. Especially, sun.misc.Unsafe) variant.buildConfigField( "boolean", "$key_DUMMY", "$value " ) FeatureFlagPlugin.kt (Gradle plugin)

Slide 79

Slide 79 text

Implementation of Flag Modification 1/2 `buildConfigField` outputs `static final` (I don’t like Java’s reflection. Especially, sun.misc.Unsafe) variant.buildConfigField( "boolean", "$key_DUMMY", "$value; " ) FeatureFlagPlugin.kt (Gradle plugin)

Slide 80

Slide 80 text

Implementation of Flag Modification 1/2 `buildConfigField` outputs `static final` (I don’t like Java’s reflection. Especially, sun.misc.Unsafe) variant.buildConfigField( "boolean", "$key_DUMMY", "$value; public static boolean $key = $value" ) FeatureFlagPlugin.kt (Gradle plugin)

Slide 81

Slide 81 text

Implementation of Flag Modification 1/2 `buildConfigField` outputs `static final` (I don’t like Java’s reflection. Especially, sun.misc.Unsafe) public static final boolean FEATURE_STICKER_DUMMY = true; public static boolean FEATURE_STICKER = true; BuildConfig.java (output)

Slide 82

Slide 82 text

Implementation of Flag Modification 2/2 Make overridable flag accessible without reflection - Create a flag list with supplier/consumer pair

Slide 83

Slide 83 text

Implementation of Flag Modification 2/2 Make overridable flag accessible without reflection - Create a flag list with supplier/consumer pair val codeBuilder = StringJoiner("\n", "new HashMap<>(){{", "}}") flags.forEach { key -> codeBuilder .add("""put("$key", new Pair<>(() -> $key, (b) -> $key = b));""") } variant.buildConfigField( "Map>", "FEATURE_FLAG_ACCESSOR_MAP", codeBuilder.toString() ) FeatureFlagPlugin.kt (Gradle plugin, pseudocode)

Slide 84

Slide 84 text

Implementation of Flag Modification 2/2 Make overridable flag accessible without reflection - Create a flag list with supplier/consumer pair public static final Map<...> FEATURE_FLAG_ACCESSOR_MAP = new HashMap<>() {{ put( "FEATURE_STICKER", ) ... BuildConfig.java (output, pseudocode)

Slide 85

Slide 85 text

Implementation of Flag Modification 2/2 Make overridable flag accessible without reflection - Create a flag list with supplier/consumer pair public static final Map<...> FEATURE_FLAG_ACCESSOR_MAP = new HashMap<>() {{ put( "FEATURE_STICKER", new Pair<>( () -> FEATURE_STICKER, (b) -> FEATURE_STICKER = b) ) ... BuildConfig.java (output, pseudocode)

Slide 86

Slide 86 text

Topics ‣ Pain of feature branches ‣ Overview of feature flag ‣ Flag implementation ‣ Limitations and considerations

Slide 87

Slide 87 text

Appropriate Cases of Feature Flags ‣ Easy to apply a feature flag - Replace a class, layout file, or value ‣ Hard to apply a feature flag - Update API, library, or SDK version ‣ Enough to use a branch - Make a prototype or trial implementation

Slide 88

Slide 88 text

Considerations for Pull Requests ‣ Keep each pull request small ‣ Think about the order/structure of pull requests eg., create a skeleton class → implement the logic

Slide 89

Slide 89 text

Considerations for Release Testing ‣ Regression tests required for halfway feature ‣ Hard to guarantee the flag completeness - Might be revealed partially

Slide 90

Slide 90 text

Summary ‣ Pain of feature branches ‣ Overview of feature flag ‣ Flag implementation ‣ Limitations and considerations

Slide 91

Slide 91 text

Summary ‣ Pain of feature branches ‣ Overview of feature flag ‣ Flag implementation ‣ Limitations and considerations

Slide 92

Slide 92 text

Summary ‣ Unmanageable conflicts ‣ Overview of feature flag ‣ Flag implementation ‣ Limitations and considerations

Slide 93

Slide 93 text

Summary ‣ Unmanageable conflicts ‣ Overview of feature flag ‣ Flag implementation ‣ Limitations and considerations

Slide 94

Slide 94 text

Summary ‣ Unmanageable conflicts ‣ Good for release process / information sharing ‣ Flag implementation ‣ Limitations and considerations

Slide 95

Slide 95 text

Summary ‣ Unmanageable conflicts ‣ Good for release process / information sharing ‣ Flag implementation ‣ Limitations and considerations

Slide 96

Slide 96 text

Summary ‣ Unmanageable conflicts ‣ Good for release process / information sharing ‣ Extendible Gradle plugin implementation ‣ Limitations and considerations

Slide 97

Slide 97 text

Summary ‣ Unmanageable conflicts ‣ Good for release process / information sharing ‣ Extendible Gradle plugin implementation ‣ Limitations and considerations

Slide 98

Slide 98 text

Summary ‣ Unmanageable conflicts ‣ Good for release process / information sharing ‣ Extendible Gradle plugin implementation ‣ Feature flags are not silver bullets

Slide 99

Slide 99 text

References ‣ GitLab flow - https://docs.gitlab.com/ee/workflow/gitlab_flow.html ‣ Trunk based development - https://trunkbaseddevelopment.com/

Slide 100

Slide 100 text

Thank you for listening