Branching Strategy
This document defines the standard branching strategy used across software engineering projects.
Polk County’s Software Engineering Team uses GitHub Flow as the default branching model. GitHub Flow is a lightweight workflow centered around short-lived feature branches, pull requests, and frequent integration into the main branch.
This strategy supports rapid iteration, continuous integration, and a stable deployment-ready main branch.
GitHub Flow
Section titled “GitHub Flow”---
config:
theme: base
gitGraph:
showCommitLabel: false
---
gitGraph
commit
branch "feature 1"
branch "feature 2"
checkout "feature 1"
commit
commit
checkout main
merge "feature 1" tag: "v0.1.0"
checkout "feature 2"
commit
commit
checkout main
merge "feature 2" tag: "v0.2.0"
branch "hotfix"
commit
checkout main
merge "hotfix" tag: "v0.2.1"
branch "feature 3"
commit
commit
commit
checkout main
merge "feature 3" tag: "v1.0.0"
The following principles apply to all repositories using the GitHub Flow branching strategy:
- Keep
mainin a deployable state. It contains the latest stable version of the codebase. - No direct commits are made to
main. All changes go through feature branches and pull requests. - Each new feature, bug fix, or improvement is developed in a separate branch created from
main. - Open a pull request for all changes before merging.
- Use peer review and automated validation as part of the merge process.
- Automated checks should pass before a pull request is merged.
- Prefer small, incremental changes over large, long-lived branches.
- Squash merge pull requests to maintain a clean commit history.
- Delete branches after merging to keep the repository clean.
Standard Workflow
Section titled “Standard Workflow”The standard GitHub Flow process is:
-
Create a branch from
mainUse a short-lived branch for the change.
-
Make and commit changes
Keep commits focused and descriptive, following the Conventional Commits standard.
-
Push the branch to GitHub
Publish the branch so a pull request can be opened.
-
Open a pull request
Describe the purpose of the change and any relevant context.
-
Run automated checks
CI workflows should validate the proposed changes.
-
Complete review
Address reviewer feedback, update the branch from the latest
origin/mainwhen needed, and confirm readiness to merge. -
Merge into
mainMerge only after approval and successful validation.
Squash merging is preferred to maintain a clean history.
-
Delete the branch
Remove the short-lived branch after merge.
Updating a Feature Branch After Opening a PR
Section titled “Updating a Feature Branch After Opening a PR”When a developer has already opened a pull request and later discovers that their feature branch is behind origin/main, the preferred approach is usually to rebase the feature branch onto the latest origin/main and then push the updated branch back to the PR.
This keeps the pull request focused on the feature work and avoids adding an extra merge commit from origin/main into the branch.
Before updating
Section titled “Before updating”gitGraph
commit id: "A"
commit id: "B"
commit id: "C"
commit id: "D"
branch feature-branch
checkout feature-branch
commit id: "E"
commit id: "F"
In this state, the PR is open from feature-branch into main.
Main advances while the PR is open
Section titled “Main advances while the PR is open”gitGraph
commit id: "A"
commit id: "B"
commit id: "C"
commit id: "D"
branch feature-branch
checkout feature-branch
commit id: "E"
commit id: "F"
checkout main
branch other-feature
checkout other-feature
commit id: "G"
commit id: "H"
checkout main
merge other-feature id: "M1"
At this point, feature-branch is behind origin/main and needs to be updated before the PR can be merged.
After rebasing onto origin/main
Section titled “After rebasing onto origin/main”gitGraph
commit id: "A"
commit id: "B"
commit id: "C"
commit id: "D"
commit id: "M1"
checkout main
branch feature-branch
checkout feature-branch
commit id: "E'"
commit id: "F'"
After the rebase, the feature commits are replayed on top of the latest origin/main.
Commits E' and F' are rewritten versions of the earlier commits E and F. The branch name stays the same, but the commit IDs change after the rebase.
Recommended commands
Section titled “Recommended commands”git checkout feature-branchgit fetch origingit rebase origin/mainIf Git reports conflicts during the rebase:
# resolve conflicts in your editorgit add <resolved-files>git rebase --continueRepeat that process until the rebase finishes.
Then update the branch on the remote:
git push --force-with-lease origin feature-branchWhy --force-with-lease
Section titled “Why --force-with-lease”A rebase rewrites commit history, so a normal push will usually be rejected. --force-with-lease is the safer way to update the remote branch because it avoids overwriting unexpected remote changes.
Why rebase is usually the best choice for an open PR
Section titled “Why rebase is usually the best choice for an open PR”Rebasing usually gives the cleanest pull request because it:
- keeps history linear
- avoids a merge commit from
origin/main - makes the PR diff easier to review
- presents the feature as if it were built on the latest
origin/main
Alternative approach: merge origin/main into the feature branch
Section titled “Alternative approach: merge origin/main into the feature branch”Some teams prefer merging instead of rebasing.
git checkout feature-branchgit fetch origingit merge origin/maingit push origin feature-branchgitGraph
commit id: "A"
commit id: "B"
commit id: "C"
commit id: "D"
branch feature-branch
checkout feature-branch
commit id: "E"
commit id: "F"
checkout main
branch other-feature
checkout other-feature
commit id: "G"
commit id: "H"
checkout main
merge other-feature id: "M1"
checkout feature-branch
merge main id: "M2"
This is valid, but it adds a merge commit to the PR, which can make review history noisier.
Rule of thumb
Section titled “Rule of thumb”For a developer-owned feature branch with an open pull request, the best default is:
- Fetch the latest refs from the remote.
- Rebase the feature branch onto
origin/main. - Resolve any conflicts.
- Run tests.
- Push with
--force-with-lease.
When not to rebase
Section titled “When not to rebase”Rebasing may not be the best option when:
- multiple developers are actively sharing the same branch
- your team prefers merge commits for branch updates
- branch protections or repository policies do not allow rewritten history
Compact workflow
Section titled “Compact workflow”git checkout feature-branchgit fetch origingit rebase origin/maingit push --force-with-lease origin feature-branchWhat happens to the PR
Section titled “What happens to the PR”The pull request remains open. Once the updated branch is pushed, the PR automatically reflects the rebased commits and updated diff against the latest origin/main.
Branch Naming
Section titled “Branch Naming”Branch names should clearly describe the purpose of the work.
Recommended patterns include:
feat/short-descriptionfix/short-descriptionchore/short-descriptiondocs/short-description
Examples:
feat/add-permit-searchfix/handle-null-api-responsechore/update-dependenciesdocs/update-onboarding-guide
Use lowercase letters and hyphens for readability and consistency.
Pull Request Expectations
Section titled “Pull Request Expectations”All changes to shared branches should be introduced through pull requests.
Pull requests should:
- Clearly describe what changed
- Explain why the change was made
- Reference related issues or tickets when applicable
- Remain focused in scope
- Be reviewed before merging
- Pass all required automated checks
Pull requests are the primary checkpoint for collaboration, validation, and quality control.
For detailed expectations regarding pull request size, reviewer responsibilities, and approval requirements, see the code review standards.
CI Requirements
Section titled “CI Requirements”Repositories should use automated workflows to validate changes before merging.
Typical checks include:
- Linting and formatting checks
- Unit and integration tests
- Build verification
- Static code analysis and security scans
A pull request should not be merged until required checks have completed successfully.
Keeping Branches Short-Lived
Section titled “Keeping Branches Short-Lived”Branches should be kept short-lived to reduce merge conflicts and simplify review.
To support this:
- Start new work from the latest
main - Merge frequently
- Avoid bundling unrelated changes together
- Break large efforts into smaller pull requests when possible
Long-lived branches increase the risk of drift, review complexity, and integration problems.
What to Avoid
Section titled “What to Avoid”The following practices should be avoided:
- Direct commits to
main - Long-lived feature branches
- Large pull requests with unrelated changes
- Skipping pull request review
- Merging with failing automated checks
- Reusing old branches for new work