The Need for Speed: A Toptal JavaScript Coding Challenge Retrospektive
Veröffentlicht: 2022-03-11Toptal startete die JavaScript Coding Challenge Web-App, um Menschen zu unseren Konferenzständen zu locken. Als wir sahen, wie erfolgreich es war, beschlossen wir, ein Pilotprojekt im Internet zu erstellen, das für alle in unserer Community und ihren Netzwerken offen ist.
Zum Startzeitpunkt hatten wir motivierte Entwickler ermutigt, sich kreative Wege einfallen zu lassen, um bei der gesamten JavaScript-Codierungsherausforderung hoch abzuschneiden. Einige der wichtigsten Erfolgsfaktoren großartiger unabhängiger Freiberufler sind die Fähigkeit, über den Tellerrand hinaus zu denken und kreative Wege zu finden, um innerhalb einer Reihe von Einschränkungen zu arbeiten.
Fragen zur JavaScript-Codierungsherausforderung
Der Fehdehandschuh bestand aus einer Reihe von JavaScript-Fragen – ähnlich denen, die als Interviewfragen verwendet werden könnten –, die von wirklich einfachen JavaScript-Challenge-Fragen reichten:
box.double = function double (x) { //return x doubled };
…zu den mittleren:
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) };
Wir wollten, dass sowohl Anfänger als auch fortgeschrittene Entwickler Spaß haben und ihre Freunde und Kollegen einladen, mit ihnen um die höchste Punktzahl zu konkurrieren. Die Fragen waren nach Punkten sortiert, sodass auch Junior-Entwickler einige Punkte sammeln konnten. Die Reihenfolge der Fragen mit der gleichen Punktzahl war zufällig, sodass die Erfahrung jedes Mal etwas anders war, während der gesamte Fragensatz die ganze Woche über gleich blieb.
Ziel war es, so viele Aufgaben wie möglich innerhalb des Zeitlimits von drei Minuten zu erledigen. Für den Fall, dass jemand einen Weg gefunden hat, alle Aufgaben der JavaScript-Programmierungsherausforderung zu erfüllen, wurden 10 Punkte für jede verbleibende Sekunde vergeben. Wir haben mehrere Versuche zugelassen, um die Herausforderung abzuschließen.
Das erste, was wir erwartet hatten, war, dass sich die Leute etwas Zeit nehmen würden, um die Fragen in gemächlichem Tempo zu lösen und dann die Antworten zu kopieren und in die Webanwendung einzufügen.
Nach einer Stunde und 40 Minuten nach dem Start der Herausforderung folgte die erste Person diesem Ansatz und erhielt die 1445 maximal zulässigen Punkte der Anwendung, plus einige zusätzliche Punkte, die wir für jede verbleibende Sekunde vergeben.
Ein Wendepunkt in der JavaScript-Codierungsherausforderung
Mit dem Copy-and-Paste-Ansatz hatten die Top-Teilnehmer nichts mehr davon, sich darauf zu konzentrieren, die Antworten selbst zu codieren. Stattdessen konzentrierten sie sich darauf, ihre Automatisierungsfähigkeiten zu beschleunigen.
Der einfachste Ansatz an dieser Stelle bestand darin, JavaScript zu schreiben, das jede Aufgabe löst, während auf eine Schleife gewartet wird, bis die Schaltflächen fertig sind, und es in die Browserkonsole zu kopieren und einzufügen:
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()
Dieser Teil könnte auch mit regulären Automatisierungstools wie Selenium automatisiert werden. Ein schnellerer Weg wäre jedoch, die Verwendung der API zu automatisieren und die Lösungen an die Aufgaben zu senden:
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)
Zu beachten ist, dass der Code in dieser Version der Speedcoding-Herausforderung nur auf der Clientseite getestet wurde. Dadurch war es möglich, anstelle des Codes einfach die Antworten auf die Testfälle zu versenden. Dies ermöglichte eine Optimierung und einige Millisekunden auf der Client-Seite einzusparen.

