McTaba Labs logo
By Bonaventure Ogeto|

Best Git Workflow for Small Teams

For small teams of 2 to 8 developers, use GitHub Flow: one main branch that is always deployable, short-lived feature branches for every change, and pull requests with at least one review before merging. Skip Git Flow, trunk-based development with feature flags, and anything else that adds process you do not need yet. GitHub Flow gives you safety (code review, a clean main branch) without ceremony.

The workflow: GitHub Flow

GitHub Flow is simple by design. Here is the entire process:

  1. Main branch is always deployable. Nothing gets merged into main that is broken. This is the one rule you never bend.
  2. Create a feature branch for every change. Branch off main, name it descriptively: feat/mpesa-checkout, fix/login-redirect, chore/update-deps.
  3. Commit often, push regularly. Small commits with clear messages. Push to the remote so your work is backed up and visible.
  4. Open a pull request when ready. Write a brief description of what the PR does and why. Tag a teammate for review.
  5. Get at least one approval. The reviewer reads the code, leaves comments, and approves when it looks good.
  6. Merge into main. Use squash merge to keep the main branch history clean. Delete the feature branch after merging.
  7. Deploy from main. Either automatically (CI/CD) or manually. The point is that main is always the source of truth for production.

Branch naming conventions

Consistent branch names make it easy to understand what everyone is working on. Use a prefix followed by a short description:

feat/user-registration      # New feature
fix/password-reset-email    # Bug fix
chore/upgrade-next-15       # Maintenance task
refactor/auth-middleware     # Code improvement, no behavior change
docs/api-endpoints          # Documentation only

Keep branch names lowercase, use hyphens instead of spaces, and be specific. feat/stuff tells your teammates nothing. feat/mpesa-stk-push-endpoint tells them exactly what to expect in the PR.

Some teams add ticket numbers: feat/PROJ-42-user-registration. This is useful if you use a project tracker like Linear or Jira. For smaller teams without a formal tracker, descriptive names are enough.

Commit messages that help

Write commit messages that explain why, not just what. The diff already shows what changed. The message should explain the reason.

# Bad
fixed bug
updated code
changes

# Good
fix: prevent duplicate STK Push on double-click
feat: add email verification on signup
chore: remove unused payment utility functions

The Conventional Commits format (type: description) is worth adopting even for small teams. It makes your git log scannable and can be used to auto-generate changelogs if you need them later.

Common types: feat (new feature), fix (bug fix), chore (maintenance), refactor (code change with no behavior change), docs (documentation), test (adding or fixing tests).

Code review that actually works

Code review is the most valuable part of this workflow. It catches bugs, shares knowledge, and keeps code quality consistent. But it only works if you do it right.

For the PR author:

  • Keep PRs small. 50 to 200 lines of changed code is ideal. A 2,000-line PR does not get reviewed carefully. It gets rubber-stamped.
  • Write a description. What does this change do? Why? Are there any areas you are unsure about? A two-sentence description saves your reviewer 10 minutes of confusion.
  • Self-review before requesting a review. Read your own diff. You will catch obvious issues before your teammate does.

For the reviewer:

  • Review within 24 hours. Stale PRs block progress and create merge conflicts. If you cannot review today, say so and let someone else pick it up.
  • Comment on logic and design, not style. If you have a formatter (Prettier, ESLint) handling style, do not argue about semicolons in code review.
  • Be direct but kind. "This will break if the callback returns null" is helpful. "Why would you do it this way?" is not.
  • Approve when it is good enough, not perfect. Blocking a PR for minor preferences slows the entire team.

Merge strategy: squash and merge

When merging a feature branch into main, you have three options: merge commit, squash merge, or rebase merge. For small teams, squash merge is the best default.

Squash merge takes all the commits in your feature branch and combines them into a single commit on main. Your main branch history reads like a list of completed features and fixes, not a log of every "wip" and "fix typo" commit you made along the way.

# Main branch history with squash merge
7a2b3c4 feat: add M-Pesa checkout flow (#42)
5d6e7f8 fix: resolve login redirect loop (#41)
9g0h1i2 chore: upgrade Next.js to 15 (#40)

The feature branch history is preserved on GitHub in the closed PR. If you need to see the individual commits, they are still there. But the main branch stays clean and scannable.

Set "Squash and merge" as the default merge method in your GitHub repository settings under Settings > General > Pull Requests.

What to skip (for now)

Git Flow. This workflow uses develop, feature, release, and hotfix branches. It was designed for software with scheduled releases (desktop apps, mobile apps with app store reviews). If you deploy continuously from a web app, Git Flow adds branches you do not need.

Trunk-based development. In this workflow, everyone commits directly to main with feature flags controlling what is visible. This works well at large companies with strong CI/CD and testing infrastructure. For a small team without comprehensive test coverage, it is risky. One broken commit on main blocks everyone.

Long-lived branches. Avoid keeping a branch open for weeks. Long-lived branches drift from main, merge conflicts pile up, and the eventual merge is painful. If a feature takes more than a few days, break it into smaller PRs that can be merged independently.

GitHub Flow sits in the sweet spot: safe enough to prevent disasters, light enough to not slow a small team down. When your team grows to 15+ developers and you need release trains, revisit this decision. Until then, keep it simple.

Frequently Asked Questions

Should I rebase or merge when updating my feature branch?
For small teams, either works. Rebasing gives you a cleaner commit history but rewrites history, which can cause problems if multiple people are working on the same branch. Merging is safer and simpler. A practical approach: use merge when collaborating on a branch, use rebase when you are the only one working on it and want a clean history before opening a PR.
Do I need branch protection rules?
Yes, even for small teams. At minimum, enable "Require a pull request before merging" and "Require approvals (1)" on your main branch in GitHub settings. This prevents anyone from accidentally pushing broken code directly to main. It takes 30 seconds to set up and can save hours of debugging.
How do I handle hotfixes?
Branch off main, fix the bug, open a PR, get a quick review, and merge. The process is the same as any other change, just faster. If the fix is truly urgent and your reviewer is unavailable, document why you merged without review and have someone look at it after the fact. Urgency does not mean skipping code review permanently.

Ready to build real-world apps?

Join the McTaba Labs full-stack marathon. Ship 8 production apps with M-Pesa, USSD, and WhatsApp integrations, and get career support until placement.

See Programs