Kenapa Menggunakan Enum pada Tipe Kolom Database
10 min read

Kenapa Menggunakan Enum pada Tipe Kolom Database

Saat mendesain skema database, pemilihan tipe kolom sering dianggap detail kecil yang tidak perlu dipikirkan terlalu lama. Padahal keputusan ini berdampak besar terhadap konsistensi data, performa query, dan kemudahan maintenance jangka panjang. Salah satu pertanyaan yang sering muncul: apakah sebuah nilai sebaiknya disimpan sebagai string biasa, atau menggunakan enum? Artikel ini membahas kenapa enum sering menjadi pilihan yang baik, bagaimana cara kerjanya di level database dan aplikasi, serta trade-off dan best practice supaya penggunaannya tidak menjadi jebakan di kemudian hari.

Apa Itu Enum dalam Konteks Database?

Enum (enumeration) adalah tipe data yang merepresentasikan sekumpulan nilai terbatas dan terdefinisi. Beberapa contoh yang sering ditemui di aplikasi production:

  • status user: ACTIVE, INACTIVE, BANNED
  • status order: PENDING, PAID, SHIPPED, CANCELLED
  • role: ADMIN, USER, MODERATOR

Secara konsep, enum menyatakan satu hal sederhana: kolom ini hanya boleh berisi nilai-nilai tertentu, tidak lebih dan tidak kurang. Kamu bisa mengimplementasikan konsep ini dengan beberapa cara:

  • Native enum database — misalnya tipe ENUM di MySQL atau PostgreSQL
  • Enum berbasis integer — kolom smallint/int dengan mapping di level aplikasi
  • Lookup table — kolom berupa foreign key yang menunjuk ke tabel referensi

Artikel ini fokus pada pendekatan enum berbasis integer dengan representasi teks, karena pendekatan ini paling fleksibel dan paling umum dipakai di sistem skala besar.


Cara Kerja Enum Berbasis Integer

Ide utamanya adalah memisahkan apa yang disimpan di database dengan apa yang dilihat oleh konsumen API. Database menyimpan angka, sementara aplikasi yang bertanggung jawab menerjemahkan angka tersebut menjadi teks yang bermakna.

Representasi di Storage

Pada pendekatan ini, nilai logis dipetakan ke integer kecil:

Nilai LogisNilai Tersimpan
ACTIVE1
INACTIVE2
BANNED3

Yang benar-benar disimpan di kolom database adalah integer, bukan string "ACTIVE" atau "INACTIVE".

Representasi di Aplikasi

Di sisi aplikasi, terjadi proses mapping dua arah:

  • Backend memetakan integer dari database menjadi enum di kode
  • Frontend atau API response mengirimkan teks enum, bukan angka mentah
{
  "status": "ACTIVE"
}

Diagram berikut menggambarkan alur data dari database hingga response API:

sequenceDiagram
    participant DB as Database
    participant Repo as Repository
    participant Service as Service Layer
    participant API as API Response

    DB->>Repo: status = 1
    Repo->>Service: OrderStatus.ACTIVE
    Service->>API: "status": "ACTIVE"

Dengan pendekatan ini, kamu mendapatkan dua keuntungan sekaligus: database tetap efisien karena bekerja dengan angka, sementara API tetap human-friendly karena mengirimkan teks yang mudah dibaca.


Alasan Utama Menggunakan Enum di Kolom

Ada beberapa alasan konkret kenapa enum berbasis integer lebih disukai dibanding string biasa, mulai dari performa query hingga kemudahan refactor.

Lebih Optimal untuk Index dan Query

Integer memiliki ukuran tetap (1–4 byte) dan perbandingannya sangat cepat karena CPU-friendly. String, sebaliknya, memiliki panjang variabel dan dibandingkan byte per byte. Akibatnya, index berbasis integer lebih kecil, lebih banyak index page yang bisa masuk ke memory, dan query dengan WHERE, JOIN, atau GROUP BY menjadi lebih efisien.

-- BENAR: filter menggunakan integer, lebih cepat dibandingkan dan lebih ringan di index
SELECT * FROM orders WHERE status = 2;

-- ANTI-PATTERN: filter menggunakan string, index lebih besar dan perbandingan lebih lambat
SELECT * FROM orders WHERE status = 'SHIPPED';

