
こんにちは。
今年度、新卒で株式会社メルペイに入社し、SREチームの配属になったkeke(Twitter: @_k_e_k_e)です。
本記事では、メルカリの社内サービスであるWi-Fi打刻システムWIAS(Wi-Fi Attendance System)のお話をします。
会社に所属すると出退勤を記録する必要があります。その中で面倒に感じる、忘れてしまう、記録方法が分からない……など色々な問題が出てきます。しかし、Wi-Fiと勤怠システムをうまく組み合わせることによって社員体験を向上させることができた私達のチャレンジを紹介します。
目次
本記事は以下のセクションで構成されています。ご興味のあるセクションだけでもご覧ください。
WIASとは

WIASとは、Wi-Fi打刻システム(Wi-Fi Attendance System)の略称でわいあすと呼んでいます。
その名の通り、社内Wi-Fi接続を通して勤怠の記録ができるサービスです。メルカリでは、勤怠システムにKING OF TIME(以下、KOT)を採用しています。WIASは、登録しているデバイスの社内Wi-Fiへ接続した時間に基づいて、毎日、出勤と退勤をKOTへ記録します。
「社員体験の向上」というゴール
WIASのゴールは社員体験を向上させることです。
私は、社員体験(Employee Experience)の向上をはかることを強い目標にして開発をしていました。
その中でいくつかの技術的なチャレンジがありました。Google Cloud Platform(GCP)のCloud FunctionsやCloud Runを使ったサーバーレスアーキテクチャを採用しています。また、CircleCIによるデプロイメントワークフローであるCIOps(=CIツールによるソフトウェアをデプロイする手法)やチャットによるオペレーションであるChatOpsなど様々な工夫(後に取り上げます)もありますが、どれもゴールではありませんでした。
私がWIASとともに目指したのは、毎日の勤怠の記録からメルカリの社員を解放し、誰もが毎日の業務でValueを発揮することに専念できる環境を整えることです。
これから「社員体験を向上させる」という軸をもって開発した背景とWIASの全体像を紹介していきます。
WIASの開発背景
どのような背景のもとWIASを開発したのかを紹介します。
旧システムの問題
実はWIAS以前にもWi-Fi打刻システムはありました。しかし、いくつかの問題がありました。
- 申請から利用開始までタイムラグがある
- システムの1日の切替時間と勤怠の実態の間で乖離がある
- 労務の方の手動運用が多い
- サービスとユーザーの接点が少なく、サービスに対するフィードバックしにくい
1. 申請から利用開始までのタイムラグ
旧システムでWi-Fi打刻機能を利用するにはGoogleフォームで利用申請をする必要があり、なおかつ申請が反映されるタイミングは月に1回しかなかったため、利用開始までにタイムラグがありました。
この問題は、新卒の同期などからもっとも強く改善を希望されていました。勤怠記録は入社して初日から毎日つけなくてはならない一方で、Wi-Fi打刻をしたい場合、タイミングが悪いと利用開始までに約1ヶ月半もかかってしまうこともありました。(例: 4月21日に申請、6月3日に有効化など)
もちろん、旧システムのアプリケーションに制約があったからではなく利用開始までのフローに問題がありました。

旧フローでは、上の図のように労務の方が申請者ごとの情報をGoogle Sheetsに追加していました。しかし、申請があるたびに逐一対応する運用は難しいため「毎月ごとに締め切りを設けて、まとめて対応する」という運用になってました。
毎月コンスタントにWi-Fi打刻の利用開始の申請があり、運用コストを考えると、やむを得ない状況でした。
2. 1日の切替時間
Wi-Fiで打刻をつけるには「1日を定義する」という作業が必要不可欠です。
Wi-Fiの接続ログから毎日の出退勤を記録しています。接続ログにはUPとDNがあり、社内Wi-Fiへ接続されたときと、接続が切れたときを表しています。しかし、この情報しかないからこそ、オフィスをただ離れて戻ったのか、退勤をして時間が経って出勤をしたのか、実際の状態を知ることは不可能です。
そのため「1日を定義する」という作業が必要です。これは言い換えると、「XX:XXからYY:YYの間の最初の接続を出勤、最後の接続を退勤にする」というルールのことです。
このルールが旧システムは00:01 ~ 23:59でしたが、勤務形態上必ずしもこの範囲内に出退勤が収まるとは限らず、例えば24時以降に残っていた人は正しく打刻されないという問題がありました。
3. 労務の方の手動運用が大きい
「申請から利用開始までのタイムラグ」のセクションでも紹介した利用開始のフローに限らず、労務チームが直接的に協力しなければならないことがありました。
例えば、Google Sheetsに毎月、社員情報を入力する作業を繰り返したり、利用開始ではなく利用停止や不具合のときの場合にSlackチャンネルで対応したり、開発者だけで完結するものではありませんでした。
不具合のときは、バケツリレーのような以下のようなやり取りがありました。
- ユーザー「打刻されてないのですが、なんでですか?どうしたらいいですか?」
- 労務「ちょっと確認しますね」
- (労務がメンテナにコンタクトをする)
- 労務「なにか今日は挙動がおかしいようです…確認してもらえますか」
- メンテナ「ちょっと様子を見ますね…今日は各々で打刻をお願いしますとお伝え下さい」
- 労務「わかりました」
- (労務が問い合わせチャンネルへ投稿する)
- 労務「今日はWeb UIで打刻をお願いします」
- ユーザー「わかりました」
労務の人を挟むことによって労務の方の労力が必要となる上に、ユーザーを待たされる時間も増え、運用の面では負担がありました。
このような問題があり、WIASを開発することによって改善しようと試みました。
アーキテクチャの概要と基本的な流れ
WIASでは、GCPのCloud Fuctions、Cloud Runのサーバーレスなサービスを使用し、データベースにはCloud SQL、KOTへのプロキシにはCompute Engineを使用しています。プロキシサーバーを使っている理由は、KOTではKOT Web APIを使うにあたりIP制限があるからです。Cloud Functionsなどのサーバーレスなサービスは実行環境によってIPアドレスが変わってしまうため導入しています。

