Need for Speed: Topowa retrospektywa wyzwań związanych z kodowaniem JavaScript
Opublikowany: 2022-03-11Firma Toptal uruchomiła aplikację webową z kodowaniem JavaScript, aby przyciągnąć ludzi do naszych stoisk konferencyjnych. Widząc, jak odniósł sukces, postanowiliśmy stworzyć pilotażowy program w sieci, otwarty dla wszystkich członków naszej społeczności i ich sieci.
W momencie premiery zachęcaliśmy zmotywowanych programistów do wymyślania kreatywnych sposobów na osiągnięcie wysokich wyników w ogólnym wyzwaniu kodowania JavaScript. Niektóre z kluczowych czynników sukcesu świetnych niezależnych freelancerów to umiejętność nieszablonowego myślenia i znajdowania kreatywnych sposobów pracy w ramach zestawu ograniczeń.
Pytania dotyczące wyzwań związanych z kodowaniem JavaScript
Rękawica składała się z szeregu pytań JavaScript — podobnych do tych, które mogą być użyte jako pytania do rozmowy kwalifikacyjnej — począwszy od naprawdę podstawowych pytań wyzwań JavaScript:
box.double = function double (x) { //return x doubled };
…do bardziej średniozaawansowanych:
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) };
Chcieliśmy, aby zarówno początkujący, jak i zaawansowani programiści dobrze się bawili i zapraszali swoich przyjaciół i kolegów do rywalizacji z nimi o jak najlepsze wyniki. Pytania zostały posortowane punktowo, dzięki czemu nawet młodsi programiści mogli zdobyć punkty. Kolejność pytań o tej samej liczbie punktów była losowa, więc doświadczenie za każdym razem było nieco inne, podczas gdy pełny zestaw pytań pozostawał taki sam przez cały tydzień.
Celem było wykonanie jak największej liczby zadań w limicie czasu trzech minut. Gdyby ktoś znalazł sposób na wykonanie wszystkich zadań w wyzwaniu kodowania JavaScript, za każdą pozostałą sekundę przyznawane byłoby 10 punktów. Zezwoliliśmy na wielokrotne próby ukończenia wyzwania.
Pierwszą rzeczą, jaka się spodziewaliśmy, jest to, że ludzie poświęcą trochę czasu na rozwiązanie pytań w wolnym tempie, a następnie skopiują i wkleją odpowiedzi do aplikacji internetowej.
Po godzinie i 40 minutach od uruchomienia wyzwania, pierwsza osoba zastosowała się do tego podejścia i uzyskała 1445 maksymalnych punktów, na które pozwalała aplikacja, plus kilka dodatkowych punktów, które przyznawaliśmy za każdą pozostałą sekundę.
Punkt zwrotny w wyzwaniu kodowania JavaScript
Dzięki podejściu „kopiuj i wklej” najlepsi uczestnicy nie mieli nic więcej do zyskania, skupiając się na samym kodowaniu odpowiedzi. Zamiast tego skupili się na przyspieszeniu swoich umiejętności automatyzacji.
Najłatwiejszym podejściem w tym momencie było napisanie kodu JavaScript, który rozwiąże każde zadanie podczas oczekiwania w pętli, aż przyciski będą gotowe, a następnie skopiuj i wklej go do konsoli przeglądarki:
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()
Ta część może być również zautomatyzowana przy użyciu zwykłych narzędzi do automatyzacji, takich jak Selenium. Ale szybszym sposobem byłoby zautomatyzowanie korzystania z API, wysyłanie rozwiązań do zadań:
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)
Należy zauważyć, że w tej wersji wyzwania szybkiego kodowania kod był testowany tylko po stronie klienta. Dzięki temu możliwe było po prostu wysłanie odpowiedzi do przypadków testowych zamiast kodu. Pozwoliło to na optymalizację i skrócenie kilku milisekund po stronie klienta.

