Collaborating on GitHub properly means a short-lived branch per change, a pull request that a real reviewer actually reads rather than rubber-stamps, branch protection that makes the main branch un-skippable, a CODEOWNERS file so the right person gets pulled into the right review automatically, and a deploy pipeline where merging to main is what makes a change live, not a separate manual step someone has to remember. Most of the friction teams associate with GitHub collaboration comes from skipping one of these five, not from the tool itself.
Every GitHub best-practices post lists the same five bullet points, branch, review, protect, and so on, without explaining why each one exists or what breaks when it is missing. The practices are not process for its own sake; each solves a specific failure mode teams hit without them. This post covers the workflow in the order it actually gets used, ending with the part most guides skip entirely: what happens automatically once a pull request merges.
Table of contents
- Branching: short-lived over long-lived
- Pull requests: what a real review actually checks
- CODEOWNERS and branch protection
- Issues and Projects for tracking the work itself
- Preview deploys per branch, and deploying on merge
- Common collaboration failures, and the rule that prevents each
- How this fits the rest of the stack
- FAQ
Branching: short-lived over long-lived
The failure mode long-lived branches produce is a merge that gets harder every day it is delayed — two branches drifting further from each other until reconciling them is its own project. Short-lived branches, created for one change and merged within a day or two, avoid this by construction: there is not enough time to drift far.
Favor branching over forking for regular collaborators on the same repository — forks make sense for outside contributors on an open-source project, but for a team working together daily, everyone branching off the same repository keeps CI, reviews, and branch protection working uniformly rather than duplicated across forks. The git vs GitHub vs GitLab post covers the underlying distinction if that terminology is still fuzzy.
Pull requests: what a real review actually checks
A pull request that gets approved within ninety seconds of opening is not a review, whatever the tool logs it as.
- Does the change do what the description says it does, checked against the actual diff, not just the description.
- Is there a test for the behavior that changed, or a clear reason one is not needed.
- Does it touch anything outside its stated scope, which is often the first sign a change is doing two things at once.
- Would the reviewer understand this code in six months with no other context than what is in front of them.
A small, focused pull request gets a real review in minutes. A three-thousand-line pull request gets a rubber stamp, because no reviewer actually reads three thousand lines carefully, and everyone involved knows it.
CODEOWNERS and branch protection
A CODEOWNERS file maps paths in the repository to the people or teams who should review changes to them, and GitHub can require their approval automatically rather than relying on someone remembering to tag the right reviewer.
# CODEOWNERS
/backend/ @team-backend
/frontend/ @team-frontend
*.sql @db-team
/infra/ @platform-team
Branch protection rules turn the policy into something that cannot be skipped under deadline pressure: require a passing status check, require at least one approval, require the CODEOWNERS review for touched paths, and disallow direct pushes to main entirely, including from repository admins if the team wants it to be truly unskippable.
The combination, CODEOWNERS plus branch protection, is what makes the right person reviews the right code a property of the repository rather than a habit that erodes as the team grows.
Issues and Projects for tracking the work itself
GitHub Issues and Projects exist so that what a pull request changes is traceable to why — linking a pull request to the issue it closes gives anyone reading the history later the context a diff alone never carries.
The practical habit worth keeping: reference the issue number in the pull request description using GitHub’s closing keywords, so merging automatically closes the issue, and use a Project board only as far as it reflects actual state — a board nobody updates is worse than no board, because it actively misleads anyone trusting it.
Preview deploys per branch, and deploying on merge
The part most GitHub collaboration guides skip entirely, because it is a hosting concern rather than a Git concern: a pull request is far easier to review when the reviewer can click a live preview of the change instead of reading a diff and imagining the result.
A preview deploy per branch, a temporary, unique URL built from the pull request’s commits, turns does this look right from a guess into something you can actually look at. Deploying to production on merge to main closes the loop: the branching model, the review, and the deploy pipeline all agree on the same single source of truth, main, with no separate manual deploy step for someone to forget.
On RunxBuild, a static site or web service built from a GitHub repository gets exactly this: a build and a live route from a push, with the deploy history and rollback to fall back on if a merge to main turns out to be wrong.
Common collaboration failures, and the rule that prevents each
A short list of cause and prevention, drawn from the same handful of failures every team eventually hits:
- A broken main branch. Prevented by requiring a passing CI status check before merge, not after.
- The wrong person reviewing critical code. Prevented by CODEOWNERS plus a required-reviewer branch protection rule.
- A merge that undoes someone else’s recent work. Prevented by requiring branches to be up to date with main before merging, not just passing checks on an old base.
- A deploy that only works on one machine. Prevented by a build-from-repository pipeline that runs the same build for every branch, preview and production alike.
How this fits the rest of the stack
None of this is about process for its own sake — each rule exists because a specific failure happened to someone before it was written down. Branch protection, CODEOWNERS, and a build that runs the same way for a preview and for production are what make collaboration on GitHub scale past two people trusting each other to remember the rules. Deploying from GitHub on RunxBuild covers the build-from-repository side of that pipeline, the RunxBuild hosting calculator prices the service behind it, and the dashboard is where the deploy history and rollback live once it is set up.
Useful related references:
- GitHub Pages SEO: What You Can Fix and What You Cannot
- Git vs GitHub vs GitLab: One Is a Program, Two Are Companies
- Undo Commit on GitHub: What You Can Do in the Browser and What Needs the Terminal
- Deploying from GitHub on RunxBuild
FAQ
What is the best branching strategy for a small team on GitHub?
Short-lived branches off main, one per change, merged within a day or two through a pull request. Long-lived branches drift further from main the longer they exist, turning an eventual merge into its own project. Favor branching over forking when everyone is a regular collaborator on the same repository.
What does a CODEOWNERS file actually do?
It maps paths in the repository to the people or teams responsible for reviewing changes there, and lets GitHub require their approval automatically through branch protection. It replaces relying on someone remembering to tag the right reviewer with a rule the repository enforces on its own.
What should branch protection rules require on the main branch?
At minimum: a passing status check before merge, at least one approval, and the relevant CODEOWNERS review for any protected paths touched. Many teams also disallow direct pushes to main entirely, including from admins, so the rule cannot be skipped under deadline pressure.
How do I get a preview deploy for every pull request?
Use a build pipeline that deploys every branch to its own temporary URL from the same build process as production, rather than a special-cased staging environment. On a platform built around deploying from a repository, this is typically automatic for every pushed branch, not something configured per pull request.
What is the point of linking issues to pull requests on GitHub?
It preserves the reason behind a change for anyone reading the history later — a diff shows what changed, an issue shows why. Using GitHub’s closing keywords in a pull request description also closes the issue automatically on merge, keeping the two in sync without a manual step.