Feature flags are used in software to enable or disable different behaviors of an app. In a normal scenario, flags exist in the app until a feature goes live. At that point, the flag is enabled in test and production environments and can be safely removed.
But, what happens when an app needs behaviors to be enabled in one location but disabled in another? What happens when flags become dependent on each other due to their long lifespan? This combination of factors can arise during a project and create unnecessary complexity that could be avoided.
Part 1: No flag
We were building an app for a manufacturing company to use on the factory floor. In the early stages of the project, we worked without feature flags. This kept development fast and simple. There were fewer pesky if checks and the branching logic they entail. We were forced to add flagging when different factory sites needed different configurations. This created the first interaction of our feature flag structure.
Part 2: Factory Specific Flags
We already kept a record in our database for each factory location the app was used in. So we added a factoryConfig connected to each site’s ID. The initial table had some flags in it and looked like this:
Adding a Flag
Each column is a specific feature, and then each row is a different production site. This worked until we were asked, “Can you add a feature to track how often we sweep the floor when floor tasks are being performed?” Yes we could, but we didn’t want the new feature to be visible until we were ready. So we added another column to track how often the floor was swept. With the new feature flag, the table looks like so:
The flag was easy to add but now we have a problem. What happens if we enable the trackSweepingTheFloor flag but leave enbaleFloorTasks disabled? Like so:
What should the app do in this case? If we want to know if we should track floor sweeping, we need to check that the site has those tasks enabled in the first place. If the site isn’t doing floor tasks, then we shouldn’t do tracking even if the flag isn’t enabled.
Fixing the Problem
The code now needs to be built to handle this situation. If we put the combinations of enableFloorTasks and trackSweepingTheFloor in a matrix we get the following:
Where all four combinations need to be factored into the development of the feature to make sure we don’t get to an invalid page or cause a crash. However, only 3 combinations can be described as behaviors the user can see. Replacing the TRUEs and FALSEs in the above matrix we get:
The app should support the green cases but the red case hasn’t been asked for so it would be a waste of dev time to support an invalid case.
Part 3: Modeling the dependency
Since the trackSweepingTheFloor is dependent on the enableFloorTask flag then we should include that behavior in our data model the solution we went with was to create an enum where each valid state is represented as a name and replaced the overall flag with it. EnableFloorTasks can now have a value of “Off,” “Tasks without sweeping,” and “Tasks with sweeping.” In the location config table, it looks like so:
Now the app knows based on one config value what should and should not be displayed for a site. The enum flag also leaves room for expansion if other sites need other special behaviors.
Feature Flags aren’t always an on/off switch
As a project grows and has to support more stakeholders those stakeholders might want customized behaviors. Instead of trying to track which combination of behaviors each stakeholder wants through a series of on/off switches. Making a named group that represents each stakeholder may be easier to track and refine when developing and communicating about a feature.