
Git Workflow
- 355 installs
- 60 repo stars
- Updated May 16, 2026
- asyrafhussin/agent-skills
Enforce branch naming, commit message conventions, rebase vs merge choices, and PR preparation while an agent edits a shared codebase.
About
Teaches agent-friendly git workflow conventions—branching, atomic commits, rebasing, and pull request hygiene—so automated coding sessions produce clean, reviewable history on shared repositories.
- Defines branch and commit conventions for agent commits
- Covers PR creation, rebasing, and conflict avoidance
- Keeps history readable for human reviewers
- Pairs with CI and code review expectations
Git Workflow by the numbers
- 355 all-time installs (skills.sh)
- Ranked #127 of 733 Git & Pull Requests skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/asyrafhussin/agent-skills --skill git-workflowAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 355 |
|---|---|
| repo stars | ★ 60 |
| Last updated | May 16, 2026 |
| Repository | asyrafhussin/agent-skills ↗ |
What it does
Enforce branch naming, commit message conventions, rebase vs merge choices, and PR preparation while an agent edits a shared codebase.
Files
Git Workflow
Git best practices, commit conventions, branching strategies, and pull request workflows. Guidelines for maintaining a clean, useful git history.
When to Apply
Reference these guidelines when:
- Writing commit messages
- Creating branches
- Setting up git workflows
- Reviewing pull requests
- Maintaining git history
Rule Categories by Priority
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Commit Messages | CRITICAL | commit- |
| 2 | Branching Strategy | HIGH | branch- |
| 3 | Pull Requests | HIGH | pr- |
| 4 | History Management | MEDIUM | history- |
| 5 | Collaboration | MEDIUM | collab- |
Quick Reference
1. Commit Messages (CRITICAL)
commit-conventional- Use conventional commitscommit-atomic- Atomic commits (one logical change)commit-present-tense- Use imperative present tensecommit-meaningful- Descriptive, meaningful messagescommit-body- Add body for complex changescommit-references- Reference issues/ticketscommit-git-hooks- Enforce standards with Husky + commitlint
2. Branching Strategy (HIGH)
branch-naming- Consistent branch namingbranch-feature- Feature branch workflowbranch-main-protected- Protect main branchbranch-short-lived- Keep branches short-livedbranch-delete-merged- Delete merged branchesbranch-release- Release branch strategybranch-workflow-strategies- GitFlow vs GitHub Flow vs Trunk-Basedbranch-monorepo- Monorepo git workflows
3. Pull Requests (HIGH)
pr-small- Keep PRs small and focusedpr-description- Write clear descriptionspr-reviewers- Request appropriate reviewerspr-ci-pass- Ensure CI passespr-squash- Squash when appropriatepr-draft- Use draft PRs for WIP and early feedback
4. History Management (MEDIUM)
history-rebase- Rebase vs mergehistory-no-force-push- Avoid force push to shared brancheshistory-clean- Keep history cleanhistory-tags- Use tags for releaseshistory-worktree- Work on multiple branches with git worktree
5. Collaboration (MEDIUM)
collab-code-review- Effective code reviewscollab-conflicts- Handle merge conflictscollab-communication- Communicate changescollab-gitignore- .gitignore best practices
Essential Guidelines
Conventional Commits
# Format
<type>(<scope>): <subject>
<body>
<footer>Types
| Type | Description |
|---|---|
feat | New feature |
fix | Bug fix |
docs | Documentation only |
style | Formatting, no code change |
refactor | Code change, no feature/fix |
perf | Performance improvement |
test | Adding/updating tests |
chore | Maintenance, dependencies |
ci | CI/CD changes |
build | Build system changes |
revert | Revert previous commit |
Examples
# ✅ Good commit messages
feat(auth): add password reset functionality
fix(cart): resolve quantity update race condition
docs(readme): add installation instructions
refactor(api): extract validation into middleware
test(user): add unit tests for registration
chore(deps): update dependencies to latest versions
# With body and footer
feat(orders): implement order cancellation
Add ability for users to cancel pending orders within 24 hours.
Cancelled orders trigger refund process automatically.
Closes #123
BREAKING CHANGE: Order status enum now includes 'cancelled'
# ❌ Bad commit messages
fix bug
update
WIP
asdfasdf
changes
misc fixesBranch Naming
# ✅ Good branch names
feature/user-authentication
feature/JIRA-123-password-reset
fix/cart-total-calculation
fix/issue-456-login-redirect
hotfix/security-patch
docs/api-documentation
refactor/database-queries
chore/update-dependencies
# ❌ Bad branch names
new-feature
john-branch
test
fix
temp
asdfBranch Workflow
# Main branches
main # Production-ready code
develop # Integration branch (optional)
# Supporting branches
feature/* # New features
fix/* # Bug fixes
hotfix/* # Production hotfixes
release/* # Release preparation
# Workflow
git checkout main
git pull origin main
git checkout -b feature/user-profile
# Work on feature...
git add .
git commit -m "feat(profile): add profile page"
# Keep branch updated
git fetch origin
git rebase origin/main
# Push and create PR
git push -u origin feature/user-profileAtomic Commits
# ❌ One commit doing multiple things
git commit -m "Add login, fix header, update deps"
# ✅ Separate commits for each change
git commit -m "feat(auth): add login page"
git commit -m "fix(header): correct navigation alignment"
git commit -m "chore(deps): update React to v18.2"Commit Frequently, Push Regularly
# ✅ Small, frequent commits
git commit -m "feat(cart): add product to cart"
git commit -m "feat(cart): display cart item count"
git commit -m "feat(cart): implement remove item"
git commit -m "test(cart): add unit tests"
# ❌ One massive commit
git commit -m "feat: implement entire shopping cart"Pull Request Best Practices
PR Title
# Format (like commit)
<type>(<scope>): <description>
# ✅ Good PR titles
feat(auth): implement OAuth2 login
fix(checkout): resolve payment processing error
docs(api): add endpoint documentation
# ❌ Bad PR titles
Update
Fix stuff
WIPPR Description Template
## Summary
Brief description of what this PR does.
## Changes
- Added user authentication endpoints
- Implemented JWT token generation
- Added password hashing with bcrypt
## Testing
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Manual testing completed
## Screenshots (if UI changes)
[Add screenshots here]
## Related Issues
Closes #123
Related to #456
## Checklist
- [ ] Code follows project conventions
- [ ] Self-review completed
- [ ] Tests added/updated
- [ ] Documentation updatedKeep PRs Small
# ✅ Focused PRs
PR #1: Add user model and migrations
PR #2: Implement user registration endpoint
PR #3: Add email verification
PR #4: Implement login/logout
# ❌ Large unfocused PR
PR #1: Add entire user authentication system
(2000+ lines, 50+ files)Rebase vs Merge
# ✅ Rebase for feature branches (clean history)
git checkout feature/my-feature
git fetch origin
git rebase origin/main
# Resolve any conflicts
git push --force-with-lease # Only your branch!
# ✅ Merge for integrating to main (preserve context)
git checkout main
git merge --no-ff feature/my-feature
# Creates merge commit, preserves branch history
# ❌ Never force push to shared branches
git push --force origin main # NEVER DO THISInteractive Rebase (Clean Up)
# Before pushing, clean up commits
git rebase -i HEAD~5
# In editor:
pick abc123 feat: add login page
squash def456 fix typo
squash ghi789 more fixes
pick jkl012 feat: add logout
# Result: clean history with meaningful commitsHandling Conflicts
# During rebase
git rebase origin/main
# CONFLICT in file.ts
# 1. Open conflicted files
# 2. Resolve conflicts (remove markers)
# 3. Stage resolved files
git add file.ts
# 4. Continue rebase
git rebase --continue
# Or abort if needed
git rebase --abortGit Hooks
# .husky/pre-commit
#!/bin/sh
npm run lint
npm run test:unit
# .husky/commit-msg
#!/bin/sh
npx commitlint --edit $1
# commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
};Tagging Releases
# Semantic versioning tags
git tag -a v1.0.0 -m "Release version 1.0.0"
git push origin v1.0.0
# List tags
git tag -l "v1.*"
# Tag format
v1.0.0 # Major.Minor.Patch
v1.0.0-beta.1 # Pre-release
v1.0.0-rc.1 # Release candidate.gitignore Best Practices
# Dependencies
node_modules/
vendor/
# Build outputs
dist/
build/
.next/
# Environment files
.env
.env.local
.env.*.local
# IDE
.idea/
.vscode/
*.swp
# OS files
.DS_Store
Thumbs.db
# Logs
*.log
logs/
# Test coverage
coverage/
# Cache
.cache/
*.cacheUseful Git Commands
# View commit history
git log --oneline --graph --all
# See what changed
git diff --staged
git diff HEAD~1
# Undo last commit (keep changes)
git reset --soft HEAD~1
# Discard local changes
git checkout -- file.ts
git restore file.ts # Git 2.23+
# Stash changes
git stash
git stash pop
git stash list
# Cherry-pick commit
git cherry-pick abc123
# Find who changed a line
git blame file.ts
# Search commits
git log --grep="fix"
git log -S "functionName"Output Format
When reviewing git practices, output findings:
[category] Description of issue or suggestionExample:
[commit] Use imperative mood: "Add feature" not "Added feature"
[branch] Branch name 'test' should follow pattern: feature/description
[pr] PR is too large (50+ files), consider splittingHow to Use
Read individual rule files for detailed explanations:
rules/commit-conventional-format.md
rules/branch-naming-convention.md
rules/pr-small-focused.mdReferences
- Git Official Documentation - Comprehensive Git documentation
- Pro Git Book - The complete Pro Git book
- Conventional Commits - Commit message specification
- GitHub Flow - Lightweight workflow
- How to Write a Git Commit Message - Commit message guide
- Google Code Review Practices - Code review best practices
Examples from Well-Known Projects
Learn from projects with excellent git practices:
- Linux Kernel - Detailed commit messages and patch workflow
- React - Conventional commits and thorough PR reviews
- Vue.js - Clean commit history and good PR templates
- TypeScript - Structured branching and clear release process
- Next.js - Conventional commits and automated releases
Configuration Examples
Commitlint
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'ci', 'build', 'revert']
],
'subject-max-length': [2, 'always', 72],
'body-max-line-length': [2, 'always', 100]
}
};Husky Git Hooks
# .husky/commit-msg
#!/bin/sh
npx commitlint --edit $1
# .husky/pre-commit
#!/bin/sh
npm run lint
npm testGitHub PR Template
# .github/PULL_REQUEST_TEMPLATE.md
## Summary
Brief description of changes
## Changes
- List key changes
- One per line
## Testing
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Manual testing completed
## Related
Closes #issue-number---
Metadata
Skill Version: 1.2.0 Last Updated: 2026-03-08 Total Rules: 31 Categories: 5 (Commit Messages, Branching Strategy, Pull Requests, History Management, Collaboration)
Compatible With:
- Git 2.0+
- GitHub, GitLab, Bitbucket, Azure DevOps
Recommended Tools:
- Git - Version control system
- GitHub CLI - GitHub command-line tool
- Commitlint - Commit message linter
- Husky - Git hooks
- Semantic Release - Automated versioning
---
License
MIT License. This skill is provided as-is for educational and development purposes.
Git Workflow - Agent Guidelines
Version: 1.2.0 | Author: AsyrafHussin
Git best practices, commit conventions, branching strategies, and pull request workflows.
---
Rule Categories
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Commit Messages | CRITICAL | commit- |
| 2 | Branching Strategy | HIGH | branch- |
| 3 | Pull Requests | HIGH | pr- |
| 4 | History Management | MEDIUM | history- |
| 5 | Collaboration | MEDIUM | collab- |
How to Apply
Apply rules in priority order: CRITICAL → HIGH → MEDIUM. Consider team size and project context.
Output format:
[category] Description of issue or recommendationExample:
[commit] Use imperative mood: "Add feature" not "Added feature"
[branch] Branch name should follow pattern: feature/description
[pr] PR is too large (50+ files), consider splitting---
What to Check Per Category
1. Commit Messages (CRITICAL)
- Conventional commit format:
type(scope): subject - Subject line ≤ 72 characters, imperative mood
- Body present for complex changes (explains why, not what)
- Breaking changes marked with
!orBREAKING CHANGE:footer - Issue/ticket references in footer (
Closes #123,Fixes #456) - Atomic commits — one logical change per commit
- Git hooks (Husky + commitlint) enforce format automatically
2. Branching Strategy (HIGH)
- Branch names follow
type/descriptionpattern (kebab-case) main/masterprotected — no direct pushes- Feature branches short-lived (merged within days, not weeks)
- Merged branches deleted after merge
- Release branches used for versioned releases
- Workflow strategy defined: GitHub Flow, GitFlow, or Trunk-Based
- Monorepos: branches scoped to package, affected detection in CI
3. Pull Requests (HIGH)
- PR size: ideal < 200 lines, flag > 400 lines
- Description includes summary, changes, and testing notes
- PR title follows conventional commit format
- Appropriate reviewers assigned
- CI checks required before merge
- Draft PRs used for work-in-progress
4. History Management (MEDIUM)
- No force pushes to shared branches (
main,develop) - Consistent merge strategy (squash, merge, or rebase — pick one)
- Release tags follow semantic versioning (
v1.2.3) - No WIP/fixup commits in main branch history
git worktreeused for parallel branch work instead of stashing
5. Collaboration (MEDIUM)
- Code review comments are constructive and specific
- Merge conflicts resolved by the branch author
- CODEOWNERS file defines ownership for critical paths
- Breaking changes communicated to the team
.gitignorepresent, no secrets or build artifacts committed
---
Priority Rules for Common Requests
"Review my commit message"
1. Check conventional format (commit-conventional-format) 2. Check subject length and mood (commit-meaningful-subject, commit-imperative-mood) 3. Check body if complex change (commit-body-context) 4. Check footer references (commit-references, commit-breaking-changes)
"Review my branch name"
1. Check naming pattern (branch-naming-convention) 2. Check type prefix matches the work type (branch-feature-workflow)
"Review my PR"
1. Check size (pr-small-focused) 2. Check description quality (pr-description-template) 3. Check CI requirements (pr-ci-checks) 4. Check reviewer assignment (pr-reviewers) 5. Check merge strategy (pr-squash-merge)
"Set up git workflow for new project"
1. Set up commitlint with conventional commits config 2. Configure Husky hooks for commit-msg validation 3. Create .github/PULL_REQUEST_TEMPLATE.md 4. Enable branch protection on main 5. Create CONTRIBUTING.md with git guidelines 6. Set up semantic-release for automated versioning
---
Common Issues and Fixes
Vague commit messages
# Bad
git commit -m "fix bug"
# Good
git commit -m "fix(auth): resolve token expiry not refreshing session"Oversized PR
- Split by layer: API endpoints PR → UI components PR → integration PR
- Separate refactoring from feature work
- Use feature flags to merge incomplete features safely
Unprotected main branch
gh api repos/:owner/:repo/branches/main/protection --method PUT --input - <<EOF
{
"required_pull_request_reviews": { "required_approving_review_count": 1 },
"required_status_checks": { "strict": true, "contexts": ["ci/test"] },
"enforce_admins": true,
"allow_force_pushes": false
}
EOFInconsistent branch naming — enforce with CI
# .github/workflows/branch-naming.yml
name: Branch Naming
on: pull_request
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Check branch name
run: |
if [[ ! "${{ github.head_ref }}" =~ ^(feature|fix|hotfix|refactor|docs|test|chore)/.+ ]]; then
echo "Branch name must match pattern: type/description"
exit 1
fi---
Agent Self-Check
Before completing a review, verify:
- [ ] Checked all CRITICAL rules first (commit messages)
- [ ] Provided specific, actionable recommendations
- [ ] Included concrete examples or configuration snippets
- [ ] Explained why each issue matters
- [ ] Prioritized issues by impact
- [ ] Considered team size and project context
---
References
{
"version": "1.2.0",
"skill": {
"name": "git-workflow",
"version": "1.2.0",
"description": "Git best practices, branching strategies, commit conventions, and PR workflows",
"author": "Agent Skills Team",
"license": "MIT",
"keywords": [
"git",
"version-control",
"commits",
"branches",
"pull-requests",
"code-review",
"conventional-commits",
"git-flow",
"trunk-based-development"
]
},
"categories": [
{
"id": "commit",
"name": "Commit Messages",
"priority": "critical",
"description": "Standards for writing clear, meaningful commit messages",
"rule_count": 8
},
{
"id": "branch",
"name": "Branching Strategy",
"priority": "high",
"description": "Guidelines for creating, naming, and managing branches",
"rule_count": 8
},
{
"id": "pull-request",
"name": "Pull Requests",
"priority": "high",
"description": "Best practices for creating and reviewing pull requests",
"rule_count": 6
},
{
"id": "history",
"name": "History Management",
"priority": "medium",
"description": "Techniques for maintaining a clean, useful git history",
"rule_count": 5
},
{
"id": "collaboration",
"name": "Collaboration",
"priority": "medium",
"description": "Practices for effective team collaboration using git",
"rule_count": 4
}
],
"references": [
{
"title": "Git Official Documentation",
"url": "https://git-scm.com/doc",
"description": "Comprehensive official Git documentation covering all commands and concepts",
"type": "official"
},
{
"title": "Pro Git Book",
"url": "https://git-scm.com/book/en/v2",
"description": "The entire Pro Git book, written by Scott Chacon and Ben Straub",
"type": "book"
},
{
"title": "Conventional Commits",
"url": "https://www.conventionalcommits.org",
"description": "A specification for adding human and machine readable meaning to commit messages",
"type": "specification"
},
{
"title": "Git Best Practices",
"url": "https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project",
"description": "Official guidance on contributing to Git projects and best practices",
"type": "guide"
},
{
"title": "GitHub Flow",
"url": "https://docs.github.com/en/get-started/quickstart/github-flow",
"description": "GitHub's lightweight, branch-based workflow",
"type": "workflow"
},
{
"title": "GitFlow Workflow",
"url": "https://nvie.com/posts/a-successful-git-branching-model/",
"description": "Vincent Driessen's popular git branching model",
"type": "workflow"
},
{
"title": "Semantic Versioning",
"url": "https://semver.org/",
"description": "Versioning specification for software releases",
"type": "specification"
},
{
"title": "How to Write a Git Commit Message",
"url": "https://chris.beams.io/posts/git-commit/",
"description": "Comprehensive guide on writing great commit messages",
"type": "guide"
},
{
"title": "GitHub Pull Request Best Practices",
"url": "https://docs.github.com/en/pull-requests/collaborating-with-pull-requests",
"description": "Official GitHub documentation on pull requests and code review",
"type": "guide"
},
{
"title": "Git Merge vs Rebase",
"url": "https://www.atlassian.com/git/tutorials/merging-vs-rebasing",
"description": "Detailed comparison of merge and rebase strategies",
"type": "tutorial"
},
{
"title": "Code Review Best Practices",
"url": "https://google.github.io/eng-practices/review/",
"description": "Google's engineering practices for code review",
"type": "guide"
},
{
"title": "Trunk Based Development",
"url": "https://trunkbaseddevelopment.com/",
"description": "Source-control branching model for continuous integration",
"type": "workflow"
}
],
"tools": [
{
"name": "Git",
"url": "https://git-scm.com/",
"description": "Distributed version control system"
},
{
"name": "GitHub CLI",
"url": "https://cli.github.com/",
"description": "Command-line tool for GitHub operations"
},
{
"name": "Commitlint",
"url": "https://commitlint.js.org/",
"description": "Lint commit messages according to conventional commits"
},
{
"name": "Husky",
"url": "https://typicode.github.io/husky/",
"description": "Git hooks made easy"
},
{
"name": "Semantic Release",
"url": "https://semantic-release.gitbook.io/",
"description": "Automated version management and package publishing"
},
{
"name": "Conventional Changelog",
"url": "https://github.com/conventional-changelog/conventional-changelog",
"description": "Generate changelogs from git metadata"
}
],
"examples": {
"projects": [
{
"name": "Linux Kernel",
"url": "https://github.com/torvalds/linux",
"description": "Excellent example of detailed commit messages and patch workflow"
},
{
"name": "React",
"url": "https://github.com/facebook/react",
"description": "Conventional commits, thorough PR reviews, clear branching"
},
{
"name": "Vue.js",
"url": "https://github.com/vuejs/vue",
"description": "Clean commit history, conventional commits, good PR templates"
},
{
"name": "TypeScript",
"url": "https://github.com/microsoft/TypeScript",
"description": "Structured branching, detailed PR descriptions, clear release process"
},
{
"name": "Next.js",
"url": "https://github.com/vercel/next.js",
"description": "Conventional commits, automated releases, excellent PR workflow"
}
]
},
"configurations": {
"commitlint": {
"file": "commitlint.config.js",
"example": "module.exports = { extends: ['@commitlint/config-conventional'] };"
},
"husky": {
"file": ".husky/commit-msg",
"example": "#!/bin/sh\nnpx commitlint --edit $1"
},
"github": {
"pr_template": ".github/PULL_REQUEST_TEMPLATE.md",
"codeowners": ".github/CODEOWNERS"
}
},
"meta": {
"created": "2026-01-17",
"last_updated": "2026-03-08",
"rule_count": 31,
"structure_version": "2.0",
"compatibility": {
"git": ">=2.0.0",
"platforms": ["GitHub", "GitLab", "Bitbucket", "Azure DevOps"]
}
}
}
Git Workflow
Git best practices, commit conventions, and branching strategies for clean, maintainable repositories.
Version: 1.2.0 | Rules: 31 | License: MIT
---
Overview
This skill provides guidance for:
- Commit message conventions and enforcement
- Branch naming, management, and workflow strategies
- Pull request workflows
- History management and git worktree
- Team collaboration and .gitignore
Categories
1. Commit Messages (Critical) — 8 rules
Conventional commits, atomic changes, meaningful messages, git hooks enforcement.
2. Branching Strategy (High) — 8 rules
Branch naming, feature branches, protected main, GitFlow vs GitHub Flow vs Trunk-Based, monorepo workflows.
3. Pull Requests (High) — 6 rules
Small PRs, descriptions, reviews, CI checks, squash merge, draft PRs.
4. History Management (Medium) — 5 rules
Rebase vs merge, no force push, clean history, tags, git worktree.
5. Collaboration (Medium) — 4 rules
Code reviews, conflict resolution, communication, .gitignore best practices.
Usage
Ask Claude to:
- "Review commit message"
- "Suggest branch name"
- "Check PR description"
- "Set up git workflow for new project"
- "Which workflow strategy should I use?"
- "How to use git worktree?"
Quick Reference
Commit Types
feat: New featurefix: Bug fixdocs: Documentationrefactor: Code restructuretest: Testschore: Maintenanceci: CI/CD changes
Branch Prefixes
feature/: New featuresfix/: Bug fixeshotfix/: Production fixesdocs/: Documentationrefactor/: Refactoring
Workflow Strategy Guide
| Strategy | Best For | Complexity |
|---|---|---|
| GitHub Flow | Small teams, continuous deploy | Low |
| GitFlow | Scheduled releases, versioned apps | High |
| Trunk-Based | Large teams, multiple deploys/day | Medium |
Example Commits
feat(auth): add OAuth login
fix(cart): resolve total calculation
docs(api): update endpoint documentation
chore(deps): upgrade React to v19References
Git Workflow Rule Sections
This document defines the organizational structure for git workflow rules.
Section Categories
1. Commit Messages (commit)
Priority: Critical Description: Standards for writing clear, meaningful commit messages that document code changes effectively.
Commit messages are the permanent record of why changes were made. Well-written commits enable:
- Automated changelog generation
- Semantic versioning
- Easier debugging with git bisect
- Code archaeology and understanding historical decisions
- Better code reviews
Rules in this section:
- Conventional commit format
- Atomic commits
- Imperative mood
- Meaningful subject lines
- Body for context
- Issue references
- Breaking change documentation
---
2. Branching Strategy (branch)
Priority: High Description: Guidelines for creating, naming, and managing branches to enable parallel development and stable releases.
Effective branching strategies allow teams to work in parallel without conflicts while maintaining a stable main branch. This includes:
- Feature branch workflows
- Branch naming conventions
- Branch lifecycle management
- Release strategies
Rules in this section:
- Branch naming conventions
- Feature branch workflow
- Protected main branch
- Short-lived branches
- Delete merged branches
- Release branch strategy
---
3. Pull Requests (pull-request)
Priority: High Description: Best practices for creating and reviewing pull requests to ensure code quality and knowledge sharing.
Pull requests are the primary mechanism for code review and collaboration. Good PR practices:
- Enable thorough code review
- Facilitate knowledge sharing
- Maintain code quality standards
- Document changes for future reference
Rules in this section:
- Small, focused PRs
- PR description templates
- Reviewer assignment
- CI checks
- Squash merge strategy
- Draft PR usage
---
4. History Management (history)
Priority: Medium Description: Techniques for maintaining a clean, useful git history that aids debugging and understanding.
Git history is a valuable resource when:
- Debugging issues with git bisect
- Understanding why code exists
- Reverting problematic changes
- Onboarding new team members
Rules in this section:
- Rebase vs merge
- Avoiding force push on shared branches
- Cleaning up commits
- Tags and releases
---
5. Collaboration (collaboration)
Priority: Medium Description: Practices for effective team collaboration using git workflows and communication.
Successful git collaboration requires:
- Effective code review practices
- Clear communication
- Conflict resolution skills
- Team coordination
Rules in this section:
- Code review best practices
- Merge conflict resolution
- Team communication
---
Priority Levels
| Priority | When to Apply | Impact |
|---|---|---|
| Critical | Always enforce | Core to git workflow success, affects entire team |
| High | Enforce on most projects | Significant quality and collaboration impact |
| Medium | Context-dependent | Important but may vary by team/project |
Category Relationships
Commit Messages (critical)
↓ forms foundation for
Pull Requests (high)
↓ reviewed through
Collaboration (medium)
↓ coordinates
Branching Strategy (high)
↓ managed via
History Management (medium)Using These Sections
When reviewing git practices: 1. Start with Commit Messages - the foundation 2. Check Branching Strategy - the structure 3. Review Pull Requests - the process 4. Verify History Management - the maintenance 5. Assess Collaboration - the team dynamics
Each section builds on the previous ones to create a comprehensive git workflow.
[Rule Title]
[One-sentence description of what this rule is about and why it matters]
Bad Example
# [Description of the bad practice]
git command that demonstrates the problem
# Comments explaining why this is problematic
# [Another example of the bad practice]
git command that shows another anti-pattern
# More context about the issueGood Example
# [Description of the good practice]
git command that demonstrates the solution
# Comments explaining why this works well
# [Another example of the good practice]
git command that shows best practice
# More context about the benefits
# [Advanced or comprehensive example]
git command sequence for complete workflow
# Detailed explanation of the approachWhy
[Explanation of why this rule matters, with specific benefits]
1. [Benefit 1]: Description of first major benefit 2. [Benefit 2]: Description of second major benefit 3. [Benefit 3]: Description of third major benefit 4. [Benefit 4]: Description of fourth major benefit 5. [Benefit 5]: Description of fifth major benefit
[Additional context about the rule:]
| Aspect | Detail |
|---|---|
| When to use | [Situations where this rule applies] |
| When NOT to use | [Exceptions or special cases] |
| Team size | [How this scales with team size] |
| Project type | [Project types this applies to] |
[Practical guidelines or checklist:]
- [Guideline 1]
- [Guideline 2]
- [Guideline 3]
- [Guideline 4]
[Optional: Configuration or automation:]
# Tool configuration
# Example: .gitconfig, GitHub settings, CI configuration[Optional: Related commands or workflow:]
# Useful related commands
git command --options
# Explanation
# Common troubleshooting
git command to fix issues
# When to use this---
Template Guidelines
YAML Frontmatter
- title: Clear, concise rule name
- category: One of: commit, branch, pull-request, history, collaboration
- priority: critical (always enforce), high (most projects), medium (context-dependent)
- tags: 3-5 descriptive tags for searchability
- related: 2-4 related rules that connect to this one
Bad Example Section
- Show 2-3 concrete anti-patterns
- Use real git commands
- Add inline comments explaining the problem
- Be specific about why it's bad
Good Example Section
- Show 2-3 correct approaches
- Use real git commands that work
- Add inline comments explaining benefits
- Progress from simple to comprehensive examples
Why Section
- List 4-5 concrete benefits
- Use bold headers for each benefit
- Include a comparison table if helpful
- Add practical guidelines as bullet points
- Show configuration/automation when relevant
General Writing Guidelines
- Use imperative mood for commands ("Do this", not "You should do this")
- Be specific and actionable
- Include real-world context
- Show both the problem and solution
- Focus on git best practices, commit conventions, PR workflows
- Use good git examples from well-known projects
Delete Merged Branches
Clean up branches after they've been merged to keep the repository tidy and navigable.
Bad Example
# Accumulating merged branches
git branch -a
# feature/user-login (merged 6 months ago)
# feature/payment-v1 (merged 1 year ago)
# feature/old-dashboard (merged 2 years ago)
# fix/ancient-bug (merged 18 months ago)
# ... 200 more stale branches
# Keeping "just in case"
git checkout -b feature/backup-of-merged-branch
# The history is already in main!
# Never cleaning up remotes
git fetch
# Fetching hundreds of stale remote branches
# Reusing merged branch names
git checkout feature/login # old merged branch
git commit -m "new changes"
# Confusing history, unclear what belongs to which PRGood Example
# Delete local branch after merge
git checkout main
git pull origin main
git branch -d feature/user-auth
# -d is safe, won't delete unmerged branches
# Delete remote branch after merge
git push origin --delete feature/user-auth
# Combined cleanup after merge
git checkout main
git pull origin main
git branch -d feature/user-auth
git push origin --delete feature/user-auth
# Clean up all merged local branches
git checkout main
git branch --merged | grep -v "main\|master" | xargs git branch -d
# Clean up stale remote tracking branches
git fetch --prune
# or
git remote prune origin
# Enable auto-delete on GitHub
# Settings > General > Automatically delete head branches
# Periodic cleanup script
git checkout main
git pull --prune
git branch --merged | grep -v "main\|master\|develop" | xargs git branch -d
# Note: xargs -r (skip if empty) is Linux/GNU only — omit on macOSWhy
Cleaning up merged branches maintains repository health:
1. Clarity: Easy to see active work vs. completed work 2. Performance: Fewer branches means faster git operations 3. Navigation: Finding the right branch is easier 4. Reduced Confusion: No ambiguity about branch status 5. Professionalism: Clean repository reflects well on the team
When to delete:
- Immediately after PR is merged
- During regular repository maintenance
- As part of sprint cleanup
Safe deletion commands:
git branch -d- Only deletes if fully mergedgit branch -D- Force delete (use cautiously)git push origin --delete- Delete remote branch
Automation options:
- GitHub: Enable "Automatically delete head branches" in repo settings
- GitLab: Enable "Delete source branch when merge request is accepted"
- CI/CD: Add cleanup step to merge pipelines
Exceptions (branches to keep):
main/masterdevelop(if using GitFlow)release/*branches (until end of support)- Protected environment branches
Feature Branch Workflow
Develop new features in dedicated branches, keeping main stable and deployable at all times.
Bad Example
# Working directly on main
git checkout main
# make changes directly
git commit -m "add new feature"
git push origin main
# Creating feature branch from outdated main
git checkout -b feature/new-widget
# (main has moved ahead by 50 commits)
# Now merge conflicts are inevitable
# Long-running feature branch without sync
git checkout -b feature/big-refactor
# work for 3 months without rebasing
# massive merge conflicts when done
# Multiple features in one branch
git checkout -b my-work
git commit -m "add feature A"
git commit -m "add feature B"
git commit -m "fix bug C"Good Example
# Start from updated main
git checkout main
git pull origin main
git checkout -b feature/user-dashboard
# Make focused commits
git commit -m "feat: add dashboard layout component"
git commit -m "feat: add user stats widget"
git commit -m "test: add dashboard component tests"
# Regularly sync with main
git fetch origin main
git rebase origin/main
# or
git merge origin/main
# Push feature branch for backup and collaboration
git push -u origin feature/user-dashboard
# Create PR when ready
gh pr create --base main --head feature/user-dashboard
# After PR merged, clean up
git checkout main
git pull origin main
git branch -d feature/user-dashboard
git push origin --delete feature/user-dashboardWhy
Feature branch workflow is essential for team collaboration:
1. Isolated Development: Work on features without affecting others or main 2. Code Review: Changes are reviewed via PR before merging 3. CI/CD Integration: Run tests on feature branches before merging 4. Easy Rollback: If a feature causes issues, revert the single merge commit 5. Parallel Work: Multiple features can be developed simultaneously 6. Clean History: Main branch contains only reviewed, tested code
Workflow steps: 1. Create branch from latest main 2. Develop and commit changes 3. Push branch to remote 4. Open pull request 5. Address review feedback 6. Merge when approved and CI passes 7. Delete feature branch
Best practices:
- One feature per branch
- Keep branches short-lived (days, not months)
- Sync with main regularly to minimize conflicts
- Push frequently for backup and visibility
- Delete branches after merging
Protected Main Branch
The main branch should be protected from direct pushes and require pull requests for all changes.
Bad Example
# Pushing directly to main
git checkout main
git commit -m "quick fix"
git push origin main
# Force pushing to main
git push --force origin main
# Bypassing branch protection
git push origin main --no-verify
# Merging locally and pushing
git checkout main
git merge feature/something
git push origin main
# Resetting main to different state
git checkout main
git reset --hard HEAD~5
git push --force origin mainGood Example
# All changes through pull requests
git checkout -b fix/login-issue
git commit -m "fix: resolve login timeout"
git push -u origin fix/login-issue
gh pr create --base main
# Wait for reviews and CI
gh pr checks
gh pr status
# Merge through GitHub/GitLab UI or CLI
gh pr merge --squash
# For hotfixes, still use PR (just expedited)
git checkout -b hotfix/security-patch
git commit -m "fix: patch XSS vulnerability"
git push -u origin hotfix/security-patch
gh pr create --base main --label "urgent"
# Request expedited reviewWhy
Protected main branch is crucial for code quality:
1. Code Review: Every change is reviewed by at least one other person 2. CI Verification: All tests must pass before merging 3. Audit Trail: All changes are traceable through PRs 4. Reduced Risk: Prevents accidental pushes of broken code 5. Compliance: Required for many security and regulatory standards
Recommended protection rules:
# GitHub branch protection settings
main:
required_pull_request_reviews:
required_approving_review_count: 1
dismiss_stale_reviews: true
require_code_owner_reviews: true
required_status_checks:
strict: true
contexts:
- "ci/tests"
- "ci/lint"
enforce_admins: true
required_linear_history: true
allow_force_pushes: false
allow_deletions: falseProtection levels:
- Minimum: Require PR, 1 approval
- Standard: Require PR, 1 approval, CI passing
- Strict: Require PR, 2 approvals, CI passing, CODEOWNERS
- Maximum: All above + enforce for admins, require linear history
Even maintainers and admins should follow the PR process to maintain consistency and set a good example.
Monorepo Git Workflows
Adapt branching and PR strategies for monorepos to avoid unnecessary builds and keep changes scoped.
Bad Example
# PR touches everything — triggers full rebuild for unrelated change
git diff main --name-only
# apps/web/src/components/Button.tsx
# apps/mobile/src/screens/Home.tsx
# libs/shared/utils/format.ts
# apps/api/src/routes/users.ts
# One PR, four unrelated packages changed
# No affected detection — runs all tests on every commit
# CI: run all 4000 tests for a README change
# CI time: 45 minutes for a 1-line docs update
# Flat branch names with no package context
git checkout -b fix-button
# Which package? Which app?Good Example
# Scope branches to the package being changed
git checkout -b feat/web-auth-login
git checkout -b fix/api-user-validation
git checkout -b chore/shared-utils-cleanup
# Keep PRs scoped to one package when possible
git diff main --name-only
# apps/web/src/components/Button.tsx ← one package
# apps/web/src/components/Button.test.tsx
# Use affected commands to run only what changed
# Nx
npx nx affected --target=test
npx nx affected --target=build
npx nx affected --target=lint
# Turborepo
npx turbo run test --filter=...[main]
npx turbo run build --filter=...[main]# CI — only run affected packages
# .github/workflows/ci.yml
name: CI
on: pull_request
jobs:
affected:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for affected detection
- name: Determine affected projects
run: npx nx show projects --affected --base=origin/main
- name: Test affected
run: npx nx affected --target=test --base=origin/main
- name: Build affected
run: npx nx affected --target=build --base=origin/main# Conventional commits with package scope
git commit -m "feat(web): add login page"
git commit -m "fix(api): resolve user not found error"
git commit -m "chore(shared): update lodash dependency"
# Tag releases per package (independent versioning)
git tag web-v2.1.0
git tag api-v1.4.2
git tag shared-v3.0.0Why
Monorepos require adapted practices to stay performant:
1. Affected detection: Running all tests on every commit wastes CI time. nx affected or turbo --filter only runs tasks for packages that changed (directly or via dependency graph).
2. Scoped PRs: Mixing changes across packages makes reviews harder and can cause unintended side effects. One PR per package keeps changes reviewable and rollbacks simple.
3. Scoped commits: Including the package name in the commit scope (feat(web):, fix(api):) makes the changelog and git history meaningful at the package level.
4. Independent versioning: Each package can have its own version and release cycle. Tag packages individually (web-v2.1.0) rather than tagging the whole repo.
5. `fetch-depth: 0` in CI: Shallow clones break affected detection — the tool needs full history to determine what changed relative to the base branch.
Branch Naming Convention
Use consistent, descriptive branch names that indicate the type and purpose of the work.
Bad Example
# Vague or meaningless names
git checkout -b fix
git checkout -b update
git checkout -b johns-branch
git checkout -b test123
# Inconsistent formats
git checkout -b Feature_Login
git checkout -b fix-auth
git checkout -b ISSUE-456
git checkout -b add_new_stuff
# Too long or including unnecessary info
git checkout -b feature/add-the-new-user-authentication-system-with-oauth-and-jwt-support-for-the-mobile-app
# Spaces or special characters
git checkout -b "my feature"
git checkout -b feature@loginGood Example
# Type prefix with descriptive slug
git checkout -b feature/user-authentication
git checkout -b fix/login-timeout
git checkout -b hotfix/security-patch
git checkout -b refactor/database-layer
# Include ticket/issue number
git checkout -b feature/AUTH-123-oauth-integration
git checkout -b fix/BUG-456-null-pointer
git checkout -b feature/GH-789-dark-mode
# Short but descriptive
git checkout -b docs/api-reference
git checkout -b test/payment-integration
git checkout -b chore/upgrade-dependencies
# Release and environment branches
git checkout -b release/v2.1.0
git checkout -b hotfix/v2.0.1Why
Consistent branch naming provides many benefits:
1. Quick Identification: Instantly understand the purpose of any branch 2. Automation Ready: CI/CD can trigger different workflows based on branch prefixes 3. Filtering: Easily list branches by type (git branch --list 'feature/*') 4. Issue Tracking: Link branches to tickets for traceability 5. Clean History: Merge commits reference meaningful branch names
Recommended prefixes:
feature/- New features or enhancementsfix/- Bug fixeshotfix/- Urgent production fixesrefactor/- Code restructuringdocs/- Documentation updatestest/- Test additions or fixeschore/- Maintenance tasksrelease/- Release preparation
Naming guidelines:
- Use lowercase letters
- Separate words with hyphens (kebab-case)
- Keep names concise but meaningful
- Include issue/ticket numbers when applicable
- Avoid personal identifiers (use ticket IDs instead)
Release Branch Strategy
Use a consistent branching strategy for releases that fits your deployment model and team size.
Bad Example
# No release strategy - deploying random commits
git checkout main
git tag v1.2.3 # Tagged without preparation
git push --tags
# Mixing release and feature work
git checkout release/v2.0
git commit -m "feat: add new feature" # Features in release branch!
git commit -m "fix: hotfix for release"
# Inconsistent tagging
git tag release-1.0
git tag v1.1.0
git tag 1.2
git tag version-1.3.0
# No clear production state
git log main # Which commit is in production?
# Nobody knows
# Release branches that never merge back
git checkout release/v1.0
git commit -m "fix: production hotfix"
# Hotfix never merged to main, gets lostGood Example
# Trunk-Based Development (recommended for CI/CD)
git checkout main
# All development happens on main
# Deploy from main with feature flags
git tag -a v1.2.3 -m "Release version 1.2.3"
git push origin v1.2.3
# GitFlow for scheduled releases
git checkout -b release/v2.0.0 develop
# Only bug fixes in release branch
git commit -m "fix: correct date format in reports"
# When ready:
git checkout main
git merge --no-ff release/v2.0.0
git tag -a v2.0.0 -m "Release version 2.0.0"
git checkout develop
git merge --no-ff release/v2.0.0
git branch -d release/v2.0.0
# Hotfix workflow
git checkout -b hotfix/v2.0.1 main
git commit -m "fix: critical security patch"
git checkout main
git merge --no-ff hotfix/v2.0.1
git tag -a v2.0.1 -m "Hotfix release 2.0.1"
git checkout develop
git merge --no-ff hotfix/v2.0.1
git branch -d hotfix/v2.0.1
# Release with changelog
git checkout main
git tag -a v1.5.0 -m "Release version 1.5.0
Features:
- Add user dashboard (#123)
- Implement search functionality (#456)
Fixes:
- Resolve login timeout issue (#789)
Breaking Changes:
- API v1 deprecated, use v2"Why
A clear release strategy ensures reliable deployments:
1. Predictability: Everyone knows how releases work 2. Traceability: Clear mapping between tags and deployments 3. Hotfix Path: Quick way to patch production issues 4. Stability: Production code is always identifiable 5. Rollback: Easy to revert to previous release
Common strategies:
Trunk-Based Development
- All development on main
- Feature flags for incomplete work
- Deploy main continuously
- Best for: Small teams, frequent deployments
GitHub Flow
- main is always deployable
- Feature branches merge to main
- Deploy after merge or on tag
- Best for: Web apps, SaaS products
GitFlow
- main = production
- develop = integration
- release branches for preparation
- Best for: Scheduled releases, versioned software
Tagging conventions:
# Semantic versioning
v1.0.0 # Major.Minor.Patch
v1.0.0-rc.1 # Release candidate
v1.0.0-beta # Beta release
v1.0.0-alpha # Alpha release
# Always use annotated tags for releases
git tag -a v1.0.0 -m "Release message"Short-Lived Branches
Keep feature branches short-lived to minimize merge conflicts and integration challenges.
Bad Example
# Branch exists for months
git log --oneline feature/big-rewrite
# Shows commits spanning 4 months
# 200+ commits, massive diff from main
# Stale branch that diverged significantly
git checkout feature/old-feature
git diff main --stat
# 150 files changed, 10000 insertions, 5000 deletions
# "Development" branch that never merges
git checkout develop
# Perpetually behind main, used as dumping ground
# Long-running feature with no intermediate merges
git checkout -b feature/new-architecture
# 6 months later, impossible to mergeGood Example
# Feature branch lives for days, not weeks
git checkout -b feature/add-search
# Day 1: implement basic search
# Day 2: add tests, create PR
# Day 3: address review, merge
# Break large features into smaller branches
git checkout -b feature/search-api
# Merge after API is done
git checkout -b feature/search-ui
# Merge after UI is done
git checkout -b feature/search-integration
# Merge after integration is done
# Use feature flags for incomplete features
git checkout -b feature/new-checkout
git commit -m "feat: add new checkout (behind feature flag)"
# Can merge to main even though feature isn't complete
# Regular rebasing keeps branch fresh
git checkout feature/user-profile
git fetch origin main
git rebase origin/main
# Conflicts are small and manageableWhy
Short-lived branches reduce risk and improve collaboration:
1. Fewer Conflicts: Less time to diverge from main = fewer merge conflicts 2. Easier Reviews: Small PRs are reviewed faster and more thoroughly 3. Faster Feedback: Issues discovered earlier in smaller increments 4. Reduced Risk: If something goes wrong, less code to debug/revert 5. Better Flow: Work moves through the system continuously
Guidelines for short-lived branches:
- Target: 1-5 days
- Maximum: 2 weeks (exceptional cases)
- If longer needed: break into smaller pieces
Strategies for large features:
- Feature Flags: Merge incomplete features behind toggles
- Vertical Slices: Deliver end-to-end thin slices
- Branch by Abstraction: Introduce new implementation alongside old
- Dark Launching: Deploy without exposing to users
Warning signs of long-lived branches:
- Multiple "sync with main" merge commits
- Commit messages like "merge conflicts resolved"
- Fear of rebasing due to size
- Multiple developers afraid to touch the branch
Workflow Strategies
Choose the right branching strategy for your team — GitFlow, GitHub Flow, or Trunk-Based Development.
Bad Example
# No defined strategy — everyone does their own thing
git checkout -b johns-stuff
git checkout -b new-feature-v2-final
git checkout -b HOTFIX_URGENT
# Inconsistent, uncoordinated, leads to merge chaos
# Using GitFlow for a small team with continuous deployment
# Overhead of develop, release, hotfix branches adds complexity
# with no real benefit for a 2-person team deploying dailyGood Example
GitHub Flow — Simple, continuous deployment
Best for: Small teams, SaaS products, frequent releases
# Only two concepts: main + feature branches
git checkout -b feature/user-notifications
# work, commit, push...
gh pr create --title "feat: add user notifications"
# Review → CI passes → Squash merge to main → Deploy
git branch -d feature/user-notificationsmain ──●──────────────────●── (always deployable)
\ /
feature/xyz ──●GitFlow — Structured release cycles
Best for: Mobile apps, versioned software, scheduled releases
# Long-lived branches
main # Production releases only
develop # Integration branch
# Supporting branches
feature/* # New features → develop
release/* # Release prep → main + develop
hotfix/* # Production fixes → main + develop
# Create a feature
git checkout -b feature/payment develop
# Complete feature
git merge --no-ff feature/payment develop
git branch -d feature/payment
# Prepare release
git checkout -b release/v1.2.0 develop
# Bug fixes only...
git merge --no-ff release/v1.2.0 main
git tag -a v1.2.0
git merge --no-ff release/v1.2.0 developTrunk-Based Development — Maximum CI/CD velocity
Best for: Large teams, microservices, high deployment frequency
# Everyone commits to main (trunk) daily
# Feature flags control what's visible to users
git checkout main
git pull
# Make small change
git commit -m "feat(search): add index for user queries behind flag"
git push origin main
# CI runs → Deploy immediately
# Short-lived feature branches (max 1-2 days)
git checkout -b feature/search-v2
# Complete in hours, not days
gh pr create # Small PR, fast review
# Merge same dayWhy
Choosing the right strategy prevents coordination problems:
| Strategy | Team Size | Release Cadence | Complexity |
|---|---|---|---|
| GitHub Flow | 1–10 | Continuous | Low |
| GitFlow | Any | Scheduled (weekly/monthly) | High |
| Trunk-Based | 10+ | Multiple times/day | Medium |
Decision guide:
- Deploying multiple times per day → Trunk-Based
- Scheduled releases (app stores, versioned APIs) → GitFlow
- Everything else → GitHub Flow
The worst outcome is mixing strategies — pick one and document it in CONTRIBUTING.md.
Code Review Best Practices
Conduct thorough, constructive code reviews that improve code quality and share knowledge.
Bad Example
# Rubber-stamp approval
gh pr review 123 --approve --body "LGTM"
# No actual review conducted
# Harsh, unconstructive feedback
gh pr review 123 --request-changes --body "
This code is terrible.
Why would you do it this way?
This is completely wrong."
# Nitpicking style over substance
# 50 comments about spacing and naming
# 0 comments about logic or architecture
# Delayed reviews blocking progress
# PR sits for 2 weeks without any review
# Team velocity suffers
# Review scope creep
gh pr review 123 --body "
While you're here, can you also:
- Refactor the entire module
- Add 100% test coverage
- Update all documentation"Good Example
# Thorough review with specific feedback
gh pr review 123 --comment --body "
Overall this looks good! A few suggestions:
**Logic:**
- The retry logic on line 45 could cause infinite loops if the server keeps returning 503
**Security:**
- User input on line 78 should be sanitized before SQL query
**Testing:**
- Consider adding a test case for empty input
**Minor:**
- Typo on line 23: 'recieve' -> 'receive'"
# Constructive change request
gh pr review 123 --request-changes --body "
Thanks for this implementation! I found one issue that should be addressed before merging:
The password comparison on line 56 uses string equality (`==`) instead of a timing-safe comparison, which could be vulnerable to timing attacks.
Suggested fix:
\`\`\`python
import hmac
if hmac.compare_digest(stored_hash, input_hash):
\`\`\`
Let me know if you'd like to discuss this approach!"
# Approval with minor suggestions
gh pr review 123 --approve --body "
Looks great! Nice clean implementation.
Optional suggestions (not blocking):
- Line 34: Could use early return for readability
- Consider adding a docstring to the main function
Approving as-is - feel free to address these in a follow-up PR if you agree."Why
Effective code review is essential for team success:
1. Quality Assurance: Catch bugs before they reach production 2. Knowledge Sharing: Spread understanding of the codebase 3. Mentorship: Junior developers learn from feedback 4. Consistency: Maintain coding standards across the team 5. Security: Fresh eyes catch vulnerabilities
Review checklist:
Functionality
- [ ] Does the code do what it's supposed to do?
- [ ] Are edge cases handled?
- [ ] Is error handling appropriate?
Security
- [ ] Input validation present?
- [ ] No sensitive data exposed?
- [ ] Authentication/authorization correct?
Design
- [ ] Is the approach appropriate?
- [ ] Are there simpler solutions?
- [ ] Does it follow existing patterns?
Testing
- [ ] Are tests present and meaningful?
- [ ] Do tests cover edge cases?
- [ ] Are tests maintainable?
Maintainability
- [ ] Is the code readable?
- [ ] Are names descriptive?
- [ ] Is it properly documented?
Review etiquette:
- Review promptly (within 24 hours)
- Be constructive and specific
- Explain the "why" behind suggestions
- Distinguish blocking issues from suggestions
- Ask questions instead of making demands
- Praise good code, not just critique issues
- Remember: review the code, not the person
Team Communication in Git Workflows
Communicate effectively with your team through commits, PRs, and related tools to maintain smooth collaboration.
Bad Example
# Surprise large changes with no communication
git push origin main # (direct push bypassing PR)
# Team wakes up to 500 changed files
# Vague PR with no context
gh pr create --title "updates" --body ""
# Team has no idea what this is or why
# No response to review comments
# Reviewer asks questions
# PR author ignores for days
# Eventually force merges
# Working on same area without coordination
# Developer A: feature/user-profile
# Developer B: feature/profile-redesign
# Both discover conflict after 2 weeks of work
# Silent rebases/force pushes
git rebase main
git push --force
# No notification to collaborators on the branchGood Example
# Announce significant changes in advance
# Slack/Teams: "Heads up - I'll be refactoring the auth module this week.
# PRs touching auth/* might have conflicts."
# Detailed PR description communicating intent
gh pr create --title "feat: implement user notification system" --body "
## Summary
Adding push notification support. This is the first part of the
notification epic (see #100 for full scope).
## Team Notes
- @backend-team: I added new API endpoints, please review the contracts
- @mobile-team: This enables the notification feature you're waiting for
- @devops: New env vars needed: PUSH_NOTIFICATION_KEY, PUSH_NOTIFICATION_SECRET
## Dependencies
- Requires #234 to be merged first
- Mobile app changes tracked in mobile-repo#567
## Testing
Please test with staging push notification service."
# Responsive PR communication
# Reviewer: "Why did you choose this approach?"
# Author: "Good question! I considered X and Y but chose Z because...
# Let me add a comment in the code explaining this."
# Coordinate overlapping work
# Before starting: "I'm going to be working on the profile page this week.
# Is anyone else planning changes there?"
# Communicate before force push
# In PR comment: "I'm about to rebase this branch to resolve conflicts.
# @collaborator - please push any local changes before I proceed."Why
Good communication prevents problems and builds trust:
1. Avoid Conflicts: Know what others are working on 2. Faster Reviews: Context helps reviewers understand quickly 3. Better Decisions: Team input improves solutions 4. Reduced Surprises: No unexpected breaking changes 5. Team Morale: Respectful communication builds relationships
Communication points in the workflow:
| When | What to Communicate |
|---|---|
| Starting work | What you're working on, expected scope |
| Creating PR | What, why, testing notes, dependencies |
| During review | Answer questions promptly, explain decisions |
| Before force push | Warn collaborators on shared branches |
| After merging | Announce if it affects others |
| Blocking issues | Escalate early, don't wait |
Channels for different purposes:
Git commits -> Future developers ("why was this done?")
PR description -> Current reviewers ("what should I review?")
PR comments -> Specific code discussion
Slack/Teams -> Urgent coordination, announcements
Issues/Tickets -> Permanent record, tracking
Documentation -> Long-term referencePR comment best practices:
- Respond to all reviewer comments
- Mark resolved conversations
- Use suggestions feature for small changes
- Tag relevant people for specific questions
- Update description when scope changes
Team conventions to establish:
- Review turnaround time expectations
- PR size limits
- Required reviewers/approvals
- Communication channels for different purposes
- On-call/escalation procedures
Merge Conflict Resolution
Handle merge conflicts carefully and systematically to maintain code integrity.
Bad Example
# Blindly accepting one side
git checkout --ours .
git add .
git commit -m "resolve conflicts"
# Potentially lost important changes
# Blindly accepting incoming changes
git checkout --theirs .
git add .
git commit -m "resolve conflicts"
# Potentially lost your own work
# Leaving conflict markers in code
<<<<<<< HEAD
const timeout = 5000;
=======
const timeout = 10000;
>>>>>>> feature/update-timeout
# Code doesn't compile, markers committed
# Not testing after resolution
git add .
git commit -m "merge conflict resolved"
git push
# Broken code pushed without verification
# Complex merge without understanding both sides
# "I'll just delete this stuff and hope it works"Good Example
# Check conflict status
git status
# Shows files with conflicts
# View conflicts in detail
git diff --check
# Open conflicted file and understand both changes
# Look for conflict markers:
# <<<<<<< HEAD (your changes)
# =======
# >>>>>>> branch-name (their changes)
# Resolve thoughtfully, keeping necessary parts of both
# Before:
<<<<<<< HEAD
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
=======
function calculateTotal(items, taxRate) {
const subtotal = items.reduce((sum, item) => sum + item.price, 0);
return subtotal * (1 + taxRate);
}
>>>>>>> feature/add-tax
# After (merged thoughtfully):
function calculateTotal(items, taxRate = 0) {
const subtotal = items.reduce((sum, item) => sum + item.price, 0);
return subtotal * (1 + taxRate);
}
# Stage resolved file
git add src/utils/pricing.js
# Continue rebase or complete merge
git rebase --continue
# or
git commit -m "merge: resolve conflict in pricing calculation
Merged tax calculation feature with existing implementation.
Made taxRate optional with default of 0 for backward compatibility."
# Verify resolution
npm test
npm run buildWhy
Proper conflict resolution prevents bugs and preserves work:
1. Code Integrity: Both sets of changes are considered 2. Functionality: Merged code works correctly 3. History Clarity: Meaningful merge commit explains resolution 4. Team Trust: Others' work is respected 5. Prevention: Understanding conflicts helps prevent future ones
Conflict resolution workflow:
# 1. Identify conflicts
git status
# 2. For each conflicted file:
# - Open in editor
# - Understand both versions
# - Create merged version
# - Remove conflict markers
# - Stage the file
git add <resolved-file>
# 3. Complete the merge/rebase
git commit # for merge
git rebase --continue # for rebase
# 4. Verify everything works
npm test
npm run build
# 5. Push when confident
git pushUseful tools for conflict resolution:
# Use merge tool
git mergetool
# View specific version
git show :1:file # Common ancestor
git show :2:file # Your version (HEAD)
git show :3:file # Their version (incoming)
# Abort if overwhelmed
git merge --abort
git rebase --abortPrevention strategies:
- Keep branches short-lived
- Sync with main frequently
- Communicate about overlapping work
- Use feature flags to avoid parallel changes
- Coordinate large refactors with the team
.gitignore Best Practices
Use .gitignore to keep secrets, build artifacts, and local config out of the repository.
Bad Example
# No .gitignore — committing everything
git add .
git commit -m "initial commit"
# Commits: node_modules/, .env, dist/, .DS_Store, *.log
# Overly broad ignore (ignores too much)
echo "*" > .gitignore
# Breaks git — nothing gets tracked
# .gitignore added after secrets already committed
git log --all --full-history -- .env
# commit abc123: "add .env" ← secret is in history forever
# Adding to .gitignore now does NOT remove it from history
# Personal ignores in the shared .gitignore
echo ".idea/" >> .gitignore
echo "*.suo" >> .gitignore
# IDE-specific files belong in global gitignore, not the repoGood Example
# .gitignore — committed to the repository
# Dependencies
node_modules/
vendor/
.venv/
__pycache__/
# Build outputs
dist/
build/
.next/
out/
*.egg-info/
# Environment & secrets — NEVER commit these
.env
.env.local
.env.*.local
*.pem
*.key
secrets.json
# Logs
*.log
logs/
npm-debug.log*
# Test coverage
coverage/
.nyc_output/
# OS files — use global gitignore instead, but common to include
.DS_Store
Thumbs.db
# Cache
.cache/
.parcel-cache/
.eslintcache# Set up a global gitignore for personal IDE/OS files
git config --global core.excludesfile ~/.gitignore_global
# ~/.gitignore_global
.idea/
.vscode/
*.suo
*.swp
.DS_Store
Thumbs.db# If secrets were accidentally committed — remove from history
git filter-repo --path .env --invert-paths
# Rotate ALL exposed secrets immediately — history removal is not enough
# Force push and notify all collaborators to re-cloneWhy
A well-maintained .gitignore prevents three categories of problems:
1. Security: .env files, private keys, and API tokens must never reach the repository. Once committed, secrets are compromised even after removal — they remain in git history and may have been cloned or cached.
2. Repository bloat: node_modules/ can contain 100,000+ files. Committing it makes clones slow and diffs unreadable.
3. Noise: Generated files (dist/, *.log, OS artifacts) create meaningless diffs and conflict with teammates' local builds.
Key rules:
- Add
.gitignoreas the very first commit - Never commit
.env— use.env.examplewith placeholder values instead - IDE-specific files (
.idea/,.vscode/) go in the global~/.gitignore_global, not the repo - Use
git rm --cached <file>to stop tracking a file that was accidentally committed (then add to.gitignore) - After committing secrets: rotate credentials immediately, then clean history
Atomic Commits
Each commit should represent a single, complete, logical change that can be understood and reverted independently.
Bad Example
# Multiple unrelated changes in one commit
git add .
git commit -m "fix login bug, add new dashboard feature, update styles, fix typos"
# Partial implementation that breaks the build
git commit -m "start working on user profile"
# (code doesn't compile or tests fail)
# Mixing refactoring with feature changes
git commit -m "add payment processing and refactor entire codebase"Good Example
# Single focused change
git commit -m "fix: prevent null pointer in login validation"
# Complete feature in one commit (if small enough)
git commit -m "feat: add password strength indicator to signup form"
# Separate commits for separate concerns
git commit -m "refactor: extract validation logic to separate module"
git commit -m "feat: add email format validation"
git commit -m "test: add unit tests for email validation"
# Each commit leaves the codebase in a working state
git commit -m "feat: add user profile API endpoint"
git commit -m "feat: add user profile UI component"
git commit -m "feat: connect profile UI to API"Why
Atomic commits provide critical benefits:
1. Easy Reversion: If a change introduces a bug, you can revert just that commit without losing other work 2. Clear History: Each commit tells a complete story about what changed and why 3. Simplified Code Review: Reviewers can understand changes one logical unit at a time 4. Bisect Friendly: git bisect works effectively when each commit is a complete, working state 5. Cherry-Pick Ready: Individual changes can be applied to other branches without bringing unrelated code 6. Reduced Conflicts: Smaller, focused changes are less likely to conflict with others' work
Rule of thumb: If you need "and" in your commit message to describe what you did, consider splitting it into multiple commits.
Commit Body for Context
Use the commit body to explain the motivation behind changes and provide additional context that isn't obvious from the code.
Bad Example
# No body when context is needed
git commit -m "fix: change timeout from 30s to 120s"
# Why? Was 30s too short? For what operation?
# Body that just repeats the subject
git commit -m "fix: update user validation
Updated the user validation."
# Body describing obvious code changes
git commit -m "refactor: rename variable
Changed 'x' to 'count' on line 45.
Changed 'y' to 'total' on line 46.
Changed 'z' to 'average' on line 47."
# No blank line between subject and body
git commit -m "feat: add caching layer
This improves performance significantly."Good Example
# Explain the WHY, not just the WHAT
git commit -m "fix: increase API timeout from 30s to 120s
Large file uploads were failing silently because the
default 30-second timeout was too short. Analysis showed
uploads over 10MB consistently need 60-90 seconds.
Setting timeout to 120s provides buffer for network variance.
Fixes #892"
# Provide context for non-obvious decisions
git commit -m "feat: implement custom retry logic instead of using library
The popular retry-lib package has a memory leak in v2.x
(see github.com/retry-lib/issues/234) that affects long-running
processes. Our custom implementation:
- Uses exponential backoff with jitter
- Respects Retry-After headers
- Has zero dependencies
Will revisit when retry-lib v3 is stable."
# Document trade-offs and alternatives considered
git commit -m "perf: use Redis for session storage
Benchmarks showed 40% latency reduction vs. PostgreSQL
for session lookups. Considered alternatives:
- Memcached: Faster but no persistence
- In-memory: Doesn't scale across instances
Redis provides good balance of speed and durability.
See: docs/adr/005-session-storage.md"Why
A well-written commit body is invaluable:
1. Future Context: Six months later, you'll forget why you made certain decisions 2. Code Review Aid: Reviewers understand intent without asking questions 3. Onboarding Help: New team members learn project history and reasoning 4. Debugging Support: Understanding past decisions helps when investigating issues 5. Documentation: Commit messages are searchable, permanent documentation
What to include in the body:
- Motivation: Why was this change necessary?
- Approach: Why this solution over alternatives?
- Trade-offs: What compromises were made?
- Side Effects: Any non-obvious impacts?
- References: Links to issues, docs, or discussions
Format guidelines:
- Blank line between subject and body
- Wrap at 72 characters
- Use bullet points for lists
- Include relevant links
Documenting Breaking Changes
Clearly mark and document breaking changes that require consumers to modify their code or configuration.
Bad Example
# No indication of breaking change
git commit -m "refactor: update API response format"
# Consumers have no warning their code will break
# Buried breaking change
git commit -m "feat: add new features and improvements"
# Breaking changes hidden among other updates
# Vague breaking change notice
git commit -m "fix: change function signature (BREAKING)"
# What changed? What should consumers do?
# Breaking change in patch-level commit
git commit -m "fix: rename getUserById to fetchUser"
# This is not a fix, it's a breaking changeGood Example
# Conventional commit with breaking change indicator
git commit -m "feat(api)!: change response format to JSON:API spec"
# Breaking change with detailed footer
git commit -m "feat(auth)!: require API key for all endpoints
BREAKING CHANGE: All API endpoints now require authentication.
Previously, read-only endpoints were public.
Migration guide:
1. Generate an API key in the dashboard
2. Add 'Authorization: Bearer <key>' header to all requests
3. Update rate limit expectations (authenticated: 1000/min)
See: docs/migration/v3-auth.md"
# Multiple breaking changes documented
git commit -m "refactor(core)!: modernize configuration system
BREAKING CHANGE: Configuration file format changed from INI to YAML.
Before (config.ini):
[database]
host=localhost
port=5432
After (config.yaml):
database:
host: localhost
port: 5432
BREAKING CHANGE: Environment variable prefix changed from APP_ to MYAPP_.
Run 'npx migrate-config' to automatically convert your configuration."
# Deprecation notice (warning before breaking)
git commit -m "feat: add new validation API, deprecate old one
The validateInput() function is deprecated and will be removed in v4.0.
Use the new Validator class instead.
Deprecated:
validateInput(data, rules)
New:
new Validator(rules).validate(data)
See: docs/migration/validation.md"Why
Proper breaking change documentation is essential:
1. Semantic Versioning: Breaking changes trigger major version bumps 2. Changelog Generation: Tools extract BREAKING CHANGE footers automatically 3. Consumer Awareness: Developers know to check migration guides before upgrading 4. Upgrade Planning: Teams can schedule time for necessary code changes 5. Trust: Clear communication builds confidence in your library/API
What constitutes a breaking change:
- Removing or renaming public API
- Changing function signatures (parameters, return types)
- Changing default behavior
- Removing or renaming configuration options
- Changing data formats (API responses, file formats)
- Increasing minimum dependency versions
- Changing error types or codes
Best practices:
- Use
!after type/scope for conventional commits - Include
BREAKING CHANGE:in the footer - Provide migration instructions
- Link to detailed migration documentation
- Consider deprecation warnings before removal
- Group breaking changes in dedicated releases when possible
Conventional Commit Format
Use the Conventional Commits specification for standardized, machine-readable commit messages.
Bad Example
# Vague, non-standard format
git commit -m "fixed stuff"
# No type prefix
git commit -m "add user authentication"
# Mixed formats in the same repo
git commit -m "[FEATURE] login page"
git commit -m "BUG: fix crash"
git commit -m "updated tests"Good Example
# Standard conventional commit format
git commit -m "feat: add user authentication module"
# With scope for more context
git commit -m "fix(auth): resolve token refresh race condition"
# With body for detailed explanation
git commit -m "feat(api): add rate limiting to endpoints
Implement token bucket algorithm for rate limiting.
Default limit is 100 requests per minute per user.
Closes #234"
# Breaking change indicator
git commit -m "feat(api)!: change response format to JSON:API spec"Why
Conventional Commits provide several benefits:
1. Automated Changelog Generation: Tools can automatically generate changelogs from commit history 2. Semantic Versioning: Commits directly map to version bumps (feat = minor, fix = patch, breaking = major) 3. Clear Communication: Team members instantly understand the nature of changes 4. Searchable History: Easy to filter commits by type (e.g., find all fixes) 5. CI/CD Integration: Automate releases based on commit types
Common types include:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, semicolons)refactor: Code changes that neither fix bugs nor add featuresperf: Performance improvementstest: Adding or updating testschore: Maintenance tasks (dependencies, build scripts)ci: CI/CD configuration changes
Git Hooks for Commit Enforcement
Use Git hooks to automatically enforce commit message standards and run checks before commits reach the repository.
Bad Example
# No hooks — bad commits reach the repo unchecked
git commit -m "fix"
git commit -m "wip"
git commit -m "asdfasdf"
# All accepted with no validation
# Manual-only enforcement — relies on discipline
# Team agrees to follow conventions but nothing enforces it
# One developer ignores conventions, pollutes historyGood Example
# Install Husky and commitlint
npm install --save-dev husky @commitlint/cli @commitlint/config-conventional
# Enable Husky
npx husky init
# Create commit-msg hook
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
chmod +x .husky/commit-msg
# Create pre-commit hook for linting and tests
echo "npm run lint && npm run test:unit" > .husky/pre-commit
chmod +x .husky/pre-commit// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'ci', 'build', 'revert']
],
'subject-max-length': [2, 'always', 72],
'subject-case': [2, 'always', 'lower-case'],
'body-max-line-length': [2, 'always', 100],
},
};// package.json — ensure hooks are installed on npm install
{
"scripts": {
"prepare": "husky"
}
}# Now bad commits are rejected automatically
git commit -m "fix"
# ✖ subject may not be empty [subject-empty]
# ✖ type may not be empty [type-empty]
git commit -m "feat(auth): add OAuth login"
# ✔ Commit message is validWhy
Git hooks automate enforcement at the source, before bad commits reach the repository:
1. Immediate Feedback: Developers know instantly if their commit message is wrong 2. Zero Drift: Conventions are enforced consistently — no exceptions 3. Automated Changelog: Properly formatted commits enable tools like semantic-release 4. No CI Dependency: Catches issues locally before pushing, saving CI minutes 5. Onboarding: New developers learn conventions through immediate feedback
Hook types commonly used:
| Hook | When | Use For |
|---|---|---|
commit-msg | After writing message | Validate commit format |
pre-commit | Before commit | Lint, format, unit tests |
pre-push | Before push | Integration tests, build check |
Important: Add .husky/ to version control so all team members get the same hooks. The prepare script in package.json installs hooks automatically after npm install.
Imperative Mood in Commit Messages
Write commit messages in the imperative mood, as if giving a command or instruction.
Bad Example
# Past tense
git commit -m "fixed the login bug"
git commit -m "added user authentication"
git commit -m "updated the README"
# Present participle (-ing form)
git commit -m "fixing memory leak in cache"
git commit -m "adding support for dark mode"
# Third person present
git commit -m "fixes issue with form validation"
git commit -m "adds new API endpoint"
# Descriptive statement
git commit -m "this commit adds a new feature"
git commit -m "changes to the database schema"Good Example
# Imperative mood - like giving a command
git commit -m "fix the login bug"
git commit -m "add user authentication"
git commit -m "update the README"
# Think: "This commit will..."
git commit -m "fix memory leak in cache"
git commit -m "add support for dark mode"
# Matches git's own conventions
git commit -m "merge branch 'feature' into main"
git commit -m "revert 'add broken feature'"
# Complete examples
git commit -m "refactor database connection handling"
git commit -m "remove deprecated API endpoints"
git commit -m "implement retry logic for failed requests"Why
Using imperative mood provides consistency and clarity:
1. Git's Convention: Git itself uses imperative mood ("Merge branch...", "Revert...", "Initial commit") 2. Completes the Sentence: The message completes "If applied, this commit will..."
- "If applied, this commit will fix the login bug" (correct)
- "If applied, this commit will fixed the login bug" (incorrect)
3. Conciseness: Imperative mood is typically shorter and more direct 4. Action-Oriented: Focuses on what the commit does, not what was done 5. Industry Standard: Most open source projects and style guides recommend this convention
Quick test: Read your commit message after "This commit will..." - if it sounds grammatically correct, you've used imperative mood.
Meaningful Commit Subject Lines
Write clear, descriptive subject lines that explain WHAT changed and WHY it matters.
Bad Example
# Too vague
git commit -m "fix bug"
git commit -m "update code"
git commit -m "changes"
git commit -m "WIP"
# Too long (hard to scan in logs)
git commit -m "fix the bug where users could not log in when they had special characters in their password because the validation regex was incorrect"
# Implementation details instead of purpose
git commit -m "change line 42 in auth.js"
git commit -m "add if statement to check null"
# Meaningless references
git commit -m "fix issue"
git commit -m "address review comments"
git commit -m "final fix"
git commit -m "fix fix"Good Example
# Clear, specific, and concise (50 chars or less ideal)
git commit -m "fix: allow special characters in passwords"
# Explains the what and implies the why
git commit -m "feat: add rate limiting to prevent API abuse"
# Specific about the affected area
git commit -m "fix(auth): handle expired JWT tokens gracefully"
# Descriptive but concise
git commit -m "perf: lazy load dashboard widgets"
# When more context needed, use body
git commit -m "fix: prevent duplicate form submissions
Users reported seeing duplicate orders when clicking
the submit button multiple times. Add debounce and
disable button after first click.
Fixes #567"Why
Good subject lines are crucial for maintainability:
1. Scannable History: git log --oneline becomes a useful changelog 2. Quick Understanding: Team members can understand changes without reading code 3. Efficient Debugging: When using git bisect, meaningful messages help identify problematic commits 4. Documentation: Commit history serves as documentation of the project's evolution 5. Better Tooling: GitHub, GitLab, and other tools display subject lines in many places
Guidelines for great subject lines:
- Keep under 50 characters when possible (hard limit: 72)
- Capitalize the first letter (after type prefix if using conventional commits)
- No period at the end
- Focus on "what" and "why", not "how"
- Be specific enough to distinguish from similar commits
Issue and PR References in Commits
Link commits to relevant issues, pull requests, and external resources for traceability.
Bad Example
# No reference to the issue being fixed
git commit -m "fix: resolve login timeout issue"
# Which issue? Where was it reported?
# Vague references
git commit -m "fix: address the bug from last week's meeting"
# Reference in wrong format (won't auto-link)
git commit -m "fix: login issue (issue 123)"
# Reference without context
git commit -m "fix: #456"
# What does #456 refer to?Good Example
# GitHub/GitLab auto-linking keywords
git commit -m "fix: resolve race condition in auth flow
Fixes #234"
git commit -m "feat: add bulk export functionality
Implements #567
Closes #568"
# Multiple references with context
git commit -m "fix: handle edge case in payment processing
The payment gateway returns different error codes for
the same failure type depending on the merchant account.
Fixes #891
Related to #445
See also: payment-gateway/docs/error-codes"
# External references
git commit -m "security: patch XSS vulnerability in comments
Apply sanitization to user-generated content before rendering.
Fixes #234
CVE-YYYY-NNNNN
See: https://owasp.org/xss-prevention"
# Co-author attribution
git commit -m "feat: implement new search algorithm
Based on discussion in #123 and design doc.
Co-authored-by: Jane Doe <jane@example.com>
Co-authored-by: John Smith <john@example.com>"Why
References create valuable connections:
1. Traceability: Link code changes to requirements, bugs, and discussions 2. Auto-Linking: GitHub/GitLab automatically link #123 to issue/PR 123 3. Auto-Closing: Keywords like "Fixes #123" automatically close issues when merged 4. Context Preservation: Future developers can find the full discussion 5. Audit Trail: Important for compliance and debugging
Common keywords that auto-close issues:
Fixes #123Closes #123Resolves #123
Keywords for reference without closing:
Related to #123See #123Part of #123Refs #123
Best practices:
- Always reference the issue being fixed
- Include CVE numbers for security fixes
- Link to relevant documentation or ADRs
- Credit co-authors for pair programming
- Reference external bug trackers if applicable
Clean Up Commits Before Merging
Use interactive rebase to clean up commit history before creating or merging a pull request.
Bad Example
# PR with messy commit history
git log --oneline feature/auth
# abc123 fix typo
# def456 oops forgot file
# ghi789 WIP
# jkl012 more WIP
# mno345 actually working now
# pqr678 fix tests
# stu901 add auth feature
# vwx234 WIP don't push
# yza567 merge main
# bcd890 start auth feature
# Commits that don't compile
# Each commit should leave the project in working state
# Commit messages that don't explain changes
# "stuff" "changes" "update" "fix" "wip"Good Example
# Interactive rebase to clean up before PR
git checkout feature/auth
git rebase -i main
# In the editor:
pick bcd890 feat: add authentication service
squash stu901 add auth feature
fixup yza567 WIP don't push
pick vwx234 feat: implement login form
fixup mno345 actually working now
fixup jkl012 more WIP
fixup ghi789 WIP
pick pqr678 test: add authentication tests
fixup def456 oops forgot file
fixup abc123 fix typo
# drop merge commits
# Result: clean, logical commits
git log --oneline feature/auth
# 111aaa feat: add authentication service
# 222bbb feat: implement login form
# 333ccc test: add authentication tests
# Reword commits during rebase
git rebase -i HEAD~3
# Change 'pick' to 'reword' for commits needing better messages
# Reorder commits for logical progression
# In interactive rebase, just reorder the lines
# After cleanup, force push to your branch
git push --force-with-lease origin feature/authWhy
Clean commit history provides long-term value:
1. Meaningful History: Each commit tells a clear story 2. Easier Review: Reviewers see logical progression of changes 3. Better Bisect: Each commit is a valid state for debugging 4. Useful Blame: git blame shows meaningful commits 5. Simpler Reverts: Clean commits are easier to revert selectively
Interactive rebase commands:
| Command | Effect |
|---|---|
pick | Keep commit as-is |
reword | Keep commit, edit message |
edit | Pause to amend commit |
squash | Combine with previous, keep message |
fixup | Combine with previous, discard message |
drop | Remove commit entirely |
Cleanup workflow:
# 1. Start interactive rebase
git rebase -i main
# 2. Squash WIP commits
# Change 'pick' to 'squash' or 'fixup'
# 3. Reorder if needed
# Move lines in editor
# 4. Reword messages
# Change 'pick' to 'reword'
# 5. Save and resolve any conflicts
# 6. Force push to your branch
git push --force-with-leaseGuidelines:
- Each commit should compile and pass tests
- Commit messages should be clear and conventional
- Related changes should be in the same commit
- Unrelated changes should be separate commits
- WIP commits should be squashed away
Avoid Force Push on Shared Branches
Never force push to shared branches like main or develop, as it rewrites history and disrupts other developers.
Bad Example
# Force pushing to main
git checkout main
git reset --hard HEAD~3
git push --force origin main
# Everyone's local main is now broken
# Force pushing to shared feature branch
git checkout feature/shared-work
git rebase main
git push --force origin feature/shared-work
# Colleagues' work may be lost
# Amending pushed commits on shared branch
git commit --amend -m "better message"
git push --force origin feature/team-project
# Team members can't pull cleanly
# Force pushing after failed rebase
git rebase main
# Conflicts everywhere, give up
git reset --hard ORIG_HEAD
git push --force
# But you already pushed the partial rebaseGood Example
# Use --force-with-lease for personal branches
git checkout feature/my-branch
git rebase main
git push --force-with-lease origin feature/my-branch
# Fails safely if someone else pushed
# Revert instead of force push on shared branches
git checkout main
git revert abc123
git push origin main
# History preserved, change undone
# Fix mistakes with new commits, not rewrites
git commit -m "fix: correct the previous commit's error"
git push origin shared-branch
# No history rewrite
# If you must coordinate a rebase on shared branch
# 1. Notify all collaborators
# 2. Everyone commits and pushes their work
# 3. Everyone stops pushing
# 4. One person rebases and force pushes
# 5. Everyone resets to remote
git fetch origin
git reset --hard origin/feature/shared-workWhy
Force pushing to shared branches causes serious problems:
1. Lost Work: Other developers' commits can be overwritten 2. Broken History: Local branches diverge from remote 3. Sync Issues: Developers see conflicts when pulling 4. Confusion: Commit SHAs change, breaking references 5. CI/CD Issues: Builds may reference non-existent commits
The --force-with-lease safety net:
# Regular force push - dangerous
git push --force
# Overwrites remote regardless of state
# Force with lease - safer
git push --force-with-lease
# Fails if remote has commits you haven't fetched
# Protects against overwriting others' workScenarios and solutions:
| Situation | Bad | Good |
|---|---|---|
| Undo commit on main | reset --hard && push --force | git revert |
| Clean up commits | Rebase shared branch | Squash merge at PR |
| Fix typo in message | --amend && --force | New commit or squash at merge |
| Sync feature branch | Merge (creates noise) | Rebase personal branches |
Branch protection prevents force pushes:
# GitHub branch protection
main:
allow_force_pushes: false
allow_deletions: falseEven with --force-with-lease, communicate with your team before force pushing to any branch others might be using.
Rebase vs. Merge
Understand when to use rebase versus merge to maintain a clean and useful git history.
Bad Example
# Merging main into feature branch repeatedly
git checkout feature/login
git merge main # Creates merge commit
# more work
git merge main # Another merge commit
# more work
git merge main # Yet another merge commit
# History now has 10 merge commits
# Rebasing shared branches
git checkout main
git rebase feature/experimental
# Rewrites main history, breaks everyone's local copies
# Rebasing after pushing without force
git rebase main
git push # Fails: "Updates were rejected"
git push --force # Overwrites colleagues' work
# Inconsistent approach causing confusion
# Sometimes rebase, sometimes merge, no clear patternGood Example
# Rebase feature branch onto main (preferred for updating)
git checkout feature/login
git fetch origin main
git rebase origin/main
# Clean, linear history within the feature branch
# Interactive rebase to clean up before PR
git rebase -i HEAD~5
# Squash WIP commits, reorder, improve messages
# Merge for bringing feature into main (via PR)
git checkout main
gh pr merge --squash # Squash merge is recommended
# Rebase workflow for feature branches
git checkout feature/dashboard
git fetch origin
git rebase origin/main
# Resolve any conflicts
git push --force-with-lease # Safe force push for YOUR branch
# Use merge for shared long-lived branches (if using GitFlow)
git checkout develop
git merge --no-ff release/v1.0
# Preserves release branch historyWhy
Understanding when to use each strategy is crucial:
Use Rebase when:
- Updating your feature branch with latest main
- Cleaning up commits before creating PR
- Working on your own branch that hasn't been shared
- You want a linear history
Use Merge when:
- Bringing completed features into main (via PR)
- Working with shared branches others depend on
- You need to preserve the exact history
- Combining long-lived branches (GitFlow)
Comparison:
| Aspect | Rebase | Merge |
|---|---|---|
| History | Linear, clean | Branching, complete |
| Commit SHAs | Changed | Preserved |
| Safe for shared branches | No | Yes |
| Conflict resolution | Per-commit | Once |
| Traceability | Simplified | Complete |
Golden rules: 1. Never rebase shared branches (main, develop) 2. Always rebase your own feature branches to update them 3. Use `--force-with-lease` instead of --force when pushing rebased branches 4. Squash merge to main for clean history
# Safe force push (fails if remote has changes you haven't seen)
git push --force-with-lease
# Dangerous force push (avoid)
git push --forceTags and Releases
Use annotated tags to mark releases and maintain a clear versioning history.
Bad Example
# Lightweight tags with no context
git tag v1.0.0
git push --tags
# No message, no author info, no date
# Inconsistent tag naming
git tag release-1.0
git tag v1.1.0
git tag 1.2
git tag version_1.3.0
# Impossible to sort or automate
# Tagging random commits
git tag v2.0.0 # Tags current HEAD which might be WIP
# Forgetting to push tags
git tag -a v1.0.0 -m "Release 1.0.0"
# But never pushed to remote
# Moving tags after push
git tag -f v1.0.0 HEAD
git push --force --tags
# Breaks anyone who already pulledGood Example
# Create annotated tag with message
git tag -a v1.0.0 -m "Release version 1.0.0
Features:
- User authentication
- Dashboard widgets
- API rate limiting
Fixes:
- Resolved login timeout issue
- Fixed memory leak in cache"
# Push specific tag
git push origin v1.0.0
# Push all tags
git push --tags
# Tag a specific commit (for releases)
git tag -a v1.0.0 abc123 -m "Release 1.0.0"
# List tags with messages
git tag -n
# Create GitHub release from tag
gh release create v1.0.0 \
--title "Version 1.0.0" \
--notes "## What's New
- User authentication system
- Dashboard widgets
- API rate limiting
## Bug Fixes
- Login timeout issue resolved
- Memory leak in cache fixed
## Breaking Changes
- API v1 deprecated, use v2"
# Create release with assets
gh release create v1.0.0 \
--title "Version 1.0.0" \
--notes-file CHANGELOG.md \
dist/app-1.0.0.zip \
dist/app-1.0.0.tar.gzWhy
Proper tagging enables release management:
1. Version Identification: Clear mapping between code and releases 2. Deployment Reference: CI/CD can deploy specific tags 3. Changelog Generation: Tools extract release notes from tags 4. Rollback Target: Easy to identify and revert to previous versions 5. Communication: Users and team know what's deployed
Semantic Versioning (SemVer):
MAJOR.MINOR.PATCH
v1.0.0 - Initial release
v1.1.0 - New feature (backward compatible)
v1.1.1 - Bug fix (backward compatible)
v2.0.0 - Breaking changePre-release versions:
v1.0.0-alpha.1 # Alpha release
v1.0.0-beta.1 # Beta release
v1.0.0-rc.1 # Release candidateTag commands reference:
# Create annotated tag
git tag -a v1.0.0 -m "Message"
# Create tag on specific commit
git tag -a v1.0.0 <commit-sha> -m "Message"
# List all tags
git tag -l
# List tags matching pattern
git tag -l "v1.*"
# Show tag details
git show v1.0.0
# Delete local tag
git tag -d v1.0.0
# Delete remote tag
git push origin --delete v1.0.0
# Checkout specific tag
git checkout v1.0.0Best practices:
- Always use annotated tags for releases (
-aflag) - Follow semantic versioning
- Include release notes in tag message
- Create GitHub/GitLab releases for visibility
- Never move or delete published tags
Git Worktree
Use git worktree to work on multiple branches simultaneously without stashing or switching branches.
Bad Example
# Interrupt current work to switch branches
git stash
git checkout main
git pull
git checkout -b hotfix/critical-bug
# fix the bug...
git commit -m "fix: resolve critical auth bug"
git checkout feature/dashboard
git stash pop
# Hope the stash applies cleanly
# Clone the repo again just to work on another branch
git clone https://github.com/org/repo.git repo-hotfix
cd repo-hotfix
git checkout -b hotfix/payment-bug
# Now maintaining two full clones with duplicated node_modulesGood Example
# Add a worktree for a hotfix without leaving your current branch
git worktree add ../repo-hotfix hotfix/critical-bug
# Work in the new worktree
cd ../repo-hotfix
# fix the bug, commit, push...
git commit -m "fix(auth): resolve token validation bypass"
git push origin hotfix/critical-bug
# Return to your original work — nothing was interrupted
cd ../repo
# Your feature branch is exactly where you left it
# List all worktrees
git worktree list
# /Users/dev/repo abc1234 [feature/dashboard]
# /Users/dev/repo-hotfix def5678 [hotfix/critical-bug]
# Remove worktree when done
git worktree remove ../repo-hotfix# Common worktree workflows
# Review a PR without leaving your branch
git worktree add ../repo-review origin/pr-branch-name
cd ../repo-review
# Run the app, review changes...
git worktree remove ../repo-review
# Work on two features simultaneously
git worktree add ../repo-feature-b feature/analytics
# Main repo: working on feature/dashboard
# ../repo-feature-b: working on feature/analytics
# Shared .git directory — no duplicate history
# Create new branch directly in a new worktree
git worktree add -b feature/new-feature ../repo-new-feature main# Worktree with separate dependencies (Node.js)
git worktree add ../repo-v2 feature/v2-upgrade
cd ../repo-v2
npm install # Install deps for this branch separately
# Each worktree has its own node_modulesWhy
git worktree solves the context-switching problem without the downsides of stashing or cloning:
1. No stash risk: Stash pops can create conflicts. Worktrees keep each branch's state completely separate and intact.
2. Shared `.git`: Unlike a second clone, worktrees share the same git history — no duplicate .git directory, no need to git fetch in multiple places.
3. Parallel work: Respond to urgent hotfixes, review PRs, or work on two features simultaneously — all without interrupting each other.
4. Clean state: Each worktree has its own working directory and index. Changes in one worktree are completely invisible to others.
| Method | Shared .git | Interrupts work | Risk |
|---|---|---|---|
git stash | ✅ | ✅ | Stash conflicts |
| Second clone | ❌ | ❌ | Disk space, stale history |
git worktree | ✅ | ❌ | None |
Limitation: The same branch cannot be checked out in two worktrees simultaneously. Each worktree must be on a different branch.
CI Checks Must Pass
All continuous integration checks must pass before a pull request can be merged.
Bad Example
# Ignoring failing tests
gh pr merge --admin # Bypass required checks
# Disabling tests that fail
git commit -m "test: skip flaky test to unblock PR"
# test.skip('this test fails sometimes')
# Merging with lint warnings
# "It's just style, I'll fix it later"
gh pr merge # With 50 lint warnings
# Ignoring security scan results
# "That vulnerability doesn't affect us"
gh pr merge # With critical security finding
# Committing to make CI pass without fixing issue
git commit -m "chore: disable eslint rule"
# /* eslint-disable security/detect-sql-injection */Good Example
# Wait for all checks to pass
gh pr checks --watch
# All checks passed
# Fix failing tests before requesting review
npm test
# Fix: update test assertions for new behavior
git commit -m "test: update assertions for new API response"
# Address lint issues
npm run lint -- --fix
git commit -m "style: fix lint issues"
# Address security findings
npm audit fix
git commit -m "chore: fix security vulnerabilities"
# If test is legitimately flaky, fix the flakiness
git commit -m "test: fix race condition in async test"
# Required checks configuration (GitHub)
# Settings > Branches > Branch protection rules
# - Require status checks to pass
# - Require branches to be up to dateWhy
Enforcing CI checks maintains code quality:
1. Quality Gate: Automated verification before human review 2. Consistency: Same checks run for every change 3. Early Detection: Catch issues before they reach production 4. Time Saving: Automated checks are faster than manual verification 5. Confidence: Green CI means basic quality bar is met
Essential CI checks:
# Example GitHub Actions workflow
name: CI
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run lint
type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run type-check
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm audit --audit-level=high
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run buildCommon checks to require:
- Unit tests
- Integration tests
- Linting / code style
- Type checking (TypeScript, mypy)
- Security scanning (npm audit, Snyk)
- Build verification
- Code coverage thresholds
- Performance benchmarks
Never bypass CI for "just this once" - that's how bugs reach production.
PR Description Template
Use a consistent, informative template for pull request descriptions to aid reviewers and document changes.
Bad Example
# No description
gh pr create --title "fix stuff"
# Body: (empty)
# Minimal unhelpful description
gh pr create --title "Update code" --body "Made some changes"
# Just restating the title
gh pr create --title "Add login feature" --body "This PR adds a login feature"
# Technical without context
gh pr create --title "Refactor auth" --body "
Changed AuthService.ts
Modified UserController.js
Updated tests
"Good Example
gh pr create --title "feat: add two-factor authentication" --body "$(cat <<'EOF'
## Summary
Implements TOTP-based two-factor authentication for user accounts, addressing security requirements from the Q4 audit.
## Changes
- Add TOTP secret generation and verification in AuthService
- Create 2FA setup flow with QR code display
- Add backup codes generation and storage
- Update login flow to prompt for 2FA when enabled
## Testing
- [ ] Manual testing of setup flow completed
- [ ] Verified QR codes work with Google Authenticator
- [ ] Tested backup code usage
- [ ] Unit tests added for TOTP verification
## Screenshots

