Bagaimana Yandex mengajari saya cara mewawancarai programmer

Setelah saya menceritakan kisah "pekerjaan" saya di Yandex dalam komentar di catatan sensasional "Bagaimana saya bekerja selama 3 bulan di Y. Pasar dan berhenti", tidak adil menyembunyikan manfaat yang saya dapatkan dari pengalaman Yandex.Message saya.

Tanggung jawab pekerjaan saya meliputi wawancara teknis terhadap kandidat untuk posisi Pengembang Fullstack JavaScript / TypeScript, saya telah terlibat aktif dalam bisnis ini (apakah layak dikatakan bahwa saya sedikit muak?) Selama lebih dari setahun, saya memiliki lebih dari 30 wawancara teknis.

Sebelumnya dalam wawancara teknis, saya mengajukan pertanyaan yang agak bodoh kepada kandidat seperti "apa itu penutupan", "bagaimana pewarisan diterapkan dalam JavaScript", "di sini adalah tabel dalam database dengan indeks seperti itu, beri tahu kami bagaimana Anda dapat mempercepat ini dan itu request ”, yang, meskipun mereka membantu mengidentifikasi kemampuan rekayasa kandidat, tidak memungkinkan mereka untuk menyimpulkan seberapa baik seseorang dapat menyelesaikan masalah dan seberapa cepat dia dapat mengetahui kode yang ada. Apa yang tidak bisa tidak mengarah pada konsekuensi yang menyedihkan ...

Tetapi semuanya berubah setelah saya melalui empat putaran wawancara teknis di Yandex.

Biasanya, batu terbang ke kebun pewawancara Yandex untuk:

1. Tugas yang tidak memiliki nilai praktis;
2. Kebutuhan untuk menyelesaikan masalah ini pada selembar kertas dengan pensil atau papan tulis.

Sudah tahun 2019 dan sekarang saatnya untuk meluncurkan lini produksi terpisah untuk membuat boiler di neraka bagi mereka yang memaksa orang untuk menulis teks dengan tangan, belum lagi kode. Setiap orang menulis dengan cara yang berbeda, dan dalam mempersiapkan teks untuk catatan ini, saya harus menulis ulang, misalnya, paragraf khusus ini enam kali - jika saya menulis catatan untuk Habr di atas kertas, saya tidak akan menulis catatan untuk Habr.

Tapi saya tidak setuju dengan tesis tentang kesia-siaan praktis dari tugas-tugas Yandex. Bahkan pengembangan rutin tidak, tidak, tetapi itu akan membuat Anda tugas yang memiliki beberapa solusi. Anda tidak perlu memikirkannya untuk waktu yang lama, tetapi tidak optimal dalam hal ukuran kode, kinerja, atau ekspresi. Yang lain justru sebaliknya, tetapi membutuhkan programmer beberapa pengalaman dalam membangun algoritma yang efisien dan dimengerti. Berikut ini adalah contoh dari sebuah wawancara:

/*    getRanges,    : */ getRanges([0, 1, 2, 3, 4, 7, 8, 10]) // "0-4,7-8,10" getRanges([4,7,10]) // "4,7,10" getRanges([2, 3, 8, 9]) // "2-3,8-9" 

Pada masalah ini, saya benar-benar tumpul, dan memutuskan itu bukan cara yang paling indah. Solusinya, sayangnya, belum dilestarikan, jadi saya akan memberikan solusi kepada salah satu kandidat kami:

 function getRanges(arr: number[]) { return arr.map((v, k) => { if (v - 1 === arr[k - 1]) { if (arr[k + 1] === v + 1) { return '' } else { return `-${v},` } } else { return v + ',' } }).join('').split(',-').join('-') } 

Dari minus: mengakses array pada indeks tidak ada dan manipulasi string yang jelek: join-split-join. Dan solusi ini juga salah, karena dengan contoh getRanges ([1, 2, 3, 5, 6, 8]) "1-3,5-6,8," dikembalikan, dan untuk "membunuh" koma pada akhirnya, Anda perlu untuk lebih meningkatkan kondisi dengan mempersulit logika dan mengurangi keterbacaan.

Berikut adalah solusi gaya Yandex:
 const getRanges = arr => arr .reduceRight((r, e) => r.length ? (r[0][0] === e + 1 ? r[0].unshift(e) : r.unshift([e])) && r : [[e]], []) .map(a => a.join('-')).join(',') 

