Have you ever had the delicate problem where you've made a new feature that the customer doesn't wish to activate quite yet? What do you do? One option is of course to comment out the sections and commit the source change. Another is to never commit the feature to version control until the customer has verified that he/she wants it in production.
In the end, probably the best way to do it is to incorporate the code into production code but with a small boolean making sure that the function isn't active. That way the code doesn't have to be maintained separately and any bug introduced by new code should also be discovered quite early.
This method is usually the recommended in projects with short sprints or even continous delivery.
So, trying to apply this in a real world scenario (i.e. the portal I'm working on) one gets frustrated in one or all of these scenarios
- Customer wishes to review the function, just as you added the if (false)-statement and deployed
- Customer also wishes activate the function in production environment, but no down-time (even 1 minute for redeployment of code) is accepted
- Customer didn't really want the function - remove it again! (or statement somewhat differently: there's a bug!)
We needed a way to be able to feature toggle functions without the hassle of redeployment, compiling of code or changing portal-ext.properties and restarting the servers!
Feature Toggle Portlet
So, the requirements are pretty simple. Make a portlet that can answer one simple question: "Is feature XYZ enabled?"
It should also be able to change the state of a feature, back and forth.
Happy coding days
With this in mind, happy coding days had arrived, get the keyboard and open up the favourite editor!
The service is very simple, it models a Feature that has feature key, a boolean state, a description and also, for scoping functionality, a group id.
The portlet needs to present the user with a UI where all features in the system are presented and with a way to toggle their state (active/inactive). The portlet needs to understand its scope and use global scope if shown inside control panel, otherwise use the group id of the site if added to a site layout. This way, a feature can be toggled to on state in one group, off in all others and not shown at all in the global scope.
Message bus API
Besides using the Service API there should be an easy way of asking the question "Is feature XYZ enabled", without worrying about class loader problems or CLP. Even the service API is binding the clients too tightly, any change in the service API would affect the client eventhough the client still just wants an answer to the same question, is the feature enabled?
Using message bus for this loosely coupled API seems like a neat idea, especially since the only coupling done is sharing a message destination string.
Going out to the bus each time we want to check a feature seems like wasting a lot of resources, especially when a feature is being used by all pages in the portal (yes we have a couple of those features), so a cache is a really good idea. Now, each plugin cannot implements its own message bus cache, or at least, we will soon have 20 duplicates of the same code. So, with the strength of Maven a common project with a helper that caches any previous responses (and clears the cache upon state change)
With the portlet in place and a helper class in a common library we're all set to use it from code in all plugins within the portal! Our portal consists of roughly 30 plugins, not all of them needs to feature toggle their code of course but once in place, it is surprising how often it is being used. At least for a short period of time!
The helper class isn't doing much magic, however it does cache any responses in a single vm pool. This cache value can be reset from other side of message bus as well, thus being able to invalidate the cache upon state change.
Once the portlet was written a lot of code got feature toggles, some because we wanted to try out new functionality in live environments but only by enabling it to a specific set of users/sites. We've also used it to enable/disable whole pages for our sites (~1500 sites) that are otherwise controlled by an active site template propagation. This way the user can choose if he/she wishes to use blog functionality (without necessarily having to access control panel).
Our code is mostly generic and could probably be used by others. There are some minor specific implementations. Give me a shout if you wish to see the code or use it!