Proposal untuk Versi Paket dalam Go

Russ Cox
26 Maret 2018

Pendahuluan

Delapan tahun yang lalu, tim Go memperkenalkan goinstall (yang menyebabkan adanya go get) dan bersamaan dengannya muncul lah path impor, yang ter-desentralisasi dan mirip URL, yang dikenal oleh para pengembang Go sampai sekarang. Setelah kami merilis goinstall, salah satu pertanyaan yang diajukan oleh orang adalah bagaimana menerapkan informasi versi. Kami akui kami tidak tahu. Sepanjang waktu, kami percaya bahwa permasalahan dari versi paket akan lebih baik ditangani oleh perkakas tambahan, dan kami mendukung para pengembang lain untuk membuatnya. Komunitas Go membuat banyak perkakas dengan pendekatan yang berbeda-beda. Tiap-tiapnya membantu kami lebih memahami permasalahan yang dihadapi, namun pada pertengahan 2016 cukup jelas bahwa sekarang sudah terlalu banyak solusi. Kami butuh mengadopsi sebuah perkakas resmi.

Setelah diskusi komunitas di GopherCon pada Juli 2016 yang berlanjut sampai musim gugur, kami semua percaya bahwa jawabannya adalah mengikuti pendekatan versi paket yang dicontohkan oleh Cargo dari bahasa Rust, dengan sebuah tag versi semantik, sebuah manifesto, dan sebuah berkas pengunci, dan sebuah SAT solver yang memutuskan versi mana yang digunakan. Sam Boyer memimpin sebuah tim menciptakan Dep, yang mengikuti langkah dasar tersebut, dan yang kita gunakan sebagai model untuk integrasi dengan perintah go. Namun semakin kami belajar implikasi dari pendekatan Cargo/Dep, semakin jelas bagi saya bahwa Go akan lebih baik bila menggunakan beberapa pendekatan yang berbeda, terutama yang memperhatikan kompatibilitas.

Impak dari Kompatibilitas

Fitur baru paling penting dari Go 1 bukanlah sebuah fitur bahasa. Melainkan penekanan pada kompatibilitas. Sampai pada titik tersebut kami telah melakukan rilis stabil setiap bulan, setiap rilis dengan perubahan yang tidak kompatibel. Kami melihat adanya akselerasi yang signifikan dalam ketertarikan dan adopsi secara langsung setelah rilis dari Go 1. Kami percaya bahwa dengan menjamin kompatibilitas membuat pengembang merasa lebih nyaman bergantung pada Go untuk penggunaan di tingkat produksi dan salah satu alasan kunci bahwa Go sekarang menjadi terkenal. Sejak 2013, Go FAQ telah mendukung para pengembang paket untuk menyediakan bagi pengguna mereka ekspektasi yang sama dari kompatibilitas. Kami menyebutnya dengan aturan kompatibilitas impor: "Jika sebuah paket lama dan sebuah paket baru memiliki path impor yang sama, maka paket yang baru haruslah kompatibel dengan paket lama."

Secara independen, versi semantik telah menjadi standar de facto untuk menentukan versi pada perangkat lunak di dalam banyak komunitas bahasa pemrograman, termasuk komunitas Go. Dengan menggunakan versi semantik, versi selanjutnya diharapkan supaya kompatibel dengan versi sebelumnya, selama masih dalam versi mayor yang sama: v1.2.3 haruslah kompatibel dengan v1.2.1 dan v1.1.5, namun v2.3.4 tidak perlu kompatibel dengan salah satu diantaranya.

Jika kita mengadopsi versi semantik untuk paket-paket Go, seperti yang para pengembang Go harapkan, maka aturan kompatibilitas impor mengharuskan bahwa versi mayor yang berbeda harus menggunakan path impor yang berbeda. Observasi ini membawa kita ke semantic import versioning (versi impor semantik), yang mana versi v2.0.0 mengikutkan versi mayor di dalam path impor: "my/thing/v2/sub/pkg".

Setahun yang lalu saya percaya bahwa mengikutkan nomor versi ke dalam path impor hanya lah masalah selera, dan saya skeptis bahwa dengan menggunakan versi pada path impor tidak terlalu elegan. Namun keputusannya ternyata bukanlah karena selera namun karena logis: kompatibilitas impor dan versi semantik bersama-sama membutuhkan semantic import versioning. Saat saya menyadari ini, kebutuhan logis tersebut mengejutkan saya.

Saya juga terkejut menyadari bahwa ada rute logika independen kedua terhadap semantic import versioning: perbaikan kode secara gradual atau pembaruan kode secara parsial. Dalam sebuah program yang besar, adalah hal yang tidak realistis untuk berharap semua paket dalam program untuk memperbarui sebuah dependensi tertentu dari v1 ke v2 pada saat bersamaan. Namun, akan lebih memungkinkan bagi beberapa program tetap menggunakan v1 sementara bagian lain di-upgrade ke v2. Ketika program dibangun, dan hasil akhir program harus mengikutkan kedua dependensi v1 dan v2 tersebut. Membuat kedua versi tersebut menggunakan path impor yang sama akan mengakibatkan kebingungan, melanggar apa yang kita sebut aturan keunikan impor: paket yang berbeda haruslah memiliki path impor yang berbeda. Satu-satunya cara untuk pembaruan kode secara parsial, dengan keunikan impor, dan versi semantik adalah dengan mengadopsi semantic import versioning juga.