## Related
- Closes #234
- Depends on #230 (merged)
- Security audit: AUDIT-2024-15
## Notes for Reviewers
- The TOTP library choice is discussed in #220
- Backup codes are hashed, not encrypted (intentional)
- Please verify the rate limiting logic in AuthService.ts:45-60
EOF
)"
# Bug fix template
gh pr create --title "fix: resolve payment timeout on slow connections" --body "$(cat <<'EOF'
## Problem
Users on slow connections (>500ms latency) experience payment failures due to API timeout.
## Root Cause
The payment gateway timeout was set to 5 seconds, but slow connections need 10-15 seconds for round-trip.
## Solution
- Increase timeout to 30 seconds for payment endpoints
- Add retry logic with exponential backoff
- Show loading indicator during processing
## Testing
- Tested with network throttling (3G, slow 3G)
- Verified no regression on fast connections
- Added integration test for timeout scenarios
## Fixes
Closes #567
EOF
)"Why
Good PR descriptions improve the entire development process:
1. Context for Reviewers: Understand WHY before reviewing HOW 2. Documentation: PR descriptions become part of project history 3. Self-Review: Writing forces you to think through your changes 4. Onboarding: New team members learn from well-documented PRs 5. Debugging Aid: Future investigators find context for changes
Recommended template sections:
## Summary
Brief description of what this PR does and why.
## Changes
- Bullet list of significant changes
- Focus on what's notable for reviewers
## Testing
How was this tested? Include manual and automated testing.
## Screenshots/Videos
For UI changes, include before/after screenshots.
## Related
- Links to issues, other PRs, documentation
- Dependencies or blockers
## Notes for Reviewers
- Areas that need extra attention
- Questions or uncertainties
- Non-obvious decisionsStore template in .github/PULL_REQUEST_TEMPLATE.md to auto-populate.
Draft Pull Requests
Use draft PRs to share work-in-progress, get early feedback, and run CI before requesting formal review.
Bad Example
# Requesting review on incomplete work
gh pr create --title "WIP: new feature" --reviewer "senior-dev"
# Wasting reviewer's time on unfinished code
# Waiting until everything is perfect
# 3 weeks of solo development
# Then: "Here's my 5000-line PR, please review"
# No visibility into ongoing work
# Team has no idea what you're working on
# Surprise: massive PR appears
# Using comments to indicate draft status
gh pr create --title "[WIP] [DO NOT MERGE] [DRAFT] feature"
# Inconsistent, easy to accidentally mergeGood Example
# Create draft PR early
gh pr create --draft --title "feat: implement checkout flow"
# CI runs on draft PRs
gh pr checks --watch
# Fix issues before requesting review
# Share work in progress for early feedback
gh pr create --draft --title "RFC: new architecture approach" --body "
## Status: Draft - Seeking Feedback
This PR explores a new approach to state management.
Not ready for detailed review, but looking for:
- [ ] Is this direction worth pursuing?
- [ ] Any concerns with the overall approach?
- [ ] Similar patterns we should consider?
Will mark ready for review once implementation is complete.
"
# Convert to ready when complete
gh pr ready 123
# Or mark ready with reviewers
gh pr ready 123 --reviewer "tech-lead,domain-expert"
# Use draft for running CI on experiments
gh pr create --draft --title "experiment: try new bundler"
# Test in CI without requesting review
# Close without merging if experiment failsWhy
Draft PRs improve collaboration and code quality:
1. Early CI Feedback: Catch build/test issues before review 2. Visibility: Team sees work in progress 3. Early Design Feedback: Get directional input before investing heavily 4. No Premature Reviews: Clearly signals "not ready for detailed review" 5. Reduced Rework: Course-correct early based on feedback
When to use draft PRs:
| Situation | Use Draft? |
|---|---|
| Work in progress | Yes |
| Seeking architectural feedback | Yes |
| Running CI on experiment | Yes |
| Waiting for dependency PR | Yes |
| Ready for review | No (convert to ready) |
| Hotfix that needs immediate review | No |
Draft PR workflow: 1. Create draft PR when starting work 2. Push commits as you develop 3. CI runs automatically 4. Request informal feedback if needed 5. Mark ready when implementation complete 6. Add reviewers for formal review 7. Address feedback and merge
Draft PR etiquette:
- Don't request formal reviewers on drafts
- Use PR description to explain what feedback you want
- Update status in description as work progresses
- Convert to ready promptly when done
- Close drafts that won't be completed
Protect against accidental merges:
# Branch protection
# [x] Require pull request reviews
# Draft PRs cannot be merged until marked readyRequesting and Assigning Reviewers
Thoughtfully select reviewers who can provide valuable feedback on your changes.
Bad Example
# No reviewers assigned
gh pr create --title "feat: new feature"
# PR sits for days with no review
# Adding everyone to every PR
gh pr create --reviewer "alice,bob,charlie,david,eve,frank"
# Diffusion of responsibility - no one reviews thoroughly
# Only selecting friends for easy approvals
gh pr create --reviewer "my-buddy"
# Missing expertise, potential rubber-stamping
# Random reviewer selection
gh pr create --reviewer "whoever-is-online"
# Missing domain knowledge
# Assigning junior developers to review critical security changes
gh pr create --title "fix: security vulnerability" --reviewer "intern"Good Example
# Select reviewers based on expertise
gh pr create --title "feat: add payment processing" \
--reviewer "payment-team-lead,security-expert"
# Use CODEOWNERS for automatic assignment
# .github/CODEOWNERS
# /src/payments/ @payment-team
# /src/auth/ @security-team
# *.sql @database-team
# Balanced review team
gh pr create --title "feat: new dashboard" \
--reviewer "frontend-expert,product-owner"
# Request specific feedback
gh pr create --title "refactor: database layer" --body "
## Reviewers
- @db-expert: Please review query optimization
- @api-lead: Please verify API contract unchanged
"
# Escalate important changes
gh pr create --title "fix: critical security patch" \
--reviewer "security-team,tech-lead" \
--label "security,urgent"Why
Thoughtful reviewer selection improves code quality:
1. Domain Expertise: Reviewers catch issues specific to their area 2. Knowledge Sharing: Reviews spread understanding across the team 3. Accountability: Clear ownership of the review process 4. Balanced Perspectives: Different viewpoints catch different issues 5. Mentorship: Junior developers learn by reviewing senior code
Reviewer selection criteria:
- Code Ownership: Who maintains the affected code?
- Domain Knowledge: Who understands this feature area?
- Recent Context: Who worked on related changes recently?
- Growth Opportunity: Can this help someone learn?
- Availability: Who has bandwidth for timely review?
CODEOWNERS file setup:
# .github/CODEOWNERS
# Default owners
* @tech-lead
# Frontend
/src/components/ @frontend-team
/src/pages/ @frontend-team
# Backend
/src/api/ @backend-team
/src/services/ @backend-team
# Security-sensitive
/src/auth/ @security-team
/src/payments/ @security-team @payment-lead
# Infrastructure
/terraform/ @devops-team
/.github/ @devops-teamBest practices:
- Require at least 1-2 reviewers
- Include at least one domain expert
- Rotate reviewers to spread knowledge
- Don't overload individuals with reviews
- Set expectations for review turnaround time
Small, Focused Pull Requests
Keep pull requests small and focused on a single concern to enable thorough reviews and reduce risk.
Bad Example
# Massive PR with multiple unrelated changes
gh pr create --title "Updates for Q4" --body "
- Add user authentication
- Refactor database layer
- Update all dependencies
- Fix 15 bugs
- Add new dashboard
- Change color scheme
"
# 5000+ lines changed, 100+ files
# PR that mixes refactoring with features
gh pr create --title "Add search and cleanup code"
# Reviewers can't distinguish intentional changes from refactoring
# Kitchen sink PR
git diff main --stat
# 200 files changed, 15000 insertions, 8000 deletions
# "I'll just add one more thing..."Good Example
# Single feature, single PR
gh pr create --title "feat: add user search functionality" --body "
## Summary
- Add search input to user list
- Implement debounced API calls
- Display results with highlighting
## Changes
- 4 files changed
- ~200 lines
"
# Separate PRs for separate concerns
gh pr create --title "refactor: extract validation utilities"
# Merge refactoring first
gh pr create --title "feat: add email validation to signup"
# Then add feature using refactored code
# Breaking up large features
gh pr create --title "feat: add checkout API endpoints"
gh pr create --title "feat: add checkout UI components"
gh pr create --title "feat: integrate checkout UI with API"
# Each PR is reviewable in 15-30 minutes
git diff main --stat
# 8 files changed, 150 insertions, 20 deletionsWhy
Small PRs dramatically improve code quality and velocity:
1. Faster Reviews: Reviewers can focus deeply on fewer changes 2. Better Feedback: Detailed review comments are more likely 3. Reduced Risk: Smaller changes = smaller potential for bugs 4. Easier Debugging: If issues arise, the cause is easier to identify 5. Quicker Merging: Less waiting, faster feedback loops 6. Fewer Conflicts: Less time open = less chance of conflicts
Size guidelines:
- Ideal: 50-200 lines changed
- Acceptable: 200-400 lines
- Large: 400-800 lines (needs justification)
- Too Large: 800+ lines (should be split)
Strategies for smaller PRs:
- Separate refactoring from feature work
- Use feature flags to merge incomplete features
- Break features into vertical slices
- Submit preparatory PRs (add tests first, then implementation)
- Extract shared utilities into separate PRs
The "1-hour rule": If a PR can't be reviewed in under an hour, it's probably too big.
Squash Merge Strategy
Use squash merging to maintain a clean, linear history while preserving development context in PRs.
Bad Example
# Regular merge creating noise in history
git log --oneline main
# a1b2c3d Merge pull request #123
# d4e5f6g WIP
# g7h8i9j fix typo
# j1k2l3m add feature
# m4n5o6p WIP again
# p7q8r9s forgot file
# s1t2u3v Merge branch 'main' into feature
# ...50 more commits for one feature
# Merge commits from syncing with main
git log --oneline
# Merge branch 'main' into feature/x
# Merge branch 'main' into feature/x
# Merge branch 'main' into feature/x
# Inconsistent merge strategies
# Some PRs squashed, some regular merged, some rebased
# History is unpredictableGood Example
# Squash merge via GitHub CLI
gh pr merge 123 --squash
# Squash merge via GitHub UI
# Select "Squash and merge" from dropdown
# Result: clean single commit in main
git log --oneline main
# a1b2c3d feat: add user authentication (#123)
# b2c3d4e fix: resolve payment timeout (#122)
# c3d4e5f feat: add dashboard widgets (#121)
# Squash merge with custom message
gh pr merge 123 --squash --subject "feat: add user search" --body "
Implements full-text search for users with:
- Debounced input handling
- Result highlighting
- Pagination support
Closes #100"
# Configure repo to default to squash
# Settings > General > Pull Requests
# [x] Allow squash merging
# [ ] Allow merge commits
# [ ] Allow rebase merging
# Or allow all but make squash default
# Default to pull request title for squash merge commitsWhy
Squash merging provides the best of both worlds:
1. Clean History: Main branch has one commit per feature/fix 2. Easy Revert: Revert entire feature with single git revert 3. Clear Changelog: Each commit represents a complete change 4. Development Freedom: WIP commits during development are fine 5. Preserved Context: Full commit history remains in the PR
Comparison of merge strategies:
| Strategy | Main History | PR History | Bisect | Revert |
|---|---|---|---|---|
| Merge | Noisy | Preserved | Complex | Complex |
| Squash | Clean | In PR only | Simple | Simple |
| Rebase | Clean | Lost | Simple | Per-commit |
When NOT to squash:
- Commits have different authors who need credit
- Individual commits are meaningful and should be preserved
- Repository uses conventional commits for changelog generation per-commit
Squash merge commit message format:
feat: add user authentication (#123)
* Add login form component
* Implement JWT token handling
* Add session persistence
* Add logout functionality
Co-authored-by: Jane Doe <jane@example.com>Configure GitHub to use PR title as squash commit message for consistent conventional commits.