If you’ve been following along with some of our posts, you would know that we absolutely love feature flags. As a developer, they give you so much confidence in rolling out updates to gating new feature rollouts.
Well, today we are here to talk about one more way you can utilize feature flags in your development workflows. We use LaunchDarkly extensively to gate new feature rollouts, whether it be in our application or our servers.
We also integrate directly with a few third-party APIs, such as Stripe and Shopify. Now each of these API providers have versioned APIs, which also means we have to regularly keep updating our servers to hit the latest API version. We were doing this manually till recently, and it would look something like the below:
return axios({
method: 'POST',
headers: {
'content-type': 'application/json',
accept: 'application/json',
'X-Shopify-Storefront-Access-Token': storeFrontToken,
},
data: { query, variables },
url: `https://${shopifyRoot}/api/2022-04/graphql.json`,
});
Notice the hard-coded API version in the URL in the snippet above. So of course, everytime we would upgrade the version, we would find all instances of that and replace it.
How can we make this better?
Well, the obvious thing to do would be to extract the version into a common constant or variable instead of it being hard-coded. So maybe it could look like the following:
import { apiVersion } from './constants/shopify';
// Lots of code skipped here
return axios({
method: 'POST',
headers: {
'content-type': 'application/json',
accept: 'application/json',
'X-Shopify-Storefront-Access-Token': storeFrontToken,
},
data: { query, variables },
url: `https://${shopifyRoot}/api/${apiVersion}/graphql.json`,
});
Well, this is already better. Our code is consolidated, and version updates are all centralized. But this still leaves us with one or two problems that we could solve:
- Version changes require a code change, commit and deploy.
- If things go wrong (which they often do at scale), rolling back can take time, which for a business can be costly.
Feature flags to the rescue
So, how do we make this even better?
The answer we’ve found is to use feature flags for controlling the API versioning as well. The way it works is to use a LaunchDarkly feature flag that controls the current version of the API, instead of a constant.
How does this work in LaunchDarkly? You can just use LaunchDarkly’s web-based configuration to:
- Create a new feature flag.
- Set the variations. In this case, it would be a string flag, with two variations, the old API version number and the new.
- Set the default variation which would be the old version to start with.
The below diagram illustrates how LaunchDarkly can work for you to make feature flags automated and changeable without any additional deployments at your end:
All of the above can be done completely from LaunchDarkly’s website. Once that is done, you can use LaunchDarkly’s SDK to pull in the value of the feature flag into your code. This might look something like the following:
const apiVersion = await flags.variation(
Features.features.ApiVersion,
{
key: Features.buildOrgIdKey({ orgId }),
},
'2022-04'
);
The above allows us to create an ApiVersion
feature flag that has a default value of 2022-04
. We can further target the rollout by specifying which customers we want to roll it out to by targeting customers with an orgId
.
This helps us do a few things:
- Change the API version completely from the LaunchDarkly configuration instead of having to make any code changes.
- Roll out the API version upgrades through a percentage rollout or specifically to a few customers before rolling it out fully.
We’ve started doing this for most of our dependent APIs which support versioning, and it’s been a major developer experience improvement in how we manage to version.
A Caveat
There is just one thing to be careful of when you use feature flags for API versions as above. Turns out, the defaults you set in your code for LaunchDarkly are used whenever there is any issue connecting to LaunchDarkly within a specified period of time.
What this meant for us is that in a very small percentage of requests (less than 0.5%), LaunchDarkly was not responding in time, and so it would fall back to the default we specified in code instead of in LaunchDarkly.
We ended up having phantom calls to an older version of the API because of this. Thus, it becomes important to change the default in code after a full rollout of a version update to avoid this problem.
Summary
In this post, we covered how you could migrate from code-based API versioning to using something like LaunchDarkly (or any other feature flag management tool) to setup, rollout and test API version upgrades. We also covered the advantages of setting it up this way, as well as one thing to watch out for once you are done with your rollout.
About me
Hi, I'm Shyam, and I lead Product Development at Builder.io
We make a way to drag + drop with your components to create pages and other CMS content on your site or app, visually.
You can read more about how this can improve your workflow here.
You may find it interesting and useful!
Visually build with your components
Builder.io is a headless CMS that lets you drag and drop with your components right within your existing site.
// Dynamically render your components
export function MyPage({ json }) {
return <BuilderComponent content={json} />
}
registerComponents([MyHero, MyProducts])