Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Production Git Workflow Standards

This handbook establishes mandatory, professional-grade conventions for Git branching and committing. Adherence to these standards is essential for achieving traceability, enabling automated MLOps gates, ensuring accurate changelogs, and streamlining architectural reviews.

The workflow is enforced through a Three-Tier Naming Structure, a strict Conventional Commits policy with structured body bullets, and a Squash-and-Merge strategy that produces one atomic commit per PR on trunk.

Intro: Three-Tier Naming Structure

All changes must follow this strictly enforced hierarchy for complete lifecycle context.

TierComponentPurposeRequirement
1. SCOPEBranch Prefix + Ticket ID
fix/29-fix-broken-feature
Defines the scope of work (e.g., feature/) and its associated work item.MANDATORY for all branches.
2. INTENTCommit Title Prefix
fix: Fix login in app
Defines what changed (Conventional Commits type, e.g., feat:, fix:).MANDATORY for all commits.
3. JUSTIFICATIONArchitectural Tag (ArchTag)Defines why a structural change was made. Placed in the commit body.CONDITIONAL (Mandatory for refactor:, perf:, BREAKING CHANGE Footer).

Tier 1: Branch Prefixing Policy - Scope & Traceability

Branch names MUST use the format:

<prefix>/<TICKET-ID>-<short-kebab-description>

Names must be lowercase with hyphens. This format enables automatic linking to issues and Pull Requests. Not implemented yet.

PrefixWork ScopeExample
main/The main development branch (e.g., main, master, or develop)N/A
feat/New features or significant enhancements/additions.feat/456-add-metrics-endpoint
fix/Correcting a user-impacting defect/bug.bugfix/123-login-404-error
hotfix/Urgent fixes applied directly to the production branch.hotfix/123-payment-gateway-bug
release/Preparation for a formal release (e.g., final testing, version bumps).release/v1.2.0-final-prep
chore/For non-code tasks like dependency, docs updates.chore/TICKET-789-update-deps

Branches are different from commits—they are temporary and mainly used until merged. Introducing too many types for branches would be unnecessary and would make them harder to manage and remember.

Conventional Branch docs

Tiers 2 & 3: Conventional Commit Policy - Intent & Justification

Tier 2: Commit Title Prefix - What Changed

The commit title MUST start with <type>: <description>. The description must be 50 characters maximum and written in the imperative mood.

GroupTypeIntentArchTag Required?SemVer Impact
Corefeat:A new feature or enhancement.NOMinor
fix:A bug fix.NOPatch
Breaking(any type) + BREAKING CHANGE footerIntroduces an incompatible API/behavior change.YESMajor
Architecturalrefactor:Code restructuring that neither fixes a bug nor adds a feature; can change public API.YESPatch/Minor/Major
perf:Code change that measurably improves performance; can change public API.YESPatch/Minor/Major
Routinedocs:Changes to documentation only.NONone
test:Adding or correcting tests.NONone
chore:Routine maintenance, dependency updates, minor clean-up.NONone
ci:Changes to CI/CD configuration and workflows.NONone
pr:Promotional or announcement posts (e.g., Telegram channel).NONone

The official way to signal changes that break compatibility is:

<type>(<optional scope>): <description>

BREAKING CHANGE: <description of breaking impact>
IntentCorrect Conventional Commit Form
Removing a feature (user-facing)feat: remove legacy auth API
BREAKING CHANGE: The /v1/login endpoint is deleted. Use /v2/auth.
Removing internal code (no external impact)refactor: delete unused cache module
(No BREAKING CHANGE needed)
Removing a config option or API (breaking)fix: drop support for deprecated TLS 1.0
BREAKING CHANGE: TLS 1.0 connections are now rejected.

Tier 3: Architectural Tagging - Why Changed

For commits of type

  • refactor:,

  • perf:, or

  • BREAKING CHANGE Footer,

the architectural intent MUST be provided as an ArchTag (Architectural Tag).

  • Format: The tag MUST be the first line of the commit body: ArchTag:TAG-NAME (one tag only). Structured body bullets (see Structured Commit Body Format) follow after the ArchTag line. Each bullet uses the format - <Verb>: \` — <what/why>`.

  • Syntax Rules: The tag MUST NOT include the # symbol or any spaces.

  • Validation: CI/CD automation tools will validate the tag’s presence and correctness.

