TECH PLAY

株式会社メルカリ

株式会社メルカリ の技術ブログ

261

Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 BigQueryのコンピューティングリソース管理の取り組み 」の書き起こしです。 @gouki:「BigQueryのコンピューティングリソース管理の取り組み」にと題して発表します。 株式会社メルペイ ソフトエンジニアのGo Kojimaです。よろしくお願いします。私は2年前にメルペイにジョインし、当初は機械学習システムの基盤開発に携わりました。その後、現在も所属しているデータマネジメントチームに異動し、主に今回発表するBigQueryコンピューティングリソースの最適化を担当しております。 メルカリ・メルペイではデータ管理データ分析の基盤となるデータウェアハウスとして、Google社が提供するBigQueryを利用しております。 本発表ではBigQueryを利用するにあたって必要となるコンピューティングリソース管理の取り組みについてお話しします。内容としてはこちらの通り、初めにBigQueryとその課金モデルについて触れ、管理に当たっての課題をお話した上で、その解決に向けた取り組み内容をご紹介し、最後に今後予定している取り組みについてもご紹介いたします。 BigQueryはGoogleが提供するデータウェアハウスサービスです。サーバーレスアーキテクチャを採用しており、利用者としてもサーバーインスタンスのサイズや台数等を管理する必要のないサービスです。 対象のデータが大量にあったとしても、ほとんどの場合、数秒もしくは数分程度の実行時間で結果を返してくれます。ただし、クエリやデータによっては数時間以上かかるようなケースもあって、この後お話しする課題にも繋がってくるというような問題になります。 参照記事: https://cloud.google.com/bigquery/docs/introduction?hl=ja こちらの表がBigQueryの課金モデルをざっくりと表現したもので、BigQueryは大きくクエリ処理とデータ保存に使われるストレージに対して課金がなされます。ストレージについては保存領域ごとのデータ量で課金される方式となっていますが、本日はこちらの処理側の方を中心にお話ししますので、ストレージ側の詳細は割愛します。 処理側は、オンデマンド型と定額型の二つのモデルにわかれています。オンデマンド型の場合、処理データ量に基づいて課金されるモデルになっています。月単位で1TBまでは無料で使えます。 ただし、オンデマンド型の場合は、一度に利用できるSLOTは2000までという制限があります。SLOTという概念についてはこの後すぐに説明しますが、クエリ処理に使われる仮想CPUの単位をお考えください。この制限があるので、より多くの計算リソースが必要な場合は、定額型を利用することになります。 定額型の場合、特定の期間の間、仮想CPUを利用する権利を予約購入して利用するモデルになっています。 通常月単位や年単位で購入しますが、これらを超えて必要なったときのみ、秒単位で利用仮想CPUを増加させて利用するFlex Slotsという方法もあります。Flex Slotsは通常BigQueryを実行する前後で、Flex Slotsの追加とキャンセルの処理を事前にプログラムとして自動実行するような使い方になります。必要な間だけFlex Slotsによる仮想CPUを追加して使っていくという形になります。 すでにSLOTとはBigQueryがクエリを処理するときの仮想CPUのことであるとご説明しましたが、特に定額型の課金モデルで利用する場合に、運用上非常に重要な要素となります。 一定の期間、一定数のSLOTの利用料金を支払う方式となっていますので、全く使わない場合でも、その分の料金を支払う必要があります。実際にクエリを実行する際にどれほどのSLOTを利用するのかは、利用者側で指定することができず、BigQueryが実行を進めながら判断して余剰SLOTがあれば利用して実行する仕組みになっています。 余剰SLOTが不足しているような場合は、その時点で使える分だけのSLOTを使って実行が継続され、SLOTが十分にある場合と比較して、実行時間が遅くなる形です。 このような仕組みになっているので消費SLOTを利用者側でコントロールすることは完全にはできません。よって、非常に難しいのですが、無駄にならず、かつ、許容できる実行時間でクエリ処理が完了できる程度のSLOT数になるように予約しておく必要があります。 不足した場合に備える手段としてFLEX SLOTや自動スケールの機能もあるのですが、その場合のSLOTの単価は通常のものに比べて高くなっていますので、それも踏まえてSLOT予約量を調整する必要があります。 またFLEX SLOTの場合は、先ほども簡単に触れました通り、通常はプログラムの中で利用する用なので、その中で無駄遣いが発生しないように慎重に準備しておく必要があります。 予約購入したSLOTはそのまま利用できるようにはなってません。SLOTには、Commitment、Reservation、Assignの概念があり、予約購入した状態ですと、Commitmentとして購入したSLOTが存在するしているという状態になります。 SLOTはGoogle CloudのOrganization単位で購入利用できるようになっているのですが、一つ以上のグループにSLOTを振り分けて利用する形式になっております。この振り分けの単位がReservationです。 一つのグループにはOrganizationのプロジェクトを一つ以上割り当てることができ、グループ内の数のプロジェクトで、グループに割り当てられたSLOTを利用することになります。このReservationに対するプロジェクト割り当てのことを、Assignと呼んでいます。 こちらはGoogleのマニュアルにあるSLOTの利用の例の図になっており、Commitment Reservation Assignの関係を示しています。この例では、まず、トータルのCommitmentとしてSLOTとして1000SLOT分保持してる状態で、Reservationのグループとしてds、elt、biの三つを作成して、それぞれSLOTを割り当てています。この例では保持しているSLOT全てを割り当て切った状態になっています。 例えばdsプロジェクトで、実行プロジェクトとしてBigQueryで、クエリを実行します。とdsグループに割り当てられた500SLOTの中からSLOTが割り当てられて実行されます。 なお、この図の中に両方向に矢印が書いてあるマークが意味するところとして、JOB実行の際にReservationに割り当てられているSLOTでは不足しているというような場合に、他のReservationで利用していないSLOTがあれば、それを利用することができることを表現しています。 このとき利用される余剰SLOTのことをアイドルSLOTと呼ぶのですが、このアイドルSLOTの利用を設定上停止することもできます。 ただし設定できるのは、他のReservationのアイドルSLOTを利用しないという設定で、反対方向にある他のReservationにアイドルSLOTを使わせないという設定ができません。 Reservationが二、三個程度の数しかないようなケースであれば、アイドルSLOTを優先的に利用するReservation以外のReservationで、アイドルSLOTを使わないという設定をすればいいわけですが、Reservation数がそれ以上に多い場合は、アイドルSLOTを融通し合うように設定しておいた方が、CommitmentのSLOTを使い切りやすくなります。 ここから我々のBigQuery SLOTの管理について、課題とその解決策をご紹介いたします。 まず我々のBigQuery環境ですが、定額のSLOT Commitmentを予約購入するモデルで利用しております。規模としては、データセット数にすると1500超、JOB数にすると1日あたり30万件超、ユーザー数であれば1日あたり700人超程度です。 BigQueryはSQLクエリさえかければ、大量データに対しても、数分程度の実行時間で結果が返ってくる非常に便利なデータウェアハウスです。利用者も非常に多くてQueryも多数使われており、利用者に対して、特に使い方の制限もしていないので、ほぼコミットしているSLOTの上限まで利用されています。 それにより実行時間が遅くなってしまったり、タイムアウトしてしまうというような問題も発生しております。 この問題に関して、SLOTの追加のCommitmentを購入することで、この問題に対処することもできるのですが、無限にSLOTを購入し続けることもできないのでそれ以外の対策が必要でした。 対策として、三つの柱を立てました。 最終的にSLOT消費削減のために施策を進めていたいきたいのですが、その前の準備段階として状況把握と管理効率化を進めました。SLOTの管理者だけでなく利用者の方々自らSLOTの利用状況や自分の状況を把握し、今どうなっているのか、SLOT量が増えているのか・減っているのかを認識できるように準備を進めました。 目的や優先度が異なるJOBが一つのReservationに混在していると、優先度の高いJOBのためのSLOTを優先度が低い所が食いつぶしてしまって、優先度が高い所のために十分なSLOTと配分できないという状況があります。 これを解消するために、目的や優先度を見定めてReservationを整理する作業も実施しました。順にこの三つについてご紹介いたします。 一つ目のSLOTの状況把握ですが、まずその状況を定義することから始めました。SLOTが枯渇すると、同じクエリでもそうでない場合と比べて実行時間が延びてしまう、いわゆる重い状態になるので、統一的に表現できるように試行錯誤し、データ処理量あたりの実行時間、一定SLOTを消費するのにかかる時間を、SLOT状況を表すメトリクスとして採用しました。 また、タイムアウト発生を避けたいので、その有無もSLOT状況を表すメトリクスとして採用しました。メトリクスについて、過去に緊急的にSLOT Commitmentを追加した前後で、どのようにそれらのメトリクスが変化したのかを分析した上で、Reservationごとにスレッショルドを定めて、そのスレッショルドに収まるメトリクスになることを各Reservationのキャパシティの要件として定めることとしました。 なお、相当の初期状況についてBigQueryの機能としてモニタリング用のWeb UIも提供されているのですが、我々が定義したキャパシティ要件メトリクスのような、より詳細な情報を得たい場合は、Web UIでは不可能なので、そういった場合に備えてBigQueryの機能として、特別なViewが提供されております。ここで紹介したようなメトリクスもそのViewを利用してモニタリングしています。 そのViewは、具体的には、JOB単位でJOBの詳細に関する情報が得られるJOBS_BY_ORGANIZATION view、各JOBの1秒ごとのタイムスライス単位で情報が得られるJOBS_TIMELINE_BY_ORGANIZATION viewが提供されています。これらを活用することで、Web UIでは得られないJOBごとの詳細な情報を得ることもできます。 こちらは、それらのViewの利用例のクエリになります。画面左側の部分でJOBS_BY_ORGANIZATIONからJOB終了時間を秒単位にならしたエラージョブズを取得して、画面右上側の部分でJOBS_TIMELINE_BY_ORGANIZATIONから行間隔でJOB数および平均SLOT消費を取得し、時刻とReservationグループ単位で、その二つをジョインしています。 こちらのクエリを実行しますと、このような結果が得られます。JOB単位の情報とタイムスライスごとの情報を組み合わせることで秒単位でJOBとSLOTの推移を確認できます。 今ご紹介したViewを活用してSLOT管理者および利用者向けにこのようなダッシュボードを作成し、社内公開して随時状況を把握できるようにしております。 また定期的にキャパシティ要件の状態をチェックして、違反の状態を検知したらすぐにSlackで通知する仕組みも用意して、タイムリーに状態を把握して、必要に応じて対策を打てる形にしております。状況把握については以上になります。 次に管理効率化のためのReservation整理について紹介します。課題として、一つのReservationに大きく2種類にわかれるJOBが同居してしまっているという状況がありました一つはAd-hocな分析用、もう一つはシステム開発用途です。 Ad-hocなものは使われ方としては比較的処理時間は短めで、素早く結果を得て活用するというタイプ。システム開発用途とはシステムが必要とするデータを整備するパイプラインを開発するための用途で結果を直接業務に活用するというよりかは本番用の動作検証という側面が大きいものになります。 このような使い方の違いから、Ad-hoc側はSLOT消費が比較的少ないものが大量に頻度で実行されるという傾向があって、システム開発用途ではバッチ処理系のSLOT消費の大きい少量のJOBが実行されるという傾向がありました。 そのため、要件としてもAd-hocでは長時間の実行が控えてSLOT消費を抑えるようにして、その他の通常のJOBに影響が出ないようにししばらくデータが得られるようにしたいという意見があり、システム開発用途では逆に実行が長時間になってしまったとしても安定的にデータ処理部を運用したいという要件があって、共存させたままだと非常に管理しづらいという問題がありました。 対策として元のReservationに所属しているプロジェクトごとにJOBの傾向を見定めた上で、新たにもう一つ別のReservationを作成して、JOB傾向に合わせてプロジェクトを振り分け直すことをしました。 これによってSLOTの配分がしやすくなったことに加えて、Ad-hocについてはReservation内の一定のSLOT消費以上、実行が続いている長時間JOBを強制停止するという仕組みを導入することも可能となりました。 一緒のままだとシステム開発用途のバッチ処理などを停止することになってしまっていましたけれども、Reservationを分離することによってそれも可能となって、優先度の高いJOBが長時間JOBにSLOT消費を奪われずに効率的に実行できるように改善することができました。 Reservationは元々、BigQueryのマニュアル上にも目的や優先度が聞かれたものを集めて構成するのが良いとされており、それによってSLOT管理をより効率的に行えるようになるメリットがあることについてご紹介しました。 最後に、SLOT削減に向けた取り組みについてご紹介いたします。不要なSLOT消費を削減するためには管理者だけではできることが限られており、実際に利用者の方々にご協力いただく必要があります。 そこで、先ほどご紹介した、JOBS_BY_ORGANIZATION、やJOBS_TIMELINE_BY_ORGANIZATIONを活用してSLOT消費が大きいテーブル作成JOBで、そのテーブルへのアクセスは全くないというものを探し出して、JOBの作成者に対してメンション付きでSlackで通知しています。 これらのViewやその他関連するテーブルに対して、クエリ実行し結果をもとに、JOBに対する操作を行うとか、あるいは結果データを使って通知を行うという処理自体は、いろいろな使い道が考えられるため、汎用的に利用できるように、SQLと通知内容をテンプレートで定義すれば、通知処理が行えるフレームワークを独自に開発し、これをBigQueryのJOB間に関連する通知の仕組みとして整備しました。 こちらは現段階では定量的な効果検証までできていないのですが、すでにSLOT消費の大きな情報を作成した方々には、見直しをしていただいていたり、アクセスのないJOBについては適時停止していただいたりしています。 これ以外のその他の取り組みについて、こちらにリストしております。 テーブルを特定のカラムの値をもとに分化するPartition filter requirement機能がBigQueryにはあるのですが、こちらはWHERE句で特定の値に絞り込んで実行することで、実行時間とSLOT消費を抑えられます。その絞り込みを行っていない場合は、クエリの実行自体を許可しないように強制する機能があり、この機能をオンにしています。もちろん実際に適用する際には、既存の定期JOBなどにも影響がありますので、その対策は必要です。 次はBI Engineです。こちらはBigQueryの機能として提供されているEメモリのキャッシュ機能として、実行プロジェクトごとにキャッシュサイズを設定して、キャッシュできます。 データセットを保持しているプロジェクトに対する設定をするのではなくて、実行プロジェクトの設定になります。キャッシュを利用したい対象のテーブル郡に対するクエリを実行するプロジェクトに対してキャッシュを設定する必要があります。 キャッシュ定義としてはメモリサイズの他にキャッシュを優先するテーブルを列挙するというようなことができるようになってます。 続いて、中間テーブルの作成活用となります。複数種類のJOBの間で共通的に実行している部分テーブルを中間テーブルとして定期的に作成しておくことで、こちらの自分の実行を効率化することができます。 メルカリ、メルペイではこうした中間テーブル作成するためのOSSのdbtを使った中間テーブルを作成を運用しております。 次はダッシュボードになります。複数のダッシュボードのソリューションを利用していますが、利用者はこれらを活用することで個別に分析用にSQLクエリを作成することなく、分析作業を行うことができます。SQLを直接利用する場合に比べて効率的なクエリになりやすく即座にビジュアライズすることもでき、SLOT消費の削減にもつながります。 最後に、今後の課題を述べます。BigQueryは最初にご紹介した料金体系から新たな料金体系に変更が予定されており、これまでよりもSLOTの単価が上がります。一方でAuto scaling SLOTと呼ばれる新たな機能で、SLOTが不足した場合のみ利用されるように事前に定義しておくことが可能になってます。 Auto scaling SLOTは単価が通常のSLOTより高めなものを使った分だけ課金されるというモデルになっていて、通常のCommitmentとAuto scaling SLOTが最適なるように設定していくことが重要になります。どのReservationにどれだけのAuto scaling SLOTを設定するのかが重要です。 また、BI Engineについてですが、こちらも設定したキャッシュメモリの量の分だけ課金されます。ただ、クエリ実行時にキャッシュヒットした場合はSLOT消費をしないので、SLOT割当とBI Engine用のメモリ量を最適に設定することが重要となります。こちらも利用しながら最適な設定を見定める予定です。 以上、メルカリ、メルペイにおけるBigQueryのSLOT管理について、課題と対策、今後の取り組みについてご紹介しました。ご清聴ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 拡張性を備えたソフトウェア設計 」の書き起こしです。 @Rupesh:こんにちは。今日は、「拡張性を備えたソフトウェア設計」というトピックで、マーケティング担当者向けのエンゲージメントプラットフォームというシステムのケーススタディを交えて紹介します。 私の名前は@Rupeshです。ソフトウェアプロダクトの開発を専門としており、ITインフラ管理などのドメインでソフトウェアプロダクトを構築した経験があります。2021年にバックエンドエンジニアとして入社しました。それ以降、CRMシステムの開発を担当しました。お客さまへさまざまなキャンペーンを展開しているプラットフォームです。 ではエキサイティングなトピックに行きたいと思います。ソフトウェアの拡張性とは、ソフトウェアを構成可能でスケーラブルにするものです。ソフトウェアの変更や新機能を、大規模なリファクタリングなしに構築可能にします。 ConfigurabilityとScalabilityの拡張性の概念の、密接な関係性を考えてみましょう。 Configurabilityはソフトウェアでアトリビュートの構成をしていきます。Configurabilityを活用することによって、ソフトウェアの寿命を長くすることができます。 Scalabilityに関しては、ソフトウェアの機能でリソースを使いながら、リクエストのボリュームを処理します。ソフトウェアのScalabilityとは、ソフトウェアのExtensibilityともつながります。 参照元: https://en.wikipedia.org/wiki/Extensibility なぜこの拡張性が必要なのでしょうか? ターゲットユーザーは多様です。全てのユーザーをソフトウェアのライフタイムで把握できるわけではありません。数十年に渡るときもあります。ソフトウェアの製品に関しては、開発を開始する時点で全てのユースケースを把握できるわけではありません。 次に、プロダクトをサードパーティと連携していかなければいけません。ソフトウェアの開発や拡張性に対しては重要です。それにあたって、どのような道をたどっていくべきでしょうか。 ソリューションとプロダクト/プラットフォームの違いは、スライドをご覧ください。 ソフトウェアソリューションは、特定の問題のために構築されています。その要件は、開発の初期段階で収集され決まります。 一方、プロダクト/プラットフォームは、長いランタイムに向けて作られ、要件は継続的に整備されます。ソリューションは、要件に合わせたオーダーメイドで問題に非常にタイトにフィットします。しかし、プロダクト/プラットフォームには成長の余地があります。 またソリューションの場合、拡張性は重視されません。与えられた時間やリソースの中で問題を解決することに主眼が置かれます。しかしプロダクトの場合、拡張性は避けて通れません。 ソリューションは、時間とリソースがプロジェクトの決定要因である場合に最適です。しかしプロダクトの場合、プロダクトロードマップのビジョンが明確であれば、プロダクトを出発点として選択するのがベストです。 ソリューションは通常短命です。しかしプロダクト/プラットフォームの場合、時間とともに進化し続けます。これらが、両者の違いです。 では、どのように開発するのでしょうか。 ソフトウェアプロダクトを開発する最も基本のステップがこちらです。 まずは、モジュールの定義です。このスライドでは、特にモジュールという用語を使っていますが、コンポーネントとも言います。まず最初にすることは、モジュールやマイクロサービスを定義することです。 これらのマイクロサービスを定義した上で、何をすべきかの責任を定義します。各モジュール、エンティティを定義し、関係を定義します。 エンティティの中で、ビジネスエンティティのカプセル化をします。そこでエンティティを定義し、関係の提言をします。これはビジネス要件に基づいた形でプロダクトを設計をしていきます。 次がインターフェースの定義です。コンポジットタイプを使います。コンポジットタイプを使う理由は、柔軟性を担保できるからです。要求に応じてインターフェースを変えなくても属性を追加できるからです。 次に、ボリュームの予測です。こちらもプロダクトを考えた場合に複雑になります。サービス間やモジュール間のコミュニケーションにとって、このボリュームの正確な予測は重要となってきます。 これらのソフトウェアプロダクトの理解をもとに、エンゲージメントプラットフォームのケーススタディをしてみましょう。 エンゲージメントプラットフォームは、お客さまとのコミュニケーションを提供し、お客さまの成長と維持につながるソフトウェアで、成長ニーズに応えるために構想された社内プラットフォームです。 ソフトウェアの開発に際しては、非常にシンプルなユースケースから始めています。まず登録したお客さまにクーポンというリワードを与えたいという単純なユースケースから始めました。 ユースケースとしては非常にシンプルなもので実現できましたが、ここでプロダクトとして構築をしたのは当社のPMが「進化し続けるニーズに対応できる単一のプラットフォームとして開発する」というビジョンを持っていたからです。そのため、将来的な拡張にも対応できるようなコード設計になっています。 エンゲージメントプラットフォームがどのように機能するかというランタイムも入れたハイレベルな図です。最初はキャンペーンの提供を始めています。これはマーケティング担当者が実行します。 このキャンペーンモジュールを使います。ここでのお客さまにクーポンというリワードを紐づけたら、お客さまに何らかの方法でお知らせします。 このキャンペーンの構成が終わりましたら、どのようにこのキャンペーンのランタイムがお客さまへのリワードと通知の配信を扱っているのかを見ていきます。 左下が、Mercari Appです。例として、メルカリに新しく登録したお客さまにリワードを与えるようなキャンペーンをマーケティング担当者が構成したとしましょう。 フローはお客さまがアプリに登録したときにトリガーされます。アプリへの登録イベントがセグメントモジュールに通知され、セグメントモジュールの方でこれらのイベントを七つのカテゴリーに分けていきます。 キャンペーンは、セグメントの定義がされています。プラットフォームに登録をしているお客さま全てにリワードを与えなければいけないという定義になっていますので、お客さまが登録されたときに、キャンペーンが走り、その後通知がお客さまに対して送られます。 Distribution Hubがモジュールとして、報酬・通知を担当するモジュールに通知を行います。 報酬とはポイントまたはクーポンでも構いません。通知を送るための三つのモジュールが、報酬の送信と通知のユーザースクリーンへの送信を扱っています。このようにイベントのジャーニーが、アプリへリアルタイムで行われています。 どのようなことをプラットフォームの中で検討・考慮したのかについてです。 これらのモジュールは疎結合になっています。これは大変重要です。これらのモジュールが密に結合していると、一つのモジュールに対する変更が他のモジュールの変更にも影響してしまいます。 もう一つは、エンティティモデリングです。さまざまな可能性のあるシナリオというのを検討・考慮しています。全てのエンティティは、いろいろなユースケースへ将来的に対応できるように考慮しています。 さらにもう一つ、全ての設計の中で、リクエストとレスポンスにおいてコンポジットオブジェクトを公開するように設計をしています。 また、それぞれのサービスのスコープを定義し、境界がしっかりあって重複しないように設計しています。 サービスの変更をする人は、変更の範囲を把握して、新しい要件が出たときには、この要件をサービスに応じて分けて、より早く簡単に開発できるようにしていきます。 次に、サービス間のコミュニケーションの戦略も定義していきます。これらの検討の裏側のアイディアは、新しい要件が出てきたときにできるだけ開発がしやすくなるようにすることです。 次にデータモデルを見ていきます。最初のステップは、ソリューションを構成しているさまざまなエンティティを見ていくことです。エンゲージメントプラットフォームには四つのエンティティがあります。 お客さまとのコミュニケーションとして、新規登録のキャンペーンやお客さまのオンボーディングキャンペーンなどが含まれていて、これらのキャンペーンエンティティは、マーケティング担当者がキャンペーンを定義するときに使います。どのセグメントのお客さまとコミュニケーションをとるのか、どういうリワードや通知を提供するのかが含まれます。 次のエンティティがセグメントです。セグメントとは、お客さまのセグメントの定義です。シンプルなユースケースとしては、メルカリというプラットフォームに出品しているお客さま、それ以外に購入だけしているお客さまなどがあります。もっと複雑なユースケースとしては、24時間以内に5つの商品を出品しているお客さま、あるいは、24時間以内に出品と売却をしているお客さまなどもあります。 こういったセグメントを重要なエンティティとしたインセンティブというエンティティがあります。インセンティブの中には、リワードの考え方が含まれております。ほとんどのキャンペーンは、何かしらのリワードが関わっています。これらのリワードは、ポイントやクーポンです。あるいは将来的に他のリワードが出るかもしれません。 最後が通知です。通知のエンティティには、お客さまに対してコミュニケーションするチャネルが含まれています。リワード関連のコミュニケーションであれば、通知エンティティを通じてお客さまに対して通知が行われます。通知オンリーのキャンペーンは、マーケティング担当者がお客さまとコミュニケーションをしたいときに使われます。 それでは細かいデータモデルを見ていきます。こちらのスライドでは、エンティティの関係性を示しています。 キャンペーンがメインのエンティティで、一対多の関係をセグメントとして持っています。それぞれのキャンペーンは、一つあれば複数のお客さまのセグメントにターゲットを絞ることができます。それぞれのセグメントはどのようなお客さまの行動に対してインセンティブを提供するのか、あるいは通知するのかを提供します。そのため、セグメントごとに複数のインセンティブや通知が関わることもあります。 このスライドで重要なポイントは、インセンティブの通知の間には直接的な関係はないことです。これらはセグメントと独立した形で関連されています。それぞれのキャンペーンは複数のセグメントを持つことができます。 ここで仮説としてお客さまのオンボーディングキャンペーンを例にとってみたいと思います。オンボーディングキャンペーンは、キャンペーンの仕様で、キャンペーンとしてプラットフォーム上でいろいろなアクティビティをするように動機づけようとするものです。 このキャンペーンでは、登録時に1000ポイントを与えます。そして、お客さまが登録するとできるだけ出品してもらいたいと思います。 もう一つのセグメントとして、出品者向けのクーポンと出品の行動を関連付けるものもあります。商品を出品するとクーポンが与えられて、もう一つ、販売することによって購入時に使用できるクーポンが得られます。 これらは、異なる通知の仕組みとも連動しています。リワードを渡していますので、お客さまに対して通知をしていく必要があります。 それでは、キャンペーンがどのようにリアルタイムで動いてるのかをビジュアルで見ていきます。 これはキャンペーンを可視化したもので、キャンペーンのエンティティがあり、それに関連したセグメントが示されています。 今回は簡単にするため、三つのセグメントを示しています。これらのセグメントはシリーズにわかれています。最初のセグメントは、アプリに登録をしているお客さまです。二つ目は、アプリに登録し出品をしているお客さまです。三つ目はアプリに登録して、出品した商品を販売しているお客さまです。 リワードと通知がどのように送られるのか。あるセグメントに入ったときにリワードと通知がどのように送られるのかを見ていきます。最初のセグメントで登録をすると1000ポイントが渡されます。プライベートメッセージあるいはプッシュ通知で通知が行われます。 登録後は、キャンペーンのコミュニケーションとして出品をするとリワードが与えられます。お客さまの次のステップとしては出品をすることになります。出品をするとまたクーポンが与えられます。 出品したものが売れると、さらに購入時に使用できるクーポンを受け取ります。そうすると、プラットフォーム上でのお買い物が起こりえるわけです。これがキャンペーンの一連の流れとなっています。 次に、イベント処理の戦略についてです。これは、拡張性あるいは信頼性などにも役立ちます。 全てのイベントは非同期な形で処理をすることに決めました。これは私たちが判断したことです。UIが関わっておらず、お客さまがアクションを取ったときには、通知チャネルのどれかを使って通知をする必要があるからです。 次に行ったのは、イベントの冪等性を決めることです。つまりリトライやリカバリーをできるようにすることです。イベントが起きたときに、インフラの問題で重複する可能性があります。そのため、イベントをシステムが受け取ったときに、新しいイベントなのかそれとも過去に受け取ったイベントなのかを区別して把握できるようにする必要があるからです。 続いて、ログについてです。イベントのサービスに入って出てくるときにログを取る必要があります。イベントでインシデントが起きたときに、インシデントの境界を明確にし、追跡が可能なように担当しているサービスを明確にする必要があります。 そして、同期のコールをするときはキャンペーンの設定だけです。これは、マーケティング担当者がユーザーインターフェースとやり取りをしている場所だからです。 それがエンハンスメントではどのように役立つのかを見ていきます。 こういった小さな機能の強化が簡単にできます。元々はクーポン配布用に作ったのですが、同じようにポイントも配布できるようになっています。 お客さまがリワードを受けている回数ですとか、お客さまが受け取ってくる最大のポイントをもとに機能強化を簡単にできるようになっています。 それでは、プラットフォーム上で行った主な機能強化についてお話しします。 元々このプラットフォームはリアルタイムの処理に対応することになっていたのですが、バッチでの配布もサポートするようになりました。お客さまが過去に行った行動に対しても、リワードを与えたかったからです。元々この製品はこういったことには対応していませんでしたが、最初の調査後、変更が必要なモジュールはセグメンテーションモジュールだけで、他のモジュールは影響を受けないことがわかりました。そのため、セグメンテーションモジュールでSQLクエリを確認する対応をとっています。 これをやっている中で大きな問題としてあったのが、イベントのバーストです。リアルタイムと比べると、これが実行されたときにイベントがバーストしてしまって、これによってフローコントロールやイベント優先順位などの新しい課題が生まれました。 プラットフォーム上で、大きな変更としてサポートしたのが通知だけの配布です。元々のシステムの設計としては、お客さまにリワードを与えて通知を与えることです。データモデルの中では、意図的にこの通知とリワードの間には関係は持たせませんでした。リワードはセグメントの中のオプションとすることで、簡単に対応できました。 この要件は元々バッチフローで要求されていたものだったのですが、のちのちにリアルタイムのキャンペーンに対しても追加の変更なく扱えるようになりました。 次に、ソフトウェアを本番で実行するとどうなるのかをお話しします。 プラットフォームを本番環境で実行する場合には、こういった項目をチェックする必要があります。 まずは後方互換性を担保することです。一つの変更点が、過去にサポートされていたユースケースを壊すことがあるからです。長期的に実施されているキャンペーンもあれば、後方互換性がない変更をしてしまうと古いキャンペーンが実行できず、Failしてしまいます。 次にしっかりとしたマーケティング担当者向けのドキュメントが必要です。自分自身で簡単にオンボーディングできるドキュメントが必要です。製品は大変幅広いお客さまのセグメントがあり、必ずしも直接やり取りができるわけではありません。マーケティング担当者が使いやすいインターフェースがあり、しっかりとしたドキュメントがあることによってオンボーディングが簡単になります。 セキュリティとお客さまのセグメント間でのデータアクセスの管理も同じ理由で大変重要です。幅広い多様なお客さまのセグメントがあると、それぞれのお客さまごとに実行範囲があるかと思いますので、しっかりとしたアクセスコントロールを持っていることが大変重要です。 最後に、モニタリングと継続性も重要です。一つの不具合が複数のビジネスオファリングに影響する可能性があるからです。こういったプラットフォームも本番で実行するためには、堅牢な信頼のできるモニタリングが必要です。不具合が起きたときにビジネスの継続性をしっかりと担保する必要があります。 皆さんに役立つ、エキサイティングな内容であればと思っております。ご清聴ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 BigQueryのデータ監視の社内サービスを作った話 」の書き起こしです。 @hyrrot:株式会社メルペイ Data Management Team、Data Managerの髙橋です。「BigQueryのデータ監視の社内サービスを作った話」と題し、発表します。 自己紹介をします。髙橋宏文と申します。2022年より、メルペイでData Managerとして、メルカリグループのアナリストの皆さまに最強のデータ分析環境を享受していただくため、日々奮闘しております。 メルカリグループでは、データウェアハウスとしてGoogle BigQueryを利用しています。データウェアハウスに関わる主要なチームとして、二つのチーム「Data Platform Team」「Data Management Team」があります。 Data Platform Teamは、サービスデータベースやログからBigQueryにデータを届けるパイプラインの開発と運用を担当し、Data Management Teamは、データの利用者が安全に安心して簡単にデータを活用できるような仕組みやプロセス構築サポートを行います。 今回はData Management Teamの成果にフォーカスしてお話しします。 Data Management Teamは、データ利用者のデータ活用を促進するため、BigQueryのデータを整え、データ利用者に届ける仕組みを運用しています。 例えば、生テーブルを中間テーブルに変換するために、dbtを用いています。dbtの定期実行のための基盤として、Cloud Buildをはじめとした、GCPのマネージドサービスやArgo Workflowsなどを利用しています。 ある日、社内の複数チームから別々のリクエストをもらいました。どちらのリクエストも具体的な要件は異なりましたが、「BigQuery内のテーブル内のデータが正しくないときに通知が欲しい」というものでした。 一方のチームでは、「BigQueryのテーブル内のデータを用いて、顧客企業に対する経費精算を行っているが、テーブルのデータがクエリの前提条件を満たしていないときに、それを検出したい」、もう一方のチームでは、「BigQueryテーブルのデータを用いて、お客さまに対してポイントの付与オペレーションを行っている。テーブルがポイントの誤付与が発生したことを示すようなデータを含む場合、それを検出したい」という要求がありました。 このような要求に簡単に応えるサービスは、社内にはまだありませんでした。 リクエストをもらった2チームの依頼に応えるため、また、今後他のチームから同様の依頼を受けたときのために、全社で利用できるBigQueryデータの監視システムを作ることにしました。このようなシステムは、「BigQuery内のデータが正しくないときに通知が欲しい」という要求に応えるものとなるでしょう。ここで言うデータの正しさとは、監視対象のデータに依存するものとなります。 ここでは、データのドメイン条件を熟知しているそれぞれのチームに、データが正しいとみなされる場合に0行の結果を返し、正しくないとみなされる場合に、1行以上の任意の結果を返すクエリを作ってもらいます。 監視システムはそのクエリを実行し、1行以上の結果が返ってきた場合、およびクエリの実行に失敗した場合にチームに通知するようにします。このような仕様を持つ監視システムを作り、それにQueryMon(クエリもん)という名前をつけ、社内に展開することにしました。 QueryMonの最初のバージョン1.0を、Argo Workflows上に実装しました。まず、クエリ実行のプロセスを管理するWorkflowTemplateを用意します。このプロセスは、パラメータとしてService Account名、実行するクエリ、通知先となるSlackチャンネル名などを受け取り、指定されたService Accountにimpersonateしてから、クエリを実行します。 その結果が1行以上、あるいはクエリの実行に失敗した場合に、Slackチャンネルにメッセージを送信します。CronWorkflowは、WorkflowTemplateのパラメータの実際の値を持ち、また指定された時間にプロセスを実行する役割があります。 このシステムはシンプルですが、いくつか問題点があります。 まずは、何らかの原因で監視が行われるべきタイミングで、実際行われなかったという場合に、それを知ることができない点です。データに問題があった場合、チームが問題を見逃してしまうリスクに繋がります。 次に、監視設定の変更に対して、QueryMon管理者のレビューを必要としていた点です。監視設定は、Argo Workflowsのリソースとして表現され、このリソースは社内のC/ICDシステムを経由して、変更するように設定されており、それがQueryMon管理者のレビューと承認を必要としていました。 QueryMonとその管理者は、データのドメイン知識に関与しないので、この監視設定変更の権限をチームに委譲するのが望ましいです。 また、Argo Workflowsを運用するチームが私たちとは別のチームであったため、トラブルシューティング時のコミュニケーションコストが上がる点も厄介でした。 それらの問題を解決するバージョン2.0を開発することにしました。 GCPのCloud Scheduler、Cloud Pub/Sub、Cloud Functions、Datadogを利用して実装しました。チームはそれぞれ独立したGCPプロジェクトを持ち、その中にチーム名、クエリ名、クエリ、サービスアカウント名、BigQueryの実行プロジェクト、タイムアウト時間の情報をJSON形式として、QueryMonのPub/SubトピックにpublishするCloud Schedulerのジョブを用意します。 チームが所有するプロジェクト内のリソースは、チーム内のメンバーのみの承認で変更できるようになっています。QueryMonは、そのPub/SubトピックがトリガーとなっているCloud Functions関数を持っています。 この関数は、トピックにpublishされた情報に含まれるService Accountにimpersonateし、クエリを実行します。さらに、クエリの実行結果をメトリクスとしてDatadogに送信します。 Datadogは、クエリ結果の行数が1行以上である場合、失敗した場合、および開始結果が一定時間存在しない場合に、Slackにメッセージを通知するように設定しています。 このシステムでは、Cloud Schedulerのジョブが、Pub/Subトピックにメッセージをpublishする必要があります。元々Cloud Schedulerは、Pub/Subトピックにpublishするジョブをサポートしていますが、このシステムのように、SchedulerのジョブとPub/Subトピックが別のプロジェクトにある場合は利用できないようになっています。 今回は、SchedulerからPub/Subの publish HTTPS API を呼び出すことで、この問題を解決しています。 Pub/Subトピックのpublish APIは、publishするデータをBASE64エンコードしたものを、パラメータとして受け取ります。 Cloud SchedulerのUIで、このような設定を直接管理するのは困難であるため、Terraformを利用して管理することにしました。 具体的には、図の赤文字で記載されたように、Terraformの関数を利用し、BASE64エンコードされたJSON形式のデータ構造を作成します。これにより、Terraformリソースのコードを通じ、人間にとってわかりやすい形で監視設定を確認できます。 次に、Datadogにmonitorを設定します。monitorは、QueryMonから送られてきたmetricの条件が成立したときに、Slackにメッセージを送るように設定します。 例えば、1行以上データが返ってきたときは、querymon.monitor_result.returned_row_countというmetricの値が0より大きくなります。スライドに記載のDatadogクエリを用いて、メッセージを送信する条件を指定できます。 Datadogクエリに、クエリ名とチーム名を条件に加えることで、特定のチームとクエリだけを選択的に通知の対象とします。また、クエリの実行に失敗したことを検出するために別のmetricを用意しており、上記と似た方法でmonitorを設定しています。 さらに、このmonitorの評価と対象となるmetricが、一定時間存在しない場合にalertを発生させることで、監視が行われていないことを検知できるようにしています。 Ver.2.0は1.0と比べて、複数の点が改善されました。DatadogのAlert Conditionの機能を活用し、監視が行われなかったことを検出できるようになったという点。監視設定をチーム内所有のCloud Schedulerに持つことにしたことで、チームが監視の設定を自分たちで変更できるようになったという点。DatadogがサポートするSlack以外の通知先も設定できるようになったという点。別チームの管理であった、Argo Workflowsを利用しなくなり、トラブルシューティング時のコミュニケーションコストが軽減された点です。 一方で、未解決の問題も残っています。一つは、チームが所有していないService Accountを利用し、任意のクエリを実行できてしまう点です。 QueryMon2.0は、Pub/Subトピックから渡されたデータに含まれるService Accountの権限を使ってクエリを実行します。あるチームが自分たちの責任範囲外のService Accountを用いることを拒否できません。 これにより、セキュリティレベルの高いデータを漏えいするリスクを軽減するために、いくつかの対策を行っています。 まずはPub/Subトピックのpublish権限を事前に申請されたチームのService Accountのみが持つことにしていること。次に、QueryMonがテーブルのデータそのものを扱わないことです。 監視に必要な情報は、クエリが成功したか、成功した場合に返ってきた行数は何行であるかのみです。 BigQueryのquery API をmaxResultsパラメータに0を指定して呼び出すと、クエリの成功失敗と結果の行数を返し、データそのものを返さない挙動となります。これは前述の仕様に合っており都合がよく、そのようにしています。 今回は、BigQueryのデータが正しくないことを検出したいという複数の要件を叶えるため、BigQueryのデータを監視する仕組みを作り、社内に展開した話をしました。同様の課題を持つ皆さまにとって参考となりましたら、嬉しく思います。 本日はありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 メルコインにおけるシステム間のデータ分離を実現するための通信アーキテクチャ 」の書き起こしです。 @pobo380:皆さん、こんにちは。このセッションでは、「メルコインにおけるシステム間のデータ分離を実現するための通信アーキテクチャ」についてお話しさせていただきたいと思います。Fintech ArchitectのKohei Nodaと申します。 最初に少し自己紹介をさせてください。改めて、名前はKohei Nodaといいます。経歴としては、2014年にMIXI入社した後、クライアントエンジニア・バックエンドエンジニア・エンジニアリングマネージャーなど、さまざまなポジションでゲーム開発を行ってきました。 その後、昨年の4月に転職してメルコインに入社し、Architectというポジションで仕事をしてきました。今年の1月からメルペイとメルコインとのアーキテクチャが、合流してFintech Architectという形になって、今はFintech Architectチームで仕事をしています。 早速ですが今日お話しすることをお伝えしたいと思います。一つ目が、メルコインにおける、システム間のデータ分離がどういうものかという話で、二つ目が、開発者体験を損なわずにデータ分離をどのように実現したかという話をしたいと思います。そして、最後にまとめをお話できたらなと思います。 まずメルコインのシステム間のデータ分離はどういうものなのかをご説明します。 メルコインにおけるデータ分離とは、メルコインのシステムとメルカリ/メルペイのシステム間で、それぞれが持つお客さまのデータを容易に紐付けられないようにすることです。 例えばメルコインでは暗号資産を取り扱う業務をしていますので、メルコイン側のシステムでは、お客さまの暗号資産情報を預かる一方、メルカリ/メルペイのシステムではお客さまの住所や氏名などの個人情報をお預かりしています。 もしこれらの情報に同時アクセスできる人がいると、その人はどこの誰がどれだけの暗号資産を持つのかという情報が得られることになります。この情報には価値があり、それを誰かに売る、何かに悪用するなどの可能性が考えられます。 そのため、メルコインのサービスを提供するにあたって、仮に内部の従業員だったとしても、メルコインとメルカリ/メルペイのシステムの間でデータの紐付けができない状態を担保したいという要求がありました。 データ分離を実現するための基本的な仕組みとして、そもそもシステムの分離があります。この図であるように、メルコインとメルカリ/メルペイのシステムはそういう分離されて作られていて、メルコインの従業員はメルコインのお客さまのデータ、メルカリ/メルペイの従業員はメルカリ/メルペイのお客さまデータにしかアクセスできないということが、システムの分類によって実現されています。 この図では、メルカリ/メルペイの従業員がメルコインのお客さまデータにアクセスできないことを示しています。逆もまた然りで、メルコインの従業員がメルカリ/メルペイのお客さまデータにアクセスできません。 ですが、このシステムの分類だけだと、ここでもしメルコインにインシデントが発生したとしてお客さまのデータが流出してしまったと仮定すると、メルカリ/メルペイのアクセス権しか持たない従業員だったとしても、メルカリ/メルペイのお客さまデータと、メルコインのお客さまデータを同時にアクセスして紐付けすることが可能になってしまいます。 さらに悪いケースとして、メルコインだけでなく、メルカリ/メルペイのお客さまデータも同時に流出したとすると、その両方のデータにアクセスできる人は、データの紐付けができ悪用できる状況になります。 このような問題を解決するために、UserIDの分離を行うことになりました。メルコインのシステムとメルカリ/メルペイのシステムで、同じお客さまに対して異なるIDを振り、それぞれのシステムで保存する仕組みです。 同じお客さまでも、異なるIDが振り分けられているので、もし両方のデータが流出したとしてもデータの紐付けが不可能になります。 このように、UserIDの分類をすることで、データの紐付けが流出したとしても、インシデントが起きたことを考えたとしても、紐付けができなくなります。 一方で、メルコインのシステムは、メルカリ/メルペイのシステムに依存し、実際メルコインのサービスとしてもメルカリアプリの中でビットコインの購入や売却ができるようになっているので、どうしてもメルカリ/メルペイのシステムと連携する必要があります。 しかし、メルコインのシステムとメルカリ/メルペイのシステムで異なるIDを使っているとどのお客さまがどのUserIDなのかがわからないので、通信をしても連携ができません。 そこで、通信の際には、お客さまを特定できるIDに変換してから、システム間の通信を行う必要があります。そのために、今回のメルコイン・メルカリ/メルペイのシステム間の連携では、このように内部用IDと外部用IDを用意しました。 単純に考えると、メルコインとメルカリ/メルペイのそれぞれにお客さまのUserIDがあり、交互に変換すれば良いんではないかと思われます。 しかし、例えばメルコインからメルカリ/メルペイに通信を送ることを考えたときに、メルコインの中で通信を送ろうとしたときにメルカリ/メルペイのIDに変換してから送るとすると、メルコインの中にメルカリ/メルペイのID変換を行う権限がないといけません。それをメルコインのサービスの中で付与すると、メルコイン側がメルカリ/メルペイのIDを知っている状況と変わりません。IDの変換権限を持ったサービスやそこにアクセスできる従業員は、実際にはメルコインのお客さまデータとメルカリ/メルペイのデータを紐付けることができるようになってしまいます。 そこで通信用に外部用IDを導入して、2種類のIDを使って変換する仕組みにしています。 それぞれIDを呼び分けているのですが、以降の説明では、メルコインの内部で使う通常のお客さまのIDをMercoin UserID、通信用のものをMercoin PPIDとします。同様に、メルカリ/メルペイのシステムの内部のIDはメルカリ/メルペイUserID、通信用のIDは、メルカリ/メルペイのPPIDとします。 PPIDの詳細については、IDPチームの方が書いたエンジニアリングブログを読んでみてください。 参考記事: Applying OAuth 2.0 and OIDC to first-party services 先ほどの説明で4種類のIDが登場しましたが、メルコインからメルカリへの通信のケースを考えると、このような変換ステップが必要になります。 一つ目はメルコイン側のサービスで、Mercoin UserIDをMercoin PPIDに変換してリクエストを送るというステップになります。次にメルカリ/メルペイのサービスが、リクエストを受け取ったら、リクエストに含まれるメルコインのPPIDをMercari UserIDに変換し、リクエストの処理を行います。 処理をしてレスポンスが生成されたら、レスポンスに含まれるMercari UserIDをメルコインに返すためMercoin PPIDに変換してレスポンスを返す必要があります。最後にレスポンスを受け取ったメルコイン側のサービスはMercoin PPIDをメルコイン内部のUserIDに変換して、レスを受け取ったレスポンスを処理するステップです。 UserIDの分離によってユーザーのデータプライバシーやデータを悪用される可能性を減らせる一方で、4種類のIDが存在するとID体系が複雑で、かつID変換を行った通信もすごく通信のステップ変換のステップも多くてややこしくなります。 今回のシステム連携ではさまざまなシステム連携が必要で、いくつものシステム間の通信を行う箇所がありました。これらのID変換を各マイクロサービスで実装するとするとコストがとても大きくなります。 そこで、システム間で異なるUserIDを用いることでユーザーのデータを守りたい。その一方で、開発者の体験も損なわないようにしたいという要求がありました。これらを同時に満たすアーキテクチャを作りたいという状況でした。 ここからは、そのような要求を満たすアーキテクチャをどう作ったかという話をしたいと思います。 目指した開発者体験を先にお話しすると、メルカリグループでは全面的にマイクロサービスを採用しています。そのため、システム間の通信メルカリ/メルペイとメルコインとのシステム間の通信もマイクロサービス同士の通信になります。 このマイクロサービス同士の通信が、内部マイクロサービスと通信するときと全く同じようにID変換を意識せずに、自分たちのUserIDだけ意識して開発すれば、外部の通信のときには自動的に外部IDに変換されて相手に到達するし、逆にAPIを呼び出される側になったとしても、自分のマイクロサービスに届くときには内部UserIDになって届いているという状況を目指しました。 それをどういうふうに実現したか。メルカリグループでは、基本的にはマイクロサービスの通信はProtocol BuffersというIDLで定義されています。そのため、マイクロサービス間でAPI通信を行うためのリクエストとレスポンスは全てProtobufメッセージとして定義されていることになります。 このメッセージの定義を利用して、通信時にメッセージに含まれているUserIDが、自動的に通信経路上で、開発者が意識することなく適切に変換されているをやることで開発者がID変換を意識する必要がない状態を作ることができました。 具体的なProtobufメッセージに含まれるIDの変換なんですけれども、Goの実装例も書いてあるんですが、イメージとしてはProtobufのあるメッセージとID変換の向きがUserIDからPPIDなのか、PPIDからUserIDなのかに従って、ID変換の呼び分けを行います。 メッセージの中に含まれるIDを全て変換して値を置き換えることが、メソッドを呼び出したときに行われるイメージです。 どのフィールドにあった値が含まれるかを知らなければならないのですが、どのフィールドに値が含まれるかという情報は、CustomOptionというProtocol Buffersの仕組みを使って、protoファイルの定義の中でアノテーションを行います。 これが、実際にプロファイルに対してアノテーションを行うときのイメージです。 例えばGetUserRequestというユーザー情報を取り出すリクエストがあったとして、引数にUserIDがあるときには、フィールドに対してID変換を有効にする、アノテーションをつけます。またレスポンスも同様に、レスポンスに含まれるUserIDにアノテーションをつけます。 そのためのCustomOptionの定義はこのような内容になります。 ここまでで説明したProtobufメッセージに含まれるIDの変換の機会を、通信経路上の二つの箇所で行いました。 一つは呼び出し側(Caller)でのID変換はgRPC Client Interceptorで、呼び出される側(Callee)でのID変換はGatewayというサービスを使って行いました。 通信経路全体としてはこのような形になります。 Caller側のID変換は、マイクロサービスに含まれるgRPC Client Interceptorで行います。そのinterceptorがIDを変換して、Gatewayを経由して、相手のマイクロサービスにリクエストを送ります。 そのGatewayでもう一度ID変換が行われて、Calleeのマイクロサービスに届きます。メルカリとメルコインのシステムを例に出していますが、逆向きの通信についても同様の経路です。 通信系の上の二つのID変換について説明する前に、図の中に出てきたID Providerについて説明します。 これはUserIDとPPIDのマッピングを持っているサービスで、メルカリ内のID ProviderのIDPのチームが管理しているサービスで、UserIDとPPIIDを相互に変換するAPIを提供しています。gRPC Interceptorでの変換とGatewayでの変換で、ID Providerと通信をして、IDマッピングを手に入れて変換することになります。 CallerのID変換ですが、これは先ほど説明したようにgRPC Client Interceptorで行います。呼び出し側のCaller側のマイクロサービスの中で変換を行います。 gRPC Client Interceptorは、gRPCのクライアントからAPIを呼び出すときに、いろいろな操作ができます。 そのできる操作の一つにリクエストレスポンスを変更することがあります。gRPC Client Interceptorの中で、メッセージに含まれているIDを取り出し、変換し、Gatewayに送信しています これはAPI呼び出しのたびに何かする必要はなくて、gRPCのClientをセットアップするときにInterceptorを挟むという設定をしておけば、自動的にInterceptorが呼び出されます。 このinterceptorでは、リクエスト送信時にCallerのUserIDからCallerのPPIDに変換して、Gatawayから戻ってきたレスポンスに対してCallerのPPIDからCallerのユーザーに変換するという処理が行われます。 CalleeのID変換は、Gatewayで行われます。Gatewayはすでにメルカリ内で多く利用されているサービスで、外部のインターネット(例:お客さまからの使っているアプリ)からくるリクエストの入口となるサービスです。いわゆる、API Gatewayに近い実装です。これが一番手前にあり、内部のマイクロサービスにルーティングを行います。 Gatewayの中で、Callerから渡ってきたgRPCの呼び出しのメッセージに対してID変換を行います。リクエストを受信したときには、CallerのPPIDでくるので、CalleeのUserIDに変換して、マイクロサービスにルーティングを行い、メッセージを渡します。 自分たちのマイクロサービスからレスポンスが返ってきたら、そこにはCalleeのUserIDが含まれているので、それをCallerのPPIDに変換します。 gRPCにはServer Interceptorがあって、Client Interceptorと同じように、Callee側のマイクロサービスでもリクエストレスポンスの変更が可能です。なぜここで、Callee MSではなく、GatewayでID変換を行っているのかという話をすると、CallerのPPIDからCalleeのUserIDの変換は、各マイクロサービスではなく、限られたコンポーネントで行いたいという要求がありました。 呼び出される側は、いろいろなところから呼び出されるので、相手のPPIDが送られてきます。その際、受け手のマイクロサービスは、任意の相手のPPIDが送られてくる可能性があるので、このIDを自分のUserIDに変換するという権限を持つ必要があります。 これはかなり強い権限であり、相手のPPIDを自分のUserIDに変換できるということは、もし相手のPPIDとユーザーデータに自分がアクセスできる状態になったときに、自分の持っているデータとその相手のデータの紐付けが可能になるという権限になります。 なのでここは各マイクロサービスに権限を渡すのではなくてGatewayという限られたサービスだけに権限を渡して変換を行うという選択をしました。 もう一つの観点としてCalleeのマイクロサービスにServer Interceptorを導入しなきゃいけないという、手間もなくなるというメリットもありました。 ID変換と通信フローのおさらいです。このように、Caller側のマイクロサービスからgRPC Client Interceptorにメッセージがあって、ID変換が行われ、Callee側のGatewayにメッセージがわたり、そこでまたID変換流れ、Calleeのマイクロサービスは内部のUserIDだけでリクエストを処理する場合と、レスポンスを生成して、Gatewayに返し、ID変換が行われ、gRPC InterceptorでID変換行われ、また呼び出し元のCallerのマイクロサービスにレスポンスが返ってくることになります。 Gatewayと、gRPC Client Interceptorによる変換で、このような開発者体験が得られました。開発者がやらなければいけないこととしては、Caller側は、gRPC Client Interceptorを、導入するだけですね。一度だけセットアップすれば良いです。 また、通信に使うProtobufのメッセージに含まれる、UserID、そのフィールドに対してアノテーションを付与すること。この二つを行うだけで、開発者はID変換というのを意識することなく、UserIDだけ扱って、内部通信と同じようにシステムを超えた、API呼び出しが可能になりました。 ここまで通常の同期通信のケースを考えましたが、実際にはシステムをまたいで非同期通信をしたいケースもありました。 ここでいう非同期通信は、メッセージキューを使う通信です。また、メルカリグループでは、ほとんどのサービスで、Cloud Pub/Subを利用しています。Cloud Pub/Subを経由した通信でも、開発者がID変更を意識することなく開発できるようにしたい状況でした。 そこで、Cloud Pub/Subのトピックに加えて、Pub/Sub Pusherというサービスが出てきます。 Pub/Sub Pusher自体はID変換のためだけに存在しているものではなく、Cloud Pub/SubのトピックからPull SubscriptionでメッセージをPullして、それをgRPCリクエストに変換し、メッセージを受け取りたいSubscriberに対してgRPCリクエストを送る機能を持ったコンポーネントです。 これは元々Pull Subscriptionを実装するというテーマがあるので、他のマイクロサービスの通信と同じように、Cloud Pub/Subを使いたいという要望が要求があって、作られたものになります。これはサブスクライブするトピックと、gRPCリクエストを送る先をKubernetesのmanifestとして記述すると、カスタムリソースになっていてカスタムコントローラーとして実装されていて、裏で実際にその処理をしてくれるものです。 これに拡張を行って、今回のシステム連携のために、ID変換機能をまずこのPubSub Pusherに追加しました。PubSub Pusherの中でPublisher側のUserIDから、SubscriberのPPIDへ変換しています。 同期呼び出しの場合は、Publisher PPIDに変換する形でしたが、ここではSubscriberのPPIDに変換しています。 Pub/Sub Pusherが行うのは、通常は内部の通信なのでこれをGatewayに対してリクエストを送ることをしています。 あとはGatewayは同期的なAPI呼び出しと同じように、gRPCに変換されているので、リクエスト受信時とレスポンスの送信時に、同じ変換を行えばいいことになります。 これで非同期通信に関しても、開発者ID変換を意識しないという体験が得られました。開発者が行うことは、Pub/Sub Pusherの設定ファイルを記述して、ID変換とGatewayへのPushを有効にすることだけです。 最後に、まとめです。 改めてですが、メルコインではシステム間でUserIDを分離することで、データの紐付けを不可能にして、高いレベルでのデータプライバシー保護を実現することができました。 一方で、ID変換という複雑な作業は、通信経路上で透過的にID変換を行うアーキテクチャを導入することで、効率的な開発ができるようになりました。マイクロサービスの開発者は実装時にID変換を意識する必要がありません。 なぜこれを実現できたかというと、マイクロサービスといえどほとんどがGoで実装されていて、gRPCを使って、通信しているProtobufでメッセージが提起されている状況だったからこそだと思います。 最後に、残っている課題を紹介して終わろうかと思います。 一つは、メルカリの中では一部Goでない実装があり、その場合は今回のようなID変換に対応できないという課題があります。 もし解決しようとすると、よりマイクロサービスにInterceptして入れるのではなく、例えば通信経路上のプロキシなどを経由して、呼び出し側も変換することが考えられます。 不具合調査時のTraceabilityは、お客さまに問題が起きて、メルカリのユーザーデータと実際見る行為のお客さまのデータを紐付けて調査しなければならないとき、それができない仕組みになっているので、かなり調査がやりづらいという状態です。 最後の一つはUserID以外のデータの紐付けです。 例えばメルカリでの購入履歴をメルコインでも共有していて、同じデータをそれぞれのDBに保存し、それぞれにユニークIDが保存されていると、UserIDでなくても紐付けが可能になってしまっています。現状の解決策はないんですけど、これをどう防ぐかを考えています。 発表は以上です。ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 メルコイン決済基盤の実践話 」の書き起こしです。 @foghost:皆さん、こんにちは。これからのセッションは、2023年3月にリリースされたメルカリアプリでのビットコインの取引の裏側にある、「メルコインの決済基盤の実践話」を紹介します。 まず、簡単に自己紹介します。私はJunwei Liangと申します。社内では、@foghstとも呼ばれています。 2016年11月にメルカリに入社し、メルペイの立ち上げ時期から決済基盤の開発に関わってきました。現在はEngineering HeadとしてValue Circulation Platformチームでメルカリグループ全体の各事業を支えるためのビジネス基盤の開発・運用をしています。 私たちのチームでは、「プロダクトチームにとってベストチョイスとなる基盤をプロダクトとして提供する」ことをビジョンとして掲げています。 具体的には、本日紹介する決済基盤や加盟店管理、KYC、カスタマーサポートなどの基盤をプロダクトチーム向けに提供しています。 そして本日のセッションの内容は、このようになります。最初に軽く全体の概要を紹介し、三つのドメインについてそれぞれ話します。 こちらは、決済基盤の概要です。ご覧のように、中は独立した二つの決済基盤にわかれています。右側は、メルペイの決済基盤です。メルカリのフリマアプリやメルカリShops、メルペイの各種決済手段に対応するための決済機能を提供しています。左側は、メルコインの暗号資産取引サービス向けの決済基盤です。 なぜ二つにわかれているかというと、メルコインの暗号資産取引の事業には、最初はセキュリティとITリスク観点での要件から、システムのインフラから開発運用を全部既存のシステムと切り離すという判断があったからです。 そのため、既存の決済基盤から機能を提供できなくなり、メルコイン専用の決済基盤を再構築しました。メルコインの決済基盤は、赤色の決済処理・台帳管理・帳簿管理という三つのドメインで構成されています。 私たちが提供している決済基盤における決済の定義は、「さまざまな決済取引において参加者たちのお財布を操作して、価値の移転・交換を行う」こととしています。 メルコインにおいては「暗号資産取引においてお客さまの取引口座、暗号資産口座などの財布を操作して、価値の移転・交換を行う」ことを決済と言います。 中でも最も基本となるのが、お客さまが持っている口座や口座の中で管理される価値の変動などの管理です。これを私たちは「台帳管理」というドメインで整理しています。 メルコインのお客さまが持っている口座の種別はこのようになっています。まずメルコインのお客さまは、基本メルペイを利用しているお客さまなので、メルペイ側の口座としてはメルカリのポイント口座またはメルペイの資金の口座を持っている状態です。 メルコインのサービスを利用し始めると、メルコインの法定通貨を扱う取引口座として暗号通貨ビットコイン(以下、BTC)を扱う暗号資産口座が作られます。ちなみにここのBTC口座は、あくまでもシステムの中の口座の話で、ブロックチェーン上のウォレットではないです。 これらの口座を操作するときに、価値の変動を記帳します。方法は二種類あります。 一つは、単式です。例えば、お客さまがメルコインでメルカリのポイント1000円と、メルコインの取引口座の残高1000円を利用してBTCを2000円分を購入するというユースケースがあげられます。 この場合、決済処理のところからそれぞれメルカリのポイントを引いて、または台帳サービスから1000円を引いて、最後にまとめて2000円のBTC付与を台帳サービスに記帳します。 この記帳方法では、取引におけるお金の移動については、移動元の口座と移動先の口座でそれぞれ単独で操作・記帳しています。 また、単独のため、ある口座のお金がどこから入った・出たなどの追跡はできないようになっています。先ほどの例では、2000円分の内訳は追跡できません。 もう一つの記帳方法は、私たちは複式と読んでいます。台帳に連携する際に、移動先と移動元をセットで操作・記帳します。 例えば、取引口座の1000円を引いて、コインBTC1000円を付与した場合、先ほどと違って、1000円の取引口座の残高消費と1000円のBTC口座の付与は一つの記帳処理として台帳に連携され、台帳で二つの口座を同時に操作・記帳することになります。 取引におけるお金の動きについては、移動先・移動元の口座は必ずセットで操作され、台帳に記帳されます。お金の出入りについて、移動先・移動元を追跡できます。 メルコインの台帳サービスでは、複式記帳を採用しています。例えば先ほどの例で言うと、取引口座の1000円を引いてBTC1000円分を購入した場合は、上流から台帳に記帳のリクエストを投げ、台帳システムのTransaction Layerでインプットを受け取り、そこで指定された口座を扱ってそして該当する金額分の価値の交換処理を行います。 価値の交換によって口座の内容が変動するのですが、変動ログなどを保存するため、口座の裏側にログやスナップショットなどの細かいデータが作られています。 複式記帳法については、一つ課題があります。 メルコインの台帳システムで管理してない口座を操作する時に、メルカリポイントだとメルペイの決済基盤で管理されてますので、メルコインの台帳システムでは、口座としては置いていません。 複式記帳する際に口座がないので、本当は実現できないのですが、記帳の実態がない内部口座を設けて今処理してます。 例えばメルカリポイントであればメルカリポイントの預かり金口座を、メルペイの残高であれば、メルペイの残高の預り口座を用意しています。そして、メルコイン自身でお客さまにBTCを付与するキャンペーンがあるときは、費用負担の口座も作っています。 また、複式を採用すると、口座の種別が2倍になる可能性もあるので、口座を増やすときに、随時開発が必要になる、生産性が低い仕組みになってしまいます。 そこで私たちが行ったのが、Configurable Value Accountの仕組みの導入です。基本全ての口座のデフォルトの動きが一緒なんですけど、それぞれの口座の特性によって特殊な動きが発生するときに、それを属性として定義・コントロールします。 すると、新しい口座が作られるときに既存の口座で提供している動きであれば、Configを書けば、あんまり実装コストをかけずに、機能を提供できます。 次に、決済処理について説明します。お客さまが持っている、内部もしくは外部の口座を操作して価値交換を行うところを決済処理と定義しています。 そしてシステムを跨いでも決済処理をするので、複数のサービスを跨いだ際の整合性担保も、重要な役割です。 機能としては、このように、メルコインでは価値交換という形で決済のスキームを抽象化して、提供しています。メルペイでは決済の取引に参加しているお客さまの種別や口座種別、サポートするアクションによって、決済APIを分けて対応しました。 メルコインでは、もう一段階抽象度を上げて、相手同士で持っている口座が価値交換という形で決済処理できるよう機能を提供しています。 メルコインでの取引は基本、お客さま自身が持っている口座間になるので、相手はお客さまです。そしてお客さまが持っている各種口座からお客さまのある口座に価値を交換してあげることを、この価値交換APIを通して実行できます。 サポートする処理としては、上流側で即時で確定したい場合は、即時確定モードを使ったり、条件をクリアした後に確定処理をしたいというニーズであれば、仮処理してから確定sよりを行う2 Phaseの処理もできるように機能設計しています。 BTC購入のユースケースを例に説明します。お客さまに提示している価格などの条件を使って、実際の取引の約定処理をするのが上流の暗号資産取引を担当するサービスです。約定処理の中で決済処理を担当するサービスにお客さまが持っている取引口座やメルカリポイント口座を表記させるという依頼を、APIを通して依頼を投げてきます。 上流側で約定を確定したタイミングで、ここで定義している確定処理を呼べば、取引口座やメルカリポイント口座から押さえた残高を確定し、最終的にお客さまの暗号資産口座にBTCを付与します。 続いて、BTC売却を例にお話しします。先ほどとは逆に、お客さまが持っているBTC口座からBTCを消費し、上流側の取引が約定確定したら、確定処理を行って、最終的にBTCが消費され、お客さまの取引口座に該当する金額分の売上残高が付与されます。 もう一つのトピックとしては、複数のサービスをまたいで決済処理を行うので、分散型のトランザクションをハンドリングする必要があります。これについては既存のメルペイでも同じ課題がありました。メルコインでは、この課題について新しい取り組みをしてきました。 参考記事 マイクロサービスにおける決済トランザクション管理 メルコイン決済基盤における分散トランザクション管理 メルコインの決済サービスの開発と一緒に、社内の他のチームでも汎用的に使えるワークフローのSDKを開発しました。SDKを使えば、通常のプログラミングと同じ体験でワークフローの関数を定義すれば、ワークフローのロジックを組み立てることができます。 そして、一つの決済処理を複数のActivityに分解して、ロールバックが必要なエラーが発生したら、右側の補償処理をActivityとして定義すれば、自動的に実行し、最終的に決済処理の結果整合性を担保する仕組みです。こちらの詳細について別のセッションでチームメンバー(@susho)から詳しく紹介してくれます。 参考記事: メルコイン決済マイクロサービスのトランザクション管理を支える技術 もう一つのトピックは、メルコインではマイクロサービスを跨いだ整合性担保をProcessing Tracerという仕組みを使って担保しています。 この仕組みを使うと、各マイクロサービスで今まで独自でバッチなど実装している処理が全部共通の仕組みでイベントベースで処理されます。 また、突合する結果もProcessing Tracerに報告が求められるので、報告のレポート がProcessing Tracerにシングルソースとして集められます。 そうすると、例えば会計帳簿のところで仕分けする時に、後で手戻りがないように、一つの処理の会計データに対してその処理の突合が終わっているかどうか確認した上で処理できます。 最後に帳簿管理のドメインについても説明します。具体的には、会計および法定帳簿の管理の話です。 帳簿と台帳との定義の違いは、独自で定義している部分もありますが、一応どちらも取引におけるお金の変動・流れを記録するためのものです。 台帳と私たちが呼んでいるのが、プロダクトサイド向けにお客さまが持っている口座の価値の増減や移動の管理を行っているものです。それ以外の目的で、お金の移動・流れを記録するものを帳簿と定義してます。 具体的には、会計処理するための帳簿が会計帳簿、法定要件を守るための帳簿を法定帳簿と定義しています。 最初に、会計帳簿連携の話をします。メルペイでは、社内に共通の会計サービスがあるので、会計要件が発生するときに、各マイクロサービスから必要に応じて必要な会計イベントを定義して、会計サービスに連携しています。 またメルペイの台帳サービスは単式記帳を採用しているので、片側しか取れてないので会計連携にするためにデータが不足しています。 既存のメルペイの手法にはこれらの課題を感じています。 一つは、各マイクロサービスについて必要に応じて会計連携すると、システムや会計のドメイン知識が必要なので、特に決済基盤で一番コアな決済サービスでは、たくさん決済種別があって、決済種別によって予定会計連携のデータ群の整形が間違っています。すると、会計や運用のコストが高くなります。 また、メルペイの台帳システムは単式記帳を採用しているので、台帳のLayerから会計の連携を全て行うことは現状不可能です。 台帳と会計帳簿はどちらも上流側の決済取引によって発生したお金の動きなので、そこのリコンサイル観点でも細かく実施したいですが、まだ台帳と会計帳簿の連携は今バラバラで、特に記録する時間が各マイクロサービスを処理時刻になってずれることもあるので、正確にはリコンサイルができない状態にはなっています。 それを考慮した上で、メルコインの会計帳簿の連携の仕組みははこのようになっています。 複式記帳を採用した台帳サービスをメルコインは作っているので、取引で発生しているお金の移動元と移動先が台帳サービスの中でも取れている状態です。それを使って、会計仕訳に必要なデータを連携できるようになります。 帳簿サービスは、会計帳簿を行っているところです。会計帳簿という新しい会計のイベントを集めて仕分け処理を行い、会計に欲しい仕分け帳簿などのデータを作るコンポーネントも開発しました。 メルコインが採用している会計連携の仕組みの特徴としては、台帳のサービスのレイヤーのみ会計データの連携をするので、それ以外のマイクロサービスが会計連携の責務は考えなくても良く、開発運用コストは軽くなります。 基本確定された台帳データを使って会計帳簿にデータを連携するので、台帳と会計帳簿のリコンサイルの仕組みも簡単に作れます。 最後に、法定帳簿についても軽く紹介します。法定帳簿は、暗号資産交換業における法定要件を満たすための帳簿データの集計と管理が求められるものです。 「顧客・自己注文伝票」「顧客・自己感情元帳」「分別管理表」などの法定帳簿を集計する必要があります。これらの帳簿を作成するために、同じく帳簿のドメインとして、帳簿サービスの中で、法定帳簿を管理する機能を作っています。 データソースとしては、上流側の暗号資産取引サービスから必要な取引のドメインイベントを集めたり、台帳サービスから提供しているAPIを使って、お客さまが持っている各種口座の変動データを参照しながら、この辺が必要になる法定帳簿の集計を日次で行っています。 最後に、まとめです。本日は、メルコインの決済基盤について紹介しました。 ハイライトとして、複式の記帳手法を採用した新しいの台帳サービスの開発、そしてより汎用的に利用できる価値交換の決済機能を提供する決済サービスの開発、整合性を担保をするために、ワークフローのSDKやProcessing Tracerの新しい仕組みの取り組みも行いました。 最後に、会計および法定帳簿の管理をするための帳簿サービスの開発も、プロダクトサイドで行いました。 これからのチャレンジとしては、メルコインで、実践した経験を生かして、メルペイ側の決済基盤もこれから進化させていきます。また組織横断で、台帳帳簿の決済については共通化できそうなドメインコンポーネントも見えているので、共通化できるシステム設計にもこれから挑戦していきたいです。 以上で本日のセッションを終わりにします。ご清聴ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 メルペイMLにおける品質保証とリスク管理 」の書き起こしです。 @shuuk:みなさんこんにちは。それでは「メルペイMLにおける品質保証とリスク管理」の発表を始めます。よろしくお願いいたします。 それではまず、私たちの自己紹介をします。まず私は、Shu Kojimaといいまして、Slacknameは@shuukとなっております。元々は受託開発でWebエンジニアをしておりましてその後、株式会社ALBERTというところで、データサイエンティストを経て、データアナリストとしてメルペイに入社しました。 その後は、MLエンジニアにジョブチェンジをして、現在は不正検知のMLを扱うFraud Prevention Teamでマネージャーをしています。本日はよろしくお願いいたします。 @yukis:Yuki Saitoと申します。メルペイでシステムリスクセキュリティのDivisionのディレクターを務めております。私自身はもともと新卒で大手の精密機器メーカーでネットワークエンジニアとして働いていました。 あまりにも金融事業をやりたくて、金融ベンチャーに飛び込んでそこからは金融事業をやっていく上でのIT周りだとかリスク管理を責任者としてやっていました。システムコンサルも挟んで現職に至ります。よろしくお願いします。 @haruki:Haruki Kanekoと申します。メルペイではリスク管理チームのマネージャーをやっております。キャリアとしては信用リスク管理一筋で、最初の頃は銀行系のカードローン会社、それからベトナムのハノイに駐在して、ノンバンクでリスク管理をやっていたり、直近ですとドイツ車メーカーの個人向けローンに関するスコアリングモデルの開発や与信戦略の立案、規制対応を経て、2021年からメルペイのリスクチームにジョインしています。本日はよろしくお願いいたします。 @shuuk:それでは早速始めていきます。まず、概要を説明させてください。私たちメルペイでは、与信と不正検知の領域でMLを活用しております。Fintechでは求めるリスク管理の基準も非常に高いので、品質を担保するためにルールやフローの整備を今まで進めてきました。 中でも現場のスピード感と品質を両立するなど、いろいろと悩みがあったので、そこにどう折り合いをつけたのかを今日はお話できればいいかなと思っております。 ではまず、最初冒頭数分程度で前提となる知識をご紹介して、その後パネルディスカッション形式でお話しします。まずは弊社のMLシステムの紹介を簡単にします。 一つ目が、与信のMLのシステムで、こちらは過去のお客さまの取引の情報から将来の貸し倒れの確立を予測して、メルカードなどの後払い系のサービスの利用限度額を決定することをミッションにしたものになっています。 そしてもう一つの柱が不正検知です。こちらは不正な動きをしている人とかトランザクションを検知することに機械学習を用いております。 ではここからメルペイにおけるリスク管理の全体像に関して@harukiさんから説明をお願いいたします。 @haruki:まず、メルペイにおけるリスク管理の全体像について説明します。お客さまのお金を取り扱うFintechでは、プロダクトにも当然高い品質が求められます。またインシデントや障害を起こしてしまうと、監督官庁、具体的には経済産業省や、金融庁への説明責任も発生してしまうという背景がございます。 特に当社メルペイの強みの一つでもあるのですが、メルペイは認定包括信用購入あっせん業の認定を日本で初めて獲得、これによって、MLモデルによって、分割払いやクレジットカードにおけるお客さまの利用限度額を算出することができております。 その分このMLシステムが想定外の挙動を起こさないように、きちんとリスク管理を行う必要性があります。 参考記事: メルペイ、事業者として第1号となる「認定包括信用購入あっせん業者」の認定を取得 金融機関における一般的なガバナンスの体制についてご説明したいと思います。こちらをThree Lines Modelと呼んでいます。 一番左側のプロダクトマネージャーやエンジニアなど、お客さまに対して製品やサービスを提供する部署を第1線とし、真ん中のリスク管理の立場から客観的に第1線を支援する部署を第2線としています。社内の最後の砦として、内部監査室を第3線としています。社内の役割を分けることで、ガバナンス体制を強化するという目的があります。 真ん中の第2線ですと、リスク、リーガル、コンプライアンスなどのチームがあり、私のリスク管理の立場からでは、信用リスク管理上の観点からMLチームのエンジニアの検証結果に関してダブルチェックを行ったり、第2線の立場から異なる視点で検証してみたりと、第1線部署と綿密にコミュニケーションしつつ多角的に評価して、安心安全なプロダクトを世の中に送り出すというのがミッションになります。 第2線はリスクを軽減するために、そのルールや社内規定、マニュアル、ポリシーといったドキュメント類を第1線と共同で作っていくというのが重要なミッションでございます。 本日お話したいのが、メルペイのML領域において、このルールがあまり明確には存在していなかったので、これを作りました。 @shuuk:私の方からMLにおけるリスクが2種類あるというお話をさせていただきます。 MLには「システムリスク」と「モデルリスク」という大きく二つのリスク管理の考え方が並列して存在しております。システムリスクとは、システムの想定外の挙動によるリスクで、例えばアプリが停止したり第三者にのっとられたりといったものを想定しています。 モデルリスクはモデルの想定外の挙動によるリスクで、貸倒率や不正な取引の確率の予測精度が下がって結果的に事業損失が出ることを想定してます。 想定しているロジックも違っていて、システムリスクは基本ルールベースを想定していますが、モデルリスクはブラックボックスアルゴリズム(AI)を想定しています。 リスクを低減する具体的な方法も違っていて、システムリスクの場合は開発チームから独立したQAチームが、QAを実施することが基本的な方法の一つになります。モデルリスクの方は、どちらかというとビジネス指標の確認やML精度指標の確認、リリース前後のバックテストモニタリングなどを手法としております。 ここからディスカッションパートに入ります。 @haruki:メルペイでどういったモデルを使っているかや、ガバナンスの体制、システムリスク・モデルリスクについてご説明させていただきましたが、私からシステムリスクについていろいろお伺いしたいです。ですけれども、このMLのシステムリスクを考える上で、QAをどうするかが問題としては大きかったと思います。当時はどういった議論があったのでしょうか? @yukis:QAに限らずシステムリスクの規定というと堅いのですが、ポリシーを考える上でI TGCと言われるIT全般統制の中で、特にQAの領域は、開発者とは独立した組織によってクオリティアシュアランスをするという組織上の権限分離が行われたもので、統制・ルール整備をするのが正攻法です。メルペイももちろんそういうポリシーとして定めています。 一方でそれは、開発者と違ったナレッジを持っている組織であったとしても、ルールベースのロジックをベースにして、クオリティアシュアランスが可能であることを前提としたルール・統制となっています。 ここはMLの領域にまるっと適用するのはあまり現実的ではないということが、課題としては大きかったです。 @shuuk:MLはルールベースみたいにテストケースを作成するのは難しくて、一応不確実性のあるものをQAする方法もいろいろ提案されてはいるものの、まだまだ発展途上の段階だと思います。しかもそれをQAチームに第三者としてやってもらうのは、現実的ではないとは思っていました。 @haruki:そういった難しさがある中で、社内で合意形成していくのは大変だと思うのですが、当時はどのようにして合意形成したのですか。 @shuuk:最初は無邪気に「モデル部分のQAが難しいので除外できませんか」と経営陣にも提案をしていましたね。ただ、「品質保証を何もやらなくていいんですか」という話は当然ありまして、回答に悩みました。 @yukis:僕も同じで、モデルの部分の正しさや、間違い・不正が介在した場合というときの事業インパクトを考えると、誰も「品質保証をしなくていい」とは経営陣は言わないということは、おっしゃる通りだと思います。 でも、それを汎用的なルールとしてあてがうより、例外的にどのようなポリシーで進めるのが良いかを考えました。 そもそも独立したQA組織による品質保証をなぜ正攻法でやりがちなのかというと、原点に立ち返ったときに、基本的には開発者自らが品質保証をやってもいいのですが、組織として見たときに、開発者自らが品質保証を怠ってしまったり、脆弱性や不正なソースコードを埋め込んでしまうリスクがあります。第三者的にそのリスクがないことを説明しなければならないとき、開発者とは独立した部隊(QA)がアシュアランスをしているのは効率的なんです。 必ずしも第三者がやる必要がない一方で、第三者による品質保証が困難となった場合、それでも第三者がやらなきゃいけないとなると、それ自体がそもそも品質保証低下につながってしまう可能性があるので、そういったポリシーはむしろ不適当です。 代替的なガバナンスは、今のメルペイのプロダクトの性質や組織の性質を考えたときにどうすべきかを柔軟に検討する必要性があったと思います。そこをMLチーム・ITリスクチームと試行錯誤してきました。 @shuuk:代替的なガバナンスという考え方はすごい鍵だったと思います。当時はまだモデルリスクという考え方を社内でそこまで持ってなくて、システムリスクだけが先にありましたが、システムリスクで保証しきれないときに、モデルリスクという新しい品質保証の柱を立ち上げて、ブラックボックス的なものはモデルリスクで見ましょうという線引きがなされました。 具体的には、バグを全部片っ端からQAするより、結果として出てくる事業KPI影響のバックテストやモニタリングを事業リスクをみている第2線の方と一緒に指差し確認することをルール化することで、変なモデルが世の中に出ないことを、担保できるという結論になりました。 @yukis:マクロ的にそう見たときにも違和感はなくて、世の中的にもグローバルな標準規格では代替的なコントロールをどう定義するかは世の中のポリシー的にも結構出てきています。 QAって大量のシステム分岐をあらゆる観点をチェックして問題がないかを確認すること自体が目的ではなく、クリティカルなバグを見つけ出すことができていれば良いと思っています。事業サイドとの合意形成によってクリティカルなバグを見つけて修正するプロセス自体は、良いアプローチの一つだったんじゃないかなと個人的には思っております。 @haruki:従来の品質保証の定義ややり方では対応できず、モデルリスク・システムリスクと切り分けて対応しました。いろいろあったと思うのですが、他にはどんな課題がありましたか。 @shuuk:もうひとつの論点として、ブラックボックスは杓子定規にQAをしないと決まったとして、ルールベースの処理を全部QAするのかという話はありました。 MLシステムといってもブラックボックスのアルゴリズムって実はほんの一部で、ルールベースの処理の方がむしろたくさんあります。それを全部QAチームに依頼するのかと考えたときに、開発スピードは正直当然落ちてしまいます。 また、コストと効果が見合うのかも考えなければなりませんでした。例えば特徴量重要度が最下位の特徴量の品質保証を第三者がめちゃくちゃ頑張ったとして、大幅にリスクが減るのかという話があがりました。 @haruki:そうですよね。MLモデルだと多量の特徴量を使って、判別力や頑健性を上げていこうというものですので、網羅的に一つ一つやっていたらきりがなくなってしまいますよね。その辺りはどのように折り合いをつけたのでしょうか? @shuuk:モデルリスクとシステムリスクの境界を工程別に分けました。スライドの通り、私たちの利用している教師あり学習は基本的には何らかの確率値を出しています。確率値の後処理を誤ると、例えば利用限度額を誤ってしまうなどの直接的にプロダクトに影響があります。そのため、ここはQAをしようという話になりました。 一方、前処理や特徴量がバグっていたとして、最終的には確率値の精度に吸収されます。よってここはモデルリスクで吸収をして、事業KPIの影響を見た上で品質を保証する形になりました。 ただそもそも、バッチが動いてないとか、システムとして何も動いてないという話は普通にシステムリスクでやるという線引きをして、QAの負担を最小限にしつつ品質保証する形にしました。 @yukis:かなりいろいろ考えさせられたトピックでした。モデルリスクとシステムリスクそれぞれ定義にまで立ち戻って考えました。 システムリスクは予期せぬコンピューターダウンや不正によってコンピュータが誤作動してしまうことをいいます。一方でモデルリスクはシステムリスクとは違うような特性を持っています。 この場合、それぞれのリスクを担保するための保証が何かというと、システムリスクは同じテストケースと同じアプローチで品質保証すると、何度やっても同じ結果になりますが、一方でモデルリスクにおける品質保証はアノマリーを検知してそこから改善してビジネスとして適切なモデルとしていくことだと個人的に感じています。 「一定の理論・法則に基づいて合理的に説明できないけど何かおかしい結果である」という状態を改善するのが、モデルリスク上の品質保証だと思います。一般的な他のドメインでしているようなQAという考え方に当てはめるのは違うと個人的には思っています。 セキュリティの振る舞い検知的な話にも近いと思います、特定の挙動的にはOKだけど、特定の挙動がある時間帯で連続したり、特定の挙動の後にこの挙動が起こると明らかにおかしくなるといったことと似ていると思います。 @yukis:続いて、メルペイでのモデルのリスク管理をきちんと定めるようになったきっかけについて聞かせてください。 @haruki:まず、メルペイが置かれてる状況として、業界として歴史あるクレジットカードた分割払いといった社会的な重要性が高く、割賦販売法という厳格な法律に基づいて、与信管理が求められているというのがあります。 一方で、我々のAI与信ライセンスを、国内で唯一経産省から認められてまして、MLのモデルでお客さまの利用限度額を独自にモデルで算出できる点で、非常に柔軟性高い一方で、品質には配慮しなきゃいけないという背景があります。 @shuuk:MLの出力によって自由に決められることは、責任が増えるということでもあります。MLエンジニアとしても、基準を満たしていく必要があるので、変な精度のモデルをデプロイしてお客さまを混乱させないように、緊張感のある開発を行っています。 @yukis:高い要求水準の中で実際には当初からその要求水準を満たせていたんですよね。 @haruki:数年前はいろいろと難しい局面もありました。例えば与信戦略の変更で、当時は意思決定が属人的で、一部のお客さまの与信を上げた際に、スコアの想定値を上回る延滞率なってしまって、これで追加調査を求められたり、与信戦略を戻さないといけないといったことがありました。 常にバッファを設けて貸倒率・延滞率はコントロールしていますが、モデルの挙動が想定外になると財務的な損失が一気に出てしまうので、非常にセンシティブだと思います。 @shuuk:当時もビジネスサイドと会話しながら要求品質と決めてたのですが、ポリシーとして明文化されてないので意思決定が属人的になっていたことは、当時振り返ると否めないです。 また、見る指標もいわゆるAUCといったMLエンジニアが見るような人が中心だったので、それが具体的にビジネスにどれぐらい影響があるのかがわかりづらいということもありました。 @yukis:その後、より品質高く与信を運用していくためにどういうフローを構築していったのかを聞きたいです。 @haruki:モデルの整理やトリガーになるイベント、開発の工数、モニタリング項目、有識者が会議体で決議できるように可視化することが必要でした。 お客さまの与信という社会の生活を密接に関わるものを取り扱っておますので、MLモデル実装後、AUCだけでなく、ビジネス上のKPI、例えば、想定延滞率と実績延滞率の乖離をきちんと見るように意識したというのが大きいです。 なお、整備したルールは、作って終わりではなく、MLチームと一緒にどうしたらこれを浸透させられるかを考えたり、チェックリストを作ってガバナンスを浸透させていくことは、今も継続議論しています。引き続き安心安全なプロダクトをリリースするために、一緒に頑張っていきたいなと思っています。 参考記事: 与信モデル更新マニュアルを作成した話 それでは、ご視聴ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 日本におけるお客さま本人確認と今後の技術的課題 」の書き起こしです。 @tim:みなさん、こんにちは。このセッションでは、「日本におけるお客さま本人確認と今後の技術的課題」についてお話しします。 @tim:Tim Tosiです。私はフランス、イギリス、そして日本で仕事をしてきました。フランスのパリ出身です。私はメルペイに入社する前、いろんな業界を経験し2020年の1月にメルペイに入社しました。KYCチームのバックエンドエンジニアから始まり、2021年3月からはKYCチームのEMをしています。 @mann:こんにちは。Manpreetと申します。私はバックエンドのソフトウェアエンジニアとしてメルペイに勤めています。 元々はインドでキャリアをスタートし、銀行、通信、eコマースでの経験があります。2019年頭に日本に引っ越しました。2021年10月にメルペイに入社し、KYCチームのバックエンドエンジニアとして勤めています。 @chris:みなさん、こんにちは。Christopheと申します。私は2014年に来日しました。最初は通信、ビッグデータに勤めて、2021年にメルペイに入社し、KYCのテックリードとしてバックエンドエンジニアをしています。 @chris:まずは、eKYC関連の法律が出る前に、日本での本人確認がどのように行われてきたかお話しします。 eKYCへの前に知られていたのが、本人確認法です。これは2008年に犯罪収益移転防止法に変わりました。引き続き、組織犯罪処罰法、麻薬特例法が今でも継続しています。簡単に最初の法律だけに焦点を当てて話をします。 まず、再犯罪防止という意味で、条約に基づきFATFがプロセスを制定したもので、口座開設時、大口取引時の本人確認の実施などが義務付けられています。 2001年にFATFはテロ資金供与に関する特別勧告を発表し、2006年末までに1000米ドルまたは1000ユーロを超える現金供与の本人確認を義務付けるように求めました。これを受けて日本では10万円を超える現金供与における本人確認を義務付ける条文を追加し、2007年1月4日に発効しました。 日本では、2000年代に入ってから、携帯電話や電子メールを利用して詐欺行為を行う架空請求詐欺や特殊詐欺が社会問題化しており、これらの犯罪で騙し取った金銭を安全に受け取る手段として架空口座を利用することがあります。この法律の目的は金融機関と取引する際にお客さまの身元を確認することで、偽名・なりすまし取引による資金獲得を防止することです。 より具体的に、どういうときにお客さまを特定すべきかというと、三つの要素があります。 まず一つ目は、金融機関と取引を開始するときです。例えば口座開設や信託取引の締結、保険契約の締結をする際です。二つ目は、10万円以上の取引がある場合、三つ目は、トラベラーチェックなどで200万円を超える場合です。 本人確認方法は、個人の場合、氏名・住所・生年月日が必要です。保険証や運転免許証、外国人在留カードを使うことが多くなっています。次に法人の場合、担当者は本人確認に加えて会社名と事務所の所在地が記載された証明書を提出する必要があります。 これらの情報を提供するとトランザクションが行われますが、本人確認が一度済めばこういった確認を提供する必要はありません。証明するためには金融機関の担当者に直接身分証明書を提示したり、パスワードや本人しか知らない情報を提供したりします。とはいえ、詐欺やなりすましの疑いがある場合、本人確認のプロセスを全てやり直す必要があります。 本人確認が行われた場合、取引時確認記録を作成し、金融機関が7年間保管しなければなりません。本人確認の要求にお客さまが応じない場合、金融機関が取引を行わないことを免除されます。 残念ながら、いくつか起きた問題をご紹介します。 最初に「学費」です。日本では大学入学金が10万円を超えることは珍しくありません。この法律が施行されたときTVや窓口には「指定された期日に納付するために、適切な身分証明書を持参するように」という注意書きが貼られました。 次が、預金詐欺です。例えば、お金を盗まれた原告が、「金融機関が適切な本人確認を行わなかった」と主張した訴訟があります。ですが裁判所はこの法律はマネーロンダリングを防止するものであり、預金詐欺を防止するためではないことから、金融機関は本人確認を進める必要はなかったと判断しました。 最後に、個人情報の収集です。この法律は、金融機関にしか適用しません。無関係の企業がこの法律を利用して、個人情報収集の目的でお客さまに個人情報を尋ねることがあります。また、勤務先や親族に関する情報など、無関係の情報を尋ねることで、さらに踏み込んだ情報を得ることもあります。 最終的にこの法律は2008年3月1日に廃止され、代わりに犯罪による収益の移転防止に関する法律が制定されました。 @mann:次のトピックはeKYCについてです。 eKYCとは、オンラインで本人確認を行う方法です。これまでとは異なり、お客さま自身が個人情報と身分証明書をシステムにアップロードします。 ここからは、特に日本におけるKYCにおける出来事をいくつか振り返ります。 1988年12月に 麻薬及び向精神薬の不正取引の防止に関する国際連合条約 が採択され、1989年7月に金融活動作業部会(FATF)が設立されました。これによってFATFと日本の総合評価において、日本では1992年7月に 麻薬特例法 、そして2000年2月に組織的犯罪処罰法、2003年1月に本人確認法が施行され、お客さまの本人確認と取引時確認記録の保存の義務が課せられました。 2007年3月に犯罪による収益の移転防止に関する法律が成立し、改正顧客識別法と組織的犯罪処罰法の一部という二つの法律に基づいて作られました。その後法改正が繰り返され、2018年には犯罪による収益の移転防止に関する法律施行規則の改正が行われました。 Fintechと相性の良い効率的な本人確認方法をいくつか挙げて、オンラインKYCを実現し、今日のKYCとeKYC対策を形作りました。 それを元に、なぜKYCを行うのか、なぜメルカリグループにKYCという部門があるのかについてご紹介をします。 本人確認は、犯罪収益移転防止法に準拠して実施されています。特定事業者が提供するソフトウェアにより、氏名、住所、生年月日、顔写真などを確認したり、書類の厚みを判別して改ざんされていないことを確認したりするなど、本人確認を目的とした画像データの利用が規定されました。 また、お客さまの利用目的や職業も確認しています。特に、特定事業者は取引時確認記録を作成し7年間保存しなくてはなりません。お客さまとの取引の中に疑わしい取引があれば規制当局に報告をする必要があります。 現在のAML(アンチマネーロンダリング)の体制は、この四つの目標を達成することにフォーカスしています。まず一つ目は、犯罪収益移転防止法の外為法に対応されており、2と3は主に組織的犯罪処罰法と麻薬特例法で対応してます。四つ目については、脅迫罪処罰法で対応し、外為法、国際テロリスト資産凍結法で対応しています。 これらの法律に加えて、金融情報センターで提供されるガイドラインでも対応していますし、AMLやCDD(Customer Due Diligence)について法令を遵守した運用を行うためのガイドラインも作られています。 ここではいくつか提供されているガイドラインについて紹介します。お客さまは本人確認を実施するときに、その場で写真撮影をし、偽造されていない画像データを提出する必要があります。 ここで受け入れられている身分証明書は、運転免許証やマイナンバーカード、在留カードなどです。 上のグラフのパーセンテージは昨年の割合で、eKYCの方法として45%のお客さまは運転免許証を選択しています。 身分証明書の正当性を確認するために、ガイドラインではドキュメントの厚さの確認をしています。例えば、写真を撮るときに傾けてもらうなどの方法をとっています。ライブネスチェックでは、お客さまにランダムなポーズを取ってもらっています。 最後に、本人確認書類の厚みなどを目視で確認します。改めて強調していきますが、特定事業者はお客さまの氏名、住所、生年月日、利用目的、職業を確認することが義務づけられています。 まずお客さまは在留カードやパスポートなど、書類のタイプを選択します。そして、お客さまが書類の画像データを提出します。書類の正当性が確認されます。そして、有効性チェックのためにお客さまはランダムなポーズを要求されます。お客さまは氏名、生年月日などの個人情報を入力します。そしてeKYCが提出され、合否が反映されるまで、通常約1週間かかります。 ICチップを使用するeKYCは、オンラインで本人確認を行います。これがまた別の方法となります。この方法では、ICチップに搭載されるデータを使用します。関連当局とお客さまの身元を確認しICチップを読み取ります。 メルカリでは、マイナンバーカードを利用しています。ICチップを読み取って、JPKIという公的個人認証サービスで電子署名を行っていきます。そして認証を行います。 金融庁のガイドラインによれば、在留期間のある外国人のお客さまについては、リスクに応じたCDD、eKYCを行う必要があります。口座が売買されていたり犯罪に利用されているおそれがあるからです。滞在期間の延長がこれ以上確認できない場合には、利用制限をかけるなど、適切なリスク対策を講じる必要があります。 外国人は、在留カードなどを使うことができます。また、氏名、住所などの基本情報に加えて、国籍、ビザの種類、在留期間を記入する必要があります。 @tim:メルペイのKYCチームは、ほとんどが外国人メンバーで構成されていますので、ネガティブな印象を持ちました。 実は新しい措置に関する潜在的な問題は、eKYCの受け止め方によって変わってきます。SNSを見てみるとネガティブに受け止めているのは、私たちだけではないようです。必ずしも不正対策そのものが問題ではなく、対策が新たに追加されることは、ほとんどの場合、お客さまの手間が増えることとなります。 政府機関も日本の企業も新たな詐欺対策を決定する際に、このことを念頭に置くということが重要です。 もう一つ私たちが強く考慮しなければならないのは、新しい脅威です。しばらく前からある強力な脅威の一つがディープフェイクテクノロジーです。これは、機械学習や人工知能のアルゴリズムを使って、写真、動画、音声トラックなどのメディアを操作し、人物をすり替えることです。 技術的なことについて私は専門外なので、このトピックに深く踏み込むことは避けたいと思っています。ただ、興味ある方々は調べていただければと思います。 私たちの申請記録であるビデオなどの資料は、身元確認に合格した人物が使用され、身元確認書類の使用者であることを確認するために手作業でチェックされます。 しかし、人間がディープフェイクを判定するのは、実際にはかなり難しいです。現在の法律で日本で期待されている本人確認に関しては本当の脅威となる技術が登場していることになります。日本がこの現実に合わせて規制を更新する必要があります。 特に反社会的勢力に関するヒントを与えたくないので、実際に話せないことがたくさんあります。十分に曖昧なままとなりますけれども、私たちは、2021年にこの問題の調査をパートナーである日本の大学とメルカリR4Dチームで共同チームを設立しました。 ディープフェイク技術が社内の本人確認フローを通過する可能性を調査しています。 社内の本人確認システムだけではなく、複数の方法で反社会的勢力と戦っています。 また、お客さまの行動を監視したり、アプリケーションの使用におけるパターンを検討することを行っています。 最近、マイナンバーカードに関するニュースを目にすることが多いかもしれません。マイナンバーカードに関わる法律の一部を改正することになりました。行政手続における特定の個人を識別するための番号として利用されることになります。( 行政手続における特定の個人を識別するための番号の利用等に関する法律等の一部を改正する法律案 ) これに続く社会の基本的なデジタル化を進めて、多くの行政の業務をオンラインで行えるようにすることが公式のスタンスとなっています。 改正のポイントは2つあります。まず、いくつかの身分証明書がマイナンバーカードに統合されることです。例えば健康保険証がマイナンバーカードに統合されることです。近い将来、eKYCを実施するための書類が減るということです。 業界にとって最大のインパクトは、将来お客さまがeKYCに使用できる書類がマイナンバーカードだけになることを政府が後押ししているということになります。ICチップの中に含まれているお客さまの個人情報を確認するため、手作業による身元確認をやめ、システムとマイナンバーカードの埋め込み署名を信頼するということで、ディープフェイクの影響を減らすことができます。 二つ目として、お客さまは手動で情報を入力する必要がないため、ユーザーエクスペリエンスが大幅に向上します。Fintech製品への登録中に、ミスを減らすことができます。 マイナンバーカードICチップを通じたeKYCの本人確認フローは、即座に完了します。お客さまが登録時に待つ必要もありません。eKYCはマイナンバーカードに埋め込まれたICチップを通じてのみ実行することを認めるかどうかは、現在では不明です。 私たちは、政府がこの方法をとることを望んでいます。マイナンバーを本人確認に利用することで、安全性とユーザーエクスペリエンスの両方が向上することは明らかであるにもかかわらず、多くの人々がこのような状況でマイナンバーを利用することに消極的です。 業界として私たちはお客さまに対してなぜ私たちの製品のやり方を変えるのか、法律の改正が業界の絶え間ない変化・進化に適用する必要性をどのように生み出すのかを説明する際に、もっとうまくやる必要があります。変化は必ずしもネガティブなものではありません。 とはいえ、あまりにも早い変化は複雑な問題を引き起こすことになります。最近、マイナンバーに関する複数の問題が報道されています。デジタル庁は、2016年1月に、国民が行政へ給付を受けられるようにするために、マイナンバーカードの銀行口座への登録受付を開始しました。 2021年10月から2023年5月にかけて、いくつかの問題が起きました。まずコンビニで住民票を取得しようとしたら、不適切な住民票が発行されたという問題がありました。 その後、一部の健康保険情報が本人ではなく、別の人に紐付けされたこともありました。 2023年5月、日本デジタル振興センターは、マイナンバーに紐付けられた銀行口座情報が他のものとなっていることがわかりました。 この問題は、手入力が問題であり、マイナンバー制度そのものに起因するものではないと覚えておく必要があります。マイナンバーカードが良い選択肢ではないのではなく、日本の本人確認手続きが誰にとってもより安全・簡単にこなせるようにすることが必要です。 私の見解ではいくつかのプロセス改善が必要で、マイナンバー制度は業界にとって非常にいい選択肢だと思います。 このプレゼンテーションは以上です。ID認証のイノベーションを望む全ての人の安全を守りたいと思います。 ご清聴ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 Merpay iOSにおけるSwift Concurrency対応の挫折と今後 」の書き起こしです。 @takeshi:「Merpay iOSにおけるSwift Concurrency対応の挫折と今後」という話をTakeshi Satoがさせていただきます。 自己紹介です。Takeshi Satoと申します。2019年にメルペイに入社して、支払いタブ画面、E2Eテストの整備、eKYC(本人確認)画面の開発を担当しました。今ではTnS(Trust and Safety)という不正対策チームでメルカリアプリの安全を守っております。「一冊でマスター!Swift Concurrency入門」という本を出しております。 今日私がお話するのは、失敗プロジェクトの共有です。私がリーダーをし、Merpay iOSにSwift Concurrencyを導入しようとしたものの、中断したお話をします。 今振り返って気づいたダメだった点をお伝えし、同じようにConcurrency対応やその他プロジェクトのコードを大きく変更する方の参考になればと思います。 Swift Concurrency対応プロジェクトは、2022年9月頃から進めていました。そのプロジェクトの概要をお伝えします。 次に、ロードマップの方向転換。最初は少しずつ対応してリリースしようと思ったのですが、Swift Concurrencyは一気に全て変更しないといけないことが判明し、そのように方向転換をしました。 次に、並行してメルペイのコードをGitリポジトリに統合するプロジェクトが進んでおりまして、その影響をお話します。最後にプロジェクトを中断した理由と、そこから得られた学びを発表します。 まず、そもそもSwift Concurrencyとはどういうものかを説明します。 Swift Concurrencyは、Swift5.5から登場した、言語レベルの並行処理の機能です。並行処理を簡潔に記述でき、Data raceを防ぐことができます。Data raceは、複数スレッドで同じデータを読み書きしたときに、データが不整合になってしまう状態です。それをコンパイル時にチェックする機能です。 キーワードとしては、async/await/Task/actor/@MainActor/Sendableなどがあります。 現在のSwiftは、リリースがSwift 5.8、ベータ版のXcode15ではSwift5.9が載っています。 Swift 6になると、Concurrencyのチェックが厳密になって、適応してないコードはコンパイルエラーになってしまいます。そのためSwiftコアチームは、今のSwift 5から段階的な適用のため、Swift 5系でも使えるコンパイルオプションを提供しています。 Swift 6になると、Concurrencyに対応してないソースはビルドができなくなるのは困りものです。そのため、早めに準備をしなければなりません。 参照: https://forums.swift.org/t/concurrency-in-swift-5-and-6/49337 具体的なコンパイルオプションはこちらです。 Swift 5.6まではOTHER_SWIFT_FLAGSに-warn-concurrencyと-enable-actor-data-race-checksを指定できました。-warn-concurrencyはSwift 6ではエラーになるコードを、Swift 5系でワーニング・エラーで教えてくれるオプションです。これを使って、Swift 6の準備ができます。-enable-actor-data-race-checksは実行時のデータ競合を診断するオプションです。 -warn-concurrencyは強いオプションなので、Swift 5.7から段階的に指定できるように、新しくSWIFT_STRICT_CONCURRENCYという専用のフラグができました。minimal、targeted、completeの3つが指定できます。 minimalが一番弱いオプションで、Sendableとactor分離を明示的に書いているところで、Concurrencyのチェックをします。Xcode14からはデフォルトのオプションになっています。 targetedはもう少し制約が強くなります。actor分離と、Sendableを明示的に書いているところで、Concurrencyのチェックをします。minimmalとの違いは、actor分離を正しく書くのを強制されているところです。ただしSendableのコードを書いてなかったらチェックはしません。 最後にcompleteが一番強いオプションです。モジュール全体でactor分離とSendableのConcurrencyのチェックをします。適切に書いていなければ、エラーかワーキングが出てしまいます。 completeと-warn-concurrencyは同じ意味です。Swift 5.7でも引き続き-enable-actor-data-race-checksを使えるので、指定します。 それでは、メルペイにおけるSwift Concurrency対応プロジェクトについて説明します。 これはMerpayiOSコードにSwift Concurrencyのビルドオプションを追加するプロジェクトになります。 目的は、Swift 6で必須になるConcurrency対応の事前準備です。現状のコードがSwift 6ではコンパイルエラーになるので、時間のあるときに対応していこうという意図があります。Sendableのエラーが出ないようにしていくのが目標です。 コンパイルチェックで、並行処理の不具合を減らす目的もありました。Swift Concurrencyの本来の目的をメルペイでも取り入れていきたいと思い、このプロジェクトを進めました。 ここで、メルペイコードのモジュールの構成を説明します。メルペイはSDKとしてモジュール化され、メルカリに組み込まれています。QRコードを出すQRモジュールや、クーポンを出すクーポンモジュールなどの各Featureのモジュールに、Sharedモジュールという形で、CoreモジュールやAPIモジュールがあり、それぞれのFeatureが依存しています。Coreは、基本的なプロトコルや、Dependencyを定義するものです。 プロジェクト当初のロードマップを説明します。 まずは、それぞれのFeatureモジュールに対応します。20以上の各モジュールにビルドオプションを渡し、それぞれビルドオプションを渡してビルドエラーを直し、Concurrency起因のワーニングをなるべく修正して、それぞれリリースします。その後コアのモジュールを対応していくという、少しずつリリースしていくロードマップを引きました。 ビルドオプション追加後、どんなエラーが出てどう修正しているのかを具体的に見ていきます。例えば、MainActorが付けられていないメソッドでUIKitのViewのプロパティを変更するものです。 UIViewとUIViewControllerはクラスにMainActorが付けられているので、メソッドの呼び出しやプロパティの更新は、MainActorのメソッドやクラスで行わないとエラーになります。 この例では、UILabelのTextプロパティを変えているのですが、MainActorがないと、コンパイルエラーになってしまいます。 それを直すには、Task@MainActorで囲うか、そもそもメソッドをMainActorにして更新する必要があります。 ワーニングの修正の一部も紹介します。例えば、DispatchQueueのasyncのクロージャーは Sendableのクロージャーなので、変数はSendableになる必要があります。 元のコードでは、asyncを実行する前にvarで変更可能な変数を定義し、クロージャで更新していたのですが、ワーニングが出てしまったので、別途変更したいデータはActorなどで全部定義した後で、クロージャ内で値を変更する必要がありました。 この対応でコードの書き方も変えました。メルペイでは、MVVMアーキテクチャを採用していて、各画面にビューモデルを実装しています。中身は薄いクロージャーでビューのイベントを検知したら、HTTP通信などして結果をクロージャーでビューに伝えます。 メルペイのコードはまだUIViewControllerで実装されているビューと接続するときには、全てMainActorが必要です。ViewModelはViewに近い操作ということで、型ごとViewModelにMainActorを追加しました。 また、Swift Concurrencyのプロトコルには少し厄介な仕様がありました。メルペイではCoreモジュールにプロトコルを定義して、それを各モジュールで準拠しています。例えばInputAppliableがCoreで定義されていて、使う側はそれを読み込んでいたのですが、例えばSubViewがUIViewを継承すると、SubViewがMainActorになります。 そこでInputAppliableのプロトコルのInput applyメソッドを実装すると、ワーニングが出てしまいます。MainActorのapplyメソッドはプロトコルに準拠していないということです。 プロトコルにはMainActorがないのですが、SubVirwのapplyメソッドには暗黙的にMainActorがついてしまうので、ワーニングが出てしまいます。 これが厄介です。各FeatureモジュールはCoreモジュールに依存しているのですが、InputAppliableの他に、Coreモジュールでプロトコルをいくつか定義していました。そのため、コアモジュールがプロトコル@MainActorにするまでは、各依存でワーニングが出てしまいます。 そのため、今回のConcurrency対応プロジェクトで各Featureにワーニングがたくさん増えてしまうという事態に陥りました。 そこで、ロードマップの方向転換を決めました。 各Featureモジュールを対応したらそれぞれリリースするのではなくて、全てのモデル対応が終わったら、リリースすることにしました。 これが、プロジェクト失敗の原因だった思います。一発リリースにすることで、プロジェクトの難易度は上がってしまいました。 そこで、ロードマップの方向転換を決めました。 各Featureモジュールを対応したらそれぞれリリースするのではなくて、全てのモデル対応が終わったら、リリースすることにしました。 これが、プロジェクト失敗の原因だった思います。一発リリースにすることで、プロジェクトの難易度は上がってしまいました。 さらにConcurrency対応と並行してGitリポジトリ統合のプロジェクトも始まりました。メルカリのGU Appのプロジェクト後に、メルペイもリポジトリを統合することになりました。 今まではリポジトリを分けてmerpay-ios-sdkというリポジトリにMercari GU Appを組み込んでいたのが、mercari-groundup-iosという一つのリポジトリにすることになりました。 Concurrencyプロジェクトは2022年9月から進んでいましたが、Gitリポジトリの統合プロジェクトによって、11月・12月はお休みし、2023年1月からConcurrencyプロジェクトが再開しました。 Gitリポジトリ統合プロジェクトが終わると、メルペイ画面をSwiftUIに書き換えるプロジェクトが始まりました。今までの画面はレガシーコードとして保守することになりました。ただ、Swift Concurrency対応のプロジェクトは、レガシーコードが対象でした。 今までのコードはレガシーコードとして扱われ、UIKit・MVVMアーキテクチャでなるべく更新しないようにする方針でした。それをGround UP Appアーキテクチャに変えようというプロジェクトです。 SwiftUIでCombineによるGround UP Appのアーキテクチャで、新規画面はこっちで実装しようという話になりました。 その後Swift Concurrencyの実装が完了しました。ただ、いろいろな問題が発生しています。 一気に書き換えたので、GitHubのファイルチェンジが1250ファイルと更新規模が膨大になってしまいます。 また動作確認したところ、不安定な挙動が頻発しました。例えばメインスレッドで動作すべき処理が別スレッドで動いていたり、別スレッドで動作すべき処理がメインスレッドで動作していたり。QRコードを読み込むカメラの処理で、AVFoundationのセッションをスタートするときに、誤ってメインスレッドで動いてしまうこともありました。 このように、品質を保証するのが難しい状態で、レガシーコードは保守運用チームの方針と矛盾する形になってしまいました。バグを直すかプロジェクトを中止するかの判断が問われる事態になりました。 ただSwift ConcurrencyはSwift 6の準備のために始めたプロジェクトです。Swift 6のリリーススケジュールを把握してないと、こちらの都合でプロジェクトを辞めたとしてもSwift 6がリリースされたらすぐ対応しなければなりません。 しかし調べてみると、Swift 6のリリーススケジュールはまだ発表されておらず、2023年はConcurrencyの他にオーナーシップに取り組む予定だと、ブログに書かれていました。またswift-evolutionでも、Swift 5.9のリリースをアナウンスされていますが、まだSwift 6のリリーススケジュールは出されていません。少なくとも2023年中にSwift 6がリリースされることはなさそうでした。 参照: https://www.swift.org/blog/focus-areas-2023/ Swift 6のリリーススケジュールとメルペイのConcurrency対応の現状を踏まえて、2023年3月にチームで話し合いをしました。 Swift Concurrencyのスレッド間で不具合がなくなるとはいえ、レガシーの積極的な更新をすべきではありません。また、一気に書き換えたSwift Concurrency対応のコードで不具合がたくさん見つかってしまっている状態です。さらに、Swift 6がリリースされても、しばらくはSwift 5モードでコンパイルする手段が提供される見込みであると発表されていました。 もちろんSwift6のどこかのバージョンでこの機能が消される可能性はありますので、最終的には対応すべきですが、Swift 6が出た当初はまだ時間がありそうです。 さらに、Xcode14.1、Swift 5.7ではUIキットWKNavigationDelegateやAVFoundationなどのConcurrency未対応のクラスやフレームワークが多い状態でした。そのため、まだまだSwift Concurrencyの書き換えは時期尚早と思われました。これらを踏まえ、Swift Concurrency対応は、SwiftUI書き換えプロジェクトの後でいいという判断になりました。 引き直した後のロードマップはこちらです。SwiftUI書き換えプロジェクトを2024年いっぱいまで終わらせ、その後にSwift Concurrencyプロジェクトを行う形にしました。 これは私の勝手な予想ですが、Swift 6のリリースはおそらく2024〜2025年の間です。SwiftUI書き換えプロジェクトが終わってからでも、この順番でできそうだと思います。 当てが外れて2024年にSwift 6がリリースされたら、書き換えプロジェクトとSwift Concurrency対応を同時にしなければなりませんが、ひとまずは書き換えの後にSwift Concurrencyの対応を考えています。 メルペイのSwift Concurrencyプロジェクトは、コードを一気に変えるという方向転換とチームとしてコードベースが変わるという影響でプロジェクトは中断されました。 チームの方針の影響はありますが、プロジェクトの方針として、一気に変える方針をとってしまったのも、中断の原因となりました。一気に変えると、影響範囲が見えにくくなって、QAが長引く原因になります。 誤算だったのは、Swift Concurrencyを一気に書き換えないと、ワーニングが増えてしまうことです。今回はワーニングが入ることで各モジュールリリースから全て書き換えのリリースに変えましたが、書き換えのベネフィットとチームの状況を見つつ進める必要がありました。 とはいえ、今思えば細かくリリースして早くコード反映した方が良いと思います。影響範囲が狭くなりますし、QAしやすいし、バグが出ても修正がしやすくなります。今回の出来事を通して、一気に書き換えるプロジェクトの難しさを痛感しました。 ワーニングが出てもチームを説得して細かいリリースをした方が、Concurrency対応を少しでも入れられたと思います。今回難しかったのはSwift Concurrency自体が時期早尚であることと、意外とSwift 6までの猶予期間があることでした。 チームによってコードの状況も変わる中で、プロジェクト中断はそれはそれで良い判断だと思いますけれども、プロジェクトリーダーとしては、不確実性を減らすために、細かいリリースを死守すべきだったと思います。 教訓は、大きな機能も細かなスケジュールを立てようということです。この経験が皆さんの参考になれば幸いです。 ご清聴ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 1週間リリースを支えるAndroid自動テスト運用のその後 」の書き起こしです。 @kenken:それでは、「1週間リリースを支えるAndroid自動テスト運用のその後」について発表します。 簡単に自己紹介をします。 @kenken:メルペイAndroidチームの@kenkenと申します。2021年5月にメルペイへ入社しました。現在はメルペイのあと払いをはじめとした、与信領域の機能開発やリグレッションテストの自動化などを担当しています。 @shinmiy:同じくメルペイAndroidチームの@shinmiyといいます。2021年12月に入社し、Androidのエンジニアとして、支払いタブやクレジットカード関連の開発、テストの自動化などを担当しています。 @kenken:それではまず、メルカリアプリの現在のリリースサイクルとリグレッションテストの自動化フローについて説明します。この内容は、2021年12月にMerpay Advent Calendarで公開された「 1週間リリースを支えるAndroidテスト運用 」というブログでも紹介しています。 現在、メルカリアプリでは、年末年始などを除いて基本的に毎週リリースが行われています。こちらの図は、リリースまでの流れを表しています。 まず、一番右のリリース日の4営業日前に、リリース用のブランチが作成されます。このブランチに含まれているコードが、リリースの対象となります。 従ってリリースしたいコードに関するQAがこのタイミングまでに完了し、かつメインブランチにマージされている必要があります。その後、Androidアプリ・iOSアプリともに、1営業日をかけてリグレッションテストを実施し、クリティカルな不具合がなければ、各ストアへ申請を行います。このリグレッションテストの一部をE2Eテストとして自動化しています。 現在は毎週行われているリリースですが、以前は隔週で行われていました。 当時の隔週のリリースサイクルでは、QAの完了からリリースまでに最長2週間待つ必要があるため、実装が完了した機能をお客さまに届けるまでのタイムラグが大きくなってしまう、Hotfixが必要になったときのリリース日の調整コストがかかってしまうなどの課題がありました。 そこで、現在のようにリリースサイクルを毎週に変更することが検討されました。また、Hotfixが必要になった際は、よほど大きな影響がない限りは、翌週のリリースまでに対応する方針も検討されました。 ここで判明した課題の一つが、リグレッションテストにかかる期間です。リリースサイクルが短縮されたとしても、リグレッションテストの実施内容は以前と大きく変わらないため、リリースサイクルのアップデートを実現するためには、2日にわたって行っていたリグレッションテストを1日に収める必要がありました。 この期間を短縮するために、リグレッションテストの自動化が求められるようになり、今回話す取り組みが始まりました。 参考記事: メルカリ・メルペイで行ったリリースサイクルのアップデート このような経緯があり、メルペイではリリース前に行うリグレッションテストの一部をE2Eテストとして自動化しています。 テストコードは、Espressoというフレームワークを使って書いており、CI上で特定のラベルが付いたプルリクエストをフックしたり、夜間にE2Eテストを実行したりしています。 テスト自体はFirebase Test Labというクラウド上でテスト端末などを提供するサービスを使って、並列に実行しています。 そして、Firebase Test Labでテストした結果をTestRailというテスト管理ツールにAPIを介して記録しています。この流れでE2Eテストを実行しています。 細かい部分でもいくつか工夫をしています。CIの実行にはお金と時間がかかるので、全てのプルリクエストに対してE2Eテストを実行するのではなく、特定のラベルやコメントを付けることで、必要なテストを必要なときにだけ動かしています。 こちらの例では、GitHubのコメントに「mp-uitest-filter dashboard」とコメントすることで、dashboardというパッケージのテストコードのみが実行されるようになっています。 また、今回の発表では詳細は割愛しますが、テストユーザー作成用のuser-tkoolという社内ツールを使うことによって、特定のユーザーを使い回さずにテストを行えるようにしています。こちらは 過去のブログ記事 でも触れているので、興味ある方はご覧ください。 次に、リグレッションテストを自動化するまでの流れをお話しします。リグレッションテストの自動化はQAチームと連携して行っています。 パターンとしては、新しくテストケースが作成される場合と、既存機能の改善に伴い、テストケースを更新する2つのパターンがあります。いずれの場合でも、TestRailとJiraというタスク管理ツールを使ってやり取りしています。 テストの実装については、TestRailに書かれている内容をもとに行います。左から順に、QAチームがテストケースを作成・更新して、内容をTestRailに反映します。TestRailでは、テストケースの実行種別でmanualかautoを指定できるようになっており、最初はmanualを指定します。その後、Jiraチケットを作成し、該当のケースへのURLを記載します。 チケットが作成されたら、エンジニア側でチケットに記載されているTestRailのテストケースを確認して、自動化が可能か、テストケースとしてケースの粒度が適切かなどの確認を行います。 この時点で自動化が難しいと判断した場合は、該当のチケットをクローズします。自動化が可能と判断した場合は、テストケースの実装を行い、該当のチケットをクローズし、TestRailのテストケースの実行種別をmanualからautoに変更します。 リグレッションテストの結果、autoのテストケースのうち、失敗率が高かったものに関しては、QAチームがマニュアルで該当のテストを再実行します。 次に、ブログ公開から現在までの約1年半の中で変化したことについて話します。 この期間に、メルカリ・メルペイではいくつかの大きな変化が起こっています。私たちの取り組みに影響する部分としては、大きく2点あります。 メルカリでは、GroundUP Appと題したリアーキテクチャプロジェクトを2020年から続けていて、2021年の後半にリリースをしています。これは、メルカリアプリをイチから書き直したもので、コードベースを今のアーキテクチャの潮流に合った形に置き換えています。 Androidアプリとしては、UIに関する部分をJetpack Composeというライブラリに置き換えています。しかし、メルペイの機能に関するコードについては、事業成長のための機能開発・改善を優先するために、GroundUP Appプロジェクトのリリース当初は、Jetpack Composeへの移行は行っていませんでした。現在では追従するように、機能ごとに順次Jetpack Composeへの移行を行っています。 メルペイとしては、最初に支払いタブのリニューアルを行い、リニューアルのタイミングでJetpack Composeに置き換えています。支払いタブというのは、右側にある図の画面です。このあたりについても、昨年のMerpay Tech Fest 2022で、取り上げているので、よろしければご覧ください。 参考記事: 【書き起こし】段階的Jetpack Compose導入〜メルペイの場合〜 – Junya Matsuyama【Merpay Tech Fest 2022】 開発体制においては、メルペイは昨年末より、「プログラム体制」をとるようになりました。 それまでは、プロジェクトと職種の2軸をもとに担当を決める「プロジェクトマトリックス体制」をとっていて、Androidチームのメンバーも、四半期ごとにさまざまなプロジェクトにアサインされる形をとっていました。 現在では、いくつかの機能を種別ごとにまとめたプログラムに他の職種のメンバーと一緒にアサインされるようになり、それぞれが担当のドメインを持つようになりました。Androidチームの各メンバーは、右側の図にある各プログラムのいずれかに所属しています。 これらの変化に対処して、メルペイAndroidチームとしても変化が起こっています。 支払いタブのリニューアルに伴って、UI部分の技術スタックは、Android ViewからJetpack Composeと変わり、リニューアルのタイミングで画面構成や機能にも大幅に変更がありました。 それに伴い、E2Eテストも修正する必要がありました。特に支払いタブは、メルペイが持っているほぼ全ての機能の入口となる画面でもあるので、この部分を修正しないと、E2Eテストの大部分が失敗するという状況でした。 そのため、復旧が急務となり、AndroidチームとしてはチームOKRの一部に組み込む形で、課題に取り組むことにしました。 また、プロジェクトマトリックス体制からプログラム体制へ移行したことに伴い、E2Eテストの実装に関わるメンバー構成も変更しました。具体的には、図の枠で囲った4つのプログラムから、1名ずつE2Eテストの実装に関わるようにしています。各ドメインの機能に精通したメンバーが参加することで、実装時に困った点を解決するまでのスピードが以前と比べて上がりました。 また、実装の優先度を決める際などに、「この画面には近いうちにこういう変更が入る」といった情報をキャッチしやすくなるといった、副次的なメリットもありました。 @shinmiy:こうした変化に対応してチームとして運営していく中で、いろいろな工夫をしています。 弊社で使っているSlackにはハドルという通信機能がありまして、他の会議ツールよりも気軽に集まって話せるようになっているのが特徴です。 この機能を使って1週間の中で定期的に集まる時間を作って、作業通話をしています。私たちの場合は毎週火曜日の午後に、全員が集まれる時間を「わいわい会」と称して作り、その時間に全員でハドルに入ってそれぞれの作業を進めます。 各々が困ったことがあったら、画面共有をしながら全員で問題解決を試みています。特にComposeについてはまだまだ新しい技術なので、手分けをして調べたりアイディアを出し合いながら、うまくテストケースを満たせる実装を探っていっています。 また、メルペイ自体が複雑な機能を持っているということもあり、知らない機能のテスト自動化を担当することもあります。ただ、全員が別々の機能群を担当しているので、お互いに質問し合って、テストケースへの理解を深め合っています。 お互いの忙しさがそれとなく確認できる場でもあるので、作業の進捗を確認して担当するタスクを調整したり、応援したりしています。 タスクの調整には、簡易的なカンバン方式を使っています。社内ツールとしてはJiraを使っているので、テストの追加や修正が必要な場合には、必ず1件ずつJiraでチケットを用意して週の進捗をカンバン方式で管理しています。 あくまでもサブプロジェクトの立ち位置ではあるので、他の大規模なプロジェクトのように、朝会や本格的な進捗確認会は行っていませんが、常に進捗自体は確認できるようにはしています。 最初に、目標として全体で取り組むチケット数やざっくりとしたアサインはメンバー内で決めて、進めていく中でメインの業務のタスクに応じて、定期的に担当を調整しています。 現在の状況を可視化して、メンバー間でお互いに補えるようにすることで、サブプロジェクトながらしっかり目標を達成できるような運用を可能にしています。 E2Eテストの作業では、似たような細かいタスクを大量にこなしていく形になります。普段はメインのタスクの合間に各々がテストを実装するのですが、並行して別々の実装を進めているので、似たような問題に遭遇しやすい状況です。 誰かが問題を解決した一方で、解決策を知らない他のメンバーが類似の問題に直面し、苦労してしまうという悲しいことが起こっています。 解決策をきちんとドキュメント化することも可能ですが、それだけで途方もない時間がかかってしまうので、それぞれが気軽に書き込める「雑にUIテストの知見を記録するメモ」を用意しました。 雑に課題と結論だけを書くドキュメントで、タイポしやすい間違いや、特定のユーザーの状況の作り方まで、実装中に気づいたことやコツ、間違いやすいポイントなどの知見が集まっています。 この知見がテストの実装に役立つことが多く、見返すことで、テストやメルペイ自体の仕様への理解も深まります。苦労して実装した結果を吐き出す場としても機能しており、読んでいくとストレスの発散の跡が見られます。 プルリクエストにもちょっとした工夫をしています。 メルカリ・メルペイの機能はかなり複雑な上に、テストの手順もどうしても言葉だけだと伝わりづらいものが多いので、テストの実装をしている間にテストが動作する様子をキャプチャし、プルリクエストに動画として載せています。 こうすることで、作業者が実現しようとしていることをレビュアーが理解しやすくなる、少なくとも作業者のローカル環境では、テストが通っていることの証拠にもなります。 ローカルでは動いているのにCI上で失敗している場合でも、この動画自体は比較対象として機能するので、どのステップで失敗したかが明確になって、修正にも役立ちます。 最後に、達成会です。有志で集まっている以上は、明確にゴールがあるとモチベーションが保ちやすく、みんなで一つの目標に向かっていくという一体感が出やすくなります。 もちろんチームとしても、「テストの自動化をいくつ完了させる」などの目標は立てていますが、それと合わせて、チームの間で「ここまで完了すれば、達成会を開催しよう」というサブ目標を立てることにしました。 こうすることで、メインの担当のタスクが重なってつらいときでも、「あと何個テストを書けば打ち上げだ」という形でモチベーションが保てるようになりました。 個人的には、TDD(Test-Driven Development)と呼んでいるのですが、あまり浸透はしていないですね。今回は、写真の通りとにかく肉を食べました。 テストの自動化には楽しく取り組めた一方で、大変なことや、課題に感じている改善ポイントもあります。 一つは、とにかく実装に時間がかかること。メルカリアプリの規模に起因する点でもありますが、フルビルドに大体15〜20分ぐらいかかるので、他の作業やミーティングの合間にブランチを切り替えて少しテストを書くという気軽さでは対応できません。 各々作業する時間を確保してテストを書くのですが、どうしても始めるときに「さあやるか」という意気込みとともにビルドボタンを押して、10分ほど待ち、休憩してから作業に戻ります。 また、E2Eの特性上仕方がないことではありますが、結果が不安定なことが多いのも大変な点です。想像以上に時間がかかってテストがタイムアウトしたり、画面遷移の際にうまくタップ対象を認識できなかったりと、安定して成功しないことが多いです。できる限り対策を行うんですけれども、現実的に解決できない問題も多く、ある程度の諦めが肝心です。 特に今の状況では一つのテストがとにかく安定することよりも、自動化されているテストの数を増やすことで、手動テストの負荷を軽減することを目指しています。 それから、仕様変更の部分です。仕様変更によって、せっかく自動化したテストが壊れることもあります。今の仕組み上、仕様変更で実装を変えたときは機能QAを通してリリースするんですが、テストの自動化した部分を直すのはリリースした後で、テストが壊れたのを確認して起票して直すというプロセスを行っています。ここは今後見直していきたいです。 それぞれのメンバーにメインの担当業務があるため、時間の捻出が難しいという課題もあります。プロジェクトの進行上仕方がないので、どのような進め方であれば効率がよいか、いろいろと試すしかないと考えています。 例えば集まる時間を1日にして集中的に実装する、物理的に顔を合わせながら実装して、コミュニケーションコストを下げる日を作るなどいろいろなアイディアはあるのですが、これからいろいろと試していくつもりです。 メルペイでは、リリースサイクルの変更をきっかけにE2Eテストの自動化にずっと取り組んできました。GroundUp Appとともに支払いタブのリニューアルを経て、テストが大幅に壊れた時期もありましたが、Composeのテストの取り組みを始めてテストの追加を頑張ってきた結果、一時期11%程度だったカバレッジが今では約50%と以前と同水準にまで回復させることができました。 ですが、これで終わりではありません。今後も引き続きテストのメンテナンスは必要だと思います。 組織的にも変更がありましたが、有志で集まって工夫をしながら進めた結果、ある程度の土台作りはできました。これからは、コードの変更に対応したテストの運用をしていけるように取り組みたいと思います。 ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 WYSIWYGウェブページビルダーを支える技術的マジックの裏側 」の書き起こしです。 @Hal:みなさん、こんにちは。私たちは、Merpay Growth Platform Frontendチームです。今日はFigmaのようなWYSIWYGツールの使いやすさにコンポーネントの合成や条件付きレンダリングのような強力な機能を追加した、私たちが取り組んでいるプロジェクトについて紹介します。 始める前に、このプロジェクトを支えるチームを紹介します。 まず、Tech LeadのArvinです。彼は、チームをリードし、メンバーの生産性を維持すると同時にこのプロジェクトや他のプロジェクトに貢献しています。 次に、Benです。彼は私たちのWYSIWYGツールにおける高度な機能の多くを概念化し、ソースコードに大きく貢献しツールのユーザーであるマーケターをサポートしています。 Jasは比較的新人ですが、基盤技術の大規模なリファクタリング、アーキテクチャの変更など、私たちのソフトウェアの多くの面を担当しています。 私は、チームのマネージャーであるHalです。問題解決に向けて関係者と話し合い、チームの成功に必要なことを調整します。 今日は私たちのウェブページビルダーである「Engagement Platform Pages」というツールについて、皆さんとともに興奮を分かち合いたいと思います。 このプロジェクトに取り組むことは毎日ワクワクしますし、とてもチャレンジングでもあります。フロントエンド技術の基本を深く掘り下げなければならないので、私たちは多くのことを学びました。 その前に@arvinhvからこのプロジェクトの背景とここまでの経緯について説明してもらいます。 @arvinhv:まず、数年前を振り返ると、このスライドにあるキャンペーンページを作成するためには、マーケター、デザイナー、エンジニアの多大な労力が必要でした。マーケターがキャンペーンのテーマや内容を決め、デザイナーがUIをデザインし、CSSやHTMLを使ってゼロからウェブページを作ります。最後にエンジニアがJavaScriptを組み込み、統合されたページに仕上げます。 平均して月に30のキャンペーンページの作成が必要です。ほぼ毎日、1ページを作成します。この3者が関与するので、何度もやり取りをしなければならず、全員に負担がかかっていました。 みんなの仕事をもっと楽にする方法はないのでしょうか?エンジニアとして問いかけ、この状況を改善するために、私たちは第1世代のCMSツールを作りました。 このツールは、JavaScript、HTML CSSをコンポーネントにカプセル化することでデザイナーが必要なコーディングの量を最小限に抑えることができます。 このツールはエンジニアによって始められたもので、このプロジェクトにリソースを投入できるよう、チームのロードマップの一部にしました。 このツールがどのように見えるかをお見せしましょう。これが第1世代ツールのユーザーインターフェースです。右側にプレビューエリア、左側に設定フォームがあります。 あらかじめ組み込まれたコンポーネントのリストから、コンポーネントの追加ができます。それがプレビューエリアに表示されるため、すぐに見た目を確認できます。また、コンポーネントのフォームからコンテンツの色の変更もできます。 つまりページを作成する方法は、これらのコンポーネントの設定であり、ページを構成するコンポーネントはいくつでも追加できます。 ここでわかるように、新しいコンポーネントは前のコンポーネントの後に追加されています。このツールはわかりやすいもので、これまでデザイナーがHTMLやCSSを書いていた問題が解決されました。マーケターが自分でウェブページを作成できるようにもなりました。 第1世代のCMSツールですが、すでに私たちの目的を達成しているように見えます。マーケター、デザイナー、その他ページを作る必要がある誰でも自分でページを作ることができるようになりました。 シンプルで、基本的にページ内にあるものが画面に表示され、各要素は設定可能なビルドを使って表せます。うまく機能していますよね。 他に何が必要だったのでしょうか?最初は、これは究極の解決策だと思ってました。しかし、多くの社内メンバーであるユーザーがツールを使い始めるにつれて、いくつかの問題に気づきました。 システムにコンポーネントを追加し、コンポーネントフォームにフィールドを追加していくと、ページを作成するときに巨大なフォームが表示され、ユーザーが修正したいフィールドを見つけるのが難しくなります。 第2に、新しいコンポーネントやコンポーネントの新しいフィールド追加など、新しい要件をサポートするために、エンジニアリング能力が必要でした。例えばコンポーネントにペインティングフィールドを追加するといった小さな変更の場合でも、リリースが必要であり、全てのパイプラインの通過が必要で、スピードも遅くなります。 さらに、要求がPMからエンジニアに、エンジニアから最終的な実装へと伝えられるプロセスでコミュニケーションギャップが生まれます。 各ステージごとのバイアスによりアウトプットが不正確になり、エンジニア、デザイナー、PMがフィードバックややり取りの調整に多くの時間を費やしていました。エンジニアとしてここは改善したいと思いました。 そこで思いついたのが、ツールをもっとカスタマイズできるようにして、マーケターがエンジニアに依存せず、エンジニアはシステムの改善に集中できるようにしたらどうだろうということでした。 そのやり方で、マーケターはこのシステムを使ってビジネス要件に対処できます。そうすれば頻繁なやり取りは不要になります。 この例を使って、考え方を説明しましょう。つまり、マーケターが既存のコンポーネントに新しい種類のログを追加したい場合、第1世代のシステムでは、チケットを作成し、エンジニアがチケットにスケジュールを設定し、リリースしなければいけません。 このプロセスには小さな変更であっても通常数日はかかりますよね。なぜなら、要件を確認し、検証し、マーケターの承認が必要だからです。 では、マーケターがページから直接何かを選択し、新しいログやアクションをページに追加できるようにしたらどうでしょうか?プロセスの生産性がそれによって大幅に向上します。 このアイディアを練るために1クォーターを費やし、アプリケーションのモックアップを作成しプレゼンテーションを行いました。 今でも覚えているのは、ミーティングを開いたり、ごはんを食べながらのOpendoorを開催したりして、多くのメンバーを集め、この新しいCMSツールの必要性を伝えていきました。なぜなら、これらの技術が大きな影響をもたらすと信じていましたし、非常にエキサイティングでもあったからです。 新しいメンバーが加わり、2、3ヶ月に及ぶ活発な開発を実行し、第2世代のシステムが完成しました。 この第2世代のものは大きく刷新されたものです。システムのコンポーザビリティやエディターの動き方、見た目を改善しました。スライドスクリーンショットをご覧いただければ、その違いはおわかりいただけるかと思います。 @jasにこの新しい変更について詳しく説明してもらいましょう。 @jas:コンポーザビリティについては、粒度を細かくすることで、柔軟性が増し、多様なコンポーネントを作成できるようになります。 以前は、コンポーネントを編集する際に二つの課題に直面しました。まず機能ごとに別々のコンポーネントを作成することは、拡張性も効率性も良くありません。 例えば、機能やスタイルが異なるボタンごとにエンジニアが開発し、エディターで使用する前にさまざまなプロセスを経る必要がありました。時間がかかりました。 二つ目の課題は、単一のカラムLayoutにしか対応ができないことです。 第2世代のエディタでは、四つの基本コンポーネントを提供します。Layout、Text、Image、Markdownです。これらのコンポーネントを組み上げることで、エディターは開発やリリースを待つことなく、複雑な機能を持つコンポーネントを作成できます。スライドの右側は、コンポーネントのスタイルコントロールパネルです。 スタイルコントロールは、Tail window CSSを使用しています。つまりCSSの機能のほとんどをコンポーネントに適用してより複雑なビジュアルスタイルやLayoutを作成できます。 さらに、異なるページの状態やユーザーの環境に応じて、異なるコンテンツを表示できるロジックコントロールコンポーネントも追加し、それによって、よりパーソナライズされた体験を提供できています。それはWHENコンポーネントです。例えばユーザーがログインしていないときにログインボタンを表示をしたり、APIのレスポンスに応じたコンテンツを表示できます。 最後に、アクションコンポーネントも導入しました。アクションコンポーネントは、ダイアログの表示やAPIのリクエストなど、多くのアクションをサポートしています。 では、実際の画面を見てみましょう。エディタを使って簡単なページを作り確認してみましょう。 これがエディタです。まず、ADDボタンをクリックして、メニューからLayoutコンポーネントを選択しましょう。 このLayoutをお客さまがウェブブラウザを使用するときに表示されるコンテンツのをコンテナとして使用します。ですので、これはウェブコンテンツと名付けます。 続いて、QRコードのコンポーネント、Textのコンポーネントを挿入して、お客さまにアプリのダウンロードとインストールを求めるブロックを作成しましょう。 次に、アプリを利用するお客さまのためのコンテンツを保持するために別のLayoutコンポーネントを追加します。 モバイルコンテンツと名付け、その中にボタンを設置しましょう。全てのコンテンツを作成した後、WHENコンポーネントを使用してそれらをラップし、さまざまな環境に基づいてそれらを表示する条件を追加します。 これはアプリのお客さま向けです。そこで、モバイルアプリを使用している条件を適用します。 ウェブブラウザを使っているお客さまのためにもう一つWHENコンポーネントを追加しましょう。ブロックの一つだけを表示して結果を確認してみましょう。 これはアプリのお客さまが見るものです。 こちらがウェブブラウザのお客さまに押されるものです。コンディションレンダリングができました。 ログインしていないお客さまに対してのみ、両方のコンテンツを表示したいとしましょう。これを実現するにはアクションコンポーネントを使用して、AuthサービスにAPIのリクエストを行い、お客さまのログイン状態を確認します。 その後、全てのコンテンツをゲストユーザー条件付きのWHENコンポーネントでラップしてログインしていないお客さまだけにコンテンツを表示できます。最後にお客さまがクリックしたときに、ログイベントを拡張するためにボタンにアクションを追加します。 @Hal:おそらく、これまで持っていたCMSツールよりも直感的になっていると思います。しかし、そんなに簡単に学べそうには思えません。 機能が多くて、マーケターはどうやってツールを使い始めたのでしょうか? @ben.hsieh:おっしゃる通りです。プロジェクトをローンチしたあと、マーケターの方々からたくさんの問い合わせが来ました。そのため、日々ハンズオンのセッションを行いました。たくさん開催しましたね。 これはみなさんにツールの使い方を学んでいただき、同時にフィードバックをもらうためでもあります。このときに、ユーザビリティが問題なのだと気づきました。 第2世代のシステムを作り始めたときに、私たちは柔軟性を考慮していました。それは、マーケターの方々にパワーを与えたかったからです。 彼らが、エンジニアからの手伝いをもらわなくてもできるようにしたかったからです。しかし、システムの柔軟性を高めると、適切に使うためには技術的な知識が必要になります。なので、その二つのバランスが必要になります。 ページエディターを作る上で、かなり労力を割いています。今日はいくつか例を使いながらその道筋を紹介していきたいと思います。 まず、例としてLayoutシステムをあげます。 ここでは、 Tail window CSS を使って柔軟なLayoutシステムを実現しています。ページエディターは、どんなLayoutでも作れるようになっています。CSSを裏側で使っているからです。 ですが、前提条件として、Tail window CSSクラスネームをかけるようにする必要があります。 これがすごく大きな問題です。マーケターの方々は、CSSの書き方がよくわからないからです。初めて紹介したときにマーケターの方々はすごく混乱しました。 そこでお手伝いをするために、CSSクラスネームビューを小さなものに分けたり、色付けをしたりしたらどうかと考えました。 それによって、使い方がわかり、クラス名を適切なフィールドに入力できるようにし、適切なクラスネームを把握できるようにしようとしました。 新しいバージョンをマーケターに見せたときに、また混乱が起こりました。 ドキュメンテーションを見て、クラスネームを少しずつ把握する必要があったからです。 そこで、もう一つ、新しいアイディアを見つけました。これは素晴らしいアイディアだと思いました。例えば IntelliSense のようなものを作ることです。デベロッパーはIntelliSenseが大好きだからです。 クラス名などが入力するごとに自動補完で表示されるようにしたら、マーケターはドキュメンテーションを調べなくてもいいじゃないかと思いました。 このスクリーンショットのように、何かを入力すると、クラス名が表示されるだけではなく、クラスネームの説明も表示されるようになります。そうすることで、マーケターは使いやすくなりますし、Tail window CSSを少しずつ使いながら学べると思いました。 これはすごく有効だと考えたんです。しかし、驚いたことに実際にマーケターの方々に試してもらったところ、また混乱してしまいました。 「CSSを知らなくてはならない」という前提は変わらないからです。クラスネームを作るプロセスが簡単になったとしても、使うための知識は必要です。なので、根本的な問題は完全に払拭できていませんでした。 その結果、Figmaあるいはブラウザウェブツールなどを作っていくことにしました。このソリューションは受け入れられ、上手く機能しました。 マーケターは、このGUIのLayoutエディタでやり取りをすると、裏側ではこの入力をTailwind CSSのクラスネームに変換しているんです。なので、元々のソリューションを諦めているのではなく、エディターの裏側に隠れているのです。 このGUIのLayout Editorはまだまだ制約があって、このクラスネームを使うことで、できることもあるけれども、GUIエディターできないこともあるので、エンジニアの方々は事前に設定をする上で使ったり、緊急の要件を満たすために使うことができます。 つまり、いろいろな技術レベルに応じて、いろいろな使い勝手が生まれます。 もう一つの例がCondition Systemです。先ほど申し上げましたように、私たちの新しいシステムは、条件に応じてコンテンツのレンダリングができます。その裏側ではJavaScriptのExpressionを使って、一部のコンテンツをレンダリングするかを判断しています。 これを作るときにこれらのコンディションはすごくシンプルで、JavaScript Expression1〜2行だろうと思っていました。このスクリーンショットにあるように、store?.user?.profileのようなものだと思っていました。 ですが、実際に幅広く使い始めてみたところ、この条件がかなり複雑で、10行以上になってしまうことがよくありました。 場合によっては、nullの検証やフォーマットのコンバージョンをしなければならなくなり、Expressionがさらに長くなります。 コンポーザビリティを増やすことによって、コンポーネントがネスティングできるようになります。そうすると、コンディションがあまりにも複雑になってしまって、マーケターは自分で設定ができなくなり、エンジニアが関わる必要が出てきます。 私たちのソリューションは、Layerとシステムとほぼ同じですが、複雑な部分をラップする作り方にしています。 よって、マーケターは機能へシンプルにアクセスできるようにしています。例えば、コンディションについてですが、マーケターに自分たちで条件を書くのではなく、GUIツールを作って一般的に使われているコンディションをテンプレートとして提供しています。 マーケターは、いくつかのコンディションから選びます。例えば、iOSアプリを利用するお客さまにだけ示したいときには、iOSアプリを利用するお客さまを選びます。そうするとGUIフィルターで条件を自動入力できます。 また、テンプレートの引数も提供していますので、多くの場合、柔軟に使えるようになっています。例えば、コンテンツとして、お客さまが販売しているものの個数に応じて表示する場合、マーケターがテンプレートを選ぶと別のフィールドが表示されて個数を入力するようになります。マーケターは、数字を入力し、テンプレートの条件を完成させます。 二つ目は、ネステートコンフィギュレーションレンダリングです。ここでもマーケターの方々が、再使用可能なコンポーネントを自分たちで作れるようにしています。 第1世代ではエンジニアがコンポーネントを書く必要がありましたが、エディターでコンポーネントを作って、彼らが再使用して、カスタム変数を使って振る舞いをカスタマイズしたり、見た目をカスタマイズできるようにしています。複雑なページのコンピレーションを将来的には再利用して、一部カスタマイズできるようになります。 二点目ですが、コンセプトは大体同じです。高い柔軟性から始めて受け入れられるレベルまで徐々に調整をしていきます。 これが、例です。高い柔軟性を提供した結果、ユーザビリティの問題がありました。しかし、高い柔軟性は開発の初期段階ではいい選択肢だと思います。オンデマンドで将来的にいろいろなレベルの柔軟性を提供できるからです。 2点目として、柔軟性とユーザビリティの問題に対応するためには、まずは小さなPoCからはじめることをおすすめします。巨大なソリューションから始めるべきではありません。マーケターが気に入ってくれるかどうかわからないからです。 IntelliSenseと同じように労力を割いているけれども、うまく使えなかったケースもあり得るからです。 3点目として、WYSIWGエディターを作るときには課題があることを覚えておいてください。技術的なものだけではなくて、さまざまな課題が発生します。ですが、1回やると、すごく学んですごく成長できると思います。 @Hal:教訓が、いくつかあります。まず問題があったときに、再発しそうな問題の場合は、効率を改善するやり方を考えてください。 例えばテンプレートの作成や自動化したり、この仕事をしなければならない人をエンパワーしたりすることを考えてください。2点目としては、エンジニアは興味のあるプロジェクトに関わった方が効率が上がります。 まとめです。 問題があり、そこに対してのソリューションを作りました。つくってみると拡張性や利用する人の能力における問題が新たに発生し、そこに対応するためにソフトウェアを再開発する必要がありました。良いバージョンのものを半年で作り、ツールの使い方をマーケターにオンボーディングし、新しいツールの確固たるファンデーションを作り、テクノロジーの使用を拡大しようとしています。 ご清聴いただきましてありがとうございました。何か新しいものを学び、勇気を持ってぜひ仕事の仕方の改善に役立てていただければと思います。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 fake clock microservice -時刻をハックしてテストする方法- 」の書き起こしです。 @vvakame:「fake clock microservice -時刻をハックしてテストする方法-」というセッションを始めたいと思います。よろしくお願いします。 @vvakame:僕は@vvakameと申します。メルペイ Solutions Teamで社内ツールなどを作っている関係上、いろいろなマイクロサービスの時刻に関わる設定を変更した上でデータを作りたいというニーズがある当事者の1人でございます。 @hiraku:スライドの真ん中に写っている、@hirakuです。メルペイではCredit Designというチームに所属してまして、メルペイのあと払いや与信関係のサービスのバックエンドエンジニアをやっております。 @tanaka0325:@tanaka0325と申します。2021年1月にメルペイに入社して、今はCredit Design Teamのバックエンドエンジニアをやっています。日々信用を創造して、なめらかな社会を作っています。よろしくお願いします。 @vvakame:おふたりはCredit Design Teamからということで、メルペイの中でも最も業務ドメインが複雑と言われるCredit Design Teamが、いかにして今まで苦しんできたかという話を今日していこうかなという感じですね。 本セッションの構成は、最初に前提となる時刻とテストの問題について解説をし、それに対する解決策・fakeclock serviceについてご説明して最後に我々3人でトークセッションをしていこうと思います。まずは前提の共有からやっていきたいと思います。 @hiraku:最初に、まずこの問題のことを前提知識としてインプットしていただければと思います。現在時刻に対応するロジックはあちこちにあると思います。 これはメルペイのあと払いのヘルプページから取ってきた画像です。8月1日に購入して、8月31日に締めがあって、9月30日までに支払うという2ヶ月間のライフサイクルがあるみたいなことが、金融領域を扱う我々メルペイにとってありがちなんです。これを現実時間で、もしテストしようとしたら2ヶ月間のQAが期間が必要になってしまいます。 修正したら、すぐリリースしたいので、素早く効率的にテストしようと思うと、「時刻をいじってしまおう」と考えるわけです。 8月1日に時刻をいじった状態で購入をテストして、9月1日に変更して請求のバッチをテストします。9月20日を超えて10月1日になると、延滞状態になりますので、その状態をテストするというイメージです。 これは、実際にメルペイの社内でもあちこちで実装されていて、テスト環境限定のAPIがあります。debug.SetNow APIが各所に実装されており、日付時刻を設定してあげると、その通りに時刻を固定できます。 この方法には、いくつか実は問題点がありました。 まず一番大きいのが、弊社はマイクロサービスアーキテクチャを採用していて、マイクロサービスが複数あることがすごく問題になっています。一つの機能を実現するのに何個もマイクロサービスが関わっているので、それぞれに対してSetNowを叩いていかないと求めた状態になりません。また、設定漏れも起きがちです。そして、それぞれのマイクロサービスでAPIの実装するのも、無駄だという問題がありました。 もし一つだけ設定を忘れた場合、デバッグがとても大変になります。 本番だと絶対にあり得ないのでそういうことを配慮したコードはなかなか書いてないんです。他のマイクロサービスと時間がずれてるという状態が、いかなる問題を引き起こすのかは、起きてみないとわからないレベルです。 昔あったのが、スナップショットのレコードを保存しようとしてたんだけど、時間が完全に固定されていたので、2件挿入しようとすると、レコードが重複してエラーになってしまったことがあります。 もう一つの問題が、テスト環境全体に対して時刻を設定しているため、テスト担当者が複数人いて、同時に複数のテストケースを実行しようとすると、Aさんが8月1日に、その直後にBさんが8月15日に日付を上書きすると、2人とも困ってしまいます。 これを並行してテストするために、テスト環境をそれぞれ人数分用意し、頑張って実行しています。環境をいっぱい用意するのは大変ですし、無駄が多いなという問題があります。 @vvakame:Aさんの環境は安定しているけど、Bさんの環境は不安定で、これはバグなのか迷ってしまうこともあります。 @hiraku:まとめていくと、この辺を求めているわけです。並行してテストしたいので、一度の操作で、各マイクロサービスの時刻を一気に変更したいし、環境ごとではなくてリクエストごとに時刻を操作したい。環境をいっぱい用意するのではなく気軽に時刻を操作できるようになりたいということです。 そこで我々の方でいろいろ検討し、タイトルにもあったfake clock serviceに行き着きました。 それまでにあがった検討案として、最初が「環境をいっぱい用意する」の亜種という形で「必要なタイミングでマイクロサービス群を複製する」という案がありました。ボタンを押すと、マイクロサービス群が一気に立ち上がるような環境があればいいんじゃないかということは、アイディアとしてありました。これは本番環境にすごく近い状態にできますし、安定してるのではないかと考えました。しかし、環境を1から立ち上げようとするとどんなに最適化しても数分はかかってしまうので、なかなか気軽にテストできる状況にはなりませんでした。 もう一つが、「メタデータとして伝播させる」ということ。 マイクロサービスのサービス内容は、たいてい何らかのリクエストから始まります。HTTPのリクエストだったらHTTPヘッダー、そういったメタデータ部分に、現在時刻の情報を含め、最初に受け取ったサービスがメタデータを後ろのマイクロサービスに伝播させれば、全てのマイクロサービスで、リクエスト単位で時刻設定できるんじゃないかという考えに至りました。 これでうまくいくと思ったのですが、1個問題がありました。社外システムが途中に挟まっているというケースがあったのです。 我々だけで作っていない部分がいくつかあります。メルペイの社内であれば融通が利くのですが、社外のシステムにおいてはそうはいかず、社外システムのコールバックが本当の現在時刻で返ってきてしまいました。 あと社内であっても、どこかのサービスが工数や優先度の問題で、メタデータ伝播の実装がなかなかできないとなると、裏側のサービスがいつまでたってもメタデータを受け取れないという問題がありました。部分的に導入していくというのが厳しいという問題になりましたね。 そして出てくるのが、マイクロサービスを立ててしまおうという案です。マイクロサービスアーキテクチャの問題はマイクロサービスを立てれば解決するというアイディアです。 真ん中にfake clock serviceという新しいマイクロサービスを1個立てます。これが、全ての時刻のマスターのように振る舞います。ただ、一個のサービスがマスタークロックを持ってます、というだけでは他のサービスが全部同じ時刻に切り替わってしまって並列度が上げられないので、時刻の管理をユーザー単位にしようということで、ユーザーIDをキーにして時刻を取れる仕組みにしました。ユーザーさえ違えば、別の時刻帯をそれぞれ管理できるようにしました。 大体良さそうだったんです。ユーザー単位で大体テストしているので、並列度が欲しければ、ユーザーをいっぱい作ってしまえばいいという話です。 メタデータの問題だと外部システムが途中に挟み込んであるので困ったことがありましたが、こっちの案はもし外部システムから戻ってきたタイミングで、改めてfake clock serviceに問い合わせしに行くと時刻を強制的に復元して、また元のロジックに戻っていけるというメリットがあります。 また、外部システムと同じで途中に未実装のサービスがあっても、導入済みサービスだけでも恩恵がある状態を作れるので、部分的に導入しやすいという面もあります。 ただ、問題としては、こんなサービスを本番には作るわけにはいかないので本番環境と構成が異なります。また、このfake clock serviceがテスト環境限定の単一障害点となり、これが落ちると全部テスト環境がうまく動かなくなってしまうというデメリットがあります。 現状は、導入を進めている最中です。 今ちょうどこれから各マイクロサービスに導入していくフェーズです。運用が開始されてからの知見が実はないんです。ステータスとしてはSDKとマイクロサービス本体が実現されているのと、user-tkoolに関しては稼働しています。 @vvakame:user-tkoolとは、社内用のデバッグ操作をSlackコマンドでできる便利なサービスです。オプションを設定するインターフェースはすでに作ってあるというイメージです。 参考記事:テスト用お客さまデータ作りツール user-tkool の近況 @hiraku:user-tkoolの中でいろいろ時刻をセットして、一つだけ購入したことのあるユーザーを作ることもできるので、内部的に時刻を操作する部分を使っています。 ただ、マイクロサービスに導入するときに、検証をするのが大変なので、導入が止まっているのが現状です。 @vvakame:ここで、質問をいただいています。「内部のアクセスが1hop増えるからレスポンスが遅くなる可能性があるのではないですか」という質問がありました。 これについてはとりあえず機能を達成してQAの工数を圧縮できるのがまずは優先で、将来的にそうなったらメタデータ型との複合で、サービスに設定値を取りに行かなくてもメタデータにあればすぐ返せるという実装も考えています。 @hiraku:負荷テストで性能を測定したい場合は、テスト環境であってもfake clock serviceの設定を切ることもできるのでその辺で調整していこうかなと考えています。そもそも一度の負荷試験で日付をまたぐことはないと思います。 @vvakame:次の質問です。「マイクロサービスは、現在時刻を取るときに常にユーザーIDを渡していますか?」。そうですね、マイクロサービス間の通信には内部的なアクセストークンが発行され、そこにuser customer IDが常に含まれているので、gRPCインターセプターなどのLayerでアクセス元のIDを確認できるようになっています。 次の質問「各マイクロサービスはどのタイミングで fakeclockにリクエストするんでしょう?」というのも大体gRPC インターセプターでサーバーにリクエストを受け取ったタイミングで1回だけ取りに行きます。 @tanaka0325:今はユーザーIDでとりあえず始めていますが、ユーザーIDがキーじゃないサービスもあるかも知れません。一意のキーであれば何でも良いので、今後はその対応もしていこうかなという話は出ています。 @vvakame:ここからはパネルディスカッションに入ります。 トピックはこちらです。まずは、「今どこまで進んでいるのか」。先ほど話がありましたね。 言い訳をさせていただくと、Merpay & Mercoin Tech Festのスピーカーが募集されたタイミングで、「fake clockはどうですか」と打診されたとき、その頃には最低一つか二つぐらいのマイクロサービスには導入が終わってるだろうと高を括っていましたが、案外進みませんでした。 @hiraku:マイクロサービス本体は@vvakameさんが入ってきてくれてからは、サクサク進んでそれは問題なかったんですけど、導入側が大変でした。 @vvakame:次のテーマ「導入時のQAどうするのか問題」ともつながります。一応一つのマイクロサービスに対して導入のプルリクエストは作りましたが、これをマージするときにどうやってQAをするのかという課題が持ち上がりました。 @tanaka0325:単純に考えると影響範囲があるところはQAしたいという気持ちがあるので、どこが影響範囲なんだというと、時刻は取り扱うところはありとあらゆる箇所に散らばっているので極論全部という話になってしまいます。 それを全部やると、いつまでたってもリリースできないので、これは何か考えないとなということで今議論しています。 @vvakame:SetNowを呼ぶところは構造上は少ないんですけど、Goのtime.Now()のようなものはコード上にたくさん散らばっているので、その無影響確認と、本当に時刻が変更できているという影響確認の両方が難しいです。 @hiraku:過去のQAに関しては、当然時刻操作しながらするQAシナリオがいっぱいあったので、元のマイクロサービスごとに実装されていたfakeclockのシステムを使っていたのですが、それを置き換える形になるので、テスト自体にも影響があります。 書き換えたコード自体もテスト対象だし、テスト自体も書き換えないといけない。単純な無影響確認とも言い切れません。 @vvakame:品質担保は我々金融の決済領域なので、非常に神経質に行っています。QAは工数が厳しそうですよね。とはいえ、fakeclock serviceの導入はQAの効率化のためにもQAのエンジニアの方々からも切望されているので、何とか前に進まないといけませんね。 @tanaka0325:ここで、関連の質問をいただきました。「進まなかった理由はありますか」という質問です。今話した部分で、金融だからQAは大変なのと、テスト自体も直すという作業があること、そしてこれとは別に今まで通りの施策は進んでいて、その作業もありつつ、プラスアルファでこのような作業を行うという点で、バランスをとっていくことが難しかったからです。 @hiraku:導入に関しても、プルリクエストで一気に書き換えてリリースするのではなく、もう少しプロジェクト的に考えて、少しずつ分割してやっていかないと厳しいということで、作戦を変更して進めています。 @vvakame:ではマイクロサービスとE2Eテストについてはいかがですか?E2Eテストがしっかり揃っているとやりやすいけど、でもE2Eをいっぱい書くためには、便利なfake clock serviceが必要という問題があります。 @tanaka0325:厳しいですね。今もE2Eテストはあって、fake clock serviceの前はどうやっていたかというと、環境が人数ごとにあったように、E2E用にもありました。とあるサービスによっては、fake clockで時刻をいじるためだけに複製した環境が何十個もあったりしました。 環境をメンテナンスするだけでも大変だったので、E2E観点でもマイクロサービスは早く移行したいという気はしています。 @hiraku:逆にE2Eテストがコードベースで仕上がっているマイクロサービス機能群に関しては、あちこちでSetNowを叩きまくらないといけない件に関しても、そんなに困りません。そのため、優先度を下げてもいいという温度感になっています。 手動テストがメインな部分はE2Eテストが欲しいですが、手動テストがメインだと、こういう置き換えがとてもしづらいというデッドロックがあります。 @vvakame:続いて、グループ全体から見た位置づけについて話しますか。 グループ全体から見た位置づけとしては、メルペイの特にCredit Design Teamが非常に複雑なので、Credit Design Teamの要求を満たせるサービスであれば、グループ全体の要求を満たせる可能性が高いというコメントをもらったことがあって、納得しました。 ただこれが全体に広がるかは、Credit Design Teamのユースケースをケーススタディとしてちゃんと使えるかが関わってきます。 @tanaka0325:あと払いは、絶対に時刻が関係するので、Credit Design Teamが一番使うと思います。メルペイ自体の機能が増えてきたり、複雑度が増してきて、他のマイクロサービスがCredit Design Teamに依存したり、時刻に関連するマイクロサービスができたりしていて、今後はより他のチームでも必要性が増してくると思います。なるべく早くCredit Design Teamで成功事例を作って、他のチームでも使えるように広げていきたいです。 @vvakame:最後に、質問をいただいています。「修正したNowから徐々に時間が経過してほしいという、相対的な時間指定ができるようになりますか?」ということです。これは今回の実装からできるようになりました。 続いて、「実装優先を決断するのはどなたですか?」という質問をいただいています。Credit Design Teamはどういう感じですか。 @hiraku:意見を出すという意味では、みんな関わってはいますよね。 @tanaka0325:優先度づけのときに事前にエンジニアリングのヘッドや、PMのヘッドなど、いろいろな人が集まって各状況を整理してみんなで優先度を決めるので、特段誰かが決めるというよりは、割とみんなで決めることが多いです。 @vvakame:fakeclock serviceのような新しい解決手段を提案したり考えたりするのは、メルカリグループの場合はボトムアップ的に行っています。そういう意味ではこういった新サービスを作ろうというのは我々で決断して勝手に実行します。 我々はこういったことを一緒にやってくださる仲間の募集しています。「新しいソリューションを考えたい」という方がいらっしゃったら、ぜひ応募してください。 最後の締めとして、新しくこういった時刻系の実装をしなければいけない人たちに対して一言ずつアドバイスをいただけますか。 @tanaka0325:DBのカラムでcreated_atなどのシステム時刻が自動で入るものがよくあると思うのですが、あれはシステム時刻のためのもので、ロジックにあれを使うと、時刻を操作したいときに操作できなくなってしまうんです。 その場合は専用のカラムを準備して、アプリケーション側で操作できるようにしておくことは大事だと思います。 @hiraku:後から導入しようとするとすごい大変だぞということを伝えたいです。早い段階で、時刻操作の問題が今後どんどん厳しくなっていくから、早めに解決手段をとりましょうという判断ができていたら良かったと思います。 @vvakame:僕からの皆さんのアドバイスとしては、マイクロサービスをやらないと組織がどうしようもならなくなるまでは、モノレポや一つのリポジトリでやった方が時刻操作も楽なので、マイクロサービスにしないで良いのであればマイクロサービスにするのはやめましょうということです。 以上、fakeclock serviceについての発表とパネルディスカッションでした。ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 フロントエンドチームの技術課題評価システム改善の取り組み 」の書き起こしです。 @tokuda109:それでは、「フロントエンドチームの技術課題評価システム改善の取り組み」というタイトルで発表します。 まずは自己紹介です。@tokuda109といいます。2019年にフロントエンドエンジニアとしてメルペイに入社し、さまざまなプロダクト開発を担当してきました。プロダクト開発以外では、技術評価システムの改善などに携わっています。 今日お話しする内容は、四つのセクションにわかれます。一つ目が、フロントエンドチームの採用プロセスについて。二つ目が、技術課題。採用プロセスの一つが、技術課題です。三つ目が本発表のメイントピックで、技術課題を評価するときに使う評価チェックシートについて紹介します。そして最後がまとめです。 これが基本的な採用プロセスで、全部でこれだけのステップがあります。技術課題は、採用プロセスの2番目のステップで、候補者に簡単なアプリケーションの実装をお願いしています。 候補者が課題を提出すると、評価者が2人アサインされ、評価システムに沿って評価をしていきます。 技術課題は、上記の通りです。このスライドで記載されている内容は、公平性を担保するために、採用ページに記載されているものから引用しています。 まず、候補者に求められる必須条件として、HTMLとCSSを用いた堅牢なUIを実装できること。次に、JavaScriptに関する知識があり、UIライブラリやフレームワークを用いた開発経験があること。これらを満たしているかを、技術課題を通して判断しています。 次に課題内容として、「Web技術全般に関する高度な知識と技術力で、プロダクト開発に貢献できるかどうか」を判断するために、簡単なアプリケーションの実装をお願いしています。 評価では、独自の評価システムを使っています。技術力を評価するための方法は、外部サービスとして使えるものからフレームワークとして提供されているものなど、さまざまなものがありますが、私たちは独自の評価システムを使っています。 独自路線になった経緯はわかっていませんが、単純な点数だけを評価しているわけではないからだと個人的には考えています。基本は技術力を評価しますが、ソースコードから読み取れる候補者のカルチャーフィットやチームにジョインした後、バリューを発揮して業務できる方かを総合的に見ており、技術課題の点数は評価の一部でしかありません。 ただ、独自のものを使ってうまく機能させるためには、やってみると意外と大変で、さまざまな問題が発生しました。技術課題の評価をするにあたって課題となったことが二つあります。 一つ目が、提出物の評価に時間がかかること。評価システムが体系化していないことで、時間が思ったよりかかったり、細かく見すぎていて評価に時間がかかるということが多々発生していました。 次に、一定の評価基準で評価することができないこと。評価者によって重要視する項目が異なることで評価基準が一定にならず、評価が割れることが多々発生しました。 これらの解決になったのが、ペア評価と評価チェックシートの二つです。ペア評価は二人で画面共有しながら、一緒に技術課題を評価する方法です。一人がアプリケーションを起動し、画面共有しながらペアとの評価を主導し、ペアは議論した内容をメモするという役割分担です。 評価チェックシートは、確認すべき評価観点をリスト化したものです。それを基に提出された課題を評価すれば、一時間で評価が完了する仕組みになっています。評価者によって重要視する評価観点が異なることを防ぎ、個人の恣意的な評価を平準化します。また、評価漏れを防ぐ目的もあります。 評価チェックシートの内容について、細かく見ていきます。この図は今回のイベント用に作成したもので、実際の評価観点とは中身が異なりますが、基本的なフォーマットは同様です。 このチェックシートは、技術課題の評価と総合的な判断の二つのセクションで構成されます。まず技術課題の評価ですが、一つの行が一つの評価観点になっていて、現時点で全部で40個ほどの評価観点があります。 一番左に評価観点があり、「◯◯を使った品質の高いコードになっているか」「◯◯対応ができているか」のような大きなくくりとして、何を評価するのかが分類分けされています。 次に、B列の採点方法ですが、評価ポイントと採点の二つが記載されています。評価ポイントは、「どのような箇所を確認するのか」「どういう実装していると評価するのか」などの確認ポイントが記載されています。それをもとに確認し、記載されている採点基準に当てはまる点数を元に、スコアを付けます。 最後に、D列の採点時のメモですが、ここはペア評価時にペアの方がメモをしていくためのスペースです。これを上から順番に行い、全て評価が終わると点数が算出されます。その採点をもとに、総合的な判断のセクションに進みます。 ここでは、レジュメや採点、作業内容をもとに、「候補者がカルチャーフィットするのか」「チームにジョインしたときに、バリューを発揮して業務をできる方なのか」を評価者同士で議論し、次のインタビューに進めるかどうかを判断しています。 次に、評価チェックシートの変遷です。評価チェックシートは最初からあのフォーマットになっていたわけではなく、何回もアップデートを繰り返したことで、現在のフォーマットに落ち着きました。 それまでに三つのフェーズがあり、フェーズ1では評価観点が体系化されておらず、共通評価の共通認識ができていないところからスタートしました。評価に時間がかかる問題や、評価基準が安定しない問題が発生したのも、このフェーズです。 評価観点を体系化し始めて共通認識を揃え始めたのが、フェーズ2です。ここで評価チェックシートの原型ができあがりましたが、改善点はたくさんありました。 具体的には「〇〇が設定されているかどうか」のような単なるチェックリストのようになっており、実装内容を評価する評価観点はまだほとんどありませんでした。 次に、定期的にミーティングをすることで、共通認識を評価観点に落とし込めるようになったのが、フェーズ3です。2年ほど定期的にこれを続け、最近になってこのフェーズに到達できました。 まだ改善点はたくさんありますが、ただのチェックリストから実装内容を評価することができるところまで改善できたことは、大きな進歩だと思います。 次に、例として「評価観点:TypeScript」を紹介します。 最初は、TypeScriptで実装されているかどうかという評価観点で、これでは「TypeScriptを使っているからいいのか」「TypeScriptを使って、型安全な実装できていればいいのか」がわからず、評価者によってばらつきが出ます。体系化を始めたときに一番議論が紛糾したのが、この観点です。 まず出されたのは、「TypeScriptで実装されていない時点で、全体的に品質の高いコードではない」と言えるという意見。次に、「TypeScriptで実装されていた方がいいが、テストを書いたりうまく設計することで補い、他の観点も踏まえて品質が高いかどうかを判断すればいいのではないか」という意見でした。 この二つの意見を「『TypeScriptを使っていない』は、一つの評価観点が全体に与える影響が大きすぎる」「『別の観点で補えているか』は、別の観点を独自に当てはめて評価している」と整理しました。 元々がTypeScriptで実装されているかという評価観点でしたが、TypeScriptを使うことで、何を解決したいのかを評価観点としてチェックできるように、チェックシートを更新しました。 TypeScriptで解決したい問題として「型があることで、アプリケーション内の処理で型安全を担保できる」という点があると思います。APIデータやイベントハンドリングなどのアプリケーションの外側から渡されるデータを適切に型付けしない場合、どのようなデータ型も許容してしまいます。 最終的にはそのような点を評価観点として記載し、APIデータやイベントハンドリングなどの箇所を重点的に見るように、評価項目として記載しました。 評価チェックシートを何年もかけて更新してきました。最初はただのチェックリストでしたが、徐々に改善され、メルペイ・メルコインで活躍できる方であると判断するための仕組みとして機能するものになりました。 もし、メルペイ・メルコインの開発に興味があれば、採用ページを見てみてください。 Software Engineer, Frontend / ソフトウェアエンジニア (Frontend) – Merpay Software Engineer, Frontend / ソフトウェアエンジニア (Frontend) – Mercoin 以上です。ご清聴ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 記事は、「 発行枚数100万枚を支えたメルカードGrowth施策の裏側 」の書き起こしです。 @kazuya:「発行枚数100万枚を支えたメルカードGrowth施策の裏側」のセッションを開始します。セッションは3名の異なる職種のメンバーでお送りします。よろしくお願いいたします。 @kazuya:私はKawashimaと言いまして、メルペイでPMをしています。 @ksoichiro:メルペイバックエンドエンジニアのKashimaと申します。2019年にメルペイに入社し、Growth Platformチームでプロダクト開発を担当しています。よろしくお願いします。 @mikael:iOS Tech LeadのMikaelです。2019年に入社しました。今までメルカリアプリの支払いタブなどを開発しました。よろしくお願いします。 @kazuya:私がメルカードプロジェクトのPM、@ksoichiroさんがバックエンド、@mikaelさんがiOSエンジニアです。今回は3名でお送りします。 @kazuya:セッションのタイトルにもあった「メルカード」について、私から説明します。 メルカリアプリに「メルペイ」というスマホ決済サービスが追加され、メルペイの中には、メルペイスマート払いという、あと払いのスマホ決済サービスを展開していました。 2022年11月に、「メルカード」というメルカリが発行するクレジットカードをリリースしました。メルカードはメルペイスマート払いと同じ与信を使っているので、すでにあったメルペイあと払いを拡張したサービスとなります。 メルカードは、2022年発行で、比較的後発であるからこそ、メルカリとしてどういったクレジットカードがいいのかを、いろいろなメンバーと議論してサービスを設計しました。その中での大きな特徴として、「メルカリでのお買い物においてお得」ということは非常に重要です。 メルカリ内でアクションをとると、お買い物がお得になっていき、最大4%還元までレベルが上がっていく設計です。 続いて、メルカードの特徴について説明します。 Point1として、本人確認をすでに終えている方が1000万人以上いらっしゃいます。彼らはとても簡単にアプリ上で申し込みが完了します。 Point2としては、アプリ前提のクレジットカード体験を、ゼロから設計しています。例えば、スマホ決済と同じく、決済後すぐに通知が飛ぶ設計になっています。 クレジットカードの場合、アプリはあるけど、明細に反映されるのが数日後になり、利用状況がタイムリーにわからないこともあると思います。その点、メルカードはクレジットカード・iD決済・コード決済のあらゆる決済ですぐに通知が来て確認できるというシームレスな体験を実現しています。 Point3は、使った金額をいつでも清算できることです。例えば3800円のお買い物をしたときに、翌月末まで待つのではなく、今の時点で支払いたいときは支払いを済ませて管理をしやすくすることが可能です。 Point4は、AI与信です。クレジットカードを日本で作ろうとすると、年齢、職業、会社の勤続年数、年収などの情報が必要だと思うんですけれどもメルカリやメルペイの利用実績に基づいてAI与信をすることで、そういった属性情報の入力が大幅に省略できます。 11月の発表後、約半年で100万発行を達成しました。これは国内のクレジットカードとしても、トップに入る規模発行数になっておりますので、その点に関してはうまくいっているかなと思います。 参考記事: 「メルカード」、提供開始から約半年で発行枚数100万枚突破 ここで、メルカリ/メルペイのカルチャーについて紹介します。メルカリでは、社内外の方に対して多様性を大事にしたいと思っており、メルカードという言葉にもその意味を込めています。 また、リサイクルPVCというエコな素材を用いていたり、色が変化するメルカリのHologram logoを載せたりと、ダイバーシティ&インクルージョンという考え方でデザインされています。 ここで、本題である「グロースをどのようにエンジニア含めて実現したか」という話に入ります。 一番大事なのは、メルカリが発行したクレジットカードなので、メルカリを使う中で自然と「メルカードを作ろう」と思っていただける体験です。 例えばホーム画面やチェックアウトのフロー、商品詳細で訴求をすると言ったように、メルカリの機能に対してもインテグレーションしました。キャンペーンも実施し、マーケティングとプロダクトが連携しながら進めてきました。 参照 メルペイ、「メルカリ」や「メルカード」などの利用でおトクな特典を受けられる「メルカリご利用特典」を提供 メルペイ、「メルカード」の新規入会で「メルカリ」でのお買い物が最大半額になるご利用特典と新TVCMが6月1日より開始 @ksoichiro:私からはグロース施策における体制について説明します。 まず一つ目に、必要に応じてメルカリとメルペイを横断して体制を組んでいます。 続いて、プログラム体制です。メルペイ内部の開発体制は、プログラム体制というバーチャルな組織体制が採用されています。一つ一つの箱がプログラムと呼ばれており、主要なカスタマージャーニーの単位で分けられたJourneyの他に、Foundation、Enablingなどの組織に分けられています。これとは別に通常の組織図に基づいたレポートラインもありますが、そもそもPM組織・エンジニアリング組織とわかれているので、実際の開発はこれらが一緒になって、チームやプロジェクト体制が組まれていきます。 プログラム体制におけるプログラムには複数のチームが含まれ、開発スケジュールが競合するとリソースの調整が発生してしまいますが、プログラム体制においてはまず、プログラムの中で調整するのが基本的な手法です。 メルカードのグロース施策に特に関わりが深いのは、Loyality Program / Payment journeyとGrowth Platform。この場にいる3名においても、2人はLoyality Program / Payment journeyに属していて、私はGrowth Platform所属です。 Loyality Program / Payment journeyには、メルカードそのものや各種決済における体験後は使っており、Growth Platformはグロース施策に必要となるポイント還元やキャンペーン訴求のための仕組み、メルカードにおける還元率の管理などを扱っています。 マーケティングの施策では、OKRをもとにそれぞれ締め切りが設定されてきます。基本的にはそれに基づいて開発スケジュールを計画・進行します。プロダクト開発のメンバーが複数の施策に関わっていくこともありますので、スケジュールが競合し、技術リソースの調整が必要になることもありますね。 これらに関わるメンバーは、特定の職種だけでなく、PMやマーケター、デザイナーなど、総動員で関わってきます。以上が推進体制の基本的な説明です。 @kazuya:メルカリ/メルペイと会社をまたいだり、メルペイの中でもいろいろなプログラム体制があったり、マーケットも協業してしていたりするという説明でした。 @mikael:メルカード関連でよく使ったツールを紹介します。JIRAとBrazeです。 JIRAは、私たちの会社で使用しているプロジェクト管理ツールです。私たちのチームでは、全員が全ての機能を見られるように使用しています。 もし、プロジェクトマネージャーが新しい機能を開始したい場合は、EPICを作成します。さらに、新しい機能における各メンバーごとのタスクを作成します。これにより、バックエンドエンジニア、モバイルクライアントエンジニア、そしてデザイナーも、現在の機能の状態を追跡することが可能になります。 JIRAのようなツールでは、多くの自由があります。私たちがこれらのツールを使用する際のアプローチは、使いやすく読みやすいようにシンプルに保つことです。 例えば、サブタスクの作成は避けます。また、EPICが大き過ぎるか、時間がかかる場合は機能を複数のEPICに分割して、誰にとっても管理しやすくします。 Brazeは、お客さまに表示する内容を変更できるようにするためのツールです。私たちの場合、Brazeはマーケティングをキャンペーンのエディタとして活用しています。マーケティング担当者やプロジェクトマネージャーがWebポータルを通じて、複雑なキャンペーンを簡単に作成・編集できます。 AndroidやiOSでこのようなキャンペーンを実施するためには、バックエンドエンジニア、デザイナー、PMと協力して、私たちのニーズに答えるものを作成する必要があります。最新のキャンペーンでは、商品価格をベースにしてクーポンなどの特別のオファーをお客さまに提案することができます。 SwiftUIで作成されたUIのセットアップ等をカスタムBraze枠によって、マーケティング担当者は情報の提示方法や計算方法を選択できます。例えば500円引きという固定された割引だけでなく、ある限度額までの商品価格に対するパーセンテージを提示することもできます。 @kazuya:マーケターが、キャンペーン中であっても、訴求をA/B Testできたり、様々な運用しやすくするためにもこういったツールを活用しています。 ツールを活用していくわけなんですけれども、Kashimaさんからは、バックエンドチームとして、さまざまなタスクがありながらキャンペーンなど、グロースを支えるための機能を作っていくというところの話をしていただこうかなと思います。 @ksoichiro:ここからは、バックエンド開発について説明します。グロースに関わる開発は、いろいろなものが並行して動いています。私はGrowth Platform チームの一部でTech Leadを務めていますが、それでも全部に関われている訳ではありません。そのため、この場では私が関わったものについて、具体的な事例をいくつかお伝えします。 一つ目はキャンペーンのための開発で、特定のキャンペーンスキームに合わせて対応するときの開発です。二つ目はプラットフォームの開発で、これは私が所属するGrowth Platformのメインの領域です。ポイント還元やキャンペーン訴求などのグロース施策を実行する上で必須になるような仕組みを作るというところです。 一つ目のキャンペーンのための開発も、何度も使うことが想定されているものであれば仕組み化が求められます。 最後はプロダクトのコア体験を作る開発です。例えば還元率がどのように上下するのかを決める仕組みは、細かい仕様が広い範囲の体験に影響しますし、キャンペーンなどの施策がなくても、日々改善して提供していく基本的な部分なので、別の枠として整理しました。 その上で自分が担当してきたことについて、関係するPMの方とどう関わってきたかにも触れつつ振り返ります。 スライドに載せているのは、私が関わった開発の事例の一部です。 一つ目のお得枠は、メルカリアプリの支払いタブの中にあるキャンペーン訴求を表示するエリアのことです。この部分にマーケターや、PMが運用できるような仕組みというのを用意しています。この前身となる類似の仕組みを数年にわたって運用してきてたので、私も担当のPMも知見を持っていて、その上で今後の利用予定をある程度見据えながら設計して準備しました。 二つ目は、入会特典の1000ポイントの付与です。メルカードを作ると1000ポイントもらえるという施策がリリースの当初からあります。 「ポイント付与精度の向上」は当たり前じゃないかと思われるかもしれません。ですが、このタイミングで新しい仕組みを使うという取り組みがあって、そのためにエンジニアリング的な努力が必要になりました。これはその後の大型キャンペーンの運用にも生きる取り組みだったのかなと思っています。 三つ目の特典ページはメルカードのために作られた還元率や特典を確認できるページのことです。私はその開発の初期から関わっていて、ここに関わるPMは1人ではなく、それぞれの施策担当する方と仕様検討しながら進めてきました。初期から数えるとおそらく7、8人は関わっています。 最後の還元率の管理についてですが、メルカードのリリースに向けて開発した新しい取り組みなので、トラブルもありました。メルカードを使い始めたお客さまにおいては、還元率がサクサク上がる体験をおそらくしていただけているんじゃないかなと思います。しかし、逆に意図せず還元率が落ちてしまう悪い体験も起きていて、それを防ぐための対策をとってきました。 こういうときに実際のデータを見ながら、発生しているパターンや件数を細かく分析するのですが、中には判断が難しいものもあり、そのときはPMと相談してステークホルダに説明することが必要になりました。 以上が私が関わった開発の一部です。振り返ってみると、リリース前から仕込んできたものの役割が大きいなと思います。計画的に積み重ねてきた土台があるからこそ、低コストで運用ができて、結果として新しい施策にも手を出せるという状況になっているのかなと思います。これはGrowth Platformチームの成果とも言えるのかなと思っています。 それからPMとの連携というところに関しては、バックエンドエンジニアは基本的には担当するマイクロサービスに対し長く関わっているので、該当の機能についてPMと同じかそれよりも詳しいこともよくあります。 それを踏まえてなるべくPMの目線を理解しつつ、一緒に自分事として関わっていくのか大事だと思います。 それから、体制の説明でも触れましたが、結構な数の施策が並行して進んだり、期間が短くても仕様を変えなければならなかったりします。 その中で変更のサイクルを早く回すには、エンジニアが技術面だけでなく体験面にも関心を持って仕様に口を出したり、インシデントが起きないようにエッジケースも注意しながらコミュニケーションを取ったり、ボールが落ちないようにプロジェクトマネジメントにも積極的に関わったりするのも重要だったんじゃないかなと思っています。 @kazuya:最後に、質問しつつ3人で話していきたいなと思います。 まず、グロース施策を行うときは、数字を達成するために急ぎのタスクが発生することもあります。そのときに、エンジニアとしては他のことをしていることもあると思うんですけれども、どのように受け止めていますか。 @mikael:そうですね。クライアントエンジニアから見ると、バグのfixやリファクタリングをしたいです。一方で、OKRを考えなければならないので、OKR関係のタスクを高いプライオリティにしなければならないと思います。バグのfixやリファクタリングのタスクがあれば、別のOKRを作って対応します。 @kazuya:会社全体の目標(カンパニーOKR)を掲げて皆さんが日々コミュニケーションを取っていることが一つの要因になっているのかなと思いました。 @ksoichiro:エンジニアリングの目標も並列にありますが、ビジネス目標は優先しなければならないので、エンジニアリングの目標は後回しにしなきゃいけないこともありますね。 エンジニアリングのOKRを一緒に立てて並べて、どっちを優先するみたいなのをクォーターごとに決めることで、うまく成り立っています。 @kazuya:続いて、自分たちがやりたいことがある中でもビジネス目標があるという状況で、どのようにモチベーションを維持していますか? @mikael:開発者として開発をする際にテクニカルチャレンジがあるときに、楽しい気持ちになるので、それを大切にしています。 例えばメルカード関係のフィーチャーを作るときに、お客さまを考えながら作りますね。PMが全て決めることでなく、エンジニアも考えないといけないことがたくさんあるので、そこでモチベーションが上がります。 @ksoichiro:一つ目の話にも関係しますが、クォーターの最初で、計画が全部決まっていることはなくて、途中で変わることもあるので、やりたいことができないときもあります。でも、私自身いろいろな施策に関わり、プロダクトの成長に直接的に貢献できているということ自体がモチベーションにはなっています。 @kazuya:以上、こちらのセッションは、3人でお送りいたしました。皆さん、ご清聴ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 メルカードの常時ポイント還元開発の裏側 」の書き起こしです。 @keiitaj:こんにちは、メルペイバックエンドエンジニアのKeita Suzukiと申します。「メルカードの常時ポイント還元開発の裏側」というタイトルで発表します。 最初に、自己紹介です。2015年からOrigamiPayというサービスを提供していた株式会社Origamiに入社し、スマホのQRコード決済や銀行連携の開発に携わりました。 2020年、Origamiのメルカリグループ参画に伴いメルペイに入社し、現在はメルペイのGrowthに向けたプロダクト開発を行っています。 今回のアジェンダはこちらです。 常時ポイント還元とは、メルカリが提供しているメルカードというクレジットカードでお買い物すると、ご利用金額に応じて最大4%のポイントが還元される施策です。 現状では、メルカリでのお買い物は1〜4%の還元率で、コンビニやスーパーマーケットなど、メルカリ以外の店での還元率は1%で固定となっています。還元率は、お客さまの取引実績に応じて変動します。 この施策の主な機能はポイント還元と付与予定ポイントの表示です。メルカリでお買い物した翌月の請求に対して、清算したタイミングでポイントを即時還元しています。 また、メルカリの商品詳細画面や決済時メールやプッシュ通知、決済履歴に、付与予定ポイントを表示しています。 ポイント還元は、アプリから見えないところで非同期で処理されます。それに対して付与予定ポイントの表示は、アプリから見えるところにリアルタイムで同期的に行われます。 そのため、Pub/SubとAPIの開発が行われました。 同期か非同期かの大きな違いですが、ポイント計算や還元対象判定など共通のロジックは多いです。お客さまの還元率を決定した上で、ご利用金額に応じてポイントを計算します。 還元対象かどうかの判定も行っています。お客さまのカードステータス判定や請求の決済単位で、対象加盟店かどうかの判定を行います。例えば電子マネーのチャージなど、一部対象外となる加盟店もあります。 これらの常時ポイント還元の前提を踏まえ、開発の話に移ります。 まずシステム構成の話をします。関連マイクロサービスはこちらです。 本セッションの主役のサービスはSantaです。キャンペーンの管理ポイント関連がサービスの責務となります。常時ポイント還元の関連サービスは主にこの三つです(他にもありますが、割愛します)。 関連サービスの責務はそれぞれこのような役割を持っています。 loyaltyサービスのステージ管理について、お客さまのメルカリで売る・買う・支払うのアクションによってステージが上がるので、そのステージの管理をここで行っています。 SantaやこれらのサービスはgRPCやPub/Subを通じて情報の受け渡しを行います。各マイクロサービスにはオーナーシップを持つチームが存在しており、私が所属するGrowth PlatformのチームではSantaとloyaltyの開発と運用を担当しています。 ポイント関連のシステム構成とプロセスについて説明します。 メルカードの清算が完了すると、defpayというサービスからPub/Subメッセージが発行されます。このPub/Subメッセージをサブスクライブすることが処理の起点となっています。 Pub/Subメッセージから清算済みの請求情報を取得し、ポイント還元対象かを判定します。対象判定のため、各マイクロサービスからメルカードのステータスや決済加盟店の情報を、gRPC APIを通じて取得しています。 また、お客さまのステージを取得し、変動する還元率を決定し付けた上で、ポイントの計算を行っています。 そして、最後にポイントの付与を実行しています。これがポイント還元の一連の流れです。 次に、付与予定ポイント表示のシステム構成とプロセスについて説明します。付与予定ポイントは、メルカリの商品詳細画面やプッシュ通知、決済履歴で表示されているのですが、今回は時間の都合上、メルカリの商品詳細画面のケースでのみ説明します。 APIによる処理で同期的にアプリに付与予定ポイントを返す必要があるため、gRPCサーバーを立てています。 アプリからgateway-api、item-detailという商品詳細に責任を持つマイクロサービスを通じて、商品金額が渡ってきます。 ポイント還元のプロセスと同様、還元対象と判定するために、メルカードのステータスの情報をgRPCAPIを通じて取得し、またお客さまのステージを取得し、還元率を決定づけた上でポイントの計算を行っています。 最後にレスポンスとしてポイントを返却し、アプリ上で表示できるようにしています。 次に、Santaサービスのバックエンド開発にフォーカスを当てて説明します。 Santaサービスは、Cloud Spannerのスキーマと接続し、キャンペーンやポイント付与のデータを持てるようにしています。メルカードの常時ポイント還元については、Campaignsテーブルの中でデータ定義されています。 キャンペーンによってポイント還元率は変動するので、CampaignStageRatesという親子関係のテーブルを作ることで、一つのキャンペーンに複数の還元率を定義することを可能にしています。 loyaltyサービスから取得したお客さまのステージの値によって還元率を決定しています。 データのイメージはこのような形になっています。メルカリでの購入の場合、還元率が0.1%ごとに変動するようレコード定義しています。 フィルターにはJSON形式の文字列が格納され、セットしたフィルターの内容に応じて対象判定が行われます。 キャンペーンによってフィルターは変わりますが、常時ポイント還元ではメルカードのステータス判定やメルカリ外決済、対象外加盟店を判定しています。 以降は、開発で工夫したところをいくつかピックアップして発表できればと思います。Loyaltyサービスは、この機能で新規ローンチしたマイクロサービスだったのですが、ポイントの還元率をLoyaltyサービスとSantaサービスのどちらで持つべきかという議論がありました。 現状ではLoyaltyをSantaの還元率管理のために使用していますが、今後、Loyaltyを他のマイクロサービスに展開していく将来性を考え、Loyaltyにはあくまでお客さまのステージの管理のみを責務とし、ステージに合わせた還元率など、お客さまへの対応は各マイクロサービスに委ねる方針をとりました。 次に、ポイント還元の付与予定ポイント非表示のユースケースについてです。還元対象判定や還元率に応じたポイント計算など、振る舞いはほぼ共通しています。しかし、非同期処理と同期処理という大きな違いがあり、求められるSLOは異なります。 そのうちの一つの指標がLatencyです。メルカリの商品詳細画面は何千RPSというリクエストが流れており、売り上げに対するインパクトも大きいため、Latencyが高まることはサービスにとってとても致命的です。そのため、ポイント計算のCalculatorは、還元上限を考慮するものとしないものに分けています。 還元上限を考慮するものは過去の付与実績をクエリした上でポイントを計算するため、多少負荷が高く、非同期処理のみで使用するようにしています。 また、決済手段によって加盟店IDが異なる場合があり、単一の加盟店IDで判定不可能なことがあります。Paymentでは、通常よく起こり得る問題かと思います。 今回のケースでは、メルカリ上のApple Payが当てはまります。この決済手段の場合、他社パートナーさまが加盟店管理を行っているため、加盟店IDがメルカリのものとは異なり、対応を見逃すとメルカリ以外で発生した決済とみなされてしまいます。 メルカリとメルカリ以外の買い物での還元率を変えているので、メルカリ上のApple Payは、メルカリで発生した決済であることを特定しなければなりません。加盟店管理を行う他社パートナーさまから決済加盟店の情報を連携いただき、特定することで、この問題を解決しています。 最後に、より開発現場の空気感を知っていただきたいので、現状どのようなことをしているかと、今後の展望について話せればと思います。 最近では、メルカードの普及促進に向けたキャンペーンの開催を行っています。 毎月8日にお買い物をするとお得になるキャンペーンやメルカードの入会特典などです。これらは先ほど発表した内容と同様のスキームで、SpannerのCampaignsテーブルにキャンペーンのレコードを追加することによって実現しています。 還元上限に合わせて対象判定のフィルターを変える、そのフィルターの追加開発が発生することもあります。 日々運用改善も行っています。Loyaltyでは、お客さま体験をより良くするためのステージ遷移ロジックの改善や、Santaではマニュアルオペレーションが多い引当金連携の自動化、加盟店マスターと連携して決済手段ごとに異なる加盟店IDをマスター判定する取り組みを行っています。 今後の展望として、メルペイ単独ではなくメルカリグループ全体のプロダクトと組織を横断して連携を強化する方針があり、グループのGrowth基盤であるエンゲージメントプラットフォーム(EGP)を拡張し、今日発表した内容も含めて、そちらに統合する計画を進めています。 EGPに関しては、@Rupeshのセッション「拡張性を備えたソフトウェア設計」をご覧ください。 【書き起こし】拡張性を備えたソフトウェア設計 – Rupesh Agrawal【Merpay & Mercoin Tech Fest 2023】 発表は以上です。ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 メルペイ加盟店売上精算の仕組み 」の書き起こしです。 @shiv3:本セッションでは、「メルペイ加盟店売上精算の仕組み」についてお話しします。 最初に自己紹介です。Shibazakiと申します。社内ではSlackネームである@shiv3と呼ばれています。現在、メルペイでソフトウェアエンジニアをしており、2021年9月に入社し、加盟店精算領域の開発を担当しています。現在はそこのチームでテックリードをしています。 アジェンダは、この通りです。主に加盟店精算についての概要と、加盟店精算の仕組みについてお話しします。 加盟店精算は、コード決済・ネット決済のメルペイ加盟店さまが、お客さまから決済を受けたときに、それを月1回および月2回の頻度で入金するために金額を集計して、入金日に合わせて事前に入金指示を行います。 加盟店精算における精算は、クリアリングとセトルメントの二つのステップで行われます。クリアリングは、加盟店さまの未精算の売上や手数料など、過不足なく正確に計算し、最終売上を確定させる処理になります。 セトルメントは、確定した売り上げを加盟店さまに実際に振り込み、もし返金などで、当月中の売上金の赤残が発生する場合、その金額を確定させて、加盟店さまへ請求するというフローです。 メルペイではお客さまには個人と加盟店さまの2種類があり、それぞれのお財布・決済アカウントを管理しています。ここでは加盟店精算で用いる加盟店さまのお財布・決済アカウントのみを紹介します。 まず内部のアカウントとしては、決済をするたびに売上がたまる未精算売上口座および、精算のプロセスが完了した後で確定された売上が入る精算済み売上口座があります。また、売り上げが債権として精算される場合は、精算済み債権口座に入ります。 マイクロサービス全体での精算処理がこのようなフローで行われます。加盟店精算のリクエストが決済サービスに来ると、決済処理を担当するサービスが決済処理を行い、精算サービスは決済サービスからそのイベントを受け取り、イベントをもとにリアルタイムで手数料を発生させ、その後月1回・2回で最終的な売上を確定する精算サイクルです。 次に各ステップを詳しく見ていきます。 精算サービスは、決済イベントを受け取るとリアルタイムで決済サービスにメルペイの決済手数料をリクエストします。 その後、精算サービスは、手数料の決済時イベントも決済サービスから受け取り、清算対象にします。ここで、手数料自体の決済に対しては、再度手数料をかけることしないようにしています。加えて精算サービスは、どの決済に対して手数料を発行を行うか、もしくは各加盟店のためにどういう加盟店やどういう決済に手数料をどうかけるかという設定も管理しています。 次に、決済イベントのデータをもとに、精算用のデータを作成・保存するステップに移ります。 加盟店精算用のデータは、加盟店さまから、および加盟店さまへを対象に、お金の動きをToとFromで管理しています。お客さまから加盟店さまに決済として売上があった場合は、Toの方に加盟店さまのIDを設定し、決済手数料では、Toの方にメルペイ、Fromの方に加盟店さまのIDを設定してします。返金および手数料の返金に関しては、その逆のフローです。 次に、保存した精算用のデータが正しく保存されているかを、他のマイクロサービスと突合を行うリコンサイルという処理を行います。先ほどのステップで保存した取引データを1件ずつ会計帳簿サービスのデータと突合し、突合済みのデータとして更新します。 その後にリコンサイルの取引データを対象に、日時の集計を行います。ここでは、リコンサイルの結果を基に、Xという加盟店が10月3日の売り上げとして300円になったという日次精算のデータを保存しています。 また、集計時に入金集約というパターンが存在します。複数の加盟店の売り上げを一つの振り込み先に集約させるフローで、集計元の加盟店さまの売り上げは、入金先の加盟店さまに対しての一時精算データとして集計します。ここでは子パートナーa・b・cの100円ずつの精算を、親パートナーAの精算金額として保存しています。 その後、月1回・2回の精算サイクルで入金額の集計を行います。ここでは、月1回の精算サイクルで集計する場合を紹介します。 10月1日から10月31日までの日次精算の1ヶ月分のデータを合計し、10月分の売上データとして保存します。ここではSalesSummariesという名前のデータとして保存しています。その後、データを用いて入金額の集計を行います。ここではPayoutsというテーブルに集金額を入れています。 ここで前回の入金が失敗した場合や、繰越設定があった場合は、入金額に前回の分を合算します。 精算サイクルごとに精算データを作成した後は、売上金のクリアリングを行います。未精算売上口座から精算済み売上口座にお金を動かす際、加盟店売上金クリアリングリクエストを決済サービスにリクエストし、精算済みの口座にお金の移動を行います。 ここでは、未精算売上口座にあった5000円を、精算済み売上口座に加算しました。 返金などで当月中の精算サイクル締め期間の売り上げがマイナスになった場合は、赤残と呼んでいます。 精算済み売上金口座にクリアリングを行いましたが、赤残が発生した場合は清算済み債権口座にお金を動かすためのリクエストを発行します。例えば売上金がマイナス2000円になってしまった場合、その2000円は精算済み債権口座に移動させます。 クリアリングのステップの最後に、債権口座と売上口座の相殺処理を行います。ここでは債権口座に2000円、売上金口座に5000円ある場合、それを相殺して精算済み口座に3000円が入る状態にします。 最後に、加盟店さまの入金日に合わせて、売上金のセトルメントと振り込みを行います。クリアリング時に作成した入金金額のデータをもとに、入金日に合わせて決済サービスを通じて、銀行接続サービスから振込リクエストを行います。 入金結果を銀行接続サービスが入金日の後に入金結果を取得し、精算サービスはデータを決済サービス経由で受け取り、入金状態を更新します。ここでは12000円振り込んだので、その金額の結果をPayoutsに保存しています。 最後に、課題についてです。 課題は二つあり、一つ目は加盟店の手数料発行と精算処理が密結合していることです。精算データの作成処理の手数料の発行処理とほぼ同じタイミングで行っている都合上、精算処理のドメインとしては、手数料発行は異なりますが、それぞれの処理が密結合しています。これは、データの作成部分や処理のフローを分離することで対応しようと思っています。 二つ目はリコンサイル時に会計帳簿に依存しており、会計帳簿サービスと決済処理サービス間のリコンサイルに不整合や遅れが発生した場合に、精算に影響が出る可能性があることです。 こちらについてはリコンサイルのためにProcessing Tracingというサービスの導入を進めており、それによって会計帳簿サービスへの依存を減らす方針で進めています。 以上で、今回のセッションを終わりにしたいと思います。ご清聴ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるEngineeringを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 gRPC Federation を利用した巨大なBFFに対するリアーキテクチャの試み 」の書き起こしです。 @goccy:それでは「gRPC Federation を利用した巨大なBFFに対するリアーキテクチャの試み」というタイトルでメルペイ Engineer ProductivityチームのGoshimaが発表します。よろしくお願いします。 初めに自己紹介です。@goccyというアカウントで活動しています。2012年に新卒で株式会社MIXIに入り、ウェブやアプリのフロントエンドからバックエンドまで技術領域を問わずいろいろなものを作ってきました。 2012年や2013年にYAPC::Asiaで自作のPerl処理系に関して登壇したり、5年くらい勤めた後に転職し、ゲーム系のベンチャー企業でテックリードを務め、3年くらい働いた後、2020年にメルペイに入社しました。OSS開発が好きで、最近はGoのOSSをよく書いています。夢は1万スターを達成することで現在5000スターを超えたくらいです。写真は我が家の愛犬です。 今日のアジェンダです。最初にメルペイでのBFFの変遷です。メルペイが今までBFFをどのように扱い、これからどう扱っていきたいかを話します。 また、その中で課題が出てきたので、それを解決するために、弊社で使っているgRPC Federationについても解説します。 最初に、メルペイでのBFFの変遷についてです。 はじめに、BFFの役割とメリット・デメリットについて話します。BFFは「Backend for Frontend」の略です。文字通り、フロントエンド、つまりクライアントに特化したレスポンスを返す専用のサービスです。 BFFのメリットは、クライアントがBFFとのやり取りだけを考えて通信すればいいので、他のバックエンドサービスを意識せずBFFに集中できることです。一方、バックエンドからもクライアントを意識せずにレスポンスを好きに返すことができ、BFF側でレスポンスをいい感じにクライアントに合う形に集約して返すので、バックエンドを楽に実装できるという特徴があります。 一方、多数のチームが一つのBFFを開発する体制になりがちです。そのため、保守しにくい巨大なモノリスになりやすいというデメリットがあります。多数の人が関わることで、責任の所在が曖昧になりやすいことも課題です。 参考記事: Pattern: Backends For Frontends 続いて、メルペイでBFFをどのように扱ってきたかを解説します。 メルペイではAPI Gatewayの直下にBFFとしてMerpay APIというサービスが存在します。Merpay APIは、多数のマイクロサービスの結果を集約して、クライアントに最適化された結果を返します。 GatewayとMerpay APIのようなBFFを一つにして管理している会社はたくさんありますが、Merpay APIが巨大なため、弊社では責務をそれぞれGatewayとMerpay APIで明確に分けるためにサービスとして独立させ、別のチームが責任を持ってメンテナンスしています。 メルペイのリリースから4年以上が経ち、Merpay APIに多数のチームが機能を追加していくうちに、徐々にオーナーシップが不明確になっていくという課題が出てきました。 現在では応急処置として特定のチームがMerpay API全体の責任を持つ状態になっていますが、Merpay APIが巨大すぎて、保守するコストが無視できないレベルになってきました。 この図のように、Merpay APIに対して多くのチームが機能開発をしていますが、メンテナンス・運用するチームが少数のため、負担が大きくなっています。 これを解決するために、今年「Merpay APIリアーキテクチャプロジェクト」がスタートしました。 Merpay APIが持つ全てのAPIに対して、ひとつずつAPIに責任を持つチームを明確にし、Merpay APIを複数のBFFに分割することで管理するプロジェクトです。 Before(図の左側)のように、Merpay APIに対していろいろなチームが開発している体制から、Merpay APIのAPI一つ一つに、どのチームの持ち物なのかを明確にし、チームの持ち物に対して新しくBFFを切り、チームとBFFを一対一で対応させ、責任を明確化する構成を作ります。 このプロジェクトによって、複数のBFFを作ることになりました。これによってチームとBFFの対応関係が明確になるので、対応チームの「BFFの開発・保守に割くコストを低くしたい」という要求がどんどん高まることが考えられます。 一方で、BFFは責務がシンプルなため、多くが定型作業になると予想されます。そのため、自動化などの恩恵を受けやすいことを踏まえ、BFFを低コストで開発運用する何らかの仕組みが求められていると考えています。例えば、GraphQL FederationのようなFederated Architectureを使う、定型作業の自動化を進める、などの方法が考えられます。 そこで、GraphQL Federation(Apollo Federation)を検討しました。これはGraphQLを用いたFederated Architectureを構築する仕組みで、近年いろいろな会社で採用されています。 導入を検討したものの、今までgRPCだけで巨大なサービスを運用しており、新しくGraphQLを導入するコストが高すぎるという課題がありました。 各マイクロサービスに対応する形でGraphQLサーバーを導入する際のコストが高い、バックエンド開発者全員にGraphQLの知識を新しくインストールする必要がある、GraphQLに対する運用監視の知識が新しく必要になるなどの課題があり、結果的に採用を見送ることにしました。 では、どうするのか。Federated Architectureを構築するためにgRPCを使う方法がないなら、作ればいいんです。そこでgRPCを用いたFederated Architectureを構築する仕組みを開発しています。それをgRPC Federationと呼んでいます。 その仕組みを使って、gRPCサーバーであるBFFを自動生成します。BFFは今まで通りgRPCロトコルを用いて、配下のサービスと通信します。 それでは、gRPC Federationについて説明します。 gRPC Federationは、gRPCを用いたFederated Architectureを構築する仕組みです。Protocol Buffersのオプションで振る舞いを記述します。結果として、自動生成によってgRPCサーバーを作ることができます。 ものによってはProtocol Buffers上だけでは表現できない複雑なロジックを持つ場合もありますが、その部分についてはGoで書くことが可能です。Protocol BuffersとGoのハイブリッドで記述するイメージです。 BFFのように自身でデータを持たず、マイクロサービスの呼び出し結果を集約して返すサービスで有効ではないかと考えています。 最初にgRPC Federationを作るにあたっての設計思想について簡単に触れます。 Protocol Buffers上のレスポンスに注目して開発しました。レスポンスに相当するmessageを取得するために、gRPCメソッドを呼び出すと考えます。普通はgRPCメソッドを呼んだ結果がレスポンスになるので逆になるのですが、考え方としてmessageに注目したかったので、レスポンスに着目しています。 もう一つは、全てのmessageにはそれを取得するためのgRPCメソッドが必ず存在すると考えることです。message と gRPCメソッドは、必ず1対1に紐づけ可能であるという前提を置いて作っています。 gRPC Federationで「どんな体験を提供したいか」も最初に考えています。 一つは「BFFを構築する上で必要な、典型作業を自動化したい」ということ。具体的にはBFFと依存先のサービス間の大量の型変換を自動化したり、サービスの依存関係をProtocol Buffersの解析だけで把握できるようにしたいと考えています。gRPC Federationが想定している使い方をしてもらえれば、メソッド単位の依存関係もわかるようになると考えています。 「BFFと依存先サービス間の型変換を自動化したい」というモチベーションについて説明します。まず、BFFを構築する上で、依存先のサービスにあるmessageを返したいときに同じmessageをBFF側にも作る必要があります。 これをしないと、BFFの呼び出し元が依存先のサービスを常に意識する必要があるため、設計としてBFFを置いている意味がなくなってしまいます。 ただ、このような設計にすることで、BFF側の立場としてはパッケージを跨いだ同じ型の変換作業が大量に発生してしまいます。gRPC Federationでは、これらの対応関係を記述することで、変換処理を自動生成したいと考えています。 もう一つ、サービスの依存関係をProtocol Buffersの解析だけで把握できるようにするモチベーションについて説明します。 マイクロサービスアーキテクチャにおいて、サービス間の依存関係を把握できるといろいろなメリットがあります。メソッド単位での依存関係がわかることで、パフォーマンスの理論値を算出したり、リファクタリングの影響範囲を把握したりと、いろいろなことができるようになります。 gRPC Federationが提供する専用のGoのライブラリや、Protocol Buffersのリフレクション機能などを利用することで、依存関係を機械的に取得できるようになり、いろいろな用途に使えると考えています。 簡単にgRPC Federationに付属しているツールの一部を紹介します。 Protocol Buffersのプラグインとして動作させるように、protoc-gen-grpc-federationというツールがあり、protoc-gen-goやprotoc-gen-go-grpcといったプラグインと組み合わせて使うことで、gRPCのBFFサーバーを自動生成します。 linterはスタンドアローンのツールで、Protocol Buffersのコンパイルをした上で、gRPC Federationの記述ミスを指摘してくれるものです。静的解析ではなくコンパイルするので正確に解析できます。 他にもlanguage-serverを最初から用意しており、gRPC Federationのオプション記述を支援してくれるので、linterによるエラーや補完がエディタ上ですぐに利用できます。 続いてgRPC Federationの具体的な使い方を説明します。 Post ServiceとUser Service、それからBFFとして作るFederation Serviceという三つのサービスを使って説明します。 図のように、Federation Serviceという今回作るサービスにpost idが送られたら、それをPost Serviceに渡すことによってPost messageを取得し、Post messageに存在しているuser_idというフィールドの値をもとに、それを使ってUser Serviceに問い合わせし、User messageを取得します。postとuserという二つのmessageを合成して、postの中にuserが含まれるmessageにした上で返すサービスを考えていきます。 続いて、各サービスのProtocol Buffersの定義についてです。 一番左側がFederation Serviceに相当するProtocol Buffers内容です。GetPostというメソッドがあり、中身には Post messageが入ります。Post messageには User messageが直接入る構成です。 右側二つはPost ServiceとUser Serviceの内容です。それぞれGetPost / GetUser と自身が管理する message を返すメソッドを一つずつ持ちます。Post message にはuser_idというフィールドがあります。これらを使って説明していきます。 gRPC FederationのProtocol Buffersのオプションについて簡単に説明します。Protocol Buffersのsyntaxのservice、message、fieldそれぞれに合わせてオプションを用意しています。 serviceに対してはgrpc.federation.serviceが、messageに対してはgrpc.federation.messageが存在します。それぞれについて説明します。 まず、grpc.federation.serviceというオプションです。これはgRPC Federationの自動生成対象となるサービスを指定するために利用します。機能として、dependenciesというセクションがあり、どのサービスに依存しているかを定義できます。 この例であれば、Federation Serviceにgrpc.federation.serviceというオプションをつけると、dependencieを用いてPost ServiceとUser Serviceに対して依存関係があることを定義できます。 grpc.federation.messageオプションは、一番重要なオプションです。大きくresolverとmessagesにわかれています。message自身の各フィールドに割り当てる値自体を取得するための定義を書くオプションです。 resolverはgRPCメソッドを呼び出すための定義です。この定義によってmessageとメソッドが一対一で紐づきます。resolverだけでは自身のmessageに割り当てる値を取得しきれないことがあります。そのときにmessagesを用いて他の message への依存関係を定義できます。 messages は複数指定することが可能です。 この二つを組み合わせることで、必要な値を手に入れます。 最初に、resolverから見ていきます。左側がFederation Serviceの定義で、右側がPost ServiceのProtocol Buffersの定義です。赤枠で囲われたresolverというセクションについて話します。 最初に、resolverはgRPCメソッドを定義します。methodというフィールドに対して呼び出すメソッドの名前をFQDNで指定します。今回であればPost messageを作るためにGetPostというPost Serviceのメソッドを呼び出したいので、この形で指定します。右側のPostServiceの赤くハイライトした部分が対応している部分です。 続いて、メソッドを呼び出すためには、リクエスト時の値を指定する必要があります。赤枠のrequestという箇所でそれを指定しています。右側のPost ServiceのGetPostRequestというmessageに対応しており、GetPostRequestの中身を埋めていく作業になります。fieldにフィールド名を書いて、byでpost_idフィールドに対する値を指定します。 最後に、呼び出した結果のうち、どの値をどのような名前でgRPC Federation中で参照していくかを定義するために、response を定義します。これはPostService側のGetPostReplyというmessageの内容と対応しています。 次にそれぞれのフィールドについて説明します。nameで、取得したレスポンスをどういう名前で参照するかを定義します。ここでresという名前をつけているので、この名前でGetPostReplyのpostフィールドの値を参照するという意味になります。 次にfieldです。そのレスポンスのうちにどのフィールドを採用するかを指定します。ここではpostというフィールドを指定しているので、GetPostReplyの中のpostフィールドの値だけを使うという意味です。 最後に、「autobind: true」について説明します。 レスポンスの各フィールドと同じ名前・同じ型のフィールドがBFF側にも存在するならば、フィールドのバインディングをできるだけ省略した方がいいという考え方のもと、右側のPostService側のPost messageにあるid・title・contentというフィールドと同じ名前・同じ型のフィールドが左側のFederationService側にも存在するので、それらの値を自動的にバインドするという機能です。これによって option の記述を大幅に省略することができます。 次にmessagesのセクションについて説明します。Post messageを作る上でresolverだけでは足りません。肝心のuserの値はまだ取得できていない状況です。 最初にmessagesの中で、nameに着目します。これは依存するmessage、今回であればFederation Service側のUser messageに依存したいので、User messageに対して、取得したときにどういった名前で扱うかを指定します。 ここではuという名前を付けているので、この名前でUser messageを参照していきます。 次に、参照するmessage自体を書かないといけないので、messageにUserと書くと、右側の赤枠で囲われた User messageを指すという意味になります。 次に、一番重要なargsについてです。右側のUser messageを見てください。こちらにもgRPC Federationのオプションが書かれていて、中ではGetUserメソッドを呼び出し、userを取得して自身のフィールドにバインドするということが書かれています。 GetUserメソッドを呼び出すためにuser_idが必要です。この値を User message に渡すことを考えなければなりません。これを実現するためにargsを利用し、依存messageを取得する際に必要になるパラメータを指定します。このパラメータを、gRPC Federationでは「メッセージ引数」と呼んでいます。 argsの中にはnameがあります。ここでは、messageに対する名前を指定することができ、依存先のmessage、この場合User message側でこの名前に「$.」というプレフィックスをつけることで参照できます。 では、引数の値はどうやって指定するのでしょうか。これは、byで指定できます。byで指定する値自体はどうやって受け取るのかというと、resolverでレスポンスに対してつけたresという名前を参照し、レスポンスのuser_idフィールドを参照することを示すためにres.user_idと書くと指定できます。これにより、User message側にGetPostメソッドのレスポンスにあるuser_idの値が渡ります。 最後に、grpc.federation.fieldというオプションについて説明します。messageオプションによってフィールドのバインディングに必要な情報が集まりました。 最後にfiledオプションで定義した名前や、自身のmessageに対するmessage引数などを参照しながら値をフィールドに紐付けます。Protocol Buffersの定義にあるように、messagesで書かれているUser messageに対して「u」という名前をつけていますが、この名前を使ってgrpc.federation.field のbyという機能を使って4番目のuserフィールドにUser messageの値を紐付けています。 最後にレスポンスにオプションを追加して完成です。最初に設計思想でレスポンスのmessageに着目したいという話をしました。 今まで作ってきたPost messageを作っただけだと、まだgRPCメソッドであるGetPostの全実装を完成できていません。 レスポンスのmessageであるGetPostReplyに対してオプションを追加し、レスポンスの実装が完成していることを示すことが必要です。 特殊な仕様として、レスポンスのメッセージ引数に相当するものは、gRPCメソッドのリクエスト側のmessageの各フィールドになるというものがあります。GetPostReplyというmessageを作るためにオプションを書いていきますが、grpc.federation.messageというオプションの中で、リクエストに対応するGetPostRequest messageの各フィールドを参照できるので、$.id でリクエストの内容を参照できます。 その結果取得した Post messageに対してpという名前をつけているので、pをgrpc.federation.fieldというオプションで参照して、自身の1番目のpostフィールドに紐付けて完成になります。 他にもいろいろな機能が実装されています。他のパッケージに定義されている messageを参照することでgRPC Federationの資産を再利用したり、複雑なロジックを定義したい場合は、messageやfieldオプションの中で「custom_resolver = true」と記述すると、その部分だけGoで実装することができます。 他にもgrpc.federation.methodというオプションでメソッドレベルの制御ができるようになり、例えばタイムアウトを設定できます。また、oneofに対しては専用のgrpc.federation.oneofというオプションを用意し、oneof内で条件分岐を定義できるようにすることも考えています。 実はgRPC FederationはOSSとして公開していまして、grpc-federationというリポジトリで、誰でも利用することができるようになっています。 現在アルファバージョンですが、今年中に社内の本番環境で活用できるように改善を続けている状態で、随時更新しています。 Federated Architectureを構築する上で、GraphQLに代わる一つの解として gRPC Federation を選択できるように頑張っていこうと考えています。 現時点ではプルリクエストは受け付けておりませんが、機能要望や、改善案、使用感などのフィードバックはウェルカムですので、issueやTwitterのコメントで反応してくれると嬉しいです。 grpc-federation: https://github.com/mercari/grpc-federation 最後に、発表のまとめです。マイクロサービスアーキテクチャにおけるBFFの重要性とメリットデメリットについて触れました。 メルペイでは、巨大なBFFのオーナーシップ問題を解決するために、いくつかのBFFに分割することを考えています。各BFFの開発を効率的に行うためにgRPC Federationという仕組みを作っています。gRPC Federationを使ったシンプルなBFFの構築例を示しました。こちらはOSSになっており、誰でも使えるようになっているため、ぜひ使っていただいてフィードバックをお待ちしています。 それでは本発表を終わります。ご清聴ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 メルコイン決済マイクロサービスのトランザクション管理を支える技術 」の書き起こしです。 @susho:それでは「メルコイン決済マイクロサービスのトランザクション管理を支える技術」というタイトルで、Merpay Payment Platform Teamの@sushoが発表します。よろしくお願いします。 僕の名前は、Shota Suzukiです。社内では、@sushoというアカウントで活動しています。所属は、株式会社メルペイのバックエンドエンジニアです。2018年にメルペイに入社し、その後iDやコード決済のマイクロサービスを開発し、現在はメルコインの決済や会計のマイクロサービスを開発しています。 本日のアジェンダです。最初にメルコイン決済マイクロサービスについて説明し、次にトランザクション管理、リコンサイル、最後にまとめです。 最初に、メルコイン決済マイクロサービスについてです。 これは、お客さまの決済処理を担うマイクロサービスで、主にスライドのようなユースケースが存在しています。 次は、マイクロサービスのトランザクション管理の話です。 一般的に、マイクロサービスを跨いだトランザクションでは、マイクロサービスはデータベースをそれぞれ持っているため、依存先マイクロサービスのデータベースのロールバックを実行できないと思います。 また、2フェーズコミットなどに代表される分散トランザクションでは、リソースの状態をロックする必要があるため、サービスの可用性が下がる可能性があります。 そのため、実行した操作を取り消すビジネスロジックを 補償トランザクション として実装し、それらを最後まで順次実行する結果整合性というアプローチを取っています。 次に、Sagaについてです。これは、結果整合性を使ったアーキテクチャの一つと言われています。弊社でもこれを採用していて、トランザクションを複数のトランザクションに分割し、それらを順次実行します。 途中でリトライ不可能なエラーが発生したら、逆処理となる補償トランザクションを順次移行していくことを指します。自分たちで開発しているマイクロサービスでもこのアプローチを採用してトランザクションの設計をしています。 トランザクション管理がどのようなものかを、メルコイン決済マイクロサービスの「暗号資産購入」というユースケースに絞って説明します。 左上から見ていくと、決済マイクロサービス自身で状態を管理したいので、自分たちのDBにリソースを書き込みます。その後、購入に利用するメルカリポイントの減算処理を実行するのが次のトランザクションです。 それが成功したら、メルコイン残高のJPYを減算します。続いて、加算した分のBTCの残高を加算処理し、自身のリソースを更新します。最後にEventとして、リソースの状態をPublishします。この計六つのトランザクションとなっています。 補償トランザクションの定義は、スライドの通りです。例えばBTCの加算処理を失敗した場合、JPYの減算処理の逆処理となるJPYの加算処理を補償トランザクションとして提起し、メルカリポイントの減算処理の逆処理となる加算処理を次の補償トランザクションとして定義し、その後リソースの更新、EventのPublish処理を定義しています。 続いて、各処理で失敗したときの実行順序を見ていきます。例えば、BTCの加算処理が失敗した場合、JPYの加算処理、メルカリポイントの加算処理リソースの更新、PublishEventの順に実行していきます。 もしJPYの減算処理で失敗した場合には、一つ前の逆処理となるメルカリポイントの加算処理から逆順に補償トランザクションを実行します。 次に、メルカリポイントの加算処理に失敗した場合には、リソースを更新してEventをPublishするという補償トランザクションの実行順になります。 このようにトランザクションの設計をすることで、途中でリトライ不可能なエラーになったとしても、安全にロールバックできます。 これらをどのように実装するかを検討したところ、弊社ではクラウドサービスとしてGCPを利用しているため、オープンソースのプロダクトかGCPの製品が候補として挙がり、スライドにある2つを検討しました。 一つ目は、 GCP Workflows です。これは、各トランザクション処理をHTTPのAPIのエンドポイントとして実装する必要があるので、開発時にユニットテストがしづらいという懸念があります。 またオープンソースの製品である Cadence や Temporal は、弊社が利用しているCloud Spannerに対応していないため、自社で開発するのが良いという結論に至りました。 続いて、自社で開発したWorkflow Engineのアーキテクチャの概要を説明します。基本的にWorkflow Engineは、アプリケーションサーバーと同じポットやサーバーでデプロイされることを想定しています。 Goランタイムで動くようになっていて、SDK・Libraryのような使い方で利用していくことを想定しています。 アプリケーションはWorkerと呼ばれるものを、ライブラリのインターフェース経由でアクセスします。主に二つのインターフェースを使ってコミュニケーションをします。 一つ目は、Register Workflow Functionです。トランザクションは、Goのコードなので、エンジン側にどのようなWorkflowを使うかを登録します。そのためのインターフェースがこれです。Workerは登録の要求が来たら、自分たちのインメモリのRegistryに格納し、実行するときは、リフレクションを使って実行します。 もう一つが、Workflowを実行するインターフェースです。これが呼び出されると、Workerは、まず最初にEngine Serverと呼ばれる、Spannerで、WorkflowやActivityの実行状態を管理する単純なプラットのgRPCのサーバーなのですが、どのようなインプット・functionネームで実行するかをWorker側がEngine Serverに書き込みます。その後、WorkerはChannelにWorkflowが開始したというEventをPublishします。 するとWorker側はそのEventをSubscribeして、Goのreflect.ValueOf.Call()関数を使って実行します。それが完了したら、WorkerはEngine Serverに実行結果を保存するためにWorkflowのコンプリートのリクエストを投げ、ChannelにcompletedEventを投げます。 その後、Workflowの実行結果を、アプリケーションで確認したい。アウトプットを受け取って、クライアントなどにレスポンスを返したいという要件もあると思うので、Workerは非同期に、アプリケーションが待ち受けている処理にレスポンスを返します。 途中で失敗してコンプリートを呼べない場合も想定されます。その場合はリカバリーWorkerというものが動作し、Engine Serverに対してコンプリートになっていないWorkflowやActivityをリストし、リトライします。このようにして、アプリケーションのWorkflow Engineを実行管理しています。 続いて、それぞれの用語についてまとめました。 Workflowは、複数のActivityを使ったビジネスロジックと定義しています。先ほど説明した、「全体のトランザクション」をイメージしていただければ良いかなと思います。Activityがビジネスロジックの最小単位です。「それぞれのトランザクション」をイメージしてください。 次に、Workerです。Workerはアプリケーションサーバーから要求に従ってEngine Serverへコミュニケーションします。また、ChannelからEventをSubscribeして、Eventの種別に沿った処理を実行します。 リカバリーWorkerは、Workerとほとんど役割が同じですが、完了していないワークの一覧を取得し、トライするものです。 アプリケーションから渡されたGoのコードをインメモリの構造で管理しているのが、Workflow Function Registryです。 続いて、Channelです。これは、WorkflowやActivityの状態遷移Eventハブとなります。 現状では4種類のEventが存在していて、WorkflowStarted、WorkflowCompleted、ActivityStarted、ActivityCompletedの4種類です。StartedはWorkflowを実行するためのEventでこれを受け取ったら、Workerはリフレクトを使って関数を呼び出します。その後Completedが、このEventハブに届くので、完了した後に、アプリケーションレスポンスを返すためのEventです。 ActivityStarted・ActivityCompletedは、Workflowとほとんど同じ利用用途です。 Engine Serverは、Workflowの状態を管理するシンプルなCRUDのgRPCサーバーです。WorkflowやActivityの渡されたインプットやアウトプットも保存しています。 これにより、途中で失敗してリトライするときに、以前まで実行していたActivityのアウトプットが保存されているので、再度実行せずにアウトプットをただ返して冪等に処理することが可能です。 続いて、先ほどの暗号資産購入のトランザクションを、Workflow Engineを使うとした場合の構成を見ていきます。 全体として、「暗号資産購入」というWorkflowを定義し、それぞれのトランザクションをActivityとして定義します。同様に補償トランザクションもActivityとして定義し、これらをどの順序で実行するかをGoのコードで書きます。 コードサンプルを見ていきます。appという構造体を用意し、初期化処理の中で、実行に使うWorkflow、Activityを事前に登録しておきます。 RunSayHelloというメソッドが呼び出された場合を見てみると、これはWorkflowとしてSayHelloを実行して、レスポンスを受け取るシンプルなコードです。 これによってWorkerでSayHelloの関数が実行され、レスポンスを受け取れます。SayHelloの中身を見ると、このSayHelloはHelloというActivityを実行して、そのレスポンスを待ち受けています。 これによってWorkflow SayHelloが実行されて、そのSayHelloは、ActivityHello を実行するようにアプリケーション側で定義されているので、その順番で成功するまでリトライし続けられ、完了します。 このようにして、先ほどメルコインのユースケースで紹介したWorkflowをGoのコードに落とし込んで実行することで、トランザクション管理をしています。 次はリコンサイルを説明します。 リコンサイルとは、依存先マイクロサービスと整合性が取れているかを突合して検証するプロセスです。 サービスの特性上、お客さまの資産を預かるため、マイクロサービス間で不整合が起きていないことを、トランザクション管理とは別プロセスで検証したいという目的があります。 また、自分たちのマイクロサービスだけではなくて、依存している全てのマイクロサービスで検証しその結果整合性が取れていることを確認した上で、会計処理を実施したいという目的のもと、実行していきます。 そこで開発したのが、Processing Tracerというマイクロサービスです。これはマイクロサービスのリコンサイル処理をフックし、リコンサイル状況の監視・アラートするためのマイクロサービスです。 概要を見ていきます。こちらも、暗号資産購入の場合を想定しています。このときにOrder、Payment、Balanceが、マイクロサービスがこの暗号資産購入の取引に依存しているとします。 Orderがこのトランザクションのエントリーポイントとなるので、まず最初にOrderがProcessingIDというものを生成し、その後Paymentにリクエストを投げるときに、そのProcessingIDをつけます。 その後、PaymentはBalanceにリクエストを投げるときにのProcessingIDをつけます。そうすることで、この三つのマイクロサービスでProcessingIDが伝播されます。 OrderはProcessing TracerにRegister ProcessingIDというAPIを呼び出して、生成したProcessingIDを登録します。 Processing Tracerは、CronJobによって、このProcessingIDがOrderから登録されていることがわかるので、Orderサービス専用のPub/Subトピックに対して、リコンサイルのEventをPublishします。 するとOrderサービスは、トピックに紐づいたサブスクリプションを作成し、リコンサイル処理を実装します。Eventが呼び出されたら、リコンサイル処理が実行されることになります。 中身としては、Paymentに問い合わせてリソースの状態を突合し、OKだった場合は、Processing Tracerにリコンサイルレポートという形で、gRPCのリクエストを投げます。 このとき、OrderはPaymentに依存しているので、レポートの中にPaymentという識別子を付加してレポートを投げます。 そうすることで、Processing Tracerは新しくこのProcessingIDはPaymentに依存しているとわかるので、CronJobによってPaymentのPub/Subトピックに、Eventを投げてPaymentはBalanceと突合します。 その後レポートにBalanceという識別子を含めて投げることで、Processing Tracerは、後でBalanceにPub/SubのEventをPublishして、全体としてリコンサイル処理が伝播されます。 もしBalanceなどがリコンサイルできていなかった場合は、それを補足してSlackにアラートを飛ばす仕組みがあります。 それぞれの用語について説明します。 まず、ProcessingID。これは、リコンサイル処理を一意に識別するためのIDです。基本的に処理のエントリーポイントとなるマイクロサービスが生成し、ユニーク性担保のために生成には専用のSDKを利用します。 Register ProcessingIDは、ProcessingIDを登録するもの。Reconcile Eventは、ProcessingIDが登録され、一定期間後に、マイクロサービスそれぞれのPub/Subトピックに向けてEventを発行するものです。 Report Reconcileは、各マイクロサービスがリコンサイル処理を実施して成功したレポートを送信するものです。アラートは、リコンサイルされていないProcessingIDについて、そのマイクロサービスのオンコールChannelにメッセージを投稿するものです。 最後に、メルコインマイクロサービスのリコンサイル処理について、軽く説明します。 Processing TracerからReconcile Eventを受け取ったら、Paymentは、ProcessingIDから自身のリソースを見つけて、PaymentやBalanceにリクエストを投げます。 このときに自分の状態と依存しているマイクロサービスの状態を突合し、OKだったらレポートを投げます。 最後に、まとめです。 まずトランザクション管理についてです。複数のマイクロサービスを跨いでトランザクション処理を実行するために、Sagaを採用したWorkflow Engineを開発し利用しています。 リコンサイルでは、サービスの特性上、リコンサイルすることで、最終的にその処理が想定通り実行されているかを確認する必要があります。それをProcessing Tracerというマイクロサービスを開発することで処理の共通化をしています。 以上です。ご清聴ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、Productやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 Merpay & MercoinにおけるLLM活用の取り組み 」の書き起こしです。 @nu2:みなさん、こんにちは。早速ですが、「Merpay & MercoinにおけるLLM活用の取り組み」についてセッションを始めます。現在、世界中のFintech企業も何かしらの投資対象として注目している。LLM技術について、メルカリグループで取り組んでいる内容をお伝えするセッションです。 Merpay & Mercoin Tech Fest 2023の最大の見どころの一つと言っても過言ではないと私は思っていて、非常に楽しみです。昨日もちょうどGoogle Cloud様が、LLM withビジョンという、テキストメッセージに対して、画像検索の形で応答するデモを公開されていまして、これはメルカリの学習データを提供して実現しており、タイムリーな話題になっています。 まずは自己紹介です。私は@nu2と申します。本日は、進行役を務めます。 私は5月に入社するまで、15年ほどWeb検索の領域に関わってきましたので、ここまでLLMが飛躍的な進化を遂げるとは正直思っていませんでした。今回はメルカリからVP of Generative AI/LLMの@mazeさんをお招きしておりますので、ディープダイブした内容を伺いできればと思います。 @maze:こんにちは、メルカリの@mazeです。現在は生成AIの担当役員をやっています。直近では、ソウゾウの代表をやっていました。今は120%生成AI関連にコミットしています。ソウゾウ立ち上げ前はメルペイにおり、メルペイの立ち上げや、金融の与信周りに携わっていました。今日はよろしくお願いします。 @tori:みなさま、はじめましてよろしくお願いします。Torigoeと申します。私は、2018年にメルペイに機械学習エンジニアとして入りまして、あと払いや不正対策の機械学習の応用に携わりました。メルペイの機械学習全体のマネージャーと、LLMや新しい技術を使って価値を出すチームのマネージャーも兼任しています。 今日は半年くらいいろいろ取り組んできたお話の経験を基にできるだけ楽しくお話できればと思います。よろしくお願いします。 @maze:まず、簡単にメルカリグループ全体の生成AIの取り組みを紹介します。 今年の5月に メルカリで生成AI/LLM専任チー ムを作りました。 ミッションとしては二つあります。生成LLMの技術を用いて、新しくお客さま体験を作ることと、それによる事業インパクトの最大化を一つ目のミッションとしています。 二つ目のミッションは、メルカリグループ全体として2000人以上従業員がいるのですが、従業員の生産性を劇的に上げることです。なるべく専任チームを作って、機動的に動ける形にしています。 チームでは、EnablingとBuildingの二つを掲げて進めています。 生成AIの技術は特定のチームに閉じるべきじゃないと思います。できればメルカリの従業員2000人全員が、十分に使えるという状態にしていければなと思いますので、なるべく推進できるようにする活動も僕らのチームとしてEnablingとしてやっています。 もう一つ、僕らはなるべくものを作れるチームで、Buildingでやっております。 Enablingについて、具体的にやっていることとしてまず挙げられるのが、ガイドライン策定です。 メルカリはこれまでもずっとAIに投資していまして、MLエンジニアも多数在籍しています。生成AIはAIの民主化的側面もあり、一般のソフトウェアエンジニアの方でも、気軽に触れるようになったと思います。皆さんが安心してAI開発できるように、各所と連携しながらガイドラインを作っています。 また、みんな「気になっているけれど忙しい」ということもあるので、ハッカソンの機会を作っています。3ヶ月おきに開催しており、頻度は高いです。 次はBuildingの一例です。僕がチームで初めに行ったのが、メルカリ社員専用のChatGPTを作ることです。Open AI社のChatGPTは、入力した情報が学習に使われてしまうので、メルカリ社員専用の業務内容を入れてもいいChatGPTを作りました。 それだけでは面白みがないので、メルカリ社員であれば無料でGPT4が使えたり、Googleのモデルにも対応していまして、同じプロンプトで出力がどう変わるかも試していただける形で作っております。 僕らが今注力したのはこちらで、既存のプロダクトにLLMを入れていくことに注力しています。グループ全体として、いろいろなFunctionチームがあり、そこと連携しながら、プロダクト開発を進めています。 進め方は、僕らLLMチームが企画から実装まで全部行うパターンと、各種Functionチームにリードをお願いして、プロンプト周りやどのモデル・ツールを使うのかといった細かい相談のみこちらで対応するパターンの2つがあります。 メルカリには、MLエンジニアを持っているチームも多数あるので、その場合は2つ目の「共創」のパターンで、発見を共有し合いながら進めていきます。 施策内容としては、既存プロダクトへの適用だけでもかなり幅が広いです。Fintechとまとめて書いていますが、いろいろな使われ方があります。それ以外に社内ツールの実装や、生成AIを生かして新規事業を考えることも含めていろいろ試しています。 生成AIの専任チームなので、技術の探索もあわせて行っています。モデルの選定について、ビッグテックが出しているLLMのAPIをどう使うのかという話や、OSSのモデルを利用するという話をしています。内製の基盤モデルを作るのも、選択肢としては一応あると思います。 LLMの進展に伴い、非常にいろいろなツールが充実しています。 実際にでてきたら触って、それがどう生かせるかを考えています。毎日何か新しいものが出てくるので、いろいろ試しつつ、メルカリではすぐにプロダクトに活かして、実際に本番環境でどうなるかを見れる、フィードバックをもらえるので面白いと思います。 @nu2:実際にSlackでドックフーディングのように、エンジニアの方たちがいろいろいじったり触ったりしているのを側から見ていて、本当にすごいなと思います。一方でFintechにおけるLLMの導入を進めかたは、メルカリとは違うところがあるのですが、@toriさん、いかがでしょうか。 @tori:Fintechでは、最初にLLMの波がきたときに、当然MLエンジニアが、各々独自に探索する動きはあったのですが、途中で専門でフォーカスしないと世の中に遅れてしまうなと思い、@mazeさんとほぼ同じタイミングで専門チームを立ち上げました。 それ以降、最初の3ヶ月間は、2つのアプローチを取りました。エンジニア自身が企画も含めて考えて、PoCするというアプローチと、MLエンジニアだけだと、どう使えばいいのかが悩ましかったので、LLMを使ったコンテスト(ぐげん会議)を行い、社内のいろいろな人のアイディアを借りるアプローチです。 最初は技術をキャッチアップしながら、どういう使い方をするのかをエンジニアがボトムアップで考えつつ、皆さんのアイディアも借りながら会社全体でどれがいいかを考えてきたところです。 ぐげん会議には、@nu2さんにも出ていただきました。 参考記事: LLMを活用してなにがつくれるか?——「ぐげん会議」開催から見えてきた、AI活用の新たな可能性 @nu2:ぐげん会議や、実用化を目的としたハッカソンを開催されていましたが、実用化したユースケースはありますか。 @tori:今まさにいろいろ仕込み中ですが、新しくPoCして取り組んで、結果としてそれをぐげん会議に出して、入賞したというユースケースがあるので、それについてチームメンバーからプレゼンテーションを用意しているので、ご覧ください。 @hmj:今回はLLMを活用した文章構成の取り組みについて発表します。 私は、@hmjといいます。株式会社メルペイで、機械学習を担当しております。 2018年より株式会社メルペイにて機械学習や、自然言語処理を中心とした与信モデルの設計開発を行い、その後不正検知の領域の機械学習のモデルや、システム開発に携わっていました。2023年4月より、LLMなどの新しい技術をキャッチアップするチームに所属しており、こういった取り組みをしております。今回はこちらの取り組みを紹介します。どうぞよろしくお願いします。 本日のアジェンダはこちらです。初めに概要を紹介し、その後課題や解決策の話をし、最後にツールの紹介と、簡単にまとめをしたいと思います。 では、はじめて行きたいと思います。 まずメルカリではステークホルダーの方とのタッチポイントがこの図の形でいっぱいあります。例えばお客さまに対するアプリ内での通知やキャンペーン情報、CMなどで日常的に多くの場所でメルカリに関する言葉が届けられます。 コーポレートやプロダクトの枠を超えて、数々のメディアや、アプリ内のお知らせでそういったタッチポイントが多くあるのでとても言葉には影響力があります。 そのため、社内の文章であってもしっかりとしたワーディングに関するチェックがとても大事になってきて、チェックをする仕組みが必要になってきます。 今回は文章校正チェックにLLMを活用できないかということでツールの開発を行いました。今回はそちらの取り組みについて紹介したいと思います。 では実際に課題感と、どう解決したかをご紹介したいと思います。 解決したい課題の一つは、ワーディングルールが全ての方に浸透していなくて、活用できないという場面があることです。また二つ目としては、チェックをしてワーディングルールを見ながら行うんですけど、非常に数や量が多くて、人の手でチェックをするのに限界があるということです。そこで求められていたのは、誰でも簡単に短時間でチェックを行える仕組みです。 その中で今回機能として求められていたのは、このようなところにあります。単純に校正した文章を出して終わりではなくてこの三つが求められていることが、社内議論する中で見えてきました。 一つ目は、指摘箇所が文章中でわかるようにすること。文書の中で、どの部分がチェックに該当するのかがわかるようにしたいということです。二つ目は、指摘の理由がわかるようにすること。ある言葉がチェック入ったときに、それがなぜチェックされているのかがわかるように、どのワーディングルールに該当するかを利用者にわかるようにしたいということです。最後に、指摘をして修正した文章を、アウトプットとして取得できるようにしたいということがありました。 続いて、求められていることについて、どういった社内ツールを作ったかをご紹介します。 デモツールの実際に作った画面の一部を抜粋しながら説明していきます。 全体的にデモではあまり説明を入れずに使っていただけることを意識しています。最初に表示するテキストに例文が入ってるんですけれども、ここに校正したい文章を入力するとその下に結果が出てくるというシンプルな仕組みになっています。 特徴的なのは、会社ごとにチューニングされたワーディングルールがあるので、どの部分を活用したいかを利用者の人が選べるようなUIも作っています。Submitボタンを押すと処理が実行されます。 結果はこういった形になっていて、少し入り組んでいるので、少しずつご紹介したいと思います。 1個目がAnnotatedによる指摘です。これは、文章中でどこを指摘したかがわかりやすいように色付きで表示されるようになっています。また、色ごとに理由のカテゴリーが分かれています。 2個目に、指摘の解説です。指摘されている箇所がどういった理由で指摘されているか、どういったワーディングルールに基づいてるかを表示しています。 最後にSuggestion、これは全体の指摘を修正した文書が出力される場所となっています。利用者の方が、指摘がそのGPTによるモデルが合っているとは限らないので、利用者の方が、「絶対違う」というのがあったら、「採用しない」という選択肢も選べるようになっています。 次に裏側のロジックを簡単に紹介します。校正は簡単に三つのステップで成り立っています。 一つ目が、誤字・脱字・誤植や文法ミスのチェック。二つ目は、会社独自のワーディングルールに照らし合わせたチェック。最後に、内容が間違っている、あるいは最新の情報ではないなどのチェックです。 それらを実装するために、今回作成したフローは、こういった形になります。利用者の方が、インプットしてアウトプットするという流れに対して、それぞれ誤字脱字、ワーディングルール、公開情報とのチェックを行います。 全てLLMではなくて、自然従来の自然言語処理を使いつつ、OpenAIの Chat completions API のプロンプトで、柔軟に工夫しています、公開情報とのチェックについては外側からデータを入れ、どこが間違ってるかをチェックするという活用の仕方をして、フロー作りました。 最後にまとめに行きたいと思います。 今回のまとめとしては、LLMをGPT-3.5のモデルを活用して社内の文章校正ツールを作りました。文章の生成や修正理由の指摘はLLMが得意なところだったのでLLMに任せました。 逆にLLMでなくてもできるところは従来の技術や他の方法を使って棲み分けることで実現できました。 私自身MLエンジニアですがLLMという学習済みのモデルをどう使うかが、今までとは少し仕事の内容が違ったので、今後そういった取り組みを踏まえながら、どこでバリューを発揮できるかを考える機会になりました。今回とても面白いチャレンジングな取り組みができたんじゃないかと思います。 @nu2:ありがとうございました。先ほど、@toriさんからあったぐげん会議で私もスポンサードさせてもらったのが、障害報告書を自動生成するものでした。 そのような活用方法がデフォルトで考えうる使い方だなとは思います。FintechにおけるLLM応用の特殊性について、着々と実用化の準備が進んでいるとのことですが、Fintech事業ならではの応用について特殊なことってありますか。 @tori:一つ明確にあると感じるのは、金融商品は法律や自主規制など、いろいろな前提の上に乗っかっているもので、お客さまに伝えたいことも難しくなってしまうんですよね。 これに対して、ルールベースでいろいろコミュニケーション取ることはできますが、どうしても単調でわかりづらく、伝わらないこともあると思います。 こうした課題に対して、LLMは単調ではなく柔軟にしたり、固い表現を温かみのある表現にしたり、難しいものをわかりやすく変換して、よりプロダクトや金融商品とお客さまの距離を近くしてくれるアプローチに使える可能性があると思います。まさにLLMじゃないとできないポイントだと思います。 また、社外のお客さまとの接点だけでなく、社内にも全く同じ問題がありました。内部的なリスクチェックや省庁への報告などをエンジニアやリスク管理など、専門性をまたぐようなコミュニケーションに対して、同じ日本語を扱ってるんですけど、相互に理解したり、同一の文書にまとめたりするところが難しいです。 こうした社内のコミュニケーションやドキュメントをスムーズにして、結果として質の高い金融サービスをお届けするという点でも、可能性を感じます。 @nu2:障害報告書でもアウトプットに高い品質が求められると思うのですが、品質管理については今後どのように取り組んでいくかを聞かせてください。 @tori:正直言うと、悩みながらやっています。走りながら同時に道路を作っているようなイメージです。 Fintech関係なく、様々なドメインでそれぞれ共通的に気にするポイントがあるので、@mazeさんと連携しながら、社内の集合知としてガイドラインの形で作っています。ハルシネーションや、個人情報保護の観点、制度品質の担保などにフォーカスしています。 ただ、システム単体で見るというよりは、誰に対して向けているものなのか、お客さまとの間で取り決めするポリシーすらも変数として、総合的に必要な品質を考えるべきだと思います。 @nu2:もう少し応用性をちょっと深掘りして、ドメイン問わずお伺いしていきたいなと思います。私も今まで出てきたものにも触ったりしてるんですけれども、そんなに誰もが利用したいと思えるツールがChatGPT以外があんまり見当たらないなと感じています。 実際に現場で苦労しながら、取り組んでる点やどのような価値の創出や出し方が考えられていますか。 @mazu:ドメインを問わずで言うと、いくつか今入れてるところを抽象化して考えると、人が本来あまりやらなくて良いところや、機械化されることで人が解放されたり、クリエイティビティが発揮されるところに時間をより使えたりするところなど、何かにおまかせしたい仕事という部分がフィットするパターンが多いと思います。 クオリティ・精度を考えると、人手で時間をかければ90〜100%の精度でできるところも、スピードが重要で、60%の精度で良いところにLLMを使ってショートカットすべきところについては有効だと思います。 LLMを使わなくてもできるエリアでも使うことはあります。例えば、SEOのロジックにLLM使っていい感じのmetaタグ作ってみたり、分析のRAWデータを入れて、1人で分析するのではなく、GPTに分析してもらうことも、前処理的に入れるとかなり時間を短縮できるなという印象はあります。 @tori:ぐげん会議では、開発期間が1ヶ月もなかった中でとても良いプロトタイプが出せまして、これは一面でいうと成熟した技術をしっかり丁寧に使えば100点取れますが、LLMで60-80点のものを過去にないスピードで作れる長所が活きたということだと思います。 例えばテキストのポジティブネガティブ分析は、LLMでも準備なくとても良い精度のものが出せるので、これは典型だなと僕は思います。 @maze:おっしゃる通り、NLPを今ままでやってきた人にとって大変な作業が、すごい速さで実現できます。 @nu2:あとは、一般の方たちがどう使うかという問題もあると思います。我々はプロンプトエンジニアリングの観点で、コンテキストを突き詰めて、パフォーマンスを引き出す術を、ある程度もっています。一方で、一般のお客さま様・消費者は、何を聞いていいのかがわかりません。ハイコンテキストをローに落とすという変換が大事だと思います。 最後に、今後の方向性についても、お伺いできればと思います。 @maze:メルカリグループは非常にグループ間の敷居が低く、僕もメルペイに以前在籍をしていて、その後ソウゾウに行って今またメルカリに所属しています。引き続き各社グループ会社と連携しながら、それぞれが得た発見をなるべく共有していけるといいと思います。 プロダクト面については、メルカリグループにあるデータを活用して、お客さまの体験を便利にしていきたいです。解くべきIssue自体はこれまでと変わらないので、新しい手法で解決しに行くこと進めていけたらと思います。 社内の生産性の観点で言うと、生成AIを使える方をとにかく増やしたいです。なるべく社員が生成AIを使う機会を用意し、使いこなせるようにするサポートも引き続き進めていければと思います。生成AIを使いたいと思われてる方は、メルカリグループにジョインするとチャンスがあるんじゃないかと思います。 @tori:日々技術が進化していくので焦る気持ちもありますが、シンプルに長く使われる、かつLLMにしか解決できなさそうな問題に対してのプロダクトをリリースしていきたいです。 @nu2:メルカリ生成AI/LLMチームは、絶賛優秀な方を募集しています。スライドのQRコードを読み込んで、応募いただければと思います。 本日はご視聴ありがとうございました。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 メルカリのカスタマージャーニーにおける不正防止の取り組み 」の書き起こしです。 @codechaitu:みなさん、こんにちは。Merpay & Mercoin Tech Fest へようこそ。本日は、メルカリ・メルペイのシステムにおいてカスタマージャーニーでの不正防止の取り組みについてお話しします。 まずは、自己紹介します。私のニックネームは、@codechaituです。大学を卒業して2018年にメルカリに入社しました。その後、CRM関連のツールを社内で構築するプロジェクトなどいくつかのプロジェクトに関わりました。2022年11月に不正取引についてもっと学ぶため、メルペイへ異動しました。 トピックに入ります。メルカリのカスタマージャーニーとはどういう意味かを理解するにあたって、タイムラインを見ていきましょう。まず、お客さまがいます。 次のステップとして、アプリの登録を行います。 同じルートを皆さんがたどることになります。ここでは、新規のお客さまや既存のお客さまからの不正を防ぐために最大限の注意を払っています。 次のステップでは、私たちは不審なお客さまか普通のお客さまかをチェックします。では、見ていきましょう。 すでに利用制限がかかっている不審なお客さまが、ある商品にいいねをしたがっていますが、このときに制限がかかっています。同様に、コメントを入力しようとすると、やはり制限がかかっています。 同じように、もしこの人が商品を購入しようとしても、購入は制限されています。 続いて、普通のお客さまについてです。 普通のお客さまがホームページで何か商品を購入したりいいねをしたりしようとすると、それが実行できます。商品を購入することも可能です。制限はありません。 そのお客さまが不審だと思った場合だけチェックをして、不審ではないと判断されれば、彼らはその後、任意の取引を行えます。 次は、取引を実行するステップに進みます。 この取引は、商品を購入することや商品を販売すること、いいね!として登録することなどを意味しています。 ここでは、お客さまが取引を実行すると、一連のステップがさまざまなマイクロサービスを経由して行われます。私たちのマイクロサービスまたは私たちのチームTrust&Safety(TnS)チームに通知されます。 それでは、次に私たちTnSチームのシステム概要を紹介します。 ここでアーキテクチャと、どのようなステップを踏むのか、お話をします。ここには四つのステージがあります。では一つ一つずつ説明をしていきます。 まず、最初のステップ・Sourcesです。これは、change data caputure(CDC) や他のマイクロサービスからデータを受け取ります。 例えば、お客さまが商品を見ようとしていいねを押しました。これは、イベントとなります。コメントした場合、また別のイベントとなります。全てのイベントのストリームがあります。これはCDCプラットフォーム上で実行されます。 他のマイクロサービスからのデータを見ることもできます。いいね!を管理するサービスやコメントを管理するサービスなどです。また、3rd partyのデータも使用します。これはTnSチームになります。 二つ目の段階です。データの前処理を行います。なぜこれが必要なのでしょうか?それは、膨大な量のデータが、さまざまなマイクロサービスや他のチームから来るからです。しかし、処理をするためにはこのフォーマットを私たちが処理可能な形式に変更する必要があります。 そのため、前処理を行っていきます。複数のインプットからのデータを処理します。 ここで二つ、チームで開発された機能で私が最も気に入っているものがあります。 まず一つ目は、あんしん支払い設定、英語に言い換えると、Safe payment settingsです。 この機能について見ていきましょう。 これはダミーの例です。メルカリのお客さまが、フィッシングサイトとは知らず、攻撃者が開発したサイトにIDやパスワードを入力してしまった場合です。メルカリのお客さまは、フィッシングサイトであるということを知らないので、お客さまは認証情報を入れてしまいます。攻撃者はその認証情報を使って実際のWebサイトにログインします。 これが新しいデバイスからであれば、メルカリは実際にお客さまであるかどうかを確認します。そのときにOTP(One Time Password)を送信します。攻撃者は、フィッシングサイトでも同じOTP画面を表示させようとします。お客さまがフィッシングサイトにOTPを入力すると、攻撃者がOTPを取得し、実際のメルカリアプリに侵入します。このように、メルカリへの不正アクセスが可能となります。 これを防ぎ、メルカリのお客さまに金銭的な損失を与えないようにするために、ある種のロジックを用意しています。 簡単な例で見ていきましょう。新しいお客さまのログインがあった場合です。ここで新しいデバイスからかどうかをチェックします。新しいデバイスの場合には、あんしん支払い設定のお客さまオプションを有効にします。このときお客さまは支払不能となりますが、後であんしん支払い設定を無効にできます。 新しいデバイスでなければ、あんしん支払い設定は発動せず何も変更されません。お客さまがフィッシング攻撃によって金銭的な影響を受けないことを保証しています。これがあんしん支払い設定機能です。 二つ目の機能は、 3D Secureです。 メルカリのお客さまがクレジットカードを使ってメルカリで「商品を買いたい」とリクエストします。メルカリは、クレジットカード発行会社にリクエストを出します。これが実際のお客さまのものであれば、カード会社はこれは低リスク取引と判断します。 しかし、他の悪質な行為者が、クレジットカードを盗み、メルカリで購入しようとした場合、メルカリは、悪質な行為者が使用したカードを認証するために送信し、ある計算に基づいてリスクをチェックします。 悪質な行為者がOTPやパスワードを扱おうとしても、メルカリ側で、ハイリスクであると判断されれば取引が拒否されます。 お客さまの購入取引がある場合、それが正しいかどうかをチェックし、問題がなければ取引を継続します。もし不審な取引であれば、3D Secure機能を利用することになります。 3D Secureの認証によりその取引がSecureと判断された場合、取引は続行され、そうでない場合は取引が拒否されます。 次のトピックは、Rule Engineです。複数のソースからのデータを処理し、チームがデータを利用して結果を得ることができます。これが不審なトランザクションがどうか、その発見に使用します。現在は、Rule EngineとしてSplunk Cloudを使用しています。 システムには、多くのルールがあります。それを使って、疑わしい取引かどうかをチェックします。次のセクションで例を挙げます。 現在私たちは、バッチ処理でSplunkを使用しています。最近では、リアルタイムの不正検知を行うようになり、そこではApache Flinkを使っています。 なぜApache Flinkを使うのか、お話しをします。 私が個人的に検討した二つのオプションを比較します。一つはGoogle Dataflow、もう一つはApache Flinkです。 Google Dataflowの主なメリットは、フルマネージドサービスであることです。高負荷時には自動スケーリングが有効になります。これは本当に良いオプションです。 しかし、デメリットもあります。デベロッパーサイトではチェックポイントを実装できません。また、Flinkが提供している高可用性オプション、Dataflowでは99.9%のSLAはありません。 そして、私たちにとってかなり高額になるからです。そこで私たちは他の選択肢を探しApache Flinkを見つけました。 ApacheFlinkのメリットは、Check PointingやSave Pointingができることです。OSSなので、Kubernetes上にジョブをデプロイでき、デバッグも簡単にできます。また、社内でFlinkに取り組んでいるチームがありますので、必要に応じて彼らがサポートしてくれます。 デメリットは、全てのリソース管理をしなければならない点です。これらの選択肢のメリット・デメリット・他のいくつかのパラメータを勘案し、Apache Flinkの採用を決めました。 これは、TnSでのアーキテクチャの概略図です。 他のバックエンドのマイクロサービスからPub/Subへ、ほとんどのインフラはGCP上にあります。インプットPub/Subトピックから前処理に行き、FlinkとトピックPub/Subへリアルタイムでデータ処理を実現したいので、事前処理されたデータはKubernetes上のFlinkに送られます。 Flinkには、ルールや実行すべき定義されたロールが含まれています。このFlinkがデータを処理し、アウトプットPub/Subトピックに送信をし、データは他の下流のサービスで使われます。 例を挙げましょう。 あるお客さまが6時間以内で100万円以上使った場合、疑わしい取引であるとみなされます。 以前のマイクロサービスと違う事として、小さな変更を変えました。Cloud Schedulerを使って、ある時間枠内にイベントを送信するようにしました。 すでにデプロイされており、ジョブマネージャー、タスクマネージャーが動きます。クラウドSchedulerを作成し、FlinkSQLと命名し、毎分実行されます。 実行後、Pub/Subトピックが置き換えられ、データを取得ができます。この例のテストデータでは、ユーザーID「1234」を使用しており、総購入額が収録されており、100万円を超過していることがわかります。 ここでは、処理するべき多くのルールがあります。開発時に開発環境にデプロイして出力結果が期待通りかどうかをチェックするのは、簡単な作業ではありません。そのため、デプロイを行う前に、ローカルでデバッグを行います。 全ての手順はGitHubにあります。サンプルデータを提供していますので、ご自身のローカルマシンでFlinkの使い方を試してみてください。 次は、ローカルでデバッグするための例です。 SQLのゲートウェイを使って、Flink SQLのジョブのデバッグをします。最初のステップは、SQL クライアントの初期化でFlinkで実行しています。init scriptを実行しています。これで、必要なSQLの処理を実行できるようになりました。Flink SQLのクライアントも起動しています。 user_transaction_sourcesというテーブルを作成しました。これは前の例でCloud Schedulerが全てのデータを送信するソースデータとなります。しかし、今回はローカルのテストデータを使うので、ユーザーID、商品ID、購入金額、購入時間など、いくつかのパラメータだけを取って、その他のいくつかのフィールドにデータを入力します。 もう一つのテーブルには疑わしいユーザーIDとそのお客さまが6時間以内で使った合計金額が表示されます。100万円に達した場合のみ、フィルタリングされます。 ここまでで不審なお客さまのテーブルが前のステップで作成されました。合計金額が100万円を超え、かつ6時間以内であれば、疑わしい取引であるとみなされます。この情報は、他の下流サービスにも送信されます。このデバッグステップでは、ロジックの正当性を確認します。 insert.SQLを使います。テストのデータを見ると、四つのレコードがあります。私が最も注目しているのは、ユーザーID 1234です。3回の購買があり、過去6時間以内で100万円を超える支出をしています。このお客さまの取引は、疑わしいと考えられるでしょう。 さらに、insert.SQLを取り、コピーをし、Flink SQL クライアントで実行すると、ユーザーID1234が選択され、予想通り100万円を超えている状況です。Flink SQLクライアントでジョブIDの出力を確認できます。ジョブID、「541完了」となっています。完了すると、 データの出力先が表示されますが、ここではexample directryとなっています。ここでも、ユーザーID1234、100万円を超える購入金額が見れます。 もし万が一エラーが発生した場合、ログのセクションでなぜエラーが発生したのかというステートメントがありますので、これを使ってデバッグできます。これが、リアルタイムで不正検知を行うために、Apache Flinkで実装したステップです。 次に、出力処理です。これが私たちのチームでは最後のステップです。ブロック単位として考えた場合、ここではお客さまの取引が疑わしいか否かを、マニュアルでチェックします。 疑わしいと判断された場合、社内のカスタマーサポートチームが判断した上で、そのお客さまに対して疑わしいというマークをつけ、その後、お客さまに何らかの制限をかけるステップが取られます。 例えば、何日以上は売ってはいけない、何万円以上は買ってはいけないといった制限です。これは、取引ごとに変わります。出力データに基づいても変わってきます。 さらに、GCPのMemorystoreを、重複するイベントをフィルターするためにキャッシュとして使用しています。 これは、ApacheFlinkが疑わしい取引を見つけた場合や大きなアプリケーションのウィンドウを使用する場合、複数のイベントが発生してしまいます。それらにフィルターをかけるために、GCPのMemorystoreを使用します。 最後に、このような活動をする大きな理由は、メルカリの不正取引を防止し、全てのお客さまに安全な環境を提供したいからです。わずかな時間のご説明となりましたが、私たちは全てのお客さまにとってより良い場所をつくるために日々努力をしています。 お客さまが安全な取引を続けることができれば、お客さまの満足度・信頼度が上がるでしょう。それが私たちのミッションでもあります。 以上です。ご清聴ありがとうございました。ぜひ、Apache Flinkをお試しください。
アバター
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 Building a Global environment at Merpay: India & Japan 」の書き起こしです。 @robert:皆さんこんにちは。本日はセッションにご参加いただいてありがとうございます。「Building a Global environment at Merpay: India & Japan」というタイトルのディスカッションです。 @robert:私はRobertといいます。ヨーロッパ出身です。過去にはいろいろな国で仕事をしておりまして、メルペイには5年ほど前に入社しました。元々はバックエンドエンジニアをしておりました。今はエンジニアリングマネージャーをしています。 @keigoand:私はブラジル出身のKeigoと言います。私はわりと早くコンピューティングエンジニアリング、ソフトエンジニアリングを始めました。ドイツに引っ越し、インドオフィスのチームと仕事をしていたこともあります。メルカリに入社したのは2021年で、エンジニアのマネジメントを数年前から務めております。 @Sumil:私はSumilと申します。私はエンジニアリングマネージャーとしてメルカリのインドオフィスから関わっております。メルカリインドには2023年2月に入社し、Growth Platformで仕事をしています。メルカリに入る前は、Amazonで5年ほど経験を積み、10年ほどCiscoで仕事をしておりました。 3人ともインドのオフィスからライブで参加しています。このトピックに大変わくわくしております。 @robert:昨年のMerpay Tech Festに参加した方もいらっしゃると思います。その当時私たちはグローバルなプランについてお話ししました。 インドのCenter of Excellenceを開設 するということで、当時は何が起きるのか、誰が関わるのか、どういう課題があるのかもわかっていませんでした。 しかし、メルカリのバリューである「Go Bold」を元に勇敢に進め、1年ほどいろいろな経験を積みました。今日は、その進捗を共有します。 参考記事: 【書き起こし】 Building an inclusive multicultural environment at merpay: Past, Present and Future – Tim、Robert【Merpay Tech Fest 2022】 メルカリは、「Marketplace」と「Fintech」にわかれています。Marketplaceは、フリマアプリ「メルカリ」で誰でも安心して簡単に売り買いが楽しめるプラットフォームを提供し、FintechはiD決済などのメルペイ、アプリ内で完結するクレジットカード「メルカード」などさまざま金融サービスを提供しています。日本国内の開発で、「Marketplace」と「Fintech」はお互いに連携しています。 インドオフィス設立時、どこの開発を担当してもらうかを考えました。日本国内の開発と同様、「Marketplace」と「Fintech」の開発両方に関わってほしいという気持ちはあるものの、「Fintech」の開発は難しい部分がありました。日本の金融サービスということで、セキュリティや言語などについての検討が必要です。そこで、「Fintech」領域ではそこまで金融特有の課題が少ないGrowth Platformの開発に関わってもらっています。 @keigoand:マーケティングもグロースの一つです。いろいろなツールやサービスがキャンペーンに使われています。例えばバナーやランディングページ、通知、インセンティブなどでお客さまとコミュニケーションをとります。 Growth Platformのエンジニアリングはメルペイに所属しています。日本リージョン全体に提供するもので、影響力がたくさんあります。 また、いくつか決定的な瞬間がありました。1年ほど前、10月にバンガロールのオフィスを訪問しました。当時すでにいくつかのチームメンバーがインドのオフィスに参加していて、メンバーの選考やオンボーディングをしました。10月は、このチームにとっては大変決定的な瞬間で、インドのオフィスでは重要なタイミングでした。選考した人たちが今も在籍しているので、それは良かったと思います。 そして1月には、開発プロセスをよりよくするためにスクラムの改善を検討・調整しました。 2月にマネージャーとしてSumilが入ったことで、コラボレーションをさらに強化できました。それまでは、多くの人たちが入社して問題も多かったのですが、メルカリの10周年の頃に落ち着いてきました。 @Sumil:私は2023年2月頃に入社しました。 3月に、日本オフィスを訪問する機会がありました。オフィスに訪問するまでは、まだまだまとまっておらず、うまくいったことも改善しなければならなかったこともたくさんありました。どこを調整すべきなのかを直接お話しする、良い機会となりました。 その副次効果として、お互いの関係をチームの中で構築できたことはよかったと思います。そして、お互いに安心できるようになりました。以前のコミュニケーションはオンラインだったので、直接会ったのが良かったと思います。 当時の方向性として組織が考えていたのは、インドチームを自律的にしようということです。そして次の四半期の責任やロールを検討し、インドからテックリードのロールを持つというのが一つの方向性として決まりました。 それ以降、「自律性を持つ」という方向性に基づいて、4月頃にインドチームが日本オフィスをサポートし、より自律できるようになりました。それ以降、日本人がインドオフィスを訪問してきました。これによって、さらに絆を深めることができたと思います。 @robert:続いて、どのような課題があったのか、どう乗り越えたのかをお話ししたいと思います。 @keigoand:この写真は10月に私がバンガロールに来たときの写真です。ホワイトボードでの打ち合わせを行い、対応すべき課題が見えてきました。例えば、当時はやらなきゃいけないこと・やりたいことはありましたけれども、そのプロセスがあまり明確ではありませんでした。 またカルチャーも異なっており、それも課題だったと思います。カルチャーは何なのかという定義も必要ですが、言語の壁もありました。それらの課題というのが結果的には強みにもなったと思います。期待値や人材の採用といった課題もあったので、次に、深掘りしたいと思います。 プロセスの不明瞭さについてはどう対応したのでしょうか? @Sumil:プロセスについて、コンテキストを明確にしました。インドと日本だけではなくさまざまな国籍の人たちがチームに関わっています。連携をし始めたときに何が面白かったかといいますと、チームがスクラムをしようと言ったときに、スクラムを普遍的に理解できたことです。それがチームの基礎となり、どうやって実行するのかという基盤になったと思います。 全てを教科書通りに行っていたのですが一つ例外があります。それは日々の進捗についてです。毎日30分くらいの定例を行っていました。チームの何がうまくいったのか、昨日やったのは何なのか、今日何をしなきゃいけないのかを共有しました。ディスカッションがある一定領域を超えたら、そこから派生してもいいということになりました。それが6、7ヶ月ぐらいうまくいっていたと思います。 もう一つ補足したいのが、みんな何かしらのアクセントがあることです。喋るとき、アクセントがわかりづらいというときもあります。それから面白いのが、誰かがミーティングを行っている間、何かを書き留めています。それによってメンバー全員に内容が伝わったと思います。 また他のドキュメントもあります。たとえば、ディスカッションしたかがわかるデザインのドキュメントも使いました。これが、そのコラボレーションで役に立ったと思います。 @keigoand:ただ、全てパーフェクトにはなりません。スクラムでも制約があります。ただ、クリアなプロセスがあるということで、これは非常に役に立ちました。さらに、ドキュメンテーションもなかなか簡単ではありません。またコミュニケーションにも時間がかかるということもあるんですよね。 また、柔軟性も非常に重要だと思います。文化が違うところもあるので、これが我々の強みだと思います。 @Sumil:我々は文化の違いの話をしてきましたが、個人個人でも違うわけです。非常に多様性があります。メルカリでも個人個人のレベルでも尊重していると思います。 例えばディスカッションしていると、問題に対して楽観的に見ています。しかし、個人個人、ある部分を見てないこともあるかもしれません。そうすると誰かがこういう非常にクリティカルな過程からそれを見たときに、その人が問題を提起することもあります。 そういう意味でそれぞれのこの意見の違いを尊重しています。そうすることで、正しい意思決定ができると思います。開発の中ではこういったものを、やっていくわけですが、プランニングデザインのディスカッションでも同じようなことが起こると思います。また楽しむということも大切です。例えば新しい言葉を学ぶということを楽しんでいます。 @keigoand:私は食べ物に関連する言葉をよく学びました。技術的なところでもそうだと思います。技術的な言葉を学ぶのは面白かったです。 @keigoand:言語にいろいろなバリアがありましたが、もう1年くらい経っています。Global Operations Teamのサポートでチームミーティングや1on1などいろいろなミーティングをしています。 DeepLは、翻訳ツールです。日本語・英語に翻訳することができます。情報を共有したいときにはDeepLをよく使います。 @robert:日本人以外にも非常に多くの人たちが日本語をしゃべります。ただ、言語を学ぶというのは時間がかかりますよね。 ある日、インドオフィスのエンジニアが英語で書いたテキストを日本語に変えました。そして日本人のカスタマーサポートメンバーが回答を日本語で書いて、さらにDeepLで翻訳してといったコミュニケーションをSlack上でとっていました。これはいい例だと思います。 @keigoand:このメッセージが日本語だったら、「この言語は知らないから」と無視してしまうかもしれません。しかし、カスタマーサポートのようなチームでは日本語の方が都合がいいこともあります。一方、エンジニアはドキュメンテーションは英語の方ががいいと言っています。お互いに話をするということが必要な際に、DeepLは非常に役立ちます。 @Sumil:続いて、Availabilityについてです。これも、非常に面白い内容です。 インドと日本では、Timezoneが違います。日本は3時間半早いわけですよね。このチームの中には12時間のAvailabilityがあります。 また、オーバーラップの時間があるということもあります。このオーバーラップを1人当たり持つということで、ここでコラボレーションができるという利点があります。祝祭日がありますが、グローバルですから、誰かがすでに働いています。よって業務に支障は出ません。 本番へのアクセス、例えばインシデントが起きた時にどうするのかという問題があるのですが、法的な要件がありますから、それ以外のところでは、本番環境にアクセスするときにはアクセスコントロールがあります。 @keigoand:また、期待ということで、これもみんなでお互い理解するのがいいと思います。コミュニティ・委員会のようなものがあり、Site LeadやCTOがいて、そういった人たちとも定期的にミーティングをしてサポートしてもらっています。 特に最初はより多くのミーティングを設け、お互いを理解するようにしています。またそれを文章化しています。いろいろなポイントについてお話をしますので、期待値を文章化したドキュメントがありました。 @keigoand :もう一つはHiringです。 インドオフィスの立ち上げを成功させるために、理想的ではないけどエンジニアリングマネージャーを採用する前に、現場のメンバーから採用をしました。エンジニアリングマネージャーを採用することは、より時間がかかるからです。プロセス自体はきちんとルール付けをしました。 Hiringだけではなく、採用後のチームの仕組みなどのオプションについてもマネージャー同士でディスカッションしました。 特に新しいことを始めるときにはいろいろな人を採用します。そこでは完璧な方法といったものはありませんが、プランが1回策定されるとあとは楽になっていきます。 @Sumil:それから、チームビルディングについてもたくさん話をしました。これは、日本人がインドのオフィスに来た時のものです。エンジニアたちがいろいろなことを一緒にやってるということで、非常に大きな絆を作ることができました。 インドオフィスがオープンして1年になるので、パーティを今週行います。非常に良いタイミングだと思います。このような形の新しい写真が各々増えていくと思います。 参考記事: Japan’s Largest C2C Marketplace Mercari, Expands Presence in India -Expands GCoE footprint and inaugurates new office- @robert:最後に、今後についてお話ししていきましょう。 @keigoand:我々が改善すべき点は、監視やインシデント処理です。進捗は良いですが、さらに進めていく必要があります。また、サービスの統合をMarketplaceやメルペイチームと一緒に進めていきます。 @robert:また、メルペイ/メルコインについてです。Fintechは日本で行うため、法律の規制が伴います。ここでも、ベストプラクティスを共有することが大切だと思っています。 オペレーションやOKRを調整することが必要になると思います。日本リージョンからスタートして、メルカリ全体のために我々は仕事をしております。またいろいろなOKRのプライオリティの調整はなかなか大変ですが、今後も成長していきたいと考えております。こういったセッションは、ぜひ来年もやりたいです。 以上です。ありがとうございました。
アバター