Mungkin sudah ada yang familiar dengan SemVer, tulisan ini khusus untuk kamu yang belum familiar atau bahkan belum mengetahui tentang Semantic Versioning. Semantic Versioning atau SemVer sendiri bukanlah satu-satunya standar untuk melakukan versioning, ada Calendar Versioning ataupun menggunakan project codename.

Jika kamu membuat software ataupun library, pastinya kamu harus melakukan versioning. Wajib. Tidak perlu pertanyaan "kenapa?". Itu sudah menjadi standar untuk setiap orang yang menulis & merilis kodenya.

SemVer in high-level

Dalam SemVer, ada 3 istilah dan melakukan versioning: Major, minor, patch. Gambarannya adalah seperti ini:

2.33.7

  • 2 adalah versi major nya
  • 33 adalah versi minor nya
  • 7 adalah versi patch nya

Kita akan bahas lanjut seputar apa itu major, minor, patch dibawah.

Major version

Versi major adalah tentang perubahan yang terjadi terhadap public API. Bila versi major tersebut bernilai 1, berarti software tersebut baru dirilis ke publik. Ya, jika kamu baru pertama kali merilis software/library kamu ke publik, dengan catatan sudah "stabil", maka seharusnya kamu menggunakan nilai 1 untuk versi major nya.

Kondisi kapan versi major harus ditambah adalah ketika kamu melakukan perubahan terhadap public API yang mana perubahan tersebut sudah tidak mendukung versi sebelumnya.

Kita ambil contoh ketika React merilis versi 16. Apa saja perubahan yang terjadi?

  1. Behavior dalam scheduling dan lifecycle. Salah duanya adalah menggunakan "Fiber" dalam reconciliation dari yang sebelumnya menggunakan "Stack", dan memanggil setState di methods render akan menyebabkan perubahan dari yang sebelumnya tidak.
  2. Perubahan struktur direktori. Dari yang sebelumnya hanya memisahkan antara versi asli dan versi "minified" nya, sekarang ditambah: Versi production dan development meskipun masih tetap ada versi "minified" ataupun aslinya.

Yang artinya, perubahan yang terjadi dari versi major terbaru "berpengaruh" terhadap "behavior" dari versi sebelumnya. Seharusnya, versi major ini jangan cepat berubah. Atau users akan kabur karena sering kali terjadi perubahan yang membuat users harus "mempelajari lagi".

Di level aplikasi, mungkin bumping major version dilakukan ketika user seharusnya tidak bisa menikmati fitur sebelumnya bila memang menggunakan SemVer. Misal, dari yang sebelumnya (v1.x.x) pembayaran bisa menggunakan kartu kredit, di versi sekarang (v2.x.x) tidak bisa karena blablabla.

Intinya, jangan terlalu sering melakukan bumping version major. Atau user kamu akan kabur :))

Minor Version

Jika major version adalah tentang perubahan yang menyebabkan dampak terhadap behavior sebelumnya, di minor version tidak. Kita ambil contoh React Hooks, di versi 16.8.x. React Hooks adalah tentang membuat component dengan gaya fungsional, namun tetap stateful.

Sebelumnya untuk membuat component yang stateful, kita harus menggunakan class-based component. Tidak sedikit masalah yang dihadapi di class-based component: boilerplate, this binding, constructor, dsb. Dengan React hooks, kita bisa menyelesaikan masalah tersebut. Karena boilerplate-free, automatic this bindung, dan zero constructor. Because it just a function!

Hooks tidak mengubah cara kita membuat component, melainkan "menambah". Ya, minor version adalah tentang "fitur baru". Asiknya, upgrade minor version tidak akan menyebabkan dampak yang terjadi terhadap kondisi yang sudah ada.

Aman untuk diupgrade (sebagai users), dan aman untuk di rilis (sebagai owner).

Juga, bila terjadi "deprecated" terhadap public API, maka minor version harus ditambah nilainya. Deprecated adalah kondisi dimana API tersebut akan "dihilangkan" dan users harus menggantinya ke API terbaru sebelum melakukan upgrade ke major version terbaru. Bila version minor ditambah, maka versi patch harus di reset ke 0.

Patch Version

Di versi ini adalah tentang fix bug yang tidak menyebabkan perubahan terhadap public API. Karena fix bug biasanya hanya terkait terhadap kode internal, bukan public API yang digunakan oleh users.

Biasanya, versi patch di version ditambah bila terjadi bug yang sangat krusial. Bila perubahan tersebut tidak terlalu krusial, alias hanya berdampak "kecil" terhadap public API, seperti fix typo; Enhancement in performance dsb, rilis tersebut dilakukan di versi minor. Alias, digabung dan dianggap sebagai "fitur".

Namun bila bug seperti memory leak, seputar security dsb, maka harus melakukan bumping version patch dengan catatan tidak merubah behavior yang ada di public API.

Demo sederhana

Kita membuat library untuk berinteraksi dengan AWS S3. Karena belum stabil, kita rilis kode tersebut dengan versi 0.1.0 dengan catatan kode tersebut sudah bisa digunakan untuk melakukan proses upload dan membaca file dari S3.

Lalu kita menambah fitur baru, delete file. Kita tambah versi minornya, yang berarti menjadi 0.2.0. User yang sudah menggunakan kode kamu, bisa melakukan upgrade version terhadap library kamu untuk bisa menikmati fitur delete tanpa harus meragukan kompatibilitas terhadap kode yang ada sebelumnya yang bergantung dengan library kamu.

Lalu kamu menemukan bug dan mem-fix nya. Memory leak, proses tetap berjalan meskipun user sudah membatalkan proses tersebut yang menyebabkan library tetap mengupload namun konten nya kosong. Maka kamu harus melakukan bump version patch, yang berarti menjadi 0.2.1.

