Git and GitHub for Real Projects: Branching Strategies That Actually Work
Git flow, trunk-based development, and the actual, practical middle ground most real teams land on — with the reasoning for why, not just a diagram of branch names.
A branching strategy is a real, practical decision, not a formality — the wrong one for a given team size and release cadence produces genuine friction: merge conflicts, unclear release state, features blocking each other's deploys.
Git Flow — heavyweight, for a reason that mostly doesn't apply anymore
Long-lived `develop` and `main` branches, feature branches merging into `develop`, release branches cut from `develop` for QA before merging into `main`. It was designed for a real constraint: infrequent, versioned releases (think: shipping a boxed software product, or an app store release cadence) where you genuinely need a stable staging ground separate from what's actively being developed. For a web app deploying continuously, that constraint mostly doesn't exist, and the extra branch layers become pure overhead.
Trunk-based development — lightweight, for continuous deployment
Everyone branches directly off `main` (or commits straight to it, for very small teams), merges back quickly — ideally within a day — and feature flags gate anything not ready for real users yet, rather than a long-lived branch holding the code back. This fits continuous deployment well specifically because it minimizes the time any branch spends diverged from `main`, which is exactly when merge conflicts and integration surprises compound.
The practical middle ground most real teams actually land on
main → always deployable
feat/booking-flow → short-lived, one focused change, merged via PR
fix/double-booking → same pattern for a bug fixShort-lived feature branches off `main`, one focused change each, merged via pull request with review, deleted immediately after merge. No long-lived `develop` branch to keep in sync. This is close to trunk-based development with a thin PR-review layer added — the actual reason it's so common is that it keeps Git Flow's real benefit (a review gate before code lands) without its real cost (long-lived branches drifting apart).
The rule that actually matters more than which strategy
Branch lifetime is the real variable to optimize, regardless of which named strategy a team nominally follows — the longer a branch lives before merging, the more it and `main` diverge, and the worse the eventual merge conflict gets. A short-lived branch under any strategy beats a long-lived one under any other strategy, which is the actual, transferable lesson underneath all of these named approaches.
Part 2 of the engineering practice series.