Why a Team Needs Agreed Rules
Git does not care how you organise your branches. It will let four people commit straight to main at the same time, or create forty branches nobody can name. A workflow is the set of rules a team agrees on so that everybody makes the same assumptions: which branch is trustworthy, where new work goes, how it gets back in, and how a release is marked.
The absence of rules has a recognisable shape in student projects. Everyone works on main. Two people edit the same file, one pushes, the other force-pushes over it. main is broken half the time, so nobody dares pull. Eventually somebody keeps a private folder as the "real" version and shares it on chat, and the repository stops meaning anything. Every part of that is preventable with three or four agreed rules.
There is no single correct workflow. The right one depends on how many people are involved, how often you release, and whether anything is running in production that users depend on. What matters is that the rules are written down — in the README or CONTRIBUTING.md — and that everyone follows the same ones.
- Which branch is always safe to demonstrate or deploy from
- Where new work happens, and how branches are named
- How work gets merged — pull request, direct merge, who may approve
- Who may push to the protected branch, and whether force-push is blocked
- How a released version is marked, so you can go back to it
- What happens when something urgent breaks after release
- Whatever you choose, protect the main branch in the GitHub settings. Requiring a pull request and blocking force-pushes enforces most of a workflow automatically, so it does not depend on everyone remembering the rules at midnight.
GitHub Flow: Start Here
GitHub Flow is the simplest workflow that is still safe, and it is the right default for almost every college project and small team. It has one long-lived branch — main — and one rule about it: main always works. Anything you can see on main should run, and you should be willing to demonstrate it without checking first.
Everything else is a short-lived branch. You branch from main, do one piece of work, push, open a pull request, get it reviewed, merge, and delete the branch. A branch lives for days, not weeks. That is the whole model, and there is nothing else to memorise.
The reason it works so well for students is that it makes the panic scenario impossible. If main is always working, then the night before a demo you check out main and you are done. Nobody has to remember which of five branches had the good version. Broken work sits on its own branch, harming nobody.
One rule keeps it honest: never merge something into main that you have not run. "It should work" is how main becomes untrustworthy, and once it is untrustworthy once, people stop relying on it and the workflow collapses.
# The entire workflow
# 1. Always start from the latest main
git switch main
git pull
# 2. One branch per task
git switch -c feature/event-registration
# 3. Work in small commits
git add src/register.js
git commit -m "Add event registration form"
git add src/register.js
git commit -m "Validate email before submit"
# 4. Push and open a pull request
git push -u origin feature/event-registration
gh pr create --title "Add event registration"
# 5. Review, then merge (squash keeps main tidy)
gh pr merge --squash --delete-branch
# 6. Update local main and clean up
git switch main
git pull
git fetch --prune - For a team of two to five people building one thing, stop reading here. GitHub Flow plus branch protection plus honest commit messages will handle your entire project. The workflows below solve problems you probably do not have yet.
Git Flow: When Releases Are Formal
Git Flow is a more elaborate model built for software that ships in versioned releases — desktop applications, libraries, anything where several versions exist in the world at once and users are not all on the latest. It adds a second permanent branch and three categories of temporary ones.
main holds only released versions, and every commit on it corresponds to something users actually have. develop is where finished features accumulate between releases. feature/* branches come off develop and go back into it. When enough has accumulated, a release/* branch is cut for final testing and version bumps, then merged into both main and develop. A hotfix/* branch comes off main directly for emergencies and also merges back into both.
The strength is control: you always know exactly what is released, and an urgent fix can go out without dragging along half-finished features sitting in develop. The cost is real overhead — more branches, more merges, and more chances to merge into the wrong place.
Be honest about whether you need it. A continuously deployed website has no meaningful concept of "release 1.2", so Git Flow adds ceremony and nothing else. Teams that adopt it for a small web project usually abandon it within a month. Use it when you genuinely maintain versions.
# Branch roles
# main released versions only, every commit is tagged
# develop integration branch for finished features
# feature/* branch from develop, merge back to develop
# release/* branch from develop, merge to main AND develop
# hotfix/* branch from main, merge to main AND develop
# A feature
git switch develop && git pull
git switch -c feature/user-profile
# ... work ...
git switch develop
git merge --no-ff feature/user-profile
# A release
git switch -c release/1.2.0 develop
# ... version bump, final testing, no new features ...
git switch main
git merge --no-ff release/1.2.0
git tag -a v1.2.0 -m "Release 1.2.0"
git switch develop
git merge --no-ff release/1.2.0
# An emergency fix on released code
git switch -c hotfix/payment-timeout main
# ... fix ...
git switch main && git merge --no-ff hotfix/payment-timeout
git tag -a v1.2.1 -m "Fix payment timeout"
git switch develop && git merge --no-ff hotfix/payment-timeout - Suits versioned software: mobile apps, desktop tools, libraries with published version numbers
- Gives a clear answer to "what exactly is in production?" — whatever
mainis tagged at - Lets an urgent fix ship without releasing unfinished work
- Costs more merges, and a hotfix must be merged into two branches or it silently reappears next release
- Usually too heavy for a continuously deployed website or a student project
Trunk-Based Development, and Choosing
Trunk-based development goes the other way and removes almost all branching. Everyone integrates into main — the trunk — at least daily, and branches, where they exist at all, live for hours. It is what many large teams do, because it eliminates the long, painful merges that come from branches drifting apart for weeks.
The obvious objection is how you integrate an unfinished feature into a branch that must keep working. The answer is feature flags: the code is merged but wrapped in a condition that keeps it switched off until it is ready. The feature is present and inert rather than absent and diverging.
This only works with strong discipline. It needs automated tests running on every push, small changes, and people who will not merge something they have not verified. Without those it becomes exactly the "everyone commits to main" chaos described at the start of this lesson, which is why it is a poor first workflow to learn on — the safety comes from the practices around it, not from the branching model.
Choosing between the three is mostly a question of scale and release style. Small team, continuous deployment, one version live: GitHub Flow. Versioned software with parallel maintained releases: Git Flow. Large experienced team with thorough automated testing: trunk-based. When in doubt, start with GitHub Flow — it is the easiest to adopt and the easiest to grow out of.
- GitHub Flow — one
main, short feature branches, pull requests. The default for small teams and student projects. - Git Flow —
mainplusdevelopplus release and hotfix branches. For software with real version numbers. - Trunk-based — everyone merges to
maindaily, incomplete work hidden behind feature flags. Needs strong automated testing. - Forking workflow — contributors work in their own forks and send pull requests. Standard for open source and anywhere contributors lack write access.
- Whichever you pick: protect
main, keep branches short-lived, and never merge code you have not run
- Mark releases with annotated tags —
git tag -a v1.0.0 -m "First release"— and push them explicitly withgit push origin v1.0.0, since tags are not sent by an ordinary push. A tag is a permanent, human-readable name for a commit, which is far easier to find in six months than a hash. - Write your team's workflow into the README before you start coding. Five lines saying which branch is safe, how branches are named, and that everything goes through a pull request will prevent more problems than any Git command in this course.
