Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This was terrible at the time and it remains terrible now. Extremely few teams need this level of complexity and some parts of it – like the redundant long-lived develop branch – are pointless safety blankets. It seems to have only caught on because a lot of people felt adrift when it comes to Git and this was written far too authoritatively for what it is. I’ve seen so many small teams tie themselves up wasting time working for Git Flow that I think this might actually be the worst thing to ever happen to version control. I’m glad the author has mostly walked it back, but unfortunately the damage has been done.

If you are putting a team together and haven’t picked a Git workflow and are unsure what to do, start with something simple like trunk-based development and add complexity if and when you encounter problems that justify it.

https://trunkbaseddevelopment.com

Previous discussions:

Please stop recommending Gitflow: https://news.ycombinator.com/item?id=22485489

GitFlow considered harmful: https://news.ycombinator.com/item?id=9744059

The problem with Git Flow: https://news.ycombinator.com/item?id=23622071



Strong agreement with everything you've said here. I can't say enough good things about trunk-based development. I started managing a team with 15+ developers. They had used long-lived branches, with a develop, a staging, and a release branch. Not using TBD, "releases" were a huge ceremony and extremely inertial. Velocity went way, way up when we adopted TBD. It took some time for people to become comfortable with the new approach, not because it's complex, but precisely because they had grown so used to unnecessary complexity and had to unlearn many things.


> Strong agreement with everything you've said here. I can't say enough good things about trunk-based development.

I don't agree. Git flow is just trunk development applied to release-centric, non-web projects, after you learned very basic lessons on how to put together a production-ready release. There is absolutely no good reason to advocate in favour of ignorance and force you to reinvent the wheel on a process that exists for decades.

Failing to understand these needs only set you up to learn hard lessons the hard way at a time you least want to learn them: when Product is pushing you to release the damn thing and asking you why you are incapable of just posting a bugfix over a previous release.


In my experience, git tags are far more effective at managing releases than git branches. Each version is released once and only once. The relative immutability of a tag is a higher guarantee of success than the merge swamps that long-running release branches can sometimes become. If you need a bug fix to a previous release you check out the tag of the past release into a new short-lived branch, PR what you need to for the bug fix to that branch (and also any long term changes to the trunk branch), similarly version tag any releases made from the short-lived branch and then delete it.


> In my experience, git tags are far more effective at managing releases than git branches.

I'm sorry to say that your experience doesn't seem to be with production software with a significant release cadence. Tags fail very basic requirements of any release process. A release requires release-specific changes such as bumping version numbers, toggling configurations and feature flags, update changelogs and documentation, and more importantly accommodate bugfixes without having to merge in unrelated work on features actively under development that are not intended to ship.

Tags are only good to point to specific commits. Unless you're working on a pet project or you're the single developer working on software no one uses, a release is way more than pointing to a random commit in the development branch.


You don't know my experience and making assertions about what you don't know here verges on an ad hominem attack. Please rethink your approach.

I never said tags come only from a single development branch, I said that release branches are best when short-lived and that tags are a better long-term representation of released versions than long-running release branches.

If you want to discuss the complexities of the differences in workflow that entails, feel free to ask questions without assuming you have any idea what my experience level is.


Second that.

If one names development=trunk it is the same.

In git flow team also should not be active on release branch, it should only be fixes for the release and release branches should not be long lived. Well you might have staging or acceptance main branch but you don’t use it like long lived branch once you start release development/trunk becomes new truth so team should not maintain these “branches” really.

Developers worry only about trunk/development an their own feature benches in git flow. For me it is exactly the same as TBD.


> It seems to have only caught on because a lot of people felt adrift when it comes to Git and this was written far too authoritatively for what it is.

My hypothesis is the design of the page and the images played a big role in convincing people this is "legit".


It seemed much beloved by teams who’d used SVN or other source controls that had incremental version numbers, switched to Git, and strongly wanted to implement their old workflow in Git.