Akankah google membantu menulis solusi elegan seperti itu? Untuk menghasilkan kode seperti itu, Anda memerlukan dua komponen: pengalaman dengan banyak algoritma dan pengetahuan bahasa yang sangat baik. Dan inilah persisnya yang diperingatkan oleh perekrut Yandex: mereka akan bertanya tentang algoritma dan bahasa. Yandex lebih suka mempekerjakan pengembang yang bisa menulis kode keren. Pemrogram seperti itu efektif, tetapi, yang paling penting, dapat dipertukarkan: mereka akan menulis tentang solusi yang sama. Secara teoritis, para pengembang yang kurang paham tentang satu tugas dapat memberikan puluhan solusi yang beragam, terkadang luar biasa: salah satu kandidat untuk lowongan kami menggunakan kruk sehingga mata saya terangkat.

UPD: seperti yang dicatat pengguna MaxVetrov , solusi saya salah:

 getRanges([1,2,3,4,6,7]) // 1-2-3-4,6-7 

Jadi, saya sendiri belum dapat menyelesaikan masalah ini dengan baik sejauh ini.

UPD2: Secara umum, komentar meyakinkan saya bahwa kode ini ternyata buruk, bahkan jika itu berfungsi dengan benar.

Tidak sia-sia saya menghabiskan waktu menerjemahkan makalah di kantor Yandex, karena selama pelajaran ini saya mengerti bagaimana menjadi pewawancara yang efektif. Saya mengambil ide dan format tugas mereka sebagai dasar, tetapi:

  • Saya tidak menawarkan untuk menulis kode di atas kertas, tetapi diminta untuk menulis di code.yandex-team.ru . Ini adalah editor kode online multi-pengguna tanpa kemungkinan eksekusi. Mungkin ada pilihan lain yang lebih nyaman, tetapi ada pencarian dan masih ada kemalasan;
  • Dia tidak membutuhkan solusi yang ideal, tetapi diminta untuk menyelesaikannya;
  • Dia tidak membutuhkan pengetahuan bahasa dengan hati, fungsi atau metode yang diinginkan dapat ditanyakan;
  • Dia mengurangi waktu untuk wawancara teknis menjadi 30 menit.

Salah satu tujuan wawancara teknis kami:

 let n = 0 while (++n < 5) { setTimeout(() => console.log(n), 10 + n) } //     ? 

Saya pikir ini adalah tes yang sangat signifikan bagi pengembang JavaScript. Dan intinya di sini bukanlah penutupan dan pemahaman tentang perbedaan antara preincrement dan postincrement, tetapi untuk beberapa alasan yang tidak dapat dijelaskan, seperempat orang yang diwawancarai percaya bahwa console.log akan dijalankan sebelum siklus berakhir. Saya tidak melebih-lebihkan. Orang-orang ini memiliki riwayat hidup dan pengalaman kerja setidaknya dua tahun, dan mereka berhasil menyelesaikan tugas-tugas lain yang tidak terikat dengan panggilan balik. Entah ini adalah generasi baru pengembang JavaScript yang tumbuh di async / menunggu, yang telah mendengar hal lain tentang Janji, tetapi panggilan balik untuk mereka - seperti telepon disk untuk remaja modern - akan memanggil nomor itu, meskipun bukan yang pertama kali, tetapi tidak akan mengerti cara kerjanya dan mengapa.

Tugas ini memiliki kelanjutan: Anda perlu menambahkan kode sehingga console.log juga berjalan di dalam setTimeout, tetapi nilai 1, 2, 3, 4 ditampilkan di konsol.Pepatah "hidup, belajar," sesuai di sini, karena pernah salah satu yang diwawancarai mengusulkan solusi seperti itu:

 setTimeout(n => console.log(n), 10 + n, n) 

Dan kemudian saya menemukan bahwa setTimeout dan setInterval meneruskan argumen ketiga dan selanjutnya ke callback. Sayang sekali, ya. Omong-omong, pengetahuan ternyata bermanfaat: Saya telah menggunakan fitur ini lebih dari sekali.

Tapi saya meminjam tugas ini dari Yandex karena:

 /*    fetchUrl,     .  fetchUrl     fetch,    Promise      reject */ fetchUrl('https://google/com') .then(...) .catch(...) // atch     5       fetchUrl 

Berikut adalah keterampilan yang diuji dengan Janji. Biasanya saya meminta untuk menyelesaikan masalah ini dengan janji-janji murni, dan kemudian menggunakan async / menunggu. Dengan async / menunggu, solusinya sederhana:

 function async fetchUrl(url) { for (let n = 0; n < 5; n++) { try { return await fetch(url) } catch (err) { } } throw new Error('Fetch failed after 5 attempts') } 

Anda juga dapat menerapkan pepatah "hidup, belajar," untuk keputusan ini, tetapi sehubungan dengan pewawancara Yandex saya: dia tidak menentukan bahwa async / menunggu tidak dapat digunakan, dan ketika saya menulis solusi ini, dia terkejut: "Saya tidak bekerja dengan async / menunggu, saya tidak berpikir itu bisa diselesaikan dengan mudah. ​​" Dia mungkin berharap melihat sesuatu seperti ini:

 function fetchUrl(url, attempt = 5) { return Promise.resolve() .then(() => fetch(url)) .catch(() => attempt-- ? fetchUrl(url, attempt) : Promise.reject('Fetch failed after 5 attempts')) }'error' 

