Ada satu kelas bug yang jauh lebih mahal dari error biasa: bug yang tidak kelihatan seperti bug. Saya kehilangan sekitar tiga jam ke salah satunya di sebuah proyek klien, dan yang bikin sakit bukan penyebabnya, tapi kenyataan bahwa selama tiga jam itu saya sebenarnya sedang men-debug sesuatu yang tidak pernah sampai ke server.
Gejalanya: semuanya terlihat normal
Waktu itu saya sedang mengerjakan navigasi glossary di Sanity Studio yang tertanam di aplikasi Next.js. Saya ubah komponen, commit, push. Build jalan di Railway. Saya buka Studio di produksi untuk mengecek hasilnya.
Tidak ada perubahan.
Reaksi pertama saya jelas: pasti ada yang salah di kodenya. Jadi saya kembali, periksa struktur komponennya, perbaiki sedikit, push lagi. Masih tidak ada perubahan. Saya ulangi siklus ini berkali-kali, tiap kali dengan asumsi yang makin rumit tentang kenapa navigasinya tidak muncul.
Yang sama sekali tidak saya curigai: situs yang saya lihat itu bukan hasil build terbaru saya. Build-nya gagal, tapi versi lama yang masih sehat tetap dilayani. Dari sisi browser, semuanya tampak hidup dan normal. Tidak ada halaman error, tidak ada 500, tidak ada tanda apa pun bahwa deploy terakhir tidak pernah mendarat.
Errornya ada, cuma tidak saya lihat
Waktu akhirnya saya buka log build, errornya sudah menunggu di sana sejak awal:
Module not found: Can't resolve '@sanity/ui'Gagal di tahap Turbopack. Build rusak sejak pukul 13:21 dan baru saya sadari menjelang pukul 17:00.
Ini pelajaran pertamanya, dan mungkin yang paling berharga: kalau perubahanmu tidak muncul di produksi, verifikasi dulu bahwa build-nya benar-benar berhasil sebelum menyalahkan kodemu. Saya melewati langkah itu karena situsnya terlihat baik-baik saja, padahal justru itu yang menyesatkan.
Akar masalahnya: dependency transitif yang selama ini menumpang
Beberapa komponen kustom di Studio saya meng-import dari @sanity/ui:
import { Card, Stack, Text } from "@sanity/ui";Masalahnya, @sanity/ui tidak pernah ada di package.json saya. Paket itu ikut terinstall karena sanity sendiri memakainya, jadi dia hanya dependency transitif, bukan dependency langsung.
Dan di sinilah perbedaan dev dan produksi menggigit:
- Mode dev santai soal resolusi. Modul yang cuma numpang di
node_moduleslewat paket lain tetap ketemu, jadi import saya jalan mulus di lokal. - Build produksi Turbopack tidak begitu. Dia menuntut paket yang kamu import dideklarasikan langsung. Kalau tidak ada di
package.json, dia menolak me-resolve.
Jadi kode yang sama persis: hijau di lokal, mati di produksi. Yang bikin makin runyam, ini sebenarnya luka yang saya buat sendiri. Sebelumnya saya pernah menghapus dependency itu dari package.json waktu mencoba memperbaiki masalah loading Studio yang lain. Diagnosis saya waktu itu salah, dan biayanya baru ditagih berminggu-minggu kemudian dalam bentuk build yang gagal diam-diam.
Perbaikannya
Deklarasikan paketnya secara langsung, dengan range versi yang sama persis dengan yang dipakai sanity:
{
"dependencies": {
"sanity": "^5.21.0",
"@sanity/ui": "^3.1.14"
}
}Menyamakan range itu penting. Kalau kamu asal tulis versi lain, package manager bisa memasang dua salinan @sanity/ui sekaligus, dan Studio akan pecah dengan cara yang jauh lebih membingungkan daripada sekadar gagal build. Setelah install, pastikan hanya ada satu instance:
pnpm why @sanity/uiKalau outputnya menunjukkan satu versi yang di-dedupe, kamu aman.
Diagnostik untuk deploy yang nyangkut
Karena kasus ini bisa terulang dalam bentuk lain, saya sekarang punya kebiasaan cek cepat kalau curiga sedang melihat versi basi. Bandingkan waktu file statis yang dilayani dengan waktu commit terakhir:
curl -sI https://situs-anda.com/_next/static/chunks/main.js | grep -i last-modified
git log -1 --format=%cdKalau Last-Modified jauh lebih tua dari commit terakhirmu, kamu sedang melihat build lama. Berhenti men-debug kode, buka log build.
Yang saya bawa pulang
- Deploy yang gagal tidak selalu menurunkan situs. Kalau versi lama tetap dilayani, kegagalannya jadi tak terlihat, dan itu jauh lebih berbahaya daripada error yang jujur.
- Setiap paket yang kamu
importharus ada dipackage.json. Numpang lewat dependency transitif bisa jalan di dev dan mati di build produksi. - Kalau menambahkan paket yang juga dipakai paket lain, samakan range versinya supaya tidak ada duplikat instance.
- Sebelum menyalahkan kodemu, buktikan dulu bahwa kode itu benar-benar terpasang.
