Rapid Deployment of New Subscription Billing with Flag-Driven Development

Written by: dgiordano

As software developers, we are tasked with the challenge of telling the future. When we release a new feature, there should be little to no surprises - although many times this is not the case. We try to minimize surprises through various practices - discovery, grooming, code review, and test coverage, etc. Unfortunately, these take a lot of energy, effort, but more importantly - time. Releasing a new feature can take weeks, and have unfortunate side-effects like gigantic feature branches that conflict with your controller branch. A worse side-effect: losing a customer because you didn’t have a feature ready in time. In this post, I’ll outline how flag-driven development (FDD) can be:

  • A useful mental model to help reduce the time from idea to execution to deployment

  • FDD works very well with features that are defined by life-cycle states that tie to different audiences

  • FDD can manifest into rapid deployment of a feature

Subscription billing

More recently at CloudBees Feature Management I was tasked with building subscription billing for our customers. The goal was to make touchless subscription sign-ups available as quickly as possible using a Recurly integration. It got me thinking about the scope of work in terms of the life cycle of a subscription. There are different states that define the life of a subscription. Here is the state diagram of the subscription life-cycle in CloudBees Feature Management:

The rectangles are different states, the rounded rectangles are actions that lead to other states (buttons on the UI). The solid lines are state transitions and the dotted lines are options based on the subscription state. In a Trial state, the only option you have is Signup which will transition you to an Active state. The above state diagram represents the full feature of subscription billing. Getting to this point in the real world will take time and effort. Discovery, grooming, and testing (and perhaps large feature branches) this entire feature is not conducive to rapid deployment. Breaking it down into smaller, functional components and feature flagging them is conducive to rapid deployment - as well as avoiding large feature branches.

Using FDD to Re-Define Scope

FDD forces you to break down a feature into smaller, functional pieces that are easier to think about. Let’s look at how it would like if we just focused on the “Active life-cycle” portion:

With FDD we can narrow the scope down to just the active state of a subscription. Canceled, expired, reactivation and renewal are all states and actions that we don’t have to think about for now.

However we still want to give the impression that the user can still modify their subscription - this is where the notions of a base case/fallback method come in.

Base Case / Fallback Method

With FDD, you have to think about your various states, but it’s important that all states have the option of leading to a base state that is always static, non-changing. You can think of this as your “fallback method”.

With subscription billing, the “base” state for our subscription billing feature was just a Zendesk dialogue. The customer would click on “Change Subscription” or “Change Billing” and that would trigger a live-chat support pop-up. They would then engage with customer support and support would be able to perform these changes manually through the Recurly dashboard. Let’s see how this looks like in our state diagram:

Using our feature flags, we can now divert all active subscriptions to the “base case” which is our live-chat pop-up.

Feature Flag Code

Let’s see how this looks like in the code using the CloudBees Feature Management SDK:

1    const activeSubscriptionFlags = {
2       enableSubscriptionCancellation: new Rox.Flag(false),
3       enableSubscriptionUpgrade: new Rox.Flag(false),
4       enableSubscriptionDowngrade: new Rox.Flag(false)
5    }
6    // Register the flags
7    Object.keys(activeSubscriptionFlags).forEach((key) => Rox.register(‘MyApp’[c], key))
8
9     // Customer properties allow us to change the behavior of the flag based on the subscription state
10  Rox.setCustomStringProperty('recurlySubscription.state', () => this[d].state.recurlySubscription.state)

In the above, we are:

  • Defining feature flags for all of our Actions associated with the Active state of the subscription. We are passing in false, so the flags return false by default, so by default, they will use our fallback method.

  • Registering the flags with CloudBees Feature Management

  • Using a Customer Property to let our flags know the state of our subscription

Our view code might look something like this:

handleCancelClick = () => {

     if (activeSubscriptionOptions.enableSubscriptionCancellation.isEnabled()) {

         // Todo: handle subscription cancellations!

     } else {

          LiveChat('Hello, I would like to cancel my plan.') // Base state

      }

}


handleUpgradeClick = () => {

     if (activeSubscriptionOptions.enableSubscriptionUpgrade.isEnabled()) {

         // Todo: handle subscription upgrades!

     } else {

          LiveChat('Hello, I would like to upgrade my plan.')

     }

}


handleDowngradeClick = () => {

     if (activeSubscriptionOptions.enableSubscriptionDowngrade.isEnabled()) {

         // Todo: handle subscription downgrades!

     } else {

          LiveChat('Hello, I would like to downgrade my plan type.')

     }

}

This approach allows us to rollout one feature at a time to subscriptions in the Active state.

Example: Implementing Cancellation

Let’s see how it looks like when we are ready to implement cancelation:

handleCancelClick = () => {

     if (activeSubscriptionOptions.enableSubscriptionCancellation.isEnabled()) {

         cancelSubscription() // Assume this makes a Recurly API cancellation call

     } else {

          LiveChat('Hello, I would like to cancel my plan.') // Base state

      }

}

Now that we have cancellation implemented, we can configure the feature flag to return true only for Active subscribers in the CloudBees Feature Management dashboard. Under “Target Groups” we can define a new target group / audience called “Recurly active subscribers” using our customStringProperty that we’ve set up on our code - it will match any subscription object that has a state of “active”.

In our flag experiment, we can match the “enable subscription cancellation” feature flag to only return true if our target group matches “Recurly active subscribers”.

Now we can roll out subscription cancellation without ever having to think about upgrades, downgrades, expiration, reactivation or any other states or state transitions! We can then do the same for the enableSubscriptionUpgrade and enableSubscriptionDowngrade flags - allowing for rapid deployment of these features. We can even go as far as limiting the flag to be enabled for specific users in production, such as QA engineers, engineering directors, or whomever[e].

Conclusions:

  • Using Flag-driven development (FDD) as a mental model, we can take a complicated feature and break it down into smaller, more functional components.

  • Thinking of a “base case” or “fallback method” allows us to build in a static solution that gives users the impression that they still have access to a full feature

  • Using target groups and feature flags, we can implement and roll out one component at a time, and expose it to the audience of our liking with very low risk. No need for large feature branches.

Stay up to date

We'll never share your email address and you can opt out at any time, we promise.