Hacker News new | ask | show | jobs
by hoorayimhelping 3183 days ago
>Feature flags are a nice idea, but nobody outside of Facebook seems to be embracing them

I worked at Etsy from early 2012 to late 2015 and I used feature flags every day I was there.

>How do you prevent your codebase from becoming if-statement spaghetti?

You remove the feature flag checking code when you turn off or turn on the feature. It's an extra deploy, but it needs to be part of the prod/eng process. This is the most salient issue of feature flagging in my experience and something you need to get ahead of from the get-go.

>How do you prevent new features from being leaked to the user?

You branch at the server/request level, not the client level, so there is no set of features that gets displayed, it's just what the server returns.

>What do you use to control feature flags?

You can use a service like LaunchDarkly or Optimizely or you can write your own service, or you can have a file in memory. Usually it starts simple with boolean toggles and evolves into a ramp up system that let's you selectively target users. The important part is that you need to be able to change features without redeploying code.

1 comments

You remove the feature flag checking code when you turn off or turn on the feature. It's an extra deploy, but it needs to be part of the prod/eng process. This is the most salient issue of feature flagging in my experience and something you need to get ahead of from the get-go.

I don't understand. I was asking about the development source code, not the code delivered to the user. The dev source code isn't stripped of if-statements, right? That's where the feature flag toggling lives. But then it seems like it's easy to devolve into spaghetti.

You branch at the server/request level, not the client level, so there is no set of features that gets displayed, it's just what the server returns.

Ok, but how? I'm not playing dumb. Assume an express server. You want to put a feature flag into your codebase. What is step #1 (and #2 and ... #N) to achieve this?

I looked up LaunchDarkly. $299/mo for basic team support. Uhh... That's almost the cost of WeWork office space.

Are feature flags really so nascent that there isn't a FOSS solution for it?

> The dev source code isn't stripped of if-statements, right?

When I use a feature flag as an alternative to branching, then I delete the if statements once the feature becomes permananent and gets released to all users. It's analogous to merging a branch. Obviously I'd leave it there during QA and A/B testing, but once toggling is no longer necessary, I remove the toggle.

> Ok, but how? Assume an express server.

You probably have feature flags already and just didn't call them that. But normally in any given project, there are multiple ways to wrap a feature in a toggle.

Here are a couple of examples I use:

- I use handlebars for my frontend templating. When I have a feature that needs to serve or withhold some HTML to/from the user, I put the feature flag in my JSON file of variables that get passed to handlebars. Handlebars has a way to wrap HTML with a boolean test.

- I use the Google Closure compiler to optimize, uglify, and minify my code. When I need a feature flag that is code only, and I don't want users to accidentally get exposed, I use a boolean @define in JavaScript, and wrap feature code in an if statement. When the Boolean is false, Closure compiler removes that block from the output code.

- I set up my own JSON file of config constants that are injected into the code during build. If I have a feature flag to A/B test something, or that I do want released to users but has a secret switch to enable, then I put the constant in the JSON file, and write UI code to toggle the flag. This one usually generates more code than the two above, but whenever I decide that the feature is either dead or permanent, I remove all the code to enable and disable the feature.

- For fully dynamic toggling, I put my boolean config variable into my database. I use Firebase which is a giant pub-sub, so I wrap the toggle in a subscription to the database variable, which might then (for example) inject some HTML and/or toggle the CSS display attribute of some things on my page.

> Are feature flags really so nascent that there isn't a FOSS solution for it?

No, feature flags have always been around, since long before branching. There isn't a FOSS solution for it separate from your framework(s) because how they work depends entirely on your stack. They're also normally really simple, adding the dependencies of another project just to have a boolean in your config file or in your code is usually overkill. That said, there are FOSS and commercial solutions for A/B testing, which is usually a feature flag workflow, but it comes with lots of other stuff you might not need, like the reporting, analytics and statistics parts.

> I delete the if statements once the feature becomes permananent

Right. In theory. Does this really scale when there's more than a dozen developers touching the same code paths? It's not just you scratching off your own TODOs, it's multiple entagled condiditionals. Can you really delete your flags with confidence that this does not affect any other code path?

Which brings me to my real question: How you you guys test this stuff? When I have experimented with feature flags before this has been to much detriment for testing. Suddenly the new payment API our provider will launch in June needs to be tested with the new backend we'll launching Tuesday, and with the new asynchronous database driver our DBAs wants us to migrate to. And so on. The complexity explodes. Especially when the features have dependencies external to you.