Tentu saja memungkinkan untuk membangun sistem yang menggunakan versi semantik tanpa semantic import versioning, tetapi kita akan kehilangan pembaruan kode secara parsial atau keunikan impor. Cargo membolehkan pembaruan kode secara parsial dengan kehilangan keunikan impor: sebuah path impor bisa memiliki arti yang berbeda di bagian yang berbeda pada sebuah pembangunan. Dep menjamin keunikan impor dengan kehilangan pembaruan kode secara parsial: semua paket-paket harus menggunakan satu versi dependensi yang sama, menyebabkan kemungkinan adanya program yang besar tidak bisa dibangun ulang. Cargo benar dalam memaksa pembaruan kode parsial, yang mana adalah sebuah hal yang kritis dalam pengembangan perangkat lunak berskala besar. Dep juga benar dengan memaksa keunikan impor. Penggunaan direktori "vendor" yang kompleks pada Go dapat melanggar keunikan impor. Kedua masalah tersebut sangat cukup menantang bagi pengembang dan perkakas untuk dipahami. Dengan memilih antara pembaruan kode parsial dan keunikan impor membutuhkan prediksi yang mana lebih merugikan bila dihilangkan. Versi impor semantik membuat kita menghindari pilihan tersebut dan memiliki keduanya.

Saya juga cukup terkejut menjumpai berapa banyak kompatibilitas impor menyederhanakan pemilihan versi, yang mana merupakan permasalahan menentukan versi paket mana yang digunakan untuk sebuah pembangunan. Batasan-batasan dari Cargo dan Dep membuat pemilihan versi sama dengan pemecahan kepuasan Boolean, artinya, bisa sangat mahal untuk menentukan apakah sebuah konfigurasi versi yang valid itu benar ada. Dan bisa saja banyak konfigurasi yang valid, tanpa ada kriteria untuk memilih yang "terbaik". Bergantung pada kompatibilitas impor membuat Go menggunakan algoritme yang biasa, dengan waktu yang linear untuk menemukan sebuah konfigurasi yang terbaik, yang selalu ada. Algoritme ini, yang saya sebut dengan minimal version selection (pemilihan versi minimum), ternyata mengeliminasi kebutuhan untuk berkas lock dan manifest yang terpisah. Ia menggantinya dengan sebuah berkas konfigurasi yang singkat, disunting secara langsung oleh pengembang dan perkakas, yang masih mendukung pembangunan yang dapat direproduksi.

Pengalaman kita dengan Dep menunjukkan impak dari kompatibilitas. Mengikuti pengembangan Cargo dan sistem sebelumnya, kami merancang Dep untuk menyerah dengan kompatibilitas impor supaya dapat mengadopsi versi semantik. Saya tidak percaya bahwa kita memutuskan ini secara sengaja; kami hanya mengikuti sistem-sistem yang lain. Pengalaman awal saat mencoba Dep membantu kita memahami lebih baik kompleksitas yang disebabkan oleh adanya path impor yang tidak kompatibel. Dengan menghidupkan kembali aturan kompatibilitas impor dengan memperkenalkan versi impor semantik menghilangkan kompleksitas tersebut, menghasilkan sistem yang lebih simpel.

Progres, Prototipe, dan Proposal

Dep dirilis Januari 2017. Model dasarnya--kode yang di-tag dengan versi semantik, bersamaan dengan sebuah berkas konfigurasi yang menspesifikasikan kebutuhan dependensi--adalah langkah paling maju dari kebanyakan perkakas vendor Go, dan menggabungkan ke Dep itu sendiri juga merupakan tahap yang jelas. Saya dengan sepenuh hati mendukung adopsi Dep, terutama untuk membantu pengembang supaya terbiasa berpikir tentang versi paket Go, baik untuk kode mereka sendiri dan bagi dependensinya. Sementara Dep tampak jelas membawa kita ke arah yang benar, saya memiliki kekhawatiran tentang detail kompleksitas di belakangnya. Saya secara khusus khawatir tentang tidak adanya dukungan untuk pembaruan kode parsial pada Dep untuk program yang besar. Selama tahun 2017, saya berbicara dengan banyak orang, termasuk Sam Boyer dan kelompok kerja manajemen paket lainnya, namun tidak ada dari kita yang dapat melihat cara untuk mengurangi kompleksitas. (Saya menemukan banyak pendekatan yang ditambahkan ke Dep.) Mendekati akhir tahun, tampaknya solusi SAT dan pembangunan yang tidak memuaskan mungkin hal yang terbaik yang dapat kita lakukan.

