Perubahan API

Breaking change: apa yang terhitung dan cara merilisnya

8 menit baca diperbarui

Breaking change adalah perubahan yang tidak bisa dilalui pemanggil yang ditulis dengan benar. Definisi ini penting karena kebanyakan perdebatan tentang apakah sesuatu “terhitung” sebenarnya adalah perdebatan tentang siapa yang salah pegang. Jika pemanggil mengikuti dokumentasi Anda dan perubahan Anda membuat kodenya berhenti bekerja, perubahan itu breaking. Apa yang Anda maksudkan tidak ada hubungannya dengan itu.

Itu seluruh tesnya. Sisa artikel ini adalah apa yang muncul darinya: apa yang gagal tes, apa yang lolos, cara menangkap kegagalan sebelum di-merge, dan apa yang harus dilakukan begitu Anda tahu sedang merilis satu.

Apa yang terhitung sebagai breaking change?

Terapkan tes ke pemanggil, bukan ke diff. Perubahan bersifat breaking ketika pemanggil yang hanya mengandalkan perilaku terdokumentasi harus mengubah kode, konfigurasi atau datanya untuk tetap berfungsi. Menghapus field, mengganti nama endpoint, memperketat validasi, mengubah default dan mengubah jenis nilai semuanya memenuhi syarat. Menambahkan field opsional tidak. Memperbaiki bug biasanya tidak, dengan satu pengecualian penting di bawah.

PerubahanBreaking?Mengapa
Menghapus atau mengganti nama field, endpoint, flag atau opsiYaPemanggil yang benar merujuknya
Menambahkan field opsional atau endpoint baruTidakPanggilan yang ada tidak berubah
Membuat input opsional menjadi wajibYaPanggilan yang melewatkannya sekarang gagal
Memperketat validasi yang sebelumnya diterimaYaInput yang berhasil sekarang ditolak
Mengubah nilai defaultYaPemanggil yang tidak mengaturnya mendapat perilaku baru
Mengubah jenis (string ke angka, nilai tunggal ke array)YaParser yang ditulis untuk jenis terdokumentasi gagal
Mengurutkan ulang kunci sebuah objekTidakKecuali Anda mendokumentasikan urutannya
Memperbaiki bug yang diandalkan pemanggilPraktiknya yaLihat bagian tentang kontrak tak sengaja
Menaikkan batas laju atau batas ukuranTidakTidak ada yang berhasil berhenti berfungsi
Menurunkan batas laju atau batas ukuranYaLalu lintas yang baik-baik saja sekarang dibatasi
Mengubah kata-kata pesan errorTergantungBreaking jika Anda mendokumentasikannya atau pemanggil mencocokkan itu

Apa yang bukan breaking change?

Perubahan bersifat non-breaking ketika setiap panggilan yang berhasil sebelumnya tetap berhasil, tanpa berubah, dan tetap bermakna sama. Menambahkan endpoint baru, menambahkan parameter permintaan opsional, menambahkan field ke respons, membuat input wajib menjadi opsional, menaikkan batas dan memperbaiki pesan error yang tidak dicocokkan siapa pun semuanya lolos tes. Perubahan aditif ini bisa dirilis dalam rilis minor dengan entri changelog biasa.

Perubahan aditif tetap bisa merusak pemanggil dalam tiga situasi. Klien yang deserializer-nya menolak field tak dikenal gagal pada field respons baru pertama, jadi dokumentasikan sejak awal bahwa pemanggil harus mengabaikan field yang tidak mereka kenali. Nilai enum baru merusak setiap pemanggil dengan switch yang menyeluruh (lebih lanjut di bawah). Dan respons yang membesar bisa mendorong pemanggil melewati batas ukuran, timeout atau lebar kolom yang tidak pernah harus mereka pikirkan.

Empat baris tabel layak dilihat lebih dekat, karena di situlah ketidaksepakatan terjadi.

Empat breaking change yang terlewat tim

Kontrak tak sengaja. Jika API Anda telah mengembalikan field yang sama, tidak terdokumentasi, selama tiga tahun, seorang pemanggil telah membangun di atasnya. Hukum Hyrum adalah versi singkatnya: dengan cukup banyak pengguna, setiap perilaku yang bisa diamati dari sistem Anda akan diandalkan seseorang. Itulah mengapa “itu perbaikan bug” bukan pembelaan. Perbaikannya mungkin benar dan tetap saja breaking. Rilis itu sebagai satu.

Perubahan perilaku tanpa perubahan skema. Field-nya masih ada, jenisnya sama, dan nilainya sekarang berarti sesuatu yang berbeda. status yang dulunya active atau inactive dan sekarang juga mengembalikan suspended merusak setiap pemanggil dengan switch yang menyeluruh. Timestamp yang pindah dari waktu lokal ke UTC merusak siapa pun yang tidak membaca dokumen dua kali. Tidak ada di diff berkas OpenAPI yang menunjukkan ini.

