Final StretchOrdered learning track

Case Study: Small Team to Platform Team

Learn Git In Action - Part 119

Case study evolusi workflow Git dari small team sederhana menuju platform team dengan protected trunk, release train, policy-as-code, dan operational playbooks.

10 min read1826 words
PrevNext
Lesson 119126 lesson track104–126 Final Stretch
#git#version-control#workflow#platform-engineering+2 more

Case Study: Small Team to Platform Team

Tujuan part ini bukan memberi template workflow yang bisa ditempel mentah-mentah. Tujuannya adalah memperlihatkan bagaimana workflow Git berevolusi ketika organisasi berubah: dari 5 engineer yang bisa saling teriak di meja yang sama, menjadi platform team yang memelihara kontrak lintas service, shared libraries, deployment pipeline, security gates, release branches, dan audit evidence.

Small team biasanya tidak gagal karena kurang aturan. Mereka gagal ketika aturan lama tetap dipakai setelah sistem menjadi lebih besar. Workflow Git yang terasa ringan di awal bisa berubah menjadi sumber incident saat jumlah branch, reviewer, environment, service, dan release line meningkat.

Kita akan membangun case study fiktif tapi realistis: tim produk kecil bernama Atlas berkembang menjadi platform team yang melayani banyak stream engineering.


1. Initial State: Small Team yang Masih Cepat

Kondisi awal:

DimensiKondisi
Engineer5 orang
Repository1 repository aplikasi utama
DeploymentManual/semi-manual, 1-2 kali per minggu
Branch modelmain + feature branch pendek
Reviewinformal, kadang pair review
CIunit test + build
Releasedeploy dari main terbaru
Governancetrust-based

Workflow mereka kira-kira seperti ini:

# developer

git checkout main
git pull --ff-only
git checkout -b feature/add-case-priority
# work
git add -p
git commit -m "Add case priority field"
git push -u origin feature/add-case-priority
# open PR

Merge policy awal:

small PR + one review + CI green -> squash merge to main

Ini bukan workflow buruk. Untuk small team, ini cukup sehat karena:

  1. branch lifetime pendek,
  2. komunikasi murah,
  3. reviewer tahu konteks domain,
  4. jumlah release line kecil,
  5. rollback biasanya revert sederhana,
  6. CI masih cepat.

Masalah belum terlihat karena sistem belum punya tekanan organisasi.


2. Growth Pressure: Gejala Awal Workflow Mulai Retak

Setelah 12 bulan, Atlas berubah.

DimensiKondisi Baru
Engineer35 engineer lintas product squads
Repositoryaplikasi utama + shared packages + deployment config
Deploymentbeberapa kali per hari
Branch modelfeature branches, release branches, emergency hotfix branches
Reviewwajib, tapi reviewer sering tidak punya konteks penuh
CIunit, integration, contract, security, container build
Releasestaged rollout, release candidate, hotfix
Governanceaudit, security review, platform ownership

Gejala yang muncul:

GejalaPenyebab Git/Workflow
PR kecil mulai lama mergereviewer routing tidak jelas
Main sering merahchecks tidak merepresentasikan merge result terbaru
Hotfix sulit dilacaksquash merge menghilangkan commit identity granular
Release notes tidak akuratrelease boundary tidak eksplisit
Backport sering salahpatch dependency tidak dianalisis
Conflict makin seringbranch lifetime naik
Owner file tidak jelasrepository tumbuh tanpa ownership boundary
Tag release kadang dibuat ulangrelease identity belum immutable
CI checkout kadang gagal changelogshallow clone terlalu agresif
Engineer takut rebase/force pushprotokol rewrite tidak jelas

Masalah utamanya bukan Git command. Masalahnya adalah workflow lama tidak punya invariant cukup kuat untuk skala baru.


3. Failure Timeline: Incident yang Memaksa Perubahan

Satu incident membuat tim berhenti dan mendesain ulang workflow.

Timeline:

Root causes:

  1. PR CI tidak menguji merge result terbaru terhadap main.
  2. Tidak ada merge queue.
  3. Squash merge membuat mapping PR -> original commits hilang di Git history.
  4. Hotfix tidak punya manifest.
  5. Tidak ada rule bahwa release tag immutable.
  6. Tidak ada owner untuk deployment config.
  7. Tidak ada documented playbook untuk broken main.

Yang menarik: semua orang merasa sudah “mengikuti proses”. Artinya prosesnya sendiri yang lemah.


4. Target State: Platform Git Workflow

Tim memutuskan workflow baru tidak boleh hanya “lebih ketat”. Ia harus memberi safety tanpa memperlambat small changes.

Target invariants:

InvariantMakna
main harus selalu releasableminimal: build, unit, integration critical, migration gate green
release identity immutabletag release tidak boleh dipindah
production artifact traceableartifact punya commit SHA, tag, build id, digest
sensitive paths punya ownerperubahan auth, infra, migration, CI, security wajib reviewer owner
hotfix punya forward-porttidak boleh fix hanya hidup di release branch
branch lifetime dibatasisemakin lama branch, semakin tinggi integration risk
forced update dibatasiprivate feature branch boleh dengan lease, protected branch tidak boleh
PR diff harus stabilbase drift harus terlihat
deployment bukan branch mergepromotion memakai artifact identity, bukan merge antar environment branch

