Kompleksitas tak terduga dari program sederhana

Lebih dari sekali saya terkejut ketika penilaian kompleksitas proyek diumumkan: "Mengapa begitu lama?", "Ya, di sana, sekali, dua kali, dan Anda selesai!", "Anda bisa mengambil X dan menempelkannya di Y ! " Pemrogram terbiasa mengevaluasi tenggat waktu sebagai waktu yang dihabiskan untuk menulis dan men-debug kode, meskipun tugas besar melibatkan lebih banyak lagi.





Tahukah Anda bahwa pada kenyataannya, gunung es terletak secara horizontal di dalam air , dan bukan secara vertikal, seperti pada kebanyakan gambar stok?



Tetapi bahkan jika Anda lupa tentang sekumpulan gadget perusahaan tradisional seperti analitik, dukungan kompatibilitas mundur, dan pengujian A / B dan hanya berfokus pada kode yang terkait langsung dengan fungsionalitas yang diterapkan, Anda dapat melihat bahwa kerumitannya sering kali lepas kendali.



Pada artikel ini, saya akan memberi tahu Anda tentang beberapa fitur yang saya dan kolega saya terapkan di Joom pada waktu yang berbeda, dari pernyataan masalah hingga detail implementasi, dan akan menunjukkan betapa mudahnya hal-hal yang tampaknya sederhana berubah menjadi jalinan logika kompleks yang membutuhkan banyak pengembangan. iterasi.



Cari berdasarkan pengguna



Salah satu bagian besar dari aplikasi Joom adalah jaringan sosial internal, tempat pembeli dapat menulis ulasan produk, menyukai dan mendiskusikannya, dan saling berlangganan. Dan jaringan sosial yang luar biasa tanpa pencarian pengguna!



Tentu saja, pencarian bukanlah tugas yang mudah (setidaknya setelah artikel saya sebelumnya ). Tetapi saya sudah memiliki semua pengetahuan yang diperlukan, dan kami juga memiliki komponen siap pakai di perusahaan kami joom-mongo-connector



yang dapat mentransfer data dari koleksi ke MongoDB ke indeks Elasticsearch, jika perlu menyesuaikan data tambahan dan melakukan beberapa pemrosesan pasca lainnya. Tugas itu terdengar sangat sederhana.



Sebuah tugas... Buat backend untuk pencarian oleh pengguna jejaring sosial. Tidak perlu filter, mengurutkan berdasarkan jumlah pelanggan akan dilakukan sebagai permulaan.



Oke, kedengarannya sederhana. socialUsers



Kami mengonfigurasi overflow dari koleksi ke Elasticsearch dengan menulis konfigurasi di YAML. Di backend, kami menambahkan endpoint baru dengan API yang mirip dengan API penelusuran produk, tetapi sejauh ini tanpa dukungan untuk filter dan sortir (hanya teks permintaan dan paginasi yang tersisa, itu saja). Di handler, kami membuat permintaan sederhana ke cluster Elasticsearch (yang utama adalah jangan membuat kesalahan dengan cluster!), Dari hasil kami mendapatkan ID dari dokumen yang ditemukan - mereka adalah ID pengguna - menurut pengguna sendiri, lalu kami mengonversi ke klien JSON, menyembunyikan informasi pribadi dari mata yang mengintip, dan siap. Atau tidak?



Masalah pertama yang kami temui adalah transliterasi. Nama pengguna diambil dari jejaring sosial, di mana pengguna dari Rusia (dan mayoritas pada saat itu) sering menulisnya dalam bahasa Latin. Anda mencoba menemukan Mads, dan dia ada di Facebook Mads, dan hanya itu - dia tidak ada di hasil. Demikian pula, Ivan tidak akan dapat menemukan Ivan, tetapi saya sangat ingin.



Ini adalah komplikasi pertama - saat mengindeks, kami mulai membuka Microsoft Translator API untuk transliterasi dan menyimpan dua versi nama depan dan belakang, dan komponen pengindeksan umum mulai bergantung pada klien penerjemah (dan masih bergantung).



Nah, masalah kedua, yang mudah diramalkan jika bahasa ibu Anda adalah bahasa Rusia, tetapi ada juga dalam bahasa Eropa lainnya - bentuk kecil dan singkatan nama. Jika Ivan memutuskan untuk menamai dirinya Vanya di Facebook, permintaan Ivan tidak akan menemukannya lagi, tidak peduli seberapa banyak Anda melakukan transliterasi.



Jadi komplikasi berikutnya adalah kami menemukan indeks nama kecil di Gramota.ru (dari kamus unik Nikandr Aleksandrovich Petrovsky tentang nama Rusia), menambahkannya ke basis kode sebagai pelat hardcode (sekitar dua ribu baris) dan menjadi indeks tidak hanya nama dan transliterasinya, tetapi juga semua bentuk kecil yang ditemukan (fakta menarik: dalam bahasa Inggris ada istilah hipokorisme untuk mereka). Kami mengambil setiap kata di nama pengguna dan melakukan pencarian di spreadsheet sederhana kami.





