Open source menjaga gerbang ketat karena penyumbangnya orang asing. Tim kecil — dua sampai lima orang satu repository — kebalikannya: rekan saling kenal, kecepatan mata uang utama, dan setiap upacara dibayar dari waktu anggota. Bahayanya datang dari dua arah: tanpa aturan, main rusak dan saling menimpa; dengan aturan tim besar, pekerjaan tenggelam di bawah birokrasi. Workflow tim kecil berdiri di tengah dengan lima aturan secukupnya — cukup untuk kualitas, cukup ringan untuk tidak terasa.

Lima Aturan Secukupnya #

  1. main diproteksi. Push langsung ditolak platform; semua perubahan lewat pull request — mekanismenya dibahas di Protected Branches .
  2. PR wajib, review satu orang cukup. Satu pasang mata kedua menangkap sebagian besar galat; dua approvals hanya memperlambat.
  3. Branch pendek. Hidup kurang dari tiga hari; lebih dari itu, pecah pekerjaannya — utang konflik tumbuh per hari, pelajarannya di Menjaga Branch Tetap Sinkron .
  4. Squash merge. Satu PR menjadi satu commit di main — riwayat linear yang enak dibaca dan dibisect.
  5. Rilis menyusul saat dibutuhkan. Mengikuti pola Workflow Release begitu ada pengguna nyata — bukan sejak hari pertama.

Pilihan squash di aturan keempat punya pesaing yang sah:

Strategi mergeBentuk riwayatCocok saat
merge --no-ffgelembung per fiturfitur besar yang ditelusuri sebagai satu unit
squashsatu commit per PR, linearPR kecil harian; main tetap pendek
rebase mergecommit asli berjejer luruskontributor berdisiplin commit logis
fast-forwardtanpa jejak mergebranch privat sendirian

Squash menang karena PR tim kecil memang kecil: commit “perbaiki typo” dan “lagi” tidak memperkaya main. Perilaku tombolnya di platform dijelaskan di dokumentasi merge pull request .

Sesi Nyata: Dua PR Paralel, Satu Berkas Bersama #

Panggungnya model shared: bare pusat, dua meja:

bash
mkdir tim-kecil && cd tim-kecil
mkdir budi && cd budi
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf '# Toko Budi\n\n- [Produk](produk.md)\n' > beranda.md
printf '# Produk\n' > produk.md
git add .
git commit -m "Buat kerangka toko"
git init --bare ../toko-bersama.git
git remote add origin ../toko-bersama.git
git push -u origin main
cd ..
git clone toko-bersama.git siti
git -C siti remote set-url origin ../toko-bersama.git
git -C siti config user.name "Siti Rahma"
git -C siti config user.email "[email protected]"

Output yang Dihasilkan (git commit dan git push):

text
[main (root-commit) 4ab526a] Buat kerangka toko
 2 files changed, 4 insertions(+)
 create mode 100644 beranda.md
 create mode 100644 produk.md
text
To ../toko-bersama.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

Dua fitur dikerjakan paralel: Budi menambah footer, Siti menambah kontak. Keduanya menautkan fiturnya di navigasi beranda.md — berkas bersama, tabrakan kecil menunggu di tikungan:

bash
# meja Budi
cd budi
git switch -c fitur/footer
printf '# Footer\n' > footer.md
printf '# Toko Budi\n\n- [Produk](produk.md)\n- [Footer](footer.md)\n' > beranda.md
git add -A
git commit -m "Tambah footer"
git push -u origin fitur/footer

Output yang Dihasilkan (dua perintah terakhir):

text
[fitur/footer e1cb20c] Tambah footer
 2 files changed, 2 insertions(+)
 create mode 100644 footer.md
text
To ../toko-bersama.git
 * [new branch]      fitur/footer -> fitur/footer
branch 'fitur/footer' set up to track 'origin/fitur/footer'.
bash
# meja Siti
cd ../siti
git switch -c fitur/kontak
printf '# Kontak\n' > kontak.md
printf '# Toko Budi\n\n- [Produk](produk.md)\n- [Kontak](kontak.md)\n' > beranda.md
git add -A
git commit -m "Tambah kontak"
git push -u origin fitur/kontak

Di platform, dua pull request terbuka bersebelahan. PR Budi ditimbang lebih dulu: review satu orang, disetujui. Integrator menjalankan squash merge dari terminal:

bash
cd ../budi
git switch main
git fetch origin
git merge --squash origin/fitur/footer
git commit -m "Tambah footer (#1)"
git push origin main
git push origin --delete fitur/footer

Output yang Dihasilkan (git merge –squash, git commit, dan penghapusan remote branch):

text
Updating 4ab526a..e1cb20c
Fast-forward
Squash commit -- not updating HEAD
 beranda.md | 1 +
 footer.md  | 1 +
 2 files changed, 2 insertions(+)
 create mode 100644 footer.md
