Feature branches, Conventional Commits, interactive rebase, merge vs rebase, git bisect for bug hunting, and Git hooks for automated quality gates.
Git is the universal version control system for web development, yet most developers use only a fraction of its capabilities. The practices separating senior developers are not obscure commands — they are principled habits around how changes are organised, communicated, and integrated.
git checkout main && git pull origin main
# Name branches with type prefix
git checkout -b feature/user-authentication
git checkout -b fix/checkout-total-calculation
git checkout -b perf/lazy-load-product-images
git checkout -b chore/upgrade-react-18
The main branch is always deployable. Every change — feature, bugfix, refactor — happens on its own branch and enters main through a pull request with code review.
# Format: type(scope): description
feat(auth): add Google OAuth2 single sign-on
fix(cart): correct VAT calculation for EU customers
perf(images): convert hero to WebP, LCP improved 40%
refactor(api): extract UserService from UserController
chore(deps): upgrade React 18.2 → 18.3
# Bad commit messages:
'wip' 'fix stuff' 'changes' 'done'
git rebase -i HEAD~5
# Commands:
# pick = keep as-is
# reword = edit message
# squash = merge into previous (keep messages)
# fixup = merge into previous (discard message)
# drop = delete entirely
Merge creates a merge commit preserving complete history. Non-destructive, never rewrites SHAs. Use for integrating completed features into main.
Rebase replays commits on top of another branch creating linear history. Rewrites commit SHAs. Use for keeping feature branches current during development. Never rebase commits already pushed to a shared branch others are working from.
git fetch origin
git rebase origin/main
git push --force-with-lease origin feature/my-feature
git bisect start
git bisect bad # current commit has the bug
git bisect good v2.4.0 # this tag was clean
# Test each midpoint Git checks out, then mark:
git bisect bad # or:
git bisect good
# Automate
git bisect run npm test
npm install --save-dev husky lint-staged
npx husky init
# package.json
{
"lint-staged": {
"*.{js,ts,jsx,tsx}": ["eslint --fix", "prettier --write"],
"*.{css,scss}": ["prettier --write"]
}
}
Three most valuable hooks: pre-commit (lint staged files), commit-msg (validate Conventional Commits format), pre-push (run test suite).
A clean, well-structured commit history makes every future debugging session, code review, and onboarding faster and more effective. Invest in it from day one.