I worked at a shop that tried really hard to pretend that it was possible to strictly order commits (“What if dev A writes commit 1234, then dev B writes commit 5678 and pushes it, then dev A pushes 1234. Which came first?” “The earlier one!” “What does ‘earlier’ mean here?” “Why can’t you just do it!”) and they thought Git Flow was the greatest thing ever.

Put another way, it was popular among the same people who write every language as though it were their favorite. Have Python but they only have Java experience? All their Python looks like Java! Well, they thought Git Flow was spiffy, too.


This is the second comment I've seen in this thread comparing "Gitflow" to what was typical in SVN. Having been there when SVN was in vogue, I can assure you that our process looked nothing like Gitflow. SVN encourages what we now refer to as "trunk based development". The default branch was even called "trunk".


I was used to SVN too, and you’re right about the trunk bit. I’m mainly talking about SVN’s nicely linear revision numbers and how it was unambiguous that r23 came after r22. That’s the part that GitFlow adherents wanted to simulate.


I used SVN extensively since the early 2000's, and while I still don't like git (at least for projects that also involve non-coders), I liked git-flow even less (and as the sibling poster said, SVN encouraged a much simpler trunk-based development model).

My impression was that git-flow was pitched by people who didn't have much experience with other version control systems and were also new to git.


> it was possible to strictly order commits

This holds no relevance at all. Nothing in gitflow calls for strictly ordered commits. Just because there is a branch represented with semver that does not mean that the revision control system is involved in versioning.


Thank you.

This blog post is responsible for more toil than anything else I can think of.

> It seems to have only caught on because a lot of people felt adrift when it comes to Git and this was written far too authoritatively for what it is.

I think "Git Flow" was successful for the same reason that Javascript "Standard" linter was: misleading branding that lends credibility.


Agreed. In my teams, I've always implemented a simple model, which hasn't failed me. Main branch triggers deployment, and name rest of the branches <author>/<feature>. Just works.

In some cases, an extra caveat to the above was triggering deployment/build in Main, only when the tag/version number gets incremented.


> Agreed. In my teams, I've always implemented a simple model, which hasn't failed me. Main branch triggers deployment, and name rest of the branches <author>/<feature>. Just works.

So you work with a small team that does not need to put together releases nor does have quality assurance engineers verifying your code before it's released. That's perfectly fine.

Your team also has the luxury of pausing any additional development work while lining up a release. That's cool too.

Perhaps you don't even have releases, and just continuously integrate everything you throw at it to deploy it somewhere and rely on a multi-stage pipeline to catch problems. That's fine. That's also not the usecase for gitflow.

I don't think it's a good idea to project your team's needs to absolutes, and pretend that just because you don't benefit from a process it means none do.


You are making too many assumptions in the answer there; most of the things you assume about me, my team or experience are not correct :)

I do think the gitflow model is over-complicated.


Personally, I simply don't want to see any merge-commits, ever. Merges are hard to follow and may include conflict resolutions which are hard to show (if they show up at all).

Sure, "merging" is an amazing feature. But I am not working on projects with an organizational complexity like the Linux kernel. Short-lived & rebased branches so that code gets integrated into main fast. Conflict resolution is never postponed nor shared. Versioning/release/deploy/rollback is dealt with mostly outside of git, or with tags-on-main at most.


Merge commits also break regression finding, since if you checkout a commit that was part of a merge, you land in a tree state reflecting what tree at this commit looked like before the merge, which is a state the master branch has never been in. It often breaks things which work both before and after the merge commit. So you can never find with certainty the regression point at commit level, other than locally linearising this merge into a series of cherry picks. It’s just awful.


On the other hand, rebasing can create its own problems re: regression finding

If you rebase more than one commit, you can also have an intermediate commit which does not run fine, or even compile. This can happen silently, without conflict resolution while you rebase. Think a function call in trunk needs one additional parameter.

So you have a commit message saying "Protocol X tested successfully with Y", which was true when the code done and tested. After the rebase, it might not run anymore because of changes done in the trunk. You will probably notice it and correct it in a later commit, but good luck in 3 month when someone points a bug at protocol X and you want to re-run the test in this commit...