Tangkapan layar yang disahkan dari basis kode Joom. Sekitar 2018.



Tetapi kemudian, agar tidak menyinggung separuh pengguna kami yang lain, yang didistribusikan dalam lapisan yang tidak rata di seluruh dunia yang tidak berbahasa Rusia, kami melontarkan teriakan kepada manajer negara Joom dan meminta mereka untuk mencarikan kami buku referensi singkatan nasional. nama di negara mereka. Jika bukan akademis, setidaknya beberapa. Dan ternyata di beberapa bahasa, selain tradisi memiliki nama majemuk (Juan Carlos, Maria Aurora), juga ada pengurangan dua, tiga atau bahkan empat kata menjadi satu (MarΓ­a de las Nieves β†’ Marinieves).



Keadaan baru ini membuat kami kehilangan kesempatan untuk mencari satu kata dalam satu waktu. Sekarang kita perlu membagi urutan kata menjadi fragmen dengan panjang sembarang, dan terlebih lagi, partisi yang berbeda dapat menghasilkan singkatan yang berbeda! Kami tidak ingin mendalami linguistik dan menulis kecerdasan buatan yang menyingkat nama Spanyol seperti yang disingkat oleh orang Spanyol yang masih hidup, jadi kami membuat sketsa, memaafkan Knut, kombinatorial yang berlebihan.



Dan, seperti yang selalu terjadi pada penelusuran kombinatorial, penelusuran tersebut meledak di salah satu pengguna dan kami harus segera memotongnya ke batas jumlah maksimum ejaan yang dihasilkan. Ini semakin memperumit kode, yang ternyata sangat sulit untuk tugas ini.



Terjemahan mesin barang



Tugas . Diperlukan untuk menerjemahkan nama dan deskripsi barang yang disediakan oleh penjual dalam bahasa Inggris ke dalam bahasa pengguna.



Semua orang mungkin pernah melihat meme tentang terjemahan yang tidak benar dari nama barang Cina. Kami juga melihatnya, tetapi waktu yang diinginkan untuk memasarkan tidak memungkinkan kami untuk menghasilkan sesuatu yang lebih baik daripada menggunakan beberapa API yang ada untuk terjemahan.



Mudah untuk menulis klien HTTP, membuat akun, dan ketika barang dikeluarkan untuk pengguna, mudah untuk menerjemahkannya ke dalam bahasa perangkat. Namun terjemahan itu tidak murah, dan akan sia-sia jika menerjemahkan produk populer yang sama ke dalam bahasa Rusia untuk masing-masing dari puluhan ribu tampilan. Oleh karena itu, kami mengaktifkan caching: untuk setiap produk, kami menyimpan terjemahan ke database dan, jika ada terjemahan di sana, kami tidak lagi pergi ke penerjemah.



Namun potensi penghematan masih ada. Kami memutuskan bahwa kompromi yang wajar antara kualitas dan harga terjemahan adalah dengan mengalahkan deskripsi untuk kalimat dan menyimpannya dalam cache - lagipula, frasa template yang sama sering ditemukan dalam produk, dan akan sia-sia untuk menerjemahkannya setiap saat. Ini adalah bagaimana lapisan abstraksi lain muncul di penerjemah kami - lapisan antara klien HTTP dan cache yang menyimpan seluruh barang dalam bahasa yang berbeda, yang terlibat dalam memecah teks menjadi fragmen.



Setelah peluncuran, kualitas terjemahan tentu saja menghantui kami, dan kami berpikir: bagaimana jika kami menggunakan penerjemah yang lebih mahal? Tapi apakah itu bagus untuk lirik spesifik kita? Anda tidak dapat membandingkannya dengan mata, Anda perlu melakukan tes A / B. Jadi di cache terjemahan kami, selain ID produk, ID penerjemah muncul, dan kami mulai meminta terjemahan dari ID penerjemah, bergantung pada grup pengujian A / B tempat pengguna berada.



Penerjemah tersayang berkinerja baik, tetapi masih terlalu boros untuk menjalankannya di semua produk. Tetapi kami pergi ke negara-negara yang bahasa nasionalnya sangat sulit ditangani oleh penerjemah utama kami sehingga kami siap membayar untuk peluncuran yang sukses; jadi logika memilih penerjemah menjadi lebih rumit.



Kemudian kami memutuskan bahwa beberapa toko di platform sangat bagus dan platform tersebut sangat mendukung kesuksesan mereka sehingga selalu siap untuk menerjemahkan barang mereka dengan penerjemah yang lebih mahal. Jadi logika memilih penerjemah mulai bergantung pada pengguna, negara, dan ID penyimpanan.



