- Ship faster with more confidence, knowing you can quickly turn a feature off if it starts causing errors.
- Merge unfinished, work-in-progress code into the main branch and deploy it to production behind a feature flag instead of building up large pull requests that hard to review and merge. Push code faster, avoid getting blocked and spend less time fixing conflicts.
- Decouple feature releases from code deployments so you don't have to think about what features are being released in each code push.
- Empower product managers, customer success and sales reps to control feature flags themselves.
- Keep code paths deactivated until they're ready.
- Instantly turn off code paths when there are errors.
- Correlate error spikes with feature flag changes.
- Test changes in production instead of staging or dev where they may behave differently.
- Roll out features to subsets of users like employees or beta users before a wider release so you can get feedback on your feature earlier than if you had to wait for a full launch.
- Collaborate on features with teammates like other developers, product managers, designers, QA.
- A/B test new features to ensure they don't regress key metrics related to the business, product or overall system.
- Granularly control the release of new features instead of tying them to code deployments.
- Keep track of what is being released to users and when.
- Keep track of how much features are used.
Auto-generated, type-safe flags, based on your GraphQL schema, that return the right types and give you compiler errors for typos.
Flags could be in kebab-case or camelCase. No compile-time type errors so if you guess wrong or make a typo, you get a runtime error. Manual, unsafe type casts for non-Boolean flags like Ints and Strings.
Code completion in your IDE.
No code completion so you have to leave your IDE, find the flag in the UI, copy its name, paste it in your code as a raw string, then figure out if you need to convert it to camelCase.
Type-safe enum flags that return enums you can exhaustively switch on.
Enum flags not really supported. They return raw strings so you can't exhaustively switch across all possible cases. If you miss or misspell a case, your code silently breaks.
Type-safe object and list flags based on your GraphQL configuration schema. Easy to change the types later.
Object and list flags not really supported. They return raw JSON so you have to manually write (and maintain) brittle code to parse it. High risk of runtime errors. Difficult to change the type later.
Pass context arguments like userId, email, etc, in a type-safe way with compiler errors if you miss or misspell one.
Context arguments not passed in a type-safe way. If you miss or misspell one or it has the wrong type, your flags silently break. Particularly bad for enum and object arguments like planType, environment, user, etc.
Code completion and parameter info for context arguments in your IDE.
No code completion for context arguments so you have to leave your IDE, browse a few flags in the UI to see what arguments they use, copy their names, paste them in your code as raw strings and pass them with the correct type.
To clean up a flag, remove it from your query, re-run code generation and fix all the type errors in your IDE.
To clean up a flag, first figure out if it's in kebab-case or camelCase, then manually search for it, then carefully remove each reference. Miss a reference and get runtime errors.
To add a context argument, add it to your schema, re-run code generation and fix all the type errors in your IDE.
To add a new context argument, manually search for the right place in the code to add it, add it and hope you did it right.
Browse flags in the auto-generated code, directly in your IDE, like any other library.
To see what flags are available, you have to leave your IDE and browse the UI.
Find all references of a flag using your IDE, like any other function.
To find flag references, first figure out if it's in kebab-case or camelCase, then manually search for it. Get incomplete and cluttered results.
Type-safe UI that only lets you reference valid context arguments, as defined in your schema, and use them in valid places.
No type-safety in the UI so it's easy for your flag logic to silently break by referencing invalid arguments (like org_id instead of orgId) or using them incorrectly (like trying to concatenate two numbers).
Type-safe UI that only lets users select options that type-check and prevents them getting into invalid states.
No type-safety when editing JSON flags in the UI. If you miss or misspell a key that your client expects, you'll break production.
Exhaustively switch on enum context arguments in the UI.
Can't exhaustively switch on enum arguments in the UI like planType, environment, language, etc, so it's easy to silently break your flag logic by missing or misspelling a case.
A full programming language under the hood. No limits on your logic. Express anything you can express in code. Not locked into a limited DSL.
Locked into basic disjunctive normal form so can't express flag logic like "if (A or B or C) and D". Forced to rewrite as "if (A and D) or (B and D) or (C and D)" with duplicated logic that's error-prone and harder to input, read and maintain.
Embed logic within flag values, e.g. within object fields or list items. Or keep logic at the top-level to return whole different objects or lists.
Can't embed logic within flag values, e.g. can't embed logic in JSON fields or list items.
Code interface to edit flag logic (coming soon).
Can't edit flag logic like code.
Embed a snapshot of your flag logic in your app bundle for guaranteed, instant SDK initialization with no network latency. The only platform for Jamstack-compatible feature flags, A/B testing and content personalization with zero performance impact.
No build-time mode. Forced to wait for SDK initialization from the server before accessing flags or get incorrect fallback values. So forced to block rendering your app or use the fallbacks and risk a UI flicker when they change.
Maintains a good user experience even if the SDK fails to initialize from the server.
Bad user experience when the SDK fails to initialize.
Build-time-only mode for critical configuration like permissions, pricing plans, in-app content, etc.
No build-time-only mode. Not suitable for critical configuration.
All your flags and configuration are versioned together with a single Git history. Quickly pinpoint bad commits after incidents and instantly roll back to known good states.
Flags are versioned separately. Difficult to figure out what combination of flag changes caused an incident then roll back to a good state.
Group changes to multiple, related flags in a single, atomic commit to prevent bad combinations and easily roll them back in one step.
Can't make changes to multiple, related flags in a single, atomic commit. Risk breaking your product with bad combinations of flag values as you make a sequence of flag changes. No single commit in your history that you can easily understand later and roll back.
Integrate with GitOps workflows (coming soon).
No Git integration.
See how often different branches of your logic are called, e.g. how many users pass each flag targeting rule.
No granular analytics of how many users pass each flag targeting rule. Only aggregated analytics.
Set granular permissions on your logic, e.g. to let customer success edit a list of user IDs but not the surrounding logic.
Can't set granular permissions, e.g. to allow customer success to add new users to a flag but not change the other targeting rules.
Define flag-specific arguments, like projectId or promoCode, that are only relevant to a specific flag or group of flags.
Can't have flag-specific arguments. Context arguments can only be passed at the top-level.
Flag arguments like promoCode can change on different evaluations in the same user session. So you can have flags that depend on the session state like user input, selected options, the current view, etc.
Can't have flag arguments that change on different evaluations in the same session. All context arguments must be passed at SDK initialization and stay the same for the whole session. So you can't have flags that depend on the session state.
Group and namespace flags in object hierarchies in your schema.
No namespacing so flags have long, complex names that are hard to find, read, remember and communicate.
Manage the possible combinations of flag values by grouping them with shared targeting logic.
Can't prevent bad combinations of flags like disabling topNavbar and sideNavbar at the same time (leaving users with no way to navigate). Have to duplicate targeting logic across related flags.
Pin clients to a specific schema version so breaking changes, like removing a flag, don't break production.
No schema or schema versioning. If you remove a flag without removing its usages, your clients will break in production.
Only query the flags you need at SDK initialization. Get auto-generated code based on this query.
All flags are fetched at SDK initialization.
Flag logic is partially evaluated as much as possible on the server before being sent to SDKs to minimize the network payload and so less logic needs to be evaluated on the client.
Doesn't partially evaluate logic as much as possible on the server.
All flag getters are synchronous.
Some SDKs use asynchronous flag getters which risk your code hanging due to a promise that never resolves.
Type-safe GraphQL API, based on your schema, to query flags from any client without using an SDK.
No type-safe API to query flags from any client.
A/B tests are a first-class citizen. Drop them anywhere in your logic. Reuse a single A/B test across multiple flags, e.g. run a "Rebrand" test across newNavBar, newLogo and colorTheme.
A/B tests are tightly coupled to flags. Can't reuse a single A/B test across different, related flags.
Make A/B tests multi-dimensional. Use different dimensions on different flags to find the best combination of flag values.
Can't make A/B tests multi-dimensional.
Nest A/B tests inside each other. Run mutually exclusive tests on randomly split, distinct segments of the population. Set up a long term holdout across many shorter term tests.
Can't nest A/B tests inside each other.
Nest logic within A/B test branches, e.g. to test personalized content or dynamic pricing.
Can't nest logic within A/B test branches. Forced to make code changes to test personalized experiences.
Break down A/B test results by user attributes and any other features you log.
Can't break down A/B test results by any feature.
Create a machine learning "loop" to automatically find the best flag value for a given user or set of features. Make it multi-dimensional to find the best combination of flag values.
Can't automatically optimize flags and configuration for a given user or set of features.
Build a funnel from your events, A/B tests and ML loops to see drop-off and conversion rates broken down by user attributes and other features.
No funnel analytics to see drop-off and conversion rates across multiple steps.
Use for in-app content management, landing page optimization, pricing plans, rules and limits, magic numbers and more complex backend configuration.
Doesn't scale to other code configuration use cases.