← Back to list

Git Commits & Branches

The Professional Playbook

Hossam Soliuman · 2026-03-07 22:13 · 1 claps · 7.5 min read
#git #branch #commit #workflow #laravel
Open on Medium ↗
Wiki topics: 🔓 · Open Source

Git Commits & Branches

The Professional Playbook

Every developer writes commits. But most developers write commits that nobody — including themselves — can understand three weeks later. Commit messages like fix stuff, asdf, or WIP are noise. They tell a story of someone who didn't care. And in a professional team, that's a problem.

This guide will teach you the exact system used by top engineering teams around the world: Conventional Commits for commit messages, and Git Flow for branch naming and workflow. By the end, you’ll write commits that inform, branches that communicate intent, and you’ll fit naturally into any professional team.

Part I — The Commit Message System

The industry standard is called Conventional Commits. It gives every commit a predictable structure that both humans and tools (like CI/CD pipelines and changelog generators) can understand.

The format looks like this:

<type>[optional scope]: <description>
[optional body - more detail]
[optional footer - references, breaking changes]

A real example in full:

feat(auth): add Google OAuth login
Integrates Google OAuth 2.0 flow using Passport.js.
Users can now sign in with their Google account in addition to email/password.
Closes #204
BREAKING CHANGE: removed legacy /auth/google-old endpoint

Golden Rule: Complete this sentence — “If applied, this commit will…” Your description should finish it perfectly. “If applied, this commit will add Google OAuth login.” ✓

feat — New Feature

Used when you add something new that didn’t exist before. If a user can now do something they couldn’t do before — this is your type.

Rule: “If applied, this commit will add a new capability to the product.”

feat(auth): add JWT token refresh logic
feat(dashboard): add dark mode toggle
feat(cart): allow guest checkout without account

fix — Bug Fix

Used when you fix something that was broken. Something was wrong; now it’s right. Only use this when a defect existed.

Rule: “If applied, this commit will fix a broken behavior.”

fix(login): resolve infinite redirect loop on logout
fix(api): handle null response in /users endpoint
fix(ui): correct button alignment on mobile screens

refactor — Code Restructure

Used when you rewrite or reorganize code but behavior stays exactly the same. No new features, no bug fixes — pure internal improvement.

Rule: “Same input, same output — but the code inside is cleaner.”

refactor(auth): simplify token validation logic
refactor(cart): extract price calculation to utility function
refactor: rename userId → user_id for API consistency

chore — Maintenance Work

Used for behind-the-scenes tasks that don’t affect users at all. Updating dependencies, cleaning up unused files, configuration changes.

Rule: “Nobody notices this change except developers.”

chore: update npm dependencies to latest
chore: add .env.example file for onboarding
chore(deps): bump axios from 1.2.0 to 1.4.0

docs — Documentation

Used when you only change documentation — README files, code comments, wikis, API docs. Not a single line of executable code changed.

Rule: “You only touched .md files, comments, or documentation.”

docs: add API authentication section to README
docs(contributing): update PR guidelines for new hires
docs: add JSDoc comments to utility functions

style — Code Formatting

Used for cosmetic code changes only — spaces, semicolons, indentation, quote styles. Zero logic change. A linter or formatter told you to do it.

Rule: “Prettier or ESLint complained. This is that fix.” ⚠️ This is NOT about CSS styling — it’s about code formatting.

style: fix indentation in auth controller
style: remove trailing whitespace across codebase
style: convert single quotes to double in config files

test — Tests

Used when you add, fix, or update tests only. Production code is untouched.

Rule: “You only touched .test.ts or .spec.js files.”

test(auth): add unit tests for login service
test: fix broken snapshot test in Header component
test(api): add integration test for /users endpoint

perf — Performance

Used when code becomes faster or more efficient without any behavior change. The user gets the same result, just faster.

Rule: “Same result — measurably faster or uses less memory.”