Konsistensi Data Terjamin

Tanpa enum, kolom status rawan menyimpan variasi nilai yang sebenarnya bermakna sama: active, Active, ACTIVE, atau bahkan typo seperti actve. Dengan enum, hanya nilai yang diizinkan sistem yang bisa masuk — tidak ada variasi liar atau kesalahan penulisan.

Hal ini menjadi sangat penting ketika sistem sudah besar, melibatkan banyak service, dan dikerjakan oleh banyak developer. Enum berfungsi sebagai kontrak data antar tim.

Lebih Mudah Dipahami di Level Domain

Enum merepresentasikan konsep bisnis, bukan sekadar data mentah. Bandingkan dua potongan kode berikut:

// ANTI-PATTERN: angka mentah tanpa konteks, sulit dipahami dan rawan salah
if status == 3 {
    // apa maksud 3 di sini?
}

// BENAR: enum membuat kode eksplisit dan self-documenting
type OrderStatus int

const (
    OrderPending OrderStatus = 1
    OrderPaid    OrderStatus = 2
    OrderShipped OrderStatus = 3
)

if status == OrderShipped {
    // jelas maksudnya
}

Enum membuat kode lebih eksplisit, lebih mudah dibaca oleh developer baru, dan lebih sulit disalahgunakan karena nilai yang valid sudah didefinisikan secara terpusat.

Hasil Query Tetap Human-Friendly

Walaupun disimpan sebagai integer, enum tidak harus tampil sebagai angka ke pengguna akhir. Ada dua cara umum untuk menampilkan teks yang bermakna:

Opsi pertama, mapping dilakukan di aplikasi — backend mengubah 2 menjadi "PAID" sebelum dikirim sebagai response.

Opsi kedua, mapping dilakukan langsung di SQL menggunakan CASE:

SELECT
  CASE status
    WHEN 1 THEN 'PENDING'
    WHEN 2 THEN 'PAID'
    WHEN 3 THEN 'SHIPPED'
  END AS status
FROM orders;

Kedua opsi ini membuat hasil query tetap mudah dibaca oleh developer, analyst, maupun tim support, tanpa kehilangan keuntungan performa dari penyimpanan integer.

Lebih Hemat Storage

Perbandingan kasar ukuran tipe data:

TipeEstimasi Size
INT4 byte
VARCHAR(20)hingga 20+ byte

Pada tabel dengan jutaan baris, perbedaan ini berdampak signifikan: storage lebih hemat, index jauh lebih ramping, dan cache hit ratio cenderung meningkat karena lebih banyak data yang bisa muat di memory.

Lebih Aman untuk Refactor dan Rename

Bayangkan kamu ingin mengubah nama status WAITING_PAYMENT menjadi UNPAID. Jika status disimpan sebagai string, perubahan ini berarti harus melakukan update pada jutaan baris data, dengan risiko typo dan potensi downtime.

Jika status disimpan sebagai enum integer, angka yang tersimpan di database tetap sama. Yang berubah hanya mapping teks di level aplikasi. Ini membuat proses rename atau refactor jauh lebih aman dan tidak menyentuh data historis sama sekali.


Perbandingan dengan Lookup Table

Enum integer bukan satu-satunya cara untuk merepresentasikan nilai terbatas. Alternatif lain yang umum dipakai adalah lookup table, yaitu tabel referensi yang dihubungkan melalui foreign key.

AspekEnum IntegerLookup Table
Join tambahanTidak perluPerlu
PerformaSangat cepatLebih lambat
Fleksibilitas runtimeRendahTinggi
Cocok untukNilai stabilNilai dinamis

Rule of thumb yang bisa dipakai: jika nilai jarang berubah dan merupakan bagian dari domain model, gunakan enum. Jika nilai sering berubah atau didefinisikan oleh user (misalnya kategori produk yang bisa ditambah admin kapan saja), gunakan lookup table.


Menambah Entry Enum pada Data yang Sudah Besar

Menambah nilai enum baru terlihat sepele, tetapi pada sistem dengan data besar, banyak service, dan banyak consumer, ini adalah salah satu sumber bug dan insiden production yang paling sering diremehkan.

Dampak di Level Database

