Live data from Hacker News

How to use feature flags without technical debt

blog.launchdarkly.com

21–30 of 51 posts

Re: How to use feature flags without technical debt

#21
post #13

Haven't tried it myself, but why not use authorization libraries instead of specialized 'toggle' libraries? After all, both are concered with whether user X is allowed to do Y. Using just one approach might be a clean, maintainable approach. The original code `if can?(:use_feature_x, user)` is written just once, and then never needed to be removed. The only thing that changes, gradually and cleanly, are the business…

In canary launches, you might want to roll a new feature out to 10% of your users, then 20%, etc. Once it is released to 100% of your users, you might want to remove the check, since it is a no-op.

I'm not aware of any authorization libraries that let you grant access to a percentage of your users, but maybe they are out there? It is a strange use case from an 'authorization' standpoint.

Re: How to use feature flags without technical debt

#22
Hmm. This is a different kind of feature flag than I've used, to solve a different kind of problem.

If the feature you're writing takes several man years of effort, you can't have a feature branch living for several months; continuously keeping it up to date with the trunk is expensive and easy to procrastinate.

Migrations are expensive and you want to front load them to make turning the feature on less stressful. And you may want to let customers use the feature on a beta basis for a few months before committing to it, and then it may take a year or more before all customers have moved.

For a big feature that cuts across large segments of a big app, I don't think there's an alternative to if statements.

Different apps, different business models, etc.

Re: How to use feature flags without technical debt

#23

On a tangent, how long-lived are feature toggles usually? I have very limited experience, and it points to a wide range from a few days to few months. When I stumble about a 1y+ old flag, I tend to delete it (and the dead code path that it comes with). What's your experience?

In my experience, there are a few different types of feature toggles. Some are permanent, and are useful for operational tasks, like putting an application into read-only mode, or disabling one service that is overloaded to prevent a cascading failure.

For the temporary type of toggle, which is what I was addressing with this blog post, my experience coincides with yours-- usually a few weeks.

The trick with deleting a year-old flag (which I was trying to address with this post) was that you need to be careful when deleting code that you haven't worked on in over a year. If you have the list of necessary changes all pre-baked in a branch, this can be at least a little easier.

Re: How to use feature flags without technical debt

#24
In the TXR language interpreter, I have a -C option which takes a numeric argument: it means, simulate the old behaviors of version N. If you don't specify -C, you get the latest behavior.