この図ではCloud Runは登場していませんが、Cloud Functionsと合わせて、後に紹介するSlackコマンドのバックエンドとして使われています。
1. Google Formsを受け付ける部分

Google Formsから提出された情報をCloud Pub/SubとCloud Functionsで処理をしています。
2. Wi-Fiログを受け取り、出退勤を付ける部分

Wi-Fiログを受け取り、Cloud Functionsで接続ログを処理して、1日の最初の接続を「出勤」として打刻をしています。
また、Cloud Schedulerを使って退勤を午前4時頃に打刻しています。
アーキテクチャの選定理由
ここで、このようなアーキテクチャを選定した理由を紹介しようと思います。
Cloud FunctionsやCloud Runを採用している理由
WIASのユースケースとサーバーレスの長所がマッチしており、サーバレスの欠点が問題にならないので採用しています。
私が最初に開発を着手するときに、すぐサーバーレスに運用しようと思いました。それは
- Wi-Fiへの接続時や利用申請時など、リクエストはイベントベースであり、常にサーバーが稼働する必要がないこと
- Wi-Fiログを処理、Google Formsの処理、Slackコマンドの処理など、要求が異なるものが多くあり、システムとしての境界が明確だったこと
- 最近のKnativeやCloud Runの盛り上がりを感じ、実際にサーバーレスでシステムを動かしたいという関心があったこと
- Wi-Fiログや登録申請に対してはすぐにレスポンスを返す必要がないため、Cloud Pub/Subと合わせて非同期処理をしても問題がないこと
などいくつか理由があります。特に最初の理由は顕著です。夜中や土日のリクエスト数は著しく少ないため、サーバーを常に稼働するのはリソースがもったいないです。

上の図のようにリクエストが実際の出退勤のイベントに応じて発生するため、サーバーレスなシステムにしました。
コストも非常に安く、200万回のリクエスト数で数百円程度です。現在、メルカリの社員数は約1800人で、仮に月に社員が100台登録しても(呼び出し回数だけの単純計算だけでは)数百円に抑えることができます。
例えばCompute Enginedでサーバーを運用していた場合、n1-standard-1
という標準的なマシンタイプでも月額4000円程度はかかります。
今回のユースケースでは、サーバーレスのCold Start(インスタンスが作成されていない状態ではレスポンスタイムが著しく悪くなること)が問題にならないため、採用を決意しました。
多くの要素を抱えるからこそのCIOps

CircleCIによって各Cloud Functionsをデプロイしています。最初は、手元でgcloud
コマンドを使ってデプロイをしていました。しかし開発が進むにつれて、私一人が見渡さないといけないサービスも増えて、管理が難しくなってしまったためCIOpsを採用しました。
デプロイのフィードバック
CIOpsはCIツールがデプロイするので、どのバージョンがデプロイされたのかをユーザーが知るには、フィードバックを用意する必要があります。
Cloud FunctionsやCloud Runは、異なるバージョンによるトラフィックスプリッティングなどがGoogle App Engineのように現時点(2019/08/30)では提供されていません。そのため、ロールバックが容易ではなく、一つのデプロイが致命的になる可能性があります。
慎重に、かつデプロイの状態を可視化するためにSlackチャンネルへフィードしています。

