Git is the universal constant in software development — virtually every team uses it. Yet teams frequently struggle with the operational consequences of poor Git practices: merge conflicts that take days to resolve, releases blocked because the main branch is in an unknown state, code reviews that nobody wants to do because the pull request is 2,000 lines long.
The branching model, commit conventions, review process, and CI/CD integration are strategic decisions that compound over time. This guide covers the four major branching strategies (GitFlow, GitHub Flow, trunk-based development, release flow), monorepo vs. polyrepo tradeoffs, commit convention standards, merge strategies, and Git automation. By the end, you will have a framework for choosing and implementing the right Git workflow for your team's size and deployment cadence.
Git Version Control: Branching Strategy Selection
The branching strategy is the most consequential Git decision a team makes. It determines how frequently code integrates, how long features live in isolation, and how release management works. Choosing wrong creates operational friction that compounds monthly.
GitFlow
GitFlow, defined by Vincent Driessen in 2010, organizes work around five branch types: main (production-stable code), develop (integration branch), feature/* (individual feature development), release/* (release preparation), and hotfix/* (emergency production fixes).
The model was designed for software products with explicit version numbers and scheduled release cycles — desktop applications, mobile apps, APIs with versioned consumers.
GitFlow is appropriate when:
- Your team ships versioned releases (v2.3.1) rather than continuous deployment
- Multiple release lines are maintained simultaneously (v1.x and v2.x in parallel)
- The release cycle is weeks or months, not days
- Enterprise software with formal release approval processes
GitFlow problems in modern contexts:
- Long-lived feature branches accumulate merge conflicts
- The develop-to-main merge is a high-risk event
- Release branch overhead slows teams that deploy frequently
- Not compatible with continuous deployment
GitHub Flow
GitHub Flow reduces GitFlow's five branch types to two: main (always deployable) and short-lived feature/* branches. A feature branch opens, work happens, a pull request is reviewed, it merges to main, and main deploys immediately.
This simplicity is the model's primary advantage. There are no separate integration or release branches. Main is always in a deployable state. The deployment question — "is this ready?" — has a clear answer: if it merged to main, it's ready.
GitHub Flow is appropriate when:
- Your team deploys continuously (multiple times per day or once per day)
- Your software is a web application or SaaS product
- You have strong CI with comprehensive automated tests
- You need developer-facing simplicity
GitHub Flow limitations:
- No built-in mechanism for maintaining multiple release versions
- Hotfix process requires additional conventions
- "Main is always deployable" assumes test coverage that many teams don't have
Trunk-Based Development
Trunk-based development pushes GitHub Flow further: engineers commit directly to the main branch, or merge feature branches within one to two days. Long-lived branches are explicitly discouraged. Incomplete features are hidden behind feature flags rather than living in branches.
Google, Meta, and Microsoft are well-documented practitioners. The practice scales to thousands of engineers committing to a single monorepo (Google's repository has over 2 billion lines of code and hundreds of thousands of commits per day).
Trunk-based development is appropriate when:
- Your team has strong automated test coverage (90%+ on critical paths)
- You use feature flags for progressive release
- You have rapid CI (builds complete in minutes, not hours)
- The team has a culture of small, frequent commits
What makes trunk-based development work: The crucial enabler is not the absence of branches — it is the presence of fast feedback. If CI takes 30 minutes, committing to trunk creates a 30-minute feedback loop for the entire team. Trunk-based development requires a CI pipeline that returns results in 10-15 minutes or less.
Release Flow
Release flow, used by Microsoft's Azure DevOps team, is a hybrid: development happens on trunk (main), and dedicated release branches are cut for each release. Hotfixes are cherry-picked from trunk onto the release branch.
This retains trunk's continuous integration benefits while supporting explicit release management. It works well for large-scale products that deploy to enterprise customers with different update schedules.
Choosing your branching strategy:
| Factor | GitFlow | GitHub Flow | Trunk-Based | Release Flow |
|---|---|---|---|---|
| Deployment frequency | Monthly/quarterly | Daily/weekly | Multiple/day | Weekly/monthly |
| Team size | Any | Small-medium | Any (with infrastructure) | Large |
| Multiple versions | Yes | No | No (with flags) | Yes |
| Operational complexity | High | Low | Low (with tooling) | Medium |
Monorepo vs. Polyrepo
Repository organization is a separate but related architectural decision that affects collaboration, dependency management, and build performance.
Monorepo Benefits
A monorepo contains all of an organization's code in a single repository. Atomic commits span services: a change to a shared library and all consuming services can be a single commit, making it impossible to have a library version mismatch.
Refactoring across services is simpler in a monorepo. Renaming an interface, updating a shared type definition, or changing an API contract can be done in one pull request with one code review, rather than coordinated across multiple repositories.
Google, Meta, and Stripe use monorepos for their core products. The scaling challenges — large repository size, slow clone times, slow CI when all code is in one place — are addressed with specialized tooling.
Polyrepo Benefits
Polyrepo keeps each service in its own repository. Teams have full autonomy over their repository's CI/CD, dependency management, and branching strategy. Access control is granular — teams can restrict access to their repository without affecting others.
For open-source projects, polyrepo is usually the right structure. Each component can be released, versioned, and contributed to independently.
Practical Decision Framework
The decision depends primarily on code coupling and team structure:
Monorepo works well when:
- Services share significant code (shared libraries, internal tooling)
- Teams need to coordinate changes across services (common in platform engineering)
- You can invest in monorepo tooling (Turborepo, Nx, Bazel, or Pants)
Polyrepo works well when:
- Services are independently deployable with minimal shared code
- Teams operate autonomously with different deployment schedules
- Access control between teams is a primary concern
Hybrid approaches are common: a monorepo for the core product, separate repositories for standalone tools and open-source components.
Commit Conventions
Commit message quality affects code review efficiency, debugging speed, and release automation. Consistent, structured commit messages enable changelog generation, semantic versioning, and automated tooling.
Conventional Commits
The Conventional Commits specification defines a structured format:
<type>(<scope>): <description>
[optional body]
[optional footer]
Common types:
feat: New feature (triggers minor version bump in semantic versioning)fix: Bug fix (triggers patch version bump)docs: Documentation changesrefactor: Code refactoring without behavior changeperf: Performance improvementtest: Adding or modifying testschore: Tooling, dependency updates, build changesci: CI/CD pipeline changes
Examples:
feat(auth): add OAuth2 refresh token rotation
fix(payment): resolve race condition in concurrent checkout
docs(api): update authentication endpoint documentation
refactor(core): extract database connection pooling to module
BREAKING CHANGE: payment API response format changed to include tax breakdown
The BREAKING CHANGE footer triggers a major version bump in semver tooling (semantic-release, standard-version).
Enforcement with commitlint
Commitlint enforces Conventional Commits format via Git hooks:
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"type-enum": [2, "always", ["feat", "fix", "docs", "refactor", "test", "perf", "chore", "ci"]]
}
}
Combined with Husky, commitlint runs before each commit and rejects messages that don't follow the convention. This is a one-time setup with permanent benefits.
Code Review Best Practices
Code review is the highest-leverage team practice for maintaining code quality. It is also the practice most commonly done poorly: reviews that focus on style (which tools handle automatically) rather than architecture, reviews that take days and block the developer, and reviews that are rubber-stamps.
Pull Request Size
Research across multiple organizations consistently shows that review quality degrades dramatically above 400 lines of change. Above 1,000 lines, reviewers miss a significant fraction of defects — not from inattention, but because the cognitive load of understanding 1,000 lines of change exceeds what one review session can hold.
The practical guidance: limit pull requests to 200-400 lines of substantive change. Large features should be decomposed into sequential PRs. The overhead of multiple smaller PRs is lower than the cost of bugs that slip through large-PR reviews.
What Code Review Should Focus On
Reviewers should spend their attention on things that tooling cannot catch:
- Architectural correctness: Is the approach consistent with the system's design? Does it create technical debt? Does it handle edge cases?
- Logic errors: Does the code do what the description says? Are error cases handled?
- Security implications: Are inputs validated? Is authorization checked? Are secrets handled correctly?
- Test adequacy: Do the tests cover the expected behavior? Are edge cases tested?
Style, formatting, and naming conventions should be handled by linters and formatters, not human review. A reviewer commenting on indentation is wasting both their time and the author's time.
Review Turnaround Time
Pull requests should be reviewed within 24 hours. The cost of review delay is compounded: the author's context on the code decays, merge conflicts accumulate if the main branch is moving, and the developer's motivation to make improvement based on feedback drops.
Async review (reviewer leaves comments, author addresses them, no synchronous meeting required) is more efficient for most feedback. Synchronous review (pair programming or screen-share) is more efficient for complex architectural discussions.
Merge Strategies
Git provides three merge approaches, each with different effects on project history:
Merge commit: Preserves the complete branch history with an explicit merge commit. The history accurately records when branches were created and merged. The downside: a busy repository with many contributors produces a history that is hard to read linearly.
Squash and merge: Collapses all commits from a feature branch into a single commit on main. The result is a linear, readable history where each commit represents a complete feature or fix. git bisect works cleanly on this history. The downside: the granular development history (including intermediate commits and iteration) is lost.
Rebase and merge: Replays branch commits onto main, producing a linear history without merge commits. Each original commit is preserved, but commit SHAs change. This makes rebasing shared branches dangerous — other developers' branches that are based on the original commits become inconsistent.
For most teams: squash and merge for feature branches (clean linear history), and rebase + merge-commit for release branches (preserve the release branch structure).
Git Automation
Git Hooks
Git hooks run scripts at specific points in the Git workflow. Local hooks (pre-commit, commit-msg, pre-push) enforce quality at the source; server-side hooks enforce policy at the remote.
High-value hook configurations:
- pre-commit: Run linter and formatter on staged files (lint-staged)
- commit-msg: Validate commit message format (commitlint)
- pre-push: Run unit tests before pushing
Husky is the standard hook manager for JavaScript/TypeScript projects. It stores hook configuration in package.json and installs hooks when developers run npm install, making the setup automatic.
lint-staged is a complementary tool: it runs configured commands only on Git-staged files, making pre-commit hooks fast by avoiding running linters on the entire codebase.
Branch Protection Rules
Branch protection rules on the remote repository enforce policy that local hooks cannot: they run even for users who don't have Husky installed, and they enforce CI requirements.
Essential production branch protections:
- Require at least 1 approving review
- Require status checks to pass (CI build, tests, security scan)
- Dismiss stale reviews when new commits are pushed
- Restrict who can push directly to main
- Prevent force pushes
CODEOWNERS files extend protection to file level: specific files or directories require review from designated owners. Security modules, database migrations, and API contract definitions are natural candidates for CODEOWNERS protection.
CI/CD Integration
The branching strategy directly determines the CI/CD pipeline design.
For trunk-based development: CI runs on every commit to main. The test suite must be fast enough to return results before the next commit arrives. Broken main is a team-blocking event that is fixed before any other work.
For GitHub Flow and GitFlow: CI runs on every pull request and on merges to main. A broken main is less disruptive (fewer direct commits) but still requires immediate attention.
Path-based triggers are critical for monorepos: run only the affected service's CI pipeline when code in that service changes. on: push: paths: filtering in GitHub Actions, rules: changes: in GitLab CI. Without path-based filtering, a documentation change in service A triggers a full build for all 20 services in the monorepo.
Environment-based branches (separate branches for dev, staging, production) are an anti-pattern in modern CI/CD. Configuration differences should live in environment variables and IaC parameters, not in different code branches. Maintaining separate branches for each environment creates three-way merge complexity and makes it easy for environments to drift from each other.
Related Articles
MLOps Guide: Taking Machine Learning Models to Production [2026]
87% of machine learning models built by data science teams never reach production. The models work — they pass cross-validation, they score well on holdout sets, they demonstrate genuine predictive value. The problem is not the modeling. The problem is everything that happens between a notebook experiment and a reliable, monitored, production system. MLOps is the discipline that closes that gap. This guide covers the full MLOps stack: maturity levels, tooling choices (MLflow, DVC, Kubeflow
Read More: MLOps Guide: Taking Machine Learning Models to Production [2026]LLM Fine-Tuning Guide: Custom Model Training with LoRA and QLoRA [2026]
General-purpose LLMs are impressive. They can write code, summarize documents, answer questions, and translate between languages with reasonable accuracy. But "reasonable" is not good enough when your application requires consistent output format, domain-specific terminology, a particular tone, or behavior that the base model was never trained to exhibit. That gap is where fine-tuning matters. Fine-tuning updates a model's weights on your specific data, changing how the model behaves — not
Read More: LLM Fine-Tuning Guide: Custom Model Training with LoRA and QLoRA [2026]Computer Vision Applications: Object Detection, OCR, and Industrial AI [2026]
Computer vision has moved well past the research phase. The models are trained, the frameworks are mature, the hardware is accessible, and the use cases are generating measurable returns. What was a specialized capability requiring deep expertise in 2018 is now deployable infrastructure — if you know which component to reach for and where the real complexity lives. This guide covers computer vision applications across industrial, medical, logistics, and document processing domains. It expl
Read More: Computer Vision Applications: Object Detection, OCR, and Industrial AI [2026]
