メルペイでの3ヶ月間:非同期処理への移行とASDD開発の学び

はじめに

こんにちは。メルペイの Balance チームでインターンをしている @minato です。
この記事は、Merpay & Mercoin Advent Calendar 2025 の18日目の記事です。

2025年10月からメルペイでバックエンドのインターンを始め、3ヶ月目になります。
本記事では、インターン期間中に取り組んだタスク、得ることのできた学びについて紹介します。

取り組んだタスク

今回のインターンでは、債権データを管理するマイクロサービスにおいて、これまで同期的に行っていた債権残高の更新処理を非同期処理へと移行するタスクに取り組みました。
以下、非同期処理へ移行するに至った背景、設計、実装、負荷試験について説明していきます。

背景

メルペイでは現在、世界共通のプラットフォームを提供する「メルカリ グローバルアプリ 」プロジェクトが進められています。これまで国ごとに限定されていたプロダクトをグローバルに展開するにあたり、これまでになかった量の Transaction が集中することが想定され、負荷分散機能の検討・開発が必要となりました。

このような理由から、僕は、負荷分散を必要とする機能の一つである債権の作成機能の改善に取り組みました。
債権データの作成プロセスでは、債権を作成した後、該当する債務者の債権残高を更新する必要があります。なので、債権データの作成リクエストが連続して行われた際、特定の行やカラムに更新が集中してしまい、その箇所が hotspot となり、以下の問題を起こします。

  • 高負荷が特定の行に集中し、ロック競合が多発する
  • 更新待ちが発生し、スループットが低下する

この問題を解決するために、債権残高をリクエスト中に同期的に更新するのではなく、非同期で更新することにしました。非同期化することで、本来複数の Transaction で処理されるはずの複数の Request による債権残高の更新を、一つの Transaction でまとめて処理することができ、hotspot 問題を解決することができます。

設計

大まかな設計としては、債権残高の更新処理が発生した際に債権更新イベントテーブルへレコードを作成し、worker がそのテーブルを取得して Job Queue に push、Job Processor が Job Queue 内のアイテムに基づいてアカウントを更新するという流れとしました。

また、Job Queue については、債権更新イベントテーブルに、Job Queue に入っているかを表すフィールドを設けることで、DB を Job Queue として使用できる仕組みとしました。なぜイベントの管理を、外部サービスではなく DB で管理としたのかというと、外部サービスへの依存を避けられること、同様の処理を行うマイクロサービスにおいて DB で管理する構成で正常に機能しているためになります。
以下の画像は、非同期イベントをどのように処理しているのかについて表したものです。Workerは DB から処理されていないイベントレコードを取得し、server上のメモリ(以下の図の Queue) に保存します。その後、Job Processor が Queue 内のイベントをまとめて処理するという構成になります。

実装では、Worker、Queue、Job Processor のそれぞれについてカスタマイズしやすい struct を定義し、他の非同期処理において再利用できるようにしました。

実装

今回の実装では、社内で活用が推進されている ASDD(Agent Spec Driven Development) という方法を使用しました。ASDD とは、AI agent が仕様書を作成し、別agentがその仕様書に基づいて実装を進めるという手法です。詳しくは、pj-double: メルカリの開発生産性向上に向けた挑戦 — AI-Native化が辿り着いたASDDとプロセス変革の全貌 の記事をご覧ください。
アカウントの非同期化は、システム全体に関わる非常に大きな機能改善であったため、厳密に設計をDocsとして記録すること、レビューのしやすさを考慮し、仕様書自体は僕が手作業で作成したため、時間を消費してしまいましたが、その後の実装はあっという間にAIによって完了し、とても驚きました。
「もし社内の全員が ASDD を使いこなす未来が来たらどうなるのか」 と想像するだけでワクワクする体験でした。

負荷試験

負荷試験については、まだ行っていないため、これから取り組む予定のことを説明します。今回の負荷試験では、想定される平均リクエスト数と最大リクエスト数をテスト環境に対して送信し、システムがどの程度安定して処理できるかを確認する予定です。

また、債権残高の更新イベントが生成されてから、非同期的に実際に更新されるまでの処理時間についても計測しようと思います。というのも、債権残高の更新される速度は、債権の処理を行う上流のサービスにとって非常に重要なものになるためです。

テスト計画を立てる中、驚いたことは、負荷試験のためのリポジトリが整備されていたことです。専用のリポジトリが存在することで、誰でも容易に負荷試験シナリオを作成でき、さらに他者が同じ条件で負荷を再現できるため、検証の透明性と再現性を保つことができます。

インターンを通して得た学び

今回のインターンでは、日常の開発ではなかなか扱うことのできない規模のリクエスト数を処理するシステムの開発を経験することができ、技術的な学びはもちろん、コーディングに対する姿勢やプロダクトへの向き合い方について学びました。

コーディングへの取り組み方

インターンを通じて特に印象的だったのは、コメントを書かずとも、関数名、コードから類推がつき、後から読んだ際に一目で何をする処理なのかを理解できるレベルを目指す姿勢です。保守・運用のことまで考えたコードを書くことは普段の開発においても、心掛けているのですが、普段の自分を見つめ直し、コーディングへの姿勢を改めるきっかけになりました。
また、レビューの際に、ファイル内のコメント一つに対しても、どのような意図を持って書いたのか、また、そもそもそのファイルは何のためにあるのかといったことを指摘していただき、開発においてメタ的に考えることと具体的に考えることの両方を行うことの重要性を実感しました。

プロダクトのことを第一に考える開発

また、プロダクトに対する心構えについても、非常に刺激を受けました。チームのメンバー一人一人が、プロダクトの未来を考えながら、意思決定をしており、「将来的にこう使われる可能性があるから、今の内にこの手法を採用しておく」、「お客さま体験を良くするためにこのデータ構造はこうあるべき」といった議論が開発中だけではなく食事中にも頻繁に行われており、プロダクトへの愛とメルカリの Values の一つである Be a Pro の精神を強く感じました。

その他の活動と経験

インターン期間中は、開発のタスクにのみ取り組むのではなく、Qに1回開催されている社内イベントのTech Talkに登壇してみたり、他のチームの方と1on1やランチに行ったり、技術書やテックブログの読み合わせ会に参加したりと、いろいろな活動をしました。

ランチではバックエンドエンジニアの方だけではなく、セキュリティエンジニアの方とも話す機会をいただき、なぜその職種を選んだのかというキャリアの話から、最近の攻撃手法、日々の開発で意識すべきセキュリティ観点まで、バックエンドに閉じない視点を得ることができました。

また、Tech Talk や読み合わせ会に参加し、メルカリは、自発的に技術を学びに行ったり、技術について情報発信する環境が本当に整っていることを強く実感し、エンジニアにとって最高の環境だと思いました。

さいごに

インターンを通して、タスクに取り組む中で技術的な学びだけではなく、AIの活用方法、エンジニアとしての姿勢についても多くの学びを得ることができました。特に、エンジニアとしての姿勢は、これからの働き方に活かしていこうと思います。インターンとして参加できる残りの期間では、負荷試験までやり切り、可能であればリリースまで完了させたいと考えています。

今回の開発をリードしてくださった@yutaroさん、メンターの@kobaryoさん、そしてチームの皆さんを始め関わってくださった全ての方々、本当にありがとうございました!

明日の記事は@komatsuさんです。引き続きお楽しみください。

  • X
  • Facebook
  • linkedin
  • このエントリーをはてなブックマークに追加