Git from First PrinciplesPost 9 of 16 · 16 of 16 written

9. Workflows and conventions

Git · 9 of 163 min readOct 2026

A workflow is an agreement about which branches exist, who writes to them, and how a change reaches main. Git enforces none of it; hosts enforce some of it through branch protection. For solo and small-team AI work, the default to start with is GitHub flow with short-lived branches.

Three common workflows

Workflow How it works Fits
GitHub flow main is always working. One short branch per change, a pull request, review and CI, then merge. Most web and ML projects, including solo work
Trunk-based Everyone integrates into main at least daily. Branches live hours, not weeks; unfinished features hide behind flags. Teams with strong CI and frequent deploys
Git flow Long-lived develop plus feature/*, release/* and hotfix/* branches; main holds releases. Versioned products with scheduled releases; heavy for most others

The common thread: long-lived branches are expensive. The longer two lines diverge, the bigger and riskier the eventual merge.

Branch names

Use lowercase words with hyphens and a short prefix: feat/stratified-split, fix/nan-loss, exp/lora-r16, chore/bump-deps. An exp/ prefix for experiments is especially useful in ML repositories.

Commit messages

Add stratified split to data loader

A random split put only 2% of the minority class in validation,
which made F1 swing widely across seeds. Stratify on the label so
each split keeps the class ratio. Fixes #42.
  • Subject line: about 50 characters, imperative mood, no full stop. Test it: “If applied, this commit will add stratified split to data loader.”
  • A blank line, then a body wrapped near 72 characters that explains why and any trade-offs.
  • Reference issues or tickets so the context survives.

Conventional Commits add a type prefix: feat:, fix:, docs:, refactor:, test:, perf:, chore:, with ! marking a breaking change. Tools can then generate changelogs and version bumps automatically. It is optional but widespread.

Atomic commits

Each commit should hold one logical change and leave the project working. Keep refactors apart from behaviour changes, and formatting apart from logic. Small, clean commits make review readable, reverts surgical and bisect precise.

Pull requests and review

  • Keep them small. A few hundred changed lines get a real review; a few thousand get a rubber stamp.
  • Describe what changed, why, and how you tested it.
  • For ML changes, add evidence: metrics before and after, the dataset version, and the config diff.
  • Review the diff, not the description. Pull the branch and run it when the change is risky.
  • Protect main: require a pull request, passing CI and a review, and block force-pushes.

Versions and releases

Semantic versioning uses MAJOR.MINOR.PATCH. Bump MAJOR for breaking changes, MINOR for new compatible features, PATCH for fixes. Release by creating an annotated tag such as v1.4.2, pushing it, and publishing a release on your host.

git describe --tags names any commit relative to the nearest tag. v1.4.2-3-gabc1234 means three commits after v1.4.2, at commit abc1234. That makes a precise, human-readable build ID.

For models, keep the code version and the model version separate. Tag the commit that produced each released model, and record that hash and the data version in the model card.

Check yourself

  • What does “main is always working” demand of every pull request?
  • Rewrite “fixed stuff” as a proper message for a change you made recently.
  • What does v2.3.1-5-g1a2b3c4 tell you about a build?