Git workflows are more complex than they need to be for enterprise teams, and most teams are not leveraging Git’s full potential for governance and scale.

Let’s look at a typical scenario. Imagine a team of 50 developers working on a large, mission-critical application. They’re using Git, but the actual workflow is a chaotic mess of feature branches, occasional merges directly to main, and a lot of "who broke the build?" finger-pointing. The governance aspect is weak: there’s no clear standard for pull request reviews, no automated enforcement of code quality, and tracking changes across different modules is a nightmare. Scaling this to 100 or 200 developers would be an unmitigated disaster.

The core problem this solves is managing complexity and ensuring stability in large-scale software development using a distributed version control system. When you have many contributors, many features, and a need for rigorous quality control, ad-hoc Git usage breaks down. A well-defined workflow provides structure, predictability, and auditability.

Here’s a look at a robust workflow, often referred to as a "Gitflow-inspired" model, but with some pragmatic enterprise adaptations:

1. main (or master) Branch: This is the single source of truth for production-ready code. It should always be deployable. No direct commits are allowed here. All changes are merged in via pull requests.

2. develop Branch: This is the integration branch where all completed features are merged. It represents the current state of development. Think of it as the staging environment for features.

3. Feature Branches: Each new feature, bug fix, or experiment starts on its own branch, typically named like feature/JIRA-123-add-user-authentication or fix/BUG-456-null-pointer-exception. These branches are created from develop.

4. Release Branches: When you’re ready to prepare for a release (e.g., a new version), you create a release branch from develop. For example, release/v1.2.0. This branch is used for final testing, minor bug fixes, and documentation updates. Only critical bug fixes should be merged into a release branch. Once stable, the release branch is merged into main and also back into develop to ensure develop has any last-minute fixes.

5. Hotfix Branches: For critical production bugs that cannot wait for the next develop integration, you create a hotfix branch directly from main. For example, hotfix/CRITICAL-789-security-patch. Once fixed and tested, the hotfix branch is merged into main and then also into develop to ensure the fix is incorporated into ongoing development.

How it works in practice:

Let’s say a developer, Alice, needs to add a new user profile page.

  1. git checkout develop: Alice ensures her develop branch is up-to-date.
  2. git pull origin develop: Pulls the latest changes.
  3. git checkout -b feature/JIRA-101-user-profile: Creates her new feature branch.
  4. Alice makes her changes, commits them (git commit -m "feat: Implement user profile page"), and pushes her branch (git push origin feature/JIRA-101-user-profile).
  5. She then opens a Pull Request (PR) from feature/JIRA-101-user-profile to develop.
  6. Her team reviews the PR. Automated checks (CI, linting) run.
  7. Once approved and checks pass, the PR is merged into develop.

When it’s time for version 1.2.0:

  1. git checkout develop: A release manager checks out develop.
  2. git pull origin develop: Pulls latest changes.
  3. git checkout -b release/v1.2.0: Creates the release branch.
  4. git push origin release/v1.2.0: Pushes the release branch.
  5. Team performs final testing, fixes any critical bugs found only on this branch.
  6. Once stable:
    • git checkout main
    • git pull origin main
    • git merge --no-ff release/v1.2.0: Merges the release branch into main. --no-ff ensures a merge commit, preserving the branch history.
    • git tag -a v1.2.0 -m "Release v1.2.0": Tags the release.
    • git push origin main --tags: Pushes main and tags.
    • git checkout develop: Switches back to develop.
    • git pull origin develop: Pulls main’s latest state into develop to ensure develop has the release’s fixes.
    • git merge --no-ff release/v1.2.0: Merges the release branch into develop.
    • git push origin develop: Pushes the updated develop.

The most surprising truth about this workflow is how the develop branch acts as a crucial buffer. Many teams try to merge features directly into main or skip develop altogether. This creates a constant state of instability on main because features are often incomplete or introduce regressions when merged. develop isolates this integration churn from the production-ready main branch, allowing for a more predictable release cadence. It’s not just about having more branches; it’s about creating clear boundaries for different stages of the development lifecycle.

The next conceptual hurdle is understanding how to effectively automate this process with CI/CD pipelines.

Want structured learning?

Take the full Git course →