Jika kamu menggunakan enum berbasis integer, menambah enum baru tidak mengubah data yang sudah ada. Tidak perlu update jutaan baris, index tetap valid, dan performa relatif aman.

EnumNilaiKeterangan
PENDING1
PAID2
SHIPPED3
CANCELLED4enum baru

Secara database, kamu hanya menambah makna baru — angka lama tidak berubah sama sekali.

Bahaya besar terjadi jika:

  • Nilai integer enum lama diubah
  • Enum baru disisipkan di tengah urutan, bukan di akhir

Kedua hal ini bisa menyebabkan data lama berubah makna dan memunculkan bug silent — tidak error, tapi logika menjadi salah.

Perbedaan dengan Native ENUM Database

Jika kamu menggunakan native ENUM (misalnya di MySQL atau PostgreSQL), menambah nilai baru berarti melakukan ALTER TYPE atau ALTER TABLE. Operasi ini bisa mengunci tabel tergantung database dan versinya, serta memakan waktu lama pada tabel besar.

-- ANTI-PATTERN: ALTER TYPE pada tabel besar berisiko locking dan downtime
ALTER TYPE order_status ADD VALUE 'CANCELLED';

Inilah alasan banyak sistem skala besar menghindari native ENUM dan memilih pendekatan integer dengan mapping di aplikasi.

Dampak di Aplikasi dan Microservices

Masalah paling umum biasanya bukan di database, melainkan di aplikasi. Ketika enum baru ditambahkan, ada kemungkinan Service A sudah mengetahuinya sementara Service B belum di-deploy. Akibatnya, Service B menerima nilai enum yang tidak dikenal, yang bisa memicu exception, default branch yang salah, atau logic bisnis yang bocor.

// ANTI-PATTERN: tidak ada default case, enum baru menyebabkan undefined behavior
switch status {
case OrderPending:
    // ...
case OrderPaid:
    // ...
case OrderShipped:
    // ...
}
// enum baru (misal OrderCancelled) tidak tertangani sama sekali

Backward Compatibility adalah Kunci

Setiap penambahan enum harus dianggap sebagai breaking change potensial. Best practice-nya adalah selalu menyediakan state UNKNOWN atau default, sehingga logic lama bisa mengabaikan enum baru atau memperlakukannya sebagai fallback yang aman.

// BENAR: ada state UNKNOWN sebagai fallback aman untuk enum yang belum dikenal
const (
    OrderUnknown   OrderStatus = 0
    OrderPending   OrderStatus = 1
    OrderPaid      OrderStatus = 2
    OrderShipped   OrderStatus = 3
    OrderCancelled OrderStatus = 4
)

Dengan pendekatan ini, service lama tidak crash ketika bertemu enum baru, dan sistem secara keseluruhan tetap stabil selama proses rollout.

Dampak ke Data Analytics dan Reporting

Enum baru juga berdampak ke dashboard, report, dan query BI. Jika tidak diantisipasi, grafik bisa terlihat tidak konsisten, data seolah “hilang”, atau hasil agregasi menjadi salah.

SELECT status, COUNT(*) FROM orders GROUP BY status;

Enum baru yang belum terpetakan di dashboard akan tampil tanpa label yang jelas. Solusinya adalah memperbarui mapping di sisi analytics secara bersamaan dengan penambahan enum, dan mendokumentasikan setiap perubahan enum sebagai bagian dari proses rilis.

Deployment Strategy yang Aman

Untuk sistem besar, urutan deployment yang aman saat menambah enum baru mengikuti diagram berikut:

flowchart TD
    A[Deploy aplikasi yang toleran enum baru] --> B[Pastikan fallback / UNKNOWN aktif di semua service]
    B --> C[Mulai menulis data dengan enum baru]
    C --> D[Update mapping analytics & dokumentasi]

Jangan langsung menulis enum baru ke database saat sebagian service belum siap menanganinya — urutan yang terbalik inilah yang sering menyebabkan insiden lintas service.

Ringkasan Risiko Utama

RisikoPenyebab
Data salah maknaMengubah nilai enum lama
Service crashEnum baru tidak dikenali
DowntimeALTER ENUM di tabel besar
Bug silentTidak ada default handling

