Skip to content

Branching & Deployment Strategy — mpac

Current State

WhatStatus
Branch modelmain + ad-hoc feature branches
CIPR to main, path-based filtering (pgw, sdks, infra, obs)
CD stagingAuto-deploy on push to main (pgw only)
CD productionManual workflow_dispatch, 2 approvers
Environmentsdev (manual), staging (auto), production (manual)
Release trackingSingle tag v2026.3.1, no formal process

Strategy: Trunk-Based Development with Staged Promotion

Single trunk (main), short-lived feature branches, and a dev → staging → production promotion flow where each environment is gated:

  • dev: auto-deploy on merge — continuous integration feedback
  • staging: manual promote — QA gets a stable snapshot
  • production: tag and deploy — auditable, traceable releases

1. Branch Model

main (trunk)

  ├── feature/PSP-123-add-psp-domain        ← short-lived, from main
  ├── feature/PSP-124-sftp-fetcher           ← short-lived, from main
  ├── fix/PGW-99-hmac-edge-case              ← short-lived, from main

  └── (tags: v2026.3.1, v2026.4.0, ...)     ← cut from main for production

Branch Types

BranchNamingLifetimeMerges to
mainpermanent
Featurefeature/<TICKET>-<slug>days (< 1 week ideal)→ main via PR
Fixfix/<TICKET>-<slug>hours–days→ main via PR
Hotfixhotfix/<TICKET>-<slug>hours→ main via PR, then tag

Rules

  • main is always deployable — CI must pass before merge
  • No long-lived feature branches — use feature flags if a feature takes > 1 week
  • PRs require review — 1 reviewer minimum
  • No direct pushes to main — enforce via GitHub branch protection
  • No release branches — tags cut directly from main (simpler than maintaining release branches)

2. Environment Mapping

┌──────────────┐      ┌──────────────┐      ┌───────────────┐
│     dev       │─────►│   staging     │─────►│  production    │
│               │      │               │      │                │
│ Auto on main  │      │ Manual trigger │      │ Manual trigger  │
│ Every merge   │      │ Specific SHA  │      │ Release tag     │
│ Fast feedback │      │ QA validates  │      │ 2 approvers     │
└──────────────┘      └──────────────┘      └───────────────┘
EnvironmentTriggerWhat gets deployedApprovalPurpose
devAuto on push to mainmain HEADNone (CI passed)Continuous integration, catch infra/migration issues early
stagingManual workflow_dispatchSpecific commit SHA from mainNoneQA validation on a stable, frozen snapshot
productionManual workflow_dispatchRelease tag v*2 approversLive traffic

Why this order

PrincipleHow it's achieved
Every merge is tested in a real environmentDev auto-deploys
QA tests a stable snapshot, not a moving targetStaging is manually triggered with a specific SHA
Production runs exactly what was validatedTag points to the same SHA that passed staging
Audit trailCalVer tags + GitHub environment approvals

What changes from today

CurrentProposedWhy
Staging auto-deploys on push to mainDev auto-deploys on push to mainFrees staging for intentional QA
Dev is manual and rarely usedDev is automatic and always currentEvery merge gets real-environment feedback
Production uses git SHAProduction uses release tagsAuditable, rollback-friendly
No formal release processTag from main when staging passes QATraceable releases for payment compliance

3. Promotion Flow

Day-to-day: Feature Development

bash
# Developer works on feature
git checkout -b feature/PSP-123-add-psp-domain
# ... code, test locally ...
git push origin feature/PSP-123-add-psp-domain
# Open PR → CI runs → 1 reviewer approves → squash merge to main
# → dev auto-deploys ← happens automatically

Promoting to Staging

When enough features have landed on main and been verified on dev:

bash
# 1. Identify the commit SHA validated on dev
git log --oneline main   # e.g., abc1234

# 2. Trigger staging deploy via GitHub Actions workflow_dispatch
#    Input: ref = abc1234

Staging now holds a frozen snapshot. QA tests against it. New merges to main continue deploying to dev without disturbing staging.

