What Are Branches and Commits?
Understanding Commits
Key components of a commit:
- Hash (ID): A unique 40-character SHA-1 identifier (e.g., `a3c7f12`)
- Author: Who made the changes
- Date/Time: When the commit was created
- Message: Description of what changed and why
- Content: The actual file changes (diff)
Understanding Branches
Why Branches and Commits Matter
For Business Leaders
-
Risk Mitigation
Branches provide a safety net—production code remains stable while new features are developed -
Faster Time-to-Market
Multiple teams can work simultaneously without blocking each other -
Quality Assurance
Every change is tracked and can be reviewed before deployment -
Audit Trail
Complete history of who changed what and when, crucial for compliance -
Cost Savings
Bugs can be traced to specific commits, dramatically reducing debugging time
For Development Teams
-
Parallel Development
Team members work independently without conflicts -
Experimentation
Try new approaches without risk to stable code -
Code Review
Changes can be reviewed before integration -
Rollback Capability
Instantly revert to any previous state -
Context Preservation
Future developers understand the reasoning behind changes
When to Create Branches
Feature Development
- Adding new functionality that takes more than a few hours
- When multiple related commits will be needed
- Before starting work that might be experimental
- When you need code review before merging
Bug Fixes
Hotfixes for Production
Experimental Work
When to Commit
Commit Frequency: Finding the Balance
When to commit
- After completing a discrete unit of work (function, fix, feature component)
- Before switching to a different task
- At the end of your work session
- After making code that compiles and passes tests
- When you want to save a checkpoint before trying something risky
When NOT to commit
- Broken or incomplete code on shared branches
- Code that doesn't compile
- Untested changes to critical systems
- Multiple unrelated changes together
Commit Messages: Writing for Humans and Machines
The Conventional Commits format
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Common commit types
- feat: A new feature for the user
- fix: A bug fix for the user
- docs: Documentation changes only
- style: Code style changes (formatting, missing semicolons)
- refactor: Code restructuring without changing behavior
- test: Adding or updating tests
- chore: Maintenance tasks (dependencies, build configs)
- perf: Performance improvements
- build: Changes affecting the build system
- ci: CI/CD pipeline changes
The Conventional Commits format
// Excellent commit message examples
// 1. Simple feature addition
git commit -m "feat(auth): Add two-factor authentication support"
// 2. Bug fix with context
git commit -m "fix(api): Resolve rate limiting causing 429 errors
The rate limiter was counting requests per IP instead of per user,
causing mobile users sharing IPs to hit limits prematurely.
Changed to user-based rate limiting with Redis cache.
Closes #456"
// 3. Breaking change
git commit -m "feat(api)!: Change authentication endpoint to use JWT
BREAKING CHANGE: The /api/auth endpoint now returns JWT tokens
instead of session cookies. Clients must update to include
Authorization: Bearer <token> header in all API requests.
Migration guide: docs/migration/v2-auth.md"
// 4. Performance improvement
git commit -m "perf(db): Add indexes to frequently queried columns
Added composite index on (user_id, created_at) for posts table.
Reduced query time from 2.3s to 45ms on posts listing endpoint.
Benchmark results in PR #567"
// 5. Refactoring
git commit -m "refactor(payment): Extract validation logic into separate module
No functional changes. Moved payment validation from controller
to dedicated validator class for better testability and reuse."
The 50/72 Rule
- Subject line: Maximum 50 characters
- Body lines: Wrapped at 72 characters
- Imperative mood: Write as if giving a command (e.g., "Add" not "Added")
The Conventional Commits format
# Good structure
git commit -m "feat(payment): Add Stripe webhook handler" \
-m "Implements webhook endpoint to process payment events
from Stripe. Handles successful payments, failures, and
refunds with appropriate status updates in database.
Validates webhook signatures using Stripe secret key.
Adds retry logic for failed database updates.
Related to #234"
Why these rules matter
Branch Naming Conventions
Standard Branch Naming Patterns
# Feature branches
feature/user-authentication
feature/JIRA-123-payment-integration
feature/dark-mode-support
# Bug fix branches
bugfix/login-timeout
bugfix/PROJ-456-memory-leak
fix/email-validation-regex
# Hotfix branches
hotfix/critical-security-patch
hotfix/production-crash-fix
# Release branches
release/v1.2.0
release/2024-Q1
# Experimental branches
experiment/react-migration
experiment/new-caching-strategy
Branch naming best practices:
-
Use separators
Use hyphens (`-`) or slashes (`/`) for readability -
Include ticket numbers
Reference issue trackers (e.g., `feature/JIRA-123-description`) -
Be descriptive
Name should indicate purpose at a glance -
Keep it concise
Short but meaningful (avoid excessive length) -
Use lowercase
Consistency across team members -
Avoid special characters
Stick to alphanumeric and hyphens
Best Practices Summary
For Commits
- Commit often but keep each commit atomic and focused
- Write meaningful messages in imperative mood
- Follow conventions like Conventional Commits
- Keep subject lines under 50 characters
- Use body for context explaining "why" not "how"
- Reference issues when applicable
- Never commit secrets or sensitive data
- Test before committing to shared branches
For Branches
- Branch for every feature/fix no matter how small
- Use descriptive names with prefixes (feature/, bugfix/)
- Keep branches short-lived (days, not weeks)
- Pull frequently to stay in sync with main
- Delete merged branches to keep repo clean
- Use pull requests for code review
- Choose appropriate strategy for your team size and deployment frequency
- Protect main branches with required reviews and CI checks
Explore project snapshots or discuss custom web solutions.
Building Better Software Through Better Practices
-
For commits
Write clear, conventional messages that explain what changed and why. Commit frequently but meaningfully. Each commit should tell a story that future developers (including yourself) will understand. -
For branches
Use them liberally for every feature, fix, and experiment. Choose a branching strategy that fits your team's size and deployment frequency. Keep branches short-lived and focused. -
For teams
Establish clear conventions and stick to them. Use pull requests for code review. Automate checks with CI/CD. Communicate about overlapping work to prevent conflicts.
Commits serve as an archive of changes. They can become an ancient manuscript to help us decipher the past, and make reasoned decisions in the future.
Frequently Asked Questions
According to GitLab's best practices and Eduonix's version control guide, commit early and commit often, but ensure each commit represents one logical change. Commit after completing a discrete unit of work such as a function, a fix, or a test. According to the Enlume blog on Conventional Commits, each commit should represent a single, focused change—breaking down complex tasks into smaller, atomic commits makes it easier to understand the history of the codebase, isolate issues, and perform effective code reviews. However, avoid committing broken or incomplete code to shared branches. If working alone on a feature branch, commit frequently even for work-in-progress to save your progress.
The choice depends on your deployment frequency and team structure. According to Graphite's branching strategies guide, consider team size (smaller teams benefit from GitHub Flow or Trunk-Based Development, while larger teams might prefer Git Flow), deployment frequency (frequent releases align well with GitHub Flow, while infrequent releases suit Git Flow), and project complexity (simple projects thrive on GitHub Flow, while complex projects may require Git Flow). GitKraken notes that there is not a one-size-fits-all Git branch strategy—you should choose based on your team's environment, product, and specific development needs, and you can optimize it with further modifications.
According to Eduonix's guide, rebasing your feature branch onto the latest main branch can help create a cleaner, linear history compared to using git merge. Rebasing re-applies your commits on top of the latest changes, avoiding unnecessary merge commits. Merge preserves the complete history including when branches diverged and converged, creating merge commits that show the integration points. Rebase rewrites history by replaying your commits on top of another branch, creating a linear history. However, never rebase commits that you've already shared with others, as it rewrites history and can cause confusion for collaborators. Use merge for shared branches and public history; use rebase for cleaning up local branches before pushing.
According to the Conventional Commits specification and multiple best practice guides, focus on using simple, clear language with the standard format. The Conventional Commits format helps by providing structure: `<type>(<scope>): <description>`. Use imperative mood (Add, Fix, Update) rather than past tense. Keep the subject line short and factual (what changed). Use translation tools if needed, but prioritize clarity over perfect grammar. Your team will appreciate consistent formatting and clear descriptions of what changed more than perfect English. Many successful open-source projects have contributors from around the world writing clear, effective commit messages in simple English.
According to GitKraken and GitHub Flow principles, delete branches immediately after they're successfully merged to keep your repository clean. The general rule is to delete a branch when it has served its purpose and its commits are safely integrated into the main branch. For feature branches, delete after merging to main or develop. For release branches in Git Flow, delete after merging to both main and develop. For hotfix branches, delete after deploying the fix and merging back to necessary branches. For experimental branches, delete if the experiment was unsuccessful or after merging if successful. You can always recover deleted branches using git reflog if needed, so don't worry about losing work. Most platforms like GitHub and GitLab offer options to automatically delete branches after pull request merges.
Comments are closed