Pendahuluan
Transmisi data antar microservices kini menjadi pilar arsitektur backend modern. Salah satu teknologi yang populer digunakan adalah Protocol Buffers (Protobuf). Keunggulan utamanya antara lain serialisasi cepat, sekecil mungkin, lintas bahasa, dan—yang tak kalah penting—fleksibilitas dalam schema evolution. Namun, menjaga kompatibilitas saat schema berevolusi bisa jadi rumit dan fragile jika kita tidak jeli. Artikel ini mengupas tuntas aspek penting tersebut, dari teori, praktik, hingga contoh kode dan simulasi.
Apa Itu Kompatibilitas Schema?
Secara sederhana, schema compatibility berarti aplikasi versi lama tetap mampu membaca atau menulis data menggunakan schema versi baru (atau sebaliknya) tanpa error yang merusak integritas data.
Dalam Protobuf, terdapat dua istilah penting:
- Backward Compatibility: Aplikasi lama bisa membaca data yang diproduksi aplikasi baru.
- Forward Compatibility: Aplikasi baru bisa membaca data yang dibentuk aplikasi lama.
Kunci utama di Protobuf adalah penggunaan field tags yang eksplisit pada setiap field. Ini membedakan Protobuf dengan format seperti JSON atau XML.
Contoh Schema Sederhana
// v1: Pesan Awal
message UserProfile {
string name = 1;
int32 age = 2;
}
Evolusi Schema: Studi Kasus
Bayangkan Anda perlu menambahkan field baru email
. Apa yang harus diperhatikan agar tetap kompatibel?
// v2: email ditambahkan
message UserProfile {
string name = 1;
int32 age = 2;
string email = 3; // tambahkan field baru dengan tag unik
}
Penjelasan:
- Field lama (
name
,age
) tetap pada tag lama (1, 2). - Field baru (
email
) memakai tag baru (3).
Aplikasi lama yang hanya tahu v1 akan mengabaikan field tag 3
secara otomatis dan membuangnya, sehingga garis besar backward dan forward compatibility tetap terjaga.
Aturan Emas Evolusi Schema Protobuf
Berikut tabel singkat evolusi schema beserta dampaknya pada kompatibilitas:
Aksi | Backward Compatible | Forward Compatible | Aman Dipraktikkan? |
---|---|---|---|
Menambah Field Optional | Yes | Yes | Yes |
Menghapus Field (tanpa reuse tag) | Yes | Yes | Yes |
Menghapus Field (dengan reuse tag) | No | No | Jangan! |
Mengubah Type Field | No | No | Jangan! |
Mengubah Tag Number | No | No | Jangan! |
Mengubah Nama Field | Yes | Yes | Aman |
Menjadikan Field Wajib (from optional) | No | No | Jangan! |
Diagram Alur: Evolusi Schema yang Direkomendasikan
flowchart TD A[Mulai Evolusi Schema] B{Perlu Tambah Field?} C{Perlu Hapus Field?} D[Re-use tag lama?] E[Aman: tambahkan dengan tag baru] F[Aman: biarkan tag menganggur] G[Berbahaya: jangan reuse tag!] H[Perlu Ubah Type Field?] I[Berbahaya: akan korup data!] J[Selesai] A --> B B -- ya --> E B -- tidak --> C C -- ya --> D D -- ya --> G D -- tidak --> F C -- tidak --> H H -- ya --> I H -- tidak --> J E --> J F --> J G --> J I --> J
Simulasi Evolusi: Studi Kode
Berikut adalah versi Golang dari contoh dua arah forward compatibility dan backward compatibility pada Protocol Buffers:
user_v1.proto
syntax = "proto3";
package userpb;
message UserProfile {
string name = 1;
int32 age = 2;
}
user_v2.proto
syntax = "proto3";
package userpb;
message UserProfile {
string name = 1;
int32 age = 2;
string email = 3;
}
⚙️ Generate File Go
protoc --go_out=. user_v1.proto
protoc --go_out=. user_v2.proto
Akan menghasilkan:
user_v1.pb.go
user_v2.pb.go
Kode Go: Simulasi Dua Arah Kompatibilitas
package main
import (
"fmt"
"log"
"google.golang.org/protobuf/proto"
userpbv1 "path/to/user_v1"
userpbv2 "path/to/user_v2"
)
func main() {
fmt.Println("=== 1. Versi Lama Membaca Data Versi Baru ===")
// V2 menulis data dengan field tambahan: email
msgV2 := &userpbv2.UserProfile{
Name: "Budi",
Age: 30,
Email: "budi@example.com",
}
data, err := proto.Marshal(msgV2)
if err != nil {
log.Fatalf("Gagal serialisasi v2: %v", err)
}
// Dibaca oleh versi lama (v1) yang tidak tahu field email
msgV1 := &userpbv1.UserProfile{}
if err := proto.Unmarshal(data, msgV1); err != nil {
log.Fatalf("Gagal deserialisasi v1: %v", err)
}
fmt.Println("Deserialized (v1):", msgV1) // email diabaikan tanpa error
fmt.Println("\n=== 2. Versi Baru Membaca Data Versi Lama ===")
// V1 menulis data tanpa field email
msgV1Old := &userpbv1.UserProfile{
Name: "Rina",
Age: 20,
}
dataOld, err := proto.Marshal(msgV1Old)
if err != nil {
log.Fatalf("Gagal serialisasi v1: %v", err)
}
// Dibaca oleh versi baru (v2) yang mengharapkan email
msgV2Read := &userpbv2.UserProfile{}
if err := proto.Unmarshal(dataOld, msgV2Read); err != nil {
log.Fatalf("Gagal deserialisasi v2: %v", err)
}
fmt.Println("Deserialized (v2):", msgV2Read) // email == "", default
}
Output:
=== 1. Versi Lama Membaca Data Versi Baru ===
Deserialized (v1): name:"Budi" age:30
=== 2. Versi Baru Membaca Data Versi Lama ===
Deserialized (v2): name:"Rina" age:20 email:""
Hasil:
Case | Status | Output |
---|---|---|
Old code baca pesan new | Backward compatible | email diabaikan |
New code baca pesan old | Forward compatible | email kosong/default |
Praktik Terburuk: Reuse Tag Number
Skenario yang harus diavoid:
// v2 - SALAH, reuse tag!
message UserProfile {
string name = 1;
int32 age = 2;
string phone = 3; // tadinya 'email', sekarang 'phone', tag sama!
}
Parse data lama dapat menyebabkan field corruption. Data dengan tag 3
mungkin berisi email, tapi diinterpretasi sebagai phone number.
Tips Engineer: Evolusi Aman
Jangan Pernah Mengubah/Merubah Tipe Field
Perubahan tipe field (misal daristring
keint32
) berpotensi mengakibatkan parsing error dan data korup.Jangan Gunakan Ulang Tag Number
Setelah tag dihapus, biarkan tag itu tidak terpakai selamanya.Hindari Perubahan Optional jadi Required
Pesan lama yang tidak punya field tersebut akan gagal diparse jika mandatory.Pertimbangkan Versioning
Untuk perubahan besar, gunakan pesan baru:UserProfileV2
dst, dan buat prosedur migrasi data.
Kesimpulan
Kompatibilitas schema adalah aspek vital dalam integrasi sistem berbasis Protobuf. Dengan mengikuti aturan sederhana (jangan pernah mengubah/merubah tag number, tipe field, dan selalu tambahkan field secara additive), kita dapat memastikan evolusi schema berjalan tanpa drama. Simulasi di atas memperlihatkan bagaimana backward dan forward compatibility bekerja secara nyata.
Selalu uji kompatibilitas schema Anda saat mengalami perubahan dan, jika memungkinkan, bangun pipeline CI agar integritas schema ini selalu terjaga. Evolving fast, but without breaking things.
Rujukan:
- https://developers.google.com/protocol-buffers/docs/proto3#updating
- https://protobuf.dev/programming-guides/proto3/
Happy engineering! 🚀
23 Modularisasi File Resolver dan Skema
Artikel Terhangat
26 Apa Itu Mutation dan Kapan Digunakan?
07 Jul 2025
48 Custom Options di Protobuf
07 Jul 2025
47 Reserved Fields dan Reserved Numbers
07 Jul 2025
23 Modularisasi File Resolver dan Skema
07 Jul 2025
45 Cara Menggunakan `option` di Protobuf
07 Jul 2025

26 Apa Itu Mutation dan Kapan Digunakan?

48 Custom Options di Protobuf

47 Reserved Fields dan Reserved Numbers

23 Modularisasi File Resolver dan Skema
