Type-safe feature flags
Hypertune is the most powerful feature flag, A/B testing and app configuration platform. We've built it for the modern stack with end-to-end type-safety and Git version control.
21 Jun 2023 ‧ 5 min read
While the rest of the stack has dramatically improved with type-safety, code completion, "Find All References", etc, feature flags have been left behind.
Instead you have to litter your code with raw strings, copy and paste flag names around, risk runtime errors, manually search for flag references, manually clean up flags and risk leaving around stale references. It's common for flags to never be cleaned up at all, making code harder to understand and more prone to bugs.
When you pass context arguments for your flag targeting logic like the current user, organization, environment, etc, there's no schema or type-safety so it's easy to reference an invalid property in the UI and break your targeting.
You could get some type-safety by writing your own wrapper for accessing flags.
But you'd have to manually maintain a list of flag names in your code and keep this in sync with the UI whenever someone creates or deletes a flag. And you still wouldn't have type-safety for the context arguments.
It only gets worse when you need enum flags.
Why would you need an enum flag? Well, imagine you have one flag called "showLeftNavBar" and another called "showTopNavBar". If you accidentally disable both, users won't have any way to navigate. To avoid bad boolean combinations, you can use a single enum flag called "navBarPosition" with 3 values: Left, Top, Both.
In LaunchDarkly and other tools, enum flags return raw strings so you have to manually check all the possible cases and it's easy to miss a case or make a typo, silently breaking your code.
It gets even worse when you need object flags. Imagine you have a few related flags like "timeoutMs", "retryInterval" and "maxRetries", and you want to return specific combinations of them. This is hard to coordinate if they're separate flags. But if you use a single object flag, you can return them together.
In LaunchDarkly and other tools, object flags don't have a schema and return raw JSON so you have to manually write code to parse them into the right type. It's easy to break production by editing an object flag from the UI in a way that the client doesn't expect.
Besides lack of type-safety, there are two other big problems with LaunchDarkly and other tools.
Firstly, each flag is versioned separately so it's hard to prevent, debug and recover from bad combinations of flag changes. Specifically, you can't make changes to multiple, interacting flags in a single, atomic "commit" so you risk bad intermediary states. You can't run tests to ensure each commit results in a good global configuration before it gets merged and deployed. And you can't see the historical state of all your flags together to pinpoint where things went wrong and quickly roll back.
Secondly, if the platform is unreachable and the SDK fails to initialize, like on a mobile device with no network connection or a browser with overeager ad blockers, your flag will just use a hardcoded fallback value instead of your targeting logic, which results in inconsistent user experiences and corrupts A/B test results.
We've solved these problems at Hypertune with end-to-end type-safety via auto-generated code, Git version control for all of your flags and configuration together, and build-time snapshots of your flag logic so the SDK can reliably, locally initialize before fetching the latest logic from the server.