Branching is bad, use feature toggling instead!
-
This popped up in my Google feed last night:
-
#ifdef TRWTF
Write stupid article.
#endif
The suggestion:
if (useNewLogic) { awesomeNewFeatureEntryPoint(); } else { legacyEntryPoint(); }
Ugh. I wouldn't even want to do that when I'm the only one coding, let alone trying to coordinate that shit with other people.
-
I especially like all the people in the comments pointing out the benefits of branching, and OP defends not-branching with "branching causes all kinds of communications & planning problems", and then the commenters are like, "if you and your fellow devs can't communicate & plan well enough, then fix that, don't tell people branches are bad"
Seems like what OP really needs is:
-
From the comments
We have 40+ engineers and we all commit directly to trunk - no branches, no pull requests, no QA department. Every push to git is deployed to production, provided that the push passes the pre-push test and quality checks, CI automated tests, etc.
Spotted the guy working on Windows10 updates
-
Good luck modifying data types using "feature toggling" in javascript without a preprocessor. He's either a C jockey coming in to "teach JS noobs a lesson", or a troll. Or both.
-
@boomzilla said in Branching is bad, use feature toggling instead!:
#ifdef TRWTF
Write stupid article.
#endif
The suggestion:
if (useNewLogic) { awesomeNewFeatureEntryPoint(); } else { legacyEntryPoint(); }
Ugh. I wouldn't even want to do that when I'm the only one coding, let alone trying to coordinate that shit with other people.
I did that on a single page that was made by my predecessor. If you used the URL parameter "legacy=true" You get a little bit of the old magic (and your entire time clock history).
That's since been eradicated.
-
I stopped reading at "development cicle."
Then after writing this post I'll probably continue reading it. But with an especially cynical mood. Let's hope I don't hurt myself with these low expectations.
-
Alright, I'm going to preach to the choir and just dissect this fucker:
A team of five people have to do a painting of a big landscape, so they split the canvas in five pieces and then each of them paints their part without even looking at what their partners are doing and in two weeks they put all the five pieces together to deliver the product. What do you expect will happen? of course the painting will be a mess with miss aligned items on it that will need to be redrawn at the last minute pulling an all nighter on the worst case. Sounds familiar?
No. Or, maybe once or twice in the 5 years I've been doing feature branching on Git. And those rare times was due to some miscommunication which we learned from.
First the conflict part: Communication is key, daily meetings are there for a reason and should be used to smooth cooperation between developers working at the same classes.
I know plenty of people in the field with their own jobs who do feature branches, and not a single one of them don't communicate daily. In my case, if we have any doubts about our implementation of something or we detect another person's feature might rub against mine, I'll pipe up and bring up my concerns, which we resolve in that daily meeting. It's not rocket science.
And most feature branches last, at most a week. If it's more, it's for a damn good reason, and the dev involved is fully expecting conflicts to arise from it, and will often pull regularly from the develop branch. Because that dev is not an idiot.
For the part of committing without breaking the system for everyone there is a very good technique called "feature toggling". Basically what you do is put an if like this:
if (useNewLogic) { awesomeNewFeatureEntryPoint(); } else { legacyEntryPoint(); }
Poe or Noe thread is
This way of working also benefits of the fact that because everyone is working at the same place if your new feature conflicts with some change of class contract you can fix it quickly, it means that you are doing continuous integration.
How the fuck does this reduce conflicts? It will make conflicts even MORE confusing and possibly even go missed. If I'm working on the new feature on the "if true" block, and someone changes something in the legacy code that's in the "else" block which I should have ported to my new feature, then GIT IS NOT EVEN GOING TO REPORT THAT AS A CONFLICT! Communication is great, but it isn't foolproof.
I would love to read your opinions of this matter on the comments.
Yeah, I read some of them. Apparently every good argument against this is retorted with "but continuous integration!" TDMSYR!
-
The most appalling thing is that he not only invokes a $CURRENT_YEAR argument for his decisions and thought process, but he also uses git seriously.
-
A previous workplace was bringing in feature toggling just as I was serving my notice. It immediately sounded like a great way to build up a shitload of technical debt very quickly. I was talking to an ex co worker a few years later and he told me that it had all gone to shit not long after I left
-
@Jaloopa
I think you'll have to go back there and work for several years, so we can build up enough control sample to determine whether things went to shit because of feature toggling, or because of a critical lack of @Jaloopa
-
@Groaner said in Branching is bad, use feature toggling instead!:
he also uses git seriously.
Using git is no laughing matter!
-
@HardwareGeek said in Branching is bad, use feature toggling instead!:
@Groaner said in Branching is bad, use feature toggling instead!:
he also uses git seriously.
Using git is no laughing matter!
I have yet to see a compelling advantage of using it over TFS or Mercurial, or even SVN for that matter if you're a team of one. So it's still filed neatly away in the "fad-chasing idiots" section of my mind.
I guess Linus used it a couple times and therefore it's forever cool?
-
@Groaner It's the original blockchain, in more ways than one.
-
@Jaloopa said in Branching is bad, use feature toggling instead!:
A previous workplace was bringing in feature toggling just as I was serving my notice. It immediately sounded like a great way to build up a shitload of technical debt very quickly.
Feature toggling does have its place - for example, multi-tenant or multi-tenant-like applications. But to replace branching? Hell no.
-
@boomzilla said in Branching is bad, use feature toggling instead!:
Ugh. I wouldn't even want to do that when I'm the only one coding, let alone trying to coordinate that shit with other people.
I've worked with one or two old Fortran project where something like this was done. I doubt they called it feature toggling or anything fancy - I just think they really didn't know better. It wasn't necessarily new vs old features either, just a shitload of features that you could turn on or off (sometimes cleverly placed in the innermost loops).
It's barely manageable if you can limit this to a single if() per feature in a single place, but let's face it, whatever feature you're updating is going to touch more than one place in your code, so you're going to end up with a bunch of branches. Assuming your code wasn't already an unmaintainable mess, it's going to be one very quickly. Then it's going to rapidly degrade into an unintelligible mess, rapidly followed by a transition to an unholy mess, to finally end up as something that transcends description.
So, uhm, yeah, don't do that. It's dumb(tm). Your code is probably going to be complicated enough as is.
-
Feature Flags can easily become a nightmare, but when done well (which requires a good design) they are AWESOME. One team I am working with pushed out a new version of production code (public facing commercial with financial aspects) nearly every night (about 4% of the time an issue is flagged, but 96% of work nights is pretty darn good.
They can then turn the various features on/off based on many different criteria. This allows for A/B testing, Staged Rollout, Effective Rollback, all with a single code base.
-
@TheCPUWizard Rust, of course, has this functionality built in.
#[cfg(feature="new-shit")] do_new_thing(); #[cfg(not(feature="new-shit"))] do_old_thing();
-
@pie_flavor said in Branching is bad, use feature toggling instead!:
@TheCPUWizard Rust, of course, has this functionality built in.
#[cfg(feature="new-shit")] do_new_thing(); #[cfg(not(feature="new-shit"))] do_old_thing();
But what about go?
-
@pie_flavor said in Branching is bad, use feature toggling instead!:
@TheCPUWizard Rust, of course, has this functionality built in.
I checked the docs [https://doc.rust-lang.org/unstable-book], and could not find that; but5 it really does not matter.
Feature Flags go far beyond annotations at the line/block of code level. Consider a "server" type application. All users in Geography "A" should see "Feature 101", All "Early Adopters" should see "Feature 102".... DYNAMICALLY determined on a per request basis!
If you (or anyone) is interested, spend some time with https://launchdarkly.com/ to get a feel as to where this can head.
-
@The_Quiet_One said in Branching is bad, use feature toggling instead!:
How the fuck does this reduce conflicts? It will make conflicts even MORE confusing and possibly even go missed. If I'm working on the new feature on the "if true" block, and someone changes something in the legacy code that's in the "else" block which I should have ported to my new feature, then GIT IS NOT EVEN GOING TO REPORT THAT AS A CONFLICT! Communication is great, but it isn't foolproof.
Your Build Verification Tests should catch that. :)
-
@JazzyJosh I have an excellent example of how this looks like in real life courtesy of a legacy code base I used to work with (thankfully not too much!). In this case preprocessor macros are used to toggle the "features":
#if _WTF_NEW_FEATURE #if _WTF_IMPROVE_NEW_FEATURE bool Something::Init( SomeClass *param5, int param1, float param2 , float param6, float param4, float param7, float param8, int param8 #if _WTF_FEATURE_FIX , bool param10 #endif ) #else //WTF_IMPROVE_NEW_FEATURE bool Something::Init(SomeClass *param5,int param1, float param2, SomeClass ¶m3, float param4) #endif //WTF_IMPROVE_NEW_FEATURE { // <Redacted line of code> #if !_WTF_FIX_MORE_THINGS // <Redacted line of code> #endif //WTF_FIX_MORE_THINGS #if !_WTF_FIX_UNEXPECTED_USECASE // <Redacted line of code> #endif //!WTF_FIX_UNEXPECTED_USECASE #else bool Something::Init(int param1, float param2, SomeClass ¶m3, float param4) { #endif #if _WTF_FIX_UNEXPECTED_USECASE // <Redacted line of code> // <Redacted line of code> // <Redacted line of code> // <Redacted line of code> // <Redacted line of code> // <Redacted line of code> // <Redacted line of code> // <Redacted line of code> #endif //WTF_FIX_UNEXPECTED_USECASE // <Redacted line of code> #if _WTF_FEATURE_FIX // <Redacted line of code> #endif #if !_WTF_IMPROVE_FEATURE_MORE #if _WTF_OTHER_IMPROVEMENT // <Redacted line of code> // <Redacted line of code> #else //!_WTF_OTHER_IMPROVEMENT // <Redacted line of code> #endif //_WTF_OTHER_IMPROVEMENT #endif //!WTF_IMPROVE_FEATURE_MORE #if _WTF_NEW_API_THING // <Redacted line of code> #endif //WTF_NEW_API_THING // <Redacted line of code> // <Redacted line of code> // <Redacted line of code> // <Redacted line of code> // <Redacted line of code> // <Redacted line of code> // <Redacted line of code> // <Redacted line of code> }
No thanks. There's a reason we switched to feature branches instead.
-
-
@Deadfast said in Branching is bad, use feature toggling instead!:
@JazzyJosh I have an excellent example of how this looks like in real life courtesy of a legacy code base I used to work with (thankfully not too much!). In this case preprocessor macros are used to toggle the "features":
Those are not feature toggles... (The big hint's are that you can not change them at runtime, nor can you expose different features to different users of the same executable - concurrently)
-
@TheCPUWizard Maybe, but it's indicative of the mess you'll end up with.
-
@boomzilla said in Branching is bad, use feature toggling instead!:
#ifdef TRWTF
Write stupid article.
#endif
The suggestion:
if (useNewLogic) { awesomeNewFeatureEntryPoint(); } else { legacyEntryPoint(); }
Ugh. I wouldn't even want to do that when I'm the only one coding, let alone trying to coordinate that shit with other people.
We have to do that. Because how else are we going to A/B test it?!? Of course, that's developed in a feature branch.
-
@dcon said in Branching is bad, use feature toggling instead!:
Ugh. I wouldn't even want to do that when I'm the only one coding, let alone trying to coordinate that shit with other people.
We have to do that. Because how else are we going to A/B test it?!? Of course, that's developed in a feature branch.
The 5 principles of SOLID go a long way. The inlint part of the code:
var invoiceProcessor = Factory.GetInvoiceProcessor(…); invoiceProcessor=>DoStuff();
Now we can centralize the conditional (perhaps database driven) code to return the "right" class based [DIP] on the current enabled feature set. If the interface that is returned is minimal and focused [ISP] the operations that can be performed will be small. If the implementation is such that the implementation class is minimal and well encapsulated [SRP] then overlap will be minimized. Since a new capabilities are added by new concrete implementations, the existing implementations do not need to be changed [OCP]...
-
@cartman82 said in Branching is bad, use feature toggling instead!:
Good luck modifying data types using "feature toggling" in javascript without a preprocessor.
Of all the languages to choose from to state data type modification is hard/impossible with feature branching, you chose a dynamic language?
-
I've worked on a product that did feature toggles (and also feature branches, release branches and bugfix branches... In clearcase.)
Of all the stupid shit in that codebase, the feature toggles were the least hard to work with. They also made business case sense, since the features were paid for extras that customers bought licenses to use, most of the time.
To use them as this nutbag suggests, to deal with concurrent software changes is utterly dumb, and will bite you in the ass.
-
@Deadfast said in Branching is bad, use feature toggling instead!:
I have an excellent example of how this looks like in real life courtesy of a legacy code base I used to work with (thankfully not too much!). In this case preprocessor macros are used to toggle the "features":
I have some code that looks like that. In my case, the feature gating is driven by the version of the library it is built against (it supports LLVM 3 through LLVM 7, and every damn release changed something a little bit; about 5-10% of the API changes with every release) and it exports as consistent an API as possible to downstream code. However, I view it as one codebase, and not as an alternative to branching. (I guess I could have maintained branches for each supported LLVM version, but that'd be crazy awkward for users to work with.) At some point I should stop supporting very old versions, but with CI support for building against each it isn't too hard.
But that's structure within a version. I use feature branches to develop new features because of course I do; I'm not an idiot!
-
@Zecc said in Branching is bad, use feature toggling instead!:
Good luck modifying data types using "feature toggling" in javascript without a preprocessor.
Of all the languages to choose from to state data type modification is hard/impossible with feature branching, you chose a dynamic language?
Dynamic/static doesn't matter as much as the fact it doesn't have a real C-like preprocessor.
I mean, yes, you can technically overwrite some types or use dynamic imports or something, but that'd be super inconvenient for a branching scenario that the OP is advocating.
-
The only time I ever seriously used this was when my monitoring service was used on a few servers running on Asterisk 11 while the rest were running on Asterisk 13, there were some subtle differences between the two, but my own interfaces didn't change (and we built other features on top of that), so I had a bunch of preprocessor macros toggling stuff on and off.
It worked, but deleting that once we shunned Asterisk 11 was extremely satisfying. I'm happy it's not there any more, because as relatively simple as my switches were, they could be a pain at times. Completely replacing branching with that? Hell no!
-
@cvi said in Branching is bad, use feature toggling instead!:
I've worked with one or two old Fortran project where something like this was done. I doubt they called it feature toggling or anything fancy - I just think they really didn't know better.
And it probably began before there was such a thing as version control systems and branching.
-
Maybe the guy has a long term plan:
-
@swayde said in Branching is bad, use feature toggling instead!:
@pie_flavor said in Branching is bad, use feature toggling instead!:
@TheCPUWizard Rust, of course, has this functionality built in.
#[cfg(feature="new-shit")] do_new_thing(); #[cfg(not(feature="new-shit"))] do_old_thing();
But what about go?
You need to make a separate OS installation for each possible combination of features.
-
@TheCPUWizard said in Branching is bad, use feature toggling instead!:
Rust is the programming language of the future and BTW our documentation may crash for no reason.
-
@boomzilla said in Branching is bad, use feature toggling instead!:
And it probably began before there was such a thing as version control systems and branching.
I'm fairly sure that the projects themselves don't predate CVS, they are not that old. There was some code that predated CVS, but that was essentially third-party. There was at least one major overhaul in git-times too.
-
@Lorne-Kates said in Branching is bad, use feature toggling instead!:
@TheCPUWizard said in Branching is bad, use feature toggling instead!:
Rust is the programming language of the future and BTW our documentation may crash for no reason.
At least it doesn't eat your laundry anymore.
-
if (useNewLogic) { awesomeNewFeatureEntryPoint(); } else { legacyEntryPoint(); }
Two months later:
if (useEvenNewerLogic){ evenAwesomerNewFeatureEntryPoint(); }else{ if (useNewLogic) { awesomeNewFeatureEntryPoint(); } else { legacyEntryPoint(); } }
The following March...
-
@PJH said in Branching is bad, use feature toggling instead!:
The following March...
Posted it there: https://dev.to/pauljherring/comment/6f07
See how long it lasts/what comments it gets.
-
@Lorne-Kates said in Branching is bad, use feature toggling instead!:
@TheCPUWizard said in Branching is bad, use feature toggling instead!:
Rust is the programming language of the future and BTW our documentation may crash for no reason.
It describes unstable features of Rust; the book itself isn't unstable.
-
@PJH We have some A/B tests that are more like A/B/C/D/E ...
-
@pie_flavor said in Branching is bad, use feature toggling instead!:
@Lorne-Kates said in Branching is bad, use feature toggling instead!:
@TheCPUWizard said in Branching is bad, use feature toggling instead!:
Rust is the programming language of the future and BTW our documentation may crash for no reason.
It describes unstable features of Rust; the book itself isn't unstable.
-
@PJH said in Branching is bad, use feature toggling instead!:
The following March...
Refer back to my previous post... Once the code was structured to use Feature Flags, the posted code would not need any edits "two months later" and probably not even "the following March"
-
@TheCPUWizard said in Branching is bad, use feature toggling instead!:
Once the code was structured to use Feature Flags
Uh huh. And you think the author of the article would contemplate such a thing?
Sounds a bit too much like actually doing some forward-planning, which seems a tad optimistic for the author.
-
@PJH said in Branching is bad, use feature toggling instead!:
Sounds a bit too much like actually doing some forward-planning, which seems a tad optimistic for the author.
Why do forward-thinking when you can do backward-thinking?
-
@cvi said in Branching is bad, use feature toggling instead!:
@PJH said in Branching is bad, use feature toggling instead!:
Sounds a bit too much like actually doing some forward-planning, which seems a tad optimistic for the author.
Why do forward-thinking when you can do backward-thinking?
Why do any thinking at all?
-
@Deadfast said in Branching is bad, use feature toggling instead!:
Why do any thinking at all?
No thinking would be an improvement over the sheer amount of backward-thinking that I occasionally get to observe.
-
Through chance I encountered a different author's article which explains a similar point that you should use as few branches as possible, rather than picking up Gitflow and doing merges between feature / release branches all the time:
(They actually made a dedicated website called Trunk Based Development to discuss it in even more detail)
At least it is less radical than the article in the OP: it allows for short-lived branches and it doesn't care for branches on the developer machines, but it does require that no features are really developed in isolated branches (either by having real feature flags or setting up abstractions where only new code gets the incomplete implementation).
It does seem acceptable that a single main branch is actually something to strive for if whatever you are making supports has very few different versions running at the same time, and hence doing a fix-forward approach might work (bugfixes are always done for the next release and only seldomly backported). Your tech stack still needs to support it though, as .NET's Entity Framework migrations tend to be hard to cherry-pick or merge.
-
@JBert we mostly just do stuff in the trunk (svn) here. I make branches when I'm doing something experimental, when it's a really big chunk of work and needs lots of incremental commits before it's ready for merging into the trunk, or when I'm doing something that's meant to go into a future sprint because I don't have stuff for the current sprint to be doing.