TECH PLAY

株式会社メルカリ

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

261

こんにちは。メルペイ Payment & Customer Platform / Client EM の@anzaiです。 この記事は、Merpay & Mercoin Tech Openness Month 2025 10日目の記事です。 E2E テスト実装におけるDevin活用の現状について紹介します。 はじめに モバイルアプリケーション開発において、テスト自動化の重要性は言うまでもありません。特に、メルカリアプリのような大規模かつ複雑なプロダクトでは、品質保証の観点からリグレッションテストは欠かせないプロセスであり、その自動化は非常に重要な課題となっています。 本記事では、AIソフトウェアエンジニアである「Devin」を活用してE2E(End-to-End)テスト実装の課題に取り組んだ事例を紹介します。テスト自動化の効率化やAI技術の導入に関心のある開発者の方々の参考になれば幸いです。 メルカリのE2Eテスト実装における課題 メルカリのリグレッションテストとして実施しているE2Eテストは、お客さま体験を保証しつつ毎週行っているリリースを担保する上で欠かせない要素 ^1 ですが、その実装には以下のような課題があります。 コンテキストスイッチの負荷 : 通常の開発業務からE2Eテストの実装に切り替える際に、大きな負担が発生します。 実装・実行時間の長さ : E2Eテストを正確に実装するには時間がかかり、テストケースが増えるにつれて実行時間も長くなる傾向があります。 コードリーディングの困難性 : メルカリアプリのリグレッションテストとしてのE2Eテスト実装では、アプリの機能を横断的に理解する必要があります。メルペイには非常に多くのコードが存在するため、エンジニアは自分の担当ドメイン外のコードも読み解く必要がありました。これには多くの時間と、コードの実装意図や仕様の理解が困難な時に担当のチームに確認するコミュニケーションコストがかかります。 テストケース判断の複雑性 : テストケースで書かれている手順や期待値はどのように実装すれば満たしたといえるのか、といった判断には詳細なコード解析や事例の確認が必要になり、多くの時間がかかります。 これらの課題により、開発者の時間が圧迫され、本来注力すべき機能開発に遅れが生じる可能性があります。 AIソフトウェアエンジニア「Devin」によるアプローチ このような背景から、私たちはDevinに注目しました。 Devinとは何か Devinは、完全自律型AIソフトウェアエンジニアです。従来のコード補完ツール(GitHub Copilotなど)とは大きく異なり、自然言語による指示だけで、ソフトウェアの設計からコーディング、デバッグ、デプロイまで、開発プロセス全体を自律的に実行できます。 なぜDevinがE2Eテスト実装に適していると考えたのか 今回のE2Eテスト実装において、Devinが特に有効だと考えた理由は以下の通りです: 1. パターン化された実装環境 メルカリアプリのE2Eテストは「ページオブジェクトモデル ^2 」で実装されており、ある程度決まった形で書けるようになっています。また、すでに十分な量の参考テストケースが存在していたため、Devinが学習しやすい環境が整っていました。 2. コード理解の負担軽減 前述のコードリーディングの困難性でお話しした通り、リグレッションテストとしてのE2Eテストの場合、様々なドメインを横断的に理解する必要があります。 しかし、DevinならACU(計算リソース)が許す限り、どこまでも深くコードを読み続けることも可能です。人間のように疲れることがないのは大きなメリットでした。 eKYC画面におけるE2Eテスト実装事例 具体的な検証として、メルペイのeKYC(オンライン本人確認)機能のリグレッションテストのE2Eテスト化のタスクをDevinに任せてみました。 実施方法 メルカリのテストケースはTestRailで管理されています。そのため、以下のフローでDevinにタスクを渡すフローを構築しました。 ① Cursor上からREST APIでTestRailのテストケースを取得 ② 取得したテストケースから実装可能なようにタスクを詳細化 TestRail上の情報は人間が直接操作して実行する前提で記載されていますが、Devinが処理可能なフォーマットに変換する必要があり、それにはアカウントのコード上における作成方法や操作手順が重要になります。 ③ 公式のAtlassian MCP Server経由でJIRAチケットを作成 ④ そのJIRAチケットをDevin が読み、実装作業を開始 TestRailからJIRA、Devinへのデータフロー構成 ロゴ出典: https://lobehub.com/ja/icons/cursor https://www.testrail.com/ https://atlassian.design/foundations/logos https://deepwiki.com/ このフローにした理由は以下の通りです: セッション管理の最適化 : 一つのセッションが大きくなりすぎるとDevinの能力が低下してしまうため、テストケース単位で作業を分割することで品質を保つ 実装タイミングの制御 : 開発チームのスケジュールに合わせて、実装開始のタイミングをコントロールできる 継続的な改善 : 試験的な取り組みだったため、Cursor上で作成したJIRAチケットのフォーマットや内容をみながら逐次JIRAチケット生成プロンプトを改善したい Devinにテスト実装を任せる際、特に意識した情報は以下の2つです: 前提条件の詳細化 メルペイではさまざまなアカウント状態(年齢、本人確認ステータス、銀行接続状況など)が存在します。そのため、各テストに必要なテストアカウントの作成方法やセットアップ手順について詳しく記載しました。 例) eKYC未実施の状態 -> XXX() という関数を呼び出してアカウントを生成してください eKYC実施済みかつ銀行口座接続済みの状態 -> YYY() という関数を呼び出してアカウントを生成してください 完了条件(assertion)のパターン化 テストの操作内容は多様でしたが、完了条件については一定のパターンがありました。そこで以下のような内容を事前に詳しく記載しました: 「画面遷移が成功したかどうかの判定方法」 「エラーメッセージが正しく表示されているかの確認方法」 「各UI要素が期待通りに表示されているかの検証方法」 これらの情報はTestRailsに記載されているテストケースには記載がないため、Cursor上でJIRAチケットに情報を追加するプロンプトを設定しています。 JIRA チケット作成プロンプト一部抜粋 結果 DevinはJIRAチケットの内容に対応するテストコードを含むPull Requestを自動生成しました。生成されたコードを人間がレビューしたところ、以下のような品質でした: コード品質 : 参考にできる水準で、実装内容も妥当 テストロジック : 期待していた通りの流れで実装されていた ページオブジェクトパターンの適用 : 既存のコードスタイルに合致した実装 特に印象的だったのは、複雑な前提条件についてもしっかりと理解し、適切なテストアカウントのセットアップコードを生成できていた点です。現状はシンプルなテストケースの依頼のみですが、試したほとんどのテストケースについて人間の修正なく実装ができました。 さらに、GitHub上で行われたレビューコメントに対して、Devinが自動でコードを修正するといった挙動も確認できました。これは、開発プロセスにおけるコミュニケーションコスト削減への寄与も期待させるものです。 Devinがcommentを確認し、リアクションをつけている Devin活用の評価:メリットと今後の課題 今回の検証を通じて、Devinを活用するメリットをまとめました。 メリット コンテキストスイッチの軽減 : E2Eテスト実装にかかる細切れの作業時間を大幅に削減できました。例えば、「業務開始時にDevinに実装タスクを指示し、夕方にレビュー、翌日に結果確認と再レビュー」といった効率的なワークフローを作ることができます。これにより、開発者は他の重要なタスクに集中しやすくなります。 反復的な作業の委譲 : Devinは、人間なら多大な集中力が必要なコードの追跡や全体像の把握といったタスクを、粘り強く実行してくれます(ACUというリソース制限は存在します)。このような作業をDevinに任せることで、開発者はより創造的な問題解決に時間を使うことができます。 今後の展望 今回は開発者が手元のCursorでタスク生成を行いながらフローを構築しましたが、今後はより完全な自動化を目指しています。 具体的には、以下のような仕組みを検討しています: TestRailのMCPサーバー構築 : TestRailの変更を監視して自動的にJIRAチケットを生成 JIRAチケット監視機能 : チケットの更新を検知して、自動的にDevinのセッションを開始 完全な無人運用 : 人間の介入なしに、TestRailの更新からテストコード実装まで自動実行 これにより、テストケースの追加や変更があった際に、開発者が意識することなく自動的にE2Eテストが実装される環境を実現したいと考えています。 まとめ Devinを使ったE2Eテスト実装の取り組みは、開発効率向上の可能性を示してくれました。特に、コンテキストスイッチの削減や、定型的で負荷の高い作業の自動化は、開発者の生産性向上に貢献する重要なポイントです。 AI技術はまだ発展途上で、Devinも例外ではありません。しかし、その可能性は非常に大きく、今後の技術進歩に期待が持てます。 本記事が、開発現場におけるE2Eテストの課題解決や、AI技術活用の検討に役立てば幸いです。Devinのような新しい技術を効果的に取り入れ、より良い開発プロセスの実現を目指していきたいと思います。 次の記事は @Tさんの「SRE2.0: LLMサービスの信頼性を測る新しい評価指標の紹介」です。引き続きお楽しみください。 参考資料
アバター
こんにちは。メルカリモバイル フロントエンドエンジニアのtoshickです。 この記事は、 Merpay & Mercoin Tech Openness Month 2025 の9日目の記事です。 この記事は以下の二部構成となっています。 (その1)GAS(Google Apps Script)に助けられたMVNO開発の話 (その2)Jiraのissue作成のためのGAS (その1)GAS(Google Apps Script)に助けられたMVNO開発の話 わたくしtoshickは2025年 今年リリースされたメルカリモバイル というプロジェクトのフロントエンド担当として開発をしています。 メルカリモバイルとはメルカリがMVNO事業者としてモバイル通信事業に参画したものです。 当然MNP(Mobile Number Portability)に対応しているため、お客さまが他のモバイル事業者から転入してきたり、逆にメルカリモバイルから他事業者へ転出されたりするようなケースにも対応しておく必要があります。 これらをポートイン(転入)、ポートアウト(転出)と呼びます。 他事業者との連携なのであらかじめやりとりのためAPIを決めておき、エンドユーザーが任意のタイミングでポートインやポートアウトを実行できるようにしておきます。 MNPワンストップ方式とMNPツーストップ方式 MNPの手続きの大まかな流れは以下です。 お客さまは今自分が契約している通信事業者に対してMNP予約番号の発行を要請しこれを取得する。 お客さまはその予約番号の有効期限内に乗り換え先となる通信事業者にて予約番号を入力して新規契約を行う。 このようにお客さまが自ら予約番号を取得して乗り換え先にもっていくMNPの方法をMNPツーストップ方式と呼びます。 一方で最近はその予約番号発行手続きをAPIから行うようにした、手順が簡略化されたフローが主流となっています。 これがMNPワンストップ方式と呼ばれる方法です。 お客さまからすると特にデメリットはないので、もし利用できる場合には多くのお客さまはこちらのMNPワンストップ方式で事業者変更の手続きを行っているはずです。 MNPワンストップ方式での動作テスト メルカリモバイルもこのMNPワンストップ方式に対応している事業者となります。 ここで問題になるのがワンストップ方式時のテストになります。 ワンストップ方式はメルカリ内部だけで完結するものではないので、ワンストップ時のAPIの受け口となる本番と同様のふるまいをする別MVNO事業者のようなものが必要になります。 これを動作検証仮事業者と呼ぶことにします。(以降 仮事業者) メルカリと仮事業者のAPIでのやりとりを模したテストのためのダミーの事業者としてふるまいます。 開発もかなりすすんできていた段階で、仮事業者も静的なページを用意すればなんとかなると考えていたのですがよく考えると問題がありました。 静的なWebページだとpostのリクエストを処理できないのです。 その事実に気づいた時にやっと「仮事業者はWebサーバ上で起動されたWebアプリケーションである必要がある」ということに気づきました。 動作検証仮事業者の開発 社内でカジュアルにWebアプリケーションを立ち上げて任意のjsを実行できるような環境をスピーディに用意する必要にかられていろいろ相談しましたが、今から新しくマイクロサービスを作成するのは時間的に不可能だということがわかりました。 素早く、新規マイクロサービス作成とかいう高コストでもなく、社内QAからアクセス可能で、postリクエストを処理可能な、社内向けのWebアプリケーションを作成する方法を見つける必要があります。 この問題をチームに相談したところ 「GASでできるかもしれない」 というアドバイスをもらいました。 自分はいままでほとんどGASに触ってこなかったのでそのアイデアは目からウロコです。 早速サンプルのGASを作成してpostのリクエストを送信したところちゃんとリクエストパラメータを取得して画面に出力することができました。 さらにjs実行によりform post(同期post)の実行もできることがわかったのです。 これなら仮事業者としてメルカリモバイルのAPIにむけて特定のパラメータ付きでpostリクエストをコールしたり、メルカリモバイル側からpostリクエストをうけとってから任意のタイミングでコールバックをコールしたりするような本番の外部MVNO事業者と同じふるまいをするアプリケーションが用意できます。 GASによる仮事業者の作成 本格的にGASでアプリケーションを作成してみたところいろいろ課題や混乱が発生しました。 まず最初はコード管理の問題でした。 通常の開発はGitを利用した差分管理およびGitHubへのpush、review、mergeといった手順をふむのが普通ですが、GASの場合はなんとブラウザ上でコードを修正し、ブラウザ上でデプロイを行うものでした。 これだとGit開発でうけている恩恵がうけられません。コードの差分のreveiewも不可能です。 claspの導入 みなさんがどうやってGASを開発しているのか調べたところ、claspというcliツールがgoogleから提供されていることがわかりました。 https://github.com/google/clasp?tab=readme-ov-file claspはGAS開発のための便利なコマンドを提供しており、ローカルから自分のGASアプリへコードをpushすることが可能になります。 これでコードをGit上で管理しつつ、GASへの反映もブラウザ上のファイル編集ではなくローカルのファイルのpushにより可能になるのでより安全な開発になります。 GASの癖 GASのWebアプリケーション作成は少し違和感を覚えるかもしれません。 まず、ランタイム時の依存ファイルのインポートが不可能です。 通常のJavaScript(js)を利用したWebアプリケーションはimport文(もしくはrequire文)を利用して依存するファイルをロードするのが普通ですが、GASだとそれができません。 アプリのアクセス時のSSR処理時にコードをinjectするしか方法がないということです。 PHPのようなやり方ですね。 // これでjsの書かれたhtmlをinjectできる <script> <?!= includeFile('jsUtil') ?> </script> もし関数を定義してそれをコールしたい場合、それはグローバルに定義してあらかじめhtmlのヘッダで読み込んでおく必要があります。 次に、GAS上で取り扱うファイルは拡張子がhtmlである必要があります。 上記のコード includeFile(‘jsUtil’) は jsUtil.html をここに読み込んでくださいという命令になっています。 htmlとなっていはいますが、中身はjsです。 GASが「htmlしか動的に読み込ませない」というルールにしているためにこのようなヘンテコなことになっているようです。 開発側は定義した関数をjsとしてファイルに書き込み、デプロイ時はその拡張子をhtmlとしてpushする。 さらに実行html内には includeFile関数をつかってヘッダからその関数をグローバルに読み込む。 といった工夫が必要になります。 別に直接実行htmlに関数を書いてもよいですが、複数htmlに同じ関数は書きたくないでしょう。 GASアプリからURLのクエリに含まれた値やpostでリクエストされた値を取得する場合built-inで用意された変数があるので、以下のような関数をhtmlに定義すればjs側でランタイム時に利用できるようになります。 <script> <!-- postパラメータをinject --> function getHtmlPostParams() { return { <? for (let key in postParams) { ?> <?= key ?>: <?= postParams[key] ?>, <? } ?> }; } </script> <!-- クエリパラメータをinject --> <script> function getHtmlGetParams() { return { <? for (let key in getParams) { ?> <?= key ?>: <?= getParams[key] ?>, <? } ?> }; } </script> これ以外にも新規デプロイするたびにURLが変わるけど、URLが変わらないデプロイ手順があったりと癖の多いツールですが理解さえしてしまえばなんとかなりました。 動作検証仮事業者の画面サンプル 仮事業者ポートアウト 仮事業者ポートイン 仮事業者ポートイン完了 ポートインとポートアウトという言葉はわかりやすいようでわかりづらいです。 なぜなら一方でのポートインは、同時に他方からするとポートアウトだからです。 ここは混乱しないように仮事業者のタイトルにはわかりやすく主語を明記しておきました。 結論その1 無事メルカリモバイルの動作チェックのための仮事業者のアプリケーションを立ち上げることができました。 仮とはいってもQAのためのアプリなので、メルカリモバイルの堅牢性確認のため様々なパターンのリクエストを発生させる機能も入っています。 このように、claspとGASを利用して手軽にpostリクエストを処理できるWebアプリケーションを社内に用意することができました。 post処理可能なWebアプリケーションを簡単に作成できるツールがこんなに身近にあるとは発想としてありませんでした。 みなさんもテストなどの際に社内におけるpostやgetを処理するための受け皿となるWebサービスが必要になるときが来るかもしれません。 そのとき、これほど簡単に自前サーバーも必要なく立ち上がってくれるこのツールを思い出すと役に立つかもしれません。 (その2)Jiraのissue作成のためのGAS もうひとつGASで効率をあげた話をします。 メルカリは開発のサポートとしてAIを積極的に導入しています。 その一環でMCPサーバ経由からのJira issueの作成を試してみました。 MCPサーバとはAI経由でサードパーティのツール(Jiraやfigma等いろいろ)を操作するための仕組みです。 今まではAIに操作のやり方をおしえてもらって自分でツールを操作していたと思いますが、これを使うとAIが直接ツールを操作することができるようになります。つまり、Jiraのissue作成もやってもらえます。 CursorというAIツール上のプロンプトから日本語で、issue作成を依頼してみました。 XXXプロジェクトに以下のJira issueを作成してください タイトル: MCP経由からのチケットさくせい (AIからの回答) Jiraチケットが正常に作成されました! チケット番号: XXX-0000 タイプ: タスク ステータス: To Do URL: https://****** なんと簡単にissueが作成されてしまいました。 しかしこれだけではわたくしは満足せず、どうせならGoogleスプレッドシートに属性をすべて定義しておいてそれからissueを作成してほしいなと考えました。 // シートのイメージ サマリー | issueのリンク | Epic | 担当 | ラベル | スプリント | ストーリーポイント rowごとにissueの属性を定義し、isseの生成が完了するとリンクがシートに追加されるというものです。 前からJiraのissue作成後の手動属性追加は面倒だと考えていたため、属性をシートでまとめて管理できるのはよいアイデアだと考えました。 Jiraのissue管理は便利な反面、指定すべき項目が多いです。 手動で付与していると設定忘れが発生したりします。 タイトル 説明 担当 複数ラベル(Frontendラベルおよびチームのラベル等) スプリントへの追加 ストーリーポイントの追加 親Epicの追加 リリースバージョンの追加 ローカルnodeアプリケーションからのJira issue作成 最初は対象のスプレッドシートを読み込んでJiraのAPIをコールするようなローカルのnodeアプリケーションを考えたので、Cursorに聞きながらnodeのexpressアプリケーションを作成しました。 expressである必要もなかったかもしれないですが、即座にコードが出力されて割と簡単にアプリを作成してJira issueが追加できるようになりました。 ほぼ想定どおりの動きをするアプリケーションができあがったあと、いちいちローカルのWebアプリを起動するのも面倒だなと考えてしまいました。 GASによるスプレッドシートからのJira issue作成 ということで、結局はこのローカルコードをクラウド上で実行すれば一番楽だということに気づいたのでCursorに依頼をしました。 このnodeアプリケーションをGASに持っていって、GAS上のボタンを押すとJiraのissueが追加されるようにしたいです (AIからの回答) GAS (Google Apps Script) への移行についてアドバイスさせていただきます。 現在のNode.jsアプリケーションをGASに移行するために、主な変更点と実装方法を説明します。 まず、コードベースを確認させていただきます。 ... 現在のNode.jsアプリケーションをGAS(Google Apps Script)に移行するために、以下のような変更を提案します: 1. GASプロジェクトの作成 2. スプレッドシートとの連携 3. Jira APIとの連携 4. UIの実装(ボタンの追加) ... 素晴らしいです。ものすごくわかりやすく段階的な手順を明示してから具体的なコードを提示してもらえました。 これにより無事スプレッドシート上で動くGASアプリを使って簡単にJira issueを作成することができるようになりました。 いままでどちらかというとJiraのissue作成は面倒に感じていたのですが、せっかく自分でツールを作成したとなるとそうは言っていられません。 シートのタブごとに実行できるので文脈をかえてどんどんissueを作成していけます。 リリースバージョンごと、アサイニーごと、エピックごとというようにさまざまな文脈でシートを作成してissueを作成していけます。 もちろん属性のつけ忘れは存在しません。 ボタンを押した後にissue作成が成功するとIssueのカラムに自動的にリンクが付与されます。 すでにリンクが存在している行は無視されて作成からは除外されるようになっています。 ラベルも任意の数紐づけが可能です。 自分で作成したJira issueのヒストリーも見られるのがよいなと感じています。 結論その2 このように、GASを利用して効率をあげることができた事例を紹介しました。 他にも気づいていないだけでさまざまな業務を要所要所で自動化できるかもしれません。 ひらめいたアイデアをツールに落とし込むためのAIの存在も見逃せないところです。 いままでアイデアはあったけれどもどうやってツールにすればよいかわからなくてあきらめていたという人は、ぜひともAIに問いかけてアイデアを実現してみることをおすすめします。 以上でわたくしからの話は終わりです。 読んでいただきありがとうございました。 明日の記事は @anzaiさんの「Davin にE2Eテストの実装を任せる」と @Soma Nakaoさんの「インターンレポート」です。引き続きお楽しみください。
アバター
目次 はじめに 背景 広告審査における課題 AI審査の概要とシステム構成概要 プロンプトの検証 まとめ はじめに こんにちは。メルカリAdsのバックエンドエンジニア、chapaと申します。 今回は、メルカリAdsがOpenAIを活用して実現した広告審査プロセスについて解説します。 本記事を通じて、AI活用による課題解決について具体的にお伝えできればと思います。 背景 メルカリAdsでは多くの広告主様にご利用いただいており、さまざまな広告素材が日々入稿されています。その中には、残念ながら不適切な表現を含むものもあり、それらがメルカリのお客さまの目に触れることは、お客さま体験を損ねるリスクとなります。 これまではサンプリングした広告素材を人の目で確認していましたが、広告主様の増加に伴い、手動での対応に限界が見えてきました。そこで、審査の自動化に取り組む必要性が生じました。 広告審査における課題 広告事業をおこなうにあたって、入稿されている広告素材全てに対して審査を行う必要があります。 ただ入稿される広告素材は非常に多く、手動審査での対応では難しい状況です。 そのため、自動判定を取り入れることで基準を満たすかを効率的にチェックして、手動審査するべき数を減らし、運用の効率化を進めています。その手法の一つがAI審査です。 AI審査の概要とシステム構成概要 AI審査では、以下の機能を実現しています。 広告画像やタイトルテキストなどに、不適切な表現が含まれていないかをチェック 禁止表現が検出された場合は、広告配信を即座に停止 システム構成については以下のようになっています。 広告主様が更新するすべての広告をリアルタイムでOpenAIへ送信するとコストが非常に大きいため、以下の工夫を採用しました。 リアルタイムチェックは行わず定期的にBatchAPIを用いてOpenAIへ送信 ライトなモデル(gpt-4o-mini)を利用することでOpenAIへのコストを削減 OpenAIのオプション設定も以下を採用 結果のブレをなくすため、 Temperature は 0.01 に設定 結果はJSON形式にするため ResponseFormat は json_object に設定 プロンプトの検証 審査を実施するためのプロンプトを作成し、さまざまなパターンを用意して検証しました。 Playgroundでプロンプトと質問を送信した際には、期待通りに結果が返ってきましたが、BatchAPIを使用した際にいくつかの課題がありました。 システム的な課題と要件的な課題をいくつかサンプルとともにご紹介します。 ① 期待しているJSON形式で返却されない 返却値はOpenAIのオプション設定にてJSON形式と定義し、プロンプトに例を記載しました。しかし、以下のようなケースが見られました。 JSON形式として正しいものの、期待している統一フォーマットではない場合がある JSON形式そのものが正しくない場合がある 期待される返却値の一例: // 期待される返却値の一例: { "id": "test-id-00001", "result": <審査結果値>, "reasons": [{"type": 1, "value": "審査理由詳細・・・"}] // 審査理由があれば配列で設定される } // 実際の返却値の例: { "id": "test-id-00001", "result": <審査結果値>, "reasons": [""] // 不正な形式(配列が空欄の際にダブルクォートのみが入っている) } このケースの場合はプロンプトに出力形式を厳守するようプロンプトに記載し解決しました。 これによりほぼ不正な形式で返却されることは解消されました。 // 省略 ## 出力形式 **必ず以下の出力フォーマットを厳守** { "id": <id>, "result": <int>, "reasons": [{type: <int>, value: <string>}] } ② 期待しているID値が返却されない 質問で送信したIDをキーとして返却されることを期待していましたが、以下のようなケースが見られました。 // 送信値の例: { "id": "test-id-00001", "text": "test-text" } // 期待される返却値の一例: { "id": "test-id-00001" "result": "<審査結果値>", "reason": [] } // 実際の返却値の例: { "id": "test-id-00", "result": "<審査結果値>", "reason": [] } このケースはBatchAPIへリクエストを送信する際にユニークなID( custom_id )を設定することができるので、返却後もそのIDを正として利用することで解決しています。 参照: https://platform.openai.com/docs/api-reference/batch/request-input ③ 性的な表現と下着広告の判別 広告素材の中には性的表現を含む画像と下着広告が混在しています。これにより、下着広告が誤って性的表現と判定される問題がありました。 入稿されたデータから無作為にデータを取得して、期待値を設定し正答率を取りながらプロンプトの改善を行いました。 初期プロンプト 正答率: 70.76% 「性的表現」と「下着広告」を区別する定義が曖昧 改善プロンプト 改善後の正答率は 77.33% に向上しました。これは下着広告に関するプロンプトの条件追加が要因と考えられます。 しかし、「性的表現」と「下着広告」の詳細定義が曖昧なままでした。 最終プロンプト 「性的表現」と「下着広告」の詳細に定義することで不明瞭な基準に基づく誤判定が大幅に減少し、正答率が 13.37ポイント 向上し 90.67% となりました。 例としては、以下のような記載を追加しました。 性的表現: 過度に性的な印象を与える画像の構図・演出等を明確定義 下着広告: 通常の下着製品画像は適切なファッション広告として区別 こうしたプロンプトの改善にもAIを活用しており、OpenAIにプロンプト、質問内容、返却された結果を伝えて、改善提案してもらい結果の正答率を上げることができました。 プロンプトの書き方の改善が最も大事だと改めて認識しました。 とうとう有識者とではなくとも、AIと壁打ちできるようになってしまったと感じました。 まとめ この記事では、メルカリAdsにおけるOpenAIを活用した取り組みを紹介しました。 特別な工夫を施さなくても、基本的なAI活用によって大量のデータを効率的に処理できる可能性をお伝えすることができたと思います。 今後も引き続き改善を行い、より安心で安全なお客さま体験を提供できるよう努めていきたいと考えています。
アバター
はじめに こんにちは。メルペイVPoEの@jorakuです。 この記事は、 Merpay & Mercoin Tech Openness Month 2025 8日目の記事です。 AI Agent / AI Code AssistなどのAI Toolsが日々リリースされており、めまぐるしい時代を楽しく過ごしています。ここで記載されているものも1ヶ月後には古くなる可能性もありますが、現時点での情報を残しておきます。 目次 AI 2027 シナリオ AI Code Assist / AI Agentの登場 One Person, One Release 役割の再定義 AI Nativeの夜明けに求められるもの AI 2027 シナリオ 一時期話題になった非営利団体 AI Futures Projectが作成した レポート です。元Open AIの方などが著者です。 レポート内容は衝撃的でシンギュラリティについても触れられています。賛否両方のコメントもあり、正確性については敢えてここでは言及しません。というか私には分かりません。 AI Code Assist / AI Agentの登場 ただ、現実的に今、ソフトウェア開発に大きな変化が生まれています。 ソフトウェア開発の風景は、AIコードアシスタントやAIエージェントの登場により、根本的な変革の岐路に立っています。これらの技術は、単に「何を作れるか」だけでなく、「どのように作るか」を根本から変えつつあります 。マイクロソフト社のKevin Scott氏が「我々の生涯で起こった最も重要な技術プラットフォームのシフトかもしれない」と 述べる ように、この変化の大きさは計り知れません 。AIは単なる技術ではなくプラットフォーム、ソフトウェア開発ライフサイクル(SDLC)における協調的なパートナー、場合によっては自律的なエージェントへと進化しています 。 20年以上前の話になりますが、Public Cloudという概念を作ったAWSの登場も衝撃的でした。 AWS/Azure/Google Cloudの登場により、私たちはより安全に、より安定した、拡張性の高いプロダクトを顧客にいち早く提供できるようになりました。ただ、今回のAI Code Assist / AI Agentは、コード生成だけでなく、テスト、デプロイ、保守に至るまでソフトウェア開発ライフサイクル(SDLC)全体に影響を与えており、その進化のスピードは目を見張るものがあります。 One Person, One Release これまでの技術的な専門性や、業界の知識などの専門性を活かしながら価値貢献してきている時代でした。ただ、これからはそれが両極端になっていくと考えます。より専門性の深い分野に特化していくのか、それとも、幅広い領域で素早く価値提供するのか。 これまでのAI Toolsの登場でプロンプトだけでアプリケーションが作れる時代になりました。プロンプトベースでアプリケーションを素早くプロトタイピングし、PoCレベルまで到達できる時代になっています。非エンジニア職種の方でも、一定のアウトプットが可能な環境が整ってきました。逆にエンジニアだとしても良い体験設計できるデザインをできるようになりました。 誰もがプロダクトを作れる時代に入り、“プロダクトをつくる会社”と“プロダクトを利用する会社”の境界が曖昧になりつつあります。もはや「プロダクトカンパニー」というのは古い時代の言葉になるのかもしれません。 そこで期待されるのは、起業したばかりのスタートアップの様に、一人の人がより多くの役割をスピード感もって価値提供していくことになると考えます。 次年度の Engineering Roadmap を思案している所ですが、Visionとして「One Person, One Release」を入れていきたいと考えています。 PMもEngineerも壁を越えていきます。一人の人が企画から開発、QA、リリースまで一気通貫で出来ることを目指します。 技術の壁を越え、ドメイン知識を越え、役割を越えて行くためのAIの活用とし、それらを使い熟すのです。 役割の再定義 それでは今後それぞれの役割はどのように変化するのでしょうか。より多くの方がこれまでの役割を越えていけると考えます。越えて行かねばならない。とも言えるかもしれません。 専門性がより深化していく流れは今の専門性と変わらない点があるかもしれませんが、より影響範囲を広げてお客さまへの価値提供が出来る役割です。仮にここではAI Agent Orchestration Engineerと名付けます。( 参考記事 ) もちろん、AIを駆使することで各職種がコードに触れることも可能になりますが、求められる責任や精度はそれぞれの職能に応じて変わります。 役割 責任 AI Expert AIの専門家としてAI/LLM製品やツールの開発 System / Domain Architect AI/LLMでは補えない技術的難易度の高いものやPlatformなどの基盤を整備する役割。また、法令要件や求めるべき倫理など業界の専門性が高い要件を構築・監査する役割 Agent Developer 自社の業務や運用に合わせてデータの整備やMCPの構築、業務に合わせた自動化/省人化するAgentの開発。また、Agent to Agentのような基盤の開発も担う AI Agent Orchestration Engineer AIエージェント同士を組み合わせ、お客さまの課題に対して機能や体験を統合的に設計・実装・提供する役割。One Person, One Release を実現する役割 AI Nativeの夜明けに求められるもの 新しい時代の夜明けです。地球が回る限り後戻りすることはありません。 最後に新しい時代に応じて何が求められてくるのか、あくまで個人的な意見ですが、夜が明けた今、個人・組織とって大切にしていきたいマインド・スキルセットを記載します。 組織 セキュリティと利便性の両立 AIは利便性高く、生産性にも大きく寄与しますが、自社の大事な情報の流出や新たなハッキングのリスク、また倫理的な観点のリスクも発生します。守るべき点ももちろん大事ですが、攻めと守り両方を求めていく姿勢が大事だと考えます。 情報管理/情報戦略 これまでの情報資産や、情報化されていない経験知見をどのように蓄積していくのか、組織においてはこれまで以上に重要な要素になります。 AI活用の定量評価 自社でどのようにAIが使われているのか、それを定量的に観測し、評価にも組み込んでいく事が必要になってきます。もちろんお客さまにどのような価値を提供できたのかとても大事な部分ですが、エンジニア含めたプロダクト開発する組織の一つの指標としては追うべき数値と捉えています。 個人 言語化能力 AIに理解させるためには、正しく言葉で伝えていく必要がありますし、それらを学習してもらう必要があります。何がしたいのか、どうしたいのか、誰にでも分かるように論理的に記載して良く必要があります。 メルカリ入社後に驚いた事の一つとしてドキュメント文化があります。今後の時代にはこのドキュメントが功を奏するように持って行きたいと考えています。 好奇心 個人のマインドセットとしてとても大切だと考えるのが好奇心です。wakuwakuする心ですね。好奇心を持つのではなく、それを自ら作り出せる事がとても大事です。時代の変化は激しいですし、自分の領域を一部奪われてしまうのではないかという猜疑心も生まれます。止まっていては何も始まらないので進むしかありません。前に進むための動力としての好奇心を持つことが大切です。 もし、まだAIを活用しきれていないという課題感を感じている方、大丈夫です。今はAI Agent / AI Code Assistの勃興時代です。日々変わりますし、1ヶ月前の情報や経験がもはや古くなる時代です。 ですから、今までのアドバンデージは無く、今から始めても直ぐに先頭を走ることが出来ます。みなさま「好奇心」をもってこの変化を楽しんでいきましょう! 明日の記事は @toshickさんによる「GASで効率化!MVNOの動作検証仮事業者&Jira issue作成 with AI」です。引き続きお楽しみください。
アバター
こんにちは。メルカリモバイル Tech Leadの @_seitau です。 この記事は、 Merpay & Mercoin Tech Openness Month 2025 の7日目の記事です。 今回は、CursorをはじめとするAIコーディング支援ツールに、社内コードの検索能力を持たせるための取り組みをご紹介します。 はじめに メルカリでは、社内向けのマイクロサービス開発フレームワークや、ScenarigoというE2Eシナリオテストツールなど、独自の技術基盤が整備されており、これらが開発速度を大きく向上させています。 一方で、社内に類似した実装が豊富にあるにもかかわらず、Cursorがそれを認識できず、実装内部を自律的に把握できないという新たな課題も生まれていました。 社内基盤の充実がCursorの制約になっていた背景 メルカリでは、メルカリモバイルチームを含む複数のチームで共通のマイクロサービス開発フレームワークが使われており、構成や実装パターンが似ているため、他チームの実装を参考にすることは日常的です。ScenarigoによるE2Eテストのシナリオ記述も同様に、既存のパターンを参照する場面が多くあります。 しかし、これらの実装は複数のプライベートリポジトリに分散しており、通常のGitHub検索では、求めるコードを効率的に見つけることが難しい状況でした。単一リポジトリに閉じた実装や、広く知られた著名なライブラリを使用した実装であれば、Cursorは十分にその能力を発揮できていましたが、社内独自のライブラリやツールを参照している箇所では、Cursorが期待通りに機能していない状態でした。 解決策:Sourcegraph MCP Server 既存のGitHubのMCP Serverも存在しますが、GitHubのAPIは社内のコードを横断的に検索するには限界がありました。特にメルカリのように複数のマイクロサービスが多層的に構成され、リポジトリが分割管理されている場合、単一リポジトリ検索に依存するGitHub APIでは、Cursorにとって十分な参照性を持たせることが困難です。 そこで、Sourcegraphのクロスリポジトリ検索機能と、構造化された検索性を活かす形で、SourcegraphのMCP Serverを社内向けに実装しました。 このMCP Serverにより、Cursorにプロンプトの中でSourcegraphを使用するように指示することで、必要な社内コードを自発的に検索し、その結果を回答やコード生成に反映することが可能になります。 自作Sourcegraph MCP Serverのインターフェース 今回私が開発したSourcegraph MCP Serverは、Cursorが社内コードにアクセスするための非常にシンプルなAPIインターフェースを提供しています。主に以下の2つのメソッドで構成されています。 1. コード検索ツール ( mcp_sourcegraph_search_code ) このツールはSourcegraphの強力な検索機能を利用し、メルカリの全社内リポジトリを横断してコードを検索できます。 パラメータ q (required): Sourcegraphのクエリ構文に従った検索クエリテキスト リクエストサンプル 基本的な検索: { "q": "PubSubLogWithFieldsInterceptor" } 言語を指定した検索: { "q": "import React language:typescript" } 関数名での検索: { "q": "func main language:go" } ファイル名を含む検索: { "q": "handleUserLogin file:*.go" } レスポンスサンプル { "results": [ { "repository": "github.com/mercari/service-a-repo", "file": "internal/interceptor/logging.go", "lineNumber": 45, "content": "func PubSubLogWithFieldsInterceptor() {...}", "url": "https://sourcegraph.com/github.com/mercari/service-a-repo/-/blob/internal/interceptor/logging.go#L45" }, { "repository": "github.com/mercari/shared-lib", "file": "pkg/logging/interceptor.go", "lineNumber": 23, "content": "type PubSubLogWithFieldsInterceptor struct {...}", "url": "https://sourcegraph.com/github.com/mercari/shared-lib/-/blob/pkg/logging/interceptor.go#L23" } ], "totalCount": 12, "hasNextPage": true } 2. ファイル内容取得ツール ( mcp_sourcegraph_get_file_content ) このツールは、指定したリポジトリの特定のファイル内容を直接取得します。 パラメータ repository (required): github.com/を含む完全なリポジトリパス filePath (required): リポジトリ内のファイルパス commitID (optional): 特定のコミットIDまたはブランチ名 リクエストサンプル 基本的なファイル取得: { "repository": "github.com/mercari/service-a-repo", "filePath": "src/main.go" } 特定ブランチのファイル取得: { "repository": "github.com/mercari/service-a-repo", "filePath": "config/app.yaml", "commitID": "develop" } 特定コミットのファイル取得: { "repository": "github.com/mercari/service-a-repo", "filePath": "README.md", "commitID": "a1b2c3d4e5f6789" } 深いパスのファイル取得: { "repository": "github.com/mercari/service-a-repo", "filePath": "internal/services/user/handler.go" } レスポンスサンプル { "content": "package main\n\nimport (\n \"fmt\"\n \"log\"\n \"os\"\n)\n\nfunc main() {\n if len(os.Args) < 2 {\n log.Fatal(\"Usage: program <arg>\")\n }\n \n fmt.Printf(\"Hello, %s!\\n\", os.Args[1])\n}", "repository": "github.com/mercari/service-a-repo", "filePath": "src/main.go", "commitID": "main", } 主要メリット これらのツールが提供する主なメリットは以下の通りです。 1. リポジトリの横断検索 メルカリ組織の全リポジトリを検索対象にできるため、広範囲なコード探索が可能です。 共有ライブラリの実装詳細を効率的に見つけるのに役立ちます。 2. 高度なクエリ構文 Sourcegraphの強力なクエリ構文をサポートしており、柔軟な検索が可能です。 言語、ファイル名、パスなどで検索を絞り込むことができます。 3. 直接的なファイルアクセス リポジトリをクローンすることなく、ファイル内容を直接取得できます。 特定のコミットやブランチのファイルにアクセスできるため、過去の履歴や開発中のブランチのコードも確認できます。 Cursor Ruleによる自発的な検索を実現 Sourcegraph MCP Serverを利用すると、Cursorに明示的に「Sourcegraphを利用して」と指示するだけで必要な時にコードベースを検索してくれます。 さらに、以下のようにCursor用のルールを設定することによって、明示的に指示を与えずとも、Cursorが自発的にMCPを利用して検索を行うようになります。 # 社内コード検索のためのルール メルカリ組織内のリポジトリでコードを検索する際は、search_code MCPツールを使用してください。これにより、すべての社内リポジトリを横断的に検索できます。 使用例 mcp_sourcegraph_search_code(q="PubSubLogWithFieldsInterceptor") 利点 - 社内すべてのリポジトリを横断的に検索可能 - 共有ライブラリの実装詳細を特定できる - 通常のリポジトリ閲覧では見つけにくいコードにもアクセス可能 - 社内フレームワークの実装やパターンをより深く理解できる 一例として、以下のようにCursorに「〜のリポジトリの実装を参考にして」と伝えるだけで、Sourcegraphを通じて社内のコードベースを検索し、実装を進められるようになっていることがわかります。 まとめ Cursorに対して必要な情報を与えるだけでなく、自ら必要な実装を探しにいける環境を整えることが重要でした。GitHub APIよりも社内実装の検索に適したSourcegraph APIを採用し、それをMCP Server経由でCursorから能動的に利用できるようにしたことで、より実用的なコード生成パートナーとして活用できるようになりました。 今回の取り組みが、Cursorを活用した開発体験の向上や、社内における生産性のさらなる向上につながれば幸いです。 明日の記事は メルペイ VPoE @Jorakuさんです。引き続きお楽しみください。
アバター
はじめに こんにちは。メルペイ Payment Coreチームの @susho です。 この記事は、 Merpay & Mercoin Tech Openness Month 2025 の6日目の記事です。 我々のチームでは、メルペイにおける各決済手段に応じた決済処理を提供しています。今回、新しくチェックアウトソリューションという、決済処理の実装と画面を一括で提供するソリューションを提供することにしました。 ( Stripe Checkout の内製版のようなものをイメージしてもらえると良いと思います。) 詳細は 決済基盤の新たな挑戦: 決済チェックアウトソリューションの開発 をご参照ください。 この記事では、そのソリューションのバックエンドに着目し、アーキテクチャを紹介したいと思います。 これまでの課題 これまでメルカリグループでは、決済処理が必要な新規サービスを立ち上げる際、提供する決済手段に応じて各サービス提供者で決済処理を実装する必要がありました。単純な同期処理だけの場合であればそこまで難易度は高くないですが、3DSを利用したクレジットカード決済など、リダイレクトが必要になる非同期処理が含まれる場合、実装コストは格段に高くなります。 また、決済処理における画面も実装する必要がありますが、基本的にはどのサービスも必要な画面の部品は共通のものが多くなります。 車輪の再発明のように、新規でサービスを立ち上げる際これらの実装をしなければならず、爆速にサービスを立ち上げることが難しくなっていました。 そこで、これらの機能を備えてソリューションを提供することで、これらの課題を解決できるのではないかと考えました。 アーキテクチャ概要 まずはアーキテクチャの概要を紹介します。 Checkout Solution Service 決済処理の一連のフローを管理するリソースを管理し、決済処理を実行するマイクロサービスです。 技術スタックとして、主にSpannerやPub/Subを利用しています。また、安全に分散トランザクションを管理するために、内製のWorkflow Engineを利用しています。Workflow Engineに関しては こちら を参照してください。 Checkout Frontend 決済画面を提供し、BFFを経由してCheckout Solutionの決済処理を呼び出します。 Checkout BFF Frontendで決済画面を提供するために必要なAPIを提供します。 grpc-federation を利用してBFFを構築しています。 Processing Tracer データの整合性が担保されているかどうかをチェックをするためのリコンサイル処理をキックし、その成否を管理するマイクロサービスです。詳細は こちら を参照してください。 Payment Service さまざまな決済手段を提供するための各種APIを提供しているマイクロサービスです。Checkout SolutionはこれらのAPIを組み合わせて決済処理を提供します。 利用者であるClient Servicesはお客さまへ決済画面を提供するために、Checkout SolutionへAPIを呼び出し、そのレスポンスに含まれるURLへ遷移させることで決済機能を提供することができます。 アーキテクチャ詳細 Checkout Solution Serviceの詳細を説明します。 ベースとなる部分は Stripe Checkout を参考にしています。 API まず、Checkout Solution Serviceで提供するAPIの機能について説明します。 CreateCheckoutSession API 決済処理のフローを開始するためにCheckoutSessionを作成するAPIです。決済画面の一意なURLが払い出され、Client ServicesはそのURLへ遷移することで、お客さまへ決済機能を提供することができます。 IdempotencyKeyを受け取り、冪等性を担保します。 ConfirmCheckoutSession API 決済処理を実行するためのAPIです。お客さまが決済ボタンを押下することでFrontendから呼び出されます。ある決済手段で決済が失敗した場合でも別の決済手段で決済できるように、このAPIは決済が成功するまで有効期限が切れるまで呼び出すことができます。 IdempotencyKeyを受け取り、冪等性を担保します。 リソース 次に、Checkout Solution Serviceで管理する主なリソースについて説明します。 CheckoutSession 決済処理の一連のフローを管理するリソースです。 状態遷移 open 初期状態。CreateCheckoutSession APIが呼び出されることでこの状態になります。 complete 後述するPaymentIntentがrequires_captureになるとこの状態になります。 expired 有効期限が切れるとexpiredになります。 PaymentIntent 実際の決済処理を管理するためのリソースです。 状態遷移 requires_payment_method 初期状態。決済処理を実行するまで待っている状態です。 processing 決済処理の実行中に必ず遷移する状態です。この状態にある場合、他のリクエストによってこのリソースを操作することはできず、ロックされます。 requires_capture 決済処理のオーソリが完了したら遷移する状態です。 requires_action 決済処理のオーソリを実行するために追加のアクションが必要になる場合に遷移する状態です。例えば、3DSの認証などが必要な場合はこの状態に遷移します。 succeeded オーソリが確定したら遷移する状態です。 canceled オーソリがキャンセルされたり、追加のアクションを待っている場合に有効期限が切れてキャンセルされた場合に遷移する状態です。 Charge 単一の決済処理を管理するリソースです。PaymentIntentから作成されます。各決済手段の機能を提供しているPaymentのリソースと対応する形で状態を管理します。 CheckoutConfig Clientでカスタマイズしたい設定を管理するリソースです。例として、レイアウトの設定などを管理しています。 シーケンス 次に、実際にこれらのリソースがどのように関連して処理を実行するかのシーケンスを紹介します。 CreateCheckoutSession API リソースを作成するためにDBへINSERTし、Clientへリソースを返します。ここに決済画面へのURLが含まれます。 ConfirmCheckoutSession API PaymentIntentを作成し、決済処理を実行します。冪等かどうかをチェックし、冪等であれば処理を進め、そうでない場合にすでにロックされていたらリクエストをエラーで終了させます。また、決済処理が失敗した場合、その失敗理由をDBに保存します。 オーソリが失敗した場合、状態をprocessingからrequires_payment_methodへ戻すようにすることで、再度APIが呼ばれてもまた別のオーソリを実行できるようにしています。 APIの処理全体をWorkflow Engine経由で実行するため、途中でタイムアウトエラーになった場合、非同期でリトライされます。 冪等性の担保 ここで、APIの共通機能である、冪等性の担保についてどのように実装しているかについて紹介します。 APIでIdempotencyKeyを受け取ります。 同じIdempotencyKeyでDBに保存されているレコードがあるかどうかをチェックします。 存在していない場合は、DBに保存し処理を進めます。 存在している場合は、リクエストフィールドのハッシュ値から計算されたFingerprintと、保存されていたFingerprintが一致しているかどうかをチェックします。 一致している場合は、レスポンスを返すか、処理を進めます。 一致していない場合は、エラーを返します。 このようにすることで、同じIdempotencyKey、リクエストフィールドであれば処理を続行、または即座にレスポンスを返せるようにでき、そうでない場合はエラーにすることが可能になります。 おわりに この記事では、チェックアウトソリューションのアーキテクチャを紹介させていただきました。 3DS認証が必要な場合など、リダイレクト処理が必要になるためまた複雑になるのですが、今回は1番シンプルなケースを書きました。内部の状態遷移や冪等性の担保など、少しでも参考になれば幸いです。 明日の記事は seitauさんの「Sourcegraph × 自作MCP Serverによる社内コード検索連携の取り組み」です。引き続きお楽しみください。
アバター
この記事は、 Merpay & Mercoin Tech Openness Month 2025 の5日目の記事です。 この記事では、Payment & Customer Platform (PCP) Vision 2.0の一環として進行中の、決済チェックアウトソリューション開発に関する背景、プロダクトビジョン、全体設計、そして現在の状況について紹介します。 はじめに こんにちは。メルペイPayment & Customer Platform(PCP)チームのEngineering Headの @foghost です。 PCPの各ドメインチームが現在メルペイの事業だけでなく、メルカリグループが展開するすべての事業を支えるための決済、KYC、加盟店管理の社内共通ビジネス基盤(Foundation)の開発を行っています。 (現状をVision 1.0と定義します) しかし、現状複数の事業に利用可能な共通機能の提供はできてるとはいえ、機能の拡張性が不足してたり、導入時のコストがかかったりする課題があり、各事業の立ち上げやグロースを爆速させる武器にはまだなりきれていない状況です。 昨年(2024年)から、今後の10年を見据えたTech Roadmapとして、新たな PCP Vision 2.0 を策定し、事業拡大のスピードを大幅に加速させる共通ビジネス基盤への進化を目指して取り組んできました。 PCP Vision 2.0では、「 Functionalities Evolution(機能の進化) 」と「 Domain Architecture Evolution(アーキテクチャの進化) 」と2つの側面から、現在の課題を整理しながら、各ドメイン領域において考えられる将来の姿を定義し、取り組みを進めています。 今回ご紹介する 決済チェックアウトソリューション は「Functionalities Evolution(機能の進化)」を実現するため、決済基盤の領域でチャレンジしている取り組みの一つになります。 課題定義 現在、メルペイは決済事業だけでなく、メルカリやメルカリShopsなどのEC事業にも同じ決済基盤を提供する決済APIを使用しています。 共通の決済APIソリューションの提供のみでも外部決済手段の接続、複合決済含めた決済トランザクションの管理、不正検知、会計連携など含めて共通化ができて各EC事業における決済機能の導入の開発負担を大きく削減できています。 しかし、決済APIのみでは、新しい決済手段(例:コンビニ支払い、ビットコイン支払い)に対応するには、各プロダクトごとに個別の実装が必要になります。このため、画面の改修やバックエンド処理のフローを含めた実装作業が必要です。プロダクトの企画、要件定義、機能設計、開発などをすべて含めると、数ヶ月かかる場合もあります。 また、プロダクトにおけるチェックアウトのコンバージョン率を向上させるためのチューニングや、チェックアウトのUX改善においても、複数のプロダクトで重複した開発労力が発生しています。 このような課題を解決するために、Stripeなどの海外の決済事業者が提供してるLow Code Checkout Solutionのような、社内向けの Low Code Solution を開発できないかについて、2024年6月頃から検討を始めていました。 プロダクトビジョン ソリューションの企画段階では、解決したい課題に向けて、US事業を含むEC事業におけるチェックアウトのユースケースを調査し、PMを含めて以下のようにソリューションのプロダクトビジョンを明確にしました。 各プロダクトにチェックアウトソリューションのClient/Frontendの画面実装を シームレスに組み込められる Low Codeで簡単にインテグレーションできる 社内統一したDesign Systemに基づいた統一感のあるUXの提供 Configurable 利用可能な決済手段や、クーポンなどの共通要素はConfigでカスタマイズできる。設定すれば、すぐにその決済手段を利用できるようになる チェックアウト画面についてもConfigでカスタマイズが可能であり、プロダクト側の独自な画面要素も簡単に拡張することができる プロダクトを横断して、利用者の 決済設定を共通化 することができる 利用者が一度決済設定(クレジットカード情報など)をすれば、複数のプロダクトを横断して利用することができる 国内事業とGlobal事業 、両方サポートできる  国内事業に向けて自社決済手段のサポート 台湾や香港の越境取引を行っているGlobal事業に向けて現地決済手段のサポートや、General Data Protection Regulation(GDPR)などの現地の法的規則に準拠するシステム設計 ソリューション設計 既存のAPIソリューションに加えて、Low Codeのソリューションを検討する際に考えられる実現方法はいくつかあります。以下の観点からそれぞれ5段階評価して選定しました。 インテグレーションコストの削減効果 これは最も解決したい課題であり、プロダクトに最小限のインテグレーションコストでチェックアウト機能を組み込むことを目指したい ガバナンスの容易さ 複数のプロダクトを横断してUX体験のガバナンスが可能 共通のチェックアウト機能・体験を横断的に最適化しやすい状態を目指したい フレキシビリティの高さ プロダクトごとに独自の画面設計や要素を拡張したいニーズが必ず存在するため、トレードオフが発生することもあるが、拡張性の高い仕組みを提供することを目指したい。 パータン 手法 インテグレーションコスト削減 ガバナンスの容易さ フレキシビリティーの高さ チェックアウト画面のオーナーシップ A 決済APIのみ提供(比較のため) 1 1 5 プロダクトチーム B 共通のチェックアウト画面を提供し、画面要素ごとに一定のカスタマイズ性を提供する 5 5 2 決済基盤チーム C チェックアウト画面を自由に組み立てる共通の仕組みを提供する。画面要素について共通の画面要素(Core Element)とプロダクト特有画面要素(Flex Element)どちらも組み込み可能にする 4~5 4 4 決済基盤チーム D 画面の組み立てはプロダクトに任せる、共通の画面要素(決済手段など)をSDKなどで提供する <4 3 4.5 プロダクトチーム 最終的にソリューションの立ち上げ時の実装方法を「 C 」に決めました。また、タイミングもよく社内で一緒に共同開発できる新規事業のプロジェクトがあり、このプロジェクトはWebサービスであるため、最初は 決済基盤側でself-hostedしたWebのチェックアウトソリューション の開発からスタートしました。将来必要があれば、蓄積された共通の画面要素をパターンDのようにSDKとしてプロダクトへ提供することも可能だと考えています。 プロダクト視点からチェックアウトソリューションを導入するときの処理フローが以下のようになります。初期のセットアップ、インテグレーションの実装が必要になるが、チェックアウトにおける各種共通機能(例: 決済手段、クーポン)を利用するにはチェックアウトの設定だけで対応できるようになります。 プロダクトのチェックアウト要件に応じてチェックアウトの設定情報を作成する 購入処理をトリガーにバックエンド経由でチェックアウトのセッションを発行する チェックアウトセッションに含まれるチェックアウトのトップページの遷移URLへ遷移すれば、決済基盤が提供するチェックアウト画面が表示される それ以降プロダクトの利用者がチェックアウトが提供する各種決済手段を利用して決済処理できる 決済の結果についてCallback経由、もしくは非同期イベントの通知から受け取ることができる 受け取った決済結果に基づいて、プロダクトのバックエンド側で最終的なバリデーションを行い、決済を確定したり、キャンセルしたりすることができる プロダクト特有画面要素のサポート ソリューションとして一見簡単に見えますが、各プロダクトで共通化がまだ難しい画面要素をどのようにサポートすれば良いか、非常に悩ましい課題でした。 この課題を解決するために以下のように共通の画面要素とプロダクト特有の画面要素を分けて、それぞれ決済基盤とプロダクトサイドで開発できるためのフレームワークを開発してます。 複数のプロダクトで利用可能な共通の画面要素(例:決済手段、クーポン)については、「 Core Element 」として基盤チームが担当して開発を行う。 プロダクト固有の画面要素については、「 Flex Element 」としてプロダクト側のエンジニアが独自で開発できる。 プロダクトは、ゼロから自前で実装するのではなく、提供される共通の画面要素(Core Elements)と、独自で開発した画面要素(Flex Elements)を組み合わせて、製品固有のレイアウトを作成する。そうすることで、独自のカスタマイズされたチェックアウト体験を実現できるようになる。 開発の現状と今後の展望 現在、チェックアウトソリューションはメルカリの新規サービス「 メルカリNFT 」のリリースと共にすでに本番で機能提供し始めています。また他の新規サービスのリリース向けにも共同開発を進めており、既存プロダクトのチェックアウト機能のリプレースも現在検討しています。 今後は冒頭でもお伝えしたように、各事業の立ち上げや成長を加速させるための強力な武器となるよう、以下の観点からチェックアウトソリューションをさらに成熟させていきたいと考えています。 決済手段を含む共通画面要素の拡充 即時決済だけでなく、継続払いなど多様なユースケースへの対応 プロダクトを横断した一貫性のあるチェックアウト体験の継続的な改善 チェックアウトソリューションに関連する記事が公開予定なので、あわせてご確認ください。 6/9公開予定「 チェックアウトソリューションのバックエンドアーキテクチャ 」 6/18公開予定「 Checkout frontend design 」 明日の記事は sushoさんの「チェックアウトソリューションのバックエンドアーキテクチャ」です。引き続きお楽しみください。
アバター
この記事は Merpay & Mercoin Tech Openness Month 2025 の 4 日目の記事です。 こんにちは、Merpay の Payment Core チームでエンジニアリングマネージャーをしている komatsu です。 普段は決済基盤を開発するチームのマネージャーをしており、最近では社内で AI/LLM 関連の導入や登壇などもしています。 この記事では、私たちの組織で実施した「PCP LLM Week」という取り組みについてのレポートと、イベントを通して得られた知見についてご紹介します。 PCP LLM Week は、50 人程度のエンジニア組織で一週間にわたって一切の手動コーディングを禁止し、AI/LLM のみを使用した開発を強制的に行うという、かなりチャレンジングな実験でした。 PCP LLM Week とは 2025/05/08 から 2025/05/14 までの 1 週間にわたって、Merpay の Payment & Customer Platform (PCP; Payment Core チームを含む、決済基盤や KYC、パートナー向けの基盤機能の開発をするチームが所属する領域) 内で実施しました。 対象となったのは約 50 名のメンバーで、バックエンドエンジニア、クライアントエンジニア、QA エンジニア、そして各チームのエンジニアリングマネージャーを対象に開催しました。 このイベントの最大の特徴は、期間中は手動でのコーディングを基本的に禁止し、LLM のみを使用して開発を行うという厳格なルールを設けたことです。 なぜ始めたのか: 組織の課題と解決策 この取り組みを始めた背景には、メンバーとの 1on1 や普段のコミュニケーションの中で上がっていた以下の課題がありました。 学習機会の不足 : 多くのエンジニアが AI/LLM に興味を持っていたものの、日々の業務が忙しくまとまった学習時間を確保できない 組織的な情報格差 : 全社的に進めているライセンスやセキュリティの整備状況が全員に伝わっておらず、何を使ってよいのか分からない 高度な活用方法の未習得 : MCP (Model Context Protocol) を活用したドキュメント連携や Slack 連携などの応用的な使い方を試せている人が少ない 情報交換の機会不足 : AI/LLM についてエンジニア間で気軽に情報交換する機会が限られている 参考事例: 強制的な学習環境の効果 今回の PCP LLM Week は、 ある企業の CTO が「エンジニアのコーディングを禁止する」という指令を出した事例 と その結果 を参考にして企画しました。 この事例では、短期的には生産性が約半分に低下したものの、AI の得意・不得意分野が明確になり、組織全体の AI 活用能力向上という長期的価値を確認できたと報告されていました。 私たちも同様のアプローチを取ることで、個人の自発的な学習に頼るのではなく、組織として確実に AI/LLM と向き合う機会を作ることができると考えました。 ツール選択: なぜ Cursor なのか 今回のイベントでは以下の理由から主に Cursor エディタを推奨しました。 学習環境の共有 : 全員が同じツールを使うことで、知見の共有やサポートがしやすい 高度な機能 : MCP ツールとの連携など、より発展的な使い方を学べる 実用性 : (企画時点において) 実際の業務で継続的に使用できるレベルの機能を持っている Kotlin や Swift を主に扱うクライアントエンジニアにとっては制約が大きくなりますが、全員が同じツールを使うことで統一された学習体験を提供し、知見の共有やサポートをより効果的に行うことができると判断しました。 また、当時社内では GitHub Copilot も利用可能でしたが、社内での Cursor への注目や性能を加味して原則 Cursor に寄せました。 設計思想: 目標とルール イベントを有意義なものにして、同じ方向に向かっていくためには適切な目的と目標設定が必要です。 そのため、イベントを企画したタイミングで考え、モチベーションやゴールを記載したドキュメントを作成し、事前に参加メンバーに共有しました。 目的と目標 Motivation AI/LLM がエンジニアの開発スタイルを大きく変革している昨今に、「実際の開発現場でどの程度活用できるのか」を組織全体で実践的に検証する 日常の開発業務を LLM のみで実行することにより、AI との効果的な協働方法や、人間が担うべき領域との適切な境界線を、チーム全体で発見・共有する Goals 以下の 5 つをイベントの目標として設定しました。 LLM の能力と限界を直接体験する : LLM によって何ができて何ができないのかを肌で感じる 開発ワークフローの最適化戦略を構築する : LLM を現在の開発プロセスに効果的に統合し、持続可能な生産性向上を実現するための実践的な活用戦略を学ぶ AI コラボレーションスキルを習得する : プロンプトエンジニアリングや LLM による問題分解のスキルを身につける 開発パラダイムの再考 : 従来のコーディングアプローチをゼロベースで考え直し、新しい問題解決アプローチを探求する AI 拡張時代への適応 : 開発者の役割がどのように変化していくかの洞察を得る LLM は開発プロセスにおける大きなパラダイムシフトですが、愚直に適用すると既存のプロセスに LLM を上乗せするだけになってしまいます。 LLM を体験して既存の業務プロセスに適用するだけでなく、ゼロベースで開発プロセスやエンジニアのあり方を見つめ直すことが近年の AI/LLM 時代に必要だと考え、このような構成にしました。 Non-Goals 一方で、以下は今回のイベントの目標ではないことを明確にしました。 イベント期間内の生産性向上 : あくまで中長期的な生産性向上の一環であり、短期的な生産性低下は気にしない この取り組みを実現するために、Director、VPoE レベルでの組織的な意思決定を行い、短期的な生産性低下を許容して学習投資として位置づけることで、エンジニアが安心して実験できる環境を整備しました。 ルール設計 厳格なルールを設けることで、全員が LLM と向き合う環境を作りました。 基本原則 手動コーディングの完全禁止 : 一行のコード修正であっても、必ず LLM を通して行う 小さな編集も例外なし : 変数名の変更、コメントの追加など、些細な変更も LLM で実施する 普段の業務での実践 : 特別なタスクを用意するのではなく、日常業務を LLM で行う 許可される例外 緊急対応 : インシデント対応やシステム障害への対処 締め切り間近のタスク : リリース直前など、時間的制約が厳しい場合 環境設定 : LLM ツール自体のセットアップや設定変更 ドキュメント作成 LLM による作成を推奨 : 技術仕様書、設計書、README 等は LLM で作成することを推奨 手動修正は許可 : LLM が生成した内容の事実確認や微調整は人間が行ってもよい なぜ日常業務で実践するのか 今回のイベントでは、特別なタスクやサンプルプロジェクトを用意するのではなく、これらの理由から、あえて普段の業務に LLM を適用することにしました。 現実的な活用可能性の検証 実際の業務環境で LLM がどの程度役立つのかを正確に把握するため 理想的な条件ではなく、制約のある現実の中での効果を測定するため 既存のコードベースや技術スタックとの相性を確認するため 真の課題と限界の発見 サンプルプロジェクトでは見えない、実業務特有の困難さを体験するため レガシーコードや複雑な依存関係がある環境での制約を理解するため ドメイン知識が必要な場面での LLM の限界を実感するため 継続可能性の評価 イベント終了後も継続して使えるかどうかの判断材料を得るため 日常的なワークフローに LLM を組み込む際の現実的な課題を把握するため チーム開発や既存プロセスとの統合における問題点を発見するため 組織全体での実用性確認 個人の実験レベルではなく、チーム・組織レベルでの実用性を検証するため 異なる役割 (バックエンド、フロントエンド、QA、マネージャー) での効果の違いを確認するため 実際のプロダクト開発における生産性への影響を測定するため これらの方針によって、より実用的で価値のある知見を得ることを狙いとしました。 実施スケジュール イベントは参考にした事例と同じく 1 週間の構成で実施しました。 初日 (5/8) にイベント紹介、他部署の AI 活用事例紹介、Cursor ハンズオン、設定・開発時間を行い、その後 1 週間 (5/8-5/14) 各自で普段の開発業務に AI/LLM を活用し、最終日 (5/14) に成果発表会を開催しました。 成功例と課題: 実践から見えた現実 イベント後にアンケート調査を行ったりメンバーとの 1on1 を通してさまざまなフィードバックを得たりしました。 全体的な満足度と効果 まず、メンバーのイベントに対する満足度について、多くの (92%) メンバーがイベントを効果的に活用し、その機会に満足していました。 特に初日に細かい設定について話し、まとまった準備時間を取ることで社内で開発されている MCP ツールの導入など、発展的な設定までできたことが良い体験だったという声もありました。 また、アンケート結果から、参加者のスキルレベルによって異なる効果が見られました。 このイベントによって、多数の「初心者」だったメンバーが「中級者」へとレベルアップしました。 LLM ツールを初めて使う人にとって、強制的に使用する環境が効果的な学習機会となったようです。 基本的な使い方から応用的な活用方法まで、短期間で幅広く体験できたことが、さまざまな知見の習得につながったと思われます。 一方で、既に「上級者」レベルの参加者については、さらなるレベルアップは限定的でした。 これらのメンバーには、より高度な学習機会や異なる学習スタイルが必要であることが判明しました。 ただし、上級者には他のメンバーへの指導やベストプラクティス共有という重要な役割があり、組織全体の底上げに大きく貢献していました。 アンケート結果では、ほぼすべて (96%) のメンバー が「今後も使い続けたい」と回答しており、スキルレベルに関わらず継続的な活用への意欲が高いことが確認できました。 この記事の執筆時点の統計を見ると、使用頻度の差はありますが、PCP における Cursor の使用率は非常に高い水準になっていました。 このイベントが多くのメンバーがツールを習得し、スキルレベルの底上げに寄与したことが一つの要因だと思うので、主催者としてはとても満足しています。 期間については、ほとんどの参加者が「1 週間が適切」と回答しており、学習効果と業務への影響のバランスが良好であることが確認できました。 短すぎず長すぎない、集中して取り組める期間として評価されています。 実際、普段のアサインもある中でこれ以上長いと多少支障が出てきますし、短すぎても消化不良になる可能性もあるので、適切な期間設定だったと感じます。 実際の参加者の声もいくつか紹介します。 全く触ったことがない状態だったが、Copilot のときと同様に無くてはならないものになった。簡単な仕事なら AI で完結できる感覚がある。 どのように指示するかでアウトプットのクオリティは変わるものの、もう開発に使用できるレベルまで LLM の信頼性があったことに驚いた。 社内のドキュメントや repository をワークスペースに追加することで、社内の技術基盤や事情に沿ったコーディングを LLM がしてくれた体験が良かった。 元々かなり有用だという噂を聞いていた程度だったが、実際に使ってみてその効果を実感できたため、どのように活用できるかをタスクごとに考えるようになってきた。 It seems to allow us to work more efficiently by being able to review many lines of codes and files to find and summarize information. It also allows us to peek at where it found such information to confirm the accuracy of its results as well. It’s also able to help refactor and find unused code as well. 曖昧な指示だとずれた変更が行われるのでコンテキストを明確にする必要があり、自分がやろうとしていることをコンテキストなしの状態から言語化するところに慣れが必要だと感じた。また、一見自然言語でも通じるように見えるので素朴に質問してしまいがちだが、特に MCP server などでは内部でどのような問い合わせが行われるのかを理解した上での利用が必要といった難しさがあると感じた。 コード生成にはまだ一定の限界がある。一発ではできない。k8s や tf のレポジトリはファイルが多すぎるため LLM にとってはノイズになることもある。 多くのメンバーの LLM に対する信頼度が向上し、その活用方法についても考える良い機会になったことが伺えます。 また、同時に現状の LLM の性能の限界を理解する良い機会にもなり、どのようなタスクに適用していくかの洞察を得ることができました。 成功例: LLM の可能性を実感した瞬間 開発効率の大幅向上 テスト作成、API 修正などの定型的なタスクで劇的な時間短縮 既存コードの解析や大規模リファクタリングでの威力 ボイラープレートコード生成による生産性向上 高度なコーディング支援 Cursor や GitHub Copilot による的確な修正提案 複雑なコード生成による協働的な開発体験 一発で修正箇所を発見できる精度の高さ ワークスペース統合とドキュメント活用 社内ドキュメントやリポジトリとの連携による組織固有の技術基盤に沿ったコード生成 既存データソースを基にした技術仕様書の自動生成 MCP を活用した Confluence の仕様から Jira チケットの自動生成 プロンプトエンジニアリングの重要性の理解 明確で詳細な指示の重要性 (人間への指示と同様) プロンプトの品質が出力品質に直結することの実感 当初の狙い通り、多くのメンバーが直近の LLM の性能について理解し、活用できる開発プロセスを発見していました。 また、MCP や v0.50 でちょうど追加されたワークスペース機能なども駆使し、複数マイクロサービスに跨った開発なども可能となり、高度な活用をしているメンバーも多々いました。 課題: 直面した限界と困難 また、イベントを通して現状の AI/LLM の課題も見えてきました。 精度と品質の問題 不正確な出力や意味不明な結果の生成 複雑で非標準的な概念への対応困難 コード品質の一貫性の欠如 効率性とワークフローの課題 AI との反復的なやり取りによる時間コスト 特定タスクでは人間の方が依然として高速 既存ワークフローとの統合やツール切り替えの煩雑さ AI 生成コンテンツの検証時間が手動作業と同等かそれ以上 プロンプトとコンテキストの難しさ 曖昧な指示に対する AI の対応困難 大規模で複雑なコンテキストの処理限界 自分の要求を詳細に言語化することの困難さ 知識ギャップと限界 ドメイン固有知識やインターネット上にない情報への対応困難 暗黙知やコードで明示されていない側面の理解不足 技術領域による制約 iOS/Swift 開発など、特定の技術スタックでの効果的な活用の困難さ 当然ではありますが、コードや仕様には載っていない各メンバーが持っているドメイン知識を適切に伝えるには一定の負荷がかかり、そのことが生成されるコードの限界になることがわかりました。 これを機に私のチームではドキュメンテーションを自動化したり不足しているコードコメントを追加したりするといった次のアクションにつながっており、良い学びができたと感じています。 また、強制的な機会だったからこそ、AI でできることの限界を知ることができました。 開発に対する考え方の変化 イベントを通じて、メンバーの開発に対する考え方に以下のような変化が見られました。 実用性への確信の高まり 「思っていたより実用レベルに達している」という認識の変化 従来の開発手法を置き換える可能性への確信 コード編集などの特定領域での代替可能性の実感 戦略的活用の理解 全てのタスクの代替ではなく、適材適所での活用の重要性 設計ドキュメントやコードレビューなど特定用途での高い効果 人間の専門知識を最終段階に残すプロセスの有効性 継続学習の必要性 効果的な使用方法を見つけるための実験の重要性 他者の経験から学ぶことの価値 ベストプラクティスの継続的な学習の必要性 組織的なシナジー効果 全員が LLM を使うことで生まれる相乗効果への気づき チーム間での活用度合いの差とベストプラクティス共有の重要性 実際に全員で使うことで、どのように LLM を活用していくのか、どのようにエンジニアの役割が変わっていくのかといった当初の目標を考える有意義な機会になりました。 また、継続的に知見の共有や学習を続けていく必要性も再度実感できたと思います。 マネージャー視点: 組織運営への影響 EM の観点でも多くの学びがありました。 組織運営に関する学び まず、 強制力の重要性 を痛感しました。 普段から AI/LLM という声は多く聞いていましたが、実際には日々の業務に追われて学習時間を確保できないメンバーがほとんどでした。 今回のように組織全体で取り組む期間を明確に設けることで、全員で LLM に真剣に向き合う機会を作ることができました。 PCP のメンバーは自走力の高いエンジニアが多いですが、そのような環境だとしても自発的な学習だけに頼らず、組織全体としてのスキルアップの機会を提供することの重要性を再確認しました。 次に、 情報共有の活性化 が想定以上の効果を生みました。 イベント用に作成した専用 Slack チャンネルは、当初は質問や困りごとを共有する場として考えていましたが、実際には知見の共有やちょっとした発見の報告など、非常に活発なコミュニケーションの場となりました。 イベント終了後も継続的な学習コミュニティとして機能しており、組織の学習文化醸成に大きく貢献しています。 そして、 スキルレベルの標準化 という予想外の効果も得られました。 これまでは AI/LLM の活用レベルに個人差が大きく、チーム間での知見共有も限定的でした。 しかし、全員が同じ体験をすることで、組織全体の AI リテラシーが底上げされ、共通言語で議論できるようになったのは大きな収穫でした。 生産性への現実的な影響 短期的には実際に生産性が多少低下しましたが、これは組織として予想し、受け入れていた結果でした。 また、個人的には予想していたほどの生産性の低下は見られず、直近の LLM の性能向上による恩恵が大きいと感じています。 同時に生産性観点でも長期的な価値を確認できました。 適材適所の理解 : どのタスクに AI が向いているかの判断力向上 開発スタイルの変化 : プロンプトエンジニアリングを通じた問題分解能力の向上 AI ファーストな思考習慣 : 課題に直面した際に AI による解決を当然の選択肢として考える習慣の定着 個別最適化されたワークフロー : 各メンバーが自身の開発スタイルに最適化された AI ツールの組み合わせと活用方法を確立 特に AI による解決策を常に考える習慣ができたことは、ゼロベースでアプローチを考え直す上で非常に価値ある体験だったと感じています。 また、この結果は、組織のリーダーシップが「短期的なコストを払ってでも、長期的な AI 活用能力を獲得する」という明確な意思決定を行ったからこそ実現できたものであり、組織として成長していくことへの重要性を再度確認しました。 マネージャーアンケートから見えた現実 マネージャー向けのアンケートでは、AI 導入による生産性向上とリソース計画への影響について以下のような現実的な見解が得られました。 生産性向上への見通し 生産性向上については、短期的な劇的な変化ではなく、中長期にわたって徐々に向上していく見込みであることが分かりました。 現在はツールの乱立や性能差の変化により、短期では一長一短の状況が続いており、これから模索や選定を継続的に行っていくことが重要です。 また、AI の効果は作業の種類と開発者の LLM 経験に大きく依存することも明らかになりました。 適切なタスク選択と生産性維持のためのトレーニングが重要であり、特に問い合わせ対応や運用系など、直接的な生産性に結びつかないタスクでの効率化に期待が寄せられています。 リソース計画への影響 現時点では従来のリソース計画手法を大幅に変更するレベルの変化は見られませんでした。 また、短期で AI によってリソースに大幅な余裕が出るには依然として壁がありますが、組織横断の開発スタイルは導入しやすくなったという手応えを感じています。 段階的な環境整備が現実的なアプローチであることが確認できました。 マネージャーとしての学び 組織的な取り組みの価値として、IC からの結果を聞いて想定よりも課題が多いことを実感しました。 一方で、全員が使うことで生まれるシナジー効果を確認でき、組織として LLM にどう寄り添っていくかを考える機会の重要性を認識しました。 現実的な期待値設定については、短期的な劇的な生産性向上への過度な期待は禁物であることが分かりました。 中長期的な投資として捉え、継続的な学習と改善が必要であり、ツールの組み合わせや切り替えの最適化が今後の課題となります。 その後の展開: 継続的な取り組み イベント最後には以下の項目でメンバーを表彰し、効果的な活用方法を共有しました。 LLM Code Generation Champion : 最も多くのコードを生成した人 LLM Refactoring Champion : 最も多くのコードを削除 (リファクタリング) した人 Precision Prompter : 最も高い Accept Rate を達成した人 同僚がどのように活用できているかを間近でシェアすることで、全員がより自分ごととして AI スキルセットに対する理解を深めたり、期ごとの個人目標に追加したりと、全体的な AI に対する視座向上ができたと思います。 継続的な取り組み 情報共有チャンネルの継続 : イベント用 Slack チャンネルを汎用的な名前に変更し、継続的な情報交換の場として活用 ベストプラクティスの共有 : 効果的な活用方法を組織内で継続的に共有 新機能のキャッチアップ : AI/LLM ツールの新機能を組織全体で迅速に取り入れる体制構築 まとめ PCP LLM Week は、短期的な生産性低下というコストを払いながらも、全員で体験することで組織全体の AI 活用能力を大幅に向上させる貴重な機会となりました。 特に重要だったのは、「全員で同じ体験をする」ことで生まれた学習効果と、その後の継続的な情報共有文化の醸成です。 AI/LLM の活用は個人のスキルに依存する部分が大きいですが、組織として取り組むことで、より大きな価値を生み出せることを実感しました。 特に昨今のモデルやエコシステムの進化は個人でキャッチアップしていくにはあまりに膨大なため、組織として方向性を示し、スキルアップの機会を提供することでモチベーションを獲得し、各メンバーの自走力に繋げる良いサイクルが生まれると感じました。 今後も組織として AI-Native になるための機会や仕組みを継続的に作っていきたいです。 明日は同じく PCP の foghost さんの記事です。 引き続き Merpay & Mercoin Tech Openness Month 2025 をお楽しみください。
アバター
こんにちは。メルコイン バックエンドソフトウェアエンジニアの @toshinao です。 この記事は、 Merpay & Mercoin Tech Openness Month 2025 の3日目の記事です。 これまでの採用フロー メルコインはメルカリグループですが、メルペイやメルカリとは別に採用を行っています。これまでメルコインのソフトウェアエンジニア採用(バックエンド)は、「技術課題 → 1次面接 → 2次面接 → 最終面接」という流れでした。技術課題はGoまたはJavaで出題され、応募者は1週間以内に提出します。1次面接はエンジニア、2次面接はマネージャー、最終面接は役員が担当します。 技術課題の問題点 技術課題は、応募者の経験している技術言語をもとにご自身で選択していただいています。従来の技術課題は、GoとJavaで内容が異なっていました。Goでは課題プログラムの修正や機能追加を行う形式で、応募者のプログラミングスキルやコードの理解力、バグ修正・実装力などを評価していました。一方、Javaではアプリケーションを1から開発する課題が出され、設計・実装・テストまで一連の開発プロセスを通じて、総合的な開発力や設計力を評価していました。 メルコインのBackend開発はGoで書かれており、Java経験者はポテンシャルによる採用となるため、Javaの課題はアプリケーション全体の設計能力が強く要求され、応募者にとってハードルが高いという課題がありました。 また、技術課題をGoだけにすると応募者が減ってしまう懸念がありました。 また、応募者に大きな時間的負担がかかっていました。応募者は想定回答時間が5時間\~10時間の課題を1週間以内に提出する必要がありました。実際に課題を受けて入社した社員に聞いたところ10時間以上かかった社員が多くいました。課題に加えて、1次面接も1時間程度必要で、全体として多くの時間をいただいていました。 導入の経緯 こうした課題を解決するため、技術課題と1次面接をSystem Design Interview(以下、SDI)を導入しました。すべての採用フローがSDIになったわけではなく、主にGo未経験者を対象に導入しています。SDIの導入にあたっては、他社の事例を参照したり、社内メンバーに何回も試し、ブラッシュアップを繰り返しました。 開発経験、とくにシステム設計ができる方であれば、Go未経験でもメルコインで活躍できると考えています。実際、Javaの課題で合格した方もメルコインで活躍しています。Goのスキルも必要ですが、金融システムとしてスケールや耐障害性を考慮した設計力も重要です。設計力があれば、既存コードを参考にしつつ、メンバーのサポートを受けてGoでの開発も可能だと考えています。 System Design Interviewとは System Design Interviewは、ソフトウェアエンジニアの採用面接でシステム設計能力を評価する手法です。GoogleやAmazon、Microsoftなどの大手テック企業で広く採用されており、バックエンドエンジニアやシステムアーキテクトの重要な評価基準となっています。 SDIの進め方 SDIでは、面接官がシステムの要件を提示し、応募者は要件を確認しながら対話形式でアーキテクチャを設計していきます。例えば「決済サービスの設計」や「大規模なログ収集基盤の設計」など、実際の業務に近い課題が出されます。応募者は、要件のヒアリングから始め、システムの全体像をホワイトボードやオンラインツールで図示しながら説明します。 一般的なSDIでは、YouTubeやX(旧Twitter)のような大規模サービスや、検索機能・レコメンデーションシステムなどの設計課題が出されます。応募者は、要件定義・スコープの明確化、データモデル設計、コンポーネント分割、インターフェース設計、スケーラビリティ・パフォーマンス・可用性・耐障害性・セキュリティ・コスト・運用性など、さまざまな観点から設計を進めます。 メルコインのSDIでは、より実務に近いバックエンドシステム設計の課題を出題しています。Product Managerから作りたいシステムの要件を教えてもらい、システム設計をしていく形になっています。 評価基準 SDIで重視しているのは、与えられた要件の理解、適切な技術選定、スケーラビリティ・セキュリティ・パフォーマンスなどを考慮した設計力です。この過程で、技術的知識だけでなく、問題解決力やコミュニケーション能力など、実務で必要なスキルも評価できます。特に、設計の根拠を論理的に説明できるか、トレードオフを意識した提案ができるか、チームでの議論を想定したコミュニケーションができるかを重視しています。 SDIの面接は90分で、最初の70分がSDI、次の10分が過去の開発経験の質問、最後の10分が応募者からの質問時間です。面接官は、応募者が本質的な課題に集中できるよう、適宜ヒントを出したり、議論の方向性を調整したりしています。 SDIの効果 SDI導入により、当初想定していた以上の効果が得られました。 まず、技術課題でいただいていた応募者の時間が削減されたことで、選考にかかる期間を短くすることができました。さらに、評価の質も向上し、システム設計能力だけでなく、コミュニケーション力や問題解決アプローチも評価できるようになりました。これにより、実際の業務での活躍イメージをより正確に把握できるようになっています。 また、特定のプログラミング言語経験に依存しない評価方法となったことで、多様なバックグラウンドを持つ人材の発掘にもつながっています。これにより、技術チームに多様な視点や経験を持つエンジニアを迎え入れることができています。 さらに、SDIを通じて応募者の「考え方」や「価値観」も把握しやすくなりました。例えば、障害発生時の対応方針や、セキュリティリスクへの意識、コストとパフォーマンスのバランス感覚など、実際の業務で重要となる観点を深掘りできるようになりました。 SDIの課題 一方で、SDIにも課題があります。 元々の問題が70分で最後まで回答するのが難しいため、設計の本題と関係ない部分に時間を取られると、ほとんど進まないまま終わってしまうことがあります。そのため、面接官が軌道修正する必要があり、話を遮る場面も増えます。応募者に不快な思いをさせないよう配慮が必要です。 また、応募者からの質問にどこまで答えてよいかの判断も難しいです。正解をそのまま伝えてしまうことを避けるため、曖昧な回答になりがちです。特に、設計の根幹に関わる部分はどこまで答えるか非常に難しいです。 これらの課題を解消するため、面接ごとに曖昧さを減らし、「この試験で見ないこと」や「SDIの進め方の補足」などを追加し、本題から逸れないよう問題をブラッシュアップし続けています。面接官同士での振り返りや、応募者からのフィードバックも積極的に取り入れています。 今後の展望 今後は、SDIの課題バリエーションを増やすなど、SDIの質を上げていくことや、面接官のトレーニングや評価基準のさらなる明確化にも力を入れ、より公平で納得感のある選考プロセスを目指します。 また、SDIの内容や運用ノウハウを社内外に発信し、他社やコミュニティとの情報交換も積極的に行っていきたいと考えています。 まとめ SDIの導入により、メルコインの採用プロセスは大きく改善されました。時間的な効率化だけでなく、より実践的な評価が可能となり、多様な人材の発見にもつながっています。一方で、面接の進め方や質問対応など課題もありますが、継続的な改善を通じて解消を図っています。 今後も、SDIを通じて実践的なシステム設計能力を持つエンジニアを発見・採用し、より強固なシステム開発チームの構築を目指していきます。 メルコインにご興味ある方は、下記よりご応募ください。 Product Engineer,Backend – Mercoin 明日の記事はkomatsuさんです。引き続きお楽しみください。
アバター
こんにちは!メルペイ Growth Platform Frontend チームのインターン生の @uta です。 この記事は、 Merpay & Mercoin Tech Openness Month 2025 の2日目の記事です。 はじめに 私は3月から5月末までの3ヶ月間、フロントエンドエンジニアとしてメルペイのインターンに参加しました。今回は、インターン期間中に取り組んだタスクについて振り返り、そこで得た学びや気づきについて以下の内容をまとめたいと思います。 取り組んだタスク チームについて Engagement Platformカレンダーの開発 クーポン検索機能の開発 インターンで得た学びと気付き メルカリカルチャーの体験 初めての実務から学んだエンジニア像 この記事が、メルペイのインターンに挑戦しようと考えている方や、興味を持っている方の参考になれば幸いです! 取り組んだタスク チームについて 私が配属された Growth Platform Frontend チームは、Engagement Platform(通称EGP)という社内向けマーケティングツールを開発しています。このツールを使うと、マーケターや PM(プロジェクトマネージャー)がポイントやクーポンなどのインセンティブ配布、LP(ランディングページ)の作成・公開、キャンペーン作成といった CRM業務をコーディング不要で簡単に行えます。 EGPについての詳細は下記ブログもあわせてご確認ください。 WYSIWYGウェブページビルダーを支える技術とSever Driven UIへの拡張 Enhancing Collaboration and Reliability: The Journey of Version History in our Page Editor Tool 【書き起こし】WYSIWYGウェブページビルダーを支える技術的マジックの裏側 – Hal Amano / Arvin Huang / Ben Hsieh / Jas Chen【Merpay & Mercoin Tech Fest 2023】 Engagement Platformカレンダーの開発 インターン期間中、最も注力したタスクがEGPカレンダー画面の開発です。 問題点 EGPではキャンペーンの作成および管理を行うことができます。これまでは、作成されたキャンペーンの確認のために検索機能を備えたテーブルが提供されていました(図1.1)。 図1.1 キャンペーンリストテーブル しかし、この表示方法ではキャンペーンのスケジュールを一元的に把握することができません。キャンペーンに伴う通知の重複や、システムのキャパシティを超える可能性が可視化されていないという問題がありました。 改善策 そこで、私はキャンペーンのスケジュールを可視化するカレンダーの開発に取り組みました(図1.2)。このプロジェクトはPMの@ChloeさんがPRD(プロダクト要求仕様書)に起こしたもので、@Chloeさんやチームメンバーにサポートをいただきつつ、要件の確認・Figmaによるデザインの作成から実装・リリースまで取り組みました。 図1.2 キャンペーンカレンダー 苦労した点 このプロジェクトの中で最も苦労した部分が、キャンペーンの仕様理解とUIの考案です。 キャンペーンには作成画面からもわかるように、さまざまな設定事項があります(図1.3)。これらの変数がどのような操作を決定しているのか、また、それをどのようにカレンダー上のUIに落とすかという部分に悩みました。 図1.3 キャンペーン作成画面 キャンペーンの仕様理解 キャンペーンには大きく分けて、real-timeとbatchの2種類が存在し、それぞれで配布条件やタイミングが異なります(図1.4)。 図1.4 キャンペーンの種類 また、キャンペーンの配布タイミングを決定づける重要な変数が2種類存在します。1つ目は配布期間を定めるcampaign schedule、2つ目は配布対象を評価するクエリに関する条件を定めるsegmentationsです。さらに、キャンペーンの種類によって、これらの変数が実際の配布スケジュールにどのように関与するかも、それぞれ異なります(図1.5)。 図1.5 キャンペーンの配布タイミング例 カレンダーUIの考案 このような仕様の違いを、次のようにUIに落とし込みました(図1.6)。real-timeキャンペーンは期間中に配布資格を満たしたタイミングで配布されるキャンペーンであるため、キャンペーン期間を1日単位で可視化しました。一方、batchキャンペーンは単発もしくは定期的に配布されるキャンペーンです。そのため、キャンペーン期間はラベルに記載するのみに留め、カレンダーでは時間単位で実際に配布されるタイミングを可視化しました。 図1.6 キャンペーン種別のカレンダーへの表示方法 学んだこと このcampaign scheduleとsegmentationsの複雑さは、campaign scheduleが過去のインシデントを受けて後から追加された機能であることに由来していると伺いました。これまで私は、このような歴史的経緯を持つプロジェクトに取り組んだ経験がなかったため、大規模なプロジェクトにおけるコードや仕様の理解の難しさを実感しました。また、このような状況で自ら質問することの重要性を学びました。キャンペーンの理解からデザイン考案、実装に至るまで、チームメンバーをはじめ、PMの方や他のチームの方々から大変貴重なサポートをいただきました。 クーポン検索機能の開発 インターン期間中、最も技術的に挑戦したタスクがクーポン検索機能の開発です。 問題点 EGPではクーポンの作成および管理を行うことができます。これまでは、作成されたクーポンを確認するためのテーブルが提供されていましたが、検索機能は存在していませんでした(図2.1)。また、キャンペーンのリワードとしてクーポンを選択する際にも、検索機能がないことで効率が悪く、この機能は多くの場面で長らく待望されていました。 図2.1 クーポンリストテーブル データフロー これまでのクーポンテーブルでは、他のチームが開発したAPIからデータを取得していました。検索機能の実装において、まずそのAPIを利用することを考えます。しかし、クーポンは歴史的に古いページであり、APIにもフィルターや検索機能が実装されていないという問題がありました。 そこで、既存のデータベースに加えてSpannerを用いる新しいデータフローを採用しました(図2.2)。新たにクーポンのデータを既存のデータベースとSpannerの両方に保存し、SpannerとGraphQLを活用して検索機能を実装しました。これにより、より効率的で拡張性の高いデータ取得が可能となりました。 図2.2 クーポン検索機能のデータフロー データの移行やリリースはインターン期間内に間に合いませんでしたが、テキストによるクーポン名の検索機能や、リターンタイプに基づく絞り込み機能を実装することができました(図2.3)。 図2.3 クーポン検索画面 学んだこと SpannerやGraphQLといったバックエンド領域の技術に挑戦する機会を得られたことは、大きな学びとなりました。私はこれまで主にフロントエンドの技術を扱ってきましたが、バックエンド領域にも触れることで、自分の視野を広げることができました。また、チーム全体を見渡しても、フロントエンドチームでありながら、必要に応じてバックエンド領域のタスクにも積極的に取り組んでおり、その姿勢にプロフェッショナル性を強く感じました。こうした環境でインターンができたことは非常に刺激的で、自分自身の成長につながったと思います。 インターンでの学びと気付き メルカリカルチャーの体験 このインターン期間中には、メルカリカルチャーを感じられる機会がたくさんありました。 まず驚いたことは、 大量の情報にアクセスできる環境です。ほぼ全てのSlackチャンネルやドキュメントへのアクセスが許可されており、それらの情報を自由に閲覧できることに驚きました。こうしたアクセスの範囲は、社員とほとんど同じであり、インターン生であっても「会社の一員」として扱われているように感じました。 また、印象的だったのがAll Hands です。これは各部署が定期的に開催するミーティングで、インターン生も自由に参加できるものでした。この場では、チーム外の取り組みや会社全体の目標について詳しく知ることができ、普段接する機会の少ない他部署の活動にも触れることができました。ミーティング中にはSlackの random チャンネルが活発に使われており、メンバーが所属部署や役職に関係なく意見を交換し合ったり、気軽にリアクションを飛ばし合う姿が非常に印象的でした。 これらの体験を通じて、メルカリが複数の事業領域を抱える大規模な組織であるにもかかわらず、一体感のあるワンチームとして進んでいる文化を強く感じました。また、インターン生であっても社員と同じ情報にアクセスし、実際にメルカリで働くイメージをリアルに描くことができました。企業の現実的な働き方や意思決定のプロセスに触れることができ、非常に貴重な経験でした。 初めての実務から学んだエンジニア像 今回のインターンは、私にとって初めての実務経験でしたが、エンジニアとしての価値を考える大きなきっかけとなりました。これまでは、エンジニアとしてキャリアを築くためには、機能を実装するコーディングスキルを高めることが重要だと考えていました。しかし、実務を通じて、技術力だけではなくチーム全体への貢献が不可欠であることを学びました。 特に印象に残っているのは、EM(エンジニアリングマネージャー)の@ben.hsiehさんとの関わりです。@ben.hsiehさんは定期的に1on1を実施し、私の状況や目指したい方向性について丁寧に聞いてくださいました。そして、それらを踏まえた上で適切なタスクを割り振っていただきました。例えば、EGP内で使用されるLP作成ツールの使い方を知りたいと相談した際、そのツールを活用した実際のタスクを割り当てていただき、実践的な学びを得る機会となりました。 こうした環境の中で私は、ただコードを書く力を高めるだけでなく、チームの一員としてプロダクトや目標に貢献できるエンジニアでありたいと考えるようになりました。エンジニアとして目指すべき在り方や方向性をより明確にすることができたと感じています。 EM の@ben.hsiehさんが記事を公開していますので、あわせてご確認ください。 「 Rethink Tool’s UI/UX – Human-Centric to AI-Driven 」 おわりに 本記事では、メルペイのインターンで取り組んだタスクや、そこから得た学びと気づきについてお話しました。技術的なスキルを磨くだけでなく、メルカリグループの文化に触れ、エンジニアとしての価値を考えるきっかけを得ることができた、とても貴重な3ヶ月間でした。 このような充実した経験が得られたのも、メンターの@togamiさんをはじめ、チームメンバーや関わってくださった全ての方々の手厚いサポートのおかげです。この場をお借りして改めて感謝を申し上げます。ありがとうございました! 現在、メルカリではインターンを募集しています。このブログを読んで「自分も挑戦してみたい!」と思った方は、ぜひ一歩を踏み出してみてください。きっと素晴らしい経験が待っていると思います! Students | 採用情報 明日の記事は メルコイン Opsチーム @toshinaoさんです。引き続きお楽しみください。
アバター
はじめに こんにちは。メルペイVPoEの @keigow です。 この記事は、 Merpay & Mercoin Tech Openness Month 2025 の初日の記事です。 これまでもメルペイ及びメルカリグループでは、社内向けChatGPTとも言える Ellie の取り組みや、LLMをプロダクトや業務効率化に活かすためのハッカソンイベント ぐげん会議 の開催、プロダクトへのLLM利用も含め、AI/LLMの活用を推進してきました。 AI/LLMの進化のスピードは想像以上に早く、毎日のように新しいアップデートがあります。最近ではこれまで以上にAI Nativeな組織、プロダクトに生まれ変わっていくべくさまざまな取り組みを進めており、その一部をご紹介できればと思います。 AI Coding Assistant Toolの活用 元々2023年の6月に GitHub Copilot の利用を開始し、社内での利用も徐々に増えていましたが、 Cursor の利用開始に伴い、社内での活用が大きく進みました。現在CopilotとCursorだけでも、Engineering組織全体の約8割のメンバーが利用しています。またCoding Agentとして Devin の利用も広がっています。 Engineering組織としても今QのOKRの中で一番重要なKR1として、全てのエンジニアが100%何らかのAI Coding Assistant Toolを活用し、生産性を高めることを目標に設定しました。生産性を計測する仕組みとしてこれまで社内では独自で Four Keys などを取得していましたが、新たに DX を全社で導入しました。こちらの詳細については17日目のntkさんの記事で紹介予定です。 目標の設定と全社員向けにツールを提供する予算の確保、世の中の盛り上がりも相まって急速に導入が進み、各チーム単位でAI活用のオフサイトを実施したり、一週間Vibe Codingのみの期間を設定するなど社内でも盛り上がりを見せています。それぞれのイベントについての取り組みの様子も今後の記事で紹介予定なので、ぜひ御覧ください。 MCPサーバによる社内ツール連携 Model Context Protocol (MCP)を活用した社内ツールの連携も急速に進んでいます。MCPサーバについてはセキュリティの観点から安全性が確認されているものを活用するため、社内でMCPサーバの実装をまとめたRepositoryが作られています。JIRA、Confuluence、Slack、Google Driveを始めとした3rd Party製のツールや、内製のMicroservicesの管理ツール、Google SpannerやBigQueryなどのMCPサーバが作られ、Cursorなどを利用して主にLocal環境で活用されています。これらのツールの利用者はエンジニアに限らないため、さまざまな職種のメンバーが自身の業務に合わせてツールの活用方法を検討するようになりました。 並行して各ツール自体にもEmbedされたAI Chat機能が導入されてくるようになっているため、今後もベストプラクティスの検討をしていきます。MCPサーバの活用については7日目のseitauさんの記事でも触れられる予定です。 Merpay AI Labsの取り組み 業務フローの改善におけるAI/LLMの活用を推進するため、メルペイにはAI Labsという専門のチームがあります。 日毎にアップデートされるモデルやツールの進化に合わせて、各チームでのAI活用ニーズが増すなか、AI活用による業務フローの改善を進めたいが、どのように進めればよいのかわからないといった声を聞く機会が増えてきました。 実際に部署内で行われている業務のフローは複雑かつ、ステークホルダーも多く、さまざまな課題があったとしても、それが業務フローを改善すべき問題なのか、単にシステム化をすればいいだけなのか、あるいはAIを活用することで大きなアウトカムを埋める領域なのかの判断自体も難しいという問題がありました。 Engineeringの部署としてもそれをサポートし、AI活用を推進することが組織としてのインパクトが大きいと考え、今年の1月に改めてチームのMissionやVisionを設定しました。以下は抜粋になりますが、会社全体のAI推進を行うことをMissionとして定義しています。 Mission AI LabsはFintechのAI活用と開発をEnable/Driveします。AI Labsはそのための、実践者であり、伝道者であり、触媒になります。 FintechにおけるAI Nativeなアプリケーション開発/業務設計のCenter of Excellence(中核となる組織)としての能力を確立し、AI Nativeな事業推進に貢献します。 このチームで取り組んだプロジェクト例としては以下のようなものがあります。成果が出たもの出なかったものなど含め、結果はさまざまですが、着実に知見を貯めることができていると思います。 コンセプトからのアプリのデザインの自動生成 仕様からのQAのテストケースの自動生成 画像生成AIを用いたキャンペーンのキービジュアル生成 お客さま問い合わせの要因分析 専門チームの問い合わせ工数削減のための対応効率化Botの作成 画像生成の例 特にアプリのデザインの自動生成については、現在進行系で面白い取り組みになってきています。こちらは18日目のhiroさんの記事で詳細をご紹介予定です。 おわりに これまで取り組みの一部をご紹介してきましたが、世の中の盛り上がりと勢い同様に、社内でも各チーム、各メンバーが次々と新しくAI活用に取り組み、さまざまなツールが作られるという状況が続いており、その全てを把握することも困難な状態になっています。キャッチアップだけでも大変な時代になってきましたが、この熱狂の中で仕事に取り組めていることは幸せな状況だなと感じており、この目まぐるしい変化を楽しんでいければと思います。 明日の記事はbenさんによる「Rethink Tool’s UI/UX – Human-Centric to AI-Driven」とutaさんによる「メルペイインターン体験記:実務の中での学びと気付き」の2本です。引き続きお楽しみください。
アバター
こんにちは。メルペイ Engineering Engagement チームの @mikichin です。 メルカリグループは「あらゆる価値を循環させ、あらゆる人の可能性を広げる」をミッションに、さまざまなサービスを展開しています。 メルペイは単なる決済サービスではなく、新しい「信用」を基盤として、それに基づく循環型社会、なめらかな社会を創ることを、メルコインはテクノロジーによって、さまざまな価値観の境界線を打ち破り、誰もが暗号資産・デジタル資産などあらゆる価値を簡単に交換できる世界の実現を目指しています。 そのためには、お客さま・企業・金融機関など、さまざまなステークホルダーに対して「OPENNESS」な姿勢で向き合うことで、もっと身近なものに変えていきたいと考えています。 本企画は、技術も「OPENNESS」にしていこうという考えのもと、2019年にスタートしました。 今回から「Merpay & Mercoin Tech Openness Month」とリニューアルし、よりパワーアップした形でお届けします。 「Merpay & Mercoin Tech Openness Month 2025」では、メルペイ・メルコイン・メルカリモバイルの開発をしているエンジニアたちの取り組みをご紹介します。 各エンジニア組織がテクノロジーでお客さまの課題解決を実現することを大切にし、その挑戦の中で得た知見を6月2日から約1ヶ月間に渡り毎日公開していきます!技術、開発設計や思想、組織ストラクチャー、Tips、その他最近の取り組みなど、幅広くお伝えします。 2019年は こちら 2020年は こちら 2021年は こちら 2022年は こちら 2023年は こちら ▼公開予定表 (こちらは、後日、各記事へのリンク集になります) Title Author メルペイにおけるAI活用の取り組み @keigow Rethink Tool’s UI/UX – Human-Centric to AI-Driven @ben.hsieh メルペイインターン体験記:実務の中での学びと気付き @uta メルコインでSystem Design Interviewを導入しました @toshinao PCP LLM Week レポート @komatsu (仮) チェックアウトのソリューション開発 @foghost チェックアウトソリューションのアーキテクチャ周りの話 @susho Cursorへの社内ツール知識の効率的な注入: MCPサーバー活用事例 @seitau 生成AI時代のエンジニアの生き残り戦略について @Joraku GASをつかって効率をあげました(mvnoダミー事業者とjira作成補助) @toshick Devin にE2Eテストの実装を任せる @anzai (仮)インターンレポート @Soma Nakao SRE2.0: LLMサービスの信頼性を測る新しい品質指標の紹介 @T メルカリモバイル Dev OffsiteでAI Hackathonをした話 @k_kinukawa Checkout frontend design @David, @anzai gRPC Federationで3rd party向けAPIを実装する @fivestar 6年間のインシデント対応/管理で直面した課題と改善 @foostan Mercari Pipeline (旧Mercari Dataflow Template) v1をリリースしました @orfeon (仮)AI関連でなにか or Universal link関連 @takeshi in iOS WWDC2025参加レポート @Shunta TBD: Dev Ex Improvement Cycle using DX @ntk1000 (仮)AI Creator @hiro TBD: AI関連 @abcdefuji Integration of AppIntents to a Project That Uses Bazel Build System @cyan メルカリWebにメルコインを組み込む検証をした話 @y-arima kubecon参加レポート @keitasuzuki どんな知見が得られるのか、毎日が楽しみです。 Merpay & Mercoin Tech Openness Month 2025 の1日目は、メルペイ VPoE @keigow が執筆予定です。 ひとつでも気になる記事がある方は、この記事をブックマークしておくか、 エンジニア向け公式Twitter をフォロー&チェックしてくださいね!
アバター
はじめに こんにちは!メルカリのヘルプセンターチームで、2025年の2月中旬から5月中旬までの3か月間インターンをしていた@markunです。私は普段、討論の構造を可視化するシステムについて研究しているのですが、システムの使いやすさを改善するなかで、ユーザーのニーズを的確に反映するスキルを伸ばしたいと感じていました。そこで今回、お客さまの体験を重視し、大規模なユーザーを抱えているメルカリでどのようにプロダクト開発が行われているかを学ぶべく、フロントエンドエンジニアとしてインターンに参加しました! チームについて ヘルプセンターチームは、お客さまが疑問や問題を自己解決するためのヘルプコンテンツの提供と、解決が難しい場合のメルカリ事務局への問い合わせのためのプラットフォームの構築を行っています。また、ガイドコンテンツやお問い合わせフォームを管理する社内向けシステムの開発・運用を通じて、お客さまのサポート体制を支えています。 アジャイル開発について アジャイル開発とは、ユーザーのニーズに柔軟に対応しながら迅速に改善を重ねる開発スタイルの一種です。といっても、コーディング規約などの具体的なルールがあるわけではありません。ここで鍵となるのは、チームの一人ひとりが「 アジャイルソフトウェア開発宣言 」に代表される数々の原則を実践する意識を持つことです。 アジャイル開発はお客さまの満足度を効果的に高められますが、経験がない方にとっては中々イメージが掴みにくいと思います。そこで本記事では、インターンで体験したヘルプセンターチームでのアジャイル開発の実践例をご紹介したいと思います。アジャイル開発への理解を深められたら幸いです! ガイド記事編集用の社内ツールの多言語対応 メルカリは、今年3月に 国際メルカリ便 という海外のお客さまが日本国内で出品されている商品を購入できるサービスをリリースしています。これを受けて、ヘルプセンターでは海外のお客さま向けのガイドの提供が始まりました。そこでメンターと相談して、社内向けの記事編集ツールの多言語対応の改善点について、実際にツールを使用している方に直接話を聞くことにしました。 話し合いの結果、日本語話者も多言語に対応した記事一覧ページをよく使うこと、その際に記事一覧が常に中国語で表示され、目的の記事を探しにくいということが明らかになりました。この課題を解決するため、フロントエンドに記事一覧画面の表示言語を切り替える機能を追加することにしました。 取り組んだこと いざ実装に取り掛かると、大きな課題に直面しました。記事一覧を表示する際、指定された言語の翻訳データが用意されていない記事は取得されない仕様だったことが判明したのです。そのためフロントエンド側で記事一覧の表示言語に日本語を指定するだけでは、一部の記事が欠けてしまうという問題が生じました。そこで、翻訳が存在しない場合は他の言語の記事を埋めて返す、言語フォールバックと呼ばれる処理(下図参照)をバックエンド側に実装することで対応しました。 アジャイルなポイント アジャイル開発には、ユーザーを第一に考えるという原則があります。この原則に沿って、実際にツールを使う人たちに直接話を聞いたり、担当していたフロントエンドだけでなくバックエンドにも積極的に挑戦したことで、最初に仕様が決まった時からの状況や要望の変化に柔軟に対応できました。また、ヒアリングから約1か月という短期間で、今必要とされている機能をリリースすることができました!この成果は、まさにメルカリのバリューである「Move Fast」を体現したものだったと感じます。 この経験を通じて、開発者の主体性とユーザーとの継続的な対話を重視するアジャイル開発の強みを実感しました。また、自分がこれまでに経験してきたことに縛られずにユーザーを最優先する姿勢が、メルカリの特徴であるGo Boldなプロダクト開発に繋がることが分かりました! お困りの商品選択画面のリファクタリング ヘルプセンター では、商品を選択すると関連するガイドのページに遷移する機能があります。サービスの運用を続ける中でこの仕組みに関する処理が複雑化していたのですが、ページ遷移を制御するロジックがフロントエンド側に実装されていたため、今後拡張する際に開発のボトルネックになるのではないかという懸念がありました。そこで、そのロジックをバックエンドへ移行することを検討しました。 取り組んだこと この機能を実装するうえで、様々な選択肢がありました。例えば、データベースに新たなテーブルを追加する、テーブル構造は変えずにバックエンドのロジックのみ変更する、あるいはそもそも変更を見送るという選択肢もありました。そこで私は、考えられる全ての選択肢とそのメリット・デメリットを詳細に言語化してドキュメントに整理し、それをもとにチームで実装の方針を議論しました。最終的に、バックエンドのロジックのみを変更する方針で実装を進めました。 アジャイルなポイント アジャイル開発では、課題解決に主体的に取り組むことが重視されています。このケースでは、APIのインターフェースやテーブル構造の変更などのあらゆる可能性を徹底的に議論し、実装の方針決めからリリースまで裁量を持って推し進めることができました。 この経験を通じて、自分で最初から最後までやり遂げるアジャイル開発の難しさを実感すると同時に、責任を持ってやり通す楽しさとやりがいを身に染みて感じられました。また、同じ処理でも書き方は想像以上に数多く存在すること、その中で広い視野を持って何故その処理をそこに書くのかを常に考えることが大切であると学びました。 インターンで得た学び これまで紹介したこと以外にも、ソフトスキルとハードスキルの両面で大きく成長することができました。 ソフトスキルの面では、相手の意見をうまく聞き出すには質問の範囲を絞った方がよいということを学びました。私は最初、タスクの方針について相談する際に「どんな機能があると良いですか?」「解決策は三つありますが、どれが良いですか?」といった漠然とした聞き方をしていました。これは一見幅広く意見を引き出せるようにみえますが、実際には混乱を招いてしまうことがよくありました。相手の意思を尊重しつつ、自分の考えや質問の目的を明確にした上で話を聞いた方が円滑に議論を進めやすいということが分かりました。 また、チームワークの大切さを再認識しました。私たちのチームでは2週間に1回、レトロスペクティブと呼ばれる振り返り会を行っています。このミーティングでは毎回感謝のコメントが枠をはみ出すぐらい盛り上がり、そこで生まれたモチベーションや連帯感が私のインターンでの取り組みの原動力になっていました。チームワークの重要性は当たり前すぎて見過ごしがちですが、その価値を改めて深く実感しました。 ハードスキルの面では、レイヤーごとの役割を把握することの重要性を学びました。取り組んだタスクの中には、機能自体はスムーズに実装できたものの可読性や拡張性などの観点からレビューを受け、関数の設計から見直すといったこともよくありました。インターンであっても対等に接してもらえたおかげで、何故そのコードをそこに書いたのかを明確にする、様々な職種の方に伝わるような変数名を考えるなどの基礎的な工夫に一切の妥協を許さないことがいかに重要かということが分かりました。 また、テストに関しても大きな学びがありました。私は、インターンに参加するまでテストを書いたことがなく、単にバグを発見するための手段だと思っていました。もちろんそれも重要ですが、特にユニットテストのコードは仕様書としての役割も果たしており、どの関数に何が期待されているかが一目でわかるよう徹底的にシンプルに書くべきだということを学びました。また、そのためにfor文やif文さえ可能な限り使わないようにしていることを知り、驚くと同時にテストコードの奥深さに感銘を受けました。 こうした経験を通じ、コードの質を高めるには高度なテクニックを使いこなすことよりも、シンプルさや目的に即した書き方になっているかを考える力を磨くことが重要だとわかりました。実際の現場では何が求められるのかを身をもって知ることができ、自分のコードの保守性や可読性だけでなく、システム開発の姿勢そのものへの意識も大きく変わりました。 福岡合宿の話 インターン開始から1か月が経った頃、福岡で開催されたオフサイトに参加しました。ヘルプセンターチームは福岡や大阪など日本各地を拠点としているメンバーが多く、普段はオンラインでのコミュニケーションが中心なため、対面での顔合わせも兼ねて企画されました。ミーティングでは、現状の共有やヘルプセンターの今後の展望について議論が白熱しました! その後はみんなで夕食をとり、普段なかなか顔を合わせられないチームでの親睦を深められました!!とりまぶし、絶品でした。ただ、残念ながらとりまぶしは気がついたら食べ終わっていて写真を撮り忘れてしまったので、代わりに締めで頂いたラーメンを載せます。こちらも同じぐらい美味でした! おわりに 本記事ではメルカリのインターンで取り組んだことと、アジャイル開発の経験を通して学んだことをご紹介しました。フロントエンドエンジニアとして参加しながら、バックエンドのコーディングにも積極的に取り組み、課題を見つけては素早く改善するというサイクルを繰り返し回すことができました! このように大胆に挑戦できたのは、お客さまのニーズを徹底的に考え抜き、チームとの綿密な議論を通じて何故その課題に取り組むのかを明確にできたこと、その中で責任と裁量を持ってタスクをやり遂げるアジャイル開発の原則を実践できたこと、そして何より挑戦を歓迎してくれる環境があったからだと強く感じます。 メンターの@monkukuiさんはじめ、お世話になった皆さまに、この場を借りて感謝させていただきます。ありがとうございました! 本ブログがメルカリのインターンや入社を検討している皆様の参考になれば幸いです。この記事を読んで興味が湧いた方は、ぜひメルカリにチャレンジしてください!! https://careers.mercari.com/jobs/
アバター
はじめに こんにちは!北陸先端科学技術大学院大学修士1年の@midorinです。 メルペイのBalanceチームにて1月から3月にかけての2ヶ月間、バックエンドのインターンに参加しました。 今回は、インターンで主に取り組んだ通知の改善とメルペイで学んだことを本ブログに記載します。 メルペイ Balanceチームについて メルペイはメルカリの売上金や銀行口座のお金が使える決済サービスです。Balanceチームでは売上金などの残高やポイント、債権を管理するサービスを開発しています。 ポイント失効通知 メルペイではポイントの失効期限が近づいた際に通知が届くようになっています。 具体的には当日から起算して1日後、7日後、30日後のいずれかに失効するポイントがあれば通知が届く仕様となっています。この通知は毎朝11時に実行されるバッチ処理により実現されており、このバッチ処理はBalanceチームが管理しています。 現行のポイント失効通知は付与されたポイント量に関わらず通知が届くため、1Pの付与のみでも失効期限の30日前、7日前、1日前の計3回届く煩わしさがありました。これにより、少額付与のキャンペーンが実施しづらいという弊害が生じていました。 本インターンでは主にこの通知の改善に取り組みました。 改善の流れ 今回の改善は主に以下のような流れで行いました。 仕様の確認 改善案の提案 実装〜リリース 順に説明します 仕様の確認 実装を見ながら現状の仕様を確認し、ドキュメントにまとめました。結果、以下のような仕様であることがわかりました。 1, 7, 30日後に失効期限がくるポイントが存在するか確認する あれば、それらのうち最も直近の日付について失効するポイントの通知を送る たとえば、1, 7日後それぞれに失効期限がくるポイントが存在すれば1日後が優先される 改善案の提案 仕様を確認したのち、現状の通知の総数、消費されたポイント額が通知のタイミングでどれだけ増えているか、参考となる他社の事例などを調べました。調査結果として、現状の仕様における通知の効果はそこまで大きくなく、お客さま体験向上のために減らしても良いだろう、という結論に至りました。 チームの方と協力して改善案を考えました。ミーティングを重ねる中で「少額のポイントに関しては通知の価値が低く煩わしく感じているのではないか」という仮説が浮かび上がりました。これを受け、以下のような案を候補とし、PMなどが参加している、プロダクトとしての仕様を決定するミーティングにて話し合いました。 各日に閾値を設け、失効するポイントが閾値を超えない場合は通知しない 定期便のように決まった期間の失効するポイントをまとめて通知する ミーティングにて合意がとれ、リーガルから法的な問題がないことも確認いただけたため、閾値を設ける案で実装を進めることにしました。 現状とのギャップが小さいところから段階的に導入するために、1日後の通知には閾値を設けず、7日後には100、30日後には500の閾値を設けました。閾値の根拠として、付与しているポイントは100ポイントと500ポイントが特に多いという点があげられます。傾向として額が大きくなるほど付与数は減っているのですが、100ポイントと500ポイントの付与だけがキャンペーンで付与することが多く、例外的に増えていました。ここがお客さまの体感が変わる境界であり、効果的に煩わしさが解消できると考え、ここに閾値を設けることにしました。 この仕様により、以下の図のように1P付与の際は通知が一回のみになる改善がなされます。予測として、ポイントの失効に関する通知が30%以上減少することを見込んでいます。 △保有ポイントが1Pのお客さまが受け取る失効通知(閾値導入後) 実装〜リリース ここまでで提案した仕様を実装しました。単純に閾値の追加をするだけでなく、新たにテストを記述し、QAを経てリリースへ持ち込みました。 テストの記述ではテストケースの追加しやすさや意味など、さまざまな指導をいただきながら実装を進めました。 QAでは担当の方に仕様を伝えテストケースの漏れがないかを確認して、リリース時に問題が起きないように努めました。 インターン期間の最終週にリリースをし、問題なく通知量を減少させることができていることを確認しました。 今後は失効総額が上がらないことを目標に掲げ、リリース後も通知数や消費タイミングなどのデータを見ながら継続的に改善していくのがベスト、という話に落ち着きました。 学び 今回のインターンではデータを元に仕様を策定し、実プロダクトに適用するという貴重な経験をさせていただくことができました。この経験を通していくつかのことを学びました。 コード品質を担保する仕組み テストをただ書くだけでなく、追加しやすく品質を保証するようなものを書いたり、バグを生みにくいコードを書いたりなど、普段書いているようなコードからさらに深く考えて進められており、さまざまな発見がありました。Balanceチームが管理しているサービスは一つバグが起きるだけでも致命的になり得るため他よりもコード品質に重点が置かれているという話を伺い、サービスの目的によってコードの良さの指標が変わるという学びを得ました。 効率的にReviewを進める心がけ いくつかのタスクを進める中で速度は良いが見落としがある旨のフィードバックがありました。自身でも課題に感じており、改善方法を相談したところ他の人のPRをReviewしたり、Self-Reviewしたりするのが良さそう、という一つの解決策を提示いただけました。これを実践したところ、見落としは少なくなり、新たな知識を得られるような本質的な指摘、議論へと素早く移動できるようになり、有意義に時間を使うことができるようになったと感じています。 データ駆動の改善プロセス ポイント失効通知の改善は以前から課題として認識されていましたが、具体的なデータが不足しており、着手できずにいました。今回、チーム全体でデータを用意しPMらに提案することで実装を進めることが可能になりました。 用意したデータについて、Balanceチームで管理しているサービスではDBにSpannerを利用しており、このデータは定期的にBig Queryに同期されています。今回はBig QueryのデータをLooker Studioにて可視化し、分析しました。これにより、PMらにもわかりやすいデータの提示をすることができ、スムーズに議論を進めることができました。客観的な議論、方針の策定をするためにデータが役立つことを改めて実感しました。 お客さま体験の考え方 今回の改善は通知を減らすものであるため、短期的に見るとアプリを開く確率が減り、利用率などに影響してしまう可能性はあります。しかし、お客さま体験を向上させることで長期的には利用を継続していただいたり、価値の高い通知に絞る事で通知をオンのままにしていただいたりというデータに現れにくいメリットも存在します。お客さま体験を上げるビジネス的な良さを話し合えたのはとても良い経験だったと感じています。 インターン実務以外の話 今回のインターンではタスクを進める以外にもいろいろな経験があり、どれも素晴らしい体験だったため共有します。 開発合宿 インターン開始後すぐに合宿があり、さまざまな方と交流したり、Swiftを書いたりしました。業務的なコミュニケーションを始める前に社員さんのあたたかい雰囲気をしることができたため、とても良い経験でした。 開発合宿初日に財布を落としたのですが、期間中に戻ってきて日本のあたたかさを感じることもできました。届けてくださった方、ありがとうございます。。。 開発合宿の様子は、こちらをご確認ください。 勉強会 チーム内だけでなく、有志で集まった勉強会が定期的に開催されており、技術に対するモチベの高さを伺うことができました。 Tech Talkと呼ばれるゆるめのLTも存在しており、こちらでは業務内外のさまざまな知見を得ることができました。 mertip メルカリではmertipと呼ばれるピアボーナスの仕組みが存在します。インターン生でもこれを利用することができ、自身も積極的に利用していました。他の方に貢献することが奨励されている、というのが目に見えてわかること、質問などをする際に気後れしないことなどがとてもよかったと感じています。 まとめ 本ブログではメルペイのインターンで取り組んだポイント失効通知の改善とその経験を通して学んだことを記述しました。今回の経験で、バグの生みにくいコードの書き方などのようなハードスキルと、いかにしてタスクを進めていくかなどのようなソフトスキルを得ることができ大きく成長できたと感じています。 また、インターン参加前後で会社の印象に変化がありました。参加前は技術力が強い方々が黙々と作業をしている印象でした。インターンを通して、技術力が高いという印象は変わらず、技術、プロダクトに対する興味が高い方々が楽しく仕事を進めている印象に変わりました。肩肘はらず、モチベの高い環境でとても刺激を受けることができました。とても貴重な経験ができ、メンターの@kobaryoさんはじめ、チームの方々、関わった皆様方にすごく感謝しています。ありがとうございました! 本ブログがインターンを検討している皆さんの参考になれば幸いです。 記事を見ていいなと思ってくれた方はBalance teamに応募してみてください!Balance teamに限らずメルペイは インターンを募集してます !
アバター
こんにちは。Mercari DBRE(Database Reliability Engineer) チームのエンジニアの @takashi-kun です. 今回私達のチームでは複数の Cloud SQL for PostgreSQL インスタンス(以下 Cloud SQL インスタンス)の一部を一つのインスタンスに統合するという作業を行いました. 本記事ではその作業の詳細やダウンタイムを短くするために選択した方法などを紹介します. はじめに 今回の対象サービスのメルカリ Shops では microservice アーキテクチャを採用(ref: メルカリShopsはマイクロサービスとどう向き合っているか )しており, それぞれの microservice 毎にデータベースが存在します. スペックやデータサイズは大小様々ですが, おおよそ 50 程度のインスタンスが稼働しています. これらのインスタンスは運用効率化のためすべて Enterprise Plus Edition で稼働しています. 先のブログでも紹介されていますが, データベースが microservice 毎に別となっているため, 障害の局所化や他 microservice のデータベースを意識せずに開発できるなど, 開発からリリースまでを迅速に行えるという多くのメリットを享受しました. 一方でサービスの利用が拡大していき, それらをそのまま運用していくうえでいくつかの課題がありました. https://pixabay.com/photos/elephant-herd-animals-trunk-safari-8359382/ メルカリ Shops DB の課題 この章では現在メルカリ Shops において直面していた DB 運用の課題についていくつか紹介します. コスト まず1点目はコスト(サーバ費用)です. microservice 毎に DB が存在しているため, 利用が少なく最低スペックにも関わらずインスタンスを起動させ続けなければならない, という問題があります. インスタンス1台の費用は小さいですが, microservice の数が多いため “チリツモ” で費用が膨れていくにも関わらず, インスタンス Tier は最低で運用しているため CUD 以外のコスト削減の手段を取れていませんでした. 管理 microservice 毎にインスタンスがあるため, near-zero downtime といえどメンテナンスが大量に一度に通知されてしまいます. またIaC で管理されているとはいえ, 数が多いため現在のチーム体制ですべてを網羅的に管理運用することが難しくなっていました. 性質上, それぞれの microservice を廃止/統合するといったことは難しく, かといってインスタンスを減らすということはできず限られたメンバーで多くのインスタンスの対応が必要となっていました. 余剰ストレージ Cloud SQL にはストレージの自動拡張機能と PITR がありますが, メルカリ Shops ではかつてこの機能を全てのインスタンスで有効化していました. 特に更新(INSERT/UPDATE/DELETE)が多く行われる系統のインスタンスでは, PITR のために保存している WAL のサイズが多く, それが要因でインスタンスのストレージサイズを拡張し続ける, という問題が発覚しました. 止血対応として PITR を無効化したものの, Cloud SQL では 一度拡張したストレージサイズは縮小できない ため, 添付のよう余剰なストレージサイズに対して課金し続けていました. 4.5TB のディスクに対し実データは 250GB 程度 対策の手法 メルカリ Shops では上記のような運用/インスタンス費用の課題に対する対策として, Cloud SQL インスタンスの統合を行うことを決定しました. 単純にインスタンスを統合するにもいくつか方法があり, それぞれについて簡単に解説し, 今回メルカリ Shops がどのような手法で統合を行ったかについて説明していきます. 前提 サービス要件 まず, 統合作業時のサービス側の要件について紹介します. メルカリ Shops は利用者数が店舗・購入者ともに拡大しており, かつメルカリからの導線もあるため, 長時間(1時間以上)の停止メンテナンスを行うことはできませんでした. また作業時の停止についてもメルカリ Shops の DB は基本的に read heavy な構成のため, 書き込み(INSERT/UPDATE/DELETE)は停止しても読み込み(SELECT)については停止しないように進める必要がありました. 加えて統合対象の中には決済やレポート(店舗側に売上内容を表示する)機能といったサービス的にクリティカルな機能も含まれていたため, ダウンタイムをできるだけ短くしたい要望がありました. 一方, 今回の統合作業において重要な観点として 切り戻しの準備は不要 ということで合意をしました. つまり仮に切り替え後に何らかの問題があって切り戻した場合には, 切り戻し完了までに書き込まれたデータは欠損しても問題ない, ということです. これによって構成が少しシンプルになります. システム メルカリ Shops ではインスタンスはすべて各 microservice 毎に論理データベース, クレデンシャル(user/password), 権限(GRANT)が分離されています. また, 接続方法については各インスタンスの持っている Private IP に接続する方式となっており, Cloud SQL Auth Proxy や Cloud SQL Go Connector などを利用していませんでした. そのため, インスタンス統合後の切り替えのために Instance Connection Name( ${project}:${region}:${instance} )などを変更する必要がなく, アプリケーション側での作業が不要で切り替えを実施できます. 統合方法案 上記の前提を踏まえて, インスタンス統合で検討した方法と実際に行った方法を紹介します. https://wiki.postgresql.org/wiki/Logo DMS まず, Google Cloud 上で DB の移行が可能な managed service で Data Migration Service(DMS) があります. 詳細については省略しますが, これは Cloud SQL(source) を primary とする external replica の作成と切り替えをフォローする managed service です. DMS は現時点では統合はサポートしていないこと(destination のインスタンスに DB があると実行できない), 切り替えは DB の切り替えのみサポートしていてアプリケーションの変更は別途必要ということで今回は要件に見合いませんでした. export/import 当初やろうとしていた方法はこれで, source インスタンスで書き込みだけを停止し該当 DB を export , そしてそれを destination へと import し, 完了したらアプリケーションを destination へと変更する方法です. この方法は最も手順がシンプルかつ事前準備もほとんどいらないため, 最も楽な方法ではある一方, 書き込みの停止時間が長くなります. 特にメルカリ Shops では source インスタンスを参照している分析用バッチなどが多く動いていて, それらを import 後に切り替えるなどをするとどうしても 1 時間以上書き込み停止発生してしまい, その停止時間がサービスにクリティカルな機能の要件に見合わないということで, この方法も断念しました. Logical Replication(manual) 最後に検討した方法が PostgreSQL の Logical Replication を利用する方法です. Cloud SQL では Logical Replication をサポートしている ため, これらを利用してデータの同期と切り替えを行います. Logical Replication は, primary を source としないといけない, 同期レプリケーションはサポートしてない(設定できない), ConnectorEnforcement が有効化されている場合は機能しないなどの Cloud SQL としての制限 や, DDL は伝播しない, large object は伝播しないなど Logical Replication としての制限 がいくつかありますが, 今回のケースではいずれも大きな問題とはなりませんでした. 加えて export/import で問題になった分析用バッチなどもこの方法だと同期が完了したタイミングで参照先をアプリケーションより前に変更しておくなど, 切り替え方法を工夫することで停止時間を短くできるため, 今回はこの方法をとることにしました. 統合手順 Logical Replication を構成し統合するにはこのような流れで進めていきます: インスタンスで cloudsql.logical_decoding を有効化 schema dump & restore Logical Replication を設定 同期完了を待つ アプリケーションの接続を DNS ベースにする source 側で書き込み block(downtime 開始) ブロックする前までのデータ更新が追いついたことを確認 DNS 変更 source 側で既存の接続を kill 大きく分けて Logical Replication 準備(1-4), 切り替え前作業(5-7), 切り替え(8-9)の3つに分けて説明していきます. Logical Replication 準備 ここではインスタンスで Logical Replication を構成するための設定をします. 以下, 統合先インスタンスを destination, 統合元インスタンスを source とします. まず最初に source に対して cloudsql.logical_decoding を有効化する必要がありますが, これには再起動が必要となります. データ量や TPS などによって異なるかと思いますが, 大体 30s ~ 60s 程度で起動が完了しました. 続いて destination に source と同じ PostgreSQL user, database を作成します. user , database を作成したら destination に source の schema をリストアします. 実データは Logical Replication の COPY によって同期されていくため単に空のテーブルとアクセス権のみ設定するだけでよいです. インスタンスに接続するのは前述の Cloud SQL Auth Proxy 経由で接続しています(この後の Cloud SQL Auth Proxy の起動は省略). また, この後何度もインスタンスに接続をするので, このように /etc/hosts や ~/.pgpass を設定しておいて機械的にアクセスできるようにしておくと良いでしょう. ## /etc/hosts 127.0.0.1 source-001 127.0.0.2 destination-001 ## ~/.pgpass ### source-001, destination-001 に src user として src database に接続 source-001:5432:src:src:${PASSWORD} destination-001:5432:src:src:${PASSWORD} $ cloud-sql-proxy \ ${PROJECT}:${REGION}:source-001 \ --address $(grep source-001 /etc/hosts | cut -d' ' -f1) $ cloud-sql-proxy \ ${PROJECT}:${REGION}:destination-001 \ --address $(grep destination-001 /etc/hosts | cut -d' ' -f1) なお Logical Replication は DDL を伝播しないという制約があり, dump/restore やLogical Replication 中に誤って DDL を実行してしまわないようにスクリプト側でもブロックするなどしてそれに対応しました. ## dump $ pg_dump \ -U ${USER} \ -h source-001 \ --schema-only > src.$(date '+%Y%m%d-%H%M%S').sql ## restore $ psql -h destination-001 \ --user src \ --dbname src < src.xxxxxx.sql Logical Replication を構成するためには publication を source 側に, subscription を destination 側に作成する必要があります. それぞれ以下のような形で作成可能です: ## publication の作成 CREATE PUBLICATION pub FOR ALL TABLES; SELECT * FROM pg_publication_tables; subscription でつなぐユーザーは, 区別できるようにアプリケーションが利用しているものとは別のものを用意するとよいでしょう. ## subscription の作成 CREATE SUBSCRIPTION sub_src CONNECTION 'host=xxxxxxx port=5432 dbname=src user=replication password=xxxxxx' PUBLICATION pub; SELECT * FROM pg_stat_subscription; これで Logical Replication が開始し, COPY コマンドが source 側で実行され, データの初期同期(既存のデータのコピー)が行われます. COPY が終わったらデータの差分同期が行われるようになります. データサイズの大きいテーブルだとかなり時間がかかるため, COPY 作業が終わったかどうかは source 側で pg_stat_activity を見るか, destination 側で COPY 対象テーブルにロックが掛かってるかを見るとわかります: ## source SELECT * FROM pg_stat_activity WHERE query LIKE '%COPY%'; ## destination SELECT * FROM xxx LIMIT 1; COPY が終わり差分同期が始まったら source と destination の差はこのようにして求められます. 両方 0 だった場合は追いついています. ## source SELECT pg_wal_lsn_diff(sent_lsn,write_lsn) write_diff, pg_wal_lsn_diff(sent_lsn,flush_lsn) flush_diff, pg_wal_lsn_diff(sent_lsn,replay_lsn) replay_diff FROM pg_stat_replication; ## destination SELECT pg_wal_lsn_diff(received_lsn,latest_end_lsn) FROM pg_stat_subscription; COPY が終わりそれぞれの diff が 0 となっていたら差分同期も追いついているため, 切り替え前作業に進みます. 切り替え前作業 ここでは書き込みを停止して実際に切り替えを実行する前までの作業を説明します. 前述しましたがメルカリ Shops の DB 構成は DB への接続は直接 IP ベースで接続している write/read ユーザーを分けていない, cloudsqlsuperuser Role を持つ ということがあります. 書き込み停止を確実に行い, かつ短時間で済むようにするにはいくつかの工夫をする必要があります. 接続方法の変更 IP ベースでの接続となると, アプリケーションを統合先インスタンスへと向き先を変更する必要があり, そのためにはデプロイが必要となります. 書き込み停止時間中にデプロイをすると数十分かかるため, 事前にアプリケーションの接続を FQDN を使うように変更することにしました. 例えば DSN では postgres://src:xxxxxx@10.0.0.1:5432/src と 10.0.0.1 に向いていたものを src.db-consolidation.mercari.internal と FQDN に変更します: postgres://src:xxxxxx@src.db-consolidation.mercari-shops.internal.:5432/src こうすることによって書き込み停止した後に src.db-consolidation.mercari.internal を source から destination に変更することで, アプリケーションのデプロイを伴わずに切り替えが可能となります. 書き込み停止 書き込みの停止については write/read ユーザーが分かれておらず同一ユーザーでクエリが実行されていたため, 少し工夫が必要です. もしユーザーが分かれていれば write ユーザーの RENAME などでブロックすることも可能ですが, そうなってはいないためユーザーの権限を剥奪するかデータベースごと read only にするかの 2 通りの方法が考えられます. ユーザーは cloudsqlsuperuser Role を持っているため, REVOKE を実行して更新権限剥奪するよりも, 有効/無効が単純なデータベースを read only にすることにしました: ALTER DATABASE xxx SET default_transaction_read_only TO on; ただし, この ALTER 文は default を変更するのみで transaction で明示的に SET TRANSACTION などが実行されている場合は書き込み可能となってしまうため, それが利用されていないことを確認する必要があります. メルカリ Shops では DB 周りは https://github.com/ent/ent を利用しており, SET するには WithVar を実行するか, 直接 SET XXX を実行する必要がありますが, そのどちらも実行されていないため ALTER DATABASE を採用しました. 実際の流れですが, まずアプリケーションの DSN を FQDN(向き先は source-001) に変更しデプロイします. デプロイ完了し, 実際に FQDN が利用されていることが確認できたら書き込みを停止させるために source 側で ALTER 文を打ちます. この ALTER 文は Connection Pooling などすでに接続されているクライアントには有効ではないため, 一度すべての接続を切断して再接続を促します. ## 書き込みを停止 ALTER DATABASE src SET default_transaction_read_only TO on; ## 自分以外の接続をすべて KILL SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND datname = current_database() AND usename = CURRENT_USER; このあとの接続はすべて default_transaction_read_only = ON となっているため, (明示的に指定しない限り)書き込みは失敗します. 切り替え 切り替えは FQDN の向き先を destination の IP に変更することで切り替え可能です. その前にデータが同期されているかなどを確認しておく必要があります. Cloud SQL での Logical Replication は同期レプリケーションではないため, 厳密にはすべての更新が伝播したかをチェックする必要がありますが, 前述の通りメルカリ Shops は read heavy なユースケースであって差分同期は十分早いため, シンプルな確認だけするようにしました. ## source SELECT pg_wal_lsn_diff(sent_lsn,write_lsn) write_diff, pg_wal_lsn_diff(sent_lsn,flush_lsn) flush_diff, pg_wal_lsn_diff(sent_lsn,replay_lsn) replay_diff FROM pg_stat_replication; ## destination SELECT pg_wal_lsn_diff(received_lsn,latest_end_lsn) FROM pg_stat_subscription; これらの source/destination での WAL の同期状況, 実行状況を何度か確認しそれぞれ 0 のままだった場合は切り替え可能として進めます. また, Logical Replication は仕様として sequence は同期されないため, もし利用している場合はそれもここで合わせておく必要があります: ## source SELECT * FROM pg_sequences; SELECT max_value FROM pg_sequences WHERE sequencename = '${SEQUENCE_NAME}'; ## destination SELECT setval('${SEQUENCE_NAME}', 99999); SELECT max_value FROM pg_sequences WHERE sequencename = '${SEQUENCE_NAME}'; これで source と destination のデータが揃っている状態となるため, FQDN の向き先を destination の Private IP に変更し, DNS が伝播した後に再度 source 側の接続を KILL します. これで source 側に残っている接続がすべて destination に切り替わります. $ dig +short src.db-consolidation.mercari-shops.internal. 10.0.0.2 ## source SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND datname = current_database() AND usename = CURRENT_USER; ## destination SELECT * FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND datname = current_database() AND usename = CURRENT_USER; FQDN を変更し source への接続を KILL しすべての接続が destination に向くことが確認できたら, downtime は終了となります. 統合した結果 上記の手順で統合作業を行うことで, 長くても 2~3 分程度の停止時間で切り替えが完了し, 実作業はメンテナンスなどを設けずにすべてオンラインで日中に完了させられました. エラーも切り替え作業前後の接続を KILL することによる再接続でエラーとなる程度で, 瞬断程度の問題で済みました. また統合による効果として コスト効果: 25%程度削減 余剰なインスタンス削除による運用負荷軽減 など, 当初の課題をいくつか解消することができました. メルカリ Shops では IP ベースでの接続をしていたためこのような手法で統合/切り替えを行ってきましたが, Cloud SQL Auth Proxy などではまた別のアプローチ( v2.15.0 の DNS ベースを利用するなど)を取る必要があるかと思います. また, より強い整合性が求められるケースや切り戻し用インスタンスを準備して切り戻しに備えるなど, 今回の統合手法よりも難しいケースも考えられます. 普段チームでよく運用している MySQL とは勝手が違うことが多くかなり勉強になりました. 最後に 現在, メルカリでは学生インターン/エンジニアを積極的に募集しています. ぜひ Job Description をご覧ください。
アバター
株式会社メルカリのPlatform Enablerチームで新卒エンジニアとして働く Tianchen Wang (@Amadeus) です。今回は、Large Language Model (LLM)を利用してフリマアプリ「メルカリ」の次世代インシデント対応を構築した事例を共有します。 今日の急速に進化する技術環境において、堅牢なオンコール体制を維持することは、サービスの継続性を確保するために重要です。インシデントは避けられないものですが、迅速に対応し解決する能力は、お客さまに安心・安全の体験を提供するために不可欠です。これは、メルカリのすべてのSite Reliability Engineer(SRE)と従業員が共有する目標です。 この記事では、Platform Enablerチームが生成AIを活用して開発したオンコールバディであるIBIS (Incident Buddy & Insight System) の紹介をします。IBISは、エンジニアのインシデント解決を迅速化し、MTTR(Mean Time to Recovery)を短縮することで、組織やエンジニアが負担するオンコール対応コストを削減することを目的として設計されています。 課題の認識と解決のモチベーション メルカリでは、お客さまが安心・安全に製品を利用できることがすべての従業員によって共有される優先の目標およびビジョンです。このために、異なる部門が協力し、オンコールチームを設立しました。毎週、オンコールメンバーには多くのアラートが発生し、その多くは実際にお客さまに影響を与えたインシデントとして扱われます。これらのインシデントはお客さま体験の悪化をもたらすため、インシデントが回復するまでの平均時間(MTTR)を短くすることが被害を最小化する上で重要です。 さらに、オンコールメンバーはこれらのインシデントに対処するために多大な時間を割かなければならず、新しい機能を開発するために利用できる時間が間接的に削減され、ビジネス目標の達成能力に影響を及ぼします。 結果として、 インシデント発生時にMTTRを短縮し、オンコールメンバーへの負担を軽減 することが、プラットフォームチームにとって重要な課題となっていますが、Large Language Model (LLM)の登場により、これらのインシデント対応を自動化することが可能な解決策として浮上しました。 深掘り:IBISのアーキテクチャ インシデント対応システム「IBIS」のアーキテクチャを詳しく見ていきましょう。 図1. IBISのアーキテクチャ 高レベルの視点から、過去のインシデントについての振り返りレポート情報をインシデント管理ツール Blameless から抽出します。これらのレポートには、暫定措置、根本原因、障害による損害などのデータが含まれています。これらのデータはクレンジング、翻訳、および要約のプロセスを受けます。その後、OpenAIの埋め込みモデルを使用して、これらのデータソースからベクターを作成します。 ユーザーが自然言語でSlackボットに質問を投げかけると、これらのクエリもベクターに変換されます。その後、会話コンポーネントが質問に関連するベクター埋め込みを検索し、関連する言語構造を整理してユーザーに応答を形成します。 アーキテクチャ全体を「データの前処理」と「会話機能」の2つの主要コンポーネントに分けて詳しく説明します。 データの前処理 以下はIBISがインシデントデータを前処理する方法です。 図2. IBISのデータ処理プロセス データ抽出 Blameless には各インシデントのプロセス詳細、インシデントSlackチャンネルからのチャットログ、振り返りおよびフォローアップアクションなど重要なインシデント関連情報が含まれています。Google Cloud Schedulerを活用し、Blamelessの外部APIから最新のインシデントレポートを定期的にGoogle Cloud Storageバケットにエクスポートします。このプロセスはサーバーレスの原則に基づいて設計され、Google Cloud Run Jobs内で実行されます。 データクレンジング Blamelessから取得したデータを無差別にLarge Language Model (LLM)に送信することはできません。それは、データに多数のテンプレートが含まれており、ベクター検索( コサイン類似度 )の精度に大きく影響を与える可能性があるだけでなく、膨大な量の 個人識別情報(PII) が含まれているためでもあります。潜在的な情報漏洩のリスクを軽減し、生成される結果の精度を高めるため、データクレンジングは必要なプロセスです。 データからテンプレートを除去するため、データがMarkdown形式であることを利用し、LangChainが提供する Markdown Splitter 機能を使って関連するセクションを抽出します。PIIに関しては、種類が多いため、 SpaCy NLPモデルを使用してトークン化し、語の種類に基づいて潜在的に存在するPIIを削除します。 データクレンジングコンポーネントはGoogle Cloud Run Functionsで実行されます。このステージ以降は、Google Cloud Workflowを使用してシステム全体を管理します。Google Cloud Storage Bucketに新しいファイルが追加されると、Eventarcが自動的に新しいワークフローをトリガーします。このワークフローはHTTPを使用してデータクレンジング用のCloud Run Functionを起動し、完了するとプロセスの次のステージに進みます。クラウドワークフローを導入することで、ETLプロセス全体のコードメンテナンスが容易になります。 翻訳、要約、エンベディング クリーンになったデータはプロセスの次の段階に進みます。データクレンジングのおかげで、LLMモデルを利用して、データをよりスマートに処理することができます。メルカリでは、インシデントレポートが日本語と英語で書かれているため、これらのレポートを英語に翻訳することは、検索精度を向上させるために重要なステップです。翻訳ステップをGPT-4oベースのLangChainに依頼しています。また、多くのレポートが長文であるため、内容の要約もベクター検索精度を向上させるために重要です。GPT-4oがデータの要約を支援します。最後に、翻訳された要約済みのデータはエンベディングを経て、ベクターデータベースに格納されます。 翻訳、要約およびエンベディングプロセスはGoogle Cloud Run Jobsで実行されます。データクレンジングが完了すると、Cloud WorkflowがCloud Run Jobを自動的にトリガーします。図2に示されているように、エンベディングされたデータはLangChainが提供する BigQueryベクターストア パッケージを使用して、BigQueryテーブルに格納されます。 会話機能 Slackベースの会話機能はIBISのコア機能です。私たちの設計では、ユーザーはSlackでボットに言及することで、自然言語でIBISに直接質問を投げかけることができます。この機能を実現するために、Slackからのリクエストを常時受信し、ベクターデータベースに基づいて応答を生成できるサーバーが必要です。 図3. IBISの会話システム 図3に示すように、このサーバーはGoogle Cloud Run Service上に構築されています。ベクターDBとして機能するBigQueryから関連情報を取得し、それをLLMモデルに送信して応答を生成します。 クエリの処理に加えて、会話コンポーネントは、短期記憶などの他の機能もサポートしており、インタラクティブな体験を向上させます。 短期記憶 エンジニアがインシデントの理解を時間とともに深めることを考慮し、同一スレッド内で記憶機能を取り入れることは、インシデントの解決策を提供するIBISの能力を強化するために重要です。図4に示されているように、LangChainのメモリ機能を使用して、同じチャンネルからのユーザーのクエリとLLMの応答を保存します。同じチャンネルで追加のクエリが投げかけられる場合、スレッド内の以前の会話がLLMに送信される入力の一部として付加されます。 図4. 短期記憶の設計 このストレージソリューションは、メモリをCloud Run Serviceインスタンスのメモリ内に配置するため、新しいバージョンのIBISを再デプロイしてCloud Run Serviceを更新すると、メモリが消失します。詳細については、 LangChainのメモリドキュメント を参照してください。 図5. 短期記憶のケース インスタンスをアクティブに保つ 短期記憶機能のメモリデータが現在インスタンスに保存されているため、コールドスタート時にメモリが失われないようにこのインスタンスをアクティブに保つ必要があります。これを達成するために、この ドキュメント のガイダンスに基づいた戦略を実施しました。Cloud Run Serviceインスタンスに定期的にアップタイムチェックを送信して、アクティブな状態を維持します。このアプローチはシンプルで、コストも最小限です。また、このサービスのスケールアップを制限し、インスタンスの最大数と最小数の両方を1に設定しました。 今後の展望 正確に ユーザーフィードバックを収集する ことが主要な目標の一つです。自動評価のためのヒューマン・イン・ザ・ループアプローチを採用し、ユーザーの調査応答をデータポイントとして収集し、IBISを継続的に改善する計画です。 従来の言及ベースのクエリ方法から Slackフォームベースの質問アプローチ に移行する予定です。この変更は、ユーザーのクエリを精緻化することにより、応答の精度を向上させることを目的としています。 社内ツールの継続更新を考慮し、会社のドキュメントに基づいて LLMモデルをfine-tuningする 計画です。これにより、モデルが最新で関連性のある回答を提供することを確実にします。 まとめ このプロジェクトは2024年12月末に初期バージョンをリリースしました。このブログを書いている時点までで(2024年1月)、IBISはメルカリのインシデント対応用のいくつかのslackチャンネルで使用可能になりました。このツールを利用するユーザーの数は増え続けているので、継続的にユーザーフィードバックを収集し、回復までの平均時間(MTTR)への影響を監視していきます。 さいごに 現在、株式会社メルカリでは学生インターン・新卒エンジニアを積極的に募集しています。ぜひ Job Description をご覧ください。
アバター
こんにちは、九州大学大学院1年の@masaと申します。 私は、2024年11月から12月末までの2ヶ月間、メルカリ ハロのフロントエンドエンジニアとして、インターンに参加しました。 左からインターンの@masa、メンターの@d–chanさん 今回は、その中で特に注力したインテグレーションテスト戦略と、メルカリでの学びについてお話しします。 なぜ「メルカリ ハロ」のインターンに参加したのか インターンに参加した主な目的は、大規模サービス、特にtoC向けのサービス開発を体験することでした。メルカリのサービスの中でもメルカリ ハロは、リリースして1年も経っていない新規のプロダクトであり、スピードと品質が求められる現場で、実践的な開発プロセスを学ぶ絶好の機会だと考えました。 また、メルカリという会社の雰囲気やカルチャーを直接体験して解像度を上げてみたいという興味も、参加の大きな動機の一つでした。 インテグレーションテストへの取り組み インターン期間中、大小様々なタスクに取り組みましたが、中でも注力したのが事業者向け画面のインテグレーションテストです。私がジョインした時点で、テックリードの@ryotahさん主導で技術選定と環境構築は完了しており、テストカバレッジ向上に取り組む段階でした。 メルカリ ハロでは、インテグレーションテストに関して、メルペイのフロントエンドテスト方針も踏襲して、仕様書(Spec)に準拠した、ページ単位のインテグレーションテストを実装しました。この過程で以下の2点に関して、改良に取り組みました。 冗長なコードの回避 バリデーションテストの最適化 冗長なコードの回避 Specに従ってテストを記述することで、チーム内でのテスト粒度や方針の一貫性を保てます。しかし、Specに厳密に従いすぎると、異なる画面で同じフォームコンポーネントを使う場合などに 検証内容が重複してしまい、コードが冗長になりがちです。 この問題に対し、以下の3つのアプローチを検討しました。 共通コンポーネントにテストを記述 メリット:テストコードの重複を解消できる。1つの共通コンポーネントに対するテストを集約できるため、同じ検証ロジックを何度も書く必要がなくなる。 デメリット:インテグレーションテストとしては「実際のアプリケーションに近い形でテストしたい」という方針とやや乖離する。複雑な部分をコンポーネント化してしまうと 「人によってテストの書き方が異なってしまう」 という懸念もある。 全ての画面でテストを記述: メリット:上記二つの中間的アプローチで、各ページでの実際のユーザー操作を想定したSpecに基づいたテストを忠実に書くことになるため、微妙に異なるユースケースやバグを見落としにくい。 デメリット:同様のテストロジックを大量に書くことになり、変更があった際の修正も多岐にわたってしまいメンテナンスが大変。 代表的な1画面でのみ共通コンポーネントのテストを記述 : メリット: 上記2つの中間的アプローチで、テストの冗長性をある程度抑えつつ、基本機能の担保が可能。 デメリット: 完全な網羅性はないものの、必要に応じて各ページごとに追加テストを柔軟に書くことで補える。 最終的には、 「代表的な1画面でのみ共通コンポーネントのテストを書く」 方式をベースに、 ページ固有のロジックがある場合だけテストを追記する という運用に落ち着きました。現状のチームリソースや開発速度を考慮すると、これが最も 現実的かつ柔軟 なアプローチだと判断しました。 バリデーションテストの最適化 フォームライブラリ(react-hook-form)の標準的なバリデーションはユニットテストでカバーし、インテグレーションテストでは、ユニットテストでは検証しにくいバリデーションに集中しました。 たとえば、以下のように submit時にエラーがあった場合にモーダルを表示 するロジックは、react-hook-formのschemaテストだけではカバーしにくいケースです。 const onSubmit = (value) => { // 入力項目に誤りがある場合 if (value.name !== 'hoge') { setShowModal(true) } // データの送信など } ここのような部分をPlaywrightを使ったインテグレーションテストで検証します。 // Playwright を使用したインテグレーションテストの例 test('入力項目に誤りがある場合にモーダルを表示する', async({page}) => { // 省略 // ... await page.getByLabel('名前').fill('foo'); await page.getByRole('button', {name: '送信'}).click(); await expect( page.getByRole('dialog', { name: '名前にはキーワードを含めてください' }).toBeVisible(); }); テストを書くコストとリターンのバランスを意識しつつ、後々の技術負債にならないよう、意味のあるテストコードを心がけました。 また、開発プロセスの透明性と効率性を高めるため、インテグレーションテスト用のSlackチャンネルを作成しました。背景としては、フロントエンド領域で気軽に技術的な相談ができる場が十分に整備されていなかったことや、別チームのエンジニアとコミュニケーションを取る機会が少なかったことが挙げられます。そこで、実装中に直面した課題や疑問点を具体的なケースとともに共有できるようにしたことで、 チーム全体で問題意識を共有することができ、より良いソリューションを見出す ことができました。 その他の活動と経験 インターン期間中に、生成AIを活用した業務効率化アイデアソンにも参加しました。 90分という限られた時間内で、チームビルディングからアイデア出し、プロトタイプ作成まで行うというかなり濃密なスケジュールでしたが、とても刺激的で楽しかったです。 アイデア選定時は「共感が得られるか」と「短時間で成果を出せるか」を重視しました。最終的には、「カレンダーたのんだ〜」という、参加者の空き状況や入れたい予定の性質をもとにGoogleカレンダーの予定調整を効率化するアイデアに取り組むことにしました。 チームの方々が優秀すぎて、自分の役割を見出すのに最初は戸惑いましたが、自分にできる貢献を考え、ワークフローの設計および実装を担当しました。Zapierを使用してカレンダー情報を取得する部分も実装したかったのですが、時間の制約で叶いませんでした。 そして結果はなんと、優勝することができました🎉 (チームメンバーのみなさんに感謝です🙇‍♂️) 英語でのコミュニケーションでの苦労 インターン選考時の面接では、所属予定のチームは英語の使用頻度が高くないので、英語が苦手でも大丈夫というお話を伺っていました。しかしチームの状況が変わり、私が参加した初週から、週に1回フロントエンドのMTGは英語になりました。英語でのコミュニケーションには正直不安があり、特に司会役を担当しながら英語で進行しなければいけない回は、大変苦労したことを覚えています。事前にチートシートを用意するなどして、なんとか乗り切りました…。 さらに、オフィスには多くの外国籍社員が在籍しており、社内イベントへ参加することで自然と英語を使う機会が増えました。また、Pull Requestのレビューも英語でやりとりするため、日常的に英語に触れられる環境だと実感しました。 予想以上に英語を使うシーンが多かったことで最初は戸惑いましたが、そのおかげで英語学習のモチベーションが格段に上がりました。技術的スキルだけでなく、グローバルなコミュニケーション能力も磨ける環境は、エンジニアとしての成長に大きな価値があると感じています。 さいごに 今回、メルカリ ハロのインターンを通して、大規模サービス開発の現場で多くの貴重な経験を積むことができました。特に、インテグレーションテストの実装を通じて、効率的で品質の高いテストコードを書く考え方や、チームコミュニケーションの大切さを深く学んだと感じています。 この2ヶ月間で得た知識や経験を、今後の学びやキャリアに活かしていきたいと思います。最後になりましたが、メンターのd–chanさんをはじめ、あたたかく迎えてくださった皆さまに心より感謝申し上げます。
アバター
はじめに こんにちは、メルカリでJapan RegionのCTOを担当している木村です。僭越ながら今年も最後のアドベントカレンダーの投稿を担当させていただきます。 昨年投稿した開発組織にとっての Engineering Roadmapの必要性 についての記事では、「開発スケジュールの期待値調整」が容易になったり、「将来を見越したアーキテクチャ」を作ることができたり、「Visionを組織に浸透させやすくなるメリット」があることなどをご紹介しました。しかし、昨年は実際にEngineering Roadmap(以下Roadmapと呼ぶ)にどのようなアイテムがあるのか、あるいはどのように運用されているのかといった具体的なご説明までには至ることができませんでした。本稿では、運用上難しかった話なども含めて、より実践的な内容をお話ししたいと思います。 昨年のRoadmapを振り返る まずは、前回のRoadmap作成時の狙いと実際に1年後どのような結果になったのかをご説明したいと思います(まだビジネス上オープンになっていないものもあるため、公表されているものに絞ってお話しします。Roadmapに関連するビジネス的なイベントとしては 2024年3月6日に新規事業であるメルカリ ハロをリリース。2024年8月29日に、台湾のお客さまがWeb版「メルカリ」を通じて日本で出品された商品を購入できる「 越境取引 」 の展開をスタートしたほか、 2024年9月10日には生成AIを活用した「AI出品サポート」の提供 を開始しました。 これらを技術的に実現させるため、23年12月の段階で、Roadmapの大きな方向性として以下のように定めていました。 ① 3つの領域の"開発コストの低下"と"Enabling"を実現する 中長期的なビジネスの拡張を実現するために、以下の3つの事項のバランスを保ちつつ、Biz Enablingと開発コストの低減を実現する。 既存サービス開発簡易化 新規事業展開簡易化 国際展開 メルカリグループではメルペイやメルコイン、今回のハロのように継続的に新規事業を提供しています。このような新規事業を立ち上げるたびにすべてを0から開発するのではなく、既存のプラットフォームを拡張・活用することによって、新規事業の展開をより高速かつ低コストで実現することをゴールに掲げました。この方針を応用することで、国内の新規事業の立ち上げのみではなく、多国展開もより効率的に実現することも目指しています。当然ながら新規のものだけでなく、既存サービスへの改善もあるので、既存サービス開発の効率化も同時に目標として掲げていました。 これらを実現するためにRoadmapの中にアクションアイテムとして定義していたものを一部抜粋してご紹介します。 Golden Path これまで、メルカリグループの組織としてどの技術を標準と位置付けるのかは特に明文化されていませんでした。もちろん言語やデータベースの選定などにおける暗黙的なコンセンサスは組織のなかにありましたが、基本的には各事業が必要な技術の選定をそれぞれで行ってきました。これらは柔軟性や自由度の高さという観点ではうまくワークすることもありましたが、一度使い始めると長期的なメンテナンスコストが発生したり、事業の立ち上げ時にゼロから投資を行う必要があったり、 選定のために同じような議論を繰り返すことになるなど、スピード面で課題がありました。 Golden Pathはグループ内での技術の標準化やフレームワークを作ることによって、開発と運用コストを落とすことと、同時に新規サービスを作る際の効率化を狙いました。アクションアイテムとしては以下のように設定していました。 技術標準をベースとしたアプリケーション構築領域(Web, Mobile, Backend)での Bootstrapping tool の開発を進め、Global Expansion への活用が可能な状態になっている。 これは言い換えると、アプリケーションを作る際に、メルカリの環境に適した効率的で標準化されたフレームワークを提供することを目的としています。1年後どのようになったかというと、残念ながらすべての領域でこれを実現することはできなかったのですが、WebについてはBootstrapping toolが完成されて、新規事業を作る際のWeb開発を大きく効率化することができました。また、改めてADRや Tech Radar の仕組みが整備され、BackendやMobile開発においても開発に使われるTech Stackを改めて標準化することによって、関係者にコンセンサスを取る手間が省けるようになり、技術選定のコストを低減させることができました。 また、新しい microservice や Webアプリケーション を本番環境で運用する前の基準を定めたチェックリストである Production Readiness Check (PRC) の効率化・短縮化 も実施しました。これまで2ヶ月以上かかってしまっていた PRC を自動化することによって効率化する試みです。 技術の標準化というものは、ごく当たり前のことに聞こえてしまうかもしれませんが、弊社でも継続的な新規事業の創出や技術的なトレンドの変化によって、全社でのコンセンサスを取ることが難しくなってきていました。ここで、この課題をそのままにせず、一度立ち止まり、全社で標準的に用いるTech Stackを再整理することで、改めて開発の高速化を狙う決断をしました。 IDP いわゆるアカウントIDに関するプラットフォームの改善です。IDはビジネス戦略に合わせて先行して技術基盤を用意しなければならず、Roadmapの中でも最重要な項目です。PassKeyに関する開発や普及に関してもこの項目に含まれます。こちらも、国際展開に向けて以下のようなアクションアイテムを設定していました。 新しくなったアカウント登録・ログインプロセスが、実際に日本以外のregionから利用される状態になっている。 計画的に開発を行い、こちらも1年後の現在、台湾で提供されているサービスでのアカウント作成に活用されています。上記にも述べましたように、IDはビジネス戦略の根幹になる技術と言っても過言ではありません。 昨年の記事 にも記載しましたように、お客さまへ新しい価値を提供するには、開発組織としてビジネスの方向性にアラインしつつ、先行してプラットフォームを用意する必要があります。まさに台湾での越境取引の件は昨年の時点で、他国へのビジネス展開が概ね決まっていたので、先行してそれを実現するためのアクションプランを計画的に実装し、提供することができました。また、これまでにもメルコインやメルカリ ハロを提供する中で、メルカリのIDとeKYCさえ完了していればとてもシームレスに新規サービスをご利用いただける仕組みができあがったのも、継続的にIDPが先を見越した開発ができていたことに起因しています。 AI出品サポート この時期に生成AIの活用の推進も強化しており、9月にリリースされた AI出品サポート のアクションアイテムも設定してありました。 AI出品サポート(出品補助) 生成AIのポテンシャルの大きさは明らかではありましたが、これをいち早くビジネスに導入して、特にメルカリにおける出品の利便性を向上させてお客さまに早く価値を提供したいと考えていました。この段階ではまだ生成AI社内でも検証段階でありましたが、早い段階でサービス活用の指針を定められていたことで、業界でも比較的早い段階で生成AIを実際にサービスに活用することができました。 Roadmapのアクションアイテムは実際にはより多くのものがあるのですが、雰囲気を掴んでいただくために公開できる範囲で一部のみ抜粋させていただきました。 この先にも述べますが、Roadmap作成の最大のメリットは、Visionを示すだけに留まらず、やること・やらないことを明確に意思表示できることだと感じています。特に大きなリアーキテクチャが伴うものについては「やらないといけないと思っていた」や「やろうと思っていた」ことは、なるべく早くに意思決定して、早く取り掛からなければ、後々解決するのが困難になってしまうことが多々あります。私たちは継続的に解決しなければならない課題について議論し、ビジネスとの方向性とアラインしながら技術的な投資の決定を継続的に行っています。 Next level of Scalability and Resiliency 今後のサービスの成長をより堅牢にするために、Infrastructureレベルでの改善もRoadmapに設定していました。これまでも私たちはInfrastructureのResiliencyやSalabilityの改善を行ってきましたが、今後の国際展開によるお客さまの増加や金融事業を提供しているメルペイのResiliencyを改善するためには抜本的な仕組みの改善が必要でした。 Scalabilityの改善に関して、特に大きな進歩は、大規模なコアなデータをMySQLの物理サーバに保存していたものを、 慎重な検証を重ねた上 で、TiDB Cloudへのmigrationを始めたことです。これによりSclabilityの改善と運用コストの大幅な低減の実現を狙っています。 そして、国際展開するための基盤の準備として、複数拠点でサービスを運用するためのMulti RegionでのInfrastructureの構築も進めています。こちらについては、現在も進行中であり、まとまった形で発表できる状態になったら再度詳しくご説明したいと思います。しかし、並行してInfrastructureのコストを最適化しつつも、Multi Regionでのサービス運用を実現するためにはコストの増加は避けられません。したがって、 FinOpsの文化醸成を継続的に行い 、具体的なコストの低減を全社の目標として共有しています。今年は、特にCUD採用率、Spot VM採用率を全社で上げていき、コンピュートリソースの最適化を実現することができました。 Roadmapの活用と運用 この作成したRoadmapをどのように活用、運用しているのかについてご説明します。 Visionの浸透に活用する Engineering組織で重要なことについてVisionの浸透があります。「私たちは今後何を実現したいのか」、そして「どのような過程を経てこれを実現させるのか」を一人一人のエンジニアに理解してもらうことが大事です。浸透について、私たちも特別なことをやっているわけではないのですが、Roadmapが完成したら、Engineerが全員参加するAll Handsで作成したRoadmapを使って説明し、Visionの浸透を図っています。まさに、年末の今も来年のRoadmapを作っているところであり、年明けに来年からの3ヵ年に実現したいVisionとRoadmapを全社で説明することになっています。なかなか一度の説明では浸透しないので、プレゼンテーション資料だけではなく、言語化されたRoadmapの文章をいつでも誰でも見られる状態にすることや、誰でもこれに対してFeedbackできる仕組みを築くことが重要です。それによって、一人でも多くのエンジニアがRoadmapを自分事として捉えて、Roadmapについて真剣に考えてくれることを目指しています。 OKRの設定に活用する 私たちはクォーターごと、つまり3ヶ月ごとにOKRを設定しています。OKRを3か月ごとに考えるのは計画性がないととても大変な作業ですし、OKRの設定に時間がかかってしまうと、設定と同時にすぐにまた次のOKRを考えなければならないといった悪循環となってしまいます。Roadmapで年間の計画が決まっていればOKRに設定しなければいけないオブジェクトの多くをRoadmapから転用することができるので、とてもスムーズに作成することができます。 運用について 当然ながら、Roadmapは掲げたままにしないこと、形骸化させないことが非常に重要です。そのために継続的に進捗を確認することやRoadmap自体をメンテナンスすることが重要です。これが完璧な運用方法というわけではないですし、将来変わることもあると思いますが、参考までにわたしたちの現在の運用方法をお話ししたいと思います。 基本的には以下のイテレーションでロードマップの作成とアップデートを行なっています。 12月にRoadmapのメジャーアップデートを行い(前年のRoadmapをリバイズして1年-3年の計画を作成する) その後はクォータ末に毎回マイナーアップデートを行う(3月、6月、9月) プログレスのチェックは月に1回各アクションアイテムのプログレスを確認しています。当然ながらプログレスの確認は必ずやったほうが良く、やりながら方向性をアジャストすることもできますし、この継続的な運用によってさらにVisionの浸透が強化されます。 Engineering Roadmapを運用する上で難しい点と工夫 ビジネスプライオリティの影響 1-3年間のRoadmapを立てて、着々と開発を進めても、ビジネス上のプライオリティが下がってしまったり、方向性が変わることはどうしても発生します。むしろ、そういう変化は受容できる仕組みにしなければ現実的な運用は厳しいと考えています。そのため、私たちは月1回のプログレスチェックでの方向性のアジャストや3か月ごとのマイナーアップデートを行って、なるべくフレキシブルにビジネスの要求に応えられる運用を目指しています。 アイテムが多くなりすぎる問題 どうしてもやりたいことを整理するとRoadmapに追加したい項目が多くなりがちになってしまいます。しかし、項目が多くなればなるほど、エンジニアをはじめとする現場のメンバーの理解を得ることが難しくなりますし、現実的には全てに手をつけられなくなってしまうリスクがあります。実際に私も「やらないことを決める」努力をして項目を減らす努力はしているのですが、まだまだ多い状態です。毎年Roadmapを洗練させていくなかで、少しずつ数を絞ってはいますが、実際に運用してみると「少し少ないかな」と思うくらいの方がいいと個人的には思います。 最後に 今回は少し具体的にRoadmapの内容や運用についてご説明させていただきました。本当はすべてのRoadmapを公開して、それぞれの狙いや振り返り、改善点などもお話することができるとよりイメージをお伝えしやすいのですが、まだまだ世に公開できていないものもありますので、それはまた来年末にとっておき、ご容赦いただけたら幸いです。Roadmapの設定と運用において、当たり前の内容ではあるのですが、せっかく作成したRoadmapを形骸化させないためには、議論を重ねて極力正確なものを作り、継続的に見直していくことがポイントとなります。そして、作成したものをそのままにせずに、いつでも誰でもアクセスできて、Feedbackを提供できる仕組みと雰囲気づくりをすることによって、血の通ったRoadmapを作成することができます。Roadmapは方向性を言語化することで、ビジネスとエンジニアリングの間の理解の差を埋めて、方向性の不確実性を減らすことができ、自信を持って開発し続けるために欠かせないツールだと考えています。ご一読くださったみなさんにとって、少しでも手助けになったら幸いです。
アバター
こんにちは。Mercari Corporate Products Teamのエンジニアの @yuki.watanabe です。 この記事は、 Mercari Advent Calendar 2024 の21日目の記事です。 はじめに 現在、内製の会計仕訳システムの開発に携わっています。このシステムには様々なバッチ処理が実装されているのですが、BigQueryへクエリしデータを抽出するためのバッチで誤検出の問題がありました。本記事ではこの問題に対して検討した複数のソリューションと結果的にどの方法を採用したのかについて紹介します。 バッチ処理の課題を解決する際の参考にしていただけると幸いです。 リコンサイルエラー検出のバッチについて 会計仕訳システムにおけるデータの流れ まず、会計仕訳システム(図のAccounting System)におけるデータの流れを紹介します。お客さまがメルカリやメルペイを使用した場合、取引内容に応じて様々なMicroservicesが処理を行い、金銭に関わるデータがある場合は、会計仕訳システムのPub/Subに送信します。会計仕訳システムではCloud Functionsでバリデーションを行い、Spannerのaccounting_dataテーブルへ登録します。 次に、各Microservicesは会計仕訳システムのPub/Subに送信済みのデータについて、会計仕訳システムのリコンサイル用APIへ送信します。このAPIは後述するリコンサイルと呼ばれる突合処理を行い、結果をSpannerのreconciliationテーブルへ登録するもので、Kubernetes上のgRPC ServerのAPIとして実装されています。 Spannerへ登録されたデータ(accounting_data、 reconciliation)は、Cloud ComposerとDataflowを用いて、BigQueryへ1日に1度差分を同期しています。 リコンサイルは会計データの確からしさを検証する仕組み リコンサイルとは、会計仕訳システムと会計データの送り元となるMicroservice間のデータの突合処理のことを指します。Microserviceはデータベースに登録した会計データをリクエストデータに含め、リコンサイル用APIへリクエストします。APIでは、リクエストデータと会計仕訳システムに登録された会計データ(accounting_data)を突合し、突合結果をreconciliationテーブルのstatusカラムに保持して登録します。このリコンサイルを通じて、Microservice側のデータと会計仕訳システム側のデータが一致していることを保証しています。 以下はreconciliationに登録されるstatusカラムの値のイメージです。 突合成功: status=’success’ 突合失敗: status=’failed’ リコンサイル検証用バッチでリコンサイルのエラーがないかを確認する 突合が失敗したデータについてはリコンサイルのエラーと考えられます。そこで、リコンサイルエラー検出用のバッチをCronJobを用いて実装しています。このCronJobでは1日に1度BigQueryへクエリし、リコンサイルエラーのデータを抽出します。エラーのデータが存在する場合は、Microservice Teamへ共有し、再度のリコンサイルAPIへのリクエストを依頼しています。 SpannerとBigQueryの同期タイムラグによる誤検出 しかし、上記のバッチには課題が存在しました。Spannerにはリアルタイムにリコンサイル結果が登録されていますが、バッチが参照しているBigQueryには1日に1度しか同期されません。このSpannerとBigQueryの同期タイムラグにより、バッチの実行結果には誤検出である偽陽性のデータが含まれていました。「Spannerには突合済みのデータが存在するが、BigQueryには未同期」のデータは本来は突合が成功していますが、バッチでは突合が失敗したデータとして検出されていました。 このため、バッチによってリコンサイルエラーとして抽出されたデータについて、エンジニアが「Spannerにクエリをして本当にリコンサイルのエラーがあるのかどうかを調べる」という手動の運用作業が発生していました。 会計仕訳システムでは、会計データを扱っているという特性上、毎月の月初に前月分のデータを確定する、いわゆる「締め」が必要になります。月末付近に発生したリコンサイルエラーは速やかに送り元であるMicroservice Teamにリコンサイル依頼をし、リコンサイルエラーを解消しなければなりません。しかし、上記の運用作業が発生する場合、リコンサイルエラーの検出から解消までに日をまたいでしまうこともあり、会計業務への影響が出てしまうこともありました。 これらの運用課題の解消のためには、「リコンサイルエラー検出のバッチの誤検出をゼロにする」ということが必要でした。 Spanner Data Boostの採用 検討したソリューション 運用課題の解消のため、複数のソリューションを検討しました。 1. StreamingでSpannerからBigQueryへ同期する方法 まず、Spannerに登録されたデータをStreamingでリアルタイムにBigQueryへ同期する方法を検討しました。Dataflowの Spanner change streams to BigQuery template などを利用し同期用のJobを作成することで、技術的には実現可能な方法ではあります。Streamingでリアルタイムに同期できると、上記以外の課題の解消にも役立てられるため、大きな恩恵を得られたでしょう。一方で、Stremingの同期用のJobを採用する場合は、同期の不具合がある場合にも備えなければなりません。例えば、同期用Jobが停止する、BigQueryへ二重でデータが登録される、BigQueryへの一部のデータの登録が失敗するなどが考えられます。こうした不具合が発生した場合には、手動運用でリカバリするか、もしくはリカバリ用のシステムの実装が必要になりますが、初期の実装とその後の運用まで含めた工数を考慮すると、既存の課題に対するソリューションとしては過大だと考え、採用を見送りました。 2. SpannerとBigQueryの同期頻度を増加する方法 次に、SpannerからBigQueryの同期頻度を増加する方法を検討しました。現状1日に1度行っている同期を2〜3回に増加させ、その後にリコンサイルエラー検出のバッチを実行する方法です。これまでの方法と比較し、偽陽性のデータを減らすことは可能だったかと思います。しかし、Spannerに登録されているBigQuery未連携のデータは多少なりとも存在するため、リコンサイルエラー検出のバッチの誤検出をゼロにすることには向かないと考え、採用を見送りました。 3. Spanner federated queriesとSpanner Data Boostを利用する方法 最終的に、Spannerの Data Boost を活用することにしました。上述の通り、リコンサイルエラー検出のバッチではBigQueryにクエリをしていますが、このクエリを修正し、BigQueryの Spanner federated queries の機能を使い、Spannerへのクエリもしています。Spannerへクエリする際に「Spannerには登録済みだがBigQueryには未同期のデータ」も併せて取得することで、BigQueryとSpannerのデータをどちらも考慮して、リコンサイルエラーのあるデータのみを抽出することができるようになっています。 重要な点としては、Spanner federated queriesを利用する際に、Data Boostを有効化するということです。Data BoostはSpannerのPrimary Instanceへ負荷を与えることなくSpannerへクエリすることができる非常に便利な機能です。リコンサイルエラー検出のバッチでは、Spannerへ登録された1日分のデータを取得しますが、1日分でもかなりのレコード数となるため、もしPrimary Instanceへクエリした場合はパフォーマンスへの影響は避けられません。そこで、Data Boostを有効化しこの問題を回避しています。 この方法を採用した理由は、「リコンサイルエラー検出のバッチの誤検出をゼロにする」という目的を達成可能であり、かつ実装工数の観点でも既存のクエリの改修の範囲で早急に実現できることが見込まれたためです。 BigQueryへのクエリ改修前後のサンプルコード リコンサイルエラー検出のバッチで使用しているBigQueryへのクエリの改修前後のサンプルコードを記載します。 改修前のクエリサンプル SELECT * FROM example_dataset.reconciliation WHERE status != 'success' まず、改修前のクエリではBigQueryのDatasetであるexample_datasetのreconciliationテーブルをFROM句に指定し、statusがsuccess以外のレコードを抽出していました。 改修後のクエリサンプル WITH spanner_reconciliation AS ( SELECT * FROM EXTERNAL_QUERY('spanner_connection_example', """ SELECT * FROM reconciliation WHERE created >= TIMESTAMP(CURRENT_DATE("Asia/Tokyo"), "Asia/Tokyo") """) ) SELECT * FROM example_dataset.reconciliation LEFT JOIN spanner_reconciliation ON reconciliation.id = spanner_reconciliation.id WHERE status != 'success' AND (spanner_reconciliation.id IS NULL OR spanner_reconciliation.status != 'success') 改修後のポイントは2点あります。1点目は、WITH句でEXTERNAL_QUERYの関数を利用している点です。第1引数にSpannerを指定したBigQueryのConnection IDを指定し、第2引数には、クエリ実行日に登録されたreconciliationテーブルのレコードを抽出するクエリを指定しています。 2点目は、メインのクエリのWHERE句の絞り込みです。FROM句にreconciliationテーブルを指定することは改修前と同様ですが、加えてWITH句で定義したspanner_reconciliationテーブルをLEFT JOINし、WHERE句で利用しています。これにより、「BigQueryのreconciliationテーブルのレコードでエラーが発生している」かつ「実行日に登録されたSpannerのreconciliationテーブルのレコードでエラーが発生している、またはレコードが存在しない」条件に該当したレコードのみを抽出できるようになっています。 まとめ 会計仕訳システムのリコンサイル検証用バッチには、SpannerとBigQueryの同期タイムラグによる誤検出の課題が存在しました。そこで、バッチで実行しているBigQueryへのクエリを改修し、Spanner federated queriesとData Boostを利用しSpannerへもクエリすることで、BigQueryへ未同期のデータも抽出するようにし、同期タイムラグによる課題を解決しました。 本記事執筆時点で、改修版のリリースから2ヶ月程度が経過しています。改修前と比較し、月に10件程度発生していたSpannerの手動クエリによる運用作業がほぼゼロになるなどの効果が出ています。 また、この経験が早速別の機会にも役立ちました。あるバッチでSpannerへクエリする処理が、とある変更をきっかけにインデックスが効かなくなり大幅にパフォーマンスが悪化してしまう問題があったのですが、Spanner federated queriesとData Boostを利用することで、これを解決することができました。 今後もSpannerとBigQueryをしばらく使い続けることが予想されるため、Spanner federated queriesとData Boostを利用したアプローチを様々な場面で活用できるかと思います。 明日の記事は kimras さんです。引き続きお楽しみください。 参考資料 Data Boost Overview | Spanner | Google Cloud Connect to Spanner | BigQuery | Google Cloud Spanner federated queries | BigQuery | Google Cloud データ指向アプリケーションデザイン ―信頼性、拡張性、保守性の高い分散システム設計の原理 データエンジニアリングの基礎 ―データプロジェクトで失敗しないために
アバター
こんにちは。メルペイ Engineering Managerの @masamichi です。 この記事は、 Merpay & Mercoin Advent Calendar 2024 の記事です。 メルペイのモバイルチームでは現在、メルカリアプリ内に存在するメルペイの数百画面をSwiftUI/Jetpack Composeに移行するプロジェクトを推進しています。 この記事では、プロジェクトの経緯とその進め方について紹介します。 メルペイのリリース メルペイが搭載されたメルカリアプリがリリースされたのは2019年2月です。初期の開発は主に2018年に進めていましたが、当時はSwiftUIやJetpackComposeは発表されておらず、メルペイを含むメルカリアプリはUIKit/Android Viewで開発していました。 その後、2019年内にiOS/Android共に宣言的UIフレームワークであるSwiftUI/Jetpack Composeが発表されました。 GroundUP Appプロジェクト 一方2020年ごろから、母体となるメルカリアプリは長年の開発で積み重なってきた課題を解決するために コードベースを刷新するGroundUP Appプロジェクトが立ち上がりました。 GroundUP AppプロジェクトではSwiftUI/Jetpack Composeが全面採用され、2022年にリリースすることができました。 プロジェクトの詳細についてはコアメンバーの記事を参照ください。 メルカリの事業とエコシステムをいかにサステナブルなものにするか?かつてない大型プロジェクト「GroundUp App」の道程 これからメルカリのエンジニアリングはもっと面白くなる──iOS&Androidのテックリードが振り返る、すべてがGo Boldだった「GroundUp App」 メルペイの各種機能はモジュール化してある程度疎結合な状態でメルカリアプリに組み込んでいたため、新しいアプリにも組み込まれた状態を実現し、GroundUP Appプロジェクトと並行しながら新機能の開発を続けていきました。 メルペイの移行についてはこちらの記事を参照ください。 メルカリアプリのコードベースを置き換える GroundUP App プロジェクトの話 【書き起こし】Merpay iOSのGroundUP Appへの移行 – kenmaz【Merpay & Mercoin Tech Fest 2023】 DesignSystem メルカリではDesignSystemを定義し画面デザインおよび開発を行っています。メルカリでは2019年ごろから段階的にアプリへの導入を進めてきました。 特にGroundUPプロジェクト後の新しいアプリではSwiftUI/Jetpack ComposeのUIコンポーネントに刷新され、DesignSystemの全面的な採用によって画面のUI/UXの統一、ダークモード対応やアクセシビリティの向上が実現しました。 一方で、メルペイは前述の通り初期から開発してきたモジュールをそのまま新しいアプリに統合しました。それらの画面はUIKit/Android Viewで作られており、DesignSystemについてもUIKit/Android Viewの旧バージョンの実装となっていました。それによって、UI/UXの差分、ダークモード非対応、UIフレームワークが違うことによるアーキテクチャの差分といった課題がありました。 GroundUPプロジェクトで得た効果を最大限活用するため、2023年よりメルペイの既存画面のマイグレーションを進めるプロジェクトを開始しました。 Engineering ProjectsとGolden Path メルペイの数百画面をマイグレーションしていくには、長期的な取り組みが必要となります。メルペイではこういった長期的なエンジニアリングへの投資を推進するためにEngineering Projectsという枠組みを構築してきました。 Engineering Projectsの詳細については、VP of Engineeringの@keigowさんの記事をご覧ください。 メルペイのエンジニアリングへの投資を推進する仕組み また、現在メルカリグループ全体で標準的な技術スタックをGolden Pathとして定義し、開発効率の向上や技術資産の再利用を目指しています。DesignSystemで採用されているSwiftUI/Jetpack ComposeはGolden Pathとして定義されており、メルペイのマイグレーションプロジェクトは社内ではわかりやすくDesignSystemマイグレーションプロジェクトと呼んでいます。 グローバル展開を推進する開発組織をつくる——Meet Mercari’s Leaders:木村俊也(CTO) 実際にマイグレーションを実施していくには工数が必要であり、優先度の議論も必要となります。本プロジェクトを立ち上げるにあたってプロジェクト計画書を作成し、背景やアクション、体制やマイルストーンを明確にしました。Golden Pathのような会社の長期的な方針やEngineering Projectsの取り組みもあり、本プロジェクトをEngineering Projectsの1つとして推進しています。 プロジェクト体制と進め方 体制 メルペイでは、プログラムという大きなドメイン毎にプロダクトマネージャーやエンジニアを含めたクロスファンクショナルなチーム体制で事業を推進しています。 Design System マイグレーションを進めるには全プログラムのモバイルチーム、デザイナーとの連携が必要不可欠です。モバイルチームのリーダーとデザイナーで隔週の定例ミーティングをセットし、進捗やブロッカーの共有、マイルストーンの設定を定期的に行っています。 プロジェクトの立ち上げ期は週次で集まって進め方の型を作っていくのが良いと思いますが、ある程度固まってくると隔週がちょうどいいと感じています。 プロジェクトの情報を全て集めたページを社内のConfluenceに作っています。ここでプロジェクト計画書や体制図、機能ごとのSlackのコミュニケーションチャンネル、デザインや開発のノウハウ、QAのテストケース、機能のリリース状況、定例のミーティング議事録などプロジェクトに必要な情報を俯瞰して見ることができるようにしています。 Table of Contentsの一部抜粋 マイグレーションを進めるには工数とタイミングが重要です。プロダクトの新規施策を導入するタイミングで同時にマイグレーションできれば、効率的に進めることができます。一方、それだけでは変化の少ない機能のマイグレーションが進められません。また、緊急度の高い開発に関してはスピードを優先して一旦既存の画面への開発を行うケースもあります。既存の機能をそのまま移行するケース、プロダクトの新規施策を導入するタイミングで同時に移行するケース、どちらもバランスよく進められるように、各プログラムのデザイナーおよびモバイルチームリーダーと密に連携をとりながら進めています。 Screen Listと進捗の追跡 画面のマイグレーションをしていくにも、まずどれくらいの機能および画面があるのかをできるだけ正確に把握する必要があります。 メルペイではプロジェクトを立ち上げる際に全ての画面一覧をスプレッドシートにまとめたスクリーンリストを作成しました。これによって画面数や画面パターンを正確に把握したり、機能のオーナーシップを持つチームや開発・デザイン担当者を一元化して把握することができるようになりました。全ての画面にIDを振ってチーム内で対象とする画面の認識齟齬がないようにもしています。 各画面には以下のような進捗ステータスも付けてグラフにすることで、全体の進捗を視覚的に追跡できるようにしています。 TODO Design In Progress Design In Review Design Done Dev in Progress In QA Done 隔週の定例ミーティングでマイグレーションに取り組んでいる機能の進捗状況を更新しています。 各画面の状況を正確に把握することで、Engineering Projectsの定例ミーティングでもCTO, VPoEに対して透明性高く正確な情報をレポートすることができています。 Screen Listのシート一部抜粋 Strategy Sharing メルペイでは四半期の後半に一度Strategy Sharingという、次の四半期の施策の優先順位の決定や戦略・ロードマップのレビューを行い全社的に共有するタイミングを設けています。その中で、Engineering Projectsとしても次の四半期にターゲットとする機能と進捗率を定義し、全社的にプロジェクトのマイルストーンを共有しています。これによってエンジニアリング部門以外の方々でも進捗を把握することができ、全社的な認知を得ることができています。 現在の進捗状況 これまで2023年から2024年にかけて約2年間プロジェクトを推進してきましたが、2024年12月現在、Androidは約65%、iOSが約60%のマイグレーションを完了してリリースできています。開発中のものも含めると70% ~ 80%のマイグレーションが進んでいます。 Android Progress iOS Progress 今後もメルペイのモバイルエンジニアリングをアップデートすべく、チーム一丸となって100%を目指してプロジェクトを推進していきます。 終わりに この記事では、メルカリアプリ内に存在するメルペイの数百画面をSwiftUI/Jetpack Composeに移行するプロジェクトプロジェクトの経緯とその進め方について紹介しました。 プロジェクト規模も大きく長期的な取り組みで困難なことも多いですが、テックカンパニーとしてこのような取り組みに挑戦できていることはメルカリグループのエンジニアリング組織としての強みだと思います。 SwiftUI/Jetpack Composeへ移行を検討しているチーム、移行を進めているチームの皆さまの参考になれば幸いです。 次の記事は @kimuras さんです。引き続きお楽しみください。
アバター