- Go 1.21 berfokus pada penurunan beban upgrade dengan memperluas kompatibilitas berbasis GODEBUG, agar toolchain baru dapat mengimplementasikan perilaku versi Go lama se-stabil mungkin
- Sejak Go 1 pada 2012, Go telah menjanjikan kompatibilitas source dan mengurangi kerusakan akibat penghapusan atau perubahan melalui pemeriksaan API publik serta pengujian internal berskala besar
- Bahkan perbaikan yang diizinkan dalam dokumentasi seperti peningkatan presisi
time.Now, perubahan implementasi sort, perubahan output compress/flate, perluasan input strconv.ParseInt, dan perubahan parsing net.ParseIP tetap dapat merusak program lama
- Mulai Go 1.21, pengaturan GODEBUG untuk kompatibilitas dipertahankan minimal selama 2 tahun atau 4 rilis Go, dan dapat mempertahankan perilaku lama sebagai nilai default berdasarkan versi
go di go.mod
- Go 2 tidak akan hadir sebagai spesifikasi baru yang merusak program Go 1; meski menambahkan fitur baru, Go tetap memprioritaskan kompatibilitas agar upgrade toolchain tetap stabil
Prinsip dasar kompatibilitas Go 1
- Pada Go 1 tahun 2012, melalui dokumen “Go 1 and the Future of Go Programs”, Go menetapkan tujuan bahwa program yang sesuai dengan spesifikasi Go 1 akan terus dapat dikompilasi dan dijalankan dengan benar tanpa perubahan selama masa hidup spesifikasi tersebut
- Inti dari janji ini adalah kompatibilitas source
- Saat memperbarui ke versi Go baru, kode harus dikompilasi ulang
- API baru boleh ditambahkan, tetapi penambahan yang merusak kode lama harus dihindari
- Tidak mungkin menjamin bahwa perubahan di masa depan tidak akan pernah merusak program apa pun
- Jika program bergantung pada perilaku bug, program itu bisa rusak saat bug tersebut diperbaiki
- Go berupaya menjaga upgrade tetap stabil sambil meminimalkan kerusakan semaksimal mungkin
Mencegah kerusakan kompatibilitas dengan pemeriksaan API publik
- Dalam proses pengembangan Go, daftar API publik tiap package dikelola di file terpisah dari package sebenarnya
- Sebagai contoh,
go/api/go1.21.txt mencatat entri fungsi, metode, dan tipe dari bytes, cmp, context, dan lainnya
- Pengujian standar memeriksa apakah API package yang sebenarnya sesuai dengan file-file tersebut
- Jika API baru ditambahkan, file API juga harus diperbarui agar pengujian lulus
- Jika API lama diubah atau dihapus, pengujian akan gagal
- Bukan hanya penghapusan API, perubahan tipe juga bisa merusak kompatibilitas
os.Stdout adalah variabel global bertipe *os.File
- Jika itu diubah menjadi interface dengan metode yang sama, kode seperti
greet(f *os.File) yang memerlukan *os.File akan rusak
- Pemeriksaan API berguna untuk menangkap perubahan atau penghapusan API, tetapi tidak dapat mencegah semua perubahan tidak kompatibel yang mungkin terjadi di Go
Kerusakan halus yang terungkap lewat pengujian
- Versi pengembangan dari rilis Go baru terus diuji terhadap seluruh kode Go internal di Google
- Jika pengujian lolos, commit tersebut dipasang ke toolchain Go produksi milik Google
- Jika pengujian internal rusak, itu dianggap sebagai indikasi bahwa kode eksternal juga mungkin rusak, lalu dicari cara untuk mengurangi dampaknya
- Dalam kebanyakan kasus, perubahan dibatalkan atau ditulis ulang agar tidak merusak program
- Beberapa perubahan tetap penting dan dapat dipertahankan sebagai perubahan yang kompatibel menurut dokumentasi meski merusak program
- Bahkan dalam kasus ini, cakupan dampak tetap diperkecil dan potensi masalah dicatat dalam catatan rilis
Dua contoh yang muncul di Go 1.1
-
Struct literal dan field baru
- Pada Go 1,
net.TCPAddr adalah struct dengan dua field, IP dan Port, dan composite literal tanpa nama field juga bisa dikompilasi
- Saat field
Zone ditambahkan ke net.TCPAddr di Go 1.1, kode lama tidak lagi bisa dikompilasi karena error “too few initializers in struct literal”
- Cara penulisan yang kompatibel adalah menggunakan literal dengan label field
var myAddr = &net.TCPAddr{
IP: net.IPv4(18, 26, 4, 9),
Port: 80,
}
- Jika
Zone tidak ditentukan, field tersebut memakai zero value berupa string kosong
- Persyaratan untuk menggunakan composite literal berlabel pada struct di standard library tercantum dalam dokumen kompatibilitas, dan
go vet melaporkan literal tanpa label yang memerlukan label agar tetap kompatibel dengan versi mendatang
-
Presisi waktu
- Setelah Go 1,
time.Now diubah agar mengembalikan presisi nanodetik, bukan mikrodetik
- Perubahan ini dapat merusak pengujian yang mengharapkan nilai
time.Now tetap identik setelah bolak-balik melalui save dan load
- Jika representasi penyimpanan hanya mempertahankan presisi mikrodetik, pengujian bisa berhasil di Go 1 tetapi gagal di Go 1.1
- Untuk membantu memperbaiki pengujian seperti ini, Go menambahkan metode
Round dan Truncate, lalu merangkum potensi masalah dan metode baru tersebut dalam catatan rilis
- Karena presisi yang lebih tinggi adalah perilaku yang lebih baik dan masih berada dalam cakupan fungsi yang terdokumentasi, perubahan ini tetap dirilis meski merusak sebagian program
Tiga jenis perubahan yang dapat merusak kompatibilitas
-
Perubahan output
- Perubahan output terjadi saat fungsi menghasilkan output berbeda dari sebelumnya, tetapi output baru itu tetap sama benarnya atau bahkan lebih benar daripada output lama
- Penambahan presisi nanodetik pada
time.Now adalah contoh utamanya
- Di Go 1.6, implementasi
sort diubah agar sekitar 10% lebih cepat, sehingga urutan elemen yang dianggap bernilai sama ikut berubah
- Output Go 1.5:
[red blue green white black yellow orange indigo violet]
- Output Go 1.6:
[red blue white green black orange yellow indigo violet]
- Pengurutan tetap dianggap benar meski mengembalikan urutan apa pun untuk elemen yang setara, tetapi program yang mengharapkan urutan tertentu bisa rusak
- Di Go 1.8,
compress/flate ditingkatkan agar menghasilkan output yang lebih kecil dengan overhead CPU dan memori yang serupa
- Akibatnya, build arsip reproducible internal di Google tidak lagi dapat mereproduksi arsip lama secara persis
- Proyek tersebut akhirnya mem-fork
compress/flate dan compress/gzip untuk mempertahankan algoritme lama
- Untuk mengantisipasi perubahan output, sebaiknya program dan pengujian ditulis agar menerima semua output yang valid
- Jika benar-benar membutuhkan output yang reproducible secara persis, kode bisa di-fork, tetapi konsekuensinya adalah terlepas juga dari perbaikan bug
-
Perubahan input
- Perubahan input terjadi saat fungsi mengubah input yang diterima atau cara memprosesnya
- Go 1.13 menambahkan sintaks underscore untuk keterbacaan angka, dan
strconv.ParseInt juga diubah agar menerima sintaks baru ini
- Kode milik pengguna eksternal yang memakai angka dipisahkan underscore sebagai format data terpisah pun rusak
- Kode itu lebih dulu mencoba
ParseInt, lalu hanya menangani underscore jika parsing gagal, tetapi kini ParseInt tidak lagi gagal
net.ParseIP awalnya menerima alamat IP desimal dengan nol di depan mengikuti contoh dalam RFC IP awal
- Go membaca
18.032.4.011 sebagai 18.32.4.11
- Library C keluarga BSD menafsirkan nol di depan sebagai awalan oktal, sehingga string yang sama dibaca sebagai
18.26.4.9
- Di Go 1.17,
net.ParseIP diubah agar menolak nol di depan sama sekali
- Pilihan ini dibuat agar saat Go dan C sama-sama berhasil mem-parse alamat IP, keduanya memiliki makna yang sama
- Kubernetes khawatir konfigurasi tersimpan lama mungkin tidak bisa di-parse di Go 1.17, dan mulai memakai fork dari
net.ParseIP lama
- Untuk input pengguna, pendekatan yang baik adalah memvalidasi dulu sintaks yang ingin diterima sebelum mem-parse nilainya, tetapi dalam beberapa kasus kode mungkin tetap perlu di-fork
-
Perubahan protokol
- Perubahan protokol terjadi saat perubahan package muncul dalam bentuk yang terlihat pada protokol komunikasi dengan dunia luar
- Go 1.6 menambahkan dukungan HTTP/2 otomatis
- Klien Go 1.5 hanya memakai HTTP/1.1, sehingga bisa berfungsi normal di lingkungan perangkat perantara jaringan tertentu
- Setelah upgrade ke Go 1.6, HTTP/2 akan digunakan, dan jika HTTP/2 tidak berjalan di lingkungan tersebut, program bisa rusak
- Go ingin mendukung protokol modern secara default, tetapi pengaktifan HTTP/2 dapat merusak program meski tidak ada kesalahan pada program maupun Go itu sendiri
- Go 1.6 merangkum perubahan ini dalam catatan rilis dan menyediakan cara untuk menonaktifkan HTTP/2
- Mengatur field
TLSNextProto secara eksplisit
- Menetapkan
GODEBUG=http2client=0, GODEBUG=http2server=0, atau keduanya
- Dukungan sertifikat HTTPS berbasis SHA1 juga merupakan contoh perubahan protokol yang lebih halus
- Otoritas sertifikat berhenti menerbitkan sertifikat SHA1 pada 2015, dan browser utama berhenti menerimanya pada 2017
- Go 1.18 menonaktifkan dukungan sertifikat SHA1 secara default dan menyediakan bypass lewat GODEBUG
- Karena beberapa instalasi Kubernetes masih memakai sertifikat SHA1 privat, Go memutuskan untuk mempertahankan pengaturan bypass itu lebih lama dari rencana awal
Dukungan GODEBUG yang diperluas di Go 1.21
- Go 1.21 memperluas dan memformalkan penggunaan GODEBUG untuk lebih mengurangi masalah kompatibilitas yang halus
- Untuk perubahan yang diizinkan oleh aturan kompatibilitas Go 1 tetapi tetap dapat merusak program lama, ditetapkan pengaturan GODEBUG agar tiap program dapat menolak perilaku baru tersebut
- Mungkin ada kasus sangat langka ketika penambahan pengaturan seperti ini tidak memungkinkan
- Pengaturan GODEBUG untuk kompatibilitas dipertahankan minimal selama 2 tahun, yaitu 4 rilis Go
- Pengaturan seperti
http2client dan http2server dapat dipertahankan jauh lebih lama, bahkan tanpa batas dalam beberapa kasus
- Jika memungkinkan, tiap pengaturan GODEBUG dihubungkan dengan counter
runtime/metrics
- Nama counter memakai format
/godebug/non-default-behavior/<name>:events
- Sebagai contoh, jika
GODEBUG=http2client=0 diatur, maka /godebug/non-default-behavior/http2client:events menghitung jumlah HTTP transport yang dikonfigurasi tanpa HTTP/2
- Nilai default GODEBUG pada program disesuaikan dengan versi Go yang tertulis di
go.mod package utama
- Jika
go.mod berisi go 1.20 dan Anda memperbarui ke toolchain Go 1.21, maka perilaku yang dikendalikan GODEBUG dan berubah di Go 1.21 akan tetap memakai perilaku Go 1.20 sampai go.mod diubah menjadi go 1.21
- Setiap pengaturan GODEBUG dapat diubah lewat baris
//go:debug di package main
- Semua pengaturan GODEBUG dirangkum dalam daftar pusat
Kasus panic(nil)
- Di Go 1.21,
panic(nil) kini memicu runtime panic yang bukan nil
- Perubahan ini membuat hasil
recover dapat memberi tahu secara andal apakah goroutine saat ini sedang panic
- Perilaku baru ini dikendalikan oleh pengaturan GODEBUG dan bergantung pada baris
go di go.mod package utama
- Jika
go 1.20 atau lebih rendah, panic(nil) tetap diizinkan
- Jika
go 1.21 atau lebih tinggi, panic(nil) berubah menjadi panic dengan runtime.PanicNilError
- Nilai default berbasis versi ini dapat ditimpa secara eksplisit dengan menambahkan baris berikut ke
package main
//go:debug panicnil=1
- Kombinasi ini memungkinkan upgrade ke toolchain baru sambil mempertahankan perilaku toolchain lama, mengendalikan hanya pengaturan yang diperlukan secara rinci, dan memantau di produksi apakah perilaku non-default sedang digunakan
- Rincian lebih lanjut dirangkum dalam “Go, Backwards Compatibility, and GODEBUG”
Go 2 tidak merusak Go 1
- Dokumen “Go 1 and the Future of Go Programs” memang memuat catatan bahwa spesifikasi Go 2 mungkin suatu hari akan muncul
- Go 2 dalam arti tidak lagi bisa mengompilasi program Go 1 tidak akan hadir
- Go 2 dalam arti revisi besar terhadap Go 1 yang dimulai pada 2017 sebenarnya sudah terjadi
- Go menilai kompatibilitas jauh lebih berharga daripada memutus hubungan dengan masa lalu, dan memilih arah yang semakin memperkuat kompatibilitas
- Ke depan, pekerjaan baru yang menarik akan terus berlanjut, tetapi dilakukan dengan cara yang hati-hati dan kompatibel agar upgrade antar-toolchain tetap sestabil mungkin
1 komentar
Opini Hacker News
Pertanyaan penting soal kompatibilitas bukanlah “apakah akan melakukannya”, melainkan “bagaimana melakukannya”. Sebenarnya ini bukan sekadar kompatibilitas ke belakang, melainkan lebih dekat ke keinginan agar mulai sekarang kode saya terus berjalan begitu saja
Go 1.21 menyediakan dua hal inti yang sulit ditemukan sekaligus di ekosistem bahasa lain: setiap perubahan punya pengaturan GODEBUG, bisa dikembalikan per perubahan, dan ada juga metrik untuk mendeteksi apakah implementasi lama digunakan. Selain itu ada versi toolchain per modul, dan toolchain Go yang lebih lama maupun lebih baru dapat otomatis diambil dengan aman seperti modul
Sebagai bonus, jika menentukan versi tertentu seperti
go 1.21.2, maka bahkan saat dijalankan di Go yang lebih baru, konfigurasi opt-out terkait akan otomatis diterapkan sampai Anda secara eksplisit meminta perilaku baru. Ini cara yang sederhana dan elegan yang dapat dideklarasikan di mana saja—kode,go.mod, atau variabel lingkungan—sehingga mencakup hampir semua kasus penggunaan kompatibilitas, dari pengembang sampai pihak yang men-deployuse v5.24di awal berkas, ia bisa dibuat berperilaku seperti Perl 5.24, dan ini berlaku per berkas, bukan per modulSekadar apakah mendukung versi baru atau tidak saja sudah menimbulkan masalah yang luar biasa rumit, lalu muncul jurang dalam di mana “kami mengira mendukung versi baru dan versi baru juga mengira mendukung kami, tetapi keduanya saling melewatkan sesuatu”
Saya sangat menyukai arah seperti ini. Tidak ada yang sebaik bisa masuk ke codebase Go dan berharap cukup menaikkan versi Go saja semuanya akan tetap berjalan baik
Namun saya khawatir sistem tipenya sulit ditingkatkan secara besar tanpa perubahan yang memecahkan kompatibilitas seperti “ini kode yang salah, jadi sekarang tidak lagi bisa dikompilasi”. Saya tidak tahu apakah tim Go tertarik pada hal seperti ini, tetapi ada banyak buah yang tergantung rendah yang bisa sangat meningkatkan ketangguhan saat kompilasi tanpa menambahkan fitur bahasa
Misalnya pelaporan
nilyang tidak diperiksa, pemeriksaan akses array, inferensi tipe untuk literal struct bertingkat, dan pemeriksaan menyeluruh untuk enum. Khususnya saat menulis panggilan gRPC bertingkat, yang dibutuhkan hanya satu tingkat inferensi tipe, dan karena tipe sudah ada di signature fungsi yang dipanggil, ini terasa sangat menyakitkan di GoJika sistem tipe Go diperbaiki, saya berharap tipe data aljabar yang sederhana dan indah seperti ini ditambahkan. Itu akan sangat cocok untuk Go, jadi semoga Go menghadiahkannya untuk dirinya sendiri
Sepenuhnya setuju. Alasan rilis Go dinantikan adalah karena sering kali hal-hal berguna ditambahkan tanpa merusak apa pun
Tidak ada bagian bahasa yang tampak begitu rusak sampai tidak bisa diperbaiki dengan perubahan yang kompatibel ke belakang. Bahkan beberapa jebakan seperti assignment variabel loop umumnya punya proposal yang tetap menjaga kompatibilitas
Secara pribadi saya bersimpati pada cara pandang yang menantikan rilis Go, tetapi perbedaan sikap antara kedua ekosistem itu menarik
Dulu saya pernah merangkum sebagian kerusakan yang saya alami: https://news.ycombinator.com/item?id=29763324
Rust atau gcc memberi fitur bahasa saat compiler dinaikkan, tetapi sebagian besar library yang kompleks tetap terpisah dan bisa dinaikkan secara terpisah. Misalnya HTTP di Rust adalah
hyper, dan di C adalahlibcurlSebaliknya, saat menaikkan compiler Go, Anda tidak hanya ikut mendapatkan generics,
embed, dan fitur toolchain, tetapi juga perubahan padatls, library keamanan, serta klien/server HTTP. Gumpalan besar standard library sepertihttp,tls, dancryptoseharusnya jauh lebih baik jika berupa library terpisah. Dengan begitu compiler bisa dinaikkan tanpa rasa takut, sedangkan library bisa dinaikkan sesuai kecepatan saya sendiriasync, dan karena pada edisi 2015 orang bisa membuat variabel atau fungsi bernamaasync, itu adalah perubahan yang memecahkan kompatibilitasKompatibilitas ke belakang memang bagus, tetapi sulit merasa yakin apakah masa depan di mana Go tidak bisa melakukan inovasi X karena harus menjaga kompatibilitas adalah masa depan yang baik
Saya sangat mendukung sikap bahwa Go 2 di masa depan sama sekali tidak akan merusak kompatibilitas Go 1. Kalau bahasa memang harus diubah sebesar itu, menurut saya lebih baik di-fork saja dan diganti namanya
Namun saya penasaran mengapa tidak melangkah lebih jauh dan mengatakan “Go 2 tidak akan ada” untuk menghilangkan ambiguitas. Jika Go 2 secara teoretis menjalankan semua program Go 1, dalam hal apa ia berbeda dari rilis Go 1.xx? Tulisan itu mengatakan “tidak akan ada Go 2 yang merusak program Go 1”, tetapi tampaknya tidak mengatakan lebih dari itu
https://github.com/golang/proposal/blob/d661ed19a203000b7c54...
Dijelaskan bahwa jika proses di atas berjalan sesuai rencana, dalam arti penting Go 2 tidak akan ada, dan transisi akan berlangsung perlahan melalui fitur bahasa dan pustaka baru. Pada suatu titik, secara pemasaran mungkin saja disebut “sekarang Go 2”, tetapi bisa juga dilewati begitu saja. Logikanya, kalau C 2.0 tidak ada, mengapa Go 2.0 diperlukan
Bahasa populer seperti C, C++, dan Java pun pada dasarnya selalu berada di versi 1.N, dan menurut saya Go sebaiknya mengikuti itu. Go 2 yang sebenarnya, dalam arti bahasa baru atau pustaka inti baru yang tidak kompatibel, bukanlah pilihan yang baik bagi pengguna dan bisa merugikan
Pada akhirnya, menurut saya pendekatan itu masuk akal
Jika ada fitur bahasa baru yang jelas-jelas diinginkan komunitas, dan cara yang benar atau satu-satunya cara untuk memasukkannya adalah keyword baru, maka aturan bahwa kompatibilitas source tidak boleh pernah dirusak berarti fitur itu tidak akan pernah bisa ditambahkan. Saya setuju untuk perubahan besar yang mengubah semantik bahasa, tetapi perubahan yang tidak kompatibel ke belakang tidak selalu merupakan perubahan raksasa
Secara teori, gagasan bahwa kompatibilitas mundur tidak pernah berubah itu bagus, tetapi di dunia nyata ada juga breaking change yang bermakna. Tidak selalu terasa menguntungkan jika harus memikul selamanya fitur bahasa yang pada desain awalnya tidak mempertimbangkan X atau Y
Saya banyak memakai Go, dan arah seperti ini benar-benar menenangkan
Kompatibilitas mungkin tidak terlalu menyenangkan bagi tim bahasa. Karena harus selalu menaruh satu kaki dengan kokoh di “masa lalu yang jauh”. Tetapi bagi orang yang harus memelihara sistem Go besar, ini benar-benar hadiah besar
Jika tidak bisa dimasukkan dengan benar, berarti belum tepat, jadi kami mencoba lagi; kalau tetap tidak cocok, mungkin kami belum cukup memahami masalahnya, sehingga memang benar untuk membiarkannya matang lebih lama
Saya sangat senang bekerja dengan orang-orang yang berpikir hati-hati dan berusaha agar ide yang tepatlah yang masuk. Saya berterima kasih karena Russ menjalankan peran BDFL, dan karena bisa bekerja dengan Ian, Rob, dan Rob; berkat itu saya menjadi engineer yang jauh lebih baik
Tulisan terkait: Forward Compatibility and Toolchain Management in Go 1.21 - https://news.ycombinator.com/item?id=37122932
Kalimat “Membosankan itu bagus. Membosankan itu stabil. Membosankan berarti Anda bisa fokus pada pekerjaan sendiri tanpa memikirkan apa yang berubah di Go” benar-benar terasa mengena
Di pekerjaan utama saya menggunakan NodeJS dan ekosistem JS secara umum, dan memang sangat melelahkan. Ekosistemnya terfragmentasi dan semua orang punya caranya sendiri, sehingga sulit membuatnya stabil. Meski begitu saya masih menikmati pekerjaan ini, tetapi saya berharap ekosistem JS juga memiliki fondasi modern yang stabil yang bisa dipercaya dan diandalkan
Banyak tool dan API ditulis dengan Go, dan fakta bahwa sistem target tidak membutuhkan runtime terpisah sering disebut sebagai keunggulan besar. Bahasanya juga tampak sederhana untuk dipelajari dan digunakan, dukungan VSC serta GoLand bagus, dan keluhan umum seperti error handling pun tidak terlihat seperti cacat fatal
Saya penasaran apa lagi yang dibutuhkan Go agar menjadi arus utama pengembangan selama beberapa dekade ke depan, atau setidaknya mengambil bagian besar di pasar kerja. Di beberapa tempat, Go masih dianggap bahasa niche
Masalahnya adalah membuat semua orang naik ke baseline ini. Kalau sudah sampai di sana, sepertinya akan jauh lebih baik
Selain itu, ekosistem JS juga mencakup pekerjaan UI frontend, dan bidang penerapannya begitu luas sehingga kemunculan berbagai implementasi tidak bisa dihindari. Bahkan itu bisa dibilang baik
Memindahkan sebagian kode dari Python ke Golang sangat membantu dalam penskalaan. Saya sangat senang mendengar bahwa Go akan terus menjaga deklarasi intinya, yaitu kompatibilitas mundur
Berbicara soal parsing IP. Saya jadi penasaran, sebenarnya bagaimana
inet_ntoaBSD yang asli ditulis?sscanfyang memakaiatoi/atoldan%d/%uselalu mem-parse tepat sebagai bilangan bulat desimal, jadi untuk menghasilkan efek aneh seperti ini, mestinya harus memakai%iataustrtouberbasis 0inet_atonsaya menyebutkan bahwa itu berasal dari 4.3BSD: https://github.com/dank101/4.3BSD-Reno/blob/master/lib/libc/...inet_addrpada 4.2BSD yang lebih awal juga memakai logika yang sama: https://github.com/dank101/4.2BSD/blob/master/lib/libc/inet/...inet_atondaninet_addrmem-parse alamat dengan cara yang paling alami. Kalau memakai sesuatu sepertistrtoul, atau apalagisscanf, rasanya akan canggung. Keindahan pointer C adalah membuat pekerjaan parsing sederhana menjadi sangat mudah, dan mungkin malah terlalu mudah/etc/hostsdengan memberi padding 0 pada setiap oktetAkhirnya saya harus kembali dan menghapus angka 0 itu
Sebagai perancang bahasa, saya menghormati pilihan-pilihan yang diambil di sini, termasuk keputusan untuk tidak membuat Go 2 yang sebenarnya
Saya juga berniat memakai teknik-teknik untuk menjamin kompatibilitas