Despu茅s de exponer mi historia de "empleo" en Yandex
en el comentario sobre la nota sensacional "C贸mo trabaj茅 durante 3 meses en Y. Market y renunciar", ser铆a injusto ocultar el beneficio que obtuve de mi experiencia con Yandex.Message.
Mis responsabilidades laborales incluyen entrevistas t茅cnicas de candidatos para el puesto de Desarrollador Fullstack JavaScript / TypeScript. He participado activamente en este negocio (驴vale la pena decir que estoy un poco harto?) Durante m谩s de un a帽o, he tenido m谩s de 30 entrevistas t茅cnicas.
Anteriormente en una entrevista t茅cnica, le hice al candidato preguntas bastante est煤pidas como "qu茅 es un cierre", "c贸mo se implementa la herencia en JavaScript", "hay una tabla en la base de datos con dichos 铆ndices, por favor d铆ganos c贸mo puede acelerar tal y cual solicitud ", que, aunque ayudaron a identificar las habilidades de ingenier铆a del candidato, no les permiti贸 concluir qu茅 tan bien una persona puede resolver problemas y qu茅 tan r谩pido puede descubrir un c贸digo existente. Lo que no pudo sino conducir a tristes consecuencias ...
Pero todo cambi贸 despu茅s de pasar por cuatro rondas de entrevistas t茅cnicas en Yandex.
Por lo general, las piedras vuelan al jard铆n de los entrevistadores de Yandex para:
1. Tareas que no tienen valor pr谩ctico;
2. La necesidad de resolver estos problemas en trozos de papel con un l谩piz o en una pizarra.
Ya es el a帽o 2019 y es hora de lanzar una l铆nea de producci贸n separada para lanzar calderas en el infierno para aquellos que obligan a las personas a escribir texto a mano, sin mencionar el c贸digo. Cada persona escribe de diferentes maneras, y al preparar el texto para esta nota, tuve que reescribir, por ejemplo, este p谩rrafo en particular seis veces; si escribiera notas para Habr en papel, no escribir铆a notas para Habr.
Pero no estoy de acuerdo con la tesis sobre la inutilidad pr谩ctica de las tareas de Yandex. Incluso el desarrollo de rutina es no, no, pero le dar谩 una tarea que tiene varias soluciones. No necesita pensar en uno durante mucho tiempo, pero no es 贸ptimo en t茅rminos de tama帽o de c贸digo, rendimiento o expresividad. El otro es todo lo contrario, pero requiere que el programador tenga experiencia en la construcci贸n de algoritmos eficientes y comprensibles. Aqu铆 hay un ejemplo de una entrevista:
getRanges([0, 1, 2, 3, 4, 7, 8, 10])
Sobre este problema, me equivoqu茅 seriamente y decid铆 que no era la forma m谩s hermosa. La soluci贸n, lamentablemente, no se ha conservado, por lo que le dar茅 una soluci贸n a uno de nuestros candidatos:
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('-') }
De las desventajas: acceder a la matriz en un 铆ndice inexistente y manipulaci贸n de cadena fea: join-split-join. Esta soluci贸n tambi茅n es incorrecta, porque con el ejemplo getRanges ([1, 2, 3, 5, 6, 8]), se devuelve "1-3,5-6,8" y "matar" la coma al final para aumentar a煤n m谩s las condiciones al complicar la l贸gica y reducir la legibilidad.
Aqu铆 hay una soluci贸n al estilo 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(',')
驴Google ayudar谩 a escribir soluciones tan elegantes? Para producir dicho c贸digo, necesita dos componentes: experiencia con muchos algoritmos y excelente conocimiento del idioma. Y esto es exactamente lo que advierten los reclutadores de Yandex: le preguntar谩n sobre algoritmos y lenguaje. Yandex prefiere contratar desarrolladores que puedan escribir c贸digo genial. Dichos programadores son efectivos, pero lo m谩s importante, intercambiables: escribir谩n sobre las mismas soluciones. Los desarrolladores con menos conocimientos te贸ricos en una tarea pueden dar docenas de soluciones diversas, a veces simplemente sorprendentes: uno de los candidatos para nuestra vacante envolvi贸 una muleta que me puso los ojos en la frente.
UPD: como
se帽al贸 el usuario
MaxVetrov , mi soluci贸n es incorrecta:
getRanges([1,2,3,4,6,7])
Por lo tanto, yo mismo no he podido resolver adecuadamente este problema hasta ahora.
UPD2: En general, los comentarios me convencieron de que este c贸digo result贸 ser malo, incluso si funcionaba correctamente.
No fue en vano que pas茅 tiempo traduciendo art铆culos en la oficina de Yandex, porque durante esta lecci贸n entend铆 c贸mo convertirme en un entrevistador efectivo. Tom茅 la idea y el formato de sus tareas como base, pero:
- No ofrec铆 escribir c贸digo en papel, pero ped铆 escribir en code.yandex-team.ru . Este es un editor de c贸digo en l铆nea multiusuario sin la posibilidad de su ejecuci贸n. Quiz谩s haya otra opci贸n m谩s conveniente, pero hubo una b煤squeda y qued贸 flojera;
- No necesitaba una soluci贸n ideal, pero pidi贸 resolverla de alguna manera;
- No requer铆a el conocimiento del idioma de memoria, pod铆a preguntarse la funci贸n o m茅todo deseado;
- Redujo el tiempo para una entrevista t茅cnica a 30 minutos.
Uno de los objetivos de nuestra entrevista t茅cnica:
let n = 0 while (++n < 5) { setTimeout(() => console.log(n), 10 + n) }
Creo que esta es una prueba muy importante para el desarrollador de JavaScript. Y el punto aqu铆 no es el cierre y la comprensi贸n de las diferencias entre preincremento y postincremento, sino que, por alguna raz贸n inexplicable, una cuarta parte de los entrevistados cree que console.log se ejecutar谩 antes de que termine el ciclo. No estoy exagerando. Estas personas tienen un curr铆culum y experiencia laboral de al menos dos a帽os, y resolvieron con 茅xito otras tareas que no estaban vinculadas a las devoluciones de llamadas. O bien, esta es una nueva generaci贸n de desarrolladores de JavaScript que crecieron en async / wait, que escucharon algo m谩s sobre Promise, pero las devoluciones de llamadas para ellos, como un tel茅fono de disco para un adolescente moderno, marcar谩n el n煤mero, incluso si no es la primera vez, pero no lo entender谩n c贸mo funciona y por qu茅.
Esta tarea tiene una continuaci贸n: necesita agregar el c贸digo para que console.log tambi茅n se ejecute dentro de setTimeout, pero los valores 1, 2, 3, 4. se muestran en la consola. El dicho "vive, aprende" es apropiado aqu铆, porque una vez uno de los entrevistados propuso tal soluci贸n:
setTimeout(n => console.log(n), 10 + n, n)
Y luego descubr铆 que setTimeout y setInterval pasan el tercer argumento y los posteriores a la devoluci贸n de llamada. Es una pena, s铆. Por cierto, el conocimiento result贸 ser 煤til: he usado esta funci贸n m谩s de una vez.
Pero tom茅 prestada esta tarea de Yandex como es:
fetchUrl('https://google/com') .then(...) .catch(...)
Aqu铆 hay habilidades probadas con Promise. Por lo general, pido resolver este problema con promesas puras, y luego usar async / await. Con async / await, la soluci贸n es intuitivamente simple:
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') }
Tambi茅n puede aplicar el dicho "vive, aprende" a esta decisi贸n, pero con respecto a mi entrevistador de Yandex: no especific贸 que se puede / no se puede usar async / waitit, y cuando escrib铆 esta soluci贸n, se sorprendi贸: "No trabaj茅 con async / wait, no pens茅 que se pudiera resolver tan f谩cilmente ". Probablemente esperaba ver algo como esto:
function fetchUrl(url, attempt = 5) { return Promise.resolve() .then(() => fetch(url)) .catch(() => attempt-- ? fetchUrl(url, attempt) : Promise.reject('Fetch failed after 5 attempts')) }'error'
Este ejemplo puede mostrar qu茅 tan bien una persona entiende las promesas, esto es especialmente importante en la parte posterior. Una vez que vi el c贸digo JavaScript de un desarrollador que no entendi贸 completamente las promesas, prepar茅 una promesa para la transacci贸n de secuencia de la siguiente manera:
const transaction = Promise.resolve() for (const user of users) { transaction.then(() => { return some_action... }) }
Y pregunt谩ndose por qu茅 solo un usuario aparece en su transacci贸n. Se podr铆a usar Promise.all, pero se podr铆a saber que Promise.prototype.then no agrega otra devoluci贸n de llamada, sino que crea una nueva promesa y ser谩 as铆:
let transaction = Promise.resolve() for (const user of users) { transaction = transaction.then(() => { await perform_some_operation... return some_action... }) }
Este caso me hizo pensar en complicar la tarea de comprender las promesas, pero el candidato que se neg贸 a resolver los problemas me ayud贸 a formular una nueva tarea, los llam贸 literalmente basura y dijo que estaba acostumbrado a trabajar con c贸digo real, que rebusqu茅 durante un par de minutos. en el c贸digo fuente de uno de nuestros proyectos, le dio el c贸digo real:
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) }) }) }
Y se le pidi贸 que elimine las palabras clave async / wait. Desde entonces, esta tarea fue la primera y, en la mitad de los casos, la 煤ltima en la entrevista, a menudo est谩 realmente abrumado.
Yo mismo nunca he resuelto este problema antes de escribir esta nota y lo hago por
primera vez por tercera vez (la
primera es demasiado larga y en la
segunda no not茅 que quedaba una espera):

驴Qu茅 conclusi贸n se puede sacar de todo esto? Las entrevistas son interesantes y 煤tiles ... Por supuesto, si no est谩 buscando trabajo con urgencia.
Al final, te dar茅 otra tarea de la historia con Yandex, todav铆a no se la he mostrado a nadie
* , me he ocupado de lo que se llama un caso especial. Hay un conjunto de pancartas, cada pancarta tiene un "peso", que indica con qu茅 frecuencia se mostrar谩 la pancarta en relaci贸n con otras pancartas:
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 }, ]
Por ejemplo, si hay tres pancartas con los pesos 1, 1, 2, su peso combinado es 4, y el peso del tercero es 2/4 del peso total, por lo que debe mostrarse en el 50% de los casos. Es necesario implementar la funci贸n getBanner, que al azar, pero teniendo en cuenta los pesos, devuelve un banner para mostrar. La soluci贸n se puede verificar
en este fragmento , donde se muestra la distribuci贸n esperada y real.
UPD: comenzaron no solo a menos el art铆culo en s铆, sino tambi茅n a quemar karma a pasos agigantados y lo ocult茅 ocultando material, que es feo en relaci贸n con los comentaristas. Corrijo este mudaquismo de mi parte.