WIASのサービスとしての特徴
WIASは、「毎日の出退勤がWi-Fiによって記録できる」という点では旧システムと変わりませんが、旧システムの問題をいくつも解決しています。
WIASがどのようにそれらの問題を解決していったのか、特徴と織り交ぜて解説します。
1. 翌日から利用開始できるフロー
最悪の場合、利用開始するには今まで1ヶ月半もかかっていました。しかし、今まで労務の方が行っていた作業を自動化することにより実現することができました。
自動化の流れとしては
- 申請を受け付けて、社員情報を集めるフロー
- 社員情報をもとにWi-Fi打刻を有効にするフロー
の2つに分かれています。申請を受け付ける部分は以下のようになっています。

- Google FormsがサブミットされるとGASが実行され、Cloud Pub/Subへ入力された社員番号、MACアドレスとGASのAPIで取得できる提出者のメールアドレスをCloud Pub/Subへストリームし、Cloud Functionsが処理される
- メールアドレスからSlack APIを使ってSlack IDを取得する
Slack IDを取得している理由は2つあります。提出者が実際にSlackのワークスペースに存在しているユーザーであることを確かめるためと、申請者にSlack APIを通してWIASからメッセージを送信するための2つです。

申請受付をしただけでは、打刻はされません。毎日0時にCloud Schedulerをトリガーにバッチ処理を回しています。この処理の中で、KOTへの打刻に使うKOT Employee KeyをKOT Web APIから取得をし、利用開始をしています。

ここでは、Google Formで申請したときに使ったメールアドレスがKOT上のメールアドレスと一致をしているかを確認しています。
基本的には1回のバッチ処理で利用開始ができます。しかし、KOTの登録されているメールアドレスが異動などを機に変更されていたり、社員番号を間違えて申請していたりすると、バッチ処理の中でエラーとなります。そして、翌日以降のバッチ処理で利用開始されるのを待つことになります。
一般的に、いつ有効になったか、または有効になるのかを知れないことはサービスのユーザー体験としては良いものではないです。
そのため、先程の申請受付のメッセージでは、申請が有効になったときに通知することを明記しています。
そして、実際に有効化されると以下のようなメッセージを送って、ユーザーに伝えています。

2. 1日の切替時間を変更
1日の区切りは、これまでは24時でした。つまり、24時以降まで残って作業をすると、後日にKOT上で修正する必要がありました。これはユーザーへ手間を掛けてしまう一方で、システム的にも1日の区切りを定義しなければいけない問題を両立しなければなりません。終電が24時半まであるので、社員が24時以降に残る可能性は高いです。
そのようなことを踏まえて、実験的に朝の5時から次の日の朝4時までを1日の区切りとしています。この設定値は環境変数によって指定をしているため、簡単に1日の定義を変えられます。
今後は、社員の出勤状況を分析して、改めて調整をしてみようと思っています。

多くの社員の働き方に合うと思い変更していますが、まだ実験的な試みです。
3. 労務の負担を(ほぼ)完全に取り除いた
労務の負担を減らすために、労務の介入が必要なくなるフローを考えていました。どうしても人間による介入がフローの中に存在するとボトルネックになりがちです。
例えば、Wi-Fi打刻システムを利用停止したいときなどは、以前までは労務の方へお願いする形で削除していました。
しかし、マニュアルオペレーションでした。労務の方が対応できないときは停止するまでに時間がかかったり、忘れられたりしていました。このフローは自動化できると思い、以下のようなフォームを作りました。

バックエンドは以下のようになっています。利用開始の申請とほぼ同様のフローで、人を介さずに利用停止ができます。

実際に削除されたか、こちらもSlack通知が来るようになっています。ユーザーは、安心感を持って正しくデバイスが削除されたことを知ることができます。

サービス開発では、煌めいて見えるコア機能ばかりに時間を割きがちです。しかし、削除を含めて泥臭いですが必要不可欠な機能も提供することで、一連のサービス体験の中でボトルネックがないように心がけています。
4. 働き方に合わせたSlackからの出退勤打刻

WIASは、Wi-Fiが登録されたデバイスを検知して出退勤を記録する以外にも、Slackによる打刻ができます。
営業先や、急に対応しないといけない業務が発生したときなど、色んなケースで使われているという社員の声を聞いています。
コマンドは以下の2つがあります。
# 出勤の記録 /wias hello # 退勤の記録 /wias bye
私はGitのコミットメッセージに絵文字プレフィックスをつけているため、WIASでも少し遊び心を入れて季節の絵文字を入れることにしました。
こちらは同一日ではありませんが、私の個人チャンネルで出退勤を記録している様子です。


