スピードの必要性:トップタルJavaScriptコーディングチャレンジの回顧展

公開: 2022-03-11

Toptalは、会議ブースに人々を引き付ける方法として、JavaScriptコーディングチャレンジWebアプリを開始しました。 それがどれほど成功したかを見て、私たちはWebでパイロットを作成し、コミュニティとそのネットワークのすべての人に公開することにしました。

ToptalのJavaScriptSpeedCoding Challengeのスクリーンショットで、最初の質問の1つを示しています。 「double」という名前の関数が提供され、その本体は「returnxdoubled」というコメントで構成されています。

立ち上げ時に、私たちは意欲的な開発者に、JavaScriptコーディングの課題全体で高得点を獲得するための創造的な方法を考え出すように促しました。 優れた独立したフリーランサーの重要な成功要因のいくつかは、既成概念にとらわれずに考え、一連の制約の中で働くための創造的な方法を見つける能力です。

JavaScriptコーディングの課題に関する質問

ガントレットは、非常に基本的なJavaScriptチャレンジの質問から、面接の質問として使用される可能性のあるものに似た、多数のJavaScriptの質問で構成されていました。

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

…より中間的なものへ:

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

私たちは、初心者と上級開発者の両方に楽しんでもらい、友人や同僚を招待して最高のスコアを競うことを望んでいました。 質問はポイントごとに並べ替えられているため、後輩の開発者でもポイントを獲得できます。 同じポイント数の質問の順序はランダムであったため、経験は毎回わずかに異なりましたが、質問のフルセットは1週間を通して同じままでした。

目標は、3分の制限時間内にできるだけ多くのタスクを完了することでした。 誰かがJavaScriptコーディングチャレンジのすべてのタスクを完了する方法を見つけた場合、残りの1秒ごとに10ポイントが付与されます。 チャレンジを完了するために複数回の試行を許可しました。

私たちが最初に予想したことは、人々がゆっくりとしたペースで質問を解決するのに時間がかかり、次に回答をコピーしてWebアプリケーションに貼り付けることです。

チャレンジを開始してから1時間40分後、最初の人はこのアプローチに従い、アプリケーションで許可されている最大1445ポイントに加えて、残り1秒ごとに獲得した追加ポイントを獲得しました。

JavaScriptコーディングチャレンジのターニングポイント

コピーアンドペーストのアプローチでは、トップの出場者は、答えを自分でコーディングすることに集中することから得られるものは何もありませんでした。 代わりに、彼らは自動化スキルにスピードをもたらすことに注意を向けました。

この時点での最も簡単なアプローチは、ボタンの準備ができるまでループを待っている間に各タスクを解決するJavaScriptを作成し、それをブラウザコンソールにコピーアンドペーストすることでした。

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

この部分は、Seleniumなどの通常の自動化ツールを使用して自動化することもできます。 ただし、より高速な方法は、APIの使用を自動化し、ソリューションをタスクに送信することです。

 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)

注意すべき点の1つは、このバージョンのスピードコーディングチャレンジでは、コードはクライアント側でのみテストされたことです。 このため、コードの代わりにテストケースへの回答を送信することが可能でした。 これにより、最適化が可能になり、クライアント側で数ミリ秒を短縮できました。

3日後、競争は本当に熱くなり始め、3時間のバケットあたりの試行回数は1,000未満から100,000近くに急増しました。

3日間、スコアは同じままでした。 コードのマイクロ最適化を作成している人もいれば、サーバーの混雑が緩和されて数ポイント先を行くことができるようになることを期待して、ソリューションをループで送信している人もいます。 私たちは大きな驚きの予定でした。

JavaScriptコーディングチャレンジの最大スコアを破る

まず、簡単な計算をしてみましょう。すべてのタスクの完了に対して合計1445ポイントが付与され、180秒の時間枠が与えられました。 アプリケーションに残り1秒あたり10ポイントを付与した場合、すべての回答を即座に提出した場合、理論的に達成可能な最大スコアは3245になります。

ユーザーの1人が6000を超えるスコアを達成し、時間の経過とともに着実に成長し続けました。

どうしてこんなに高いスコアを獲得できるのでしょうか?

簡単なレビューの後、何が起こっているのかがわかりました。 私たちのトップの出場者であり、プロのフルスタック開発者であり、15年以上の競技プログラミングの経験を持つToptalerは、コーディングの課題の設定に抜け穴を見つけました。 彼は複数のボットを生成し、サーバーの速度を低下させました。 その間、彼は同じタスク(最も多くのポイントを獲得したタスク)を可能な限り何度も完了し、それらのスコアを1つのエントリに割り当てて、そのエントリのスコアに継続的に追加することができました。

創造的な解決策を許可したので、これは規則に違反していませんでした。 ただし、彼が使用していた特定の方法により、サーバーがかなりビジーになり、他のすべてのユーザーのネットワーク要求が遅くなりました。 私たちが最初にしたことは、サーバーの電力を増やすことでした。これにより、彼は56,000ポイントから70,000ポイントになり、最初の場所に留まりました。

人々がチャレンジを操作する方法に介入したくはありませんでしたが、これらの試みはサーバーの速度を低下させ、他のユーザーがチャレンジを使用するのが困難になったため、抜け穴を修正することにしました。

この修正により、JavaScriptコーディングチャレンジの最終日に他の人が同じスコアを達成することができなくなりました。 このため、上位の出場者に与えられる賞の数を増やすことにしました。 もともと、最優秀賞(AirPodsのペア)は、1人の最優秀選手だけに贈られることになっていた。 結局、AirPodsは最初の6つの場所を保持している人に与えられました。

謙虚な始まりと凶暴な終わり:いくつかのJavaScriptコーディングチャレンジ統計

私たちの最高得点者でさえ、始めるのにいくらかの困難がありました。 実際、5回以上試行したすべての人の中で、最初の試行の最高スコアは645であり、そのグループの最初の試行の中央値はわずか25ポイントでした。

コンテストの終わりまでに、試行の総数から試行されている戦略を推測することができます。 いくつかは他よりも効率的でしたが、遠く離れたトップの競技者は最も高い試行回数を持っていました。

上位20人の出場者の最高スコアと合計試行回数を示す結合された対数棒グラフ。最高の試行回数は最高得点者に行きましたが、2番目、3番目、4番目に高い試行回数は、それぞれ13位、9位、2位の出場者に行きました。 4位と6位の出場者を含むトップ20のちょうど半分は、1,000回未満の試行でした。 2人の競技者は100回未満の試行を行い、もう1人は10回未満の試行を行いました。

前進する

将来はどうなるのでしょうか?

これは、JSコーディングチャレンジの最初の反復でした。 今後、JavaScriptプログラミングの課題をさらに増やし、以前の実行から教訓を学び、さらにエキサイティングなものにしたいと考えています。 最初に実行したいのは、送信の数を制限するための試行スロットリングを実装することです。 また、JavaScriptでのコーディングの課題を超えて、幅広いプログラミング言語で利用できるようにしたいと考えています。

最後に、JavaScriptコーディングの課題をより安全にするための対策を講じている間、ボットやその他の創造的なアプローチを今後の課題に使用できるようにする予定です。


チャレンジとこの記事に貢献してくれたPavelVydra、Anton Andriievskyi、Tiago Chilanti、Matei Copotと、コンテストアプリの基礎となったオープンソースプロジェクトを提供してくれた@Zirakに特に感謝します。 同様に、コンテストに参加し、実行したすべての人に感謝します。