Tag NameIntentHeuristic / Automation Gate
DEPRECATION-PLANNEDSunset code, APIs, or features scheduled for removal.PR requires Architect Approval (Hard Gate).
TECHDEBT-PAYMENTReducing complexity, upgrading dependencies, or simplifying code.Signals maintenance work.
REFACTOR-MIGRATIONMajor architecture shift or pattern change (e.g., monolith to microservice).PR requires Architect Approval (Hard Gate).
PERF-OPTIMIZATIONCode change explicitly addressing a performance bottleneck.Benchmarks must be provided in the commit body.

Structured Commit Body Format

Every commit on trunk (the squashed commit produced by Squash-and-Merge) MUST have a structured body with at least one changelog bullet. This extends Tier 2 (Intent) with detailed change manifests that enable automated CHANGELOG extraction.

Body Convention

<type>[(<scope>)]: <subject line>

- <Verb>: <file-path> — <what_and_why>

Co-Authored-By: ...

The <what_and_why> portion explains what changed in the file and why — this is a changelog, so each bullet should capture both the substance and the motivation.

Rules:

  1. Body MUST contain at least one line starting with - (a changelog bullet).

  2. Each bullet is a self-contained changelog entry. No line length limit — one bullet = one line, regardless of length. Commits are primarily read in Git host UIs which wrap automatically.

  3. Verb prefix before : — Created, Updated, Deleted, Renamed, Fixed, Moved, Added, Removed, Refactored, Configured.

  4. <file-path> is a path relative to the repo root. Single file: check_adr.py. Glob/pair: 01_guide.(md|ipynb). Rename: old.py → new.py. Every change lives in a file — always use the nearest file path, no abstract targets.

  5. Git trailers (after blank line, Key: Value format) are excluded from parsing.

  6. Non-bullet lines in the body (prose context) are ignored by the parser but allowed for human context.

  7. ArchTag:TAG-NAME line (Tier 3) is preserved for justification validation but excluded from changelog output.

  8. Sub-bullets provide detail under a file target: 4-space indent + em-dash + space + lowercase verb + description ( — added feature X). Sub-bullets MUST follow a main bullet — orphan sub-bullets are rejected. Sub-bullets are excluded from CHANGELOG output (only main bullets survive).

Sub-bullet Format

Sub-bullets break down what changed within a single file target. They are validated by the pre-commit hook but do not appear in the generated CHANGELOG (only main bullets are extracted).

- <Verb>: <file-path> — <what_and_why>
    — <lowercase_verb> <specific_detail>
    — <lowercase_verb> <another_detail>
- <Verb>: <file-path> — <what_and_why>

Sub-bullet rules:

  • Format: exactly 4 spaces + — (em-dash + space) + lowercase verb + description

  • MUST follow a main bullet — no orphan sub-bullets

  • Multiple main bullets can each have their own sub-bullet group

  • Sub-bullets are validated but not included in CHANGELOG output

Examples

Complex commit (docs) — real commit 3e652bc
docs: Restructure website deployment documentation

- Created: tools/docs/website/01_github_pages_deployment.(md|ipynb) — the GitHub Pages guide was invisible on the website because it lacked a Jupytext pair; new canonical guide consolidates scattered setup instructions
- Renamed: mystmd_website_deployment_instruction.(md|ipynb) → 02_self_hosted_deployment.(md|ipynb) — clear numbering signals the active guide (01) vs deprecated alternative (02)
- Refactored: 02_self_hosted_deployment.md — duplicated MyST init/config/testing sections diverged from the canonical guide; replaced with cross-references to eliminate drift
- Deleted: tools/docs/git/github_pages_setup.md — content fully absorbed into 01_github_pages_deployment; keeping both would cause conflicting instructions
- Updated: architecture/adr/adr_26022...md — self-hosted link pointed to a renamed file; split single reference into two entries (active + deprecated) for clarity
- Updated: architecture/packages/README.md — GitHub Pages setup link pointed to the deleted file
- Updated: architecture/packages/creating_spoke_packages.md — same dead link fix in Next Steps section
- Fixed: configs/mutli-site/ → configs/multi-site/ directory typo

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Simple commit (fix)
fix: Correct broken MyST term reference in ADR-26019