Pada pertengahan November, mencoba sekali lagi menyelesaikan bagaimana supaya Dep dapat mendukung pembaruan kode parsial, saya menyadari bahwa saran lama kita tentang kompatibilitas impor mengimplikasikan versi impor semantik. Hal ini tampak seperti solusi yang nyata. Saya menulis draf pertama yang menjadi artikel versi impor semantik, dengan kesimpulan menyarankan supaya Dep mengadopsi konvensi tersebut. Saya mengirim draf tersebut ke orang-orang yang telah berbicara dengan saya sebelumnya, dan mendapatkan respons yang sangat kuat: semua orang menyukainya atau membencinya. Saya menyadari bahwa saya perlu menjelaskan lebih lanjut tentang implikasi dari versi impor semantik sebelum menyebarkan ide tersebut lebih luas, dan saya lakukan itu.

Pada pertengahan Desember, saya menemukan bahwa kompatibilitas impor dan versi impor semantik keduanya membolehkan memotong pemilihan versi menjadi pemilihan versi minimum. Saya menulis implementasi dasar untuk memastikan apakah saya benar-benar paham, saya habiskan beberapa waktu untuk mempelajari teori di balik kenapa ia begitu sederhana, dan saya tulis sebuah draf artikel yang menjelaskan hal tersebut. Walaupun begitu, saya masih tidak yakin pendekatan tersebut akan berguna bagi perkakas seperti Dep. Cukup jelas bahwa sebuah prototipe diperlukan.

Pada bulan Januari 2018, saya mulai mengerjakan sebuah perintah go sederhana yang mengimplementasikan versi impor semantik dan pemilihan versi minimum. Beberapa pengujian sederhana bekerja dengan baik. Mendekati akhir bulan, program saya dapat membangun Dep, sebuah program yang dibuat dari banyak versi paket-paket. Program saya tersebut masih belum memiliki antarmuka baris-perintah--walaupun ia bisa membangun Dep namun dengan kode yang menggunakan beberapa konstanta--tetapi pendekatan tersebut sangat memungkinkan.

Saya habiskan tiga minggu awal Februari membuat program tersebut menjadi sebuah program go terpisah, vgo; menulis draf dari seri blog memperkenalkan vgo; dan mendiskusikannya dengan Sam Boyer, kelompok kerja manajemen paket, dan tim Go. Dan kemudian saya habiskan seminggu terakhir Februari berbagi tentang vgo dan gagasan-gagasan di baliknya dengan seluruh komunitas Go.

Sebagai tambahan dari ide inti dari kompatibilitas impor, versi impor semantik, dan pemilihan versi minimum, prototipe vgo memperkenalkan sejumlah perubahan kecil yang signifikan yang dimotivasi oleh delapan tahun pengalaman dengan goinstall dan go get: konsep baru dari sebuah Go modul, yang merupakan kumpulan paket-paket dengan versi sebagai sebuah unit tersendiri; pembangunan yang dapat diverifikasi dan pasti; dan perintah go yang mengenal versi, membolehkan bekerja di luar $GOPATH dan menghilangkan (kebanyakan) direktori vendor.

Hasil dari semua itu adalah sebuah proposal Go yang resmi, yang saya kirim akhir minggu lalu. Walaupun tampak seperti implementasi yang penuh, ia masih prototipe, sebuah karya yang harus kita selesaikan bersama. Anda bisa mengunduh dan mencoba prototipe vgo lewat golang.org/x/vgo, dan Anda bisa membaca Tur dari Go berversi untuk merasakan seperti apa vgo.

Langkah ke depan

Proposal yang saya kirim minggu lalu adalah proposal awal. Saya tahu bahwa ada permasalahan yang tim Go dan saya tidak dapat lihat, karena para pengguna Go menggunakan bahasa Go dengan cara yang cerdik yang tidak kita ketahui. Tujuan dari proses umpan-balik proposal adalah supaya kita bekerja bersama mengidentifikasi dan menyelesaikan permasalahan tersebut dalam proposal yang sekarang, untuk memastikan bahwa implementasi yang final yang diikutkan pada rilis Go selanjutnya bekerja dengan benar untuk semua pengembang sebisa mungkin. Silahkan ajukan permasalahan di isu diskusi proposal. Saya akan mencatat kesimpulan diskusi dan memperbarui FAQ saat saran-saran bermunculan.

Agar proposal ini sukses, ekosistem Go secara keseluruhan--dan khususnya proyek-proyek Go besar sekarang--perlu mengadopsi aturan kompatibilitas impor dan versi impor semantik. Supaya dapat berjalan dengan mulus, kami juga melakukan sesi umpan-balik lewat konferensi video dengan proyek-proyek yang memiliki pertanyaan tentang bagaimana menggunakan proposal versioning yang baru dengan basis kode mereka atau menerima saran tentang pengalaman mereka. Jika Anda tertarik ikut serta dalam sesi seperti itu, silahkan kirim email ke Steve Francia di spf@golang.org.

Kami menantikan (akhirnya!) menyediakan komunitas Go sebuah jawaban resmi terhadap pertanyaan bagaimana menggunakan versi paket pada go get. Terima kasih untuk semua orang yang telah membantu kita sampai sekarang, dan kepada semua orang yang akan membantu kita maju di masa depan. Kami berharap, dengan bantuan Anda, kita dapat membuat sesuatu yang disukai oleh pengembang Go.