30周年記念グッズ配布システムを作ってみたけど、失敗と学びの連続だった
2023年07月31日 月曜日
CONTENTS
IIJ は2022年12月3日に30周年を迎えました。
この時、広報部が主導して約2,000人の社員に数種類のグッズを配布することになったのですが、社員それぞれに対しどのグッズを何個渡すかで複数のパターンがあるため、配布の効率化が課題となりました。
この課題を解決した時のことを、ITmedia さんが記事にしてくださっています。
今回は、その中でも技術的な側面にフォーカスし、どのように解決したかを書いていこうと思います。
プロトタイプができるまで
発端
IIJ には、技術工作室という課題を持つ人と技術者をマッチングさせる取り組みがあります。
新たな技術を試しながらプロダクトを作りたい技術者と、社内の業務課題をマッチングする取り組みです。課題解決のアイデアを練るだけでなく、協力する従業員とともに実践のなかで試行錯誤を繰り返して技術力を向上し、より価値のあるプロダクトやサービスの開発につなげていくことを目指しています。
人材の育成
ここに広報の方から「30周年記念グッズを配布する上で、もう少し配布をスムーズにしたい」という要望が書き込まれました。
2022年の11月10日、11時ごろの出来事です。
案出し
Microsoft Forms で事前に調査を行い、各社員がどのグッズを何個欲しいかは集計済みだったようです。
そのうえで、5日間という短い期間でいかに効率よく配布を行うかを議論していました。
最初はカードリーダーを利用して社員証を読み取る案が提案されたのだったと記憶しています。
出席確認システムなどを流用することはできないか、という話になったのですが、「社員証による照合では代理受け取りができない」という点で却下されました。
次いで発案されたのが 二次元コード案です。
配布物情報も 二次元コードの情報として含めてしまえば良いのではないか、ということになりました。
二次元コードであれば、カメラを搭載している端末なら簡単に動作させられるため、配布レーンをスケールさせることが可能である点も評価されました。
そこで議論になったのが、「二次元コードをどのように配布するか」という点です。
最初は PowerAutomate で配布する案などが出ていたと思います。
参画、プロトタイプ完成
このあたりから私がプロジェクトに参画しました。
IKE(社内 Kubernetes サービス)に実装されているアカウント認証機能を利用すれば、集計済みデータからアクセスしたユーザの必要グッズ情報を抽出するのは難しくない、と発言します。
二次元コード自体も事前に社員数分作成しておかずとも、JavaScript に任せて各自のクライアント側で表示時に生成することで問題なさそうであることを、すぐに確認しました。
結果としてこの案が可決され、15時半に実装に着手、16時半にはログインしてきたユーザ名で二次元コードが生成され、表示されるページのプロトタイプが完成しました。
IKE の便利さには目を見張るものがあります。
この時はまだ、Webrick を使って渡ってきた情報を足元にある CSV と突き合わせて表示している簡素なものでした。
あくまでプロトタイプで実現可能性を示して、トンズラしようかなと考えていました。
システム構成
話は一旦、完成後に移ります。
様々な議論の末に私の担当範囲は瞬く間に大きくなり、以下のような要件のシステムを私ともう一人の社員の2名体制で開発することになりました。
- ユーザ用ページ (私担当)
- ログインすると 二次元コードが表示されるページ
- サーバ上に保存された情報から社員と配布物を特定し、情報を出力する
- JavaScript はこの情報を基にして 二次元コードをレンダリングする
- 二次元コードのほかに、受領したかどうかのステータスが表示される
- ログインすると 二次元コードが表示されるページ
- スタッフ用ページ
- 二次元コード読み取り/ 受領画面 (もう一人が担当)
- HTML + JavaScript で開発された、二次元コード読み取り機能を含むページ
- 二次元コードを読み取ると配布するべきグッズ情報が表示される
- 「引き渡し」ボタンをタップするとサーバに POST され、配布ステータスが変更される
- 配布情報一覧ページ(私担当)
- 全ての配布対象者の配布物リスト、および配布ステータスが表示されるページ
- なんらかのエラーに備えて、ステータスは未受領に戻すボタンも配置
- 未受領者の一覧もファイルに出力する機能を備える
- ステータス管理システム(私担当)
- 各ユーザが受け取るべき物品リストと、受領ステータスを管理するシステム
- 全ての対象ユーザの受け取る物品リストを含む DB を持つ
- ユーザ名の POST を受け、そのユーザのステータスを変更する
- 二次元コード読み取り/ 受領画面 (もう一人が担当)
簡単に言うと 二次元コード読み取り部分以外はすべて担当したのですが、各コンポーネントのつくりや工夫、後悔している点など紹介します。
システム全体
どんどん要件が多くなってしまい、ユーザ用ページとスタッフ用ページは入口を変えた方が良いのでは……など多くの課題が見つかったため、Webrick はやめて Rails で書き直しました。
全ての情報が格納された xlsx ファイルを広報部の方から渡していただけるとのことだったのですが、データの成形などで苦労をかけたくなかったため、xlsx ファイルを読み込んで import する機能を実装しました。
が、これは要らぬ努力で、Excel のファイルを頑張って読み取れるようにするよりも、CSV で渡してくださいと素直に言えばよかったと今では思っています。
2000行以上の insert が走るので馬鹿正直に逐次処理すると45秒ほどかかってしまっていましたが、Rails の insert_all を利用したら2秒ぐらいになりました。
この方法だと model のインスタンスが作成されないので、model に実装した validation は効きません、注意しましょう。
例えば二重に insert されると困るので、その辺りは insert_all の unique_by で unique なカラムを指定しました。
アクセスされる URL は Rails の route で constraints を利用し、host ヘッダを見てアクセスできる path を制限しています。
これによって、IKE に2つ作成した ingress がそれぞれ同じバックエンドを参照していても、異なる情報を見せられるよう実装できました。
二次元コード表示ページ
認証を抜けて来たユーザ名を利用して find し、出てきた情報を 二次元コードに変換しているだけのページです。
このライブラリには余白が出力されないため、CSS でわずかに余白を持たせていました。ですが、二次元コード画像を直接ダウンロードしてから持ってきてくださる方が存外多く……
実は 二次元コードはマージン(クワイエットゾーン)がきちんと設定されていないと読み取れないことがあり、ローンチ直後の 二次元コード画像にはそれが含まれていないため、受け取りがうまく行かないことが多かったようです。申し訳ありませんでした。
急いでクワイエットゾーンを付与する PR を取り込んで差し替えました。
また 二次元コードの内容が平文であったという点も反省しています。
誰が読み込んでも内容が表示できるうえに、そこから判明したフォーマットを利用して 二次元コードを偽造することができてしまいます。
読み取り画面と表示ページで共有した PSK を利用して、暗号化を施す修正を実装しましたが時すでに遅し、実装の完了を共有するころにはすでに 二次元コードの表示方法が案内された後でした。
今回は性善説で社員を信じてのローンチとなりましたが、できればつけておきたかった機能です。
配布情報一覧ページ
DataTables を利用して、スタッフ用の配布情報一覧ページをつくりました。
Table を指定して JavaScript で渡すことで、<td> タグ等をパースしてレンダリングしてくれる優れものです。
今回は2000件以上あるということで、そんな Table をパースするのは5, 6秒程度時間がかかってしまう問題がありました。
そこで、出力済みの Table を渡すのでなく、JavaScript であらかじめ作った大きな配列を渡すことでパースの手間を省き、表示を1秒以下にできました。
その他
技術工作室のプロジェクトを進めていく上での誤算や失敗ポイント、良かったところなどを今後に活かせるよう書いておきます。
要件が膨らんでいく
技術工作室のプロジェクトは、「こんなのがあったらどうだろう」という軽い気持ちで提案できるのが良いところだと思います。
一方で、これは「作り始めてから要件が固まっていく」ということに他なりません。
ハードルの低さとトレードオフになると思いますが、同じような取り組みをする際はフォーマットを作るなどして、頭の中にあるイメージの言語化を簡単にできるような取り組みがあれば、技術者も提案者もお互いに幸せになれたのかも、と思います。
私も軽い気持ちでプロトタイプを作った時、まさかここまでの範囲を一人で作ることになるとは思っていませんでした。
事前のすり合わせは大切
あるあるだと思いますが、「こんなフォーマットのファイルを渡します」と伝えられ、それを基に実装したところ本番前に認識とちがうファイルがやってきた、というトラブルがありました。
そもそも Excel のファイルをパースするのが間違いかもと思いつつ、自分の知らない列がいつの間にか追加されていてパース時に壊れた時はちょっと泣きました。
セルに「こんな値が入るだろう」という自分の中の認識を軽く超える情報が格納されていた時も、壊れてちょっと泣きました。
整数かと思いきや文字列なんていうのはザラで、数字が入ると思っていたら「個別対応」みたいな文字列が入っていたりするなど、自分がいかにバイアスを含んだ目でデータを見ているか気づくきっかけになりました。
こういったトラブルも、事前にもっとすり合わせておけば回避できたなあ、と反省しています。
可用性の担保
可用性をどうやって確保すべきか、これは最後まで課題だったのですが時間が無く、そのまま IKE にプロダクトを載せてローンチしてしまいました。
懸念していた通り IKE 側にトラブルが発生し、2日目の受け渡しは途中から急遽手渡しに切り替えての対応となりました。
広報の方からは「手渡しと比較することでどれだけ効率化できたか、再認識できた」と嬉しい言葉をいただきましたが、起きると思った事故は起こるものです。懸念があった時点で相談等を行い有識者のアドバイスなどを募るべきだったと反省しています。
まとめ
技術者として貴重な体験をさせていただきました。学びが多くとても楽しかったです。
また何かの機会で、こういった活動をしていきたいと思います。