Kesalahan Umum dalam Penggunaan Enum

Banyak tim menyimpulkan bahwa “enum itu buruk” bukan karena konsepnya salah, tapi karena kesalahan implementasi berikut. Di sistem besar, kesalahan-kesalahan ini hampir selalu berujung insiden.

Kesalahan paling fatal adalah mengubah nilai integer enum yang sudah dipakai. Jika PAID = 2 diubah menjadi PAID = 5, data lama langsung berubah makna tanpa ada error sama sekali — bug bersifat silent dan sangat sulit dilacak. Aturan kerasnya: nilai integer enum tidak boleh berubah selamanya.

Kesalahan kedua adalah menyisipkan enum baru di tengah urutan, bukan di akhir.

// ANTI-PATTERN: enum baru disisipkan di tengah, merusak makna data historis
PENDING   = 1
PAID      = 2
CANCELLED = 3  <- disisipkan, padahal 3 sudah dipakai SHIPPED
SHIPPED   = 4

// BENAR: enum baru selalu ditambahkan di akhir urutan
PENDING   = 1
PAID      = 2
SHIPPED   = 3
CANCELLED = 4

Jika data lama sudah memakai 3 = SHIPPED, menyisipkan CANCELLED = 3 akan membuat makna data historis rusak, dan report atau analytics menjadi salah total.

Kesalahan ketiga adalah tidak menyediakan default atau state UNKNOWN, yang sudah dibahas pada bagian sebelumnya. Kesalahan keempat adalah menulis enum baru ke database sebelum semua service siap menanganinya, sehingga service lama menerima nilai yang tidak dikenal dan memicu error lintas service.

Kesalahan kelima adalah menggunakan native ENUM di tabel besar tanpa strategi — ALTER TYPE atau ALTER TABLE bisa locking dan berisiko downtime, terutama di MySQL dan PostgreSQL pada tabel dengan jutaan baris.

Kesalahan keenam, dan ini soal desain, adalah menganggap enum sebagai konfigurasi. Jika kamu sering diminta “tolong tambahin value” untuk kolom tertentu, atau domainnya tidak jelas, itu tanda bahwa kolom tersebut seharusnya menggunakan lookup table, bukan enum.

Ringkasan Kesalahan Fatal

KesalahanDampak
Ubah nilai enum lamaData rusak tanpa error
Sisip enum di tengahMakna historis berubah
Tidak ada UNKNOWNService crash
Tidak backward compatibleInsiden lintas service
Native ENUM tanpa strategiDowntime

Kapan Enum Tidak Disarankan?

Enum bukan solusi universal. Ada beberapa kondisi di mana lookup table justru lebih tepat dibanding enum.

GUNAKAN enum jika:
  ✓ nilai stabil dan jarang berubah
  ✓ nilai merupakan bagian dari domain model
  ✓ jumlah nilai terbatas dan terdefinisi jelas

PERTIMBANGKAN lookup table jika:
  ✗ nilai sering berubah oleh admin
  ✗ nilai perlu ditambah tanpa deploy ulang
  ✗ jumlah nilai sangat besar atau dinamis
  ✗ nilai bersifat konfigurasi, bukan domain tetap

Ringkasan

  • Enum berbasis integer menyimpan angka di database, sementara aplikasi memetakannya menjadi teks yang human-friendly di API.
  • Integer lebih optimal untuk index dan query dibanding string, serta lebih hemat storage pada tabel besar.
  • Enum berfungsi sebagai kontrak data — mencegah variasi nilai liar dan typo seperti active vs Active vs actve.
  • Enum membuat kode lebih eksplisit dan self-documenting dibanding angka mentah tanpa konteks.
  • Nilai integer enum tidak boleh diubah dan enum baru selalu ditambahkan di akhir, bukan disisipkan di tengah.
  • Selalu sediakan state UNKNOWN/default agar service lama tidak crash saat menemukan enum baru.
  • Native ENUM di MySQL/PostgreSQL berisiko locking saat ALTER TYPE pada tabel besar — pertimbangkan integer + mapping sebagai alternatif yang lebih aman.
  • Jika nilai sering berubah, ditentukan oleh admin, atau bersifat konfigurasi, gunakan lookup table, bukan enum.

Portofolio