Skip to content

Ganti Kosakata Status Tanpa Sekali Jalan: ENUM Tiga Langkah

Adityo Guni Waluyo

Enam status baru, empat status lama. MODIFY + UPDATE + MODIFY bikin kosakata status ganti tanpa copy tabel dan tanpa baris invalid di tengah jalan.

Ringkasan

Spec butuh enam status baru tapi DB masih pakai empat status Inggris lama jadi nggak bisa asal ganti kolom. MySQL cuma boleh nambah ENUM di akhir biar in-place, kalau diselip di tengah malah copy tabel dan data bisa geser. Makanya migrasinya dibikin tiga step expand-migrate-contract, down-nya lossy dan relasi sengaja nggak pakai foreign key biar soft delete aman.

Enam status di spec, empat status di tabel

Spec baru dari klien minta alur pengajuan enam nilai: pengajuan, verifikasi, diproses, disetujui, ditolak, selesai. Realita di database KotaPortal: dua tabel pengajuan masih memakai empat status bahasa Inggris warisan lama, pending sampai rejected. Dua dunia yang nggak bisa dipertemukan lewat satu baris ALTER TABLE aja.

Pikiran pertama saya memang sederhana: tinggal MODIFY COLUMN, ganti daftar nilainya, beres. Tapi begitu membuka dokumentasi resmi MySQL 5.7, satu kalimat langsung mengubah rencana: menyelipkan anggota ENUM di tengah daftar memicu penomoran ulang nilai lama dan memaksa copy tabel penuh [1]. Copy tabel artinya operasi berat, dan kalau urutan nilai berubah, data lama yang tersimpan sebagai indeks bisa melompat ke arti yang salah.

Untuk tipe kolom lain, kegagalan bahkan lebih eksplisit. MySQL menolaknya dengan pesan Cannot change column type INPLACE dan saran pindah ke ALGORITHM=COPY [1] Status memang boleh diubah in-place, tapi hanya dalam satu bentuk: nilai baru di-append di akhir daftar, selama ukuran penyimpanannya nggak naik [1]. Dari sini saya putuskan migrasinya harus tiga langkah, bukan sekali jalan.

Koreografi expand, migrate, contract

Urutannya gini. Langkah pertama: MODIFY COLUMN dengan daftar gabungan, nilai lama dan baru berdampingan, yang lama tetap di depan. Langkah kedua: UPDATE memetakan data, pending jadi pengajuan, reviewed jadi verifikasi, approved jadi disetujui, rejected jadi ditolak. Langkah ketiga: MODIFY COLUMN lagi, sekarang cuma enam nilai Indonesia. Pola yang sama dijalankan dua kali, untuk tabel pendaftaran dan tabel pengaduan.

Kunci ada di langkah pertama itu. ENUM yang menggabungkan empat nilai lama plus enam nilai baru ternyata masih masuk kategori ukuran penyimpanan yang nggak berubah, jadi ALTER-nya sah dijalankan in-place [1]. Ada detail kecil yang bikin trik ini aman: kolom ENUM yang NOT NULL itu default-nya elemen pertama daftar [2]. Karena daftar mid-state saya taruh pengajuan di posisi pertama, baris yang kebetulan masuk di sela proses migrasi dapat default yang justru benar, bukan nilai sampah.

Pranala tanpa foreign key, down yang lossy

Selain ganti kosakata status, migration yang sama menambah kolom-kolom pranala ke entitas tujuan: entity_id, id paket sewa, tanggal mulai dan selesai. Semuanya BIGINT polos, tanpa foreign key. Ini keputusan yang sadar. Entitas yang ditautkan pakai soft delete, dan foreign key yang ketat berarti penghapusan entitas induk bisa mematahkan pengajuan yang masih berjalan. Konsistensi pranala saya percayakan ke level aplikasi, bukan ke constraint database.

Lalu rollback. Konvensi golang-migrate memang satu migration = sepasang file up dan down, dieksekusi naik sesuai urutan versi dan turun dalam urutan terbalik [3]. Tapi down saya nggak mulus, dan saya tulis begitu adanya di komentar: status diproses kembali jadi reviewed, selesai jatuh ke approved. Data status yang lahir setelah migrasi nggak punya padanan satu-satu di kosakata lama. Doktrin round-trip ala Kubernetes bilang objek harus bisa bolak-balik antar versi tanpa kehilangan informasi [4], dan down ini jelas melanggarnya. Migrasi database di MySQL 5.7 sering memaksa pilihan begini: mundur dengan pemetaan yang kehilangan detail, atau nggak bisa mundur sama sekali. Saya pilih yang pertama, dengan tabel application_status_history yang mencatat setiap perubahan status sebagai jejak pemulihan.

Bukti dua arah sebelum produksi

Sebelum dijalankan ke database dev, migration ini saya uji di database test yang sekali pakai: naik, turun, naik lagi. Siklus up-down-up itu membuktikan dua arah sekaligus, file up jalan mulus tanpa pesan error in-place itu [1], dan file down benar-benar mengembalikan skema seperti semula. Satu detail kecil yang gampang kelewat: tabel riwayat baru harus ikut masuk daftar cleanup di utilitas test, kalau nggak, data bocor antar test case.

Kalau kamu lihat error itu di migration sendiri, cek dulu urutan daftar nilainya. Kebanyakan kasus yang saya temui, penyebabnya nilai baru diselipkan di tengah daftar, bukan di-append di akhir. Sisanya memang tipe kolom yang nggak didukung in-place, dan jawabannya bukan dipaksa, tapi dipecah jadi langkah yang masing-masing sah.

Sources

[1] MySQL 5.7 Reference Manual, Online DDL Operations (snapshot 2025-12-16)

[2] MySQL 5.7 Reference Manual, The ENUM Type (snapshot 2026-01-03)

[3] golang-migrate, MIGRATIONS.md

[4] Kubernetes API deprecation policy

Artikel terkait