- Fixed: `{term}`ADR 26001`` → `{term}`ADR-26001`` across 11 ADR files — missing hyphen caused unresolved glossary references, breaking cross-linking in the rendered site

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refactor with ArchTag (Tier 3 + structured body coexist)
refactor: Simplify model loading logic

ArchTag:TECHDEBT-PAYMENT
- Refactored: model_loader.py — cyclomatic complexity was 15, causing test failures on edge cases; reduced to 8 by extracting format-specific loaders
- Deleted: legacy_loader.py — its ONNX path was the only unique logic; moved into the main loader to eliminate a 200-line file with no test coverage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Multi-file change with sub-bullets (feat) — real commit 00362f0
feat: support sub-bullet format in commit body validation

- Updated: tools/scripts/validate_commit_msg.py
    — added _SUB_BULLET_RE for 4-space indented em-dash lines
    — added validation that sub-bullets must follow a main bullet
    — updated validate_body docstring with format examples
- Updated: tools/tests/test_validate_commit_msg.py
    — added TestParseCommitMessage class (4 tests for message parsing)
    — added 7 sub-bullet validation tests (orphan, valid, multiple mains)
    — added CLI integration tests for sub-bullet format

Co-Authored-By: Qwen-Coder <qwen-coder@alibabacloud.com>

Release-Time CHANGELOG Generation

The CHANGELOG is a generated artifact, not a manually curated document. It is produced at release time by generate_changelog.py, which extracts structured body bullets from git history.

The “Ingredients-First” pattern:

  1. At commit time: validate_commit_msg.py (pre-commit hook, commit-msg stage) ensures every commit body contains parseable bullets — the “ingredients” are validated continuously.

  2. At release time: generate_changelog.py runs once on the frozen release branch, extracting all structured bullets since the last tag — “cooking” the CHANGELOG from validated ingredients.

# Generate changelog between two refs
uv run tools/scripts/generate_changelog.py v2.4.0..HEAD --version 2.5.0

The generator uses git log --first-parent to scan only squashed trunk commits, ignoring noise from deleted feature branches. Legacy commits (pre-standard, no body bullets) are included with subject line only — graceful degradation, not failure.

The type → CHANGELOG section mapping (e.g., feat: → “New Features”) and section ordering are configured in pyproject.toml under [tool.commit-convention.changelog-sections].

Local Checkpoint Commits: Handling Intermediate Work

WIP: is not a valid commit type — the validator rejects it. For local checkpoint saves (end of day, switching machines), use --no-verify to bypass the pre-commit hook:

git commit --no-verify -m "WIP: save work before switching context"

This is an explicit opt-out: the developer consciously bypasses validation, and CI will reject the commit if it reaches a PR without being squash-merged into a proper commit.

Since the repository enforces Squash-and-Merge (see Merge Strategy), local checkpoint commits are automatically eliminated when the PR is merged — the Git host produces a single squashed commit on trunk.

Optional local hygiene: Developers may use git rebase -i to clean up their branch history before opening a PR. This improves PR reviewability but is not required — the squash at merge time handles consolidation.

PR System Guardrails (Hard Gates)

  • Mandatory Merge Strategy: The repository is configured to enforce “Squash and Merge” for all feature branches into mainline branches. The Git host must populate the squash commit message from the PR description, ensuring structured body bullets flow into the final commit. This guarantees that the final history is composed of single, clean, semantic commits.

The most effective enforcement mechanism is layered: automated hooks catch structural issues, reviewers assess semantic quality.

PolicyImplementationRationale
Commit Message Validationvalidate_commit_msg.py runs as a commit-msg pre-commit hook locally. CI repeats the check on the PR’s squash commit candidate.Hard gate. Enforces Conventional Commits subject format and structured body bullets at both commit time and merge time. Rejects all non-conforming commits including WIP:, Merge , fixup!, squash!. The only bypass is --no-verify. See ADR-26024.
Reviewer ResponsibilityPeer reviewers assess the semantic quality of the PR description (which becomes the squash commit body): are the bullets accurate, complete, and meaningful?Structural validation is automated. Humans focus on whether the message correctly reflects the change’s intent — a judgment that cannot be automated.
Enforce Squash-and-MergeSet the repository merge policy to “Squash and Merge” only. Configure the Git host to populate the squash commit message from the PR description template.Each PR becomes exactly one commit on trunk. The squash commit body contains the structured changelog bullets. Reverts are atomic (one commit = one revert).