When everything is in master and everything must be in shippable condition you need to test everything with everything else, while previously the new payment API in June could live in it's own branch and test on their own schedule and not bother everyone else everytime the payment provider's test environment took a nosedive for some reason.

So you either end up with brittle tests that can take days to execute, or you end up scratching a lot of the less pressing combinations. Which brings us to the situation above. How can you then be confident to remove conditionals without testing the newly exposed code paths?

It is so far my suspicion that most people who speak of feature flags as an alternative to branches either work with trivial products where most developers fit in a small room (in which you are not the use case for these things) or talk out of some theoretical conviction which has yet to meet with reality. These things are hard, because of all those details which seem small if you squint enough.

To be clear, I personally use branching a lot (e.g., for sprint-sized features) and I create new feature flags rather infrequently (for multi-sprint sized features or for A/B tests), and only for pretty large features. But I can see reasons why some teams/environments do go, or want to go, feature-flag always. Before git, feature toggles were more widespread as a workflow.

As far an entanglement with other code, I would evaluate this wrt branching. If it's entangled, the branching analogy is a merge conflict. The most common case is you can delete the toggle without conflict, and occasionally you have to spend ten or twenty minutes untangling. Every once in a rare while it gets messier.

For testing, the prod tests run the prod config, which is likely with all optional feature toggles turned off. My team would run our own tests with our feature toggles turned on, but not with other teams' feature toggles. I don't run all combinations of features. Usually, a feature toggle will also wrap new tests that belong to that feature.

Again, the same question you ask applies to a branch workflow. Do you run all possible branches at the same time? Is it an exponential combinatorics problem to have multiple branches? Not really, each team runs the tests for the features they work on. Same either way.

The great thing about feature flags for testing is that you have greater control over who is exposed to your new feature. With a feature toggle, you can expose some teams in your company outside of your own, without exposing the whole company. You can expose 10% of your customers and do testing without inflicting bugs you didn't forsee on 100% of your customers. If there's an emergency, you can limit the damage radius with a well designed feature flag. Branches can do this only to the extent that the merge structure allows it, and they can't help with customer testing.

> It is so far my suspicion that most people who speak of feature flags as an alternative to branches either work with trivial products [...] or talk out of some theoretical conviction which has yet to meet with reality

I think I know why you feel that way, but that sounds (to me) like you haven't been exposed to (and so can't imagine) workflows outside of git branching. I mean no insult, but you are jumping to conclusions from lack of experience. Feature toggles as a pseudo-branching workflow predate git by decades, they are used on the largest codebases in the world -- did you miss the comments in this thread from MS and Google?

Feature flags are used in nearly all repos where branching exists. It's not one or the other, you can use both workflows at the same time. A/B testing is based on feature toggles, and all sizable websites on the web are doing A/B testing constantly. Chances are high that you use them already and just haven't yet recognized that they're the same thing we're talking about. If you have config files for your project, then you have feature toggles. If you ever change a value in one of them, then you could instead branch your code, remove the config variable and rewrite the code that used to reference the feature flag to be hard coded instead. Is that making sense?

FWIW, just to put some specifics and back up my own 'conviction' with real world examples, I've been on teams that used feature toggles to test and ship features during the production of several animated films, movies you probably saw. When I worked in film, it predated git. We had a hierarchical repo, but there were no branches, only pushing to master and feature flags.

When I worked in games, the team I lead used feature flags to support cross-team testing on a $1B console franchise.

At a certain web company, in addition to constant A/B testing, my team shipped features to customers who signed up to be on the 'beta' versions of features before they were rolled out to all of the >1M users. We used feature flags to control which teams internally could see & test certain features, and we used feature flags for either long-running or large cross-team features when branching & merging would cause undue amount of branch noise.

And at my own startup now, I prefer to wrap my features in feature flags even when I'm branching. That way I can back out a feature when it causes problems or has bugs without having to do git surgery or even risk a merge conflict. With a few thousand active customers, this has saved my ass multiple times.

> If it's entangled, the branching analogy is a merge conflict.

I like this observation. As long as two developers touch the same code, you will have a conflict. Now your choice will be where to best place that conflict. In an SCM, in conditional statements, or somewhere else.

> For testing, the prod tests run the prod config, which is likely with all optional feature toggles turned off.

I don't understand this. If you are going to activate a new feature in prod, surely you test it first? Or do you start testing all over again before you switch on a new codepath? Feature toggles must also include externalities such as backend systems changing their APIs. Even if you are forwards compatible, you must take care so your application doesn't break when that happens, and that means testing in advance.

If you really follow the master-is-production branch free model all merging must cease before you activate a feature in prod? That doesn't sound very practical.

