The Need for Speed: una retrospettiva sulla sfida della codifica JavaScript

Pubblicato: 2022-03-11

Toptal ha avviato l'app Web per la sfida della codifica JavaScript come un modo per attirare persone nelle nostre cabine per conferenze. Vedendo quanto successo ha avuto, abbiamo deciso di realizzare un progetto pilota sul web, aperto a tutti nella nostra comunità e alle loro reti.

Uno screenshot della JavaScript Speed ​​Coding Challenge di Toptal, che mostra una delle prime domande. Viene fornita una funzione denominata "double" e il suo corpo è costituito da un commento "return x doubled".

Al momento del lancio, abbiamo incoraggiato gli sviluppatori motivati ​​a trovare modi creativi per ottenere un punteggio elevato nella sfida complessiva della codifica JavaScript. Alcuni dei fattori chiave di successo dei grandi liberi professionisti indipendenti sono la capacità di pensare fuori dagli schemi e trovare modi creativi per lavorare all'interno di una serie di vincoli.

Domande sulla sfida della codifica JavaScript

Il guanto di sfida consisteva in una serie di domande JavaScript, simili a quelle che potrebbero essere utilizzate come domande di intervista, che andavano da domande di sfida JavaScript davvero di base:

 box.double = function double (x) { //return x doubled };

…a quelli più intermedi:

 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) };

Volevamo che sia i principianti che gli sviluppatori avanzati si divertissero e invitassero i loro amici e colleghi a competere con loro per i punteggi più alti. Le domande sono state ordinate per punti, in modo che anche gli sviluppatori junior potessero segnare alcuni punti. L'ordine delle domande con la stessa quantità di punti era casuale, quindi l'esperienza era leggermente diversa ogni volta, mentre l'intera serie di domande è rimasta la stessa per tutta la settimana.

L'obiettivo era quello di completare il maggior numero possibile di attività entro il limite di tempo di tre minuti. Nel caso in cui qualcuno trovasse un modo per completare tutte le attività nella sfida di codifica JavaScript, verrebbero assegnati 10 punti per ogni secondo rimasto. Abbiamo consentito più tentativi per completare la sfida.

La prima cosa che ci aspettavamo sarebbe che le persone avrebbero impiegato del tempo per risolvere le domande a un ritmo lento, quindi copiare e incollare le risposte nell'applicazione web.

Dopo un'ora e 40 minuti dall'avvio della sfida, la prima persona ha seguito questo approccio e ha ottenuto i 1445 punti massimi consentiti dall'applicazione, più alcuni punti extra che abbiamo assegnato per ogni secondo rimasto.

Un punto di svolta nella sfida di codifica JavaScript

Con l'approccio del copia e incolla, i migliori concorrenti non avevano più nulla da guadagnare concentrandosi sulla codifica delle risposte stesse. Invece, hanno rivolto la loro attenzione a portare la velocità alle loro capacità di automazione.

L'approccio più semplice a questo punto era scrivere del JavaScript che risolvesse ogni attività mentre si attendeva un ciclo fino a quando i pulsanti non fossero pronti e copiarlo e incollarlo nella console del browser:

 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()

Questa parte potrebbe anche essere automatizzata utilizzando normali strumenti di automazione come Selenium. Ma un modo più veloce sarebbe automatizzare l'uso dell'API, inviando le soluzioni alle attività:

 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)

Una cosa da notare è che, in questa versione della sfida di speedcoding, il codice è stato testato solo sul lato client. Per questo motivo, è stato possibile inviare semplicemente le risposte ai casi di test anziché il codice. Ciò ha consentito un'ottimizzazione e il taglio di alcuni millisecondi lato client.

Dopo tre giorni, la competizione ha iniziato davvero a scaldarsi, con il numero di tentativi per ogni intervallo di tre ore che è passato improvvisamente da meno di 1.000 a quasi 100.000.

Per tre giorni, i punteggi sono rimasti gli stessi. Alcune persone stavano scrivendo micro ottimizzazioni per il loro codice e molte persone inviavano le loro soluzioni in un ciclo, sperando che il server diventasse meno affollato in modo da poter ottenere qualche punto in più. Dovevamo fare una grande sorpresa.

Superare il punteggio massimo della JavaScript Coding Challenge