Validasi yang diperketat. Anda mulai menolak email tanpa TLD, atau spasi di akhir, atau nama lebih dari 80 karakter. Setiap pemanggil yang mengirim tepat itu sekarang mendapat 400 untuk permintaan yang berhasil minggu lalu. Perubahan validasi adalah yang paling umum dirilis sebagai perbaikan “pengerasan”.

Default yang berubah. Tidak ada yang menyetel nilai secara eksplisit yang menyadari apa pun. Semua yang tidak melakukannya, yang merupakan kebanyakan pemanggil, mendapat perilaku baru tanpa mengubah satu baris pun. Default yang berubah merusak mayoritas pengguna Anda tepatnya karena mereka tidak pernah melihat pengaturannya.

Bagaimana cara mendeteksi breaking change sebelum dirilis?

Bandingkan kontrak pada pull request dengan kontrak pada branch utama, di CI, dan gagalkan build jika ada perbedaan yang breaking. Alat diff skema tersedia untuk sebagian besar format antarmuka, dan masing-masing tahu aturan breaking dari formatnya sendiri:

AntarmukaAlatYang dibandingkan
REST (OpenAPI)oasdiffDua spesifikasi OpenAPI, dengan laporan breaking change
gRPC (Protobuf)buf breakingBerkas .proto, pada tingkat wire atau sumber
GraphQLGraphQL InspectorDua skema, menandai perubahan breaking dan berbahaya
Crate Rustcargo-semver-checksAPI publik terhadap versi terbitan terakhir
Paket TypeScriptAPI ExtractorLaporan API publik paket yang di-commit

Alat-alat ini menangkap field yang dihapus, operasi yang diganti nama dan jenis yang berubah dengan andal. Mereka tidak bisa melihat dua jenis pertama dari empat di atas, kontrak tak sengaja atau perubahan perilaku, karena keduanya tidak muncul di skema. Gunakan alatnya untuk menghentikan yang jelas dan pertanyaan tinjauan “bisakah pemanggil yang benar menyadari ini?” untuk sisanya. Job CI yang sama adalah tempat alami untuk mewajibkan entri changelog, seperti dijelaskan di menegakkan entri changelog di CI, dan perubahan API gRPC dan Protobuf membahas kasus tingkat wire.

Bagaimana cara menandai breaking change di commit?

Dengan Conventional Commits, breaking change ditandai dengan ! sebelum titik dua (feat(api)!: remove the legacy export endpoint) atau dengan footer yang diawali BREAKING CHANGE: diikuti deskripsi. Keduanya dipetakan ke versi major. Tulis footer sebagai draf pertama entri changelog, dengan menyebut siapa yang terdampak dan apa yang harus mereka lakukan. Conventional commits dan changelog membahas sejauh mana konvensi itu membantu Anda.

Aturan yang sama berlaku untuk pustaka. Fungsi publik yang dihapus, jenis parameter yang dipersempit atau nilai kembalian yang berubah adalah versi major di bawah versi semantik. Pustaka tidak selalu mengikutinya: sebuah studi atas 119.879 peningkatan versi di Maven Central menemukan 16,6% melanggar versi semantik, namun hanya 7,9% proyek klien yang terdampak, karena sebagian besar perubahan itu menyentuh kode yang tidak dipanggil klien mana pun. Kerusakan diukur di pemanggil.

Bagaimana cara merilis breaking change?

Anda merilisnya secara terbuka, dengan tanggal, dengan jalur. Langkah-langkah di bawah ini berurutan, dan yang terakhir adalah yang paling sering dilewati tim: memberi tahu orang yang terdampak bahwa hal yang mereka tunggu sekarang telah terjadi.

  1. Putuskan apakah itu satu. Gunakan tes di atas, bukan diff. Jika dua insinyur tidak sepakat, itu breaking; ketidaksepakatan itu bukti bahwa pemanggil bisa saja secara wajar mengandalkan perilaku lama.
  2. Versikan. Di bawah versi semantik breaking change adalah versi major. Jika Anda menjalankan API bertanggal atau berversi, itu masuk ke versi baru dan yang lama tetap berfungsi hingga tanggal yang dinyatakan. Jika Anda tidak bisa memversi, Anda tidak merilis breaking change, Anda merilis gangguan dengan entri changelog. Skema mana yang membawa versinya adalah subjek dari praktik terbaik versi API.
  3. Tulis entri sebelum kode di-merge. Entri punya bentuk tetap: apa yang berubah, siapa yang terdampak, apa yang harus mereka lakukan, dan sampai kapan. Jika Anda tidak bisa mengisi keempatnya, perubahan belum siap. Template release notes meletakkan entri ini pertama, dengan tanggal alih-alih nomor versi, tepatnya karena ini.
  4. Berikan tenggat waktu, bukan nomor rilis. “Dihapus di v5” tidak berarti apa-apa bagi yang tidak melacak rilis Anda. “Berhenti berfungsi pada 1 November 2026” berarti hal yang sama untuk semua orang.
  5. Sediakan migrasinya. Contoh kode dari panggilan lama di samping yang baru. Jika perubahannya adalah penggantian nama, sebutkan kedua nama di kalimat yang sama. Jika field yang dihapus, katakan ke mana datanya pergi.
  6. Umumkan di mana pun perilaku lama didokumentasikan. Changelog, halaman dokumen yang menjelaskan endpoint, release notes SDK, dan header deprecation di respons jika Anda punya satu.
  7. Tutup lingkarannya. Jika pelanggan meminta perubahan, atau melaporkan bug yang menyebabkannya, beri tahu mereka saat dirilis.