> Do you run all possible branches at the same time? Is it an exponential combinatorics problem to have multiple branches? Not really, each team runs the tests for the features they work on.

A team can not be responsible for testing only their own development work, they must also know that it works with other people's changes that might activate before theirs does. That cross team communication is hard enough using branches, but at least teams do not disturb each other's testing. That enables asynchronous development. In a master must always be production model, which seems to be what most branchless development people seems to advocate, every combination is blocking. That's the difference.

> The great thing about feature flags for testing is that you have greater control over who is exposed to your new feature.

Right. I'm not saying that feature flags are useless. I'm just questioning that it can be used instead of branching in non-trivial projects. There are a lot of use cases where it makes sense, for example user interface stuff where branching unnecessary.

As you point out, feature flags are just a new term for application configuration. We've had that forever. It is as an alternative to developing in branches I question it. We generally don't celebrate making everything an option.

> I mean no insult, but you are jumping to conclusions from lack of experience. Feature toggles as a pseudo-branching workflow predate git by decades, they are used on the largest codebases in the world -- did you miss the comments in this thread from MS and Google?

Oh, absolutely. I type this because I want to hear from people with experience. What I meant by "my suspicion so far" is that the experience with branchless development I've heard so far is either trivial or theoretical. There's a lot of pointing at Facebook as a good example but not much in way of first hand knowledge.

And yes, I did notice the comments. The Microsoftie called it "if-statement hell", the Google commenter said it "impacted developer velocity" primarily because of the testing requirements. The former can perhaps be explained with culture, but the latter matches my experience exactly. These comments concerned feature flags in general however, not branchless development which is a special case of it.

Your examples from the game and film industry sounds like it could be made workable, but I imagine that development looks different from the average ERP system or backend service. Certainly with smaller startups you shouldn't overcomplicate matters.

> If you are going to activate a new feature in prod, surely you test it first?

Yes, my teams test features before pushing to master regardless of branch strategy. What I meant is that the prod/master/nightly tests stil run the prod config as-is. Teams run tests, but features under toggles aren't tested using the master branch's automation tests until the feature is turned on. Exactly like how a team branch won't get tested in the nightly run until merged.

The QA dept. is typically running the staging branch, which is whatever got pushed to master, but hasn't gone live yet. When I turn on a toggle, it's just like I merged a branch, so QA starts testing the new features at that point. When we want extra testing, we set the feature toggle so that QA is exposed and the public is not, then if it passes, the code goes live with the feature toggle turned off.

> A team can not be responsible for testing only their own development work

When we test feature toggled code, my team runs all the automation tests with my team's features turned on. Similar to how we'd run tests as if our branch was merged into master, but without pushing the merge.

What we don't do is speculatively run tests with other teams' feature toggles turned on. Exactly like how you wouldn't run tests against someone else's unmerged branch. Their features are tested against ours after one of the teams turns their own toggle on, once one of the features is made public -- to at least our teams if not everyone.

> The Microsoftie called it "if-statement" hell

I've heard first hand accounts about their branch hell too. And it's a worse hell than I've ever seen personally. From about 10 years ago, not today, so I don't know how different it is, but someone in Research said it took a full month of merging and conflict resolution for a code merge in any given dept. to propagate to the rest of the company.

If that's happening, feature toggles definitely won't fix it. There is a chance it could make things worse.

I'd tend to agree if you strictly feature-flagged all commits as a complete replacement to branching, it would probably be ugly. Git is useful, and branches are a nice workflow for most dev.

My criteria is to choose the one that makes sense. Feature toggles are currently better IMO for long-running feature dev or very large features, branches are better for medium sized features. Adding, merging, then removing a feature toggle during a single sprint is more overhead than it's worth.

What we did in games before git was everyone pushed to master at all times (or whatever they called it in Perforce, or the lovely SourceSafe before that.) Most people did not wrap their features in toggles. We lost a ton of developer velocity due to someone checking in a bug that broke the build for everyone who pulled after that. More often than not people would hurry to try and fix their bug rather than revert, occasionally checking in further bugs. Company-wide breakage was a weekly occurrence. Those were dark days, it was ridiculous. Feature toggles were the only safety mechanism we had, which is maybe why I see them in such a positive light.

I would not recommend going whole hog with feature toggles and eschewing branches. But I do recommend trying one or two and feeling out exactly how the whole process would work for you from start to finish. They are still useful in a branch environment, especially for features that would cause lots and lots of branch noise.

> then I delete the if statements once the feature becomes permanent and gets released to all users

