Back to blog

Git Workflow & Best Practices for Development Teams

6 min readBy Mustafa Akkaya
#Git#DevOps#Workflow#Best Practices

Effective Git workflows are the backbone of successful team collaboration. Poor Git practices lead to merge conflicts, lost work, and wasted time. Let's explore proven strategies that professional teams use every day.

Why Git Workflow Matters

A solid Git strategy provides:

- Clear History - Understand what changed and why

- Safe Collaboration - Multiple developers working simultaneously

- Easy Rollbacks - Revert problematic changes quickly

- Code Quality - Review before merging to main

- CI/CD Integration - Automated testing and deployment

Popular Branching Strategies

Git Flow (Traditional)

Best for projects with scheduled releases:

script.sh

# Main branches

main # Production-ready code

develop # Integration branch

# Supporting branches

feature/* # New features

release/* # Release preparation

hotfix/* # Production fixes

Workflow:

script.sh

# Start class="keyword">new feature

git checkout develop

git checkout -b feature/user-authentication

# Work on feature

git add .

git commit -m class="keyword">class="string">"feat: implement JWT authentication"

# Merge back to develop

git checkout develop

git merge feature/user-authentication

GitHub Flow (Simplified)

Perfect for continuous deployment:

script.sh

main # Always deployable

feature/* # All changes are features

Benefits:

- Simpler than Git Flow

- Faster deployments

- Better for web applications

Trunk-Based Development

Used by high-performing teams:

app.ts

class="keyword">class="comment">// Short-lived branches(1-2 days max)

main; class="keyword">class="comment">// Always stable

feature / quick - fix; class="keyword">class="comment">// Small, focused changes

class="keyword">class="comment">// Feature flags class="keyword">class="keyword">for incomplete features

class="keyword">class="keyword">const features = {

newDashboard: process.env.FEATURE_NEW_DASHBOARD === class="keyword">class="string">"true",

aiAssistant: process.env.FEATURE_AI_ASSISTANT === class="keyword">class="string">"true",

};

Commit Message Best Practices

Conventional Commits

Follow a standard format:

script.sh

<class="keyword">type>():

Types:

- feat: New feature

- fix: Bug fix

- docs: Documentation changes

- style: Code formatting

- refactor: Code restructuring

- test: Adding tests

- chore: Maintenance tasks

Examples:

script.sh

feat(auth): add OAuth2 login with Google

Implement Google OAuth2 authentication flow using NextAuth.js.

Users can now sign in with their Google accounts.

Closes #123

---

fix(api): resolve race condition in user creation

The user creation endpoint was vulnerable to race conditions

when multiple requests arrived simultaneously.

Fixes #456

---

docs(readme): update installation instructions

Add missing environment variables and clarify setup steps.

Essential Git Commands

Daily Workflow

script.sh

# Update your local main

git checkout main

git pull origin main

# Create feature branch

git checkout -b feature/class="keyword">new-dashboard

# Stage and commit changes

git add src/components/Dashboard.tsx

git commit -m class="keyword">class="string">"feat(dashboard): add analytics widget"

# Push to remote

git push origin feature/class="keyword">new-dashboard

# Update branch with latest main

git checkout main

git pull origin main

git checkout feature/class="keyword">new-dashboard

git rebase main

Handling Conflicts

script.sh

# During rebase or merge

git status # See conflicted files

# Manually resolve conflicts in editor

git add resolved-file.ts

git rebase --class="keyword">continue

# Abort class="keyword">class="keyword">if needed

git rebase --abort

Useful Commands

script.sh

# View commit history

git log --oneline --graph --all

# Undo last commit(keep changes)

git reset --soft HEAD~1

# Discard local changes

git checkout -- file.ts

# Stash work in progress

git stash save class="keyword">class="string">"work in progress"

git stash pop

# Interactive rebase(clean up commits)

git rebase -i HEAD~3

Pull Request Best Practices

Creating Great PRs

README.md

class="keyword">class=class="keyword">class="string">"mt-8 mb-4 text-3xl font-bold text-zinc-900 dark:text-zinc-50">Description

Add user profile page with avatar upload functionality.

class="keyword">class=class="keyword">class="string">"mt-8 mb-4 text-3xl font-bold text-zinc-900 dark:text-zinc-50">Changes

- Created ProfilePage component

- Implemented image upload with S3

- Added profile edit form with validation

- Updated user API endpoints

class="keyword">class=class="keyword">class="string">"mt-8 mb-4 text-3xl font-bold text-zinc-900 dark:text-zinc-50">Testing

- ✅ Unit tests class="keyword">class="keyword">for ProfilePage

- ✅ API integration tests

- ✅ Manual testing on staging

class="keyword">class=class="keyword">class="string">"mt-8 mb-4 text-3xl font-bold text-zinc-900 dark:text-zinc-50">Screenshots

[Include relevant screenshots]

class="keyword">class=class="keyword">class="string">"mt-8 mb-4 text-3xl font-bold text-zinc-900 dark:text-zinc-50">Checklist

- [x] Code follows style guidelines

- [x] Tests added/updated

- [x] Documentation updated

- [x] No console.log or debugging code

Review Process

script.sh

# Checkout PR locally class="keyword">class="keyword">for testing

gh pr checkout 123

# Run tests

npm test

# Leave feedback

gh pr review 123 --comment -b class="keyword">class="string">"LGTM! Great work on error handling."

.gitignore Best Practices

script.sh

# Dependencies

node_modules/

.pnp/

# Environment variables

.env

.env.local

.env*.local

# Build outputs

dist/

build/

.next/

out/

# IDE

.vscode/

.idea/

*.swp

# OS

.DS_Store

Thumbs.db

# Logs

*.log

logs/

# Testing

coverage/

.nyc_output/

Protecting Your Main Branch

GitHub repository settings:

config.yaml

Branch Protection Rules: ✅ Require pull request reviews(2 approvals)

✅ Require status checks to pass

✅ Require branches to be up to date

✅ Require conversation resolution

✅ Include administrators

✅ Restrict force pushes

Git Hooks for Automation

Pre-commit Hook

script.sh

#!/bin/bash

# .git/hooks/pre-commit

# Run linter

npm run lint || exit 1

# Run class="keyword">type check

npm run class="keyword">type-check || exit 1

# Run tests

npm test || exit 1

echo class="keyword">class="string">"✅ All checks passed!"

Using Husky

data.json

{

class="keyword">class="string">"husky": {

class="keyword">class="string">"hooks": {

class="keyword">class="string">"pre-commit": class="keyword">class="string">"lint-staged",

class="keyword">class="string">"commit-msg": class="keyword">class="string">"commitlint -E HUSKY_GIT_PARAMS"

}

},

class="keyword">class="string">"lint-staged": {

class="keyword">class="string">"*.{ts,tsx}": [class="keyword">class="string">"eslint --fix", class="keyword">class="string">"prettier --write"],

class="keyword">class="string">"*.{json,md}": [class="keyword">class="string">"prettier --write"]

}

}

Common Mistakes to Avoid

Don't:

- Commit directly to main

- Write vague commit messages ("fixed stuff")

- Create massive PRs (500+ lines)

- Force push to shared branches

- Commit sensitive data (API keys, passwords)

- Leave console.log in production code

Do:

- Use feature branches

- Write descriptive commit messages

- Keep PRs focused and small

- Communicate with your team

- Use .gitignore properly

- Review your own code before requesting review

Useful Git Aliases

Add to ~/.gitconfig:

script.sh

[alias]

st = status

co = checkout

br = branch

ci = commit

unstage = reset HEAD --

last = log -1 HEAD

visual = log --oneline --graph --all --decorate

undo = reset --soft HEAD~1

Conclusion

A solid Git workflow is essential for team productivity. Start with a simple strategy like GitHub Flow, enforce it with branch protection rules, and iterate based on your team's needs. Remember: consistency is more important than complexity.

The best workflow is the one your team actually follows. Start simple, document your process, and improve continuously based on real experience.

Key Takeaways:

- Choose a branching strategy that fits your team

- Write meaningful commit messages

- Keep PRs small and focused

- Protect your main branch

- Automate quality checks with Git hooks

- Communicate through code reviews

Happy collaborating! 🚀