Seperti apa entri breaking change yang baik?

Entri yang baik menyebutkan pemanggil yang terdampak di baris pertama, menyatakan tanggalnya, dan menyertakan perbaikannya. Berikut satu untuk kasus validasi yang diperketat, dalam bentuk yang kami gunakan:

Alamat email tanpa domain ditolak mulai 1 November 2026. POST /users dan PATCH /users/:id saat ini menerima nilai email seperti alice@localhost. Mulai 1 November, ini mengembalikan 400 invalid_email. Memengaruhi integrasi mana pun yang membuat pengguna dari direktori internal. Migrasi: kirim alamat yang sepenuhnya memenuhi syarat, atau lewatkan field-nya dan atur kemudian. Tidak perlu perubahan jika alamat Anda sudah punya domain, yang berlaku untuk 99,4% akun yang dibuat tahun ini.

Di mana pemberitahuan ini seharusnya berada, dan apa lagi yang seharusnya menyertainya, dibahas di changelog API.

Persentase di akhir bukan hiasan. Itu memberi tahu pembaca apakah mereka harus khawatir, yang merupakan pertanyaan saat mereka membuka entri.

Mengapa tidak menghindarinya saja?

Karena alternatifnya lebih buruk. API yang tidak pernah merusak apa pun menumpuk setiap kesalahan yang pernah dibuatnya: field yang salah nama, default yang salah, timestamp dalam waktu lokal. Masing-masing adalah pajak bagi setiap pemanggil baru selamanya, untuk melindungi pemanggil yang bisa saja bermigrasi dalam satu sore. Tim dengan reputasi stabilitas terbaik merusak sesuatu jarang, sesuai jadwal, dengan jalur migrasi dan peringatan yang mencapai orang-orang yang dituju.

Mekanisme peringatan itu dibahas di men-deprecate API. Entri itu sendiri disusun seperti entri lain mana pun di feed changelog: dari pull request yang di-merge, ditahan untuk manusia, lalu diterbitkan di tempat pemanggil yang terdampak sudah membaca.

FAQ

Apa perbedaan antara perubahan breaking dan non-breaking? Perubahan breaking memaksa pemanggil yang benar mengubah kode, konfigurasi atau datanya agar tetap berfungsi. Perubahan non-breaking membiarkan setiap panggilan yang ada tetap berfungsi dengan makna yang sama, itulah mengapa penambahan biasanya aman dan penghapusan, penggantian nama serta aturan yang diperketat biasanya tidak.

Apakah menambahkan field wajib terhitung? Ya. Setiap panggilan yang ada melewatkannya, jadi setiap panggilan yang ada sekarang gagal. Tambahkan sebagai opsional dengan default yang masuk akal, atau versikan endpoint-nya.

Apakah perbaikan bug terhitung? Bisa jadi. Jika pemanggil mengandalkan perilaku yang bug, memperbaikinya merusak mereka, apa pun yang dikatakan dokumentasi. Perlakukan perbaikan apa pun yang mengubah keluaran yang bisa diamati sebagai breaking kecuali Anda bisa menunjukkan tidak ada yang mengandalkannya.

Apakah versi semantik berlaku untuk API web? Aturannya iya: breaking change mendapat versi major baru dan yang lama tetap berfungsi selama periode yang dinyatakan. Nomornya sering berada di URL atau header tanggal alih-alih versi paket.

Berapa banyak pemberitahuan yang cukup? Cukup bagi pemanggil untuk menemukan pemberitahuan dan melakukan pekerjaannya. Sembilan puluh hari adalah batas bawah umum untuk API publik; lebih lama untuk apa pun yang digunakan dalam kode yang dikirim ke pengguna akhir dan tidak bisa diperbarui dari jarak jauh.


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

Terkait di changeloop: Templat catatan rilis, Dokumentasi developer

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