Postingan ini adalah bagian dari serangkaian postingan blog yang menjelaskan perubahan yang kami lakukan pada arsitektur DevTools dan cara pembuatannya.
Sebagai tindak lanjut dari migrasi ke modul JavaScript dan migrasi ke Komponen Web, hari ini kami melanjutkan seri postingan blog tentang perubahan yang kami lakukan pada arsitektur Devtools dan cara pembuatannya. (Jika Anda belum melihatnya, kami memposting video tentang pekerjaan kami dalam Mengupgrade arsitektur DevTools ke web modern, dengan 14 tips tentang cara melakukan peningkatan pada project web Anda.)
Dalam postingan ini, kami akan menjelaskan perjalanan selama 13 bulan kami beralih dari pemeriksa jenis Closure Compiler ke TypeScript.
Pengantar
Mengingat ukuran codebase DevTools dan kebutuhan untuk memberikan kepercayaan kepada engineer yang mengerjakannya, menggunakan pemeriksa jenis adalah suatu keharusan. Untuk itu, DevTools mengadopsi Closure Compiler pada tahun 2013. Dengan menggunakan Closure, engineer DevTools dapat melakukan perubahan dengan percaya diri; compiler Closure akan melakukan pemeriksaan jenis untuk memastikan bahwa semua integrasi sistem telah di-type dengan baik.
Namun, seiring waktu, pemeriksa jenis alternatif menjadi populer dalam pengembangan web modern. Dua contoh yang penting adalah TypeScript dan Flow. Selain itu, TypeScript menjadi bahasa pemrograman resmi di Google. Meskipun popularitas pemeriksa jenis baru ini meningkat, kami juga melihat bahwa kami mengirimkan regresi yang seharusnya tertangkap oleh pemeriksa jenis. Oleh karena itu, kami memutuskan untuk mengevaluasi ulang pilihan pemeriksa jenis dan mencari tahu langkah berikutnya untuk pengembangan di DevTools.
Mengevaluasi pemeriksa jenis
Karena DevTools sudah menggunakan pemeriksa jenis, pertanyaan yang harus kita jawab adalah:
Apakah kita terus menggunakan Closure Compiler atau bermigrasi ke pemeriksa jenis baru?
Untuk menjawab pertanyaan ini, kami harus mengevaluasi pemeriksa jenis berdasarkan beberapa karakteristik. Karena penggunaan pemeriksa jenis kami berfokus pada keyakinan engineer, aspek yang paling penting bagi kami adalah kebenaran jenis. Dengan kata lain: Seberapa andal pemeriksa jenis dalam menemukan masalah sebenarnya?
Evaluasi kami berfokus pada regresi yang telah kami kirim dan menentukan akar penyebabnya. Asumsinya di sini adalah, karena kita sudah menggunakan Closure Compiler, Closure tidak akan menangkap masalah ini. Dengan demikian, kita harus menentukan apakah pemeriksa jenis lain dapat melakukannya.
Kebenaran jenis di TypeScript
Karena TypeScript adalah bahasa pemrograman yang didukung secara resmi di Google dan popularitasnya meningkat dengan cepat, kami memutuskan untuk mengevaluasi TypeScript terlebih dahulu. TypeScript adalah pilihan yang menarik, karena tim TypeScript sendiri menggunakan DevTools sebagai salah satu project pengujian mereka untuk melacak kompatibilitasnya dengan pemeriksaan jenis JavaScript. Output pengujian referensi dasar pengukuran mereka telah menunjukkan bahwa TypeScript menangkap sejumlah besar masalah jenis - masalah yang tidak selalu terdeteksi oleh compiler Closure. Banyak dari masalah ini kemungkinan merupakan akar penyebab regresi yang kami kirimkan; hal ini, pada gilirannya, membuat kami yakin bahwa TypeScript dapat menjadi opsi yang layak untuk DevTools.
Selama migrasi ke modul JavaScript, kami telah menemukan bahwa Closure Compiler menemukan lebih banyak masalah daripada sebelumnya. Beralih ke format modul standar telah meningkatkan kemampuan Closure untuk memahami codebase kami, sehingga meningkatkan efektivitas pemeriksa jenis. Namun, tim TypeScript menggunakan versi dasar DevTools yang sudah ada sebelum migrasi modul JavaScript. Oleh karena itu, kita harus mencari tahu apakah migrasi ke modul JavaScript juga telah mengurangi jumlah error yang akan ditangkap oleh compiler TypeScript.
Mengevaluasi TypeScript
DevTools telah ada selama lebih dari satu dekade, dan telah berkembang menjadi aplikasi web yang berukuran cukup besar dan kaya fitur. Pada saat penulisan postingan blog ini, DevTools berisi sekitar 150.000 baris kode JavaScript pihak pertama. Saat menjalankan compiler TypeScript pada kode sumber, jumlah error yang sangat banyak membuat kami kewalahan. Kami dapat mengetahui bahwa meskipun compiler TypeScript memunculkan lebih sedikit error yang terkait dengan resolusi kode (~2.000 error), masih ada 6.000 error lainnya yang ada di codebase kami yang terkait dengan kompatibilitas jenis.
Hal ini menunjukkan bahwa meskipun TypeScript dapat memahami cara me-resolve jenis, TypeScript menemukan sejumlah besar inkompatibilitas jenis di codebase kita.
Analisis manual terhadap error ini telah menunjukkan bahwa TypeScript (sebagian besar waktu) sudah benar.
Alasan TypeScript dapat mendeteksinya dan Closure tidak dapat mendeteksinya adalah karena compiler Closure sering kali menyimpulkan jenis sebagai Any
, sedangkan TypeScript akan melakukan inferensi jenis berdasarkan penetapan dan menyimpulkan jenis yang lebih akurat.
Dengan demikian, TypeScript memang lebih baik dalam memahami struktur objek kita dan menemukan penggunaan yang bermasalah.
Satu hal penting yang perlu diperhatikan adalah penggunaan compiler Closure di DevTools mencakup penggunaan @unrestricted
yang sering.
Memberi anotasi pada class dengan @unrestricted
secara efektif menonaktifkan pemeriksaan properti ketat compiler Closure untuk class tertentu tersebut, yang berarti developer dapat menambah definisi class sesuka hati tanpa keamanan jenis.
Kami tidak dapat menemukan konteks historis mengapa penggunaan @unrestricted
umum di codebase DevTools, tetapi hal ini telah menyebabkan compiler Closure berjalan dalam mode operasi yang kurang aman untuk sebagian besar codebase.
Analisis lintas regresi kami dengan error jenis yang ditemukan TypeScript juga menunjukkan tumpang-tindih, yang membuat kami yakin bahwa TypeScript dapat mencegah masalah ini (asalkan jenisnya sendiri sudah benar).
Melakukan panggilan any
Pada tahap ini, kita harus memutuskan antara meningkatkan penggunaan Closure Compiler atau bermigrasi ke TypeScript. (Karena Flow tidak didukung di Google atau di Chromium, kami harus melepaskan opsi tersebut.) Berdasarkan diskusi dengan, dan rekomendasi dari, engineer Google yang mengerjakan alat JavaScript/TypeScript, kami memilih compiler TypeScript. (Baru-baru ini kami juga memublikasikan postingan blog tentang memigrasikan Puppeteer ke TypeScript.)
Alasan utama untuk compiler TypeScript adalah peningkatan akurasi jenis, sedangkan keuntungan lainnya mencakup dukungan dari tim TypeScript secara internal di Google dan fitur bahasa TypeScript, seperti interfaces
(bukan typedefs
di JSDoc).
Memilih compiler TypeScript berarti kami harus berinvestasi secara signifikan dalam codebase DevTools dan arsitektur internalnya. Oleh karena itu, kami memperkirakan bahwa kami memerlukan waktu minimal satu tahun untuk bermigrasi ke TypeScript (ditargetkan pada Kuartal 3 2020).
Melakukan migrasi
Pertanyaan terbesar yang tersisa: bagaimana kita akan bermigrasi ke TypeScript? Kami memiliki 150.000 baris kode dan tidak dapat memigrasikannya sekaligus. Kami juga tahu bahwa menjalankan TypeScript di codebase akan menemukan ribuan error.
Kami mengevaluasi beberapa opsi:
- Dapatkan semua error TypeScript dan bandingkan dengan output "gold". Pendekatan ini akan mirip dengan yang dimiliki tim TypeScript. Kelemahan terbesar dari pendekatan ini adalah tingginya frekuensi konflik penggabungan, karena puluhan engineer bekerja di codebase yang sama.
- Tetapkan semua jenis yang bermasalah ke
any
. Hal ini pada dasarnya akan membuat TypeScript menyembunyikan error. Kami tidak memilih opsi ini, karena sasaran kami untuk migrasi adalah ketepatan jenis yang akan dirusak oleh penekanan. - Perbaiki semua error TypeScript secara manual. Hal ini akan melibatkan perbaikan ribuan error, yang memakan waktu.
Meskipun upaya yang diharapkan cukup besar, kami memilih opsi 3. Ada alasan tambahan mengapa kami memilih opsi ini: misalnya, opsi ini memungkinkan kami mengaudit semua kode dan melakukan peninjauan setiap sepuluh tahun sekali terhadap semua fungsi, termasuk penerapannya. Dari perspektif bisnis, kami tidak memberikan nilai baru, tetapi mempertahankan status quo. Hal ini mempersulit justifikasi opsi 3 sebagai pilihan yang benar.
Namun, dengan mengadopsi TypeScript, kami yakin bahwa kami dapat mencegah masalah di masa mendatang, terutama seputar regresi. Dengan demikian, argumennya bukan "kita menambahkan nilai bisnis baru", tetapi lebih ke "kita memastikan tidak kehilangan nilai bisnis yang diperoleh".
Dukungan JavaScript dari compiler TypeScript
Setelah mendapatkan dukungan dan mengembangkan rencana untuk menjalankan compiler Closure dan TypeScript pada kode JavaScript yang sama, kami memulai dengan beberapa file kecil. Pendekatan kami sebagian besar bersifat bottom-up: mulai dengan kode inti dan lanjutkan ke arsitektur yang lebih tinggi hingga mencapai panel tingkat tinggi.
Kita dapat melakukan paralelisasi pekerjaan dengan menambahkan @ts-nocheck
secara preemptive ke setiap file di DevTools. Proses "memperbaiki TypeScript" adalah menghapus anotasi @ts-nocheck
dan menyelesaikan error apa pun yang akan ditemukan TypeScript. Artinya, kami yakin bahwa setiap file telah diperiksa dan sebanyak mungkin masalah jenis telah diselesaikan.
Secara umum, pendekatan ini berhasil dengan sedikit masalah. Kami mengalami beberapa bug di compiler TypeScript, tetapi sebagian besar bug tersebut tidak jelas:
- Parameter opsional dengan jenis fungsi yang menampilkan
any
diperlakukan sebagaimana mestinya: #38551 - Penetapan properti ke metode statis class merusak deklarasi: #38553
- Deklarasi subclass dengan konstruktor tanpa argumen dan superclass dengan konstruktor argumen menghilangkan konstruktor turunan: #41397
Bug ini menyoroti bahwa, untuk 99% kasus, compiler TypeScript adalah fondasi yang solid untuk membangun. Ya, bug tersembunyi ini terkadang menyebabkan masalah untuk DevTools, tetapi sebagian besar waktunya bug ini cukup tersembunyi sehingga kita dapat dengan mudah mengatasinya.
Satu-satunya masalah yang menyebabkan beberapa kebingungan adalah output non-deterministik file .tsbuildinfo
: #37156.
Di Chromium, kami mewajibkan dua build dari commit Chromium yang sama menghasilkan output yang sama persis.
Sayangnya, engineer build Chromium kami menemukan bahwa output .tsbuildinfo
bersifat non-deterministik: crbug.com/1054494.
Untuk mengatasi masalah ini, kita harus melakukan monkey-patch pada file .tsbuildinfo
(yang pada dasarnya berisi JSON) dan memprosesnya secara pasca-pemrosesan untuk menampilkan output deterministik: https://crrev.com/c/2091448
Untungnya, tim TypeScript menyelesaikan masalah upstream dan kami segera dapat menghapus solusi kami. Terima kasih kepada tim TypeScript karena telah menerima laporan bug dan memperbaiki masalah ini dengan cepat.
Secara keseluruhan, kami puas dengan ketepatan (jenis) compiler TypeScript. Kami harap Devtools sebagai project JavaScript open source besar telah membantu memperkuat dukungan JavaScript di TypeScript.
Menganalisis dampaknya
Kami dapat membuat progres yang baik dalam menyelesaikan error jenis ini dan perlahan meningkatkan jumlah kode yang diperiksa oleh TypeScript. Namun, pada Agustus 2020 (9 bulan setelah migrasi ini dimulai), kami melakukan pemeriksaan dan mendapati bahwa kami tidak akan memenuhi batas waktu dengan kecepatan saat ini. Salah satu engineer kami membuat grafik analisis untuk menunjukkan progres "TypeScriptification" (nama yang kami berikan untuk migrasi ini).
Progres Migrasi TypeScript - Melacak baris kode yang tersisa yang perlu dimigrasikan
Estimasi kapan kami akan mencapai nol baris yang tersisa berkisar dari Juli 2021 hingga Desember 2021, hampir setahun setelah batas waktu kami. Setelah berdiskusi dengan manajemen dan engineer lainnya, kami setuju untuk meningkatkan jumlah engineer yang bekerja untuk bermigrasi ke dukungan compiler TypeScript. Hal ini dapat dilakukan karena kami mendesain migrasi agar dapat diparalelkan sehingga beberapa engineer yang mengerjakan beberapa file yang berbeda tidak akan saling bertentangan.
Pada tahap ini, proses TypeScriptification menjadi upaya seluruh tim. Dengan bantuan tambahan ini, kami dapat menyelesaikan migrasi pada akhir November 2020, 13 bulan setelah memulai, dan lebih dari setahun sebelum estimasi awal kami.
Secara total, ada 771 daftar perubahan (mirip dengan Permintaan Pull) yang dikirimkan oleh 18 engineer. Bug pelacakan kami (https://crbug.com/1011811) memiliki lebih dari 1.200 komentar (hampir semuanya adalah postingan otomatis dari daftar perubahan). Sheet pelacakan kami memiliki lebih dari 500 baris untuk semua file yang akan di-typescript, penerima tugasnya, dan changelist tempat file tersebut “Di-typescript”.
Mengurangi dampak performa compiler TypeScript
Masalah terbesar yang saat ini kami hadapi adalah performa compiler TypeScript yang lambat. Mengingat jumlah engineer yang mem-build Chromium dan DevTools, bottleneck ini sangat mahal. Sayangnya, kami tidak dapat mengidentifikasi risiko ini sebelum migrasi, dan hanya pada saat kami memigrasikan sebagian besar file ke TypeScript, kami menemukan peningkatan waktu yang signifikan di seluruh build Chromium: https://crbug.com/1139220
Kami telah melaporkan masalah ini ke upstream tim compiler Microsoft TypeScript, tetapi sayangnya mereka memutuskan bahwa perilaku ini disengaja. Kami berharap mereka akan mempertimbangkan kembali masalah ini, tetapi untuk saat ini kami berupaya memitigasi dampak performa lambat di sisi Chromium sebanyak mungkin.
Sayangnya, solusi yang tersedia bagi kami saat ini tidak selalu cocok untuk kontributor non-Googler. Karena kontribusi open source ke Chromium sangat penting (terutama dari tim Microsoft Edge), kami secara aktif mencari alternatif yang akan berfungsi untuk semua kontributor. Namun, saat ini kami belum menemukan solusi alternatif yang sesuai.
Status TypeScript saat ini di DevTools
Saat ini, kami telah menghapus pemeriksa jenis compiler Closure dari codebase dan hanya mengandalkan compiler TypeScript. Kita dapat menulis file yang dibuat TypeScript dan menggunakan fitur khusus TypeScript (seperti antarmuka, generik, dll.), yang membantu kita setiap hari. Kami semakin yakin bahwa compiler TypeScript akan menangkap error jenis dan regresi, yang merupakan hal yang kami harapkan akan terjadi saat kami pertama kali mulai mengerjakan migrasi ini. Migrasi ini, seperti banyak hal lainnya, berjalan lambat, rumit, dan sering kali menantang, tetapi karena kami mendapatkan manfaatnya, kami yakin bahwa hal ini sepadan.
Mendownload saluran pratinjau
Sebaiknya gunakan Chrome Canary, Dev, atau Beta sebagai browser pengembangan default Anda. Saluran pratinjau ini memberi Anda akses ke fitur DevTools terbaru, memungkinkan Anda menguji API platform web canggih, dan membantu Anda menemukan masalah di situs sebelum pengguna melakukannya.
Hubungi tim Chrome DevTools
Gunakan opsi berikut untuk membahas fitur baru, update, atau hal lain yang terkait dengan DevTools.
- Kirim masukan dan permintaan fitur kepada kami di crbug.com.
- Laporkan masalah DevTools menggunakan Opsi lainnya > Bantuan > Laporkan masalah DevTools di DevTools.
- Tweet ke @ChromeDevTools.
- Berikan komentar di video YouTube Yang baru di DevTools atau video YouTube Tips DevTools.