TECH PLAY

株式会社メルカリ

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

267

Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 社内用GitHub Actionsのセキュリティガイドラインを作成した話 」の書き起こしです。 @goro:今回、「社内用GitHub Actionsのセキュリティガイドラインを作成した話」というタイトルで発表させていただきます。株式会社メルコインで、バックエンドエンジニアをしております。Toshiki Kawamuraと申します。よろしくお願いします。 私は株式会社メルコインに、2022年の6月に入社しました。メルコインでは主にビットコイン取引サービスの立ち上げに参画して、バックエンドエンジニアとして開発運用を担当しております。 今回発表の題材になった、GitHub Actionsのセキュリティガイドラインの作成などの取り組みも関わっております。 まず発表の最初として、今回テーマに置いているGitHub Actionsのセキュリティガイドラインについて説明させていただきます。 GitHub Actionsセキュリティガイドラインとは、社内でのGitHub Actionsの利用の広がりに合わせて、社内有志によって検討策定された。セキュリティのガイドラインになっております。 GitHub Actionsを使うにあたり、どういった点に留意すれば、最低限の安全性を確保できるか学習してもらいたい、定期的に本ドキュメントを見返してもらい、自分たちのリポジトリが安全な状態になっているかを点検する際に役立ててもらいたいという想いに基づいて作成されているガイドラインになります。 ガイドラインは、合計4人のチームで作成しました。 まずは株式会社メルカリのSolutions Teamに所属している@vvakameさん、株式会社メルカリのバックエンドエンジニアをしている Motonori Iwataさん、株式会社メルコインのエンジニアリングマネージャーをしている sadahさん、僕の4人の有志メンバーで、このガイドラインを作成していきました。 それでは、本発表の流れを事前に紹介します。まず最初にガイドラインの中身を一部紹介いたします。その後に、ガイドラインの社内での活用状況について紹介した後に、最後にガイドライン策定の裏話ができればと思います。 それでは発表に入っていきます。まずはガイドラインの中身を一部紹介します。 ガイドラインを策定する上で、まずはチーム目標を決めました。 まず一つ目に「常に達成したいこと」として決めたのは、「外部の攻撃者からの攻撃を防ぐこと」。二つ目に、「可能であれば考慮したいこと」として、「内部と同等の権限を持つ攻撃者からの攻撃を防ぐ」。この二つを大きな目標に置いて、ガイドラインを作成していきました。 ガイドラインの構成は3部になっています。 まずはどのような脅威があるのかを知ってもらうという意図を込めて、第1部では、脅威を知るというテーマで、GitHub Actionsを利用するにあたって、起こりうるセキュリティ上の脅威を紹介しました。 2部では、1部で紹介した脅威に対して、どのような対策が取れるのかを紹介しました。 最後の3部では、主に2部の内容をベースに、どのような項目を満たすと、実際にセキュリティの対策ができるのかがわかりやすくなるように、チェックリスト形式で行って、ほしい対策について、具体的な設定方法を含め、記載しました。このような内容でガイドラインは構成されています。 それでは1部から紹介していきます。 脅威として紹介したのは、「権限設定の不備を突く攻撃」になります。プルリクエストを契機に起動するトリガーは攻撃者が何かを仕掛ける余地が大きく、不注意にワークフローを構築すると、シークレットを外部に送信されて攻撃を受ける可能性があります。 補足情報として、GitHub Actionsのトリガーにはどのようなものがあるかを紹介していきたいと思います。 例えば、ワークフローのリポジトリで発生したイベントです。例えば、リポジトリのDefault Branchにプッシュが行われたときや、リリースが作成されたとき、あるいはIssueがオープンされたとき。 プルリクエストが作成されたときなどに、ワークフローを実行するように設定することが可能です。 他にも、GitHubの外部で発生し、GitHubでリポジトリディスパッチイベントを発生させるイベントですとか、時間指定での自動実行など、さまざまなトリガーがあります。 そのようなワークフローの権限設定において不備があると、例えばシークレットが外部に送信されるなどの危険性があります。 ここで、外部に送信される方法として考えられるものをいくつか記載しました。例えば、ビルドスクリプトに細工をする、依存関係にあるライブラリを悪意のあるものに差し替える、自動実行の仕組みに相乗りされる(例えばnpmのpreinstallやpostinstall)。 また、過去にも実際に人気のライブラリでローカルファイルをスキャンする事例がありました。このような方法でシークレットが外部に送信される危険性があります。 それ以外にも、権限設定の不備があると、攻撃が行われるような影響が考えられます。 例えば、攻撃者に悪意のあるActionsや、侵害されたActionsによってGitHub Actionsの計算リソースを不正に利用される可能性が考えられます。 他にも、侵害された または 悪意のあるActionsによって、リポジトリの自動ワークフローが中断される可能性もあります。他にもDeployment Keyやアクセストークンなどの、シークレットへの読み取りアクセスは攻撃者が他のリソースを侵害するために利用される可能性があります。以上が権限設定不備があった場合に、起こりうる事象の紹介です。 次に紹介したいのは、インジェクションによる攻撃です。一見安全に見えるワークフローにおいても、コードやコマンドインジェクションを引き起こす可能性があります。 事例を二つ紹介します。まず、事例の一つ目になります。スライドのコードを見てください。 このコードにはインジェクションの脆弱性があります。コメントを二重括弧で囲っている箇所がありますが、ここに1+1のようなものを入れると、Actionsは内部で二重括弧のあたりを補完するためにlodashを使っているため、Node.jsのコードが実行され、出力が2になります。 二つ目の事例です。ワークフローのインラインスクリプトに直接インジェクションを配置するシナリオも考えられます。また、ブランチ名やメールアドレスへのコマンドインジェクションも可能です。 ここで具体的に紹介するのは、こちらのコードです。 内部の式の二重括弧が評価され、結果の値に置き換えられるため、コマンドインジェクションに対して脆弱になる可能性があります。ここではプルリクエストのタイトルが二重括弧に囲まれています。 攻撃者が実際にどのようなことができるかというと、「a”;ls $ GITHUB_WORKSPACE*」というタイトルのPRを作成する可能性があります。これを利用して、ステートメントを中断し、ランナーでコマンドを実行できるようになっています。実行すると、lsコマンドが確認できます。 インジェクションが起こると、どのような影響があるかを紹介します。インジェクションをされると、攻撃者は任意のコマンドを実行できるため、外部のサーバーにシークレットを送信するHTTPリクエストを行うことが可能になります。 リポジトリへのアクセストークンを取得しても、ワークフローが完了すると失効するので、攻撃自体は簡単ではありません。しかし、攻撃者が自動化し、管理するサーバーにトークンを呼び出してコンマ数秒で攻撃を実行することが可能です。 その場合、GitHub APIを利用してリリースを含むリポジトリのコンテンツを変更するなどの影響も考えられます。 なので攻撃者は悪意のあるコンテンツをGitHub Context経由で追加できるので、潜在的に信頼できない入力として扱う必要があります。以上が第1部「GitHub Actionsでの脅威を知る」の紹介でした。 第2部は「対策を考える」です。 まず対策の一つとして紹介したいのは、「最小権限の原則に従う」ということです。最小権限の原則は、ソフトウェアがタスクを達成するために必要な最小限の権限セットで実行されるべきであるという原則です。 ワークフローで利用可能なシークレットの権限と、ワークフロートリガーの種類に基づいて自動的に提供される一時的なリポジトリトークンの両方に当てはまります。 この原則に従うと、GITHUB_TOKENの権限のデフォルト設定は、読み取りと書き込み権限から読み取り専用に変更した方がいいと思います。 実際にこの設定をやろうとすると、リポジトリのSettings > Actions > Generalから変更できるので、ぜひ変更してみてください。 また、GitHub Actionsの権限はジョブ単位で設定を行うことで、権限を最小化できますのでなるべく権限は細分化して設定することが推奨されます。 シークレットの利用についても、いくつか対策があります。例えば、Long=lived tokenを使用しない、Workload identity federationを用いたSecret Managerの利用を検討する、JSONなどの構造化データをシークレットにしないことがあげられます。 3つ目については、なぜかというと、GitHub Actionsは、全文をマスクデータとして扱ってくれますが、部分マスクはされないためです。 ワークフロー内で使用される全てのシークレットマスクをマスクするように登録することも、対策として考えられます。シークレットに保存されたアクセストークンの利用状況を観察することも必要です。 他にもスコープが最小限のクレデンシャルを使用する、登録されたシークレット監査およびローテーションする、シークレットへのアクセスについてレビューを要求する。こういった対策が考えられます。 次は、イベントトリガーの対策です。 利用すべきイベントトリガーとして、リポジトリへのwriteはできないように制限されているので、プルリクエストの処理にはpull_requestイベントを使った方がいいです。 少し制限を緩めたものとして、pull_request_targetがあります。Github Actionsのワークフロー自体は、pull_request_targetだと、Default Branchのものが使われます。ワークフローのyamlに直接記載する場合は、攻撃者によって上書きされることはありません。チェックアウトしたコードに含まれるComposite Actionを使う場合は注意が必要となります。 ここで、Composite Actionについて補足させてください。Composite ActionはカスタムActionの一つであって、使用することで、ワークフローの複数Stepを組み合わせて一つのActionsにできます。 例えば、複数のrunコマンドを一つのActionにまとめて、そのActionsを一つのStepとしてワークフローから呼び出して実行することが可能になってきます。 なのでpull_request_targetをイベントトリガーとして使う場合、Composite Actionは、攻撃者によって上書きされる可能性があるので、注意が必要です。 シークレットの内容を露出する際、可能な限り単位を狭くする方がいいです。Job単位よりStep単位の方がより良いと考えられます。Step間のファイルによるデータのやり取りは、全ステップから可視であると考えてください。 Jobは処理によって分けることも一つの対策になります。例えば、テスト/ビルド/デプロイはそれぞれJobを分けた方がいいです。これはなぜかというと、必要なGithub ActionsのPermissionやクラウドプロバイダーの権限を制御できるためです。 次に紹介したいのが、Dependabot / Renovateを利用したGithub Actionsでの更新になります。Actionsはバグの修正や新機能によって、頻繁に更新されます。Dependabot / RenovateでGitHub Actionsの依存関係を最新に保つことができるため、設定しておくとより良いと考えます。 次はサードパーティのActionsを利用する際の注意点です。サードパーティのActionsを利用する場合、基本的にFull Changeset Hashに固定するのがいいと考えています。 サードパーティのActionsの書き方は、四つあります。 まず一つ目がFull Changeset Hash。これは基本的には衝突が困難になっています。次にあるのがShort Changeset Hash。これも衝突がしにくいですが、脆弱となっています。次に、よく使われるTag / Releaseです。この場合、タグを後で変更されて意図しない変更が混入してしまう可能性があるので、注意が必要です。Branch Nameの場合意図しない変更が混入してしまう可能性もありますし、将来壊れる可能性があるので、なるべくFull Changeset Hashで指定するのがいいと考えています。 Full Changeset Hashで記入すると、このバージョンを使っているのかがいまいちわからないなっていうのがあるので、その場合はバージョンコメントを記載するのがわかりやすくておすすめです。 同じくサードパーティActionsを利用する際の注意点でもありますが、Actionsのソースコードをしっかり観察して、サードパーティのホストにシークレット送信するなどの疑わしいことがないか確認することが必要です。 ワークフロー内で利用しているサードパーティActionsのAction permissionsの設定をセキュリティ観点で見直すことも推奨されます。この設定に関しては同じくリポジトリのSettingsで可能になっています。不要なワークフローやJobは削除した方がいいです。不要なものは削除して、なるべく依存を減らすのが良いです。 先ほども触れたインジェクションについてですが、これを防ぐためには信頼されない式の入力値を中間環境変数に設定することが、対策として考えられます。 例ではこのようにenvに中間環境変数を入れていますが、この方式は、スクリプトの生成に影響するのではなく、メモリに保存されて変数として使用されます。このように、信頼されない式の入力値を中間環境変数に設定するのも有効です。 他にも、シェル変数をダブルクォートして単語の分割を避ける。これはシェルスクリプトの一般的な水槽事項でもあります。 GitHubのカスタムアクションやワークフローを書くときは、信頼できない入力に対して書き込み権限でコードを実行することがあることを考慮した方がいいです。外部Actionsとなりますが actionlist を使用することで対策できるので、導入を検討したり、GitHub Security Labの開発する CodeQL queries を利用したりすることも対策として考えられます。 それでも完全に攻撃を防ぐことは不可能と考えて、問題が発生したときに受ける影響を最小限に抑える必要があります。 例えば、プロダクション環境に影響を及ぼす(サービス停止など)ことが最悪のケースなので、対策が必要です。もう一つ、GitHub Action がPRを作成またはオーナーとして承認しないようにすることも対策の一つです。 最後に、第3部の「セルフチェックリスト」です。 セルフチェックリストは、定期的にチェックすることで、GitHub Actionsの安全な利用に繋げるという目的があり、ガイドラインで学習した内容が本チェックリストでカバーすることを目指して作成されています。 第2部の内容をベースに、講じてほしい具体的なセキュリティ対策を設定方法含め、チェックリスト形式で記載しています。 ここでは一例を紹介します。例えばCODEOWNERSの設定を見直すことが一つチェックリストにあります。CODEOWNERSというファイルが.githubディレクトリにあるのですが、そこで適切にコードのオーナーが設定されることが必要になってきます。 Protected Branchの設定で、Default BranchへのPull RequestがCODEOWNERSによる承認が必須になっていることも、チェックする必要があります。 続いて、ワークフロートリガーを見直すこと。コードプッシュをトリガーとする場合、pull_requestか、それが難しければ、pull_request_targetを使うことを考えた方がいいです。 on: psuhをpull_request用に使っていたら見直す必要があります。 このように、具体的な対策をチェックリスト形式で書いています。 さらに詳しい内容はブログに公開しておりまして、そちらを見ていただきますと、今回発表したガイドラインの内容が更に詳しくなっておりますので、ぜひご覧いただければありがたいです。 参照 社内用GitHub Actionsのセキュリティガイドラインを公開します 次は、ガイドラインが実際に社内でどのように活用できているかを紹介します。 一つ目の活用状況として紹介したいのが、Developer Documentationの追加です。これは主に、メルカリ、メルペイ、メルコインのバックエンドエンジニアがよく参照する社内プラットフォームの使い方がまとまった社内ポータルです。そこにGitHub Actionsのセキュリティガイドラインを掲載していただきました。 次はSecure Coding Guidelinesの掲載です。このガイドラインはSecurity Teamがメンテナンスする社内基準のセキュリティルールを満たすためのガイドラインです。ここにもGitHub Actionsのガイドラインを掲載しました。 他にも各チームでのガイドライン提供やサポートなどを行っており、プロジェクトメンバーに自チームに関するリポジトリに対して、今回作成したガイドラインの内容を適用させたり、他のチームがガイドラインを適用する際のサポートや質問を受け付けるような体制となりました。 最後に、「ガイドライン策定の裏話」をします。 まず、このガイドラインをどのように作ったかについて紹介します。まず最初に、作成する上で行ったことは、GitHub Actionsのセキュリティに関する文献記事をチームメンバーで読んでいくことです。記事には複数の記事がリンクされているので、それらも読んでいきました。 そこで得たインプットをもとにガイドガイドラインのアウトラインをまとめて、3部構成を作りました。参考文献の設定を試したりしながら、ガイドラインを変えていくフェーズに入り、その後に自分たちでガイドラインをレビューして、あとSecurity Teamにもレビューしていただきました。レビューをいただいた内容を修正して、英訳して公開しました。 ちょうど1年前ぐらいから始まったプロジェクトで、2022年の7月から9月の間にメンバーを招集して、アウトライン・執筆を開始していきました。その後、10月から12月の間にレビューを実施したり、リファクタリングをした後に、2023年に入ってから最終レビューが完了して、正式版を公開しました。4月から6月の間に、エンジニアリングブログでの社外公開なども行いました。 ガイドラインを作成していく中で、気をつけた点についても、4点ほど紹介させてください。 まず一つ目は「小さく始める、無理をしない」ということ。これはボランティアメンバーで、それぞれのメンバーが別のプロジェクトを持ちながら進めていったので、なるべく無理をしない形で、進めていきました。 二つ目が、「絶対完成させるという強い意志を持つ」。こういう有志の取り組みを長期的に継続していくのは難しいかなと思うのですが、でも絶対完成させるという強い思いをみんなで持って、完成させました。 三つ目が、「適切な量のフィードバックをもらえるように意識する」。これはちゃんと外部の意見を取り入れながらリファクタリングできるようにという名目でもありますし、大量にフィードバックが降ってくると修正も大変なので、適切な量になるようにコントロールしてもらいました。 四つ目が、「正式版を公開してから育てていく」。GitHub Actionsやセキュリティなどに関連する技術は、今後も日々アップデートしていくので、正式版を公開したから終わりではなく、今後も育てていくようにしていきたいです。 GitHub Actionsのセキュリティガイドラインは、今後も適切に更新していき、よりスムーズで安全な開発をサポートできるように努めていきたいと思っています。また更新した際には外部向けにも発信していこうと考えておりますので、ぜひご覧いただけるとありがたいです。 以上です。ご清聴ありがとうございました。
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 Keynote 」の書き起こしです。 @kimuras:メルカリFintech領域のCTOを担当しているKimuraです。本日はMerpay & Mercoin Tech Fest 2023をご視聴いただき、誠にありがとうございます。 メルペイが2017年に設立してから、2023年になって決済や与信、クレジットカード、暗号資産など、多くの金融サービスを提供してまいりました。今回は、多くの成功を支えてきた技術をふんだんにまとめ、Merpay & Mercoin Tech Fest 2023を実施することになりました。 また、これら金融サービスを伝える技術的な土台をいかして、これからもFintechサービスを成長させ、「世界をなめらかにする」というミッションを実現していくことを目指し、次世代のFintechサービスを作るためのアイディアやヒントになるような、気づきとなるものをご提供できたら幸いです。 私はKimuraと申します。メルペイ・メルコインのCTOを担当しています。 もともと機械学習領域を担当していましたが、2017年より株式会社メルカリに入社し、研究開発組織R4Dの立ち上げを行い、AIを中心とした幅広い研究領域のリサーチを担当しました。その後、AIと検索エンジン領域のエンジニア組織を設立してDirectorとしてメルカリのAI導入をリードしました。 2022年7月より、社内のプラットフォーム開発を統括するVP of Platform Engineeringを担当し、CTOとして4月から働いています。 まず、メルカリグループのFintech事業を振り返ります。 2023年2月にメルカリグループは10周年を迎え、「あらゆる価値を循環させ、あらゆる人の可能性を広げる」というミッションを新しくしました。 メルペイはフリマアプリ「メルカリ」での売る・買うの取引を通じて信用情報を可視化し、新しい信用の形を生み出して、それに基づいてお金を自由に使える世界を作ることを目指しています。 メルコインはモノ・お金だけではなくて、暗号資産、NFTなど、あらゆる価値をめぐらせて新しい経済を作ることを目指しています。世界中のモノやコト、人には見出されていない価値がたくさんあり、その価値を必要としている人もまた世界中で数多く存在していると思っております。 メルカリグループはテクノロジーの力で世界中の人々をつないで、有形・無形に限らずあらゆる価値が循環するエコシステムを作ることを通じてその人の可能性を広げる存在でありたいと考えています。 メルペイは2019年2月にサービスを開始しました。iD決済・コード決済といった決済領域から始まり、あと払いサービスであるメルペイスマート払いで、与信領域を本格的にスタートさせました。 その後、バーチャルカードやメルペイスマートマネー、メルカリの利用実績等で限度額が決まってアプリで利用と管理が完結できるメルカードをローンチするなど、サービスを拡充しました。 メルカードは提供開始から 約半年で発行枚数100万枚を突破 して、国内で今トップレベルとなっております。そして2023年3月からメルカリアプリ内でビットコインが売買できるようになりました。この開発を担うのがメルコインとなります。 金融機関からチャージした残高はもちろん、メルカリで不用品を売って得た売上金やポイントを活用して、1円という少額から安心して始めることができます。こちらも提供開始から 3ヶ月強で利用者数50万人を突破 しています。 これらのように2019年からサービスの提供を開始して、Fintech領域では1,571万人ものお客さまにご利用いただくまでに成長しました。 メルカリグループで構築する「循環型金融」ということで、ここからこれらのサービスを支える技術について触れていきたいなと思います。 多くのサービスを開発し、約1,500万人ものお客さまに金融サービスを提供してきた成長の背景には どのようなものがあるのかをお話しします。 金融サービスはセキュリティ的な要件が非常に高く、技術的には妥協が許されない領域です。 そしてメルペイの特徴でもある「なめらかな社会を実現する」ため、簡単で便利なUIを提供するという攻めの姿勢と、セキュリティを高めるための守りの姿勢は、基本的に相反する関係にあります。 UIを簡易的にすればするほど、セキュリティは甘くなってしまいますし、セキュリティを厳しくすればするほど、簡易なUIを提供することは難しくなってしまいます。 我々はこの攻めと守りのバランスを保つことで、大きな障害を避けつつも便利なUIを提供することでお客さまの支持をいただいてきたと考えています。 攻めと守りを実現するために、人材投資にもバランスを保ってきているのが、我々を支えてきた源泉だと考えています。 大きく組織を説明しますと、メルペイのエンジニア組織は大きく分けて、プロダクト開発をメインで行っているProduct Engineeringと、Foundationやプラットフォームの開発を行っているPlatform Engineeringの二つの組織があります。 メルペイは設立当初から現在までProduct EngineeringとPlatform Engineeringに対して、バランスよく人材投資を続けており、人数はおおむね同じ割合となっております。 なめらかなUI提供をするためにProduct Engineeringは重要ですし、堅牢なFoundation Platformを構築するPlatform Engineeringと同じ割合で投資することで、複雑なシステムであってもSecurityやAvailabilityを担保し続けているという背景があります。 Fintechの成長を支えてきたテクノロジー投資についても説明します。多くのFoundationへの投資の中でも、今回は不正対策、セキュリティ、リアーキテクチャ、独自の与信モデルについてご紹介したいなと思っております。 基本的に不正対策はルールベースのものもありますが、機械学習をメインに活用し、Vertex AI Pipelinesを導入してモデルのトレーニングやデプロイを共通化しています。また特徴量を共有化することによるコスト削減はFeature StoreとしてFEASTを導入しています。これにより保守性担保やコスト削減だけじゃなくて結果としてエンジニアがモデル開発の改善に、より時間を費やすことができるようになり、生産性向上や品質改善にもつながりました。 メルペイの不正対策に活用しているのが、グラフ理論というものです。節点と呼ばれるノードの集合と、辺と呼ばれるリンクの集合で構成されるグラフに関する数学の理論を活用しています。メルカリ・メルペイには非常に多種多様なデータがあります。お客さまのアカウントの情報や出品、決済、購入といった情報をグラフとして表現し、類似度を計算するといったことができます。 【書き起こし】グラフ理論と不正対策 つながりをデータから解き明かしたい – hmj 【Merpay Tech Fest 2022】 不正対策・不正検知については、今年もセッションを用意していますので別セッションで詳細をご覧ください。 【書き起こし】メルカリのカスタマージャーニーにおける不正防止の取り組み – codechaitu【Merpay & Mercoin Tech Fest 2023】 【書き起こし】発生可能な取引の属性データを用いた素早い不正検知 – Liu / Li【Merpay & Mercoin Tech Fest 2023】 続いて、セキュリティについてです。セキュリティでは3DセキュアやFIDO、パスキーの導入を行ってきました。 3Dセキュアの導入は不正利用を未然に防ぐための対策です。2021年12月に比べて、不正利用数は10分の1まで抑えることができました。図は1年前の状態を示しています。直近でもこの低水準の状態を継続できています。 本件に関しても、去年のセッションやブログで紹介しています。今年も進化した部分であると、2022年11月に、 FIDOアライアンスに加盟 して、メルカリの各種サービスにFIDO認証の実装を進めています。2023年3月にリリースをしたメルコインにFIDOあるいはパスキーを導入しています。 【書き起こし】Credit Card Payment Security: adding 3D Secure SDK for Merpay iOS – Mikael LE GOFF 【Merpay Tech Fest 2022】 【書き起こし】メルカリグループの認証基盤における理想と現状、今後の取り組み – kokukuma 【Merpay Tech Fest 2022】 約2年半かけて、iOS Androidアプリケーションでも同じく、スクラッチから作ったアプリケーションに移行するプロジェクトを進めました。 今はすでにこの新しいバージョンを全てお客さまに提供できておりまして、コードベースもモダンかつコンパクトな状態です。今後は生産性が改善によって上がり、よりよい機能をより早くお客さまに提供できるようになっています。 リアーキテクチャした背景として、メルカリのアプリリリースから約10年近くの月日が経っております。アプリのアーキテクチャの潮流やUI/UXのフレームワーク、OSが提供する機能など、何もかもが大きく変わっています。 またメルカリはこの間に大小さまざまな機能がリリースされており、コードベースも膨大になってビルドやメンテナンスに大きなコストがかかっていました。モダンなフレームワークの導入や新たなデザインシステムの採用などに取り組んできましたし、最終的には生産性の観点からAndroidでもコード量を約半分の程度に減らすことができたということで、大きなメリットを得ることができました。 ほかにもさまざまなチャレンジをしていますので、詳細は他セッションをご覧ください。 【書き起こし】Merpay iOSのGroundUp Appへの移行 – kenmaz【Merpay & Mercoin Tech Fest 2023】 【書き起こし】Merpay iOSにおけるSwift Concurrency対応の挫折と今後 – Takeshi Sato【Merpay & Mercoin Tech Fest 2023】 技術的な観点では、独自の与信モデルも欠かせません。メルペイでは包括信用購入あっせん業者の認定を日本で初めて取得し、勤続年数や年収など、一般的な属性情報だけじゃなく、メルカリが持っているデータ、あるいはメルカリの行動実績に基づいて、信用を判断することができます。独自与信モデルはメルペイスマート払いやメルペイスマートマネー、メルカードなどのサービスで活用されています。 機械学習は不正対策だけではなく、このような独自与信モデルも使っています。詳細は、こちらのセッションをご覧ください。ITエンジニアとITリスクマネジメントのメンバーが密にコミュニケーションをとり、AIの危険性やリスク管理にどのように対応するかを解説します。 【書き起こし】メルペイMLにおける機械学習の品質保証とリスク管理 – shuuk / Haruki Kaneko / Yuki Saito【Merpay & Mercoin Tech Fest 2023】 ここからは、これからの技術的な挑戦について簡単に説明します。 これまで説明したように、Fintech領域でおおむね土台となるサービスを実現してきました。「なめらかな社会を実現する」という意味では、大きな土台が出来上がりました。 大きく分けると、簡単で安全に利用できる決済システムや、メルカリでの行動に基づいたAI与信、簡単に本人確認ができるeKYCサービス、簡単に暗号資産を購入できるメルコイン、アプリで利用と管理が完結するクレジットカードサービスのメルカードなどがリリースされました。 しかし、これでFintechのテクノロジーが終わりではありません。今後もよりなめらかな世界を実現するためには、Fintechサービス外からでも活用できるようにプラットフォームの強化をしていく必要があります。 今後Fintechを解き放ち、より世界をなめらかにしていくためには、三つのポイントが重要だと考えています。一つ目はこれまで培ってきたFintechサービスを生かして、プラットフォームとしてFintech外でも活用されること。 Fintechは単体でも価値のあるものですが、同時にメルカリグループ内でも重要なプラットフォームになっています。今後はよりグループシナジーの強化につながるプラットフォームとしての機能強化や、APIの整備に力を入れる予定です。 二つ目はデータ基盤整備です。Fintech領域では、日々、膨大な決済情報が蓄積され、よりメルカリグループでのサービスのデータ連携を強化することで、お客さまにより価値が提供できると考えています。 しかし、データ量は増えていくと同時に、データをうまく活用するためにはデータの基盤の改善・変革が必要となってきます。情報のAccessibilityとMaintainabilityをより改善していき、安全なサービス開発、そしてグループ間でのデータ連携を実現していきたいです。 最後に、LLMの活用強化も重要です。これまでメルペイでは、不正検知やAI与信でLLMの活用を本格的に行ってきましたが、LLMの実用化によって、Fintech領域でのAIの活用はさらに進化を遂げると考えています。より安全かつ便利で、金融領域に詳しくないお客さまへの洗練されたサポートを実現し、よりなめらかな社会構築に貢献できるのではないかなと思っております。 グループシナジーの強化について簡単にご説明します。メルペイやメルコインでは、決済、与信、信用情報、暗号資産など会社にとって重要な機能や情報を持っています。 これらの機能や情報は、すでにメルカリグループ内で活用は進んでいます。しかし、より今後お客さまに便利でお得にサービスをご利用いただくためにも、メルカードのポイント還元であるロイヤルティプログラムでデータ活用をより促進していくためのデータ連携やAPI強化は重要になってきます。また、メルカリ内で行っているサービスなどにあわせた決算基盤を提供するとか、そういったことがグループ会社として今後重要になってきます。 メルカリではさまざまな新規事業も展開しておりまして、新規事業を実現する上でも、決済機能や与信機能、セキュリティ機能などを提供することによって、新規事業のサービス提供スピードがより向上して価値のあるサービス提供が可能になり、グループシナジーの強化につながると思っています。 また、具体的にはまだ進められていませんが、将来的にメルカリ外にも機能提供する基盤が整備されることによって、メルカリの金融サービスとしての価値向上や、世界の金融サービスの利便性向上にも貢献できるのではないかと考えています。 メルペイではメルカードをご利用いただいてるお客さまに、よりお得にサービスをご活用いただくためにポイント還元をするロイヤルティプログラムを提供しています。 これがデータ基盤にとても関わっており、現在もこのサービス自体は提供しています。提供を続けるには、日々お客さまの売買データを効率的に分析できるように基盤を整備していかなければいけません。 メルカリとのデータ連携を強化するためにも、中間データの整備やデータ構造自体の整備が今後重要になってきます。AIのためのトレーニングデータを作成し、包括的に管理する仕組みが重要になってきますし、特にLLMでは多くのテキスト情報とメタデータの付与が重要になるのでAIに特化したデータ構造や、データパイプラインの構築が今後重要になってきます。 同様にデータ利活用が進めば進むほど、データベースのインフラコストは増大します。 今後 AI活用が強化されている中で、世界中でもインフラコストの最適化は重要なトピックになっています。我々もサービス改善を目的として日々データの量は増加しているので、攻めと守りの姿勢を継続してインフラコスト最適化を進めていきたいです。 最後にLLM活用なんですけども、こちらもセッションが別にございますので、ぜひこちらで議論を見ていただけたら幸いです。 【書き起こし】Merpay & MercoinにおけるLLM活用の取り組み – Yuki Ishikawa / Daisuke Torigoe / Noriaki Utsunomiya / hmj【Merpay & Mercoin Tech Fest 2023】 今回のイベントは、「Unleash Fintech」というテーマで、これからも世の中を便利にしてなめらかな社会を作り、多様な価値をめぐる新しい経済を作り、そして人々の可能性をより解き放つために、テクノロジーの力でまだまだできることがあるという強い気持ちを持って開催させていただくことになりました。 我々がこれまで培ってきたテクノロジーを公開することで、少しでも世界のFintechの進化に貢献できたら、とても嬉しく思います。 そして、これからも技術に謙虚な反省を忘れず、時には大胆に、時には冷静に世の中をよくするために自信を持った技術を使っていきたいという気持ちがあります。 以上です。ご清聴ありがとうございました。
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 Merpay iOSのGroundUP Appへの移行 」の書き起こしです。 @kenmaz:今回は、「Merpay iOSのGroundUP Appへの移行」というタイトルで、iOSチームの@kenmazが発表します。 こちらが私の自己紹介です。 今回のメインテーマであるMercari iOSのGroundUP App(GU App)とは何かについて説明します。 GU Appは、メルカリ本体のコードをフルスクラッチで書き換え、さらにBazelによる高速・高信頼のビルド環境に置き換え、メルカリをイチから作り直すプロジェクトです。 メルカリの開発が始まってから10年以上経って、技術的負債がかなりたまってきている状態になったので、フルスクラッチで書き換えるのが本プロジェクトの目的です。 このプロジェクトではiOSではSwiftUI、AndoirdではJetpack Composeといった最新のUIフレームワーク、さらにDesign System v3.0という社内のUIライブラリを最新版に置き換えることが盛り込まれたプロジェクトです。 フルスクラッチで書き換えるプロジェクトなので、再構築期間中は新規機能開発を凍結し、フルスクラッチでのリライトに集中する流れで進みました。 参考記事 メルカリアプリのコードベースを置き換える GroundUP App プロジェクトの話 メルカリiOSアプリのBazelを使った高速・高信頼性ビルド メルカリアプリの上にはメルペイの機能が載っています。メルペイにおいてのGU Appへの移行について説明します。 メルカリ本体のコードと比べるとメルペイのコードは、比較的技術的負債が少ない状況でした。開発が開始してから、当時で2〜3年ほどしか経っておらず、比較的クリーンなUIKit + Design System 1.5を使っていました。そのため、メルペイのコードについては、フルスクラッチで書き換えるよりも、最小限の変更でそのままGU Appに載せ替える方針を決めました。 さらにGU Appの裏で、メルペイでは新規機能開発の必要性があり、メルペイ自体の開発を止めるわけにはいきませんでした。移行期間中でもメルペイの新規開発は極力止めずにそのまま載せ替えることを目的としました。 こちらがプロジェクト全体の概要です。上の段の「Production」が実際にお客さまに届けるアプリの状況で、下の段の「Development」が社内で開発しているコード群です。 GU Appのプロジェクト自体は2020年4月頃に始まりました。最初はメルカリのコア部分のみをSwiftUIで書き換え、アーキテクチャや全体の構成などを固める作業からスタートしました。 その後2021年10月に全ての準備が完了して、本格的にリライトのプロジェクトが開始されました。この段階は、メルカリ側の機能群の開発を凍結し、全ての開発メンバーはメルカリのリライトに注力する期間でした。この期間が2022年8月まで続くことになります。 一方メルペイについては裏で重要なプロジェクトが動いていたので、機能群自体は変更せず、メルカリ側とメルペイ側のコードを Integrationするため、 IntegrationLayerと呼ばれる中間Layerのみを書き換えることで、メルペイの機能群のコードを変更せずに新しいアプリと古いアプリ両方で同じメルペイのコードが動作するように対応する必要がありました。 そこで IntegrationLayerの開発にメルペイチームが参加しました。このGroundUP Appのプロジェクトが始まった同時期に、社内では重要なプロジェクトがいくつか進みました。 メルペイのトップ画面をリニューアルするプロジェクトや、メルカードというメルカリのクレジットカードを開発するプロジェクト、メルコインというビットコイン関係のプロジェクトなど、いろいろなプロジェクトが走っている中で行われたのがGU Appです。 このメルカリと Integrationの実装が完了し、2022年8月に、Phase1としてGroundUP Appがリリースされました。この時点で、メルカリ側は全てSwiftUIにリライトされ、メルペイ側は今まで通りUIKitで動いています。 その後、2022年10月にメルペイ側でSwift UIで書き換えられたものがリリースされ、残りのメルペイの機能やメルカードの機能はまだUIKitで作られていました。2022年11月にメルカードの機能、翌年にメルコインの機能がリリースされました。 フェース3は進行中ですが、今残っている古いUIKitで作られてる部分を最終的には全部書き換え、最終的には100%Swift UIのアプリにする動きが続いています。 それぞれのPhaseについて説明します。まずはPhase1のMerpay SDK GU App Integrationです。 Merpay自体はSDKという形で開発されていて、比較的メルカリと疎結合の状態で設計されていました。 これにはいろいろな背景がありますが、一つは、メルカリの機能はメルカリが担当し、メルペイの支払いや決済に関する機能はメルペイで開発を担当していて、会社自体がわかれていたのでリポジトリを分けて、設計も極力結合なものにした方がよいという考えで、このような構造になっています。 メルカリアプリとは別にメルペイアプリ単体のアプリを作る話もあがっていたので、かなり疎結合でポータビリティを持った設計になっていました。 参考記事: メルペイのスケーラビリティを支えるマルチモジュール開発 GU App Integrationは4つのステップにわかれています。 一つ目は、準備段階としてメルペイ関連機能のSDKへ集約することです。 Merpay SDKの中にはメルペイの必要な機能が入っていますが、いくつかの機能はメルカリ側に直接実装されているコードもあります。そこで、メルペイが管理すべきコードは一旦全部Merpay SDKに集約することにしました。例えば、本人確認機能や売上金の振り込み申請機能などはSDKに移しました。 これらはUIKit + ReactiveCocoaやObjective-Cで書かれていましたが、一旦メルペイのアーキテクチャに合わせてUIKit + MVVMに書き換えました。 これで全てのコードがMerpay SDKに入ったので、次はビルドの環境を設定変更します。旧メルカリアプリはCocoaPods/Carthage/git-submoduleを使ってビルドを行っていましたが、新しいメルカリアプリであるGU Appでは、全てBazelを使ってビルドを行うように変更されました。 したがって、Merpay SDKを含む全ての社内外のコードをBazelでビルドできるように設定する必要がありました。 merpay-ios-sdkやDesign System、Google Maps SDK、Lottie-iOS、CryptSwiftなど社内外のコードをBazelで全てインポートし、一つのメルカリアプリとしてまとめてビルドできるようにする設定変更が行われました。 次はコーディングの段階です。メルペイにはMerpayDependencyRegistryというDIコンテナのようなものがあります。これを用いることにより、メルカリ側で実装されている機能をメルペイ側に注入できます。 全ての依存関係がここに集約されているので、メルカリ側の実装をメルペイに注入する作業をひたすら行います。実際に注入したコードとしては、Feature Flagやイベントログなど、図にある通りです。 これでメルペイのコードは全てMercari GU Appの上でビルドできるようになったので、最後に画面遷移の実装を行います。 GU Appでは、基本的に全ての画面はSwift UIで作られていますが、画面遷移周りはすべてUIKitで実装されています。Wireframe Layerというものがあり、そこでSwiftUIの画面は一旦UIHostingControllerでUIKitのViewControllerとして変換され、それをUINavigationControllerが画面遷移を制御します。 メルペイの画面はすべてUIViewControllerで作られているので、DependencyRegistryを経由して、UIViewControllerをWireframeにそのまま渡します。あとはWireframe内部で細かい画面遷移の実装を行います。 以上で統合完了です。この段階で1年ぐらいかけて行われてきたGU Appのアプリがリリースされました。メルカリアプリは全てSwiftUIで書き換えられていますが、メルペイの機能が集約されている「支払い」タブに関してはまだUIKitのままの状態です。 しかし、更なる最適化の作業が残っています。 まずはGitリポジトリ統合です。GU Appが始まる前は、mercari-iosリポジトリでメルカリのコードが管理され、merpay-ios-sdk、mercari-jp-ios-coreは別リポジトリとして管理されていました。 GU Appでは、メルカリ側のコードはmercari-groundup-iosという新たなリポジトリで管理されています。メルコインのコードもすべて同じリポジトリに実装されています。しかし、メルペイの機能やメルカリのいくつか残りの機能、たとえばメルカリのToday Extensionの機能やメルペイでしか使用していないDesign Systemのコードは、依然として別のリポジトリで管理されており、Bazelによって都度インポートされる構成になっています。 しかし、この構成には開発効率の観点から二つの問題があります。 一つはリポジトリが分かれているのでメルペイの機能の開発を直接行えないという問題です。メルペイの機能を開発する際は、まずMerpay SDKのリポジトリをチェックアウトして、ソースコードを編集・プッシュして、GU Appに戻って、Bazelでリポジトリをインポートし直してビルド・動作確認、という非常に煩雑なデバッグプロセスが必要です。 また、GU Appのビルドインフラを活用できないという問題もありました。GU Appとメルペイのモジュールはそれぞれ別のビルドインフラ上でCIが実行されます。GU AppはBazelでビルドが行われているのでユニットテストも非常に高速にできます。一方、メルペイモジュール単体のビルドにはBazelは使用していないので、その恩恵を受けることができません。 Gitレポリポジトリ結合では、すべてのコードを単一のGU Appのリポジトリに移動することによって、上記の問題を解決します。 なおDesign System1.5はメルペイでしか使われてない古いUIライブラリなので、これは例外としてこのまま別リポジトリとして、コードフリーズした状態でインポートすることにします。 リポジトリ統合にはいくつか方法がありますが、一番単純なのは、ファイルコピーです。一番簡単ですが、Gitの履歴が消えるという問題がありました。 なるべく履歴を壊さずにソースコードをGU Appのリポジトリに移動する手段として、Subtree Mergingというリポジトリの結合方法があります。これによって履歴は保持されますが、リポジトリ全体をマージしてしまうので、この2〜3年間で蓄積された不要なデータまでマージされてしまいます。リポジトリのサイズが増えることで、CIの時間に影響を与えてしまう問題があります。 そこで、もう一つの解決策としてgit-subtreeコマンドを使って、リポジトリを部分的に結合する方法をとりました。必要最小限のコードのみをピックアップして結合し、かつ履歴を保持することが可能になります。必要なコードをピックアップする必要があるので少々作業が煩雑になってしまいますが、これによってリポジトリの肥大化を抑えつつレポジトリ統合を行うことができます。 参照 Subtree Merging: https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_subtree_merge git-subtree: https://git.kernel.org/pub/scm/git/git.git/plain/contrib/subtree/git-subtree.txt リポジトリに全てのファイルを移動できた後は、Bazelビルドに合わせた最適化を行います。ソースコードのレイアウトの変更や、画像アセットを最適にして無駄なデータを含めないようにすること、Bazelのビルドとビルドターゲットとするために、それぞれのモジュールをBazelのモジュールとして定義する作業などが行われました。 また移行期間中に古いコードに変更が入るとコンフリクトが発生するので、コードフリーズ宣言を行ってコードの変更を禁止することで、コンフリクトを防ぎました。 間違いで変更してしまうこともあり得るので、変更を検知するためのモニタリングの仕組みなども入れ、リポジトリ統合を進めました。 このようにおよそ2ヶ月半ぐらいかけて、26モジュールを段階的に移行し、Gitレポジトリ統合が完了しました。 もう一つの問題は、いくつかのメルカリの機能のモジュールがMerpay SDKに直接依存している点です。これによって、テストバンドルのキャッシュが肥大化してしまい、最終的にCIの実行時間やキャッシュストレージの使用量が増加してしまうことがわかりました。 この問題を解決するために、Merpay SDKのDIコンテナのインターフェースのみを抽出し、別のモジュールとして分離する設計の変更を行いました。 これはモジュールの相関関係を表す図です。「MK Feature modules」がメルカリの各機能を実装したモジュールです。いくつかのメルカリの機能はメルペイの機能に依存しているので、それらはMerpay SDKの中の「MerpayCoreKit」モジュールに依存する必要があります。 ただ、MerpayCoreKit自体はさらに、Protocol Buffersのモジュールや、Design System1.5のモジュールなどの、メルカリの機能にとっては不要なモジュールにも依存しています。それらのモジュールはサイズが大きいので、メルカリの機能モジュールをビルドしてテストしようとすると、依存関係にある全てのモジュールがビルドされ、Bazelのキャッシュとして残り続けて、最終的にCIインフラのストレージの使用量の増加を引き起こしてしまう問題がありました。 そこで設計を変更して、MerpayCoreKitが提供していたコードのインターフェース部分だけをプロトコルとして切り出し、「MerpaySDKInterface」という別のモジュールとして切り出し、メルカリの機能モジュールは軽量なインターフェースモジュールのみに依存する形式に変更しました。 これによってモジュールごとのテストバンドルのキャッシュサイズが300MBから150MBまで削減され、ストレージの使用量の問題も解決されました。 またMerpay SDKの最小限の機能のみをメルカリ側に整理・公開する設計にしたので、SDKの債務の明確化が行われ、SOLID原則に従ったSDKの設計にも貢献できたという副産物もありました。 参照: Single Responsibility Principle in SOLID 以上で、Integrationのプロジェクト自体は完了しました。GU Appリリース後、Phase2として、次はメルペイの新規開発画面をShiftUI+GUアーキテクチャで開発するプロジェクトが始まりました。 ここで、メルカリアプリ自体のアーキテクチャの変遷について振り返っていきたいと思います。 旧メルカリアプリでは、10年の間に様々な内部的なアーキテクチャの変遷がありました。最初は純粋なMVC、そこからMVVM+ReactiveCocoa、さらにMicro View Controller + Stateアーキテクチャに変遷しました。しかも全てが変遷していたわけではなく、部分的には古いMVCが残っている状況でした。 一方メルペイは独自のシンプルなMVVM Without 3rd party libsというシンプルなアーキテクチャを採用していました。このように、GU Appの前はいろいろなアーキテクチャが一つのアプリの中に共存している状態でした。 参考資料 Mercari iOSにおける きらやばArchitectureとAutomation Mercari iOSクライアント Re-Architectureのその後 / After Re-Architecture of Mercari iOS client Introducing ViewModel Inputs/Outputs: a modern approach to MVVM architecture GU Appでは、それが一新され共通アーキテクチャが策定されました。SwiftUIをベースとし、Reduxや TCA などからインスパイアされた単方向データフローの独自のアーキテクチャです。 メルカリおよびメルコインの機能は全てこのGUアーキテクチャに基づいて開発されています。メルペイもこの共通アーキテクチャに統合する方針を決定しました。 ちょうどメルペイのトップ画面のリニューアルする新規開発プロジェクトが別で計画されていたので、そこでGUアーキテクチャを試験的に先行導入することにしました。 Phase2の開発が完了しました。この段階でメルペイのトップ画面に関しては100%SwiftUIが達成できました。 メルペイのトップ画面以外の既存画面に関しては、未だにUIKitのままです。Phase3では、既存画面をすべてSwiftUIに書き換えます。これは現在進行中です。 ここで、なぜ最初に技術的負債が比較的少ないメルペイのコードを書き直す決断をしたのか、そのモチベーションについてお話します。 技術的負債負債が比較的少ないといえども、初期のコードは2018年に作られたものです。当然UIKitベースのコードなので、GUで刷新された基盤機能の恩恵は受けられせん。 SwiftUI自体ははもちろん導入できなくはないのですが、メルカリの機能で使用されているDesign System3.0を使用するには、GUアーキテクチャへの移行も必要になります。 アクセシビリティについてもGUアーキテクチャはかなり手厚くサポートされていて、新規イベントログ基盤もGUアーキテクチャにより最適化されたものになっていたなどの事情がありました。これらの事情によって、メルペイ側の既存コードも段階的にGUアーキテクチャに移植するのが良いという決断になりました。 とはいえ、メルペイの既存機能がかなりの数があるので、まず現状の仕様整理から始めました。まず、移植作業の計画を立てます。大量のメルペイの既存画面の仕様を整理・リストアップし、優先度を決めて移植作業を少しずつ始める計画を立てようと考えました。 大量の既存コード・仕様書はありましたが、ここで既存コードと仕様書の対応関係が不明瞭であったり、対応する仕様書が見つからなかったりといった問題が発生しました。仕様書によっては同じ画面に対して別の呼び方がされていることもあり、特に非日本語話者にとってはその理解が非常に困難な状況でした。 そこで既存の仕様を整理する前に、一旦全てのメルペイの画面を一意に特定する「Merpay Screen ID」を導入することにしました。 例えばここにあるように、”MP-BNK-001” はメルペイの銀行接続の1番目の画面を示します。このような採番作業を全ての画面に対して実施しました。 Screen IDをソースコード、仕様書、Figma上で横断的に記載することで、それぞれの関係を明確化しました。これによって、認識の齟齬を解消できる上、非日本語話者でも理解しやすくなりました。これは社内の開発ガイドラインにも組み込まれているので、この辺の整理が今後も進んでいくと思います。 これを導入したことによって、スクリーンに関して仕様書を探したいときにScreen IDで検索すればそれに関連する仕様書が全て出てきます。 またFigmaでUIレイアウトを確認したいときも、Figmaの検索ボックスにScreen IDを入れれば、正式なレイアウトを確認できます。 ソースコード中にこのようにScreen IDを埋め込んでおけば、ソースコードと仕様書の関係、その画面に関する実装についても発見できます。 このように、事前の準備をした上で計画を立て、現在絶賛移植作業中です。全体の77%は古いコードのままなので、今後数年かけて移植する予定です。 こちらが現在移植中の銀行接続画面の例です。 GU App Integrationの開発プロジェクトを進めることで、メルペイ自体の機能開発を止めることなく、GU AppのIntegrationが完了しました。メルペイのコードを変更することなくそのまま新しいGU Appのコードベースに移植することに成功しました。 またBazelのメリットを最大限に生かす構成に変更しました。さらにメルペイの既存画面をShiftUIで移行するにあたり、いろいろと基盤を整備しました。 以上です。ご清聴ありがとうございました。
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるEngineeringを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 Enabling ProgramのEngineering Headをちょっとやってみている 」の書き起こしです。 @kazegusuri:それでは「Enabling ProgramのEngineering Headをちょっとやってみている」というタイトルで@kazegusuriが発表します。 まずは自己紹介です。@kazegusuriという名前で、社内でも社外でも活動しています。メルカリに入社したのは約8年前で、当時はSREとして入社しました。そこから1年ほど経って、旧ソウゾウと呼ばれている会社に移動して、ID & Payment Platformチームに所属しました。 これは、現在のメルペイの基礎になったIDと、決済の仕組みを作っていたチームです。さらにまた1年経って2018年にメルペイができると同時にチームごと異動して、それを機にアーキテクトとして仕事をすることになりました。 現在もアーキテクトの仕事自体は続けていますが、今年の4月からEnabling ProgramのEngineering Headを兼務することになりました。今回はEngineering Headの仕事について紹介します。 今日話すことは三つあります。まずはProgram体制の話と、所属しているEnabling Programの説明をした後に、Engineering Headについて説明をし、自分がEnabling ProgramのEngineering Headとして何をしているかを説明します。 Program体制とは、メルペイでの開発組織の体制のことです。2023年1月からスタートしました。 Program体制は、役割に応じて大きく三つのグループにわかれます。一つ目がJourney、二つ目がFoundation、もう一つがEnablingです。 Journeyはその中に三つのプログラムが存在していて、Foundationは二つのプログラムが存在します。自分が所属しているEnabling Programは一つです。 続いて、それぞれの役割について説明します。Journeyはお客さま体験を改善するいわゆるプロダクト開発を行い、お客さまの機能を提供する組織として活動しています。 Foundationの役割は、Journeyがプロダクト開発をするにあたって共通基盤を提供する組織です。Enabling Programは、Journey、Foundationの開発を支援する組織です。 Program体制はメルペイにおける組織であって、つまりメルカリ・メルコインにおいては開発体制が異なります。 グループ全体のプロジェクト開発に対して、インフラを提供しているのが、Microservices Platformです。 次に、Engineering Headの役割について説明します。各プログラムには、Engineering HeadとProduct Headがいます。 ただし、Enabling Programには現状Product Headがいないので、Engineering Headが、Product Headの役割も兼ねている状態です。彼らはいわゆる会社で言うとCTOとCPOのようなものです。技術的な意思決定をする人やプロダクトに対する意思決定をする人というイメージです。 Enabling Program・Product Headには、People Management的な役割がないので、組織には彼らとは別にManagerを置いています。 Enabling Programに所属しているチームは、この七つです。 Backend Architectは、バックエンドの開発を支援したり、アーキテクチャを考えたりするチーム。Client Architectには、モバイルやフロントエンドに携わる人が在籍し、各プログラムのクライアント開発を支援します。Engineering Productivityは、バックエンドの開発の生産性を上げるためのチームです。 SREは、会社全体の信頼性を向上させるために、いろいろな仕組みを考えています。Data PlatformとData Managementは、会社のマイクロサービスにおける各種データやプロダクトで得られたデータを収集して、それらをプロダクト開発に生かせるようにする目的で活動しています。QA Optimizationは、各チームのQAをより良い最適化を行うために活動しています。 Enabling Programを構成するチームは、多岐にわたります。では、Enabling ProgramのEngineering Headは、何を期待されているのでしょうか。 例えば、トップダウンで、Engineering Headとして今後の戦略を考えて、各チームにその戦略を遂行するように適用していく。もしくは、各チームがボトムアップでやりたいことを考えて、それに対して意思決定をすることが考えられます。 ただし、現状自分がまだEngineering Headになってから3ヶ月であることや、Enabling Programに所属しているチームの技術エリアがすごく広いので、戦略を立てたり、意思決定をすることは、正しい精度ですることが難しいと思います。 さらに、このProgram体制ができる前から、これらのチームは存在していました。当時から各チームは自分たちが何をすべきかを考え、自分たちの責任で遂行していくことがすでにできていた自立したチームでした。今回Program体制になったからといって、短期的にいきなりその方向性を変える必要はないと、個人的には思います。 では、自分がなぜ今回Engineering Headになりたいだろうと思ったのか、目的を説明します。 Engineering課題を解決するときのプロセスをこの会社として決めたいという理由がありました。メルペイでArchitectチームとして5年以上活動した中で、いろいろなEngineering課題があって、解決するためのいろいろなプロセスをとりました。 プロセスはタイミングや課題の内容に応じて変わります。 例えば今回の課題を解決するために、いろいろな開発チームに対策・対応をお願いしないといけないとき、どうやって対応を依頼をするのか。会社全体が大きくなった今、どうやって対応するのかを考えることは難しいです。 また、Engineering課題が決まったとしても、プロダクトとの優先度をどうやって決めるのか、そのプロセスが決まっていた方がやりやすいと感じていました。 これは、Enablingチーム全員の共通の課題です。そこで、この辺りをまずは解決したいなと思いました。 さらに、裏目標として「テックカンパニーを目指したい」ということがあります。 会社としては元々「グローバルテックカンパニーを目指しています」と公言していますが、個人的にもテックカンパニーをずっと目指しています。 Enabling Programで作っている技術はよくできていて、社内でもまだ知らない人にもっとアプローチしていきたいですし、社外にももっと自慢した方がいいと思います。 現在ある仕組み・技術をもっと発展させるために、技術に対する投資をする必要があります。そのようなサイクルを回すためにはまずは、Enablingチームに所属しているチームが、どれだけ会社にとって有用性があるのかを伝えていく必要があると思います。 個人ではなく、Enabling全体として伝えていきたいなと思って、Engineering Headをやりたいと思いました。 次に、実際にEngineering Headになってやっていることを三つ説明します。 一つ目は、課題の可視化。二つ目が、会社としての課題に設定する。三つ目が成果報告。それぞれ説明していきます。 まず一つ目の「課題の可視化」についてです。プロジェクトロードマップを作っています。この目的は、Enablingプログラムのやっていることや成果を他のチームの人たちが理解できる状態にするためです。 「プロジェクトロードマップ」という名前通り、プロジェクト単位でそのプロジェクトのロードマップを作成しています。JIRAを使って可視化していこうと思っているのですが、まだやりきれていないところです。 プロジェクトとは、中長期的な施策のことを言います。メルカリでは、全体的にOKRという考え方を、短期的な目標設定と成果を計測するために使っています。 今のOKRの使い方は、短期的な目標には使いやすい一方、中長期目標には使い勝手が悪い状態です。そのため、代わりにプロジェクトという考え方を用いています。 この考え方はメルカリ・メルペイ全体で使われており、同じプロジェクトという考え方をEnabling Programでも使っていこうと思います。 次に、プロジェクトの種類です。これは、Enabling Programの中でのプロジェクトの種類を指しており、通常プロジェクトと重要プロジェクトの大きく二つに分けて考えています。 通常プロジェクトの定義は、達成することで他のチームにインパクトがあること。これを達成すると、影響がある人たち、つまりプロダクトを開発している全ての人たちに対して、行っていることやその影響を知ってもらいたいという目的があります。 重要プロジェクトの定義は、達成することで会社レベルの目標にインパクトが出るものです。カンパニーのOKRや重要な目的に対して影響があるということです。周知したい人は、VPや他のProduct/Engineering Headです。 二つ目「会社としての課題に設定する」については、Merpay Engineering Projectsを実施しています。 これはメルペイでの重要なEngineering課題です。元々メルペイでは、この会社レベルで、重要な指標としてEngineering OKRがありました。しかし、やはりOKRであると短期的な目標になってしまいがちです。一方、重要なEngineering課題は中長期的なものが多く、会社としてもプロジェクトという形をとっています。 Merpay Engineering Projectsという会社レベルのプロジェクトは、Enablingの重要プロジェクトから取り入れてもらっています。Enablingの重要プロジェクトをEngineeringプロジェクトとして採用してもらうことによって、担当のVPがアサインされて、サポートが得られます。 大きな意思決定が必要な場合や、他のプログラムの優先順位の変更が必要な場合、VPレベルで説明してもらうことが可能です。 Engineering Projectsは、2週間に1回、各プロジェクトの進捗や課題を報告して、ブロッカーを洗い出しています。場合によっては、VPなどに対して対応を求めやすい状態です。 三つ目「成果報告」についてですが、成果発表会を行っています。これは、Enabling各チームの代表者が発表してもらっていて、3ヶ月に1回、成果を発表してもらっています。 今までは各チームでOKRを設定していていましたが、OKRの成果を他のチームに知らせる機会がほとんどありませんでした。 それを改善するために、成果発表会という形でEnablingチームがやっていることを発表したいと思いました。発表会には、エンジニアだけじゃなくてPMなど、開発に関わる広い範囲の人を招待しています。 実際に前クォーターの成果発表会を開いたときは、100名もの方に参加いただきました。Enabling Programが何をしているのか、みなさんが興味を持ってくださったおかげです。 これまで自分がEnablingのEngineering Headになって、プロセスを改善するためにいろいろなことをしてきました。でも、まだ改善することが多いです。 例えば、Engineering課題が会社レベルで設置されたとしても、プロダクトの優先順位をどうするのかという問題や、出した成果をもっと投資してでも伸ばしていくべきなのか、それともすでに成果が出ているからStayでいいのかという説明をしなければなりません。 今後としては、引き続きこれらの改善をしていくと同時に、すでに行ってきたことを継続していくことも重要だと考えています。 発表は以上です。ご清聴、ありがとうございました。
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、Productやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 Merpay Engineering Career Talk 」の書き起こしです。 @keigow:今回のセッションは、「Merpay Engineering Career Talk」というタイトルでディスカッションを進めていきます。 @keigow:メルペイVP of Engineeringの@keigowと申します。私は、2016年にメルカリグループに入った後に、2018年のメルペイ立ち上げのタイミングから3年、メルペイに所属してました。2年ほどグループ会社のソウゾウという会社で新規事業の立ち上げに取り組んでいたんですけれども、この4月にメルペイに戻りました。 @osamingo:皆さんこんにちは、@osamingoです。@keigowさんと同じく、2016年8月にメルカリの子会社のソウゾウに入社しました。そのときのメンターは@keigowさんで、個人的には感慨深いです。 元々はバックエンドエンジニアとして入社し、プロジェクト開発に従事しました。2018年頃にメルペイへ異動し、バックエンドエンジニアをしていたのですが、約1年半後にエンジニアリングマネージャー(以下、EM)になりました。最初は、コード決済や加盟店管理/審査、加盟店精算周りに携わっていたのですが、現在は、Fintech ArchitectやEngineering Productivityという社内のデベロッパー向けのサービス開発のEMをやっています。本日はよろしくお願いします。 @fivestar:@fivestarです。本名はKatsuhiro Ogawaと申します。メルカリには2018年1月に入社し、6年目になります。最初はコーポレートエンジニアリングという、社内の評価システムや社内向けのプロダクトを開発する部門の立ち上げにジョインして、そこから1年半ぐらいメルカリ側で開発をしていまして、そのときはバックエンドアーキテクトということで、バックエンド寄りの部分も担当していました。 そこから2019年にメルペイに転籍し、ずっと与信部門に所属しています。そこでメルペイのあと払いやメルペイスマートマネーといった与信系サービスの開発を行っています。 エンジニアではありますが、組織上Engineering Headという役割を賜っていまして、チームのエンジニアリングに関する意思決定の取りまとめもしています。過去にはマネージャーや、スタートアップのCTOをしていた時期もあり、エンジニアとしてのキャリアをまた再構築している状況なので、少しでも参考になればいいなと思っております。 @keigow:では、早速ディスカッションの方に入っていきます。 @fivestar:@keigowさんがVPになったのはいつでしたっけ? @keigow:正式には今年の7月ですが、メルペイに戻ってきたのが4月なので、そこから徐々に似たような役割をしていました。 @fivestar:僕がメルペイに入って1年経たないくらいから一時期、@keigowさんにマネジメントしてもらったことがありましたが、メルペイに戻ってきたと思ったらいきなりVPになったじゃないですか。きっかけが気になりますね。 @keigow:もともとソウゾウでHead of Engineeringという形でエンジニアリング組織を見ていました。ちょうど今年の4月のタイミングでメルカリShopsの事業がメルカリ本体と事業統合し、組織が変わるタイミングで、これからどうしようか迷っていました。 その中でメルペイに戻ってきてVPの役割を担当しないかという打診がありました。メルペイ自体はすごい大きな組織で難しさもよくわかっていたので悩みましたが、こういう機会はなかなかないと思っていて、チャレンジしないのはもったいないという気持ちが最終的には勝ちました。 @fivestar:元々VPはキャリアの選択肢として考えていましたか? @keigow:元々ポジションにこだわりはありませんでした。自分のスキルが一番会社に貢献できる場所ってどこなんだろうと、ずっと考えていました。一番好きなのはプロダクト開発だったので、VPやCTOになりたいとは考えていませんでした。 でも、最初マネージャーをやったときは、マネージャーでいっぱいいっぱいでしたし、人が増えていく中で、中間管理の人が必要だと思ったタイミングで挑戦しようかなと思いました。 ただ、VPになりたいという気持ちもそこまでなくて、常に当時の上長には「たまに現場に戻りたいって思うんですよね」という話はずっとしていました。でも行動していく中で少しずつ考え方がアップデートされていきました。その結果としてVPにチャレンジをしたいと思うようになりました。。 @fivestar:どのタイミングで、マネジメントの方向に行こうと決心したのですか? @keigow:これというタイミングはなかったです。最初はプレイングマネージャーだったので、コードも書きつつマネジメントしていました。また、プロダクトマネージャーの時期も挟んだことでコードから離れてしまい、それが続いて今に至ります。そのときの状況に応じて最適だと思う選択肢を選んできました。 次に、@osamingoさんからの質問です。 @osamingo:@fivestarさんは、Credit Designという与信やあと払いを管理しているチームに異動されてからも、こだわりをもって業務をされていると思います。組織が大きくなり、サービスがリリースされていくという変化が激しい中でも、永く所属されています。この中で、エンジニアリングの責任の深さや広さはどう変化し、またどう対応してきましたか。 @fivestar:もともと前のチームでバックエンドのアーキテクトという立場で、複数のサービスを組み合わせて全体のアーキテクチャを見ていましたし、自分の一番関心のある領域は、ドメインモデルやアーキテクチャでした。そのような設計を取りまとめるという役割を考えて、ずっとキャリアを歩んできています。 だから、僕が来たときと比べると、与信サービス事業はめちゃくちゃ拡大しているのですが、キャリア的な考えで言うと、もともとメルペイにきたときからある程度広く見ていけるようにという意識はもっています。その中で、僕は20代の頃はコードを書きたいと思っていたのですが、年齢を重ねるごとに、設計をきちんとして良いプロダクトを生み出し、課題解決をやっていきたいという思いがあります。 Credit Designの与信事業にすごく興味があったので、異動の希望を出して今に至ります。でも、最初はチームの中で信頼されることが大事だと思うので、最初は本当に小さいマイクロサービスの開発チームに入って積み上げていって、大きなマイクロサービスのテックリードをやったり、あとはメルペイ立ち上げからいろいろな課題があってその解決にオーナーシップをもって取り組むことを意識しています。 確かに広さ・深さは実質的に変化していますが、現場の第一線で、際限なくいろいろな課題を解決したいという思いがあったので、マネジメントではなく現場側の役割でやらせてもらっています。 @osamingo:特にメルペイだと、自分が作ったサービスに触れる喜びはかなり強いと思うんですよね。全体を設計することに対するメンタルやモチベーションについてはいかがですか? @fivestar:チームのマネージャーとコミュニケーションをしていく中で、「もっとパフォーマンス発揮の仕方として、周りをうまく活用して」と言われたことで、少しずつ意識の変化が起こりました。 それから、今後LLMによってコードを書かなくていい世の中になるかもしれませんし、コードを書いていればお金がもらえる世界じゃなくなるかもしれません。それよりは、課題の本質に触れる方を自分の仕事にしていった方が、食いっぱぐれるリスクが少ないかなという打算的な部分もありました。 @osamingo:昔からそういう意識はあって、今このタイミングでもともと持っていたものが発揮されたイメージですかね。 @fivestar:与信事業はかなり課題が複雑で、お客さまの体験という点でいろいろな接点があります。 例えばあと払いサービスを使うところから返済して、そのお金を回収しなきゃいけなかったり、単体でひとつの事業としてPLを持つくらい複雑な事業です。法要件も複雑で、割賦販売法や貸金業法などの理解も含めて難しいドメインですが、難しい方が自分としてはチャレンジのしがいがあります。複雑なものを紐解いたときは嬉しいです。 メルペイの面白いところは、強い人が集まっていることです。特に与信チームは強いメンバーが多くて、会社からの期待値が常に高いんです。いろいろなサービスをどんどんアプリとして、事業を引っ張るプロダクト開発チームなので、会社からの期待値も含めてやればやるほど成果が出ます。そこで責任を持てて、しかも現場の中で必要に応じたマネジメントをしながら、組織を引っ張る立場にいられるのは、自分が昔から目指していた形の一つだったのでやりがいを感じます。 @osamingo:リスペクトできるメンバーと働けるのは、明文化できない福利厚生ですよね。 @fivestar:@osahimgoさんはこの会社に入ってからマネージャーになりましたよね。ICに戻りたいという葛藤はありましたか? @osamingo:ありましたよ(笑) 「Go Bold」という会社のバリューがありますから、「自分がBoldに行けるところはどこなんだろう」ということを常に探っているタイミングで、ICに戻るという選択肢を考えたこともあります。でも今のところはマネージャー業が楽しいので続けています。 @fivestar:どういうところが楽しいでしょうか? @osamingo:EMになったきっかけとして、メルカリグループに入社して2年半くらい経ったときに、自分のパフォーマンスを最大化させるときに「自分はこのままでいいんだろうか」とモヤモヤしていた時期が半年ほどありました。 当時、メルペイがリリース準備でいろいろ頑張っているタイミングでした。そのときに会社で開催されていた「Engineering Manager Philosophy Talk」のイベントに参加し、自分の気持ちが明確になりました。そのイベントは、外部の講師を招いてEngineering Managerの哲学や経験について語っていただくというものでした。そこで、タイミングよくEMに登用されました。 もともとコード決済や加盟店審査、加盟店管理などのプロダクトサイドを長く担当していて、例えば、NTTドコモさんとd払いで連携する場面や国の省庁と連携して何かをやる場面に立ち会うことが多々ありました。 それもすごく楽しかったのですが、EMの活動として、プラットフォームサイドにもチャレンジをしたいという気持ちがあって、今はArchitectやEngineering Productivityという裏方寄りのところを担当しています。 今の領域はプロダクトマネージャーの人がいなくて、エンジニアが自発的に動き、かつEMも動きもあるという、動き方やEMへの期待値が違うため、エリアによってかなり求められるマネジメントスキルが違います。そのギャップが楽しいです。 @keigow:今視聴者さんから質問が来ていますが、「キャリアを築いていく中で現場との距離から焦燥感が発生したときの向き合い方を知りたい」とのことです。 やっぱり、EMになって、自分がコードを書かないことで知識が遅れていくんじゃないかと思うこともあるのかと思いますが、どうでしたか。 @osamingo:そういう考えは、ありました。 一線で書いてないと、特にエンジニアの知識量、エッジなテックに対しての対応能力が著しく下がるし、コードを書くスピードは3分の1にまで落ちるという現象が早めに来てしまって。「俺はエンジニアとして死んでしまったんじゃないだろうか」という焦燥感に苛まれることは僕もありました。 そのとき何をやったかというと、1on1でメンバーから教えてもらうということです。マネージャーからメンバーに聞いた方が「こいつはまだテクノロジーに対してちゃんと関心を持ってるんだな」というアピールもできますし、現場から離れて焦燥感はあるのですが、チームで仕事しています。メンバーとのコミュニケーションという中で、僕はそこを埋めてきたところはあります。 @keigow:@fivestarさんにも聞きたいのですが、マネジメントではないですが、Engineering Headという立場で、コードを書く時間が減ってきているという話があったと思うのですが、似たような課題感や焦燥感はありましたか? @fivestar:正直あるのですが、押さえておかなければいけない部分はある程度押さえていると思います。また、与信領域の全てを押さえておくというよりは、ある程度責任の移譲は必要だし、必要な意思決定を自分がしていく一方で頼ることも大切だと思います。 僕の場合、周りにいるのはテックリードなど、同じような役割の人たちなので、役割分担して自分が全部抱えないようにしています。 @keigow:最後、個人的に僕が聞きたかった質問なんですけれど、@fivestarさんは組織にもかなり興味を持っていると思います。実際、マネージャーという職種には興味を持っていますか? @fivestar:1年前ぐらいまでは全然考えていなかったのですが、今のロールはいずれ他のチームメンバーにtake overしていかなきゃいけないと思います。そうなったときに、次に自分に求められるのがマネジメントの可能性もあると思っています。マネージャーは、選択肢として持ってもいいかなと思っています。自分がやりたいと思ったときは、改めて相談させてください(笑) @keigow:まだまだ聞きたいこと・話したいことがたくさんあるかと思うのですが、時間が来てしまったので、本日はこれでおしまいにしたい思っております。 ご清聴ありがとうございました。
Merpay & Mercoin Tech Fest 2023 は、事業との関わりから技術への興味を深め、プロダクトやサービスを支えるエンジニアリングを知ることができるお祭りで、2023年8月22日(火)からの3日間、開催しました。セッションでは、事業を支える組織・技術・課題などへの試行錯誤やアプローチを紹介していきました。 この記事は、「 SwiftUIでビットコインの価格チャートを改善・再実装した話 」の書き起こしです。 @andooown:「SwiftUIでビットコインの価格チャートを改善・再実装した話」というタイトルで発表します。 まずは簡単な自己紹介です。株式会社メルコインのクライアントチームで、iOSエンジニアをしている、Yoshikazu Andoと申します。GitHubやSNSでは、@andooownというIDで活動しています。2019年に新卒で株式会社MIXIに入り、ウォレットサービスのiOSアプリの開発を行っていました。 その後、2021年にメルカリグループにジョインして、ビットコイン取引サービスの立ち上げに参加し、引き続きiOSアプリ開発者として、設計やグループ内の連携も含めて担当しています。 では、改めてSwiftUIでビットコインの価格チャートを改善・再実装した話をします。 まずは、メルカリのビットコイン取引機能について説明します。ビットコイン取引は、ありがたいことに、サービス開始から3ヶ月強で口座開設数が50万人を突破しました。引き続き伸ばしていきたいと考えています。 右の画像がサービスのトップ画面です。ビットコイン取引は、メルカリアプリの機能のひとつであり、メルカリアプリにSDKとして実装されています。これによって疎結合にしつつ、グループ共通の基盤機能やコンポーネントを利用して開発されています。 アプリ内の主な動線は、マイページ。口座開設や普段の利用も、マイページからご利用いただけます。ビットコイン取引機能も含めて、メルカリアプリはSwiftUIを基本として開発されています。SwiftUIも含めたアプリ全体のリライトの話は、メルカンに掲載されておりますのでぜひご覧ください。 参考記事: メルカリの事業とエコシステムをいかにサステナブルなものにするか?かつてない大型プロジェクト「GroundUp App」の道程 また、パスワードレスの認証システムであるFIDOを使用しているので、セキュアにビットコイン取引が行えることも特徴です。こちらはメルカリサービスで最初に採用されています。 本題のビットコインの価格チャートについてです。 チャートはサービスのトップ画面にあり、一番お客さまの目に入る画面となります。チャートには、ビットコインの価格の推移を表示しており、バックエンドから配信された価格のデータをもとに点の間を補完して描画しています。また、画面を開いている間は、一定間隔でデータが更新されます。お客さまはチャートの下のボタンから閲覧する期間を変更できます。 チャートはタップでき、それによって実際の価格や時刻が表示されたり、見た目が変わったりします。 タップされている位置より右側のラインの色はグレーになり、グラデーションの塗りつぶしもなくなります。タップ後には、線の太さや補間の方法が変化し、その間はアニメーションによって連続的になっています。 初期の段階では、このチャートの実装にOSSのChartsライブラリを利用していました。おそらく一番有名な danielgindi/Charts ライブラリで、Appleプラットフォームでチャートやグラフを実装したことがある方ならご存知なのではないでしょうか? こちらの画像はGitHubのREADMEから持ってきたものですが、このようなシンプルなチャートであれば、簡単に実装できます。 グラデーションでFillする機能もあり、こちらのサービスのチャートの要件にマッチしています。また、去年のWWDCで発表されたApple公式のSwift ChartsですがこちらはiOS 16から利用可能となっており、サービスの要件にマッチしていないため、今回は採用を見送りました。 OSSのChartsは、UIKitで作られており、メルカリアプリはSwiftUIを基本として開発されています。そのため、今回はUIViewRepresentableでラップした状態でサービスに組み込みました。 また、タップしている間に表示される価格や日時のコンポーネントはSwiftUIで実装しています。ChartViewと併せてVStackに入れ、タップされてる位置などを同期する必要があるので、Stateを引きまわして実装しています。 この実装にはいくつかの課題がありました。 まず感じていたのは、Chartsライブラリの制約によって、無理やり感のある実装設計になっていたことです。例えば、サービスとして実現したインタラクションがありましたが、そのためには、ライブラリ側で用意されているデリケートメソッドだけでは足りず、自前でUIGestureRecognizerを追加していました。 また、タップした位置の左右で色を変えたり、塗りつぶしの有無を変えたりという要件がありましたが、標準の機能ではこれを実現できませんでした。悩んだ上で、二つのChartViewを生成し、それぞれタップした左側用の設定・右側用の設定で描画し、それらをクロップした上で、ZStackで重ねる方法をとっていました。 ChartsのChartViewはかなり高機能で、そのようなViewを二つ生成しているので、無駄が多かったと思います。 この部分は後になってライブラリのRendererなどをオーバーライドして自作することで、一つのChartViewで実装できるようになりましたが、それによってメンテナンスコストが上がり、どちらにしても課題が残る状態となっていました。 次の課題として、データフローが複雑になっていることがありました。 UIViewRepresentableを使った実装ではあるあるだと思いますが、StateはSwiftUI側にあり、initializerでUIViewに渡します。その上で、タップイベント等はUIKit側から発生します。チャートの上にある価格表示部分はSwiftUIであり、座標をUIKit側と同期する必要があるため、SwiftUIのStateにも反映します。 UIKit、ChartViewも高機能であるため少なからずStateは持っており、SwiftUI側にも、もちろんStateがあります。これによって、Stateやライフサイクルの同期がうまくいかないことによるバグも発生していました。 例えば、「指を離しているのに線が残る」というもので、これはChartViewのバグにより想定しているデリケートメソッドが呼ばれず、Stateがずれてしまうということが原因でした。 この話に関連して、Chartsライブラリにも課題を感じていました。実装当時、Chartsライブラリは頻繁にメンテナンスされているとは言えず、バグ修正のPRはあったものの、1年半放置されている状況でした。そのため初期のChartの実装では、ブランチのライブラリを利用していました。 発表に際して、改めて状況を確認すると、最近新しいメジャーバージョンがリリースされていて、今後は多少活発になることが予測されます。 最大の課題は、ここまでの制約によってやりたいことができないということです。初期段階では、デザイナーさんなどがいろいろな表現の案を提案してくれていましたが、制約によって「かなり無理やりなことをしないと無理そう」と断ることが度々ありました。 チャートはサービスのトップ画面にあり、一番最初に目に入る機能のため、ここにはこだわりたい気持ちがありました。しかし、他のタスクや今後のメンテナンスを考えると、断らざるを得ない状況でした。 あるときPMが、「チャートは取引所サービスの顔」と言っていることがありましたが、その通りだと思いますし、もどかしい思いをしていました。アニメーションもこうして断念した表現の一つであり、初期の実装では、アニメーションなしで見た目が切り替わっていました。 「チャートはサービスの顔だが、不完全燃焼」という状態ですが、このままリリースするのは勿体ないということで、チーム内で合意を取って、チャートをフルスクラッチで実装し直すことになりました。 我々のサービスは、暗号資産交換業ということもあって、スペックや仕様については細かく文書化されているものの、チャートの部分については最低限守るべきスペックのみを定め、iOS・Androidのプラットフォームごとにできる表現をとことん突き詰めることも、このときに同意しました。この認識をチームで共有できたことで、この後が進めやすくなったので非常によかったです。 リライトしていくにあたり、まずは設計方針を決めました。チャートのコンポーネントは二層で構成し、汎用的な部分とサービストップのビットコイン価格チャート固有のドメインを含む部分とで分けることにしました。 汎用的な部分については、X方向に位置、Y方向にビットコイン価格を取る、グラフにおいて汎用的に使うであろう数値と座標の変換ロジックや、チャートのサイズの管理などを含みます。 トップ画面のドメイン固有のレイヤーに関しては、この時点では、どの程度サービスで再利用できるかがわからなかったこともあり、非常に多くのものを含んでいます。 価格の点の間を補完するロジック、画面仕様に合わせて、各個のコンポーネントをレイアウトするロジック、実際にチャートのラインや、グラデーションを構成描画する部分も、ドメイン固有のレイヤーに含まれます。 レイアウトの描画に関しても、SwiftUIのView・Shapeで構成する方針を立てました。これとは反対に、公式のSwift ChartsのようにChartContentというprotocolと専用のResult buildersを作ってデータモデルを構成し、それをもとに内部で描画をする方法もあります。 しかし、この場合は、SwiftUIに用意された豊富なレイアウト方法や、既存の資産を使うことができないため、表現の幅を制限しないためにも、View・Shapeを使うことにしました。これらの決定は後ほど活きてくることになります。 この方針をもとに、2週間ほどでPoCを作成しました。 作ったものについて説明します。リライト後、左の赤枠の範囲のViewは、ViewBuilderを使って通常のViewのように右のコードのように記述できるようになりました。 コードは実際のものを簡略化していますが、構成は同じで、汎用的な座標計算などをしてくれるContainer ViewであるLineChartの中に、SwiftUIの LayoutコンポーネントであるVStackなどを使ってチャート構成要素をレイアウトしています。 では、具体的に見ていきます。先ほどのコードで一番外側を囲っていたのがこのLineChartです。 これ自体もSwiftUIのViewであり、initializerでChartの点の配列と実際の表示要素を返すViewBuilderのクロージャを受け取ります。 そして、受け取ったクロージャに対してChartContextというオブジェクトを渡してViewを生成します。 bodyを見るとわかるように、LineChartは渡されたクロージャーから生成できるView以外に表示要素は持ちません。 そしてChartContextは、チャートの作成に必要な情報を持っています。右側のコードのように、実際はLineChartからさまざまなものをinitializerで渡して作成されています。 entriesは引き続きチャートの点のデータの配列。sizeやtransformerについてはこの後説明いたします。LineChartはこのチャートの作成に必要な情報を管理することが責務です。 ChartContextが持つsizeプロパティは、チャート部分の大きさを表しています。 ここでいうチャート部分とは、右の画像の赤の実線の範囲です。先ほどの通り、LineChart自体は赤の点線の範囲になりますが、チャートとして数値に関連した座標系を持つ範囲は実践の範囲ですので、その部分の大きさがsizeとして保持されています。 これは座標や数値の計算、各コンポーネントをレイアウトに利用するため、Contextに保持されています。 そのsizeですが、LineChartの中でどのように取得されているかというと、このようなmarkAsChartContent()というカスタムModifierが利用されています。 ViewのbackgroundにGeometryReaderを挿入して、サイズを取得するSwiftUIではおなじみの実装です。取得したサイズはpreferenceに記録されます。 LineChart側では、左側のように、preferenceを読み取り、privateな@Stateとして保持し、それをContextに渡しています。 実際の場面では、右側のコードで赤く囲まれたところのように、LineChartのViewBuilderの中で、座標系の範囲に相当するViewにModifierがつけられています。赤枠の上のMarkerViewはタップ時に価格や時刻を表示するコンポーネントなので、座標系は持ちません。 Contextの最後はMatrixTransformerオブジェクトです。これは左下のように、チャートの数値データと画面上の座標を相互変換する機能を持っています。これも各コンポーネントをレイアウト・描画するときに必要になります。 MatrixTransformerも右の画像のようにサイズを用いて作成されます。これによって、数値と座標の相互変換ができます。 実際のChartViewでは、これらの情報が詰まったContextを使ってViewを作ります。 このコードはタップ時に表示される縦の点線の例ですが、Contextのtransformerを使ってタップされているデータから、実際の画面上のX座標を取得してレイアウトされています。価格のラインやグラデーションなども同様にContextを利用して作成されています。 もう一つこのコードからわかるのは、タップされているデータであるselectedEntryが、LineChartの外で管理されていることです。LineChartは、数値・座標の管理のみが責務です。現時点では、触って数値を見れるという機能が、今後実装されるチャートでも同じかわからないため、このような設計になっています。 ここまでは、初期の実装にあったものをただSwiftUIでリライトしただけです。発表タイトルの再実装のみが回収されました。 ここから時間を取って改善を始めました。ただ再実装しただけであれば、開発面で運用コストが減ったかもしれませんが、プロダクトとして良くなった点はありません。 今まで制約によってできなかったけど、やりたい表現を実現するために、右のスクリーンショットのようなデモアプリを作成し、手元の端末で触れる形でPMやデザイナーも含めて配布しました。デモアプリではチャートに関連するパラメータをUIから操作できるようになっていて、タップしているときといないときの線の太さ、チャートをなめらかにするための数値処理、点の間の補間方法など、さまざまなものがあります。 アニメーションもこの段階で実装し、アニメーションの長さなども調整できるようになっていました。このデモアプリがあることによって、今までは「もっと線を丸い感じで」「なめらかにしたい」など、言語化しにくかった表現が、具体的なパラメータとして共有できるようになり、改善のPDCAが加速しました。 後半では、「デザイナーさんが気に入ったパラメータ」「PMさんが気に入ったパラメータ」のように、各々が気に入ったものをプリセットとして登録して、呼び出す機能も実装しました。最終的にはチームで画面共有をしながら、お客さまに届ける際のパラメータを決定しました。 デモの実装においては、SwiftUIでリライトしたことで、宣言的UIになったことや、アーキテクチャがSingle Source of Truthになっていることが存分に活きました。大量のパラメータを1ヶ所で管理し、各コンポーネントはパラメータに応じた振る舞いを記述するのみでよくなります。 UIKitなどの命令的なUIでは、パラメータが多い場合、パラメータ自体とパラメータを利用するView、パラメータを設定するためのViewの同期を取って更新するのが難しいと思います。 また、ViewBuilderの恩恵によってViewの構造を変化させるのも容易なため、一つのViewに全てを実装するのではなく、「補間方法に応じたViewを用意する」などもやりやすかったです。 リライトしたことの別の大きな恩恵は、アニメーションが簡単に実装できたことです。シェイプ自身がAnimatable protocolに準拠しており、その仕組みのおかげで実装できました。アニメーションは、UIKitの仕組みの場合は簡単には実装できなかったと思います。 アニメーション自体は初期からアイディアはあったものの、ライブラリの制約によって断念したため、改善フェーズで実装できて本当に良かったです。 アニメーションの実装方法に少しだけ触れておきます。左は、チャート上のラインを構成するコンポーネントで、SwiftUIのシェイプになっています。Animatable protocolでは、animatableDataプロパティを通じて、アニメーションにおいて連続的に変化する値を設定します。 ここでは、通常時は0、チャートをタップしたときに1となるような数値をanimatableDataとしています。これによって、アニメーション発生時はSwiftUIによって、animatableDataが0から1の間で0.1、0.2といったように、連続的に設定された状態でViewが描画され、アニメーションが実現されます。 チャートでは、滑らかなラインから詳細な価格がわかるラインへとアニメーションさせたいので、animatableDataデータの値に合わせて、二つの価格推移データの間を取る値を計算し、補間処理についてもその強度をanimatableDataを基に計算することで、アニメーションを実現しました。 改善を終えてみた感想です。やはりSwiftUIの特徴を生かしてチームでPDCAを回し、実際にプロダクトをより良いものにできたことは非常によかったと感じています。 デモ準備したりと工数はかかってしまうものの、共通の動くものを見ながら議論・意思決定するのはやはり迅速で、かつ同じものを見ているため、認識のずれも少なかったと思います。 これによって、リモートワークの環境でも、言語化しにくいUIの部分を改善できました。そして、ありきたりですが、リライトを通じてSwiftUIの理解はかなり深まったと感じています。 普段の画面開発や趣味の小さな実装では、なかなか深い理解が難しいことも多いですが、製品レベルでチャートのようなチャレンジングな課題に取り組むと、理解が早く深いものになるなと改めて感じました。 一方で、汎用的な設計を目指しましたが、それがどこまで通用するのかは未知数だとも思っています。OSSのChartsにも言えますが、Alamofireのように、通信データに関するものや、UI系でもAuto LayoutのためのSnapKitのようなユーティリティ系とは異なり、それ自身がUIコンポーネントを提供するタイプのものは、汎用するのが難しいなと改めて感じました。表現はサービスによって十人十色であり、ライブラリを使った表現が最適なサービスばかりではないからです。これはライブラリを利用する側にも作る側にも当てはまる話だと思います。 社内レベルのコンポーネントだとしても、年月を経て、最初は小さかったものが次第に高機能になり、逆に要件を満たしづらくなることもよくあることかと思います。 そして最後に製品のクオリティのために早くサービスをリリースしたいであろう立ち上げ時期に時間を取らせてくれた、また一緒に改善に臨んでくれたチームに感謝したいと思います。今回の進め方で都度チームで合意をとっていたのが良かったなと個人的には思っていますが、裏を返せば、合意をしてくれたチームのおかげです。本当にありがとうございました。 今後は、どこかの時点でパフォーマンスチューニングをしたいと考えています。もちろん、リリース時点で実機でカクつかないことを確認していますが、チャートtの補間部分やViewの構造など、最適化の余地はたくさんあると思っています。どこかでProfileを取りながら進めていきたいと考えています。 また、デモや動画など、動くものを使った議論の効率の良さを改めて目の当たりにしたので、これは継続したい思っています。 忙しくなってくると、「進捗がわかるものを出してください」と言われているわけではないので、自発的なスクリーンキャプチャーの共有などはおろそかにしがちです。 それによって実装が全て終わった後で、認識齟齬があって手戻りしてしまうことは、あるあるではないでしょうか?今後は、「今こんな感じです」と共有する気持ちを忘れずにいきたいと思います。 発表は以上になります。メルコインのクライアントチームでは、日々和気あいあいとプロダクトの開発に取り組んでいます。ご興味がありましたらぜひご連絡ください。 Software Engineer, iOS – Mercoin また、今回ご紹介したチャートはOSSで公開をしています。 https://github.com/mercari/swiftui-chart ご清聴ありがとうございました。
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】 発表は以上です。ありがとうございました。