As a developer, you want to be able to release code early and often. It helps you to get feedback from users, and it helps you avoid having to deal with large, complex Git merges. However, it’s not always so straightforward. What happens if you want to test a new feature you’ve been working on with a subset of users? Alternatively, maybe you want to hide a feature that isn’t quite ready for production or enable a feature for a specific user or team on a specific subscription level. This is where feature flags come in. They allow you toggle features in your applications based on given conditions. In this article, we’ll discuss what feature flags are, as well as the advantages and disadvantages of using them. We’ll also take a look at how to use feature flags in Laravel using the first-party Laravel Pennant package. We’ll then delve into testing feature flags in your Laravel applications. By the end of this article, you should have a good understanding of what feature flags are and feel confident enough to start using them in your own applications.
What are feature flags?
Feature flags, sometimes referred to as “switches” or “feature toggles”, are a concept in the software development world that involves toggling features on or off based on certain conditions at runtime. Generally, they allow you to programmatically toggle a feature without needing to redeploy an application. This might be based on a user’s subscription level or toggled by a user in an admin panel.
To provide an example of what a feature flag is, let’s say you’re building a blogging platform and working on a new social-sharing feature. This feature will allow users to share their blog posts directly to their social media platforms after publishing them. Imagine that you want to test it with a subset of users before releasing it to everyone. You could use a feature flag to toggle the feature on or off for a specific user or group of users. This means the feature will only be available to those users, and everyone else will not see it. After a phase of initial testing, if you’re happy with the stability of the feature, you can then release it to everyone.
The advantages of feature flags
To get a better understanding of what feature flags achieve, let’s take a look at some of the use cases and advantages of using them.
Enabling features for specific users or organizations
Imagine that you have a large enterprise application that offers three different features: email, chat, and video conferencing. You could use a feature flag for each of these features which only allows the users or organizations to access the features if they’re enabled. For example, you may have one organization using your application that only wants access to your email feature. Using feature flags in your code, you could hide the chat and video conferencing features from them. You might also want to add an admin panel that your sales team (or similar) can use to enable these features at a later date if the organization decides they want to use them. Similarly, if you have a software-as-a-service (SaaS) application that offers different subscription levels, you could use feature flags to enable or disable features based on the subscription level of the user. This means that as a user upgrades their subscription, they will automatically gain access to the features available at that level.
Another use case for feature flags is A/B testing. This is where you test two different versions of a feature to see which one performs better. For example, you might want to test two different versions of a landing page to see which one converts better. You could use a feature flag to determine which version of the page to show to the user. Using an A/B testing tool or analytics platform, you could then assess which version of the page is the more optimized version.
Complements trunk-based development
You’ll likely be used to using Git Flow as your branching strategy when working on projects. Let’s quickly recap the basics of Git Flow. This involves having a master (or sometimes called main) branch that contains all the production-ready code that has been released or is due to be released. There is also a develop branch, which tends to contain all the work in progress and will be implemented in the next release. There may also be a staging branch that contains the code currently in the staging environment, such as a separate server where the application can be tested before release. Typically, before each release, changes from the develop or staging branch (depending on whether a staging branch is being used) will be merged into a release branch (such as release/v1.1.0) that will then be merged into the master branch once the release is ready. Git Flow places a focus on “feature branches”. This is where you’ll create a new Git branch off the develop (or similar) branch called something like feature/auth-system. The feature branches typically focus on adding a particular feature and are only merged once the feature is production-ready and the team is comfortable with releasing it. These feature branches can exist for weeks or months, meaning that the code being added to them can become stale and out of date. This can lead to merge conflicts when you try to merge the feature branch back into the develop branch, which can be time-consuming and frustrating to deal with. In cases of large merge conflicts, it may also lead to bugs being introduced into the codebase if the conflicts aren’t dealt with correctly. Another issue is that if you have two separate feature branches, you have no idea how the changes implemented in each one will affect the other until they’ve been merged into the develop branch. This can then lead to the developers needing to make more changes to the code to fix any issues that arise from the merge. Another branching strategy that proposes a different approach to Git Flow is trunk-based development (TBD). TBD proposes that you should only have a single master branch that contains all the production-ready code. It emphasizes creating short-lived branches that are merged into the master branch as soon as possible. The branches don’t necessarily need to include an entire feature. Instead, they can be used to implement a small part of a feature. For example, let’s say you’re building a profile page your application. You might follow this type of workflow: Create a branch from master. Add a basic profile page that allows you to update your profile name. Merge back into master. Create a branch from master. Add the functionality to the profile page to update your profile picture. Merge back into master. Create a branch from master. Add functionality to the profile page to enable two-factor authentication. Merge back into master. And so on… As you can see, the branches are focused and short-lived. However, because the code is constantly being merged back into the master branch, it might be constantly leading to new code being released to production, even if the feature isn’t ready yet. This is where feature flags can really complement the TBD workflow. You could create a feature flag for a profile page that is disabled by default. This means you can merge as much profile page-related code into the master branch as you want, but the feature won’t be available to users until you’re confident that the profile page feature is production-ready. This means you can keep your branches short-lived and avoid merge conflicts and avoid having to release code that isn’t ready yet. As soon as the feature is complete, you can then enable the feature flag and release it to everyone. It’s worth noting that while TBD can be a good approach to take, it’s not necessarily suitable for all projects. It’s important to consider the pros and cons of each branching strategy and choose the one that’s right for your project. It requires you to have a very good-quality test suite and continuous integration (CI) process in place to reduce the chances of introducing bugs into your codebase or accidentally releasing code that isn’t ready yet without hiding it behind a feature flag.
The disadvantages of feature flags
Although feature flags can be useful,…