Cutting a Release for Production

When QA signs off on staging:

bash
# 1. Tag the exact SHA that passed staging
git tag v2026.4.0 abc1234
git push origin v2026.4.0

# 2. Trigger production deploy via workflow_dispatch (input: tag v2026.4.0)
# 3. 2 approvers approve in GitHub environment

Hotfix on Production

bash
# 1. Fix on main via normal PR flow
git checkout -b hotfix/PGW-100-fix-settlement-parse
# ... fix ...
# PR → CI → merge to main → auto-deploys to dev

# 2. Verify fix on dev

# 3. Deploy the fix commit to staging, verify
# workflow_dispatch: ref = <fix-commit-sha>

# 4. Tag and deploy to production
git tag v2026.4.1 <fix-commit-sha>
git push origin v2026.4.1
# workflow_dispatch: tag = v2026.4.1, 2 approvers

No cherry-picks, no release branches. Hotfix follows the same path as any change: main → dev → staging → production.

Versioning Scheme

v<YEAR>.<MONTH>.<PATCH>
v2026.3.0   ← March 2026 initial release
v2026.3.1   ← patch/hotfix
v2026.4.0   ← April release

CalVer (calendar versioning) fits because:

  • Payment systems are compliance-sensitive — auditors want to know when code shipped
  • You already started with v2026.3.1
  • Simpler than SemVer for a platform (not a library)

4. CI/CD Pipeline Details

On Pull Request (any branch → main)

PR opened/updated

  ├─ [pgw changed?]     → Go tests (80% coverage), lint, race detection
  ├─ [sdk-js changed?]  → pnpm test, type-check, lint
  ├─ [sdk-go changed?]  → Go tests (80% coverage)
  ├─ [sdk-py changed?]  → pytest
  ├─ [infra changed?]   → cfn-lint, CloudFormation validate
  └─ [obs changed?]     → Config validation (Prometheus, Loki, Tempo, Alloy)

No changes needed — path-based filtering already works well.

On Merge to main → Dev (auto)

Push to main

  ├─ Build Docker image → push to ECR (mpac-pgw-dev)
  ├─ Run migrations → RDS (dev)
  ├─ Deploy to ECS (mpac-pgw-dev)
  ├─ Wait for stability
  └─ Post deploy status to commit

Workflow: cd-dev.yml, trigger: push: branches: [main]

Manual Promote to Staging

workflow_dispatch (input: ref/SHA)

  ├─ Build Docker image from <ref> → push to ECR (mpac-pgw-staging)
  ├─ Run migrations → RDS (staging)
  ├─ Deploy to ECS (mpac-pgw-staging)
  ├─ Wait for stability
  ├─ Run smoke tests against staging
  └─ Notify mpac-infra via repository dispatch

Workflow: cd-staging.yml, trigger: workflow_dispatch with ref input

On Release Tag → Production

workflow_dispatch (input: tag)

  ├─ [2 approvers approve in GitHub environment]
  ├─ Verify image exists in ECR (reuse staging-built image if same SHA)
  ├─ Tag image: production-YYYYMMDDHHMMSS
  ├─ Run migrations → RDS (production)
  ├─ Deploy to ECS (mpac-pgw-production)
  ├─ Wait for stability
  ├─ Run smoke tests against production
  └─ Notify + post deployment summary

Workflow: cd-production.yml, trigger: workflow_dispatch with tag input


5. Commit SHA Traceability

The same SHA flows through all three environments:

main merge (SHA abc1234)


  dev deploys abc1234          ← auto


  staging deploys abc1234      ← manual, input: ref=abc1234


  tag v2026.4.0 → abc1234     ← git tag
  production deploys v2026.4.0 ← manual, 2 approvers

Guarantee: production runs exactly the code that was tested on dev and validated on staging. No merge, no cherry-pick, no drift.


6. Submodule Coordination

Dependency Order

mpac-infra (infrastructure first)

mpac-pgw (application)

mpac-obs (observability — can lag behind)

Coordination Rules