Drei Tage lang blieben die Werte gleich. Einige Leute schrieben Mikrooptimierungen für ihren Code, und mehrere Leute reichten ihre Lösungen in einer Schleife ein, in der Hoffnung, dass der Server weniger überfüllt sein würde, damit sie ein paar Punkte Vorsprung erzielen könnten. Uns erwartete eine große Überraschung.
Das Brechen der Höchstpunktzahl der JavaScript Coding Challenge
Rechnen wir zunächst einmal kurz nach: Wir hatten insgesamt 1445 Punkte für die Erledigung aller Aufgaben und eine Zeitvorgabe von 180 Sekunden. Wenn wir 10 Punkte pro verbleibender Sekunde in der Bewerbung vergeben, wäre die theoretisch maximal erreichbare Punktzahl 3245 – im Falle einer sofortigen Abgabe aller Antworten.
Einer unserer Benutzer hat eine Punktzahl von über 6000 erreicht, die im Laufe der Zeit stetig gewachsen ist.
Wie konnte jemand eine so hohe Punktzahl erreichen?
Nach einer kurzen Überprüfung fanden wir heraus, was passiert war. Unser Top-Teilnehmer, ein professioneller Full-Stack-Entwickler und Toptaler mit mehr als 15 Jahren Erfahrung in der Programmierung von Wettkämpfen, fand eine Lücke im Setup der Coding-Challenge. Er hat mehrere Bots gespawnt, die den Server verlangsamt haben; In der Zwischenzeit könnte er dieselbe Aufgabe (diejenige, die die meisten Punkte vergeben hat) so oft wie möglich erledigen und ihre Punktzahlen einem einzelnen Eintrag zuordnen, wodurch die Punktzahl dieses Eintrags kontinuierlich erhöht wird.
Dies verstieß nicht gegen die Regeln, da wir kreative Lösungen zuließen; Die spezielle Methode, die er verwendete, führte jedoch dazu, dass der Server erheblich ausgelastet war, wodurch Netzwerkanforderungen für alle anderen langsamer wurden. Das erste, was wir taten, war, unsere Serverleistung zu erhöhen, was ihn nur von 56.000 auf 70.000 Punkte brachte und auf dem ersten Platz blieb.
Obwohl wir nicht in die Art und Weise eingreifen wollten, wie die Leute mit der Herausforderung interagieren, verlangsamten diese Versuche den Server so sehr, dass die Herausforderung für andere Benutzer schwer zu verwenden war, also entschieden wir uns, die Lücke zu schließen.
Der Fix machte es anderen Leuten unmöglich, am letzten Tag der JavaScript-Programmierherausforderung dieselbe Punktzahl zu erreichen. Aus diesem Grund haben wir uns entschieden, die Anzahl der Auszeichnungen für die besten Teilnehmer zu erhöhen. Ursprünglich sollte der Hauptpreis – ein Paar AirPods – nur an den einen Top-Kandidaten gehen. Am Ende wurden die AirPods an die ersten sechs Plätze vergeben.
Bescheidene Anfänge und wilde Enden: Einige Statistiken zu JavaScript-Codierungsherausforderungen
Selbst unsere besten Torschützen hatten einige Startschwierigkeiten. Tatsächlich betrug bei allen Personen, die fünf oder mehr Versuche unternommen hatten, die Höchstpunktzahl für den ersten Versuch 645, und die mittlere Punktzahl für die ersten Versuche in dieser Gruppe betrug nur 25 Punkte.
Am Ende des Wettbewerbs konnte man anhand der Gesamtzahl der Versuche erraten, welche Strategie versucht wurde. Während einige effizienter waren als andere, hatte der mit Abstand beste Kandidat die höchste Anzahl an Versuchen.
Vorwärts gehen
Was hält die Zukunft bereit?
Dies war nur die erste JS-Coding-Challenge-Iteration. Wir möchten in Zukunft noch viele weitere JavaScript-Programmierherausforderungen meistern, Lehren aus den vorherigen Läufen ziehen und es noch spannender machen. Als Erstes wollen wir eine Versuchsdrosselung implementieren, um die Anzahl der Übermittlungen zu begrenzen. Wir möchten auch über die Codierungsherausforderungen in JavaScript hinausgehen und sie in einer Vielzahl von Programmiersprachen verfügbar machen.
Während wir Maßnahmen ergreifen, um die JavaScript-Codierungsherausforderung sicherer zu machen, planen wir schließlich, die Verwendung von Bots und anderen kreativen Ansätzen für zukünftige Herausforderungen weiterhin zuzulassen.
Besonderer Dank geht an Pavel Vydra, Anton Andriievskyi, Tiago Chilanti und Matei Copot für ihre Beiträge zur Challenge und zu diesem Artikel sowie an @Zirak für das Open-Source-Projekt, das die Grundlage der Wettbewerbs-App bildete. Ebenso vielen Dank an alle, die an dem Wettbewerb teilgenommen und ihn durchgeführt haben.