Building a Standard Interface for Feature Flags with Node.js
Feature flags are an essential part of many modern development processes. They help us decouple deployment from release and gain more control over user access, and they also allow us to toggle features on and off. However, just enabling feature flags is not enough. To have more predictable releases, it’s essential to create a standard process for how flags are used.
At LogDNA, feature flags were part of our software development life cycle (SDLC), but weren’t organized in a standard way, which led to confusion across the organization. For example, in our web application, there are multiple sources where feature flags originate. From there, they were combined into a single location in an unpredictable fashion. This made it hard to know what took precedence and what overrides existed.
On the backend, feature flags were tacked onto account records from the database or even stored in environment variables. Storing flags in the database introduces complexity and risk — the exact thing you’re trying to avoid— when flags need updating because you must manually modify a production database or use a tool to make updates in bulk. Additionally, flags that are left lying around after they’re no longer needed can cause confusion.
To solve these pain points, we adopted LaunchDarkly. Now, we have a central home where all feature flags live and a standard process for rolling out new features. Not only can we pick out specific accounts to toggle a feature for, we can also target accounts based on specific properties.
For example, we support customers across a dozen IBM environments as well as our direct customers who are hosted on LogDNA infrastructure. There are cases where we want to toggle a feature only for IBM accounts or only for LogDNA accounts. LaunchDarkly even offers percentage rollouts, where only a certain percent of accounts see a new feature. This facilitates A/B testing and can help with reducing impact if there’s an issue with a new feature. With LaunchDarkly, features can be disabled for accounts without requiring a rollback, which can be helpful during incidents. Alternatively, if a feature rolls out smoothly to a percentage of accounts, we can easily bump it up so that all accounts see the feature.
Adopting LaunchDarkly has helped us move faster, experiment more and mitigate risk. However, we wanted to push its capabilities even further by creating a standardized interface for interacting with the service.
The Node.js Client
Since our codebase is written in Node.js, we decided to build a wrapper around LaunchDarkly’s node-server-sdk. We wanted it to be able to read flags from LaunchDarkly, enforce some validation and format the user data before it’s sent to LaunchDarkly to request a feature flag.
The initial implementation of the client worked well, but we ended up running into some issues with it. During testing, we found that we needed to add a lot of code to capture the requests LaunchDarkly’s sdk client makes and to mock the results it receives. This was just too much effort and required vendor-specific logic just to get testing to work. Also, this would need to be duplicated in every project we wanted to add our client to. We had to go back to the drawing board and design the client in a way to make testing simple.
The feature flags client can now be dropped in anywhere in our codebase. It requires only a small amount of code to be added, and testing with it is relatively simple.
For the LogDNA engineering team, adopting LaunchDarkly was the first step in rolling out a robust feature flagging process. Standardizing our interface with the service with a custom-built Node.js client has taken this process to the next level, and now we’re deploying with more confidence than ever before.