Perubahan API

Protobuf breaking changes: apa yang bertahan di wire

5 menit baca

API REST berubah saat bentuk JSON berubah, dan sebagian besar bentuk itu terlihat di respons yang bisa Anda baca di browser. API gRPC berubah saat berkas .proto berubah, dan format biner wire milik Protocol Buffers punya aturan sendiri tentang apa yang bisa ditoleransi client yang tidak ada hubungannya dengan apa yang dikatakan nama field. Dua penyuntingan yang terlihat sama kecilnya di diff, menomori ulang sebuah field versus menambahkannya, jatuh di sisi berlawanan dari garis yang breaking changes gambar secara umum: satu tidak terlihat bagi setiap client yang ada, yang lain merusak semuanya sekaligus. Membedakan protobuf breaking changes dari yang aman berarti membaca aturan format wire itu sendiri, bukan menebak dari bagaimana perubahan itu terbaca di diff .proto.

Mengapa penomoran field lebih penting daripada nama field dalam Protobuf?

Karena format wire mengkodekan field berdasarkan nomor, bukan nama. Kode yang dihasilkan dalam setiap bahasa membaca dan menulis nomor-nomor itu; nama field email di berkas .proto Anda adalah kemudahan bagi manusia yang tidak pernah menyentuh byte biner yang dikirim lewat jaringan. Mengganti nama field, email menjadi email_address, aman di wire biner selama nomornya tetap sama, yang mengejutkan insinyur yang terbiasa dengan REST, di mana key JSON yang diganti namanya adalah persis jenis perubahan yang merusak client. Pengecualiannya adalah kasus REST yang sama: format ProtoJSON dan text menserialisasi nama, jadi mengganti nama merusak transcoding JSON (grpc-gateway, misalnya), berkas text-format dan field mask. Menomori ulang field yang sama, mempertahankan nama tapi mengubah 1 menjadi 7, adalah kebalikannya persis: tidak terlihat dalam review kode yang hanya menampilkan nama, dan itu merusak setiap pesan yang dikirim atau diterima client sejak titik itu.

PerubahanAman di wireMengapa
Mengganti nama field, mempertahankan nomornyaBiner ya, JSON dan text tidakPengkodean biner menggunakan nomor; ProtoJSON dan format text menggunakan nama
Mengubah nomor sebuah fieldTidakSetiap pesan yang ada sekarang dibaca sebagai field yang salah
Menambah field baru dengan nomor baruYaClient lama mengabaikan field yang tidak mereka kenali
Menghapus field, menggunakan ulang nomor lamanya untuk hal lainTidakData lama diterjemahkan ke field baru yang salah
Mengubah tipe field secara tidak kompatibel (mis. int32 ke string)TidakPengkodean wire berbeda per tipe

Apa yang membuat menghapus field berbeda dari melakukannya dalam respons JSON REST?

Nomornya menjadi radioaktif. Panduan resmi Protobuf merekomendasikan menandai nomor field yang dihapus sebagai reserved alih-alih membiarkannya digunakan ulang, karena penggunaan ulang adalah di mana kerusakan sebenarnya terjadi: client yang masih menjalankan kode yang dihasilkan bulan lalu mengirim pesan menggunakan nomor lama field itu untuk makna lama, dan server, yang sekarang mengharapkan nomor itu berarti sesuatu yang lain, salah menafsirkan data secara diam-diam alih-alih menolaknya secara langsung. REST tidak punya jebakan setara, karena key JSON yang dihapus hanya berhenti muncul; tidak ada cara bagi permintaan client lama untuk diam-diam ditafsirkan ulang sebagai sesuatu yang lain. Berkas .proto dengan reserved 4, 9, 12; di atas sebuah message adalah bekas luka permanen, dan itulah maksudnya: itu menghentikan nomor tersebut diberikan ke field baru oleh seseorang yang tidak tahu sejarahnya.

message Invoice {
  reserved 4; // dulu `legacy_customer_id`, dihapus 2026-06-01
  reserved "legacy_customer_id"; // namanya juga, untuk JSON/text
  string customer_id = 5;
  string status = 6;
}

Apakah menambah field pernah membutuhkan entri changelog sama sekali?

Biasanya bukan entri breaking change, tapi sering kali entri biasa, karena “aman di wire” dan “tidak terlihat bagi pembaca yang peduli” adalah dua klaim berbeda. Menambah field ke pesan respons tidak membebani apa pun secara struktural, client lama menerjemahkan pesan dan mengabaikan field baru secara otomatis. Tapi seseorang yang membangun integrasi baru terhadap layanan itu tidak punya cara mengetahui field itu ada kecuali seseorang memberi tahunya, karena tidak ada yang di build sukses atau tes yang lolos membuat field opsional baru terlihat. Changelog API membahas secara umum apa yang dihutangi entri aditif kepada pembaca; alasan khusus gRPC untuk tetap menulis satu adalah tidak ada padanan untuk menjelajahi respons REST di debugger untuk menyadari bahwa key baru muncul.