Target topology:


5. Phase 1 Migration: Normalize Local Developer Workflow

Sebelum menambah server-side governance, mereka memperbaiki local workflow.

5.1 Standard Sync

Mereka melarang git pull default yang ambiguity-nya tergantung config personal.

Standard:

git fetch --prune origin
git switch main
git merge --ff-only origin/main

Atau alias:

[alias]
  sync-main = !git fetch --prune origin && git switch main && git merge --ff-only origin/main

Kenapa --ff-only?

Karena update main lokal dari remote tidak perlu membuat merge commit lokal. Jika tidak bisa fast-forward, berarti local main sudah menyimpang dan harus diinvestigasi.

5.2 Feature Branch Start Rule

Rule:

git sync-main
git switch -c feature/<ticket>-<slug>

Branch tidak boleh dibuat dari branch arbitrary kecuali memang stacked change.

Validation script:

base=$(git merge-base HEAD origin/main)
main=$(git rev-parse origin/main)
if [ "$base" != "$main" ]; then
  echo "Branch did not start from current origin/main or is now stale. Rebase or explain stack parent."
  exit 1
fi

5.3 Force Push Rule

Allowed:

git push --force-with-lease origin feature/my-branch

Forbidden:

git push --force origin main
git push --force origin release/1.8

Policy:

