Recently, while working on a project at GoGrow we encountered a very common issue: ingesting data from various API’s. This is nothing out of the ordinary, and we were able to resolve it by mapping the data into models and storing them locally.
Everything was fine, until we started seeing errors in Bugsnag in the staging environment. This is because we did not implement a safeguard that makes the ingestion run only in the Production environment.
You may have seen something like this in your codebase:
This solution works and is easy to implement: add a single line on top of your method body and you’re good to go. However, after taking a closer look at it, you may come to realize that this implementation, although fast, is not that great. Now, you are adding environment-dependent code to it and may run into hard-to-debug issues in the future.
Enter feature flags
Feature Flags
Feature flags are a way to enable or disable certain features in your application at runtime. This can be useful for a number of reasons:
- You can use feature flags to test new features with a small group of users before rolling them out to everyone.
- Feature flags can help you roll back changes quickly if something goes wrong.
- You can use feature flags to customize the experience of groups of users.
Although there are many different implementations out there already (see the great flipper gem), they don’t always adapt 100% to your situation. This is why we decided to go with our own approach.
The requirements were the following:
- Have an easy way for the dev to toggle features on/off without doing any commits to the code.
- Be able to have a default logic for the feature flags but be able to override them in different environments.
- Have an easy way to test the features, with separate contexts for the possible states.
- We are not taking users/audiences into consideration for now, but we may want to add this capability in the future.
The solution we came up with was to have a default logic for the feature flags, but be able to override them with environment variables.
After thinking about this for a while, this is the interface we came up with:
We knew that to keep it dry, we would have to use a bit of metaprogramming to define singleton methods with the same name as the feature flag. Having these methods would help make code clearer and easier to understand.
This is the implementation
Now you can go ahead and create the feature flags you want at the bottom of the class and paste it into the app/services/feature.rb
. Overriding features in your environments should be as easy as adding an environment variable:
Use it in your code wherever with:
To override feature flags in your specs, simply add this to your specs:
That’s it! With this you are now ready to remove that pesky return unless Rails.env.production?
guard from your methods.