Workflows: How Teams Actually Use Git

This chapter covers how teams use git day to day: trunk-based, feature branches, PRs, and picking a strategy that fits your project.

Why Workflow Matters

The commands in Chapters 1 to 9 don't care about process. You can commit directly to main or run a seven-branch parallel release system, using the exact same commands.

Workflow is about the human side: who can push where, how changes get reviewed, how releases happen. Pick one that matches your team size, deploy cadence, and risk tolerance. Don't invent a new one.

Trunk-Based Development

The simplest workflow. Everyone commits to main (or a short-lived branch that merges to main within a day). No long-lived branches. No release branches.

main: ---A---B---C---D---E---F---G--- (production)

How It Works

  • Every commit on main should be deployable.
  • A feature that isn't ready is hidden behind a feature flag, not kept in a side branch.
  • CI runs on main after every merge; green means shippable.
  • Releases are tags (v1.4.0) on main, not separate branches.

When It Fits

  • Small teams (up to ~20 engineers).
  • Continuous deployment, or near-daily releases.
  • Strong CI with fast, reliable tests.
  • Feature flags in place for in-progress work.

When It Doesn't

  • Regulated environments that require formal release approvals.
  • Teams where features regularly take weeks and must be committed incrementally.
  • No CI, or slow/flaky CI.

Feature Branches (GitHub Flow)

Work happens on short-lived branches. Each branch becomes a pull request. Merging to main triggers a deploy (or is always deployable).

main: ---A---B----------M---N (production)
              \        /
feature/x:     C---D---E (PR, merged via M)

How It Works

  • Branch from main for any change: feature/login, fix/rate-limit.
  • Push early; open a draft PR.
  • Rebase or merge main periodically to stay current.
  • When reviewed and CI passes, merge (typically squash-merge).
  • Delete the branch.

Merge Options in a PR

Three styles. Pick one per repo.

  • Squash-merge. The whole branch becomes one commit on main. Clean linear history, loss of intermediate detail. The most popular default.
  • Merge commit. Full branch history is preserved, with a merge commit on top. More detail, messier graph.
  • Rebase-merge. The branch's commits are replayed onto main linearly, no merge commit. Clean and detailed, but intermediate commits must each be green (few teams enforce this).

Squash-merge is the right default for most teams. Chapter 12 has the long version.

When It Fits

  • Most teams most of the time.
  • Works with GitHub, GitLab, Gitea, Bitbucket.
  • Scales from two engineers to a thousand.

GitFlow

A heavier-weight workflow with multiple long-lived branches: main, develop, plus release/*, hotfix/*, and feature/* branches.

main:     ---M1--------------M2--------M3 (tags: releases)
              \              /\        /
release:       R---R---R----/  \      /
              /                 \    /
develop:  --D1---D2---D3---D4----D5-/---D6--- (integration)
                  \          /       \
feature:           F---F---F/         \
                                       hotfix

How It Works

  • develop is integration. Features merge to develop.
  • Periodically, release/1.4.0 branches off develop, gets stabilized, then merges to main as a new release.
  • Production bugs get hotfix/* branches off main, fixed and merged back to both main and develop.

GitFlow is popular in tutorials and unpopular in working teams. It was designed for a world of quarterly releases and manual QA gates. If your team ships weekly or faster, GitFlow is overhead without benefit.

When It Fits

  • Scheduled, versioned releases (desktop apps, firmware, libraries).
  • Regulated environments where release branches need independent stabilization.
  • Teams that genuinely support multiple released versions in parallel.

When It Doesn't

  • Continuous deployment.
  • Web apps where "the release" is whatever's on main.

Release Branches (Without Full GitFlow)

A middle-ground pattern for teams that mostly do trunk-based but need an occasional release branch to stabilize a version while main moves on.

main: ---A---B---C---D---E---F---G (development continues)
                  \
release/1.4:       C---C1---C2 (fixes during release, tagged 1.4.0)
  • Cut a release/1.4 from main when you're close to shipping.
  • Fix bugs on the release branch; cherry-pick those fixes back to main.
  • Tag the release from the branch.

Works well when you need a stable candidate for final QA while feature development continues.

Hotfixes

The pattern for "production is broken and we can't wait":

git switch main
git pull
git switch -c hotfix/session-leak
# fix and commit
git push -u origin hotfix/session-leak
# open PR, merge fast

Key points:

  • Hotfix branches branch from main (or the release tag in production), not from develop or a feature branch.
  • Keep the change as small as possible. Bigger fixes go through normal review.
  • Cherry-pick the fix to any parallel branches that need it (old release branches, for example).

Choosing a Strategy

A practical decision tree:

  1. Are you deploying continuously or near-daily? → Trunk-based or feature branches with squash-merge.
  2. Do you have long-lived parallel versions in production? → Release branches on top of feature-branch flow.
  3. Are you shipping once a quarter with heavy manual QA? → Maybe GitFlow, though even here simpler often wins.
  4. Are you a team of 1 to 3 prototyping? → Just commit to main. Worry about workflow when you have collaborators.

Write it down. A CONTRIBUTING.md that says "branch from main, open a PR, squash-merge" prevents 90% of workflow arguments.

Pull Request Practices

Whatever branching model you pick, PRs work better with a few habits:

  • Keep them small. A PR that touches 40 files usually contains three PRs glued together. Split it.
  • One logical change per PR. A review that mixes a refactor and a feature is harder and slower.
  • Write a description. What, why, how to test. Future-you reads these when grepping commits.
  • Review your own PR first. Catches more embarrassing typos than any reviewer will.
  • Rebase before review, merge after. Rebase keeps your branch up to date; merge happens when the PR is approved.

Common Pitfalls

Adopting GitFlow because you read a blog post. Most teams don't need it. Overhead is real; simpler is usually better.

Long-lived feature branches. A branch that lives for a month accumulates conflicts at an exponential rate. Merge main in weekly, or split the work into smaller PRs.

No CI gating merges. Without CI, main drifts into a broken state and nobody notices until someone deploys. Protect main in the repo settings so PRs can't merge without green CI.

Deploying from a branch other than main. Confusing and error-prone. The deployed branch should be obvious. Usually it's main.

Branch name chaos. Pick a convention (feature/*, fix/*, chore/*) and enforce it with a hook if you have to. Makes sorting branches much easier.

Next Steps

Continue to 11-tooling.md for the tools around git.