Throughout the code, old behaviors are emulated, subject to tests which look similar to this:

   if (opt_compat && opt_compat 
I think that tying specific old behavior to a proliferation of specific options is a bad idea. It does provide more flexibility (give me some old behavior in one specific regard, but everything new otherwise), but that flexibility is not all that useful, given its level of "debt".

The purpose of compatibility is to help out the users who are impacted by an incompatible change; it gives them a quick and dirty workaround to be up and running in spite of the upgrade to the newest. They can enjoy some security fix or whatever, without having to rewrite their code now.

However, they should put in a plan to fix their code and then stop relying on -C.

If users are given individual options, that then encourages a behavior whereby they use new features with emerging releases, yet are perpetually relying on some compatibility behaviors. This leads to ironies: like being on version 150, and starting to a feature that was introduced in 145 and changed incompatibly in 147 and 148---yet at the same time relying on a version 70 behavior emulation of some other feature. Hey we don't care that this new thing was broken recently twice before being settled down; we never used it before! But we forever want this other thing to work like it did in version 70, because we did use it in version 70. It's like using C++14 move semantics and lambdas, but crying that GCC took away your writable string literals and -fpcc-struct-return (static buffer for structure passing).

It's very easy to hunt down the opt_compat uses in the source code just by looking for that identifier, and the version numbers are right there. If I decide that no emulation older than 120 will be supported in new releases going forward, I just grep out all the compat switch sites, and remove anything that provided 119 or older compatibility. The debt is quite minimal, and provides quite a bit of value.

Re: How to use feature flags without technical debt

#25
post #2

This misses the problem -- often, the new feature is buggy in ways that are not seen in the initial testing. In critical systems, it's often desirable to keep the ability to flip back to the old code around for a couple of release cycles. In a previous life, that has saved my team's bacon. This comes at a cost, and somehow saying "just delete the flags promptly" is a facile solution; If it was easy to just delete the…

> This misses the problem -- often, the new feature is buggy in ways that are not seen in the initial testing.

Indeed. The only good way to enable features gradually is to use a tool like GitHub Scientist [0] that exercises the new code path and records its effects but then uses the effects of the old code path in production. This allows weird edge cases to be found and dealt with before enabling the new feature.

[0] http://githubengineering.com/scientist/ Previous discussion: https://news.ycombinator.com/item?id=11027581

Re: How to use feature flags without technical debt

#26
post #25
post #2

This misses the problem -- often, the new feature is buggy in ways that are not seen in the initial testing. In critical systems, it's often desirable to keep the ability to flip back to the old code around for a couple of release cycles. In a previous life, that has saved my team's bacon. This comes at a cost, and somehow saying "just delete the flags promptly" is a facile solution; If it was easy to just delete the…

> This misses the problem -- often, the new feature is buggy in ways that are not seen in the initial testing. Indeed. The only good way to enable features gradually is to use a tool like GitHub Scientist [0] that exercises the new code path and records its effects but then uses the effects of the old code path in production. This allows weird edge cases to be found and dealt with before enabling the new feature. [0]…

Yeah, I actually wrote about something similar recently (in the context of a database migration): http://blog.launchdarkly.com/feature-flagging-to-mitigate-ri...

Re: How to use feature flags without technical debt

#27
post #9

This obsession about avoiding technical debt is quite strange. It's a tool to use when it makes sense, like loans in the bank...

Debt, both technical and financial, is always best avoided. And in both cases, once you have it, you have to pay it down sooner or later, and the later you do the more expensive it gets.

It is a tool, but having it is always a negative that is offsetting a bigger negative. By all means, take the loan when you need a boost that you can't otherwise afford. But take the smallest loan you need, and pay it back as fast as you can.

Re: How to use feature flags without technical debt

#28

On a tangent, how long-lived are feature toggles usually? I have very limited experience, and it points to a wide range from a few days to few months. When I stumble about a 1y+ old flag, I tend to delete it (and the dead code path that it comes with). What's your experience?

Feature toggles can last decades.

For instance GCC has a feature flag called -ansi which gives you C90 compatibility.

C90 was superseded in 1999 by C99, and so that's 17 years of compatibility, and counting.

Re: How to use feature flags without technical debt

#29
post #8

If you do feature flags by inserting if blocks throughout your code you will create tech debt anyways. The goal is to have one if block and hide the changed behavior behind interfaces (or polymorphic functions if you are using functional languages). Dependency injection is your friend. If you don't do this, you won't scale beyond a hand full of feature flags. Chrome has hundreds, for example.

Can you expand on this? I'm interested to see how this works in a real code base.

I'm thinking something like an initial (maybe massive) if block in the setup of the application that sets all of the behavior/features by declaring which implementations get set to which interfaces? After this if block, all of the DI stuff is set?

This of course means you need to use a DI framework of some sort.

Using feature flags is something I'm investigating because our current model is a git branch for every feature, and I wonder/fear it only works because we're a small team that has worked together for a while and in the future when we grow this will break down.

Re: How to use feature flags without technical debt

#30
post #2

This misses the problem -- often, the new feature is buggy in ways that are not seen in the initial testing. In critical systems, it's often desirable to keep the ability to flip back to the old code around for a couple of release cycles. In a previous life, that has saved my team's bacon. This comes at a cost, and somehow saying "just delete the flags promptly" is a facile solution; If it was easy to just delete the…

If a new feature is completely buggy, but it does not impact any existing features, then that doesn't require any compatibility switch. You just fix the feature and issue an update or new release.

If a new feature is buggy but useful (say, the behavior is wrong in a way that users figure out, and start depending on), then you can have an option to emulate that buggy behavior.

If a new feature causes a regression in existing features, then that is a call you have to make. You have a situation in which something worked up to version K. Then was broken between K+1 and L, either not working at all or working differently. Then as of L+1, it was discovered, fixed and works again as before.

If it was completely broken, then you just fix it and that's that. If it was broken in ways that left it useful, such that users may have come to depend on the altered behavior, then just subject it to compatibility. Emulate that behavior if users request compatibility with a version between K+1 and L. If they request compatibility with K or less, or L+1 and higher, enable the current fixed behavior.

Post reply on HN