Doesn't this throw out some of the point of feature flags? That if your production servers are getting hammered, and you're having trouble scaling (for whatever reason), that you can degrade service by toggling off various features which are more resource-intensive so that your service doesn't crash entirely.

Feature toggles implemented through conditional logic seems like an anti-pattern to me. Developers make mistakes, and it's very easy to forget to wrap work around the necessary toggles and accidentally expose not-ready code in production, which may also have security consequences.

It seems to me like a better pattern would be to feature-toggle on an API level, and force developers to plan and commit to relatively stable APIs when they deploy new features to production. This is achieved through an API gateway model - a versioned API is introduced to the gateway, and new versions of services must pass integration testing proving that they adhere to that API before the API gateway will serve the new version of the service with traffic. Developers may introduce new code, and new unstable APIs, and deploy them to production, but they will not be user-facing until a new, stable API is pushed to the API gateway to serve to users. This preserves developer flexibility in pushing new work to production, without exposing that work to the user, until the feature is stabilized and ready.

> Doesn't this throw out some of the point of feature flags?

Not if you're using feature flags as an alternative to branching, nor if your feature flag is intentionally temporary. A branch is temporary, and merging the branch doesn't eliminate the point of branches, right?

For performance toggles you want to keep and use, then definitely just keep those.

> Feature toggles implemented through conditional logic seems like an anti-pattern to me. Developers make mistakes, and it's very easy to forget to wrap work around the necessary toggles

This is a valid concern generally, but in my experience doesn't detract much from the reasons to use feature toggles. In particular, the good examples you brought up (performance features & API feature toggles) you can't have without conditional logic, so there isn't any choice or alternative, right?

> It seems to me like a better pattern would be to feature-toggle on an API level

That's a good idea when the feature in question is an API feature. Are you a backend kind of person? ;) Lots of features are frontend, lots of features are full-stack. It all just depends on which feature, but generally speaking having API level feature toggles in your toolbelt is a great idea.

Most languages have FOSS feature flag libraries. One that comes to mind is waffle for python/django.
The expectation of a bunch of weird branches making everything confusing (I had that too) ends up being unfounded. You typically only see a few places with branching logic, and people usually draw attention to it being branching logic for a feature or experiment.

How and where you branch really depends on the feature. If you're A/B testing a button color, you'll deploy a different strategy than if you're trying to introduce new logic with minimal regressions. But you can plan out your strategy in both cases to minimize leaking information and disrupting user experience (and you should).

>Ok, but how? I'm not playing dumb. Assume an express server. You want to put a feature flag into your codebase. What is step #1 (and #2 and ... #N) to achieve this?

The general flow is: get status of features from source of truth. Check current feature flag's status. Do something based on that status. You can make it as complicated or simple as you like. You can have a global boolean flag for all features where it's always either true or false. You can have different flags for different users, you can have certain percentages of users have different values for flags. But it all starts from getting the value of a flag for this current request from the source of truth.

Let's say you're writing a crud app on an express server that communicates via http to a database service. Your database service is rolling out v5 of their api, and they're at the last stages of their beta so they're pretty confident. You decide you want to test it by routing half of all the traffic to your crud app through v5 and the other half as normal through v4. You could lay out your code in several ways, and you'll have to figure out what works best for your model and your team.

Let's assume you have a feature service you query on every request (maybe it's express middleware) to see if an experiment is enabled for the current user, and you've queried your current user against api v5, and you have a boolean variable indicating whether to show v5 or v4 and it's used in a route handler for your home route.

You could change the url you hit based on the state of the experiment, provided the interface to the api is the same.

    const routeUrl = '/api/v4/home/'
    if (featureIsEnabled) {
      routeUrl = '/api/v5/home/'
    }
Inside the route home route handler you could have an if/case statement that did a completely different request and response.

    if (featureIsEnabled) {
      return queryV5('home').then(respondToV5).catch(handleV5);
    } else {
      return queryV4('home').then(respondToV4).catch(handleV4);
    }
You can also do this on the front end - a lot of feature / experiment services have front-end libraries. I prefer to do it server side.
> Let's say you're writing a crud app on an express server that communicates via http to a database service.

As a side note - a 'database service' that's only job is to serve as an HTTP wrapper over DB seems like an anti-pattern. Service boundaries here are not related to any business concern (if you're thinking along the SOA lines) and rewriting already existing DB remote interfaces via http seems like overengineering (if you're doing n-tier architecture). Some additional reasoning on the matter by Udi Dahan: https://vimeo.com/113515335

Would be interesting to hear your thoughts if you think otherwise.

Using properly interfaces and implementations with an IoC container solves this problem without causing any messy if-else leakage throughout the code.