Skip to content

Latest commit

 

History

History
72 lines (56 loc) · 3.12 KB

feature-flags.md

File metadata and controls

72 lines (56 loc) · 3.12 KB

Feature Flags

In WooCommerce Android, we use feature flags to allow us to merge in-progress features into trunk, while still allowing us to safely deliver builds for testing and production. It's mostly useful for features that require multiple PRs to ship, and may take some time to complete.

We currently do this through FeatureFlag.

FeatureFlag

The FeatureFlag enum contains a case for any in-progress features that are currently feature flagged. It contains a single method isEnabled. This determines whether the feature should currently be enabled. This is typically determined based on the current BuildConfig. Here are a couple of examples:

enum class FeatureFlag {
    SHIPPING_LABELS_M4,
    DB_DOWNGRADE,
    ORDER_CREATION,
    CARD_READER;

    /// Returns a boolean indicating if the feature is enabled
    fun isEnabled(context: Context? = null): Boolean {
        return when (this) {
            SHIPPING_LABELS_M4 -> PackageUtils.isDebugBuild() || PackageUtils.isTesting()
            DB_DOWNGRADE -> {
                PackageUtils.isDebugBuild() || context != null && PackageUtils.isBetaBuild(context)
            }
            ORDER_CREATION -> PackageUtils.isDebugBuild() || PackageUtils.isTesting()
            CARD_READER -> CardPresentEligibleFeatureChecker.isCardPresentEligible
        }
    }
}

Here, we have three features which will be active in the following circumstances:

  • SHIPPING_LABELS_M4 will only be enabled in debug builds and during tests until the feature is released.
  • DB_DOWNGRADE will only be enabled for debug / beta builds
  • CARD_READER will be enabled if the store is currently eligible for payments i.e when WooCommerce Payments plugin is available.

Putting it all together

The final step is to check the current status of a feature flag to selectively enable the feature within the app. For example, this might be displaying a button in the UI if a feature is enabled, or perhaps switching to a different code path in a service:

if (!FeatureFlag.SHIPPING_LABELS_M4.isEnabled()) {
    binding.expandIcon.isVisible = false
} else {
    binding.expandIcon.isVisible = true
}

Writing tests for logic that involves feature flag

When writing tests for logic that involves feature flags, it's important to note that we cannot directly use the enum constant, as shown above. Instead, we should create a wrapper class that can be easily mocked during testing.

class IsShippingLabelsEnabled @Inject constructor() {
    operator fun invoke(): Boolean = FeatureFlag.SHIPPING_LABELS_M4.isEnabled()
}
if (!isShippingLabelsEnabled()) {
    binding.expandIcon.isVisible = false
} else {
    binding.expandIcon.isVisible = true
}

Now, you can mock the IsCardPresentEligible class to write unit tests

whenever(isShippingLabelsEnabled.invoke()).thenReturn(true)

Once a feature is ready for release, please remove the feature flag and the old code path.