That’s why at work we have a “commit queue” that tests that every commit builds as it comes in in the order it’s merged (or more accurately: cherry-picked). Don’t leave those things to manual checks and human vigilance. They’re too fallible.


I think this can be mostly worked-around by requiring branches (PRs) to be rebased and tested/CI-passed before merging.

If PRs contain multiple commits, either squash them (on merge or before), or use some kind of queued / stacked PR system.


--first-parent, and both with git bisect and many other things, is your friend here.


That doesn’t fix the issue I described. It leads to a situation where you can say that a merge introduced a regression, but you don’t know which of the merged commits did, because it skips over them all. So it’s not commit-level.


> Merges are hard to follow and may include conflict resolutions which are hard to show (if they show up at all).

My experience is much the opposite. You can follow all the merge parents and three(+)-way diff them with the final merge result to see just the conflict resolutions in a merge.

On the other side, tracking down accidental bad conflict resolutions (which result in regressions) in rebased branches is nearly impossible because there's no single commit that just represents the conflict resolutions; depending on the developer doing the rebases they might be smeared across every commit in the entire branch. It gets hard to tell what changes were deliberate and which were conflict resolutions that the developer handled in some way maybe not so deliberately. There are some massive failure cases for regressions I've seen including having to get deep into the woods of git's rerere cache infrastructure and forcing developers to clear that cache so that they stop reintroducing the same kinds of hard to track down regressions in their rebases. (I am grateful that this worst case behavior is rare enough not everyone feels it; I've seen it just enough I prefer that junior developers rarely to never rebase because merge commits preserve more of the process and don't hide the mistakes under the rug.)

That said, I definitely agree that short-lived branches and code getting integrated as fast as possible so that conflict resolution is done early and often and as visibly as possible is a good idea.


> Versioning/release/deploy/rollback is dealt with mostly outside of git, or with tags-on-main at most.

This is simply outright wrong. Releases are not a tagged versions of a development branch. There is stuff that belongs in releases but should not be present ever in develop branches at all, and vice-versa.

Once you need specific changes going into a release, you need branches.

Also, you should not block development work on the development branch just because you need to push a release.

Also, if you find a bug in a release candidate, you fix that bug alone. You do not add other changes just because someone committed them to the development branch.

Cutting a release means picking up an unstable version and work your way up to make it stable. This means freezing the introduction of additional random features, and commit bugfixes only. Tagging does not allow that.


> Once you need specific changes going into a release, you need branches.

I don't have this need. Then I don't need to deal with the sheer insanity of loosely spreading all those different git refs you mention over everything including the future.

Changes get integrated fast into main, because nothing is blocked, frozen, or left for someone else in the future. This also makes commits/PRs/changes small. Feature flags are used to ease this even more.

In the face of absolute disaster it is possible to simply revert/rollback a release/deploy. However in practice I don't really need to do this, almost never. Some hotfix here and there maybe, but it's still all much faster than the sheer insanity of git-flow overcomplications. Often a hotfix is the only solution, a revert/rollback fixes nothing when the problem is outside of git (DBs, client storage...).

In the end, you have to evaluate all the time you sink into this model, and how much risk you actually mitigate. Wherever I've seen git-flow, the overhead was huge and ever-growing, and it never really helped with any risk mitigation. I would go as far as saying that overall it increased the risk, due to its complexity.


> There is stuff that belongs in releases but should not be present ever in develop branches at all, and vice-versa.

Divergence between development branches and release branches may be a sign of stuff that should be configuration-only/environment-configurable and outside of source control. "Build-once, run-everywhere" is a useful mantra: once a version has been cut, if it can be built once and only once and run in every development or release environment, you know that you are stably testing the same build artifacts in your QA environments that you will eventually release.

> Also, if you find a bug in a release candidate, you fix that bug alone. You do not add other changes just because someone committed them to the development branch.

If you are marking your released versions with git tags then you just build a short-lived branch from the release candidate tag, PR the fix to the short-lived branch (and any long term changes to the main/trunk branch), and cut the bug fix version from the short-lived branch (tag the new version). You don't need long-lived release branches to bug fix older versions and releases.


