SellVia Docs — menu
DocsInfrastructure & DevOpsGit Repository Strategy

Git Repository Strategy

Infrastructure & DevOps/Git Repository Strategy.md
backendUpdated Aug 24, 2026

Git Repository Strategy

Purpose

How the codebase is organized in version control, and how changes flow through branches — resolves the open question left in CI/CD Pipeline ("monorepo vs. two separate repos — not yet decided").

⚠️ Update (2026-08-24): Reversed — Two Separate Repositories, Not a Monorepo. The Decision section below (originally "Monorepo," 2026-08-04) is superseded. Frontend and backend now live in two separate repositories, alongside this documentation repository as a third — three repos total. The monorepo's original reasoning (atomic cross-cutting PRs, fewer moving parts for a solo founder) is kept below for history, but is no longer the operating model. Everything under "Decision," "Structure," "CI Runs Only What Changed," and the 2026-08-04 "Service-Scoped Feature Branch Naming" update is superseded by this reversal — read down to the Current Decision section for what's actually in effect.

Current Decision: Two Separate Repositories

Three repositories, not one:

  • sellvia-frontend — Next.js + shadcn/ui + Tailwind
  • sellvia-backend — FastAPI + SQLAlchemy
  • sellviadocs — this repository; documentation only, published via Vercel, both app repos' own instructions files point their dev agents at it as the central source of truth (see this repo's root instructions.md)

(Repo names above are illustrative — match whatever the actual GitHub repo names end up being.)

Each app repo is independently owned, independently versioned, and independently deployable. The trade-off the original monorepo decision was explicitly optimizing against — a cross-cutting change (new endpoint + the frontend code calling it) now needs two PRs across two repos instead of one — is accepted. See "Coordinating Cross-Cutting Changes" below for how that's handled without a shared repo.

Structure

Each repo is a normal single-purpose project root, not a subdirectory of something larger:

sellvia-frontend/
  app/ (or src/)      — Next.js application code
  .github/workflows/  — this repo's own CI (lint, type-check, tests, deploy)

sellvia-backend/
  app/ (or src/)      — FastAPI application code
  alembic/            — migrations
  .github/workflows/  — this repo's own CI (lint, tests, deploy)

There is no packages/shared-types — see the next section for how a shared contract works without one.

Shared Contract Between Repos (replaces packages/shared-types)

With no monorepo package to hold a hand-shared type definition, the contract flows one direction: backend is the source of truth for the API shape, frontend generates against it.

  • Backend (FastAPI) already generates an OpenAPI spec for free (per UX/AI Agent & Machine Readability).
  • Backend publishes that spec at a stable URL (e.g. /openapi.json) in every environment, including Staging.
  • Frontend generates its typed API client from that spec (e.g. openapi-typescript, orval, or equivalent) as a build/dev step, rather than importing a hand-shared package.
  • When a breaking backend shape change ships, the frontend's next client regeneration is what surfaces the mismatch (a type error), not a shared-package version bump. This makes the OpenAPI spec itself the thing both sides need to agree on — see API/API-CONTRACT-SHEET.md for the practical day-to-day mechanism (a hand-maintained endpoint registry) that exists precisely because there's no compiler-enforced shared package doing this automatically across two repos.

Coordinating Cross-Cutting Changes

For a change that spans both apps (new endpoint + the frontend code that calls it):

  1. Backend PR ships first — additive/non-breaking where possible (new endpoint, new optional field) so it can merge and deploy independently without the frontend change being ready yet.
  2. Frontend PR follows, consuming the now-live (or Staging-deployed) endpoint.
  3. API/API-CONTRACT-SHEET.md's plannedreadylive status column is the coordination point across the two repos — frontend can start building against a planned row's agreed shape (mocked) before backend ships it, per that doc's existing "avoiding conflicts in practice" guidance.
  4. Reference the same tracking issue/ticket number in both PRs' descriptions so the split is traceable after the fact, since there's no single PR to look at anymore.

CI Per Repo

