Itu 2016. Trump belum terpilih menjadi presiden, sehingga gerakan #DeleteUber belum dimulai. Travis Kalanick tetap gender, kami mengalami fase pertumbuhan hiperaktif dengan pembukaan cabang di negara lain, suasana hati publik umumnya positif, semua orang senang, Uber dalam kondisi terbaiknya.
Tapi hypergrowth bukannya tanpa masalah, dan aplikasinya sendiri mulai tidak berfungsi. Sebelumnya, jumlah pengembang berlipat ganda hampir setiap tahun, dan ketika Anda tumbuh secepat itu, Anda mendapatkan berbagai keterampilan yang luar biasa. Dikombinasikan dengan mentalitas peretas yang kami sebut "Let builder's build", ini berarti arsitektur aplikasi yang kompleks dan rapuh. Saat itu, aplikasi Uber memiliki logika yang sangat berat, sehingga sering kali error. Kami terus-menerus merilis hotfix, patch, rilis yang tidak direncanakan, dll. Selain itu, arsitekturnya tidak berskala dengan baik.
Sebagai akibat dari semua masalah ini, gerakan yang berkembang dimulai di semua tingkat organisasi yang mendukung gagasan "menulis ulang aplikasi dari awal." Sebuah tim dibentuk untuk membuat arsitektur seluler baru untuk aplikasi baru tersebut. Idenya adalah untuk menciptakan arsitektur yang "akan mendukung pengembangan seluler Uber selama lima tahun ke depan." Kami mengembangkan untuk kedua platform sekaligus. Seluruh siklus pengembangan dimulai kembali.
Departemen iOS mengambil kesempatan ini untuk mengimplementasikan Swift (saat itu di versi 2.x). Uber telah mencoba Swift di masa lalu, tetapi seperti banyak lainnya pada tahap awal pengembangan teknologi itu, Uber mengalami banyak masalah dan implementasi yang tertunda.
Namun, perasaan umumnya adalah bahwa sebagian besar masalah Swift pada saat itu disebabkan oleh interoperabilitas yang buruk dengan Objective-C. Dan jika kita menulis aplikasi Swift murni, kita bisa menghindari masalah utama.
Ada juga ide untuk menggunakan pola arsitektur dasar yang sama di Android dan iOS. Pengembang Android adalah penggemar berat RxJava pada saat itu. Pustaka RxSwift yang sesuai memanfaatkan paradigma pemrograman fungsional di Swift. Segalanya tampak sederhana.
Dengan demikian, tim pengembangan kecil (Desain, Produk, dan Arsitektur) beralih ke pola fungsional / reaktif baru, bahasa baru, dan aplikasi baru selama beberapa bulan. Semuanya baik-baik saja. Arsitekturnya sangat bergantung pada kemampuan bahasa tingkat lanjut dari Swift.
UI dapat menskalakan ke sejumlah besar aplikasi Uber, paradigma pemrograman fungsional tampak kuat (meskipun agak sulit dipelajari), dan arsitekturnya didasarkan pada protokol jaringan streaming real-time baru (saya menulis bagian ini).
Setelah beberapa bulan dan beberapa demo yang mencolok, gerakan memperoleh momentum. Proyek itu tampak berhasil. Dengan jumlah insinyur yang sedikit, fungsionalitas yang sangat baik dapat dikembangkan dalam waktu singkat. Sebagian besar produk sudah siap. Panduannya cantik.
Kemudian penyebaran ke seluruh perusahaan dimulai. Berbagai tim telah mulai menambahkan fitur mereka sendiri ke aplikasi baru. Pada awalnya, kegembiraan yang baru menciptakan kesibukan motivasi dan produktivitas. Arsitektur menyediakan isolasi fungsi, yang memungkinkan kemajuan pesat.
Tetapi segera setelah lebih dari sepuluh insinyur menguasai Swift, mekanisme yang terkoordinasi dengan baik mulai berantakan. Kompiler Swift masih jauh lebih lambat daripada Objective-C hari ini, tetapi secara praktis tidak dapat digunakan. Waktu perakitan meleset. Debugging benar-benar berhenti.
Di suatu tempat ada video dari salah satu demo, di mana seorang insinyur Uber mengetik pernyataan satu baris di Xcode, lalu menunggu 45 detik sampai huruf-huruf itu perlahan, satu per satu, muncul di editor.
Kemudian kami menabrak dinding dengan linker dinamis. Saat ini, pustaka Swift hanya dapat ditautkan secara dinamis. Sayangnya, penaut berjalan dalam waktu polinomial, jadi jumlah maksimum pustaka yang disarankan Apple dalam satu file biner adalah 6. Kami memiliki 92, dan jumlahnya terus bertambah ...
Akibatnya, setelah mengklik ikon aplikasi, butuh 8-12 detik bahkan sebelum memanggil utama. Aplikasi baru kami yang mengkilap ternyata lebih lambat dari yang lama dan canggung. Lalu ada masalah ukuran biner.
Sayangnya, ketika masalah mulai menampakkan diri dengan serius, kami telah melewati titik tanpa harapan. Ini adalah kekeliruan logis dari sunk cost fallacy. Pada saat itu, seluruh perusahaan mengerahkan seluruh energinya untuk aplikasi baru.
Ribuan orang dari berbagai arah, jutaan dan jutaan dolar (saya tidak bisa memberikan angka sebenarnya, tapi lebih dari satu). Semua manajemen sepakat mendukung proyek ini. Saya melakukan percakapan pribadi dengan bos saya tentang perlunya berhenti.
Dia bilang kalau proyek ini gagal, dia harus berkemas. Hal yang sama berlaku untuk bosnya hingga wakil presiden. Tidak ada jalan keluar.
Jadi kami menyingsingkan lengan baju kami dan menempatkan pengembang terbaik pada setiap masalah, memprioritaskan masalah kritis (tautan dinamis, ukuran biner). Saya diberi tautan dinamis dan ukuran biner, dalam urutan itu.
Kami segera menemukan bahwa masalah penautan saat startup aplikasi dapat diselesaikan dengan menempatkan semua kode di file executable utama. Tapi seperti yang kita semua tahu, Swift menggabungkan namespace dengan kerangka kerja; oleh karena itu perubahan kode yang sangat besar akan diperlukan, termasuk pemeriksaan namespace yang tak terhitung jumlahnya.
Saat itulah Richard Howell yang brilian memeriksa output build Xcode dan menemukan bahwa setelah build selesai, dia dapat mengambil semua file objek perantara dan menautkannya kembali ke biner utama menggunakan skrip khusus.
Karena Swift mendistorsi namespace objek selama kompilasi, itu berarti Swift dapat mengoperasikannya. Ini memungkinkan kami untuk secara efisien menautkan perpustakaan kami dan mengurangi waktu startup utama dari 10 detik menjadi hampir nol.
Masalah selanjutnya adalah ukuran. Pada saat itu, sebagai jaring pengaman, kami berencana untuk mengemas aplikasi baru dengan yang lama - dan menerapkannya dengan hati-hati saat runtime. Untuk mengurangi ukuran, hal pertama yang kami lakukan hanyalah mencopot pemasangan aplikasi lama. Kami menyebut strategi ini "Yolo". Travis secara pribadi memberikan izin.
Kami juga telah mengganti semua struktur Swift dengan kelas . Jenis nilai umumnya memberikan banyak overhead karena penyelarasan objek dan kode mesin tambahan yang diperlukan untuk perilaku penyalinan, penginisialisasi otomatis, dan sebagainya. Ini menghemat ruang.
Namun aplikasinya terus berkembang. Segera, kami mencapai batas unduhan (100 MB) binari di iOS 8 dan yang lebih lama. Ini berarti sejumlah besar instalasi yang hilang ($ 10 + juta dalam pendapatan yang hilang karena banyak pengguna iOS yang belum diperbarui).
Pada titik ini, ada beberapa minggu sebelum peluncuran publik. Kami harus kembali ke Objective-C, atau melepaskan dukungan untuk iOS 8. Sejak iOS 9 memperkenalkan kemampuan untuk membagi arsitektur, versi ini sebenarnya berukuran setengah (memberi atau menerima). Ketika hanya ada satu minggu tersisa, kami memutuskan untuk membuang puluhan juta dolar - dan membatalkan dukungan untuk iOS 8.
Pendapat umum adalah bahwa ketika ukurannya dibelah dua, kami memiliki banyak ruang untuk bermanuver, dan masalah dengan ukuran dapat diselesaikan suatu saat nanti. saat kita menyapu sisanya. Sayangnya, kami salah besar.
Setelah aplikasi dirilis, kami mengadakan pesta besar. Aplikasi ini diterima dengan baik oleh pengguna dan pers. Itu cepat, dengan desain baru yang berani.
Banyak orang dipromosikan. Kami semua menghela napas lega. Setelah 90 minggu bekerja terus menerus, mereka akhirnya mendapat istirahat.
Tapi kemudian opini publik mulai berubah. Aplikasi baru ini berfokus pada penghitungan harga pasti perjalanan untuk rute tertentu (dulu, Anda hanya melihat tarif dan pengganda saat ini). Untuk menghitung harga, Anda harus memasukkan lokasi Anda saat ini.
Untuk kenyamanan pengguna, kami juga memasang penentuan lokasi otomatis, memungkinkan pengumpulan data lokasi di latar belakang sehingga pengemudi dapat melihat dengan tepat di mana harus mengambil penumpang saat ini. Orang-orang mulai menjadi gila. Beberapa mantan rekan kerja saya di Twitter mendesak saya untuk keluar dari perusahaan jahat yang melacak orang-orang seperti ini.
Akibat kerusuhan ini, orang-orang mulai menonaktifkan izin lokasi di iOS. Tetapi aplikasi baru tidak menyediakan kasus penggunaan ini.
Jadi kami mencoba sebaik mungkin untuk mengembalikan versi standar. Kami membahas bahwa dimungkinkan untuk menonaktifkan pelacakan lokasi latar belakang, tetapi hal itu sekali lagi merusak kegunaan sebelum naik taksi.
Kemudian Trump berkuasa (ini terjadi sekitar tiga bulan setelah rilis aplikasi baru), yang memicu reaksi berantai yang mengarah ke gerakan #DeleteUber .
Selama ini, basis kode Swift berkembang pesat. Masalah yang sedang berlangsung dan IDE yang lambat telah melahirkan dua faksi yang bertikai di antara pengembang iOS kami. Saya akan menyebut mereka fanatik Swift dan kutu buku Objective-C.
Jumlah tekanan eksternal dan internal membawa ketegangan ke maksimum. Orang-orang fanatik membantah masalah Swift. Para membosankan mengeluh tentang segala sesuatu yang bisa dibayangkan tanpa menawarkan solusi khusus.
Sekitar waktu ini, kami dihadapkan pada masalah dengan ukuran biner. Saya sedang dihubungi ketika tim memiliki masalah dengan rilis. Ternyata solusi brilian kami untuk masalah penautan dinamis menciptakan executable yang terlalu besar untuk beberapa arsitektur.
Setelah memecahkan masalah pada arsitektur ini, saya dan rekan saya @aqua_geekmelakukan sedikit penggalian dan menemukan bahwa ukuran kode yang dikompilasi tumbuh dengan kecepatan 1,3 MB per minggu. Saya mengangkat alarm. Jika tidak ada yang dilakukan, pada kecepatan seperti itu, kami akan mencapai batas unduhan melalui jaringan seluler dalam tiga minggu.
Tetapi ketegangan internal mencapai tahap sedemikian rupa sehingga para fanatik menyangkal segalanya. Salah satu pemimpin teknologi dari kamp Swift menulis artikel dua halaman tentang bagaimana batasan unduhan seluler tidak penting (Facebook, bagaimanapun juga, telah melampauinya sejak lama). Kami sendiri lelah memadamkan api.
Jadi, salah satu ilmuwan data kami mengembangkan pengujian yang secara artifisial memindahkan salah satu lapisan arsitektur ke luar batas - dan mengukur dampaknya pada kinerja bisnis. Minggu berikutnya kami menarik kembali lapisan itu dan mendorong yang lain keluar dari batas (untuk mengontrol arsitektur).
Efeknya sangat merusak. Dampak negatif pada bisnis ternyata beberapa kali lipat lebih besar daripada semua biaya penerapan Swift tahunan. Ternyata banyak orang berada di luar jangkauan WiFi saat mereka mengunduh aplikasi Uber untuk pertama kalinya (siapa sangka?)
Jadi kami membentuk kelompok pemogokan lain. Kami mulai mendekompilasi file objek dan memeriksa baris demi baris untuk menentukan mengapa kode Swift tumbuh begitu besar. Menghapus fungsi yang tidak digunakan. Tyler harus menulis ulang aplikasi watchOS kembali ke objc.
(Panjang aplikasi jam tangan hanya 4400 baris, tetapi karena arsitektur prosesor yang berbeda dan kurangnya kompatibilitas ABI, seluruh runtime Swift harus disertakan dalam aplikasi.)
Kami berada di batas kami. Lelah sekali. Tapi mereka berkumpul. Saat itulah para insinyur yang benar-benar brilian menunjukkan diri mereka. Salah satu pengembang di Amsterdam menemukan cara mengatur ulang pengoptimalan kompilator. Bagi yang belum ahli compiler, akan saya jelaskan.
Kompiler modern membuat banyak lintasan. Misalnya, satu fungsi dapat sebaris. Cara lainnya adalah mengganti ekspresi konstan dengan nilainya. Bergantung pada urutan eksekusi, kode mesin mungkin lebih kecil atau lebih besar.
Jika fungsi sebaris meneruskan nilai, kompilator dapat mengenali ini dan mengganti seluruh blok. Sebagai contoh:
int x = 3 func(x) { X + 4 }
menjadi hanya konstanta 7 jika kompilator melewati fungsi inline terlebih dahulu (yang berarti lebih sedikit kode).
Jika kompilator ini lulus yang kedua, maka ia mungkin tidak mengenali fungsi tersebut, dan Anda akan mendapatkan lebih banyak kode. Semua ini, tentu saja, sepenuhnya bergantung pada tampilan kode tertentu, sehingga sulit untuk mengoptimalkan urutan operan secara umum.
Demikian kata seorang insinyur brilian dari Amsterdam yang membangun algoritme ke dalam rilis rilis untuk menyusun ulang pengoptimalan lewat dan meminimalkan ukuran. Ini mengambil 11MB kekalahan dari total ukuran kode mesin dan memberi kami sedikit waktu untuk terus berkembang.
Tetapi pendekatan ini membuat takut spesialis kompiler Swift, mereka takut bahwa kompilator yang tidak diverifikasi akan mengungkapkan bug yang belum teruji (meskipun setiap pass harus aman secara intrinsik, sulit untuk menjelaskan kemungkinan kombinasi pass). Namun, kami tidak mengalami masalah besar.
Kami juga menerapkan banyak solusi lain (linting untuk templat kode yang sangat mahal). Kami mengukur masing-masing dalam jumlah minggu pengembangan yang mereka berikan kepada kami. Tapi masalah sebenarnya adalah kurva pertumbuhan. Pada akhirnya, semua kemenangan selalu habis dimakan.
Pada akhirnya, kami punya cukup waktu untuk menunggu langkah Apple, yang menaikkan batas unduhan melalui komunikasi seluler menjadi 150 MB. Mereka juga menambahkan sejumlah fungsi kompilator untuk membantu pengoptimalan ukuran (-Osize). Menurut pengakuan mereka sendiri, Swift tidak akan pernah sekecil Objective-C setelah dikompilasi.
Namun mulai tahun ini, kami mengoptimalkan Swift menjadi 1,5x ukuran kode mesin Objective-C, dan akhirnya Apple menaikkan batas opsional menjadi 200MB lagi. Itu cukup untuk membuat kita terus maju beberapa tahun lagi.
Tapi kami hampir gagal. Jika Apple tidak meningkatkan batas tersebut, aplikasi Uber harus ditulis ulang kembali ke ObjC. Pada akhirnya, kami juga bisa menyelesaikan masalah lain. Shiny @alanzeinodan timnya memastikan untuk menyertakan dukungan Swift dalam alat pembuatan Buck, yang secara signifikan mengurangi waktu pembuatan.
Kami kehilangan banyak orang yang kelelahan di sepanjang jalan. Menghabiskan banyak uang dan belajar pelajaran yang sulit. Anehnya, sampai hari ini, sebagian besar bersikeras bahwa penulisan ulang itu sepadan. Konsistensi arsitektural sangat populer di kalangan insinyur baru yang datang ke perusahaan. Mereka bahkan tidak tahu berapa banyak rasa sakit yang dibutuhkan untuk mencapainya.
Masyarakat telah memperoleh manfaat dari pengetahuan kami. @ ellsk1 membuat presentasi yang luar biasa dan mengikuti tur ceramah untuk berbagi pengetahuannya. Saya juga dapat memanfaatkan pengalaman ini untuk membantu perusahaan baru dan tim pengembangan membuat keputusan yang lebih baik.
Jadi, inilah tipnya. Segala sesuatu dalam pemrograman adalah tentang kompromi. Tidak ada bahasa yang lebih baik secara universal. Apa pun yang Anda lakukan, pahami apa itu kompromi dan mengapa Anda membuatnya. Hindari perang politik antara faksi yang keras kepala di dalam perusahaan.
Berusaha di titik-titik kegagalan. Cari tahu cara mengidentifikasi trade-off dan mundur jika Anda sampai ke suatu titik dan menyadari bahwa Anda telah melakukan kesalahan. Banyak upaya harus dibayar mahal, tetapi semakin Anda menyadari kompromi yang salah, semakin tinggi biayanya.
Jangan menjadi orang membosankan yang hanya mengomel dan tidak berkontribusi. Jangan menjadi seorang fanatik yang menciptakan masalah besar bagi semua orang. Insinyur terbaik tidak jatuh ke dalam perangkap ini.