Feature toggles allow us to do a lot of good things, like have continuous integration to be able to merge into development or main without needing to finish the development of a feature (i.e. even if the feature is not done, we can ship it as off while we finish), track how much usage our features are getting, quickly turn off changes in case the new feature has introduced regression issues, etc.
Now, the one thing about feature toggles is that they kind of force us to use a bunch of different if/else statements and if/else statements are not really ideal.
If you've ever had to debug code that has feature toggling logic, you probably know what I'm talking about. The code is pretty ugly, it is very difficult to test, and it's pretty much impossible to understand what are all of the different things that you have to turn on to be able to have the feature working.
So let's discuss techniques that we can use to help us out, let's talk about the nerdiest thing of all: patterns! Specifically I want to talk about the Null Object design pattern and how we can leverage it when introducing features that should be behind toggles.
If you are not familiar with it, the Null Object pattern is pretty much what it sounds, it's all about introducing objects that decide that the best thing to do is nothing.
The reason they are pretty nifty is because you can call them as if they did something, which means you won't have all of the branching logic, while at the same time they won't present any side effects.
So, let's see some code. Let's imagine that we are implementing a Wallet feature for Collect (or whichever other product). The requirements are the following:
The trick is that calculating the Wallet amount won't be so simple. This amount needs to be calculated from a mix of past purchases and their standing status (i.e. if they owe us a lot of money).
At the same time, our app is super popular so we have a lot of clients that we need to support (🤞), which means we are constantly fixing bugs and making improvements to the app. We want to keep shipping those to our clients regularly, even if the Wallet feature has not been completed.
Since our Wallet's amount will be calculated based on information coming from the Account object, that sounds like a perfect place to track whether the customer has a credit or not for their purchase.
We'll start by creating a Customer class, which will wrap an Account which is where we will be betting the information about the user’s credit and their payment history.
I got ahead of myself and added a getWallet method, which returns a Wallet object, which we don't have yet, so let's go ahead and create it:
Easy enough!
To keep things simple, our stakeholder is asking that when calculating the balance’s remaining amount for a payment, we should simply go ahead and always add the Wallet’s balance to the total. So if they have money in there, it will be credited, if they owe us money, it will be added to the balance.
So with that we can update our balance logic as follows:
Done, ship it!
But, wait a minute, I thought this blog post was all about the Null Object pattern?!?!
Oh, that's right! We can't ship this, we have to test this, validate it as a pilot/beta, and make sure it's up to our standards of quality. Let's back up.
First of all our Wallet implementation has a giant TODO. We've yet to even start with the most complex part of the problem!
So, knowing that it will take quite a while to implement the math for this project, let's add our Feature Toggle so we can ship the rest of the code safely while we finish the feature.
We'll start by creating a service to check if the toggle is on or not:
As you can see, I’m assuming that we are setting the toggle using base Salesforce, using Feature Management.
But hey, it’s just a boolean, we could just as easily implement a custom toggling solution ourselves through Custom Metadata.
Next thing is buying ourselves some precious, precious time, by being able to turn on or off the wallet feature without introducing regressions while we are working. To achieve this this we'll create the Null
Object implementation for the Wallet class, for which we'll need a bit of refactoring. Let's take it step by step:
At this point we still have one big step to take, which is putting it all together. Right now our Customer class is hardcoded to always use the same IWallet object. We know this is hardcoded because we are using the new keyword in there, which means we have no means of doing dependency injection. To fix this we have to do a bit more of refactoring, for which we'll use yet another design pattern to move that pesky new somewhere else, the Factory object:
And we will now build Wallets exclusively through the Factory
Now, we are done. We'll, at least we can ship it.
The point is to be able avoid any kind of branching code (if/else statements, switch cases) as much as possible. We implement the functionality as if the Wallet feature is always on, which makes the code where we calculate the balance pretty simple.
We also keep our responsibilities in check (SRP is the 🐐 of the SOLID principles). It is not the responsibility of the balance calculator to know if that feature is on or not, so it should never have any kind of Feature Enablement logic in it! Nevertheless, that class will now respect the toggle even if it doesn’t know it, because when it is off the Wallet will always act as if the account has no money, just like it used to before this feature even existed.
Of course, this pattern won't solve all toggling problems. For instance, we (might) still have to make sure that things are correctly displayed/hidden in the UI. But this can at least help with the main domain logic, where the riskier part of the application lives.
And for much, much more about feature toggles/flags than you thought you would ever need, give this a read https://martinfowler.com/articles/feature-toggles.html.