Dan akhirnya, kami memutuskan bahwa selama beberapa tahun keberadaan Joom, penerjemah utama kami dapat meningkat, dan mungkin masuk akal untuk memperbarui cache terjemahan pada beberapa interval. Tapi bagaimana jika tanpa pengujian A / B? Jadi bidang kesegaran muncul di tembolok kami, dan segalanya menjadi rumit lagi. Akibatnya, komponen terjemahan kami sangat kompleks, dan ini terlepas dari kenyataan bahwa kami bahkan belum memasukkan linguistik komputasi buatan sendiri ke dalamnya. Untuk sekarang.



Mengubah ukuran pakaian



Mungkin salah satu masalah paling menyakitkan saat membeli pakaian dan sepatu secara online adalah memilih ukuran yang tepat. Dan jika, saat dikirim dari gudang lokal, pemain seperti Lamoda dapat dengan mudah membawa beberapa ukuran sekaligus dan mengambil kembali yang tidak sesuai dengan kemudahan yang sama, ini tidak akan berfungsi dalam lintas batas. Paket memakan waktu lama, biaya setiap kilogram ekstra tinggi, dan pengirimnya tidak mengharapkan aliran besar surat masuk.



Selain itu, masalah ini diperparah oleh kenyataan bahwa penjual dari berbagai negara mungkin memiliki gagasan yang sangat berbeda tentang ukuran. M Cina bisa dengan mudah berubah menjadi XS Rusia, dan 9XL yang menakutkan mungkin tidak jauh berbeda dari XXL. Pengguna yang dijahit harus bergantung pada pengukuran, tetapi bahkan itu tidak selalu benar: misalnya, pengguna mengharapkan lingkar dada seseorang ditunjukkan, dan penjual menunjukkan ukuran pakaian itu sendiri - mereka berbeda lima hingga sepuluh persen . Kami tidak ingin pengguna repot-repot berbelanja di Joom!



Tugas . Alih-alih ukuran yang disediakan oleh penjual, tunjukkan kepada pengguna ukuran yang kami hitung dari beberapa tabel berdasarkan ketebalan.



Baik. Kami mengambil tabel ukuran, yang kami parse dari deskripsi produk (ini dilakukan oleh pesawat ruang angkasa terpisah untuk 5k baris) dan disimpan di bidang terpisah, dan kami mengganti ukuran di dalamnya dengan yang dihitung. Buat kode keras tabel untuk mengubah ketebalan menjadi ukuran, ditemukan di Internet, dan nikmati hidup.



Tetapi jika tidak ada tabel atau tidak ada cukup baris di dalamnya, ini tidak akan berhasil. Fitur ini dinonaktifkan pada produk secara implisit beberapa kali.



Hmm, dalam tabel lingkar tubuh manusia, dan sebagian besar penjual menunjukkannya dengan mengukur benda itu sendiri. Menjahit koefisien perbedaan. Manajer produk Rodion, pemilik M-ki sempurna yang bahagia, pergi ke mal, mengukur banyak hal yang berbeda pada dirinya sendiri dan dilengkapi dengan koefisien - keduanya serupa, tetapi berbeda secara signifikan untuk berbagai kategori barang. Untuk turtleneck yang membungkus, perbedaannya hampir 0%, dan untuk sweater, semuanya 10%. Selain itu, outerwear bervariasi dalam fit: slim fit, normal fit, loose fit, dan ini memberikan ayunan Β± 5%. Sekarang koefisien kita (diabadikan oleh saya dalam kode sebagai koefisien Rodion ) terdiri dari dua faktor.



Untuk menentukan landing, kami membuat parser lain yang mencoba mengekstraknya dari nama atau deskripsi produk. Jika produk tidak termasuk dalam salah satu kategori yang diperiksa oleh Rodion, fitur nomor dua secara implisit dinonaktifkan.



Sentuhan terakhir: banyak produk mencantumkan payudara dari ketiak ke ketiak, yang berarti hanya setengah lingkar, yang menghasilkan ukuran yang sangat kecil. Kami menambahkan logika bahwa jika lingkar kurang dari X, maka ini tidak mungkin, ini jelas setengah lingkar, dan kami mengalikannya dengan dua. Adalah baik bahwa orang dewasa biasanya tidak berbeda satu sama lain dengan dua kali lingkar dada.



Sekarang semuanya begitu rumit sehingga saat menguji fitur berdasarkan jenis produk di panel admin, tidak mungkin untuk memahami mengapa fitur itu tidak menyala atau berfungsi dengan satu atau lain cara. Kami menambahkan lapisan logika yang besar ke kode, mencatat secara detail alasan untuk mematikan konversi. Untuk dapat sepenuhnya melacak penyebab penghentian pada produk tertentu, Anda harus meneruskan pesan kesalahan ke atas, memperkaya dengan detail, beberapa kali. Kode menjadi menakutkan.



Dan semuanya bekerja secara berbeda tergantung pada kelompok pengujian A / B, tentunya.



Kesimpulan



Waspadalah terhadap Danaan, pengembang donasi yang optimis dengan tenggat waktu. Sangat sulit untuk memperkirakan waktu pengembangan, tidak peduli betapa sederhananya tugas itu, dan kejutan menanti kita di setiap langkah!



All Articles