A practical Git branching strategy for small teams

Small teams need a Git branching strategy that keeps the default branch stable, keeps review visible, and avoids process that is heavier than the work. A simple trunk based approa…

Small teams need a Git branching strategy that keeps the default branch stable, keeps review visible, and avoids process that is heavier than the work. A simple trunk based approach with short lived topic branches is usually enough.

Start with a stable default branch

Treat the default branch as the integration branch. It should represent code that has passed review and the required automated checks. Do not use it as a place to collect unfinished work.

Protect the default branch in the hosting platform. A useful minimum is:

  • require a pull request before merging
  • require at least one approving review
  • require status checks to pass before merging
  • block force pushes to the protected branch
  • block deletion of the protected branch

Those controls matter because a small team has fewer people available to notice accidental direct pushes, broken builds, or unreviewed changes. Branch protection turns the desired workflow into a repository rule instead of a team memory test.

Use short lived topic branches

Create a branch for each small change. A branch may hold a feature, bug fix, refactor, dependency update, or documentation change. Keep the scope narrow enough that the pull request can be reviewed in one sitting.

A simple naming pattern is enough:

git switch -c fix/login-timeout

Use lower case words separated by hyphens. Avoid encoding too much process into the branch name. The issue tracker, pull request title, and pull request description can carry the details.

Short lived branches reduce merge conflicts because each branch spends less time diverging from the default branch. They also make review easier because the reviewer can understand the whole change without reconstructing weeks of context.

Keep long running branches rare

Long running branches are sometimes unavoidable, but they should not be the default. They create delayed integration risk: code appears safe inside the branch but conflicts with the real product state when it finally comes back.

Prefer smaller slices behind configuration, feature flags, or inactive code paths when a change cannot ship at once. The goal is to merge safe pieces early while keeping unfinished behaviour disabled.

Use a long running branch only when there is a clear reason, such as a release stabilisation branch. Give it an owner, an expiry point, and a merge policy. Without those limits, the branch becomes a second default branch.

Decide how changes reach the default branch

For most small teams, the merge policy should be consistent and boring. Pick one of these and document it.

Squash merge

Squash merge keeps the default branch tidy by turning a pull request into one commit. It works well when contributors make many small fixup commits during review.

The trade off is that individual commits from the branch are not preserved on the default branch. That is acceptable if the pull request is the unit of review and the squashed commit message explains the final change clearly.

Merge commit

A merge commit preserves the branch topology. It is useful when the team wants to see exactly when a topic branch was integrated.

The trade off is a noisier history, especially when many small branches are merged every day.

Rebase and merge

A rebase based workflow can keep history linear by replaying branch commits onto the default branch without a merge commit. It works best when branches are private to one author and are rebased before merge.

The risk is rewriting commits that other people may already have based work on. Do not require developers to rebase shared branches unless the team understands the impact and has a clear recovery process.

Keep release branches simple

A small team does not need a complex release model for every repository. If the product deploys continuously, the default branch may be the release source.

If releases need stabilisation, create a release branch from the default branch at the cut point:

git switch main
git pull --ff-only
git switch -c release/2026-06

Only merge fixes that are needed for that release. After the release, merge or cherry pick the fixes back to the default branch if they are not already there. Do not continue normal feature work on the release branch.

Keep hotfixes visible

For an urgent production fix, branch from the commit that matches the affected release, or from the current default branch if that is what production runs. Make the smallest safe change, review it, run the checks, and merge it through the same protected path.

Avoid direct pushes even under pressure. The process is most valuable when the change is urgent, because urgent changes are more likely to miss a step.

Delete branches after merge

Delete topic branches after they are merged. The useful record is the merged commit, pull request, review discussion, and linked issue. Keeping old branches around makes the branch list harder to read and increases the chance that someone restarts work from stale code.

Document the workflow in the repository

Put the workflow in a short contributor document. Include:

  • the default branch name
  • the branch naming convention
  • the expected pull request size
  • the required checks
  • the merge method
  • the hotfix process
  • the rule for release branches

A small document is easier to follow than an oral tradition. It also helps new contributors understand how to get a change merged without guessing.

Conclusion

A good branching strategy for a small team is simple: protect the default branch, use short lived topic branches, review every change, require passing checks, and avoid long running branches unless there is a specific release need. The best workflow is not the one with the most branch types. It is the one the team can follow every day without bypassing it.