Branch classForce update
private feature branchallowed with lease
shared feature branchdiscouraged; require coordination
mainforbidden
release/*forbidden
tags v*forbidden

6. Phase 2 Migration: Branch Protection and Merge Queue

Branch protection is introduced for main:

Required:

  1. PR required before merge.
  2. At least 1 approval.
  3. CODEOWNER review for sensitive paths.
  4. Required status checks.
  5. Stale approval dismissed after new commits.
  6. Linear history or merge queue policy chosen explicitly.
  7. No force push.
  8. No delete.
  9. Signed release tags required separately.

The most important change: merge queue.

Why?

Without queue:

With queue:

The queue changes the invariant from:

PR branch is green

to:

candidate integration result is green

That is a different guarantee.


7. Phase 3 Migration: Ownership and Review Routing

The team creates ownership zones.

Example CODEOWNERS:

# Security and authorization
/services/authz/**        @platform/security
/services/identity/**     @platform/security

# Database migrations
/db/migrations/**         @platform/data

# CI/CD and deployment
.github/workflows/**      @platform/devex
/deploy/**                @platform/release

# Shared API contracts
/contracts/**             @platform/api-governance

# Frontend platform shell
/apps/web-shell/**        @platform/frontend

But CODEOWNERS alone is not enough. They add a rule:

CODEOWNERS routes review; it does not replace domain judgment.

Review class matrix:

Change classRequired review
UI-only local componentowning squad
authn/authzsecurity/platform owner
migrationdata owner + service owner
deployment configrelease/platform owner
shared API contractproducer + consumer owner
CI workflowdevex/platform owner
dependency upgradeservice owner + security if critical

8. Phase 4 Migration: Release Identity and Evidence

Before migration, release was “whatever was on main when deployment happened”.

Target release model:

Required release metadata:

{
  "version": "1.12.0",
  "gitCommit": "9f3a...",
  "gitTag": "v1.12.0",
  "buildId": "ci-78231",
  "artifactDigest": "sha256:...",
  "builtAt": "2026-07-07T09:14:00Z",
  "dirty": false
}

Release verification script:

set -euo pipefail

version="$1"
tag="v$version"

git fetch --tags --force-with-lease origin

git rev-parse --verify "$tag^{tag}" >/dev/null
commit=$(git rev-parse "$tag^{commit}")

echo "release tag: $tag"
echo "release commit: $commit"

git verify-tag "$tag"

git merge-base --is-ancestor "$commit" origin/main || {
  echo "release commit is not reachable from origin/main"
  exit 1
}

Important decision:

Patch release tags are never moved. If a release tag is wrong and publicly consumed, create a new patch version.


9. Phase 5 Migration: Hotfix and Backport Discipline

Old hotfix:

create branch from prod-ish state -> patch -> deploy -> forget

New hotfix:

Hotfix manifest:

incident: INC-2026-0712
issue: AUTHZ-8432
originBranch: release/1.12
originCommit: 3d8b71a
patchCommits:
  - a91d2fc
releaseTag: v1.12.3
forwardPort:
  branch: main
  commit: c7e1a02
verification:
  - unit-authz
  - contract-permission-v2
  - migration-not-required
approvers:
  - security-owner
  - release-manager

The invariant:

Every hotfix must either already exist in main or have an explicit forward-port record.

10. Phase 6 Migration: CI Profiles by Git Context

They stop using one checkout setting for everything.

Job typeCheckout requirement
fast PR unit testshallow may be okay
PR affected-project detectionneeds merge base
release notesneeds tags and previous release range
release buildfull enough to verify tag and commit
security auditneeds relevant history range
monorepo impact analysisneeds changed paths and base commit

Example CI preflight:

set -euo pipefail

required_ref="origin/main"

git rev-parse --verify HEAD^{commit}
git rev-parse --verify "$required_ref^{commit}"

git merge-base HEAD "$required_ref" >/dev/null || {
  echo "Cannot compute merge base. Fetch more history."
  exit 1
}

if git rev-parse --is-shallow-repository | grep -q true; then
  echo "Repository is shallow; release/changelog jobs must unshallow."
fi

11. Phase 7 Migration: From Personal Knowledge to Handbook

The platform team writes Git standards as executable guidance.

Handbook sections:

  1. branch naming,
  2. commit message rules,
  3. PR size guidance,
  4. merge method policy,
  5. force push protocol,
  6. release branch lifecycle,
  7. tag immutability,
  8. hotfix/backport playbook,
  9. broken main incident response,
  10. secret leak response,
  11. checkout profiles for CI,
  12. large file policy,
  13. submodule policy,
  14. exception process.

Good handbook entry shape:

## Rule: Release tags are immutable

### Why
Release tags bind source identity to artifact identity. Moving them breaks reproducibility and downstream trust.

### Required
- Use annotated tag for public release.
- Sign tag for production release.
- Protect `v*` tags on remote.
- Never force-push replacement release tag.

### Exception
If tag was created locally and never pushed/consumed, delete and recreate locally before publishing.

### Incident response
If public tag is wrong, create a new patch version and publish correction note.

12. Migration Order: Jangan Big Bang

Bad rollout:

enable all protections + merge queue + CODEOWNERS + signing + new branching model in one week

Better rollout:

PhaseChangeReason
1document current workflowexpose implicit rules
2add local aliases/scriptslow-friction safety
3protect main minimallystop destructive mutation
4define ownershipimprove review routing
5require critical CI checksmake green meaningful
6introduce merge queueremove integration race
7standardize release tagsfix release identity
8automate hotfix/backport manifestreduce production drift
9add policy-as-codemake invariants executable

Migration should reduce surprise. If engineers feel Git became arbitrary bureaucracy, adoption will fail.


13. Metrics After Migration

Useful metrics:

MetricWhy it matters
median branch ageintegration risk
PR queue timeprocess friction
main red durationtrunk health
revert ratequality/risk signal
hotfix forward-port lagbranch drift risk
release tag correctionsrelease identity failure
forced update countrewrite risk
stale PR counthidden integration debt
CODEOWNER review latencyownership bottleneck
CI flake ratetrust in merge gate

Avoid weaponized metrics:

Bad metric useWhy bad
commits per engineerrewards noise
lines changedpunishes simplification
PR count as productivityignores complexity
reviewer comments as qualityincentivizes performative review

Git metrics are system signals, not individual performance truth.


14. Final Workflow Contract

The platform team eventually lands on this contract:

1. `main` is protected and expected to stay releasable.
2. All product changes enter through PR and merge queue.
3. Feature branches should be short-lived.
4. Private feature branch rewrites are allowed only with `--force-with-lease`.
5. Shared/protected branch rewrites are forbidden.
6. Sensitive paths require owner review.
7. Production releases are tagged with immutable annotated/signed tags.
8. Artifacts carry commit/tag/build metadata.
9. Hotfixes require forward-port or explicit proof already present in main.
10. Environment promotion uses artifact identity, not environment branch merges.

This is not the only valid workflow. It is a coherent one because every rule maps to a failure mode.


15. Engineering Takeaways

  1. Git workflow must evolve with organizational scale.
  2. The best workflow is not the one with the most rules; it is the one with the clearest invariants.
  3. Branch protection without CI correctness gives false confidence.
  4. Merge queue solves a real integration race, not a political preference.
  5. CODEOWNERS routes attention; it does not create ownership culture by itself.
  6. Release tags are source identity boundaries and should be treated as immutable.
  7. Hotfix without forward-port creates future incident debt.
  8. Workflow standards should be executable where possible and documented where judgment is required.
  9. A platform team should reduce cognitive load, not centralize every decision.
  10. Every Git policy should answer: what failure mode does this prevent?

16. Practical Checklist

Before changing your team workflow, answer:

[ ] Which refs are protected?
[ ] Which branches are allowed to be rewritten?
[ ] What does “green PR” actually mean?
[ ] Are PRs tested against branch head or merge result?
[ ] How is release identity represented?
[ ] Are release tags immutable?
[ ] Can a deployed artifact report its Git commit?
[ ] Who owns sensitive paths?
[ ] What is the hotfix forward-port rule?
[ ] What is the broken-main playbook?
[ ] What checkout depth does each CI job need?
[ ] Which Git operations have safe aliases?
[ ] Which rules are enforced server-side vs documented as judgment?

References

  • Git documentation: gitworkflows, git merge, git push, git tag, git rev-parse, git log.
  • Pro Git: branching workflows, distributed workflows, signing, internals.
  • GitHub Docs: branch protection, CODEOWNERS, merge queue, protected tags/rulesets.
  • Trunk Based Development: short-lived branches, branch by abstraction, release branches.
Lesson Recap

You just completed lesson 119 in final stretch. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.