出退勤の様子をチャンネル上で可視化をしている理由は、オンコールやWFH(=Work From Home)などで直接出勤状況が確認できない状況でも、このWIAS Ambassadorとのやり取りをみて他の人へ伝達をできるためです。
運用の話
これまでは、WIASのサービスが社員へ提供できる特徴を説明してきました。しかし、障害についてのお問い合わせや質問対応など、いわゆる「運用業務」が日々あります。それらをどのように対処しているかをお話します。
1. QAチャンネルを設けて、不具合や疑問を聞ける場所を用意
WIASからユーザーへのダイレクトメッセージには、最後に「気になる点や質問がございましたら気軽に#pj-wias-qaチャンネルへお願いいたします。」と書いています。
やはり、社員が非常に多い会社なのでいろんな声があって当然です。それらを私自身がすべての問題を認知できるとは思っていません。そのため、気軽に聞けるQAチャンネルを用意することによって不具合の報告をはじめ、機能の提案をなどをしてもらっています。
また、普段は業務をしているので、運用に注意しててもお問い合わせを見逃したり、すぐに対応できずに放置をしてしまう可能性があります。SlackスタンプのDoneスタンプが押されていないお問い合わせは、Botが毎日8時にチェックしてくれ、その状況を運営チャンネルへ報告をしてくれます。

ユーザーは、お問い合わせなどが対応してもえないと、サービスに対して不信感をいだきます。未対応で放置してしまわないためにも、このようにBotでQAチェンネルを監視しています。日中は業務をやりつつも、ユーザーのサービス体験を損なわないように工夫しています。
2. 主な運用オペレーションはSlack上で対応できる
「働き方に合わせたSlackからの出退勤打刻」のセクションでは、出退勤をSlack上で使って出退勤を記録する方法を紹介しました。
このコマンド以外にも、社員の皆さんも知らない、運営者のためだけの管理者コマンドがあります。当然、運営メンバーしか実行することはできません。
開発者の私以外にも運営をしてくれているメンバーがこのコマンドによって、Slack上で迅速に社員のお問い合わせに対応できます。
今までは、利用停止の対応などはデータベースにある情報を書き換えないといけないため、MySQLサーバへ接続し、SQL文を実行する必要がありました。
しかし、以下のようなコマンドを用意することで、デバイスのMACアドレスを指定してSlack上から削除することができます。ユーザーが削除フォームの存在を知らなくて、直接問い合わせがあったときに即座に対応できます。

社員のメールアドレスがSlackのプロフィールに書かれているので、登録されているデバイスが分からないときも、メールアドレスを元に検索をかけることができます。「どのデバイスが打刻用に登録されていますか」や「申請したのですが、どのような状況ですか」などといったようなよくあるお問い合わせも、その場でSlackコマンドを叩くだけで対応することができます。

3. 簡単にユーザーに通知できる仕組み
何かしらの理由でシステムに不具合が生じているときは、WIAS側から登録しているユーザーへ直接、連絡したいときがあります。
デバイス登録時点でSlackIDを取得していたのも、ユーザーとの接点を持つためです。WIASからユーザー全員に通知ができる「ブロードキャスト」の仕組みを設けています。
こちらもGoogle FormsでGASを使ってCloud Functionsを起動しています。そして、Slack APIを通してユーザーに通知しています。

ユーザーには、以下のように通知されます。

4. イベントごとに逐一Slackでフィードされる
WIASのためのフィードチャンネルがあります。そのチャンネルでは、以下のようなイベントがフィードされます。
- 退勤を記録するバッチ処理の様子
- 登録申請のあったデバイスの登録バッチ
- 登録申請
- デバイスの削除申請
- …
あらゆるイベントがフィードされるため、このSlackチャンネルを眺めるだけで、おおよそのシステムの様子を知ることができました。


例えば、デバイスを重複して登録しようとすると、エラーがフィードされます。この日の場合だと、5つの登録申請があって4つが登録完了できたことがすぐに分かります。

最後に
社内で実際に使われるサービスを、新卒研修を受けながらも空いた時間に、フルスクラッチで、仕様からインフラ設計、QA対応フロー設計まで開発できて、非常に勉強になりました。作ることだけにフォーカスをすると非常に簡単です。しかし、サービスを運用をして、会社のフェーズや文化と足並みを揃えることの難しさを経験することは、会社に属してはじめてできるものでした。
社員の声を聞くだけで開発者としては嬉しい反面、不具合やサービスへの不満があるときは自分の未熟さを知るときでもあります。
障害を起こしたり、開発が難航するたびに旧システムが支えてきた重みを感じました。
旧システムを開発・メンテナンスをして、今回のWIASにも多大なアドバイスをくださった方、新卒の挑戦に相談に乗ってくれた労務の方、応援してくれた同期の皆さん、不具合があっても期待してくださった方、この記事の校正に携わってくださった多くの皆さんへ感謝を述べたいと思います。本当にありがとうございました。
このような社員の生産性や労働体験向上のためのシステム開発にも興味のある方は、ぜひ一度弊社を検討してみてはいかがでしょうか。
https://careers.mercari.com/search-jobs?dep=engineering
この記事について疑問や感想がありましたら、気軽にTwitterなどでご連絡ください。
最後までありがとうございました!