text
[main 5081a1c] Tambah footer (#1)
 2 files changed, 2 insertions(+)
 create mode 100644 footer.md
text
To ../toko-bersama.git
 - [deleted]         fitur/footer

Perhatikan Squash commit -- not updating HEAD: perubahan hanya dipentaskan ke staging area tanpa menggerakkan main — commit dibuat terpisah dengan pesan yang kita tulis sendiri, lengkap nomor PR. Nomor (#1) bukan hiasan: git log --grep="#1" dan tautan otomatis platform saling menemukan.

Saat PR Siti tiba giliran, main sudah berisi tautan footer di beranda.md — dan branch Siti juga menambah tautannya di baris yang sama:

bash
git merge --squash origin/fitur/kontak

Output yang Dihasilkan:

text
Auto-merging beranda.md
CONFLICT (content): Merge conflict in beranda.md
Squash commit -- not updating HEAD
Automatic merge failed; fix conflicts and then commit the result.

Konfliknya kecil dan lokasinya terbaca langsung dari isi beranda.md:

text
# Toko Budi

- [Produk](produk.md)
<<<<<<< HEAD
- [Footer](footer.md)
=======
- [Kontak](kontak.md)
>>>>>>> origin/fitur/kontak

Dua sisi sama-sama benar — keduanya menambah satu tautan. Penyelesaiannya menyimpan keduanya, prosedur lengkapnya dibahas di Menyelesaikan Merge Conflicts :

bash
printf '# Toko Budi\n\n- [Produk](produk.md)\n- [Footer](footer.md)\n- [Kontak](kontak.md)\n' > beranda.md
git add beranda.md
git commit -m "Tambah kontak (#2)"
git push origin main
git push origin --delete fitur/kontak
git log --oneline --decorate

Output yang Dihasilkan (git commit dan git log):

text
[main e2cef12] Tambah kontak (#2)
 2 files changed, 2 insertions(+)
 create mode 100644 kontak.md
text
e2cef12 (HEAD -> main, origin/main) Tambah kontak (#2)
5081a1c Tambah footer (#1)
4ab526a Buat kerangka toko

Hash di mesin pembaca berbeda, bentuknya tidak: tiga baris lurus tanpa satu pun gelembung merge — inilah wajah riwayat squash. Siti menutup siklus dengan menarik hasil integrasi:

bash
cd ../siti
git switch main
git pull --ff-only

Output yang Dihasilkan (perintah terakhir):

text
From ../toko-bersama
   4ab526a..e2cef12  main       -> origin/main
Updating 4ab526a..e2cef12
Fast-forward
 beranda.md | 2 ++
 footer.md | 1 +
 kontak.md | 1 +
 3 files changed, 4 insertions(+)
 create mode 100644 footer.md
 create mode 100644 kontak.md

Satu kejutan menunggu saat merapikan branch lokal. git branch -d Budi menolak menghapus fitur/footer:

text
error: the branch 'fitur/footer' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D fitur/footer'
hint: Disable this message with "git config advice.forceDeleteBranch false"

Penolakan itu jujur: commit Tambah footer memang tidak pernah ada di main — squash hanya menyalin isinya ke commit baru. Setelah terverifikasi isinya sudah tergabung di main, penghapusan dilanjutkan dengan git branch -D fitur/footer.

Komunikasi yang Murah #

Konflik beranda.md di atas bisa dicegah dengan tiga puluh detik percakapan. Ritual tim kecil yang bekerja bukan rapat, melainkan satu putaran tiap pagi: siapa mengerjakan apa. Jawaban “aku ubah navigasi beranda” membuat Siti memilih menunggu atau menyelaraskan — bukan menabrak. Tabrakan yang diketahui lebih dulu dipecahkan dengan urutan merge atau pemecahan berkas; tabrakan yang mengejutkan baru terasa saat konflik menyala. Pola yang sama berlaku untuk wilayah: satu orang memegang api/, satu memegang web/.

Right-Sizing: Jangan Membeli Jas untuk Anak #

Aturan paling sering dilanggar tim kecil bukan kekurangan aturan, melainkan kelebihannya — biasanya GitFlow lengkap yang disalin dari blog tim ratusan orang. Tabel ini memetakan perangkat ke ukurannya:

Ukuran timPerangkat yang cukupYang jadi berlebihan
1 orangmain-only, commit berkala, tagPR, review, develop
2-5 orangmain terproteksi, PR, review 1 orang, squashGitFlow penuh: develop, release/*, dua jalur merge
6-15 orangCI wajib di PR, CODEOWNERS per wilayahreview 5 orang untuk semua PR
lintas zona / rilis terjadwalprotokol eksplisit ala GitFlowtrunk-based tanpa disiplin tes

Tim kecil ini satu repository berisi satu produk. Berikutnya skala berubah arah: satu tim, banyak produk, satu repository — monorepo. Disiplinnya bergeser dari branch ke direktori: commit yang menyebut wilayahnya, riwayat yang terbaca per aplikasi, dan pemilik berkas yang ditulis dalam berkas kecil bernama CODEOWNERS.