Bagaimana ini berbeda dari apa yang dihadapi pemanggil GraphQL?

Aturan untuk penambahan sama, tapi eksposurnya berbeda. Deprecation skema GraphQL membahas model di mana client hanya menerima field yang secara eksplisit dimintanya, yang membuat perubahan aditif pada dasarnya tanpa risiko dan penghapusan menjadi satu-satunya bahaya nyata. Client gRPC, sebaliknya, menerima apa pun yang dikirim server dan menerjemahkan semuanya terhadap salinan skema hasil kompilasi mereka sendiri; paparan client tidak dibatasi oleh apa yang dimintanya, hanya oleh apa yang bisa dibaca kode yang dihasilkannya. Perbedaan itu penting untuk menulis changelog: entri GraphQL bisa secara wajar mengasumsikan bahwa client terlindungi dari field yang tidak mereka minta, dan entri gRPC sama sekali tidak bisa mengasumsikan itu.

Apakah memversikan layanan gRPC bekerja sama seperti /v1/, /v2/ REST?

Mekanismenya berbeda bahkan saat niatnya sama. Apa itu v1 dan v2 dalam API REST membahas versi sebagai jalur URL paralel yang melayani kontrak berbeda; layanan gRPC biasanya memversikan lewat nama paket di berkas .proto itu sendiri, payments.v1.InvoiceService menjadi payments.v2.InvoiceService, yang mengubah nama layanan yang sepenuhnya memenuhi syarat yang dipanggil client alih-alih segmen URL yang dimintanya. Kedua pendekatan menyelesaikan masalah yang sama, membiarkan kontrak lama tetap berfungsi sementara yang baru ada, tapi tim yang berasal dari latar belakang REST sering mencari nomor versi di tempat yang salah dan melewatkan bahwa deklarasi paket sedang melakukan pekerjaan itu.

Apa yang seharusnya benar-benar disebutkan entri changelog gRPC?

Pesannya, nomor field-nya, dan apakah itu aditif atau penghapusan yang membutuhkan migrasi, dalam urutan kepentingan itu bagi pembaca yang memutuskan apakah harus bertindak. “Menambahkan shipping_address (field 8) ke Order” memberi tahu integrator semua yang diperlukan untuk memperbarui kode yang dihasilkan dan mulai menggunakannya. “Mencadangkan field 4 pada Invoice, legacy_customer_id hilang” memberi tahunya untuk memeriksa apakah ada sesuatu di kode basisnya yang masih membaca field itu, sesuatu yang tidak dikomunikasikan catatan gaya REST “menghapus field dari respons” dengan urgensi yang sama, karena penghapusan REST hanya mengembalikan lebih sedikit data sementara penggunaan ulang field Protobuf secara aktif merusaknya.

FAQ

Bisakah tipe field pernah diubah tanpa merusak format wire? Hanya dalam grup kompatibel spesifik yang didokumentasikan Protobuf, seperti memperlebar int32 ke int64 dalam beberapa kasus. Perlakukan perubahan tipe apa pun sebagai breaking kecuali Anda sudah memeriksanya terhadap tabel kompatibilitas Protobuf sendiri; mengasumsikan kompatibilitas melalui analogi dengan sistem tipe suatu bahasa adalah bagaimana ini menjadi salah.

Apakah mendepresiasi field dalam Protobuf bekerja seperti direktif @deprecated GraphQL? Mirip: Protobuf mendukung opsi field [deprecated = true] yang bisa ditampilkan tooling. Keduanya tidak ditegakkan: server GraphQL tetap menjawab query untuk field yang di-deprecate, dan client protobuf tetap mengkodekannya. Keduanya bersifat penasihat dan membutuhkan dukungan changelog yang sama.

Apakah menomori ulang pernah aman jika Anda mengontrol setiap client? Dalam sistem yang sepenuhnya tertutup, pada prinsipnya, tapi itu menghilangkan seluruh properti keamanan yang menjadi alasan keberadaan nomor field, dan “kami mengontrol setiap client” adalah klaim yang berhenti benar begitu build di-cache, deploy tertunda, atau client ditambahkan yang tidak ada yang ingat. Cadangkan nomornya alih-alih menggunakannya ulang, bahkan secara internal.

Apakah layanan gRPC membutuhkan halaman changelog seperti API REST publik? Hanya jika tim eksternal mengonsumsinya tanpa membaca diff .proto secara langsung, tes “siapa yang ada di sisi lain” yang sama yang diterapkan changelog API internal secara umum. Layanan gRPC yang hanya dikonsumsi layanan lain dari tim yang sama sering bisa melewati changelog formal demi riwayat commit, karena siapa pun yang membacanya sudah membuka skemanya.


Klaim teknis dalam artikel ini belum ditinjau secara independen. Jika ada yang keliru, beri tahu kami dan kami akan memperbaikinya.

Terkait di changeloop: Dokumentasi developer, Perbandingan alat changelog

changeloop
Tim di balik changelog yang menutup lingkaran. Pengguna meminta sesuatu, timmu mengirimkannya, yang meminta jadi tahu.