Ada kendaraan yang sedang parkir, mesin mati, tapi sistem membaca kecepatannya 66 km/jam. Satelitnya ramai dan datanya terus masuk. Setelah lumayan lama pusing, ternyata penyebabnya cuma satu karakter di kode parser. Satu. Karakter. Hahaha.
Rewind: Januari 2025 #
Seperti saya ceritakan di episode pertama, mandat migrasi sudah menunggu sebelum saya diterima kerja. Salah satu tugas terbesarnya adalah menulis ulang parser protokol, pintu masuk bagi seluruh data armada.
Gambarannya begini: ada puluhan tipe tracker GPS, tiap vendor punya bahasa binernya sendiri, dan sistem lama punya satu parser untuk tiap bahasa itu. Tugas saya menulis ulang semuanya di Go, sementara armada terus kirim data tiap detik. Tidak ada jendela “kita matikan dulu semalam ya”.
Rutinitas saya sepanjang Januari 2025 sederhana: baca parser PHP, tulis parser Go, ulangi. Kantor bahkan membelikan langganan AI premium yang tersedia waktu itu supaya konversinya lebih cepat.
Jejaknya masih ada. Percakapan pertama saya dengan pemegang mandat tercatat 2 Januari jam tiga sore, isinya lokasi folder proyek. Pesan kerja pertama saya di sana meminta konversi file CodeIgniter pertama ke Go. Hasil sprint juga tercatat di grup atasan: konversi listener selesai sepuluh hari lebih cepat dari jadwal. Kayaknya itu satu-satunya bagian mandat yang selesai lebih awal, hehe. Malam-malam saya waktu itu habis untuk menulis ulang 21 versi parser.
Keberuntungan yang Sering Dilupakan #
Ada satu keuntungan besar: untuk setiap parser Go, kode PHP aslinya ada sebagai pembanding. Jadi porting-nya bisa 1:1, tidak perlu menebak dari spesifikasi protokol.
Caranya sederhana tapi harus telaten: bandingkan offset pembacaan GPS, struktur pembacaan elemen IO (posisi mulai baca), dan jumlah cabang case, file demi file. Saya periksa 21 versi satu per satu, tidak sekaligus dan tidak pakai asumsi “ah polanya pasti sama”. Asumsi itu hampir membuat saya salah memperbaiki masalah.
Mobil Parkir yang Ngebut #
Gejalanya muncul belakangan, di satu keluarga parser: tracker dengan codec tertentu terbaca 66 km/jam padahal diam. Cuma satu field yang aneh: kecepatan.
Teori pertama saya terdengar masuk akal banget. Di parser itu ada cabang yang menimpa kecepatan hasil pembacaan blok GPS dengan nilai elemen IO nomor 24. “Nah, IO-24-nya salah timpa! Buang saja timpaannya.” Maka saya buang, di sembilan parser sekaligus, karena “kan polanya sama”. (Iya, itu asumsi yang sama tadi. Saya tidak belajar secepat itu.)
Patch itu cuma bertahan kurang dari sehari. Setelah dicek ulang, cabang tersebut ternyata benar dan versi PHP-nya juga punya. Menghapusnya malah merusak kasus lain, jadi saya revert di hari yang sama. Riwayat commit-nya mencatat fix dan revert di hari yang sama.
Detektif Data: Device atau Parser? #
Sebelum menebak lagi, saya belajar dulu cara membedakan dua kemungkinan yang gampang tertukar: tracker-nya yang bermasalah, atau parser-nya yang salah.
Heuristiknya ternyata sederhana dan bisa diandalkan:
- Waktu GPS beku, identik di antara paket yang jam kirimnya berbeda? Itu device-nya. Parser itu deterministik, tidak mampu membekukan waktu.
- Offset lokasi acak, kadang maju kadang mundur tanpa pola? Itu drift GPS si device.
- Offset konsisten dan persis? Baru pantas mencurigai parser-nya.
Lalu saya bandingkan paket yang sama di sistem lama dan baru: waktu sama, koordinat sama, jumlah satelit sama. Bedanya cuma satu: kecepatan. Sistem lama membaca 9, sistem saya 66. Bukan device, tapi parser saya. Terbukti di atas kertas, bukan cuma modal omong hehe.
Satu Karakter yang Menggerakkan Mobil #
Biang keroknya ada di kode PHP asli: startPosition =+ startRecord.1 Di PHP, =+ itu assignment biasa dengan tanda plus unary di depan angka. Saya yang sudah mulai terbiasa menulis Go tanpa sadar mengubahnya jadi +=, artinya tambah lalu assign.
Beda satu karakter. Akibatnya posisi mulai pembacaan elemen IO bergeser beberapa byte. Parser lalu “menemukan” elemen IO di posisi yang keliru, termasuk IO-24 yang membawa angka 66. Mobil-mobil yang parkir pun seolah melaju tanpa izin pemiliknya.
Perbaikannya cuma satu karakter di dua file. Hari itu juga saya commit perbaikannya dan me-revert band-aid tadi. Jadi ujung-ujungnya, penelusuran panjang ini berakhir di beda antara = dan +=. Lucu, sekaligus bikin pengin menjedotkan kepala ke meja.
Cuci Besi: Mengembalikan 514.380 Baris #
Kode benar saja belum cukup. Data yang sempat salah juga harus dibereskan.
Untuk rentang dua minggu yang terkontaminasi, saya tarik arsip dari sistem lama, cocokkan per paket, dan perbaiki 514.380 baris lokasi di sistem baru.2 Pencocokannya 100%: tidak ada satu baris pun yang gagal dipulangkan. Glitch lamanya juga terverifikasi hilang, di timestamp yang sama, 66 berubah jadi 9.
Bagian ini tidak glamor dan jarang masuk tutorial, tapi migrasi bukan cuma memastikan kode baru benar. Data yang telanjur salah juga harus dibersihkan, tidak bisa dibiarkan begitu saja.
Jujur Soal Kata “Selesai” #
Sprint ini bagian dari periode tiga bulan yang lumayan berdampak ke kesehatan mental saya. Saya tulis apa adanya saja.
Waktu itu saya mengumumkan pekerjaan selesai dengan keyakinan yang belum penuh, karena verifikasi paritas belum 100%. Dan benar saja, sampai episode ini ditulis, sesekali masih ada perbaikan parser. Sekarang saya menganggap “selesai” itu spektrum, bukan tombol. Setiap laporan baru saya perlakukan sebagai data yang perlu diperiksa.
Catatan yang Saya Bawa Pulang #
- Porting kode biner itu, beda satu byte = beda dunia. Bandingkan file demi file, jangan menyamaratakan pola antar file yang “kelihatannya sama”.
- Paham dulu, baru patch. Band-aid yang saya buang di sembilan file sekaligus harus saya revert di hari yang sama. Mahalnya bukan di jamnya, tapi di percaya dirinya.
- Pola yang konsisten itu detektor. Beku = device, acak = drift, konsisten-persis = curigai parser.
- “Selesai” itu hipotesis sampai diverifikasi. Itu normal. Yang tidak normal adalah berhenti memverifikasi.
- Migrasi mencakup data lama. Kode baru yang benar harus diikuti pembersihan jejak salah yang tertinggal.
Episode keempat berangkat dari pertanyaan yang muncul di sini: kapan kode lama dianggap sebagai acuan, dan apa jadinya kalau “perbaikan” malah harus saya hapus sendiri?
Untuk tutorial menulis decoder GT06 dari nol, termasuk tabel byte demi byte dengan CRC yang diuji, ada di GPS Backend Part 3.