perf(images): lazy load product images on scroll
perf(db): add index on users.email column
perf(api): cache frequent database queries with Redis

ci — CI/CD Pipeline

Used when you change GitHub Actions, Jenkins, CircleCI or any pipeline configuration. The product code is untouched.

Rule: “You touched .github/workflows/ or a pipeline config file.”

ci: add automated deployment to staging on push
ci: fix failing lint check in GitHub Actions
ci: add code coverage reporting step to pipeline

build — Build System

Used when you change the build tooling — webpack, Vite, Rollup, Dockerfile, package.json scripts. Affects how the app is compiled or packaged.

Rule: “You changed how the app is built or bundled.”

build: upgrade webpack from v4 to v5
build: add multi-stage Dockerfile for production
build(deps): bump react from 18.0 to 18.3

revert — Undo a Commit

Used when you roll back a previous commit. Always reference the original commit message so the team knows exactly what was undone.

Rule: “Something broke. Reference the original commit.”

revert: feat(auth): add JWT token refresh logic
This reverts commit a4f82b3 which caused session issues in production.

! — Breaking Change

Add ! after the type when your change breaks backward compatibility. Other developers or API consumers will need to update their code because of your change.

Rule: “Someone else’s code will break because of this commit.”

feat!: remove support for v1 API endpoints
refactor!: rename userId to user_id in all responses
fix(auth)!: change token format from JWT to opaque

The Scope — What Goes in the Parentheses?

The scope narrows down which module of your codebase was touched. Keep it short, lowercase, and consistent as a team.

feat(auth):       → authentication module
fix(api):         → backend API layer
feat(dashboard):  → the dashboard page/feature
chore(deps):      → dependencies
ci(github):       → GitHub Actions specifically
build(docker):    → Docker / containerization

Team Agreement Required: Scope names only work if your whole team uses the same ones. Add them to a CONTRIBUTING.md file in your repo so everyone stays consistent.

Part II — Branch Naming & Strategy

If commits are the sentences in your codebase’s story, branches are the chapters. A well-named branch tells the entire team — at a glance — who is working on what, why, and where it belongs in the larger workflow.

main / master — Production

This is production. Every line of code here is running right now, serving real users. It is sacred and protected.

  • ✗ Never commit directly to this branch
  • ✗ Never push broken or untested code here
  • ✓ Only receives merges from release/ or hotfix/
  • ✓ Every merge gets a version tag: v1.4.0

develop — Staging

This is the staging / integration branch. All finished features collect here before going to production. Think of it as “almost ready.”

  • ✓ All feature branches merge back into here
  • ✓ This is what QA and testers test on
  • ✗ Should never be in a broken state for long

feature/ — New Work

Created for every new piece of work. Branches off develop, merges back into develop via Pull Request.

feature/user-authentication
feature/T-42-shopping-cart-redesign
feature/add-email-notifications
feature/JIRA-204-google-oauth

Best practice: include your ticket number from Jira or GitHub Issues so the branch automatically traces back to a task.

bugfix/ — Non-Urgent Bug Fix

Created to fix a bug found during development or QA testing. This bug has NOT reached production yet. Branches off develop.

bugfix/fix-login-redirect-loop
bugfix/T-310-cart-total-miscalculation
bugfix/resolve-null-pointer-in-checkout

*bugfix/ = bug not yet in production → branch from develop hotfix/ = bug already hurting real users → branch from main*

hotfix/ — Emergency Production Fix

The emergency branch. Created only for critical production bugs — things that are breaking for real users right now and cannot wait for the normal release cycle.

hotfix/critical-payment-failure
hotfix/security-xss-vulnerability
hotfix/T-99-login-broken-for-all-users
  • ✓ Branch from main directly
  • ✓ Merge into BOTH main AND develop when done
  • ✗ Never forget the develop merge — or the fix gets lost in the next release

release/ — Version Preparation

Created when develop is feature-complete and being prepared for a new production version. Only bugfixes, version bumps, and release notes go here — no new features allowed.

