Team Development Workflow - Complete GitHub Flow Guide
Working alone lets you move freely, but as soon as you join a team, rules become necessary. “Someone’s change broke my code,” or “I can’t tell which branch is up to date” — these problems are exactly what a team development workflow prevents.
This article explains the standard practices for working as a team on GitHub.
What Is GitHub Flow?
GitHub Flow is a simple branching strategy centered around the main branch. It avoids complex branch management and enables frequent releases.
The GitHub Flow Cycle
1. Create a new branch from main
2. Commit changes on the branch
3. Open a Pull Request
4. Receive a code review
5. Merge into main
6. DeployRepeat this cycle. There are no complex rules.
GitHub Flow vs. Git Flow
Another well-known branching strategy is Git Flow. Here is a comparison of the two:
| Comparison | GitHub Flow | Git Flow |
|---|---|---|
| Number of branches | main + feature branches | main, develop, feature, release, hotfix — 5 types |
| Complexity | Simple | Complex |
| Release frequency | Continuous (suited for continuous deployment) | Planned releases |
| Best for | Web services, SaaS | Libraries and apps requiring versioned releases |
GitHub Flow is recommended for most web service development. If your release process is complex and requires careful scheduling, consider Git Flow.
Branch Naming Conventions
Branch names should make it immediately clear what work is being done on that branch. Consistent naming helps the entire team understand the state of the project at a glance.
Common Prefixes
| Prefix | Purpose | Example |
|---|---|---|
feature/ | New feature development | feature/user-authentication |
fix/ | Bug fixes | fix/login-button-not-responding |
hotfix/ | Emergency fixes for production | hotfix/payment-error |
chore/ | Tests, configuration, dependency updates | chore/update-dependencies |
docs/ | Documentation updates | docs/api-reference |
refactor/ | Refactoring (no behavior change) | refactor/auth-module |
Good vs. Bad Branch Names
| Rating | Example | Reason |
|---|---|---|
| Good | feature/add-search-function | Clearly states what will be done |
| Good | fix/issue-42-null-pointer | Issue number makes it easy to trace |
| Bad | test | Content is unclear |
| Bad | john-branch | A person’s name tells you nothing about the content |
| Bad | fix | Does not say what is being fixed |
| Bad | new feature | Spaces in branch names can cause problems |
Use lowercase letters, numbers, and hyphens in branch names. Avoid spaces and non-ASCII characters.
Commit Message Conventions
Commit messages are the history of your code changes. A good message conveys not just “what changed” but “why it changed.”
Conventional Commits
Conventional Commits is a specification for standardizing commit message formats.
Format: <type>(<scope>): <description>
| Type | Meaning | Example |
|---|---|---|
feat | New feature | feat: add user search functionality |
fix | Bug fix | fix: login button not responding |
docs | Documentation change | docs: add setup instructions to README |
style | Code formatting (no behavior change) | style: fix indentation |
refactor | Refactoring (no behavior change) | refactor: extract auth logic into function |
test | Adding or fixing tests | test: add unit tests for login feature |
chore | Build config, dependency updates | chore: update eslint to v8 |
ci | CI configuration changes | ci: fix GitHub Actions workflow |
Writing Good Commit Messages
# Good example
feat(auth): add password reset feature
Added the ability for users to request a password reset link
by entering their email address.
Closes #78
# Bad examples
fix
update
asdfghjklCommit Granularity
Each commit should contain exactly one logical change. Combining “add login feature” and “change header design” in the same commit makes it difficult to track issues later.
Pull Request Best Practices
Keep PRs Small
The smaller a PR, the better. Large PRs overwhelm reviewers and lead to lower-quality reviews.
| PR Size | Approximate Line Count | Reviewability |
|---|---|---|
| Small (ideal) | Up to 200 lines | Easy to review with full focus |
| Medium (acceptable) | 200–400 lines | Reviewable with careful reading |
| Large (caution) | 400+ lines | Creates a significant burden on reviewers |
PR Description Template
## What Changed
- Added user search feature
- Added a component to display search results
## Why This Change
Addressing the user request in Issue #42.
Users lacked a way to search for other users by name.
## How to Test
1. Start the application
2. Type a name into the search bar in the header
3. Confirm that search results appear
## Screenshots
(Before) (After)
## Related Issue
Closes #42The Importance of Self-Review
Before opening a PR, review your own changes first. Looking at the diff in the “Files changed” tab often reveals typos or leftover debug code.
Code Review Culture
The purpose of code review is to improve code quality and share knowledge across the team. It is not about finding fault.
Writing Review Comments
| Type | Style | Example |
|---|---|---|
| Required change | [must] or direct request | [must] This has a SQL injection vulnerability |
| Suggestion | [nit] or a question | [nit] A more specific variable name might make this easier to read |
| Question | Ask what/why | What scenario does this condition handle? |
| Praise | Positive comment | This approach is clean and simple! |
Approve vs. Request Changes
| Action | Meaning | When to Use |
|---|---|---|
| Comment | Comment only | You have questions or suggestions, but do not want to block the merge |
| Approve | Approved | Ready to merge as-is or after minor fixes |
| Request changes | Changes required | Must be fixed before merging |
Reviewer Mindset
- Critique the code, not the person
- Explain why something is a problem
- Actively praise good code
- Aim to respond within 24 hours
Reviewee Mindset
- Do not take review comments personally
- Reply “fixed” to let the reviewer know about changes
- It is fine to discuss disagreements
- You can merge once you receive an Approve
Branch Protection Rules
Branch protection rules prevent accidental or unauthorized changes to the main branch. Rules like “no merge without a review” or “no merge if tests fail” are automatically enforced.
How to Configure
Go to “Settings” → “Branches” → “Add branch protection rule.” Enter main in the “Branch name pattern” field and configure the following settings.
Common Settings
| Setting | Description | Recommendation |
|---|---|---|
| Require a pull request before merging | A PR is required to merge | Enable |
| Required number of approvals | Minimum number of Approvals | At least 1 |
| Dismiss stale pull request approvals when new commits are pushed | New commits reset existing Approvals | Enable |
| Require status checks to pass before merging | CI tests must pass | Enable |
| Require branches to be up to date before merging | Branch must be up to date before merging | Enable |
| Do not allow bypassing the above settings | Administrators cannot bypass rules | Consider enabling |
| Allow force pushes | Permit force pushes | Disable |
Setting Up CODEOWNERS
CODEOWNERS is a mechanism that automatically assigns the right people as reviewers when files they own are changed.
Writing .github/CODEOWNERS
# Format: <pattern> <GitHub username or team>
# Assign @alice to all file changes
* @alice
# frontend/ changes require review from @bob and @carol
frontend/ @bob @carol
# Core backend logic is owned by @backend-team
src/core/ @myorg/backend-team
# Documentation changes go to @docs-team
*.md @myorg/docs-team
# Security-sensitive files always require @security-team review
.github/workflows/ @myorg/security-teamWhen combined with the “Require review from Code Owners” branch protection setting, important files are guaranteed to be reviewed by their designated owners.
Choosing a Merge Strategy
When merging a PR, GitHub offers three options. Agree on one as a team to keep your history readable.
| Merge Strategy | Characteristics | Best For |
|---|---|---|
| Merge commit | All commits are preserved; a merge commit is created | When you want to retain the full history of every change |
| Squash and merge | All commits on the branch are squashed into one | When you want a clean, readable history on main |
| Rebase and merge | No merge commit; creates a linear history | When you want to keep main history perfectly linear |
“Squash and merge” is recommended for beginner teams. It keeps work-in-progress commits (like “WIP” or “fix typo”) off main, resulting in a cleaner history.
Release Management
Using Milestones
A Milestone is a feature for grouping Issues and PRs toward a specific release or deadline.
Create one at “Issues” → “Milestones” → “New milestone.” Set a title (e.g., v1.0.0 Release) and a due date, then attach related Issues to the milestone.
A progress percentage is displayed, giving you an at-a-glance view of how much work remains before the release.
Writing Release Notes
Good release notes include the following:
## v1.2.0 (2026-03-21)
### New Features
- Added user search (#42)
- Dark mode support (#55)
### Bug Fixes
- Fixed login button not responding in certain environments (#61)
- Fixed profile image not displaying correctly (#63)
### Breaking Changes
- Minimum supported Node.js version is now v18Anti-Patterns in Team Development
These are practices to avoid in team development.
Direct Pushes to main
# Never do this
git push origin mainDirect pushes to main are forbidden. Always create a branch and merge through a PR. Branch protection rules can physically prevent this.
Oversized PRs
A PR with over 1,000 lines is practically impossible to review effectively. Split features into smaller, separate PRs.
Merging Without a Review
Skipping a review because you are “in a hurry” is dangerous. Many production incidents originate from unreviewed changes.
Opening a PR With Unresolved Conflicts
Pull the latest main changes into your branch and resolve any conflicts before opening a PR.
Meaningless Commit Messages
Messages like “fix,” “update,” or “asdf” make it impossible to understand what changed later.
Force Pushing to a Shared Branch
Do not use git push --force on branches shared with others. It can overwrite teammates’ commits. If unavoidable, use --force-with-lease and notify your team in advance.
FAQ
Q. Should I choose GitHub Flow or Git Flow?
GitHub Flow is recommended for most web service development. Git Flow has many branches to manage and a steeper learning curve. Start with GitHub Flow, and consider Git Flow only if you need to maintain multiple versions simultaneously.
Q. Is it okay to write commit messages in a language other than English?
As long as your team agrees, any language is fine. However, English is the standard for open source projects and international teams. The Conventional Commits format (feat:, fix:, etc.) can be used regardless of language.
Q. What should I do when reviewers disagree?
Try to resolve the disagreement through written discussion first. If that does not work, discuss it verbally or in a meeting. Keep the focus on the shared goal of improving code quality, not on personal opinions.
Q. I set up branch protection rules and now no one can merge.
If “Require approvals” is set to 1 or more and no one has Approved the PR, merging is blocked. Have another team member Approve the PR. If you are a solo developer, set the required number of approvals to 0 or adjust the branch protection settings accordingly.
Q. Does squash and merge delete my commit history?
The original branch still exists unless you delete it. The individual commits will no longer appear in main’s history, but you can still view them on the original branch or from the PR page.
See the references for the external specifications and background sources used on this page.[1][2]
References
- Git, Reference
- GitHub, GitHub Docs