Prologue: "What's a branching strategy?"
First day of my first collaborative project, the senior developer asked:
"We use Git Flow. Branch from
developfor your work."
Me: "What's Git Flow?"
Senior: (sighs) "You've never used Git?"
Me: "I have... just git add, git commit, git push..."
That's when I realized: "Using" Git and "using Git well" are two completely different worlds. It's like knowing how to ride a bicycle doesn't mean you understand city traffic patterns. I only knew the basic Git commands but had zero understanding of team collaboration philosophy.
Why I Studied This
I moved to a second company. It was a startup.
CTO: "We do Trunk Based Development. Push directly to main."
Me: "Wait, no branches? Isn't that risky?"
CTO: "Tests are automated, we deploy 100 times a day. Branch management is slower."
Same Git, opposite philosophies. My first company used five different branch types, but the second company had only one branch. Both used the same version control system but with completely different approaches. Initially confusing, but this became my catalyst for deeply studying branching strategies.
What Confused Me Initially
- Why does Git Flow have so many branches?
master,develop,feature,release,hotfix... my head hurts- Trunk Based uses only
main? No conflicts? - "100 deploys per day" makes sense?
- Why does one small feature take two weeks?
- Won't pushing directly to
mainbreak production?
Most importantly: "Which should our team use?" I couldn't find answers. Google searches only returned vague responses like "it depends on your situation."
The Aha Moment: "Airplane vs Scooter"
A senior's analogy made everything crystal clear:
Git Flow = Boeing 747 (Large commercial airplane):
- 2 pilots, 10 crew members, 400 passengers
- 200-item pre-flight checklist
- Fuel check, aircraft inspection, tower clearance...
- Safety first, but 2 hours until departure
- Once airborne, safely transports 400 people
Trunk Based = Electric Scooter:
- Ride alone, leave immediately
- If charged, on the road in under 1 minute
- Fast, but crashes mean immediate injury
- Helmet (test automation) mandatory!
- Easy to change destination, mistakes have limited impact
"Different scales and risk levels! That's what it was all about."
The Boeing 747 is slow but safe. It's responsible for 400 lives. Meanwhile, the electric scooter is fast but risky. But since you ride alone, mistakes only hurt you. This analogy hit home. Git branching strategies aren't about technology choices—they're about risk management strategies.
1. Git Flow: Safety-First Enterprise Style
Branch Structure
master (production)
↓
develop
↓
feature/login
feature/payment
↓
release/v1.0
↓
hotfix/critical-bug
5 Branch Types
-
master(ormain): Production release version- Never commit directly (absolutely forbidden)
- Only tags attached (
v1.0.0,v1.0.1) - 100% identical code to production environment
- Everything here is "code customers are already using"
-
develop: Development main branch- Where next release is prepared
- All features merge here
- Connected to QA environment
- "Collection of features for next month's release"
-
feature/*: Feature development- Branches from
develop - Merges to
developwhen complete - Most commonly used by developers
- Examples:
feature/user-login,feature/payment-system
- Branches from
-
release/*: Release preparation- Branches from
develop - Only bug fixes allowed (no new features)
- Merges to both
masteranddevelopwhen complete - "Final checkpoint before launch"
- Branches from
-
hotfix/*: Emergency bug fixes- Branches from
master - Merges to both
masteranddevelopafter fix - Used immediately when production bugs are found
- "Like an emergency room"
- Branches from
Actual Flow
If I were building a login feature:
# 1. Create feature branch from develop
git checkout develop
git pull origin develop
git checkout -b feature/login
# 2. Work (1 week)
git commit -m "Add login UI"
git commit -m "Add authentication logic"
git commit -m "Add error handling"
git commit -m "Add unit tests"
# 3. Merge to develop
git checkout develop
git merge feature/login
git push origin develop
git branch -d feature/login
# 4. Release preparation (QA stage)
git checkout -b release/v1.0 develop
# If QA finds bugs, fix them here
git commit -m "Fix login button color"
git commit -m "Fix validation message"
# 5. Deploy
git checkout master
git merge release/v1.0
git tag v1.0.0
git push origin master --tags
# 6. Reflect in develop (includes bugfixes from QA)
git checkout develop
git merge release/v1.0
git push origin develop
Duration: 2 weeks ~ 1 month
When I first saw this process, I thought "Isn't this too complicated?" But after being assigned to a financial sector project, I understood. In environments where a single mistake can lead to financial damage for thousands of people, these validation stages are necessary.
Pros
-
Stability: Multiple validation stages
- Developer self-testing in feature branch
- Integration testing in develop
- QA testing in release branch
- Master contains "only verified code"
-
Concurrent development: 10 people can work on different features simultaneously
- Person A on login, B on payment, C on notifications...
- Work independently without conflicts
-
Version management: Clear distinction between
v1.0,v2.0- "Bug found in v1.5" → immediately move to that tag
- Multiple version maintenance possible
-
Easy hotfixes: Immediate production bug fixes
- Branch directly from master without worrying about develop
- Merge to both sides after fix
Cons
-
Complexity: Hard for juniors to understand
- I asked "Which branch am I on?" ten times a day initially
- Accidentally committing to master causes team-wide panic
-
Slowness: 2+ weeks per feature
- Even simple button color changes must wait for release cycle
- "It's urgent..." doesn't work
-
Merge Hell: Many branches lead to conflict nightmare
- After 2 weeks on feature branch, merging to develop → 100 conflicts
- Spend entire day resolving conflicts
-
Excessive process: Even small bugs wait 2 weeks
- Fixing one typo requires waiting for release cycle
- "Can we do this as a hotfix...?" → "No, follow the process"
Who Uses It?
- Package software: 4 releases per year (quarterly)
- Finance sector: Safety first, mistakes = major incidents
- Enterprise SI projects: Dozens working simultaneously
- Legacy systems: Systems with high change impact
- Manufacturing embedded: Once shipped, can't modify
If I were to summarize, Git Flow is a strategy for "environments where mistakes are unacceptable."
2. Trunk Based Development: Speed-First Startup
Branch Structure
main (= trunk)
├─ short-lived branch (< 1 day)
├─ short-lived branch
└─ short-lived branch
Core principle: Minimize branches, merge fast
How It Works
# 1. Work on main directly or short branch
git checkout main
git pull
git checkout -b fix-button # optional
# 2. Work (few hours)
git commit -m "Fix button color"
git push origin fix-button
# 3. Merge to main immediately (or immediately after PR approval)
git checkout main
git merge fix-button
git push
git branch -d fix-button
# Or push directly to main (more aggressive teams)
git checkout main
git add .
git commit -m "Fix button"
git push
Duration: Hours ~ 1 day
When I first saw this approach, I thought "Are they crazy?" Push directly to main? Won't production break? But when the CTO showed me the dashboard, my jaw dropped. They were really deploying 100 times per day. With error rate below 0.1%.
Core Principles
-
Small commits: Even big features split into small pieces merged daily
- "Entire payment system" in one merge ❌
- "Payment UI only", "Validation logic only", "API integration only"... one per day ✅
- Small chunks mean fewer conflicts, easy rollback if problems arise
-
Feature flags: Merge incomplete features (but hidden)
- Not "finish then merge" but "merge while building"
- Code is in main but users can't see it
-
Fast feedback: CI/CD automatically tests
- Commit → test results in 30 seconds
- Immediate rollback if failed
Feature Flag Example
// Merge incomplete features to main
const PaymentPage = () => {
const { featureFlags } = useFeatureFlags();
if (featureFlags.newPayment) {
return <NewPaymentUI />; // In development (internal testers only)
} else {
return <OldPaymentUI />; // Current (regular users)
}
};
// Control via environment variables
// .env.production
FEATURE_NEW_PAYMENT=false // Regular users see false
// .env.development
FEATURE_NEW_PAYMENT=true // Developers see true
Deploy with featureFlags.newPayment = false to hide it. Once complete, just switch to true. Even gradual rollout is possible:
// Show new feature to only 10% of users
if (featureFlags.newPayment && user.id % 10 === 0) {
return <NewPaymentUI />;
}
When I embraced this pattern, the world looked different. The assumption that "feature completion = code merge" was shattered.
Pros
-
Speed: 100 deploys per day possible
- Bugs fixed in the morning reach users by afternoon
- Experiment → validate → fix cycle in hours
-
Simplicity: Just one branch
- No "which branch am I on?" dilemma
- Need to know only 5 Git commands
-
Minimal conflicts: Frequent merges → no big conflicts
- 2 weeks on separate branch → 100 conflicts on merge
- Daily merges → 0-2 conflicts
-
True CI: Original meaning of Continuous Integration
- CI is "Continuous" but merging once every 2 weeks—is that CI?
- Trunk Based is true "continuous" integration
Cons
-
Risky: Bugs go straight to production
- Skip tests and users immediately experience it
- "I made a mistake" → "Site crashed" in 5 minutes
-
Tests mandatory: Disaster without automation
- Don't need 100% test coverage but
- Core features must have automated tests
-
Culture required: Entire team must agree
- If even one person says "I don't write tests," entire team at risk
- Based on mutual trust and responsibility
-
Skill required: Ability to break big features into small pieces
- Split "payment system" into 10 small pieces
- Design each piece to work independently
- Difficult skill for juniors
Who Uses It?
- Google, Facebook, Netflix: Thousands of deploys per day
- Startups: Fast experiments, fast fixes (pivot potential)
- SaaS services: Deploy anytime environment
- Teams with perfect CI/CD: 100% test automation
3. Real Experience: Git Flow → Trunk Based Transition
I experienced this transition firsthand at a startup.
Before (Git Flow)
Feature development: 1 week
Code review: 2 days
QA: 1 week
Deploy wait: 1 week (until next release cycle)
Total: 3 weeks until user feedback
Problems:
- 3 weeks later: "Don't need this feature" → 3 weeks wasted
- Competitor launched first (2 weeks ahead of us)
- Bug fixes found in QA also wait until next release
- Developers: "It's already done, why can't we deploy?"
This actually happened. I spent 3 weeks developing a "social login" feature, and when we finally did user testing, the feedback was "Email login is actually more convenient." Three weeks evaporated.
After (Trunk Based + Feature Flag)
Day 1: Basic UI merged (hidden with Feature Flag)
Day 2: Logic added, merged (only for internal testers)
Day 3: Tests pass, 10% user release
Day 4: User feedback collected (real-time monitoring)
Day 5: Feedback applied, 100% release or rollback
Effects:
- 3 weeks → 5 days (6x faster)
- Fast experiments, fast disposal (only 5 days wasted if failed)
- Faster launch than competitors
- User feedback-driven development (data, not guesses)
When I rebuilt the same "social login" feature with Trunk Based, we got user feedback on Day 3, immediately identified "button placement is weird," and fixed it on Day 4. With Git Flow, we would have learned this 3 weeks later.
4. The Importance of CI/CD
Trunk Based Requirements
# .github/workflows/ci.yml
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Run linter
run: npm run lint
- name: Run unit tests
run: npm test
- name: Run integration tests
run: npm run test:integration
- name: Check code coverage
run: npm run coverage
# Block merge if tests fail
- name: Block if failed
if: failure()
run: |
echo "Tests failed! Cannot merge."
exit 1
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to production
run: |
echo "Deploying to production..."
# Deployment scripts for AWS, Vercel, Netlify, etc.
Rule: Test failure = merge blocked! No exceptions!
My Accident
I once skipped tests and pushed to main:
git push --no-verify # ❌ Dangerous! Ignores pre-commit hook
Reason: "I'm in a hurry... I ran tests locally and they all passed..."
Result:
- Production down for 20 minutes
- 30 complaint tweets from users
- Called by the CTO
- Apologized in team meeting
- Deploy privileges revoked for 1 week
True cause: Passed locally but production environment variables were different. Would have failed in CI/CD but I skipped it.
Lesson:
- Trunk Based without automation = suicide
- "I'm in a hurry" isn't an excuse
--no-verifyis betraying teammates
After this incident, the team made a rule: "Use --no-verify = buy coffee for everyone." Sounds like a joke but we actually enforced it, and we never had such incidents again.
5. Team Size Recommendations
| Team Size | Recommended | Reason |
|---|---|---|
| 1~3 | Trunk Based | No branch management overhead needed. Everyone knows what everyone else is doing |
| 4~10 | Trunk Based + PR | Code review but merge fast (within 1 day) |
| 10~30 | GitHub Flow | Simplified Git Flow with main + feature branches |
| 30+ | Git Flow | Multiple teams working simultaneously, version management essential |
| Finance/Medical | Git Flow | Safety first. One mistake = legal issues |
6. Hybrid: GitHub Flow (Middle Ground)
This is what I use nowadays:
main (always deployable)
├─ feature/login (PR 1~3 days)
├─ feature/payment (PR 1~3 days)
└─ hotfix/bug (Direct merge or immediate PR)
Rules:
- Feature development: Short branch + PR (merge within 1~3 days)
- PR approved → immediate merge: No "later"
main= always deployable: Can press deploy button anytime- Auto-deploy: Merge → immediate production (or staging → production)
Actual workflow:
# 1. Create feature branch (Monday morning)
git checkout -b feature/dark-mode
# 2. Commit continuously while working
git commit -m "Add dark mode toggle button"
git commit -m "Add dark mode styles"
# 3. Create PR (Tuesday morning)
git push origin feature/dark-mode
# Create PR on GitHub
# 4. Code review (Tuesday afternoon)
# Colleague: "LGTM! 👍"
# 5. Merge (Tuesday afternoon)
# Squash and merge or Merge commit
# 6. Auto-deploy (Tuesday evening)
# CI/CD automatically deploys to production
Pros:
- Simpler than Git Flow (2~3 branches)
- Safer than Trunk Based (code review mandatory)
- Fits most teams (realistic choice)
- Easy to learn (juniors adapt in 1 week)
7. Mistake Collection
Mistake 1: Using Git Flow in a Startup
CEO: "Competitors launched yesterday, where are we?"
Developer: "In QA. Next week in the release branch..."
CEO: "Next week? We need it right now!"
Developer: "But the process..."
CEO: "😡"
This actually happened at my first startup. A competitor launched a "live chat" feature, and we had already built it but had to wait 2 weeks for the release cycle. We ultimately lost 10 customers to the competitor.
Lesson: Git Flow is overkill for startups. In environments where speed is life, use Trunk Based or GitHub Flow.
Mistake 2: Trunk Based Without Tests
# Friday evening 6 PM
git add .
git commit -m "Quick fix"
git push # Push to main without tests
# Friday evening 6:05 PM
"Site's not working!"
"Payment isn't processing!"
"Login button disappeared!"
This happened to my colleague. On a Friday evening, he pushed a "small fix" without testing... ended up working the entire weekend to rollback and fix it.
Lesson: Without automation, Git Flow is safer. "Fast deployment" and "irresponsible deployment" are different.
Mistake 3: Long-Lived Feature Branch (Trunk Based Failure)
Week 1: Start feature/big-refactor
Week 2: Continue work (main is 30 commits ahead)
Week 3: Finally done!
Week 3: Attempt merge → 200 conflicts
Week 3: Spend 2 days resolving conflicts
Week 3: Test again → 50 failures
Week 4: Give up and rewrite
This hell was my personal experience. I tried to do a "big refactor" all at once, managed the branch separately for 3 weeks, and when merging, everything collapsed.
Lesson:
- Keep branches short (1~3 days)
- Break big work into small pieces
- "Merge all at once later" is a lie
Correct approach:
Day 1: Change folder structure (merge)
Day 2: Fix import paths (merge)
Day 3: Rename functions (merge)
Day 4: Optimize logic (merge)
Summary: Selection Criteria
Use Git Flow when:
✅ Long deploy cycles (monthly or longer) ✅ Multiple versions maintained simultaneously (v1 support + v2 development) ✅ Large teams (30+ people) ✅ Safety is paramount (finance, medical, infrastructure) ✅ Package software (customers install directly) ✅ Legacy systems (high change impact)
Use Trunk Based when:
✅ Deploy multiple times per day ✅ Fast experiments, fast fixes (lots of A/B testing) ✅ Perfect CI/CD automation (80%+ test coverage) ✅ Small teams (under 10 people) ✅ SaaS services (can update anytime) ✅ Mature team culture (mutual trust, responsibility)
Use GitHub Flow when:
✅ Middle ground between the two (most teams) ✅ Code review culture (PR mandatory) ✅ Daily to weekly deployments ✅ Startups to mid-sized companies (10~30 people) ✅ Both speed and stability important ✅ Realistic choice (perfect automation difficult)
Final Thoughts: "Philosophy, Not Tool"
Git Flow vs Trunk Based is a difference in philosophy, not technology.
- Git Flow: "Don't make mistakes. Verify and verify again."
- Trunk Based: "Fail fast, fix fast."
- GitHub Flow: "Be reasonably careful while moving reasonably fast."
Early career (Git Flow): "Why so slow? So frustrating..." Now (GitHub Flow): "Just the right speed."
There's no right answer. Choose what fits your team.
Lessons I learned across multiple companies:
- Team size determines strategy: 3-person team and 30-person team need different strategies
- Safety vs speed tradeoff: Can't have both perfectly
- Automation creates speed: Without CI/CD, you're bound to be slow
- Culture matters more than technology: Good strategies fail without culture
One thing is certain:
"Using Git without a branching strategy is like driving without a license."
I'm embarrassed about asking "What's Git Flow?" on my first project, but thanks to that experience, I can now design branching strategies that fit team situations. I hope you find the right strategy for your team.