Guidance: Checkpoint Saves vs. Standard Commit Types

New engineers must understand the difference to avoid misusing checkpoint saves.

ScenarioRecommended ApproachRationale
Saving work at the end of the day or switching machines.git commit --no-verify -m "WIP: current state"Work is incomplete, not ready for review. --no-verify explicitly bypasses validation. Must be squash-merged later.
Completing a logical unit of work (e.g., finishing the utility function signature, adding a new test).test: add unit test for X service or refactor: extract Z function from module AThe change is coherent, stable, and useful, even if the overall feature is unfinished. It improves branch history readability before the final squash.
Fixing a minor bug discovered while working on a feature.fix: prevent divide by zero in function YThis is a small, atomic fix that is technically correct and may be valuable on its own. It can be squashed later or kept as a separate atomic commit.

Atomic Commits: The Guiding Principle

The goal of every feature branch’s final history is logical atomicity. An Atomic Commit is a self-contained, complete, and logically isolated unit of work.

Rules of Atomicity

  • Rule 1: One Goal Per Commit. A commit must achieve one objective only (e.g., refactor logic, add unit tests, fix a bug).

  • Rule 2: The Squashed Commit Must Be Stable. The final squashed commit on trunk MUST be buildable and pass all tests. Intermediate feature branch commits are transient development artifacts — they are eliminated by Squash-and-Merge and need not individually pass CI.

  • Rule 3: Cohesive Narrative. The sequence of commits in a Pull Request (PR) should tell a clear story for reviewers. However, only the squashed commit matters for trunk history and changelog extraction — internal feature branch history is invisible after Squash-and-Merge.

The “Commit by Logic” Workflow

Granular atomic commits on feature branches are recommended for PR reviewability but are not required. Only the squashed commit on trunk matters for history and changelog extraction — internal feature branch history is invisible after Squash-and-Merge.

To create clean commits on your feature branch, DO NOT use git add . indiscriminately.

StepActionToolGoal
Stage SelectivelyUse the patch utility to stage only changes relevant to a single logical task.git add -pGroup changes by function and purpose, not by file or timing.
Review and CommitReview the staged changes (git diff --staged). If they represent one complete, stable, logical step, commit them with a semantic type.git commit -m "feat: implement X interface"Ensure the commit is atomic.

Merge Strategy: Atomic Change Submission

All changes merged into mainline branches (main, develop, etc.) MUST be integrated as a single, atomic, logical unit. This is a fundamental engineering discipline used in scaled production environments (Google, Meta, Microsoft) to ensure reviewability, revertability, and traceability.

One Change, One Commit: The BigTech Standard

In professional code review systems:

  • A Pull Request (PR) or Change List (CL) represents one logical modification to the system.

  • Intermediate development steps (e.g., test scaffolding, incremental refactors) are development artifacts, not production history.

  • Preserving them in main adds noise without operational value and dilutes accountability.

Debugging Without “git bisect” Granularity

git bisect is formally deprioritized as a debugging tool in this workflow. In ML-heavy systems, regressions are more frequently caused by data/config drift or high-level logic errors, not single-line syntax bugs. Unit tests and evaluation suites catch logic bugs faster than history-walking.

Maintaining perfect stacked-diff history for bisect represents “Process Debt” — especially without specialized wrappers like Graphite or Sapling.

Primary debugging tools:

  • Unit and evaluation tests (catch regressions at the logic level),

  • Structured logging (with trace IDs),

  • Metrics and alerting (e.g., latency, error rates),

  • Atomic revert (if a change breaks, revert the entire squashed commit — not part of it).

If a change is too large to debug or revert safely, it violates atomicity and should have been split into separate PRs before submission.

If a large feature needs decomposition, split it into separate PRs (each squash-merged independently) rather than stacking dependent diffs within a single branch.

Enforcement

GuardrailImplementation
Reviewer GateReviewers reject PRs that are not logically atomic (e.g., mix refactor + feature) o... [truncated]