ADR-0001: Three-step Git flow (feature → develop → main)

  • Status: Accepted
  • Date: 2026-05-30
  • Scope: core (all repos, every project)

Context

A project here typically spans several independent git repositories (e.g. the
Expenses ecosystem: expenses backend, expenses-web, expenses-admin, plus
this dev-handbook). Work needs a predictable path from “in progress” to “released to production”,
with a clear integration point and a production branch whose history is only
ever well-tested merges. Committing straight to a shared branch loses the
ability to gate integration and makes releases hard to reason about.

Decision

Every repo follows the same three-step branching flow:

feature/*  →  develop  →  main
  • feature/* — all active development; branched off develop before
    any file is edited. Commit freely here.
  • develop — integration branch; only receives --no-ff merges from tested
    feature branches, after the user explicitly approves.
  • main — production; only receives --no-ff merges from a release/*
    branch at release time. Tags live on main.

Releases go through a release/vX.Y.Z branch cut from develop: bump the
version (pom.xml for a Java backend; package.json + lock for a frontend — each
repo’s version file is declared in the workspace handbook.json manifest), merge
--no-ff into main and tag, then merge --no-ff back into develop, delete
the release branch, and push develop main --tags together. The /release
skill automates this. Bump is MINOR if any feat: commit exists since the
last tag, otherwise PATCH.

Rules that follow (mechanics):

  • Never commit directly to develop or main.
  • Each repo has an independent version history — versions need not match.
  • A breaking wire-contract change releases clients-first. Independent versions
    mean nothing sequences a cross-repo change except the plan. When a server change
    alters a shape a deployed client already reads, every client ships before the
    server
    , and that ordering is recorded in the ADR that owns the change — not left
    in a branch name or a chat message. Server-first is safe only for a purely
    additive change. The client that cannot be rolled back with a redeploy (a mobile
    build in review) is the one that sets the pace.
  • Co-author footer on every AI-assisted commit, naming the assisting model:
    Co-Authored-By: Claude <model name> <noreply@anthropic.com>

The behavioral agreements layered on this flow — approval before any
commit/merge/release, branching both repos for cross-repo work,
recap-before-phase — live in 0003-collaboration-working-agreements (kept
there, not restated here).

Consequences

  • main is always a clean trail of release: vX.Y.Z merges; rollback = check
    out the previous tag.
  • develop is the single “what’s next” truth the user gates on.
  • Slightly more ceremony per change (branch + two merges at release), accepted
    in exchange for a legible production history.
  • 0002-i18n-both-locales — another standing rule enforced at merge time
  • Automation: the /release skill performs the release-branch dance.