release/1.4.0
release/2.0.0-beta
release/2025-Q1-sprint3
  • ✓ Merge into main when ready → tag as v1.4.0
  • ✓ Also merge back into develop to carry over any release fixes

Part III — The Full Company Workflow

Here is how all the pieces connect in a real engineering team’s day-to-day:

main   ──────────────────────────────────────────  (production — live)
         ↑                                    ↑
     release/1.5.0                    hotfix/payment-crash
develop ─────────────────────────────────────────  (staging — tested)
         ↑              ↑                  ↑
    feature/        feature/           bugfix/
    add-login       dark-mode          cart-total

The day-to-day flow for every developer:

  1. Pick up a ticket — Grab an issue from Jira, GitHub Issues, or Linear. Note the ticket ID.
  2. Create your branchgit checkout -b feature/T-42-user-profile develop
  3. Write code, commit often — Small, focused commits. Each commit does one thing. Use the right type.
  4. Push and open a Pull Request — Target develop. Write a clear PR description.
  5. Code review + CI checks — Teammates review. CI runs tests, linting, and type checks automatically.
  6. Squash merge into develop — All your commits become one clean commit. Delete the feature branch.
  7. Sprint ends → release branchdevelop becomes release/1.5.0. QA does final testing. Version is bumped.
  8. Ship to production — Merge release/1.5.0main. Tag as v1.5.0. Deploy. Done.

Part IV — The Quick Reference Cheatsheet

Situation Commit Type Branch Prefix New feature for users feat feature/ Bug found in development/QA fix bugfix/ Bug live in production fix hotfix/ Cleaning up code (no behavior change) refactor feature/ Making something faster perf feature/ Updating docs or comments docs docs/ Updating dependencies chore chore/ Changing CI/CD pipeline ci ci/ Changing build config build chore/ Adding or fixing tests only test test/ Rolling back a bad commit revert hotfix/ Change that breaks other teams' code feat! or fix! feature/ Preparing a new version chore: bump version release/

Part V — Enforcing It Automatically

The best professional teams don’t rely on developers remembering the rules. They encode the rules into the repository so bad commit messages are rejected automatically before they’re ever pushed.

The two tools that do this are commitlint and Husky:

# Install commitlint + Husky
npm install --save-dev @commitlint/cli @commitlint/config-conventional husky
# Create commitlint config
echo "module.exports = { extends: ['@commitlint/config-conventional'] }" > commitlint.config.js
# Enable Husky git hooks
npx husky install
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'

Now if anyone on the team tries to commit with a message like fix stuff, Git itself will reject it with an error. Only properly formatted commits get through.

Pro Move: Add semantic-release on top of this setup. It reads your conventional commit history and automatically determines the next version number, generates a changelog, and creates a GitHub release — all from your commit messages alone.

Final Thoughts

A clean commit history is an act of respect — for your teammates, for your future self, and for the product you’re building together. When someone bisects a bug at 2am and finds a commit that reads fix(payments): prevent double-charge on retry, that's not just helpful. It's professional.

You don’t need to memorize every rule today. Start with the basics: use feat and fix correctly. Name your branches with feature/ or bugfix/. Include a scope when it's useful. The rest will become muscle memory within a week.

The engineers who stand out on any team are the ones who communicate clearly — and commit messages are one of the most constant, lasting forms of communication in software development.

References: conventionalcommits.org · Git Flow by Vincent Driessen · commitlint.js.org


메타데이터
post_id
0b6791c7fbd7
slug
git-commits-branches-0b6791c7fbd7
url
https://medium.com/@hossamsoliuman/git-commits-branches-0b6791c7fbd7
canonical_url
https://medium.com/@hossamsoliuman/git-commits-branches-0b6791c7fbd7
author_url
https://medium.com/@hossamsoliuman
status
ok
fetched_at
2026-07-13 06:23:13