Thanks for this insight. This is useful advice for those who are re/discovering some of these practices for newer projects and would benefit from adopting a more saner approach.

I see a reference to GitHub Flow which seems to make sense: https://docs.github.com/en/get-started/quickstart/github-flo...


I agree. Seeing this again with the added “successful” attribute made me think: “successful at what?”

At influencing developers? Yes. At helping developers? I want to say no.

I don’t want to blame the author for this catching on, but I don’t think it was helpful that it did.


I've worked in an environment where they used git-flow, but it was very much a "traditional" (?) software company; they offered long term support, that is, backporting patches to older released versions, that kinda thing.

But they also had long lived branches that lived for like 9 months. It was still manageable because the feature was usually limited to one 'domain', and they usually had only one or two people working on the feature at once.


From your linked source:

>* main for the Git community since 2020 (master with unsavory connotations before)

That was fake news. There were no "unsavory connotations" before. I can safely discard anything that author states from then on.

EDIT: And like clockwork, the author has #BLM and pronouns in his profile.


Agreed, but your linked article on trunk-based development does include this strange idea:

> the core requirement of Continuous Integration that all team members commit to trunk at least once every 24 hours


That’s been the case since day one; it’s not something trunk-based development invented.

Remember, continuous integration is literally that – people integrate their changes continuously. It’s not just “we run Jenkins”.

https://martinfowler.com/articles/continuousIntegration.html...


The article suggests *GitHub Flow* not Git-Flow. Which from reading does just seem like trunk based development.

After a while, I think we all just end up there from exhaustion.


The article is the original git-flow article. It suggests GitHub Flow for projects with continuous deployment and no version numbers in a 2020 addendum, but otherwise, it's the overengineered git-flow.


> This was terrible at the time and it remains terrible now.

I strongly disagree. This branching model didn't came out of the blue. This branching model is a direct mapping between regular run-of-the-mill release processes for applications intended to be delivered and installed by the general public, and how Git implements branches.

Let's actually look at the git flow workflow branching model and see what it does. There are:

* a develop branch. Where commits/pull requests are continuously integrated as part of the everyday development work. Is this complex? No.

* feature branches. Where developers present their changes as part of pull requests prior to merging them onto the development branch. Is this complex? No. Software development platforms such as github and gitlab even support creating them automatically as part of the normal ticket creation workflow, and even support triggering dedicated pipeline workflows.

* Release branches. These are used to pick a specific state of the development branch to prepare it to be releasable to the public, and prevent it from receiving additional features. This involves things like bumping up version numbers, update changelogs and docs, flip up feature flags, and more importantly trigger pipeline workflows to generate production-ready installers. These installers are then subjected to build verification tests, manual tests, deployment tests, and Product managers use them to evaluate what users will receive. Is this complex? No.

* bugfix branches. What happens if a bug is found in a release candidate? You push a fix, of course. You push it directly into the release branch and afterwards create another release candidate version to be subjected to BVTs and manual tests. You also need to fix the bug in the development branch. Is this complex? No.

* The prod branch. Once your release candidates are finalized, you cut a release. This can mean tweaking version numbers and docs again, but the goal is to persist the exact version that was released to the public. Also, if an important bug is found in production, such as a crash, you're going to have to prepare a patch release shipping only a fix for it. This branch is used to fork a new release candidate, tweak stuff like version numbers, changelogs, docs, get the fix in, run regression tests, have Product Managers double-check that the bug is fixed, and release a new version. Is this complex? No.

I followed this exact branching model for all non-web projects I worked on for a timespan that goes over a decade.

Here's the fun part: I followed it before I even knew this page existed. Why? Because this is what you eventually end up converging to if you are a professional doing professional work with a professional team to deliver a production ready version of a product built by professionals.

