Strategi Versioning API yang Tidak Menyiksa Konsumen
Setiap kali saya membangun API yang dipakai lebih dari satu klien — entah itu aplikasi mobile, sistem eksternal, atau modul lain dalam ekosistem yang sama — pertanyaan yang selalu muncul cepat atau lambat adalah: bagaimana caranya mengubah struktur data atau perilaku endpoint tanpa membuat semua konsumen yang sudah terhubung tiba-tiba rusak. Ini bukan soal teori akademis, ini masalah operasional sehari-hari. Sekali API dipakai orang lain, setiap perubahan yang tidak hati-hati bisa berarti downtime di sisi klien yang bahkan tidak Anda kendalikan langsung.
Yang sering terjadi, tim memilih salah satu dari dua ekstrem: tidak pernah versioning sama sekali (sehingga breaking change langsung menghantam semua orang), atau versioning berlebihan dengan membuat versi baru untuk setiap perubahan kecil (sehingga maintenance jadi mimpi buruk karena ada terlalu banyak cabang kode yang harus dijaga hidup). Titik tengah yang sehat itu yang ingin saya bahas di sini.
Bedakan Breaking Change dari Perubahan Aman
Langkah pertama sebelum bicara mekanisme versioning adalah punya definisi yang jelas soal apa yang dianggap breaking change. Menambah field baru ke response umumnya aman, karena konsumen yang benar biasanya mengabaikan field yang tidak dikenal. Yang berbahaya adalah mengubah tipe data sebuah field, mengganti nama field, menghapus field yang sudah dipakai, atau mengubah makna semantik dari sebuah status code.
Saya biasa memakai aturan sederhana: kalau perubahan itu bisa membuat kode konsumen yang sudah ada gagal parsing atau salah interpretasi tanpa mereka mengubah apa pun di sisi mereka, itu breaking change dan butuh versi baru. Kalau tidak, itu bisa masuk ke versi yang sama sebagai penambahan.
Pilih Mekanisme Versioning yang Sesuai Konteks
Ada beberapa pendekatan umum, dan tidak ada satu yang benar mutlak untuk semua kasus:
- Versioning di URL (misalnya
/api/v1/patients) — paling mudah dipahami, mudah di-debug lewat log, dan cocok kalau konsumen API Anda beragam dan tidak semuanya sanggup mengatur header custom. - Versioning lewat header (misalnya
Accept: application/vnd.myapp.v2+json) — lebih "bersih" secara REST, tapi butuh disiplin dokumentasi karena versinya tidak kelihatan langsung di URL. - Versioning per field atau per fitur — cocok untuk perubahan kecil yang terisolasi, tanpa perlu menaikkan versi seluruh API.
Untuk kebanyakan proyek yang saya tangani, versioning di URL tetap jadi pilihan default karena transparansinya. Di Laravel, ini biasanya cukup diselesaikan dengan mengelompokkan route dalam prefix, misalnya:
Route::prefix('v1')->group(function () {
Route::apiResource('patients', PatientV1Controller::class);
});
Route::prefix('v2')->group(function () {
Route::apiResource('patients', PatientV2Controller::class);
});
Kuncinya bukan pada sintaksnya, tapi pada disiplin menjaga controller v1 tetap berjalan tanpa perubahan perilaku selama masih ada konsumen yang bergantung padanya, sementara logika baru dikembangkan di v2.
Punya Kebijakan Deprecation yang Jelas
Versioning tanpa rencana pensiun sama saja menumpuk utang teknis selamanya. Saya selalu menyarankan menetapkan jangka waktu dukungan untuk versi lama sejak versi baru dirilis — sebagai ilustrasi, misalnya enam bulan sejak versi baru stabil, versi lama akan dinonaktifkan. Komunikasikan ini lewat changelog, header response (misalnya Deprecation dan Sunset), atau notifikasi langsung ke tim konsumen yang Anda kenal.
Yang tidak kalah penting: jangan hapus versi lama diam-diam. Kejutan adalah musuh utama dari hubungan baik dengan konsumen API.
Takeaway
Versioning API yang baik bukan soal mekanisme paling canggih, tapi soal disiplin: bedakan perubahan aman dari breaking change, pilih satu mekanisme versioning yang konsisten dipakai di seluruh sistem, dan selalu punya jalan keluar yang jelas untuk versi lama sebelum dimatikan. Konsumen API Anda tidak butuh API yang tidak pernah berubah — mereka butuh perubahan yang bisa diprediksi.