![Prinsip Menulis Versi pada Software (Semantic Versioning)](https://images.ctfassets.net/7wjlcr9d5ii8/5CFHkIE2v92dCTBZFkZyl9/6bdbfb353a47c29360975b4a61ffca81/software_logo_with_version.jpeg?w=1024&h=1024&fl=progressive&q=100&fm=jpg&fit=thumb&f=faces)
Semantic Versioning (SemVer) adalah prinsip penulisan versi pada software yang bertujuan untuk menjelaskan dasar perubahan secara garis besar pada produk yang diadopsi sebagai best practice perilisan software di masa sekarang. Terutama di open source, ini udah jadi standar wajib di template perilisan library. Versi pada software dipisah menjadi pola tiga bagian, yaitu ${MAJOR}.${MINOR}.${PATCH}
atau ${MAJOR}.${MINOR}.${PATCH}-${PRE-RELEASE}
jika ada Pre-release.
Major version adalah versi secara garis besar. Major version akan di-increment saat merilis versi dengan banyak perubahan, seperti perombakan code, perubahan requirement, ada fitur yang dihapus, penambahan fitur secara besar-besaran, atau perubahan yang tidak backward-compatible dengan versi Major sebelumnya. Jadi, ekspektasi user saat melakukan upgrade Major version adalah akan menghadapi kemungkinan error karena tidak backward-compatible pada software mereka. Contohnya ketika menggunakan Spring Boot 2.0.0 lalu upgrade ke Spring Boot 3.0.0 maka kemungkinan besar akan menghadapi error dan perlu penyesuaian agar compatible dengan versi terbaru. Seperti pada Spring Boot 2.0.0 requirement minimalnya adalah Java 8, sedangkan pada Spring Boot 3.0.0 requirement minimalnya adalah Java 17. Jadi, user juga perlu upgrade Java saat upgrade Spring Boot.
Minor version adalah versi perubahan yang backward-compatible. Minor version akan di-increment saat merilis fitur yang tidak berdampak error pada existing software, dan akan di-reset ke “0” saat Major version rilis. Jadi, ekspektasi user ketika melakukan upgrade Minor version adalah bisa menikmati fitur baru dan aman dari error. Kalaupun ada rencana perubahan yang ga backward-compatible, maka di Minor version perlu diberi warning, bukan langsung dihapus. Misalnya kita ingin mengganti sebuah function, maka kita tidak boleh hapus function tersebut di Minor version. Kita harus mempertahankan function tersebut dan diberi tanda @Depcreated
, lalu bikin function baru. Jadi, nantinya kita handle dua function di Minor version terbaru. Sehingga user yang menggunakan produk kita aplikasinya masih backward-compatible, tapi dengan warning agar mereka aware terhadap function baru. Function yang deprecated itu boleh dihapus saat Major version rilis. Intinya, di Minor version perubahan boleh dilakukan tapi harus backward-compatible. Contohnya pada AssertJ, ketika sebelumnya menggunakan AssertJ versi 3.16.0 lalu upgrade ke versi 3.17.0, user bisa menggunakan fitur baru BDDAssertions.thenNoException()
pada assertion unit test, dan upgrade tersebut ga akan menyebabkan error.
Patch version adalah versi yang di-increment ketika merilis bugfix, improvement, atau refactor yang masih backward-compatible dengan versi sebelumnya, dan akan di-reset ke “0” saat Minor version atau Major version rilis. Jadi, ekspektasi user saat melakukan upgrade Patch version adalah tidak ada perubahan terkait fitur, kecuali perbaikan performa atau bugfixes dari versi sebelumnya. Misalnya di versi sebelumnya kompleksitas sebuah function adalah O(n). Di versi baru, kompleksitasnya jadi O(1) setelah algoritmanya diganti. Atau sebelumnya ada error pada case tertentu, setelah di-upgrade jadi ga ada error. Tapi, jika fixing tersebut membutuhkan fitur baru, maka itu dirilis pada Minor version, bukan Patch version. Misalnya, bugs tersebut diperbaiki dengan cara membuat class baru, maka itu tidak bisa dirilis di Patch version, tapi di Minor version dan class yang lama diberi tanda @Deprecated
. Contoh rilis Patch version adalah pada library date-fns versi 3.0.5 ada bugs pada function areIntervalsOverlapping()
, lalu di versi 3.0.6 bugs tersebut diperbaiki.
Selain itu, ada lagi namanya Pre-release version, yaitu versi yang dirilis sebagai uji coba sebelum final rilis. Ini sebenarnya ga wajib, tergantung kebutuhan. Pre-release ini tujuannya untuk kebutuhan testing, bukan untuk production, karena masih uji coba dan ada kemungkinan dibatalkan sebelum rilis jika dinilai belum layak. Kalau mau digunakan di production boleh-boleh aja sih, tapi segala resiko ditanggung sendiri karena belum stabil. Pre-release ini ditulis dengan suffix “-alpha”, “-beta”, atau “-rc” (release candidate), beserta incremental number di belakangnya tergantung kesepakatan. Misalnya, sebelum merilis versi 3.1.0, maka sebagai uji coba versi 3.1.0-alpha1 yang dirilis. Jika ada fase uji coba tingkat selanjutnya, maka bisa ditulis 3.1.0-beta1. Atau jika menggunakan “-rc”, maka penulisannya jadi 3.1.0-rc1. Setelah melewati fase uji coba dan dinyatakan layak dirilis, barulah versi 3.1.0 tanpa suffix Pre-release dirilis. Contohnya library SLF4J, sebelum merilis versi 2.0.0, mereka merilis versi 2.0.0-alpha1, 2.0.0-alpha2, 2.0.0-beta1, 2.0.0-beta2, hingga versi 2.0.0 rilis.
Contoh simulasi menggunakan Semantics Versioning adalah sebagai berikut:
- Saat pertama rilis: 1.0.0;
- Ada bugfix suatu function: 1.0.1;
- Ada improvement performa: 1.0.2;
- Ada penambahan function baru yang backward-comaptible: 1.1.0;
- Ada bugfix terhadap function baru: 1.1.1;
- Ada function yang deprecated dan perlu diganti dengan yang baru: 1.2.0;
- Uji coba versi baru dengan perombakan dari versi sebelumnya: 2.0.0-alpha;
- Fitur Pre-release passed semua testing dan layak dirilis: 2.0.0;
- Ada improvement performa dari rilis sebelumnya: 2.0.1;
- Ada penambahan function baru lagi: 2.1.0;
- Begitu seterusnya...
Dengan SemVer, user yang menggunakan produk kita memiliki ekspektasi yang jelas sebelum memutuskan melakukan upgrade. Mereka paham resikonya tanpa harus membaca release notes satu-persatu. Release notes tetap penting ditulis, tapi kan ga mungkin user harus membacanya satu-persatu, apalagi jika changes-nya banyak. Dengan SemVer setidaknya mereka paham resiko secara garis besarnya. Proses upgrade bisa jadi lebih rapi dan lancar.