Git Collaboration Workflows¶
Feature branch + pull request¶
The dominant workflow in most teams: branch off main for a unit of
work, push it, open a pull request, get review + automated checks,
merge. The entire value of this workflow depends on branches staying
short-lived — a feature branch open for weeks accumulates drift
from main that makes the eventual merge painful, which is exactly
the problem trunk-based development (below) is designed around.
Trunk-based development vs. Git Flow¶
Trunk-based development: everyone works off main (the "trunk")
with short-lived branches (hours to a couple of days), merged
frequently. Incomplete work that would break main if deployed is
hidden behind a feature flag rather than kept on a long-lived branch
— the code is merged and deployed, just not activated, decoupling
"is this code in main" from "is this feature live for users."
Git Flow: a heavier convention with dedicated long-lived branches
(develop, release/*, hotfix/*) and structured rules for when each
merges into what. It was designed for projects with infrequent,
versioned releases (desktop software with release cycles) — for a
continuously-deployed web service, the extra branches mostly add
process overhead without a matching benefit, which is why trunk-based
development has become the more common default for that kind of
project specifically, not a universal replacement for Git Flow's
original use case.
Rebase-before-merge vs. merge commits, as a team convention¶
This connects directly to Branching and Merging:
some teams require rebasing a feature branch onto main before
merging (keeping main's history linear, one commit per logical
change), others allow merge commits (preserving the exact shape of
how work happened). Neither is objectively correct — it's a real trade
between "easy to read/bisect later" (favors rebase) and "preserves
exactly what happened, including exactly when branches diverged"
(favors merge commits) — but a team should pick one and apply it
consistently, since a history that mixes both conventions
inconsistently is harder to read than either applied uniformly.
Common pitfall¶
Letting a feature branch live for weeks "because the feature isn't done yet" and treating that as the reason to avoid merging early is backwards — it's precisely the long branch lifetime that makes the eventual merge risky (more conflicting changes accumulated on both sides), not a reason to delay merging further. Trunk-based development's answer — merge early and often, gate the incomplete feature behind a flag instead of behind a long-lived branch — exists specifically to break this trap, by decoupling "is the code merged" from "is the feature exposed to users," which a long-lived branch conflates into one all-or-nothing event.