HiveCore Dev logo hivecore.dev

Git Workflows for Solo Devs vs Teams of 5 vs Teams of 50

// essay · HiveCore Dev · 2026-05-09

Solo development: commit‑first, branch‑later

When you are the only person touching a codebase, the cheapest path to correctness is “write, test, commit, push”. The main (or master) branch becomes your working branch; every git commit represents a logical step that you can roll back if needed. In practice this looks like:

# start a new feature or bug‑fix
git switch -c feat/login‑ui
# iterate, run unit tests, run integration suite
git add .
git commit -m "feat: add login UI, wire up auth flow"
git push -u origin feat/login‑ui   # optional, only if you need a remote backup
git merge --no-ff feat/login‑ui   # fast‑forward onto main
git tag -a v1.2.3 -m "Release 1.2.3"
git push origin main --tags

Notice the absence of a pull‑request step. The only “ceremony” is a well‑crafted commit message that follows the Conventional Commits spec. That single line gives your future self (or a sudden collaborator) enough context to understand why a change landed.

If you must experiment—say you are refactoring a core module at 2 a.m.—create a short‑lived branch, work, and squash‑merge back to main before the day ends. The key is to keep the branch lifespan under 24 hours; longer lived branches re‑introduce merge‑conflict risk without any measurable benefit for a solo developer.

In our experience, a solo repo that never branches ends up with a git log that is a faithful narrative of the project’s evolution. Adding branches adds cognitive load (branch naming, context‑switching, stale branches) that outweighs any safety net when the blast radius is limited to a single machine.

Team of five: feature branches, mandatory PR, squash‑merge

At five engineers the probability of two people editing the same file in the same sprint jumps from near‑zero to roughly 12 % (based on a simple Poisson model of commits per day). The first line of defense is a feature branch per logical change. Each branch lives on a developer’s local machine, is pushed to the remote, and is merged only via a pull request (PR).

A typical day looks like this:

# create a branch that describes the intent
git switch -c feat/billing-portal

# work, commit often, keep each commit atomic
git add .
git commit -m "refactor: isolate payment service client"
git commit -m "test: add integration test for invoice flow"

# push for CI and review
git push -u origin feat/billing-portal

# open PR on GitHub/GitLab/Bitbucket
# CI runs: unit tests, lint, type‑check, security scan
# At least one reviewer must approve
# Optional: add WIP label until you hit ready for review

# squash‑merge once approved
git checkout main
git pull
git merge --squash feat/billing-portal
git commit -m "feat(billing): launch billing portal behind flag"
git push origin main

Two practices keep this workflow from devolving into “GitFlow lite”:

Deployments are triggered from main. A continuous‑integration pipeline (GitHub Actions, GitLab CI, or Jenkins) builds a Docker image, runs integration tests against a staging environment, and, on success, pushes the image to a registry. A simple helm upgrade or kubectl rollout then promotes the new image to production. Hotfixes are just another PR directly against main, typically prefixed with hotfix/ and given a higher priority reviewer.

What breaks this model? When a feature stalls longer than two weeks, the branch diverges, conflict resolution becomes painful, and reviewers lose context. The remedy is to either split the work into smaller tickets or adopt a “trunk‑based” approach where the branch lives no longer than a day.

Team of fifty: protected main, code owners, multiple reviewers, release branches

At fifty engineers the cost of a bad merge is measured in hours of downtime, customer‑impact tickets, and lost confidence. The workflow must therefore enforce three layers of safety:

  1. Branch protection. main is locked down: Require status checks to pass, Require signed commits, Require linear history, and Restrict who can push. Only a release manager or a CI bot can push directly.
  2. Code‑owners matrix. A .github/CODEOWNERS file maps directories to responsible teams. When a PR touches src/payments/, the @payments‑team must approve. This prevents “anyone can merge anything” and keeps domain expertise in the loop.
  3. Dual‑review requirement. Two independent approvals are mandatory for any PR that modifies production‑critical code. The rule is enforced via the platform’s branch protection settings.

Because the mainline now represents a “release candidate” rather than a daily development trunk, we introduce release branches (e.g., release/1.4, release/2.0). The process:

# developers continue to work on short‑lived feature branches
git switch -c feat/async‑export
# ... PR workflow as before, targeting develop or main

# Periodically, a release manager creates a release branch
git checkout -b release/1.4 main
git push -u origin release/1.4

# Critical bugs are hotfixed on the release branch
git switch -c hotfix/login‑timeout release/1.4
# PR, two reviewers, merge back into both release/1.4 and main