Przez trzy dni wyniki pozostały takie same. Niektóre osoby pisały mikrooptymalizacje swojego kodu, a kilka osób zgłaszało swoje rozwiązania w pętli, mając nadzieję, że serwer stanie się mniej zatłoczony, dzięki czemu będą mogli uzyskać kilka punktów do przodu. Spodziewaliśmy się wielkiej niespodzianki.
Przełamanie maksymalnego wyniku wyzwania kodowania JavaScript
Najpierw zróbmy szybką matematykę: za wykonanie wszystkich zadań otrzymaliśmy łącznie 1445 punktów i 180 sekund. Jeśli przyznamy 10 punktów na sekundę pozostałych we wniosku, maksymalny teoretycznie możliwy do uzyskania wynik wyniósłby 3245 – w przypadku natychmiastowego przesyłania wszystkich odpowiedzi.
Jeden z naszych użytkowników uzyskał wynik ponad 6000, który z biegiem czasu stale rośnie.
Jak ktoś mógł uzyskać tak wysoki wynik?
Po krótkim przeglądzie odkryliśmy, co się dzieje. Nasz najlepszy uczestnik, profesjonalny programista full-stack i Toptaler z ponad 15-letnim doświadczeniem w programowaniu konkurencyjnym, znalazł lukę w konfiguracji wyzwania kodowania. Spawnował wiele botów, co spowolniło serwer; w międzyczasie mógł wykonać to samo zadanie (to, które przyznało najwięcej punktów) tyle razy, ile to możliwe i przypisać swoje wyniki do jednego wpisu, stale dodając do tego wpisu.
Nie było to sprzeczne z zasadami, ponieważ pozwalaliśmy na kreatywne rozwiązania; jednak konkretna metoda, której używał, powodowała, że serwer był znacznie bardziej zajęty, co powodowało, że żądania sieciowe były wolniejsze dla wszystkich innych. Pierwszą rzeczą, którą zrobiliśmy, było zwiększenie mocy naszego serwera, co tylko sprawiło, że przeszedł z 56 000 do 70 000 punktów i pozostał na pierwszym miejscu.
Chociaż nie chcieliśmy ingerować w sposób, w jaki ludzie wchodzą w interakcję z wyzwaniem, te próby spowolniły serwer do tego stopnia, że wyzwanie było trudne dla innych użytkowników, więc postanowiliśmy naprawić lukę.
Poprawka uniemożliwiła innym osobom osiągnięcie tego samego wyniku podczas ostatniego dnia wyzwania kodowania JavaScript. W związku z tym postanowiliśmy rozszerzyć liczbę nagród przyznawanych najlepszym zawodnikom. Pierwotnie główna nagroda — para AirPods — miała trafić tylko do jednego najlepszego zawodnika. Ostatecznie AirPods trafiły do osób zajmujących pierwsze sześć miejsc.
Skromne początki i okrutne końce: niektóre statystyki wyzwań związanych z kodowaniem JavaScript
Nawet nasi najlepsi strzelcy mieli trudności z rozpoczęciem gry. W rzeczywistości ze wszystkich osób, które podjęły pięć lub więcej prób, najwyższy wynik za pierwszą próbę każdej osoby wynosił 645, a mediana wyniku za pierwsze próby w tej grupie wynosiła zaledwie 25 punktów.
Pod koniec konkursu można było odgadnąć strategię, która została podjęta z całkowitej liczby prób. Podczas gdy niektórzy byli bardziej wydajni niż inni, najlepszy zawodnik daleko i daleko miał największą liczbę prób.
Posuwając się do przodu
Co przyniesie przyszłość?
To była dopiero pierwsza iteracja wyzwania kodowania JS. Chcemy w przyszłości wykonać o wiele więcej wyzwań związanych z programowaniem JavaScript, wyciągnąć wnioski z poprzednich uruchomień i uczynić je jeszcze bardziej ekscytującymi. Pierwszą rzeczą, którą chcemy zrobić, to zaimplementować ograniczanie prób, aby ograniczyć liczbę zgłoszeń. Chcemy również wyjść poza wyzwania związane z kodowaniem w JavaScript, udostępniając je w szerokiej gamie języków programowania.
Wreszcie, podczas gdy podejmujemy działania, aby wyzwanie związane z kodowaniem JavaScript było bezpieczniejsze, planujemy nadal zezwalać na używanie botów i innych kreatywnych podejść do przyszłych wyzwań.
Specjalne podziękowania dla Pavla Vydry, Antona Andrijewskiego, Tiago Chilantiego i Matei Copota za ich wkład w wyzwanie i ten artykuł, a także dla @Ziraka za projekt open source, który stanowił podstawę aplikacji konkursowej. Podobnie podziękowania dla wszystkich, którzy uczestniczyli w konkursie i prowadzili go.