Nevoia de viteză: o retrospectivă a provocării Toptal de codare JavaScript
Publicat: 2022-03-11Toptal a început aplicația web de provocare de codare JavaScript ca o modalitate de a atrage oameni la cabinele noastre de conferințe. Văzând cât de succes a fost, am decis să facem un pilot pe web, deschis pentru toată lumea din comunitatea noastră și rețelele lor.
La momentul lansării, am încurajat dezvoltatorii motivați să vină cu modalități creative de a obține un scor mare la provocarea generală de codare JavaScript. Unii dintre factorii cheie de succes ai marilor freelanceri independenți sunt capacitatea de a gândi în afara cutiei și de a găsi modalități creative de a lucra într-un set de constrângeri.
Întrebări despre codificarea JavaScript
Mănușul a constat dintr-o serie de întrebări JavaScript — asemănătoare cu cele care ar putea fi folosite ca întrebări de interviu — variind de la întrebări de bază despre provocare JavaScript:
box.double = function double (x) { //return x doubled };
…la cele mai intermediare:
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) };
Ne-am dorit atât începătorii, cât și dezvoltatorii avansați să se distreze și să-și invite prietenii și colegii să concureze cu ei pentru cele mai mari scoruri. Întrebările au fost sortate pe puncte, astfel încât până și dezvoltatorii juniori să poată obține câteva puncte. Ordinea întrebărilor care aveau aceeași cantitate de puncte a fost aleatorie, așa că experiența a fost ușor diferită de fiecare dată, în timp ce setul complet de întrebări a rămas același pe parcursul săptămânii.
Scopul a fost de a finaliza cât mai multe sarcini în limita de timp de trei minute. În cazul în care cineva a găsit o modalitate de a finaliza toate sarcinile din provocarea de codare JavaScript, s-ar acorda 10 puncte pentru fiecare secundă rămasă. Am permis mai multe încercări pentru a finaliza provocarea.
Primul lucru pe care am anticipat că se va întâmpla este că oamenii vor lua ceva timp pentru a rezolva întrebările într-un ritm lejer, apoi vor copia și lipi răspunsurile în aplicația web.
După o oră și 40 de minute de la lansarea provocării, prima persoană a urmat această abordare și a obținut cele 1445 de puncte maxime pe care le-a permis aplicația, plus câteva puncte în plus pe care le-am acordat pentru fiecare secundă rămasă.
Un punct de cotitură în provocarea de codare JavaScript
Cu abordarea copy-and-paste, concurenții de top nu mai aveau nimic de câștigat din concentrarea pe codificarea răspunsurilor înșiși. În schimb, și-au îndreptat atenția spre a aduce viteza abilităților lor de automatizare.
Cea mai ușoară abordare în acest moment a fost să scrieți niște JavaScript care să rezolve fiecare sarcină în timp ce așteptați într-o buclă până când butoanele erau gata și să îl copiați și să-l inserați în consola browserului:
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()
Această parte ar putea fi, de asemenea, automatizată folosind instrumente obișnuite de automatizare precum Selenium. Dar o modalitate mai rapidă ar fi automatizarea utilizării API-ului, trimițând soluțiile la sarcini:
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)
Un lucru de remarcat este că, în această versiune a provocării de codificare rapidă, codul a fost testat doar pe partea clientului. Din acest motiv, a fost posibil să trimiteți pur și simplu răspunsurile la cazurile de testare în loc de cod. Acest lucru a permis o optimizare și a reduce câteva milisecunde pe partea clientului.

Timp de trei zile, scorurile au rămas aceleași. Unii oameni scriau micro-optimizări pentru codul lor, iar mai mulți oameni își trimiteau soluțiile într-o buclă, în speranța că serverul va deveni mai puțin aglomerat, astfel încât să poată obține câteva puncte înainte. Aveam o mare surpriză.
Depășirea scorului maxim al provocării de codare JavaScript
În primul rând, să facem niște calcule rapide: am avut un total de 1445 de puncte acordate pentru finalizarea tuturor sarcinilor și un timp alocat de 180 de secunde. Dacă acordăm 10 puncte pe secundă rămase în aplicație, punctajul maxim teoretic realizabil ar fi 3245 — în cazul trimiterii instantanee a tuturor răspunsurilor.
Unul dintre utilizatorii noștri a obținut un scor de peste 6000, care a continuat să crească constant de-a lungul timpului.
Cum a putut cineva să obțină un scor atât de mare?
După o scurtă analiză, am aflat ce se întâmplase. Concurentul nostru de top, un dezvoltator profesionist full-stack și Toptaler cu peste 15 ani de experiență în programare competitivă, a găsit o lacună în configurarea provocării de codare. El a generat mai mulți roboți, care au încetinit serverul; Între timp, el putea îndeplini aceeași sarcină (cea care a acordat cele mai multe puncte) de cât mai multe ori posibil și să-și atribuie punctajele unei singure intrări, adăugând continuu la scorul acelei intrări.
Acest lucru nu a fost împotriva regulilor, deoarece am permis soluții creative; totuși, metoda specială pe care o folosea a făcut ca serverul să fie considerabil mai ocupat, făcând cererile de rețea mai lente pentru toți ceilalți. Primul lucru pe care l-am făcut a fost să ne creștem puterea serverului, ceea ce l-a făcut doar să treacă de la 56.000 la 70.000 de puncte și să rămână pe primul loc.
Deși nu am vrut să intervenim în modul în care oamenii interacționau cu provocarea, aceste încercări au încetinit serverul în măsura în care provocarea a fost greu de utilizat pentru alți utilizatori, așa că am decis să remediem lacuna.
Remedierea a făcut imposibil ca alte persoane să obțină același scor în ultima zi a provocării de codare JavaScript. Din acest motiv, am decis să extindem numărul de premii acordate concurenților de top. Inițial, premiul principal – o pereche de AirPods – trebuia să revină numai unui concurent de top. În cele din urmă, AirPod-urile au fost oferite celor care dețin primele șase locuri.
Începuturi umile și sfârșituri feroce: câteva statistici privind provocarea codării JavaScript
Chiar și cei mai mari marcatori ai noștri au avut unele dificultăți la început. De fapt, dintre toate persoanele care au făcut cinci încercări sau mai multe, scorul maxim pentru prima încercare a oricui a fost 645, iar scorul median pentru primele încercări din grupa respectivă a fost de doar 25 de puncte.
Până la sfârșitul concursului, se putea ghici strategia încercată din numărul total de încercări. În timp ce unii au fost mai eficienți decât alții, cel mai mare concurent de departe a avut cel mai mare număr de încercări.
Inainta
Ce rezerva viitorul?
Aceasta a fost doar prima iterație a provocării de codare JS. Dorim să facem mai multe provocări de programare JavaScript în viitor, să învățăm lecții din rulările anterioare și să o facem și mai interesantă. Primul lucru pe care vrem să-l facem este să implementăm încercarea de throttling pentru a limita numărul de trimiteri. De asemenea, dorim să ne extindem dincolo de provocările de codificare în JavaScript, făcându-le disponibile într-o gamă largă de limbaje de programare.
În cele din urmă, în timp ce luăm măsuri pentru a face provocarea de codificare JavaScript mai sigură, intenționăm să continuăm să permitem utilizarea roboților și a altor abordări creative pentru provocările viitoare.
Mulțumiri speciale lui Pavel Vydra, Anton Andriievskyi, Tiago Chilanti și Matei Copot pentru contribuțiile lor la provocare și acest articol, și lui @Zirak pentru proiectul open-source care a stat la baza aplicației concursului. De asemenea, mulțumim tuturor celor care au participat la concurs și au condus.