Kegagalan login yang disebabkan oleh ketidakcocokan penyimpanan nomor telepon mengungkap sebuah celah. Akun seorang pengguna terblokir karena basis data menyimpan nomor seluler Jerman yang sama dalam dua bentuk—0171 5550134 di satu baris dan +49 171 5550134 di baris lain—sehingga sistem menganggapnya sebagai entri yang berbeda. Hasilnya: OTP tidak pernah sampai, dan tombol “kirim kode” menjadi sebuah spekulasi.

Mengapa nomor telepon lebih sulit daripada tanggal

Pengembang sering kali mengandalkan regex untuk mengelola input nomor telepon. Kepercayaan itu hancur saat sebuah nomor melintasi perbatasan atau rencana nasional berubah. Tanggal mengikuti kalender yang dapat diprediksi; nomor telepon bermutasi seiring perubahan operator, regulasi, dan konvensi budaya.

Biaya tersembunyi dari string mentah

Menyimpan nomor telepon sebagai string biasa tampak unik—sampai dua representasi merujuk pada baris yang sama. Sistem OTP yang melakukan kueri ke basis data untuk mencari "nomor tersebut" akhirnya mengirimkan kode ke entri duplikat yang tidak pernah sampai ke pengguna.

Masalah ini memburuk ketika nomor disimpan dalam kolom numerik. Kolom BIGINT akan menghapus tanda + di depan dan angka nol di awal, mengubah +49 171 5550134 menjadi 491715550134. Tanpa tanda plus, merekonstruksi format aslinya menjadi sebuah tebakan.

Kontrak E.164

Rencana penomoran telepon internasional, E.164, menetapkan satu representasi tunggal yang portabel:

  • Dimulai dengan +
  • Diikuti oleh kode negara 1 hingga 3 digit
  • Kemudian nomor pelanggan
  • Total tidak lebih dari 15 digit
  • Tidak ada spasi, titik, atau tanda hubung

E.164 tidak menjamin nomor tersebut aktif; ia hanya menjamin bahwa string tersebut mengikuti pola struktural yang valid. Perlakukan ini sebagai kontrak format, bukan sebagai penentu keterjangkauan (reachability oracle).

Jebakan umum yang tidak bisa diperbaiki oleh regex

  • Penyimpanan numerik – BIGINT membuang tanda + dan angka nol di depan. Gunakan kolom teks (TEXT atau VARCHAR) sebagai gantinya.
  • Regex yang dikodekan secara keras (hard-coded) – Rencana nasional berubah. Meksiko menghapus awalan trunk-nya pada tahun 2019; Argentina kini memerlukan angka 9 setelah kode negara untuk jalur seluler. Pola statis akan cepat menjadi usang.
  • Penghapusan nol secara membabi buta – Telepon rumah Italia mempertahankan angka nol di depan, sedangkan Jerman tidak. Aturan umum “hapus angka nol di depan” akan merusak data Italia sementara nomor Jerman tetap tidak berubah.
  • Mengasumsikan format sama dengan keterkiriman – libphonenumber memvalidasi struktur tetapi tidak dapat mengetahui apakah perangkat sedang aktif atau apakah nomor tersebut telah dipindah-tangankan (ported).

Membangun alur (pipeline) yang andal

  1. Minta negara – Tambahkan pemilih negara pada formulir pendaftaran dan teruskan wilayah tersebut ke parser.
  2. Tampilkan pemformatan langsung – Gunakan pemformatan “AsYouType” agar pengguna melihat pola yang benar saat mereka mengetik.
  3. Validasi saat kehilangan fokus (on blur) – Jalankan validasi setelah pengguna meninggalkan kolom, bukan pada setiap ketukan tombol; ini mengurangi hambatan (friction).
  4. Simpan hanya string E.164 – Simpan nomor dengan awalan + yang telah dinormalisasi ke dalam basis data.
  5. Format di sisi pengguna (at the edge) – Ubah kembali ke tata letak yang ramah pengguna hanya di UI atau templat email.

Saat melakukan migrasi data lama (legacy), simpan baris yang gagal validasi. Parse setiap entri yang ada ke dalam kolom baru, tandai kegagalan, dan buat laporan. Penghapusan data secara diam-diam (silent drops) akan menciptakan tiket dukungan yang nantinya berubah menjadi perbaikan yang mahal.

Daftar periksa cepat untuk pengembang

  • Simpan nomor sebagai TEXT/VARCHAR dalam format E.164.
  • Gunakan pustaka libphonenumber milik Google; pustaka ini menangani realitas rencana global yang rumit.
  • Sediakan wilayah default untuk pengguna yang tidak menyertakan kode negara.
  • Panggil is_valid_number untuk pendaftaran langsung; ini memeriksa apakah nomor tersebut sesuai dengan aturan regional.
  • Gunakan is_possible_number saat membersihkan data massal; ini menangkap entri yang jelas-jelas salah format tanpa menolak kasus yang meragukan (borderline).

Penanganan nomor telepon yang tepat bukanlah sekadar fitur tambahan; itu adalah prasyarat bagi sistem apa pun yang mengandalkan kontak pengguna yang andal. Dengan menormalisasi ke E.164 dan mendelegasikan parsing ke pustaka yang telah teruji, pengembang dapat menghilangkan jenis bug yang secara diam-diam mengikis kepercayaan dan pendapatan. Perlakukan nomor telepon sebagai data terstruktur, bukan teks bebas, dan biarkan standar yang melakukan pekerjaan beratnya.