Collaboration at Scale#

When teams grow beyond a handful of developers, Git workflows become critical infrastructure. The right workflow reduces merge conflicts, accelerates delivery, and preserves code quality. The wrong one creates bottlenecks and frustration.


Git Workflows Compared#

GitFlow#

main ─────────●─────────────────●──────────── (releases)
               ╲               ╱
develop ────●───●───●───●───●─●──────────── (integration)
             ╲     ╱   ╲   ╱
feature/x ────●───●     ╲ ╱
                     feature/y ──●
AspectDetails
Branchesmain, develop, feature/, release/, hotfix/*
Merge styleMerge commits (no fast-forward)
Release cyclePlanned releases with release branches
Best forPackaged software, mobile apps, versioned products
DrawbackComplex, long-lived branches, integration pain

GitHub Flow#

main ─────●──────●──────●──────●──────── (always deployable)
           ╲    ╱  ╲   ╱  ╲   ╱
feature ────●──●    ●──●    ●──●
            PR+CI   PR+CI   PR+CI
AspectDetails
Branchesmain + short-lived feature branches
Merge styleSquash or merge commit via Pull Request
Release cycleDeploy on every merge to main
Best forSaaS, web applications, continuous deployment
DrawbackRequires robust CI/CD and feature flags

Trunk-Based Development#

main ──●──●──●──●──●──●──●──●──●──── (everyone commits here)
        │  │     │        │
        small    small    small
        commits  commits  commits
AspectDetails
Branchesmain only (or very short-lived branches < 1 day)
Merge styleDirect commits or same-day PRs
Release cycleContinuous; feature flags gate incomplete work
Best forHigh-performing teams, CI/CD maturity, Google/Meta-style
DrawbackRequires excellent test coverage and feature flags

Ship / Show / Ask#

A nuanced model that categorizes changes by risk:

CategoryProcessExample
ShipMerge directly to main, no reviewTypo fix, config change
ShowMerge to main, open PR for async reviewRefactoring with tests
AskOpen PR, wait for review before mergeNew feature, architecture change
AspectDetails
PhilosophyTrust engineers to categorize their own changes
Best forExperienced teams with high trust and good test coverage
DrawbackRequires strong team culture and judgment

Comparison Table#

CriterionGitFlowGitHub FlowTrunk-BasedShip/Show/Ask
ComplexityHighLowLowestLow
Branch lifespanDays-weeksHours-daysMinutes-hoursVaries
Merge conflictsFrequentOccasionalRareRare
CI/CD requirementMediumHighVery HighHigh
Team size sweet spot5-203-505-100+3-15
Release cadenceWeekly-monthlyDailyContinuousContinuous

Large Repository Strategies#

Monorepo#

All code in a single repository:

monorepo/
├── services/
│   ├── orders/
│   ├── payments/
│   └── inventory/
├── libraries/
│   ├── shared-auth/
│   └── common-types/
└── infrastructure/
    └── terraform/
ProsCons
Atomic cross-service changesGit performance degrades at scale
Shared tooling and CIEveryone sees all changes (noise)
Easy code reuseRequires build system sophistication
Single version of truthOwnership boundaries blur

Tools: Nx, Turborepo, Bazel, Pants, Rush

Sparse Checkout (Git native)#

# Only check out what your team needs
git sparse-checkout init --cone
git sparse-checkout set services/orders libraries/shared-auth

Git Submodules / Subtrees#

ApproachUse CaseComplexity
SubmodulesPin external dependency at specific commitHigh (confusing for newcomers)
SubtreesEmbed external repo in your treeMedium (merge-based)

Signed Commits#

Verify that commits genuinely came from the claimed author:

# Setup GPG signing
git config --global user.signingkey YOUR_KEY_ID
git config --global commit.gpgsign true

# Sign a commit
git commit -S -m "feat: add payment validation"

# Verify
git log --show-signature
# gpg: Signature made ... using RSA key ID ABC123
# gpg: Good signature from "Developer Name <dev@example.com>"

SSH Signing (Git 2.34+)#

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub

Enforcement#

PlatformSetting
GitHubBranch protection → Require signed commits
GitLabPush rules → Reject unsigned commits
Localgit config --global commit.gpgsign true

Merge Queues#

Merge queues solve the “semantic conflict” problem: two PRs pass CI individually but break when combined.

The Problem#

PR #1 passes CI  ✓  (changes function signature)
PR #2 passes CI  ✓  (calls function with old signature)
Both merge → main is broken ✗

How Merge Queues Work#

PR #1 ──→ ┌─────────────┐
           │ Merge Queue │ → Test (main + PR#1 + PR#2) → Merge both
PR #2 ──→ └─────────────┘                              or reject PR#2
  1. PR is approved and added to the queue
  2. Queue creates a speculative merge (main + all queued PRs)
  3. CI runs on the combined result
  4. If it passes, all queued PRs merge; if not, the failing PR is ejected
PlatformFeature
GitHubMerge queue (built-in)
GitLabMerge trains
Bors-ngThird-party bot

Code Review Automation#

CODEOWNERS#

Automatically assign reviewers based on file paths:

# .github/CODEOWNERS (GitHub) or CODEOWNERS (GitLab)

# Global default
*                       @team-leads

# Service-specific ownership
/services/orders/       @orders-team
/services/payments/     @payments-team @security-team

# Infrastructure requires platform team
/infrastructure/        @platform-team
*.tf                    @platform-team

# Documentation — anyone can review
/docs/                  @tech-writers

# Security-sensitive files need security review
/auth/                  @security-team
**/secrets*             @security-team

Automated Checks#

ToolPurpose
Danger.js / Danger.rbCustom PR rules (size limits, changelog, labels)
Semantic PREnforce conventional commit titles
ReviewdogPost linter results as PR comments
CodeRabbit / CopilotAI-powered code review suggestions

Review Policies#

# Example: GitLab merge request approval rules
approvals_required: 2
rules:
  - name: "Security Review"
    approvals_required: 1
    groups: ["security-team"]
    applies_to: ["auth/**", "*.tf"]
  - name: "Standard Review"
    approvals_required: 1
    groups: ["developers"]

PR Size Guidelines#

Size (lines changed)CategoryExpected Review Time
1-50Small< 30 minutes
51-200Medium1-2 hours
201-500LargeHalf day (consider splitting)
500+Too largeMust be split

Rule of thumb: If a PR takes more than 30 minutes to review, it’s too large. Reviewers lose attention after ~400 lines.


Scaling Practices Summary#

ChallengeSolution
Merge conflictsShort-lived branches, trunk-based development
Broken mainMerge queues, required CI checks
Unclear ownershipCODEOWNERS, team-based directories
Review bottlenecksAuto-assignment, review SLAs, stack PRs
Commit authenticitySigned commits (GPG/SSH)
Monorepo performanceSparse checkout, build caching (Nx/Bazel)
Cross-team coordinationADRs, architecture fitness functions

Key Takeaways#

  1. Trunk-based development scales best for high-performing teams — short-lived branches and feature flags replace complex branching models.
  2. GitHub Flow is the pragmatic middle ground for most teams.
  3. Merge queues prevent the “passing individually, failing together” problem.
  4. CODEOWNERS ensures the right people review the right code automatically.
  5. Signed commits prove authorship — essential for supply-chain security.
  6. Keep PRs small (< 200 lines) — review quality drops dramatically with size.
  7. The best workflow is the one your team actually follows — complexity kills adoption.