Each repo runs its own independent CI/CD pipeline top to bottom (lint, type-check, tests, deploy) on every push/PR to itself — no path filtering needed, since each repo is already single-purpose. This replaces the monorepo's path-scoped-jobs approach with the simpler default: a frontend change literally cannot trigger backend CI, because it's a different repo. See Infrastructure & DevOps/CI CD Pipeline.md's 2026-08-24 update for the corresponding pipeline-level note.

Branching Strategy

Applies independently within each repo:

  • main — production. Protected: no direct pushes, requires a passing PR with required checks green.
  • develop — staging. Same protection, slightly lower bar (still requires checks, doesn't require the manual-approval step main does per CI/CD Pipeline).
  • feature/* — short-lived, one per feature/fix, branched from develop, PR'd back into develop. Deleted after merge — no long-lived feature branches accumulating drift.

Service-prefixed branch names (feature/frontend/..., feature/backend/..., per the 2026-08-04 update below) are now moot — a branch's repo already tells you which service it touches. Plain feature/<thing> naming is fine within each repo.

Commit Convention

Conventional Commits (feat:, fix:, chore:, refactor:, etc.) — low-cost to adopt, enables auto-generated changelogs later, and makes git log actually scannable once each repo has real history. Applies independently in each repo.

Tagging

Every Production deploy gets a semantic version tag (v0.3.1) on mainnow independently per repo, since frontend and backend release on their own cadence. A frontend tag and a backend tag no longer correspond to "the same commit," so incident/disaster-recovery correlation (Infrastructure & DevOps/Disaster Recovery) needs both repos' tag histories to reconstruct "what did the system look like right before this incident," not one shared tag.

Secrets Never Enter Git

Direct continuation of Security/Secrets Management.env files gitignored in both sellvia-frontend and sellvia-backend independently, each with its own .env.example (see API/API-CONTRACT-SHEET.md §5.5).

Revisit When

If the coordination overhead of two repos (two PRs for cross-cutting changes, keeping the OpenAPI-generated contract in sync, two release cadences to track) turns out to cost more than the independent-ownership/independent-release benefit is worth, merging back into a monorepo is the reversal path — same as any other architectural call in this doc set, not a permanent commitment either direction.

Open Questions

  • None blocking — this closes the (re-)open question from CI/CD Pipeline; revisit only under the "coordination overhead" condition above.

Update (2026-08-04): Service-Scoped Feature Branch Naming — Superseded 2026-08-24

Superseded by the 2026-08-24 reversal above — kept for history only. This update originally established: every feature branch named with its service prefix (feature/frontend/checkout-page-redesign, feature/backend/payout-batching-job) so it's immediately clear what a branch touches without opening it, with a repo-spanning change instead named without a prefix (feature/campaign-commission-locking). That convention existed to disambiguate branches within one shared repo; now that frontend and backend are separate repos, the repo itself disambiguates and the prefix convention is no longer needed (see "Branching Strategy" above).

Explicitly not adopted (still true): long-lived standing branches per service. That was considered and rejected under the monorepo model and remains rejected now — long-lived branches increase risk (drift, larger/harder-to-review merges) rather than reducing it, regardless of repo structure. Feature branches stay short-lived in both repos.

Update (2026-08-04): Original Monorepo Decision — Superseded 2026-08-24

Kept for history. One repository, not two. Given the team size (solo founder, possibly small team later) and that frontend (Next.js) and backend (FastAPI) changes often need to move together — a new API endpoint and the frontend code calling it, a schema change and the frontend types that reflect it — a monorepo keeps those changes atomic and reviewable in one PR instead of coordinating two separate PRs across two repos. Same reasoning as the earlier monolith-vs-microservices call: fewer moving parts for the current team size, not a permanent architectural commitment. (The structure at the time: sellvia/apps/frontend, sellvia/apps/backend, sellvia/packages/shared-types, one .github/workflows/ with path-filtered jobs.) This reasoning held until 2026-08-24 — see the top of this document for the current decision and why it changed.