Contoh ini mampu menunjukkan seberapa baik seseorang memahami janji, ini sangat penting di bagian belakang. Setelah saya melihat kode JavaScript dari seorang pengembang yang tidak sepenuhnya memahami janji-janji itu, menyiapkan janji untuk transaksi sekuel sebagai berikut:

 const transaction = Promise.resolve() for (const user of users) { transaction.then(() => { return some_action... }) } 

Dan bertanya-tanya mengapa hanya satu pengguna yang muncul dalam transaksinya. Seseorang dapat menggunakan Promise.all, tetapi orang dapat mengetahui bahwa Promise.prototype.then tidak menambahkan panggilan balik lagi, tetapi menciptakan janji baru dan akan menjadi seperti ini:

 let transaction = Promise.resolve() for (const user of users) { transaction = transaction.then(() => { await perform_some_operation... return some_action... }) } 

Kasus ini membuat saya berpikir tentang menyulitkan tugas untuk memahami janji-janji, tetapi kandidat yang menolak untuk menyelesaikan masalah membantu saya untuk merumuskan tugas baru, menyebut mereka benar-benar omong kosong dan mengatakan bahwa ia terbiasa bekerja dengan kode nyata, yang saya cari-cari selama beberapa menit. dalam kode sumber dari salah satu proyek kami, berikan kode nyata:

 public async addTicket(data: IAddTicketData): Promise<number> { const user = data.fromEmail ? await this.getUserByEmail(data.fromEmail) : undefined let category = data.category if (category === 'INCIDENT' && await this.isCategorizableType(data.type)) { category = 'INC_RFC' } const xml = await this.twig.render('Assyst/Views/add.twig', { from: data.fromEmail, text: data.text, category, user, }) const response = await this.query('events', 'post', xml) return new Promise((resolve, reject) => { xml2js.parseString(response, (err, result) => { if (err) { return reject(new Error(err.message)) } if (result.exception) { return reject(new Error(result.exception.message)) } resolve(result.event.id - 5000000) }) }) } 

Dan diminta untuk menyingkirkan kata kunci async / menunggu. Sejak itu, tugas ini adalah yang pertama dan, dalam separuh kasus, yang terakhir dalam wawancara - ia sering sering kewalahan.

Saya sendiri belum pernah memecahkan masalah ini sebelum menulis catatan ini dan melakukannya pertama kali untuk ketiga kalinya (yang pertama terlalu lama, dan yang kedua saya tidak melihat ada satu yang menunggu):



Kesimpulan apa yang bisa ditarik dari semua ini? Wawancara itu menarik dan bermanfaat ... Tentu saja, jika Anda tidak segera mencari pekerjaan.

Pada akhirnya, saya akan memberi Anda tugas lain dari cerita dengan Yandex, saya belum menunjukkannya kepada siapa pun * namun, saya sudah mengurus apa yang disebut kasus khusus. Ada satu set spanduk, setiap spanduk memiliki "berat" yang menunjukkan seberapa sering spanduk akan ditampilkan relatif terhadap spanduk lain:

 const banners = [ { name: 'banner 1', weight: 1 }, { name: 'banner 2', weight: 1 }, { name: 'banner 3', weight: 1 }, { name: 'banner 4', weight: 1 }, { name: 'banner 5', weight: 3 }, { name: 'banner 6', weight: 2 }, { name: 'banner 7', weight: 2 }, { name: 'banner 8', weight: 2 }, { name: 'banner 9', weight: 4 }, { name: 'banner 10', weight: 1 }, ] 

Misalnya, jika ada tiga spanduk dengan bobot 1, 1, 2, berat gabungannya adalah 4, dan berat yang ketiga adalah 2/4 dari total berat, maka itu harus ditampilkan dalam 50% kasus. Penting untuk mengimplementasikan fungsi getBanner, yang secara acak, tetapi dengan mempertimbangkan bobot akun, mengembalikan satu spanduk untuk ditampilkan. Solusinya dapat diperiksa dalam cuplikan ini , tempat distribusi yang diharapkan dan aktual ditampilkan.

UPD: mereka mulai tidak hanya untuk mengurangi artikel itu sendiri, tetapi juga untuk membakar karma dengan lompatan dan batas dan saya menyembunyikannya menyembunyikan materi, yang jelek dalam hubungannya dengan komentator. Saya memperbaiki mudachism ini di pihak saya.

Source: https://habr.com/ru/post/id470407/


All Articles