Why is that? Because real world requirements emerge. You learn the hard way that you cannot pause continuous integration when preparing a release, and thus you need release candidate branches to not block other developers. You learn the hard way that you need to do additional work over a release candidate in order to get it ready for production. You learn the hard way that after you release a version of your app then eventually you will need to scramble to release another version to ship a critical bugfix. You learn the hard way that you need to work in multiple releases in parallel. You learn the hard way that sometimes a release is scrapped because the Product Manager changes their mind and instead of shipping a hotfix we ship additional features. Etc etc etc.

Now, there are things in this branching model that might not be ideal. For example, merging release branches back into a single prod/master/mainline branch is convenient as a way to ensure the stable release is front and center in a git repo, but it doesn't work if you need to have multiple releases for multiple versions. In the last few years the teams I worked on ended up not merging release branches anywhere, and just kept them dangling out of the development branch. This helps manage the complexity of a major release bump where you need to continue maintaining both the new and old release versions, and possibly even ship minor/patch versions of the old release.


> Because this is what you eventually end up converging to if you are a professional doing professional work with a professional team to deliver a production ready version of a product built by professionals.

This is a ridiculous thing to say. GitLab says it’s too complex. GitHub says that it’s too complex. Linus Torvalds doesn't use it. Both Google and Facebook use trunk-based development. You think none of these people are professionals?


> This is a ridiculous thing to say.

I understand that inexperienced people oblivious to basic requirements of a simple release process might have a hard time understanding why those requirements need to be addressed.

> GitLab says it’s too complex. GitHub says that it’s too complex. Linus Torvalds doesn't use it.

I recommend you think things through by yourself instead of holding on to meaningless appeals to authority.

For starters, you should read what the CEO of Gitlab actually said about Gitflow. The "complexity" examples boiled down to complaining about using rebase, which is really immaterial to the discussion and pointless nitpicking, as nothing forces anyone to rebase anything anywhere nor does it have a fundamental role in gitflow, and complaining that "there should be one main branch" is frankly a pointless remark. That's the full extent of the nitpicking.

Name-dropping Linus Torvalds to point out he doesn't use it is also a very silly attempt at an appeal to authority. I mean, the Pope himself doesn't use trunk-based development. Is that relevant?

Your silly attempt at an appeal to authority is outright absurd when you factor in the fact that the Linux release process is far more complex and convoluted than gitflow, involving cutting release branches by pulling cherry-picked changes from countless remote repositories which may or may not even track the same point in the branch.

https://www.kernel.org/doc/html/v4.15/process/2.Process.html

https://news.ycombinator.com/item?id=9744606

> Both Google and Facebook use trunk-based development.

Are you really sure? I know for a fact that you are not, and I call out your bullshit by stating that I know for a fact that some FANG teams use Gitflow, because I worked in them.

> You think none of these people are professionals?

I know that your baseless and silly appeals to authority show you are certainly not a professional with any degree of experience doing releases, and that's where your name drop attempts end.


> This branching model is a direct mapping between regular run-of-the-mill release processes for applications intended to be delivered and installed by the general public, and how Git implements branches.

Git-flow kinda works for only one model, in which you have a single release out to the world at all times. If you have multiple concurrent supported releases, like Windows 2000/XP/Vista, or Chrome stable/beta/dev/canary, this model does not work.

The `master` branch and short-lived release branches in git-flow are a complicated dance and a lot of merging without much clear benefit. Compare with a trunk-based model:

* new features go into main branch (from feature branches)

* when making a big release, create a branch like release/23.09, do whatever preparations and testing you need, and release 23.09.0 - tag the commit that the release is built from

* bugfixes go into main branch (from feature branches) and then are cherry-picked into release branches (hopefully via pull requests)

* if the bug exists in release/23.08 and release/23.09, we can fix both and release 23.08.x + 23.09.y simultaneously

This model is much easier to comprehend, has less branching, and allows maintenance for older versions.


Out of curiosity...you mention fixing multiple releases at once (release/23.08 and release/23.09).

in your example, are you keeping those older release branches around forever in git, or deleting them as soon as the release is made and the commit tagged? You would recreate something like release/23.08 from the tag, right?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: