Kenapa Gue Mulai TDD Setelah 5 Tahun Nolak
Gue nolak TDD 5 tahun dengan alasan "gak butuh test, gue jago." Terus production meledak karena refactor 2 minggu lalu ngubah urutan operasi. Test yang ditulis setelah code bukan test, itu formalitas.
0xNN · · 9 min read
Gue nolak TDD 5 tahun. Selama 5 tahun itu, argumen gue selalu sama: "Buat apa nulis test dulu sebelum code? Itu ngurangin kecepatan. Gue jago nulis code, gue bisa langsung production. Test itu buat dev yang gak yakin sama kodenya."
Terus 2023. Production meledak. Fitur payment refund yang gue tulis jalan 6 bulan tanpa masalah, tiba-tiba nge-charge user dua kali. Gue debug 8 jam, akhirnya nemu: refactor 2 minggu lalu gak sengaja ngubah urutan operasi. Test yang ada gak nangkep karena test ditulis setelah code, dan test cuma verify "code jalan," bukan "code bener."
Saat itu gue sadar: test yang ditulis setelah code bukan test. Itu formalitas. Test yang bener harus ditulis sebelum code, karena fungsinya bukan verify code-nya jalan - tapi nge-spec code-nya bener. Itu TDD. Itu yang gue nolak selama 5 tahun.
Ini pengalaman gue pindah ke TDD. Bukan tutorial "apa itu TDD." Tapi kenapa 5 tahun nolak akhirnya masuk akal, dan apa yang berubah di cara gue ngoding.
---
TDD Itu Bukan Soal "Test Dulu"
Banyak yang salah paham. Mereka pikir TDD = "tulis test function sebelum nulis function." Itu superficial.
TDD = Test-Driven Development. Driven = yang nyetir. Test yang nyetir design code lo. Bukan "test dulu, code belakangan." Tapi "test menentukan code seperti apa yang harus lo tulis."
Red → Tulis test yang fail (karena code belum ada)
Green → Tulis code minimal biar test pass
Refactor → Bersihin code tanpa ngubah behavior (test tetep pass)
Tiga fase: Red-Green-Refactor. Simple. Tapi filosofinya dalam.
Red - lo nulis test yang fail. Ini ngatur: "saya mau bikin function calculateRefund yang nerima orderId, balikin amount, throw error kalau order gak ada." Test-nya:
describe('calculateRefund', () => {
it('throws when order does not exist', async () => {
await expect(calculateRefund('order-999')).rejects.toThrow('Order not found')
})
it('returns refund amount for paid order', async () => {
const amount = await calculateRefund('order-123')
expect(amount).toBe(50000)
})
})
Test fail karena calculateRefund belum ada. Good. Sekarang lo tau persis: lo harus bikin function ini, dengan kontrak ini.
Green - lo tulis code paling minimal biar test pass. Bukan code yang cantik. Bukan code yang scalable. Minimal.
async function calculateRefund(orderId) {
const order = await db.findOrder(orderId)
if (!order) throw new Error('Order not found')
return 50000 // hardcoded dulu
}
Test pass. Refactor nanti.
Refactor - sekarang lo bersihin. Hardcoded 50000 jadi order.amount. Tambahin logic. Test tetep pass sebagai safety net.
async function calculateRefund(orderId) {
const order = await db.findOrder(orderId)
if (!order) throw new Error('Order not found')
if (order.status !== 'paid') throw new Error('Order not paid')
return order.amount
}
Setiap langkah, test jadi verification. Lo refactor tanpa takut ngubah behavior karena test bakal teriak kalau salah.
---
Kenapa Gue 5 Tahun Nolak
1. "TDD bikin lambat."
Ini argumen paling sering. Dan setengah bener. Di awal, nulis test = waktu tambahan 30-50%. Tapi:
• Saat refactor: 0 bug, karena test jadi safety net
• Saat onboarding dev baru: mereka baca test, langsung paham kontrak
• Saat incident production: bug fix + test baru dalam 1 jam, bukan 8 jam
Total: lambat di awal, cepet di akhir. Yang gue gak sadar 5 tahun: lo bayar waktu di muka, dan investasi itu compound.
2. "Gue jago nulis code, gak butuh test."
Ini ego. Setelah incident double-charge 2023, gue sadar: gue bukan jago. Gue cuma beruntung. 6 bulan tanpa bug = 6 bulan untung. Bukan bukti gue jago.
Statistik gue 5 tahun terakhir:
• 70% bug production bisa dicegah pake test yang nge-cover edge case
• 90% waktu debugging habis di "code yang gue pikir bener ternyata salah"
• 100% refactor yang bikin regression = code tanpa test
3. "Test bikin code gak fleksibel, tiap ubah code harus ubah test."
Ini betul - tapi justru itu gunanya. Kalau tiap ubah code harus ubah test, berarti lo sering ngubah behavior. Behavior yang sering berubah = unstable design. Test yang "ribet diubah" itu sinyal: design lo kurang stabil, perlu di-think lagi.
Lo gak harus nulis test buat semua. Tapi bagian kritis (payment, auth, data integrity) harus ada test. Bagian eksperimental boleh gak. Pilih yang kritis.
---
Apa yang Berubah Setelah TDD
1. Lo mikir "kontrak" sebelum "implementasi."
Gue dulu langsung nulis function. Sekarang gue nulis test dulu, dan test itu memaksa gue mikir: "input apa? output apa? error case apa? edge case apa?" Implementasi jadi jelas, cuma ngejalanin kontrak.
2. Edge case jadi explicit.
Dulu gue nulis happy path, lalu "ah nanti aja edge case." Production meledak di edge case. Sekarang edge case jadi test case:
it('throws when amount is zero', () => {})
it('throws when amount is negative', () => {})
it('handles very large amounts', () => {})
it('handles null order field gracefully', () => {})
Tiap edge case gue tulis, gue sadar "iya nih, ini bisa terjadi." Test = dokumentasi behavior yang executable.
3. Refactor jadi aman.
Ini benefit paling besarnya buat gue. Dulu refactor = takut. "Kalau gue ubah ini, apa yang bakal break?" Gue gak tau. Sekarang: test pass = aman. Test fail = tahu persis apa yang break.
Refactor dari 1000 lines jadi 500 lines, dengan test pass, itu feeling yang gak bisa dijelasin. Kayak lepas beban.
4. Onboarding cepet.
Dev baru masuk tim, langsung baca test. Mereka tau: calculateRefund throw kalau order gak ada, return amount kalau paid, dst. Mereka bisa langsung refactor atau tambah fitur tanpa takut. Test = dokumentasi yang selalu up-to-date (kalau gak, test fail).
---
TDD Bukan Silver Bullet - Kapan Gak Pakai
1. Prototyping / eksplorasi.
Lo belum tau bentuk akhirnya. Nulis test buat code yang bakal diubah 10 kali = waste. Tulis code dulu, stabil, baru test.
2. UI / styling.
Test "button ini berwarna teal" = low value, high maintenance. Visual regression test ada, tapi ROI rendah buat tim kecil. Manual QA masih valid buat UI.
3. Code yang benar-benar throwaway.
Script one-off, internal tooling yang dipake 3 orang. Test = overkill. Tulis code, jalan, buang.
4. Saat lo nge-hack production fix.
Server down, user complain. Fix dulu, test belakangan. TDD bukan dogma. Tapi setelah fix, wajib nulis test regression biar gak terulang.
---
Quick Comparison: Test Setelah Code vs TDD
| | Test Setelah Code | TDD |
|---|---|---|
| Kapan test ditulis | Setelah code jadi | Sebelum code |
| Fokus test | "Code jalan" | "Code bener" (kontrak) |
| Coverage edge case | Sering ketinggalan | Dipikir dari awal |
| Refactor safety | Lemah (test verify happy path doang) | Kuat (test verify kontrak) |
| Waktu awal | Cepet | Lambat 30-50% |
| Waktu total (debugging + regression) | Lambat | Lebih cepet long-term |
| Mindset | "Code dulu, baru verify" | "Spec dulu, baru implementasi" |
---
Yang Sering Disalahpahami
"TDD itu dogma, tiap function harus punya test." - Salah. Test yang penting: business logic, edge case, integration kritis. Getter/setter, simple CRUD, gak butuh test.
"TDD = 100% coverage." - Bukan. 100% coverage bukan tujuan. Tujuan: behavior penting ter-test. 80% coverage dengan test bener lebih baik dari 100% coverage dengan test yang cuma verify "code jalan."
"Test harus perfect dari awal." - Gak. Test berevolusi sama code. Mulai dari happy path, tambah edge case belakangan. Yang penting: test itu ada sebelum code, jadi dia nge-spec, bukan verify.
"TDD cuma buat dev yang gak yakin." - Ego. TDD buat dev yang udah kenal kegagalan. Setelah 1 incident production yang bisa dicegah dengan 1 test, ego lo bakal turun.
---
Penutup yang Jujur
TDD bukan tentang "cepet" atau "lambat." TDD tentang mindset shift. Dari "gue nulis code, gue yakin bener" jadi "gue nulis spec, code harus match spec." Spec-nya executable. Code-nya verifiable. Refactor-nya aman.
Gue 5 tahun nolak karena ego. Gue pikir jago = gak butuh safety net. 6 bulan tanpa bug bukan bukti gue jago, itu cuma probability. Pas finally probability ngelawan gue, 8 jam debugging yang seharusnya bisa dicegah 30 menit nulis test.
Skill yang menurut gue wajib buat dev senior bukan cuma "bisa nulis test." Tapi:
1. Baca test, langsung paham kontrak function
2. Nulis test yang nge-cover edge case, bukan happy path doang
3. Refactor dengan test sebagai safety net
4. Tau kapan skip test (prototype, UI, throwaway)
5. Paham trade-off: lambat di awal, cepet long-term
Kalau lo belum pernah coba TDD, coba 1 fitur kecil. Tulis test-nya dulu, biarkan fail, tulis code-nya biar pass. Saat test pertama green tanpa lo ngubah test, lo bakal ngerasa "oh, ternyata." Moment itu yang ngubah mindset lo.
Kalau lo nolak 5 tahun kayak gue dulu, gue gak maksa. Tapi pas lo kena incident production yang bisa dicegah 1 test, inget artikel ini.
---
Sumber
• Martin Fowler: Test Driven Development
• Kent Beck: Test-Driven Development: By Example