Lalu kamu membuat fitur baru: Rename file yang sudah ada. Selain itu, kamu juga melakukan fix terhadap bug-bug kecil seperti typo yang mengganggu. Dan juga, kamu melakukan beberapa improvement. Misal, bila user membaca file yang sama dalam 10 detik, maka library akan memberikan versi cache nya daripada harus me-request terus-menerus ke file yang sama dalam 10 detik oleh user yang sama.

Maka kamu melakukan bumping version terhadap versi minor, dan me-reset versi patch nya, yakni menjadi 0.3.0.

Ketika kita sudah merasa bahwa library ini sudah stabil untuk digunakan, kita bisa merilis fitur major nya yang berarti versi 1.0.0 dengan fitur-fitur yang sudah dijelaskan di minor version (namun bukan yang seputar patch nya).

Lalu perubahan terjadi, sekarang oktober 2019 dan AWS sudah tidak akan mendukung S3 per-desember 2019 nanti. Sebagai gantinya, AWS merilis S4 (Super Simple Storage Service) yang mana hampir sama dengan S3 namun menggunakan protokol http3 untuk proses read nya dan menggunakan protokol TUS untuk uploadnya.

Maka kita harus melakukan bumping version versi minor, bahwasannya API uploadFile akan deprecated, sebagai gantinya gunakan updateFile namun tetap di versi tersebut method uploadFile masih berjalan. Yang berarti, versi sekarang harus ditambah minor nya menjadi 1.1.0.

Lalu sekarang 10 desember, dan 20 desember AWS sudah tidak mendukung AWS S3 lagi. Maka kamu merilis versi baru library kamu yang berarti menjadi versi 2.0.0 dengan catatan method uploadFile sudah tidak bisa digunakan lagi dan menjadi updateFile sebagai gantinya.

Pertanyaan

Berikut pertanyaan yang sekiranya bakal ditanyakan beserta jawabannya.

Bagaimana bila dependensi yang saya gunakan versi major nya bertambah?

Selagi tidak mempengaruhi public API kamu (yang menyebabkan harus menghapus API tersebut), maka kamu tidak perlu menambah versi major kamu juga.

Bagaimana bila versi minor nya?

Selagi tidak mempengaruhi public API kamu  (yang menyebabkan harus menghapus API tersebut), dan kamu ingin menggunakan fitur baru dari dependensi yang kamu gunakan, silahkan tambah fitur tersebut dan bump versi minor kamu.

Bagaimana bila versi patch nya?

Sekali lagi, jika tidak mempengaruhi public API kamu  (yang menyebabkan harus menghapus API tersebut), namun ternyata bug tersebut berpengaruh juga terhadap public API kamu, maka kamu bisa tambahkan patch version nya dari library kamu.

Misal, kamu menggunakan dependensi fs-extra, dan dia merilis versi patch terbarunya karena terdapat bug di methods open. Karena library kamu menggunakan method tersebut, kamu bisa menambah versi patch kamu.

Atau bisa ditunda nanti ketika bump minor version bila memang bug tersebut tidak terlalu berpengaruh terhadap API kamu.

Apa maksud dari versi 1.0.0-alpha.2, 1.0.0-rc.2, 1.0.0-beta.1, dan 1.0.0-rc.1+build.f862f5d?

Itu adalah versi "pre-release". Pre-release memiliki beberapa level, yakni:

  • Alpha
  • Beta
  • Release Candidate

Yang berarti: versi 1.0.0-alpha.2 < 1.0.0-beta.1 < 1.0.0-rc.1+build.f862f5d < 1.0.0-rc.2.

Release candidate adalah ketika code sudah teruji baik di versi alpha dan beta. Untuk bagian +build.f862f5d sebagai identifier bahwa versi tersebut misalnya menggunakan commit dari f862f5d ketika mem-build kode tersebut.

Kesimpulan

Melakukan versioning sebenarnya tidak terlalu sulit, melainkan yang sulit itu adalah "strategi rilis" nya. Tidak heran mengapa ada pekerjaan berjudul "Release engineer" yang tugasnya untuk mengatur rilis dari software itu sendiri kepada users. Ini salah satu interview yang dilakukan Software Engineering Daily kepada Chuck Rossi, Engineering Director, Release Engineering di Facebook. Dengarkan podcastnya disini.

Developers harus bisa mengatur strategi bagaimana cara agar versi major tidak sering melakukan bumping, atau merilis versi minor yang memang sangat berdampak terhadap developers, dan pastinya versi patch yang tidak terlalu sering dirilis :))

Dan seharusnya "versioning" ini tidak hanya selalu tentang developers, seharusnya bisa diakses oleh siapa saja meskipun software yang kita buat bukan khusus untuk developer. Membuat "catatan rilis" yang unik memang tidak salah, namun alangkah lebih bagusnya bila "catatan rilis" tersebut memiliki catatan yang berguna oleh developers & users sehingga user memiliki alasan yang jelas untuk mengapa harus mengupdate ke versi tersebut.

Membuat catatan rilis (atau yang biasanya bernama CHANGELOG) juga penting, sehingga developers & users tau sejarah dan maksud dari perubahan versi tersebut secara historis. Developers seharusnya bukan hanya tentang menulis kode, karena kamu bukanlah seorang code monkey, bukan? :))

Dari tulisan ini semoga kita sudah mengenal sedikit tentang melakukan versioning, keuntungan dari versioning itu sendiri bukan hanya tentang fitur & bug, melainkan bisa untuk melakukan hal-hal lain seperti regression testing, a/b testing, dsb.

Terima kasih telah membaca! Diskusi disini.