A necessidade de velocidade: uma retrospectiva do desafio de codificação JavaScript Toptal
Publicados: 2022-03-11A Toptal iniciou o aplicativo da web do desafio de codificação JavaScript como forma de atrair pessoas para nossos estandes de conferência. Vendo o sucesso, decidimos fazer um piloto na web, aberto para todos da nossa comunidade e suas redes.
No momento do lançamento, incentivamos os desenvolvedores motivados a criar maneiras criativas de obter uma pontuação alta no desafio geral de codificação JavaScript. Alguns dos principais fatores de sucesso de grandes freelancers independentes são a capacidade de pensar fora da caixa e encontrar maneiras criativas de trabalhar dentro de um conjunto de restrições.
Perguntas sobre o desafio de codificação JavaScript
O desafio consistia em uma série de perguntas JavaScript - semelhantes àquelas que podem ser usadas como perguntas de entrevista - variando de perguntas de desafio JavaScript realmente básicas:
box.double = function double (x) { //return x doubled };
…para os mais intermediários:
box.dateRank = function dateRank (x) { // x is a date in 2019 as string (example: "06/30/2019") // return the rank of the day in 2019 (ie, "09/01/2019" translates to 244) };
Queríamos que desenvolvedores iniciantes e avançados se divertissem e convidassem seus amigos e colegas para competir com eles pelas pontuações mais altas. As perguntas foram classificadas por pontos, para que mesmo desenvolvedores juniores pudessem marcar alguns pontos. A ordem das perguntas com a mesma quantidade de pontos era aleatória, então a experiência era um pouco diferente a cada vez, enquanto o conjunto completo de perguntas permanecia o mesmo ao longo da semana.
O objetivo era completar o maior número possível de tarefas dentro do limite de tempo de três minutos. Caso alguém encontrasse uma maneira de concluir todas as tarefas do desafio de codificação JavaScript, 10 pontos seriam concedidos para cada segundo restante. Permitimos várias tentativas para completar o desafio.
A primeira coisa que prevíamos que aconteceria é que as pessoas levariam algum tempo para resolver as questões em um ritmo lento e depois copiar e colar as respostas no aplicativo da web.
Após uma hora e 40 minutos de lançamento do desafio, a primeira pessoa seguiu essa abordagem e obteve o máximo de 1445 pontos que o aplicativo permitia, além de alguns pontos extras que atribuímos por cada segundo restante.
Um ponto de virada no desafio de codificação JavaScript
Com a abordagem de copiar e colar, os principais concorrentes não tinham mais nada a ganhar concentrando-se na codificação das próprias respostas. Em vez disso, eles voltaram sua atenção para acelerar suas habilidades de automação.
A abordagem mais fácil neste ponto era escrever algum JavaScript que resolvesse cada tarefa enquanto aguardava um loop até que os botões estivessem prontos e copie e cole no console do navegador:
const solutions = { 'double': 'return x*2', 'numberToString': '...', 'square': '...', 'floatToInt': '...', 'isEven': '...', 'squareroot': '...', 'removeFirstFive': '...', // ... 'dateRank': '...', // ... }; const get_submit_button = () => document.querySelector('.task-buttons > .col > .btn'); const solve = () => { const ace_editor = ace.edit(document.querySelector('.ace_editor')) const submission = ace_editor.getValue() for (const key in solutions) { if (submission.includes('box.' + key + ' ')) { ace_editor.insert(solutions[key]) get_submit_button().click() setTimeout(() => { get_submit_button().click() setTimeout(() => { solve() }, 400) }, 900) return } } } solve()
Esta parte também pode ser automatizada usando ferramentas de automação regulares como o Selenium. Mas uma forma mais rápida seria automatizar o uso da API, enviando as soluções para as tarefas:
const request = require('request'); const runTask = (data, entryId, callback) => { const tests = data.nextTask.tests_json; const results = Object.fromEntries( Object.entries(tests).map(([key, value]) => [key, value.result]) ); request.post(`https://speedcoding.toptal.com/webappApi/entry/${entryId}/attemptTask`, { form: { attempt_id: data.attemptId, tests_json: JSON.stringify(results), }, }, (error, res, body) => { if (error) throw error; const next = JSON.parse(body).data if (next.isChallengeEntryFinished) { callback(next) return } runTask(next, entryId, callback) }); } const runEntry = (callback) => { request.post('https://speedcoding.toptal.com/webappApi/entry', { form: { challengeSlug: 'toptal-speedcoding', email: ..., leaderboardName: ..., isConfirmedToBeContacted: ..., dateStop: ... }, }, (error, res, body) => { if (error) throw error; const { data } = JSON.parse(body); const entryId = data.entry.id runTask(data, entryId, callback) }); } runEntry(console.log)
Uma coisa a notar é que, nesta versão do desafio speedcoding, o código foi testado apenas no lado do cliente. Por causa disso, foi possível simplesmente enviar as respostas para os casos de teste em vez do código. Isso permitiu uma otimização e cortar alguns milissegundos no lado do cliente.

Durante três dias, as pontuações permaneceram as mesmas. Algumas pessoas estavam escrevendo micro otimizações para seu código, e várias pessoas estavam enviando suas soluções em um loop, esperando que o servidor ficasse menos lotado para que eles pudessem obter alguns pontos à frente. Estávamos esperando uma grande surpresa.
Quebrando a pontuação máxima do JavaScript Coding Challenge
Primeiro, vamos fazer algumas contas rápidas: tivemos um total de 1.445 pontos concedidos pela conclusão de todas as tarefas e um limite de tempo de 180 segundos. Se atribuirmos 10 pontos por segundo restantes na inscrição, a pontuação máxima teórica alcançável seria 3245 - no caso de enviar todas as respostas instantaneamente.
Um de nossos usuários alcançou uma pontuação de mais de 6.000, que continuou crescendo ao longo do tempo.
Como alguém pode obter uma pontuação tão alta?
Após uma breve revisão, descobrimos o que estava acontecendo. Nosso principal concorrente, um desenvolvedor full-stack profissional e Toptaler com mais de 15 anos de experiência em programação competitiva, encontrou uma brecha na configuração do desafio de codificação. Ele gerou vários bots, o que deixou o servidor mais lento; enquanto isso, ele poderia completar a mesma tarefa (a que deu mais pontos) tantas vezes quanto possível e atribuir suas pontuações a uma única entrada, adicionando continuamente à pontuação dessa entrada.
Isso não era contra as regras, pois permitimos soluções criativas; no entanto, o método específico que ele estava usando fazia com que o servidor ficasse consideravelmente mais ocupado, tornando as solicitações de rede mais lentas para todos os outros. A primeira coisa que fizemos foi aumentar o poder do nosso servidor, o que só o fez passar de 56.000 para 70.000 pontos e ficar em primeiro lugar.
Embora não quiséssemos intervir na maneira como as pessoas interagiam com o desafio, essas tentativas deixaram o servidor mais lento na medida em que o desafio era difícil de usar para outros usuários, então decidimos corrigir a brecha.
A correção impossibilitou que outras pessoas alcançassem a mesma pontuação durante o último dia do desafio de codificação JavaScript. Por isso, decidimos estender o número de prêmios concedidos aos melhores competidores. Originalmente, o prêmio principal - um par de AirPods - deveria ir apenas para o primeiro concorrente. No final, os AirPods foram entregues aos que ocupavam os seis primeiros lugares.
Começos humildes e fins ferozes: algumas estatísticas do desafio de codificação JavaScript
Mesmo os nossos maiores pontuadores tiveram alguma dificuldade no início. Na verdade, de todas as pessoas que fizeram cinco tentativas ou mais, a pontuação máxima para a primeira tentativa de qualquer um foi de 645, e a pontuação média para as primeiras tentativas nesse grupo foi de apenas 25 pontos.
Ao final da competição, pode-se adivinhar a estratégia que está sendo tentada pelo número total de tentativas. Enquanto alguns foram mais eficientes do que outros, o melhor competidor de longe teve a maior contagem de tentativas.
Seguindo em frente
O que o futuro guarda?
Esta foi apenas a primeira iteração de desafio de codificação JS. Queremos fazer muitos outros desafios de programação JavaScript no futuro, aprender lições das execuções anteriores e torná-lo ainda mais empolgante. A primeira coisa que queremos fazer é implementar a limitação de tentativas para limitar o número de envios. Também queremos ir além dos desafios de codificação em JavaScript, tornando-os disponíveis em uma ampla variedade de linguagens de programação.
Por fim, enquanto tomamos medidas para tornar o desafio de codificação JavaScript mais seguro, planejamos continuar permitindo o uso de bots e outras abordagens criativas para desafios futuros.
Agradecimentos especiais a Pavel Vydra, Anton Andriievskyi, Tiago Chilanti e Matei Copot por suas contribuições ao desafio e a este artigo, e a @Zirak pelo projeto de código aberto que formou a base do aplicativo do concurso. Da mesma forma, obrigado a todos que participaram – e concorreram – ao concurso.