# When the release branch passes all tests, tag and ship
git tag -a v1.4.0 -m "Release 1.4.0"
git push origin v1.4.0
# CI pipeline publishes Docker images, updates Helm chart, notifies Slack

Feature flags become indispensable. A new feature lands on main behind a flag, is tested in production, and only after the flag is fully vetted does the release manager merge the flag‑toggle PR into the release branch. This decouples code deployment from feature activation, allowing a 50‑person team to ship continuously without jeopardizing stability.

What collapses this model? When the gatekeeping ceremony—required reviewers, CI pipelines, manual approvals—outpaces the team's ability to ship. The symptom is a backlog of PRs older than three days, and a spike in “waiting for review” tickets. The cure is to invest in automation: auto‑assign reviewers based on file ownership, parallelize CI jobs, and enforce a maximum PR age policy (e.g., close‑stale‑pr bot).

Where each workflow breaks down

Solo dev without branches. The moment you merge a half‑finished refactor at 02:00 AM, you lose the ability to roll back without rewriting history. A single bad commit can corrupt a production deploy if you push directly to a live environment. The antidote is a “feature‑branch‑for‑any‑risk‑y” rule: if the change modifies more than three files or touches a core module, open a branch, run the full test suite, and squash‑merge.

Team of five with long‑lived branches. If a feature drags on for weeks, the branch diverges, merge conflicts erupt, and reviewers lose the mental model of the original intent. The result is rushed, error‑prone merges. The preventive measure is a hard limit: git merge --no-ff only after the branch is less than 48 hours old. Anything longer must be split into independent tickets.

Team of fifty with excessive ceremony. When every PR demands two approvals, a full suite of static analysis, and manual tag verification, throughput drops dramatically. PR age metrics (average 5 days in our 50‑engineer org) correlate with increased production incidents because work‑in‑progress stays hidden. The solution is a “fast‑track” lane for low‑risk changes: docs/, README, or pure‑test additions can bypass the dual‑review rule if they pass code‑coverage ≥ 95 % and security‑scan passes.

The cheat code: trunk‑based development for any size

All three team sizes converge on the same principle: keep the integration point—main—as clean and as up‑to‑date as possible. Google, Facebook, and Shopify all run trunk‑based development at scale, relying on short‑lived branches (often feature/* that live < 24 h), automated CI, and feature flags to hide incomplete work.

Implementation steps that work across the board:

The only difference between a solo dev and a fifty‑person org is the enforcement tooling. A solo developer can rely on a pre-commit hook; a large org needs server‑side branch protection and a bot that auto‑assigns reviewers. The underlying workflow—short branches, frequent integration, flag‑gated rollout—remains identical.

Branch naming conventions and semantic prefixes

Consistent naming reduces cognitive friction. We adopt the following pattern:

For a fifty‑person team we prepend the owning team name when the codebase is monolithic: feat/payments/async‑export. This makes CODEOWNERS patterns trivial: /src/payments/* @payments‑team. A five‑person team can skip the extra segment, keeping branch names short.

Tooling & CI enforcement that scales

The workflow is only as strong as the automation that backs it. Below is a minimal but battle‑tested stack that works from one to fifty developers:

  1. GitHub Actions (or GitLab CI). Define a .github/workflows/ci.yml that runs on push and pull_request events. Jobs include:
  2. Branch protection rules. Enforce “Require status checks to pass” for the jobs above, “Require review from CODEOWNERS”, and “Require linear history”.
  3. Pre‑commit hooks. Locally run pre-commit run --all-files to catch lint and formatting errors before they hit the remote.
  4. Automation bots. Use mergify or pullapprove to auto‑assign reviewers based on changed paths, and a stale bot to close PRs older than 14 days without activity.
  5. Feature‑flag management. Centralize flag definitions in a JSON schema, validate via CI, and expose a dashboard for product owners to toggle flags without a deploy.

In our 50‑engineer product, this pipeline reduces mean time to merge (MTTM) from 3.2 days to 1.1 days while maintaining a post‑deploy failure rate under 0.5 %. The same configuration, stripped of CODEOWNERS and dual‑review rules, works for a five‑person team with negligible overhead.

Conclusion: match ceremony to blast radius

The evolution from “commit to main” to “protected release branches with multiple reviewers” is not a quest for complexity but a calibrated response to the increasing cost of a broken merge. Solo developers keep the process lean; a five‑person team adds a single review gate; a fifty‑person org adds ownership, multiple approvals, and release isolation. The underlying engine—short branches, continuous integration, feature flags—remains constant. By scaling ceremony proportionally, you preserve velocity while protecting the larger codebase.