The project I work on at Microsoft uses feature flags. I see it with many Azure services as well. I can testify that it is absolutely if-statement hell, at least in the areas that rely on these feature flags. However, I will say I think 'lazy programming' is more the issue than the flags themselves. For example, isolating feature specific behavior to it's own impl and using an abstract class to share common behavior--that sort of basic stuff gets skipped in favor of 'time'.
Thankfully these are all obscured from the user for the most part.
We have a 'control panel' like thing that we used to enable/disable feature switches. It works well for its use case and let's us enable/disable with as much granularity as we want.
Hmm. I can't get into detail. But we use it for things that are mostly 'experimental' for the customer. Let's take the all-to-common TODO MVC app and say I add a caching layer between the app and the database. I would likely add a feature flag to enable or disable retrieving or storing results in the new caching layer. Or, I add a new button that lets the customer multi-select TODO tasks, that would have a feature switch, etc. The reason for this is that it lets us enable these features for customers who either insist on having them or are excited to try new features without exposing the entire customer base to the risk of these features.
The big problem with this is: you end up having 'if(feature)' type statements _everywhere_ for each of those experimental buttons or extra components or whatever. It creates considerable debt that the team has to be accountable for cleaning up as those features start to stabilize.
Thankfully these are all obscured from the user for the most part.
We have a 'control panel' like thing that we used to enable/disable feature switches. It works well for its use case and let's us enable/disable with as much granularity as we want.