Introduction — Why Branching Strategies Matter
If you can only change one thing about your engineering organisation's delivery speed, change the branching strategy. Research from the DORA team (Accelerate, 2018) consistently shows that deployment frequency correlates strongly with team performance — and your branching model is the single biggest constraint on how often you can deploy.
A branching strategy is your team's agreement about when code splits from the main line, how long it lives in isolation, and what conditions must be met before it merges back. Get this wrong and you'll face merge conflicts measured in days, releases delayed by weeks, and hotfixes that break other features.
The Relationship Between Branching and Deployment Frequency
Consider this spectrum: at one extreme, a team using full GitFlow with release branches might deploy every 2-4 weeks. At the other extreme, a team using trunk-based development deploys dozens of times per day. Both can be correct — for different contexts.
flowchart LR
A[GitFlow] --> B[GitLab Flow]
B --> C[GitHub Flow]
C --> D[Trunk-Based Dev]
A --- E["Deploy: 2-4 weeks"]
B --- F["Deploy: 1-2 weeks"]
C --- G["Deploy: Daily"]
D --- H["Deploy: Multiple/day"]
style A fill:#132440,color:#fff
style B fill:#16476A,color:#fff
style C fill:#3B9797,color:#fff
style D fill:#BF092F,color:#fff
The critical question isn't "which strategy is best?" but rather "what does your business need?" A medical device company with FDA compliance needs GitFlow's rigour. A startup shipping a SaaS product needs trunk-based development's speed.
GitFlow — The Classic Enterprise Model
GitFlow is a branching model — a set of rules that prescribe which branches exist, what each branch is for, and how code moves between them. It was designed to manage software that ships discrete, versioned releases (v1.0, v2.0, v2.1.1) rather than software that deploys continuously.
Vincent Driessen published "A successful Git branching model" in 2010, and it became the default for enterprise teams. The core idea is separation of concerns by branch: stable released code lives on main, in-progress development accumulates on develop, individual features are isolated on short-lived feature branches, and the release stabilisation process happens on dedicated release branches — so new feature work never interferes with release hardening.
GitFlow defines five branch types with strict rules about how code flows between them.
The Five Branch Types
| Branch | Lifetime | Purpose | Created From | Merges Into |
|---|---|---|---|---|
main |
Permanent | Production-ready code | — | — |
develop |
Permanent | Integration branch | main | release, main |
feature/* |
Temporary | New feature work | develop | develop |
release/* |
Temporary | Release preparation | develop | main + develop |
hotfix/* |
Temporary | Production bug fixes | main | main + develop |
gitGraph
commit id: "init"
branch develop
commit id: "dev-1"
branch feature/login
commit id: "feat-1"
commit id: "feat-2"
checkout develop
merge feature/login id: "merge-feat"
branch release/1.0
commit id: "bump-version"
commit id: "fix-typo"
checkout main
merge release/1.0 id: "v1.0" tag: "v1.0.0"
checkout develop
merge release/1.0 id: "back-merge"
checkout main
branch hotfix/1.0.1
commit id: "critical-fix"
checkout main
merge hotfix/1.0.1 id: "v1.0.1" tag: "v1.0.1"
checkout develop
merge hotfix/1.0.1 id: "hotfix-merge"
When GitFlow Works
GitFlow excels in specific contexts:
- Multiple supported versions — If you maintain v2.x and v3.x simultaneously (desktop software, mobile SDKs, embedded firmware), GitFlow's release branches are essential
- Scheduled releases — If your organisation releases on fixed cadences (quarterly, monthly), the release branch gives a stabilisation period
- Regulatory compliance — When each release requires sign-off, audit trails, and traceability, GitFlow's explicit branching makes compliance documentation straightforward
- Large teams with siloed features — When 50+ developers work on independent features that must be batched into releases
Common Pitfalls
The most common GitFlow failure is long-lived feature branches. When a feature branch lives for 3+ weeks, it diverges so far from develop that merging becomes a multi-day project. The solution — if you must use GitFlow — is to keep feature branches under 5 days and rebase frequently.
GitFlow in Practice — Step by Step
Let's walk through a typical day for a developer using GitFlow. Imagine you're adding a user authentication feature to an e-commerce application. In GitFlow, you always branch from develop (never from main) because develop contains the latest approved work that hasn't been released yet. Here's the complete lifecycle of a feature branch:
# ===================================================
# GitFlow: Complete Feature Branch Lifecycle
# ===================================================
# CONTEXT: You're adding user authentication to an
# e-commerce app. Your team uses GitFlow, so all new
# feature work branches off 'develop'.
# ===================================================
# Step 1: Start from the latest develop branch
# (Always pull first to ensure you're not behind)
git checkout develop
git pull origin develop
# Step 2: Create a feature branch with a descriptive name
# Convention: feature/[ticket-number]-short-description
git checkout -b feature/user-authentication
# Step 3: Work on the feature (multiple commits are fine)
git add src/auth/jwt-validator.ts
git commit -m "feat: add JWT token validation middleware"
git add src/auth/login-handler.ts
git commit -m "feat: implement login endpoint with bcrypt"
# Step 4: Stay up-to-date with develop (do this DAILY)
# This prevents painful mega-merges at the end
git fetch origin
git rebase origin/develop
# If conflicts occur, resolve them now while they're small
# Step 5: When the feature is complete and tested, merge back
# The --no-ff flag preserves the feature branch in history
# (creates a merge commit even if fast-forward is possible)
git checkout develop
git merge --no-ff feature/user-authentication
git push origin develop
# Step 6: Clean up — delete the branch (it's merged)
git branch -d feature/user-authentication
git push origin --delete feature/user-authentication
When enough features accumulate on develop and the team is ready to ship, a release branch is created. Think of this as a "feature freeze" — no new features are added, only bug fixes and version bumps. This gives QA time to test without new code landing underneath them:
# ===================================================
# GitFlow: Complete Release Branch Lifecycle
# ===================================================
# CONTEXT: Your team has finished 5 features on develop
# and is ready to ship version 2.1.0. You create a release
# branch to stabilise the code before it goes live.
# ===================================================
# Step 1: Create the release branch from develop
# This "freezes" the feature set for this release
git checkout develop
git checkout -b release/2.1.0
# Step 2: Bump version numbers in all relevant files
echo "2.1.0" > VERSION
git add VERSION
git commit -m "chore: bump version to 2.1.0"
# Step 3: Fix ONLY release-critical bugs here
# New features go to develop for the NEXT release
git commit -m "fix: correct pagination off-by-one error"
git commit -m "fix: handle null user in profile endpoint"
# Step 4: When ready to ship, merge into BOTH main AND develop
# Merge to main (this IS the release)
git checkout main
git merge --no-ff release/2.1.0
git tag -a v2.1.0 -m "Release 2.1.0 — user auth + dashboard"
git push origin main --tags
# Merge back to develop (so develop gets the bug fixes)
git checkout develop
git merge --no-ff release/2.1.0
git push origin develop
# Clean up
git branch -d release/2.1.0
GitHub Flow — The Simplified Model
GitHub Flow is a dramatically simpler alternative to GitFlow, created by GitHub's own engineering team in 2011. Where GitFlow has five branch types with complex rules, GitHub Flow has just two concepts: a permanently deployable main branch, and short-lived feature branches.
The philosophy behind GitHub Flow is radical simplicity: if main is always deployable, you don't need release branches. If you deploy immediately after every merge, you don't need a separate develop branch. If bugs are fixed the same way as features (branch → PR → merge → deploy), you don't need hotfix branches either.
GitHub Flow strips branching down to its essence: there is main (always deployable) and feature branches (short-lived work). That's it. No develop branch. No release branches. No hotfix branches.
gitGraph
commit id: "init"
branch feature/search
commit id: "search-1"
commit id: "search-2"
checkout main
merge feature/search id: "PR #42"
commit id: "deploy-1" tag: "deployed"
branch feature/dark-mode
commit id: "dark-1"
checkout main
branch fix/typo
commit id: "typo-fix"
checkout main
merge fix/typo id: "PR #43"
commit id: "deploy-2" tag: "deployed"
merge feature/dark-mode id: "PR #44"
commit id: "deploy-3" tag: "deployed"
The GitHub Flow Process
- Create a branch from main with a descriptive name
- Add commits — small, atomic, well-described
- Open a Pull Request — start discussion, request review
- Review and discuss — CI runs tests automatically
- Merge — squash or merge commit into main
- Deploy — main is always deployable; deploy immediately
Let's see how this looks in practice. Notice how much simpler the commands are compared to GitFlow — there's no switching between develop and main, no release branches to manage, and no back-merging:
# ===================================================
# GitHub Flow: The Complete Workflow
# ===================================================
# CONTEXT: You're adding a search API to a SaaS
# application. Your team deploys multiple times per
# day, so there's no need for release branches.
# ===================================================
# Step 1: Create a branch directly from main
# (In GitHub Flow, main is ALWAYS the source of truth)
git checkout main
git pull origin main
git checkout -b feature/add-search-api
# Step 2: Make small, focused commits
# Each commit should be a logical unit of work
git add src/search.ts
git commit -m "feat: implement search endpoint with Elasticsearch"
git add tests/search.test.ts
git commit -m "test: add search endpoint integration tests"
# Step 3: Push your branch and open a Pull Request (PR)
# The PR is where discussion, review, and CI happen
git push origin feature/add-search-api
# Open PR via GitHub CLI (or use the GitHub web interface)
gh pr create --title "feat: add search API" \
--body "Implements full-text search using Elasticsearch" \
--reviewer @team-lead
# Step 4: CI runs automatically on the PR
# Tests, linting, and build checks must pass
# Teammates review the code and leave comments
# Step 5: After approval, merge via GitHub UI or CLI
# Squash merge combines all commits into one clean commit
gh pr merge --squash
# Step 6: main is deployed to production AUTOMATICALLY
# Your CI/CD pipeline picks up the new commit on main
# and deploys it — typically within minutes
GitHub Flow works best for:
- Web applications and SaaS — where there's only one version in production
- Small to medium teams (2-20 developers)
- Continuous deployment — deploying multiple times per day
- Products without multiple supported versions
GitHub's Own Deployment Practice
GitHub itself uses GitHub Flow (naturally). Their engineering team deploys to production dozens of times per day. Every merged PR triggers a deployment pipeline that includes automated tests, canary deployment, and progressive rollout. The average time from merge to production is under 15 minutes.
Key enabler: comprehensive automated testing (>50,000 tests) combined with feature flags for incomplete work. Engineers can merge code that isn't customer-visible yet.
GitLab Flow — The Middle Ground
GitLab Flow was introduced by GitLab in 2014 as a pragmatic compromise between GitFlow's complexity and GitHub Flow's simplicity. It solves a real-world problem that GitHub Flow ignores: what happens when you can't deploy every merge to production immediately?
Many organisations have regulatory requirements, staging environments that need manual QA sign-off, or customer-facing demo environments that must stay stable. In these cases, GitHub Flow's "merge to main = deploy to production" model breaks down. But GitFlow's five-branch overhead is still too heavy.
GitLab Flow sits between GitFlow's complexity and GitHub Flow's simplicity. It introduces environment branches that map to deployment environments. Developers work exactly like GitHub Flow (branch from main, open MR, merge to main), but then code is promoted through environment branches in order:
Environment Branches
In GitLab Flow, code flows downstream through environment branches. Each branch corresponds to a real deployment target. Merging into an environment branch triggers deployment to that environment. Here's a typical promotion flow from development through staging to production:
# ===================================================
# GitLab Flow: Environment Branch Promotion
# ===================================================
# CONTEXT: Your company has a staging environment
# where QA validates changes before production.
# You can't deploy every merge immediately — changes
# must pass staging validation first.
# ===================================================
# Step 1: Developers merge to main (like GitHub Flow)
# This is where day-to-day development happens
git checkout main
git merge --no-ff feature/new-dashboard
# At this point, the feature is on main but NOT in staging or production
# Step 2: Promote main → staging
# This can be manual ("we're ready to test") or automated
# Merging into 'staging' triggers a deployment to the staging server
git checkout staging
git merge main
git push origin staging
# → CI/CD pipeline deploys to https://staging.yourapp.com
# Step 3: QA validates on staging...
# (manual testing, stakeholder review, etc.)
# Step 4: After staging validation, promote to production
# Merging into 'production' triggers the production deployment
git checkout production
git merge staging
git push origin production
# → CI/CD pipeline deploys to https://yourapp.com
# KEY INSIGHT: Code ONLY moves forward (main → staging → production)
# You never merge backward (production → staging)
# This ensures every environment is a subset of the one before it
Comparison with Other Flows
| Aspect | GitFlow | GitHub Flow | GitLab Flow |
|---|---|---|---|
| Long-lived branches | main + develop | main only | main + environment branches |
| Release branches | Yes (mandatory) | No | Optional |
| Deploy trigger | Merge to main | Merge to main | Merge to environment branch |
| Staging environment | Ad-hoc | Feature branch deploys | Dedicated branch |
| Best for | Versioned releases | Continuous deployment | Regulated environments |
Trunk-Based Development
Trunk-Based Development (TBD) is the most radical simplification of all branching strategies. The word "trunk" is an older term for the main branch (from Subversion/CVS days) — think of it as the trunk of a tree from which all branches grow. In TBD, everyone commits directly to main (the "trunk"), or uses feature branches that live for less than one day.
There is no develop branch, no release branches, no integration branches. Main is always deployable because it's always being deployed. If that sounds terrifying, you're not alone — most developers' first reaction is "won't that break everything?" The answer is no, but only if you have the right safety nets in place.
Feature Flags for Incomplete Work
The apparent contradiction — "how do you merge unfinished features into main?" — is solved by feature flags. Code exists in main but is invisible to users until the flag is enabled. Think of it like building a new room in a house: the room is constructed (code is in main) but the door is locked (feature flag is off). Guests (users) don't even know it's there until you unlock it.
Here's a real-world example of how feature flags work in a checkout flow. Notice that the new checkout code is deployed alongside the old code, but which version users see is controlled by an environment variable:
// Feature flag pattern for trunk-based development
// All code merges to main immediately, but is gated behind flags
const featureFlags = {
newCheckoutFlow: process.env.FF_NEW_CHECKOUT === 'true',
darkMode: process.env.FF_DARK_MODE === 'true',
aiRecommendations: process.env.FF_AI_RECS === 'true'
};
function renderCheckout(cart) {
if (featureFlags.newCheckoutFlow) {
// New checkout — still in development
return renderNewCheckout(cart);
}
// Existing checkout — shown to all users
return renderLegacyCheckout(cart);
}
// Feature flags can be:
// 1. Boolean (on/off)
// 2. Percentage-based (10% of users)
// 3. User-targeted (internal employees only)
// 4. Environment-specific (staging: on, production: off)
console.log("Feature flags loaded:", featureFlags);
Now let's see how a developer's daily workflow looks in trunk-based development. The key difference from other strategies is speed: branches are created and merged within hours, not days or weeks. Reviews happen in real-time, and CI/CD deploys every merge automatically:
# ===================================================
# Trunk-Based Development: A Developer's Daily Workflow
# ===================================================
# CONTEXT: You're adding a search indexer to a SaaS
# platform. Your team deploys 10+ times per day.
# Feature flags gate all incomplete work.
# ===================================================
# Step 1: Start of day — pull the latest trunk
# In TBD, main moves FAST (many commits per day)
git checkout main
git pull origin main
# Step 2: Create a VERY short-lived branch (optional)
# Some TBD teams commit directly to main — others use
# branches that live <1 day. Either is valid.
# Prefix with your initials for quick identification
git checkout -b wz/add-search-index
# Step 3: Make focused, small changes
# Each commit should be independently safe to deploy
# The feature flag ensures users won't see unfinished work
git add src/search/indexer.ts
git commit -m "feat: add search indexer behind FF_SEARCH flag"
# Step 4: Push and create PR (reviewed within HOURS, not days)
# In TBD, reviews are the highest priority — blocking a PR
# means blocking deployment for everyone
git push origin wz/add-search-index
gh pr create --title "feat: search indexer (flagged)" --reviewer @pair-partner
# Step 5: PR is reviewed immediately (often within 30 minutes)
# This is a cultural requirement — TBD only works with fast reviews
# Step 6: After approval, merge and delete the branch
gh pr merge --squash
git checkout main
git pull origin main
git branch -d wz/add-search-index
# Step 7: CI/CD pipeline deploys to production AUTOMATICALLY
# The feature is invisible to users because FF_SEARCH=false
# in the production environment configuration.
# When the feature is complete and tested, flip the flag!
Trunk-Based Development at Scale
The world's largest engineering organisations use trunk-based development:
- Google — 25,000+ engineers commit to a single monorepo trunk. Over 80,000 commits per day. Automated testing gates every change.
- Meta — Engineers commit to trunk with "landcastle" (a pre-merge test system that validates changes before they land)
- Netflix — Trunk-based with feature flags. Their system handles thousands of deployments per day across microservices
- Spotify — Moved from GitFlow to trunk-based development as they scaled. Deployment frequency increased from weekly to many-times-daily
Trunk-Based Development and Elite Performance
The DORA research programme (spanning 7 years and 36,000+ respondents) found that elite-performing teams are 2x more likely to practice trunk-based development than low performers. They also found that teams with branches living longer than one day had significantly lower deployment frequency and higher change failure rates.
The correlation is clear: short-lived branches (or no branches at all) correlate with higher throughput and better stability — contradicting the intuition that more branching equals more safety.
Workflow Comparison Matrix
Use this table to choose the right workflow for your team:
| Dimension | GitFlow | GitHub Flow | GitLab Flow | Trunk-Based |
|---|---|---|---|---|
| Deploy frequency | 2-4 weeks | Daily | 1-2 weeks | Multiple/day |
| Team size | 10-100+ | 2-20 | 5-50 | Any (with maturity) |
| Complexity | High | Low | Medium | Low (ops: High) |
| Release model | Versioned | Continuous | Staged | Continuous |
| CI requirement | Moderate | High | High | Very high |
| Feature flags needed | No | Optional | Optional | Essential |
| Multiple versions | Yes | No | Yes | No |
| Merge conflicts | Frequent (large) | Rare (small) | Occasional | Minimal |
Monorepos vs Polyrepos
Orthogonal to branching strategy is repository structure. A monorepo contains all of an organisation's code in a single repository. A polyrepo (or multi-repo) approach uses one repository per service, library, or team.
Monorepo Advantages
- Atomic changes — A single commit can update an API, its client library, and the documentation simultaneously
- Code sharing — Shared libraries live alongside consuming code, eliminating versioning overhead
- Unified CI — One pipeline, one set of tools, one configuration
- Visibility — Every developer can see, search, and learn from all code
- Dependency management — One version of each dependency across the entire organisation
Polyrepo Advantages
- Team autonomy — Teams own their repo, their CI, their release cadence
- Access control — Trivial to restrict access per repository
- Tooling simplicity — Standard Git works without custom infrastructure
- Smaller clone sizes — Developers only clone what they need
- Independent deployability — Each service has its own lifecycle
Monorepo Tooling
Managing a monorepo at scale requires specialised tooling. Standard npm install or make build won't scale when you have 50+ packages — you need tools that understand which packages changed and only rebuild/retest those. Here are the major monorepo build tools:
| Tool | Language | Key Feature | Used By |
|---|---|---|---|
| Bazel | Any | Hermetic builds, remote caching | Google, Stripe |
| Nx | JS/TS | Affected detection, computation caching | Many OSS projects |
| Turborepo | JS/TS | Incremental builds, remote caching | Vercel, many startups |
| Pants | Python/Go/Java | Fine-grained invalidation | Toolchain |
| Lerna | JS/TS | Package publishing | Babel, Jest (legacy) |
Let's look at a practical monorepo setup using Turborepo (one of the most popular tools for JavaScript/TypeScript monorepos). The package.json at the repository root defines the workspace structure — telling the package manager where to find sub-packages:
// Root package.json — defines the monorepo workspace structure
// All packages live under packages/* and apps/*
{
"name": "my-monorepo",
"private": true,
"workspaces": [
"packages/*",
"apps/*"
],
"scripts": {
"build": "turbo run build",
"test": "turbo run test",
"lint": "turbo run lint",
"dev": "turbo run dev --parallel"
},
"devDependencies": {
"turbo": "^2.0.0",
"typescript": "^5.4.0"
}
}
The turbo.json configuration file tells Turborepo about task dependencies and caching. For example, "build" depends on upstream packages being built first (^build), while "lint" has no dependencies and can run in parallel across all packages:
// turbo.json — defines how tasks relate to each other
// Turborepo uses this to parallelise and cache intelligently
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": []
},
"lint": {
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
}
}
}
Branch Protection & Policies
Regardless of which branching strategy you choose, protecting your main branch is non-negotiable for professional teams. Branch protection rules enforce quality gates before code reaches production.
Essential Protection Rules
- Required reviews — At least 1-2 approved reviews before merge
- Status checks — CI must pass (build, test, lint) before merge is allowed
- Signed commits — Verify author identity with GPG keys
- Linear history — Require rebase or squash merge (no merge commits)
- Up-to-date branches — Branch must be current with main before merging
- No force push — Prevent history rewriting on protected branches
You can configure branch protection rules through the GitHub web interface (Settings → Branches → Add rule), or declaratively using the probot/settings app which reads configuration from a YAML file in your repository. Here's what a complete branch protection configuration looks like:
# ===================================================
# Branch Protection Configuration
# ===================================================
# File: .github/settings.yml (using probot/settings app)
# This declaratively configures GitHub branch protection
# so your rules are version-controlled alongside your code.
# ===================================================
branches:
- name: main
protection:
# Require at least 2 approving reviews
# Stale reviews are dismissed when new commits are pushed
required_pull_request_reviews:
required_approving_review_count: 2
dismiss_stale_reviews: true
require_code_owner_reviews: true
# These CI checks MUST pass before merge is allowed
# 'strict: true' means the branch must be up-to-date with main
required_status_checks:
strict: true
contexts:
- "ci/build"
- "ci/test"
- "ci/lint"
- "security/snyk"
# Even admins must follow these rules (no bypassing)
enforce_admins: true
# Only allow squash/rebase merges (keeps history linear)
required_linear_history: true
# Only release managers can push directly (everyone else uses PRs)
restrictions:
users: []
teams: ["release-managers"]
For teams that require signed commits (common in regulated industries and open-source projects), here's how to set up GPG commit signing. This proves cryptographically that commits actually came from who they claim to be from:
# ===================================================
# Setting Up GPG Commit Signing
# ===================================================
# WHY: Signed commits prove your identity cryptographically.
# Without signing, anyone can set their git config to your
# name/email and make commits that LOOK like they're from you.
# ===================================================
# Step 1: Generate a GPG key pair (public + private)
# Choose RSA 4096-bit for maximum security
gpg --full-generate-key
# Step 2: List your keys and find the key ID
# The key ID is the hex string after "rsa4096/"
gpg --list-secret-keys --keyid-format=long
# Example output: sec rsa4096/ABC123DEF456 2024-01-01
# ^^^^^^^^^^^^^^ this is your key ID
# Step 3: Tell Git to use this key for all commits
git config --global user.signingkey ABC123DEF456
git config --global commit.gpgsign true
# Now every commit you make will be automatically signed
# Step 4: Export your PUBLIC key and add it to GitHub
# Go to: GitHub > Settings > SSH and GPG keys > New GPG key
gpg --armor --export ABC123DEF456
# Copy the entire output (including BEGIN/END lines) and paste into GitHub
# Step 5: Verify that signing works
git commit --allow-empty -m "test: verify GPG signing"
git log --show-signature -1
# You should see "Good signature from..." in the output
Case Studies
Google's Monorepo and Trunk-Based Development
Google stores virtually all of its code — billions of lines across thousands of projects — in a single monorepo called "google3". Over 25,000 engineers commit to the same trunk daily, producing 80,000+ commits per day.
How it works:
- Custom VCS called Piper (Perforce-derived) handles the scale Git cannot
- TAP (Test Automation Platform) runs affected tests for every change
- Changes go through mandatory code review before submission
- Feature flags gate all incomplete work
- Automated code health tools refactor code globally
Result: Despite enormous scale, Google ships hundreds of production changes per hour with low failure rates.
Atlassian's GitFlow to Trunk-Based Migration
Atlassian (makers of Jira and Bitbucket) initially promoted and used GitFlow extensively. As their products moved to cloud-first SaaS delivery, they found GitFlow's overhead unsustainable:
- Merge conflicts consumed 15-20% of developer time
- Release branches delayed features by 2-3 weeks
- Hotfix branches created divergence nightmares
Migration: They moved to trunk-based development with feature flags over 6 months. Deployment frequency increased from bi-weekly to daily. Their documentation (atlassian.com/git) now recommends trunk-based for SaaS products.
Exercises
Conclusion & Next Steps
Your branching strategy is not a religious choice — it's an engineering decision driven by your deployment model, team size, compliance needs, and product lifecycle. GitFlow serves versioned software with regulatory requirements. GitHub Flow serves SaaS teams deploying continuously. Trunk-based development serves organisations that invest in engineering maturity to achieve maximum delivery speed.
The trend across the industry is clear: shorter-lived branches, faster merges, and more automation. If your branches live longer than 2 days, you're accumulating integration risk. If they live longer than a week, you're almost certainly creating unnecessary merge conflicts.
Next in the Series
In Part 12: Build Systems & Dependency Management, we'll explore how code transforms into deployable artifacts — npm, Maven, Gradle, pip, lock files, reproducible builds, and the dependency management practices that prevent "works on my machine" failures.