Git workflows for small teams: trunk-based development vs feature branches
How trunk-based development, short-lived feature branches and GitFlow compare for teams of two to twenty, and the habits that make whichever you choose run smoothly.
Most workflow arguments are really about one thing: how long code stays away from the main branch. The longer a branch lives, the more it drifts, the bigger the merge, and the later you find out something is broken. Choosing a workflow is choosing how much of that risk you accept.
The three common options
Trunk-based development. Everyone integrates into main at least daily, usually through small pull requests that live for hours, not weeks. Unfinished features hide behind feature flags. main is always releasable.
Short-lived feature branches (GitHub flow). Each change gets a branch and a pull request, reviewed and merged in a day or two. Deploy from main. This is the practical middle ground most small teams use.
GitFlow. Long-lived develop and main branches plus feature, release and hotfix branches. It was designed for software shipped as versioned releases. For a web service that deploys many times a week, it mostly adds ceremony.
| Trunk-based | Short feature branches | GitFlow | |
|---|---|---|---|
| Branch lifetime | hours | 1 to 3 days | days to weeks |
| Merge pain | minimal | low | can be high |
| Needs feature flags | yes | sometimes | rarely |
| Fits | continuous deployment | most web teams | versioned products, mobile apps |
What actually makes a workflow work
The branching model matters less than these habits.
Small pull requests
Aim for changes a reviewer can read in 15 minutes, under a few hundred lines. Split work vertically: a migration first, then the API, then the UI, each mergeable on its own. Small PRs get reviewed faster, break less, and are easy to revert.
Fast, trustworthy CI
Every push should run linting, type checks and tests in a few minutes. Protect main so a PR can only merge when CI passes and someone has approved it:
# .github/workflows/ci.yml
name: ci
on: [pull_request, push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.12" }
- run: pip install -r requirements.txt
- run: ruff check . && pytest -q
If CI is flaky, people stop trusting it and start merging anyway. Fixing flaky tests is workflow work, not optional cleanup.
Feature flags for unfinished work
Merging incomplete code is safe when it's switched off:
if flags.enabled("new_checkout", user):
return new_checkout(cart)
return old_checkout(cart)
Flags let you merge early, test in production with a few users, and roll back by flipping a switch instead of reverting commits. Remove each flag once the feature is fully live, or they pile up.
A dev or staging branch, if you need one
Some teams keep a dev branch that deploys to a staging environment, then fast-forward main once dev has been checked. That's a reasonable safety step for a small team without a large automated test suite. Keep it a fast-forward of main rather than a parallel history, so the two never diverge.
Clean history where it helps
- Squash-merge PRs so
mainhas one commit per change with a clear message. - Write commit messages that say why, not just what.
- Rebase your branch on
mainbefore merging if it has fallen behind, so conflicts are resolved by the person who understands the change.
Choosing for your team
- 2 to 5 people, deploying often: short-lived feature branches, protected
main, squash merges. Add feature flags when a change takes more than a couple of days. - Strong test suite and continuous deployment: move towards trunk-based development; it removes most merge pain.
- Shipping versioned releases (libraries, mobile, on-premises): release branches make sense, which is the part of GitFlow worth keeping.
Whatever you choose, measure two things: how long PRs stay open and how often main is broken. If both are low, your workflow is working.
Written by RecallRun Editors for the RecallRun community. Community posts are checked for safety and reviewed by our editors before publishing, but the views and claims are the author's own. Links are the author's; open them with care. Report this post.
More from the community
- Tech articles
Logs, metrics and traces: a practical introduction to observability with OpenTelemetry
What each signal is for, how they fit together through trace ids, and how to instrument a Python service with OpenTelemetry without drowning in data.
- Tools
gitcollect: group GitHub and GitLab repos into collections, with per-repo access control
A Go CLI that groups repos from several orgs into one collection and controls who can clone which repo. Updated for v3.3.0: smarter scan, whole-module moves, self-update, and team-based access on the way.
- Tech articles
Vector embeddings explained for developers: similarity, normalisation and the mistakes that hurt search
What an embedding really is, how cosine similarity and dot product relate, why you should normalise, and the practical mistakes that quietly make semantic search worse.
Share a tech article or a tool you built. Every post is checked and reviewed before it goes live.