なまえは まだ ない

思いついたことをアウトプットします

サーバーサイドのオンボーディング課題と俺たちの果てなき戦いは続く

この記事はフラー株式会社 Advent Calendar 2023の11日目の記事です。10日目は @kanterburyさんで「AIに相談しながら正規表現を考えると捗るという話」でした。

オンボーディング課題とは

フラーのサーバーサイドでは、多くのプロダクトのサーバーサイド開発にGo言語、データベースにMySQL、そしてWebアプリケーションフレームワークにGoaを使っています。 入社してくるメンバーのレベル感や経験が様々であること、GoaがGoのフレームワークとしては少しマイナーであることなどを踏まえ、フラーでは主に中途社員向けにオンボーディング課題というものを用意しています。この辺りは過去の記事でも触れているので、ぜひそちらもご覧ください。

furusax0621.hatenablog.com

さてこの課題ですが、はじめに作成してから今日に至るまでいくつかのアップデートを重ねています。 作って終わりでなくアップデートもしてることを誰か褒めてほしい

ふと思い立ったので、これまでのアップデートの歴史を振り返ってみます。

課題の大まかな構成

課題はざっくりと三部構成になっていて、それぞれ次のようなスキル習得を目的としています

  • 第一部ではGoでHTTP(Web API)サーバーを構成する方法、 net/http パッケージや encoding/json パッケージの基本的な使い方を学ぶ
  • 第二部ではMySQLを使ったデータ管理、GoでMySQLと連携する方法を学ぶ
  • 第三部ではGoaを利用したWeb APIの記述方法を学ぶ

それぞれの課題ではWeb APIの仕様といくつかの制限事項を設けており、実装したらトレーナーにコードレビューをしてもらうという流れです。

また、進捗スピードやスキルレベルに合わせて追加で取り組めるチャレンジ課題を各課題に設けています。アプリケーションへの要件追加だったりテストの記述などが含まれます。

課題の誕生〜黎明期

以前の記事でも軽く触れていますが、この課題を作った元々のモチベーションは「Go言語未経験のメンバーが、スムーズに業務に入るために必要な最低限のスキルを培う」という点でした。私が入社して1年後(ちょうど世間で新型コロナウィルスが騒がれ始めた頃)にサーバーサイドエンジニアが立て続けに増えたこともあり、急拵えで課題を作りました。

当時は次のような四部構成でした。

課題①:簡単なWeb APIサーバーを作る

次のようなとてもシンプルなユーザー情報を扱うWeb APIサーバーを作れ、というものでした。

{
  "id": 1,
  "first_name": "Taro",
  "last_name": "Hatena",
  "age": 18
}

/users または /users/:id というエンドポイントに対して、いわゆるCRUDを実現するWeb APIを実装せよ、というものです。 なお、データストアは用意せず、固定のレスポンスを返すモックサーバーとして実装させます。メインはHTTPサーバーやJSONの扱い、隠し味にREST APIの要素も取り入れよう、という具合です。

延長課題にはインメモリ(map や slice)によるデータ永続化を設定しました。 Goroutine Safeな実装が必須になるため、初学者には適度なハードルになったと思います。

課題②:NoPasteを作る

NoPasteとはテキストやスニペットを共有するサービスのことで、共有したいテキストを入力すると専用のURLが発行され、第三者がそのURLを介してテキストを参照できるというものです。GopherにはGo Playgroundの共有機能といえば伝わるアレですね。

仕様としては以下2つのエンドポイントを作ることになります

  • 任意のテキストを入力すると専用URLを返却するエンドポイント
  • URLに対応したテキストを返却するエンドポイント

ここではデータストアをローカルストレージとして、特定ディレクトリにファイルとして保存しましょうというものでした。 共有するテキストをそれぞれひとつのテキストファイルとして表現し、検索のキーとなるテキストのハッシュ値をファイル名にして保存することを想定しています。

このあとMySQLと連携させるための布石として作った課題でしたが、解く側の反応がイマイチで後に使われなくなっていきました。

課題③:NoPasteのバックエンドをMySQLにする

課題②で作ったNoPasteのデータ保存先をMySQLにする、というものです。

NoPasteは同じ内容のテキストを同時にリクエストすると競合が発生してしまうシステムです。 MySQLで競合の検出と、それを検出したときにどう対処するかという部分を学んでもらうことを想定していました。

課題④:NoPasteをGoaで作る

③から地続きに、今度はGoaを使ってエンドポイントを実装してみよう、というものです。

課題に穴が見え始めてきた

上記のようなメニューの課題を1年ほど回してみましたが、いろんなメンバーが課題に挑戦する中で、いくつか困ったポイントも出てきました。

インメモリのデータ保存実装のハードルが高い

課題①でチャレンジ課題とした「インメモリでのデータ保存」ですが、意外と多くのメンバーが初手で実装しようとしていました。

これは課題の仕様の書き方が悪かったのだと思いますが、「リクエストされたデータを保存しなくて良い」と明記してなかったんですよね。 僕だったら「保存しろって明記されてないんだから保存しなくていいでしょ」って考えちゃうんですが、他のメンバーは僕よりずっと素直な人間だったようです。

先にも書いたとおりGoroutine Safeな実装は初学者にはそれなりにハードルが高く、この部分の実装を詰めるのに結構な時間を要しました。 本来この課題で習得してほしい部分はここではなかったので、うーんそこに時間かけてほしくないんだよな……というところです。

Webアプリケーションフレームワークを使われる

EchoやGinを使って課題を解くメンバーが現れてきました。

元々net/httpパッケージの使い方を学んでもらい、そこからGoaにつなげることでフレームワークの役割・良し悪しを実感してもらうストーリーを想定していました。 なのでいきなりフレームワークを利用されるのは想定外というか、、都合が悪かった。

データ競合を力技で突破される

NoPasteはデータ登録時にテキスト(およびハッシュ)が衝突する可能性があります。 これを適切に対処してもらうのが課題の醍醐味だったのですが、、 INSERT ~ DUPLICATE KEY UPDATE 構文(いわゆるUPSERT)で乗り切るメンバーが現れました。

まぁたしかに、NoPasteのデータ構造的にハッシュと本文だけ保持していれば良く、ハッシュ衝突時に本文の値が一致していることは保証できているため、UPSERTをしても何ら問題はありません。 これは正直、学んでほしいテーマに対して教材が良くなかったと思っています。

ここまでのまとめ

長くなったので一旦区切ろうと思います。

そんなわけでサーバーサイドの新入社員に宛てた課題というものを作って運用していましたが、月日の経過と共に見直したいポイントも色々出てきました。次回はその改善点についてお話ししようと思います。


明日も再び私で、「サーバーサイドのオンボーディング課題と俺達の果てなき戦いは続く その②」です。お楽しみに。