Branching Workflows
A branch is cheap in Git, but how a team uses branches decides whether work flows smoothly or ends up in merge-conflict pain. This page covers three things: how to use a feature branch, which team strategy to pick, and how to name branches so everyone can read them.
Feature branches
Feature branches and short-lived branches.
A feature branch is a branch you create for one piece of work: a new feature, a bug fix, or a small cleanup. You do the work there, and merge it back into the main branch when it is done.
1main: A --- B ----------------------- E (merge)2 \ /3feature/login-page: C --- D --------------Why Use a Feature Branch?
mainstays stable. Half-finished code never lands on the shared branch.- You work in isolation. Your experiments do not break anyone else.
- Review happens before merging. A pull request lets teammates read the change and run tests first.
- It is easy to throw away. If the idea fails, delete the branch. Nothing else is affected.
Because a branch is only a pointer to a commit, creating one is instant and free.
The Everyday Workflow
git switch maingit pull # start from the latest maingit switch -c feature/login-page # create the branch and move to it# ...edit files...git add .git commit -m "Add login form"git push -u origin feature/login-page # share the branch, then open a pull request# After the pull request is merged:git switch maingit pullgit branch -d feature/login-page # delete the local branchgit push origin --delete feature/login-page # delete the remote branch (GitHub can do this for you)
Why Short-Lived Matters
A feature branch should live a short time: usually hours to a couple of days, not weeks.
The reason is drift. While your branch sits there, everyone else keeps committing to main. The longer you wait, the further apart the two sides get.
1Long-lived branch (3 weeks):2 3main: A --- B --- C --- D --- E --- F --- G --- H (8 new commits from others)4 \5feature: X --- Y --- Z --- ...6 Merge day: many conflicts, a huge review, high risk.7 8Short-lived branch (1 day):9 10main: A --- B11 \12feature: X13 Merge day: tiny change, few conflicts, easy review.| Long-lived branch | Short-lived branch |
|---|---|
| Many merge conflicts | Few or no conflicts |
| Huge pull request, hard to review | Small pull request, fast to review |
| Bugs appear late, at merge time | Bugs appear early |
| Others cannot see or build on your work | Work is shared quickly |
How to keep branches short:
- Break big tasks into small pieces. Merge each piece on its own.
- One branch, one purpose. Do not mix a bug fix with a refactor.
- Merge often. Aim for pull requests that can be reviewed in minutes.
- Hide unfinished work behind a feature flag, so you can merge it without users seeing it.
Keeping Your Branch Up to Date
If main moved while you worked, bring those changes into your branch before you merge. Do this often, so each update is small.
git fetch origingit rebase origin/main # replay my commits on top of the latest main (straight history)# orgit merge origin/main # add a merge commit (keeps history as it happened)# After a rebase, the branch history changed, so push safely:git push --force-with-lease
Rule: Only rebase a branch that you alone use. Never rebase a branch that others have pulled.
How the Branch Gets Merged
Most platforms give three options for finishing a pull request.
| Option | What happens | Result on main |
|---|---|---|
| Merge commit | Keeps all branch commits and adds one merge commit | Full history, with branch shape |
| Squash and merge | Combines all branch commits into one commit | Clean history, one commit per feature |
| Rebase and merge | Replays each branch commit on top of main | Straight line, no merge commit |
For short-lived branches, squash and merge is a popular choice. Your messy "wip" and "fix typo" commits become one clear commit.
Feature Flags: Merge Early, Release Later
Sometimes a feature takes longer than a few days. Instead of keeping a long branch, merge small pieces early and hide the unfinished feature behind a flag that is turned off.
public Receipt checkout(Cart cart) {if (featureFlags.isEnabled("new-checkout")) {return newCheckout(cart); // unfinished code, off for real users}return oldCheckout(cart); // current behavior}
The code is merged and deployed, but nobody sees it until the flag is switched on. This keeps branches short and keeps main safe.
Team strategies
GitHub Flow, trunk-based development, and Git Flow. Know when each fits. Trunk-based development is commonly chosen by teams that practice CI/CD (continuous integration and continuous delivery).
A branching strategy is a set of team rules: which branches exist, where work starts, and how it reaches production. The three common ones differ mainly in how many long-lived branches they use and how often they release.
GitHub Flow
The simplest strategy. There is one long-lived branch: main. It is always ready to deploy.
1main: A ---------------------- D (merge, then deploy)2 \ /3feature/login: B --- C -------------4 |5 Pull request: review + automated testsThe steps:
- Branch from
mainwith a clear name. - Commit your work and push the branch.
- Open a pull request. Teammates review it and the tests run.
- Merge into
mainonce it is approved and the tests pass. - Deploy from
main, then delete the branch.
Rules:
- Anything on
mainis deployable. - Never commit directly to
main. Use pull requests. - Merge often and keep branches small.
Fits: web apps and services that deploy often, and small to medium teams.
Trunk-Based Development
Everyone works on one shared branch, called the trunk (usually main). Work is merged very often, at least once a day per developer.
1main: o---o---o---o---o---o---o---o---o (small commits, every day)2 \ / \ / \ /3 o o o (short branches: under 1 or 2 days)Developers either commit straight to the trunk (small, trusted teams) or use very short-lived branches that merge within a day or two.
What makes it work:
- Small changes. Big work is split into tiny steps.
- Feature flags. Unfinished features are merged but switched off.
- Strong automated tests. Every merge runs a CI pipeline, so a broken trunk is noticed and fixed within minutes.
- Fast reviews. Small pull requests are reviewed quickly, or code is written in pairs.
Releasing: you release from the trunk, often straight through CI/CD. Some teams cut a short release branch or a tag at release time for stability.
Why teams like it: work is integrated constantly, so there are no giant merges. Releases are small and frequent, which makes bugs easier to find.
Fits: teams with good tests and CI/CD that deploy many times a week or day.
How is it different from GitHub Flow? They are close. If you use GitHub Flow with branches that live only a day or two and merge many times a day, you are basically doing trunk-based development. The difference is the emphasis: trunk-based insists on very short branches and feature flags.
Git Flow
A strategy built for planned, versioned releases. It uses two long-lived branches and three kinds of short-lived branches.
| Branch | Created from | Merged into | How long it lives |
|---|---|---|---|
main | - | - | Forever. Holds released code, tagged with versions |
develop | main | - | Forever. The next release being built |
feature/* | develop | develop | Days to weeks |
release/* | develop | main and develop | Until the release ships |
hotfix/* | main | main and develop | Hours to days |
The flow:
- Developers build features on
feature/*branches and merge them intodevelop. - When enough is ready, create
release/1.4.0fromdevelop. Only bug fixes are allowed there. - When the release is ready, merge it into
main, tag it (v1.4.0), and merge it back intodevelop. - A bug found in production? Create
hotfix/1.4.1frommain, fix it, then merge it into bothmainanddevelop.
# Start a releasegit switch developgit switch -c release/1.4.0# ...only bug fixes and version bumps here...# Ship itgit switch maingit merge --no-ff release/1.4.0git tag v1.4.0# Bring the fixes back into developgit switch developgit merge --no-ff release/1.4.0git branch -d release/1.4.0
Fits: software with versions and scheduled releases, such as mobile apps, desktop software, and libraries, and products that must support several versions at once.
Drawbacks:
- It is complex, with many branches to track.
developandfeature/*branches can live for a long time, which leads to big merges.- It is slow for continuous delivery, where you want to release many times a day.
Comparison
| GitHub Flow | Trunk-Based | Git Flow | |
|---|---|---|---|
| Long-lived branches | main | main (the trunk) | main and develop |
| Branch lifetime | Short (days) | Very short (hours to 1 or 2 days) | Features can last weeks |
| Release style | Deploy from main | Deploy from trunk, often | Planned release branches |
| Feature flags needed? | Sometimes | Yes, usually | Rarely |
| Needs strong CI/tests? | Yes | Yes, essential | Helpful |
| Complexity | Low | Low | High |
| Best for | Web apps, small teams | Teams practicing CI/CD | Versioned software |
How to Choose
- Do you release many times a week and have good tests? Choose trunk-based, or GitHub Flow with very short branches.
- Do you want a simple setup for a web app with pull requests? Choose GitHub Flow.
- Do you ship numbered versions on a schedule, or support old versions? Choose Git Flow.
Rule of thumb: use the simplest strategy that fits your release process. Do not add
developandrelease/*branches unless you really need them.
Naming conventions
Prefixes such as feature/, fix/, hotfix/, and release/.
A good branch name tells you what kind of work it is and what it is about, without opening it. A common format is:
1type/short-description2type/ticket-id-short-descriptionCommon Prefixes
| Prefix | Used for | Example |
|---|---|---|
feature/ | A new feature | feature/login-page |
fix/ | A bug fix (some teams use bugfix/) | fix/null-pointer-in-cart |
hotfix/ | An urgent fix for production | hotfix/payment-timeout |
release/ | Preparing a release, often with a version | release/1.4.0 |
chore/ | Maintenance: dependencies, config, cleanup | chore/update-dependencies |
docs/ | Documentation only | docs/api-guide |
refactor/ | Code cleanup with no change in behavior | refactor/split-order-service |
test/ | Adding or fixing tests | test/cart-edge-cases |
The first four (feature/, fix/, hotfix/, release/) are the ones to know well. The others are optional extras that many teams add.
Naming Rules
- Use lowercase letters.
- Use hyphens (
-) between words, not spaces or underscores. - Keep it short and clear: a few words that describe the work.
- Add the ticket ID if you use a tracker, so the branch links to the task.
- Do not use spaces or special characters like
~,^,:,?,*, or[. - Do not put your name or the date in the branch name. The commit already records them.
Good and bad examples:
| Bad | Good | Why |
|---|---|---|
my-branch | feature/user-profile | Says nothing about the work |
Fix Login Bug | fix/login-redirect | Spaces and capital letters |
feature/AddTheNewLoginPage | feature/login-page | Too long, mixed case |
akash-work | feature/PROJ-142-search-filter | Named after a person, not the task |
test | test/cart-edge-cases | Too vague |
With a Ticket ID
git switch -c feature/PROJ-142-search-filtergit switch -c fix/PROJ-188-cart-total-roundinggit switch -c hotfix/PROJ-201-payment-timeoutgit switch -c release/1.4.0
Many trackers (like Jira) and platforms can link a branch to its ticket automatically when the ticket ID is in the name.
Slashes Make Folders (and One Trap)
The / in a name acts like a folder. Many Git tools use it to group branches together, so feature/* shows up as one group.
There is one trap: you cannot have both a branch named feature and a branch named feature/login. Git stores each branch as a file, and feature cannot be both a file and a folder.
Prefixes Match the Strategy
| Strategy | Prefixes you will mostly see |
|---|---|
| GitHub Flow | feature/, fix/, and the other type prefixes |
| Trunk-based | Short names with the same prefixes, merged within a day or two |
| Git Flow | feature/, release/, hotfix/, plus the develop branch |
Enforce the Rule
A rule that nobody checks soon gets ignored. You can check names automatically, for example in a CI step, so a wrong name fails early.
BRANCH=$(git rev-parse --abbrev-ref HEAD)PATTERN="^(feature|fix|hotfix|release|chore|docs|refactor|test)/[a-z0-9._-]+$"if ! echo "$BRANCH" | grep -Eq "$PATTERN"; thenecho "Invalid branch name: $BRANCH"echo "Use: type/short-description, for example feature/login-page"exit 1fi
Your Git host's branch protection rules can also protect main and release/* branches, so nobody pushes to them directly.
Summary
- A feature branch holds one piece of work, so
mainstays stable. - Short-lived is better. Long branches drift away from
mainand cause big, risky merges. Keep branches to hours or a couple of days, and use feature flags to hide unfinished work. - GitHub Flow: one
mainbranch, pull requests, deploy frommain. Simple and a good default for web apps. - Trunk-based development: merge into the trunk at least daily, with feature flags and strong CI. Commonly chosen by CI/CD teams.
- Git Flow:
mainanddevelop, plusfeature/*,release/*, andhotfix/*. Best for versioned releases, but complex. - Name branches as
type/short-description:feature/,fix/,hotfix/,release/. Use lowercase and hyphens, and add the ticket ID when you have one.
Interview Answer: Which Branching Strategy Would You Use?
I use feature branches: a small branch for one piece of work, merged through a pull request after review and automated tests. I keep branches short-lived, because the longer a branch lives, the more it drifts from main, and the bigger the merge conflicts and the review become.
For the team strategy, it depends on how we release. GitHub Flow is simple: one main branch that is always deployable, with work merged through pull requests. Trunk-based development goes further: everyone merges into the trunk at least daily, with feature flags to hide unfinished work and strong CI to keep the trunk healthy. It is a common choice for CI/CD teams. Git Flow uses main, develop, and release/* and hotfix/* branches, which fits versioned software with scheduled releases, but it is more complex and slower for continuous delivery.
For a web service that deploys often, I would choose trunk-based or GitHub Flow. For a mobile app with numbered releases, I would consider Git Flow.
I name branches as type/short-description, such as feature/login-page, fix/cart-total, hotfix/payment-timeout, and release/1.4.0, in lowercase with hyphens, and with the ticket ID when there is one.