# Git flow: develop, release and hotfix branches

> The release-oriented branching model: long-lived main and develop, feature branches, a release branch for each version, hotfix branches, and tags on main. When it fits (app store releases, several supported versions), why many web teams moved to GitHub flow or trunk-based development, and how to recognise each model from a branch list.

- Course: [Git and GitHub for Product People](https://productovi.com/courses/git-for-product.md)
- Module: Team workflows
- Lesson 11/15, ~39 min
- Source: https://productovi.com/courses/git-for-product/git-flow

## "Proper releases"

**Alex Tutor:** A new week on CatChow's product team, and the autumn sale banner is out in the world.

**Sofia:** The sale banner went live on Monday at 8:00, exactly on time. And then at lunch the CEO asked why we don't have "proper releases, like every serious company".

**Alex Tutor:** Some serious companies do have them. Daniel, you worked like that before CatChow, didn't you?

**Daniel:** Three years on a fitness app for phones. A new version every two weeks, each one reviewed by Apple and Google before users could download it. We used **git flow**.

**Alex Tutor:** Git flow is a branching model first described in a widely read 2010 article. Its core idea: two branches that live forever, `main` and `develop`, plus three kinds of temporary ones.

```text
Commit graph, oldest first: Git flow in a mobile app team
1. commit 2c71a0e on main "Version 2.7.0", tagged v2.7.0
2. branch develop starts from commit 2c71a0e
3. commit 4d8e2b1 on develop "Update translations"
4. branch feature/workout-reminders starts from commit 4d8e2b1
5. commit 7a3f9c2 on feature/workout-reminders "Add reminder settings"
6. branch hotfix/2.7.1 starts from commit 2c71a0e
7. commit c5e0d38 on hotfix/2.7.1 "Fix crash on login" (highlighted)
8. merge commit e8b2f14: hotfix/2.7.1 merged into main "Merge hotfix/2.7.1", tagged v2.7.1
9. merge commit 0f6c3a9: hotfix/2.7.1 merged into develop "Merge hotfix/2.7.1 into develop"
10. commit 91be4d7 on feature/workout-reminders "Send reminder notifications"
11. merge commit 5b9d7e2: feature/workout-reminders merged into develop "Merge feature/workout-reminders"
12. branch release/2.8.0 starts from commit 5b9d7e2
13. commit a4c1e86 on release/2.8.0 "Bump version to 2.8.0"
14. commit 3e7f0b5 on release/2.8.0 "Fix reminder text on small screens"
15. merge commit 6d2a9f3: release/2.8.0 merged into main "Merge release/2.8.0", tagged v2.8.0
16. merge commit b8e4c71: release/2.8.0 merged into develop "Merge release/2.8.0 into develop"
```

*main holds only released versions, each tagged. develop collects the next release. Features branch off develop and return to it; a release branch is cut from develop and merged into main and back into develop; a hotfix starts from main and is merged into both.*

**Sofia:** Five lanes instead of our one-plus-short-branches. And merges going both ways.

**Alex Tutor:** Read it lane by lane. `main` holds only released versions, and every commit on it gets a tag: `v2.7.0`, `v2.7.1`, `v2.8.0`. `develop` is where the next version is assembled. The original article called the first one `master`.

**Daniel:** Then the temporary ones. `feature/*` for each feature, `release/*` to prepare one version, `hotfix/*` for an urgent fix to what users already have.

**Daniel:** And we always merged with a merge commit, never squash, so each feature stayed visible on `develop` as its own loop. That's what the original model recommends.

> **Exercise:** Match each git flow branch with its job.

## Merged is not released

**Sofia:** So a feature is merged into `develop`, not into `main`. Does that mean users get it?

**Daniel:** No. Merged into `develop` means "in the next version". Workout reminders were merged in the middle of a sprint and reached people's phones almost two weeks later.

**Alex Tutor:** That's the biggest difference for a PM. In our GitHub flow, merged means live within minutes. In git flow, merged means "on the train", and the train leaves on a schedule.

![A finished parcel waits in a warehouse loading bay among others for the next truck, while across town customers wait at their doors.](https://productovi.com/courses/git-for-product/inline/git-flow-illustration-2.webp)

**Sofia:** So on that team the useful question is "which release is it in?", not "is it merged?"

**Daniel:** Exactly the question our marketing learned to ask. "Merged" made them announce things users couldn't see yet.

**Alex Tutor:** GitHub shows the difference if you read closely. The pull request header says it was merged "into develop", and the newest tag is still the old version.

> **Exercise:** In git flow, once `feature/workout-reminders` is merged into `develop`, users who update the app get workout reminders.

**Alex Tutor:** Try it as a status line, the kind marketing actually reads.

> **Exercise:** On Daniel's old app team (git flow), reminders were merged into `develop` on Tuesday. The next release branch is cut on Monday. Which status line would you post for marketing?

## Cutting a release branch

**Alex Tutor:** A **release branch** is where one version gets ready to ship. When `develop` holds enough for the next version, someone cuts it from `develop`: `release/2.8.0`. From that moment it's a separate lane.

**Daniel:** On the release branch we only fixed bugs, changed the version number and wrote release notes. No new features. Testers hammered it for a week.

**Alex Tutor:** Meanwhile `develop` stays open, so new features for 2.9.0 keep arriving. When the release is ready, it's merged into `main`, tagged `v2.8.0`, and merged back into `develop` so its fixes aren't lost.

**Sofia:** And a feature merged into `develop` a day after the cut?

**Daniel:** Missed the train. It waits for 2.9.0. That's why the cut date was the most important date in our calendar, and PMs fought over it every sprint.

![In git flow a feature merged into develop waits for the release cut, a week of testing and store review; users see it only after updating, almost two weeks later.](https://productovi.com/courses/git-for-product/diagrams/m4-l2-release-train.png)

![A train pulls out of the station as a person with a parcel runs onto the platform too late; the next train is visible far down the line.](https://productovi.com/courses/git-for-product/inline/git-flow-illustration-3.webp)

*Miss the cut date, and the feature rides the next release.*

**Alex Tutor:** For planning, git flow gives you a calendar: a cut date, a week of stabilisation, a release date. Roadmaps in such teams often read like "2.8.0: reminders, 2.9.0: step goals".

**Sofia:** That sounds predictable, actually.

**Daniel:** For the calendar, yes. The price is waiting: even a one-line fix on `develop` rides the next train, unless it's urgent enough for a hotfix.

> **Exercise:** Which features reach users in version 2.8.0?

**Alex Tutor:** The cut date is where PMs fought every sprint. True or false?

> **Exercise:** In git flow, `release/2.8.0` was cut from `develop` on Monday. A feature merged into `develop` on Tuesday still ships in 2.8.0, as long as it's merged before `v2.8.0` is tagged.

## Hotfix branches

**Sofia:** And if something breaks for users between releases? Wait two weeks?

**Daniel:** Not for a crash. Version 2.7.0 crashed on login for some users. We branched `hotfix/2.7.1` straight from `main`, from the `v2.7.0` tag, fixed only that, and shipped it in two days.

**Alex Tutor:** A **hotfix** starts from what users have, not from `develop`, which is full of unreleased work. It's merged into `main` with a new tag, `v2.7.1`, and into `develop`, so the next release doesn't bring the crash back.

![A mechanic patches a tyre on a car already out on the road with its driver, while unfinished new cars stand on lifts in the garage behind.](https://productovi.com/courses/git-for-product/inline/git-flow-illustration-4.webp)

*A hotfix starts from what users have, then goes back into the work in progress.*

**Daniel:** One wrinkle: if a release branch is open at that moment, the fix goes into it instead of `develop`. It reaches `develop` when the release is merged back.

**Sofia:** Why does it matter whether it goes back? The users already have the fix.

**Daniel:** Because 2.8.0 is built from `develop`. Forget the merge back, and version 2.8.0 crashes on login again. I've seen it happen once. Once was enough.

> **Exercise:** In git flow, version 2.7.0 of the app crashes on login for some users. Put the steps of the hotfix in order.

**Alex Tutor:** Now Daniel's wrinkle: what if a release branch is open at that moment?

> **Exercise:** In git flow, `hotfix/2.7.1` fixes a login crash. At that moment `release/2.8.0` is already open for testing. Besides `main`, where should the hotfix be merged?

## When git flow fits

**Alex Tutor:** Git flow fits when releases are events: a mobile app that goes through store review, software customers install and update when they like, or several versions you support at once.

**Sofia:** And for a website like ours, where there's only ever one version live?

**Alex Tutor:** Then `develop` mostly adds waiting and double merges. Many web teams moved to GitHub flow or to **trunk-based development**: everyone merges small changes into `main` at least daily, with flags hiding what isn't ready.

**Alex Tutor:** Even the model's author agreed. In 2020 a note was added to the original article: for web apps delivered continuously, a simpler workflow like GitHub flow fits better, and git flow stays for explicitly versioned software. Side by side: [git flow vs GitHub flow](/blog/github-flow-vs-git-flow).

**Daniel:** Which matches my experience. For the app it was right. Here it would slow us down for nothing.

**Sofia:** What does trunk-based look like day to day?

**Daniel:** Branches live hours, not days. You merge half a feature on Tuesday, switched off, and the other half on Wednesday. It needs very good automated tests and a lot of discipline.

> **Exercise:** Which team is the most natural fit for git flow?

## Spot the model from the branch list

**Alex Tutor:** You'll meet all three in other repositories: a partner's app, an agency, your next job. The branches page gives the model away in seconds.

```text
Branches of the fitness app repository
- [Branch] develop: Default · updated today
- [Branch] main: Updated 9 days ago · tag v2.8.0
- [Branch] release/2.9.0: Updated yesterday · 3 ahead of develop
- [Branch] feature/step-goals: Updated 2 hours ago · pull request into develop
- [Branch] hotfix/2.8.1: Updated 1 hour ago · pull request into main
```

**Alex Tutor:** `develop` next to `main`, plus `release/…` and `hotfix/…` names: git flow. Often `develop` is even the default branch, so new pull requests target it.

**Daniel:** Just `main` and a few fresh `fix/…` or `feature/…` branches with pull requests: GitHub flow, like us. Almost nothing but `main`, branches that live hours, lots of flags: trunk-based.

**Sofia:** Daniel's old repository is the textbook case, then: `develop` as the default, a release branch and a hotfix at the same time.

**Sofia:** And the tags page shows whether they number their versions at all.

> **Exercise:** Match each repository's branch list with the model it most likely uses.

## Announcing on a release train

**Alex Tutor:** Now step into Daniel's old team for a moment, back in the week reminders were merged. Marketing sees "reminders merged" in the team chat and wants to announce them on Friday.

**Daniel:** The release branch is cut next Monday, then a week of testing, then store review, usually a day or two.

**Sofia:** And even then users have to update the app before they see anything.

**Alex Tutor:** Write the reply you'd send marketing. Two or three sentences.

> **Exercise:** You are the PM on Daniel's old app team. `feature/workout-reminders` was merged into `develop` on Tuesday. `release/2.8.0` is cut next Monday, tested for a week, then submitted to the stores (review usually takes a day or two). Marketing asks: "Reminders are merged, can we announce them this Friday?" Reply in 2–3 sentences.

## Should CatChow switch?

**Sofia:** Back to the CEO. He'll want an answer, not a lecture about lanes.

**Alex Tutor:** Then find out what he's really worried about. "Proper releases" usually means "I want to know what changed and be sure nothing breaks".

**Daniel:** Both of which we can get without `develop`. The "what changed" part is next lesson: tags and release notes.

**Alex Tutor:** Write him a short recommendation. Start with the answer, then the reasons.

> **Exercise:** Write a short recommendation to the CEO: should CatChow switch to git flow?

**Sofia:** So my answer to "proper releases" is: yes to version numbers and release notes, no to `develop`.

**Daniel:** I'll set up our first tag this week. Bring coffee, and questions about what `v1.4.0` means.

- Previous lesson: [GitHub flow: small branches, fast merges](https://productovi.com/courses/git-for-product/github-flow.md)
- Next lesson: [GitHub releases and versions: tags, SemVer, changelogs](https://productovi.com/courses/git-for-product/releases-and-versions.md)

---
Productovi Learning. Free, no sign-up needed to read.