Per prima cosa, facciamo qualche veloce calcolo: abbiamo ottenuto un totale di 1445 punti assegnati per il completamento di tutte le attività e un tempo concesso di 180 secondi. Se assegniamo 10 punti al secondo rimasto nella domanda, il punteggio teorico massimo ottenibile sarebbe 3245, nel caso in cui tutte le risposte vengano inviate all'istante.

Uno dei nostri utenti ha ottenuto un punteggio di oltre 6000, che ha continuato a crescere costantemente nel tempo.

Come può qualcuno ottenere un punteggio così alto?

Dopo una breve revisione, abbiamo scoperto cosa stava succedendo. Il nostro principale concorrente, uno sviluppatore professionista full-stack e Toptaler con oltre 15 anni di esperienza di programmazione competitiva, ha trovato una scappatoia nell'impostazione della sfida di codifica. Ha generato più bot, che hanno rallentato il server; nel frattempo, poteva completare lo stesso compito (quello che ha assegnato il maggior numero di punti) il maggior numero di volte possibile e assegnare i propri punteggi a una singola voce, aggiungendo continuamente al punteggio di quella voce.

Questo non era contro le regole, poiché consentivamo soluzioni creative; tuttavia, il metodo particolare che stava utilizzando faceva sì che il server fosse notevolmente più occupato, rendendo le richieste di rete più lente per tutti gli altri. La prima cosa che abbiamo fatto è stata aumentare la potenza del nostro server, che lo ha fatto passare da 56.000 a 70.000 punti e rimanere al primo posto.

Anche se non volevamo intervenire sul modo in cui le persone interagivano con la sfida, questi tentativi hanno rallentato il server al punto che la sfida era difficile da usare per altri utenti, quindi abbiamo deciso di correggere la scappatoia.

La correzione ha impedito ad altre persone di ottenere lo stesso punteggio durante l'ultimo giorno della sfida di codifica JavaScript. Per questo motivo, abbiamo deciso di estendere il numero di premi assegnati ai migliori concorrenti. In origine, il primo premio, un paio di AirPods, doveva andare solo al miglior concorrente. Alla fine, gli AirPod sono stati assegnati a coloro che detenevano i primi sei posti.

Inizi umili e finali feroci: alcune statistiche sulla sfida di codifica JavaScript

Anche i nostri migliori marcatori hanno avuto qualche difficoltà all'inizio. In effetti, di tutte le persone che hanno fatto cinque o più tentativi, il punteggio più alto per il primo tentativo di qualcuno era 645 e il punteggio medio per i primi tentativi in ​​quel gruppo era di soli 25 punti.

Entro la fine del concorso, si poteva indovinare la strategia tentata dal numero totale di tentativi. Mentre alcuni erano più efficienti di altri, il miglior concorrente di gran lunga aveva il conteggio dei tentativi più alto.

Un grafico a barre logaritmico combinato che mostra i punteggi più alti dei primi 20 concorrenti e il conteggio totale dei tentativi. Mentre il conteggio dei tentativi più alti è andato al capocannoniere, il secondo, il terzo e il quarto conteggio dei tentativi più alti sono andati rispettivamente al 13°, nono e secondo posto. Esattamente la metà dei primi 20, compresi i concorrenti al quarto e sesto posto, ha avuto meno di 1.000 tentativi. Due concorrenti hanno avuto meno di 100 tentativi e un altro addirittura meno di 10.

Andando avanti

Cosa riserva il futuro?

Questa è stata solo la prima iterazione di sfida della codifica JS. Vogliamo fare molte altre sfide di programmazione JavaScript in futuro, imparare le lezioni dalle esecuzioni precedenti e renderlo ancora più eccitante. La prima cosa che vogliamo fare è implementare la limitazione dei tentativi per limitare il numero di invii. Vogliamo anche espanderci oltre le sfide di codifica in JavaScript, rendendole disponibili in un'ampia gamma di linguaggi di programmazione.

Infine, mentre stiamo adottando misure per rendere più sicura la sfida della codifica JavaScript, prevediamo di continuare a consentire l'uso di bot e altri approcci creativi per le sfide future.


Un ringraziamento speciale a Pavel Vydra, Anton Andriievskyi, Tiago Chilanti e Matei Copot per i loro contributi alla sfida e a questo articolo, e a @Zirak per il progetto open source che ha costituito la base dell'app del concorso. Allo stesso modo, grazie a tutti coloro che hanno partecipato e gestito il concorso.