Show HN: Why you should not use feature branches
fire.ci
fire.ci
We use short-life topic branches for each feature, each bug fix, each infrastructure-as-code change, each documentation update, etc.
We prefer short-life topic branches vs. committing to the `master` branch (or equivalent `develop` branch) because of multiple reasons:
1. A topic branch gives good code isolation for distributed development: I can share a topic branch with you, directly, without needing to merge to master.
2. A topic branch makes it really easy to do many kinds of popular git workflows; this includes many popular CI servers, git automation scripts, git aliases, etc.
3. A topic branch is great for integrating early and often into code reviews, and also with master. For example, we use a feature branch that uses feature toggles, and we can do CI/CD to propose changes to master as often as we want, and we can rebase the topic branch as often as we want. We can choose whether a repo will prefer merge, or rebase, or rebase with preserve, and between elided linear history or real graph history.
4. We favor automatic integration, meaning when we push a topic branch, and the reviews are all good, and the tests are all green, then the CI/CD does an automatic integration process, including an automatic deploy. When the topic is integrated, then the topic branch is deletable.
5. Finally, I've tried the "TBD just merge to master" approach with multiple teams, and inevitably it turns out to be significantly harder and more complex to coordinate, especially at speed, scale, and with more stakeholders.
If you're interested in git, you can see some of the git alias topic branch commands that we use:
https://github.com/GitAlias/gitalias/blob/master/gitalias.tx...
All teams I've worked for in the last few years basically did this.
Although we never distinguished between features/topics explicitly, the proces would be the same.
Having one branch per story is just very simple from a management perspective; it aligns user requirements, graphics design, development, code review and deployment into one trackable unit.
Stories are never too large so in practice code branches don't exist for too long, a couple of days usually. If two or more developers work on the same story they'll use the same branch. If two stories are code-wise interdependent they'll reuse each other's branches as they see fit.
Our git commit message conventions are:
https://github.com/joelparkerhenderson/git_commit_message
To store info about metadata, such as task board number, we edit the branch description, and enable the description to go into the merge message.
A good short intro to branch descriptions:
https://ericjmritz.wordpress.com/2015/11/13/using-branch-des...
This allow to have code reviews before something is actually in master (something you might or might not want depending on your worflow or criticity of the sw you are working on)
Rebasing (and squashing) allow to keep a linear and clean history.
Rebasing and squashing also have downsides, you lose the finer day to day commits, in exchange of ideal history, but at least it should be mentioned in a 2019 post about having too much feature branches in git...
kennethh posted a link that explains this well: https://martinfowler.com/bliki/FeatureBranch.html
With a big merge, others have time to inspect and know of breaking changes. with million tiny merges (or working on trunk, same) you either freeze change due to risk of breakage or end up with broken master regularly.
If you let them stall it becomes a mess, sure. If do it properly it's trivial.
With trunk-based development, everyone sees everyone else's work in progress and it's easier to integrate different features together. With feature branches, however, dev is more stable and there's more responsibility on merging to branch back to dev.
Are you deploying to public a few times a day like a web service, or are you working towards an early release, like a AAA game? Does your team sit together in one room, collaborating with one another, other do they take completely independent task and do them remotely, working from all across the globe, with the only point of interaction being a pull request once or twice a week?
It's impossible to say what's better or what's worse without these priors.
- "Show HN" when there is nothing really to show
- Submitted by the author of the post
- Author of the post is founder of company
- Provocative, click-baity Headline
We are better than that, HN.Anyway, this is an article that is on-topic, but it is not a good ShowHN. From https://news.ycombinator.com/showhn.html
> Blog posts, sign-up pages, and fundraisers can't be tried out, so they can't be Show HNs.
(Disclaimer: I prefer rebasing.)
[pull]
rebase = true
in your ~/.gitconfig, and just pull the branch your forked fromThis way, the git history is linear, clean and readable
Why is a linear history better again? If you cannot read history graphs, perhaps you should really not use a VCS where merging happens.
My issue with rebase is that as soon as you want a single other person to commit code to that branch, you can no longer rebase. Everything must be merged from that point on, otherwise the histories diverge and you wind up in rebase/merge hell.
The only alternative I've found is to over-communicate every push to your co-worker because it requires him to stash his changes, delete his local branch, pull down the "new" remote branch and re-apply his changes.
Also, rebase master into your feature branches regularly.
This is really important when there are parallel feature branches and even more so when features are codependent.
E.g. if feature a depends on feature b, if you rebase feature a you cannot just pull it into feature b as there will be duplicate commits.
If you want nice lines you should hire a painter. If you want easy merges, merge instead of rebasing.
While I agree with the author on the general idea of trunk-based development (and feature switches), can't we get the best of both worlds by allowing short-lived pull requests where code review can happen?
I'd be curious to hear from teams here who push directly to master and how this workflow works for them.
(But yeah, there's a problem with reduced testing during feature development. What if a feature branch breaks the main branch and it does not get noted during feature development? I'd argue if it affects the core then it's not strictly a "feature")
The advantage of trunk based development would be that the feature gets potentially more testing. Same goes for interactions with the main line code - if feature development breaks the core in subtle ways, it will be noticed earlier using trunk based development.
When you do checkpoint regular releases done often, you get a chance to smoketest and block a release. Automated smoke tests only go so far in fixing this - if you could cheaply, reliably and completely fully automate testing, everyone would already do that.
About the only way to do continuous deployment is to not deploy into production but use lagged deployment practice...
I believe that "trunk" is not commonly meant to be immediately deployed to users. But, maybe things are different in the web world, where "deploy" is as easy as "upload to server"?
The same thing happens if you have someone who just "disappears" for a day or two and effectively has a feature branch. You need to have discipline on your team to push often (I like to push at least every 20 minutes).
I also tend to think that continuous deployment with TBD is risky. It depends on how good your automated acceptance tests are, but I tend to like to do a manual sanity check before deployment. It's a lot easier when you are not working on a moving target.
Finally, sometimes you just have to break something in order to change its direction. This forces you into a feature branch if you are doing continuous deployment.
We've occasionally experimented with having an "integration branch" that acts like trunk but isn't deployed. You can then time your trunk deployments, potentially cherry picking from the integration branch. It seems like a good solution, but I've found it to be more trouble than its worth most of the time.
Having said all that, my preference would be to abandon continuous deployment and instead have regular deployment (say once a week or once every 2 weeks) with a mechanism for hot patches. Then force everyone to work mostly core hours and have continuous integration (with commits coming in every 5 minutes or so). In my experience this has produced dramatically better results than feature branches.
However... On my team it is impossible (not least because I'm 9 time zones away from the majority of my team mates). So feature branches are an acceptable second best.
That sounds extreme to me. What kind of development do you do that makes this possible? I rarely finish something within 20 minutes. Often, I'm thinking about the right way to do something for a day or longer before I decide to push some results. (UPDATE, before I even start to write some code!)
If you hold the code out for a whole day, there is no way for anyone else to know what you are doing (unless you interrupt them to tell them, or they somehow know to ask you). On a well functioning TBD team with true CI, everybody knows what everybody else is doing because they are constantly looking at diffs when they pull trunk.
But yes, it is extreme ;-) It's one of the 12 XP practices (continuous integration). Interestingly, the original idea behind CI servers was that when you push to trunk, you were never sure if there would be an integration problem. So if someone pulled trunk, they might get garbage. So the CI server was there so that you could get a visual indication of whether or not it was safe to pull trunk. And if it went red the whole team would instantly take a look to see what happened. It's a team based approach rather than an individual approach.
Some caveats: I was experimenting with a different way of doing OOP in JS. Here is a description of the design: https://gitlab.com/mikekchar/testy/blob/master/design.md There is also a coding standard that I was following: https://gitlab.com/mikekchar/testy/blob/master/coding_standa... It's not one that I advocate, it was one that I was experimenting with.
Some of the code in there is not great. Some of it is OK. It's not a big project, but it will give you an idea of the scope I use when I make commits. I use a very conservative approach when programming -- I don't pull everything apart and then try to build it back up. Instead I refactor everything and make incremental improvements. Every commit should improve either the quality of the code or add functionality. Especially in this project, I was building a test framework, so I didn't have a test framework to work with. This meant I had to use an even more conservative approach than normal. However, like I said it should give you a good idea.
Perhaps you think this is bad code. I don't think it is. It's got a strange style (because that was the point of the exercise), but generally it was quite nice to work with. There are 2 pretty nasty hacks that I made in order to get the framework bootstrapped. I would have improved them eventually, but I ended up ending the project early. If I were to really criticise my work here, I would say that my commit messages are pretty awful. Interestingly, the TODO items that I check off in the actual commit are quite good IMHO. Probably I should cut and paste them, but... it's a personal project in my spare time... I was being lazy :-P
I guess I'm responding to your comment mainly to hopefully open your mind to different ways of doing things. Maybe you'll be able to see it, maybe you won't. One of the things I've found in this job is that there are a lot of people who are stuck in "It's either my way or it's crap". Your comment seems to indicate that you might be one of those people. But in the case you are not, I hope you'll find the code interesting.
I've worked on small projects and I've worked on big projects (I once "owned" a million lines of code!). The style I use now evolved based on my experiences. My basic approach is to look at a piece of code and try to find a general strategy for what I want to do. If I don't know the code, that can take a long time, but once I've figured out what I'm doing, things can go pretty rapidly.
I make a TODO list of what I'm planning to do. Then I look at the code for a minute or two and ask myself, "Can I get there from here?" If the answer is "No", then I ask myself, "What would I ideally want?" Then I write that down in my TODO. Usually it's a fair number of things.
Normally those facilities require pure refactoring (i.e. changing the code in a way that doesn't change behaviour). I try to break up my challenge in such a way that I make an improvement to the code, and that keeps the behaviour intact. Sometimes before I start I need to write tests to document the current behaviour.
So a typical session would be: Write test(s) that are lacking. Check in code. Refactor code to provide facility(s) that I'm going to want. Check in code. Make a behavioral change with tests. Check in code. Then go back to my TODO list to get the next step.
I will leave "bad" code in the build -- sometimes for a very long time, if I don't have a good idea for how it "should" look. Even if there is a "good" way to represent it, I'll often put it off if I don't know that this "good" way is going to fit with the direction the code is going. Overworking a piece of code often leads to unintentional inflexibility and sometimes good looking code adds constraints that you don't need. However, because the code looks good, nobody will refactor it and remove the constraints, leading to overly complex code.
If you've gotten this far, you can see some of the challenges for TBD with this method, though. Every change has to keep the system completely functional! This means very deliberate and very careful modifications. It's difficult, but there is plenty of upside. IMHO there is more upside than downside by quite a margin. YMMV (and I won't tell you that you must be a crap programmer simply because you do things differently than me ;-) ).
Edit: I went through some of your commits on https://github.com/tshatrov/ichiran It's interesting to me that the commit size is not really that different than mine. There are a couple of places I would have split it up into 2 or 3 chunks, but we're not talking about order of magnitude stuff here. I'm curious how long you spend on a commit. Nice project, BTW. I'm not a stranger to building that kind of tool ;-) I actually built an English parser in FORTH once. I also built: https://github.com/mikekchar/JLDrill (which actually is awful code... and it doesn't actually run any more thanks to bitrot).
Seriously, this is the quintessential Gentoo Linux problem. Nobody can test all the combinations making the flags almost instantly useless. (And some essentially forced on.)
But I can definitely see too many feature flags leading to a byzantine web of untestable combinations.
But then, later in the article,
"It is 2019. If you don’t have a continuous integration setup that builds and runs tests automatically … then set it up yesterday. If you break anything you’ll be notified before it becomes a problem for the whole team."
Which, in my experience, greatly helps to mitigate the "it works on my machine" factor that arises from having a new branch for each task.
I appreciate the perspective, but I've found working with feature branches much better than trunk-based development. I'll take my merge conflicts any day. :-)
> Why you should not use feature branches
> Keep reading, all the objections you can think of are wrong.
The tone of the title and some parts of articles suggests that trunk-based development is objectively better than feature branches. I feel that it's rather a matter of opinion. You might want to try it out, it might work better for you than feature branches. But I don't think saying "feature branches are wrong approach" or "your objections are not valid" is true for everyone.
One example are code reviews.
> If the code review culture is strong in your team then it can very well be done on the commit to the main branch.
Personally I find this inconvenient. Reviewing a single commit does not show me the bigger picture. Let's say I refactor some code before adding a new feature. Refactoring is in a single commit, but to see whether this refactoring makes sense, it's better if the reviewer sees the next step, then they can understand better why I refactored it in this particular way.
> The main thing to improve here is probably not the branching model, but the code review process.
Maybe this is true. Maybe my way of thinking about code reviews is wrong. However, again, I feel it's a matter of preference. I tried both ways and I certainly prefer reviewing the whole feature than single commits.
One more problem I find with trunk-based development: it happened to me before that a developer did something completely wrong. They misunderstood the requirement, they misunderstood the code - it happens, the world's not perfect. With feature branch I'll just ask developer to start from scratch. `git branch -D` and that code is gone. With trunk-based development the code is already in the master branch, and while it obviously can be changed or reverted, it's already there, people already have pulled this code, they modify this code, they look at it and try to understand it, they try to figure out why it's there.
Again, I'm not saying here that trunk-based development is a bad idea. It might be working for you, it might not, everyone's different. I believe the article explains the benefits of trunk-based development pretty well, I wish though it wasn't presented as an objectively superior alternative to feature branches.