Knowing Git commands is one thing—using Git professionally is another. The difference lies in following best practices that make your code history clear, your team collaboration smooth, and your project maintainable. In this lesson, you'll learn to write commit messages that tell a story, choose when to commit for maximum clarity, select the right branching strategy for your team, and follow the conventions that distinguish professional developers. These practices will make you a better collaborator and your projects easier to maintain!
Writing Great Commit Messages
Commit messages are how you communicate with your future self and your team. They create a searchable history of why changes were made.
The Commit Message Format
Subject Line (50 characters or less)
Optional body explaining what and why (wrap at 72 characters).
This provides context for the change.
- You can use bullet points
- For multiple items
- That need explanation
Optionally include issue references:
Fixes #123
Related to #456Subject Line Rules
Do:
- Use imperative mood: "Add feature" not "Added feature"
- Capitalize first letter: "Fix bug" not "fix bug"
- No period at end: "Update README" not "Update README."
- Be specific: What changed?
- Keep it short: 50 characters max
Good vs Bad Examples
✅ Good Commit Messages:
- Add user authentication with JWT tokens
- Fix memory leak in image upload component
- Refactor database connection pooling
- Update React to version 18.2.0
- Remove deprecated API endpoints
- Improve search performance with indexing
❌ Bad Commit Messages:
- Fixed stuff
- Updates
- WIP
- asdfasdf
- More changes
- IDK what I'm doing
Conventional Commits
Many teams use structured commit messages with prefixes:
type(scope): subject
Types:
- feat: New feature
- fix: Bug fix
- docs: Documentation changes
- style: Code style changes (formatting, no logic change)
- refactor: Code restructuring (no feature change)
- perf: Performance improvements
- test: Adding or updating tests
- chore: Maintenance tasks (dependencies, build config)
- ci: CI/CD changes
Examples:
feat(auth): add password reset functionality
fix(ui): resolve button alignment on mobile
docs(readme): update installation instructions
refactor(api): simplify user service logic
perf(images): implement lazy loading
test(auth): add integration tests for login
chore(deps): update dependencies to latestDetailed Commit Message Example
Add caching layer to API endpoints
Implements Redis caching for frequently accessed endpoints to reduce
database load and improve response times.
Changes:
- Add Redis connection configuration
- Implement cache middleware
- Add cache invalidation on data updates
- Set appropriate TTL for different endpoints
Performance impact:
- Average response time reduced from 450ms to 120ms
- Database queries reduced by 75% for cached endpoints
Fixes #234
Related to #189✍️ The Why Matters Most
The code shows what changed. Your commit message should explain why it changed. Future developers (including you) need to understand the reasoning behind decisions!
When to Commit
Knowing when to commit is as important as knowing how.
The Golden Rule
Commit when you've completed a logical unit of work.
Each commit should represent one cohesive change that makes sense on its own.
Good Times to Commit
- After completing a feature: "Add user profile page"
- After fixing a bug: "Fix login redirect loop"
- After refactoring: "Extract validation logic to separate module"
- After updating docs: "Update API documentation"
- After adding tests: "Add tests for user authentication"
- When code is working: Don't commit broken code
Poor Times to Commit
- End of day dumps: "End of day commit" with mixed changes
- Broken code: Code that doesn't compile or breaks tests
- Mixed concerns: Combining unrelated changes in one commit
- Too granular: Committing after every line of code
- Debug code: Console.logs, temporary test code
Commit Granularity
Find the right balance:
❌ Too Large (one commit):
"Add entire authentication system"
- Login, registration, password reset, email verification, 2FA
- 47 files changed, 2,847 insertions
✅ Just Right (multiple commits):
1. "Add user registration with email verification"
2. "Add login with JWT authentication"
3. "Add password reset functionality"
4. "Add two-factor authentication support"
❌ Too Small (too many tiny commits):
1. "Add imports"
2. "Create function"
3. "Add parameter"
4. "Fix typo"
5. "Remove console.log"🎯 Aim for Atomic Commits
Atomic commits are self-contained changes that:
- Can be understood independently
- Don't break the build
- Can be reverted without breaking other features
- Tell a clear story when viewed in history
Branching Strategies
Different teams use different branching models. Choose one that fits your workflow.
1. GitHub Flow (Simple & Popular)
Best for: Continuous deployment, small teams, web applications
main (always deployable)
├── feature/add-search
├── feature/user-profiles
└── fix/login-bug
Workflow:
1. Create feature branch from main
2. Work and commit
3. Open pull request
4. Review and test
5. Merge to main
6. Deploy immediately
7. Delete feature branchRules:
- Main branch is always deployable
- Create descriptive branch names
- Open PR early for feedback
- Deploy immediately after merge
- Delete merged branches
2. Git Flow (Structured Releases)
Best for: Scheduled releases, larger teams, complex projects
main (production releases only)
└── develop (integration branch)
├── feature/new-feature
├── feature/another-feature
└── release/v2.0
└── hotfix/critical-bug
Branch Types:
- main: Production code (v1.0, v2.0, etc.)
- develop: Next release integration
- feature/*: New features (from develop)
- release/*: Release preparation (from develop)
- hotfix/*: Emergency fixes (from main)Git Flow Workflow
- Feature development:
- Create feature branch from develop
- Work on feature
- Merge back to develop
- Release preparation:
- Create release branch from develop
- Bug fixes only (no new features)
- Merge to main AND develop
- Tag version on main
- Hotfixes:
- Create hotfix branch from main
- Fix critical bug
- Merge to main AND develop
- Tag new version
3. Trunk-Based Development
Best for: Continuous integration, experienced teams, fast iteration
main (trunk)
├── short-lived-branch-1 (1-2 days max)
└── short-lived-branch-2 (1-2 days max)
Workflow:
1. Pull latest main
2. Create very short-lived branch
3. Make small changes
4. Merge back to main within 1-2 days
5. Use feature flags for incomplete featuresKey principles:
- Very short-lived branches (hours to 2 days max)
- Small, frequent commits to main
- Feature flags for incomplete features
- Heavy emphasis on automated testing
- Continuous integration required
Choosing a Strategy
GitHub Flow → When you:
✓ Deploy continuously
✓ Have good CI/CD pipeline
✓ Want simplicity
✓ Have small to medium team
Git Flow → When you:
✓ Have scheduled releases
✓ Need release branches for testing
✓ Support multiple versions
✓ Have larger, structured team
Trunk-Based → When you:
✓ Can deploy multiple times per day
✓ Have excellent automated testing
✓ Want maximum integration
✓ Team is experienced with CI/CDBranch Naming Conventions
Consistent branch names make collaboration easier.
Common Patterns
# Feature branches
feature/user-authentication
feature/add-search-functionality
feat/dark-mode-support
# Bug fixes
fix/login-redirect-loop
bugfix/memory-leak-upload
hotfix/critical-security-issue
# Documentation
docs/api-documentation
docs/update-readme
# Refactoring
refactor/user-service
refactor/simplify-auth-logic
# Experimental
experiment/new-architecture
spike/performance-testing
# Personal branches (if working alone briefly)
username/feature-nameBranch Naming Best Practices
- Use lowercase:
feature/loginnotFeature/Login - Use hyphens:
add-user-authnotadd_user_auth - Be descriptive: What does this branch do?
- Include issue number:
feature/123-add-search - Keep short but clear: Not too long, not too vague
✅ Good Branch Names:
- feature/user-authentication
- fix/login-redirect-bug
- docs/update-api-guide
- refactor/database-queries
- feature/234-add-dark-mode
❌ Bad Branch Names:
- test
- my-branch
- asdf
- fixes
- new-stuff
General Workflow Best Practices
1. Keep Main/Master Stable
Main branch should always be deployable.
- Never commit broken code to main
- All tests must pass before merging
- Use pull requests, not direct commits
- Protect main branch with GitHub settings
2. Pull Before You Push
# Always pull latest changes first
git pull origin main
# Work on your changes
git add .
git commit -m "Add feature"
# Pull again in case anything changed
git pull origin main
# Then push
git push origin feature-branch3. Commit Often, Push Regularly
- Commit: Multiple times per day
- Push: At least once per day
- Why: Creates backups, shares progress, prevents conflicts
4. Review Your Changes Before Committing
# Check what changed
git status
# Review diff
git diff
# Review staged changes
git diff --staged
# Only then commit
git commit -m "Clear message"5. Write Meaningful PR Descriptions
Pull request descriptions should explain:
- What: What does this PR do?
- Why: Why is this change needed?
- How: Brief overview of approach
- Testing: How was it tested?
- Screenshots: For UI changes
6. Keep Branches Short-Lived
Good:
✓ Feature branch exists 1-5 days
✓ Small, focused changes
✓ Merged quickly
Bad:
✗ Branch exists for weeks/months
✗ Massive changes
✗ Becomes painful to merge7. Delete Merged Branches
# After PR is merged, delete the branch
git branch -d feature/completed-feature
# Delete remote branch
git push origin --delete feature/completed-feature
# Or use GitHub's "Delete branch" button on PR page8. Use Tags for Releases
# Tag a release
git tag -a v1.0.0 -m "Release version 1.0.0"
# Push tags to remote
git push origin v1.0.0
# Or push all tags
git push origin --tags
# List tags
git tag
# Checkout specific version
git checkout v1.0.0Team Collaboration Practices
Code Review Workflow
- Create PR early: Open as draft if work-in-progress
- Request reviewers: Assign specific people
- Respond to feedback: Address all comments
- Update PR: Push changes to same branch
- Get approval: Wait for thumbs up
- Merge: Squash if many commits, regular merge if clean
- Delete branch: Clean up after merge
Communication Guidelines
- Discuss before implementing: Major changes need team input
- Use PR comments: Keep discussion with the code
- Reference issues: Link to related discussions
- Update documentation: Keep docs current with code
Merge Strategies
1. Merge Commit (default)
- Preserves all commits
- Creates merge commit
- Full history visible
- Good for: Important features
2. Squash and Merge
- Combines all commits into one
- Cleaner history
- Loses individual commits
- Good for: Many small commits
3. Rebase and Merge
- Replays commits on top of base
- Linear history
- No merge commit
- Good for: Clean, linear historyCommon Workflow Patterns
Feature Development Pattern
# 1. Update main
git checkout main
git pull origin main
# 2. Create feature branch
git checkout -b feature/user-search
# 3. Work and commit
# ... make changes ...
git add .
git commit -m "Add search UI components"
# ... more changes ...
git commit -m "Implement search API integration"
# 4. Keep branch updated
git checkout main
git pull origin main
git checkout feature/user-search
git merge main # or rebase main
# 5. Push feature branch
git push -u origin feature/user-search
# 6. Create pull request on GitHub
# 7. After merge, clean up
git checkout main
git pull origin main
git branch -d feature/user-search
git push origin --delete feature/user-searchBug Fix Pattern
# 1. Create fix branch from main
git checkout main
git pull origin main
git checkout -b fix/login-redirect
# 2. Fix the bug
# ... make changes ...
git add .
git commit -m "Fix login redirect loop
The redirect was caused by session validation
occurring before redirect completion.
Fixes #234"
# 3. Test thoroughly
npm test
# 4. Push and PR
git push -u origin fix/login-redirect
# 5. Fast-track review for critical bugsHotfix Pattern (Production Issues)
# 1. Create hotfix from production tag
git checkout v1.2.0
git checkout -b hotfix/security-patch
# 2. Fix critical issue
git commit -m "Fix security vulnerability in auth
CVE-2024-XXXXX: Prevent SQL injection in login
Fixes #567"
# 3. Merge to both main and develop
git checkout main
git merge hotfix/security-patch
git tag v1.2.1
git checkout develop
git merge hotfix/security-patch
# 4. Deploy immediately
git push origin main --tags
git push origin developPractice Workflow Commands
Try these commands to practice professional Git workflows:
Practice Git Workflow
Explore branch structures and commit history
Try these examples:
Common Anti-Patterns to Avoid
1. Committing to Main Directly
Don't:
git checkout main
# ... make changes ...
git commit -m "quick fix"
git push origin main # ❌ No review, no CI, risky!Do:
git checkout -b fix/issue
# ... make changes ...
git commit -m "Fix issue with validation"
git push origin fix/issue
# Create PR, get review, then merge ✅2. Giant Commits
Don't:
"Friday commit: 127 files changed, 4,523 insertions, 2,891 deletions"
Do:
Multiple focused commits: "Add user model", "Add user controller", "Add user tests"
3. Cryptic Messages
Don't: "fix", "update", "wip", "stuff"
Do: "Fix memory leak in image upload", "Update dependencies to patch vulnerabilities"
4. Committing Broken Code
Don't: Commit code that doesn't compile or breaks tests
Do: Ensure code works before committing. Run tests!
5. Long-Lived Branches
Don't: Keep feature branches for weeks/months
Do: Merge frequently. Break large features into smaller PRs
Key Takeaways
- Write clear commit messages in imperative mood: "Add feature" not "Added feature"
- Commit when you complete a logical unit of work that makes sense independently
- Keep main branch stable and deployable at all times
- Choose a branching strategy: GitHub Flow (simple), Git Flow (releases), or Trunk-Based (continuous)
- Use consistent branch naming: feature/, fix/, docs/, refactor/
- Pull before pushing, commit often, push regularly
- Keep feature branches short-lived (days, not weeks)
- Delete branches after merging
- Use tags for releases and version milestones
- Never commit directly to main—always use pull requests
What's Next?
Excellent work! You now understand professional Git workflows and best practices. These conventions will make you a better collaborator and help your team ship quality code efficiently.
In the next lesson, we'll explore Working with .gitignore. You'll learn how to exclude files from Git, understand common ignore patterns for different project types, keep sensitive data safe, and maintain a clean repository. Proper use of .gitignore is essential for security and repository hygiene!
🎯 Practice Assignment
Before the next lesson, improve your Git habits:
- Review your recent commit messages—rewrite any unclear ones
- Establish a branching strategy for your projects
- Practice writing detailed commit messages with body text
- Create a template for your commit messages
- Set up branch protection rules on one of your repositories
- Start using conventional commits (feat:, fix:, docs:)
Good habits compound over time—start practicing them now!