ScenarioProcess
PGW code change onlyNormal PR → main → dev → staging → production
Infra change required for PGWDeploy infra first, then PGW. Use repository-dispatch (already wired)
New migration + infra changeInfra first (new RDS params, etc.) → PGW migration → PGW deploy
Obs config updateIndependent — deploy anytime

Submodule Ref Updates

When tagging a production release:

bash
# In mpac root, update submodule refs to the release tag
cd mpac-pgw && git checkout v2026.4.0
cd mpac-infra && git checkout v2026.4.0
cd ..
git add mpac-pgw mpac-infra
git commit -m "release: bump submodules to v2026.4.0"
git tag v2026.4.0

This gives you a single commit in the parent repo that records the exact combination deployed to production.


7. Branch Protection Rules (GitHub)

main

Require pull request (1 reviewer minimum)
Require status checks to pass (CI jobs)
Require branch to be up to date before merging
No direct push
No force push
Require linear history (squash merge)

Tags v*

Restrict tag creation to repository admins / release managers

8. Rollback Strategy

EnvironmentRollback MethodTime
devRevert commit on main → auto-redeployMinutes
stagingRe-trigger workflow with previous SHAMinutes
productionRe-trigger workflow with previous tag (e.g., v2026.3.1)Minutes, needs 2 approvers

ECR keeps all tagged images. Rolling back = deploying a previous tag/SHA. No rebuild needed.

Database Rollback

Migrations are forward-only (psql -f). For rollback:

  • Phase 1 migrations (additive — new columns, tables) don't need rollback
  • Phase 2 migrations (destructive — drop columns) should only run 24h+ after Phase 1
  • If a migration breaks: fix forward with a new migration, don't manually undo

9. Action Items

Immediate (low effort, high value)

#ActionWhere
1Enable branch protection on mainGitHub repo settings (each submodule)
2Enforce squash mergesGitHub repo settings
3Create cd-dev.yml — auto-deploy main to devmpac-pgw workflows
4Change cd-staging.yml — manual trigger with SHA inputmpac-pgw workflows
5Change cd-production.yml — accept release tagsmpac-pgw workflows

Near-term (before next release)

#ActionWhere
6Add post-deploy smoke tests to staging and production CDworkflows
7Tag first formal release v2026.4.0 from maingit
8Document the release checklist in RELEASING.mdrepo root

Later (as team grows)

#ActionWhere
9Image promotion: reuse staging-built image for production (skip rebuild)cd-production.yml
10Deployment notifications to Slack/Teamsworkflows
11Canary/blue-green deployment for productionECS config + infra

10. Visual: Full Flow

Developer                    GitHub                         AWS
─────────                    ──────                         ───

git checkout -b feature/x
  ... code ...
git push
                         ┌─ PR created ──────┐
                         │  CI runs          │
                         │  Review (1 person)│
                         └─ Merge to main ───┘

                                 ▼ (auto)
                         ┌─ Dev CD ──────────┐
                         │  Build image      │──► ECR (dev)
                         │  Run migrations   │──► RDS (dev)
                         │  Deploy ECS       │──► ECS (dev)
                         └───────────────────┘

                          Verified on dev

                                 ▼ (manual: workflow_dispatch, ref=SHA)
                         ┌─ Staging CD ──────┐
                         │  Build image      │──► ECR (staging)
                         │  Run migrations   │──► RDS (staging)
                         │  Deploy ECS       │──► ECS (staging)
                         │  Smoke tests      │
                         └───────────────────┘

                          QA validates

                         ┌─ Tag release ─────┐
                         │  git tag v2026.4.0│
                         └───────────────────┘

                                 ▼ (manual: workflow_dispatch, tag=v2026.4.0)
                         ┌─ Production CD ───┐
                         │  2 approvers      │
                         │  Promote image    │──► ECR (production)
                         │  Run migrations   │──► RDS (production)
                         │  Deploy ECS       │──► ECS (production)
                         │  Smoke tests      │
                         └───────────────────┘

MPAC — MP-Solution Advanced Cloud Service