Pre-commit hook cepat dengan Husky, lint-staged, dan Biome membantu mencegah commit yang membawa error format atau lint tanpa membuat proses commit terasa berat. Kuncinya bukan menjalankan seluruh lint dan test setiap kali commit, melainkan membatasi pemeriksaan hanya pada file yang benar-benar berubah.
Untuk proyek JavaScript/TypeScript, kombinasi Husky sebagai pengelola Git hook, lint-staged untuk memilih file yang sedang di-stage, dan Biome untuk formatting serta linting adalah pendekatan yang praktis. Workflow ini cocok untuk tim kecil sampai menengah yang ingin menjaga kualitas kode lokal, tetapi tetap menjadikan CI sebagai lapisan verifikasi akhir.
Mengapa pre-commit hook lokal masih berguna
Hook lokal berguna untuk menangkap masalah yang murah diperiksa sebelum kode masuk ke repository: format yang tidak konsisten, import yang bermasalah, atau pelanggaran aturan lint yang jelas. Tanpa hook lokal, masalah seperti ini baru terlihat setelah push atau saat pipeline CI berjalan, sehingga feedback menjadi lebih lambat.
Namun, banyak tim jatuh ke jebakan yang sama: setiap commit menjalankan seluruh lint, type-check, dan test untuk seluruh proyek. Secara teknis ini bisa benar, tetapi dari sisi developer experience sering buruk. Commit menjadi lambat, developer mulai frustrasi, lalu hook dibypass atau dimatikan.
Prinsip yang lebih sehat adalah:
- Pre-commit hanya untuk pemeriksaan cepat dan deterministik.
- CI menjalankan verifikasi penuh untuk seluruh project.
- Pre-push opsional jika tim butuh pemeriksaan lebih berat, tetapi tetap hati-hati agar tidak mengganggu alur kerja.
Arsitektur alur commit yang direkomendasikan
Alur sederhananya seperti ini:
- Developer mengubah beberapa file.
- Developer menjalankan
git addpada file yang ingin di-commit. - Saat
git commitdijalankan, Git memanggil hookpre-commit. Huskymengeksekusi skrip hook.lint-stagedmengambil daftar file yang sedang di-stage.Biomedijalankan hanya pada file yang relevan.- Jika ada perbaikan otomatis, perubahan diperbarui di area stage.
- Jika ada error yang tidak bisa diperbaiki otomatis, commit dibatalkan.
Mengapa pendekatan ini cepat? Karena biaya pemeriksaan bergantung pada jumlah file yang berubah, bukan ukuran seluruh repository. Untuk sebagian besar commit kecil, waktu eksekusi akan jauh lebih masuk akal dibanding lint penuh.
Catatan: Hook lokal membantu menjaga kualitas sejak awal, tetapi tidak boleh menjadi satu-satunya garis pertahanan. Developer bisa melewati hook dengan
--no-verify, dan environment lokal tidak selalu identik dengan CI.
Setup Husky, lint-staged, dan Biome
1. Instal dependensi
Di proyek Node.js, pasang ketiga paket berikut sebagai dev dependency:
npm install -D husky lint-staged @biomejs/biomeJika Anda memakai pnpm atau yarn, sesuaikan perintahnya:
pnpm add -D husky lint-staged @biomejs/biomeyarn add -D husky lint-staged @biomejs/biome2. Aktifkan Husky
Inisialisasi Husky agar Git hook bisa dikelola dari repository:
npx husky initPerintah ini biasanya membuat direktori .husky dan sebuah file hook awal. Setelah itu, pastikan repository memang menyimpan folder .husky agar setiap anggota tim mendapatkan konfigurasi hook yang sama.
3. Siapkan konfigurasi Biome
Biome dapat dipakai untuk formatting dan linting dalam satu tool. Contoh konfigurasi sederhana:
{
"$schema": "https://biomejs.dev/schemas/latest/schema.json",
"formatter": {
"enabled": true,
"indentStyle": "space"
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double"
}
}
}Simpan sebagai biome.json atau nama file konfigurasi yang didukung Biome di proyek Anda. Jangan membuat aturan terlalu agresif di awal jika tim belum siap, karena hook yang terlalu sering gagal akan cepat dianggap mengganggu.
4. Tambahkan lint-staged
Konfigurasi lint-staged bisa ditaruh di package.json atau file terpisah. Untuk setup kecil, menaruhnya di package.json sering cukup praktis:
{
"scripts": {
"prepare": "husky",
"check": "biome check .",
"check:write": "biome check --write ."
},
"lint-staged": {
"*.{js,jsx,ts,tsx,json}": [
"biome check --write"
]
}
}Konfigurasi di atas berarti saat commit, file JavaScript, TypeScript, JSX, TSX, dan JSON yang sedang di-stage akan diperiksa oleh Biome, dan jika memungkinkan akan diperbaiki otomatis dengan --write.
5. Hubungkan dengan hook pre-commit
Edit file .husky/pre-commit agar menjalankan lint-staged:
npx lint-stagedJika Anda ingin isi file hook yang lebih eksplisit, contoh minimalnya seperti ini:
#!/usr/bin/env sh
npx lint-stagedDengan begitu, setiap git commit akan memicu pemeriksaan hanya untuk file yang sudah di-stage.
Contoh struktur konfigurasi yang rapi
Untuk proyek yang mulai berkembang, memisahkan konfigurasi sering lebih mudah dirawat. Misalnya:
.
├─ .husky/
│ └─ pre-commit
├─ biome.json
├─ package.json
└─ .lintstagedrc.jsonContoh isi .lintstagedrc.json:
{
"*.{js,jsx,ts,tsx}": [
"biome check --write"
],
"*.{json,css,md}": [
"biome check --write"
]
}Pemisahan ini berguna jika aturan file makin banyak atau Anda ingin membedakan perlakuan antara kode aplikasi dan file pendukung lain.
Mengapa lint-staged lebih baik daripada lint seluruh project saat commit
Masalah utama saat semua lint atau test dijalankan penuh pada setiap commit adalah latensi. Bahkan jika tooling cepat, ukuran codebase yang besar akan membuat waktu commit membengkak. Ini berdampak langsung pada ritme kerja:
- Developer menunda commit kecil karena proses terlalu lama.
- Commit menjadi terlalu besar karena orang ingin “sekalian saja”.
- Hook mulai dibypass dengan
git commit --no-verify. - Kepercayaan pada workflow menurun.
lint-staged mengubah model ini dengan membatasi pemeriksaan ke file yang sudah di-stage. Pendekatan ini masuk akal untuk formatter dan linter yang bekerja pada level file. Untuk pemeriksaan yang sifatnya global, seperti build penuh, integration test, atau type-check lintas paket pada monorepo, pre-commit biasanya bukan tempat terbaik.
Apa yang sebaiknya dijalankan di pre-commit, dan apa yang tidak
Cocok untuk pre-commit
- Formatter otomatis.
- Lint per file.
- Pemeriksaan ringan yang deterministik dan cepat.
Kurang cocok untuk pre-commit
- Test end-to-end.
- Build production penuh.
- Test unit untuk seluruh repository.
- Type-check penuh project besar jika waktunya signifikan.
Jika tim tetap ingin validasi tambahan sebelum push, pertimbangkan pre-push untuk pemeriksaan yang sedikit lebih berat. Tetapi jangan memindahkan semua beban ke sana tanpa evaluasi, karena hasil akhirnya tetap bisa sama: workflow terasa lambat dan dihindari developer.
Integrasi dengan CI sebagai verifikasi akhir
Hook lokal bukan pengganti CI. CI tetap perlu menjalankan pemeriksaan penuh untuk memastikan hasil konsisten dan tidak bergantung pada kondisi mesin developer. Minimal, CI sebaiknya menjalankan:
- Lint penuh repository.
- Type-check penuh jika proyek menggunakan TypeScript.
- Test yang relevan dengan tingkat risiko proyek.
- Build atau validasi artefak bila diperlukan.
Contoh perintah yang umum dipakai di CI:
npm run check
npm run test
npm run buildDi sisi package.json, Anda bisa menyiapkan pemisahan yang jelas antara command lokal cepat dan command CI penuh:
{
"scripts": {
"check": "biome check .",
"test": "vitest run",
"build": "tsc -p tsconfig.json"
}
}Intinya, pre-commit mengoptimalkan feedback cepat, sedangkan CI menegakkan kebenaran akhir.
Strategi menjaga hook tetap cepat
1. Batasi ke file staged saja
Ini fondasi utama. Jangan panggil command yang diam-diam memproses seluruh project jika tujuan Anda adalah commit cepat.
2. Prioritaskan formatter dan lint ringan
Biome cocok karena satu tool dapat menangani formatting dan linting. Mengurangi jumlah proses CLI yang dijalankan biasanya membantu menekan overhead.
3. Hindari rantai command yang terlalu panjang
Misalnya, jangan menggabungkan formatter, lint penuh, type-check penuh, test unit penuh, dan build dalam satu hook pre-commit. Jika commit butuh terlalu banyak waktu, developer akan cenderung menganggap workflow ini sebagai hambatan.
4. Mulai dari aturan yang realistis
Jika repository lama belum konsisten, jangan langsung memaksa aturan yang menghasilkan ratusan error di setiap perubahan kecil. Rapikan baseline terlebih dahulu atau lakukan adopsi bertahap.
5. Pantau file pattern
Pattern yang terlalu luas bisa membuat file yang tidak relevan ikut diproses. Pattern yang terlalu sempit membuat file tertentu lolos tanpa pemeriksaan. Evaluasi daftar ekstensi secara berkala.
Jebakan umum dan cara mengatasinya
Hook terasa lambat
Penyebab paling umum:
- Command yang dijalankan memproses seluruh project.
- Terlalu banyak tool berjalan terpisah.
- Ada test atau build berat di pre-commit.
Solusi praktis:
- Pastikan command menerima daftar file dari
lint-staged. - Fokuskan pre-commit pada formatting dan lint ringan.
- Pindahkan pemeriksaan berat ke CI atau pre-push.
Developer membypass hook dengan --no-verify
Ini tidak bisa dicegah sepenuhnya di sisi lokal. Karena itu, CI tetap wajib menjadi penjaga akhir. Jika bypass terlalu sering terjadi, biasanya akar masalahnya bukan pada disiplin tim, tetapi pada hook yang terlalu lambat atau terlalu sering gagal untuk alasan yang tidak penting.
Perbedaan environment lokal vs CI
Masalah ini sering muncul ketika versi Node.js, dependency lockfile, atau variabel environment tidak sinkron. Praktik yang membantu:
- Gunakan lockfile dan commit ke repository.
- Samakan versi runtime semaksimal mungkin.
- Pastikan command CI tidak bergantung pada tool global.
- Dokumentasikan setup lokal dengan jelas.
Perubahan otomatis tidak ikut ter-commit
Tool seperti formatter dapat mengubah file saat hook berjalan. Dalam workflow lint-staged, perubahan pada file staged biasanya dikelola agar hasil akhirnya tetap masuk commit. Jika perilaku ini tidak sesuai harapan, cek kembali konfigurasi command dan pastikan tool dijalankan melalui lint-staged, bukan skrip manual yang memodifikasi file di luar alur stage.
Rule lint terlalu agresif
Jika aturan terlalu ketat tanpa konteks, tim akan lebih banyak melawan tool daripada memperbaiki kualitas kode. Bedakan aturan yang benar-benar melindungi kualitas dari aturan gaya yang lebih baik ditangani formatter otomatis.
Contoh workflow yang seimbang untuk tim kecil/menengah
Untuk banyak tim kecil dan menengah, pendekatan berikut sudah cukup sehat:
- Pre-commit: Biome pada file staged melalui
lint-staged. - CI pull request: lint penuh, type-check, unit test.
- CI branch utama: build penuh dan validasi tambahan bila perlu.
Keuntungan model ini:
- Feedback lokal cepat.
- Kualitas dasar dijaga sebelum kode dipush.
- CI tetap memastikan integritas seluruh project.
- Beban tooling tidak berlebihan untuk developer.
Pendekatan ini sangat cocok jika:
- Repository belum terlalu kompleks.
- Tim ingin standardisasi style dan lint tanpa menambah friksi besar.
- Sebagian besar commit bersifat kecil dan sering.
Untuk monorepo besar atau sistem dengan validasi lintas paket yang kompleks, workflow ini tetap berguna, tetapi biasanya perlu dilengkapi strategi tambahan seperti task graph, cache build, atau pembagian pipeline yang lebih cermat.
Rekomendasi akhir
Jika tujuan Anda adalah pre-commit hook cepat dengan Husky, lint-staged, dan Biome, jangan mulai dari automasi yang terlalu ambisius. Mulailah dari satu target yang jelas: cegah masalah format dan lint dasar hanya pada file yang berubah.
Kombinasi ini bekerja baik karena masing-masing alat punya peran yang jelas:
- Husky menghubungkan workflow ke Git hook.
- lint-staged memastikan hanya file staged yang diproses.
- Biome menjalankan formatting dan linting dengan konfigurasi yang relatif sederhana.
Hasil terbaik biasanya datang dari kompromi yang sehat: hook lokal dibuat cepat dan bisa dipercaya, sementara CI tetap memegang verifikasi penuh. Jika pre-commit Anda terasa ringan, konsisten, dan jarang memberi kejutan, kemungkinan besar tim akan benar-benar menggunakannya, bukan mencari cara untuk menghindarinya.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!