メルカリエンジニアリングブログメルカリのエンジニアブログです。技術情報やエンジニアリング組織などの情報を日々発信していきます。https://engineering.mercari.com/ja© 2023 Mercari, Inc.blogTTLを利用したTiDBの効率的なインスタンス活用https://engineering.mercari.com/blog/entry/20260105-0fedbf6898/https://engineering.mercari.com/blog/entry/20260105-0fedbf6898/<p>MySQLに高い互換性を持つデータベースのTiDBには、古いデータを自動的に削除するTTL(Time to Live)の機能があります。本記事では、これを活用しコスト削減および障害耐性の向上を実現した事例を紹介します 背 [&hellip;]</p> Tue, 06 Jan 2026 07:00:46 GMT<p>MySQLに高い互換性を持つデータベースのTiDBには、古いデータを自動的に削除するTTL(Time to Live)の機能があります。本記事では、これを活用しコスト削減および障害耐性の向上を実現した事例を紹介します</p> <h2>背景</h2> <p>メルカリでは、商品に対してコメントを付与することができ、値引きの依頼だったり、さまざまなやり取りを行うことができます。このコメントが、例えば公序良俗に違反すると想定される場合、お客さまがそれを「通報」する機能がありますが、メルカリ内部でも自動的にこれらを検知する仕組みが古くから実装され、運用に利用されています。</p> <p>これらのコメントのデータは、歴史的経緯から古くはMySQL、現在はTiDBに格納されています。</p> <p>自動検知の機能自体は、事前定義されたあるキーワードがコメントに含まれるかどうか、いわゆる「全文検索」によって実現されています。</p> <pre><code class="language-sql">: 全文検索クエリのイメージ SELECT table1.* FROM table1 FORCE INDEX(idx_xxx_xxx) \ WHERE (column1 like &#039;&#039;%a%&#039;&#039; OR column1 like &#039;&#039;%b%&#039;&#039; … ) \ AND (column1 not like &#039;&#039;%c%&#039;&#039; OR column1 not like &#039;&#039;%d%&#039;&#039; … ) AND ... \ ORDER BY created DESC LIMIT 10;</code></pre> <p>またこの機能を動作させるには、多くのコメントに対し高頻度で多数の全文検索を実行する必要があり、このような使い方はもちろんMySQLにはあまり向かないものです。しかし、TiDBに移行した際にさらにこの負荷の影響が目立つようになりました。</p> <h2>初期の対応</h2> <p>TiDB User Day 2025でResource Controlの導入について紹介(<a href="https://static.pingcap.co.jp/files/2025/10/20165547/TiUD2025-Mercari.pdf">発表資料</a>)しており、詳細は割愛しますが、対象のクエリにヒント句でResource Groupを割当て、Burstableでない制約を加えることにより、TiKVでの優先度の制御と、TiDBでの多少の負荷の平滑化を実現していました。</p> <p>しかしこの負荷は、定期的に実行されかつ実行間隔が短いものでしたので、実行を完了しなければいけないタイムウィンドウの制約が比較的厳しいものであることから、平滑化による制御の効果が限定的でした。</p> <p>また、Resource Controlによる制御は、TiDBへのOLTPのワークロードに関しては比較的うまくいくものの、TiKVに対しては主に優先度でしか制御できず、リソースの隔離が不十分であるため、アプローチを変えて対応することにしました。</p> <h2>検討したアプローチ</h2> <p>問題のあるコメントの検知に関しては、コメントのデータが追記式で、後から編集できない(更新されない)というデータの性質上、ある程度最近のデータにのみクエリを発行できれば良いため、最近のデータだけをうまく処理するという方針を軸に、主に下記の観点から2点のアプローチを主に比較しました</p> <ul> <li>既存のTiDBからデータが簡易に生成できる</li> <li>コストが妥当</li> <li>クライアントの変更が少ない</li> </ul> <h3>a. TiDBからTiCDCへ流通させたデータにKSQLを発行する</h3> <figure id="attachment_36433" aria-describedby="caption-attachment-36433" style="width: 620px" class="wp-caption aligncenter"><img loading="lazy" src="https://storage.googleapis.com/prd-engineering-asset/2025/12/14ee415a-cleanshot-2025-12-25-at-16.47.27.png" alt="KSQL downstream" width="620" height="203" class="size-full wp-image-36433" srcset="https://storage.googleapis.com/prd-engineering-asset/2025/12/14ee415a-cleanshot-2025-12-25-at-16.47.27.png 620w, https://storage.googleapis.com/prd-engineering-asset/2025/12/14ee415a-cleanshot-2025-12-25-at-16.47.27-300x98.png 300w" sizes="(max-width: 620px) 100vw, 620px" /><figcaption id="caption-attachment-36433" class="wp-caption-text">TiCDCダウンストリームをKSQL DBとして活用</figcaption></figure> <p>TiDBには<a href="https://docs.pingcap.com/tidb/stable/ticdc-overview/">TiCDC</a>という、TiDBの実データを保存するTiKVの変更を取得し、ダウンストリームのデータベースに変更を伝播させるための、いわゆるCDC(Change Data Capture)の便利な機能があります。</p> <p>MySQLであれば、例えば<a href="https://github.com/debezium/debezium">debezium</a>といったソフトウェアがこれにあたり、TiCDCは変更イベントを以下のような様々なプロトコルで出力可能な柔軟さを持っています。</p> <ul> <li><a href="https://docs.pingcap.com/tidb/stable/ticdc-avro-protocol/">Avro</a></li> <li><a href="https://docs.pingcap.com/tidb/stable/ticdc-debezium/">Debezium</a></li> <li><a href="https://docs.pingcap.com/tidb/stable/ticdc-canal-json/">Canal-JSON</a></li> </ul> <p>このTiCDCを使って、Debezium形式で下流のKafkaにデータを流せば、<a href="https://www.confluent.io/blog/ksql-streaming-sql-for-apache-kafka/">KSQL</a>というストリームデータに対してSQLを発行できる機能を活用し、インメモリでクエリを発行できる可能性があります。</p> <p>KSQLの発行先であるKSQL DBのレコードはイミュータブルになります。<br /> また、KSQLは全文検索に特化した機能はありませんが、全文検索のクエリも発行できます。</p> <p>KSQL自体はすでに社内で活用/運用実績がある状況でした。</p> <h3>b. TTLにより一部のデータを保持する専用のTiDB Clusterを作成する</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/0faa6a8a-cleanshot-2025-12-25-at-16.45.40.png" alt="TTL downstream" /></p> <p>まずは、必要なテーブルのみを持つTiDB Clusterを作成します。<br /> TiCDCのレプリケーションタスクはChangefeedと呼ばれますが、テーブルの絞り込みには、このChangefeedの<a href="https://docs.pingcap.com/tidb/stable/ticdc-filter/#table-filter">Table Filter</a>という機能が活用できます。本ケースでは、コメントを保存するテーブルのみを保持するClusterを用意することになります。</p> <p>次に、このテーブルのデータを最新のデータのみ保持するようにします。<br /> TiDBには、Spanner同様、古いデータを自動的に削除する<a href="https://docs.pingcap.com/tidbcloud/time-to-live/">TTLの機能</a>があります。(MySQLにはありません)。この機能を活用すると最近のデータ以外は不要、という要件を簡易に達成できます。<br /> TTLの機能をCluster単体で利用した場合は、特に追加の考慮は必要ありませんが、TiCDCを経由したデータに対して利用した場合は、追加の考慮が必要です。<br /> すなわち、上流には存在する一方、下流には(TTLで削除されたため)存在しない、というデータが発生し、全てのイベントを伝播した場合、例えば、下流に存在しないデータに対しての削除が伝播し、エラーになるといったことが発生する可能性に対処する必要があります。<br /> この問題に対しては、Changefeedに<a href="https://docs.pingcap.com/tidb/stable/ticdc-filter/#event-filter-rules">Event Filter</a>という機能があり、特定のDELETEやUPDATEを無視する、といった設定が可能です。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/579860f1-cleanshot-2025-12-26-at-10.22.53.png" alt="" /></p> <pre><code class="language-sql">: 上記のデータセットでEvent Filterがないとエラーになるクエリの例 DELETE FROM comments WHERE id=1 /* すでに下流にはデータがない */; UPDATE comments SET comment=&quot;good morning&quot; where id=2 /* すでに下流にはデータがない */; </code></pre> <p>さらに、TTLパラメータにより、保持するデータがメモリに収まるようコントロールできれば、実質的に多くのケースでインメモリに近い形で処理可能です。</p> <p>なお、メルカリではTiDBのマネージドサービスである、TiDB Cloudを利用しており、ここで紹介したChangefeedのTable Filterおよび Event FilterはTiDB Cloudでも利用可能な機能です。</p> <h2>手法の決定</h2> <p>主にクライアントへの変更要求が少ないことから、TTLを用いる手法を選択することにしました。<br /> 万が一問題が発生した場合の切戻し、および暫定対処に関しても、クエリの発行先を、元のClusterに戻し、元のClusterのリソースを増やしてあげれば良いだけである、という便利さもあります。</p> <p>ここで改めて、隔離した一部のデータセットを作成し、そこにクエリを発行するというアプローチを取った利点を確認します。</p> <h3>利点1: 構築が容易/短期で可能</h3> <p>特筆すべきこととして、TTLが十分短ければ、初期構築が非常に迅速、かつ容易に行えることが言えます。</p> <p>既存のデータベースからある一部のデータを保持する別のデータベースを作成する際に、最も手間となるのは、既存のデータベースからのエクスポート/リストアから始まる初期データの作成ではないかと思います。</p> <p>しかし、Event Filterで所定の構成をすれば、TTLが短い場合、下流に空のデータベースを用意し、その時点からデータの複製を始めれば、保持すべきTTLを待つだけでデータの用意が完了します。</p> <h3>利点2: 障害単位が独立し個別調整の余地が出る</h3> <p>リソースを完全に独立させた専用のClusterを用意することで、障害の単位も分離でき、さらに、決まった使われ方をする独立したClusterに対して、積極的にリソースを活用するようチューニングが可能です。</p> <p>もちろん、設定が同一ではない、非対称なものが生まれるのは運用における認知負荷が上がるという問題はあり、構成変更により得られる効果とのバランスをチームで納得のいくものにする必要があります。</p> <h2>結果</h2> <p>変更前後のTiKVの負荷について示します。</p> <ul> <li> <p>A) 変更前のTiKVの負荷<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/5bc093c7-cleanshot-2025-10-30-at-12.11.18-1.png" alt="before" /></p> </li> <li> <p>B) 変更後のTiKVの負荷(ピーキーな負荷が隔離され利用量が平滑化された)<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2026/01/b86b34aa-cleanshot-2026-01-05-at-09.19.24@2x.png" alt="after" /></p> </li> <li> <p>C) 新規TiKV with TTLの負荷</p> </li> </ul> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2026/01/cd1a7078-cleanshot-2026-01-05-at-09.28.59.png" alt="new cluster" /></p> <p>このように負荷は別のClusterに分離され、上位のスペックで運用していた既存のClusterの負荷が減少し、かつ安定したことから、既存のClusterのノード数を減らすことができました。<br /> 新規に別のClusterを作成しているものの、トータルコストとしても削減でき、障害に関するリスクも分離できました。</p> <p>追記:BのグラフのCPU利用率はAより高くなっていますが、BのグラフをTiKVノードを減らした後に作成したためです。負荷が平滑化され、上下の激しい負荷が別Clusterに分離されたという文脈でご参照ください</p> <h2>まとめ</h2> <p>本記事では、Changefeedのフィルター機能(Table Filter, Event Filter)、TiDBのTTLを活用し、一部のデータに対して、高頻度で発行される全文検索の負荷を、独立したClusterに分離することでコスト削減をした事例を紹介しました。</p> <p>このアプローチでは、アプリケーションの変更および、構築、共に少ない変更ですみ、結果として早期に移行を完了することができました。</p> <p>メルカリでは、TiDBを利用していますが、初期導入のフェーズから、この記事のような継続的改善のフェーズに入っています。現在、様々な取り組みをしており、近々別の記事で紹介していく予定です。</p> <h2>関連/補足</h2> <p>本論とは少しそれる、細かな検討事項について補足します。</p> <ul> <li>TiDBの全文検索については、一部のリージョン、一部のサービスプロバイダ、一部のサービス提供形態で、試験的に利用できるものが提供されています。現状では選択肢には入りませんでした。また、商品に対するコメントについては、ある商品のコメントを表示する、コメントを書き込むというのが主な用途であり、そういった点からも元データとしてはこのデータのみを全文検索エンジンに格納するのは相応しくないと考えています。<br /> <a href="https://docs.pingcap.com/tidbcloud/vector-search-full-text-search-sql">https://docs.pingcap.com/tidbcloud/vector-search-full-text-search-sql</a></li> <li><a href="https://docs.pingcap.com/tidb/stable/tikv-in-memory-engine/">TiKV MVCC In-Memory Engine</a>については、B案の一部として試しました。今回のケースが、In Memory Engineに向いたケースではなく、「構成変更により得られる効果とのバランス」が悪かったため、今回は採用には至りませんでした。</li> </ul> AI-Nativeという選択 ー 正解のない時代に、メルカリが選んだ指針https://engineering.mercari.com/blog/entry/20251225-mercari-ai-native-company/https://engineering.mercari.com/blog/entry/20251225-mercari-ai-native-company/<p>こんにちは、メルカリCTOの木村(@kimuras)です。 今年は、ついに開催されたメルカリ主催のエンジニアイベント 「mercari GEARS 2025」 にて、Keynoteを担当しました。本記事では、その内容を改 [&hellip;]</p> Thu, 25 Dec 2025 11:00:55 GMT<p>こんにちは、メルカリCTOの木村(@kimuras)です。<br /> 今年は、ついに開催されたメルカリ主催のエンジニアイベント 「<a href="https://gears.mercari.com/" title="mercari GEARS 2025">mercari GEARS 2025</a>」 にて、Keynoteを担当しました。本記事では、その内容を改めて文章としてまとめ、皆さんにお伝えしたいと思います。メルカリがAI-Native Companyになることを宣言して以来、エンジニアリング組織に限らず、全社としてどのようにAI-Native化を進めていくのか、その指針についてお話しします。</p> <p>ご存じのとおり、AI活用による生産性向上は、まだまだ不確実性の高い領域です。さらに新たな技術が次々と登場することで、今日述べる内容も、近い将来には更新が必要になるかもしれません。しかし、だからこそ「現時点で我々がどこを目指し、どのような方向性で進んでいるのか」を言語化し、共有しておくことは、この変革期において非常に重要だと考えています。</p> <p>AI-Native化の推進は決して簡単ではありませんが、本記事の内容が、同じように挑戦されている皆様の一助になれば幸いです。</p> <h2>目次</h2> <ul> <li>全社をあげたAI-Native化とは</li> <li>現状: 確かな変化、しかし「まだ道半ば」</li> <li>発想の転換:「人間前提」から「AI前提」へ</li> <li>AI-Native化の前提条件:Knowledge Management</li> <li>AI-Centricな開発:Agent-Spec Driven Development(ASDD)</li> <li>全社的なAI化:AI Task Force</li> <li>未来のビジョン:AI化の先にあるもの</li> <li>今後に向けて</li> </ul> <h1>全社をあげたAI-Native化</h1> <blockquote> <p>「プロダクト、仕事のやり方、組織すべてをAI中心に再構築し、AIの進化を最大限に活用することで、これまでにない成果を目指す」</p> </blockquote> <p>(参考: <a href="https://careers.mercari.com/mercan/articles/53674/" title="新年度のテーマは「Back to Startup」と「AI-Native」。12周年を迎えたメルカリが目指すこれからの姿 | mercan (メルカン)">新年度のテーマは「Back to Startup」と「AI-Native」。12周年を迎えたメルカリが目指すこれからの姿 | mercan (メルカン)</a>)</p> <p>これは、社長である山田からの強い決意表明でした。<br /> AIへの対応が遅れれば、競争環境の中で後れを取るリスクは避けられません。同時に、私たち一人ひとりの成長においても、大きな変化が求められています。AIによる生産性向上は、従来の評価基準では実現が難しかった新たな価値創造を可能にします。変化を柔軟に受け入れ、成長し続けられる人にとって、これは自身の価値を飛躍的に引き上げる絶好の機会でもあります。</p> <p>そして、AI-Nativeな働き方を全社に浸透させるということは、単にAIツールを一律に導入することではありません。これまでの働き方をゼロベースで見直し、業務そのものをAIを前提とした形へ抜本的に進化させていくことが重要だと考えています。</p> <h1>現状: 確かな変化、しかし「まだ道半ば」</h1> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/14d23d0a--2025-12-23-11.03.05.png" alt="" /></p> <p>メルカリではすでに、社員の95%がAIツールを活用し、コード生成の約70%をAIが担うまでに進化しています。開発スピードも前年比で64%向上しました。数字だけを見れば、AIは確実に浸透しているように思えるかもしれません。</p> <p>しかし、私たちは現在の状態を“AI-Native”だとは捉えていません。むしろ、ここからこそ本当の変革が始まると考えています。</p> <p>最近よく議論されているように、「本当に生産性は飛躍的に向上したのか?」という問いに対して、私自身もまだ改善の余地は大きく残っていると感じています。そして、コーディングの生産性が向上しただけでは、組織全体の生産性は十分には高まりません。同じくらい重要なのは、コーディング以外の業務プロセスも含めて、AI前提の働き方に転換していくことです。</p> <h1>発想の転換:「人間前提」から「AI前提」へ</h1> <p>前述のとおり、AIを前提とした働き方を実現するためには、単にツールを導入するだけでは不十分です。私たちは、仕事に対する考え方そのものを根本から塗り替える必要があると考えています。</p> <p>これまで私たちが直面してきた限界は、「人が行うこと」を前提に組み立てられたプロセスや仕組みによって生じていました。AI-Nativeな働き方とは、そうした前提条件そのものを問い直し、業務を“人が実行するタスク”から“AIと人が最適に協働するタスク”へと再設計していくことにほかなりません。</p> <h2>これまでの限界</h2> <p>これまでの仕事や組織のデザインは、多くが“人間前提”で設計されてきました。</p> <p>つまり、人間の時間・集中力・処理能力といった制約を起点に、「その条件の中でどう成果を最大化するか」を軸に最適化されてきたのです。</p> <p>たとえば、1日8時間労働、週休2日、1チーム8名構成、週次の定例会議、こうした組織の“当たり前”はすべて、人間の能力と限界を前提として形づくられてきました。現在も一般的な働き方であり、私たち自身もその枠組みの中で働いています。</p> <p>しかし、AIを前提とした働き方とは本来どういう姿なのか。</p> <p>そこを抜本的に考え直し、AI活用を進めながら、これまでの常識を一つひとつ疑い、新しい標準をつくっていく必要があります。</p> <p>たとえば、</p> <ul> <li>1人が複数ロールを効率的にこなせるようになれば、チームは8人よりも少ない人数のほうが、むしろ高い成果を出せるかもしれません。</li> <li>AIによって単純作業が減り、人はより連続的で深い思考に集中できるようになると、脳の疲労はこれまで以上に高まる可能性があります。その場合、1日8時間労働ではなく、短時間で高集中の働き方のほうが高い生産性を生み出すことも考えられます。</li> </ul> <p>このように、AI前提の働き方は、従来の“当たり前”を根本から再設計することを意味します。</p> <h2>AI前提の世界観</h2> <p>では、AIを前提とした働き方とは、どのような発想の転換なのでしょうか。</p> <p>これまで新たなプロジェクトや事業を立ち上げる際には、前述のような「人間の限界」を前提として設計するのが一般的でした。特に人的リソースは最も大きな制約であり、どれだけ人を確保できるかが計画の起点になっていました。</p> <p>しかし、AIが前提となる世界では、この出発点が根本的に変わります。</p> <p>仕事や組織のあり方を、“人の限界から逆算する”のではなく、ビジョンや提供したい価値から逆算して設計することが重要になります。</p> <p>すなわち、</p> <p>「何を実現し、どんな価値をお客さまに届けたいのか」</p> <p>という問いからスタートし、その実現のためにAIをワークフローへ組み込み、最適な仕組み・チーム構成・役割・データのあり方などを再設計していくという考え方です。AIはミッションを前進させる創造的なパートナーであり、チームの一員として存在します。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/b60bc67f--2025-12-23-11.04.56.png" alt="" /></p> <p>これこそが、私たちが目指す“AI-Native”の世界です。</p> <h1>AI-Native化の前提条件:Knowledge Management</h1> <h2>なぜKnowledge Managementが重要なのか</h2> <p>私たちもAI Coding Assistantをはじめ、さまざまなAIツールを導入してきましたが、当初想定していたほど生産性が向上しないという課題がありました。</p> <p>その背景には、AI化を進める前に取り組むべき、より本質的な要素が欠けていたことがあります。それが、適切な情報、すなわちコンテクストの整備です。</p> <p>特にAI Agentは、十分なコンテクストが与えられなければ期待どおりに機能しません。単純な指示だけでは精度の高い成果物を生成できず、手直しが何度も発生し、かえって時間がかかってしまうことすらあります。最終的には品質が低いアウトプットになってしまうケースも少なくありません。だからこそ、タスクに関わる背景知識、関連するレギュレーション、過去の意思決定プロセスや議論のログなどをコンテクストとして適切に提供することが不可欠です。</p> <p>そうすることでAI Agentは、状況や歴史的文脈、そしてAI Agent利用者が求めるゴールを正確に理解し、より期待に近い成果物を生成できるようになります。結果として、AIを効果的かつ継続的に活用できる環境が実現します。</p> <h2>どのようなコンテクストが必要か</h2> <p>必要となるコンテクストは、AI Agentに解かせたい課題によって大きく異なります。したがって、「この情報さえあればよい」という万能のテンプレートは存在しません。ただし、コーディングを例に挙げると、以下のような情報は特に有効です。</p> <ul> <li>Microservicesの依存関係</li> <li>コーディング規約</li> <li>設計書</li> <li>関連する開発における過去の意思決定ログ</li> <li>コードレビューでの議論内容</li> </ul> <p>これらのコンテクストをAI Agentに提供することで、依存関係を正しく考慮しながら、これまでの方針や意思決定と整合性のある設計やコード生成が可能になります。</p> <h2>最も重要なコンテクスト:意思決定情報</h2> <p>私たちが最も重要だと考えるコンテクストの一つが、意思決定情報です。</p> <p>意思決定に関するコンテクストは本来きわめて重要ですが、実際には十分に整理されていないケースが多く見られます。SlackやGitHub、ミーティングメモなど複数のツールに議論が散在し、必要な情報を必要なときに取り出すことが困難になっているのが現状です。会議で決まった内容が適切に共有されず、後から意思決定の経緯をたどれない場合も少なくありません。</p> <p>当社でもDesign Docにアーキテクチャや意思決定事項をまとめていますが、その判断に至るまでの議論は依然としてさまざまなツールに分散しています。その結果、「なぜその判断に至ったのか」という重要な背景が抜け落ちるリスクがあり、散在した情報を後から統合するには非常に大きな手間がかかります。</p> <p>しかし、こうした意思決定情報はプロジェクトを適切に進めるうえで不可欠であり、AI Agentにとっても極めて重要なコンテクストです。AI Agentは、今後コーディングだけでなく、仕様書作成、デザイン、QA、リーガルチェック、セキュリティチェックなど、より広範な領域で活用されるようになります。その際に必要なのは、次のような情報です。</p> <ul> <li>このプロジェクトの目的・狙いは何か</li> <li>何が許容でき、何が許容できないのか</li> <li>過去にどのような議論があり、何を重視して意思決定してきたのか</li> </ul> <p>AI Agentがこうした背景を理解しているかどうかで、アウトプットの品質は大きく変わります。適切な意思決定コンテクストが提供されれば、AI Agentは状況を正確に把握し、より高品質で一貫性のある成果を生成できるようになります。</p> <p>この後に紹介するAI Agent Spec Driven(ASDD)でもSpecを決定した議論を録音しておき、それをコンテクストとしてSpecに提供することで、より高性能にAI Agentを活用できると紹介されています。</p> <blockquote> <p>最初のSpecだけでは表現しきれていなかった背景やニュアンスが、レビュー時の対話には多く含まれています。この文字起こしログをCoding Agentに読ませてSpecを改善させることで、当初の記述では表現できていなかった文脈や設計の抜け漏れを補足し、より精度の高いSpecへと昇華させることができます。</p> </blockquote> <p>(参考: <a href="https://engineering.mercari.com/blog/entry/20251209-d0de07214d/" title="Agent Specで小さく素早く回すメルカリモバイル開発現場">Agent Specで小さく素早く回すメルカリモバイル開発現場</a>)</p> <h2>Knowledge Managementへの取り組み</h2> <p>現実には、こうした情報は十分に整理されておらず、多くが暗黙知のまま埋もれてしまっています。そこで私たちは、議論の記録や意思決定をできる限り構造化し、いつでもコンテクストとして活用できる状態にするため、Knowledge Managementの強化に取り組んでいます。</p> <p>AIを前提とした働き方をつくると同時に、情報管理のあり方そのものを抜本的に見直しているところです。これが実現すれば、トップダウンの意思決定と、現場からの学びや提案といったボトムアップの動きがよりスムーズにつながり、全社としての意思決定速度も大きく向上します。</p> <p>こうしたAI-Readable(本稿では、データが整備されており、AIエージェントが容易にコンテクストとして参照できる状態のデータを「AI-Readable」と定義します)なデータマネジメントを実現するため、私たちは現在、Notionに情報を極力集約する取り組みを進めています。</p> <p>目的は二つあります。</p> <ul> <li>散在している情報を一つの文書管理基盤に集約し、必要なときにコンテクストを簡単に取り出せる状態にすること</li> <li>情報の残し方そのものを再設計し、AIによる議事録作成の標準化や、多様な情報資産の構造化などを通じて、AIが扱いやすいナレッジ体系をゼロから構築すること</li> </ul> <p>これにより、人間にとってもAI Agentにとっても理解しやすく再利用しやすい組織の記憶をつくることを目指しています。</p> <blockquote> <p>この方向性を元に、現在、メルカリはNotionをCentral Knowledge Baseとして位置付け、ナレッジの中央管理型への移行を進めています。本記事の主旨と離れるので、細かくは記載しませんが、ツール選定に関しては、フロー情報(議事録など、メンテしない情報)とストック情報の両方に強いという点や、AIとの親和性の高さが大きなポイントでした</p> </blockquote> <p>(参考: <a href="https://engineering.mercari.com/blog/entry/20251213-96e00d1d91/" title="メルカリが、AI時代にナレッジマネジメントに投資したわけ">メルカリが、AI時代にナレッジマネジメントに投資したわけ</a>)</p> <h1>AI-Centricな開発:Agent-Spec Driven Development(ASDD)</h1> <h2>現状の課題</h2> <p>私たちはすでに、ほぼ100%のエンジニアがAI Coding Assistantを活用しており、コード生成量も60%以上増加しています。しかし、冒頭でも述べたように、これはあくまでスタート地点にすぎません。</p> <p>本質的にAIを活用するためには、開発プロセス全体をゼロベースで見直し、AIを前提とした開発プロセスへと再構築する必要があります。AI Coding Assistantの活用を進める中で、利用率は大きく向上したものの、以下のような課題が明らかになりました。</p> <h3>プロンプト品質のばらつき</h3> <p>レギュレーションがないため、人によってプロンプトの質が大きく異なり、短時間で高品質なコードを生成できるケースがある一方、指示の反復が必要で、結果としてAIなしより遅くなるケースもありました。</p> <h3>コンテクスト収集の困難さ</h3> <p>プロンプトの書き方を理解していても、必要なコンテクストを正確に収集できず、適切な情報量をAIに与えられないことが多く発生しました。</p> <h3>生成コード品質のばらつき</h3> <p>一部ではレビューしやすい高品質なコードが生成される一方で、品質が低くレビューが困難だったり、バグの温床になりやすいコードが出力されるケースも見られました。</p> <h3>使用ツールのばらつき</h3> <p>当初はCursor を全社導入しましたが、その後Claude Codeなど新たなCoding Assistantが登場し、エンジニアによって利用ツールが異なる状況が生まれました。そのため、ベストプラクティスを集約し、共有することが難しくなりました。</p> <p>これらの課題の根底にあるのは、AIの使い方に関する共通レギュレーションが存在しないことです。そのため、チームや個人によってアウトプットの品質が大きくばらついてしまう状況が生まれていました。</p> <h2>目指す開発プロセス</h2> <p>私たちが目指す開発プロセスは、先に挙げた課題を解決したうえで、すべての開発プロセスにAIのポテンシャルが最大限活かされている状態です。</p> <p>具体的には、次のような姿を想定しています。</p> <ul> <li>コーディングだけでなく、スペック作成、デザイン、コードレビュー、QA/テストなど、あらゆる工程でAI Agentが活用され、開発全体が最適化されていること</li> <li>各プロセスに必要なコンテクストが適切に整理・提供され、AI Agentが効率よくタスクを遂行できること</li> <li>前述の課題が解消され、統一された開発プロセスとして標準化され、すべてのエンジニアが安定してAgentic Codingを実践できること</li> </ul> <p>これらを実現することで、特定のツールに依存しない、共通化されたAgentic Codingのワークフローが全エンジニアに浸透している状態をゴールとしています。</p> <h2>Doubleプロジェクト:ASDDの実現</h2> <p>私たちが現在進めているのが、DoubleプロジェクトにおけるAgent-Spec Driven Development(ASDD)です。「Double」という名称は、生産性を“2倍にする”というプロジェクトの目的に由来しています。</p> <p>ASDDは、AI Agentに必要なコンテクストを正しく与え、適切なプロンプトで誰でも開発を進められるようにするための、AIフレンドリーな仕様フォーマットを整備する取り組みです。</p> <p>そしてこれは、単なる仕様書ではありません。AI Agentやエンジニアが実際にコードを書けるレベルまで落とし込むための、実装指向の設計書を作成するためのテンプレートです。抽象的なアーキテクチャ設計ではなく、既存のコードベースやプロジェクトの流儀に完全にフィットした形で、以下のような具体要素を明確に定義します。</p> <ul> <li>API定義</li> <li>データモデル</li> <li>DBスキーマ</li> <li>処理フロー</li> <li>テストシナリオ</li> <li>具体的な実装手順(TODO)</li> <li>マイクロサービス間の依存関係</li> </ul> <p>このテンプレートの目的は、「誰が・どのタスクを・どのファイルで・どのコードスタイルで実装するか」を明確にし、誰がAI Agentを使っても、同じ品質・同じ規約で実装できる状態をつくることです。言い換えれば、ASDDはチーム全体の実装プロセスを“再現可能な工程”にするための詳細仕様テンプレートです。</p> <p>また、AI Agentが正確にコーディングできるよう、タスクの粒度を小さく保つなどの工夫もテンプレートに組み込んでいます。</p> <p>(参考: <a href="https://engineering.mercari.com/blog/entry/20251209-d0de07214d/" title="Agent Specで小さく素早く回すメルカリモバイル開発現場">Agent Specで小さく素早く回すメルカリモバイル開発現場</a>)</p> <h2>ASDDの活用範囲</h2> <p>ASDDのポイントは、コーディングだけのSpecではないということです。このSpecは全プロセスでAgentをフルに活用するためのベースとなります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/373833e0--2025-12-23-11.25.17.png" alt="" /></p> <p><strong>開発領域:</strong></p> <ul> <li>バックエンド開発</li> <li>フロントエンド開発</li> <li>モバイル開発</li> </ul> <p><strong>品質保証:</strong></p> <ul> <li>QAのテストケース自動生成</li> <li>AI Review</li> </ul> <p><strong>その他の領域:</strong></p> <ul> <li>カスタマーサポートのスペック調査</li> <li>リスクマネジメント</li> <li>コンプライアンスチェック</li> </ul> <h2>PJ Aurora:UI自動生成</h2> <p>さらに、このAgent Specは、UIを自動生成するためのAI Agentの仕様としても活用されます。UI向けのAI Agentは「PJ Aurora」というプロジェクトとして開発が進められています。</p> <p>Auroraは、プロンプトを与えるだけで、内製のデザインシステムを活用しながらUIを自動生成できる、非常に先進的な取り組みです。デザインの一貫性を保ちつつ、UI作成にかかる工数を大幅に削減できる点が大きな特徴です。</p> <p>また本プロジェクトでは、生成されたUIが社内のデザイン規約に準拠しているかを自動でチェックするAI Agentの開発も進行しています。これにより、Agenticなデザインプロセスによる効率化と品質担保の両立が期待されています。</p> <p>(参考: <a href="https://speakerdeck.com/mercari/mercari-gears-2025-pj-auroras-vision-and-automated-ui-quality-evaluation-agents" title="PJ-Auroraが描く未来と、UI品質評価を自動化するエージェント開発">PJ-Auroraが描く未来と、UI品質評価を自動化するエージェント開発</a>)</p> <h2>コンテクストの重要性</h2> <p>ここで改めて強調したい重要なポイントがあります。Agent Specを作成する際に与えるコンテクストの質は、極めて重要です。ASDDの品質は、このコンテクストの品質に大きく依存しており、適切なコンテクストが与えられなければ、期待通りに機能しません。逆に言えば、質の高いコンテクストが整理・提供されてはじめて、ASDDは本来の効果を発揮します。</p> <p>そのため、前述のとおり、このコンテクストを体系的に整備するためのKnowledge Managementプロジェクトも並行して進めています。これまでに作成された仕様書や意思決定の履歴、経営会議における意思決定情報などを整理・蓄積し、AI Agentにとっても人にとっても適切なコンテクストとして活用できる状態を整えています。</p> <blockquote> <p>メルカリのナレッジ基盤に最適化されたエージェントが自動的に一次情報にアクセスし、詳細な実装計画を生成します。また別のエージェントが、実装計画がサービスのコーディング規約に沿っているか、計画が所定のセキュリティ観点をクリアしているかなどのさまざまな調査を行います。この2つのエージェントが交互に修正と評価を繰り返し、最後に要件や仕様における不確実要素が残った場合には、開発者に追加の質問を行います。このように、「AIのコンテキストに適切な一次情報を与える」ことをプロセスによって保証しました。</p> </blockquote> <p>(参考: <a href="https://engineering.mercari.com/blog/entry/20251201-pj-double-towards-ai-native-development/" title="pj-double: メルカリの開発生産性向上に向けた挑戦 — AI-Native化が辿り着いたASDDとプロセス変革の全貌">pj-double: メルカリの開発生産性向上に向けた挑戦 — AI-Native化が辿り着いたASDDとプロセス変革の全貌</a>)</p> <h2>ASDDがもたらす価値(Customer-Centricを極める)</h2> <p>ASDDの本質的なポイントは、いわゆる直感的なVibe Codingではなく、Agenticなアプローチによって、誰でも再現性のある開発レギュレーションを実現できることにあります。私たちは社内に蓄積されたベストプラクティスを継続的に収集し、このAgent Specを育て続けることで、AI Agentのポテンシャルを最大限に引き出し続けられる開発環境を構築していきたいと考えています。</p> <p>これが実現すれば、エンジニアに限らず、すべての開発に関わるメンバーが次のような状態を手に入れることができます。</p> <ul> <li>より多くのタスクをこなせるようになる</li> <li>Trial &amp; Errorを高速に回せるようになる</li> <li>解決が難しいデザインや設計に、より多くの時間を使えるようになる</li> <li>お客さまに提供すべき新しい価値について、深く考える時間を確保できるようになる</li> </ul> <p>これまで私たちは「AI-Centric」という表現を使ってきましたが、人がよりお客さまの価値に集中できるようになるという意味において、これは本来私たちが大切にしてきたCustomer-Centricの究極の形であると、私は考えています。</p> <h1>全社的なAI化:AI Task Force</h1> <h2>開発プロセス以外にも最適化の必要性</h2> <p>ここまでは主に、開発プロセスを中心としたAI Agent活用によるプロセス改善についてお話ししてきました。これにより、プランニングからコーディング、リリースに至るまでのスピード向上が期待できます。</p> <p>しかし、実際のリリースまでを見渡すと、開発以外にも複数のプロセスが存在します。全体最適ができていなければ、どこかで必ず待ちが発生し、結果としてリリース全体のスピードは改善しません。<br /> その代表例が、開発プロセスに付随して発生する関連チームによる各種チェックプロセス です。例えば当社では、プロジェクトの内容に応じて、リリース前に以下のような確認を行い、新しく開発したサービスの安全性や品質を担保しています。</p> <ul> <li>リーガルレビュー</li> <li>PRレビュー</li> <li>セキュリティレビュー</li> <li>コンプライアンスチェック</li> </ul> <p>究極的には、全社レベルでのプロダクティビティが向上しなければ、AIを活用してもリリース速度は頭打ちになります。そのため、開発プロセスに限らず、全社のあらゆるワークフローをAgenticなワークフローへと進化させていく必要があります。</p> <p>理想的には、ASDDのSpecを活用し、これらの関連ワークフローについてもAI Agentが並列に処理を担うことで、大きなボトルネックを生むことなく、高速なリリースを実現できる状態です。その実現に向けて、私たちはコーディング以外の業務にも目を向け、Agenticなワークフローを横断的に構築していかなければならないと考えています。</p> <h2>PJ Socrates:Agenticな働き方の先駆け</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/2a7295a8--2025-12-23-11.23.03.png" alt="" /></p> <p>Agenticな開発を実現するうえで、その働き方の方向性をいち早く示してくれたのがPJ Socratesでした。Socratesは、いわばBI領域のAI Agentであり、社内に存在するさまざまなデータの取得や分析を支援する役割を担っています。</p> <p>これまで、施策のアイデア検討やビジネス戦略を考える際には、お客さまの動向や売れ行きなどを把握するために、DB上の複数テーブルをJoinした複雑なクエリを書く必要がありました。こうした作業には職人的な知識や経験が求められ、BIやAnalyticsチームのサポートなしにデータを取得することは難しいのが実情でした。Socratesの導入により、必要な分析内容を自然言語でAI Agentに依頼するだけで、データ取得から分析までを自動で行えるようになりました。</p> <p>当時はまだ「AI Agent」という考え方自体が明確でなかったこともあり、この体験は社内に大きなインパクトをもたらしました。AIは単なるタスクの自動化にとどまらず、データを取得し、分析し、さらには示唆を提示するところまで担える。つまり、AI Agentは自動化だけでなく、意思決定を含む高度なタスクも遂行できる存在であることが、具体的に示されたのです。</p> <p>そして、これまで人が担ってきたタスクを可能な限りAI Agentに委ね、人は本来より多くの時間を使うべき領域に集中していく。そのような働き方の方向性を、全社として実感する大きなきっかけとなりました。</p> <p>(参考: <a href="https://speakerdeck.com/mercari/llms-creating-an-analytics-platform-for-humans-and-data-analysis-ai-agents-to-collaborate" title="AI/LLMが拓くデータ活用の新時代:人間とデータ分析AI エージェントが協業する分析基盤へ">AI/LLMが拓くデータ活用の新時代:人間とデータ分析AI エージェントが協業する分析基盤へ</a>)</p> <h2>AI Task Forceの発足</h2> <p>このAgenticな働き方を全社で実現するために発足したのが、AI Task Forceです。AI Task Forceは2025年7月に活動を開始しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/14f62a9a--2025-12-23-11.27.32.png" alt="" /></p> <p>AI Task Forceでは、全社を33のドメインに分け、それぞれの領域のワークフローをAI化することを目的に、各ドメインへエンジニア1〜2名、PjM 1〜2名をアサインしました。さらに、各ドメインにはドメインオーナーを配置し、AI導入に向けた方向性や方針に関する意思決定を担っています。</p> <p>その結果、総勢約100名規模のメンバーがAI Task Forceに参画する体制となりました。ここでアサインされたエンジニアは、必ずしもAIのバックグラウンドを持つ人材に限定していません。あえてさまざまな領域のエンジニアを各部署から選抜し、互いに学び合いながら進める形をとっています。AI Task Force立ち上げ時の様子については、以下の記事でも紹介しています。記事中の写真からも分かるとおり、100名規模となると相当な体制です。</p> <p>活動開始当初は、AI-Native化という不確実性の高いテーマに取り組むことから、目的や目標の明確化・文書化を重視し、全社およびTask Force向けにAll Handsでの説明会を高頻度で実施することを強く意識して進めてきました。</p> <p>(参考: <a href="https://careers.mercari.com/mercan/articles/53708/" title="メルカリが本気で始めた「AI-Native」化。100名規模のタスクフォースが立ち上がるまで | mercan (メルカン)">メルカリが本気で始めた「AI-Native」化。100名規模のタスクフォースが立ち上がるまで | mercan (メルカン)</a>)</p> <h2>AI Task Forceの3つの責務</h2> <p>前述の参照記事にも記載されているとおり、AI Task Forceには大きく分けて3つの責務があります。</p> <ol> <li>各ドメインにおけるすべての業務の棚卸し</li> <li>棚卸し結果を踏まえた、将来のAI化に向けたビジョンおよびロードマップの策定</li> <li>ロードマップに基づくAI化の実行</li> </ol> <p>AI Task Forceは7月に発足し、現在までに33すべてのドメインにおける業務の棚卸しとロードマップ策定を完了しました。そして今まさに、各領域でAI Agent化に向けた具体的な開発フェーズに入っています。</p> <p>33領域を横断して業務を棚卸しした結果、定義されたワークフローの数は約4,000にも上りました。もちろん、これらすべてを一律にAI化するわけではありませんが、各ドメインで策定されたロードマップに基づき、中長期的な視点で段階的にAI化を推進していく計画です。</p> <h2>AI Task Forceからの学び</h2> <p>AI Task Forceはまだ道半ばではありますが、ここまでの取り組みを通じて、すでに多くの学びを得ることができました。ここでは、その中でも特に重要だと感じているポイントを振り返ります。</p> <h3>複数ロール間の連携強化</h3> <p>今回のAI Task Forceでは、従来の役割分担を超えた取り組みが生まれました。通常、Software Engineerはプロダクト開発を主に担い、リーガルやファイナンスといったコーポレート領域の業務に深く関わることは多くありません。しかし今回、そうした領域にエンジニアが入り込み、業務の棚卸しを行ったことで、次のような成果が生まれました。</p> <ul> <li>エンジニアのフレッシュな視点により、現場では当たり前になっていた冗長な業務に対して、効率化のアイデアを提示できた</li> <li>場合によっては、その業務自体を根本的に不要にできる可能性を示すことができた</li> <li>エンジニア自身にとっても、これまでにない経験となり、新しいチャレンジへのモチベーションにつながった</li> </ul> <p>このように、ロールを越えた協働は、業務改善だけでなく、人の成長や組織の活性化にも大きく寄与したと感じています。</p> <h3>不確実性との向き合い方</h3> <p>AI Task Forceでは、各領域ごとに業務の棚卸しを行い、AI-Native化に向けたビジョンやロードマップを策定してきました。しかし、無数に存在するタスクの中から大きな方針を定め、優先順位を決めることは決して簡単ではありません。実際、領域によっては、この意思決定に大きく悩み、時間を要したケースもありました。</p> <p>別のADVENT CALENDAR記事の中で、@panoramaさんは次のように述べています。</p> <blockquote> <p>しかし最終的には<br /> 「誰も正解がわからない。でも、手探りで失敗を繰り返したとしても進める価値がある」<br /> のがAI-Native化だと考え直しました。</p> </blockquote> <p>(参考: <a href="https://engineering.mercari.com/blog/entry/20251210-ai-tf-facing-uncertainty/" title="AI Task Forceで学んだ「不確実性との向き合い方」">AI Task Forceで学んだ「不確実性との向き合い方」</a>)</p> <p>私自身、メンバーに苦労をかけてしまったのではないかと反省する気持ちもあります。一方で、たとえ時間がかかったとしても、この不確実性と真正面から向き合い、じっくり考えて答えを出すプロセスを経たことが、AIを単なるツールとして導入するのではなく、働き方そのものを見直すきっかけになったと信じています。</p> <p>AI Task Forceをリードする立場として、私自身も不安を感じる場面は多くありましたが、悩みや不安を抱えながらも前に進んでくれたメンバーを、心から誇りに思っています。AI Task Forceのように不確実性の高いプロジェクトを進めることは容易ではありません。これから同様の取り組みを始める方には、ぜひ先ほどの記事も参考にしていただければと思います。</p> <h3>情報共有の難しさ</h3> <p>今回、33の領域をそれぞれ独立して進めたことで、各領域において迅速な意思決定が可能になった一方、反省点もありました。それは、各領域で得られたベストプラクティスやレトロスペクティブを、十分に共有できなかった点です。</p> <p>33領域すべてで定例ミーティングを行うのは現実的ではありませんが、もし領域間でスムーズに知見を共有できる仕組みがあれば、全体の効率はさらに高められたはずです。各領域のロードマップが出揃った今だからこそ、進捗や学びをよりオープンに共有し、全社としてより洗練された進め方を模索していきたいと考えています。</p> <p>同時に、この「情報共有の難しさ」もまた、Agentベースのアプローチによって効果的に解決できる課題の一つだと捉えています。</p> <p>(参考: <a href="https://www.youtube.com/watch?v=Sz2-eVnOtbQ" title="【メルカリ本気の全社AI化】100人で2000人を改革するAIタスクフォースの全貌 / すべての仕事をAI-Native化 / 鍵は">【メルカリ本気の全社AI化】100人で2000人を改革するAIタスクフォースの全貌 / すべての仕事をAI-Native化 / 鍵は</a>)</p> <h1>未来のビジョン:AI化の先にあるもの</h1> <h2>メルカリのミッションと現在地</h2> <p>ここまで、私たちはAI Task Forceを通じて全社のあらゆる業務をAI-Native化する取り組みを進めていること、そして並行して、特に開発プロセスにおいてはASDDを軸にしたAgent化を推進していることをお話ししてきました。では、このようにすべてをAI-Nativeへと進化させた先に、私たちは何を目指しているのでしょうか。</p> <p>メルカリは、「あらゆる価値を循環させ、あらゆる人の可能性を広げる」というミッションのもと、マーケットプレイス、フィンテックサービス、そして新たな事業を展開してきました。そしてこのミッションを真に実現するために、私たちにはまだ取り組むべきことが数多く残されています。</p> <h2>世界展開への道</h2> <p>近年、メルカリは<a href="https://about.mercari.com/press/news/articles/20250930_crossborder/" title="国際展開を本格的に進めており、台湾をはじめ、香港でのサービス提供も開始">国際展開を本格的に進めており、台湾をはじめ、香港でのサービス提供も開始</a>しました。私たちはマーケットプレイスを世界へと広げることで、より多くのモノの価値を循環させ、世界中の人々の可能性を広げていくというビジョンを掲げ、着実に国際展開の基盤を築いてきました。一方で、実際にサービスをグローバルに展開しようとすると、どれだけスケーラブルな仕組みを構築したとしても、各国ごとのローカライズ対応や日々の運用を考えたとき、現在の社員数のままでは限界があるという課題意識も持っていました。</p> <p>しかし、これまでに述べてきたように、AI Agentを最大限に活用し、一人ひとりの生産性や能力を拡張できれば、現実的な人数で世界展開を本当に実現することも決して夢ではない と、今では考えています。</p> <p>これはマーケットプレイスに限った話ではありません。フィンテック領域においても、グローバルに展開される金融サービスを提供し、私たち独自の与信モデルを世界に広げていくことで、世界中の人々の可能性をさらに広げられるはずだと考えています。</p> <p>@deeeetさんの記事にもあるとおり、現在メルカリでは、国際展開におけるサービスリリースと並行して、基盤開発にも非常に力を入れています。AI-Native化を進めるうえで、このようなアーキテクチャ整備を同時に行うことは、AI前提の開発プロセスをより効率的かつ持続可能なものにするために極めて重要です。</p> <blockquote> <p>先日の事業戦略発表会において共有しましたが,今後更にメルカリの海外展開を加速させるためにグローバル版のメルカリアプリを先日リリースしました.このアプリは現在提供してる日本版・アメリカ版のメルカリとは異なる新しいアプリであり,またアプリだけではなくその裏側のバックエンド基盤も新たに再構築しています.</p> </blockquote> <p>(参考: <a href="https://engineering.mercari.com/blog/entry/20251007-a09afcd49b/" title="グローバル展開にむけたアプリと基盤の再構築">グローバル展開にむけたアプリと基盤の再構築</a>)</p> <h2>新たな価値の創造</h2> <p>そして、世界中の人々の可能性をさらに広げていくためには、既存のサービスにとどまらず、これまでにない新たな価値を提供するサービスを生み出し続ける必要があります。</p> <p>これまでは、新規プロジェクトや新規事業を立ち上げる際、まず人的リソースが大きな制約となり、構想段階で断念せざるを得ないケースも少なくありませんでした。しかしこれからは、そうした前提となっていた限界を一度手放し、「実現したい価値」から発想し、次々と形にしていける世界へと変わっていくと考えています。</p> <p>その中で、新しい事業づくりにおけるAI Agent の活用は、今後ますます重要なテーマになります。すでに私たちは、並行してさまざまな実験的な取り組みを進めていますが、その成果については、来年のメイントピックとして改めてお伝えしたいと思います。ぜひ楽しみにしていてください。</p> <h2>Customer-Centricの究極形</h2> <p>そして、先ほども触れたとおり、AI-Centricな組織を築いていくことで、私たちはより多くの時間とエネルギーを、「お客さまに提供したい本質的な価値」に向けられるようになります。これは単なる効率化ではなく、Customer-Centricをさらに突き詰めていくための進化だと考えています。</p> <p>AIを前提に、より深くお客さまの価値に向き合う。それこそが、私たちが目指す「AI-Native Company」の姿です。</p> <h2>今後に向けて</h2> <p>メルカリでは、AI-Native Companyを目指すことを全社方針として明確に打ち出しました。その取り組みの中核として、ASDDを中心にAI Agentを前提とした開発プロセスの再構築を進めています。さらに、開発領域にとどまらず、全社のあらゆる業務をAI-Native化していくために AI Task Forceを立ち上げ、組織横断での変革にも着手しました。</p> <p>これらの取り組みは、まだ始まったばかりで道半ばです。しかし、不確実性と向き合いながらも一歩ずつ前進することで、人がより本質的な価値創出に集中できる組織を実現していきたいと考えています。</p> <p>これは私たちに限った話ではなく、社会全体としてもAI-Native化は確実に進みつつあります。すでに多くの業務をAI Agent化している方も多いでしょう。一方で、会社の規模が大きくなるほど、全社的にAIを本質的に浸透させる難易度は高まると、個人的には感じています。だからこそ、来年は社会にとって「AI Agentをいかに本質的に定着させるか」が問われる一年になると考えています。</p> <p>私たち自身も現在、AI Agentを効果的に活用するための実験を日々重ねていますが、来年はそれを一部の取り組みにとどめず、より広く、より深く浸透させていくフェーズに入ります。すでに始まってはいますが、改めて「AI-Native、すなわちAgentベースでまずプロセスを考える」という姿勢を、組織全体で言い続け、実践し続ける一年にしていきたいと思います。</p> <p>また、本稿では十分に触れられませんでしたが、来年はAIに関わるセキュリティ、ガバナンス、コンプライアンスも、より重要なテーマになっていくでしょう。これらは、AIを安全かつ持続的に活用し、全社に浸透させていくうえで欠かせない要素です。</p> <p>今後も、メルカリにおけるAI-Native化の進捗については、継続的に発信していきたいと考えています。最後までお読みいただき、ありがとうございました。そして今年も大変お世話になりました。どうぞ良いお年をお迎えください。</p> メルペイ インターンでの挑戦と学び:EGP Cardsと向き合った3ヶ月間https://engineering.mercari.com/blog/entry/20251225-building-egp-cards-at-merpay/https://engineering.mercari.com/blog/entry/20251225-building-egp-cards-at-merpay/<p>メルペイ インターンでの挑戦と学び:EGP Cardsと向き合った3ヶ月間 こんにちは。メルペイのGrowth Platformでフロントエンド・エンジニアとしてインターンをしている@Yusaku(宮田 優作)です。 こ [&hellip;]</p> Thu, 25 Dec 2025 10:00:57 GMT<h1>メルペイ インターンでの挑戦と学び:EGP Cardsと向き合った3ヶ月間</h1> <p>こんにちは。メルペイのGrowth Platformでフロントエンド・エンジニアとしてインターンをしている<a href="https://x.com/pkmiya__">@Yusaku</a>(宮田 優作)です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の25日目の記事です。<br /> 私は2025年10月からインターンを開始し、今月で3ヶ月目になりました(図1)。<br /> この記事では、インターン期間中に取り組んだタスクと得た学びについて紹介します。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/cf971c51-img_selfie-mercari-landscape.png" alt="図1:オフィスで撮影した私の写真" /><br /> 図1:オフィスで撮影した私の写真</p> <h2>チームについて</h2> <p>私が所属する Growth Platform Frontend チームは、Engagement Platform(通称EGP)という社内向けマーケティングツールを開発しています。<br /> このツールを使うことで、マーケターや プロジェクトマネージャーは、ポイントやクーポンなどのインセンティブ配布、ランディングページ(LP)の作成・公開、キャンペーン作成といった CRM業務をコーディング不要で簡単に行うことができます(図2)。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/20251225-building-egp-cards-at-merpay_fig-2.jpg" alt="図2:EGPのノーコードエディタ(EGP Content)" /><br /> 図2:EGPのノーコードエディタ(EGP Content)</p> <p>今回のインターンではEGP Cardsという機能の向上に取り組みました。EGP Cardsは、Web・iOS・Androidのクロスプラットフォームで利用できるカード型のコンポーネントを作成・公開する機能です。<br /> EGP Cardsは、いわゆるWebページのエディタ機能(EGP Pages)とは異なり、サーバがUIの構造を返却するというServer Driven UIアーキテクチャを採用しています。エディタ上で作成されたコンポーネントのコンテンツはJSONファイルとして保存され、各プラットフォームで共通のUIとして描画されます(図3)。<br /> Server Driven UIとEGP Cardsのアーキテクチャについては、同じくGrowth Platformチームの@togamiさん、@Stefanさんの記事をそれぞれご覧ください。</p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20241210-f7c478382a/">WYSIWYGウェブページビルダーを支える技術とServer Driven UIへの拡張</a></li> <li><a href="https://engineering.mercari.com/en/blog/entry/20251214-supercharging-user-engagement-how-mercari-is-using-server-driven-ui-to-reduce-time-to-market/">Supercharging User Engagement: How Mercari is Using Server-Driven UI to Reduce Time-to-Market</a></li> </ul> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/20251225-building-egp-cards-at-merpay_fig-3.jpg" alt="図3:EGP Cardsの編集画面" /><br /> 図3:EGP Cardsの編集画面</p> <h2>タスク1 &#8211; Dry Run for EGP Cards</h2> <h3>タスク概要:Dry Runとは?</h3> <p>Dry Runとは、変数を設定し、そこにデータを挿入することで、状態をエミュレートできる機能のことです。これにより、API呼び出しを記述したり実機で動作確認を行ったりする前に、コンテンツの挙動を確認することができます。<br /> このタスクでは、EGP CardsでDry Run機能を利用できるようにする実装を行いました(図4)。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/20251225-building-egp-cards-at-merpay_fig-4.jpg" alt="図4:今回実装した、EGP CardsのDry Run機能" /><br /> 図4:今回実装した、EGP CardsのDry Run機能</p> <h3>動作の仕組み</h3> <p>Dry Run機能は、以下の流れで動作します。</p> <ol> <li>利用者がDry Runを有効化し、フィールドにモックデータを入力する</li> <li>エディタが構造ツリーを再帰的に走査し、動的にJavaScriptのコードを評価して変数を値に置換する</li> <li>置換された値がキャンバス上に表示される</li> </ol> <h3>実装の流れ</h3> <p>以下の流れで実装を進めました。</p> <ol> <li>EGP Pagesに既に実装されていたDry Run機能について、コードリーディングやロギングを行い、実装ロジックを理解する</li> <li>EGP Cards特有の仕様を理解した上で、同様に利用できるDry Run機能を実装する</li> </ol> <p>コーディングの際には、EGP PagesとEGP Cardsで共通利用できそうな処理を探し、適度にファイルを切り出すことで、可読性・保守性を意識した実装を心がけました。</p> <h2>タスク2 &#8211; Content Agent Improvement for EGP Card</h2> <h3>背景:Content Agentの課題</h3> <p>EGPのノーコードエディタ(EGP Content)は、CardsのほかにもPagesやE-mailsなど、複数の種類のコンテンツを扱うことができます。<br /> また、EGP ContentにはAIエージェント(通称 Content Agent)が導入されており、対話を通じてコンテンツの要約や書き換えを行うことができます(図5)。<br /> 一方で、当時のContent Agentは、コンテンツ種別ごとのエディタ仕様を十分に理解していないという課題がありました。その結果、UIが崩れたコンテンツを生成してしまい、利用者の期待通りのアウトプットを提供できない可能性がありました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/20251225-building-egp-cards-at-merpay_fig-5.jpg" alt="図5:Content Agentの会話処理パイプライン" /><br /> 図5:Content Agentの会話処理パイプライン</p> <h3>実装の流れ</h3> <p>この課題を解決するため、以下の流れで実装を進めました。</p> <ol> <li>EGP Cardsの仕様やデータ構造を記述したプロンプトを作成する</li> <li>作成したプロンプトを、Content AgentのAgent Layerで条件付きで注入する</li> </ol> <p>EGP Cardsには、メディアクエリに対応していないことや、すべての要素がFlexで構成されていることなど、いくつかの制約があります。これらの制約や期待される出力をプロンプトに明示することで、Content AgentがCardsに適したコンテンツを生成できるようにしました(図6)。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/20251225-building-egp-cards-at-merpay_fig-6.jpg" alt="図6:EGP CardsでContent Agentを利用する様子" /><br /> 図6:EGP CardsでContent Agentを利用する様子</p> <h2>学んだこと</h2> <h3>チーム開発におけるアウトプットの出し方を学んだこと</h3> <p>Dry Run 機能の実装を通じて、チーム開発におけるアウトプットの出し方について多くの学びがありました。実装の正しさや機能の完成度だけでなく、Pull Request(PR)の切り方やレビューの受け方が、チーム全体の開発効率や生産性に大きく影響することを実感しました。<br /> 具体的には、バグ修正やリファクタリングであっても、PRのサイズが大きくなりすぎる場合やタスクのスコープ外に及ぶ場合には、別のPRとして切り出すことでレビューコストを抑えられることを学びました。また、コードやPRコメントには、なぜその実装にしたのか、どのような選択肢があり、何をしない判断をしたのかといった実装意図を明示することが重要です。これにより、レビュワーとの認識齟齬を防ぎ、建設的なレビューにつながると感じました。<br /> レビューを受けた際にも、指摘内容をすぐに修正として反映するのではなく、まずはレビュワーの意図を正しく理解することが重要です。場合によっては背景や前提条件をすり合わせた上で議論することで、より良い設計や実装にたどり着けることを学びました。これらの経験を通じて、個人としてコードを書く力だけでなく、チームで価値を届けるためのコミュニケーションや姿勢の重要性を強く認識しました。</p> <h3>メルカリカルチャーを実体験として理解できたこと</h3> <p>メルカリでは、情報の透明性やフラットな意思疎通によって、個人に大きな裁量が与えられていると言われることが多いです。実際にインターンを通じて、その点を強く実感しました。一方で、個人的に特に印象に残ったのは、英語を前提としたグローバルな開発環境です。<br /> これまで参加してきたインターンはすべて日本語環境だったため、ドキュメントやコミュニケーション、議論の場がすべて英語になる経験は非常に新鮮でした。グローバルなチームである以上、英語でのスムーズな意思疎通が求められることは理解していましたが、実際に業務の中でそれを実践し、議論や開発を進められたことに大きなやりがいを感じました。<br /> 実際の業務では、英語で書かれたREADMEや仕様書を読み込んだ上でPull Requestを作成し、設計意図や懸念点を英語で説明・議論しました。認識の齟齬を防ぐため、必要に応じて日本語での補足も行いながら、主体的にコミュニケーションを取ることを意識しました。この経験を通じて、メルカリのカルチャーは単なるスローガンではなく、日々の業務に根付いたものだと感じました。</p> <h3>技術的挑戦を通じて、学びの広がりを再認識できたこと</h3> <p>これまで私は、フロントエンドを主な技術領域として、インターンや個人開発に取り組んできました。そのため、フロントエンド領域における学びは、徐々に頭打ちになりつつあるのではないかと感じていました。<br /> しかし、EGPというツールに触れたことで、その考えは大きく変わりました。EGPは非常にインタラクティブでリッチなUIを持つだけでなく、その裏側では、ノーコードによるコンテンツの作成・配信の仕組みや、安全かつ効率的にAI Agentとやりとりを行うための設計など、複雑で奥深いロジックが支えられていることを知りました。<br /> タスクでは、ある程度抽象度のある状態で要件を受け取り、自分で具体的な実装タスクへ分解した上で設計・実装を進めました。また、EGPの使い方をキャッチアップしている最中に、イメージのプレビュー機能があることで利用者体験が向上するのではないかといった改善提案も行いました。<br /> さらに、Content Agentの改善では、今回のCards向け実装に閉じることなく、将来的にPagesやE-mailsなど他のContent typeにも展開しやすいよう、Typeごとにプロンプトを切り出す設計とし、可読性や拡張性を意識しました。エンジニアがプロダクトの将来を見据えて設計・実装することが、利用者体験の向上や業務効率化につながり、結果として事業価値に直結する点は、メルカリならではの魅力だと感じています。</p> <h2>おわりに</h2> <p>今回のインターンでは、EGP Cardsという機能の向上に取り組みました。インターンを通じて、技術的なスキルだけでなく、プロダクトの価値やチームとの関わり方を含めてエンジニアリングに向き合う姿勢を学ぶことができました。<br /> 実務を通して得たこれらの学びを、今後の開発や自身の成長につなげていきたいと考えています。最後までお読みいただきありがとうございました。</p> 「AIが学習しやすいナレッジ基盤」メルカリが全社で導入したNotion Architecture ver1.0https://engineering.mercari.com/blog/entry/20251224-mercariadventcalendar/https://engineering.mercari.com/blog/entry/20251224-mercariadventcalendar/<p>こんにちは!メルカリ Engineering Office チームの@kikoと@aisakaです。 この記事は、Mercari Advent Calendar 2025 の24日目の記事です。 先日の t-hiroi [&hellip;]</p> Wed, 24 Dec 2025 12:00:29 GMT<p>こんにちは!メルカリ Engineering Office チームの<a href="https://x.com/kiko_tw">@kiko</a>と<a href="https://x.com/ai_sakamoto921">@aisaka</a>です。</p> <p>この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a> の24日目の記事です。</p> <p>先日の t-hiroi の記事「<a href="https://engineering.mercari.com/blog/entry/20251213-96e00d1d91/">メルカリが、AI時代にナレッジマネジメントに投資したわけ</a>」では、メルカリが推進するナレッジマネジメント戦略について紹介がありました。その中で特に重要なのが、AI-Native な会社を実現するためには 「AI が正しく学習できるコンテクスト(文脈)を整える」ことが不可欠であるという点です。</p> <p>この考え方を軸に、メルカリでは全社を挙げてナレッジマネジメントを再設計してきました。<br /> また先日も発表がありましたが、ナレッジの蓄積・共有基盤としてNotion の全社導入を進めています。<br /> <a href="https://prtimes.jp/main/html/rd/p/000000064.000088144.html">メルカリがNotion 全社導入で「AI-Native」企業への変革を加速</a></p> <p>私たちは、このNotionを活用した全社ナレッジマネジメント基盤の構築・運用を担当しています。</p> <p>本記事では、メルカリがどのように Notion 上でナレッジを整理しているのか、そしてその基盤となる Notion Architecture ver1.0をご紹介します!</p> <h1>Notion Architectureとは</h1> <p>Notion Architectureは、Mercariグループにおける情報を<strong>「構造的に」「一貫して」「AIが活用しやすい形で」</strong>管理するための設計指針です。</p> <p>日々蓄積されるナレッジを会社の資産として活用するためには、散在しやすい社内の情報を極力構造化し、AIがコンテクストとして活用できる状態にすることが最も重要です。</p> <p>そのため Notion Architectureでは、組織ごとの柔軟性は確保しながらも、グループ全体では共通のデータベース構造・テンプレート・メタデータを用いて標準化を行いました。</p> <p>これにより次の状態を実現します:</p> <ul> <li>情報が散在せず、全社横断で検索・参照できる</li> <li>ドキュメント構造とメタデータをなるべく統一し、AIが学習しやすく</li> <li>メンバーが日々記録した情報が、自然とグループ全体の資産へと統合される</li> </ul> <h1>Notion上でのKnowledgeの置き場所</h1> <p>今後も引き続きアップデート予定ですが、まずver1.0として整理した構造を紹介します。</p> <h2>Mercari WorkspaceにおけるTeamspace設計</h2> <p>MercariのNotion Workspace配下には下記2種類のTeamspaceを設計しました。</p> <ul> <li>Group-wide Teamspace(全社員向け)→ グループ共通のナレッジ基盤を構成する領域</li> <li>Business / Domain Teamspace(事業・ドメインごと)→ 各チームが自律的に運用しながらナレッジを管理する領域<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/bf72b268--2025-12-22-16.49.44.png" alt="" /></li> </ul> <p>この2層構造を採用した背景には、</p> <ul> <li>事業・ドメインごとの文化に合わせた柔軟性</li> <li>全社では AI が学習しやすい統一された構造の維持</li> </ul> <p>という両立を実現したい、という狙いがあります。</p> <p>また、メルカリは約 2,100 名規模のグループ会社であり、Teamspace が増えすぎると管理コストが跳ね上がります。そのため、Teamspace の種類を最小限に絞ることも重要なポイントでした。</p> <h2>Teamspace間のKnowledge構造</h2> <p>各 Business / Domain Teamspace は、それぞれが独立運用できるよう設計しつつ、Group-wide Teamspace とリンクした状態で情報が流れる構造 になっています。</p> <p>こうすることで「柔軟な運用 × 集約されたナレッジ基盤」の両立を実現しています。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/2e92494a--2025-12-22-16.49.58.png" alt="" /></p> <p>では、それぞれのTeamspaceが具体的にどのような役割を担い、どう連携しているのかを説明していきます。</p> <p>以下では、Mercari Group Teamspaceと事業・ドメイン別Teamspaceそれぞれの目的と役割を整理しています。この2つのTeamspaceは補完し合い、全社のナレッジ基盤を支えています。</p> <h3>Mercari Group Teamspace</h3> <h4>目的</h4> <p>全社共通のナレッジ基盤として、Mercari Group Teamspaceは、グループ全体の中核となる複数の共通データベースを保持しハブとして機能することを目的としています。</p> <h4>役割</h4> <ul> <li>共通データベースを保持し、文書を一元管理する</li> <li>ドキュメントテンプレート、メタデータ設計、カテゴリ定義など「全社標準」を提供する</li> <li>事業・ドメイン別Teamspaceからのページ作成・参照を受け入れるハブとして機能する</li> </ul> <h3>事業・ドメイン別 Teamspace</h3> <h4>目的</h4> <p>事業やドメインごとの運用自由度を確保しつつ、Group-wide構造とリンクされた状態で情報を発信・管理することを目的としています。</p> <h4>役割</h4> <ul> <li>Group-wide共通DBへの新規ページ作成を行う(=情報を正しい構造に流す)</li> <li>Group-wide DBのViewを表示・参照し、自部門に関連する情報を整理・活用する</li> <li>必要に応じて事業独自のDBを保持し、ローカルでの文書管理を支援する</li> </ul> <h2>ナレッジはどのように管理されるのか</h2> <h3>標準化されたドキュメント構造とデータベース管理</h3> <h4>なぜデータベースとテンプレートによる標準化が必要なのか</h4> <p>おそらく多くの企業さんでも同様の課題を持っているかと思いますが、メルカリでは長年ナレッジが散在してしまう課題を抱えています。<br /> 例えば:</p> <ul> <li>プロジェクト資料の保存場所が統一されておらず、探すのに時間がかかる</li> <li>ドキュメントの構造や粒度が人によって異なり、AIが文脈を正しく理解できない</li> </ul> <p>こうした状態では記録した情報が活用されず、「書いたけど探せない」「探せないから新しく作る」という負のサイクルに陥ります。</p> <h4>メルカリのアプローチ:共通データベース × テンプレートによる自然な標準化</h4> <p>これを解決するため、Notion 上に作成されるすべてのドキュメントは、用途別に整理された共通データベース上で管理されます。</p> <p>各データベースには目的に応じたテンプレートを用意し、文書構造・メタデータ・プロパティの標準化をしています。</p> <p>こうすることにより 「記録すれば自然と整理される」ナレッジ基盤 の実現を目指しています。</p> <h3>Group-wide Databaseの設計思想</h3> <p>では、具体的にどのようなデータベース群を設計したのか紹介します。<br /> Group-wide Databaseは、Mercariグループ全体で共通化されたナレッジ基盤の中核を担うデータベース群です。<br /> 情報をタイプ別に整理し、横断検索・AI活用・全社統一運用を支える目的で設計されています。</p> <h4>設計思想</h4> <h5>目的</h5> <p>ドキュメント構造を統一し、あらゆる情報を一貫したフォーマットで再利用・検索できる状態を作る</p> <h5>方針</h5> <ul> <li>各DBは「用途単位」で分割する</li> <li>テンプレートを通じて文書構造・メタデータを標準化</li> <li>同じフォーマットで記録・共有できるようにする (AIが学習しやすくする)</li> </ul> <h3>テンプレート利用によるドキュメント標準化の推進</h3> <p>文書作成時は、各データベース(DB)に紐づいた新規ページ作成テンプレートを使用します。<br /> ※ ここでのテンプレートは、Notion公式マーケットプレースのものではなく、Mercari Workspace内で管理される共通テンプレートを指します。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/973a494c--2025-12-22-16.50.11.png" alt="" /></p> <p>テンプレートを利用することで、文書構成、メタデータ、プロパティを標準化し、全員が同じ形式と用語でナレッジを共有できるようにします。</p> <p>下記のような効果やメリットを見込んでいます。</p> <ul> <li>コラボレーションの推進</li> <li>プロセス変更への柔軟対応</li> <li>AI が学習しやすい文書構造</li> </ul> <h1>Notion Architecture ver1.0のチュートリアル</h1> <p>ここまで、Notion Architecture ver1.0の構造とその設計背景などを紹介してきました。</p> <p>メルカリがどのように Notion 上でナレッジを整理しているのか、Notionの画面を投影しながら説明します。これからNotionを導入する方は、参考程度に。既にNotionを導入している方からは、先駆者として「おいおいこういう事が起こるから気をつけると良いよ」といったアドバイスをいただけると嬉しいです!<br /> <iframe loading="lazy" title="メルカリNotion設計サンプル" width="580" height="326" src="https://www.youtube.com/embed/GnHDu1oRs58?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p> <h1>おわりに</h1> <p>本記事では、メルカリが全社で取り組むナレッジマネジメントの基盤として構築しているNotion Architecture ver1.0の概要とチュートリアルを紹介しました。</p> <p>Notion Architectureはまだ進化の途上であり、今後も組織の拡大や AI 活用の進展に合わせて継続的にアップデートしていきます!</p> <p>最後になりますが、こうした全社規模のプロジェクトには、様々な方によるサポート、貢献が不可欠です。今回のNotion導入も組織、チーム、会社を跨いだ多くの関係者の皆さんの協力のおかげで実現できました。</p> <p>特に、Notion導入を先にされていたSansanさんには、構造やテンプレート作成の際に大変お世話になりました。会社をまたいだ繋がりがあったのも本プロジェクトの醍醐味でした。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/0f393bd4--2025-12-22-16.50.30.png" alt="" /></p> <p>明日の記事はメルカリのCTO <a href="https://x.com/kimuras">@kimuras</a>さんによる最終記事です。引き続き最後までお楽しみください!</p> なぜ再発防止は、思ったように機能しないのか。メルカリのプロダクト開発でCAST分析が必要だった理由https://engineering.mercari.com/blog/entry/20251224-mercari-cast/https://engineering.mercari.com/blog/entry/20251224-mercari-cast/<p>こんにちは。メルカリで暗号資産交換業を提供しているメルコインCTOのpoohです。 この記事は、Mercari Advent Calendar 2025 の24日目の記事です。 メルカリグループ内で、CAST分析の取り組 [&hellip;]</p> Wed, 24 Dec 2025 10:00:19 GMT<p>こんにちは。メルカリで暗号資産交換業を提供しているメルコインCTOのpoohです。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a> の24日目の記事です。</p> <p>メルカリグループ内で、CAST分析の取り組みが広がりつつあります。メルコインではすでに導入しており、他のプロダクトチームでも検討や試行が始まっています。</p> <p>私はこれまで10年以上、SREとして障害対応やオンコール運用に関わってきました。 インフラ、SRE、データ基盤と領域は違っても、やってきたことは同じです。</p> <p>障害が起きる。 振り返る。 再発防止策を考える。</p> <p>それなりに真剣にやってきたはずなのに、 しばらくすると、よく似た構図の問題がまた起きる。</p> <p>この違和感を、長い間うまく説明できずにいました。</p> <h2>再発防止は「サボられている」わけではない</h2> <p>最初に強調しておきたいのは、 多くの現場で再発防止はちゃんと考えられている、ということです。</p> <ul> <li>レビューで拾えたはずだった</li> <li>QAの観点が足りなかった</li> <li>想定が甘かった</li> <li>次はもう少し丁寧にやろう</li> </ul> <p>どれも正論です。 真面目に向き合っている証拠でもあります。</p> <p>それでも、思ったように再発防止が効かない。</p> <p>これは意識や姿勢の問題ではない、と感じていました。</p> <h2>原因は、いつも「最後」に集まる</h2> <p>振り返りをすると、原因はどうしてもリリース直前や本番直前に寄りがちです。</p> <p>QAで見落とした。 本番確認が足りなかった。</p> <p>でも少し引いて見ると、違和感があります。</p> <p>仕様を考えた人がいて、 実装した人がいて、 レビューした人がいて、 QA判断をした人がいる。</p> <p>そのすべてを通り抜けて、 最後の工程だけを「根本原因」と呼ぶのは、無理がある。</p> <p>この違和感に、はっきり名前をつけてくれたのがCAST分析でした。</p> <h2>Google SREが直面している「エラーバジェット0」の世界</h2> <p>CAST分析に興味を持った大きなきっかけの一つが、 Google SREが「エラーバジェット0のシステム」を前提に考え始めている、という話(<a href="https://www.usenix.org/publications/loginonline/evolution-sre-google" title="The Evolution of SRE at Google">The Evolution of SRE at Google</a>)です。</p> <p>SREの世界では、 エラーバジェットを使ってスピードと安全性のバランスを取ります。</p> <p>一方で、Google自身が向き合っている現実もあります。</p> <p>金融、規制対応、社会インフラ。 サービスが落ちてはいけない。 失敗が許されない。 それでも人が設計し、人が運用する。</p> <blockquote> <p>today our products have losses that must never occur—error budgets of zero. The types of failures we need to prevent have evolved beyond what error budgets can effectively address.<br /> 現在では私たちの製品には絶対に発生してはならない損失、つまりエラーバジェットがゼロという状況が存在します。防止すべき障害の種類は、エラーバジェットが効果的に対処できる範囲を超えています。</p> </blockquote> <p>エラーバジェットを持てないシステムでは、 「失敗は起きる前提でうまく付き合う」というモデルだけでは足りない。</p> <p>その文脈で使われていたのが、CAST分析でした。</p> <h2>CAST分析は、問いを変える</h2> <p>CAST分析が前提にしている考え方は、とてもシンプルです。</p> <p>障害は、誰かの判断ミスで起きたのではない。 その判断を「正しい」と思わせる状況が積み重なった結果として起きる。</p> <p>だから問うべきなのは、</p> <p>なぜその判断をしたのか。 なぜその選択が合理的に見えたのか。</p> <p>結果を知ったあとに 「あの時こうすべきだった」と言うのは簡単ですが、 それは後知恵バイアスです。</p> <p>CAST分析は、 当時の情報、制約、前提の中での合理性を丁寧に扱います。</p> <h2>実際のCAST分析は、こう進む(抽象化した例)</h2> <p>ここから、実際にメルカリで行っているCAST分析の一部を、 特定につながらない形で紹介します。</p> <p>題材にしたのは、 <strong>既存のプロダクト施策に対する、ごく小さな運用変更</strong>でした。</p> <p>変更内容自体はシンプルで、 「影響範囲は広いが、作業内容は限定的」 と判断されていたものです。</p> <p>結果としては、 お客さまがサービスを正しく利用できない状態が一定時間続いてしまいました。</p> <h3>従来の振り返りだと、こうなる</h3> <p>普通に振り返ると、こう整理できます。</p> <ul> <li>必要な設定の一部が更新されていなかった</li> <li>レビューやQAで気づけなかった</li> <li>本番確認が十分ではなかった</li> </ul> <p>事実としては正しい。 でも、ここから出てくる対策はだいたい同じです。</p> <ul> <li>チェックリストを作る</li> <li>レビューを強化する</li> <li>QAを必須にする</li> </ul> <p>「もっとちゃんとやろう」で終わってしまう。</p> <h3>CAST分析で見えた構造</h3> <p>CAST分析では、原因を一点に絞りません。 システム全体の構造を見ます。</p> <p>このケースで見えてきたのは、こんな状況でした。</p> <ul> <li>過去は特定の設定だけ更新すれば問題なかった時代があった</li> <li>仕様は段階的に変化していたが、手順や認識は更新されていなかった</li> <li>PM・エンジニア・QAがそれぞれ異なる前提をもとに判断していた</li> <li>PMが自分で設定や状態を確認する手段を持っていなかった</li> <li>システム上は「仕様通りの挙動」が続き、監視では異常と判定されなかった</li> </ul> <p>誰かがサボったわけでも、 誰かが雑だったわけでもありません。</p> <p><strong>その構造の中にいれば、同じ判断をするのが自然</strong> という状態ができていました。</p> <h2>出てきた再発防止策が、まったく違う</h2> <p>ここが、CAST分析の一番大きな違いです。</p> <p>出てきた提言は、 「誰が気をつけるか」ではありません。</p> <ul> <li>不正な設定の組み合わせは、そもそも保存できないようにする</li> <li>状態を人に聞かなくても確認できる仕組みを用意する</li> <li>「施策が意図通りに機能しているか」を直接捉える指標を持つ</li> <li>深夜や休日の切り替えを原則避ける</li> <li>「軽微な変更」の定義を、工数ではなく失敗時の損失で考える</li> </ul> <p>すべて、<strong> 個人の注意力に依存しない改善</strong>です。これらの提言には従来実施してきたretrospectiveででたものもあります。網羅性ある振り返りができるというのがお気に入りです。</p> <h2>経営陣の視点で見たときの意味</h2> <p>このCAST分析が、 経営陣が読んだときに意味を持つ理由はここにあります。</p> <ul> <li>現場の誰かを責める話にならない</li> <li>なぜその判断が起きたのかを、構造として説明できる</li> <li>再発防止が「人の問題」ではなく「組織と仕組みの話」になる</li> <li>同じことが別のプロダクトでも起こりうると理解できる</li> </ul> <p>CAST分析は、 障害対応の手法というより、 <strong>プロダクト開発を安全にスケールさせるための考え方</strong> だと感じています。</p> <h2>再発防止を、本当に機能させたいなら</h2> <p>もし、</p> <p>振り返りをちゃんとやっているのに、 同じ構図の問題が繰り返される。 対策が注意喚起やチェックリストに寄っている。</p> <p>そんな感覚があるなら、 CAST分析は一度きちんと調べてみる価値があります。</p> <p>銀の弾丸ではありません。 でも、問いの立て方が変わります。</p> <p>それだけで、 再発防止は少しずつ、 本当に機能し始めます。</p> <p>CAST分析、 ぜひ一度触れてみてください。</p> <p>参考資料</p> <ul> <li><a href="https://www.youtube.com/watch?si=6wZF39NKlnl-BQCH&amp;v=2ChpAi5mcnI&amp;feature=youtu.be" title="チュートリアル">チュートリアル</a> <ul> <li>最初の15分ぐらいおすすめ</li> </ul> </li> <li><a href="https://psas.scripts.mit.edu/home/get_file9.php?name=CAST_Handbook_Japanese.pdf" title="ハンドブック">ハンドブック</a> <ul> <li>1-2章までは読みやすく、先を読みたくなります。</li> </ul> </li> </ul> <p>明日の記事は kimurasさんとYusakuさんです。引き続きお楽しみください。</p> Cursorでプログラミング言語を学び直す方法——AI駆動学習の4ステップhttps://engineering.mercari.com/blog/entry/20251223-ai-driven-learning/https://engineering.mercari.com/blog/entry/20251223-ai-driven-learning/<p>こんにちは。メルペイ Payment Mobileチームで iOSエンジニアをしている @kubomi です。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の23日目の [&hellip;]</p> Tue, 23 Dec 2025 10:00:27 GMT <p>こんにちは。メルペイ Payment Mobileチームで iOSエンジニアをしている <a href="https://x.com/kubomi____">@kubomi</a> です。</p> <p>この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の23日目の記事です。</p> <h2><strong>はじめに</strong></h2> <p>最近、自分の手でコードを書いていますか?</p> <p>AIに任せれば動くコードが出てくる「Vibe Coding」時代。便利な反面、私はこんな不安を感じるようになりました。</p> <p>「このコード、本当に理解できてる?」 「雰囲気で理解しているつもりになってるのでは?」</p> <p>同じ不安を感じているエンジニアの方々は、きっと多いのではないでしょうか。 </p> <p><strong>AIがコードを書いてくれる時代だからこそ、AIを使って「学ぶ」ことは重要ではないか</strong>——そう考えて私が試したのが、「AIにコードを書かせる」のではなく「AIに学習を設計させる」というアプローチです。</p> <p>この記事では、私が実践した「AI駆動学習」の方法を、具体的なプロンプトとともに紹介します。AIを使ってオーダーメイドの学習計画・教材を生成し、プログラミング言語を体系的に学び直す方法です。 </p> <p><strong>こんな方におすすめ:</strong></p> <ul> <li>AIに頼りきりで自分の理解度に不安がある </li> <li>新しい言語を体系的に学びたい </li> <li>学習を始めても途中で挫折しがち</li> </ul> <h2><strong>私の「AI駆動学習」サイクル</strong></h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/38e67f83-20250821_tech-talk-kubomi-ai-driven-learning_ai駆動の学習サイクル-.png" alt="ai-driven-learning-steps" /></p> <p>私が実践しているのは、学習のあらゆるステップでAIをフル活用する「AI駆動学習サイクル」です。このサイクルは4つのステップで構成されています。</p> <ul> <li><strong>Step 1: Plan — AIが学習プランを作る</strong><br /> 公式ドキュメントを参照し、体系的な学習計画を作成してもらいます。何を・いつ・どの順番で学ぶかが明確になります。 </li> <li><strong>Step 2: Learn — AIが教材を作って、人が学ぶ</strong><br /> 学習プランに沿って、解説・サンプルコード・演習課題を含む教材を生成してもらいます。そのまま実行できるファイル形式で出力させるのがポイントです。 </li> <li><strong>Step 3: Practice — 人が課題を実装して、AIがレビュー</strong><br /> 教材の演習課題を自分で実装し、AIにコードレビューしてもらいます。ここが一番学びが深まるステップです。 </li> <li><strong>Step 4: Track — AIが進捗管理</strong><br /> 学習プランからTODOリストを生成し、進捗を可視化します。次にやることが常に明確になり、継続しやすくなります。</li> </ul> <p>つまり、ほとんどAI任せの学習サイクルです。人間である私がやることは、教材を読んで理解し、課題を実装することだけ。それ以外の「プランニング」「教材作成」「レビュー」「進捗管理」はすべてAIに任せています。</p> <p>ここからは、各ステップの詳細と、実際に効果的だったプロンプトを共有していきます。</p> <h2><strong>準備するもの</strong></h2> <p>今回使用するのは <a href="https://cursor.com/"><strong>Cursor</strong></a> です。</p> <p>CursorはVS Codeベースのエディタで、AIとの対話機能が統合されています。なぜCursorを選んだかというと、<strong>学習プランの作成から、教材の生成、コードの実行、レビューまでがすべて1つの環境で完結する</strong>からです。</p> <p>ChatGPTなどのチャットUIでも学習プランは作れますが、生成されたコードをコピペして別のエディタで動かす手間が発生します。Cursorなら、AIが生成したファイルをそのまま保存し、その場で実行して動作確認できます。この「シームレスさ」が学習の継続には重要でした。</p> <p>Cursor以外でも、ClineなどのIDE統合型AIエージェントであれば同様のワークフローが実現できると思います。</p> <p>準備ができたら、空のディレクトリを1つ作成してCursorで開きましょう。このディレクトリに、学習プラン・教材・進捗管理ファイルをすべて格納していきます。</p> <h2><strong>Step 1: Plan — 学習プランを作る</strong></h2> <p>まずはCursorに学習計画を作ってもらいます。</p> <p><strong>使用したプロンプト:</strong></p> <pre><code>公式ドキュメント(https://docs.swift.org/swift-book/)を元に、 10日でSwiftをマスターする学習計画を作って、mdファイルで出力して。</code></pre> <p>ここでのポイントは2つあります。</p> <p><strong>ポイント1:公式ドキュメントを参照させること。</strong></p> <p>これによって、断片的ではなく体系的な学習プランを組み立ててくれます。公式ドキュメントを基準にすることで、正確で網羅的なカリキュラムが得られます。</p> <p><strong>ポイント2:期間を区切ること</strong></p> <p>「10日でマスターする」といった期間を指定することで、学習量や内容を自然に調整してくれます。無理のないペースで進められるプランが、あっという間に完成しました。</p> <h3><strong>生成された学習プランの例(抜粋)</strong></h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/2293fea0-image1.png" alt="leaning-plan" /></p> <p>これだけで、何を・いつ・どの順番で学ぶかが明確になります。</p> <h2><strong>Step 2: Learn — 教材を作る</strong></h2> <p>次に、先ほど作った学習プランをもとに、日ごとの教材をCursorに作らせます。</p> <p><strong>使用したプロンプト:</strong></p> <pre><code>学習プランを元に、各日の教材を作って。 各日ごとにディレクトリを作成して、その中に解説・サンプルコード・演習課題を含む実行可能な .swift ファイルを含めて。</code></pre> <p>このプロンプトのポイントは2つあります。</p> <p><strong>ポイント1:日ごとにディレクトリを分ける</strong></p> <p>日ごとにディレクトリを分けることで、学習プランと教材が対応し、管理しやすくなります。</p> <p><strong>ポイント2:実行可能なファイル形式で出力させること</strong></p> <p>これによって教材が「ただの読み物」で終わらないのです。生成されたファイルをそのまま実行して試せる。すぐに手を動かして学習できる。ちょうど<strong>教材とIDEが合体したような体験</strong>になりました。</p> <h3><strong>生成されるディレクトリ構成</strong></h3> <pre><code>swift-learning/ ├── README.md(学習プラン) ├── Day1/ │ └── 01_Basics.swift ├── Day2/ │ └── 02_ControlFlow.swift ├── Day3/ │ └── 03_Optionals.swift ...</code></pre> <h3><strong>生成された教材ファイルの例(抜粋)</strong></h3> <pre><code>/* Day 1: Swiftの基礎と基本構文 Swift公式ドキュメント: https://docs.swift.org/swift-book/documentation/the-swift-programming-language */ // MARK: - 1. 変数と定数 print(&quot;=== 変数と定数 ===&quot;) // 変数(変更可能) var myVariable = 42 myVariable = 50 print(&quot;変数: \(myVariable)&quot;) // 定数(変更不可) let myConstant = 42 // myConstant = 50 // エラー: 定数は変更できません print(&quot;定数: \(myConstant)&quot;) ... /* 演習1: 変数と定数 - あなたの名前を定数として定義してください - あなたの年齢を変数として定義してください - 年齢を1つ増やして表示してください */ // ここにコードを書いてください /* 演習2: 文字列操作 - あなたの名前と年齢を使って「私の名前は[名前]で、[年齢]歳です」というメッセージを作成してください - 文字列補間を使用してください */ // ここにコードを書いてください </code></pre> <p>コメントで解説が書かれており、サンプルコードのあとに演習問題が用意されています。コードを読んで、動かして、自分で書いてみる。この一連の流れが1つのファイルで完結します。</p> <p>教材を読んでいて分からないことがあれば、その場でCursorに質問できるのもこの学習スタイルの強みです。「なぜここでXXXを使うの?」「他の書き方はある?」など、疑問点をどんどん聞くことで理解が深まります。</p> <p>ただし、AIはハルシネーション(誤った情報の生成)を起こすこともあるので、必要に応じて公式ドキュメントと照らし合わせながら学ぶことをおすすめします。</p> <h2><strong>Step 3: Practice — 演習課題をレビューしてもらう</strong></h2> <p>3つ目のステップは、最も効果を実感できた「Practice」です。</p> <p>教材に含まれる演習課題を自分で実装したあと、そのコードをCursorにレビューしてもらいます。</p> <p><strong>使用したプロンプト:</strong></p> <pre><code>課題をレビューして、改善点を教えて。もっとSwiftらしい書き方ある?</code></pre> <p>「Swiftらしい書き方」というように聞くことで、その言語ならではの洗練された表現やベストプラクティスを提案してくれます。</p> <h3><strong>理解度クイズ</strong></h3> <p>また、Cursorにクイズを出題させて理解度をチェックするのも効果的でした。</p> <p><strong>使用したプロンプト:</strong></p> <pre><code>今日学んだ内容から5問クイズを出して。</code></pre> <p>すると、すぐに確認テストを作ってくれます。このように、アウトプット中心で学ぶことで、確実に身につく実感がありました。</p> <h2><strong>Step 4: Track — 進捗管理</strong></h2> <p>最後のステップは、進捗管理の自動化です。</p> <p><strong>使用したプロンプト:</strong></p> <pre><code>学習プランをもとにTODOリストを作って進捗管理して。</code></pre> <p>学習プランからTODOリストを生成させ、1つ終わったらCursorに報告して更新してもらいます。</p> <h3><strong>生成された進捗管理ファイルの例</strong></h3> <pre><code># Swift学習 進捗管理 ## 📊 進捗サマリー - **完了**: 3/10日 - **進捗率**: 30% ## ✅ TODOリスト - [x] Day 1: Swiftの基礎と基本構文 - [x] Day 2: 制御フローと関数 - [x] Day 3: オプショナルとエラーハンドリング - [ ] Day 4: コレクション型 - [ ] Day 5: クラスと構造体 - [ ] Day 6: プロトコルと拡張 - [ ] Day 7: ジェネリクス - [ ] Day 8: クロージャと関数型プログラミング - [ ] Day 9: エラーハンドリングと非同期処理 - [ ] Day 10: 実践的なSwiftアプリケーション</code></pre> <p>進捗が可視化されるとモチベーションも保ちやすいですし、次にやることが常に明確になります。「今日はどこまでやったか」「明日は何をすればいいか」を考える手間がなくなり、学習のハードルが下がりました。</p> <p>学習で挫折しがちなポイントの1つが「どこまでやったか分からなくなること」なので、ここをCursorに任せてしまうのはかなりおすすめです。</p> <h2><strong>AI駆動学習のメリット</strong></h2> <p>この学習サイクルを実践してみて、いくつかのメリットを実感しました。</p> <p><strong>オーダーメイドの教材が手に入る</strong></p> <p>自分の理解度や使える時間に合わせて、カスタマイズされた学習プランと教材を作れます。「1日30分しか時間が取れない」「Python中級者のためのJava入門」など、自分の状況をプロンプトに書くだけで、かなりパーソナライズされた教材が出てきます。</p> <p><strong>アウトプット中心で定着する</strong></p> <p>教材を読むだけでなく、実際にコードを書き、AIにレビューしてもらうことで、理解が深まります。</p> <p><strong>継続しやすい</strong></p> <p>進捗管理をAIに任せることで、「今日は何をすればいいか」を考える手間がなくなります。学習のハードルが下がり、継続しやすくなりました。</p> <p><strong>新しい領域に踏み出しやすい</strong></p> <p>体系的に学べる安心感があると、これまで手を出しにくかった新しい言語やフレームワークにも挑戦しやすくなります。</p> <h2><strong>まとめ</strong></h2> <p>AIの進化によって、私たちが自分の手でコードを書く機会はこれからますます減っていくでしょう。だからこそ、学ぶ時間を意識的に確保することが大事だと改めて感じました。</p> <p>AIを使えば、自分の理解度や時間に合わせたオーダーメイドの教材を作れます。そのおかげで、新しい言語にも挑戦しやすくなり、担当領域の外へも踏み出せると実感しました。</p> <p>私自身は、次のステップとしてAndroid開発に挑戦するためにKotlinを学ぼうと思っています。</p> <p>Vibe Codingでコードを書かなくなった方、AIに頼りっぱなしで不安を感じている方、ぜひこの「AI駆動学習サイクル」を試してみてください。AIを使って学ぶという、新しい学習体験が待っています。</p> <h2><strong>付録:そのまま使えるプロンプト集</strong></h2> <p>実際に使ったプロンプトをまとめました。<code>[プログラミング言語]</code> や <code>[N]</code> の部分を置き換えてすぐに試せます。</p> <h3><strong>Step 1: 学習計画生成</strong></h3> <pre><code>公式ドキュメント([公式ドキュメントのURL])を元に、[N]日で[プログラミング言語]をマスターする学習計画を作って、mdファイルで出力して。</code></pre> <h3><strong>Step 2: 教材生成</strong></h3> <pre><code>学習プランを元に、各日の教材を作って。各日ごとにディレクトリを作成して、その中に解説・サンプルコード・演習課題を含む実行可能なファイルを含めて。</code></pre> <h3><strong>Step 3: レビュー</strong></h3> <pre><code>課題をレビューして、改善点を教えて。もっと[プログラミング言語]らしい書き方ある?</code></pre> <h3><strong>理解度クイズ</strong></h3> <pre><code>今日学んだ内容から5問クイズを出して。</code></pre> <h3><strong>Step 4: 進捗管理</strong></h3> <pre><code>学習プランをもとにTODOリストを作って進捗管理して。</code></pre> <p>明日の記事は poohさんです。引き続きお楽しみください。</p> インターン生が挑んだ認証方式のマイグレーション──メルペイ加盟店管理画面へのOAuth 2.0導入https://engineering.mercari.com/blog/entry/20251219-029c76e123/https://engineering.mercari.com/blog/entry/20251219-029c76e123/<p>はじめに こんにちは。メルペイPartner Platformチームでバックエンドエンジニアのインターンをしている@takiと申します。 この記事は、Merpay &amp; Mercoin Advent Calenda [&hellip;]</p> Mon, 22 Dec 2025 10:00:32 GMT<h2>はじめに</h2> <p>こんにちは。メルペイPartner Platformチームでバックエンドエンジニアのインターンをしている<a href="https://www.linkedin.com/in/%E8%B5%B7%E6%A8%B9-%E5%B7%9D%E4%B8%8A-b48b37324?utm_source=share&amp;utm_campaign=share_via&amp;utm_content=profile&amp;utm_medium=ios_app ">@taki</a>と申します。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の22日目の記事です。</p> <p>Partner Platformチームではメルペイの加盟店向けの管理画面を開発しています。本記事では、インターン期間中に取り組んだタスクの一つである、管理画面の認証方式をマイグレーションした経験について紹介します。</p> <h2>背景</h2> <p>メルペイでは、加盟店向けに管理画面を提供しています。この画面では、店舗でのメルペイの取引履歴の確認など、さまざまな機能を利用できます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/bb6a683a-partner_dashboard.png" alt="" /></p> <h3>既存の認証方式</h3> <p>現在の管理画面では、以下のようなフローの認証方式を採用しています。</p> <ol> <li><strong>ログイン</strong>: お客さまが送信したメールアドレス・パスワードを用いて、加盟店管理画面側で認証を行います。</li> <li><strong>リソース取得</strong>: 認証が成功するとアクセストークンが発行され、そのトークンを用いて依存先の各マイクロサービスにリクエストを行います。</li> </ol> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/36e27015-screenshot-2025-12-19-at-13.50.28.png" alt="" /></p> <p>このロジックはシンプルですが、大きな課題があります。それは、他サービス(例えばメルカリShopsやメルカリAdsなど)の加盟店管理画面とは独立した認証体系になってしまっているということです。</p> <p>そのため、例えばメルペイとメルカリShopsの両方を利用している加盟店のスタッフは、それぞれのサービスの管理画面で異なるアカウントを発行する必要があります。</p> <h3>新しい認証方式</h3> <p>そこで今回、この課題の解決に向けて、認証方式のマイグレーションを行いました。新しい方式の基本的なフローは以下の通りです。</p> <ol> <li><strong>ログイン</strong>: お客さまはまず認可サーバにメールアドレス・パスワードを送信し、認証を行います。ここで使用する認可サーバは、メルカリのIDPチームが提供しているものです。</li> <li><strong>トークンを要求</strong>: 加盟店管理画面側から、認可用のトークンを認可サーバにリクエストします。</li> <li><strong>トークンを発行</strong>: 認可サーバがトークンを発行します。</li> <li><strong>リソース取得</strong>: 発行されたトークンを用いて、加盟店管理画面から依存先のマイクロサービスにアクセスします。</li> </ol> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/88ba5085-screenshot-2025-12-19-at-13.51.24.png" alt="" /></p> <p>この方式では、認証・認可のフローが各サービスから分離され、共通の認可サーバに集約されます(下図参照)。これにより、将来的にはお客さまがさまざまなサービスの加盟店管理画面に、共通のアカウントでログインできるようになります。(メルカリShopsに関してはOAuthへのマイグレーションが別途で必要になります。)</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/40420e23--2025-12-19-14.39.30.png" alt="" /></p> <p>この仕組みは、技術的にはOAuth 2.0およびOIDC(OpenID Connect)というプロトコルを使用しています。以下では、OAuth 2.0について簡単に説明します。</p> <h2>OAuth 2.0とは</h2> <p>OAuth 2.0は、サードパーティアプリケーションがお客さまのリソースに安全にアクセスするための認可(Authorization)プロトコルです。</p> <h3>OAuth 2.0の登場人物</h3> <p>OAuth 2.0では、以下の4つの登場人物が出てきます。</p> <ul> <li><strong>リソースオーナー(Resource Owner)</strong>: リソースの所有者であるお客さま。今回のケースでは加盟店のスタッフです。</li> <li><strong>クライアント(Client)</strong>: リソースにアクセスしたいアプリケーション。今回のケースでは加盟店管理画面(のBFF:Backend For Frontend)です。</li> <li><strong>認可サーバ(Authorization Server)</strong>: お客さまを認証し、アクセストークンを発行するサーバ。今回のケースではIDPチームが提供する認可サーバです。</li> <li><strong>リソースサーバ(Resource Server)</strong>: 保護されたリソースを保持するサーバ。今回のケースでは加盟店管理画面から呼び出される各種マイクロサービスです。</li> </ul> <h3>OAuth 2.0のフロー</h3> <p>OAuth 2.0にはいくつかのフロー(グラントタイプ)があります。その中で、Webアプリケーションで最も一般的なのはAuthorization Code Flowというものです。今回実装したフローも、このフローを採用しています。</p> <p>基本的な流れは以下の通りです。</p> <ol> <li><strong>認可リクエスト</strong>: お客さまがログインを試みると、クライアントはお客さまを認可サーバにリダイレクトし、認可を要求します。</li> <li><strong>ログイン</strong>: 認可サーバでお客さまがログインします。</li> <li><strong>認可コードの発行</strong>: 認可サーバは認可コードを発行し、クライアントにリダイレクトします。</li> <li><strong>トークンを要求</strong>: クライアントは認可コードを認可サーバに送信し、アクセストークンを要求します。</li> <li><strong>トークンを発行</strong>: 認可サーバは、正当なクライアントからのリクエストかどうかを検証した上で、アクセストークンを発行します。</li> <li><strong>リソース取得</strong>: クライアントはアクセストークンを使用して、リソースサーバのAPIにアクセスします。</li> </ol> <p>先ほどの図ではこのフローを一部省略して描いていましたが、OAuth 2.0のフローを踏まえてちゃんと描くと以下のような形になります。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/0a14ec2e-screenshot-2025-12-19-at-13.51.06.png" alt="" /></p> <h2>今回の開発内容</h2> <h3>実装前の準備</h3> <p>実装に入る前に、まず要件を整理する必要がありました。具体的には、既存のロジックのどの部分をどの順序で変更するべきか調査し、チケットを切るところから始めました。</p> <p>この際、参考にしたのがスキマバイト「メルカリ ハロ」の加盟店管理画面です。メルカリ ハロは最も早くOAuthによる認可を導入していたサービスでした。実際にメルカリ ハロの管理画面を触ってみた上で、メルカリ ハロへのOAuth導入時のドキュメント、および実装を照らし合わせながら理解することで、実装の進め方の解像度を大きく高めることができました。</p> <p>残念ながらメルカリ ハロは2025年12月をもってサービスが終了しましたが、メルカリ ハロで行われたさまざまな技術的挑戦で得られた知見は、こうして他のプロダクトに生かし続けられるということを実感しました。</p> <h3>実装</h3> <p>実装の方針を立てた後、実際の開発に取り掛かりました。認可サーバ側の認証・認可のロジックはIDPチーム側で既に実装されていたため、今回の開発では加盟店管理画面のBFFにおける処理の実装がメインとなりました。具体的には、上の図の③のリダイレクトを受け付ける部分や、④⑤のトークン発行の部分などです。</p> <p>OAuthのフローでは、加盟店管理画面のBFFは、ブラウザとの直接通信を多用します。例えば、ブラウザにリダイレクトを行わせたり、⑤で受け取ったアクセストークンをブラウザのCookieにセットしたりする必要があります。</p> <p>そのため、これらの処理はサーバ間通信の仕組みであるgRPCサーバではなく、HTTPサーバで行うのが適切です。しかし、元々のBFFにはgRPCサーバしか存在しなかったため、新たにHTTPサーバを構築する必要がありました(下図の赤色部分)。</p> <p>実装では、gRPCサーバと同一プロセス内の別ポートでHTTPサーバを立ち上げるようにしました。さらに、Kubernetesのポート公開の設定やGatewayのプロキシ設定を変更することで、無事にHTTPサーバを疎通させることができました。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/602d2853-screenshot-2025-12-19-at-13.50.52.png" alt="" /></p> <p>続いて、新しく立てたHTTPサーバに、③や④⑤などのフローを行うためのエンドポイントを実装し、テスト環境でOAuthによる認可を動かすところまでを実現できました。</p> <h2>インターンの感想</h2> <p>メルペイでの約3ヶ月間のインターンを通して、強く感じたことは、メルペイのエンジニアは一般的なエンジニアの担当領域にとどまらず、事業側へのインパクトを解像度高く把握してプロダクトに関わっているということです。</p> <p>実際、自分の周りのメンターの方々は、単に与えられた仕様を実装するのではなく、「自分の開発しているものが、お客さまにどのような価値をもたらし、その価値が事業の成長や収益にどうつながるのか」まで踏み込んで議論しながらプロダクトに向き合っていると感じました。</p> <p>そういった視点を前提に、必要に応じて周囲のチームと連携しながら、仕様の見直しなども含めて主体的に開発を進めていく──このスタイルがマネージャー層だけでなく、一人ひとりのエンジニアにまで根付いていることは、自分にとって大きな驚きであり、とても魅力的な環境だと感じました。</p> <p>そして、メルペイのエンジニアがこのような動きをできる理由の一つとして、メルカリ・メルペイにはVibe CodingやAgentic Codingが深く浸透しているということがあると感じました。「Coding Agentの登場によって、エンジニアの役割はアーキテクトへと変化していく」とはよく言われていることですが、メルカリではこの変化がすでにかなり進んでいると実感すると共に、自分もこのようなエンジニアを目指したいと感じる経験となりました。</p> <h2>終わりに</h2> <p>この記事では、メルペイのインターンで取り組んだ認証方式のマイグレーション、そしてインターンを通して感じたことをまとめました。</p> <p>インターン期間中は、この記事で述べた内容に限らず、技術的な面でも、それ以外の面でも、さまざまな学びを得ることができました。</p> <p>残りのインターン期間では、OAuthの導入に関するQAを実施し、本番環境へのリリースまで完了させたいと考えています。</p> <p>明日の記事は@kubomiさんです。引き続きお楽しみください。</p> Non-AI tasks in the AI task force:AIツール開発の現場でこそ必要な「AI以外の」技術選定https://engineering.mercari.com/blog/entry/20251217-2204b3261b/https://engineering.mercari.com/blog/entry/20251217-2204b3261b/<p>こんにちは、メルカリのAI Task Forceでイネーブラー(Enabler)をしている @akkie です。 この記事は、Mercari Advent Calendar 2025の21日目の記事です。 すでにご存知の [&hellip;]</p> Sun, 21 Dec 2025 11:00:16 GMT<p>こんにちは、メルカリのAI Task Forceでイネーブラー(Enabler)をしている <a href="https://x.com/akkie30" title="@akkie">@akkie</a> です。</p> <p>この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a>の21日目の記事です。</p> <p>すでにご存知の方も多いかもしれませんが、現在メルカリは「AI-Native」をテーマに掲げ、AIを基盤として組織とプロダクトを抜本的に変革する<a href="https://careers.mercari.com/mercan/articles/53708/">取り組み</a>を進めています。AI Task Forceは、メルカリをAI Nativeな組織へと変革するために立ち上がった100名規模のチームで、Enablerは「各領域でAI Nativeな業務変革を主導する役割」とされています。</p> <p>既存のAI/LLMツールの活用はもちろん、プロダクトへのAI組み込みや、マニュアル作業が多い業務のDX推進など、その活動は多岐にわたります。</p> <p>最近では、特に開発における生産性向上を目的として<a href="https://engineering.mercari.com/blog/entry/20251201-pj-double-towards-ai-native-development/">pj-double</a>に多くのエンジニアが参加しています。</p> <p>しかしこの記事では、生産性を上げるために「どうAIに効率的に仕事をさせるか」ではなく、AIを使わない部分の仕事について話したいと思います。</p> <h2><strong>システムにAIを使わないに越したことはない</strong></h2> <p>AI Task Forceに居ながらこんなこと言っていいのか?と思うかもしれませんが、多くのエンジニアが、目的を達成するために「AIを使わないで済むなら使わないに越したことはない」に同意するのではないでしょうか。</p> <p>よくあるジョークとして、コーディングしているエンジニアの様子を想像してみて、と非エンジニアの人に聞くと、目まぐるしく手を動かしてキーボードにコードを打ちこんでいる様子を想像する、けれど実際のエンジニアを見てみると、キーボードから手を離し頭を抱えながら、画面に大量に表示されたエラーをジッと見つめ原因を探っている時間の方が何十倍も長いということがあります。これは事実です。</p> <p>最近ではまずAIが70%正しいコードを書いてくれるようになった分、なおさら人間はコードを打ち込むよりも「頭を抱えてコードが動かない問題の原因を探っている」時間の方が長くなったかもしれません。使っているライブラリの古いバージョンを前提にAIがコードを書いてきたり、サードパーティのAPI仕様を理解していなかったり、間違える要素はいくらでもありますし、AIの出力は下手に正しそうに見える分デバッグにも時間がかかります。</p> <p>システム開発はいわば人間が行ってきたことを自動化することが仕事です。メルカリのアプリ開発も、これまで街中のフリーマーケットで人と人との間で行われていた商売をシステムで行うための大きな自動化に取り組んでいるとも言えるでしょう。</p> <p>AIはその大きな助けになってくれますが、人間がAIに正しく意図を伝えられていないだけというケースも含めてまだ「70%正しい」くらいの出力を返してくるのが現状です。</p> <p>それと比べて、シェルにコマンドを打ち込んだり、コードをDockerイメージ上で動かす作業はほぼ100%(もちろん環境要因で動かなくなるときはあるでしょうが)常に同じレスポンスを返してくれます。ターミナルを開いて “echo $(( 100 + 200 ))” と打てばいつだって画面には300と表示されるし、お金もかかりません。</p> <p>「100+200の答えが知りたい」ときに、ChatGPTを開いて「100+200は?」と聞く人はいませんよね。たとえ聞いてもChatGPTは多くの場合正解を返してくるでしょうが、コストパフォーマンスや精度を考えるとこんなことをAIに聞くコードをシステムに組み込む人は居ないわけです。</p> <p>また、数珠つなぎにAIに仕事を連携させていくと、その掛け算で精度は下がり、すぐに使い物にならなくなるでしょう。</p> <p>つまり、仕事(特にシステム内で行うもの)は「AIを使わずとも自動化できるのであれば、使わないに越したことはない」ということが前提にあるはずです。もちろんAIの精度を上げる努力も必要ですが、決定論的なロジックで解決できる課題にAIを持ち込むべきではありません。AIは確率論だけれど、システムはできる限り決定論であるべきです。</p> <p>AI Task Forceというチームに所属しているので、色んな組織から「これ、AIで解決できませんか?」という相談を受けます。しかし具体的に話を聞いてみると、「それはAI使わなくても解決できますね」という回答になることがよくありました。「アクセスログから◯◯の売上分析をしたい」、「人事システムに溜まった△△のデータを可視化・検索したい」、などはその例でしょう。AIにそれらをさせるのではなくそれらを実現するコードをAIに書かせる、という意味ではたくさんAIを利用することにはなりますが、一度きりでは済まないような作業をAI自身にやらせるのは悪手だと考えます。AIに魚を持ってこさせるのではなく、釣り竿を作らせるのがAIの良い使い方と考えます。</p> <p>とにかく、どうしてもAIにしか解決が難しい課題にのみAIを使いたい。私にとってこの半年の仕事は、いわば「いかにAIを使わないで済むかを考える」ことでした。</p> <p>また、AIを使うかどうかは内部の実装の話だけではなく、ユーザとのインタフェースにも同じことが言えます。何にでもAIを使うことがもてはやされる時代ですが、あえてAIを使わない選択肢をとることで向上する生産性もあります。あなたが提供しようとしている価値に、本当に自然言語によるユーザとのインタフェースは必要でしょうか?mcpサーバは必要でしょうか?</p> <p>UNIXコマンドの多くが対話型のコマンドを避け、パイプされた他のコマンドとの組み合わせによって自動化が容易になったように、自分の作っているものに自然言語による対話型インタフェースが必要かは常に慎重な見定めが必要です。 </p> <h2><strong>社内ツール開発における「9割」の正体</strong></h2> <p>たとえAIを使って業務改善のためのツールを開発することが決まっても、大半の時間はAIと全く関係ないところに使われていることにも気が付きました。特にインフラ周りです。</p> <p>LLMが当たり前に使われるようになるずっと前から、AIを使ったプロダクト開発においては**「データの収集やインフラ整備が工数の9割を占め、モデル自体の設計・改善は1割程度である」**と言われてきました。</p> <p>この現実は、LLMが登場しAI技術が進化しても変わってないように感じています。</p> <p>たとえば機械学習モデルが実際に価値を生むAPIとして、高可用性・低遅延・スケーラビリティを維持して稼働し続けるためには、モデルの学習や評価よりも多くの、複雑なインフラおよびMLOps作業が不可欠になります。</p> <p>具体的にタスクを挙げてみます。</p> <ul> <li><strong>基盤・ネットワーク:</strong> <ul> <li>コンテナ化 </li> <li>Kubernetes/サーバレス設定 </li> <li>DNS </li> <li>SSL証明書 </li> <li>ロードバランサ </li> </ul> </li> <li><strong>セキュリティ</strong> <ul> <li>ネットワークセキュリティ </li> <li>認証認可 </li> <li>シークレット管理 </li> </ul> </li> <li><strong>運用・監視</strong> <ul> <li>オートスケーリング </li> <li>ヘルスチェック </li> <li>Datadog設定 </li> <li>ログ収集 </li> <li>ABテスト </li> </ul> </li> <li><strong>DevOps</strong> <ul> <li>CI/CDパイプライン </li> <li>IaC (Terraform/CloudFormation) </li> <li>モデルのバージョン管理</li> </ul> </li> </ul> <p>当然、これに加えて機械学習に与えるためのデータの収集のパイプラインや、BigQueryなどのData Ware House整備も必要になります。</p> <p>こうしたタスクも当然AIの力で楽にはなるのですが、インフラ周りの設定は複数のGithub Repository、手動作業、目視での確認、試行錯誤などが必須となり、どうしても時間がかかってしまいます。GitHub Actionsを1行直しては試す、Terraformの設定を1行直しては試す、Kubernetesマニフェストを1行直しては試す、のような作業に膨大な時間を使った経験があるエンジニアも少なくないでしょう。</p> <p>つまり、AIによって業務を改善しようと考えるときに、アプリケーションロジックをAIに効率的に書かせることだけではなく、こうしたインフラ・基盤周りの作業を決定論的に効率化しないことには生産性は大きく上がらないのではないかと考えています。</p> <p>メルカリのAI Task Forceでは、各組織(エンジニアのいないチームを含む)にEnablerが1〜2名アサインされ、組織の業務を改善する助けをするというスタイルをとっています。</p> <p>つまり、上記のタスクを基本的にはたった1〜2名で完遂しなければなりません。</p> <p>「ここはSREにお願いして…」「ここはMLエンジニアに…」と専門家に依頼している時間はありませんし、非エンジニアの方々の課題を解きほぐし、仕様に落とし込むPMのような役割も求められます。そもそも自分の知らない領域のチームのドメイン知識を手に入れるためにも多くの時間が必要でしょう。</p> <p>ここにさらに「AI固有の悩み」が乗ってきます。モデル選定、コンテキストエンジニアリング、そもそもGoogle ADKやPydantic AIなどライブラリの進化が早くて追いつくのが大変…など。</p> <h2><strong>社内ツール開発の技術選定</strong></h2> <p>そこで、私が半年間AI Task Forceでイネーブラーとして活動している間、そうした少ない人数で社内ツールを作るために頻繁に用いた構成を紹介します。</p> <p>前提として、社内ツールにはメルカリのメインプロダクト、Microservicesほどのスケーラビリティや厳密な可用性は求められません。求められるのは**「開発スピード」「メンテのしやすさ」「低コスト」**です。</p> <p>Google WorkspaceとGoogle Cloudを社内で使っていることが前提になっています。</p> <h3><strong>1. Cloud Run + IAP (Identity-Aware Proxy)</strong></h3> <p>Google Cloud環境において、私が社内ツール基盤として圧倒的に頻繁に用いたのが <strong>Cloud Run</strong> と <strong>IAP (Identity-Aware Proxy)</strong> の組み合わせです。</p> <h4><strong>なぜCloud Runなのか?</strong></h4> <p>Cloud Run を利用する主な理由は、その優れたコスト効率、スケーラビリティ、そしてシンプルなデプロイに集約されます。</p> <p>Cloud Run の最大の利点の一つは、その高いコスト効率です。リクエストがないときは実行インスタンスがゼロになり、その間は課金が一切発生しません。これは、夜間や休日などアクセスが少なくなる時間帯がある社内ツールなどにとって、特に大きなメリットとなります。</p> <p>また、どれだけ利用者が集中したとしても、Cloud Run は自動で必要な数のコンテナを起動し、トラフィックの負荷に瞬時に対応するスケーラビリティを備えています。これにより、ピーク時のアクセス集中によるサービス停止やパフォーマンス低下の心配をする必要がなくなります。</p> <p>Cloud Run を使えばシンプルなデプロイが可能です。アプリケーションをパッケージ化した Dockerfileさえ用意すれば、あとは簡単なコマンド一つでデプロイが完了します。「レプリカセット!」「ライブネスプローブ!」「ホリズンタルポッドオートスケーラー!」といったKubernetesの呪文も覚える必要はありません。</p> <p>また、DNSやSSL証明書の管理から解放されるだけでなく、サービスで重要な基本的なメトリクスやログも自動で収集されます。これらはすべてGCPコンソールからすぐに確認できるため、運用の負担が大幅に軽減されます。</p> <h4><strong>IAPで認証を「実装しない」</strong></h4> <p>社内ツールで最も重要なことの1つはセキュリティですが、認証ロジックを自前で実装するのは大変です。</p> <p>IAP (Identity Aware Proxy) を使えば、Google Workspaceアカウントに基づいたアクセス制御をインフラ層で強制できます。アプリ側では特定のヘッダを見るだけでユーザーを特定でき、認証ロジックを書く必要がなくなります。</p> <p>TerraformでLoad Balancerやバックエンドサービスを構成するための複雑なネットワーク設定を組むと長い時間のかかる作業が、Cloud Runなら以下のコマンドだけで「社内限定公開」の環境が整います。</p> <pre><code># Cloud Runへのデプロイ(IAP有効化オプション付き) gcloud beta run deploy ${SERVICE_NAME} \ --no-allow-unauthenticated --iap \ --platform managed \ --project=${PROJECT_ID} \ --service-account=${SA_EMAIL} \ --region=asia-northeast1 \ --image=${IMAGE_ID} # IAPへのアクセス権限付与(社内ドメインや特定グループのみ許可) gcloud beta iap web add-iam-policy-binding \ --project=${PROJECT_ID} \ --member=&quot;例:group:all-members@mercari.com&quot; \ --role=roles/iap.httpsResourceAccessor \ --region=asia-northeast1 \ --resource-type=cloud-run \ --service=${SERVICE_NAME}</code></pre> <p>これだけで、Artifact Registryに上げてあるコンテナが、社内メンバーだけがアクセス可能な状態で立ち上がります。<br /> (betaなので今後GAになった際にはコマンドが変わる可能性があります)</p> <h4><strong>Vertex AI (Gemini) へのアクセスも簡単</strong></h4> <p>AIをツールから使う上で面倒なことの一つが、APIトークンの管理です。</p> <p>Cloud Runならば、Cloud Runの実行Service Accountに roles/aiplatform.user を付与しておくだけで、APIキーの管理やローテーションを気にすることなくGeminiを利用可能です。</p> <pre><code>from google import genai # 認証情報が環境から自動取得されるためAPI Keyの指定は不要 client = genai.Client(vertexai=True, project=&quot;your-project-id&quot;, location=&quot;us-central1&quot;) model = &quot;gemini-2.5-flash-lite&quot; response = client.models.generate_content( model=model, contents=&quot;元気ですか?&quot; ) print(response.text) </code></pre> <p>n8nやDifyなどのノーコードツールも魅力的ですが、将来的な要件変更の柔軟性を考えると、Cloud Runによるコードベースの開発は「セットアップコストを抑えつつ、自由度を確保する」ための最適解の1つだと感じています。</p> <h3><strong>2. データベース:Firestore と Google Spreadsheet</strong></h3> <p>「高負荷なトランザクションが不要な社内ツールにおいて、月額数千円から1万円以上のコストがかかる Cloud Spanner や Cloud SQL はオーバースペックになりがちです。そこで、コストを最小限に抑える選択肢として Firestore と Google Spreadsheet が非常に役立ちました。</p> <p>Firestore は、スキーマレスかつサーバーレスなデータベースで、完全従量課金のためスモールスタートに最適です。一方で、意外にも優秀な選択肢となるのが Google Spreadsheet です。</p> <p>Spreadsheet を利用する最大の利点は、事実上のゼロコストで運用できる点にあります。Google Workspace 環境であれば追加費用はかかりません。Cloud Run の実行サービスアカウントに権限を付与するだけで、プログラムから容易に読み書きが可能です。</p> <p>さらに、管理画面(Admin UI)を作る手間を省ける点も大きな魅力です。非エンジニアの担当者が直接データを閲覧・編集できるため、現場からは「使い慣れたツールで管理できてありがたい」という声も得られました。データ不整合のリスクという側面はありますが、数千行程度のマスタデータ管理などであれば、開発スピードとメンテナンス性を最優先した合理的な選択だと言えます。」</p> <h3><strong>3. Streamlit</strong></h3> <p>ツール開発において、現在は Next.js がデファクトスタンダードと言えますが、社内ツールであれば Streamlit を採用することにも大きなメリットがあります。</p> <p>最大の利点は、開発速度の圧倒的な速さと学習コストの低さです。Streamlit を使えば、データアナリストやエンジニアが Python を書く延長線上で UI を構築できます。フロントエンドとバックエンドの境界を意識せず、入力ウィジェットやグラフ、チャットインターフェースなどを数行で実装できるため、迅速な改善が求められる現場で真価を発揮します。将来的に非エンジニアがツール制作に携わる際や、AI エージェントが台頭して「ロジックさえあれば UI はチャットで十分」となる時代を見据えても、Python で完結するライブラリを選んでおくのは有利な選択です。</p> <p>また、Python エコシステムとのシームレスな統合も無視できません。Pandas などのデータ処理ライブラリと標準で連携できるため、Next.js のように Python バックエンドとの間に複雑な通信レイヤーを設ける手間が省けます。同一の Python 環境内でデータを直接扱い、即座に UI へ反映できるシンプルさは、デバッグやメンテナンスの工数削減に直結します。</p> <p>他の Python 製 UI ライブラリも検討しましたが、現時点では Streamlit が最も完成度のバランスに優れていると感じています。もちろん API 開発が必要なケースでは他の選択肢が必要になりますが、「開発効率」を最優先する社内ツールにおいて、Streamlit は非常に強力な武器になります。</p> <h3><strong>諦めたこと</strong></h3> <p>もちろん、上記の技術を選んだことで諦めることにした部分もあります。</p> <ul> <li>ミリ秒単位の「サクサク」した操作感 <ul> <li>Cloud Runがコールドスタートするときには数秒の遅延が発生します。また、Streamlitで複雑なことをし始めるとどうしても動きがモッサリして、CacheやSessionの管理が厄介にもなります。 </li> </ul> </li> <li>厳密なトランザクション管理や集計クエリ <ul> <li>アクセス数が少ない社内ツールでは大きな問題は起きないだろうと考え、諦めました。 </li> </ul> </li> <li>サービスの常時稼働 <ul> <li>Cloud Runには常時稼働するオプションもありますが、一気にコストが上がり、従量課金のメリットが消え去ります。常にeventをListenするアプリやバックグラウンドでの重い非同期処理にはコストパフォーマンスが良くないでしょう。</li> </ul> </li> </ul> <h2><strong>まとめ・今後の展望</strong></h2> <p>AI-Nativeな組織変革を達成するには、エンジニアと非エンジニア組織の協力が不可欠です。</p> <p>この半年間、私はコードを書くだけでなく、現場の課題を聞き出し、それを技術で解決する「技術コンサルタント」のように働く時間が多かったように思います。</p> <p>非エンジニアと密に連携し、効率よく価値を届けるためには、彼らにとってもフレンドリーな技術を選択肢に入れ、柔軟性の高い技術を選択し、また常に「本当にAIを組み込むことは必要なのか」を見極めることも大事です。</p> <p>また、上述のような技術を使っても、まだ厄介なところは「CI/CD」で、個人的に改善しようとしているところです。<br /> プルリクエストが作られれば自動的にPreview URLが生成され、マージしたら本番環境にデプロイされる。環境変数の値を変更したいときに数クリックでサクサクと変更ができる。まだまだ今は手動デプロイをすることも多いですが、そういったPaaS体験を上述の技術と合わせて達成することで格段に生産性が上がるだろうなと感じています。</p> <p>進化の早いAIを活用し、現場の業務を最速で効率化していくフェーズにおいては、サーバレスソリューションをフル活用して「明日から使えるツール」を届けてみるのも悪くないのではないでしょうか。</p> <p>明日の記事は@Kahlaさんです。引き続きお楽しみください。 </p> メルカリグループのDevEx改善、9ヶ月間の実践https://engineering.mercari.com/blog/entry/20251219-nine-months-of-devex-improvement-at-mercari-group/https://engineering.mercari.com/blog/entry/20251219-nine-months-of-devex-improvement-at-mercari-group/<p>はじめに この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の21日目の記事です。 ntk1000です。MerpayのKYCおよびPartner Platformチームのエン [&hellip;]</p> Sun, 21 Dec 2025 10:00:43 GMT<h2><strong>はじめに</strong></h2> <p>この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の21日目の記事です。</p> <p><a href="https://x.com/ntk1000">ntk1000</a>です。MerpayのKYCおよびPartner Platformチームのエンジニアリングマネージャーを務めています。 半年前、メルカリグループにおける開発者体験(DevEx)改善のための<a href="https://engineering.mercari.com/blog/entry/20250624-building-a-company-wide-framework-for-improving-devex-in-mercari-group/">全社的な取り組みを紹介</a>しました。四半期ごとの改善サイクルを設計し、エンジニアとEMから100%の参加を得て、Deep Work(集中するための中断されない時間)やCross-team Collaborationといった構造的な課題を特定しました。</p> <p>最初の6ヶ月(FY25 Q4 → FY26 Q1)では、総合的な開発者体験指標は大きな変化がありませんでした。一部のチームで大幅な改善が見られたものの、多くのチームは停滞していました。この状況を踏まえてアプローチを見直した結果、直近の四半期(FY26 Q2)では全社的に指標が大幅に改善し、特にDeep Workの領域で顕著な向上を達成しています。通常は年間目標とされる改善水準も、今回は1四半期で達成できており、アプローチ転換の効果が明確に表れました。</p> <p>本記事では、この9ヶ月間の実践と成果、そして組織全体でDevEx改善をスケールさせるための取り組みについて共有します。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/4c7ddc4b-chatgpt-image-2025年12月16日-20_00_48-1024x683.png" alt="" /></p> <hr /> <h2><strong>改善のスケール:チームから組織全体へ</strong></h2> <h3><strong>第1段階(FY25 Q4 → FY26 Q1):一部成功と全体停滞</strong></h3> <p>FY26 Q1のサーベイでは、開発者体験指標が横ばいの組織がほとんどでした。 しかし、特定の課題領域やチーム単位でのデータを分析すると、改善に成功している事例も確認することができました。</p> <h3><strong>成功事例の分析</strong></h3> <p>Deep Workを例にとると、大幅に改善したチームの取り組みを調査した結果、以下の共通パターンがありました:</p> <ul> <li>集中時間を確保する明確なポリシーを確立 <ul> <li>ミーティングの整理、統合ルールの策定と実施 </li> </ul> </li> <li>チーム全体の「ノーミーティングタイム」ブロックの設定 <ul> <li>エンジニアの集中作業Dayを定期的に開催—月1回、2日間など完全にミーティングのない期間の確保 </li> </ul> </li> <li>AI活用によるルーチンタスクの自動化</li> </ul> <p>これらの取り組みに共通するのは、<strong>チーム全体のコミットメントに支えられた、ポリシーベースの改善</strong>です。 個人レベルの工夫ではなく、チーム全体で合意した構造的な変更として実装されており、<strong>ポリシーベースのため素早い展開・適用が可能</strong>という特徴があります。 一方でチーム単位での適用となるため、改善対応にばらつきがある・成功パターンの横展開が自然には起きないという課題もありました。</p> <h3><strong>第2段階(FY26 Q1 → FY26 Q2):ボトムアップとトップダウンの融合</strong></h3> <p>上記の課題を踏まえ、チーム単位の改善だけでは限界があることが明らかになったため、<strong>ボトムアップとトップダウン、両方のアプローチを同時に機能させる</strong>体制を構築しました。 2層構造での改善サイクルをまとめると下記になります。</p> <p><strong>ボトムアップ:</strong></p> <p>FY25 Q4から確立していたチームレベルの改善サイクル:</p> <ul> <li><strong>主体</strong>: Individual Contributor(IC)とEngineering Manager(EM) </li> <li><strong>特徴</strong>: <ul> <li>小回りが効き、チーム固有の課題に素早く対応 </li> <li>チームがコントロールでき、かつ素早い対応ができる領域(Deep Work、Documentation、Code Maintainabilityなど)で高い成功率</li> </ul> </li> </ul> <p><strong>トップダウン:</strong></p> <p>FY26 Q2から本格的に構築した組織レベルの改善サイクルです:</p> <ul> <li><strong>主体</strong>: VPoE、Director、Manager of Managers(MoM) </li> <li><strong>目的</strong>: <ul> <li>個別チームでは解決困難な構造的課題への対応 </li> <li>組織横断での方針決定とリソース配分 </li> <li>成功パターンの標準化と横展開</li> </ul> </li> </ul> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/e600e6e3-chatgpt-image-2025年12月16日-20_04_18-1024x683.png" alt="" /></p> <h3><strong>具体的な取り組み事例</strong></h3> <p>Fintech組織に所属するMerpay/MercoinではDeep Workへの改善要望が継続して高く、スコアも低い状況でした。 VPoE主導でDeep Work改善を組織としての注力課題に定め、下記のような取り組みを行いました。</p> <ul> <li>Fintech内の各組織におけるDeep Work関連指標の可視化(Deep Workスコア、ミーティングが多い日や割り込みの割合) </li> <li>Deep Work改善事例の横展開とローカライズ <ul> <li>組織内での定例会議の見直しと統合 </li> <li>チーム間での成功事例の共有・標準化 </li> </ul> </li> <li>追加サーベイを用いたより詳細な課題分析 </li> <li>EMへの改善実行サポート </li> <li>進捗トラッキング </li> <li>経営層へのレポート、非エンジニア組織への取り組み共有</li> </ul> <p>個々のチームの努力だけでは変化しなかった組織平均が、VPoE主導のトップダウン施策とEM/IC主導のボトムアップ実行を組み合わせることで、<strong>1四半期で約16%の改善を実現</strong>することができました。この結果は経営会議でも報告しており、全社集会でも共有を予定しています。</p> <p>Deep Work改善に有効であった集中時間を確保する明確なポリシーは、非エンジニア組織にも有効と考えられます。全社的なDeep Work改善につながることを期待しています。</p> <p>また、今回は素早い展開・適用が可能なポリシーベースの改善がメインだったため、組織横断で効果が発揮されやすいということもあったと思います。今後は着手しやすい短期の施策だけでなく、長期的な対応を必要とするような難易度の高い課題にも取り組んでいく必要があると考えています。</p> <hr /> <h2><strong>DXからの学び</strong></h2> <p>今回の取り組み方針を再設計する上で、<a href="https://getdx.com/">DX</a>(私たちのDevExプラットフォームを提供する会社)によるプレゼンテーションで得られた知見が参考になったため簡単に共有したいと思います。</p> <h3><strong>イニシアチブには構造が必要</strong></h3> <p>プレゼンテーションで強調されたのは、多くのDevEx改善が失敗する原因は、チームの関心の欠如だけではなく適切な構造の欠如であるという点です。 成功には3つの要素が不可欠とされていました:</p> <h3><strong>1:ビジネスケースの構築</strong></h3> <p>開発者の痛みを説明するだけでなく、ビジネス成果に翻訳する:</p> <ul> <li><strong>組織のKPIに接続</strong>: 時間損失、コスト、品質、定着率 </li> <li><strong>規模での影響を定量化</strong>: 例えば、1人の開発者がビルドごとに20分損失 × 700エンジニア = 重大なコスト </li> <li><strong>価値の解放を示す</strong>: 痛みの除去だけでなく、可能になること(より速い機能、より高い信頼性、より良い回復)</li> </ul> <h3><strong>2:イニシアチブを適切に構造化</strong></h3> <ul> <li><strong>6-12ヶ月以上でタイムボックス</strong>: 本当の変化には時間がかかる。スプリントは迅速な成果を生むが習慣を形成しない。 </li> <li><strong>自然なチェックポイントを設定</strong>: ベースライン → 中間点 → 終了測定 </li> <li><strong>チームが動員できるようにする</strong>: コミュニケーション、計画、実践の埋め込みに時間を与える</li> </ul> <h3><strong>3:適切な指標を定義</strong></h3> <ul> <li><strong>Northstar KPI(組織の最重要指標)</strong>: 生産性、満足度、品質、定着率 </li> <li><strong>リーディング指標</strong>: チームが直接影響できるもの(例:ビルド時間、レビュー時間) </li> <li><strong>ガードレール</strong>: ゲーミングを防止(例:PR数単独では人為的に膨張可能)</li> </ul> <h3><strong>3つの不可欠な役割</strong></h3> <p>すべての成功したイニシアチブには必要:</p> <ul> <li><strong>エグゼクティブスポンサー</strong>: トップダウンのリーダーシップとビジネスアライメントを提供(例:VPoEの意思決定) </li> <li><strong>チャンピオン</strong>: データで問題を枠組み化し、戦術的ガイダンスを提供(例:DirectorやMoMによる組織データ理解と方針決定) </li> <li><strong>マネージャー</strong>: 時間を配分し、イニシアチブをチームレベルのアクションに翻訳(例:EMやICによるチーム改善)</li> </ul> <p>いずれかの役割が欠けていると、イニシアチブは停滞します。</p> <h3><strong>可視性による持続可能性</strong></h3> <p>イニシアチブが途中で停滞することを防ぐため、以下の仕組みが推奨されています:</p> <ul> <li><strong>可視化</strong>: 進捗/進捗不足を示すダッシュボード </li> <li><strong>運営レビュー</strong>: 課題/アクション/成果を伴う定期的なミーティング </li> <li><strong>明確なリーダーシップ</strong>: 進捗の説明責任、ビジネスアラインメント</li> </ul> <hr /> <h2><strong>今後の課題</strong></h2> <p>現時点で解決できていない課題があり、引き続き取り組みを進めています。</p> <h3><strong>横展開の難しさ</strong></h3> <p>成功パターンは自動的に広がりません。特にメルカリグループではプロダクトのフェーズや組織サイズがさまざまなので、例えば成熟した大規模組織へのアプローチとスタートアップフェーズの小規模組織へのアプローチが同じになるとは限りません。</p> <p><strong>現在の取り組み</strong>: 単なる成功事例の寄せ集めではなく、組織サイズやプロダクトのフェーズといった情報も併記することで、各組織が自律して適用可能なアクションを判別できるようにナレッジ整備を進めています。</p> <h3><strong>長期的影響の測定</strong></h3> <p>サーベイスコアと即時指標(ミーティング時間、中断頻度)は測定できますが、DevEx改善をビジネス成果(提供速度、品質、定着率)に接続することは依然として困難です。</p> <p><strong>現在の取り組み</strong>: DXスコア改善と各種サーベイ結果および定量データとの相関分析を進めています。まだ確定的な結論は出ていません。</p> <h3><strong>複数四半期にわたる継続性</strong></h3> <p>高い参加率は継続できていますが、サーベイ疲れや改善が進まない課題への不満も散見されるようになってきました。</p> <p><strong>現在の取り組み</strong>: DXのサーベイが肥大化しないように毎回調整、DX関連の社内イベントを実施して意義を定期的に伝える取り組みを続けています。</p> <hr /> <h2><strong>まとめ</strong></h2> <p>9ヶ月前、この取り組みを開始した時点では、強いエンゲージメントを達成し、構造的な課題を特定し、アクションプランを作成しました。</p> <p>現在は、初期の勢いを持続的な組織変革に変える段階にあります。一部のチームで大きな成果が出ている一方、課題に直面しているチームもあるというばらつきがある状況から、組織横断での改善を進めることができるようになってきました。</p> <p>DevEx改善を組織全体にスケールさせるために必要な要素をまとめます:</p> <ul> <li><strong>構造的サポート</strong>(改善体制、役割の明確化) </li> <li><strong>文化的コミットメント</strong>(多方面でのリーダーシップ、定期的な可視性) </li> <li><strong>実用的なフレームワーク</strong>(チームが適応できる成功事例の展開)</li> </ul> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/920b115e-chatgpt-image-2025年12月16日-20_11_28-1024x683.png" alt="" /></p> <p>改善スコアは指標であり目的ではないことにも引き続き注意する必要があります。 より重要なのは、チームが以下の能力を獲得したかどうかであり、結果としてビジネス成果を最大化することです:</p> <ul> <li>日々の仕事での摩擦を特定する </li> <li>リーダーシップに明確に表現する </li> <li>改善のために集団的アクションを取る </li> <li>結果を測定し振り返る</li> </ul> <h3><strong>継続的な実践としてのDevEx</strong></h3> <p>プロダクトの進化や組織サイズ・構造の変化、AIによる開発手法の変容といった動きに応じて、いち早く課題を特定して対処するためには継続的な実践が不可欠です。</p> <p>目標は完璧なスコアを達成することではなく、開発者体験の問題を継続的に感知し対応できる組織能力を構築し、生産性を上げることです。</p> <p>第1段階では全社的な開発者体験指標は横ばいでしたが、第2段階では大幅な改善を達成しました。 この変化が一時的なものでなく継続的な改善プロセスとして定着するように、引き続きこの取り組み自体も改善を進めていきます。</p> <p>明日の記事は @takiさんです。引き続きお楽しみください。</p> PR駆動の変更、CI/CDでOS設定を自動反映 — Terraformで実現するJamf ProのIaC+GitOps基盤https://engineering.mercari.com/blog/entry/20251220-jamf-terraform-gitops/https://engineering.mercari.com/blog/entry/20251220-jamf-terraform-gitops/<p>こんにちは。メルカリのセキュリティエンジニアの@yuです。 この記事は、Mercari Advent Calendar 2025 の20日目の記事です。 従業員の業務端末(MacBook)に利用しているデバイス管理ツール [&hellip;]</p> Sat, 20 Dec 2025 11:00:31 GMT<p>こんにちは。メルカリのセキュリティエンジニアの@yuです。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a> の20日目の記事です。</p> <p>従業員の業務端末(MacBook)に利用しているデバイス管理ツールであるJamf Proの構成管理にTerraformを活用し始めた際の所見について紹介します。</p> <h2>概要</h2> <p>Jamf Proの構成変更は、macOS, iOSデバイス管理の改善に欠かせません。一方で、変更が手作業中心だと、レビューが重くなったり、作業ミスの余地が残ったり、変更の根拠や履歴が散らばったりしがちです。特にセキュリティの観点では「何時、誰が、何故、どのようなプロセスで、何を変更できるのか」のような5W1Hを明確にし続けることが重要になります。</p> <p>そこで本記事では、Open Source Communityが開発・保守しているTerraform provider (※)を用いてJamf ProのリソースをIaC (Infrastructure as Code)化し、さらにGitHub上のPR (Pull request)を起点に<code>terraform plan</code>をレビューしてから<code>terraform apply</code>を実行する、PR駆動のGitOpsフローへ移行した取り組みを紹介します。</p> <p>※: <a href="https://registry.terraform.io/providers/deploymenttheory/jamfpro/latest/docs">https://registry.terraform.io/providers/deploymenttheory/jamfpro/latest/docs</a></p> <p>この手法自体は主要なクラウドサービスの構成管理では一般的な手法ではありますが、本件の様なデバイス管理ツール上での適用については例が少ないのが現状です。本記事では、この試みをいかにして実現したのかを紹介します。</p> <p>この仕組みによって、変更内容はコード差分とplan出力として事前に確認できるようになり、適用は自動化されます。結果として「手順書ベースのレビューや手作業変更」から、「差分とplanを一次情報としてレビューし、承認後に安全に反映する」形へ移行できました。</p> <h2>背景:なぜ“セキュアな変更管理”が必要だったのか</h2> <p>もともとJamf Proの変更は、手順書に沿った手作業が中心でした。この方法は丁寧に運用すれば回りますが、変更のたびに設計から実装までのリードタイムが伸びやすく、オペミスの余地も残ります。また、Jamf Proのリソースによっては変更履歴が十分に追えず、申請フローと実際の変更を機械的に突合できない、といった課題もありました。さらに、従来の申請フローでは「何をどう変えるか」が自然言語やスクリーンショットで記述されることが多く、変更内容が曖昧になりやすいという問題もありました。申請者・承認者・作業者の間で解釈が分かれた結果、意図と異なる設定変更やレビュー漏れが起きる余地が残ります。</p> <p>(こちらの課題の一例については昨年の <a href="https://engineering.mercari.com/blog/entry/20241220-mscp-jamf-api-macos-security-configs-iac/" title="mSCPとJamf Pro APIによるmacOSセキュリティ設定の手動IaC化の試行 | メルカリエンジニアリング">mSCPとJamf Pro APIによるmacOSセキュリティ設定の手動IaC化の試行 | メルカリエンジニアリング</a> も併せてご参照下さい)</p> <p>さらに運用が進むにつれて、リソースの所管や背景(なぜこの設定が必要か)が 変更管理ツール、SlackやNotion、Google Docsなどに散在し、後から辿るコストが増えていきます。結果として、定常メンテナンスのために高い操作権限を人に付与し続ける必要が出やすく、攻撃面管理の観点でも悩ましい状態になりがちです。一方で、私たちはお客さま向けのプロダクト環境へ設定しているものと同じ高いセキュリティ及び管理基準を当社のコーポレート環境に対しても適用したいと考えております。</p> <p>この状況を改善するために、私たちはJamf Proの変更を「コード化」し、「PRとレビューのプロセスに載せる」、そして「承認後に自動適用する」構造へ切り替えました。以降の章では、この GitOpsフローをどう設計し、どのように権限分離・承認・変更範囲のコントロールを実現したかを中心に説明します。</p> <h2>コードオーナーで「変更範囲」と「承認責務」を結びつける</h2> <p>GitOpsでは、構成変更の入口がPRになります。つまり、レビューと承認の設計が、そのまま変更管理の統制になります。Jamf Proのように、設定変更が端末群に広く影響し得る仕組みでは、誰がどの範囲を見て、どの基準で承認するかを、運用ルールだけでなく “仕組み” に落とすことが重要です。</p> <p>そこで有効なのが コードオーナー (※)です。コードオーナー を使うと、特定のディレクトリやファイルに変更が入った際に、指定したロール(チーム)がレビューに入ることを、リポジトリのルールとして強制できます。これにより「所管が曖昧」「気づいた人が見る」状態から、「この範囲はこのロールが責任を持つ」状態へ寄せられます。</p> <p>※: <a href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners">https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners</a> </p> <p>(掲載例)</p> <pre><code># CODEOWNERS (example) # Policies: local admin management /terraform/sandbox/policies/local-admin/** @sec-reviewers # Application update settings: owned by IT Dept. /terraform/sandbox/configuration-profiles/update-settings/** @endpoint-admins # Production: require multiple approvers /terraform/prod/** @sec-reviewers @endpoint-admins # Workflows /.github/workflows/** @sec-reviewers</code></pre> <p>ここでのポイントは、ディレクトリ構造自体をレビュー境界に合わせることです。</p> <h2>GitHub Actions workflowの統制:applyの実行条件を絞る</h2> <p>PR駆動のGitOpsでは、<code>terraform plan</code>が “事前確認” で、<code>terraform apply</code>が “本番反映” です。この2つを分けて考え、applyが実行され得る条件を狭くすることで、変更管理をセキュアにできます。</p> <p>本件での設計の要点は<code>terraform plan</code>と<code>terraform apply</code>の実行ワークフローを分離し、承認前に本番反映 (apply)が起きない状態をBranch Protectionで担保することです。</p> <p>具体的にはPR 作成時にはplanを実行し、レビュワーはコード差分とplan出力を一次情報として確認します。一方でapplyはmainブランチへのマージ後にのみ実行されるようにし、「承認済みの変更だけが反映される」状態をワークフロー条件とします。Branch Protectionに関わるセキュリティ上の論点については、補足として「<a href="https://engineering.mercari.com/blog/entry/20241217-github-branch-protection/" title="GitHubのBranch Protectionの突破方法 | メルカリエンジニアリング">GitHubのBranch Protectionの突破方法 | メルカリエンジニアリング</a>」も併せて参照ください。</p> <p>さらに、ワークフロー自体を保護することも重要です。GitOpsにおいてCI/CDは実質的に“実行主体”であり、ここが最重要の保護対象になります。そのため、本件ではplanとapplyのワークフローを分離し、それぞれが参照できるAPI権限も分離します。具体的には、planは読み取りに必要な範囲に限定し、applyは反映に必要な範囲に限定します。加えて、認証情報は特定のワークフローが特定の GitHubイベントで実行された場合にのみ、CI上で一時的に参照できるようアクセス制御を適用し、不要な露出を避けます。</p> <p>最後に、<code>.github/workflows/**</code>配下の変更についても勿論レビューを必須にすることで、CI/CDの改変による統制の迂回を防ぎます。</p> <p>この設計によって、「強い操作権限を常に人へ配り続け、手動での変更管理」状態から、承認プロセスに通った変更だけが自動で反映される状態への移行準備が整いました。Jamf Proは端末に対して大きな影響を与え得るため、変更管理をこの構造に乗せること自体がリスク低減になります。</p> <h2>移行:既存JamfリソースをIaCへ取り込む</h2> <h3>Import block / generate-config-out で「ゼロから書かない」移行にする</h3> <p>Jamf Pro には既に多くの設定が存在しているため、IaC化を “ゼロから書く” 方式で始めると、次の問題が起きがちです。</p> <ul> <li>現行設定の再現に時間がかかる</li> <li>再現ミス(設定の抜け漏れ)が起きる</li> <li>結果として、移行プロジェクトが止まりやすい</li> </ul> <p>そのため、本取り組みではTerraformのImport blockと <code>-generate-config-out</code>を活用し、既存のJamfリソースを起点にtfファイルを生成し、stateに取り込む流れを中心に据えました。</p> <p>(コマンド例)</p> <pre><code>terraform plan -parallelism=1 -generate-config-out=&lt;generated&gt;.tf</code></pre> <p>このアプローチの利点は、移行を“正確性”と“スピード”の両立で進めやすい点です。運用移行では、最初から完璧な整理(理想のディレクトリ構成やモジュール化)を目指すより、まず「planが安定して読める状態」まで持っていく方が、その後の GitOps 運用に繋がりやすいと考えています。</p> <p>なお、メルカリでは100を超えるJamf Proリソースがあるため、手動でimportブロック書くのも一定時間を要する可能性があり、事前に「Jamf Proのリソース情報を取得してimportブロックを自動生成する」プログラムを準備して臨みました。但し、こちらは Terraform provider側で将来<code>terraform query</code>へ対応した場合は、該当コマンドへ置き換えが可能となります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/25ea0303-20251220_mercari_engineering_blog_jamf_terraform_gitops-1024x381.png" alt="Jamf terraform plan result" /></p> <h3>つまずきどころ1:構成プロファイルの差分が毎回出る問題</h3> <p>構成プロファイルでは、初回に<code>*.mobileconfig</code>をペイロードとしてJamf Proへ作成した際に、Jamf Pro側で<code>PayloadIdentifier</code>などの付加情報が追加されることがあります。結果として、次回の<code>terraform plan</code>で差分が必ず出る、という状況が起こり得ます。</p> <p>例えば、GitHubプロジェクト側のmSCP (macOS Security Compliance Project)側で自動生成されるmobileconfigファイルをそのまま使う場合は本事象が発生します。</p> <p>この差分は “危険な変更” というより、管理システム側の正規化(自動付加)により起きているため、運用としては次のように整理しておくと混乱が減ります。</p> <ul> <li>「差分が出る=即apply」ではなく、まず差分の性質を分類する。</li> <li>既存リソースは importを起点に扱い、変更時に差分を取り込む方針に寄せる</li> <li>新規登録リソースも既存の正規化(自動付加)をsandbox環境などで検証の上、正規化された状態で本番へ適用。</li> </ul> <p>これにより、GitOpsの価値である「planを一次情報としてレビューする」を維持しつつ、ノイズ由来の差分で運用が疲弊することを避けられます。</p> <h3>つまずきどころ2:Policy更新時の差分の見え方</h3> <p>Policy 更新時は、plan/applyで例えば Self Serviceに関する属性差分が表示されるなど、UI上の変更と一致していても“コード上の差分”として表現されることがあります。将来的にTerraform provider側の改修に伴い解消する可能性がありますが、それまでは差分が「意図した変更か」を判断するためのチェック観点を押さえ、Terraformを利用する関係者で共有出来る形にすることで、円滑な運用に繋げることが出来ます。</p> <h2>運用:属人性を減らすために“プロセスで残す”</h2> <h3>PRが「背景・差分・plan」をまとめた一次情報になる</h3> <p>従来、変更の背景(なぜ必要か)や手順、結果はSlackやGoogle Docs、Notionなどに散らばりがちでした。GitOpsへ寄せると、少なくとも変更の入口がPRに集約されるため、</p> <ul> <li>PR記述 (なぜ変えるのか)</li> <li>コード差分 (何を変えるか)</li> <li>plan 出力 (何が起きるか)</li> </ul> <p>が、1つの場所に残ります。これは、コンテキスト散在や所管不明瞭の改善に直結します。</p> <h3>コードオーナー+レビューにより「気づいた人」から「責務」へ</h3> <p>コードオーナーは単に“レビューを依頼する仕組み”ではなく、責務境界をコードに埋め込む仕組みとして機能します。結果として、特定のリソースに対して「どのロールが見るべきか」を継続的に維持しやすくなります。</p> <h3>変更履歴の可視化(通知)で“あとから追える”を当たり前にする</h3> <p>GitOps では mainブランチが事実上の “唯一の正” になるため、mainの変更履歴を関係者が追える形にするのが重要です。mainの変更履歴をポストするチャンネルを用意してGitHubにアクセスせずともどのような変更が行われているかについてリーチし易い環境を用意しました。</p> <h2>導入効果</h2> <p>ここまでの取り組みを経て、私たちの運用が具体的にどのように変わったのかを紹介します。</p> <h3>1.ClickOpsからの解放</h3> <p>これまでの変更手順書に対するレビューでは、手順のWeb UIを頭でトレースしてレビューを行っていました。「この操作で意図した設定に変わるのかな?」「前提のステップや併せて変更するべきリソースはどこだろう?」という、ある意味想像力を働かせたレビュー必要でした。仮にsandbox環境でのテストをパスしていたとしても本番環境との僅かな差異によって予期せぬ挙動を招くリスクがあり、手順書作成からテスト、本番実装までのリードタイムも膨らんでいました。また、仮に完璧な手順書があったとしても、手作業中心では最後の最後でのオペミス(クリックやコピペ内容)というヒューマンエラーのリスクを内包していました。</p> <p>移行後はPRのコード差分とterraform planの出力結果という一次情報が目の前にあるため、レビュー品質とスピードがあがりました。また、変更の反映も手作業では無く「プログラムによる確実な実行」がもたらした安心感はとても良いです。</p> <h3>2.監査・トラブルシューティングが容易に</h3> <p>PRそのものが「変更背景・差分・実行結果」をすべて内包するログとなっています。いつ、誰が、何の目的・背景で変更したのかが main ブランチの履歴と紐付いて残るため、監査やトラブルシューティングの際も、迷わず一次情報へ辿り着けるようになりました。</p> <h3>3.仕組みで責任と権限の担保</h3> <p>コードオーナーによって、ディレクトリ構造とレビュー(承認)責務が紐付いています。また、「承認された変更のみが自動適用される」フローを構築したことで、将来的に「最小権限の原則」の実現に向けたよい準備となりました。</p> <h2>今後の展望</h2> <h3>1. 管理対象リソースの拡大</h3> <p>Jamf ProのTerraform providerは有用ですが、開発途上の側面もあり、リソースタイプやリソースの構成によっては”差分ノイズ”と継続的に付き合う可能性があります。ノイズが多いとplanレビューの焦点がぶれ、レビュワーが疲弊する状況になりますので、まずはsandboxでの検証を継続しつつ、本番での管理(import)は段階的に広げる運用を継続予定です。</p> <h3>2. Jamfの機能追加に伴う拡張</h3> <p>将来的には通常のJamf Proリソース管理に加え、Jamf社が公式で開発している別provider (※)で扱える領域 (例: blueprintやcompliance benchmark)を含めて、デバイスの管理運用やセキュリティコントロールを改善できる余地があります。</p> <p>※: <a href="https://registry.terraform.io/providers/Jamf-Concepts/jamfplatform/latest/docs">https://registry.terraform.io/providers/Jamf-Concepts/jamfplatform/latest/docs</a> </p> <p>2025年12月上旬現在、この機能を用いるJamf Platform APIはpublic betaのアナウンス待ちとなり、それまではWeb UIでの変更管理となります。</p> <p><a href="https://developer.jamf.com/platform-api/reference/getting-started-with-platform-api">https://developer.jamf.com/platform-api/reference/getting-started-with-platform-api</a> </p> <h3>3. 運用メンバー向けOnboarding</h3> <p>日常的に開発を行っているメンバーはGitOpsの活用が容易ですが、そうでないメンバーや別チームの関係者に対しても「使ってみようかな」と考えてもらえる仕掛けや、取りかかりし易い環境は大切です。エディタやGitHubの使い方や、Terraform特有の記法のガイドなど、一方的なドキュメントの連携だけで終わらせるのではなく、メンバーからのフィードバックによる改善など、関係者で利用促進のポイントを継続検討予定です。</p> <h2>まとめ</h2> <ul> <li>Jamf Proの変更管理を、手順書・手作業中心の運用から、TerraformによるIaC+PR駆動GitOps (planレビュー→承認→apply) へ移行しました。</li> <li>変更はPRの差分と<code>terraform plan</code>出力を一次情報としてレビューできるため、レビューの質と速度を上げつつ、適用は自動化することでオペミスの余地を減らす設計にできました。</li> <li>コードオーナーやワークフローの実行条件で「変更範囲」と「承認責務」を結びつけ、所管の曖昧さやコンテキスト散在に対しても、プロセスと履歴が残る形で改善を狙えるようになりました。</li> <li>一方で、差分ノイズが出やすいリソース(例: 構成プロファイル)などの課題もあるため、importの方針や運用ルールを整備しながら、段階的に改善していきます。</li> </ul> <p>近年、Jamf Proだけでなく法人向け他製品についても前述のようなセキュリティやガバナンス上の利点からGitOpsやIaCに関わる要素技術 (APIやTerraform provider)へ多大な投資が行われています。将来の製品選定基準の一つとして本要素を含めるのも良いかもしれません。</p> <p>本記事が各組織のmacOS, iOS構成管理の課題検討の一助になれば幸いです。</p> <p>最後に、メルカリでGitOpsをPolicy as Codeとして活用し、セキュリティとガバナンスを拡張することにご興味がありましたら、セキュリティチームの採用情報について<a href="https://careers.mercari.com/" title="mercari careers">mercari careers</a>をご覧ください。</p> <p>明日の記事は akkie さんです。引き続きお楽しみください。</p> LLMを用いたおしゃべり機能「しゃべるおさいふ」のバックエンド設計https://engineering.mercari.com/blog/entry/20251219-f15f686553/https://engineering.mercari.com/blog/entry/20251219-f15f686553/<p>こんにちは。メルペイバックエンドエンジニアの @kobaryo です。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の20日目の記事です。 はじめに メルカリグループは [&hellip;]</p> Sat, 20 Dec 2025 10:00:40 GMT<p>こんにちは。メルペイバックエンドエンジニアの <a href="https://x.com/artoy5884">@kobaryo</a> です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の20日目の記事です。</p> <h2>はじめに</h2> <p>メルカリグループは現在、AI Nativeな会社を目指しており、開発プロセスへの活用はもちろん、社内のさまざまな業務においてAIを使った効率化の取り組みが進んでいます。それ以前にも、プロダクト面ではメルカリの「<a href="https://about.mercari.com/press/news/articles/20240910_aisupport/">AI出品サポート</a>」やメルカリ ハロの「<a href="https://careers.mercari.com/mercan/articles/51281/">かんたん求人作成</a>」とAIを活用した機能がこれまでに複数リリースされてきました。</p> <p>そして今回、メルペイでもAI Nativeなプロダクトを実現すべく、2025年9月に「しゃべるおさいふ」機能のPoCを開始しました。この機能は一部のお客さま向けに限定提供しているため、体験できていない方もいらっしゃるかもしれませんが、本記事では、この「しゃべるおさいふ」機能のバックエンド設計について解説します。</p> <p>まずは機能の概要を紹介し、その後にアーキテクチャや、LLMを利用するがゆえに発生する特有のポイントについて説明していきます。</p> <h2>「しゃべるおさいふ」機能とは</h2> <p>「しゃべるおさいふ」は、メルカリアプリのおさいふタブを開くと、LLMがお客さま一人一人の決済履歴やポイントの有効期限などをもとに、ひとことコメントを返してくれる機能です。<br /> 「◯◯ポイントがあと×日で失効するので、よく行くカフェで使いませんか?」といった実用的な提案から、画像のようにちょっとした雑談めいたコメントまで返してくれます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/71b7a742--2025-12-19-16.02.27.png" alt="" /></p> <p>近年のLLM活用はチャットUIのようにお客さまが能動的に行動する形式が主流ですが、この機能は「日常的なメルペイの利用の中でAIからのコメントが届く体験」がアプリ訪問や決済行動に寄与するのかを検証する狙いがあります。この機能によって「今日はいつもと違う買い物をしたけれど、どんなコメントが返ってくるのだろう?」と、決済体験を少しでも楽しくしたいと考えています。</p> <h2>アーキテクチャ</h2> <p>ここからは、具体的な設計について説明します。PoCであることから、少人数・短期間での開発を前提とし、シンプルな構成を重視しています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/5ad101e1-wallet-ai-architecture.drawio.png" alt="" /></p> <p>短期間でのリリースを実現するため、新たなマイクロサービスは作らず、既存サービスにエンドポイントを追加する形を採用しました。ただし、LLMへのリクエストはレイテンシが大きくなることが予想され、さらにPoCのため解放人数が動的に変化し、負荷の変動が大きくなり得ます。そのため、APIサーバーとは独立したworkerを設けて処理を非同期化し、他機能への影響を避けつつ、「しゃべるおさいふ」だけを容易にスケールできるよう設計しました。この構成によって、設計開始から社内テスト用の実装まで1ヶ月弱、そこからリリースまでにもおよそ1ヶ月とスムーズに進めることができ、短期間での立ち上げに大きく寄与しました。</p> <p>大まかなフローは次のとおりです。<br /> &nbsp;</p> <ol> <li>メルカリアプリがエンドポイントを叩くと、jobがPub/Sub経由でworkerに送信され、アプリ側はjob完了までポーリングする</li> <li>workerがお客さまの情報をもとにLLMに問い合わせ、生成したコメントを保存する</li> </ol> <p>保存したコメントは、後述するコスト削減のためのキャッシュとしても活用されます。</p> <h2>実装におけるポイント</h2> <p>ここでは、LLMを用いることで生じた課題と、その解決方法について解説します。LLMの設定により解決したものもあれば、そうではないものもあります。</p> <h3>フィルタリング</h3> <p>LLM活用における最大の懸念点は、不適切または意図しない出力がそのままお客さまに表示されてしまうことです。LLM自身がある程度有害な内容を抑制するとはいえ、プロダクトとして避けたい表現は依然として存在します。</p> <p>そのため生成コメントに対して、次の3段階のフィルタリングを行い、いずれかに該当した場合は再生成する仕組みにしています。<br /> &nbsp;</p> <ul> <li>文字数制限 <ul> <li>表示上の都合から、決済に関わる重要情報(バーコードや残高など)と干渉しない長さに収める必要があります。トークン数制限もしていますが、文字数とトークン数は必ずしも比例しないため文字数チェックも併用しています。</li> </ul> </li> <li>単語単位でのフィルタリング <ul> <li>明らかにNGな語句については、後述のLLMによるフィルタを通す前に弾くことで、レイテンシとコストを抑えています。</li> </ul> </li> <li>LLMによるフィルタリング <ul> <li>設定したポリシーを添えて、生成コメントを再度LLMに渡し、内容がポリシーに沿っているかチェックしています。</li> </ul> </li> </ul> <p>さらに、これらのフィルタリングが有効に機能しているかを検証するため、バックエンドでは任意のお客さまデータやプロンプトをrequest bodyとして渡し、それを基にコメントを生成できるQA用エンドポイントも用意しました。QAではポリシーに抵触しそうなプロンプトやデータを入力し、その返答を確認するサイクルを繰り返すことで、フィルタリングの有効性を検証しています。</p> <h3>コスト・レイテンシ</h3> <p>LLMを利用するうえで、コストとレイテンシの問題は避けられません。この機能はお客さまの操作なしで自動表示されるため、リクエスト数が増えることが予想されますし、表示が遅れるとおさいふタブからの離脱につながります。</p> <p>コスト観点では、LLM側の設定と仕様側の工夫の両面から対処しています。LLM側の設定としては、プロンプトの静的な部分と動的な部分を切り分け、前者をプロンプトキャッシュすることでコストを削減しています。仕様面では、1人のお客さまに対して1日に生成するコメント数に上限を設けることで、過剰な生成を抑えています。上限を超えた場合は、既に生成済みのそのお客さまに向けたコメントを再利用するようにし、推論コストを減らしています。ただし、体験面では「コメント→決済→コメント」というサイクルが心地よく回ることが重要なため、決済が行われた際にはこの上限を緩和する仕組みを取り入れています。これにより過剰な生成は抑えつつ、決済行動に応じたコメント更新の楽しさは損なわないよう工夫しています。</p> <p>レイテンシ観点では、事前にバッチでコメントを生成しておく方法も検討できるものの、決済直後の情報がすぐにコメントへ反映される体験を重視し、リアルタイム生成を採用しています。ただし表示までに時間がかかると離脱につながるため、モデルやlocationの選定により、概ね3〜4秒以内に返答されるよう調整しています。</p> <h3>その他</h3> <p>これらの課題以外にも、いくつか工夫が必要な点があります。</p> <p>1つ目は、お客さまにとってコメントが自然に感じられるかどうかです。たとえば、レイテンシの項で述べたように、決済情報を即座にコメントへ反映する仕組みはその一例です。また、LLMは現在の日付や時刻、日付から曜日への変換をしばしば誤るため、日時情報はバックエンド側で生成し、プロンプトとして明示的に与えるようにしています。さらに、ポイントのように時間経過で値が変わる情報を使用するコメントについては、キャッシュとして表示しないようにしており、自然さが損なわれないよう配慮しています。</p> <p>2つ目は、モデルやプロンプトなどの設定を容易に変更できるようにすることです。しゃべるおさいふでは複数の LLMプロバイダのリクエスト形式に対応しつつ、コメント生成とフィルタリングの双方について、リリース作業なしでモデルやtemperatureなどの設定値を変更できる仕組みを備えています。また、ランダムに選択されるコメント種別、そのプロンプト、お客さまデータの組み合わせを宣言的に記述できるようにすることで、変更のしやすさも確保しています。</p> <h2>おわりに</h2> <p>本記事では、メルペイのPoC機能「しゃべるおさいふ」について、その概要と設計上のポイントを紹介しました。</p> <p>LLMモデルが強化され、幅広いタスクを汎用的にこなせるようになってきた今、LLMを使ったプロダクトの差別化にはUXの工夫がますます重要になっていると感じます。この「しゃべるおさいふ」プロジェクトで得られたLLM のプロダクト活用に関する知見を基盤に、今後もLLM をプロダクトにどのように組み込むべきかを継続して探求していきます。</p> <p>明日の記事はntkさんです。引き続きお楽しみください。</p> AI-Native 開発を加速する AWS Kiro の導入と、Okta を活用したアカウント管理の自動化https://engineering.mercari.com/blog/entry/20251211-4cfd1db1bf/https://engineering.mercari.com/blog/entry/20251211-4cfd1db1bf/<p>こんにちは。メルカリのバックエンドエンジニアの @amenbo と、プラットフォームチームの @siroken3 です。この記事は、この記事は、Merpay &amp; Mercoin Advent Calendar 2 [&hellip;]</p> Fri, 19 Dec 2025 11:00:45 GMT<p>こんにちは。メルカリのバックエンドエンジニアの @amenbo と、プラットフォームチームの @siroken3 です。この記事は、この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の19日目の記事です。</p> <p>メルカリでは AI-Native company を目指し、さまざまな AI エージェントの導入や運用改善に取り組んでいます。開発者の生産性向上もその一環であり、コーディング支援ツールの積極的な活用を進めています。そんな中、今年の7月にAWSからPreview公開された「Kiro」の試験導入を進めています。</p> <p>Kiro は従来の「vibe coding」を超えた、仕様書駆動の開発を実現する IDE です。ただ、新しいツールを組織に導入する際には、技術的な評価だけでなく、アカウント管理や課金管理といった運用面での課題にも向き合う必要があります。</p> <p>本記事では、メルカリが Kiro をどのように導入し、Okta と AWS IAM Identity Center を連携させたアカウント管理の自動化をどのように実現したかを紹介します。</p> <h2>Kiro とは</h2> <p><strong>Kiroの概要</strong></p> <p>Kiro は、2025年7月に AWS からパブリックプレビューとしてリリースされ、11月に一般提供(GA)が開始された spec-driven<br /> development を特徴とする agentic IDE です。VS Code のオープンソース版である Code OSS をベースに開発されています。</p> <p>従来の AI コーディングアシスタントは、その場その場で「なんとなくいい感じに」コードを生成する「vibe coding」と呼ばれるアプローチが主流でした。一方、Kiro は仕様書を中心に据えた開発フローを実現します。開発者は以下の3つのマークダウンファイルで構成される「Kiro Specs」を作成し、これを基に AI エージェントがコード生成や実装を支援します。</p> <ul> <li><code>requirements.md</code>: ユーザーストーリーと受け入れ基準を定義</li> <li><code>design.md</code>: 技術設計とアーキテクチャを記述</li> <li><code>tasks.md</code>: 実装タスクをチェックリスト形式で管理</li> </ul> <p><strong>Kiroの料金プラン</strong></p> <p>Kiro の有償プランを組織で利用するには、AWS の AI コーディングアシスタントサービスである AWS Q Developer の Pro サブスクリプションプランを使用します。具体的には、AWS IAM Identity Center を通じてユーザーを管理し、Q Developer Proのライセンスを割り当てる仕組みになっています。</p> <p>なお、2025年11月の GA 以降、Kiro は独自のサブスクリプション体系に移行しましたが、引き続き IAM Identity Center によるユーザー管理が必要です。このKiro 独自サブスクリプション体系への移行については、この記事の後半に述べます。</p> <h2>アカウント管理における課題と解決策</h2> <p><strong>既存のOkta連携基盤</strong></p> <p>組織で Q Developer Pro のライセンス管理を行うには、AWS IAM Identity Center との連携が不可欠です。メルカリでは Kiro の登場以前から、Okta と Identity Center の連携を構築しており、社内のさまざまな AWS リソースへのアクセス管理に活用していました。</p> <p>この連携では SCIM 2.0(System for Cross-domain Identity Management)プロトコルによる自動プロビジョニングを採用しています。Okta 上でユーザーやグループを追加・変更すると、その情報が自動的に Identity Center へ同期される仕組みです。</p> <p>Kiro のアカウント管理にもこの仕組みを活用しました。Okta に Kiro 利用者用のグループ( <code>AWS_kiro_JP_Users</code> )を作成し、そのグループに Q Developer Pro のサブスクリプションを割り当てます。これにより、Okta のグループにユーザーを追加するだけで、そのユーザは Kiro が利用可能になります。</p> <p>ただし、この時点では Okta へのユーザー追加自体は手動で行っており、利用申請があるたびに AWS 管理担当者がコンソールからユーザーを登録する運用でした。作業自体は数分で終わるものの、担当者の手が空いていなければ対応は後回しになり、申請者を待たせてしまうケースが発生しました。</p> <p><strong>AI エージェントによるアカウント発行の自動化</strong></p> <p>Okta との連携をさらに一歩進め、すでに Okta と連携している社内の AI エージェントを Kiro のグループを扱えるように設定し、申請プロセスそのものも自動化しました。</p> <p>ユーザーは Slack から「I want kiro」のように AI エージェントに伝えるだけで、以下のフローが自動的に実行されます。</p> <ol> <li>AI エージェントがリクエストを受け取る</li> <li>AI エージェントが Okta の API を呼び出し、ユーザーを Kiro グループ( <code>AWS_kiro_JP_Users</code> )に登録</li> <li>Okta が SCIM プロトコルで Identity Center にユーザー情報を同期</li> <li>Identity Center で Q Developer Pro アカウントが有効になる</li> <li>ユーザーが Kiro を使い始めた瞬間から従量制課金が開始される</li> </ol> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/c839d6e7-system_overview.png" alt="▲kiro アカウント申請の仕組み" /> ▲kiro アカウント申請の仕組み</p> </p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/ad65329b-agent.png" alt="▲AIエージェントを用いたアカウント申請" /> ▲AIエージェントを用いたアカウント申請</p> <p>この仕組みにより、AWS 管理担当者の手を一切煩わせることなく、ユーザーが自律的に Kiro を利用開始できるようになりました。</p> <h2>Q Developer Pro から Kiro への移行</h2> <p><strong>2025年11月のアップデートと移行の必要性</strong></p> <p>2025年11月、Kiro は大幅なアップデートを経て一般提供(GA)が開始されました。このアップデートにより、Kiro は Amazon Q Developer から独立した製品となり、独自のサブスクリプション体系が導入されました。</p> <p>先ほど説明した仕組みは GA 以前に実施したものであったため、既存の Q Developer Pro サブスクリプションを Kiro プランに移行する必要がありました。移行しない場合でも Q Developer Pro のサブスクリプションは引き続き有効ですが、Kiro 専用の新機能(Property-based Testing、Checkpointing など)は利用できない可能性があります。</p> <p>移行手順自体は非常に簡単で、AWS Management Console から対象グループ(今回の例では <code>AWS_kiro_JP_Users</code> )を選択し、希望の Kiro プランを指定するだけです。グループ単位またはユーザー単位で柔軟に移行でき、段階的な移行も可能です。</p> <p>なお、一度 Kiro サブスクリプションにアップグレードすると、Q Developer Pro には戻せません。</p> <h2>まとめ</h2> <p>本記事では、メルカリにおける AWS Kiro の導入と、Okta、AWS IAM Identity Center、AI エージェントを組み合わせたアカウント管理の自動化について紹介しました。</p> <p>手動運用から始まった Kiro のアカウント管理は、既存の Okta と Identity Center の SCIM 連携を活用し、さらに AI エージェントと組み合わせることで、完全に自動化されました。この取り組みにより、以下を実現できました。</p> <ul> <li><strong>管理負荷の大幅な削減</strong>: AWS 管理担当者の手作業がゼロに</li> <li><strong>スケーラブルな運用</strong>: ユーザー数が増えても運用コストが増加しない</li> <li><strong>セキュアなアクセス管理</strong>: Okta を中心とした統一された ID 管理とアクセス制御</li> </ul> <p>また、2025年11月の Kiro GA に伴う Q Developer Pro からの移行もスムーズに完了し、新しい課金体系のもとで運用を継続しています。</p> <p>今後、AI ツールの導入はさらに加速していくと考えられます。その際、技術的な評価だけでなく、運用面での自動化をどう実現するかが重要になってきます。本記事で紹介した Okta と Identity Center の連携パターンは、他の AI ツール導入においても参考になる取り組みではないでしょうか。</p> <p>メルカリは引き続き AI-Native company としての歩みを進めていきます。今後も、開発者の生産性向上とスケーラブルな運用を両立させる取り組みを続けていきたいと思います。</p> <p>明日の記事は @Yu Sasaki さんです。引き続きお楽しみください。</p> メルカリ内部の Dynamic Client Registration 活用事例https://engineering.mercari.com/blog/entry/20251218-26bcec59ba/https://engineering.mercari.com/blog/entry/20251218-26bcec59ba/<p>こんにちは。メルカリ IDP チームの @task(mima) です。本記事はメルカリアドベントカレンダー 2025 の 19 日目の記事です。 はじめに 株式会社メルカリには、グループ全体のシステムの認証と認可を統括す [&hellip;]</p> Fri, 19 Dec 2025 11:00:05 GMT<p>こんにちは。メルカリ IDP チームの <a href="https://x.com/task4233" title="@task(mima)">@task(mima)</a> です。本記事は<a href="https://engineering.mercari.com/en/blog/entry/20251126-mercari-advent-calendar-2025/" title="メルカリアドベントカレンダー 2025">メルカリアドベントカレンダー 2025</a> の 19 日目の記事です。</p> <h2>はじめに</h2> <p>株式会社メルカリには、グループ全体のシステムの認証と認可を統括する IdP が存在します。本記事では、この IdP における Dynamic Client Registration (以下 DCR とする) の活用事例をご紹介します。活用事例は大きく分けて以下の2つです。</p> <ol> <li>Terraform を用いた OAuth / OIDC クライアントの宣言的な管理</li> <li>MCP 認可フローにおける動的なクライアント生成</li> </ol> <p>これらの紹介のために、本記事では DCR と関連仕様について触れた後、それぞれの事例について背景とメルカリでの利用例を紹介します。</p> <p>いずれも認証認可の領域に携わっているエンジニア向けの題材ですが、調査した限りでは DCR 自体の利活用をしたケースは少なかったため、少しでも参考になれば幸いです。なお、認証認可における基本的な概念や用語については説明を割愛します。ご承知おきください。</p> <h2>従来の OAuth / OIDC クライアント管理の課題</h2> <p>メルカリの IdP は、メルカリグループの各サービスや、<a href="https://help.jp.mercari.com/guide/articles/1476/" title="メルカリと公式連携した越境EC事業者">メルカリと公式連携した越境EC事業者様</a>に応じて OAuth / OIDC クライアントを登録・管理する必要があります。これらの登録・管理に際して、従来以下のような手作業が必要でした。</p> <ul> <li>クライアント情報を登録・編集を目的とした IdP のデータベースに対する SQL 実行</li> <li>作成されたクライアント ID / シークレットの Vault での管理</li> </ul> <p>そのため、変更時の人的ミスやオペレーションコストが従来の課題でした。これらの課題を解決するために、OAuth / OIDC クライアントを機械的に登録・管理する仕組みが必要でした。</p> <p>そこで白羽の矢が立ったのが DCR でした。</p> <h2>Dynamic Client Registration (DCR) とは</h2> <p>DCR は、OAuth / OIDC クライアントを動的に登録するための標準仕様です。これにより、API 経由での登録・参照・更新・削除が可能になります。</p> <p>DCR に関連する仕様は以下の通りです。必要に応じて一次情報を参照してください。</p> <p><strong>クライアント登録</strong></p> <ul> <li>OAuth 2.0: <a href="https://datatracker.ietf.org/doc/html/rfc7591" title="RFC 7591 - OAuth 2.0 Dynamic Client Registration Protocol">RFC 7591 &#8211; OAuth 2.0 Dynamic Client Registration Protocol</a></li> <li>OIDC: <a href="https://openid.net/specs/openid-connect-registration-1_0.html" title="OpenID Connect Dynamic Client Registration 1.0 incorporating errata set 2">OpenID Connect Dynamic Client Registration 1.0 incorporating errata set 2</a></li> </ul> <p><strong>クライアント管理</strong></p> <ul> <li>OAuth 2.0: <a href="https://datatracker.ietf.org/doc/html/rfc7592" title="RFC 7592 - OAuth 2.0 Dynamic Client Registration Management Protocol">RFC 7592 &#8211; OAuth 2.0 Dynamic Client Registration Management Protocol</a></li> </ul> <p>続いて、クライアント登録およびクライアント管理のための概要を紹介します。</p> <h2>クライアント登録フロー</h2> <p><a href="https://datatracker.ietf.org/doc/html/rfc7591" title="RFC 7591">RFC 7591</a> で定義されている 抽象的な DCR のフローは以下の図 1 の通りです。</p> <pre><code class="language-plaintext"> +--------(A)- Initial Access Token (OPTIONAL) | | +----(B)- Software Statement (OPTIONAL) | | v v +-----------+ +---------------+ | |--(C)- Client Registration Request --&gt;| Client | | Client or | | Registration | | Developer |&lt;-(D)- Client Information ------------| Endpoint | | | +---------------+ +-----------+</code></pre> <p><center><b>図1: Abstract Dynamic Client Registration Flow(出典: <a href="https://datatracker.ietf.org/doc/html/rfc7591#section-1.3">RFC 7591 Section 1.3</a>)</b></center></p> <p>この図の「Client Registration Endpoint」は、クライアントを認可サーバーに登録できるように設計された OAuth 2.0 のエンドポイントです。そして、このエンドポイントとの <strong>(C) のリクエストと (D) のレスポンスがDCR のメイン処理</strong>です。(C) で「Client or Developer」が 「Client Registration Endpoint」 に対して、希望する登録メタデータを含むリクエストを送信します。認可サーバは、OAuth クライアントを登録し、(D) でクライアント ID、クライアントシークレット、登録メタデータなどを返却します。</p> <p>上記フロー図の <strong>(A) と (B) はオプショナルなセキュリティ機構</strong>です。</p> <p><strong>(A) Initial Access Token</strong></p> <ul> <li>「Client Registration Endpoint」 へのアクセスを制御するためのトークン</li> <li>誰でも自由にクライアントを登録できる状態を防ぐアクセス制御機構</li> </ul> <p><strong>(B) Software Statement</strong></p> <ul> <li>クライアントのメタデータを含む署名付き JWT</li> <li>事前承認されたソフトウェアであることを証明</li> <li>詳細は <a href="https://datatracker.ietf.org/doc/html/rfc7591#section-3.1.1" title="RFC 7591 Section 3.1.1">RFC 7591 Section 3.1.1</a> を参照</li> </ul> <p>これらは (C) のリクエストに含めて送信できます。</p> <p>具体的には、(C) と (D) の基本的なリクエストとレスポンスは以下のようになります。</p> <p><strong>リクエスト例:</strong></p> <pre><code>POST /register HTTP/1.1 Content-Type: application/json Accept: application/json Host: server.example.com { &quot;redirect_uris&quot;: [ &quot;https://client.example.org/callback&quot;, &quot;https://client.example.org/callback2&quot;], &quot;client_name&quot;: &quot;My Example Client&quot;, &quot;client_name#ja-Jpan-JP&quot;: &quot;クライアント名&quot;, &quot;token_endpoint_auth_method&quot;: &quot;client_secret_basic&quot;, &quot;logo_uri&quot;: &quot;https://client.example.org/logo.png&quot;, &quot;jwks_uri&quot;: &quot;https://client.example.org/my_public_keys.jwks&quot;, &quot;example_extension_parameter&quot;: &quot;example_value&quot; }</code></pre> <p><strong>レスポンス例</strong>:</p> <pre><code>HTTP/1.1 201 Created Content-Type: application/json Cache-Control: no-store Pragma: no-cache { &quot;client_id&quot;: &quot;s6BhdRkqt3&quot;, &quot;client_secret&quot;: &quot;cf136dc3c1fc93f31185e5885805d&quot;, &quot;client_id_issued_at&quot;: 2893256800, &quot;client_secret_expires_at&quot;: 2893276800, &quot;redirect_uris&quot;: [ &quot;https://client.example.org/callback&quot;, &quot;https://client.example.org/callback2&quot;], &quot;grant_types&quot;: [&quot;authorization_code&quot;, &quot;refresh_token&quot;], &quot;client_name&quot;: &quot;My Example Client&quot;, &quot;client_name#ja-Jpan-JP&quot;: &quot;クライアント名&quot;, &quot;token_endpoint_auth_method&quot;: &quot;client_secret_basic&quot;, &quot;logo_uri&quot;: &quot;https://client.example.org/logo.png&quot;, &quot;jwks_uri&quot;: &quot;https://client.example.org/my_public_keys.jwks&quot;, &quot;example_extension_parameter&quot;: &quot;example_value&quot; }</code></pre> <h2>クライアント管理</h2> <p><a href="https://datatracker.ietf.org/doc/html/rfc7592" title="RFC 7592">RFC 7592</a> は、登録されたクライアント情報(Client Metadata)を管理(参照 / 更新 / 削除)する仕組みを定義しています。表1に示す通り、クライアント ID をパスに含む REST API として構成することができます。</p> <p>表1: Client Metadata を管理する用途とリクエスト例</p> <table> <thead> <tr> <th>用途</th> <th>リクエスト例</th> </tr> </thead> <tbody> <tr> <td>参照</td> <td>GET /register/${clientID}</td> </tr> <tr> <td>更新</td> <td>PUT /register/${clientID}</td> </tr> <tr> <td>削除</td> <td>DELETE /register/${clientID}</td> </tr> </tbody> </table> <p>これらの仕様を踏まえた上で、活用事例の紹介に移りましょう。</p> <h2>活用事例1: Terraform を用いた OAuth / OIDC クライアントの宣言的な管理</h2> <h3>背景</h3> <p>前述の通り、従来はメルカリグループの OAuth / OIDC クライアントを作成・運用するために、データベースに直接 SQL を実行してクライアント情報を登録していましたが、以下のような課題がありました。</p> <ul> <li>変更履歴の追跡が困難</li> <li>レビュープロセスの欠如</li> <li>人的ミスによるセキュリティリスク</li> <li>複数環境(開発・ステージング・本番)での一貫性の維持が困難</li> </ul> <p>これらの課題に対処すべく、OAuth / OIDC クライアントを宣言的に管理する仕組みとして、Terraform と DCR を組み合わせた仕組みを導入しました。</p> <h3>実装概要</h3> <p>前提として、<a href="https://engineering.mercari.com/blog/entry/mercari-microservices-platform-terraform-0-12/" title="Mercari Microservices Platform における Terraform 0.12 対応 ">Mercari Microservices Platform における Terraform 0.12 対応 </a>で紹介されているように、メルカリでは社内のインフラストラクチャを microservices-terraform と呼ばれる1つのリポジトリで管理しています。この仕組みと DCR を組み合わせることで、Terraform の HCL (HashiCorp Configuration Language) で OAuth / OIDC クライアントを定義し、PR をマージすると自動的にクライアントが生成・更新・削除されます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/24c0e16b-untitled-diagram.drawio.png" alt="" /></p> <p><center><b>図2. Terraform を用いた OAuth / OIDC クライアントの宣言的な管理の概要図</b></center></p> <p>例えば、以下の図 3 に示す通り、Terraform の HCL で宣言すると Plan 結果が GitHub 上にコメントとして表示されます。後はマージするだけで自動的に OAuth / OIDC クライアントが作成される仕組みです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/0a5665cd-thoughts.png" alt="" /></p> <p><center><b>図3: 新しい OAuth / OIDC クライアント登録のための Terraform の HCL 表現とGitHub 上に表示される Terraform の Plan 結果</b></center></p> <p>この仕組みにより、以下のメリットが得られました。</p> <ul> <li><strong>変更履歴の可視化</strong>: Git によるバージョン管理</li> <li><strong>レビュープロセスの確立</strong>: PR ベースのワークフロー</li> <li><strong>自動化</strong>: マージ後の自動適用</li> <li><strong>IaC</strong>: 宣言的な管理による一貫性の確保</li> </ul> <h3>技術詳細</h3> <p>Terraform Custom Provider の実装などの技術詳細については、昨年の技術書典16で「OAuth 2.0 Client を Terraform Custom Provider で宣言的に管理してみた」と題して、<a href="https://techbookfest.org/product/4JE8riJdXX5y1vBEYq7v8L" title="Unleash Mercari Tech! vol.3">Unleash Mercari Tech! vol.3</a> に寄稿しておりますので、興味のある方はそちらをご覧ください。Terraform の Custom Provider 開発については、<a href="https://developer.hashicorp.com/terraform/plugin" title="公式ドキュメント">公式ドキュメント</a> も参考になります。</p> <h2>活用事例2: MCP 認可フローにおける動的なクライアント生成</h2> <h3>背景</h3> <p>メルカリでは、AI-Native な会社への変貌のため、様々な取り組みを実施しています。その取り組みの一環として Mercari MCP サーバを立ち上げました。この MCP サーバは、2025年11月13日に開催された <a href="https://gears.mercari.com/" title="mercari GEARS 2025">mercari GEARS 2025</a> にて、 ChatGPT からメルカリの機能を利用するデモが発表されました。</p> <p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/IIz0DH-wbKQ?si=OAr7FmOt5RtPjgPE&amp;start=1673" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p> <p>このデモでは、MCP サーバの認可のために DCR が利用されています。</p> <h3>メルカリでDCR を採用するに至った経緯</h3> <p>MCP の最新仕様である <a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization" title="Authorization - Model Context Protocol - Version 2025-11-25">Authorization &#8211; Model Context Protocol &#8211; Version 2025-11-25</a> を追っている方は、「DCR は <a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00" title="Client ID Metadata Document (CIMD)">Client ID Metadata Document (CIMD)</a> のフォールバックという位置付けなのでは?」と思われたかもしれません。その通りです。</p> <p>実際に、 <a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization" title="Authorization - Model Context Protocol - Version 2025-11-25">Authorization &#8211; Model Context Protocol &#8211; Version 2025-11-25</a> では、クライアント登録のアプローチは次の優先度に従うべき(SHOULD)とされています。 </p> <ol> <li>クライアント側で利用可能なサーバの事前登録済情報がある場合、それを利用</li> <li>認可サーバがサポートしている場合、<a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00" title="Client ID Metadata Document (CIMD)">Client ID Metadata Document (CIMD)</a> を利用</li> <li>認可サーバがサポートしている場合、フォールバックとして DCR を利用</li> <li>他に手段がない場合は、ユーザにクライアント情報の入力を催促</li> </ol> <p>DCR がこのような位置付けになっている理由として、以下の理由などが挙げられます。</p> <ul> <li>登録されるクライアントが無制限に増えるため</li> <li>登録エンドポイント自体が攻撃に利用されるリスクがあるため</li> <li>クライアントの有効性検証ができないため</li> </ul> <p>詳しくは、<a href="https://blog.modelcontextprotocol.io/posts/client_registration/" title="Evolving OAuth Client Registration in the Model Context Protocol | Model Context Protocol Blog">Evolving OAuth Client Registration in the Model Context Protocol | Model Context Protocol Blog</a> をご参照ください。</p> <p>この優先順位にもかかわらず、メルカリで DCR を選択した理由は以下の通りです。</p> <ol> <li><strong>実装時期の問題</strong>: MCP サーバの実装を開始した時点では、CIMD の仕様がまだ確定していなかったため(<a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization" title="Authorization - Model Context Protocol - Version 2025-06-18">Authorization &#8211; Model Context Protocol &#8211; Version 2025-06-18</a> では DCR は SHOULD 扱いでした)</li> <li><strong>既存実装の活用</strong>: メルカリの IdP では、Terraform による管理のために既に DCR を実装済みであり、新規実装コストが低かったため</li> </ol> <p>これらの理由で当時は DCR を採用しましたが、複数選択肢のある今、それぞれの長所短所を考慮して選定することが好ましいでしょう。</p> <h3>実装フロー</h3> <p>MCP サーバでの DCR 利用フローは以下の図 4 の通りです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/0ddb0524-mermaid-diagram-2025-12-18-182151.png" alt="" /></p> <p><center><b>図4: DCR を利用する場合の認可フローを表すシーケンス図</b></center></p> <p></p> <p>図 4 のフローは以下の通り実行されます。</p> <ul> <li>1.MCP クライアント(例: ChatGPT)がメルカリの MCP サーバへの接続を開始する</li> <li>2.MCP サーバは、WWW-Authenticate ヘッダとともにステータスコード 401 を返却する</li> <li>3-4. MCP クライアントは、MCP サーバから Protected Resource Metadata を取得し、認可サーバの情報を取得する</li> <li>5-6. MCP クライアントが 認可サーバのメタデータエンドポイントを呼び出して、DCR エンドポイントの情報を取得する</li> <li>7-8. MCP クライアントが、DCR エンドポイント経由で OAuth / OIDC クライアントを作成して、Client Metadata を取得する</li> <li>9-14-. MCP クライアントは、生成された Client Metadata を使用して認可サーバとの間で認可コードフローを実行し、アクセストークンを取得する</li> <li>15-16. MCP クライアントは、取得したアクセストークンでMCP サーバの機能にアクセスしてレスポンスを受け取る</li> </ul> <p>この仕組みにより、各 MCP クライアントに対して動的にクライアントを作成し、適切なスコープで認可を行うことが可能になりました。</p> <h2>おわりに</h2> <p>本記事では、DCR の活用事例として</p> <ol> <li>Terraform を用いた OAuth / OIDC クライアントの宣言的な管理</li> <li>MCP 認可フローにおける動的なクライアント生成</li> </ol> <p>の2点を紹介しました。</p> <p>DCR は IdP を内製化している組織だけでなく、外部 IdP を利用する場合でも有用な仕様です。一方で、MCP 認可フローにおいては先述した優先順位を鑑みて選定すると良いと思います。</p> <p>本記事が皆様の OAuth / OIDC クライアント管理の参考になれば幸いです。ここまでお読みいただきありがとうございました。</p> <p>明日の記事は @yuさんです。引き続きお楽しみください。</p> Payment Platform の 2025 年: メルカリグループを支える決済基盤のこれまでとこれからhttps://engineering.mercari.com/blog/entry/20251219-payment-platform-2025/https://engineering.mercari.com/blog/entry/20251219-payment-platform-2025/<p>こんにちは、Merpay の Payment Core チームで EM をしている komatsu です。普段はメルカリグループ全体の決済基盤や銀行接続まわりを担当しています。 この記事は Merpay &amp; Me [&hellip;]</p> Fri, 19 Dec 2025 10:00:59 GMT<p>こんにちは、Merpay の Payment Core チームで EM をしている komatsu です。普段はメルカリグループ全体の決済基盤や銀行接続まわりを担当しています。<br /> この記事は <a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の 19 日目の記事です。</p> <p>年末ということで、今回は Payment Platform がこの 1 年で取り組んできた 2 つの大きな進化の方向性を振り返ります。1 つは Checkout Solution による統合決済体験の提供、もう 1 つは 2B (法人向け) 決済への展開です。</p> <h2>背景: 決済ニーズの多様化</h2> <p>メルカリグループでは、C2C マーケットプレイスを起点としながらも、さまざまなプロダクトが展開されています。メルカリShops やメルカリNFT、さらには直近リリースした<a href="https://about.mercari.com/press/news/articles/20250930_crossborder/">メルカリ グローバルアプリ</a>など、2C の提供先が広がると同時に、スキマバイトアプリであるメルカリ ハロや広告事業であるメルカリAds、外部事業者がメルカリの商品をオファー経由で購入・再販する C2B (Consumer-to-Business) パートナーシップなど、2B の決済スキームも登場し、多様化しています。</p> <p>Payment Platform は、こうした多様な決済ニーズに応えるため、これまでマイクロサービスアーキテクチャによる決済基盤を構築し、gRPC API を通じて各プロダクトに決済機能を提供してきました。</p> <p>この 1 年では、さらに 2 つの軸で大きな進化を遂げています。</p> <h2>Checkout Solution &#8211; UI を含む統合決済ソリューションへ</h2> <h3>背景: gRPC API を超えた、統合ソリューションという選択肢</h3> <p>従来、Payment Platform は gRPC API を通じて決済のバックエンド機能を提供していました。各プロダクトチームは、この API を利用して独自に決済フローや UI を実装していました。</p> <p>この方式は柔軟性が高い一方で、いくつかの課題もありました。</p> <ul> <li><strong>開発負荷の重複</strong>: 新しい決済手段を追加する際、各プロダクトが個別に UI や決済フローを実装する必要がある</li> <li><strong>UX の分断</strong>: プロダクトごとに決済体験が異なり、統一された UX を提供しにくい</li> <li><strong>ローンチ速度</strong>: 新規プロダクトが決済機能を実装する際、フロントエンドからバックエンドまで一から構築する必要がある</li> </ul> <p>これらの課題を解決するため、gRPC API に加えて、<strong>UI を含む統合決済ソリューション</strong>という新しい選択肢を提供し始めました。これが Checkout Solution です。</p> <h3>Checkout Solution のアーキテクチャ</h3> <p>Checkout Solution は、決済のフロントエンド (UI) とバックエンド (API) を一体として提供する統合ソリューションです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/ee417b90-checkout_background.png" alt="" /></p> <p>上図のように、プロダクトは Checkout Solution の UI を組み込むだけで、決済手段の選択から決済完了までの体験を提供できます。バックエンドでは Payment Service をはじめとする既存の決済基盤と連携し、各決済手段の処理を実行します。</p> <p>詳しいアーキテクチャについては、以下の記事をご覧ください。</p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20250605-bf42ce60cf/">決済基盤の新たな挑戦: 決済チェックアウトソリューションの開発</a></li> <li><a href="https://engineering.mercari.com/blog/entry/20250606-7afd82e718/">チェックアウトソリューションのバックエンドアーキテクチャ</a></li> </ul> <p>プロダクトチームは Checkout Solution を組み込むだけで、決済フローや UI を一から実装する必要がなくなりました。新しい決済手段の追加も、Payment Platform 側で対応すれば全プロダクトで使えます。</p> <p>従来のプロダクトは引き続き gRPC API を使いながら、新しいプロダクトは Checkout Solution で迅速な開発とリリースを実現しています。</p> <h3>展開の歩み</h3> <p><strong>メルカリNFT での初導入</strong></p> <p>Checkout Solution は、まずメルカリNFT で初めて導入されました。新しいプロダクトのローンチにあたり、MVP として決済機能を素早く統合できることが実証されました。ユースケースごとの設定の切り替えや、プロダクト固有のコンポーネントの描画も含め、基礎となる機能が実現できました。</p> <p><strong>メルカリ グローバルアプリへの拡大</strong></p> <p>その後、先日公開されたメルカリ グローバルアプリでも Checkout Solution のサポートが始まりました。</p> <p>グローバルアプリでは、日本のお客さま向けに提供しているメルペイの残高やポイント、メルペイのあと払いといった決済手段ではなく、海外のお客さま向けのクレジットカード決済機能を新たに開発しました。Cross Border (XB; 越境販売) 事業に必要な決済機能も、Checkout Solution を通して提供することでスピーディーに立ち上げることができました。</p> <p>こうして、異なるプロダクト間で統一された決済体験を提供できるようになりました。</p> <p>また、このタイミングは Payment Platform にとって初めての日本円以外の決済ユースケースでした。<br /> 日本円前提だったシステムの多通貨対応も行い、決済やその裏にある会計や帳簿も含めて今後のグロースに耐えうる基盤に進化を遂げました。</p> <p>多通貨対応の詳細は近日公開予定の <a href="https://engineering.mercari.com/blog/entry/20251003-mercari-crossborder/">Guest post from FT payment platform — Engineering for Multi-Currency and Multi-Provider Payments</a> をお楽しみに。</p> <p><strong>メルカリモバイルへの導入、サブスクリプション対応: Mandate の導入</strong></p> <p>グローバルアプリとは別の新しい導入先として、既存のメルカリモバイルの決済方法登録画面の置き換えも行いました。<br /> これまでとは異なり、サブスクリプション型の決済スキーム対応のため、お客さま不在時に自動課金を行うための決済手段選択モードを追加し、<strong>Mandate</strong> (継続的な課金への同意) という概念を導入しました。これにより、タイミングに依らないより自由な決済スキームの構築が可能となりました。Mandate の詳細設計については以下の記事をご覧ください。</p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20251216-mandates-for-recurring-payments/">多様な支払い手段と継続課金を安全に扱う「Mandate」の設計</a></li> </ul> <h3>2C から 2B へも拡張可能なアーキテクチャ</h3> <p>また、Payment Platform には以前から、メルカリShops やメルペイ加盟店向けの 2B 精算の仕組みがありました。とはいえ、これらは法人が決済すると言うより、お客さまの決済によって資金が移動する先、というユースケースにとどめていました。そこにメルカリ ハロやメルカリAds が登場し、法人自身が決済を行うケース<a href="ハロではアルバイトの募集時に法人に紐づく与信枠を利用した「決済」が走る仕組みです">^1</a>も出てきたことで、2B 決済のニーズがさらに多様化してきています。</p> <p>Checkout Solution は現在 2C をメインに展開していますが、将来的にはこうした 2B 向けの決済でも活用できるアーキテクチャになっています。</p> <p>Payment Platform があらゆる決済の裏側に加えて、カスタマイズ可能な UI も提供することで、新しい決済手段を追加するたびに各プロダクトが個別に開発する必要がなくなり、統一した UX をお客さまや加盟店に届けられます。</p> <h2>2B 決済への展開</h2> <h3>2C 決済と 2B 決済の違い</h3> <p>Checkout Solution でも 2B プロダクトのサポートを視野に入れているように、メルカリ ハロやメルカリAds をはじめとして、メルカリグループでは 2B の決済ニーズも増えています。</p> <p>2B の決済体験を考えるうえで重要なのは、2C との違いを理解することです。<br /> 特に 2C と 2B では、アカウントや決済の座組が大きく異なります。</p> <p><strong>2C: シンプルな構造</strong></p> <ul> <li>1 人 = 1 アカウント</li> <li>与信、請求、決済の単位が基本的に一致</li> </ul> <p><strong>2B: 複雑な構造</strong></p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/1726c47a-tree-structure.png" alt="" /></p> <ul> <li>法人の下に支社・店舗・部署が存在し、木構造での管理が必要</li> <li>与信、請求、決済の単位が異なる場合がある (例: 与信は店舗別、請求は支社単位)</li> <li>座組が複雑で、柔軟な対応が求められる</li> <li>インボイス制度など 2B 固有の要件が存在</li> </ul> <h3>2B 決済のユースケース</h3> <p>メルカリグループでは、すでにいくつかの 2B 決済のユースケースが存在しています。</p> <p><strong>メルカリ ハロ</strong></p> <p>「メルカリ ハロ」では事業者掛け払いというスキームを提供していました。メルカリが事業者の代わりにクルーに仕事完了時に給料を支払い、月末締め翌月払いで事業者から給与および手数料を回収する仕組みです。</p> <p>詳しくは以下の記事をご覧ください。</p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20241221-invoice-payment/">メルカリ ハロにおける事業者請求払いの内製化</a></li> </ul> <p><strong>メルカリAds</strong></p> <p>メルカリAds では、パートナー企業の広告をメルカリアプリに掲載し、月末締め翌月払いを請求書経由で行う仕組みを提供しています。</p> <p><strong>C2B 決済</strong></p> <p>C2B パートナーシップでは、外部事業者がメルカリの商品をオファー経由で購入・再販するビジネスモデルを提供しています (<a href="https://about.mercari.com/press/news/articles/20241219_kaitorirequest/">大黒屋とのパートナーシップ事例</a>)。</p> <p>メルカリ出品者が販売しても売れ残った商品に対して、パートナー企業から買取リクエストが送信されます。出品者が承諾すると、メルカリが出品者から商品を購入し、その後パートナー企業に販売する C2B モデルです。</p> <p>パートナー企業からメルカリへの決済は、与信に基づくオファーで与信枠内で行われ、月末締め翌月払いで請求されます。現在、パートナー数が限定的であることから与信枠の管理は Finance チームがスプレッドシートを中心に手動で行っています。</p> <h3>2B 決済基盤に求められる要素</h3> <p>2B の決済基盤、特に事業者請求払い (掛け払い) のような与信を伴う決済では、以下の要素が不可欠となります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2024/12/030d3444-relations.png" alt="" /></p> <p>上図のように、決済から入金、会計処理まで一連のフローを支える必要があります。</p> <ol> <li><strong>決済および債権の管理</strong>: 決済実行と売掛金の計上</li> <li><strong>与信管理</strong>: 事業者ごとの与信枠の設定と残高管理、木構造での柔軟な管理</li> <li><strong>請求書の発行・送付・消込</strong>: 適格請求書の自動発行と送付、入金管理と売掛金の消し込み (入金消込)、事業者ごとに異なる管理単位への対応</li> <li><strong>入金の受け取り</strong>: 入金管理</li> <li><strong>精算処理</strong>: 月次での精算と会計処理</li> <li><strong>督促</strong>: 未払いに対する督促処理</li> </ol> <h3>これまでの取り組み</h3> <p>Payment Platform では、これらの要素を段階的に構築してきました。</p> <p><strong>外部サービスの活用</strong></p> <p>メルカリ ハロやメルカリAds のローンチ時には、外部企業が提供する掛け払いサービスを採用しました。ローンチの速度や与信管理の容易性・貸倒リスクを加味した結果、与信審査や貸倒リスクを外部に委譲し、スピーディーにビジネスを立ち上げることにフォーカスしました。Payment Platform としては、これまでの決済 API のインターフェースを保ったまま法人の与信を利用した決済手段である Invoice Payment を Payment Service に導入しました。</p> <p><strong>一部内製化と手動運用の組み合わせ</strong></p> <p>メルカリAds では一部の企業に対して、外部サービスを使わない内製掛け払いソリューションを提供しました。決済時に Debt Service で債権を計上し、月次で Settlement Service を通じて精算する仕組みです。利用先を限定していたため、請求書の発行や消込は手動運用で対応しました。このタイミングでは、中長期の内製化や掛け払いプロバイダの多様化を見越して、外部サービスの掛け払いを利用する場合と内製掛け払いを利用する場合を抽象化した概念として Invoice Payment API を提供しました。</p> <p>この取り組みにより、既存の Payment Service と Debt Service を活用した 2B 決済の基礎が築かれました。</p> <p><strong>Invoice Service のローンチ</strong></p> <p>2025 年には、2B に対する請求書の発行・管理・送付などを行う <strong>Invoice Service</strong> システムをローンチしました。これはメルカリAds やその他 2B プロダクトで内製掛け払いに移行していく際に必要となるだけでなく、メルカリ &#8211; メルペイ間の精算など、他のビジネスでも利用するための基盤として構築されました。</p> <p><strong>ここまでで実現できたこと</strong></p> <p>これまでの取り組みにより、事業者請求払いに必要な要素のうち、以下がシステムとして整いました。</p> <ul> <li>✅ 決済トランザクションの管理 (Payment Service)</li> <li>✅ 債権の管理 (Debt Service)</li> <li>✅ 請求書の発行・送付・消込 (Invoice Service)</li> <li>✅ 精算処理 (Settlement Service)</li> <li>✅ バーチャル口座 (Virtual Account) による入金管理 (Bank Service)</li> <li>❌ <strong>与信管理</strong> ← まだシステム化されていない</li> <li>❌ <strong>督促</strong> ← まだユースケースがない</li> </ul> <p>重要なのは、これらの多くがこれまでのメルカリのプロダクトのために構築した既存マイクロサービスを活用できている点です。汎用性の高い設計が、新しい決済スキームへの迅速な対応を支えています。</p> <h3>これから: 与信管理の内製化</h3> <p>前述の通り、現在 C2B の与信枠は Finance チームがスプレッドシートで手動管理しています。パートナー数が限定的な間はこの運用で対応できていますが、規模拡大に向けてシステム化が必要です。</p> <p>手動管理では与信残高の即時更新が困難で、ヒューマンエラーのリスクや運用負荷が増大します。また C2B、メルカリAds など複数プロダクトで独自に与信管理を行うと、メンテナンスコストが増大し一貫性も保ちにくくなります。同じ法人が複数プロダクトを利用する場合、与信枠の重複管理や過剰付与のリスクもあります。</p> <p><strong>与信管理サービスのシステム化</strong></p> <p>これらの課題を解決するため、統一的な与信管理基盤をシステム化する計画があります。<strong>与信管理サービス</strong>として、リアルタイムの与信残高照会・更新、複数事業で再利用可能な汎用設計、法人・支社・店舗といった木構造での柔軟な管理、既存決済基盤との一貫した会計処理を提供していく予定です。</p> <p>与信管理が内製化されると、以下のように Payment Platform のマイクロサービス群が連携して 2B 決済を支えることになります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/b732ae13-architecture.png" alt="" /></p> <p>この図から見えるのは、<strong>既存マイクロサービスと新規サービスを組み合わせて全体のソリューションを構築している</strong>点です。</p> <p>これまでに構築してきた Payment Service、Debt Service、Settlement Service、Bank Service といった共通サービスは、それぞれが明確なドメイン責務を持つため、新しいユースケースでもそのまま活用できます。2B 特有の与信管理や請求書発行だけを新しいサービスとして追加し、請求書送付には Payment Platform 外の Notification Service を活用します。</p> <p>各サービスは API を通じて疎結合に連携し、Payment Service が決済のオーケストレーションを担うことで、全体として 2B 決済ソリューションを実現しています。このアーキテクチャにより、新しい決済スキームにも柔軟に対応できる拡張性を持っています。</p> <h2>Payment Platform が目指す姿</h2> <p>Payment Platform は、メルカリグループにおける既存サービスのグロース・新規サービスのローンチを簡単に・早く・効率的にできるようにすることを目指しています。</p> <ul> <li><strong>スピード</strong>: 新規プロダクトが決済機能を素早く統合できる<br /> Checkout Solution のような統合ソリューションを提供することで、プロダクトチームは決済フローや UI を一から実装する必要がありません。</li> <li><strong>効率</strong>: 各プロダクトの開発負荷を削減<br /> 新しい決済手段を追加する際、Payment Platform 側で対応すれば、すべてのプロダクトで使えるようになります。各プロダクトが個別に実装する必要がなくなり、開発効率が向上します。</li> <li><strong>一貫性</strong>: 統一された UI/UX の提供<br /> UI を含む統合ソリューションを提供することで、異なるプロダクト間でも統一された決済体験を届けられます。</li> <li><strong>拡張性</strong>: 2C から 2B まで、あらゆる決済シーンをカバー<br /> 2B の決済ニーズをキャッチしながら機能を育て、汎用的なソリューションにすることで、メルカリグループの多様な決済ニーズに応えていきます。</li> </ul> <h2>まとめ</h2> <p>この 1 年、Payment Platform は 2 つの大きな軸で進化してきました。</p> <p>1 つ目は <strong>Checkout Solution による統合決済体験の実現</strong>です。gRPC API に加えて、UI を含む統合ソリューションという新しい選択肢を提供することで、プロダクトチームの開発負荷を削減し、統一された UX を提供できるようになりました。また、多通貨対応も合わせて行い、今後のグローバルなプロダクト展開に耐えうる決済基盤の仕組みも加わりました。</p> <p>2 つ目は <strong>2B 決済への展開</strong>という新たな挑戦です。2B 決済は始まったばかりの領域ですが、既存の 2C で培った強固なマイクロサービス群を活用しながら、段階的に基盤を構築しています。与信管理サービスによる与信管理の内製化により、2B 決済基盤が完成に近づいていきます。</p> <p>Payment Platform は、これまで多様な決済ニーズに応えてきた実績を基盤に、今後もメルカリグループの成長を決済で支えていきます。</p> <p>明日の記事は kobaryo さんです。引き続きお楽しみください。</p> <p><strong>関連記事</strong></p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20250605-bf42ce60cf/">Checkout Solution を活用したメルカリの決済体験</a></li> <li><a href="https://engineering.mercari.com/blog/entry/20250606-7afd82e718/">Checkout Solution: Backend 編</a></li> <li><a href="https://engineering.mercari.com/blog/entry/20251216-mandates-for-recurring-payments/">Mandates for Recurring Payments: サブスクリプション決済を支える仕組み</a></li> <li><a href="https://engineering.mercari.com/blog/entry/20241221-invoice-payment/">メルカリ ハロにおける事業者請求払いの内製化</a></li> <li><a href="https://about.mercari.com/press/news/articles/20241219_kaitorirequest/">大黒屋とのパートナーシップ</a></li> </ul> Kubernetes環境におけるパケットキャプチャhttps://engineering.mercari.com/blog/entry/20251218-capturing-network-packets-in-kubernetes/https://engineering.mercari.com/blog/entry/20251218-capturing-network-packets-in-kubernetes/<p>こんにちは。メルカリのPlatform Network team/SREの@mshibuyaです。 この記事は、Mercari Advent Calendar 2025の18日目の記事です。 年の瀬って忙しくなりがちです [&hellip;]</p> Thu, 18 Dec 2025 11:00:19 GMT<p>こんにちは。メルカリのPlatform Network team/SREの<a href="https://x.com/m4buya">@mshibuya</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a>の18日目の記事です。</p> <p>年の瀬って忙しくなりがちですよね。ただでさえ忙しいのに、Advent Calendar記事執筆の予定まで入れてしまい、半泣きでこの記事を書いています。いや、こうなるのはわかってはいたんですが、一年を振り返ってアウトプットの少なさに焦ってしまうとやはり記事執筆に名乗りをあげてしまうのですよね…。</p> <p>さて、今回はKubernetes環境におけるネットワークのパケットキャプチャの話をします。筆者は現在前述の通り<a href="https://engineering.mercari.com/blog/entry/20220209-introduction-of-the-network-team/">Network team</a>に所属しており、そこではメルカリのプロダクト開発を支える様々なPlatformとしてのコンポーネント群のうち、ネットワークに関わるものを中心に扱っています。</p> <p>メルカリでは数百を超える規模のマイクロサービスが稼働しており、そのサービス内外におけるネットワークコミュニケーションも複雑多岐にわたります。Network teamはその性質上、そういった環境で発生したネットワークに起因すると思われる不具合・問題の調査を依頼されることが多くあります。もちろん単純な設定不備などが原因の場合もありますが、問題が複雑で解決の糸口がなかなか見出せないような局面において、深い分析を行う手段を必要としていました。そこでパケットキャプチャが登場します。</p> <p>こういった問題調査のための手法は、それ自身の実行手順が明確になっていなければいざ問題が発生した際に役立てることはできません。緊急度の高い障害が発生しているような状況下であればなおさらでしょう。今回確立したこの手法は、みなさまの環境においてそのまま適用可能とは限りませんが、それでも安定して実行できる調査手順を紹介しておくことは、みなさまがご自身の組織で同様の手順を作る際にも役立つであろうと考えたのがこの記事を公開する狙いです。</p> <h2>なぜKubernetesでのパケットキャプチャは難しいのか</h2> <p>みなさまご存知の通り、KubernetesはハードウェアやOSといった様々なレイヤにおいて抽象化を提供しており、それによって開発者が生のリソースに煩わされることなくワークロードを実行可能な環境を提供しています。セキュリティ上、一般には利用者である開発者は生のノードにはアクセスできないようになっており、またその上で動くPodのようなワークロードもMulti-tenancyとして互いに隔離されています。そのため、昔のように単純にサーバ上でtcpdumpを叩けばおっけー、とはいかないわけですね。</p> <p>また、Service Meshにまつわる難しさもあります。メルカリではIstioを導入していますが、クラスタ内の通信は基本的にmTLSによって暗号化されているため、そのままでは通信の中身を見ることができません。この点を考慮した手法を確立する必要があります。</p> <p>こうしたKubernetesクラスタを含め、開発者が機能を簡単に素早くデリバリーするために必要な道具立て一式を提供しているのが我々Platformとしての立ち位置です。このようなネットワークにおける深いトラブルシューティングの必要性がいつ生じるかは予想できません。特別なPlatform特有の権限によることなく、必要であれば開発者がセルフサービスによってこのような調査を行えることも大事な要件としました。</p> <h2>Ephemeral Containersを活用したPodレベルでのキャプチャ</h2> <p>前述の条件を満たす手法として我々が確立したのが、Kubernetesの機能である<a href="https://kubernetes.io/docs/concepts/workloads/pods/ephemeral-containers/">Ephemeral Containers</a>を活用した手法です。</p> <p>Ephemeral Containers(エフェメラルコンテナ)は、Kubernetes v1.25でGAとなった機能です。ノード全体へのアクセス権限を持つことなく、実行中のPodのネットワーク名前空間などの共有リソース内に、一時的なデバッグ用コンテナを「外付け」する形でアタッチできます。これにより、tcpdumpなどのデバッグツールをアプリケーションコンテナ内に含める必要なくトラブルシューティングを行えるため、このようなパケットキャプチャに用いるにはうってつけです。NodeやCluster全体への特別な権限を必要としない点も大きなメリットで、Platformメンバーも開発者も同じ方法によって調査を行うことができます。</p> <p>具体的な手順としては、下記のようになります。</p> <h3>Step 1. 必要な権限の取得</h3> <p>メルカリでは、<a href="https://engineering.mercari.com/en/blog/entry/20220201-promote-zero-touch-production-further-features-of-carrier/">Carrier</a>と呼ばれる一時的な権限取得を可能にする内製ツールによって、通常時は本番環境に対する操作権限を持たないZero Touch Productionを実現しています。<br /> このため、本番環境における問題調査においてパケットキャプチャを実施する際には、対象となるPodに対する操作権限を持つRoleをまず取得する必要があります。</p> <p>上記Roleには、Ephemeral Containersに対する操作を行うために必要なpermissionを事前に付与しておきます。</p> <pre><code class="language-yaml">apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: example-role rules: # ... - apiGroups: [&quot;&quot;] resources: [&quot;pods/ephemeralcontainers&quot;] verbs: [&quot;create&quot;, &quot;delete&quot;, &quot;deletecollection&quot;, &quot;patch&quot;, &quot;update&quot;]</code></pre> <h3>Step 2. Ephemeral Containerの起動</h3> <p>権限が得られたら、対象となるPodに対してEphemeral Containerをアタッチします。ここではパケットキャプチャも含む、ネットワーク全般のトラブルシューティングに対応したリッチなツールセットがインストール済みのnetshootを用いています。</p> <pre><code class="language-bash">kubectl debug -it -n &lt;your-namespace&gt; &lt;target-pod&gt; \ --image=nicolaka/netshoot \ --custom=./root.yaml --container=netshoot-debug</code></pre> <p>ここで、ファイル <code>./root.yaml</code> には事前に以下の内容を準備しておきます。</p> <pre><code class="language-yaml">securityContext: runAsUser: 0 runAsNonRoot: false</code></pre> <p>これにより、コンテナ内でtcpdumpを実行するための必要条件である「rootとしてnetshootコンテナを実行する」を実現します。大して長い内容ではないので、本当はコマンドラインにインラインで書いてしまいたいのですが、今のところkubectl debugの引数としてはファイルを渡すことしかできないようです…。</p> <h3>Step 3. キャプチャの実施</h3> <p>無事にnetshootコンテナのシェルが開いたら、キャプチャをスタートできます。ここではファイル <code>/tmp/capture.pcap</code> に書き込んでいます。</p> <pre><code class="language-bash">tcpdump -i any -w /tmp/capture.pcap</code></pre> <p>Istioが有効化された環境化においては、この <code>-i any</code> がポイントとなります。トラフィックは eth0 だけでなく、iptablesによってリダイレクトされた仮想インターフェースをも通るため、これらを取りこぼさないように全インターフェースを対象にしています。eth0のみをキャプチャすると、mTLSによって暗号化済みの内容しか取れないため、調査の目的に対し不足することが多いでしょう。</p> <p>単純に全トラフィックをキャプチャするとデータが膨大となってしまい得ます。ここでは詳細は触れませんが、tcpdumpのオプションによってキャプチャするパケットをフィルタできるため、調査しようとしている問題に関係するパケットにできるだけ絞り込んでキャプチャするとその後の調査が楽です。もちろん、絞り込みすぎると「必要なデータが取れてなかった」というトレードオフになります。</p> <h3>Step 4. ファイルの回収</h3> <p>上記によってEphemeral Containerにファイルが作成されるので、ローカルよりkubectl cpでダウンロードすれば完了です。Step 2でつけたコンテナ名を指定するのをお忘れなく。<br /> これでキャプチャしたデータの分析に移れます。</p> <pre><code class="language-bash">kubectl cp -n &lt;your-namespace&gt; &lt;target-pod&gt;:/tmp/capture.pcap ./capture.pcap -c netshoot-debug</code></pre> <p>慣れてきたら、Step 2-4までをワンラインで行ってしまってもいいでしょう。このような見た目になります。余計な出力がファイルに混入しないよう <code>-iq</code> を使い、また標準エラー出力を捨てています。 <code>-G 10</code> でキャプチャを行う秒数を指定しています。</p> <pre><code class="language-bash">kubectl -n &lt;your-namespace&gt; debug &lt;target-pod&gt; -iq --image=nicolaka/netshoot --custom=./root.yaml -- bash -c &#039;tcpdump -i any -G 10 -W 1 -s0 -w - 2&gt;/dev/null&#039; &gt; tcpdump.pcap</code></pre> <h2>Nodeレベルでのキャプチャ</h2> <p>上記Podレベルでのキャプチャ方法とは別に、GKE NodeにSSHし<a href="https://docs.cloud.google.com/container-optimized-os/docs/how-to/toolbox">CoreOS Toolbox</a>を用いてパケットキャプチャを行う手順も整備済みです。が、Nodeに対しSSHを行える権限を持つ必要があること、また前述のようにIstioのトラフィックは暗号化されたものしか取得できないこともあり、あくまで補助的な位置づけです。主にPlatformメンバーがNodeレベルでしか観測できないようなトラブルシューティングに用いることを想定しています。</p> <h3>Step 1. 必要な権限の取得</h3> <p>メルカリではKubernetesクラスタをGoogle Kubernetes Engineによって構築・運用しています。まず、GKEノードにSSHするために必要な権限を、前掲のCarrierを用いて取得する必要があります。該当プロジェクトに対し下記の権限があれば十分なはずです。</p> <ul> <li><code>roles/compute.instanceAdmin.v1</code></li> <li><code>roles/iam.serviceAccountUser</code></li> <li><code>roles/iap.tunnelResourceAccessor</code></li> </ul> <h3>Step 2. Nodeの特定</h3> <p>kubectl get podコマンドで、対象Podがホストされているノード名を確認します。</p> <pre><code class="language-bash">$ kubectl get pod -n &lt;your-namespace&gt; your-app-pod-7f5b7f7d9f-abcde -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES your-app-pod-7f5b7f7d9f-abcde 2/2 Running 0 2d1h 10.1.2.3 gke-cluster-1-node-pool-1-a1b2c3d4 &lt;none&gt; &lt;none&gt;</code></pre> <h3>Step 3. toolbox環境に入る</h3> <p><code>gcloud compute ssh</code> を使用してノードにSSHし、<code>toolbox</code> コマンドで、デバッグツールが揃ったシェル環境に入ります。</p> <pre><code class="language-bash">gcloud compute ssh --project &lt;your-project&gt; gke-cluster-1-node-pool-1-a1b2c3d4</code></pre> <pre><code class="language-bash"># On the GKE node $ toolbox</code></pre> <h3>Step 4. キャプチャの実施</h3> <p>toolboxシェル内でtcpdumpを実行します。ホストのルートファイルシステムは <code>/media/root</code> にマウントされているため、キャプチャファイルはノードの <code>/tmp</code> に相当する <code>/media/root/tmp/</code> に保存します。<code>-i any</code> で全インターフェースからキャプチャを行うことを指定し、フィルタとしてStep 2で確認したPodのIPアドレスを指定します。</p> <pre><code class="language-bash"># Inside the toolbox shell $ tcpdump -i any -w /media/root/tmp/node_capture.pcap host 10.1.2.3</code></pre> <h3>Step 5. ファイルの回収</h3> <p><code>toolbox</code> シェル (<code>exit</code>)、SSHセッション (<code>exit</code>) の順で終了し、ローカルマシンからgcloud compute scpでファイルを手元にコピーします。</p> <pre><code class="language-bash">gcloud compute scp --project &lt;your-project&gt; gke-cluster-1-node-pool-1-a1b2c3d4:/tmp/node_capture.pcap ./node_capture.pcap</code></pre> <p>こちらのNodeレベルでのキャプチャはまだ実際の調査で利用した実績はありませんが、こうして手順として整備しておけばいざ問題が発生した際も落ち着いて調査に入れます。</p> <h2>まとめ</h2> <p>この記事では、メルカリにおけるKubernetesパケットキャプチャのプラクティスについて紹介しました。 とりわけPodレベルにおいては、Ephemeral Containersを活用することで、セキュリティと利便性のバランスを取りながら開発者が自身でトラブルシューティングを行える手順を整えています。</p> <table> <thead> <tr> <th style="text-align: left;"></th> <th style="text-align: left;">Podレベル (Ephemeral Containers)</th> <th style="text-align: left;">Nodeレベル (Toolbox)</th> </tr> </thead> <tbody> <tr> <td style="text-align: left;"><strong>主な用途</strong></td> <td style="text-align: left;">アプリケーション固有の問題調査、mTLS化された通信内容の確認</td> <td style="text-align: left;">Node全体に関わるネットワーク問題(CNI、iptablesルールなど)の調査</td> </tr> <tr> <td style="text-align: left;"><strong>必要な権限</strong></td> <td style="text-align: left;">比較的低い (Podレベルの権限)</td> <td style="text-align: left;">高い (NodeへのSSHアクセス権限)</td> </tr> <tr> <td style="text-align: left;"><strong>Istio環境でのトラフィックの可視性</strong></td> <td style="text-align: left;">暗号化前の平文トラフィックをキャプチャ可能</td> <td style="text-align: left;">暗号化後のトラフィックしかキャプチャできない</td> </tr> <tr> <td style="text-align: left;"><strong>対象の絞り込みやすさ</strong></td> <td style="text-align: left;">調査対象のPodに直接アタッチするため、トラフィックの特定が容易</td> <td style="text-align: left;">複数Podが稼働するため、対象Podのトラフィックを分離するのが比較的困難</td> </tr> <tr> <td style="text-align: left;"><strong>推奨される利用者</strong></td> <td style="text-align: left;">アプリケーション開発者、SRE</td> <td style="text-align: left;">Platformチーム、SRE</td> </tr> <tr> <td style="text-align: left;"><strong>セルフサービスへの親和性</strong></td> <td style="text-align: left;">高い (開発者が自身で調査可能)</td> <td style="text-align: left;">低い (強い権限が必要なため限定的)</td> </tr> </tbody> </table> <p>今回ご紹介した内容をさらに発展・一般化したお話を来年3月開催の<a href="https://www.usenix.org/conference/srecon26americas">SRECon26 Americas</a>において「It&#8217;s Not Always the Network (But Here&#8217;s How to Prove It): Kubernetes Packet Capture for SREs」というタイトルで発表予定です。いや、まだ実感がないというか信じられないのですが、ProposalがAcceptされたという連絡はいただいたので、登壇しにシアトルに行ってくると思います…。</p> <p>パケットキャプチャを行った次のステップとしては、実際にキャプチャしたデータを分析するフェーズが待っています。紙面の都合上、また筆者がその分野ではまだまだ修行中ということもあり今回は触れませんでしたが、今後またなんらかの知見を共有していけたらと思います。</p> <p>最後までお読みいただきありがとうございました。明日の記事はamenboさん・siroken3さんによる「AI-Native開発を加速する AWS Kiro の導入と、Okta を活用したアカウント管理の自動化」となります!引き続きお楽しみくださいー</p> メルペイでの3ヶ月間:非同期処理への移行とASDD開発の学びhttps://engineering.mercari.com/blog/entry/20251217-ce2057d0ad/https://engineering.mercari.com/blog/entry/20251217-ce2057d0ad/<p>はじめに こんにちは。メルペイの Balance チームでインターンをしている @minato です。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の18日目の記事です [&hellip;]</p> Thu, 18 Dec 2025 10:00:09 GMT<h2>はじめに</h2> <p>こんにちは。メルペイの Balance チームでインターンをしている <a href="https://linkedin.com/in/湊士-山口-60b86b277">@minato</a> です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の18日目の記事です。</p> <p>2025年10月からメルペイでバックエンドのインターンを始め、3ヶ月目になります。<br /> 本記事では、インターン期間中に取り組んだタスク、得ることのできた学びについて紹介します。</p> <h2>取り組んだタスク</h2> <p>今回のインターンでは、債権データを管理するマイクロサービスにおいて、これまで同期的に行っていた債権残高の更新処理を非同期処理へと移行するタスクに取り組みました。<br /> 以下、非同期処理へ移行するに至った背景、設計、実装、負荷試験について説明していきます。</p> <h3>背景</h3> <p>メルペイでは現在、世界共通のプラットフォームを提供する「<a href="https://about.mercari.com/press/news/articles/20250930_crossborder/">メルカリ グローバルアプリ</a> 」プロジェクトが進められています。これまで国ごとに限定されていたプロダクトをグローバルに展開するにあたり、これまでになかった量の Transaction が集中することが想定され、負荷分散機能の検討・開発が必要となりました。</p> <p>このような理由から、僕は、負荷分散を必要とする機能の一つである債権の作成機能の改善に取り組みました。<br /> 債権データの作成プロセスでは、債権を作成した後、該当する債務者の債権残高を更新する必要があります。なので、債権データの作成リクエストが連続して行われた際、特定の行やカラムに更新が集中してしまい、その箇所が hotspot となり、以下の問題を起こします。</p> <ul> <li>高負荷が特定の行に集中し、ロック競合が多発する</li> <li>更新待ちが発生し、スループットが低下する</li> </ul> <p>この問題を解決するために、債権残高をリクエスト中に同期的に更新するのではなく、非同期で更新することにしました。非同期化することで、本来複数の Transaction で処理されるはずの複数の Request による債権残高の更新を、一つの Transaction でまとめて処理することができ、hotspot 問題を解決することができます。</p> <h3>設計</h3> <p>大まかな設計としては、債権残高の更新処理が発生した際に債権更新イベントテーブルへレコードを作成し、worker がそのテーブルを取得して Job Queue に push、Job Processor が Job Queue 内のアイテムに基づいてアカウントを更新するという流れとしました。</p> <p>また、Job Queue については、債権更新イベントテーブルに、Job Queue に入っているかを表すフィールドを設けることで、DB を Job Queue として使用できる仕組みとしました。なぜイベントの管理を、外部サービスではなく DB で管理としたのかというと、外部サービスへの依存を避けられること、同様の処理を行うマイクロサービスにおいて DB で管理する構成で正常に機能しているためになります。<br /> 以下の画像は、非同期イベントをどのように処理しているのかについて表したものです。Workerは DB から処理されていないイベントレコードを取得し、server上のメモリ(以下の図の Queue) に保存します。その後、Job Processor が Queue 内のイベントをまとめて処理するという構成になります。</p> <p>実装では、Worker、Queue、Job Processor のそれぞれについてカスタマイズしやすい struct を定義し、他の非同期処理において再利用できるようにしました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/eef4978d-screenshot-2025-12-15-at-16.25.04.png" alt="" /></p> <h3>実装</h3> <p>今回の実装では、社内で活用が推進されている ASDD(Agent Spec Driven Development) という方法を使用しました。ASDD とは、AI agent が仕様書を作成し、別agentがその仕様書に基づいて実装を進めるという手法です。詳しくは、<a href="https://engineering.mercari.com/blog/entry/20251201-pj-double-towards-ai-native-development/">pj-double: メルカリの開発生産性向上に向けた挑戦 — AI-Native化が辿り着いたASDDとプロセス変革の全貌</a> の記事をご覧ください。<br /> アカウントの非同期化は、システム全体に関わる非常に大きな機能改善であったため、厳密に設計をDocsとして記録すること、レビューのしやすさを考慮し、仕様書自体は僕が手作業で作成したため、時間を消費してしまいましたが、その後の実装はあっという間にAIによって完了し、とても驚きました。<br /> 「もし社内の全員が ASDD を使いこなす未来が来たらどうなるのか」 と想像するだけでワクワクする体験でした。</p> <h3>負荷試験</h3> <p>負荷試験については、まだ行っていないため、これから取り組む予定のことを説明します。今回の負荷試験では、想定される平均リクエスト数と最大リクエスト数をテスト環境に対して送信し、システムがどの程度安定して処理できるかを確認する予定です。</p> <p>また、債権残高の更新イベントが生成されてから、非同期的に実際に更新されるまでの処理時間についても計測しようと思います。というのも、債権残高の更新される速度は、債権の処理を行う上流のサービスにとって非常に重要なものになるためです。</p> <p>テスト計画を立てる中、驚いたことは、負荷試験のためのリポジトリが整備されていたことです。専用のリポジトリが存在することで、誰でも容易に負荷試験シナリオを作成でき、さらに他者が同じ条件で負荷を再現できるため、検証の透明性と再現性を保つことができます。</p> <h2>インターンを通して得た学び</h2> <p>今回のインターンでは、日常の開発ではなかなか扱うことのできない規模のリクエスト数を処理するシステムの開発を経験することができ、技術的な学びはもちろん、コーディングに対する姿勢やプロダクトへの向き合い方について学びました。</p> <h3>コーディングへの取り組み方</h3> <p>インターンを通じて特に印象的だったのは、コメントを書かずとも、関数名、コードから類推がつき、後から読んだ際に一目で何をする処理なのかを理解できるレベルを目指す姿勢です。保守・運用のことまで考えたコードを書くことは普段の開発においても、心掛けているのですが、普段の自分を見つめ直し、コーディングへの姿勢を改めるきっかけになりました。<br /> また、レビューの際に、ファイル内のコメント一つに対しても、どのような意図を持って書いたのか、また、そもそもそのファイルは何のためにあるのかといったことを指摘していただき、開発においてメタ的に考えることと具体的に考えることの両方を行うことの重要性を実感しました。</p> <h3>プロダクトのことを第一に考える開発</h3> <p>また、プロダクトに対する心構えについても、非常に刺激を受けました。チームのメンバー一人一人が、プロダクトの未来を考えながら、意思決定をしており、「将来的にこう使われる可能性があるから、今の内にこの手法を採用しておく」、「お客さま体験を良くするためにこのデータ構造はこうあるべき」といった議論が開発中だけではなく食事中にも頻繁に行われており、プロダクトへの愛とメルカリの Values の一つである Be a Pro の精神を強く感じました。</p> <h2>その他の活動と経験</h2> <p>インターン期間中は、開発のタスクにのみ取り組むのではなく、Qに1回開催されている社内イベントのTech Talkに登壇してみたり、他のチームの方と1on1やランチに行ったり、技術書やテックブログの読み合わせ会に参加したりと、いろいろな活動をしました。</p> <p>ランチではバックエンドエンジニアの方だけではなく、セキュリティエンジニアの方とも話す機会をいただき、なぜその職種を選んだのかというキャリアの話から、最近の攻撃手法、日々の開発で意識すべきセキュリティ観点まで、バックエンドに閉じない視点を得ることができました。</p> <p>また、Tech Talk や読み合わせ会に参加し、メルカリは、自発的に技術を学びに行ったり、技術について情報発信する環境が本当に整っていることを強く実感し、エンジニアにとって最高の環境だと思いました。</p> <h2>さいごに</h2> <p>インターンを通して、タスクに取り組む中で技術的な学びだけではなく、AIの活用方法、エンジニアとしての姿勢についても多くの学びを得ることができました。特に、エンジニアとしての姿勢は、これからの働き方に活かしていこうと思います。インターンとして参加できる残りの期間では、負荷試験までやり切り、可能であればリリースまで完了させたいと考えています。</p> <p>今回の開発をリードしてくださった@yutaroさん、メンターの@kobaryoさん、そしてチームの皆さんを始め関わってくださった全ての方々、本当にありがとうございました!</p> <p>明日の記事は@komatsuさんです。引き続きお楽しみください。</p> 多様な支払い手段と継続課金を安全に扱う「Mandate」の設計https://engineering.mercari.com/blog/entry/20251216-mandates-for-recurring-payments/https://engineering.mercari.com/blog/entry/20251216-mandates-for-recurring-payments/<p>はじめに こんにちは。メルペイのPayment Core team/Backend Engineerの@tomoです。 この記事は、 Merpay &amp; Mercoin Advent Calendar 2025の1 [&hellip;]</p> Wed, 17 Dec 2025 10:00:15 GMT<h2>はじめに</h2> <p>こんにちは。メルペイのPayment Core team/Backend Engineerの@tomoです。<br /> この記事は、 <a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/" title="Merpay &amp; Mercoin Advent Calendar 2025">Merpay &amp; Mercoin Advent Calendar 2025</a>の17日目の記事です。</p> <h2>都度払いから継続決済へ</h2> <p>これまでメルペイの決済は、メルカリでの買い物や、お客さまがスマホを取り出してバーコードを提示したりボタンを押したりすることで完了する、いわゆる都度決済が中心でした。しかし、メルカリのエコシステムが拡大するにつれ、決済のあり方は大きく変化しています。</p> <p>例えば、私たちが提供を開始したメルカリモバイルでは、毎月の利用料金を支払うためにお客さまが都度アプリを開くことはありません。システムがバックグラウンドで自律的に決済を実行します。 このように、決済はもはや単発のイベントではなくなりつつあります。お客さまの操作を伴わずに実行される オフセッション決済──サブスクリプション課金のように、継続的に行われる支払い形態が増えてきています。</p> <p>そして、メルカリ特有の多様な支払い手段を組み合わせつつ、これらの継続決済を大規模に安全に実行するために、私たちは <strong>Mandate</strong>(マンデート) と呼ばれる新しい概念を導入しました。</p> <h2>Mandateとは</h2> <p>「Mandate」という言葉は日常では馴染みが薄いかもしれませんが、Fintech の領域では口座振替依頼や自動引き落としの同意を表す一般的な用語です。Stripe の SetupIntent や、インド UPI の AutoPay など、世界中の決済プラットフォームで同様の仕組みが提供されています。</p> <p>身近な例として、動画配信サービスのサブスクリプションがあります。お客さまは初回登録時にクレジットカード情報を登録し、「毎月このカードから定額を引き落としてよい」という包括的な許可を与えます。この「将来の支払いに関する包括的な同意」こそが Mandate の本質です。これらは一般に「オフセッション決済」と呼ばれます。請求の瞬間にお客さまが操作を行わずに決済が実行される決済形態を指します。</p> <p>メルカリにおける Mandate も同じ考え方で、お客さまがサービス(例:メルカリモバイルサービス)に対して「メルペイ残高やポイントなどを用いて、未来の支払いを行ってよい」と認可するデジタルな契約を示します。</p> <p>一般的なMandateは、特定の「クレジットカード」や「銀行口座」に1対1で紐づきます。たとえば、あるサブスクAの支払いに対してクレジットカードBで契約するためのMandateを作成するという具合です。しかし、メルカリのお客さまは以下のような多様な決済手段を持っています。</p> <ul> <li>売上金・残高</li> <li>無償ポイント・有償ポイント</li> <li>メルペイのあと払い</li> <li>クレジットカード</li> </ul> <p>「ポイントが余っていればポイントを優先し、足りなければ残高から、それでも足りなければあと払いで」――このような複合決済を毎回お客さまの操作なしに行うには、単一のカードへの紐付けでは不十分です。そのため、Payment Platformでは、複数の決済手段に対してMandateを作成できるようにしました。つまり Mandate は、「多様な支払い手段を組み合わせて継続的に課金する」というメルカリ特有の要件を、安全に実現するための基盤として設計されています。</p> <h2>メルペイにおける Mandate</h2> <h4>Mandate を構成する 3 つの基本要素</h4> <p>オフセッション決済では、誰が → 誰に → どう支払うか の三要素がそろって初めて正しい権限判定ができます。この三要素によって Mandate のスコープを定めます。</p> <ul> <li>Customer(誰が支払うか):お客さま</li> <li>Partner(誰に支払うか):料金を受け取るサービス(例: メルカリモバイル)</li> <li>Payment Method(どう支払うか):ポイント、残高、あと払い、クレジットカードなどの組み合わせ</li> </ul> <p>Mandate をこの三軸の組み合わせで表すことで、余計な権限を持たせることなく、監査に耐える説明可能性を確保し、かつ決済実行時の判定を機械的に行えるようになります。<br /> Mandateの管理はWallet Serviceによって行われます。Wallet Service は、あんしん支払い設定など、お客さま固有の設定や支払い権限を管理する基盤サービスです。</p> <h4>決済サービスによる必須の Mandate 検証</h4> <p>オフセッション決済はお客さまの操作を伴わないため、何よりも安全性が求められます。Mandate が存在しない、あるいは範囲外の決済が誤って実行されることは絶対に避けねばなりません。<br /> 私たちはその安全性を担保するため、決済作成 API(<code>CreateCharge</code>)に Mandate 検証ロジックを統合しました。<br /> クライアントはオフセッションとして決済を実行する意図を示すために <code>mode=off_session</code> を指定して <code>CreateCharge</code> を呼び出し、Mandate の有無を事前に確認する必要はありません。<br /> Payment Service は <code>mode=off_session</code> を受け取ると、Wallet Serviceの<code>CheckMandateExistence</code> APIを呼び出し同期的に検証を問い合わせます。Mandate が存在し、スコープ内で有効なものであれば決済を実行し、そうでなければ即座にエラーを返して処理を中断します。<br /> こうしてプラットフォームが Gatekeeper として機能することで、サービス側の実装に依存しない堅牢な安全性を確保しています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/561bc8fd--2025-12-16-18.34.52.png" alt="" /></p> <h2>決済チェックアウトソリューションで Mandate-Free な開発者体験を実現する</h2> <p>Off-session modeのCreateChargeでは、クライアントはMandateの存在を意識せず使用することができます。しかし、サービス契約時には Mandate関連のAPI 呼び出しを実装する必要があります。つまり、サービス側のエンジニアはmandateの作成・削除などライフサイクルに関する仕様を理解し、実装しなければいけません。</p> <p>そこで Payment Platform 側では、<a href="https://engineering.mercari.com/blog/entry/20250605-bf42ce60cf/">決済チェックアウトソリューション</a>と組み合わせることで、クライアントが Mandate の API 仕様を意識せずに済む「Mandate-Free」な開発体験の実現を目指しました。<br /> メルペイのチェックアウトソリューションは、決済向けの共通チェックアウト画面を提供する仕組みとして開発されました。決済 UI や 3DS 対応をプロダクトが個別に実装する必要がなくなり、基盤側で統一的に提供できるようになったものです。<br /> さらに今回、このチェックアウトソリューションに <code>setup mode</code> を新設し、支払い手段の登録フローも一括で管理できるようにしました。setup mode のチェックアウトソリューションを通じてお客さまがサービス利用のための支払い手段を登録すると、内部でMandate関連APIを呼び出し、Wallet Serviceに Mandate を作成・更新します。</p> <p>結果として、クライアントはお客さまに支払い方法を設定させるために チェックアウトソリューション を呼び出すだけでよく、以降の継続課金も <code>mode=off_session</code> の <code>CreateCharge</code> を呼ぶだけで完結します。Mandateの検証やスコープチェックは Payment Platform 側が強制するため、クライアントは決済実行時に必要となる細かな権限管理ロジックを扱う必要がなくなります。</p> <h2>メルカリモバイルにおける実践</h2> <p>Mandate と チェックアウトソリューションの統合は、現在メルカリモバイルのクレジットカード決済で本番運用されています。<br /> お客さまはサービス契約時に一度だけ チェックアウトソリューション を通じてクレジットカードを支払い手段として登録します。登録後、クレジットカードは内部的に Mandate と紐づけられ、以降の毎月の料金請求はオフセッションで自動的に処理されます。お客さまが特別な操作をすることはありません。<br /> メルカリモバイル開発者にとっても、複雑なカード登録フローや Mandate 管理といった重たい実装から解放され、「契約時に チェックアウトソリューション を使用する」「毎月 <code>off_session</code> で <code>CreateCharge</code> を呼ぶ」という最小限の実装だけで安全な継続課金が実現できています。</p> <h2>おわりに</h2> <p>本記事では、メルカリの多様な支払い手段と複雑なビジネス要件を背景に、将来の支払い権限を管理する基盤としての Mandate と、その Mandate をお客さまにも開発者にも意識させない形で統合した チェックアウトソリューション(setup mode) の実践を紹介しました。</p> <p>明日の記事は@Minatoさんです。引き続きお楽しみください。</p> TiDB Resource Groupでワークロードを制御するhttps://engineering.mercari.com/blog/entry/20251211-3846ed440d/https://engineering.mercari.com/blog/entry/20251211-3846ed440d/<p>こんにちは。Mercari Ads teamのEngineering Managerの@ogataka50です。 この記事は、Mercari Advent Calendar 2025 の15日目の記事です。 1. はじめ [&hellip;]</p> Mon, 15 Dec 2025 11:00:06 GMT<p>こんにちは。Mercari Ads teamのEngineering Managerの<a href="https://x.com/ogataka50">@ogataka50</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a> の15日目の記事です。</p> <h2>1. はじめに</h2> <p>Mercari Ads では、メインの DB として TiDB を採用しています。HTAP(Hybrid Transactional and Analytical Processing)により、広告設定などのオンライン処理(OLTP)と、impression・conversion の集計などのバッチ処理(OLAP)を単一クラスタで運用しています。しかし、ワークロードの多様化とトラフィック増加に伴い、リソース競合による性能劣化が次第に顕在化しました。具体的には次のような事象です。</p> <ul> <li>バッチ処理や集計処理が TiDB に過度な負荷をかけると、広告管理ツール(Ads Manager)の操作のレスポンスが悪化し、サービス品質(UX)に悪化</li> <li>逆に、オンライン処理が重くなったときにバックグラウンドのレポート集計やバッチ処理が大幅に遅延。これによりレポートの生成やデータ更新が遅れ、期待どおりの動作にならない</li> </ul> <p>このような異なるワークロード同士のリソース競合によって、システムの安定性やパフォーマンスが低下する課題に直面していました。</p> <p>そこで、TiDB の Resource Group を使って、用途(オンライン/バッチ/レポートなど)ごとにリソースを論理分離し、干渉しないよう制御することにしました。</p> <p>本記事では、Resource Group の概要と、どのように導入を進めたかを共有したいと思います。</p> <h2>2. Resource Control の概要</h2> <p>Resource Group は、TiDB におけるリソース制御機能のひとつで、論理的な「グループ 」に分け、各グループに対して CPU/I/O/ストレージへのアクセス量を制御できます。<br /> この制御の単位となるのが Request Unit (RU)になります。RU は CPU 時間やディスク I/O、IOPS など複数リソースの消費量を統合して評価する抽象的な単位です。<br /> TiDB の公式ドキュメントでは以下のように <a href="https://docs.pingcap.com/tidb/stable/tidb-resource-control-ru-groups/#what-is-request-unit-ru">RU の消費定義</a>が示されています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/66ee25a0-mermaid-chart-create-complex-visual-diagrams-with-text.-2025-12-11-021308.png" alt="" /></p> <p>Resource Group を使い、複数のワークロードで必要なTiDBのリソースを論理的に分離して、リソース競合を防ぎつつ、必要に応じてリソースを割り当て直すことができます。具体的には以下のような処理です。</p> <ul> <li>オンライン処理には高めの RU を割り当てる + 高優先度で処理させる</li> <li>バッチ/分析には必要最小限の RU を割り当て、低優先度で処理させる</li> </ul> <p>この仕組みによって、オンライン処理のレイテンシを維持しつつ、バッチや分析処理も同時に安定して実行できるようにすることを目指しました。</p> <h2>3. 実際の導入方法</h2> <h3>3.1 サービスごとに SQL ユーザーと Resource Group を分離</h3> <p>Mercari Ads では、各マイクロサービス (広告管理 UI、レポート生成バッチ、分析サービスなど) ごとに SQL ユーザーを分け、それぞれに専用の Resource Group を割り当てるようにしました。<br /> クエリ単位でヒントを使って Resource Group を設定することも可能ですが、多数のサービス/クエリが混在すると運用が煩雑になりやすいため、SQLユーザー単位での分離することから始めました。</p> <h3>3.2 Resource Group の定義例</h3> <p>まずは Resource Group を作り、それを SQLユーザーに紐付けるような流れになります。<br /> 次の SQL で Resource Group を作成します。</p> <pre><code>-- create Resource Group CREATE RESOURCE GROUP IF NOT EXISTS rg_online RU_PER_SEC = 2000 PRIORITY = HIGH BURSTABLE; CREATE RESOURCE GROUP IF NOT EXISTS rg_batch RU_PER_SEC = 500 PRIORITY = LOW;</code></pre> <ul> <li>RU_PER_SEC: 1 秒あたりに補充される RU の量 (トークン・バケットの回復速度) を指定</li> <li>PRIORITY: ストレージ層 (TiKV / TiFlash) 側でのタスク優先度 — 高優先度なら他より先に処理される</li> <li>BURSTABLE: 状況に応じて余剰リソースの利用を許可する設定。負荷が低いタイミングでは余裕を使える</li> </ul> <p>どの Resource Group をどの SQL ユーザーに割り当てるかを、以下の SQL で設定します。</p> <pre><code>-- bind Resource Group and SQL User ALTER USER &#039;ads_service_sql_user&#039; RESOURCE GROUP rg_online; ALTER USER &#039;batch_job_sql_user&#039; RESOURCE GROUP rg_batch;</code></pre> <p>optimizer hintsを追加することでSQL単位でResource Group を指定することも可能です。</p> <pre><code>-- SQL 単位で Resource Group を指定する例 SELECT /*+ RESOURCE_GROUP(rg_name) */ * FROM table_name;</code></pre> <h3>3.3 モニタリング → 設定の見直しサイクル</h3> <p>次のようなサイクルで適切な設定値の調整をしていきました。</p> <ol> <li>まずは制限なしのRU_PER_SEC (default / unlimited 相当) での Resource Group を作成し、各サービスの「通常負荷時」のリソース消費を観測 <pre><code>CREATE RESOURCE GROUP IF NOT EXISTS rg_monitoring RU_PER_SEC = 2147483647;</code></pre> </li> <li>1週間程度監視し、通常時のRU 使用量、I/O/CPU 負荷、クエリ数などを把握する</li> <li>用途 (オンラインかバッチなど) に応じて RU_PER_SEC・PRIORITY・BURSTABLE を設定し直す <ul> <li>オンライン処理: RU_PER_SEC 高め / HIGH 優先度、必要なら BURSTABLE を使うことを検討</li> <li>バックグラウンド(バッチ/分析)処理: RU_PER_SEC 控えめ / LOW 優先度</li> </ul> </li> </ol> <p>このサイクルを継続し、各 Resource Group の RU_PER_SEC を徐々に適正化しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/92c57137-screenshot_2025-12-11_at_6_28_44.png" alt="" /></p> <h2>4. 平均値ベースだけではなく、瞬間的な RU の消費も考慮する</h2> <p>Resource Group を SQL ユーザーごとに分け、RU_PER_SEC の設定を進めていく中で、意図通りにいかなかったケースがありました。あるサービスは、平常時はほとんど TiDB にアクセスしないのですが、ユーザが期間の長い広告レポートをリクエストした際にだけ、大量のデータを読み取る集計処理が発生し、瞬間的に多くの RU を必要とするケースがありました。このような場合平均値ベースの RU 消費は小さく見えます。しかし実際には、1 回のリクエストで数千〜数万 RU を消費するようなスパイクがあり、平均値を基準に RU_PER_SEC を設定していたため、このスパイク時に上限に達してしまい、必要な RU を確保できず著しくパフォーマンスが低下する状況が生まれてしまいました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/1ffdc554-screenshot_2025-12-11_at_6_34_24.png" alt="" /></p> <p>そのため、RU_PER_SEC の設定では平均値だけではなく、瞬間的なスパイクにも対応できるよう考慮する必要があります。こうしたユースケースでは、必要に応じて BURSTABLE を有効化し、一時的に余剰リソースを利用できるようにすることも効果的です。</p> <h2>5. Runaway Query の制御と管理</h2> <p>Resource Group によるリソース分離だけでなく、想定以上にリソースを消費するクエリを制御、管理する機能があります。<br /> Resource Group 定義に、QUERY_LIMIT オプションを追加することで、あるクエリが長時間実行される、または過剰にリソースを消費するような クエリを検知し、強制終了 (KILL)、優先度変更といった制御が可能です。<br /> TiDB では、このような予想以上にリソースを消費するクエリを Runaway Query と呼んでいます。<br /> たとえば、以下のように定義することで、「30秒以上実行されるクエリは自動的にkillする」などの制御ができます。</p> <pre><code>-- configure QUERY_LIMIT on resource group CREATE RESOURCE GROUP rg_online_limited RU_PER_SEC = 10000 QUERY_LIMIT = (EXEC_ELAPSED = &#039;30s&#039;, ACTION = KILL);</code></pre> <p>こうした制御を入れることで、意図せぬ重たいクエリや無限ループ/誤った SQL による極端なリソース消費や、他ワークロードへの影響を防ぐ一助になります。</p> <p>発生した Runaway Query は、次の SQL で確認できます。</p> <pre><code>-- select Runaway Query by Resource Group SELECT * FROM mysql.tidb_runaway_queries WHERE resource_group_name = &#039;rg_online_limited&#039; ORDER BY start_time DESC LIMIT 100;</code></pre> <h2>6. まとめ &amp; 今後の展望</h2> <p>Resource Groupの導入によって、オンライン処理に必要な RU を安定して確保できるようになり、Ads Managerのレイテンシがバックグラウンド処理の影響を受けにくくなりました。また、バックグラウンド処理側も過剰にリソースを奪わない範囲で着実に実行されるようになり、ワークロード間の競合による性能劣化が減少しました。<br /> 実施した内容をまとめると、以下の通りです。</p> <ol> <li>現状のワークロード把握 <ul> <li>各SQL user(≒サービス)ごとにどのようなシステム要求があるかリストアップ (オンライン/バッチ/Background/分析用途など)</li> <li>最初は制限なし (default / UNLIMITED 相当) で動かし、どれくらい RU を消費するか観測</li> </ul> </li> <li>モニタリング期間 (数日〜1週間) <ul> <li>通常時のRU 消費を計測</li> <li>特にバッチや分析クエリのピーク時の消費量に注意する</li> </ul> </li> <li>ワークロードごとに Resource Group の設定値を設定 <ul> <li>オンライン処理: 応答性重視 → RU_PER_SEC を比較的高め、PRIORITY = HIGH、必要なら BURSTABLE を使うことを検討</li> <li>バックグラウンド処理: RU_PER_SEC を控えめ、PRIORITY = LOWで必要以上のリソースを消費しないようにした</li> <li>必要に応じて Runaway 制御 (QUERY_LIMIT) を設定</li> </ul> </li> <li>継続的な運用とレビューサイクル <ul> <li>定期 (週次/月次) で RU 消費状況、遅延・タイムアウト・失敗のログをチェック</li> <li>発生したRunaway Queryの確認</li> <li>必要に応じてResource Group 設定を調整</li> </ul> </li> </ol> <p>今後は引き続き下記のような改善、調整を行っていこうと考えています。</p> <ul> <li>スパイクが発生するケースでは、一時的に割当を調整するなど、Resource Group と Runaway 制御を組み合わせた運用改善</li> <li>定期レビューの自動化。どのサービスがどれくらい RU を使っているか/どの程度余裕があるかなどを可視化と調整</li> </ul> <p>本記事が TiDB 運用の一助となれば幸いです。</p> <p>明日の記事は @Snehaさんです。引き続きお楽しみください。</p> <h2>7. Refs</h2> <ul> <li><a href="https://docs.pingcap.com/tidb/stable/tidb-resource-control-ru-groups/">Use Resource Control to Achieve Resource Group Limitation and Flow Control</a></li> <li><a href="https://docs.pingcap.com/tidb/stable/sql-statement-create-resource-group/">CREATE RESOURCE GROUP</a></li> <li><a href="https://docs.pingcap.com/tidb/stable/tidb-resource-control-runaway-queries/">Manage Queries That Consume More Resources Than Expected (Runaway Queries)</a></li> <li><a href="https://www.pingcap.com/blog/tidb-resource-control-workload-consolidation-transactional-apps/">TiDB Resource Control: Stable Workload Consolidation of Transactional Apps</a></li> <li><a href="https://www.pingcap.com/blog/managing-resource-isolation-optimizing-performance-stability-tidb/">Managing Resource Isolation: Optimizing Performance Stability in TiDB</a></li> </ul> メルカリAdsが広告を届けるまでの話https://engineering.mercari.com/blog/entry/20251204-563130cd63/https://engineering.mercari.com/blog/entry/20251204-563130cd63/<p>はじめに こんにちは、Ads Servingチームでバックエンドエンジニアをしている@yanapです。 この記事は、Mercari Advent Calendar 2025の14日目の記事です。 メルカリは 2025年1 [&hellip;]</p> Sun, 14 Dec 2025 11:00:26 GMT<h2><strong>はじめに</strong></h2> <p>こんにちは、Ads Servingチームでバックエンドエンジニアをしている@yanapです。</p> <p>この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/"><strong>Mercari Advent Calendar 2025</strong></a>の14日目の記事です。</p> <p>メルカリは 2025年10月時点で 月間 2,305 万人 のお客さまに利用されており、検索や閲覧などの操作に合わせて広告が表示される「メルカリAds」にも、毎日非常に多くの広告リクエストが届きます。<br /> 広告候補は用途ごとに多数存在し、その中から最適な広告を選び出す必要があります。<br /> しかし、どれだけ処理が複雑であっても、広告は 数百ミリ秒以内 に返さなければ検索体験を損ねてしまいます。<br /> 大規模なトラフィックの中で高速に広告を選定する仕組みを成立させるのは、簡単ではありません。<br /> お客さまがメルカリアプリで「トマト」と検索したその瞬間、裏側では広告配信のための小さな通信が静かに動き始めています。<br /> その通信を受け取り、複数の内部サービスと連携しながら最適な広告を短時間で選定して返す仕組みを、Ads Serving チームが担当しています。<br /> 本記事では、この広告配信フローの全体像と、低レイテンシを維持するための工夫について紹介します。</p> <h2>広告がどこに表示されるか</h2> <p>まずは、メルカリAdsが実際にお客さまの目にどのように触れるのかを見てみましょう。この記事では、広告配信の仕組みそのものにスポットを当てていますが、こうした広告がメルカリアプリ内のどこに表示されるのかを具体的に知ることで、その仕組みをより身近に感じてもらえるかと思います。<br /> メルカリAdsは、以下のような場所に表示される仕組みになっています。</p> <h3>検索結果画面</h3> <p>お客さまが検索したキーワードに関連する広告が、検索結果リストの上部や一定間隔ごとに表示されます。<br /> 検索体験を邪魔しないよう、通常の商品リストと自然に馴染むようにデザインされています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/c15e54e7-ad-request-page-5-274x300.png" alt="MercariAdsSerp" /></p> <h3>商品詳細ページ</h3> <p>「この商品を見ている人におすすめ」ブロック内に、関連性の高い広告が表示されます。<br /> 閲覧中の商品や検索履歴などをもとに、興味を持ちそうな商品を提示しています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/b59303d2-ads3-135x300.png" alt="" /></p> <p>メルカリアプリでは、このようにお客さまの操作や閲覧内容に合わせて、自然に広告が挿入されるように設計されています。</p> <h2><strong>メルカリAdsの広告配信の仕組み</strong></h2> <p>ここからは、メルカリAdsで広告がどのように選ばれ、短時間で返却されているのかを紹介します。<br /> まずは中核となる AdServer を軸に「広告が返ってくるまでの流れ」を見ていきます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/6de2d37f-ad-request-page-4-2-300x134.png" alt="Ads Flow" /></p> <ol> <li> <p>広告リクエストの開始<br /> 検索操作やページ遷移などをきっかけに、広告を表示するための通信が AdServer に送信されます。<br /> 広告枠の位置、検索キーワード、閲覧中の商品情報など、表示に必要な情報がここに含まれます。</p> </li> <li> <p>AdServer の役割:最適な広告を選ぶ司令塔<br /> AdServer は、広告配信フローにおける 中心的な役割を担うサーバー です。<br /> 操作に応じて送られた広告リクエストを受け取り、「このお客さまには今どの広告を出すべきか?」を判断します。</p> </li> </ol> <p>そのために、AdServer は裏側にある複数のサービスに問い合わせて「広告候補」を収集します。<br /> 収集された候補は、ターゲティング条件・配信設定・予算状況などにもとづいてフィルタリングされ、最終的にユーザーに適した少数の広告が選ばれて返却されます。</p> <ol start="3"> <li>表示位置に合わせた広告の返却<br /> 検索結果画面では、広告の位置や頻度は画面や文脈に応じて細かく制御されています。<br /> 選ばれた広告は、その時点の仕様に基づき、適切な位置としてクライアントに返されます。</li> </ol> <h2>高速な広告配信を支える仕組み</h2> <p>ここからは、メルカリAdsの広告配信が「なぜこれだけ複雑なのに高速なのか」を紹介します。</p> <p>広告リクエストが AdServer に届くと、最適な広告を選ぶために複数の内部サービスと連携して処理が進みます。<br /> このように重い処理ですが、実際には 多くの工夫によって数百ミリ秒で完了するように最適化されています。<br /> 数百ミリ秒、つまり0.数秒です。まばたき1回分の時間ですね。<br /> この短い時間で、多数のサービスが連携し、広告が選ばれて返却されます。</p> <ol> <li>多数の広告候補生成ロジックを&quot;並列&quot;で動かす<br /> AdServer の裏側では、用途ごとに分かれた「広告候補生成サービス」が<br /> 複数存在しています(この仕組みを社内では「Demand」と呼んでいます)。<br /> なぜ複数のサービスに分かれているのでしょうか?<br /> それは、広告を探す「手がかり」が状況によって異なるからです。</li> </ol> <p>たとえば、以下のようにそれぞれの広告製品ごとに専用の候補生成ロジックが動いています。</p> <ul> <li><strong>Product Ads(商品広告)</strong>: <ul> <li>広告主が商品データフィードを連携することで、多品目を効率的に広告配信できる製品</li> <li>システム側では、メルカリの商品データベースと連携して広告候補を探索</li> </ul> </li> <li><strong>Infeed Ads(インフィード広告)</strong>: <ul> <li>広告主が画像やテキストクリエイティブを入稿して配信する製品</li> <li>ユーザーのデモグラフィック情報を用いたターゲティング配信が可能</li> <li>システム側では、MLモデルを使って、ユーザーの興味に合った広告を推薦</li> </ul> </li> <li><strong>Shops Ads(メルカリShops広告)</strong>: <ul> <li>メルカリShopsの店舗向けに特化した広告製品</li> <li>ショップ商品を効果的にプロモーション</li> </ul> </li> <li><strong>C2C Ads(メルカリC2C広告)</strong>: <ul> <li>メルカリの個人間取引(フリマ出品)を対象とした広告</li> </ul> </li> </ul> <p>これらのサービスは、AdServer のリクエストを受けて すべて並列に処理を開始し、広告候補を生成します。</p> <p>そして異なる広告抽出ロジックを持った各Demandを複数のmicroserviceに分割し、AdServerから並列で呼び出すことで、短時間で大量の広告候補を抽出することを実現しています。</p> <ol start="2"> <li>配信設定や予算チェックの最適化<br /> 広告配信では広告を出稿している広告主が、配信の上限予算や期間を設定できます。AdServerで広告を返却する前に、これらが有効かどうか確認する必要があります。</li> </ol> <p>ここで重要なのが、データの性質による使い分けです。<br /> <strong>配信設定(ON/OFF)</strong><br /> → 頻繁には変わらないデータ<br /> → キャッシュ(一度取得した情報を保存)を使って高速化</p> <p><strong>予算残高</strong><br /> → 刻一刻と変化するデータ<br /> → 毎回リアルタイムで確認(ただし、タイムアウト時は柔軟に対応)</p> <p>つまり、「変化の速度」に応じて最適な取得方法を選んでいるわけです。</p> <p>重い判断処理であっても、データの性質に応じて最適な取得方法を使い分けることで、レイテンシを最小限に抑えています。</p> <ol start="3"> <li> <p>AIによる最終的な並び替え<br /> レスポンスを返す直前には、AIによるスコアリングによって広告を「どの順序で表示するか」を最適化します。<br /> ここでも軽量なモデルやキャッシュが利用され、処理の高速化が図られています。</p> </li> <li> <p>表示に必要な情報の組み立て<br /> 最終的なレスポンスを返すために、広告として表示するタイトル・画像などの詳細情報を取得し、クライアントがそのまま描画できる形に整えます。<br /> ここでもキャッシュの活用によって、追加の遅延を抑えています。</p> </li> </ol> <h2>まとめ</h2> <p>本記事では、メルカリAdsにおける広告配信の仕組みを紹介しました。<br /> メルカリAdsが高速に動作しているのは、次のような設計思想のもと、複数のサービスが連携しているためです。</p> <ul> <li>用途別に分かれた複数の広告候補生成ロジックを並列で呼び出せる構造</li> <li>重い処理を複数のサービスに分散し、AdServer が統合して返すアーキテクチャ</li> <li>配信設定・商品情報など、頻繁に参照されるデータはキャッシュ前提で高速化</li> <li>予算情報はリアルタイム取得で正確性を保ちつつ、タイムアウト時は柔軟に対応</li> <li>AIスコアリングも軽量化・キャッシュにより遅延を抑制</li> </ul> <p>これらの工夫により、検索結果が表示されるころには最適な広告が数百ミリ秒以内に返却されているという体験が実現されています。</p> <p>日々の開発を通してこの仕組みを理解していく中で、システムの設計思想や高速化の工夫に触れられたのは貴重な経験でした。</p> <p>今後もメルカリAdsは進化を続けていくと思います。<br /> この記事が、メルカリAdsの仕組みに興味を持つきっかけになればうれしいです。</p> <p>最後までお読みいただき、ありがとうございました。</p> <p>明日の記事は @ogataka50さんです。引き続きお楽しみください。</p> n8n &#8211; 乱立するAIツール中でPoCを成功させるために考えたこと・実際に取り組んだことhttps://engineering.mercari.com/blog/entry/20251213-862c05648c/https://engineering.mercari.com/blog/entry/20251213-862c05648c/<p>こんにちは。メルペイ Manager of Managers の @abcdefuji です。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の14日目の記事です。 アド [&hellip;]</p> Sun, 14 Dec 2025 10:00:05 GMT<p>こんにちは。メルペイ Manager of Managers の <a href="https://x.com/\_abcdefuji">@abcdefuji</a> です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の14日目の記事です。</p> <p>アドベントカレンダー内のn8nの連載企画の最終日です。</p> <p>本記事では、メルカリにおける <strong>n8n Enterprise 導入の PoC(Proof of Concept)をどのように成功させたか</strong> を紹介します。</p> <p>近年、生成 AI の爆発的な普及により、企業内には日々膨大な AI ツールが流れ込んでいます。コード生成、文章要約、データ連携、AI エージェント、自動化、その他<strong>毎日どこかで新しいツールが誕生し利用されている状態</strong> です。</p> <p>この状況が生む課題はシンプルであり、深刻です。</p> <ul> <li><strong>PoC ツールが乱立する</strong></li> <li><strong>情報アップデートに追いつけず、すべてを触り切る余裕がない</strong></li> <li><strong>PoC を始めても Adoption(定着)が進まない</strong></li> <li><strong>評価するチームも、本業と並行では十分にリソースを割けない</strong></li> </ul> <p>こうした課題は、多くの企業で共通しているはずです。</p> <p>メルカリでもn8n の PoC は、このような“AIツールが溢れすぎる時代”の中で始まりました。<br /> その中で、どのように導入まで至ったのか。</p> <p>最初に結論を言うと</p> <p><strong>n8n PoC の成功に必要だったのは、技術そのものではなく、“組織として AI をどう扱い、どう広げていくかを PoC で学んでいく姿勢” でした。</strong></p> <p>これは、ツールの良し悪しを評価する PoC ではなく、 <strong>“AIツールを組織に根づかせる方法を見つけるための PoC”</strong> だったのだと思います。</p> <h1><strong>1. PoC を始める前に見えていた背景</strong></h1> <p>メルカリでは、社内から100名を越えるメンバーが選出された横断組織「AI Task Force」 によって <strong>約4,000の業務プロセスが可視化</strong>されています。<br /> その多くは AI や自動化によって効率化できる余地がありました。</p> <p>AI Task Forceの詳細は下記の記事をご確認ください。</p> <p><a href="https://careers.mercari.com/mercan/articles/53708/">メルカリが本気で始めた「AI-Native」化。100名規模のタスクフォースが立ち上がるまで</a><br /> <a href="https://careers.mercari.com/mercan/articles/56200/">「AI Task Force」で変化を加速する。CTO @kimurasが描くメルカリの成長戦略</a></p> <p>しかし現場には次のような課題があり、どの組織でも PoC が途中で止まってしまう理由にもなっています。</p> <ul> <li>部署によって使うツールがまったく違う </li> <li>非エンジニアが参加しづらい </li> <li>セキュリティ観点の懸念が大きい </li> <li>PoC の担当者には本業があり、専念できない</li> </ul> <p>このままでは自動化が前に進みません。</p> <p>そこで私たちは、組織全体の“共通基盤”となる自動化プラットフォームとして、n8n の PoC を開始しました。</p> <h1><strong>2. PoC が始まってすぐに見えてきた“勢い”</strong></h1> <p>n8n を試した初期段階から、複数チームで自然にワークフローが作られはじめました。<br /> 気づくと次のような利用データが観測されていました</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/e546ad74--2025-12-13-22.57.14-1024x443.png" alt="worlfow volume" /></p> <table> <thead> <tr> <th style="text-align: left;">metrics</th> <th style="text-align: left;">value</th> </tr> </thead> <tbody> <tr> <td style="text-align: left;">週あたりの実行数</td> <td style="text-align: left;">約 13,000 回</td> </tr> <tr> <td style="text-align: left;">月あたりの実行数</td> <td style="text-align: left;">約 40,000 回</td> </tr> </tbody> </table> <p>PoC の段階にも関わらず、すでに “実務の中に入り始めている” という手応えがありました。</p> <p>ただし、数字だけで PoC がうまくいったわけではありません。<br /> ここからは、<strong>どのように PoC を設計し、実行したか</strong> の話になります。</p> <h1><strong>3. PoC の最初の一歩は「とにかく触ること」</strong></h1> <p>PoC の企画書を書くよりも先にやったことは、ただひたすら <strong>n8n を触る</strong> ことでした。</p> <ul> <li>UI の特徴 </li> <li>JSON 構造がどう見えるか </li> <li>どこまで柔軟に作れるか </li> <li>LLM との相性 </li> <li>非エンジニアがつまずきそうなポイント</li> </ul> <p>これらは、仕様を読むだけでは分かりません。<br /> 自分で触ってみて初めて、ツールが“どう現場にハマるか”が見えてきます。</p> <p>PoC 担当者が誰よりも詳しくなることで、現場と同じ目線で話せるようになります。<br /> これが後の推進力につながりました。</p> <h1><strong>4. n8n PoC は“やる気のある仲間探し”でもあった</strong></h1> <p>実際に PoC を動かし始めて感じたのは、<strong>PoC の成否はツールよりも人で決まる</strong> ということでした。</p> <p>今回のコアメンバーは以下の通りです:</p> <ul> <li><strong>abcdefuji(Backend Manager)</strong>:PoC 推進、ユースケース伴走 (当ブログ) </li> <li><strong>hi120ki(AI Security)</strong>:脅威モデル、ガードレール設計 </li> <li><strong>shuuk(AI Task Force)</strong>:全社プロセスとAdoptionの橋渡し </li> <li><strong>ISSA(Director)</strong>:DevEx の視点での全体整理 <ul> <li><a href="https://engineering.mercari.com/blog/entry/20251211-cf3b67a5a7/">理想の Workflow Platform という“聖杯”に、n8n でついに手が届くかもしれない | メルカリエンジニアリング</a> </li> </ul> </li> <li><strong>mewuto(Backend Eng)</strong>:静的解析 CLI(DAG / AST) <ul> <li><a href="https://engineering.mercari.com/blog/entry/20251211-580dc508a7/">n8nの静的解析CLIツールをOSS化 – JSON解析とDAGで実現するセキュリティチェックの自動化 | メルカリエンジニアリング</a> </li> </ul> </li> <li><strong>T(SRE)</strong>:Enterprise 構成、Enginerringの全て <ul> <li><a href="https://engineering.mercari.com/blog/entry/20251211-6fe0bc0838/">Making n8n Enterprise-Ready: 企業向けn8nの導入と運用の取り組み | メルカリエンジニアリング</a></li> </ul> </li> </ul> <p>全員、<strong>本業がありながら隙間時間で参加していました</strong>。 にもかかわらず PoC が成立したのは、全員に共通して<strong>「これはメルカリ全体にとって価値がある」</strong>という強い思いがあったからです。</p> <p>PoC は仲間が多いほど強くなります。 そして熱量は伝播します。 n8n の PoC はまさにその象徴でした。</p> <h1><strong>5. 小さな成功を一緒につくる:ユースケース伴走</strong></h1> <p>PoC を成功に導くためには、<strong>早い段階で小さな成功を複数つくること</strong> が重要です。</p> <p>n8n の PoC では、Marketing・QA・SRE・監査・HR などのチームに伴走し、次のような改善が生まれました:</p> <table> <thead> <tr> <th style="text-align: left;">部署</th> <th style="text-align: left;">ユースケース</th> <th style="text-align: left;">効果</th> </tr> </thead> <tbody> <tr> <td style="text-align: left;">Marketing</td> <td style="text-align: left;">KPI チェックの自動化</td> <td style="text-align: left;">月 500 分削減</td> </tr> <tr> <td style="text-align: left;">QA</td> <td style="text-align: left;">リリース作業の自動化</td> <td style="text-align: left;">90 分/週 → 0 分</td> </tr> <tr> <td style="text-align: left;">Eng</td> <td style="text-align: left;">AI Agent 開発工数削減</td> <td style="text-align: left;">2 週間 → 2〜3 日</td> </tr> <tr> <td style="text-align: left;">Audit</td> <td style="text-align: left;">情報集約の効率化</td> <td style="text-align: left;">属人性を大幅に改善</td> </tr> </tbody> </table> <p>ここで重要なのは、PoC チームが「代わりに作ってあげる」のではなく、 <strong>一緒にワークフローを作り、一緒に成功する</strong> という姿勢でした。利用者がわからない時にサポートを行う。これは Adoption を加速する最も強力な手法です。</p> <h1><strong>6. セキュリティとガバナンス:PoC で課題が出るほど良い</strong></h1> <p>n8n は自由度が高い分、いくつかのリスクもあります。<br /> PoC のなかで実際に以下のような問題が見つかりました:</p> <ul> <li>credential の誤設定 </li> <li>HTTP Node での不正なアクセス </li> <li>権限混同によるリスク </li> <li>Code Node 内の危険な処理</li> </ul> <p>しかし、これはむしろ好材料です。</p> <p><strong>PoC は問題を見つけるための場所</strong> です。ここで検知できれば、本番導入では安全に運用できます。</p> <p>PoC 中に次の仕組みを整備しました:</p> <ul> <li>External Hook による保存前チェック </li> <li>静的解析 CLI(JSON / DAG / AST) </li> <li>SSO(Okta) </li> <li>Task Runner によるコード実行の隔離 </li> <li>ガイドライン・テンプレート</li> </ul> <p>こうして、導入時に必要な安全性が PoC のなかで自然と整っていきました。</p> <p>詳細はこちら</p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20251211-6fe0bc0838/">Making n8n Enterprise-Ready: 企業向けn8nの導入と運用の取り組み | メルカリエンジニアリング</a> </li> <li><a href="https://engineering.mercari.com/blog/entry/20251211-580dc508a7/"><strong>n8nの静的解析CLIツールをOSS化 – JSON解析とDAGで実現するセキュリティチェックの自動化 | メルカリエンジニアリング</strong></a> </li> <li><a href="https://engineering.mercari.com/blog/entry/20251211-cf3b67a5a7/">理想の Workflow Platform という“聖杯”に、n8n でついに手が届くかもしれない | メルカリエンジニアリング</a></li> </ul> <h1><strong>7. 「ROI は罠」 ── 短期回収で PoC を評価してはいけない</strong></h1> <p>PoC ではよく ROI が議論されますが、短期回収だけで判断すると誤った評価になりがちです。</p> <p>AI や自動化は <strong>「導入しない」 という選択肢のほうが損をする</strong> 世界に入っています。</p> <p>参考: <a href="https://careers.mercari.com/mercan/articles/57409/">“BoldなAI活用”から1年。人間の限界を超えていく、メルカリの「AI-Native」な現在地</a></p> <p>そのため私たちは、次のような前提で PoC を進めました:<strong>導入するかどうか、ではなく、どう導入すれば成功するかだけを議論する。</strong></p> <p>数字の比較は必要ですが、まず見るべきは “成果ベースの数字” です。</p> <ul> <li>再現性のある成功事例がどれだけあるか </li> <li>非エンジニアでも価値を出せているか </li> <li>エラー削減、工数削減の実績 </li> <li>全社展開できる安全性があるか</li> </ul> <p>これらが確認できて初めて、年間コストや ROI 試算の意味が出てきます。</p> <p>また、n8nにはEstimated time saved機能があります。</p> <p>1 workflow毎にどの程度時間を削減できたかの見積もりを設定する機能です。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/64ce725d-screenshot-2025-12-12-21.28.28-1024x510.png" alt="" /></p> <p>設定後、以下のようにDashboardから各Workflowで設定された削減時間を確認することができます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/4f58f658--2025-12-12-13.39.56-1024x370.png" alt="" /></p> <p>中長期的にはこのようにn8nの効果測定をすることも可能です。</p> <h1><strong>8. Adoption戦略:広げるには“雰囲気”も必要</strong></h1> <p>PoC の後半では、次のような活動をして Adoption を広げていきました。</p> <ul> <li>All Hands や社内勉強会で紹介 </li> <li>Slack チャンネルで成功事例を共有 </li> <li>非エンジニア向けテンプレートの提供 </li> <li>質問にはすぐ返す文化づくり</li> </ul> <p>気づけば、周囲の人たちが <strong>「ちょっと n8n 触ってみるね」</strong> と自然に言うようになっていました。</p> <p>ツールが広がるには“雰囲気づくり”もとても大事です。</p> <h1><strong>9. PoC を成功に導く“再現可能な型”</strong></h1> <p>最後に、今回の PoC から得られた成功パターンをまとめます。</p> <ol> <li>まず自分が一番触る </li> <li>効率より熱量のある仲間を見つける </li> <li>小さな成功を早くつくる </li> <li>PoC 中に課題を見つけ、仕組みとして解決する </li> <li>ROI よりも成果ベースの数字を見る </li> <li>テンプレート・ガードレールで安全に広げる </li> <li>Adoption を広げる雰囲気をつくる </li> <li>PoC担当者の覚悟</li> </ol> <p>これらは n8n に限らず、AI ツール全般の導入で応用できる考え方です。</p> <p>PoC担当者には”覚悟”が必要です。達成するべきVisionを信じきる覚悟です。自身がなぜ必要・有効なのかのロジックを持ち、そしてそれを信じて突き進んでください。</p> <h1><strong>おわりに</strong></h1> <p>n8n PoC を振り返ると、成功を分けたのは技術だけではありませんでした。</p> <p>本業の合間に動きながら、皆でアイデアを出し合い、問題を潰し、成功を積み重ねていく。<br /> その“姿勢”そのものが PoC の最大の価値であり、組織が AI を受け入れる力につながりました。</p> <p>PoC はツールのテストではなく、<strong>組織が AI をどう扱うかを学ぶフェーズ</strong> です。<br /> これからも改善を続けながら、AI-Native な組織を目指して進んでいきます。</p> <p>またどこかでメルカリがどのようなworkflowを構築し活用しているのか紹介できたらと思います。ここまで読んでいただきありがとうございました。</p> <p>明日の記事は Timoさんです。引き続きお楽しみください。</p> <p><em>n8n.io logo source: <a href="https://n8n.io/brandguidelines/">https://n8n.io/brandguidelines</a></em></p> メルカリが、AI時代にナレッジマネジメントに投資したわけhttps://engineering.mercari.com/blog/entry/20251213-96e00d1d91/https://engineering.mercari.com/blog/entry/20251213-96e00d1d91/<p>こんにちは、メルカリEngineering Officeチームの@thiroiです この記事は、Mercari Advent Calendar 2025 の13日目の記事です。 はじめに Engineering Offi [&hellip;]</p> Sat, 13 Dec 2025 11:02:37 GMT<p>こんにちは、メルカリEngineering Officeチームの@thiroiです<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a> の13日目の記事です。</p> <h2>はじめに</h2> <p><a href="https://engineering.mercari.com/blog/entry/20231206-4e4f1e2323/">Engineering Office</a>は、”Establish a Resilient Engineering Organization.”というミッションを元に、エンジニア組織を横断的に支える役割を担っています。<br /> 今日は組織を裏側で支える仕組みの一つとして、「AI時代のナレッジマネジメント」をテーマに書いていきます</p> <h2>TL;DR</h2> <ul> <li>組織に情報、ノウハウを蓄積する仕組み、手法をナレッジマネジメントと言います</li> <li>ナレッジマネジメントはAIが存在する前、重要ではあるものの、コストパフォーマンスのバランスを取るのが難しいもので、メルカリでも課題を多く抱えていました</li> <li>AIの台頭により、ナレッジマネジメントの効率化が進んだことに加え、社内ナレッジAIのコンテキスト利用という新しいユースケースが生まれ、コストパフォーマンスが劇的に向上しました</li> <li>メルカリでは、この環境変化の元、AI-Nativeの推進にあたって、ナレッジマネジメントに投資を決定し、いくつかの施策を推進しています</li> </ul> <h2>ナレッジマネジメントって何?</h2> <p>ナレッジマネジメントとは、個人の持つ情報、ノウハウを、組織に蓄積、共有する仕組み、手法のことです</p> <p>例えば、以下のようなことを感じたことはありませんか</p> <ul> <li>過去に似たような障害対応をしたのに、どうやって対応したかがわからない</li> <li>現在のAPIの仕様がコードを読まないとわからない</li> <li>そもそもなんでこんな設計になったのかわからない</li> <li>ちょっと前に可視化に使ったSQLがわからない</li> </ul> <p>これらの課題に対する解決策は一つではありませんが、ナレッジとして蓄積するということは一つの解決策になります</p> <p>過去の学びや、情報を蓄積し、引き出せる状態にすることにより、学び直しや、情報の再構築を防ぐことができます。いわゆる「車輪の再発明を防ぐ」、「巨人の肩の上に立つ」をしよう、ということです。ナレッジを会社の資産として正しく蓄積し、後続するメンバーであったり、未来の自分自身がそれを利用できる状態にすることで、生産性を向上できます。これがナレッジマネジメントの目的です</p> <h2>ナレッジマネジメントの課題</h2> <p>現場において、ナレッジマネジメントは非常に難しい課題です。程度の差はあれ、社会人として「この情報が見つからない」といった課題にぶつかったことがない人はいないのではないでしょうか。ナレッジマネジメントの課題の分け方はいくつかありますが、ここでは最もよく見られる4分類を利用します</p> <ul> <li>記録されない(Create)</li> <li>発見できない(Find)</li> <li>活用できない(Use)</li> <li>更新されない(Maintain)</li> </ul> <p>上から順に、詳しく見ていきましょう</p> <h3>1. 「記録されない」</h3> <p>もっともわかりやすく、頻繁に見る課題です<br /> この発生原因は、そもそもナレッジの蓄積をするという習慣がなかったり、記録に対する時間を今取れない、記録のコストが高く感じる、といったものがあります。「後で書く」は私の感覚だと、「コードを後で書き直す」とほぼ同じく、90%以上行われません。書かないとほぼ同義です。特定の時間を取る仕組み(ドキュメントにチームで集中する日をとるなど)がない限りは非常に難しいです</p> <h3>2. 「発見できない」</h3> <p>問い合わせをしたら、「このページを参考にしてね」と言われた経験はありませんか。これは、該当するナレッジ自体は蓄積されているものの、発見ができないという状態です。<br /> ドキュメントの検索性が低い、もしくは情報を調べるという文化、環境がないことが主な原因です。ツール自体の検索性能が低かったり、複数のツールにドキュメントが散らばっている場合によく発生します。</p> <h3>3. 「活用できない」</h3> <p>見つけたものの、それが役に立たない状態です。例えば、書いてある内容がハイコンテキストすぎたり、欲しい情報に対して情報量が多すぎて、読むコストを支払う気になれなかったり、何かしらのクオリティの問題で、読んでもわからないといったことはよく発生します。結果、活用に至らず、担当者に話を聞く、結局ソースコードを見て確認することになり、情報を探したこと自体が徒労に終わります</p> <h3>4. 「更新されない」</h3> <p>ドキュメントは実体に則してアップデートされる必要があります。例えば、プロダクト開発で画面仕様書やAPI仕様書がある場合、最新機能のアップデートに応じて、これらは更新する必要性があるでしょう。</p> <p>明確なプロセスがあったり、APIを外部に公開しているといった事情がない限り、こういったドキュメントのアップデートを必要な時に行うという習慣をもっている人は非常に少ないです。特にドキュメントのオーナーが会社を去った場合などは、多くの場合、良いドキュメントですら管理されず、廃れていきます。</p> <p>これはそもそも「ドキュメントやナレッジを資産として、管理対象とする」ということが組織として合意されて、チームが管理する仕事の一部になっていないためです。「更新されない」という問題が発生する際のコストは高くつくことがあります。読み手が問題に気づき、担当者に問い合わせるならまだいい方で、最悪の場合、適説でない情報を元に業務が行われる可能性があるからです。</p> <h2>AI時代以前のナレッジマネジメント</h2> <p>これらの課題があることを踏まえ、ナレッジマネジメントではどのようなことが発生していたのか考えてみます</p> <p>AI時代以前は、情報が増えるほど整理や検索のコストが膨らみ、「一部の人しか知らない」状態が常態化しやすく、特に会社の規模の拡大や歴史の積み重ねによって情報やナレッジが増えていくと、検索難易度や管理難易度があがりやすい環境でした。<br /> しかしこれらの課題が浮き彫りになっていても、なぜか解決に至らないケースが多く見受けられます。それはコストパフォーマンスの問題が大きかったのだろうと思います。</p> <p>ナレッジを適切に管理、保管するのは非常にコストがかかりますし、標準的なプロセスを作るためには、現場の自由度を下げる可能性があるというトレードオフも存在します。多大なコストをかけて標準化を進め、コストを一定し払いそれらの課題を解決しても、ツールとしての検索性能がボトルネックになるかもしれません。</p> <p>これらの環境を踏まえると、ナレッジマネジメントに対してどのくらいの投資が適切であるかという判断は難しく、どこまでコストをかけて、どこまでの標準化やメンテナンスを行うのかは、慎重に意思決定をする必要がありました</p> <p>メルカリでも、情報が複数のツールに分散し、管理されていないナレッジも多く存在しています。結果、最初にあげた4つの問題がかなり発生している状態で、社内のEngineer向けのSurveyでも、常にナレッジマネジメントは課題のTop3に入っています</p> <p>さらに、メルカリグループの大きな特徴として、メルペイ、メルコインをはじめとする金融関連のプロダクトを保有しているという点があげられます。これらは厳格なプロセス、ドキュメンテーションが求められるドメインです。一方マーケットプレイスであるメルカリアプリ自体は、そこまで細かい管理は必要とされません。また、新規事業立ち上げもあるため、これら全てを横断した細かいプロセスで縛る場合、最も厳しいところに合わせる必要があり、それにはデメリットが少なからず伴う上、メルカリの自由度が高い風土とはマッチしづらく、課題は認識しつつも、課題解決には慎重になっていたという現状がありました</p> <p>AIはこの環境においてまさにゲームチェンジャーとして現れました</p> <h2>AIがナレッジマネジメントにもたらしたもの</h2> <p>AIがまずもたらした変革は、ドキュメンテーションの既存業務の圧倒的な生産性向上です。例えば以下のようなものは明確に、様々な課題を低減してくれました。既に多くのエンジニアが体験しているのではないでしょうか</p> <ul> <li>議事録を自動で作成してくれる(「記録されない」課題を低減)</li> <li>概要を知りたい場合、全文を読まずとも、長い文章の要約をしてくれる(「活用できない」課題を低減)</li> <li>検索性が圧倒的に向上し、必要な情報にリーチしやすく(「発見できない」課題を低減)</li> <li>既存のコードから仕様の言語化を実行してくれる(「記録されない」課題を低減)</li> <li>最新の議論を元に、Product Requirement Documentation (PRD)のアップデートが必要な箇所を見つける(「更新されない」課題を低減)</li> </ul> <p>これらの利便性の向上はまだしばらく続くでしょう。ナレッジマネジメントツールのAI特化の機能開発により、AIの恩恵をより幅広いユースケースで受けられるようになるはずです</p> <p>二つめの変革は、ナレッジ自体の価値があがったということです。ナレッジが人が読むものだけでなく、AIがコンテキストとして使うものになったからです。新しい、非常に大きなナレッジ活用のユースケースが生まれたと言い換えても良いでしょう</p> <p>AIは学習元となっている情報、コンテキストに依存します。そのため、仮に会社特有の開発のお作法や、ドメインナレッジがある場合、それをコンテキストとして正しく注入しない限り、それらは考慮されません。「会社のやり方や、ドメイン特有の内容を考慮したいい感じ」のアウトプットが欲しいわけで、そのためにはコンテキストとして、社内のナレッジをきちんと管理し、それがAIに届く状態にする必要があります</p> <p>総合すると、AIは</p> <ul> <li>ナレッジマネジメントを正しく行うコストを劇的に下げて</li> <li>ナレッジマネジメントのアウトプットの価値をあげた<br /> といえます</li> </ul> <p>大事なことなので、別の表現で言います<br /> 非常にコストがかかって、まぁまぁな価値を出していたナレッジマネジメントは、AI時代において、コストがそこまでかからず、めちゃくちゃ大きな価値を生む業務になったのです</p> <p><strong>つまり、ナレッジマネジメントは、AI時代において、コスとパフォーマンスが爆発的にあがったのです</strong></p> <h2>メルカリにおけるナレッジマネジメント戦略</h2> <p>メルカリでは、これらの背景を元に、ナレッジマネジメントをAI-Native時代において、重点的に投資すべき領域として定めています。<br /> その中で推進していることがいくつかありますが、そのうち大きなものを3つほど紹介します</p> <h3>1. 全社員共通のAI ReadyなCentral Knowledge Baseの構築</h3> <p>まず一つめは、AIとの相性が良く、AIに特化した機能開発が盛んなKnowledge Baseを一つ選定し、そこにナレッジを集約することです。ここで言うKnowledge Baseとは、いわゆる社内Wikiで、社内における情報を集約するためのツールを指します<br /> メルカリでは、複数のツールを必要に応じて許容しつつも、特段の理由がなければ、全てCentral Knowledge Baseに集める、という方向性を現在進めています。なぜ一つのツール、Central Knowledge Baseに舵を切ったかというと、以下のようなメリットがあるためです</p> <ul> <li>AIの接続のためのコストを減らせる(ツールが増えるとセキュリティ、運用構築、それらの動作テストなどかなりコストがかかります)</li> <li>AI機能含め、ナレッジベースツールに対する習熟度を会社全体としてあげやすい</li> <li>メタデータを統一できる(メタデータが異なる=検索ロジックの複雑性や、管理の標準化の難易度があがる。メトリクスも統一しづらい。)</li> </ul> <p>この方向性を元に、現在、メルカリはNotionをCentral Knowledge Baseとして位置付け、ナレッジの中央管理型への移行を進めています。本記事の主旨と離れるので、細かくは記載しませんが、ツール選定に関しては、フロー情報(議事録など、メンテしない情報)とストック情報の両方に強いという点や、AIとの親和性の高さが大きなポイントでした</p> <h3>2. ドキュメントをオープンにする文化の推進</h3> <p>せっかく情報が蓄積され、検索性があがっても、それらがAIや、社員がリーチできる状態でないと意味がありません。メルカリでは、良くも悪くも最低限のアクセス権限を付与する文化が多かれ少なかれあり、そもそも知りたい情報へのアクセス権限がないということがあったのです。例として、重要な意思決定に関するミーティングは基本的に参加者以外は見れないという状態でした</p> <p>そのため、現在はドキュメントを出来るだけオープンな場所におくための文化作りや仕組み作りをしており、重要な意思決定に関しても、公開範囲の明確化、拡大を推進しています</p> <p>もちろんこれは、同時に秘匿性の高い情報の適切な管理が大事になってくるため、 Personally Identifiable Information (PII、いわゆる個人情報)を含む情報などに関しては、管理方法の厳格化、プロセス化を併せて推進しています</p> <h3>3. ナレッジ蓄積文化の推進</h3> <p>そもそも、ナレッジを蓄積する文化というのは一朝一夕でできるものではありません。なぜそれが今重要なのか、そしてどのように蓄積すべきか、ということを繰り返し発信、推進しています</p> <p>メルカリ内でも、ナレッジ蓄積する文化が元々あるという部署もあれば、ナレッジ蓄積自体がそもそもほとんど行われていない部署もあり、温度感はかなりまちまちです<br /> そのため各組織から一緒に推進してもらうメンバーをアサインしてもらい、現場の温度感に合わせた組織ごとにカスタマイズされたオンボーディングの実行をしたり、新入社員向けのオンボーディングコンテンツにナレッジマネジメントを追加したりといったことをしています</p> <h2>今後の展望</h2> <p>現状では、AIによる検索性の向上、ドキュメンテーション作成の補助などの恩恵は既に得られているものの、メルカリにおいて、AI-Nativeなナレッジマネジメント環境の整備は、まだまだ推進初期の段階です。課題は盛りだくさん。しかも移行によって、新たに解決すべき課題も発生しており、まだまだ最善の状態までは行き着いていません</p> <p>とはいえ、AI-Nativeな環境を作るにおいて、ナレッジマネジメントへの投資は必須だと考えています。このナレッジマネジメントの推進は、単なるナレッジ関連業務の効率化に留まらず、AIを最大限に活かすための土台作りだからです。今回は我々が今取り組んでいるもののベースとなっている考え方、目指している方向について記事にしましたが、来年の今頃には、もう少し結果や、そこからの学びなどを記事にできればと思います。ここまで読んでくださってありがとうございます。</p> <p>明日の記事は@yanapさんです。引き続きお楽しみください。</p> n8nの静的解析CLIツールをOSS化 – JSON解析とDAGで実現するセキュリティチェックの自動化https://engineering.mercari.com/blog/entry/20251211-580dc508a7/https://engineering.mercari.com/blog/entry/20251211-580dc508a7/<p>こんにちは。メルペイのPayment &amp; Customer PlatformのAccountingチームでBackend Engineerをしている @mewuto(みゅーと)です。 この記事は、Merpay &#038; [&hellip;]</p> Sat, 13 Dec 2025 10:00:41 GMT<p>こんにちは。メルペイのPayment &amp; Customer PlatformのAccountingチームでBackend Engineerをしている <a href="https://x.com/mewuto">@mewuto</a>(みゅーと)です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の13日目の記事です。</p> <h2>要旨</h2> <p>メルカリでは、業務自動化プラットフォームとして<a href="https://n8n.io/">n8n</a>を導入していますが、ワークフローの自由度が高い反面、セキュリティリスクも懸念されます。特に「権限混同(Confused Deputy Problem)」は重大な脅威であり、個人の権限を不適切に共有することで、意図しないデータアクセスや操作が可能になってしまいます。</p> <p>本記事では、n8nワークフローのセキュリティレビューを自動化するCLIツールの開発と、GitHub Actionsを活用した実際の運用例について解説します。このツールは<a href="https://github.com/mewuto/n8ncheck">GitHub</a>でOSS公開しており、<a href="https://www.npmjs.com/package/@mewuto/n8ncheck">npm</a>からインストール可能です。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/e3dc703b-screenshot-2025-12-08-at-4.08.35-1024x314.png" alt="npm-@mewuto/n8ncheck" /></p> <p><strong>対象読者</strong>:</p> <ul> <li>ワークフロー自動化ツール(n8n、Zapier等)を使用している開発者・セキュリティ担当者</li> <li>JSONベースの静的解析やDAGを用いたフロー解析に興味のある方</li> <li>業務自動化基盤のセキュリティ強化に取り組んでいる方</li> </ul> <h2>目次</h2> <ol> <li>背景:n8n導入とセキュリティ課題</li> <li>セキュリティポリシーの定義</li> <li>アーキテクチャ概要</li> <li>ノードレベル検出の実装例:BigQuery本番環境アクセス検知</li> <li>シナリオレベル検出の実装例</li> <li>検出の例および社内での活用事例</li> <li>成果と今後の展望</li> <li>まとめ</li> </ol> <h2>1. 背景:n8n導入とセキュリティ課題</h2> <h3>n8nとは</h3> <p><a href="https://n8n.io/">n8n</a>は、ノーコード/ローコードで業務自動化ワークフローを構築できるオープンソースのプラットフォームです。ZapierやMakeと同様に、異なるサービス間のデータ連携や処理の自動化が可能ですが、n8nはより自由度が高く、柔軟なカスタマイズや複雑なフローの構築ができる点が特徴です。</p> <p>メルカリでは、開発者やビジネスチームの業務効率化のため、n8n Enterpriseを導入しました(参考:<a href="https://engineering.mercari.com/blog/entry/20251211-cf3b67a5a7/">理想の Workflow Platform という“聖杯”に、n8n でついに手が届くかもしれない</a>)。しかし、その自由度の高さゆえに、以下のセキュリティ課題が浮上しました。</p> <h3>Confused Deputy Problem(権限混同)の脅威</h3> <p>「Confused Deputy Problem」とはあるシステムにおいて、ユーザーが本来持つ権限よりも大きな権限が設定されているときに、アクセスできないはずのリソースにアクセスできたり変更できてしまう問題です。n8nワークフローでは、権限を持つメンバーのcredentialが組み込まれているため、以下のようなリスクが発生します。</p> <ul> <li><strong>データベースへの意図しないアクセス</strong>: 本来DBアクセス権を持たないメンバーが、Slackボットを実行することで、権限を持つメンバーのcredentialを通じて本番データベースにアクセスできてしまう</li> <li><strong>機密情報の意図しない漏洩</strong>: ワークフローが出力した機密情報(スプレッドシート、Slack Publicチャンネルへの投稿等)を、本来アクセス権を持たないメンバーが閲覧できてしまう</li> <li><strong>チーム間での権限境界の破綻</strong>: 別チームのメンバーが作成したワークフローを実行することで、本来アクセスできない他チームのリソース(社内ドキュメント、ストレージなど)にアクセスできてしまう</li> </ul> <p>これらの問題を防ぐため、ワークフロー有効化前の手動レビューを実施していましたが、以下の課題がありました。</p> <ol> <li><strong>レビュー工数の増大と品質の不安定性</strong>: 1ワークフローあたり、修正依頼や修正後の再レビューを含めて30分〜1時間のレビュー時間が必要。また、セキュリティガイドラインは整備されているものの、詳細な技術ドキュメントを参照しながらワークフローを作成することは容易ではなく、ユーザー自身での自己改善が難しい。加えて、人間によるレビューである以上、見落としのリスクも存在する</li> <li><strong>継続的監視の欠如</strong>: 承認後もワークフローは自由に変更可能であり、変更後の安全性を保証できない</li> </ol> <p>これらの課題を解決するため、静的セキュリティ解析ツールの開発に至りました。</p> <h2>2. セキュリティポリシーの定義</h2> <p>ツール開発にあたり、セキュリティガイドラインを策定しました。主要な要件は以下の通りです。</p> <h3>2.1 権限混同の防止</h3> <ul> <li><strong>入力ソースの制限</strong>: ワークフローをトリガーできるユーザーを明示的に制限</li> <li><strong>出力先の制限</strong>: 結果の出力先を閲覧権限を持つユーザー内に限定</li> </ul> <h3>2.2 Slack Bot特有の要件</h3> <ul> <li><strong>実行可能メンバーのホワイトリスト化</strong>: Slackトリガーには必ずユーザー検証を実装</li> <li><strong>エフェメラルメッセージの使用</strong>: 機密情報は一時的なメッセージで返す</li> <li><strong>Private Channel推奨</strong>: Public Channelへの出力を制限</li> </ul> <h3>2.3 データの入出力制御</h3> <ul> <li><strong>本番環境アクセスの明示</strong>: 本番データへのアクセスは明示的に警告</li> <li><strong>動的クエリの検証</strong>: 外部入力による動的クエリを検出。動的にSQLクエリを構築すると、本来意図していないテーブルやデータセットにアクセスできてしまうリスクがあるため、最小権限の原則に基づき使用範囲を制限</li> <li><strong>入力値のフィルタリング</strong>: 外部入力を使用する箇所での検証実装</li> <li><strong>出力先のアクセス権限制御</strong>: Google Sheetsなどへの出力時は、適切なアクセス権限スコープ(閲覧可能ユーザーの制限)が設定されていることを確認</li> </ul> <p>これらのポリシーを自動検証することで、手動レビューの負担を大幅に軽減できます。</p> <h2>3. アーキテクチャ概要</h2> <h3>3.1 全体構成と処理の流れ</h3> <p>ツールは以下の処理フローで動作します。</p> <ol> <li> <p><strong>n8nワークフローJSON読み込み</strong></p> </li> <li> <p><strong>静的解析による検証</strong>:<br /> 2.1. <strong>ノードレベルチェック</strong>: 個別ノード(BigQuery、HTTP Request、JavaScript Code、Google Sheets/Drive等)の設定を検証<br /> 2.2 <strong>シナリオレベルチェック</strong>: 複数ノード間の関係性(Slackトリガー後の呼び出しユーザーのバリデーション実装、スプレッドシート出力先の権限範囲等)をDAG(有向非巡環グラフ)を用いて解析。<br /> ※ <strong>なぜDAGが必要か</strong>: LLMによるチェックは再現性が保証されず、論理的なグラフ解析もまだ未発達です。DAGによる静的解析は、ノード間の実行順序や依存関係を確実に追跡でき、「バリデーション → 外部アクセス」といったセキュリティ上重要な順序関係を100%の精度で検証可能です。</p> </li> <li> <p><strong>検出結果の出力</strong>: Console、JSON、PR Comment形式で結果を出力</p> </li> </ol> <h3>3.2 ワークフローJSONの構造</h3> <p>n8nのワークフローは、以下のようなJSON形式で保存されます。</p> <pre><code class="language-json">{ &quot;name&quot;: &quot;My workflow&quot;, &quot;nodes&quot;: [ { &quot;id&quot;: &quot;node-id-1&quot;, &quot;type&quot;: &quot;n8n-nodes-base.googleBigQuery&quot;, &quot;parameters&quot;: { &quot;projectId&quot;: &quot;merpay-prod&quot;, &quot;sqlQuery&quot;: &quot;{{ $json.query }}&quot;, &quot;operation&quot;: &quot;executeQuery&quot; } }, { &quot;id&quot;: &quot;node-id-2&quot;, &quot;type&quot;: &quot;n8n-nodes-base.code&quot;, &quot;parameters&quot;: { &quot;jsCode&quot;: &quot;const data = $input.item.json;\nreturn { result: data.rows.length };&quot; } }, { &quot;id&quot;: &quot;node-id-3&quot;, &quot;type&quot;: &quot;n8n-nodes-base.slack&quot;, &quot;parameters&quot;: { &quot;resource&quot;: &quot;message&quot;, &quot;operation&quot;: &quot;post&quot;, &quot;channel&quot;: &quot;#general&quot;, &quot;text&quot;: &quot;{{ $json.result }}&quot; } } ], &quot;connections&quot;: { &quot;node-id-1&quot;: { &quot;main&quot;: [[{&quot;node&quot;: &quot;node-id-2&quot;, &quot;type&quot;: &quot;main&quot;, &quot;index&quot;: 0}]] }, &quot;node-id-2&quot;: { &quot;main&quot;: [[{&quot;node&quot;: &quot;node-id-3&quot;, &quot;type&quot;: &quot;main&quot;, &quot;index&quot;: 0}]] } } }</code></pre> <p>このJSONから以下の情報を抽出できます。</p> <ul> <li><strong>ノード設定</strong>: 各ノードのtype、parameters、credentials</li> <li><strong>フロー構造</strong>: connectionsによるノード間の依存関係</li> </ul> <p>各ノードのparametersには、ノードタイプに応じた詳細情報が含まれます。</p> <ul> <li>JavaScript Codeノードの実装内容</li> <li>BigQueryのデータセット名やSQLクエリ</li> <li>HTTPリクエストのエンドポイント、メソッド、リクエストボディ</li> </ul> <p>この構造化された豊富な情報により、実行前の詳細な静的解析が可能になります。</p> <h3>3.3 2層検出アーキテクチャ</h3> <p>セキュリティリスクは、個別ノードの設定だけでなく、ノード間の関係性によっても発生します。そのため、本ツールでは以下の2層アーキテクチャを採用しています。</p> <p><strong>ノードレベル検出</strong>:</p> <ul> <li>個別ノードの設定を検証</li> <li>例:BigQueryの本番プロジェクトアクセス、Slackの投稿先チャンネル</li> <li>各ノードが独立して安全な設定になっているかを確認</li> </ul> <p><strong>シナリオレベル検出</strong>:</p> <ul> <li>複数ノード間の関係性を検証</li> <li>DAGを用いたフロー解析</li> <li>例:<br /> (i) Slack Trigger → ユーザーバリデーション → 外部アクセスの順序検証<br /> (ii) Google Sheets Create → Google Drive Share/HTTP Request Permissionsのスコープ設定検証</li> <li>ワークフロー全体としてセキュリティ要件を満たしているかを確認</li> </ul> <p>この2層アプローチにより、個別の設定ミスだけでなく、ワークフロー全体の設計上の問題も検出できます。</p> <h2>4. ノードレベル検出の実装例:BigQuery本番環境アクセス検知</h2> <p>ノードレベル検出の具体例として、BigQueryの本番環境アクセス検知を解説します。</p> <h3>4.1 検出対象</h3> <p>以下のようなワークフローを検出します。</p> <pre><code class="language-json">{ &quot;parameters&quot;: { &quot;projectId&quot;: &quot;merpay-prod&quot;, &quot;operation&quot;: &quot;executeQuery&quot; }, &quot;type&quot;: &quot;n8n-nodes-base.googleBigQuery&quot; }</code></pre> <p>この設定では、<code>projectId</code>に<code>prod</code>文字列を含む本番環境プロジェクトへのアクセスを検出します。</p> <h3>4.2 実装</h3> <p>検出ロジックは <a href="https://github.com/mewuto/n8ncheck/blob/main/src/nodes/bigquery/checker.ts">BigQueryChecker</a> に実装されています。</p> <pre><code class="language-typescript">// Typescript // パラメータからプロジェクトIDを取得 const projectId = node.parameters.projectId; // 本番環境へのアクセスを検出 if (projectId.includes(&#039;prod&#039;)) { // 警告を追加 addWarning(&#039;本番環境へのアクセスが検出されました&#039;); }</code></pre> <p>詳細な実装は<a href="https://github.com/mewuto/n8ncheck">GitHubリポジトリ</a>を参照してください。</p> <h3>4.3 検出結果</h3> <p>検出された問題は以下のような形式で報告されます。</p> <pre><code class="language-markdown">### ⚠️ 警告 (Warnings) (1) - **[Execute a SQL query]** (n8n-nodes-base.googleBigQuery) 本番環境プロジェクト `merpay-prod` へのアクセスが検出されました。本当にアクセスして良いか確認してください。</code></pre> <hr /> <h2>5. シナリオレベル検出の実装例</h2> <p>シナリオレベル検出では、複数ノード間の関係性を解析します。代表的な実装例として、Slack User Validationシナリオを解説します。</p> <h3>5.1 検出対象のリスク</h3> <p>Slack Triggerを使用したワークフローでは、以下のリスクがあります。</p> <p><strong>想定されるリスクシナリオ</strong>:</p> <ul> <li>本来アクセス権を持たないメンバーがSlackボットにコマンドを送信し、アクセス権を持つメンバーのcredentialを使用して本番データベース等の機密リソースにアクセス</li> </ul> <p><strong>必要な対策</strong>:</p> <ul> <li>Slackトリガーの直後にユーザーバリデーションを実装</li> <li>外部アクセス(BigQuery、HTTP Request等)の前にバリデーションを完了</li> <li>バリデーションに失敗した場合はワークフローを停止</li> </ul> <h3>5.2 検出の流れ</h3> <p>Slack User Validationの検出は、以下の3ステップで実施されます。</p> <ol> <li><strong>DAG解析でバリデーションノードを探索</strong> (5.3節): Slack Triggerから下流のノードを辿り、Code/Ifノードを探索</li> <li><strong>バリデーション実装を検証</strong> (5.4節): 見つかったノードの内容を解析し、適切なユーザー検証が実装されているか確認</li> <li><strong>フロー整合性をチェック</strong>: バリデーション前に外部アクセス(BigQuery等)が存在しないか検証</li> </ol> <h3>5.3 DAGによるフロー解析</h3> <blockquote> <p><strong>n8nのループ対応</strong>:<br /> n8nではワークフロー内でループ(循環参照)を作成できます。しかし、フロー解析にはDAG(有向非巡環グラフ)が必要です。そのため、SCC(強連結成分分解)を用いてループを検出し、各SCCを1つのノードとして扱うことでDAGに変換します。</p> </blockquote> <p><strong>実装</strong> (<a href="https://github.com/mewuto/n8ncheck/blob/main/src/graph/workflow-graph.ts">workflow-graph.ts</a>):</p> <pre><code class="language-typescript">// Typescript // 1. 強連結成分(SCC)を検出 const sccs = stronglyConnectedComponents(this.graph); // 2. 各SCCを1つのノードとして扱い、Condensed DAGを構築 const condensedDAG = this.createCondensedDAG(sccs); // 3. トポロジカルソートで実行順序を決定 const executionOrder = topologicalSort(condensedDAG);</code></pre> <p>この変換により、ループを含むワークフローでも確実にフロー解析が可能になります。</p> <p>e.g. <strong>Slack Triggerからのパス解析</strong> (<a href="https://github.com/mewuto/n8ncheck/blob/main/src/scenarios/slack-user-validation/checker.ts">slack-user-validation/checker.ts</a>):</p> <pre><code class="language-typescript">// Typescript // 1. Slack Triggerからの全ての下流ノードを取得 const allDownstreamNodes = this.getAllDownstreamNodes(slackTriggerId); // 2. バリデーションノード候補を抽出(Code, If nodes) const codeNodes = allDownstreamNodes.filter( node =&gt; node.type === NODE_TYPES.CODE ); const ifNodes = allDownstreamNodes.filter( node =&gt; node.type === NODE_TYPES.IF ); // 3. Slack Triggerからバリデーションノードまでのパスを取得 const pathToNode = this.graph.getPathFromNodeToNode( slackTriggerId, codeNode.id ); // 4. パス上に外部アクセスノードがないか検証 const pathValid = this.isPathValid(pathToNode);</code></pre> <h3>5.4 バリデーションノードの検証</h3> <p>バリデーションノード候補が見つかったら、ノードタイプに応じて内容を解析します。</p> <h4><strong>Codeノードによるバリデーション</strong> (<a href="https://github.com/mewuto/n8ncheck/blob/main/src/scenarios/slack-user-validation/validators/jscode-validator.ts">jscode-validator.ts</a>)</h4> <p>BabelのAST解析で以下の4要素を検出:</p> <ol> <li><strong>User ID抽出</strong>: <code>$input.item.json.user</code>または<code>$json.user</code></li> <li><strong>許可ユーザーのホワイトリスト定義</strong>: <code>const users = { &#039;U123&#039;: &#039;user1&#039;, &#039;U456&#039;: &#039;user2&#039; }</code></li> <li><strong>検証ロジック</strong>: <code>!users.hasOwnProperty(userId)</code>等</li> <li><strong>エラーハンドリング</strong>: 検証失敗時の<code>return</code>または<code>throw</code></li> </ol> <p>4要素すべて揃っている場合は完全な実装と判定します。</p> <h4><strong>Ifノードによるバリデーション</strong> (<a href="https://github.com/mewuto/n8ncheck/blob/main/src/scenarios/slack-user-validation/validators/if-node-validator.ts">if-node-validator.ts</a>)</h4> <p>Ifノードの条件式で以下をチェック:</p> <ol> <li><strong>メールアドレスパターン</strong>: 右辺値が会社ドメイン(例: <code>@example.com</code>)のパターンと一致</li> <li><strong>ユーザーコンテキスト抽出</strong>: 左辺値にユーザー情報の抽出が含まれる</li> <li><strong>等価演算子</strong>: <code>equals</code>演算子による厳密なチェック</li> </ol> <p>If nodeで「<code>{{ $json.user }}</code> が <code>@example.com</code> メールと一致するか」をチェックするパターンを検出可能です。</p> <h3>5.5 検出結果の例</h3> <h4><strong>Codeノード(javascript)</strong></h4> <p><strong>完全なユーザーバリデーション実装</strong></p> <pre><code class="language-javascript">// Javascript // Slack Triggerの直後のCode node const userId = $input.item.json.user; const users = { &#039;U0123ABCD&#039;: &#039;authorized_user1&#039;, &#039;U4567EFGH&#039;: &#039;authorized_user2&#039; }; if (!users.hasOwnProperty(userId)) { return; // バリデーション失敗時に停止 } // 以降の処理</code></pre> <p>検出結果:</p> <pre><code class="language-markdown">### ✅ OK (1) Slack TriggerのCodeノードで適切なユーザー検証が実装されています。承認済みユーザー数: 2</code></pre> <p><strong>不完全なユーザーバリデーション実装</strong></p> <pre><code class="language-javascript">// Javascript // User ID抽出のみ実装(ホワイトリストなし) const userId = $input.item.json.user; // 検証ロジックなし</code></pre> <p>検出結果:</p> <pre><code class="language-markdown">### 🚨 重大な問題 (Critical Issues) (1) - **[Slack Trigger]** (n8n-nodes-base.slackTrigger) Slack Triggerに対するユーザー検証が不完全です。以下の「不足している要素と修正方法」を参考にCodeノード &quot;User Validation&quot; を設定してください: - 不足している要素と修正方法: - 1. 認証リスト: オブジェクトとして定義(変数名は &quot;users&quot; である必要があります): `const users = { &quot;userId1&quot;: &quot;userName1&quot;, &quot;userId2&quot;: &quot;userName2&quot; }` - 2. 検証ロジック: `if (!users.hasOwnProperty(userId))`, `if (!(userId in users))` などのチェックを追加 - 3. エラーハンドリング: 検証のif文内に `return` または `throw` ステートメントを追加</code></pre> <h4><strong>Ifノード</strong></h4> <p>Ifノードで以下のような条件を設定:</p> <ul> <li>左辺値: <code>{{ $json.user }}</code> (Slackユーザー情報の抽出)</li> <li>演算子: <code>equals</code> (等価演算子)</li> <li>右辺値: <code>@example.com</code> (会社ドメインパターン)</li> </ul> <p>検出結果:</p> <pre><code class="language-markdown">### ✅ OK (1) Slack TriggerのIfノードで適切なユーザー検証が実装されています。</code></pre> <h2>6. 検出の例および社内での活用事例</h2> <p>メルカリでは、このツールをGitHub Actionsに組み込み、ワークフロー追加時のPRで自動的にセキュリティチェックを実行しています。</p> <h3>6.1 問題のあるワークフローの検出例</h3> <p>以下のワークフローには複数のセキュリティ上の問題および懸念があります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/2538f821-error-workflow.png" alt="error-pattern-workflow" /></p> <p><strong>検出された問題</strong>:</p> <ul> <li>Slack Trigger直後のユーザーバリデーションが未実装</li> <li>BigQueryで本番環境(prod)プロジェクトへのアクセスを実行</li> <li>Google Sheets作成後のアクセス権限スコープ設定が未実装</li> </ul> <p>これらの問題は、PRコメントとして以下のように報告されます:<br /> Slack TriggerとGoogle Sheetsでは重大な問題として、BigQueryの本番環境アクセスについては警告として検出されます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/45c2b175-error-pattern.png" alt="error-pattern-pr-sample" /></p> <h3>6.2 修正後のワークフローの検証例</h3> <p>上記の問題を修正したワークフローでは、以下の対策が実装されています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/de03b561-success-workflow.png" alt="success-workflow" /></p> <p><strong>実装された対策</strong>:</p> <ul> <li>Ifノードによるユーザーバリデーション(メールドメインチェック)</li> <li>Google Driveノードによる適切なアクセス権限スコープ制御</li> </ul> <p>そして、修正後のPRコメントでは、問題が解消されたことが確認できます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/a2c20638-success-pattern.png" alt="success-pattern-pr-sample" /></p> <p>ユーザーバリデーションが正しく実装されていることを検出し、スコープ制御の設定内容も詳細に表示されます。これにより、レビュアーはセキュリティ観点を即座に確認できるようになっています。</p> <h2>7. 成果と今後の展望</h2> <p>ツール導入によって得られた定量的な成果に加え、開発を通じて得られた技術的な知見、そして今後の展望について整理します。</p> <h3>7.1 定量的成果</h3> <p><strong>レビュー工数の削減</strong>:</p> <ul> <li>手動レビュー時間: 30-60分/ワークフロー → 5-10分/ワークフロー</li> <li>削減率: <strong>約80-85%</strong></li> </ul> <p><strong>検出精度</strong>:</p> <ul> <li>重大セキュリティリスク(🚨 Error)の検出率: <strong>100%</strong></li> <li>偽陽性率: 約15%(⚠️ Warningレベル)</li> </ul> <h3>7.2 技術的知見</h3> <p><strong>JSONからの情報抽出</strong>:</p> <ul> <li>n8nのワークフローJSONには、ノード設定、接続情報、コード内容など、豊富な情報が含まれる</li> <li>この構造化データにより、他のワークフローツール(Zapier、Make等)よりも詳細な静的解析が可能</li> </ul> <p><strong>DAGによるフロー解析</strong>:</p> <ul> <li><a href="https://graphology.github.io/">graphology</a>をベースとしたグラフ構造で、ノード間の依存関係をDAGとしてモデル化</li> <li><code>graphology-components</code>でSCC(強連結成分分解)を実行し、ループをDAGに変換</li> <li><code>graphology-dag</code>のトポロジカルソートで実行順序を決定し、複雑なワークフローパターンを検出可能</li> <li>特に「ユーザーバリデーション → 外部アクセス」の順序検証など、セキュリティ上重要な関係性を自動検証できる</li> </ul> <p><strong>AST解析の有用性</strong>:</p> <ul> <li>正規表現では検出が難しい複雑なJavaScriptパターンを、ASTによる構造的な解析で実現</li> <li>Babelのtraverseを使うことで、コードの意味を理解した検証が可能</li> </ul> <p><strong>n8n公式との型定義同期</strong>:</p> <ul> <li>n8nは個別ノードの型定義を公式に提供していないため、このツールでは独自に型定義を作成</li> <li>公式パッケージには<strong>TypeScript型定義</strong>ではなく、<strong>ランタイムスキーマ定義</strong>(<code>versionDescription.properties</code>)が含まれており、各パラメータの名前、型、必須/任意などの情報を持つ</li> <li>このスキーマ定義を活用し、Schema Validator (<a href="https://github.com/mewuto/n8ncheck/blob/main/src/nodes/bigquery/schema-validator.ts">bigquery/schema-validator.ts</a>) で自作型定義のキーと公式スキーマのプロパティ名を照合</li> <li>不一致が検出された場合は警告を出力することで、公式の破壊的変更(パラメータ名変更、削除等)にも迅速に対応可能</li> </ul> <h3>7.3 今後の展望</h3> <p><strong>検出ノードの拡充</strong>:</p> <ul> <li>現在対応しているノードタイプの拡大</li> </ul> <p><strong>LLMとの連携</strong>:</p> <ul> <li>検出結果に対する修正提案の自動生成</li> <li>ワークフローの意図を理解した高度なセキュリティ分析</li> </ul> <p><strong>動的情報を活用した検出の強化</strong>:</p> <ul> <li>Slack APIを用いたチャンネル属性の検証:チャンネルIDからPrivate/Publicを判定し、Public Channelへの投稿を警告</li> <li>Credential情報から取得可能な権限スコープの検証:設定されたCredentialの実際の権限を確認し、過剰な権限付与を検出</li> </ul> <h2>8. まとめ</h2> <p>本記事では、n8nワークフローの静的セキュリティ解析ツールの開発について解説しました。</p> <p><strong>技術的ポイント</strong>:</p> <ol> <li><strong>ワークフローJSONの豊富な情報活用</strong>: ノード設定、接続情報、コード内容を完全に抽出可能</li> <li><strong>DAGによるフロー解析</strong>: 複数ノード間の関係性を解析し、セキュリティ上重要なパターンを検出</li> <li><strong>AST解析</strong>: JavaScriptコードを構造的に理解し、ユーザーバリデーションロジックの完全性を検証</li> </ol> <p><strong>ビジネスインパクト</strong>:</p> <ul> <li>手動レビュー工数を80%削減</li> <li>継続的な自動監視により、変更後のワークフローも安全性を保証</li> <li>セキュリティポリシーの標準化と一貫性のある適用</li> </ul> <p>ワークフロー自動化ツールの導入が進む中、本ツールのような静的解析アプローチは、他のプラットフォームにも応用可能です。業務効率化とセキュリティ確保の両立を目指す組織の参考になれば幸いです。</p> <p>明日の記事は abcdefujiさんです。引き続きお楽しみください。</p> <p><em>n8n.io logo source: <a href="https://n8n.io/brandguidelines/">https://n8n.io/brandguidelines/</a></em></p> Adsシステムの急成長を支える技術:信頼性と収益性を取り戻した「PJ-MARP」の全貌https://engineering.mercari.com/blog/entry/20251212-7fe4c31bf4/https://engineering.mercari.com/blog/entry/20251212-7fe4c31bf4/<p>こんにちは。メルカリでエンジニアリングマネージャーとして働いています@tokkuuです。 この記事は、Mercari Advent Calendar 2025 の12日目の記事です。 今回は、私たちのAds事業が急成長す [&hellip;]</p> Fri, 12 Dec 2025 17:06:31 GMT<p>こんにちは。メルカリでエンジニアリングマネージャーとして働いています<a href="https://x.com/tokkuu">@tokkuu</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a> の12日目の記事です。<br /> </p> <p>今回は、私たちのAds事業が急成長する過程で直面したシステム課題と、それを乗り越えるために立ち上げたプロジェクト「PJ-MARP」について紹介します。</p> <p>「PJ-MARP」は “Make Ads Robust and Profitable” の略称で、Adsシステムを「堅牢(Robust)」かつ「収益性の高い(Profitable)」状態へと再構築することを目的とした社内プロジェクトです。</p> <p>急成長する事業の足元を支えるため、技術的負債の解消からインフラ最適化、チーム運営の改善までを横断的に推進しました。</p> <p>事業の急拡大に伴うシステム負荷の増大、それに伴うコストの増加やインシデントの多発は、多くのプロダクトが通る「成長痛」ではないでしょうか。<br /> 本記事では、私たちがどのようにして<strong>システムの信頼性</strong>と<strong>収益性</strong>を取り戻したのか、技術的なアプローチと組織的な取り組みの両面からご紹介します。</p> <h2>急成長の裏で直面した「壁」</h2> <p>Ads事業はリリース当初から飛躍的に成長していきました。<br /> 具体的な数字で言うと、インプレッション数はローンチ前の4倍、コンバージョン数(CV)に至っては5〜10倍という規模にまで拡大しています。</p> <p>初期フェーズでは、市場探索と仮説検証を最優先し、広告主や代理店が利用したいと思うようなプロダクトへと早く成長させる必要がありました。<br /> このような<strong>スピードと成長</strong>を重視する戦略をとった結果、広告在庫が十分に溜まり、アカウント数が増加していき、ビジネスとして順調にスケールしていきました。<br /> しかし、その急成長の裏でシステムでは課題が徐々に露呈していきました。</p> <h2>直面していた3つの課題</h2> <p>特に2024年の年末から2025年の年始にかけて、私たちは以下のような深刻な課題に直面していました。</p> <h3>頻発するインシデント</h3> <p>我々のビジネスの成長が加速したことと相まって、広告業界として盛り上がりを見せる年末にかけてインシデントが多発しました。<br /> インシデントの突発的な対応が増え、対処療法的なスケールアップなどの対応を繰り返し、結果としてチームとして徐々に疲弊していきました。</p> <h3>インフラコストの増大</h3> <p>売上は伸びていましたが、その分それと同じ勢いでクラウド(GCP)の利用費も増大していました。スピードを優先したため非効率にコストがかかっている部分も多く、<strong>増えた売上がそのままGCP費用に消えていく</strong>ような状況になりつつありました。</p> <h3>広告表示機会の損失(Fill-rateの低下)</h3> <p>Fill-rateとは広告リクエストに対して実際に広告を返せた割合のことです。<br /> システム遅延により、タイムアウトが発生し、本来表示できるはずの広告が表示できないケースが増えていました。<br /> 具体的には改善前の1月の時点で、Worst caseでは iOSで51%、Androidで26% という危機的な状況でした。</p> <p>急成長したことによりこのような問題が多発したため、このままでは事業の成長を阻害してしまいます。</p> <div align="center"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/ee3004eb-pjmarpfigure.png"> </div> <p>そこで私たちは、収益性と堅牢性を確立することを目的としたプロジェクト、PJ-MARPを立ち上げました。</p> <h2>技術的アプローチ:アーキテクチャレベルでの見直し</h2> <p>PJ-MARPでは、単なる対症療法的なバグ修正ではなく、根本的なアーキテクチャの見直しを行いました。リアーキテクチャ前のシステムでは非効率な処理やデータストアのアクセスが多く、処理速度の向上がそのままコスト最適化に結びつくケースが多々ありました。<br /> また、広告においてログはとても重要な役割を持つため、ログを記録するパイプラインやデータストアについてはセンシティブに扱う必要があります。<br /> 私たちはこれらの理由から、<strong>パフォーマンスとコストの最適化</strong>と<strong>データストアとインフラの改善</strong>の2つの観点を重点的に対応することにしました。<br /> 詳細はこの記事では紹介しませんが、主に効果が高かった施策について簡単に紹介します。</p> <h3>パフォーマンスとコストの最適化</h3> <p>まず着手したのは、リクエスト処理の効率化です。</p> <h4>Cache-chainingの導入</h4> <p>頻繁にアクセスされるデータに対して、多層的なキャッシュ戦略(Cache-chaining)を導入しました。これにより、DBへの直接的なアクセス数を大幅に減らし、レイテンシを短縮しました。<br /> DBへのアクセス数を減らすことで、DB側のコストも削減することができました。</p> <h4>Redisコマンドの最適化</h4> <p>Adsでは検索結果のページング処理の過程で、Redisへ検索セッションのキャッシュを行っています。<br /> このときのアクセスパターンを見直し、非効率なRedis操作コマンド実行を削減しました。</p> <h4>gRPCコールの削減</h4> <p>Adsのシステムは広告を絞り込む過程で様々なマイクロサービス間の通信が発生します。<br /> スピード重視の開発によって整理されていなかったマイクロサービス間の通信においても、Profilerを用いて処理を解析し、不要なgRPCコールを洗い出すことによって、通信オーバーヘッドを削減しました。</p> <h3>データストアとインフラの改善</h3> <p>インシデントの問題となりやすい、データストア周りについても改善を行いました。</p> <h4>BigTableのホットスポット解消</h4> <p>特定のノードに負荷が集中していたBigTableのキー設計を見直し、ホットスポットを解消することでスループットを安定させました。</p> <h4>Elasticsearchのインデックス再構築</h4> <p>Adsではユーザーの検索語句に基づいて広告をマッチングするためにElasticsearchを使用しています。このマッチングパフォーマンスを向上と、オーバーヘッドを減らす目的でインデックスのマッピングやシャード設定を見直し、再構築を行いました。</p> <h4>データパイプラインのリファクタリング</h4> <p>ログ収集やその後の集計処理を行うデータパイプラインを最適化し、遅延・欠損なくデータを処理できる基盤を整えました。<br /> 具体的にはDataflow上で動く各ジョブのコードレベル、フローレベルのリファクタリングや、失敗時の対処、モニタリングの強化を行いました。</p> <h2>プロセスと組織:チームをどう動かしたか</h2> <p>これらの多数の取り組みを素早く完了させるために、技術的な修正と同じくらい重要だったのが、徹底的な可視化でした。</p> <h3>システム構造の可視化で現状を正しく捉える</h3> <p>複雑化したシステムを改善するには、まず現状を正しく理解することが欠かせません。<br /> 今回のプロジェクトは、Adsチームだけでなく他チームからの協力も多く得ながら進めたため、システム全体の理解を深め、共通認識を持つことが重要でした。<br /> そこで私たちは、時間をかけてアーキテクチャ図や処理フロー図を詳細化することにしました。<br /> あえて非同期で進めず、長い時間を確保してドキュメントを繰り返し更新しながら議論を重ねることで、ステークホルダー全員の認識を丁寧に揃えることを狙いました。<br /> その結果、「現在のシステムがどうなっているのか」「どこがボトルネックなのか」という点について、徹底的に共通理解を築くことができました。</p> <h3>KPIの可視化で改善効果をリアルタイムに把握</h3> <p>また、DataDogやLooker Studioを活用し、主要KPIを継続的にトラッキングできるダッシュボードを構築しました。<br /> これにより、改善施策の効果をリアルタイムに可視化し、意思決定のスピードと精度を高めることができました。</p> <p>たとえば、「この施策を導入した結果、コストがどれだけ削減されたのか」「Fill-rateがどの程度改善したのか」といった成果を即座に確認できます。<br /> 数値として成果が見えることで、チーム全体の達成感や次の改善への意欲が自然と高まり、モチベーション維持にも大きく貢献したと感じています。</p> <h2>成果</h2> <p>PJ-MARPの取り組みの結果、劇的な改善が見られました。<br /> 改善前は危機的状況だったFill-rateは、施策適用後の直近1ヶ月では、プラットフォームを問わず 一貫して95%以上の水準 を維持できるようになりました。</p> <p>またコスト面でも大きな成果が出ました。1月と比べて、3月は約28%のコスト削減を実現しました。</p> <p>ビジネスの成長を止めずに、利益率を大きく改善することに成功しました。</p> <h2>まとめ</h2> <p>今回のプロジェクトを通じて、私たちは多くの学びを得ました。</p> <ol> <li><strong>コストダッシュボードは初期に作る</strong>: 何かが起きてから見るのではなく、常にモニタリングできる状態にしておくことで、異常検知やトリアージが容易になります。</li> <li><strong>キャッシュは最初から考える</strong>: パフォーマンス向上のため、特に広告プロダクトのような高トラフィック低レイテンシが求められるシステムでは、キャッシュ機構はサービス開発の初期段階で組み込んでおくのがよいと感じました。</li> <li><strong>定期的な見直しと外部の視点</strong>: アーキテクチャは一度作って終わりではなく、効率化のために定期的に見直す必要があります。その際、チーム外のエキスパートの視点を入れることが非常に有効です。</li> <li><strong>本番投入後の再評価</strong>: 本番環境での挙動を確認し、設定値を再評価・チューニングするプロセスは考慮しておくべきだと感じました。この再評価とチューニングを継続的に行うことがシステムを堅牢にするうえで重要だと思います。</li> </ol> <p>PJ-MARPを通じて、システムが堅牢になっただけでなく、Adsチームの結束力が強まり、特定の個人のスキルに依存しない体制が整いました。<br /> 今後も、システムの信頼性と収益性のバランスを取りながら、Ads事業のさらなる成長を支えていきたいと思います。</p> <p>明日の記事は <a href="https://twitter.com/afroscript10">@t-hiroi</a>さんです。引き続きお楽しみください。</p> Making n8n Enterprise-Ready: 企業向けn8nの導入と運用の取り組みhttps://engineering.mercari.com/blog/entry/20251211-6fe0bc0838/https://engineering.mercari.com/blog/entry/20251211-6fe0bc0838/<p>こんにちは。JB SREの @T です。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の12日目の記事です。 弊社ではAI Workflow Platformとしてn8 [&hellip;]</p> Fri, 12 Dec 2025 10:00:11 GMT<p>こんにちは。JB SREの @T です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の12日目の記事です。</p> <p>弊社ではAI Workflow Platformとして<a href="https://n8n.io/">n8n</a>を導入しました。<br /> 概要や背景についての詳細は、前日のISSAさんによる「<a href="https://engineering.mercari.com/blog/entry/20251211-cf3b67a5a7/" title="理想の Workflow Platform という“聖杯”に、n8n でついに手が届くかもしれない">理想の Workflow Platform という“聖杯”に、n8n でついに手が届くかもしれない</a>」の記事で紹介されていますので、そちらもご覧ください。<br /> 本記事では、n8n Enterprise Edition を社内導入するにあたり、システム面で対応した内容について、いくつかご紹介します。</p> <h2>n8nを弊社基準のSecurity、ガバナンスに適合させるための挑戦</h2> <p>チームで、あるいは全社規模で自動化ツールを展開しようとするとき、ScalabilityやSecurity、ガバナンスといった壁に突き当たった経験はありませんか?<br /> n8nは非常に柔軟で強力なAI Workflow Engineですが、それを数百人規模の組織で安全かつ安定して運用するためには、デフォルトの状態から一歩進んだ設計と工夫が必要です。<br /> 本記事では、私たちがn8nを単なる便利ツールから、信頼性の高い「AI Workflow Platform」へと進化させるために実施した具体的な取り組みをご紹介します。 もしかすると、「n8nはここまで拡張・カスタマイズできるのか」という、Enterprise利用における柔軟性の高さを再発見していただけるかもしれません。</p> <h2>なぜEnterprise Editionだったのか?</h2> <p>まず、私たちが直面した最初の選択は「<a href="https://docs.n8n.io/hosting/community-edition-features/">Community Edition(CE)</a>か、<a href="https://n8n.io/enterprise/">Enterprise Edition</a>か」でした。<br /> 「Workflow Engine自体は同じコードベースなのだから、無償のCEで十分ではないか?」 そう考える方も多いでしょう。実際、私たちも最初はそう考えました。しかし、組織で利用するPlatformとしてn8nを捉え直したとき、Enterprise Editionを選択する必然性が見えてきました。 CEとEnterprise Editionの決定的な違いは、Workflow機能そのものではなく、<strong>Team collaboration</strong>、<strong>Security</strong>、<strong>Scalability</strong>といった「周辺機能」にあります。</p> <ul> <li><strong>Team collaboration</strong>: CEではWorkflowはOwner1人が属人的に運用し、個々のユーザーが限られた権限の中、パーソナルな空間で使用する設計思想が感じ取れます。一方、Enterpriseでは<a href="https://docs.n8n.io/user-management/rbac/projects/">Projects</a>機能により、チーム単位でのWorkflow共有や、<a href="https://docs.n8n.io/user-management/rbac/role-types/">RBAC (Role-Based Access Control)</a> によるきめ細かな権限管理が可能になります </li> <li><strong>Security</strong>: 企業導入の必須要件となりがちな<a href="https://docs.n8n.io/hosting/securing/set-up-sso/">SSO</a> (SAML/OIDC)や、HashiCorp Vaultなどの外部Secret store連携はEnterpriseのみの機能です </li> <li><strong>Scalability</strong>: 大規模なトラフィックを捌くための<a href="https://docs.n8n.io/hosting/scaling/queue-mode/#configuring-multi-main-setup">Multi-main mode</a>や、Databaseを肥大化させないための<a href="https://docs.n8n.io/data/binary-data/">Binary data</a>の外部ストレージ保存(S3等)もEnterpriseがサポートしています</li> </ul> <p>単にWorkflowを動かすだけならCEで十分です。しかし、私たちが目指したのは、属人化を防ぎ、監査可能性を担保し、全社規模で安心して利用できる「基盤」でした。その実現には、Enterprise Editionが提供する、特にSecurityとガバナンスに関わる機能群が不可欠だったのです</p> <h2>Enterprise導入を支えるn8nのシステム構成</h2> <p>私たちはn8nをGoogle Cloud上にSelf-hostingしています。 Enterprise水準のScalabilityとSecurityを確保するために、以下のような構成を採用しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/c9b5160a-image4.png" alt="Google Cloud architecture diagram for n8n enterprise" /></p> <h3>ComputeとNetwork</h3> <ul> <li><strong>Compute</strong>: <strong>Cloud Run</strong>を採用し、Serverlessならではの運用負荷の低さとScalabilityを享受しています </li> <li><strong>Network</strong>: <a href="https://docs.cloud.google.com/run/docs/configuring/vpc-direct-vpc"><strong>Direct VPC Egress</strong></a>と<a href="https://docs.cloud.google.com/load-balancing/docs/https/setting-up-https-serverless"><strong>Serverless NEG</strong></a>を利用し、private IPのみを持つセキュアな構成を実現しています</li> </ul> <h3>データ永続化とGCSの活用</h3> <p>Cloud Runの一時ディスクはインスタンス再起動で消えてしまうため、GCS (Google Cloud Storage) をマウントし、以下のデータを永続化しています。</p> <ul> <li><a href="https://docs.n8n.io/data/binary-data/"><strong>Binary data</strong></a>: n8nの<a href="https://docs.n8n.io/hosting/scaling/external-storage/"><code>external-storage</code></a>機能を使い、Workflowで扱うファイルの実体をDBではなくGCSに保存しています</li> <li><a href="https://docs.n8n.io/integrations/community-nodes/installation/manual-install/"><strong>Community node</strong></a>: 通常、n8nのCommunity nodeはGUIから簡単にインストールできますが、Cloud Run環境では永続化のためにマウントしたGCSへインストールする必要があります。 しかし、GUI上で直接インストールを試みると、GCSのマウントに使用しているgcsfuseの制約により、下記のエラーが発生し、正常に完了しませんでした。 <pre><code>writes will fall back to staged writes due to err: cant allocate any block as global max blocks limit is reached</code></pre> <p>本来であれば <a href="https://docs.cloud.google.com/storage/docs/cloud-storage-fuse/cli-options#write-global-max-blocks"><code>--write-global-max-blocks</code></a> optionでブロック制限を緩和すべきところですが、<a href="https://docs.cloud.google.com/run/docs/configuring/services/cloud-storage-volume-mounts">Cloud Runのvolume mount</a>ではこのoptionがサポートされていませんでした。 そこで回避策として、GitHub Actions上で<code>npm install</code>を実行し、生成されたfile群をGCSにsyncさせ、n8n起動時にそれを読み込ませるという運用フローを採用しています<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/223888df-image3.png" alt="How to install Community node to n8n enterprise" /></p> </li> <li><a href="https://docs.n8n.io/embed/configuration/#backend-hooks"><strong>External Hook</strong></a>: 後述するガバナンス用のスクリプトもGCSに配置しています</li> </ul> <h3><a href="https://github.com/GoogleCloudPlatform/cloud-run-external-metrics-autoscaling">Cloud Run External Metrics Autoscaling (CREMA)</a> によるWorkerのAutoscale</h3> <p>特筆すべき工夫として、<a href="https://docs.cloud.google.com/run/docs/deploy-worker-pools">Worker pools</a>のScalability確保があります。<br /> 「Cloud RunのAutoscale」といえば、CPU使用率やリクエスト数を基準にするのが一般的でしょう。しかし、n8nのWorkerのような非同期jobを処理するworkloadにおいては、それらの指標だけでは負荷(jobの滞留状況)を正確に反映できない場合があります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/1a8cc820-image1.png" alt="Cloud Run External Metrics Autoscaling(CREMA)" /></p> <p>そこで私たちは、<a href="https://github.com/GoogleCloudPlatform/cloud-run-external-metrics-autoscaling"><strong>Cloud Run External Metrics Autoscaling (CREMA)</strong></a> の導入を検討しています。これは<a href="https://keda.sh/">KEDA</a>を利用してDataDogなどの外部metricsを元にCloud Runをスケールさせる仕組みです。 これにより、n8nのQueueの深さなど、アプリケーション固有のmetricsに基づいた最適なAutoscalingを実現しようとしています。</p> <h2>n8nを統制のとれたPlatformへ昇華させる機能群</h2> <p>システム構成に加え、n8nの機能をフル活用して、ガバナンスと利便性を両立させています。</p> <h3>1. TerraformによるProject機能のコード管理</h3> <p>みなさんの組織では、誰がどのWorkflowをメンテナンスしているか、一目で追えているでしょうか? 複数人での利用において、Personal spaceの共有機能はSecurity的な懸念(誰にでも全権限で共有されてしまう等)がありました。 解決策として<strong>Projects機能</strong>を採用しましたが、GUI操作での管理は属人化の温床です。 そこで私たちは、<strong>n8n Terraform provider</strong>を内製化し、Projectの作成からメンバーのアサインまでをすべてTerraformでコード管理しています。Terraform providerを内製化した理由は、公式でのTerraform providerの提供がなかったのと、OSSで開発されているTerraform providerがProjects機能に対応していなかったためです。</p> <pre><code>resource &quot;n8n_project&quot; &quot;sre_team&quot; { name = &quot;SRE Team&quot; } resource &quot;n8n_project_member&quot; &quot;sato&quot; { project_id = n8n_project.sre_team.id user_id = data.n8n_user.sato.id role = &quot;project:editor&quot; }</code></pre> <p><em>e.g.: ProjectのTerraform resource設定</em></p> <p>これにより、権限設定の変更履歴がGitに残り、レビュープロセスを通すことが可能になりました。</p> <h3>2. Oktaを活用したSSOとUser-Provisioning</h3> <p>認証は全社標準のOktaとSAML連携し、SSOを実現しています。 さらに、v1.122.2から導入された<a href="https://docs.n8n.io/release-notes/#n8n11220">SAML経由でのrole-provisioning</a>を活用予定です。これは、Okta側のuser属性(<code>n8n_instance_role</code>, <code>n8n_projects</code>)に応じて、n8nログイン時に自動的にロールや所属Projectを割り当てる機能です。 これにより、入退社や異動に伴う権限変更をOkta側で一元管理でき、n8n側の運用負荷を大幅に削減できます。</p> <h3>3. Audit Logの監査とプロアクティブな自動化</h3> <p>監査ログの取得は必須要件ですが、私たちは<a href="https://docs.n8n.io/log-streaming/">Log Streaming</a>機能を使い、ログを単なる記録以上のものとして活用しています。 以前は「監査ログは何かあった時に後から見るもの」という受け身の運用でしたが、現在ではログを起点に次のアクションを自動化する運用へと変化しています。<br /> n8nから出力されるeventログ(user登録、Workflow保存など)をトリガーに、Cloud RunのSidecarを利用し、以下のような自動化を行っています。</p> <ul> <li><strong>初期設定の自動化</strong>: ユーザーが新規登録(<code>User signed up</code>)されたら、自動的に共通のprojectへ招待し、Onboardingなどの手間を省いています<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/f7aa16c1-image2.png" alt="Sequence Diagram for Initial Setup Automation" /></li> <li><strong>セキュリティ通知</strong>: WorkflowやCredentialが不適切に共有(<code>User credentials shared</code>等)されたら、Slackで通知し、Projects機能の利用を促します<img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/ae70bacd-image5.png" alt="Security Notification Sequence Diagram" /><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/298d2cb5-image10.png" alt="Slack Notification Example" /><em>e.g.: Slackでの通知メッセージ</em></li> </ul> <p>このように、ログを単なる記録として扱うだけでなく、次のアクションを自動実行するトリガーとして活用することで、プロアクティブなガバナンスを実現しています。</p> <h3>4. External Hookによる徹底したガバナンス</h3> <p>n8nの自由度は魅力ですが、統制の観点からはリスクにもなり得ます。 私たちは<a href="https://docs.n8n.io/embed/configuration/#backend-hooks">External Hook</a>機能を活用し、Workflowが保存(<code>workflow.update</code>, <code>workflow.create</code>)される直前に独自のバリデーション処理を挟み込んでいます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/c64aa6c2-image7.png" alt="External Hook Sequence Diagram" /></p> <ul> <li><strong>機密情報のチェック</strong>: Workflow内にAPI keyやパスワードなどが直書きされていないか確認し、検出された場合はWorkflowの保存をブロックして修正を促します</li> <li><strong>通信先の制限</strong>: n8nのHTTP Request nodeで、localhostや社内ネットワークの特定セグメントなど、アクセスしてほしくない宛先が設定されていないか検証します</li> <li><strong>承認フローの強制</strong>: WorkflowをActiveにする際、所定の承認プロセスを経ていない場合は有効化できないように制御します。これにより、管理されていない野良Workflowの乱立や、意図しないデータ漏洩を防いでいます</li> </ul> <p>これらのExternal Hook処理により、n8nは「自由なツール」から「ガードレールの効いたWorkflow Platform」へと変化しました。</p> <h3>5. Code node実行環境の分離と堅牢化</h3> <p>n8nのCode nodeは、ユーザーがJavaScriptコードを実行できる非常に強力な機能ですが、複数人が利用する環境においては、その実行環境の管理が課題となります。 そこで私たちは、コード実行を安全に行うための<a href="https://docs.n8n.io/hosting/configuration/task-runners/">Task runners</a>という仕組みを使用しました。<br /> Task runnersには2つの動作モードがあります。デフォルトの<code>Internal</code>モードはn8nのメインプロセス内でコードを実行しますが、私たちはより堅牢性を高めるため、<code>External</code>モードを採用しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/539d2ac3-image9.png" alt="Task runners external mode Diagram" /><br /> <em>Source: <a href="https://docs.n8n.io/hosting/configuration/task-runners/#external-mode">n8n Docs &#8211; Task runners External mode</a></em></p> <p>これは、コードの実行環境をn8n本体から切り離された独立したプロセス(CloudRun Sidecarコンテナ)で動作させるモードです。この構成により、万が一Code node内で意図しない挙動のスクリプトが実行されたとしても、その影響範囲をコンテナ内に封じ込め、n8n本体の安定性を維持することができます。 さらに、私たちは以下の環境変数を設定し、セキュリティの強化を図っています。</p> <ul> <li><strong>Pythonの無効化</strong>: <a href="https://docs.n8n.io/hosting/configuration/environment-variables/nodes/">N8N_PYTHON_ENABLED</a>環境変数をfalseに設定し、Pythonの実行を無効化しました。これは、PythonのTask runnerがまだBeta版である点を考慮したためです</li> <li><strong>一部Nodeの無効化</strong>: <a href="https://docs.n8n.io/embed/configuration/#environment-variables">NODES_EXCLUDE</a>環境変数を利用し、<a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.executecommand">Execute Command Node</a>と<a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.readwritefile">Read/Write Files from Disk Node</a>を無効化しました。これらのNodeは、envコマンドによる環境変数の閲覧や、サーバー上のファイルへの意図しないアクセスを許容してしまう可能性があるため、リスクを未然に防ぐ目的で利用を制限しています</li> </ul> <p>これらの設定を通じて、Code nodeの自由度を活かしつつも、より安心して利用できる環境を目指しました。</p> <h2>まとめ</h2> <p>n8n Enterprise Editionが提供するProjects、SSO、Log Streaming、External Hookといった機能群は、私たちが求めるセキュリティとガバナンス要件を満たす上で非常に大きな助けとなりました。 単にツールを導入するだけでなく、これらの機能を組み合わせ、自社の運用に合わせてカスタマイズすることで、n8nは真に「Enterprise-Ready」な基盤となり得ます。 私たちがここまでSecurityやガバナンスの強化にこだわった理由は、<strong>エンジニアの皆さんもエンジニア以外の皆さんも、誰もが安心して使える環境を作りたかったから</strong>です。<br /> エンジニア以外の皆さんは、<a href="https://docs.n8n.io/advanced-ai/ai-workflow-builder/">AI Workflow Builder</a>のようなChat-baseの機能を活用することで、Non-Programmingで容易にAI-Agentを作成し、業務を効率化でき、「プログラミングができないと自動化は難しい」というこれまでの考えは、AIの力によって覆されつつあります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/1b34a0d6-image8.png" alt="AI workflow Builder for n8n" /><br /> <em>Source: <a href="https://docs.n8n.io/advanced-ai/ai-workflow-builder/">n8n Docs &#8211; AI Workflow Builder</a></em></p> <p>一方でエンジニアの皆さんは、n8nを使用してプロトタイプを爆速で作成・検証し、高速なPDCAサイクルを回すことが可能になるでしょう。 更には人間が作成したWorkflowをAIが深く理解し、より堅牢でスケーラブルなアプリケーションとして自動で実装してくれる日がくるかもしれません。<br /> n8nはほぼ週次でマイナーリリースが行われるなど開発スピードが非常に速く、日々新しい機能が追加され、直近だと<a href="https://docs.n8n.io/2-0-breaking-changes/">v2.0へのメジャーアップデート</a>も控えています。 n8nの進化と、私たちが取り組んできたSecurityやガバナンスの強化が組み合わさることで、社内の「AI-Native」な活動を少しでも後押しできるような環境になれれば幸いです。</p> <p>明日の記事は mewutoさんによる、「n8nの静的解析CLIツールをOSS化 – JSON解析とDAGで実現するセキュリティチェックの自動化」についてです。n8nの承認フローの詳細が伺えるかと思います。引き続きお楽しみください。</p> <p><em>n8n.io logo source: <a href="https://n8n.io/brandguidelines/">https://n8n.io/brandguidelines/</a></em></p> OpenID Connect Core 1.0 の Claims パラメーターの利用https://engineering.mercari.com/blog/entry/20251211-c73c2b1747/https://engineering.mercari.com/blog/entry/20251211-c73c2b1747/<p>はじめに こんにちは。メルカリのバックエンドエンジニアの@kg0r0です。 この記事は、Mercari Advent Calendar 2025 の11日目の記事です。 本記事では OpenID Connect Core [&hellip;]</p> Thu, 11 Dec 2025 13:00:43 GMT<h2>はじめに</h2> <p>こんにちは。メルカリのバックエンドエンジニアの<a href="https://x.com/kg0r0">@kg0r0</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a> の11日目の記事です。<br /> 本記事では OpenID Connect Core 1.0 で定義されている Claims パラメーターについて説明し、最後にメルカリでの利用例について紹介します。<br /> 認証認可の領域を担当されているエンジニアの方であれば OpenID Connect Core 1.0 仕様に目を通したことがあるかもしれません。一方で、Claims パラメーターは比較的マイナーなパラメーターであり、利用を実際に検討したケースは多くないのではないかと想定しています。もしここで紹介する内容が OpenID Connect (OIDC) で属性情報を扱ううえでの何かしらの気づきになれば幸いです。</p> <h2>Claims パラメーターについて</h2> <p><a href="https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter">Claims パラメーター</a>は OpenID Connect Core 1.0 に定義されており、Authentication Request で指定可能なパラメーターの一つです。なお、<a href="https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#section-5.3">OpenID Connect for Identity Assurance 1.0</a> などの一部の拡張仕様内でも利用されています。<br /> Claims パラメーターは JSON オブジェクトとして表現され、以下の例のように <code>userinfo</code> や <code>id_token</code> といったメンバーによって UserInfo Endpoint や ID Token に含めて返却して欲しい属性情報を指定することができます。</p> <pre><code> { &quot;userinfo&quot;: { &quot;given_name&quot;: {&quot;essential&quot;: true}, &quot;nickname&quot;: null, &quot;email&quot;: {&quot;essential&quot;: true}, &quot;email_verified&quot;: {&quot;essential&quot;: true}, &quot;picture&quot;: null, &quot;http://example.info/claims/groups&quot;: null }, &quot;id_token&quot;: { &quot;auth_time&quot;: {&quot;essential&quot;: true}, &quot;acr&quot;: {&quot;values&quot;: [&quot;urn:mace:incommon:iap:silver&quot;] } } }</code></pre> <p>Claims パラメーターは OPTIONAL であり、通常、Relying Party が取得したい属性情報を指定する場合は <code>scope</code> パラメーターが使われます。一方で、<code>claims</code> パラメーターでは <code>scope</code> パラメーターでは指定することができない特定の組み合わせを要求することができます。また、<code>essential</code> や <code>value</code>、<code>values</code> などのメンバーによってより詳細な条件を追加することもできます。<br /> 例えば、<a href="https://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html#IndividualClaimsRequests">5.5.1. Individual Claims Requests</a> に記載されている以下の例では <code>essential</code> を true にすることで auth_time が必要であることを示すことができます。 </p> <pre><code> &quot;auth_time&quot;: {&quot;essential&quot;: true}</code></pre> <p>また、<code>value</code> を以下のように利用し sub として特定の値を指定することによって、 login_hint よりも強制力のある処理をしたいユースケースなどに利用することもできそうです。</p> <pre><code> &quot;sub&quot;: {&quot;value&quot;: &quot;248289761001&quot;}</code></pre> <p>一部の <a href="https://www.keycloak.org/docs/latest/server_admin/index.html#_mapping-acr-to-loa-realm">Identity Provider (IdP) 実装</a>では Authentication Context Class Reference (ACR) を指定する方法として <code>acr_values</code> だけでなく <code>claims</code> がサポートされており、Level of Assurance (LoA) に基づいた処理に利用することも検討できます。</p> <pre><code> &quot;acr&quot;: {&quot;essential&quot;: true, &quot;values&quot;: [&quot;urn:mace:incommon:iap:silver&quot;, &quot;urn:mace:incommon:iap:bronze&quot;]}</code></pre> <p>前述のように Claims パラメーターは、要求する属性情報に追加の条件を付与することを可能とし、Relying Party などが属性情報に基づいた特殊なユースケースを実現する際に利用できる可能性があります。<br /> 一方で、OpenID Connect Core 1.0 中では、Claims パラメーターの扱いに関する要件が複数記載されており、IdP 側の実装には少し複雑な箇所があります。<br /> 例えば、Claims パラメーターの定義において JSON オブジェクトの他に明示的に null を指定することができます。この場合、given_name はデフォルトの形式で要求されていることを示します。このため、IdP 側の実装によっては、Claims パラメーターをパースする際に明示的に null が指定されたのか値が存在しないため null になっているのか判断が必要な場合が考えられます。</p> <pre><code> &quot;given_name&quot;: null</code></pre> <p>また、各メンバーのデフォルトの動作を正しく適用する必要があり、例えば、<code>essential</code> は OPTIONAL であるため明示的に true または false が指定される以外にも、デフォルトの動作として false が指定された通りに Voluntary Claim として扱う必要があります。</p> <p>上記の通り、 Claims パラメーターは JSON オブジェクトで表現され、良くも悪くも自由度が高い構造になっています。また、OP 側の各メンバーの処理に関する要件が仕様中に複数記載されています。このため、実装が複雑になることを避けるためには、一度 Claims パラメーター以外のシンプルな方法がないか検討した方が良いかもしれません。</p> <h2>メルカリでの利用例</h2> <p>現在、メルカリでは <a href="https://www.rfc-editor.org/rfc/rfc9470.html">RFC 9470 OAuth 2.0 Step Up Authentication Challenge Protocol</a> で定義されているような LoA に基づく Step Up Authentication のメカニズムが導入されています。<br /> 例えば、Client が Protected Resource にリクエストする際に Protected Resource が要求する LoA を満たしていなければ、Client はその LoA を満たすことができるように acr_values を指定して Authorization Request をおこなうといった流れになります。<br /> ここで、Protected Resource が要求する ACR が mf (multi factor) だとします。このとき、Password のみで認証されていた場合は、MFA を実施するように誘導され、必要に応じて電話番号などの登録をおこないます。なお、Password + SMS などで認証済みであれば要求を満たすことができ、FIDO といった phishing resistant な MFA で認証済みであっても同様に要求が満たすことができます。<br /> しかし、もし Protected Resource が電話番号を必要とするサービスであった場合、FIDO によって認証されたユーザーは要求された LoA は満たしますが必要な属性情報は登録済みでない可能性があります。ここで、Claims パラメーターを利用して以下のように検証済みの電話番号が必要であることを伝えます。これにより、IdP に対して一度の Authorization Request の中で電話番号の登録が必要であることも伝達して誘導することができます。</p> <pre><code> &quot;phone_number_verified&quot;: {&quot;value&quot;: true}</code></pre> <h2>おわりに</h2> <p>本記事では OpenID Connect Core 1.0 で定義されている Claims パラメーターについて説明し、メルカリでの利用例についても紹介しました。Claims パラメーターは OpenID Connect Core 中に記載されている仕様ですが、あまり触れられていることが少ないと感じたためこの度記事にしてみました。実現したい処理をまずは標準仕様の中で行うことができないか検討することは、独自仕様の追加を避けて処理の透明性などを担保することに繋がると考えています。また、OpenID Connect Core など何度も読んだ仕様だとしても改めて読むと新しい気づきがあったりするのでオススメです。<br /> 明日の記事は @tokkuさんです。引き続きお楽しみください。</p> 理想の Workflow Platform という“聖杯”に、n8n でついに手が届くかもしれないhttps://engineering.mercari.com/blog/entry/20251211-cf3b67a5a7/https://engineering.mercari.com/blog/entry/20251211-cf3b67a5a7/<p>こんにちわ。AI Task Force の @ISSA です。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の11日目の記事です。 概要 ワークフローや業務自動化のツー [&hellip;]</p> Thu, 11 Dec 2025 10:00:51 GMT<p>こんにちわ。AI Task Force の @ISSA です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の11日目の記事です。</p> <h2>概要</h2> <p>ワークフローや業務自動化のツールは以前から数多く存在していますが、 Zapier や Make のようなノーコード・ローコードツール、Workato のようにエンタープライズ寄りの iPaaS、そして MuleSoft、Talend、Informatica といった本格的な統合基盤まで、選択肢は幅広く揃っています。<br /> しかし、ノーコード・ローコードツールは構築スピードが速い一方で複雑なロジックや AI 活用には限界があり、エンタープライズ向け ETL は強力である反面、開発・運用コストが大きくスピードを求める現場では扱いづらい側面があります。<br /> さらに AWS / GCP / Azure が提供する workflow engine は強力である一方で、業務部門とエンジニアが同じ環境で協働するにはハードルが高いという課題もあります。<br /> コーポレート IT ではノーコード・ローコードツールを活用して業務を最適化し、プロダクト開発ではフルコードで機能を磨き込む——そんな二つの世界を行き来する中で私は気づいたことがあります。<br /> それは、「スピード」「柔軟性」「協働しやすさ」を同時に満たす基盤なしに、モダンな業務自動化は成り立たないということです。<br /> こうした要件から、今回私たちは<a href="https://n8n.io/">n8n</a>を業務自動化基盤として導入しました。<br /> n8nとは、ノーコード・ローコードで多様なアプリやサービスを連携させ、業務プロセスを自動化できるオープンソースのワークフロー自動化ツールです。<br /> GUI とコードのバランス、イベント駆動とバッチ処理の両立、LLM との自然な統合など、現代の Developer Experience に求められる要素が揃っていたからです。本記事では、n8n を導入する上で Developer Experience がどのような観点で重要だったのか、その点を中心にお話しします。</p> <h2>背景</h2> <p>私はこれまで、常に “業務と開発のあいだ” に身を置いてきました。<br /> B2B SaaS のソフトウェアエンジニアとしてキャリアをスタートした後、事業会社の SE として業務システム導入とサービス開発の双方に携わりました。<br /> Salesforce を中心とする PaaS 上でのアプリ構築や ETL / iPaaS ツールの導入を経験し、ノーコード・ローコードツールによる業務システム開発の世界を本格的に知ったのもこの頃です。<br /> 前職 では、Salesforce Platform(PaaS)上での 数十万行規模の SaaS プロダクト開発 を担当しました。その中で Salesforce DX(現 Salesforce CLI)に出会い、手作業による設定移行(いわゆるチェンジセット中心の開発)から脱却し、ソース主導の開発・バージョン管理・CI/CD といった “あるべき開発者体験” を強烈に実感しました。PaaS でありながらフルコードのように扱える世界が存在する——そんな手応えを得た転機でした。<br /> 現在はメルカリの IT 部門で、業務プロセス、SaaS、プロダクト開発が交差する環境の中、さまざまなシステムの開発・導入を担当しています。ノーコードとフルコード、業務と開発の双方を理解する立場として、「組織としてどのような Developer Experience を設計すべきか」を考え続けています。こうした背景から、長年追い求めてきたワークフロー基盤の“聖杯”とも言える理想像に最も近い存在として、n8n にたどり着きました。なぜ n8n が“聖杯に近い”と感じたのか<br /> 多くのツールを触ってきましたが、それぞれ強みと弱みがあり、業務と開発の双方が求める要件を“ちょうどよく”満たすものにはなかなか出会えませんでした。<br /> n8n に触れたときに感じたのは、ノーコード・ローコードツールでありながら、ソフトウェア開発の当たり前をそのまま再現できるという稀有さです。これは単なる便利ツールではなく、業務オペレーションとプロダクト開発が協働できる “基盤” になり得るという感覚に近いものでした。ここからは、私が特に評価した n8n の Developer Experience の 3 つの特徴を紹介します。</p> <h3>1. 安全な開発とデプロイを支える環境分離</h3> <p>n8n の一つ目の特徴は、ノーコード・ローコードツールでありながら、開発(dev)と本番(prod)の環境分離を前提としている 点です。多くのツールでは、ワークフローの編集と本番実行が同じ環境で行われるため、「本番が壊れるのでは?」という不安が常につきまといます。n8n では、dev・stg・prod のように 明確に独立した環境を持てる ため、業務に影響を与えずに新しいワークフローを試し、改善することができます。環境ごとに設定や変数も独立しており、たとえば開発用の API キーやテストデータを安心して利用できます。このように、prod と dev を安全に分離して扱えること自体が、業務自動化基盤として非常に大きな価値 です。</p> <h3>2. 変更管理と協働を支える Source Control(GitOps)</h3> <p>環境分離が「どこで安全に実行するか」を決める仕組みだとすれば、<br /> Source Control は “何をどの状態で管理し、どう協働するか” を担う仕組みです。<br /> n8n の Source Control は単なる Git 連携ではなく、<br /> ワークフローを JSON として push/pull し、その時点の状態を正確に再現できる GitOps 基盤になっています。これにより、変更履歴の可視化、ロールバック、環境間の同期など、ソフトウェア開発で一般的なプロセスを低コード環境にも適用できます。PR レビュー自体は GitHub/GitLab 側で行いますが、ワークフローの更新を Git に集約することで、ノーコード・ローコードツールでは珍しいレベルの協働性と透明性を実現します。n8n が提供するこの GitOps 体験こそ、複数人で安全にワークフローを進化させるための大きな強みです。</p> <h3>3. Infra as Code を可能にする JSON ベースの構造</h3> <p>n8n のワークフローはすべて JSON で定義されています。これにより、CLI や API を使った自動デプロイ、CI/CD への統合が容易になり、ワークフローをコードと同じプロセスで管理できる 点が大きな特徴です。一般的なノーコードツールは UI 操作に依存するため、変更の再現性やテスト、自動化が難しいという課題があります。一方 n8n はワークフローそのものが構造化データとして扱えるため、<br /> IaC と同様にバージョン管理・レビュー・自動デプロイを実現できます。<br /> これにより、ワークフローは“便利なツール”の域を超え、組織全体の運用基盤として耐えうる堅牢さと拡張性を備えるようになります。</p> <h2>ノンエンジニアにとっての課題と、組織としての解決策</h2> <p>ここまで主に Developer Experience の観点から n8n を紹介してきましたが、一方で「ノーコード・ローコードだから誰でも簡単に使える」というわかりやすいストーリーだけでは語れない側面も存在します。特に、業務部門のメンバーを含む ノンエンジニアが n8nを扱う際の難易度は、正直に向き合うべきテーマだと考えています。</p> <h2>なぜノンエンジニアにとって難しいのか</h2> <p>n8n は柔軟な分、ワークフロー構築時に求められる選択肢が多く、設定項目も細かいのが特徴です。たとえば以下のようなポイントは、非エンジニアにとって大きなハードルになります。</p> <h3>HTTP ノードの設定</h3> <p>どのエンドポイントに、どの HTTP メソッドで、どの認証方式を使うのか。API の仕様理解そのものが前提となります。</p> <h3>認証情報・スコープ管理</h3> <p>API キーをどこに保持するか、どの権限が必要か、最小権限設計が適切かなど、IAM の基礎知識が求められます。</p> <h3>MCP(Model Context Protocol)の拡張</h3> <p>MCP に独自の機能を追加する際、どのように設定し、どの認証情報を与えるか。これはもはやアプリケーションアーキテクチャの理解が必要です。</p> <h3>コードノード(Python / JavaScript)の存在</h3> <p>柔軟性が高い一方で、自由度を活かそうとするとコーディング能力そのものが要求されます。<br /> このように、n8n の“できることの広さ”は魅力である一方、ノンエンジニアにとっては 情報リテラシーの要求水準が決して低くない、という現実があります。</p> <h2>メルカリのスケールで展開するために実施した3つのソリューション</h2> <p>こうした課題に対して、私たちは「個々のスキル差に依存しない、安全で再現性のある運用」を実現するために、3 つの仕組みを導入しました。</p> <h3>1.セキュリティガードレールの整備</h3> <p>詳細は別の記事で紹介されますが、n8n の柔軟性ゆえに発生し得るリスク(権限の混同、過剰な API 権限、危険な分岐など)を自動検出するため、静的解析・DAG 解析を組み合わせた独自ツールを構築しました。これにより、セキュリティレビュー工数を大幅に削減し、安全性を担保しています。</p> <h3>2.MCP による自然言語開発体験の導入</h3> <p>メルカリでは、MCP は「会社として認可されたもののみ使用可能」というルールがありますが、n8n においては一般公開されている OSS の MCPを利用可能にすることで、個々人が自然言語を使ってワークフロー構築をサポートできるようにしました。これにより、ノンエンジニアでも“どこから手を付ければよいか分からない”という状況を減らし、最初の一歩を踏み出しやすい環境が整備されています。</p> <h3>3.社内レビュー体制と Slackbot による補助</h3> <p>n8n ワークフローは、社内のレビュー体制によって品質・セキュリティが担保されています。さらに、Slackbot がレビューや申請のフローを補助することで、 レビュー依頼 → 自動チェック → 承認までをスムーズに進められるようになっています。<br /> この仕組みによって、ノンエンジニアが構築したワークフローでも安心して本番運用できる環境が整いました。</p> <h2>まとめ</h2> <p>n8n がもたらす価値は、ワークフロー自動化を“業務ツール”ではなく“技術基盤”として扱えるようにする点にあります。環境の分離、変更管理の明確さ、そしてコードと同等の扱いやすさ。これらが組み合わさることで、業務自動化が持続的に改善できるプロセスへと変わります。<br /> 一方で、導入には学習コストがかかったり、PR レビュー機能が組み込まれていないなど、改善の余地も存在します。それでも、ワークフローをエンジニアリングの文脈で扱えるツールは多くなく、n8n はその中でも際立った選択肢だと感じています。<br /> 今回の記事では Developer Experience に焦点を当てましたが、n8n には他にもセキュリティ、拡張性、運用性など、多くの語るべきポイントがあります。<br /> 今後、シリーズとしてさまざまな視点から n8n の実像を紹介していく予定です。</p> <p>明日の記事は Tさんによる、「Making n8n Enterprise-Ready: 企業向けn8nの導入と運用の取り組み」です。お楽しみください。</p> <p><em>n8n.io logo source: <a href="https://n8n.io/brandguidelines/">https://n8n.io/brandguidelines/</a></em></p> LiveContactToolにおける機微情報の取り扱い~Cloud DLPを使ったマスキングhttps://engineering.mercari.com/blog/entry/20251208-b7adaa9b98/https://engineering.mercari.com/blog/entry/20251208-b7adaa9b98/<p>こんにちは。メルカリでソフトウェアエンジニアをやっている@stersです。 この記事は、Mercari Advent Calendar 2025 の10日目の記事です。 LiveContactTool LiveConta [&hellip;]</p> Wed, 10 Dec 2025 11:00:32 GMT<p>こんにちは。メルカリでソフトウェアエンジニアをやっている<a href="https://twitter.com/sters9">@sters</a>です。</p> <p>この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a> の10日目の記事です。</p> <h1>LiveContactTool</h1> <p>LiveContactToolとは、カスタマーサービスのオペレーターがチャット応対に使用するシステムです。 “Effortless Customer Experience Project” というプロジェクトで、“世界中のメルカリのお客さまのお困りごとを 5 分で解決する” というゴールへ向かうために開発しているシステムです。</p> <p>先日行われた、Mercari Gears 2025 でも関連するセッションがありました。こちらもぜひご覧ください!</p> <p><a href="https://speakerdeck.com/mercari/mercari-gears-2025-transforming-customer-engagement-with-google-customer-engagement-suite">https://speakerdeck.com/mercari/mercari-gears-2025-transforming-customer-engagement-with-google-customer-engagement-suite</a> </p> <h1>機微情報</h1> <p>機微情報とは「個人情報のうち、特に取り扱いに注意すべき情報」として取り扱いが定められている情報です。「<a href="https://www.ppc.go.jp/personalinfo/legal/kinyubunya_GL/#a5">金融分野における個人情報保護に関するガイドライン</a>」では次のように書かれています。</p> <blockquote> <p>法第2条第3項に定める要配慮個人情報並びに労働組合への加盟、門地、本籍地、保健医療及び性生活(これらのうち要配慮個人情報に該当するものを除く。)に関する情報(本人、国の機関、地方公共団体、学術研究機関等、法第57条第1項各号に掲げる者若しくは施行規則第6条各号に掲げる者により公開されているもの、又は、本人を目視し、若しくは撮影することにより取得するその外形上明らかなものを除く。</p> </blockquote> <p>LiveContactTool以前のお問い合わせ応対の中でも、そういった情報が添付された場合には、該当の情報を削除できるような仕組み・フローが整備されています。ただしオペレーターによる手動での対応がメインのため、オペレーターの作業工数もかかっていました。</p> <h1>Cloud DLP</h1> <p>Cloud DLPとは、GCPのプロダクトのひとつで、Data Loss Prevention (DLP)を行うためのソリューションです。</p> <p>DLP とは 機密データを監視、保護をして、流出や悪用されることから守りましょう、というツール、プロセス、あるいは総称のソリューションです。保護の方法として、マスキング、難読化、匿名化をしてリスクの軽減を行うことができます。</p> <p>Cloud DLPはGCPの様々なプロダクトと連携して、自動的にDLPの処理を行うことができます。</p> <p>詳しくは公式のドキュメントを読んでいただくとよいでしょう。</p> <p><a href="https://cloud.google.com/security/products/dlp?hl=ja">https://cloud.google.com/security/products/dlp?hl=ja</a> </p> <p>LiveContactTool上での機微情報の取り扱いにおいて、このCloud DLPを含めて様々な方法を検討しました。</p> <p>前提として、手動での機微情報削除はオペレータの工数を取ってしまうため、リアルタイムなオペレーションが必要なチャット応対ではあまりやりたくありませんでした。また、手動での機微情報削除のためのリッチなシステムを構築する必要があることも採用しにくい理由でした。</p> <p>結果として、Cloud DLPを自動化された機微情報削除のシステムとして採用することにしました。この理由はいくつかあります。</p> <ul> <li>LiveContactTool上だけでなく、チャット応対として利用するCCaaSやConversational AgentsともCloud DLPと連携できる</li> <li>様々な情報がデフォルトでサポートされている <ul> <li>国ごとに固有の情報もサポートされている</li> <li>辞書や正規表現でカスタマイズ可能</li> </ul> </li> <li>“マイナンバー” が前後10文字以内にあること、のようなホットワードを設定できる</li> <li>画像ファイルのDLPを行うことができる</li> <li>APIでの即時実行と、ジョブによる非同期実行がサポートされる <ul> <li>ジョブトリガーを設定して永続的に実行しつづけることも可能</li> </ul> </li> <li>マネージドサービスのため、コンピューティングに関する管理が不要</li> <li>PoCしたところ、マスキングの精度が期待を満たしていた</li> </ul> <h1>LiveContactToolでCloud DLPをどのように使うか</h1> <p>簡略化したものですが、Cloud DLPまわりの全体像がこちらです。</p> <p><a href="https://storage.googleapis.com/prd-engineering-asset/2025/12/54125b1c-image.png"><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/54125b1c-image-1024x698.png" alt="" /></a></p> <p>現状では、チャットが終わり次第DLPを実施するようにしています。</p> <h2>テキストのマスキング</h2> <p>テキストデータを送るとDLP処理をしたテキストデータを返すAPIがあります。これをつかってチャットメッセージをマスキングしています。</p> <p><a href="https://docs.cloud.google.com/sensitive-data-protection/docs/reference/rest/v2/projects.locations.content/deidentify">https://docs.cloud.google.com/sensitive-data-protection/docs/reference/rest/v2/projects.locations.content/deidentify</a> </p> <p>1つのチャットメッセージは1つのSpanner上のレコードで管理しています。1つのセッションで複数のチャットメッセージが入ります。これをそのままCloud DLPのAPIで1つメッセージずつ処理しようとした場合、Cloud DLPのAPI呼び出しクオータの制限に引っかかってしまいます。</p> <p>そこでチャットメッセージを結合し、一定の長さの文字列にまとめてCloud DLPのAPIを呼び出すようにしています。実際にはctxや、API呼び出しのエラーハンドリング等諸々がありますが、やっていることはこんな感じです。</p> <pre><code class="language-go">type deidentifyTarget struct { index int content string } func deidentifyMessages(messages []string) []string { result := make([]string, len(messages)) copy(result, messages) separator := generateSeparator() var batches [][]deidentifyTarget var currentBatch []deidentifyTarget var currentSize int // バッチ処理のために文字列長を計算してまとめる for i, content := range messages { size := len(content) if len(currentBatch) &gt; 0 { size += len(separator) } if currentSize+size &gt; maxBatchSizeBytes &amp;&amp; len(currentBatch) &gt; 0 { batches = append(batches, currentBatch) currentBatch = nil currentSize = 0 } currentBatch = append(currentBatch, deidentifyTarget{index: i, content: content}) currentSize += size } if len(currentBatch) &gt; 0 { batches = append(batches, currentBatch) } // 各バッチについてCloudDLPのAPIを呼び出す for _, batch := range batches { var parts []string for _, t := range batch { parts = append(parts, t.content) } combined := strings.Join(parts, separator) apiResult := callCloudDLP(combined) deidentified := strings.Split(apiResult, separator) for j, t := range batch { result[t.index] = deidentified[j] } } return result }</code></pre> <h2>画像のマスキング</h2> <p>画像のバイナリを送るとDLP処理をしたバイナリを返すAPIがあります。</p> <p><a href="https://docs.cloud.google.com/sensitive-data-protection/docs/reference/rest/v2/projects.locations.image/redact">https://docs.cloud.google.com/sensitive-data-protection/docs/reference/rest/v2/projects.locations.image/redact</a> </p> <p>しかし、アプリケーションのメモリやネットワーク帯域を使うことになります。またCloud DLP側のファイルサイズの制限もあり、4MBとなっています(<a href="https://docs.cloud.google.com/sensitive-data-protection/limits">参考</a>)。この都合のためにジョブを使うようにしています。実装としては、アプリケーションはCloud DLPのジョブを作成するAPIを呼び出しています。</p> <p><a href="https://docs.cloud.google.com/sensitive-data-protection/docs/reference/rest/v2/projects.locations.dlpJobs/create">https://docs.cloud.google.com/sensitive-data-protection/docs/reference/rest/v2/projects.locations.dlpJobs/create</a> </p> <p>ジョブを使うことで非同期にCloud DLP内で処理を行います。ファイルサイズや画像の量にも依存すると思いますが、今のところ、数秒〜30秒くらいで処理が終わっているようで、そこまで大きな遅延や問題は起きていません。</p> <p>Cloud DLP終了時のイベントをアプリケーション側で受け取れるので、それを利用してSpanner上のデータを更新しています。</p> <h2>Cloud DLPの設定管理</h2> <p>DLP処理を実施するAPIでは、どのように検査やマスキングを行うかを直接指定することができます。しかし、これでは設定がアプリケーションに閉じてしまい、他のアプリケーションやシステム全体から利用することが困難になります。</p> <p>そこでTerraformを使い、CustomInfoType、InspectTemplate、DeidentifyTemplateを定義するようにしました。このようなterraformを書いてリソースを作成しています。</p> <pre><code class="language-tf">resource &quot;google_data_loss_prevention_stored_info_type&quot; &quot;custom_regexp_infotype&quot; { parent = &quot;projects/gcp-project-id-production/locations/us&quot; stored_info_type_id = &quot;CUSTOM_REGEXP_INFOTYPE&quot; display_name = &quot;CUSTOM_REGEXP_INFOTYPE&quot; description = &quot;Custom InfoType using regexp&quot; regex { pattern = &quot;(?:${join(&quot;|&quot;, local.dlp_phrases.phrases)})&quot; } } resource &quot;google_data_loss_prevention_inspect_template&quot; &quot;dlp_inspect_template_for_text_content&quot; { parent = &quot;projects/gcp-project-id-production/locations/us&quot; template_id = &quot;inspect_template_for_text&quot; display_name = &quot;Inspect template for text&quot; description = &quot;DLP Inspect Template for text content with built-in and custom InfoTypes&quot; depends_on = [ google_data_loss_prevention_stored_info_type.custom_regexp_infotype, ] inspect_config { min_likelihood = &quot;UNLIKELY&quot; info_types { name = &quot;LOCATION&quot; } info_types { name = &quot;CUSTOM_REGXP_INFOTYPE&quot; } } } resource &quot;google_data_loss_prevention_deidentify_template&quot; &quot;replace_with_detected_infotype_for_text&quot; { parent = &quot;projects/gcp-project-id-production/locations/us&quot; template_id = &quot;replace_with_detected_infotype_for_text&quot; display_name = &quot;Mercari Contact Replace with detected InfoType for text&quot; description = &quot;Deidentify template that mask data with detected InfoType for text&quot; deidentify_config { info_type_transformations { transformations { primitive_transformation { replace_with_info_type_config = true } } } } }</code></pre> <p>terraformで作ったリソースを、APIの呼び出し時のパラメータに指定することができます。これで、terraformで管理するDLP設定を使った、検査・マスキングを行います。</p> <pre><code class="language-go">func (c *client) DeidentifyStringWithTemplate( ctx context.Context, input string, inspectTemplateName string, deidentifyTemplateName string, ) (string, error) { req := &amp;dlppb.DeidentifyContentRequest{ Parent: c.parentResource, InspectTemplateName: inspectTemplateName, DeidentifyTemplateName: deidentifyTemplateName, Item: &amp;dlppb.ContentItem{ DataItem: &amp;dlppb.ContentItem_Value{ Value: input, }, }, } res, err := c.dlpClient.DeidentifyContent(traceCtx, req) if err != nil { return &quot;&quot;, err } return res.GetItem().GetValue(), nil }</code></pre> <h1>精度をあげるために</h1> <p>組み込みのInfoTypeではうまく判定されないものがあったり、正規表現や辞書での定義をすり抜けたり、あるいは間違ってマスキングされたり、といったことが起こります。そういったものをチャット応対のオペレーション中に見つけた場合、即時に対応する方針はありますが、それだけでは不十分です。</p> <p>そこで定期的に内容をサンプリングし、おかしなものが無いかをチェックすることにしました。</p> <ul> <li>ランダムにチャットメッセージを抽出し、マスキング漏れがないかを確認</li> <li>ランダムにマスキングされたチャットメッセージを抽出し、過剰なマスキングがないかを確認</li> </ul> <p>先日のサンプリング調査では、配送番号がクレジットカードやマイナンバーとして判定されてしまい、マスキングされるケースが見つかりました。配送番号がわからなくなってしまうとオペレーションに不都合が出てしまうため、このマスキングはされないように調整することが必要です。これについては、Cloud DLPのテンプレート、InfoTypeの辞書や正規表現、ホットワードを見直すことで改善をしました。</p> <p>また、検討レベルの段階ですが、システム面でモニタリングの実現可能性も探っています。Cloud DLPでは様々なメトリクスを取ることができ、何をどのくらいの一致の可能性で検出したのか、結局マスキングをしたのかしてないのか、といった情報がわかります。これらを使ったとしても、マスキング漏れや過剰なマスキングがなされていないか?を正確に知ることは難しいと思いますが、全体の傾向はわかるはずです。機微情報のモニタリングをするという点で、有用な情報になりそうだと思っています。</p> <h1>おわりに</h1> <p>LiveContactToolではCloud DLPを使って機微情報をマスキングしています、という説明をしました。</p> <p>Cloud DLPを使うことは初めてだったのですが、技術的な設計・実装以上に「機微情報をシステムとしてどう定義し、運用していくか」に難しさを感じました。これにはエンジニアメンバーだけではなく、関連するチームも巻き込んで、モニタリングしていく体制もセットで整備が必要でしょう。</p> <p>このように書くと大変そうではありますが、Cloud DLPはAPIやデータ構造の仕組みが使いやすく、様々なGCPプロダクトと連携できるのが魅力的です。フルマネージドサービスのため、細かいメンテナンスが不要なのも大きなメリットだと思います。このようにシステム面から、機微情報の取り扱いを簡単にできるようにしてくれるプロダクトだと思います。機会があればぜひ使ってみてください。</p> <p>そして明日12/11の記事は @kgoro です。引き続きお楽しみください。</p> AI Task Forceで学んだ「不確実性との向き合い方」https://engineering.mercari.com/blog/entry/20251210-ai-tf-facing-uncertainty/https://engineering.mercari.com/blog/entry/20251210-ai-tf-facing-uncertainty/<p>はじめに こんにちは。@panorama です。 この記事は Merpay &amp; Mercoin Advent Calendar 2025 の 10 日目の記事です。 私は現在、7月に新設されたAI Task Fo [&hellip;]</p> Wed, 10 Dec 2025 10:00:48 GMT<h1>はじめに</h1> <p>こんにちは。<a href="https://x.com/panorama32_" title="@panorama">@panorama</a> です。<br /> この記事は <a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/" title="Merpay &amp; Mercoin Advent Calendar 2025">Merpay &amp; Mercoin Advent Calendar 2025</a> の 10 日目の記事です。</p> <p>私は現在、7月に新設されたAI Task ForceというチームでEnablerを務めています。<br /> AI Task Forceは、メルカリをAI Nativeな組織へと変革するために立ち上がった100名規模のチームで、Enablerは「各領域でAI Nativeな業務変革を主導する役割」とされています。<br /> 私の担当領域はFT Engineering(メルペイ・メルコインのエンジニアリング組織)です。</p> <p>AI Task Forceについては以下の記事が詳しいです。</p> <ul> <li><a href="https://careers.mercari.com/mercan/articles/53708/">メルカリが本気で始めた「AI-Native」化。100名規模のタスクフォースが立ち上がるまで</a></li> <li><a href="https://careers.mercari.com/mercan/articles/56200/">「AI Task Force」で変化を加速する。CTO @kimurasが描くメルカリの成長戦略</a></li> </ul> <p>また、<a href="https://engineering.mercari.com/blog/entry/20251201-pj-double-towards-ai-native-development/">2025/12/02のAdvent Calendar</a>で@nnaakkaaiiさんから発表されたpj-doubleというプロジェクトでは、QA領域のTech Leadを担当しています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/6e5dae32-asdd-1024x571-1.png" alt="overview-of-asdd-20251210" /><br /> ▲上記図の「Double QA」のTech Lead</p> <p>AIを前提とした、仕様解析、テスト観点やテストケースの設計、自動テスト、そしてQA全体のワークフロー再設計に取り組みながら、「人間が何を行い、どのような業務体験を目指し、品質をどう担保するか」を日々議論しています。そして複数のパイロットチームとともに手法やツールの改善ループを回しています。</p> <p>最初は「AI Task Forceとpj-doubleの話」(※1) や「なぜQA領域をやることになったか」、「どのような手法を試しているのか」を記事にしようと思っていました。しかしここ数ヶ月を振り返って、一番私に取って重大だったのは<strong>マインドセットの変化</strong>だと気付きました。<br /> そこで本記事では、AI Task Forceとpj-doubleで組織と業務のAI Native化を進める中で得られた、「不確実なものへの向き合い方」にフォーカスして書いていきます。</p> <p>※1 簡潔に補足しておくとAI Task Forceとpj-doubleは現在は統合状態に近く、双方の文脈で自然に登場します。実は紆余曲折あってそのようになっているのですが、今回は本題ではないので割愛します。技術書典19にメルカリとして出版した「<a href="https://techbookfest.org/product/cXmRrrRCAVn3tTYfN7QPFJ?productVariantID=aFqdCda8MRkL5M0mbibGW5">Unleash Mercari Tech! vol.7</a>」の第6章に「AI Task Forceに異動してから何をしてきたのか、pj-doubleがどのように拡大してAI Task Forceと統合状態になったか」などを書いていますので、もし気になる方はお手に取っていただけると。</p> <h1>不確実なものへの向き合い方</h1> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/d4f0ea05--1024x1024.png" alt="不確実なものに向き合う" /></p> <p>AI Task Forceで最初に直面したのは、答えのない領域でどう判断し、どう動くかという問いでした。</p> <p>AI時代における組織や業務のあり方も、AI技術の未来も、誰も明確な答えを持っていません。<br /> そのため「失敗したらどうしよう」「確証がないままこれだけの人を巻き込んで進めて良いのか」と不安を抱く日々が続きました。</p> <p>しかし私は次第に<br /> ​</p> <ul> <li><strong>不確実な中でも挑戦する意義</strong></li> <li><strong>不確実な中で物事を決める、進める価値</strong></li> </ul> <p>という2つの考え方に行き着き、迷いを生じずに動けるようになりました。</p> <h2>不確実な中でも挑戦する意義</h2> <p>私は当初、AI Task Forceの目指す壮大な目標と答えのない挑戦にどう向き合えば良いかわからずいました。<br /> 一時は「社内固有の最適化だけ自分たちで行う。しかし一般的な大部分はソリューションプロバイダに任せてしまう方が、自分たちで挑戦するより合理的ではないか」という消極的な考え方になりました。</p> <p>しかしその考え方はAI Task Forceの挑戦を根本的に否定するものでした。</p> <p>たとえば、pj-doubleの取り組みではSDD (spec-driven development)に近い方法論が含まれており、こうした手法を検証することも活動の一部でした。<br /> 一方で、KiroのようにSDDの実現を支援するための外部ソリューションも存在しており、そうしたツールの進化に期待すれば、自分たちで手法を磨いたり検証したりする挑戦は不要なのでは、と感じる瞬間もありました。</p> <p>しかし最終的には</p> <p><strong>「誰も正解がわからない。でも、手探りで失敗を繰り返したとしても進める価値がある」</strong></p> <p>のがAI Native化だと考え直しました。</p> <p>自分たちで解を見つけられるかもしれないし、他社の成功例を参考にすることになるかもしれない。結局ソリューションプロバイダのサービスを採用することになるかもしれない。<br /> AI Task Forceでは最初に自分たちが真にAI Nativeな組織に辿り着くことを目指していますが、たとえそうでなかったとしても、築き上げた下地や知見があればすぐに最適解に追従することができます。<br /> 答えが出るまで待っていてはいつまで経っても変化が起きませんし、答えが出た頃にはまた新しい問いが生まれます。だから取り残されないために、挑戦し続けることが大切だと気付いたのです。</p> <p>実はこの考え方は、メルカリが10年以上かけて育ててきたカルチャー、Valueの一つ”<strong>Go Bold</strong>”にも含まれていました。</p> <p>Go Bold<br /> 大胆にやろう</p> <ul> <li>大きな成功のためには、思考のリミッターを外して、試行回数を増やすことが必要です。誰かと同じことをすれば普通の成果しか得られません。失敗や変化を恐れず、普通ではない大胆なチャレンジをし続けます。</li> <li>失敗自体を責めることはしません。ナイストライを賞賛します。</li> <li>ミッション達成のために一人一人がありたい姿を描き、周囲に示すことを重要視します。</li> <li>成功・失敗に関わらず振り返りを言語化して共有することで、組織全体で素早く学び、次のチャレンジの糧にします。</li> </ul> <p>私はこのバリューのことはとっくに理解したつもりになっていました。<br /> ですが今回の経験でバリューを再解釈し、メルカリはずっとこのスタンスだったのだと気付いたとき、とても背中を押されたような気持ちになりました。</p> <h2>不確実な中で物事を決める・進める価値</h2> <p>AI Task Forceは活動を開始して間もないチームです。<br /> そのため<br /> ​</p> <ul> <li>何を対象にAI Native化を進めるか</li> <li>誰を巻き込み、どの規模でやるか</li> <li>どの手法を採用するか</li> </ul> <p>など、無数に決めることがあります。</p> <p>私はAI Task Forceへ異動する前、メルカードを作るバックエンドチームに所属していました。<br /> そのチームではすでにある程度業務フローが確立しており、決定に関してはTech LeadやPM、 EM、 Directorと議論して決めていました。<br /> そのためAI Task Forceに来てしばらくはどんな選択肢に対しても「これを私が決めて良いのだろうか」「とりあえず自分の意見をまとめて”偉い人”(※2)に持っていこう」みたいな考え方をしていました。</p> <p>※2 メルカリではTech LeadやEMも1つのRoleとしてフラットに考えるカルチャーがあるため、”偉い”という概念は本来ありません。この”偉い”という言葉は私が当時持っていた考え方を言語化しただけです。</p> <p>しかしAI Task Forceで物事を決めようと思ったとき、<strong>それを決める人は自分</strong>でした。<br /> というより本当は以前からずっとそうだったのです。<br /> 「権限がないから決められない」と思い込み、判断を他者に委ねていただけでした。<br /> そこで初めて<strong>「決める」こと自体に大きな価値がある</strong>ことに気付きました。<br /> 特にそれが誰にもわからないこと、決められないこと、自分で決めたくないことであればあるほどです。</p> <p>AI Task Forceが向き合う不確実性もまさにそうです。<br /> 答えがないからこそ、決断して実行に移せることに大きな価値があります。<br /> もちろんその決断には自分なりの根拠があり、関係者から納得が得られる必要があります。<br /> ですがそういった細かいところは差し置いて、<strong>「決める」「進める」ことが不確実な領域に向き合う上でとても大切</strong>だとわかりました。<br /> (言葉の上でも”不確実”と言っているのだから、論理的に考えても当然ですよね。ですがそれをきちんと仕事上で実感したのはAI Task Forceに入ってからでした。)<br /> また可能であればその専門性をもって「決める」前に「主張する」ができるとなお良いことにも気付きました。<br /> 決めることも重要ですが、決めるためには自分なりの意見が必要で、「主張する」「主張を持つ」という前段階があることも重要だと感じました。</p> <p>たとえばpj-doubleのBackend QAでは、Scenarigo(※3)というテストツールを使うべきかという議論がありました。<br /> 使わない理由としては、Scenarigoがyamlベースで独自文法を含むため、AIがその構造と意味を十分に理解できず、AIの効果を最大限発揮できない懸念があったことです。<br /> 一方で使う理由としては、社内に既存の利用実績と知見があることと、仮に将来別ツールへ移行する場合でも、Backend QA全体フローから見れば置き換えの影響は限定的だと見積もれた点がありました。</p> <p>これらを踏まえ、最終的には Scenarigo を採用することに決めました。<br /> どちらが最適かは現時点でも確定していません。<br /> しかし、自分なりの主張を持ち、決断し、言語化したことで、停滞していた状況が一気に動き始めたのを感じました。</p> <p>これが「決める」「進める」ことが大切だと感じた場面の一例です。</p> <p>※3 Go製のテストツール。<a href="https://github.com/scenarigo/scenarigo">https://github.com/scenarigo/scenarigo</a></p> <h2>AI Nativeな未来での人間の価値</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/2f15fe0b--1024x683.png" alt="人間が決める" /></p> <p>先ほどの「決める力」や「進める力」ですが、おそらくAIに関わらずTech LeadやEMといった方向性を示す必要がある役割を担う方に取っては、従来から求められてきた力だったと思います。<br /> しかし私は最近、これらは単なる役職上のスキルを超えて、<strong>AI Nativeな未来において人間が持つ本質的な価値になる</strong>のではないかと感じています。<br /> というのも、先ほどの私の例を思い出すと、「いくつもの選択肢を検討し、それぞれのパターンで起こることを予測し、意見を言う (ただし決めない)」というのはもはやAIができていることだからです。<br /> 一方で責任と自信をもってして「決める」というのは以前の私と同じく、AIにもまだできていないことです。<br /> 今年の春頃「AIは意思や欲望を持たない。だからそれを持つことが人間の価値だ。」という言葉をよく耳にしましたが、今ならその意味がよくわかります。</p> <h1>終わりに</h1> <p>私は上記のようなマインドセットの変遷を経て、不確実への不安(「失敗したらどうしよう。確証がないまま進めて良いのか」という気持ち)を払拭し、この挑戦をすること自体に大きな価値を見出せるようになりました。<br /> またAIというもの自体が不確実性をはらんでおり、時代も含めて不確実が大きいからこそ、決める行為そのものが価値になるということに気が付きました。<br /> 決める力こそが人間に残る最後のクリエイティビティなのかもしれません。</p> <p>この記事では、ここ数ヶ月で私の中で起こった考え方の変化を赤裸々に説明してみました。<br /> この気付きが、誰かの背中を押せていたら嬉しいと思います。</p> <p>明日の記事はISSAさんです。引き続きお楽しみください。</p> Search Results Quality Monitoring with LLMshttps://engineering.mercari.com/blog/entry/20251208-search-results-quality-monitoring-with-llms/https://engineering.mercari.com/blog/entry/20251208-search-results-quality-monitoring-with-llms/<p>こんにちは。メルカリの検索領域で Software Engineer をしている @otter です。 この記事は、Mercari Advent Calendar 2025 の9日目の記事です。 メルカリの商品検索とその [&hellip;]</p> Tue, 09 Dec 2025 11:00:03 GMT<p>こんにちは。メルカリの検索領域で Software Engineer をしている <a href="https://x.com/omohayui">@otter</a> です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a> の9日目の記事です。</p> <h2>メルカリの商品検索とその品質管理</h2> <p>メルカリの商品検索は、膨大な商品のなかからお客さまの意図を的確に汲み取り、本当に探している商品を検索結果に表示することが重要です。そのため、検索キーワードと検索結果との関連性や妥当性を日々チェックし、品質を維持・向上させることは不可欠と言えます。</p> <p>この記事では、検索結果の品質チェックフローをどのようにLLM(大規模言語モデル)を活用して改善してきたかをご紹介します。</p> <h2>検索結果の品質レビューにおける課題と要件</h2> <p>これまで、プロダクトマネージャーやエンジニアがサンプリングした検索キーワードごとに、検索結果アイテムを一つ一つ目視で確認し、無関係なアイテムの表示率を計算してきました。ただ、この作業は非常に時間がかかる上、複数人で行うと評価基準にばらつきが生じ、評価結果が安定しないという課題がありました。</p> <p>こうした課題を受け、品質レビューには、日次や週次で自動化されダッシュボードで監視できることに加えて、十分なレビュー数を安定して確保できること、明確な評価基準があること、そして検索者のコンテキストや意図を正確に汲み取れることが求められるようになりました。</p> <h2>LLMと評価基準による客観的かつ安定したモニタリングの実現</h2> <p>上記の要件を満たすため、私たちはLLMを用いた検索結果品質レビューに取り組みました。</p> <p>いくつかモデルを比較した結果、Gemini 2.5 Proがユーザーの意図を最も正確に把握できていたため、採用しています。</p> <p>当初は、検索者の目線に近い形で検索結果画面のスクリーンショットのみをLLMに入力して評価を行っていました。しかし、この方法では複雑な商品情報まで踏み込んだ判断が難しく、例えば商品の仕様やカテゴリの違いによる誤判定が生じるなど、十分な精度が得られないケースがありました。そこで、評価精度を高めるために、各商品の詳細な情報、商品名、商品種別、価格、カテゴリ、サムネイル画像などもあわせてLLMへ入力するよう改良しました。</p> <h3>評価基準</h3> <p>LLMには各商品について「Relevance Score (0.0–1.0)」と、その理由を返答するように指示しています。スコアはAmazonの <a href="https://github.com/amazon-science/esci-data">ESCI</a> relevance judgements (Exact, Substitute, Complement, Irrelevant) に基づく関連性判定を用い、分類ごとにスコアを設定しています。<br />  </p> <ul> <li><strong>Exact (1.0):</strong> 指定クエリと完全に一致する商品(例:「iPhone 14 Pro Max 256GB」→完全に同じモデルと仕様)</li> <li><strong>Substitute (0.75):</strong> 機能的に代替可能な商品(例:「iPhone 14」→iPhone 13など、世代違いだが似た仕様)</li> <li><strong>Complement (0.5):</strong> 補完的な商品やアクセサリー(例:「iPhone」→iPhoneケース、充電器)</li> <li><strong>Irrelevant (0.0):</strong> 全く無関係、または条件を満たさない商品(例:「望遠鏡」→靴下)</li> </ul> <p>従来の目視評価では判断基準が属人的になりやすく、評価結果にばらつきが生じがちでしたが、このような明確なスコア定義とLLMを組み合わせることで、評価結果の安定性や客観性が大きく向上しました。</p> <h2>品質モニタリングツールの仕組み</h2> <p>検索チームにとってSearch Relevancyの品質チェックには、現在大きく2つのユースケースがあります。</p> <h3>Online Monitoring</h3> <p>本番環境の検索クエリログからランダムに抽出したキーワードで検索結果の関連性を評価します。週に1回、約1,000件の検索キーワードについて、それぞれの検索結果上位120件の商品が対象です。</p> <p>レビュー結果はBigQueryテーブルに出力され、モニタリングダッシュボード等から継続的に確認できます。また、検索品質改善のためのA/Bテストや新機能リリース時に、Average Relevance ScoreやIrrelevant Items Rateへの影響を監視できます。 </p> <h3>Offline Evaluation</h3> <p>新機能をA/Bテストする前のオフライン評価や、改善検証などに使われています。検証したいキーワードをエンジニアやプロダクトマネージャーが入力することで、その検索結果・カテゴリ・ブランド・価格帯の分布、さらにLLMによる関連性評価を即時にツール上で確認できます。また、あらかじめ用意したキーワードセットによる大量一括レビューも可能です。</p> <p>これらの2ユースケースは異なるシステム上で稼働していますが、LLMのプロンプトを共通化することで、評価基準と結果の一貫性を保っています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/7d63f813-serp_monitor_diagram-scaled.jpg" alt="SERP Monitor" /></p> <h2>今後の拡張の可能性</h2> <p>画像データとテキストデータを組み合わせることで評価精度が向上しましたが、まだ人の目による判断が必要な難しいケースも残っています。とはいえ、モデルの精度は年々大きく向上しており、今後さらに自動化できると期待しています。</p> <p>また、評価・監視用途だけでなく、LLMによる評価データそのものを学習用データとして活用し、検索機能のモデル精度を高めていく、といった応用も視野に入れています。</p> <h2>まとめ</h2> <p>メルカリの検索品質向上のために、これまで人手のみで行ってきた検索結果の関連性評価を、LLMを活用して自動化・安定化する取り組みを紹介しました。<br /> LLMの導入によって、レビュー作業の効率化だけでなく、より客観的な評価軸をもとに継続的な品質モニタリングが実現できました。<br /> 今後は、評価データを活用したさらなる検索機能の改善や、より難易度の高いケースへの対応にもチャレンジしていく予定です。</p> <p>検索や推薦システムの品質評価に悩んでいる方、またLLMの活用に興味がある方の参考になれば幸いです。</p> <p>明日の記事は @task さんです。引き続きお楽しみください。</p> Agent Specで小さく素早く回すメルカリモバイル開発現場https://engineering.mercari.com/blog/entry/20251209-d0de07214d/https://engineering.mercari.com/blog/entry/20251209-d0de07214d/<p>こんにちは。メルカリモバイル Tech Leadの@_seitauです。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の9日目の記事です。 先日公開された記事『pj-d [&hellip;]</p> Tue, 09 Dec 2025 10:00:34 GMT<p>こんにちは。メルカリモバイル Tech Leadの<a href="https://x.com/_seitau">@_seitau</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の9日目の記事です。</p> <p>先日公開された記事『<a href="https://engineering.mercari.com/blog/entry/20251201-pj-double-towards-ai-native-development/">pj-double: メルカリの開発生産性向上に向けた挑戦</a>』では、メルカリグループ全体として取り組んでいる ASDD (Agent Spec Driven Development) という新たな開発手法の概念や、AIネイティブな開発への展望について解説されました。<br /> この記事では、その実践編として、より現場の実務に焦点を当てます。 2025年3月にリリースしてからさまざまな機能追加をしてきたメルカリモバイルの開発現場において、私がどのように ASDD を運用・実践してきたか、実際のプロジェクト事例を交えて紹介します。</p> <h2>実践フロー:PRDから実装まで</h2> <p>Coding Agentの登場により、開発者は複数のタスクを並列で進めることが可能になりました。このメリットを最大化するための具体的なワークフローについて、最近リリースした メルカリモバイルの<a href="https://jp-news.mercari.com/articles/2025/12/05/mobile_multiplelines/">複数回線対応プロジェクト</a>を例に解説します。図示しているタスク名は仮のものを使用しています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/52b2ae9d-mermaid-diagram-2025-12-04-212223-1024x862.png" alt="" /></p> <h3>Step 1: PRDをPdMが作成し、リポジトリにコミットする</h3> <p>まず、情報の管理方法から見直します。 PdM(プロダクトマネージャー)は、PRD(プロダクト要件定義書)をドキュメントツールではなく、Markdown形式でGitHubリポジトリに直接コミットします。<br /> リポジトリ内に仕様が存在することで、後続のCoding Agent(Claude CodeやCursorなど)にコンテキストとして読み込ませやすくなり、エンジニアが素早く開発に着手しやすくなります。</p> <h3>Step 2: Coding Agentを活用してAgent Specを生成する</h3> <p>次に、エンジニアはCoding Agentを使用して Agent Spec(AIへの実装指示書) を生成します。<br /> この際、単にPRDを読ませるだけでなく、Slackでの議論ログやNotion上のメモなど、散らばっている関連コンテキストにも接続して読み込ませます。これにより、人間がゼロから仕様を書き起こすことなく、文脈を考慮した精度の高いSpecドラフトをAgenticに生成することができます(この仕組みの詳細は<a href="https://engineering.mercari.com/blog/entry/20251201-pj-double-towards-ai-native-development/">先述の記事</a>を参照)。</p> <h3>Step 3: タスクリストを作成し、依存関係を可視化する</h3> <p>生成されたSpecを元に、具体的な実装タスクを洗い出し、「どのタスクが独立しており、どのタスクが依存関係にあるか」 を整理します。 私は実装に着手する前に、以下のようなタスクリストを作成し、依存関係を可視化しています。</p> <table> <thead> <tr> <th>タスクID</th> <th>機能項目</th> <th>タスク名</th> <th>説明</th> <th>規模</th> <th>依存関係</th> </tr> </thead> <tbody> <tr> <td>T001</td> <td>申し込み</td> <td>回線申し込み情報取得API修正</td> <td>フィールド追加</td> <td>S</td> <td>&#8211;</td> </tr> <tr> <td>T002</td> <td>申し込み</td> <td>回線申し込みAPI修正</td> <td>バリデーション追加</td> <td>S</td> <td>T001</td> </tr> <tr> <td>T003</td> <td>申し込み</td> <td>回線数制限バリデーション</td> <td>上限追加</td> <td>M</td> <td>T001, T002</td> </tr> <tr> <td>T006</td> <td>Top画面</td> <td>回線一覧API実装</td> <td>回線一覧取得</td> <td>M</td> <td>&#8211;</td> </tr> <tr> <td>T009</td> <td>Default回線</td> <td>インデックス追加</td> <td>インデックス追加</td> <td>S</td> <td>&#8211;</td> </tr> <tr> <td>T008</td> <td>Default回線</td> <td>回線切り替えロジック実装</td> <td>申込・開通</td> <td>L</td> <td>T009</td> </tr> </tbody> </table> <p>この表において最も重要なのが 依存関係 の項目です。これを図解すると、以下のように並列実行可能なラインが見えてきます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/bff2809a-mermaid-diagram-2025-12-04-212846-1024x992.png" alt="" /></p> <p>これにより、「Stream A, B, Cは依存関係がないため、3つのAgentを同時に稼働させて並列実装が可能である」といった判断を的確に行えるようになります。</p> <h3>Step 4: チームレビューとSpecの磨き込み</h3> <p>整理されたタスクとAgent Specは、実装を開始する前にチームメンバーによるレビューを受けます。ここで仕様の抜け漏れや認識のズレを解消しておきます。</p> <h2>現場で機能させる原則</h2> <p>このフローを運用する上で、私が重視している原則が3点あります。</p> <h3>粒度を小さく保つ</h3> <p>1タスク = 1PR を基本とします。 AIに一度に大量の変更を指示すると、意図しない挙動やバグが発生するリスクが高まります。「バリデーション追加のみ」「DBカラム追加のみ」といったレベルまで粒度を細分化することで、AIの実装精度が安定し、人間側のレビューコストも低減されます。</p> <h3>依存関係を明示する</h3> <p>前述の通り、依存関係の定義は並列化における要です。 自分が設計(Spec作成)を行っている間に、バックグラウンドで複数のエージェントが独立したタスクの実装を進めているという状態を作り出すことが重要です。</p> <h3>レビューの対話をコンテキストとして活用する</h3> <p>Agent SpecのレビューMTGは、録画または文字起こしをしておくことを強くおすすめします。<br /> 最初のSpecだけでは表現しきれていなかった背景やニュアンスが、レビュー時の対話には多く含まれています。<strong>この文字起こしログをCoding Agentに読ませてSpecを改善させる</strong>ことで、当初の記述では表現できていなかった文脈や設計の抜け漏れを補足し、より精度の高いSpecへと昇華させることができます。</p> <p>例えば、チームメンバーに設計を説明する中で、</p> <ul> <li>同じ時期に走っている別PJで追加される機能との整合性は?</li> <li>なぜこのカラムにIndexを追加する必要があるのか?</li> <li>どのデータが回線に紐付き、どれがアカウントに紐づくのか?</li> </ul> <p>など、チームメンバーから設計をみた時の疑問点が浮き上がります。レビューMTG内でこれらの質問に対して説明し、そのログをSpecに反映することで、設計者だけでなくチーム全体で納得感のあるSpecになります。設計の意図を適切にSpecに反映するのは、Coding Agentの脱線を防ぐうえでも重要です。</p> <h2>導入に向けたステップ</h2> <p>もし、この手法を個人のタスクから試してみたい場合は、以下のステップから始めることをおすすめします。</p> <ol> <li>PRDをMarkdownで記述する: AIがコンテキストを理解しやすくなります。</li> <li>Coding Agentにドラフトを書かせる: 自分でゼロから書かず、PRDを読ませたAgentにSpecの叩き台を作らせてください。</li> <li>依存関係リストを作成する: 実装に着手する前に、タスク一覧と依存関係を整理してください。これが並列化の指針となります。</li> </ol> <h2>おわりに</h2> <p>あらかじめSpecを書いてから実装に着手する手法は目新しいものではありませんが、この記事に書いた原則やワークフローを意識することで、Coding Agentの力をさらに引き出し、並列に素早く実装を進めることができます。</p> <p>実際にAgent Specを用いて並列で開発するワークフローを実践するようになってから、1ヶ月かかる想定だったプロジェクトを1週間で終えることができた事例もあり、確実に開発速度が向上しています。</p> <p>これらの定量的な開発速度の変化や、AI Native化へ向けたメルカリの開発組織の取り組みについては先日開催したmercari GEARS 2025で発表しています。ぜひ併せてご覧ください。</p> <p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/YrZQZvJxhOY?si=DbKUlIWMRIedPahM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p> <p>メルカリでは、ASDD (Agent Spec Driven Development) を含め、日々よりよい開発手法を試行錯誤しています。この記事がAIネイティブな開発スタイルについて考えるきっかけとなれば幸いです。</p> <p>明日の記事はpanoramaさんです。引き続きお楽しみください。</p> QAエンジニアがAIで日々の課題を解決した話https://engineering.mercari.com/blog/entry/20251203-46bf6511f3/https://engineering.mercari.com/blog/entry/20251203-46bf6511f3/<p>こんにちは。メルカリ ハロでQAエンジニアをしている@Yu-gaです。 この記事は、Mercari Advent Calendar 2025 の7日目の記事です。 概要 「この作業、AIで効率化できないかな?」そんな模索 [&hellip;]</p> Sun, 07 Dec 2025 11:00:29 GMT<p>こんにちは。メルカリ ハロでQAエンジニアをしている<a href="https://x.com/monsterggg?t=-N08JojGYjJ5db3V4nGExA&amp;s=09">@Yu-ga</a>です。</p> <p>この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a> の7日目の記事です。</p> <h2>概要</h2> <p>「この作業、AIで効率化できないかな?」そんな模索をしていた時、人手不足という現実的な課題に直面しました。本記事では、QAエンジニアとして1〜2日で作れる小さなプロトタイプから始め、チームのフィードバックを受けながら実用的なツールに育ててきた実体験を紹介します。</p> <h2>きっかけと課題</h2> <p>メルカリが「AI Native」への転換を発表する中、QAチームでも「私たちの業務にもAIを取り入れられないか?」という議論が活発になっていました。</p> <p>限られたリソースの中で品質を維持するには、これまでのやり方を見直す必要があります。そこでチーム全体で集まり、<strong>「今後のQAプロセスをどう進化させるか」「AIでどの部分を効率化できるか」</strong> を議論しました。</p> <p>話し合いを重ねる中で、「まずは課題解決ツールのプロトタイプを作ってみよう」という共通の方向性が生まれ、限られた時間とリソースの中でQA業務全体の効率化を目指し、AIを活用した新しいQAプロセスの検証が始まりました。</p> <h2>QAエンジニア以外でもACを書けるようにしたい</h2> <p>メルカリ ハロではQAエンジニアがAcceptance Criteria(以下AC)をJiraのストーリーチケットで作成していましたが、これをQAエンジニア以外でも簡単かつQAエンジニアと同程度の品質で作成できるようにする必要があり「AC Generator」というWebツールを開発しました。</p> <p>ここでいうAcceptance Criteria(受け入れ基準)とは、<strong>その機能が「完成した」と判断するための具体的な条件</strong>のことです。<br /> ※以前のAC活用に関する取り組みについては<a href="https://engineering.mercari.com/blog/entry/20241207-mercari-hallo-2024/">こちらの記事</a>もご覧ください。</p> <p>「AC Generator」の開発にあたっては、「PMやエンジニア全員が使えること」と「人間によるレビュー・修正を必須とすること」を設計の核としました。<br /> 誰もが使えるようにしたのは、単なるQAのリソース不足解消だけが目的ではありません。QAエンジニア以外でもACを作成できるようになれば、開発の初期段階から全員が品質について同じ解像度で議論しやすくなると考えたためです。</p> <p>また、レビューを必須としたのは、AIに「ある程度の土台」を作ってもらうことで作成のハードルを下げつつ、AI特有の誤り(ハルシネーション)を人間の目で排除するためです。これにより、誰でも効率的に、かつ一定品質以上のACを作成できるようになると考えました。</p> <p>また、「過去にQAエンジニアが作成したACから厳選した学習データ」をAIに分析させ、AC作成ルールを生成しました。このルールには、AC作成の際の書き方や構造、仕様書からACを抽出する際の観点などが含まれています。</p> <p>最初のプロトタイプでは、ユーザーがConfluenceの仕様書URLを入力すると、AC作成ルールを元にAIが自動でACを生成し、Web画面で結果を表示、それを人間がレビュー・修正してJiraチケットに起票するという一連のフローを構築しました。このプロトタイプは、AIを用いて1日程度で作成することができました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/795f12cd-pr-rule.png" alt="AC Generator システムフロー" /></p> <p>実際にPMやエンジニア、QAエンジニアにツールを使用してもらい、SlackやJiraでフィードバックを収集し、それらに一件一件対応しました。例えば「成果物の精度がばらつく」という意見に対してはAIレビュー機能を追加し、「AIに再度修正して欲しい」という要望にはAIフィードバック機能を追加するなど、20件を超えるフィードバックに対応して機能を拡張しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/6c823f0d-092a-4a8e-ab7b-b8182158f6e4-1024x886.png" alt="AC Generator 実際の画面" /></p> <p>その結果、PMやエンジニアがACを作成できるようになり、個人差はあるものの<strong>最低限のACが誰でも作成可能な状態</strong> となりました。AC Generatorが一時的に使用できなくなった際に、エンジニアから「AC GeneratorがないとAC作れないよ」という声をいただき、実用的なツールに成長できたことを実感しました。</p> <p>現在は仕様書とルールベースでの生成に限定されているため、仕様書に記載されていない影響範囲の分析ができませんが、将来的には仕様書全体の取り込みやコードベースを考慮した生成にも発展させていきたいと考えています。</p> <h2>リグレッションテストケース作成を効率化したい</h2> <p>AC Generatorと同様のアプローチで、リグレッションテストケース作成の効率化にも取り組みました。「ACから重要なものをリグレッションテストに追加する」というプロセスがありましたが、その作業は後回しにされることが多く、<strong>リグレッションテストが増えない</strong> という課題がありました。</p> <p>そこで、テスト管理ツールの<a href="https://www.testrail.com/">TestRail</a>に登録されている既存のリグレッションテストとその階層構造をAIに分析させました。これにより、どのようなテストがどのフォルダに分類されるべきかという分類ルールや粒度を学習し、現状の運用に即したリグレッションテスト作成ルールを策定しました。</p> <p>先ほど紹介したAC Generatorのアーキテクチャをベースに、誰でも簡単に使えることを重視し、ACとルールからリグレッションテストを生成、Web画面で人間がレビュー・修正してTestRailに起票するというフローをAIを用いて1〜2日で構築しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/eab9ac77-regression-test-generator.png" alt="リグレッションテスト Generator システムフロー" /></p> <p>このツールにより生成されたテストケースの精度が高く、そのまま使えるレベルのものも多かったため<strong>リグレッションテスト作成の工数は体感で8割程度削減</strong> されました。しかし、作成コストは削減できたものの、リグレッションテスト作成のきっかけ作りには至らず、テスト数の増加効果は限定的でした。</p> <p>根本的な解決には、作業のトリガーとタイミングの自動化が必要でした。そこで、ACのJiraチケット完了時に自動でリグレッションテスト生成・PR作成を行うワークフローの開発にも着手しましたが、現状まだ実用段階にはなっておりません。</p> <h2>E2Eテスト作成を効率化したい</h2> <p>E2Eテスト作成においては、テストコードの書き方やTestRailの構造に合わせたファイル設計、Page Object Model、コーディング規約など習得すべき知識が多く、<strong>特定のメンバーに依存してスケールしにくい</strong> という課題がありました。</p> <p>この課題に対し、既存のE2EテストをAIに分析させてコーディング規約を自動生成し、さらに<a href="https://playwright.dev/docs/best-practices">Playwrightのベストプラクティス</a>も組み込んだルールを作成しました。<br /> 開発用エディタでの動作を前提としたE2E作成ルールと、TestRailのケースIDからテスト手順などの情報を取得するスクリプトを組み合わせることで、<strong>AIがTestRail情報を基にE2Eテストコードを自動生成できる</strong> ようにしました。このプロトタイプもAIを用いて2日程度で作成することができました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/4c27b74b-create-e2e-rule.png" alt="E2Eテスト作成効率化フロー" /></p> <p>Page Objectの書き方統一やAPI利用ルールの追加、AIレビュールールの追加など、フィードバックを受けて改善を行った結果、AIによるE2Eテスト作成の精度が向上し、以前よりも効率化されました。<br /> E2E作成ルールの整備により、チームメンバー誰もが一定品質のE2Eテストを作成できるようになりました。</p> <p>ただし、生成されたコードには人間によるレビューと修正が必要で、チームメンバーにも基礎知識が求められるため、完全な自動化には至らず、人的コストは残存しています。</p> <h2>テスト前にコードベースからIssueを検出したい</h2> <p>コードレビューによる問題特定は従来から行っていましたが、得意・不得意な技術領域があり、レビューの質に偏りがあるという課題がありました。</p> <p>そこで、AIを活用してPRの修正内容(差分)を分析し、仕様書やACと照合してコードベースからバグを検出する取り組みを行いました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/331db3b5-ac-generator.png" alt="コードベースIssue検出フロー" /></p> <p>QAエンジニアがテストを開始する前にこのチェックを行い、問題点だけでなく解決策もセットでエンジニアにフィードバックするようにしました。その結果、手動テストでは発見困難な問題(動作には影響しない型定義のミスなど)や仕様との齟齬をテスト前に検出できるようになりました。</p> <p>この取り組みにより、<strong>手動テスト前のIssue発見とコード理解</strong> の両面で大きな効果を得られました。一方で、AIの回答には誤りも多く含まれており、AIの提案を鵜呑みにせず、人間による適切な判断が不可欠であることを学びました。</p> <h2>学び</h2> <p>今回の取り組みを通じて、AIは単なる作業自動化の手段にとどまらず、QAエンジニアが抱える課題を自らの手で解決するための強力な武器になることを実感しました。一連のツール開発とチームでの運用を通じて得られた、AI活用における学びを以下にまとめます。</p> <h3>まずはやってみる</h3> <p>技術的な難易度を事前に考えすぎるより、まず手を動かして動くものを作ることの重要性を実感しました。どの機能も最初のプロトタイプは1〜2日で作成でき、AIを活用すれば想像以上に多くのことが実現可能であることがわかりました。</p> <h3>フィードバックループを早く回す</h3> <ol> <li>最小プロトタイプを1〜2日で作成</li> <li>みんなに使ってもらう</li> <li>フィードバックを収集</li> <li>改善する</li> </ol> <p>このサイクルを早く回すことで、実用的なツールに育ちました。</p> <p>また、<strong>小さく始めることで失敗のリスクを下げる</strong> ことができました。1〜2日で作れるプロトタイプなら、仮に使われなかったり方向性が間違っていても、投資した時間やコストは最小限で済みます。大規模な開発を始める前に、本当にニーズがあるのか、アプローチが正しいのかを低コストで検証できたことは大きなメリットでした。</p> <p>多くの人に使ってもらいフィードバックを得ることは簡単ではありませんが、最初のツール紹介の反応をきっかけに、発信の場や伝え方を工夫するようになりました。その結果、より多くのメンバーの目に留まり、さまざまなフィードバックを得ることができました。</p> <p>プロトタイプは使われなければ成長しないため、開発前から「どうやって使ってもらうか」「どうフィードバックを集めるか」を戦略的に考えることの重要性を学びました。</p> <h3>半分の効率化でも十分価値がある</h3> <ul> <li>AIが頭出し → 人間がレビュー・修正</li> <li>AIが下書き → 人間が仕上げ</li> <li>AIが指摘 → 人間が判断</li> </ul> <p>この協働パターンが最も効果的でした。AIが最初に生成するものには品質のばらつきがあるため、人間によるレビューと修正は必須のプロセスだと思います。</p> <p>100%の効率化を目指すと、完璧を求めるあまり開発に時間がかかったり、そもそも実現できなかったりします。しかし、50%程度の効率化であれば、短期間で実現でき、すぐに効果を実感できます。</p> <p>そして重要なのは、削減できた時間で新たな改善に取り組めることです。50%削減で生まれた時間を使って次のツールを作り、さらに効率化を進める。この積み重ねによって、結果的に大きな効率化を実現できました。</p> <h3>テストを事前に書いておく</h3> <p>AIは想定外の動きをします。フィードバックを元に機能を追加する際に、既存機能を壊してしまうということが散見されました。それを防ぐために、コア機能についてテストを事前に実装し、AIが修正を行う際には必ずテストをクリアすることを必須としました。</p> <p>例えば、AC Generatorでは以下のようなテストを実装しました。</p> <ul> <li><strong>基本表示テスト</strong>: フォーム要素が正しく表示されるか</li> <li><strong>バリデーションテスト</strong>: 必須フィールドやURL形式のチェック</li> <li><strong>完了フローテスト</strong>: フォーム入力からチケット作成完了まで</li> </ul> <p>これらのテストは品質保証だけでなく、<strong>AIに対する仕様の明確化</strong>という重要な役割も果たしました。テストコードが「期待する動作」を具体的に定義することで、AIが修正を行う際の指針となり、意図しない変更を防ぐことができました。</p> <p>しかし、AIにコード修正を依頼すると、<strong>「テストが通らないから」という理由で、AIが勝手にテストコード側を修正してしまう</strong>という問題も発生しました。これを防ぐために、「テストコードは修正禁止」というルールを明確にAIに与えることで解決しました。</p> <h2>まとめ</h2> <p>今回の取り組みを通じて、<strong>「経験がないからできない」という時代は終わった</strong>と強く感じました。</p> <p>AIの登場により、未経験の領域でも成果を出せる可能性が劇的に広がりました。実際、今回のツール開発では、これまで使ったことのない技術やアーキテクチャを容易に取り入れることができました。「やったことがないから無理だ」と諦めていたアイデアも、AIというパートナーがいれば、わずか1〜2日で形にできる時代です。</p> <p>今回の取り組みで最も強く感じたのは、AI活用において重要なのはプログラミングスキルそのものよりも、「この課題を解決したい」という強い当事者意識だということです。<br /> 「誰かが解決してくれるのを待つ」のではなく、「AIを活用して自分で解決する」。</p> <p>このマインドセットの変化こそが、業務効率化以上の価値を私自身にもたらしてくれました。AIですべてを100%解決できるとは思いませんが、自らの手で課題を解決し、より本質的な品質保証活動に注力するための強力な武器になると思います。</p> <p>今後もAIの力を借りながら、品質保証の新しい形を模索し続けていきたいと思います。</p> <p>明日の記事は @Antony Chane-Hive さんです。引き続きお楽しみください。</p> 本番環境でしか起こらない Goroutines 急増問題と解決までの道のりhttps://engineering.mercari.com/blog/entry/20251207-61e24d343e/https://engineering.mercari.com/blog/entry/20251207-61e24d343e/<p>こんにちは。Backend Enablement Team のエンジニアの @goccy です。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の7日目の記事です。 時は [&hellip;]</p> Sun, 07 Dec 2025 10:00:51 GMT<p>こんにちは。Backend Enablement Team のエンジニアの <a href="https://x.com/goccy54">@goccy</a> です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の7日目の記事です。</p> <p>時は今から4ヶ月ほど前の8月下旬、とても暑い日でした。珍しくチームの On Caller の PagerDuty がアラートを上げました。内容は Gateway からあるマイクロサービスへの呼び出しが突然 Timeout するようになり、gRPC の Deadline Exceeded が急増しているというものでした。対象のマイクロサービスは <a href="https://github.com/mercari/grpc-federation">gRPC Federation</a> を利用した BFF ( Backend for Frontend ) で、基本的にはさらに後ろのマイクロサービスにプロキシするだけの単純なサービスです。少し調査すると、該当の gRPC メソッドには Timeout が設定されていなかったため、Gateway 側で設定した Timeout ( 30秒 ) を超過した際に、Gateway 側でリクエストをキャンセルするタイミングで出ているエラーだとわかりました。この間、BFF 側でエラーが発生していないにもかかわらず Goroutines は増え続けていることから、リクエストが処理できずにサーバー上で滞留しているような状況だと推測できました。しかし、それ以上の調査が困難なことと、問題が起きているのが一部の Kubernetes Pod だけだったため、該当の Pod を再起動することを試したところ、無事解決に向かいました。その日は営業時間が終了間際だったこともあり、もやもやしたものを抱えつつ、いったん解散して帰途につくことになりました。</p> <p>このときはまだ、これから長い闘いが待っていることをチームの誰も知る由もありませんでした。</p> <p>サービス間の関係図</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/c52c28b5-day7】merpay-mercoin-advent-calendar-2025-engineering-blog-draft-template-e1764916208261.png"/></p> <p>それから二日後、先日の問題の原因究明もろくにできていないような状況で、再びアラートが上がりました。しかし今回は前回とは別のサービスで、かつ gRPC Federation を利用していました。現象は前回同様、突然該当のサービスの特定の Pod だけ Goroutines が急増し応答しなくなるという問題で、Pod を作り直すと改善するという現象も同様でした。</p> <p>立て続けに gRPC Federation を利用する複数のサービスで同様の問題が起こったため、原因が gRPC Federation 側にありそうだという線が濃厚になってきました。これはとてもまずい状況です。gRPC Federation を利用するサービスは 20 を超えます。</p> <p>突如として暗雲が垂れ込めます。この日から、チーム総出でこの問題に対処するようになりました。</p> <h2>Lock 範囲の最小化</h2> <p>まず、該当のサービスの直近のリリースに含まれる gRPC Federation の変更点などを調査しましたが、こちらは特に怪しいものはないという結論に至りました。次に、リクエストが滞留し続けることから、gRPC Federation 側で取得している Lock の待ち時間が長すぎたり、Dead Lock しているケースがないかを疑いました。該当のサービスでは Cloud Profiler を導入しており、その結果から Lock 待ちをしているスレッドが大量にあることがわかりました。Lock 箇所として複数の候補がありましたが、それらを Claude Code を使ってプロファイリング結果を参照しながら議論したところ、いずれも Dead Lock でないこともわかりました。次の可能性として、Lock を解放するまでの時間が長く、Lock を解放するよりも Lock の待ち行列に加わるスレッドの方が多くなり、結果 Goroutines が増え続けている可能性を考えました。そこで、まずは Cloud Profiler が挙げた Lock 箇所のうち、Lock 範囲を最小化することで不必要に長く Lock を取得している箇所をなくしました。( <a href="https://github.com/mercari/grpc-federation/pull/323">該当のPR</a> )</p> <p>しかし数日後、また Goroutines が急増しアラートが上がりました。この時点から、一日に3〜 5回程度不定期にアラートが鳴り続けるようになってしまい、Pod を再起動するだけとはいえ、On Caller の負担が顕著に増えました。ただ、Lock 範囲を狭めたことで、Lock 待ちをしている箇所のほとんどが gRPC Federation のプラグインを利用している箇所であるとわかりました。</p> <h2>gRPC Federation のプラグインアーキテクチャ</h2> <p>ここで、以降の説明を理解しやすくするために gRPC Federation のプラグインアーキテクチャについて解説します。gRPC Federation 自体の詳しい説明は<a href="https://engineering.mercari.com/blog/entry/20240401-4f426bd460/">私が以前書いた別記事</a>をご参照ください。</p> <p>簡単に説明すると、gRPC Federation は Protocol Buffers 上で記述できる型推論付き静的型付け言語であり、式の評価には <a href="https://github.com/google/cel-spec/blob/master/doc/langdef.md#language-definition">CEL( Common Expression Language )</a> を利用しています。 OSS として開発しているため、Protocol Buffers 上で記述する CEL 式の中でドメイン固有のロジックを利用することは通常できません。例えば認証処理を例にとると、gRPC の metadata から 認証用の JWT を取り出してパースし、得られた値をバリデーションしたり、さらに別の値を取得するような処理は CEL の表現力の範囲を超えています。ですが、こういった処理は gRPC Federation を利用するすべてのサービスで必要になる共通処理でもあります。<br /> gRPC Federation ではこのような要件のために、CEL 式の中で利用できる API を独自に定義し、その実装を Go で書いて WebAssembly に変換して利用する<a href="https://github.com/mercari/grpc-federation/blob/main/docs/cel_plugin.md">プラグインシステムを用意しています</a>。</p> <p>Go でプラグインシステムを提供する場合、WebAssembly を利用するのが最善であることは、先日 Go Conference 2025 で発表してきた ( <a href="https://speakerdeck.com/goccy/go-de-webassembly-woli-yong-sita-shi-yong-de-napuraguinsisutemunogou-zhu-fang-fa">Go で WebAssembly を利用した実用的なプラグインシステムの構築方法</a> ) ので、詳しくはそちらを参照して欲しいのですが、重要なこととして、WebAssembly を利用する場合に次のような制約があることを知っておく必要があります。</p> <ul> <li>WASI P1 という仕様に沿って動作するが、P1 の段階ではスレッドの仕様は存在しない</li> <li>スレッドの仕様がないため、必ずシングルスレッドで動作する</li> <li>WebAssembly を動作させるために作るインスタンスは、起動するだけで数十MBのメモリを消費するため、インスタンスを無制限に作るようなアーキテクチャは採用できない</li> </ul> <p>これらの制約のため、gRPC Federation ではプラグインを評価する際、gRPC Service ごとにプラグインに対する WebAssembly インスタンスを一つだけ作成し、評価の前後で Lock を取得して直列に処理します。これはつまり、サーバーに対してどんなに並行にアクセスしても、プラグインの処理の段階では直列実行になることを意味しています。</p> <p>Cloud Profiler の結果、このプラグイン評価時に取得する Lock 待ちが大量に発生していることが原因だとわかりました。</p> <p>また、以降ではプラグインの評価に際して、WebAssembly ランタイムを動かすGo 側をホスト、プラグイン側をゲストと呼称して説明します。</p> <h2>問題の再現に挑戦</h2> <p>具体的な原因がわかってきたところで、プラグインの評価を高頻度かつ大量に行うことで問題が再現するかを試し始めました。ローカルで立ち上げたサーバーに対して高負荷な状況を作ったり、開発環境にデプロイしているサーバーに対してリクエストするなどさまざまなアプローチをチーム総出で試しましたが、問題が起こった gRPC メソッドに対して 1000rps を超える負荷を 30分近くかけ続けても再現には至りませんでした。</p> <p>ここで、問題を再現することが難しいことが分かったため、問題発生時に本番環境で取れる情報を増やす方向も考え始めました。具体的には対象のサービスに pprof による計測用のエンドポイントを仕込んでおき、問題が起きたときに対象の Pod のサーバーに対してプロファイリングを実行することで、 Cloud Profiler ではわからない情報をとれるようにしました。</p> <h2>pprof による調査</h2> <p>pprof を仕込んだ翌日、問題が再発しました。今回は対象の Pod を再起動するのではなく、Pod を Kubernetes Service から切り離し、切り離したサーバーの pprof のエンドポイントにリクエストを送ることで各種データを取得しました。<br /> 取得した結果を対象に、例えば <code>go tool pprof -traces</code> を実行すると、計測時に動いていたすべての Goroutines をスタックトレース付きで把握することができるのですが、プラグイン関連のスタックトレースを見ると、プラグイン評価に際してホストとゲストそれぞれが相手からのリクエストを待っている状態になっていました。これはありえない状況です。ホストは正しくゲストに要求を伝えたと思っているが伝わっていない、もしくはその逆の現象が発生している可能性があります。<br /> それぞれが同時に相手からの要求を待ち受けているということは、永久にその要求は満たされません。そのため、デッドロックのような状況になってプラグイン評価が永遠に行われず、プラグイン評価は直列に動作するため、サーバーの処理が停止してしまう事態になっていることがわかりました。</p> <p>しかしここから調査が難航します。そもそもなぜこのような事態になっているかがわからなかったのと、問題が起こるたびにプロファイリングをしても結果が毎回同じ内容だったため、新しい情報が得られませんでした。ローカルでも引き続き再現しません。</p> <p>そこで、プラグインインスタンスに何らかの不調が起きて、突然ホストとゲスト間の疎通ができなくなると仮定し、ホストとゲスト間のメッセージのやりとりに利用するメモリの読み書き処理でエラーになった場合は、あらかじめ作っておいたバックアップインスタンスにフォールバックするような仕組みを作成しました ( <a href="https://github.com/mercari/grpc-federation/pull/327">https://github.com/mercari/grpc-federation/pull/327</a> )。もしどこかでエラーが返るようであれば、インスタンスをリフレッシュすることで復帰できるはずです。</p> <p>しかし結果は期待とは異なり、エラーが発生することもなく、予兆なしに突然問題が発生してしまいました。</p> <h2>GC を疑い始める</h2> <p>プラグインを評価する際、ホストからゲストへのリクエスト方法は大きく2通り存在します。<br /> 一つは認証処理の例で説明したような通常の API リクエストです。もう一つはGCの実行でした。<br /> WebAssembly ランタイムではアロケーション戦略としてリニアメモリを採用しており、メモリが足りなくなったら倍のメモリを確保して対処します。ですが、メモリが十分でも確保した範囲を縮小することはしません。つまり、GC が走るタイミングが遅れるほど、メモリが不必要に多く確保されてしまうことになります。これを防ぐため、プラグイン側で細かく GC を実行する必要があり、gRPC Federation では定期的にホストからゲストに対して、強制的に <code>runtime.GC()</code> を実行するように要求する戦略をリリース初期から1年以上行ってきました。</p> <p>このアーキテクチャをふまえ、本番環境でしばらく運用した際にエラーなく突然問題が起こることから、GC をトリガーした際に問題が起こるのではないかという仮説を立て、ホストからの GC 呼び出し処理をなくすことで、事態が好転するのではないかと考え始めました。仮に GC が関係なかったとしても、ホストからゲストにリクエストを投げる回数自体が減るため、問題が発生する頻度が下がるのではないかという期待もありました。<br /> トレードオフとして、プラグイン側で GC が走る頻度が減るため OOM ( Out Of Memory ) になるリスクは上がりますが、まずはホストからゲストへの GC 要求をやめて様子を見てみることにしました。</p> <h2>事態が好転し始める</h2> <p>GC の経路を削除した後、アラートが上がる頻度が目立って減りました。しかし、予想通り OOM が発生してしまったのと、頻度がゼロにはなっていないため、根本対策にもなっていません。<br /> プロファイリングしても相変わらず同じ結果でした。</p> <p>しかし、事態が今までと変わったことから、問題の切り分けのためにプラグイン側で自動で走る GC を疑ってみることにしました。プラグイン側で走る自動GCを <code>GOGC=off</code> 環境変数を指定することで止め、強制 GC のみで運用する方法です。このとき、より実装をシンプルにするため、今までホスト側で GC の終了を待っていなかったのを同期的に待つようにしました。補足として、今まで GC の終了を待っていなくて大丈夫なのかと疑問に思った方がいるかもしれませんが、ゲスト側で強制GCの場合を特別扱いして処理しているので非同期にすること自体は問題ありません。</p> <p>この状態でしばらく運用したところ、問題が発生する時は、スタックトレースから必ず強制GCの経路になっていることがわかりました。つまり、ホストから強制GCを実行したところ、その結果が返らずに停止しているということになります。</p> <h2>ホスト・ゲスト間の通信方法の改善</h2> <p>突然 <code>runtime.GC()</code> から処理が返ってこなくなるとは考えにくかったので、GC が走った影響で、ホストとゲスト間の通信に利用する何かが壊れたと推測しました。そこで、両者の通信方式をよりシンプルなものに変更することで GC の影響を減らすことができるのではないかと考え、今まで利用していた STDIO を使った通信方法をやめ、ホスト関数と Go のチャネルを利用した通信方式に変更し、すべてをホスト上で行う標準的な Go のチャネルを介した処理に変更しました。</p> <p><a href="https://github.com/mercari/grpc-federation/pull/331">https://github.com/mercari/grpc-federation/pull/331</a><br /> <a href="https://github.com/mercari/grpc-federation/pull/333">https://github.com/mercari/grpc-federation/pull/333</a> </p> <p>この処理は Go Conference 2025 で紹介した際のホスト・ゲスト間の通信処理を改善したものなので、興味のある方は見てみていただければと思います。</p> <p>別件で、プラグイン上でネットワークソケットを扱うために導入した <a href="https://github.com/goccy/wasi-go">wasi-go</a> というライブラリも疑い、ネットワークソケット関連の syscall 以外を <a href="https://github.com/wazero/wazero">wazero</a> が提供する枯れた実装に置き換えようともしましたが、wazero がファイルシステムまわりの API を private にしているために、 wasi-go 側で作った ソケットディスクリプタ を wazero 側に伝える方法がなく断念しました。</p> <p>さて、この改善を行ったことで事態がより進展すると思われましたが、問題は収束せず、プロファイリングすると、ホストとゲストそれぞれでチャネルの受信処理でブロックしているという結果になりました。ただし、いずれも強制GCの結果待ちでした。</p> <h2>ログを仕込む</h2> <p>今回の問題は本番環境で長時間運用しなければ発生しなかったため、コストの面からデバッグログを仕込むことを躊躇っていました。しかし、これ以上原因をしぼりこむことができなかったため、遂にログを追加することにしました。<a href="https://github.com/mercari/grpc-federation/pull/335">あわせて Trace も追加しました</a></p> <p>気をつけたこととして、問題が発生したらできるだけすぐにデバッグログを無効にしたかったため、環境変数を有効にしたときだけログを有効にするような Feature Flag 方式を採用し、環境変数の変更を巻き戻せばすぐに元に戻せるようにして運用しました。</p> <p>ログを有効にして計測した結果、ゲストがGC 実行の要求を受け取ったというログは見つかりましたが、結果を送信するというログがなかったため、ないだろうと考えていたGC実行中にハングしている可能性が濃厚になりました。</p> <p>また、ログを仕込んだ結果、ホストとゲストでどちらも受信待ちになっていたと思っていた不可思議な現象は、実は別のプラグインインスタンスだったことがわかりました。つまり、ホスト側とゲスト側のスタックトレースは、それぞれ別のインスタンスのものを表示していたのです。先に説明した通り、gRPC Federation は gRPC Service ごとに作るプラグインインスタンスは一つですが、 <strong>同一プロセス上で動く gRPC Service が複数ある場合はインスタンスも複数になります</strong>。このことを考慮できていなかったのが悔やまれます。しかしこれで、問題がデッドロックではなくGC実行中のハングに収束したためとてもシンプルになりました。</p> <h2>インスタンスの自動復旧対策</h2> <p>気づくと10月も半ばに差し掛かっていました。振り返ると8月下旬から数えて2ヶ月近く格闘していたことになります。</p> <p>ですが問題が明確になったことで、取れる策も具体的になりました。GC実行中に処理が返らなくなることから、まだその原因はわからないにせよ、ワークアラウンドを実装することはできます。強制GCにタイムアウトを設け、タイムアウトした場合はインスタンスが壊れたと判断し、前に実装していたインスタンスをバックアップに切り替える仕組みを使って自動的に復旧することができるはずです。<br /> ついでに、インスタンスを切り替える際にログも出力してみます。無事復旧しているならばログが出るはずです。</p> <p>この対応を行ってから、数日間様子を見ましたが、仕込んだログは定期的に出力されつつ、Goroutines が急増することもなくなりました。<br /> ひとまず、ワークアラウンドはうまく動いていそうで、障害対応が落ち着いたといえる状態になりました。</p> <p>このとき行った実装は<a href="https://github.com/mercari/grpc-federation/pull/340">こちらです</a></p> <h2>根本原因が判明</h2> <p>障害対応が落ち着いたので、今まで得られた情報からより再現しやすい環境で調査を続けたところ、長時間動作させる中でローカルで再現できるようになりました。依然として再現することは難しかったですが、それでも再現できたことは大きく、詳細なデバッグログを仕込んで複数回実行する中で、 ネットワークソケットを利用するために導入した wasi-go というライブラリの <code>PollOneOff</code> syscall 内で呼び出している <code>unix.Poll</code> から処理が返ってこなくなることがわかりました。デバッグログの結果から、どうやら通常は 0 になるはずの <code>unix.Poll</code> に渡している timeout 値が異常値(負数)になっており、永久にくることのないイベントを待ってブロッキングする挙動になっていることがわかりました。また、<code>PollOneOff</code> と GC に因果関係があるかを調べるために Go 本体のソースコードを追ってみると、GC のシーケンスで <code>PollOneOff</code> が呼び出される経路があることもわかりました。</p> <p>この問題を修正したときのPRは<a href="https://github.com/goccy/wasi-go/pull/6">こちらです</a>。<br /> ※ ライブラリは私が作ったもののように見えますが、実際には fork したものを使っています。</p> <p>この修正を適用したあと、仕込んだインスタンス切り替え時のログは出力されなくなり、無事問題が解決しました。問題発生から2ヶ月ほどのことで、あの暑かった夏から少し肌寒くなってきていました。</p> <h2>反省点</h2> <p>あらためてプロファイリング結果を思い返すと、実は <code>PollOneOff</code> のスタックトレースは一番最初に計測したプロファイリング結果に含まれていました。勘がよければ、最初の時点で <code>PollOneOff</code> が原因だと気づくこともできたのです。</p> <p><code>PollOneOff</code> は epoll のようにファイルディスクリプタに対するイベントを監視する WASI 用の syscall で、スタックトレースは最初から目に入っていましたが、初期の実装ではホストとゲスト間の通信に STDIO 方式を採用していたため、通信時に利用する標準入出力のイベント待ちで呼び出されているのかと勘違いしていました。また、wazero ( WebAssembly ランタイム ) 側で特別に別スレッドでイベント待ちをしているのかとも予想しており、あまり気にしていなかったのが悔やまれます。</p> <p>冷静に考えると、あとで Go のチャネルを利用した通信方式に切り替えてもスタックトレースに現れていたのはおかしいし、シングルスレッドアーキテクチャなのに別スレッドでイベント監視しているのもおかしいので、先入観で判断してはいけないと改めて気付かされました。</p> <p>逆に、最初からプロファイリング結果に答えは書かれていたので、pprof が大事だとも言えます。</p> <h2>振り返り</h2> <p>本番環境でしか問題が起こらない場合、問題のある Pod を単に再起動するのではなく、サービスから切り離してプロファイリングするのは非常に重要なフローでした。<br /> pprof は実行時にしか負荷がかからないので、本番用のビルドでも関係なく pprof の計測エンドポイントを持っておいた方がいいという気づきも得ました。</p> <p>また、ちょっとしたことですが、デバッグログを無効化するために実装を巻き戻して image を作り直し、 hotfix を deploy する方法だと時間がかかってしまうので、環境変数によってデバッグログの出し分けを制御できたのは良かったです。</p> <p>今回の対応では、問題のあるPodをサービスから切り離した後、pprof によるプロファイリングをメインに対応してきましたが、実はもうひとつ試したかったこととして、 Delve でのサーバープロセスへのアタッチによるデバッグがありました。しかし、いくつかの制約により実施には至らず、もし実行できていれば <code>unix.Poll</code> 時に timeout 値が変な値になっていることに気付いたかもしれないため、今後の課題です。</p> <p>メルカリグループ全体の課題として、プロファイリング結果や Core ファイルなどを永続化する手段を標準化していないため、仮にそれらの情報をディスクに書き出したとしても、Pod が消えると情報が消えてしまう問題があります。Go 1.25 から入った <a href="https://go.dev/blog/flight-recorder">Flight Recorder</a> などを効果的に利用するためにも、解析情報を Pod の外に退避させ、デバッグ時に参照できるような仕組みを構築することが重要だと考えています。</p> <p>今回修正を加えた <a href="https://github.com/goccy/wasi-go">https://github.com/goccy/wasi-go</a> は、<a href="https://github.com/dispatchrun/wasi-go">https://github.com/dispatchrun/wasi-go</a> を fork したものなのですが、fork 元のメンテナンスが停止していること、自分の方のライブラリでは今回の修正対応以外にも TLS 対応や exec.Command 対応など、実際のユースケースに応じたさまざまな改善が入っているので、これから Go が WASI P2 に対応するまでの間で WebAssembly を使いたいと思っている方には強い味方になってくれるはずです。ぜひご利用ください。</p> <p>最後に、今回 Claude Code にプロファイリング結果の分析などの壁打ち相手になってもらいましたが、AI は答えを出そうとするあまり誤った答えを平気で出してくるので、嘘を嘘だと見抜く力が必要だと強く感じました。</p> <p>明日の記事は fenomasさんです。引き続きお楽しみください。</p> YAPC::Fukuoka 2025に参加しました!! #yapcjapanhttps://engineering.mercari.com/blog/entry/20251206-yapc-fukuoka-2025/https://engineering.mercari.com/blog/entry/20251206-yapc-fukuoka-2025/<p>Merpay KYC チームの Sakabe です。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の6日目の記事です。 2025年11月14日〜15日に開催された「YA [&hellip;]</p> Sat, 06 Dec 2025 10:00:02 GMT<p>Merpay KYC チームの Sakabe です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/" title="Merpay &amp; Mercoin Advent Calendar 2025">Merpay &amp; Mercoin Advent Calendar 2025</a> の6日目の記事です。</p> <p>2025年11月14日〜15日に開催された「<a href="https://yapcjapan.org/2025fukuoka/" title="YAPC::Fukuoka 2025">YAPC::Fukuoka 2025</a>」へメルカリグループはスポンサーとして参加していました。今回はその参加レポートをお届けします!</p> <h3>YAPC::Fukuoka 2025 について</h3> <p>YAPC は Yet Another Perl Conference の略で、Perl を軸にしながらも、今では Web・インフラ・AI・SRE・フロントエンドなど幅広い技術者が集まる技術カンファレンスです。<br /> 今回のテーマは「きゅう」。探”求” や ”急”激な成長体験など、”九”州開催の YAPC らしいキーワードでした。</p> <p>開催概要<br /> 日程: 2日開催<br /> 2025年11月14日(金)<br /> 2025年11月15日(土)<br /> 会場: 福岡工業大学</p> <p>会場入口には YAPC の装飾が並び、到着した瞬間からイベントの熱気に包まれました。<br /> 福岡工業大学は駅を降りるとすぐに広いグラウンドが目に入り、学生の活気も合わさって “技術のお祭り” らしい雰囲気が一気に高まります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/f91e8ba5-img_5699-scaled.jpg" alt="" /></p> <h3>Mercari メンバーの登壇</h3> <p>今回は Mercari から 「<a href="https://fortee.jp/yapc-fukuoka-2025/proposal/d4d17d8d-e69b-43e0-8d0b-51d3f9570627" title="iPhone のマイナンバーカードを使った本人確認の実装">iPhone のマイナンバーカードを使った本人確認の実装</a>」と題しeKYC に関するトークが採択されました。<br /> iPhone のマイナンバーカードを使った本人確認の機能について、Verifier における技術的な観点や開発経緯などをkgoroさんに話していただきました。<br /> 最新の eKYC 手法ということもあり質問が非常に多く、Q&amp;A 後も聴講者が集まって議論が続くほどの盛り上がりでした。<br /> 現地ならではの熱量を強く感じたセッションでした。</p> <p>発表資料はこちらをご確認ください。</p> <p><iframe class="speakerdeck-iframe" frameborder="0" src="https://speakerdeck.com/player/27dbd43fed5a497d9bdf691e2a932f12" title="YAPC Fukuoka 2025 - iPhone のマイナンバーカードによる本人確認" allowfullscreen="true" style="border: 0px; background: padding-box padding-box rgba(0, 0, 0, 0.1); margin: 0px; padding: 0px; border-radius: 6px; box-shadow: rgba(0, 0, 0, 0.2) 0px 5px 40px; width: 100%; height: auto; aspect-ratio: 560 / 315;" data-ratio="1.7777777777777777"></iframe></p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/1efb62bc-img_5738-scaled.jpg" alt="" /></p> <h3>セッションの見どころ</h3> <p>DBからエンジニアリング、AI まで幅広い発表があり、個人的に特に印象に残ったのはこちらです。</p> <ul> <li><a href="https://fortee.jp/yapc-fukuoka-2025/proposal/1ee39cf1-a581-450d-8692-a9a9f7f39b7f" title="今、MySQLのバックアップを作り直すとしたら何がどう良いのかを考える旅">今、MySQLのバックアップを作り直すとしたら何がどう良いのかを考える旅</a></li> <li><a href="https://fortee.jp/yapc-fukuoka-2025/proposal/4dd02c14-92fc-4fd2-b93f-5b38463ec32a" title="競馬で学ぶ機械学習の基本と実践">競馬で学ぶ機械学習の基本と実践</a></li> <li><a href="https://fortee.jp/yapc-fukuoka-2025/proposal/f73df5fa-179c-4fb3-80a8-5ba78f5a6832" title="旧から新へ: 大規模ウェブクローラのPerlからGoへの置き換え">旧から新へ: 大規模ウェブクローラのPerlからGoへの置き換え</a><br /> YAPC の特徴として、特定の技術に閉じすぎず、技術と向き合う姿勢を語るセッションが多いのも魅力でした。</li> </ul> <h3>参加メンバーの感想</h3> <p>(kgoro) 今回は「iPhone のマイナンバーカードを使った本人確認」というテーマで採択いただき発表させていただきました。発表について多くのご質問をいただき、個人的にも iPhone のマイナンバーカードについて改めて深く考える貴重な機会になりました。<br /> 聴講した発表に関しては、「<a href="https://fortee.jp/yapc-fukuoka-2025/proposal/055b33be-0474-4c30-91ca-99985adbc080" title="クレジットカードの不正を防止する技術">クレジットカードの不正を防止する技術</a>」という発表が特に印象に残りました。こちらは普段扱う領域に少なからず関連する技術ですが、直接的に扱う機会が少なかったため、非常に学びになる内容でした。また、今後これらの技術を考えるうえでの後押しにもなったと感じています。<br /> 全体を通して、この度のカンファレンスは遠方かつトークの内容もさまざまなトピックがあったことから自分にとって新鮮な経験となりました。</p> <p>(myuto) 普段は機械学習まわりの開発をしていますが、分野関係なくいろんなバックグラウンドの人が集まるイベントなので、終始ゆるく楽しく過ごせました。技術系のカンファレンスって専門領域に寄りがちな印象があったけど、YAPCは良い意味で雑多で、それがすごく心地よかったです。<br /> 最新の生成AIの動向に対しても、いかにAIを使った上で開発や制作をうまく回すかといった見解や価値観もいろいろ発表や懇親会を通して伺うことができたのが楽しかったです。<br /> イベント全体の雰囲気が明るくて、参加者同士で気軽に話せるのが良かったです。普段あまり接点のない人たちとも技術の話で盛り上がれるのは、やっぱり現地イベントならではですね。スポンサーエリアも盛り上がっていて、各社の工夫が感じられて見て回るだけで楽しかったです。 </p> <h3>まとめ</h3> <p>YAPC は コミュニティ × 技術 × 熱量 が心地よく交わる、独特の魅力を持つイベントです。<br /> 福岡開催ということもあり、主催者の気合いの入り方も違いましたね。<br /> 運営・スポンサー・スピーカー・参加者のみなさん、本当にありがとうございました!<br /> 次回の YAPC も楽しみにしています!</p> <p>明日の記事はgoccyさんです。引き続きお楽しみください。</p> メルカリ ハロ Web フロントエンドの1年間の改善と学びhttps://engineering.mercari.com/blog/entry/20251205-mercari-hallo-frontend-improvements/https://engineering.mercari.com/blog/entry/20251205-mercari-hallo-frontend-improvements/<p>こんにちは。メルカリのフロントエンドエンジニアの @mattsuu です。 この記事は、Mercari Advent Calendar 2025 の5日目の記事です。 はじめに メルカリ ハロ Webフロントエンドの開発 [&hellip;]</p> Fri, 05 Dec 2025 11:00:41 GMT<p>こんにちは。メルカリのフロントエンドエンジニアの <a href="https://x.com/ryo_manba">@mattsuu</a> です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a> の5日目の記事です。</p> <h2>はじめに</h2> <p><a href="https://engineering.mercari.com/blog/entry/20240613-mercari-hallo-web-frontend/">メルカリ ハロ Webフロントエンドの開発スピードと品質両立の取り組み</a> という記事を公開してから約1年が経過しました。 前回の記事では、Next.js、Apollo Client、Tailwind CSS、そして monorepo 構成を採用し、MSW を使用したモック駆動開発などの技術スタックと開発手法について紹介しています。</p> <p>この1年間、私たちは初回の技術選定を基盤としながら、パッケージ管理、テスト戦略、開発体験の3つの領域に焦点を当てて継続的な改善を進めてきました。 本記事ではそれらの取り組みと効果について紹介します。</p> <h2>パッケージ管理の改善</h2> <p>monorepo 環境での依存関係管理とセキュリティ対策として、pnpm catalog による一元管理、minimumReleaseAge によるサプライチェーン攻撃対策、Knip による未使用パッケージの検出を導入しました。これらにより、依存関係の更新作業の効率化とセキュリティリスクの低減を実現しています。</p> <h3>pnpm catalog による一元管理</h3> <p>monorepo 環境では、同じライブラリの異なるバージョンを管理する場合があります。バージョンによって I/F が異なったり、セキュリティ update の対応が複数箇所で必要になるなど、対応コストが発生します。これを防ぐために各ライブラリで単一のバージョンを利用する方針を採用しました。</p> <p>pnpm の catalog 機能を使うことで、依存関係のバージョンを一箇所で管理することができます。</p> <pre><code class="language-yaml"># pnpm-workspace.yaml catalog: &#039;@apollo/client&#039;: 3.13.4 &#039;next&#039;: 15.3.2 &#039;react&#039;: 19.0.0 # ... その他の依存関係</code></pre> <p><code>pnpm-workspace.yaml</code> にカタログを定義し、各パッケージの <code>package.json</code> で <code>catalog:</code> を参照する運用により、同一ライブラリのバージョン分散を防いでいます。</p> <pre><code class="language-json">{ &quot;dependencies&quot;: { &quot;@apollo/client&quot;: &quot;catalog:&quot;, &quot;next&quot;: &quot;catalog:&quot;, &quot;react&quot;: &quot;catalog:&quot; } }</code></pre> <p>この仕組みにより、依存関係のバージョン更新は catalog 側を更新するだけで monorepo 全体へ反映されるため、個別のパッケージに対して複数 PR が発生する問題を解消できました。</p> <p>導入手順については次の記事を参考にしました。</p> <p><a href="https://tech.newmo.me/entry/one-version-rule-built-on-pnpm-catalog">monorepo内でのパッケージのバージョンを1つだけに統一するOne Version Ruleをpnpm catalogで実装する</a></p> <h3>Dependabot の活用</h3> <p>Dependabot を catalog と組み合わせることで、依存関係更新のワークフローが改善しました。Dependabot が catalog のバージョンを更新する PR を作成し、それをマージするだけで monorepo の依存関係を一括更新できます。</p> <p>運用面では導入して間もなかったこともあり、ナレッジ共有も踏まえて週1で担当をローテーションしていました。CI で自動テストが通り、PR に対して E2E テスト手動でトリガーして通過したらマージするようにしています。major update など破壊的変更があるものは別途チケットを切って対応していました。</p> <p>また、Dependabot の <a href="https://github.blog/changelog/2025-07-01-dependabot-supports-configuration-of-a-minimum-package-age/">cooldown 機能</a>を利用して、既存ライブラリの更新に一定の待機期間を設けていました。これにより、公開直後の不具合やセキュリティリスクを回避しやすくなりました。</p> <h3>minimumReleaseAge によるセキュリティ対策</h3> <p>前述の Dependabot の cooldown 機能は既存のライブラリアップデートに対するセキュリティ対策ですが、手元で新規ライブラリのインストールやアップデートを行う際には pnpm の <a href="https://pnpm.io/settings#minimumreleaseage"><code>minimumReleaseAge</code></a> が役立ちます。</p> <pre><code class="language-yaml"># pnpm-workspace.yaml minimumReleaseAge: 10080 # 1 week in minutes minimumReleaseAgeExclude: - &#039;@example/*&#039;</code></pre> <p>この設定により、公開されてから一定期間経過していないパッケージはインストールされません。 社内パッケージは <code>minimumReleaseAgeExclude</code> で除外し、開発の妨げにならないようにしています。また未導入ではありますが、pnpm の <a href="https://pnpm.io/settings#trustpolicy">trustPolicy</a> オプションを有効にすると、<a href="https://pnpm.io/supply-chain-security#enforce-trust-with-trustpolicy">サプライチェーン攻撃への対策</a>をさらに強化できます。</p> <h3>Knip による未使用パッケージの検出</h3> <p>プロジェクトが成長する中で、使われなくなった依存関係が残り続ける問題がありました。未使用のパッケージはバンドルサイズの増加やセキュリティリスクの原因になります。</p> <p><a href="https://knip.dev/">Knip</a> を導入することで、未使用のパッケージを自動検出できるようになりました。 CI では <a href="https://github.com/marketplace/actions/knip-reporter">knip-reporter</a> を利用していますが、現在は <a href="https://github.com/webpro-nl/knip/pull/1231">builtin の reporter が実装された</a>ため、そちらを使うと良いかもしれません。また、Knip は未使用のファイルや、export しているがどこからも import されていない型や関数も検出できるため、コードベースを健全に保つ上で効果的です。 </p> <p>詳しくは次の記事が参考になります。<br /> <a href="https://tech.basemachina.jp/entry/introduction-knip">TypeScript/JavaScriptの不要なコードを削除するツール「Knip」の紹介 &#8211; ベースマキナ エンジニアブログ</a> </p> <h2>テスト基盤の整備</h2> <p>テスト戦略として、全画面への Playwright による Integration Test の導入、Component Test の最小化、VRT の活用を進めました。Code Coverage よりも Use Case Coverage を重視し、テストの種類を絞ることでメンテナンスコストを削減しながら、安定したテスト基盤を整えることができました。</p> <h3>Playwright を用いた Integration Test と VRT の導入</h3> <p>1年前の時点では Integration Test の基盤が十分に整っておらず、軽微な機能改修でも QA エンジニアに詳細な確認を依頼する必要がありました。また、機能自体に問題がなくとも UI のリグレッションが発生した際に気づきにくい体制でした。</p> <p>この課題を解消するため、画面全体を対象とした Playwright による Integration Test を導入し、UI の差分検出には Playwright の VRT を活用しました。</p> <p>Integration Test の方針は、メルペイでのテスト自動化方針を参考に次の考えをベースとしています。</p> <blockquote> <p>基本的に、仕様書をベースにアプリケーションの振る舞いが期待通りかをテストします。実装の詳細は考慮しません。インテグレーションテストが仕様書と対応し、テストコードを見るとアプリケーション挙動がわかるように管理されると理想的です。<br /> 参考: <a href="https://engineering.mercari.com/blog/entry/20211208-test-automation-policy-in-merpay-frontend/#:~:text=%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AB%E3%80%81%E4%BB%95%E6%A7%98%E6%9B%B8%E3%82%92%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AB%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E6%8C%AF%E3%82%8B%E8%88%9E%E3%81%84%E3%81%8C%E6%9C%9F%E5%BE%85%E9%80%9A%E3%82%8A%E3%81%8B%E3%82%92%E3%83%86%E3%82%B9%E3%83%88%E3%81%97%E3%81%BE%E3%81%99%E3%80%82%E5%AE%9F%E8%A3%85%E3%81%AE%E8%A9%B3%E7%B4%B0%E3%81%AF%E8%80%83%E6%85%AE%E3%81%97%E3%81%BE%E3%81%9B%E3%82%93%E3%80%82%E3%82%A4%E3%83%B3%E3%83%86%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%86%E3%82%B9%E3%83%88%E3%81%8C%E4%BB%95%E6%A7%98%E6%9B%B8%E3%81%A8%E5%AF%BE%E5%BF%9C%E3%81%97%E3%80%81%E3%83%86%E3%82%B9%E3%83%88%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E8%A6%8B%E3%82%8B%E3%81%A8%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E6%8C%99%E5%8B%95%E3%81%8C%E3%82%8F%E3%81%8B%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E7%AE%A1%E7%90%86%E3%81%95%E3%82%8C%E3%82%8B%E3%81%A8%E7%90%86%E6%83%B3%E7%9A%84%E3%81%A7%E3%81%99%E3%80%82">メルペイフロントエンドのテスト自動化方針</a></p> </blockquote> <p>仕様書とテストコードを完全に一致させることは難しいものの、基本方針として Code Coverage よりも Use Case Coverage を重視する姿勢で運用していました。</p> <h4>テスト構成</h4> <p>テストコードの大枠とトップレベルの <code>describe</code> について次のような構成にしています。</p> <pre><code class="language-ts">/** * Specs: * https://example.com/specs/feature-abc // ① */ test.beforeEach(async ({ page }) =&gt; { await mockLogin(page); await mockAllGraphQL(page); // ② }); // 画面のパス const url = &#039;/sample-page&#039;; test.describe(&#039;Rendering&#039;, () =&gt; { test(&#039;renders correctly&#039;, async ({ page }) =&gt; { await mockGraphQL( // ③ page, SampleQueryDocument, produce(mockSampleQueryResponse(), (draft) =&gt; { // ... 必要に応じてレスポンスを調整 }) ); await page.goto(url); // ④ // ... レンダリング確認 }); }); test.describe(&#039;Validation&#039;, () =&gt; {}); test.describe(&#039;Actions&#039;, () =&gt; {}); </code></pre> <p>① 仕様へのリンクを記述する<br /> ② <code>mockAllGraphQL</code> を利用してすべての API リクエストをモックする<br /> ③ 個別ケースは <code>mockGraphQL</code> でレスポンスを上書きする</p> <ul> <li>トップレベルの <code>beforeEach</code> 内で <code>mockGraphQL</code> を利用することは避け、対象のテストと近い場所で呼び出す</li> <li>多少冗長でもモック内容とテスト内容の関連が明確になることを優先する</li> </ul> <p>④ モック設定完了後に <code>page.goto</code> を実行し、テスト対象の画面に遷移する</p> <p>トップレベルの <code>describe</code> については次の3つに分類しています。</p> <ul> <li><strong>Rendering</strong>: レスポンスに基づいて情報を正しくレンダリングできるか</li> <li><strong>Validation</strong>: 無効なリクエストが送信されるのを防げるか</li> <li><strong>Actions</strong>: 利用者の操作に応じて、正しいリクエストを生成し、レスポンスを適切に処理できるか(更新、エラー、ページ遷移を含む)</li> </ul> <p>記述ルールを明確にしたことで一貫した実装が可能になり、monorepo で複数メンバーがそれぞれのアプリケーションや機能を担当する場合でも、テストを書く負荷を減らすことができました。また、これらのルールを Claude.md にまとめておくことで、AI によるセルフレビューやテスト生成の精度も向上し、開発体験の改善につながりました。</p> <h3>Component Test 最小化戦略</h3> <p>テストの種類とバランスを考えたとき、私たちは次の方針を採用しました。</p> <ul> <li><strong>Integration Test (Playwright)</strong>: ブラウザ上で実際のユースケースを検証し、全画面をカバーする</li> <li><strong>Component Test (React Testing Library)</strong>: DatePicker など複雑な UI コンポーネントのみに限定する</li> <li><strong>Unit Test (Jest)</strong>: ロジック部分を対象にカバレッジ 80% 程度を維持する</li> </ul> <p>テストの種類を絞ることで、チームメンバーが学ぶべき範囲を限定し、テストの重複排除などメンテナンスを削減することができました。</p> <p>Integration Test に寄せた構成では CI のパフォーマンスが気になるところですが、<a href="https://playwright.dev/docs/test-sharding">Playwright の sharding</a> で4並列にテストを実行させることで実行時間が10分程度に抑えられるようにしています。</p> <p>また、Playwright にはテスト計画生成・テスト生成・自動修復を行う <a href="https://playwright.dev/docs/test-agents">Test Agents</a> が用意されており、将来的にテスト運用をより効率化できる可能性があります。QA チームが管理する E2E テストでも Playwright を採用していたため、チーム間でツールが統一されている点もメリットでした。</p> <h3>VRT (Visual Regression Testing) の活用</h3> <p>モバイル向け Web UI は事業者向け管理画面と比べて画面あたりの情報量が少なく、UI の変化を見つけやすいという特徴があります。また、UI 検知に必要なモックも少なくて済むため、実装やメンテナンスの負担を抑えられます。そのため、モバイル向け UI では Rendering のテストを VRT 中心にし、事業者向け管理画面では text や element の assertion を用いる構成にしていました。VRT には Playwright の <a href="https://playwright.dev/docs/screenshots">screenshot 機能</a>を利用して全画面実装しました。</p> <pre><code class="language-ts">test(&#039;should render page correctly&#039;, { tag: &#039;@vrt&#039; }, async ({ page }) =&gt; { await mockGraphQL( page, ExampleDocument, produce(mockExampleResponse(), (draft) =&gt; { // UI の確認に使うデータは immer で上書きする draft.data.item.name = &#039;テストアイテム1&#039;; }) ); await page.goto(&#039;/example&#039;); await expect(page).toHaveScreenshot({ fullPage: true }); }); </code></pre> <p>これまでは開発環境でデザイナーに UI を直接確認してもらう必要がありましたが、VRT の導入により特定条件の UI を Snapshot で比較・確認できるようになり、確認作業の工数を削減できました。</p> <h2>開発体験の向上</h2> <p>開発効率と品質を向上させるため、ESLint カスタムルールによる品質担保、Mock の自動生成、Claude Code Action によるセルフレビュー、Apollo MCP によるテストデータ作成の効率化といった取り組みを行いました。これらにより、レビュー負荷の軽減や開発スピードの向上につながりました。</p> <h3>ESLint カスタムルールによる品質担保</h3> <p>実装漏れが原因で QA 段階で不具合が見つかった場合には、修正と合わせて ESLint のカスタムルールを追加していました。チーム固有のルールをドキュメントや Claude.md / Agents.md にまとめる方法もありますが、静的解析によってルールを強制するほうがヒューマンエラーを防ぎやすく、品質担保に効果的です。</p> <p>カスタムルールの追加には、次の記事が参考になります。</p> <ul> <li><a href="https://zenn.dev/nissy_dev/scraps/ca7137375da3aa">no-restricted-syntax でお手軽にカスタムルールを追加する &#8211; Zenn</a></li> <li><a href="https://zenn.dev/ubie_dev/articles/a5c8fb2d219258">Ubie における ESLint 活用 &#8211; Zenn</a></li> </ul> <h3>Mock 自動生成の仕組み</h3> <p>GraphQL スキーマが更新されるたびに手動で Mock データを更新するのは負担が大きいため、<a href="https://the-guild.dev/graphql/tools/docs/mocking">@graphql-tools/mock</a> を利用した Mock データの自動生成を導入しました。スキーマが更新されたら、pnpm でスクリプトを実行するだけで Mock データを更新できます。</p> <pre><code class="language-shell"># GraphQL スキーマから型と Mock データを生成 pnpm generate:graphql</code></pre> <p>GraphQL Code Generator のカスタムプラグインを作成し、<code>@graphql-tools/mock</code> の <code>addMocksToSchema</code> を使って Mock データを生成しています。</p> <pre><code class="language-ts">import { addMocksToSchema } from &#039;@graphql-tools/mock&#039;; const mockedSchema = addMocksToSchema({ schema: graphqlSchema, mocks: { // 型に応じて初期値を設定 ID: () =&gt; &#039;mock-id-0001&#039;, String: () =&gt; &#039;Hello World&#039;, Int: () =&gt; 10, Boolean: () =&gt; true, Time: () =&gt; &#039;2021-01-01T00:00:00.000Z&#039;, }, }); // スキーマに対してクエリを実行し、Mock データを生成 const result = executeSync({ schema: mockedSchema, document: queryDocument, }); </code></pre> <p>この <code>result</code> を使って、次のように mock 関数を自動生成します:</p> <pre><code class="language-ts">const mockFn = ` export function mockExampleResponse(): ExecutionResult&lt;ExampleQuery&gt; { return ${JSON.stringify(result, null, 2)}; } `;</code></pre> <p>この仕組みにより、GraphQL スキーマから適切な Mock データが自動生成されるため、手動で値の整合性を確認する手間が大きく削減されました。</p> <p>また、特定のケースを再現したい場合は、immer を使って必要な部分のみ上書きできます。</p> <pre><code class="language-ts">import { produce } from &#039;immer&#039;; import { mockExampleResponse } from &#039;example-mockdata/base/gen/query&#039;; const customMock = produce(mockExampleResponse(), (draft) =&gt; { // 特定の条件を再現するためにデータを上書き draft.data.items[0].status = &#039;active&#039;; draft.data.items[0].count = 10; });</code></pre> <p>このように、意図的に上書きした箇所がそのまま Storybook やテストで確認したい条件として明確に残るため、Mock の管理や UI・テスト確認がシンプルになりました。</p> <h3>Claude Code Action によるセルフレビュー</h3> <p><a href="https://github.com/anthropics/claude-code-action">claude-code-action</a> を導入することで、PR レビューを依頼する前の draft の段階で実装者自身が抜け漏れに気づけるようになりました。レビュワーが指摘する前に基本的なミスを修正できるため、レビュワーの負担削減や品質の向上につながりました。</p> <h3>Apollo MCP によるテストデータ作成の効率化</h3> <p>ローカルで開発する際は、API が未実装の場合を除き、基本的にバックエンドと実際に疎通しながら進めていました。モックで開発する手もありますが、実際の API と通信するほうが挙動の再現性や信頼性が高いためです。一方で、特定の条件を再現するためのテストデータ作成には課題がありました。開発環境の管理画面を操作したり、場合によっては DB を直接触る必要があり、準備に手間がかかりがちでした。</p> <p>そこで <a href="https://www.apollographql.com/docs/apollo-mcp-server">Apollo MCP</a> を導入し、テストデータを口語ベースで簡単に作成できるようにしました。Apollo MCP は GraphQL のスキーマやクエリを操作して実行できるため、次のようなことが可能になります。</p> <ul> <li>「今日の18:00 ~ 20:00 で時給1200円の募集を北海道で作って」のような指示をすると、該当する mutation を実行しテストデータを作成</li> <li>GraphQL スキーマを自動で読み取り、どんなクエリや mutation が使えるかをで一覧・検索</li> <li>特定のクエリに必要な引数を口語形式で検索</li> </ul> <p>これにより、ローカルやテスト環境でのデータ準備が大幅に効率化され、手動テストの工数削減や開発スピードの向上につながりました。</p> <h2>おわりに</h2> <p>この1年間、日々の開発の中で出てきた課題に向き合いながら、少しずつ仕組みを整えてきました。All for One のカルチャーのもと、メンバーがそれぞれの専門性を活かしつつ、知見を共有しながら開発できたことが改善を進める上で良い循環になっていたと思います。本記事で紹介した取り組みが参考になれば幸いです。</p> <p>明日の記事は sathiya さんです。引き続きお楽しみください。</p> メルコインとメルカリNFTのフロントエンド開発のインターンシップで取り組んだことhttps://engineering.mercari.com/blog/entry/20251202-5f1fdbecce/https://engineering.mercari.com/blog/entry/20251202-5f1fdbecce/<p>はじめに こんにちは。 @Sakamoto です。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の4日目の記事です。 2025年9月からメルコインでフロントエンドのイン [&hellip;]</p> Thu, 04 Dec 2025 10:00:02 GMT<h2>はじめに</h2> <p>こんにちは。 @Sakamoto です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の4日目の記事です。</p> <p>2025年9月からメルコインでフロントエンドのインターンを始め、12月初めでちょうど3か月になりました。期間中はメルコインに加え、メルカリNFTの開発にも参加しました。<br /> 本記事では、インターン期間中に取り組んだタスクを振り返り、そこで得た学びや気づきをまとめます。</p> <h2>チームについて</h2> <p>今回のインターンではメルコインとメルカリNFTでフロントエンドエンジニアとして参加しました。それぞれ扱うプロダクトの特性が大きく異なるため、求められる視点や進め方にも違いがありました。</p> <h3>メルコイン</h3> <p>暗号資産交換業としてガバナンスやリーガルと関わりが深く、単に機能を実装するだけでなく、背景となる法律や制約を理解したうえで開発を進める必要があります。<br /> メルコインのフロントエンドチームはお客さま対応のための社内ツールのフロントエンド開発、LP(ランディングページ) の作成などを担当しています。</p> <h3>メルカリNFT</h3> <p>NFTの売買を可能にするプロダクトを持つチームで、2025年1月に提供を開始し、現在も毎週のように新機能が追加されています。<br /> そのため、よりスピード感のある開発が求められる環境でした。<br /> メルカリNFTのフロントエンドチームは主にメルカリNFTのフロントエンド開発を担当しています。</p> <h2>取り組み概要</h2> <h3>メルコイン</h3> <ul> <li><strong>社内ツールのフロントエンド改修</strong></li> </ul> <h3>メルカリNFT</h3> <ul> <li><strong>買取機能の実装・改善</strong></li> <li>メルカリNFTのくじをメルカリアプリ内から購入できるようにするための対応</li> <li><strong>リリース後のメモリ監視</strong></li> </ul> <h3>その他</h3> <ul> <li><strong>Web3領域のドメイン知識の習得</strong></li> <li><strong>社内の多職種の方とのコミュニケーション</strong></li> <li><strong>エンジニア業務でのAI活用</strong></li> </ul> <p>ここでは太字の6つについてまとめようと思います。</p> <h2>社内ツールのフロントエンドの改修</h2> <h3>背景</h3> <p>メルコインで進行しているプロジェクトの一部として、口座の状況、暗号通貨の配信価格など、お客さまからのお問い合わせに必要な情報を確認できる社内ツールの改修を担当し、フロントエンドの仕様整理から実装、QAチームとのテストケースの調整まで、一連の開発プロセスに関わりました。</p> <h3>やったこと</h3> <p>社内ツールの改修にあたって、</p> <ul> <li>表記揺れがないか</li> <li>バリデーションに過不足はないか</li> <li>エラーハンドリングが適切か</li> <li>UIの整合性があるか</li> <li>API連携が正しいか</li> <li>変更に柔軟に対応できる設計か</li> </ul> <p>といったポイントを確認し、後工程での手戻りが起きにくい状態まで仕様書のレビューを行いました。<br /> その上で自分の実装スピードや改修範囲を考慮し、適切なバッファを含めて工数見積もりを行いました。</p> <p>実装では、各マイクロサービス間の通信にはgRPCが使われているため、Protocol Buffersの定義からモックを作り、UIとの接続を進めると同時にCypressでのE2Eテストも作成・改修に取り組むことができました。<br /> またQAチームとはテストケースや指摘内容を確認しながら、仕様と実装の齟齬をなくすことに努めました。</p> <h3>学び</h3> <p>gRPCやProtocol Buffers、Cypressといったこれまで触れる機会の少なかった技術に取り組んだことで、フロントエンド以外の領域にも一部理解が広がりました。<br /> あわせて、仕様書の精度をどう高めるか、どの程度のバッファを見込んで工数を組み立てるか、レビューされやすいコードにするにはどんな書き方がよいかなど、プロダクト開発全体を俯瞰する視点も身についたと感じています。</p> <h2>買取機能の実装・改善</h2> <h3>背景</h3> <p>メルカリNFT内のGMV(流通取引総額)を伸ばすための施策として、NFTくじを購入したお客さまが買取依頼を出せる機能の追加をしました。<br /> それに伴い、商品詳細ページに新しいUIや状態管理が必要となり、この部分の改修を担当しました。<br /> また、このタスクがメルカリNFTのコードを触る最初の機能開発だったため、既存コードや周辺仕様のキャッチアップを素早く進める必要もありました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/64217c39--2025-11-26-17.54.02.png" alt="" /></p> <h3>やったこと</h3> <p>仕様書を確認し、商品詳細ページのどのタイミングで買取ボタンを表示するか、どの状態をどう見せるかといったUIの出し分けを整理しました。<br /> その上で、実装した内容の品質を担保するためにVRT(Visual Regression Test)を活用し、Playwrightを用いたインテグレーションテストも追加・修正しました。</p> <h3>学び</h3> <p>Playwrightを使ったインテグレーションテストやVRTの活用など、これまで触れる機会のなかったテスト手法を実際に使いながら理解を深められました。<br /> また、買取前後でUIが複雑に変化する仕様だったこともあり、コンポーネントの責務をどう分けるか、保守性と可読性をどこまで両立できるか、といった設計面の学びも多かったです。<br /> スピードが求められる環境の中でも、既存実装の調査や理解を並行して進めることの重要性を実感しました。</p> <h2>リリース後のメモリ監視</h2> <h3>背景</h3> <p>メルカリNFTでは、サービスを安定して提供し続けるために、リリース後のリソース状況の監視や挙動の確認を継続的に行っています。<br /> 特にメモリ使用量は、トラフィックの増減や特定機能の利用状況によって変動しやすいため、定期的に観測し、必要に応じて挙動を調査する運用を行っています。</p> <p>今回の取り組みでは、本番環境のメモリ使用状況をより細かく把握し、どのタイミングでどう増減しているのかを整理しながら、今後の安定した運用につなげるための調査を進めています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/6d4bb4c7--2025-11-28-17.56.01.png" alt="" /></p> <h3>やったこと</h3> <p>まずは「いつ・どんな状況でメモリが増えるのか」を把握するために、各Podのメトリクスを確認し、傾向を調査しました。<br /> その上で、なぜそのような増加が起こるのか仮説を立て、順に検証を進めました。</p> <p>Datadogからログやメトリクスを細かく確認しつつ、他チームにも相談し、Node.jsやNext.jsの内部挙動、キャッシュやSSR周りの可能性など、さまざまな観点から情報を集めることで検証の方向性を調整しています。</p> <h3>学び</h3> <p>日々の調査の中で得られる学びは非常に多いと感じています。<br /> Node.jsやNext.jsが内部でどのようにメモリを使うのか、CPU・GCの仕組み、キャッシュの扱いなど、技術的な理解が大きく深まりました。</p> <p>同時に、進行中の問題を扱う際のコミュニケーションの取り方や調査内容のまとめ方、仮説の立て方と検証の進め方、進捗が見えにくいタスクをどうやって周囲と共有するかなど、機能開発とは異なるスキルも求められることを実感しています。</p> <h2>Web3領域のドメイン知識の習得</h2> <p>メルコインとメルカリNFTの両方に関わる中で、Web3に関する基礎知識を継続的に身につけることを意識してきました。<br /> 暗号資産やNFTの基本概念をはじめ、法規制やコンプライアンス、チェーンの仕組み、トランザクションの流れ、ウォレットの挙動など、業務に必要な知識は社内ドキュメントとして多く整理されています。</p> <p>日々の開発と並行して、それらの資料を自分から探して読み、背景となるドメイン理解を深めるようにしてきました。<br /> Web3は学ぶ範囲も広く、継続して取り組みたい領域です。</p> <h2>社内の多職種の方とのコミュニケーション</h2> <p>もう一つ意識的に取り組んでいたのが、社内の多職種の方とのコミュニケーションです。<br /> プロダクト開発では、さまざまな職種と連携しながら仕様の確定やリリースに向けた調整を進める必要があります。</p> <p>そのため、日々のやり取りだけでなく、会社の施策として実施されているチービルやメンターランチの機会を活用しつつ、自分からも声をかけて1on1やランチの機会をつくるようにしていました。<br /> チーム全体が話しかけやすい雰囲気を持っていたこともあり、積極的にコミュニケーションを取ることができました。</p> <p>また、普段どのように技術をキャッチアップしているのか、どんな観点で仕様を見ているのか、なぜ今のキャリアを選んだのかといった話を聞くことで、自分の知らなかった考え方や知識に触れる機会も多くありました。<br /> 開発スキルだけでは得られない学びが多く、視野を広げるきっかけになったと感じています。</p> <h2>エンジニア業務でのAI活用</h2> <p>CursorなどのAIツールも積極的に活用し、コード全体の構造を素早く把握したり、関連部分の洗い出しを効率よく進めるようにしていました。<br /> 開発・実装では、メンターの方からのアドバイスも参考にしながら、どの情報を渡すと意図が正確に伝わるかといった点を意識するようにしました。</p> <p>仕様を明確にまとめてコンテキストとしてAIに渡すことで、実装の方向性がぶれにくくなり、提案されるコードの品質も安定するようになりました。<br /> また、AIから返ってくるコードはそのまま使うのではなく、自分の考えとの違いを確認したり、なぜその書き方になるのかを読み解くことで理解を深めるきっかけにもなりました。</p> <p>結果として、実装を効率化するだけでなく、コードの設計方針を比較しながら学べる良い教材のような存在にもなっていたと思います。</p> <h2>まとめ</h2> <p>3か月を通して、メルコインとメルカリNFTのそれぞれ異なる特徴を持つプロダクトの開発に携わり、多くの学びを得ることができました。<br /> 機能実装だけでなく、仕様の詰め方、テストの考え方、多職種とのコミュニケーションなど、エンジニアとして必要なスキルを幅広く経験できた期間だったと感じています。</p> <p>残りの1か月も、これまでの学びを活かしながら、より深い理解と確かなアウトプットを積み重ねていきたいと思います。</p> <p>明日の記事は @Fabさんです。引き続きお楽しみください。</p> Kubeflow PipelinesとPydantic Settingsを活用してMLパイプラインを型安全かつシンプルに実装するhttps://engineering.mercari.com/blog/entry/20251128-c46fa95168/https://engineering.mercari.com/blog/entry/20251128-c46fa95168/<p>はじめに こんにちは。メルペイのGrowth PlatformでML Engineerをしている @hokao です。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の3 [&hellip;]</p> Wed, 03 Dec 2025 10:00:47 GMT<h2>はじめに</h2> <p>こんにちは。メルペイのGrowth PlatformでML Engineerをしている <a href="https://x.com/khokawo">@hokao</a> です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の3日目の記事です。</p> <h2>背景</h2> <p>Growth Platformでは、新たにGrowth MLというMLチームを立ち上げました。私は主にMLOpsを担当しつつ、チームの生産性向上に向けた基盤づくりにも取り組んでいます。</p> <p>Growth MLチームでは、<a href="https://www.kubeflow.org/docs/components/pipelines/">Kubeflow Pipelines(KFP)</a>でMLバッチ処理を実装し、<a href="https://docs.cloud.google.com/vertex-ai/docs/pipelines/introduction">Vertex AI Pipelines</a>上で運用しています。KFPは、Kubernetes上でコンテナを使ってMLワークフローを構築・実行するためのプラットフォームであり、Vertex AI PipelinesはそれらをGoogle Cloud上でサーバーレスに運用・管理できるサービスです。KFPには<a href="https://kubeflow-pipelines.readthedocs.io/en/stable/">Python SDK</a>が用意されており、パイプラインをPythonコードとして記述できます。</p> <p>KFPはPythonでMLパイプラインを実装できる一方で、コンポーネントの設計・実装の自由度が高く、適切に使いこなすのは容易ではありません。そこで、メンテナンス性と可読性を高め、チームの開発生産性を向上させるために、KFPを用いた実装パターンを整備しました。本稿では、その概要とポイントを紹介します。</p> <h2>KFPのコンポーネント実装パターン</h2> <p>コンポーネントはパイプラインを構築する基本的な構成要素です。執筆時点では、コンポーネントの実装方法として次の3パターンが提供されています。</p> <p></p> <ol> <li><a href="https://www.kubeflow.org/docs/components/pipelines/user-guides/components/lightweight-python-components/">Lightweight Python Components</a></li> <li><a href="https://www.kubeflow.org/docs/components/pipelines/user-guides/components/containerized-python-components/">Containerized Python Components</a></li> <li><a href="https://www.kubeflow.org/docs/components/pipelines/user-guides/components/container-components/">Container Components</a></li> </ol> <h3>Lightweight Python Components</h3> <p>Lightweight Python Componentsは最も手早く実装できますが、関数スコープに完全に閉じた実装が求められるという制約があります。実装例は次のとおりです。</p> <pre><code class="language-python">from kfp import dsl @dsl.component( base_image=&#039;python:3.14&#039;, packages_to_install=[&#039;numpy==2.3.0&#039;], ) def sin(val: float = 3.14) -&gt; float: import numpy as np return np.sin(val).item()</code></pre> <p><code>packages_to_install</code>で指定したライブラリはパイプライン実行時にインストールされ、その後に関数のコードが実行されます。依存ライブラリは関数ごとに管理する必要があり、MLバッチ全体で用いる特定ライブラリのバージョンを統一することが困難です。また、ライブラリのインポートや使用するシンボルの定義を関数内に閉じ込める必要があるため、関数の責務が容易に肥大化し、単体テストの記述も難しくなります。さらに、KFPが担うコンポーネント定義などのパイプラインのオーケストレーションと、ドメイン固有のビジネスロジックが密結合になりやすく、コードの見通しも損なわれます。これらの特性から、プロダクション用途には向きません。</p> <h3>Containerized Python Components</h3> <p>Containerized Python Componentsは前述のLightweight Python Componentsに近い書き方ですが、関数スコープに閉じるという制約が一部緩和されます。コードと依存関係を含むコンテナイメージを事前にビルドすることで、関数外で定義したシンボルも参照できます。</p> <p>しかし、関数ごとに依存ライブラリを定義する必要がある点は同じです。さらに、KFPのCLIが特定のディレクトリ構造に依存して自動生成する<code>runtime-requirements.txt</code>と<code>Dockerfile</code>を前提とするため、柔軟性に欠けます。したがって、これらの制約を踏まえた上で利用する必要があります。</p> <h3>Container Components</h3> <p>3つ目のContainer Componentsは、<code>docker run</code>のようにイメージ、コマンド、引数を指定してコンポーネントを定義する方式です。</p> <pre><code class="language-python">from kfp import dsl @dsl.container_component def say_hello(name: str) -&gt; dsl.ContainerSpec: return dsl.ContainerSpec( image=&#039;gcr.io/my-project/my-base-image:latest&#039;, command=[&#039;python&#039;, &#039;main.py&#039;], args=[&#039;--name&#039;, name], )</code></pre> <p>パイプラインで用いるコードとライブラリを含んだイメージを用意する必要がありますが、依存関係の管理がはるかに容易になります。実行時にライブラリをインストールする必要もありません。また、実行するPythonスクリプトはKFP特有の制約を受けない通常のスクリプトとして実装できるため、テストも書きやすくなります。</p> <p>こうした理由から、Growth MLチームでは原則としてContainer Componentsでコンポーネントを実装することとしました。</p> <h2>Container ComponentsでのCLI実装とその課題</h2> <p>Container Componentsで実行コマンドを指定するため、処理ロジックはPythonのCLIとして実装するのが自然です。CLIコマンドは<code>docker run</code>のように実行されるため、引数としてリストや辞書などの複雑な引数を渡すときは注意が必要です。</p> <p>たとえば、次のようなパイプラインを用意したとします。実行するコンポーネントは1つだけで、CLI引数としてPythonのリストを渡します。</p> <pre><code class="language-python">from kfp import dsl, local @dsl.container_component def argparse_component( list_var: list, # KFP does not support the `list[str]` type annotation. ) -&gt; dsl.ContainerSpec: return dsl.ContainerSpec( image=&#039;kfp-demo:latest&#039;, command=[&#039;python&#039;, &#039;-m&#039;, &#039;cli.argparse_cli&#039;], args=[&#039;--list-var&#039;, list_var], ) @dsl.pipeline def pipeline() -&gt; None: argparse_component(list_var=[&#039;a&#039;, &#039;b&#039;, &#039;c&#039;]) def main() -&gt; None: # Using local Docker runner for demonstration local.init(runner=local.DockerRunner()) pipeline() if __name__ == &#039;__main__&#039;: main()</code></pre> <p>実行するCLIは次のとおりです。標準ライブラリの<code>argparse</code>で実装しており、リストを受け取り、<code>print</code>関数でそのまま出力するだけのシンプルなものです。</p> <pre><code class="language-python">import argparse def main() -&gt; None: parser = argparse.ArgumentParser() parser.add_argument(&#039;--list-var&#039;, nargs=&#039;+&#039;, required=True) args = parser.parse_args() print(args.list_var) if __name__ == &#039;__main__&#039;: main()</code></pre> <p>CLI引数として渡しているのは<code>[&#039;a&#039;, &#039;b&#039;, &#039;c&#039;]</code>なので、<code>print</code>の結果も<code>[&#039;a&#039;, &#039;b&#039;, &#039;c&#039;]</code>となってほしいところです。しかし実際には、<code>[&#039;[&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]&#039;]</code>と出力されます。</p> <p>Container Componentsはほぼ<code>docker run</code>と同じ仕組みで動作するため、引数を受け渡す際に変数の値がシリアライズ(文字列化)されます。そのため、実際の<code>docker run</code>コマンドで渡される引数は文字列<code>&#039;[&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]&#039;</code>になります。また、先ほどの<code>argparse</code>の実装では、スペース区切りで渡された複数の値をリストとして扱う仕様になっています。結果として、この文字列全体が1要素として扱われ、<code>[&#039;[&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]&#039;]</code>というリストになってしまいます。この挙動は、<a href="https://click.palletsprojects.com/">Click</a>や<a href="https://typer.tiangolo.com/">Typer</a>など他のCLIライブラリでも同様です。</p> <p>この問題を単純に解決しようとすると、ひとまず引数を文字列(<code>str</code>)として受け取り、CLI側でデシリアライズするという実装が考えられます。ただ、それだと本質ではない処理が増えてコードの見通しが悪くなり、なにより型の安全性が担保されません。</p> <p>引数が<code>int</code>や<code>bool</code>だけで済むならシンプルで理想的ですが、Pythonだけでパイプラインを書く以上、<code>list</code>や<code>dict</code>といった構造化データを引数にしたいケースはどうしても発生します。</p> <h2>Pydantic Settingsで型安全かつシンプルにCLI引数を扱う</h2> <p>いくつかのアプローチを検討・試行した結果、<a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/">Pydantic Settings</a>を使う方法が、これらの課題を最もシンプルに解決できることが分かりました。</p> <p>Pydantic Settingsは、<a href="https://docs.pydantic.dev/latest/">Pydantic</a>の型定義を使って環境変数や<code>.env</code>などから設定を型安全に読み込めるライブラリです。さらに、<code>SettingsConfigDict(cli_parse_args=True)</code>を有効にすればCLI引数もPydanticモデルでパースできます。</p> <p>先ほど<code>argparse</code>で実装したCLIをPydantic Settingsで書き直すと次のようになります。</p> <pre><code class="language-python">from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config = SettingsConfigDict(cli_parse_args=True) list_var: list[str] = Field(alias=&#039;list-var&#039;) def main() -&gt; None: settings = Settings() print(settings.list_var) if __name__ == &#039;__main__&#039;: main()</code></pre> <p>Pydanticは入力値を指定した型にパースし、その出力がその型に一致することを保証します。Pydantic SettingsでCLI引数をパースする場合、<code>list</code>や<code>dict</code>などはJSON形式をサポートしているため、KFPのContainer ComponentsでシリアライズされたCLI引数(JSON文字列)も適切にパースしてくれます。その結果、わざわざデシリアライズ処理を書く必要がなく、型安全性も自然に確保されます。</p> <h2>複雑な構造体もPydantic Settingsで型安全に扱う</h2> <p><code>argparse</code>など標準的なCLIライブラリでは<code>dict</code>などをそのまま引数型として扱えませんが、Pydantic SettingsならJSON文字列をモデルにマッピングできるので、簡単かつ型安全に扱えます。</p> <p>ここでは少し応用的な例として、KFPの<a href="https://kubeflow-pipelines.readthedocs.io/en/stable/source/dsl.html#kfp.dsl.PipelineTaskFinalStatus"><code>dsl.PipelineTaskFinalStatus</code></a>を使って実行タスクの最終状態を受け取る方法を紹介します。使い方はシンプルで、コンポーネントに<code>PipelineTaskFinalStatus</code>の型アノテーションを付けた引数を追加するだけです。変数に自動的に値が代入されるようになっているため、コンポーネントの関数を呼び出す際にこの引数を指定する必要はありません。公式ドキュメントにはContainer Componentsでの具体例はありませんが、Pydantic Settingsを使えば問題なく扱えます。</p> <pre><code class="language-python">from kfp import dsl @dsl.container_component def pipeline_task_final_status_component( base_image: str, pipeline_task_final_status: dsl.PipelineTaskFinalStatus, ) -&gt; dsl.ContainerSpec: return dsl.ContainerSpec( # The `image` argument must be cast to string. # ref: https://github.com/kubeflow/pipelines/issues/4433#issuecomment-2959874538 image=str(base_image), command=[ &#039;python&#039;, &#039;-m&#039;, &#039;cli.pydantic_settings_cli&#039;, ], args=[ &#039;--pipeline-task-final-status&#039;, pipeline_task_final_status, ], )</code></pre> <p><code>PipelineTaskFinalStatus</code>の変数をCLIの引数に指定すると、他の値と同様にJSON文字列としてシリアライズされてコンテナに渡されます。シリアライズ後の実体は単なるJSON文字列なので、対応するPydanticのモデルを定義しておけば、CLI側でそのまま型付きオブジェクトとして安全に扱うことができます。</p> <pre><code class="language-python">from typing import Literal from pydantic import BaseModel, Field from pydantic_settings import BaseSettings, SettingsConfigDict class Error(BaseModel): code: int | None = None message: str | None = None class PipelineTaskFinalStatus(BaseModel): error: Error pipelineJobResourceName: str pipelineTaskName: str state: Literal[&#039;SUCCEEDED&#039;, &#039;FAILED&#039;, &#039;CANCELLED&#039;] class Settings(BaseSettings): model_config = SettingsConfigDict(cli_parse_args=True) pipeline_task_final_status: PipelineTaskFinalStatus = Field( alias=&#039;pipeline-task-final-status&#039;, description=&#039;Pipeline task execution status (JSON format)&#039;, ) def main() -&gt; None: settings = Settings() print(settings.pipeline_task_final_status) if __name__ == &#039;__main__&#039;: main()</code></pre> <p>想定と異なる形式のデータが渡された場合、Pydanticは入力時点で検証し即座にバリデーションエラーを返します。そのため、ネストした<code>dict</code>や<code>list</code>などのやや複雑な構造のデータでも、Pydantic Settingsを使えば型に沿って安全に扱えます。</p> <p>Pydantic Settingsを併用することで、KFPのContainer Componentsの開発体験が大きく向上します。</p> <h2>おわりに</h2> <p>本稿では、KFPのコンポーネント実装パターンを整理し、Container ComponentsとPydantic Settingsを組み合わせることで、開発者がより簡単かつ型安全にパイプラインを構築できるアプローチを紹介しました。</p> <p>他にも、パイプラインの実行スケジュールを宣言的に記述し、<code>terraform apply</code>のように差分を確認・適用できる仕組みなど、運用を支えるさまざまな取り組みをしています。これらについても、また別の機会に紹介できればと思います。</p> <p>今回紹介した内容が、KFPを使った開発における設計で悩んでいる方の参考になれば幸いです。</p> <p>明日の記事は @Sakamoto さんです。引き続きお楽しみください。</p> LLM Key ServerによるLLM APIへの安全で便利なアクセス提供https://engineering.mercari.com/blog/entry/20251202-llm-key-server/https://engineering.mercari.com/blog/entry/20251202-llm-key-server/<p>こんにちは。メルカリのAI Securityエンジニアの@hi120kiです。 この記事は、Mercari Advent Calendar 2025 の2日目の記事です。 メルカリでは社内でのAI/LLM活用の拡大を目指 [&hellip;]</p> Tue, 02 Dec 2025 11:00:42 GMT<p>こんにちは。メルカリのAI Securityエンジニアの<a href="https://twitter.com/hi120ki">@hi120ki</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/">Mercari Advent Calendar 2025</a> の2日目の記事です。</p> <p>メルカリでは社内でのAI/LLM活用の拡大を目指し、様々な取り組みが行われています。これらの動きを後押しするために<a href="https://careers.mercari.com/mercan/articles/55843/">AI Securityチーム</a>はLLM APIへのアクセスを安全でありながら便利に提供するためのサービスLLM Key Serverを開発しました。</p> <p>これによりLLM APIへのアクセス申請を受け担当者が手作業でユーザーを登録していたプロセスがLLM Key Serverによって置き換えられ、LLM APIユーザーは申請の手間なく自身の社内アカウント経由で有効期限付きのLLM APIキーを取得できるようになりました。</p> <p>また、GitHub ActionsやGoogle Apps Script上でLLM APIを利用するための共通テンプレートも提供し、ローカル環境のみならずCIやクラウドやノーコードツール等の複数のサービス上でのLLM利用促進にも貢献しました。</p> <p>本記事ではLLM APIにおけるセキュリティ課題・プロセスの改善・LLM Key Serverのアーキテクチャ・実装におけるポイントについて解説します。</p> <h2>LLM APIにおけるセキュリティ課題</h2> <p>現在様々なLLMが各社から展開されており、メルカリでは複数のLLMをタスクごとの要求や従業員の好みごとに使い分けています。しかし、LLMへのアクセスを提供するAPIには多くの場合APIキーが必要となっています。</p> <p>主要なLLMベンダーが提供するAPIへのアクセスに使われるAPIキーは、有効期限がなく、流出しそれが検知されなかった場合、長い期間にわたって情報漏洩・経済的損失を受ける恐れがあります。さらに現在のAI/LLM活用の流れの中で、多くのAPIキーが発行されており、管理があいまいになってしまうことも懸念されています。また、複数社のLLMを利用する中でユーザー・チーム・権限管理が複雑になることが想定されます。このような複雑な管理体制は定期的なアクセス権の監査を難しくします。</p> <p>もちろんAPIキーを用いずWorkload Identityおよびクラウド間連携を用いてGoogle CloudやAzure経由でLLM APIにアクセスする方法が最も安全であり、推奨される方法として社内で提示しています。しかし一方で、設定の複雑さや、様々な外部のAI/LLMプロダクトがそれらをサポートせずリリースされることもあり、多くのLLM関連ツールを評価している状況下では別の方法が求められていました。</p> <p>また追加の要件として、セキュリティポリシーによって煩雑なフローを強制することはかえってポリシーの逸脱を助長しかねないため、安全性を保ちながら利便性も追求することが必要でした。</p> <h2>安全かつ便利なLLM APIへのアクセス提供</h2> <p>安全かつ便利なLLM APIへのアクセス手段を提供するため、複数のモデルを一つのAPIから利用可能にするOSSの<a href="https://www.litellm.ai/">LiteLLM</a>と、Google WorkspaceおよびGoogle Cloudの<a href="https://cloud.google.com/docs/authentication/get-id-token">OIDC IDトークン発行機能</a>を用いることにしました。</p> <p>LiteLLMは様々なモデルプロバイダーから提供されるLLMを一つのAPIから利用可能にするOSSで、基本的なLLM API呼び出しの他にClaude CodeなどのCoding Agentツールにも対応しています。</p> <p>また、OIDC IDトークン発行機能はGoogleのOAuthやサービスアカウントの権限を利用することでGoogleによって署名されたIDトークンを取得できるというもので、これを利用することでユーザーの身元を確認することができます。</p> <p>メルカリでは<a href="https://engineering.mercari.com/blog/entry/20241203-token-server-google-cloud/">Google CloudからGitHubへのアクセスを有効期限が短い認証情報へ切り替えるためのToken Server</a>を運用していますが、LLM Key ServerはこのアーキテクチャをベースにLLMへのアクセスへと発展させたものになります。</p> <h3>LLM Key Serverのアーキテクチャ</h3> <p>LLM Key Serverの認証フローは以下のようになっています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/287533cf-llmkeyserver.png" alt="" /></p> <p><center>LLM Key Serverの認証フロー</center></p> <p>まず、Claude Codeを利用したいユーザーやLLMを利用したいアプリケーションは、GoogleのAPIから自身を証明するためのOIDC IDトークンを取得します。ここではGoogle Workspaceアカウントによる認証や、Compute metadataサーバーからのサービスアカウント認証などが利用されます。</p> <p>次に、取得したOIDC IDトークンをLLM Key Serverに送信すると、LLM Key Serverはトークンの署名を検証し、トークン内の情報をもとにLiteLLMへアクセスするための一時的なAPIキーを発行します。このAPIキーは有効期限が短く設定されており、ユーザーはこのキーを用いてLiteLLM経由で様々なLLMへアクセスすることができます。</p> <p>ローカル環境での利用においてGoogle Workspaceアカウントによる認証を利用する際は、社内公開のCLIツールを利用することで1つのコマンドでOAuth認可フローを開始し、OIDC IDトークンの取得からLLM APIキーの取得までを行うことができます。</p> <p>またAPIキーはサービスアカウントに対しては1時間という短い有効期限が設定されています。しかし、クラウド上で動作しLLMを利用するアプリケーションが長時間動作することを想定し、自動的にキーを更新する仕組みも提供しています。これはGo言語のライブラリとして整備されており、自動的にキーの更新を行い、継続してLLM APIを利用することができます。</p> <p>このようにしてGoogle Workspaceアカウントによる認証やGoogle Cloud上のサービスアカウント認証を利用することで、安全にLLM APIへのアクセスを提供しつつ、有効期限付きのキーを発行することで情報漏洩リスクを低減し、さらに自動更新ライブラリを提供することで利便性も確保しました。</p> <h2>LLM Key Serverの利用形態の拡張</h2> <p>LLM Key Serverはローカル環境やクラウド上で動作するアプリケーションの利用だけでなく、様々な社内ツールやサービス上での利用も想定しています。特に以下の2つの形態での利用をサポートしています。</p> <h3>GitHub Actions</h3> <p>GitHub Actions上でLLM APIを利用するための共通テンプレートを提供しています。GitHub Actions上では<a href="https://docs.github.com/en/actions/concepts/security/openid-connect">GitHubから提供されるOIDC IDトークン</a>を用いてLLM Key ServerからLLM APIキーを取得し、LiteLLM経由で様々なLLMへアクセスすることができます。これにより、CI/CDパイプライン上でのLLM活用が促進されており、Claude Codeを用いたコードレビューが自動化されたりしています。</p> <pre><code class="language-yaml">- name: Get LiteLLM Key id: litellm uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const oidc_request_token = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN; const oidc_request_url = process.env.ACTIONS_ID_TOKEN_REQUEST_URL; const oidc_resp = await fetch(`${oidc_request_url}&amp;audience=https://key-server.example.com`, { headers: {Authorization: `bearer ${oidc_request_token}`}, }); const oidc_token = (await oidc_resp.json()).value; if (!oidc_token) { core.setFailed(&#039;Failed to retrieve OIDC token from GitHub Actions&#039;); } const res = await fetch(&#039;https://key-server.example.com/llm-key&#039;, { method: &#039;GET&#039;, headers: { &#039;Authorization&#039;: `Bearer ${oidc_token}`, &#039;Content-Type&#039;: &#039;application/json&#039;, } }); if (res.status !== 200) { core.setFailed(`LiteLLM API Error: HTTP ${res.status}`); } const body = await res.json(); core.setSecret(body.key); core.setOutput(&#039;token&#039;, body.key);</code></pre> <p>このようなGitHub Actionsのactionを用意することで、開発者が直接LLM APIキーを管理することなく、CI/CDパイプライン上で安全にLLM APIを利用できるようになりました。</p> <h3>Google Apps Script</h3> <p>Google Apps Script上でもLLM APIを利用するための共通テンプレートを提供しています。Google Apps Script上では<a href="https://developers.google.com/apps-script/concepts/scopes">OAuth scope設定</a>を用いてユーザーの認証を行い、OIDC IDトークンを取得することができます。</p> <p>Google Apps Scriptの設定ページから<code>appsscript.json</code>ファイルを表示するようにした後、以下のようにOAuth scopeを追加します。</p> <pre><code class="language-json"> &quot;oauthScopes&quot;: [ &quot;openid&quot;, &quot;https://www.googleapis.com/auth/userinfo.email&quot;, &quot;https://www.googleapis.com/auth/script.external_request&quot; ],</code></pre> <p>その後、Google Apps Script上で以下のようにOIDC IDトークンを取得し、LLM Key ServerからLLM APIキーを取得し、LiteLLM経由で様々なLLMへアクセスすることができます。</p> <pre><code class="language-javascript">function getLLMToken() { try { const cache = CacheService.getUserCache(); const cacheKey = &quot;llm_token&quot;; const cachedToken = cache.get(cacheKey); if (cachedToken) { return cachedToken; } console.log(&quot;[+] Fetching new LLM token&quot;); const token = ScriptApp.getIdentityToken(); const options = { method: &quot;GET&quot;, headers: { Authorization: &quot;Bearer &quot; + token, }, }; const response = UrlFetchApp.fetch( &quot;https://key-server.example.com/llm-key&quot;, options, ); const statusCode = response.getResponseCode(); if (statusCode !== 200) { throw new Error( `HTTP request failed with status ${statusCode}: ${response.getContentText()}`, ); } const responseText = response.getContentText(); const responseData = JSON.parse(responseText); if (!responseData.key) { throw new Error(&quot;Key not found in response&quot;); } cache.put(cacheKey, responseData.key, 50 * 60); // Cache for 50 minutes return responseData.key; } catch (e) { console.error(&quot;Error getting LLM token: &quot; + e.toString()); return null; } }</code></pre> <p>OIDCのIDトークンを検証する際には、ユーザーのメールアドレスだけでなく、Apps Scriptのバックエンドとして動作しているGoogle Cloudプロジェクトが、Google Cloud組織内の<code>system-gsuite/apps-script</code>フォルダに所属しているかどうかも確認しています。<br /> これにより、信頼できるスクリプトからのアクセスのみを許可するようにしています。</p> <p>これによりノーコードツール上での平文でのLLM APIキーの管理を避け、安全にLLM APIを利用することができます。</p> <p>この仕組みにより社内でのLLM活用が促進されており、社内ドキュメントの要約や翻訳などに利用されています。</p> <h2>さいごに</h2> <p>組織としてLLM APIキーを安全に扱うための仕組みとしてLLM Key Serverを開発しました。これにより安全性と利便性の両立を実現し、社内でのLLM活用をポジティブにSecurityチームから促進することができました。今後もAI Securityチームは安全で便利なAI/LLM活用のための取り組みを続けていきます。</p> <p>このようなメルカリでのAI/LLM活用やセキュリティに関する取り組みに興味がある方は、ぜひ<a href="https://careers.mercari.com/">メルカリの採用ページ</a>をご覧ください。</p> <p>明日の記事は@Jazzさんです。引き続きお楽しみください。</p> pj-double: メルカリの開発生産性向上に向けた挑戦 — AI-Native化が辿り着いたASDDとプロセス変革の全貌https://engineering.mercari.com/blog/entry/20251201-pj-double-towards-ai-native-development/https://engineering.mercari.com/blog/entry/20251201-pj-double-towards-ai-native-development/<p>こんにちは。メルペイ VPoE室マネージャーの @nnaakkaaii です。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の2日目の記事です。 はじめに この記事では [&hellip;]</p> Tue, 02 Dec 2025 10:00:05 GMT<p>こんにちは。メルペイ VPoE室マネージャーの <a href="https://www.linkedin.com/in/yu-nakai-swe">@nnaakkaaii</a> です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の2日目の記事です。</p> <h2>はじめに</h2> <p>この記事では、メルカリで2025年7月からスタートした取り組みであるProject Double(以下、pj-double)についてご紹介します。<br /> 「AIと協業する開発体験」「開発プロセスの再設計」「AI‑Nativeな組織づくり」に関心のあるエンジニア・PM・マネージャーの方にとって何かしらの参考になればと思い、本記事を執筆しました。</p> <p>pj-doubleは、プロダクト開発における生産性を2倍に向上させることをミッションに掲げ、開発体制・プロセスをAI-Nativeに再設計する挑戦です。しかしその背景には、私たちがAIの導入を試みる中で経験した数々の試行錯誤と、そこから得た気づきがありました。</p> <p>まずは、その前段として社内におけるAI活用の始まりと、初期フェーズでの課題から振り返ってみたいと思います。</p> <h2>2025年初頭: Vibe Codingの台頭と課題</h2> <p>2025年初頭は、AIを活用したコーディング手法の黎明期でした。社内でもさまざまなAIツールの活用、MCPサーバーの整備などがボトムアップに推進されました。それぞれの開発者が多種多様な方法論を模索する、いわゆる “Divergence” のフェーズでした。</p> <p>このAI活用の初期段階において、私たちは大きな可能性を確認しました。設計・実装・テスト環境上の動作確認までを全てAIで行い、開発工数の見積もりに対して5分の1まで効率化を実現した事例も生まれました。</p> <p>このようにAIを活用した開発手法が、一部の開発者の生産性を格段に向上した一方で、AIを使いこなしている開発者とそうでない開発者の間の溝も顕在化し始めていました。当時のAIを活用した開発手法の模索は、個人ごとに異なる個別最適化の域を出ず、組織全体としての生産性を向上するための組織的ムーブメントには至っていないという課題がありました。</p> <p>実際に、こうした開発手法に再現性を与え、組織全体で共有知化するための試みもいくつか立ち上がったものの、局所的なプラクティスの共有・CLAUDE.mdやCursor Rulesの共有知化などに留まっていました。その理由として、当時主流であったVibe Codingに構造的な要因があったと振り返っています。Vibe Codingは、開発の状態に合わせてAIに都度指示を送って、その出力を都度確認しながら方向修正を行い、AIと並走しながら実装の完了まで導くような開発手法です。この同期的でインタラクティブなプロセスの性質上、開発者が直感的・経験的に行なっている状況判断の質が生産性を大きく左右します。このように、個々の状況判断はAIとの対話ログに閉じてしまって透明性が低く、また場面ごとの判断根拠を事後的に説明することも難しいため再利用性も低かったことが、再現可能な共有知に昇華する上での決定的な課題となっていました。</p> <p>またベイエリアの新興企業を中心として、AIを前提とした開発プロセスや開発体制によって、数十人でユニコーン級のサービスを作る事例が知られるようになりました。このようなAI-Nativeな組織では、開発のプロセスも、そこにおける1人1人の役割や職能も、既存のそれらとは異なる形を取ると思います。社内においても、一部の開発者は「開発体験そのものが根底から変わる実感」を覚えていたものの、ボトムアップなAI-Native化の推進体制では、既存の開発プロセスや体制の上での局所最適なAI活用に留まってしまうという限界が見えていました。AIがない時代に最適化された開発プロセスや開発体制を、AI-Nativeな開発プロセスや開発体制へと変革することが、プロダクトデリバリーにおける持続的な競争力を有するための至上命題でした。</p> <h2>2025年7月: pj-doubleの発足</h2> <p>このような背景を受けて、AI-Nativeな開発プロセス・開発体制へのシフトを本格的に推進させるため、2025年の7月頃にメルペイのVP of Engineering Officeにて “pj-double” が発足されました。これは、さまざまな手法を模索して個人の個別最適化を達成する “Divergence” のフェーズから、全社として高生産性を再現度高く実現可能な開発プロセスのスタンダードを確立する “Convergence” のフェーズへの移行のスタートでした。私たちはこのスタンダードの確立により、社内のあらゆる知見やフィードバックがそこに集積され、持続的に組織として共有知を育てていけるような体制を実現することを目指しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/12/e00569c7-project-double-1024x576.png" alt="Pj Double" /></p> <p>時期を同じくして、技術環境のトレンドも変化しました。Prompt EngineeringからContext Engineeringへ、そしてVibe CodingからAgentic Codingへの変化です。Prompt Engineeringのもとでは「いかに良いプロンプトを与えるか」が重要でしたが、Context Engineeringのもとでは「いかに多くのデータソースと繋いで、いかに必要な一次情報を直接インプットするか」が重要視されるようになりました。また、同期的かつインタラクティブにAIを導く必要があったVibe Codingに対して、Agentic Codingでは、「最初に目的・ステップ・完了条件さえ与えれば、Agentが自律的にタスクを完遂するため、開発者はその結果のみを評価すれば良い」という非同期的かつスケーラブルな開発体験を可能にしました。</p> <p>このようにAI活用は、個人のアートから、再現性のあるサイエンスへと変化し、pj-doubleが掲げる再現可能なAI-Native開発への挑戦を後押しする鍵となりました。</p> <h2>2025年7-9月: AIを活用した開発手法の効果の実証</h2> <p>pj-doubleではこの開発手法のスタンダードを確立することを、最初の3ヶ月の目標に置きました。メルペイやメルカリモバイルにおける30以上のバックエンド開発のプロジェクトと連携を開始し、社内で蓄積されたさまざまな開発手法に関する知見を集積することから始めました。</p> <p>この連携を通して、「どのようなプロジェクトのどのような開発フェーズではどのようなAI活用が行われているのか」、「それらの手法には定性的にどのような利点・欠点があるのか」、「それらの手法が定量的にどのくらいの開発生産性向上の効果を出しているのか」、「それらの手法は他のプロジェクトにおいても同程度の効果を再現可能なのか」、などの観点について、各プロジェクトとの週例のミーティングを通して解像度高くトラックしていきました。</p> <p>ここにおいて特に、開発生産性の測定のためにどのような指標を設定すべきかが議論に上がりました。<a href="https://getdx.com/">DX</a>(メルカリで導入している開発者体験を可視化するツール)などで定量的に確認可能なメトリクスは既に複数あったものの、どれも一長一短で、また3ヶ月という期間の短さを踏まえて即効性も重視されました。結果として私たちは、プロジェクト開始時点における従来通りの開発工数の見積もりと、最終的な開発工数の実績値との間で、どれほどの開発速度向上の効果があったかを主観ベースで統計化することにしました。なるべくノイズや主観によるボラティリティを減らすため、週例のミーティングにおいて細かく開発フェーズごとに行なった作業と要した時間を報告してもらいました。これには、どの工程においてどのような課題があるのかを顕在化させる副次的な効果もありました。</p> <p>そしてこの連携を通した定量的な調査の中で、最も開発生産性向上の効果が高かった手法が、Specドリブン開発(Spec-Driven Development; SDD)でした。SDDを採用した複数のプロジェクトは、見積比で平均して150%以上の開発速度向上の効果を実現しました。またSDD以外のAIを活用した開発手法を採用したプロジェクト群と比較しても、80%以上の開発速度向上の効果を実現しました。</p> <p>また更に定性的な調査の中で、AIを活用した開発手法の効果を高く実感している開発者とそうでない開発者の間には、次のような観点で差があることがわかりました。</p> <p>(1) AIのコンテキストに適切な一次情報を与えられているか</p> <p>最初にAIに開発時の前提となる全ての一次情報を明示的に渡すことによって、AIの作業中に開発者がプロンプトから情報を補完する必要がなくなります。これにより、AIが要件を無視して実装したり、Hallucinationを起こすことを防止できます。</p> <p>(2) AIにタスクの明確な目的・ステップ・完了条件などを明示することができているか</p> <p>最初に開発のステップや完了条件などを明示することによって、AIの作業中に開発者が介入して方向修正する必要性が減ります。これにより、AIが全く意図しない方針で実装を進めてしまったり、タスクのゴールを都合良く解釈してしまうといったことを防止できます。また実装が終わるまでどのようなアウトプットになるのかわからないといった不確実性も解消されます。</p> <p>(3) AIが作業中にコンテキストに保持する情報を最適化できているか</p> <p>AIが単一のコンテキストで「資料を読み、技術調査を行い、コードベースを解析し、実装を生成し、テストを実行し、不具合を修正し、…」というステップを全て進めようとすると、不要なドキュメントやコードの情報、テスト時のログなどが全てコンテキストに含まれることになってしまい、すぐに会話がcompactされてしまいます。このcompactionによって、AI自身が何のタスクを行なっていたのか忘れてしまうこともあります。実装を複数のステップで異なる会話のコンテキストに分離したり、Claude CodeのSubagent機能のようなコンテキストを部分的に分離するような機能を用いることが、これに対する解決策となります。</p> <h2>2025年9月: スタンダードとしてのAgent-Spec Driven Development(ASDD)の提案</h2> <p>このような結果を踏まえて、SDDの高い開発生産性への効果と、上記3つの観点に代表されるAIを活用した開発手法の効果を左右する不確実性や属人性を低減するために、メルカリ流に最適化された手法が、Agent Specドリブン開発(Agent-Spec Driven Development; ASDD)でした。</p> <p>ASDDは、「資料の読解・技術調査・コードベースの解析を通して、実装計画(Agent Spec)を生成する」ステップと、「その策定されたAgent Specに基づいて実装を行う」ステップの、2つのステップから構成されます。このAgent Specには、タスクの一覧、タスクごとの詳細設計、タスクごとに「どの実装を参考に」・「どのファイルに」・「どのような変更を加えるか」などの詳細に記載されており、SDDのSpecにおいてAIにとっての明瞭性と人間にとっての可読性を両立したものになっています。ASDDは、SDDのSpecすらも自動生成することを目指した開発手法であるとも言えます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/de1adba0-content-of-asdd-1024x574.png" alt="Content of Agent Spec" /></p> <p>1つ目のAgent Spec生成のステップでは、メルカリのナレッジ基盤に最適化されたエージェントが自動的に一次情報にアクセスし、詳細な実装計画を生成します。また別のエージェントが、実装計画がサービスのコーディング規約に沿っているか、計画が所定のセキュリティ観点をクリアしているかなどのさまざまな調査を行います。この2つのエージェントが交互に修正と評価を繰り返し、最後に要件や仕様における不確実要素が残った場合には、開発者に追加の質問を行います。このように、「AIのコンテキストに適切な一次情報を与える」ことをプロセスによって保証しました。</p> <p>2つ目のAgent Specから実装を生成するステップでは、実装を行うエージェントと、テストや静的解析を行うエージェント、実装結果のAgent Specとの整合性を検証するエージェントが互いに協調しながら、タスクの完了まで自律的に実行されます。このように、「AIにタスクの明確な目的・ステップ・完了条件などを明示する」ことをプロセスによって保証しました。</p> <p>このASDDには、次のような利点がありました。</p> <ul> <li>AIは計画時・実装時のそれぞれで必要な情報のみをコンテキストに保持することができるため、コンテキスト中の情報密度が高い</li> <li>AIの実装計画をレビューできるため、実装結果の予測可能性が高い</li> <li>生成される実装は実装計画に基づく宣言的なアプローチであるため、再利用性や透明性が高い</li> <li>各ステップへのcontributionを通して、全社横断で知見の集約と持続的改善を行うことができる</li> </ul> <p>そして、ASDDによって実装全体がAIに移譲して非同期的に実行可能なプロセスになったため、高いスケーラビリティの実現が可能になりました。このスケーラビリティこそが、高い開発生産性の実現を可能にしたと考えられます。</p> <h2>2025年10月から現在: 開発プロセス全体のAI-Native化へ</h2> <p>2025年10月からは、1人から始まったpj-doubleも10人超のチームに拡大し、またその対象領域もメルペイの一部プロジェクトから、全社へとスケールしています。対象範囲も、これまでのバックエンドの詳細設計と実装のAI-Native化に加えて、要件定義や設計、iOS/Androidのクライアント開発やQAを含めた、デリバリーサイクル全体のプロセス再設計へと拡大しています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/6e5dae32-asdd-1024x571.png" alt="Overview of ASDD" /></p> <p>要件定義・設計領域では、プロダクト仕様書と技術設計書を作成・合意するプロセスを再設計しています。そこでは、PMや開発者が簡単な要件や技術方針を伝えるだけで、AIが一次情報や過去のプロジェクトを参考にプロダクト仕様書と技術設計書を自動生成することを目指してきました。</p> <p>クライアント実装の領域では、バックエンドの実装と同様にASDD開発の有効性を調査してきました。既にiOS開発では効果が検証され始めており、バックエンドとクライアントの一貫した実装体験として、ASDDの整備をさらに続けていく予定です。</p> <p>QA領域では、バックエンドQAとクライアントQAのそれぞれにおいて、またテストケースの自動設計と自動実装のそれぞれについて、ASDDと同様にQAのワークフロー全体をAgenticに実行するための手法を模索しています。テストケース自動設計においては、Claude Code Commandによって、マイクロサービスの依存関係やテストケースのもとになる仕様書の解析・テストケースの設計などを自動化する試みが模索されており、現場のQAエンジニアからはポジティブなフィードバックをいただいています。またテストケース自動実装においては、ASDDと同様にテストの実装と評価のループをAgentが自律的に行うことによって、高い精度で正しいテストケースが実装されることを確認しています。</p> <p>これらの各領域における取り組みは現在進行形で、現場のプロジェクトとの連携の中で、地に足つけた持続的な検証と改善を行いつつも、トップダウンで「プロセスがどう変化するか」「体制がどう変化するか」についてビジョンを打ち立てて、着実に一歩ずつ前進しています。</p> <h2>pj-doubleを通して得た学び</h2> <p>2025年7月から現在に至るまでのpj-doubleの道のりは、決して順風満帆なものではありませんでした。技術的な課題、組織的な摩擦、そしていわゆる「ビルドトラップ」など、多くの壁に直面しました。</p> <p>しかし、それらを乗り越える過程で得られた学びこそが、pj-doubleの道筋や目指す開発プロセスの礎となっています。ここでは、特に重要だった4つの学びを共有します。</p> <h3>学び1: AI活用は品質・保守性とのトレードオフではない</h3> <p>プロジェクト発足当初、多くの開発者が懸念していたのが、「開発速度を上げることで、品質や保守性が犠牲になるのではないか」というトレードオフでした。しかし、検証はこの直感に反する結果を示しました。</p> <p>まず機能的な品質についてです。pj-doubleでは、QAワークフローの自動化などのGuardrail整備を進めることで、「デグレが発生していないか」「意図通りに動作するか」といった機能的な品質保証をプロセスとして組み込んでいます。</p> <p>重要な視点は、これが「AIと開発者の責任分界点の再定義」であるということです。AIに実装を任せる(非同期化する)ことで、開発者はそこで浮いたリソースを、より本質的な品質担保や、開発者がリソースを責任を持つべき高度な検証作業に充てることができます。実際に、メルカリモバイル開発における事例では、実装の高速化によって創出された時間を動作検証やQAに充てることで、設計段階でのインシデントの未然防止に寄与するという成果が確認されました。</p> <p>次に保守性の観点です。「AIが書いたコードが読みにくい」「拡張性が低い」といった懸念もよく聞かれます。しかし、これらの問題はAIの能力不足ではなく、コンテキストの欠如に起因することが多いです。「社内の共通ライブラリを使って欲しい」「特定のコーディング規約に従って欲しい」といった意図があるならば、そのドメイン知識や規約をコンテキストとしてAIに与えることが効果的です。実際にASDDでは、Agent Specの生成時に、社内フレームワークの参考実装などをコンテキストに与えることで、社内の実装における暗黙的なパターンを反映させており、開発者からも「自分で書くのと同じかそれ以上の品質の実装計画が生成される」というポジティブなフィードバックを受けています。</p> <p>さらに、こうしたASDDなどの共通基盤を整備することで、組織として推奨する実装パターンや規約を誰もがコンテキストに注入できるようになります。個人のスキルに依存してバラバラに行われていたAIコーディングに対して、組織全体で品質のベースラインを引き上げることが可能になると期待しています。</p> <p>もちろん、短期的な速度向上が長期的な技術的負債につながらないよう、私たちはDXツールを用いてRevert Rate(手戻り率)やMTTR(平均復旧時間)などの指標を常にモニタリングしています。定性的な感覚と定量的な計測の両軸で確認を続けていますが、現時点ではAI活用が品質を低下させるというシグナルは出ておらず、むしろ標準化の強力な武器になり得ると確信しています。</p> <h3>学び2: レビューのボトルネックはPull Requestの粒度で解消できる</h3> <p>「AI時代の開発は、人間によるコードレビューがボトルネックになる」。これはよく耳にする懸念であり、実際に社内のDX指標を見ても、レビューのリードタイムが開発生産性の向上を阻害している傾向が一部で見受けられました。しかし、現場への定性的なヒアリングと分析を通して、これは「AIコード特有の問題」ではなく、「プロセスの運用」によって解決可能な課題であることが見えてきました。</p> <p>レビューが辛くなる原因を調査したところ、単に「AIが書いたコードだから」ではなく、AIの圧倒的な生産速度によって「1つのPR(Pull Request)に含まれる差分が膨大になっていたから」であることが判明しました。人間はインクリメンタルな情報の処理には長けていますが、一気に大量の情報を受け取ることで、認知負荷が限界を超えてしまったとも言い換えられます。</p> <p>pj-doubleではこの課題に対して、仕組みによって解決することを目指しました。Agent Specを生成する段階でタスクを「合理的かつ動作可能な最小単位」に細かく区切り、そのタスクごとにPRを作成するという運用を徹底したのです。 実際にこのプロセスを採用した開発者からは、「PRが小さく保たれることで、AIが書いたコードであってもレビューのストレスを感じなくなった」などのポジティブなフィードバックが寄せられています。AIと人間のコードの間に、レビュー工数上の有意な差は生じないというのが、私たちの現在の結論です。</p> <p>さらに私たちは、AI-Nativeな開発プロセスの再設計において、コードレビューという行為そのものの再定義も必要だと考えています。 一口にコードレビューと言っても、品質や保守性を守る側面、踏襲的なプロセスとして習慣化して手段と目的が逆転した側面、監査上の要求を満たすための側面など、その役割は複数の意味を持ちます。これら全てを「人間が目視で保証する」という形で行う必要があるのでしょうか。 pj-doubleでは、単に全ての生成コードに人間が目を通すという既存の形を超えて、より意義のある、本質的な価値提供にフォーカスした新しいコードレビューの体験を模索し続けています。</p> <h3>学び3: 現場に地に足つけたアジャイルな検証と改善のサイクルの難しさ</h3> <p>私たちがpj-doubleを通して度々実感したのは、現場での検証と改善のサイクル、いわゆるフィードバックループを回すことの難しさです。特にpj-doubleのQA領域での取り組みにおいて、私たちは典型的な「ビルドトラップ」を経験しました。</p> <p>QA領域でのAgent活用において、私たちは当初、QAワークフローを自動化するためのリッチなUI付きのツールを開発・提供しました。プロンプトだけを配布するよりも、画面を提供したほうがデモがしやすく、配布も容易で、何より入力を制限することでAgentの挙動の再現性を担保できると考えたからです。 しかし、これは結果として検証の足枷となりました。本来、検証フェーズで最も重要なのは「プロンプトや手法の改善」です。しかし、アプリという堅牢な形にしてしまったことで、改善のためにはアプリ側のコード修正まで必要になり、コントリビューションのハードルを上げてしまいました。ASDDのように最小構成で始めていれば、アーリーアダプターたちが直接プロンプトを改善できたはずが、アプリのメンテナンスに時間を取られ、本来の目的である「課題解決の手法の確立」がおざなりになるという本末転倒な事態を招いたのです。</p> <p>また、アジャイルな改善ループの実現も、想像以上にハードルが高いものでした。 当初は週に2〜3回程度の頻度でフィードバックをもらい、高速に改善を回す想定でした。しかし、現場の開発者にとって、検証内容を言語化してフィードバックを送る行為自体が大きなコストです。実際に届くフィードバックは非常に質が高く丁寧なものでしたが、それを高頻度で要求することは現実的ではありませんでした。</p> <p>これらの経験から、私たちはアプローチを修正しています。 まずツールありきで考えるのではなく、最小限の構成で「手法の洗練」に集中すること。そして、週に1度などのミーティングにおけるカジュアルなフィードバック収集や、Slack上のフィードバックの自動収集、DXの機能を活用した集計値の自動算出などの仕組みを導入しています。</p> <p>この学びを踏まえたアプローチの修正によって、pj-doubleのQA領域では提供する手法の洗練と現場からのフィードバックの収集のアジャイルなサイクルが実現されるようになりました。この2025年10月から始まったQA領域のAgentic化の取り組みは、今や急速なスピードで各領域におけるQAエンジニアの体験を塗り替えていっています。</p> <p>この取り組みを通した最も重要な教訓は、現場に対して「良いツールがあるから使ってフィードバックして」という姿勢ではうまくいかないということです。 「開発プロセスを変える必要があり、そのために協力してほしい」というスタンスで巻き込み、ツールはそのプロセス変革を支援するための手段に過ぎないという合意形成を行うこと。ツールに依存しないプロセスを現場と共に見出し、泥臭く検証し続けることこそが、遠回りのようでいて最短の道であると痛感しています。</p> <h3>学び4: 混沌とした過渡期こそ、明確なビジョンが旗となる</h3> <p>上のビルドトラップの話とも重複しますが、pj-doubleにおいて私たちが最も警戒したのは、手段の目的化、すなわち「AIを使った便利ツールの開発」に陥ることでした。</p> <p>日々新しい生成AIモデルやツールが生まれては消えるカオスの渦中で、今の技術スタックに合わせてツールを作り込んでも、それは数ヶ月後には陳腐化してしまいます。pj-doubleの模索においても、目前の課題解決に最適化したソリューションが、数ヶ月後には時代遅れになっているリスクと常に隣り合わせでした。取り組み自体を陳腐化させないためには、特定のツールや局所的な最適化ではなく、ツールに依存しない「プロセスの変革」に向き合う必要があります。しかし、私たちが今向き合っているものが、本当にツールに依存しない普遍的なプロセスなのか、それとも現在のツールの限界を補うための過渡的な対処療法に過ぎないのか、その見極めは非常に困難です。</p> <p>このような「標準化と陳腐化のジレンマ」の中で、組織として価値あるアセットを積み上げ続けるために最も重要だった思考の転換こそが、現在の手元に見える課題から積み上げ式に考えるのではなく、来たる未来から逆算する「バックキャスティング」でした。</p> <p>今のツールの限界を度外視し、技術のポテンシャルを前提に置くことから始めます。「Agentが自律的にコンテキストを収集し、コーディングルールに従って実装を完遂し、QAもAgenticに自動で行われる」。これらが当たり前に実現されたとき、プロダクトデリバリーはどのような形態を取るべきか? ボトルネックはどこに移るのか? この理想的な未来の体験から逆算して初めて、今なすべき投資が見えてくるものかと思います。</p> <p>私たちが進めているナレッジフロー整備、非同期のエージェント実行基盤開発、リグレッションQAの自動化、テスト環境への自動デプロイを含むCI/CDの改善などは、単なるインフラ整備ではなく、この描いた世界観へシームレスに移行するための戦略的な布石です。</p> <p>メディア理論家のマーシャル・マクルーハンはかつて、「私たちはバックミラー越しに現在を見ながら、未来に向かって後ろ向きに進んでいる(&quot;We look at the present through a rear-view mirror. We march backwards into the future.&quot;)」と言いました(出典:The Medium is the Massage by Marshall McLuhan)。 これは、私たちの「常識」や「思考様式」そのものが過去の技術環境によって形作られているという、構造的な限界への指摘です。私たちは今の常識(バックミラー)を通してしか世界を見ることができないため、Agentが当たり前になった新しいパラダイムにおいて、私たちがどのような思考や行動の原理を持つべきかを、現在の延長線上で想像することはできません。ゆえに、過去の成功体験や現在の思考の常識だけで将来を設計しようとすれば、それは局所最適化に留まり、本当に目指すべき世界観には辿り着けません。明確なプロセスが見えない過渡期だからこそ、ともすれば「ビルドトラップ」に陥りがちです。</p> <p>だからこそ、pj-doubleでは既存の常識を意図的に崩し、統一された未来の体験を鮮明にイメージとして描き出し、それを旗として掲げ続ける能動的な態度が不可欠であると強く意識してきました。実際に、pj-doubleでは積極的に目指す世界観やその先の開発体験を社内で発信することで、先の見えない変革の中でも、組織全体が同じ方向を向いて進むことができてきたのだと振り返っています。</p> <h2>pj-doubleが上流プロセスで直面した課題</h2> <p>ASDDでは、Agent SpecというAIにとっての中間表現を統一することで、AIの実行計画への介入が可能になりました。すなわち、開発標準・ドメイン知識・職能の専門知識を強制的に注入することに成功しました。このようにASDDは、pj-doubleが当初目指していた集合知の共通基盤としての効果を発揮しています。</p> <p>ASDDはこのように下流の「仕様が決まった後の実装」において大きな効果を発揮しているのと同時に、上流の「仕様が決まるまでのプロセス」において深刻な課題に直面しています。私たちは、開発現場における継続的な検証とフィードバックの中で、「コードを書く」速度は劇的に向上したものの、その前段である「何を作るか(What)」と「どう設計するか(How)」の合意形成プロセスにおいて、深刻なボトルネックに直面しました。</p> <p>より具体的には、これまで開発者からはASDDについて次のようなフィードバックを受けました。</p> <p>(1) Agent Spec以前に必要な合意形成が無視されている</p> <ul> <li>「Agent Specは仕様や設計方針が確定していることを前提にしているが、そもそも関係者間の合意形成に最も多くの時間がかかっている」</li> <li>「Agent Specのように自動生成されたドキュメントは判断根拠の説明が伴わないため、合意形成に用いる媒体としても適切ではない」</li> </ul> <p>(2) Agent Specの生成とレビューのコストが高く、開発サイクルの速度が落ちる</p> <ul> <li>「生成されたAgent Specや実装の意図が分からず、情報源を探すために技術設計書を遡り、プロダクト仕様書を遡り、Slackの議論を遡らないといけない」</li> <li>「毎回0から書き換わるAgent Specを一行一行レビューして修正するくらいなら、自分で書いたほうが早い」</li> </ul> <p>1つ目について、今のASDDでは、たとえ合意のない確度の低い要求でも、そのまま実装の自動生成まで行うことができてしまいます。結果として、AIは曖昧な部分を勝手に補完し、「技術的には正しいが、誰も合意していない成果物」が自動生成されます。立ち返って考えてみると、従来の上流から下流までの開発フローにおいては、仕様・設計・方針に関する段階的な合意の積み重ねこそが、開発フローのチェックポイントとなって手戻りを防止する機能を果たしていたはずです。またこのプロセスの本当のボトルネックは、これらの合意形成を同じ時間・同じ場所に集まって同期的に行うステップです。pj-doubleは要件定義段階で、プロダクト仕様書や技術設計書を自動で生成することを目指していましたが、このような一次情報が創出される創造的な議論の場や、そこにおける合意形成などの本質的なプロセスがそのスコープから抜け落ちてしまっていました。つまり、上流工程において私たちがフォーカスすべきは、一次情報を組み合わせてプロダクト仕様書や技術設計書に変換するプロセスではなく、その一次情報そのものを生成するプロセスだったという反省です。</p> <p>また2つ目について、ASDDによって生成された長大なAgent Specや実装は開発者の認知負荷を遥かに凌駕し、真面目にレビューしようと思うと、膨大なレビューコストを強いることになってしまいます。さらにAgent Specには、一次情報と自動生成された真偽不明な情報とが入り混じることもしばしばありました。開発者は、生成された情報に心当たりがない場合、その情報の出自を遡って調査する必要があります。本質的に人間の認知モデルは、インクリメンタルな認識の更新を基本としていて、今の認識との差分として追加情報を期待するはずです。他方でpj-doubleで取ってきたドキュメント自動生成のアプローチは、膨大な情報源からかき集めた新情報を既知の情報と一緒くたにして提示するため、認知過負荷を引き起こしてしまっていました。</p> <h2>課題解決への系統的分析とアプローチ</h2> <p>このような課題に対して、私たちはその原因が、「『AIと考える・AIと決める』べきことを、『AIに任せる』体験として設計してしまったこと」であったと総括しました。</p> <p>AIと開発者の協業モデルは、大きく同期プロセス・非同期プロセスに分類できると考えています。(ここで暗黙知とは言語化されていない情報、形式知とは言語化・文書化された情報を指しています。)</p> <table> <thead> <tr> <th></th> <th>同期的協業 — AIと考える・決める</th> <th>非同期的協業 — AIに任せる</th> </tr> </thead> <tbody> <tr> <td>活動の様態</td> <td>AIが情報収集・開発者が意思決定・AIが生成・開発者が評価</td> <td>AIが自律的に情報収集・意思決定・生成・評価を行う</td> </tr> <tr> <td>活動の役割</td> <td>暗黙知と形式知の交換 / 一次情報の生成を伴う</td> <td>形式知から形式知への転換 / 情報の変換に過ぎない</td> </tr> <tr> <td>活動の意義</td> <td>選択・意思決定・合意</td> <td>アウトプットの機能的側面、実利的な要求の実現</td> </tr> </tbody> </table> <p>機能やテストの実装は、動作するという機能的な要求を満足することが目標であるため、非同期的協業によって認知負荷を超えてスケールさせることが重要で、その点でAIへの移譲を促進することがAI-Native化の促進を意味します。</p> <p>一方で上流プロセスにおいては、作成される仕様や設計そのものの価値は小さく、むしろそこに至るまでの比較検討・意思決定・合意などの、人間による承認の記録としての側面が大きいです。そして承認を要するという性質上、誰かの認知負荷の範囲内で運用される必要があり、その意味で同期的協業を必要とします。すなわち、上流フェーズにおける最重要事項は、生成されるアウトプットではなく、仕様や設計が検討され、レビューされ、合意され、それらが新たな一次情報として管理されるようになることです。</p> <p>しかしpj-doubleでは、同期プロセスの意義を過小評価し、全てを非同期プロセスとして処理しようとしていました。一次情報は既知で既出の前提として、それらを用いた情報の変換(プロダクト仕様書・技術設計書・Agent Spec・実装生成)のAgentic化にフォーカスしてしまっていました。私たちは、「AIが最初から90%の完成度の設計を作る」ことと、「AIと一緒に考え、機微な方向性を決定しながら同じ設計に辿り着く」ことが、上流プロセスにおいては全く異なる意味を持つという観点を見落としていたということです。</p> <p>すなわち、上流プロセスを非同期的協業とは区別された同期的協業として再設計することがpj-doubleにおける次の命題となるわけですが、このAIとの同期的協業も更に、「AIと考える」体験と「AIと決める」体験の2つに区別することで、更に見通しが良くなると考えています。</p> <table> <thead> <tr> <th></th> <th>同期的協業 — AIと考える</th> <th>同期的協業 — AIと決める</th> </tr> </thead> <tbody> <tr> <td>活動の様態</td> <td>AIが情報を収集・提示し、開発者の思考を拡張する</td> <td>思考や意思決定を通して新たな一次情報を生み出す</td> </tr> <tr> <td>活動の役割</td> <td>形式知の提示による暗黙知の拡大</td> <td>一次情報の形式知への登録・参照</td> </tr> <tr> <td>活動の意義</td> <td>対話を通した認知の範囲・思考の深度・選択の質の向上</td> <td>合意が形成され、蓄積されること</td> </tr> </tbody> </table> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/3d43f10a-ai-collaboration-1024x548.jpeg" alt="AI Collaboration" /></p> <p>「AIと考える」プロセスは、開発者が1人では見つけられなかった情報にアクセスし、1人では実現し得なかった深度で検討することを可能にします。ここでは、AIとの自然な対話の中で、AIが設計・実装を前進させるためのガイドをしてくれることが理想的な体験です。例えば、ChatGPTやClaudeとの対話を通して、設計や実装の方針について議論するような体験です。ここでは、開発者自身への知の内面化(Internalization)の側面に価値があります。</p> <p>そのためには、人の認知モデルに基づいて、開発者の現状理解からの差分として新たな情報が補完的に提示されるようなインタラクションの設計であるべきです。これは例えば、エンジニアがテックリードとの会話を通して、「こういう設計もあったか」「この設計はこの考慮が漏れていた」などの発見を伴うプロセスに対応します。AIと開発者の関係性は、AIが生成した情報を開発者が受動的に消費するという関係性ではなく、開発者が次に必要とする情報をAIが補完的に提示するという関係性を目指すべきであると考えます。</p> <p>またこのような開発者とAIの思考の模索は、完全なフリーフォーマットで行うことも、完全なテンプレートに基づいて行うことも、どちらも適切ではないと考えています。過度な自由度は選択麻痺を引き起こし、過剰な制約は模索の余地を奪うためです。pj-doubleのプロセス再設計においても、プロセスを標準化することが何度か試みられましたが、これは思考の幅を制限してしまう過度な制約の典型例だったと言えそうです。AIが全体プロセスやそこにおけるステータスを見据えつつ、開発者を自然な対話の中で誘導できるような思考フローが理想的でしょう。</p> <p>「AIと決める」プロセスは、開発者によるさまざまな選択肢の採用・不採用の意思決定とその理由を、新たな一次情報として蓄積するためのプロセスです。ここでは、建設的なプロセスのチェックポイントとしての意思決定やその蓄積、一次情報の生成という、知の表出化的側面に価値があります。</p> <p>このように、AIと決めたことが蓄積し、それがAIと考えるために使われるという循環をデザインすることが、同期的協業の条件であると考えています。</p> <p>私たちpj-doubleは現在、このような考えに基づいた課題解決方針の模索とその検証を実施するフェーズにあります。もしその取り組みを通して更なる発見や学びがあれば、またいつか別のテックブログなどで共有させてください。</p> <h2>pj-doubleが描く世界観</h2> <p>そしてこの体験が実現した世界観は、次のようにイメージできるかもしれません。</p> <p>(1) AIと考える</p> <p>開発者はAIとの話し合いを通じて、仕様や設計を考え進めていきます。そこにおいてAIは既存のサービスやドメイン知識を駆使しながら、思索や意思決定をサポートします。このような能力の拡張により、上流工程において開発者1人が担える役割も拡大します。</p> <p>(2) AIと決める</p> <p>AIは対話の中で追加で発生する一次情報を蓄積します。PMの「なぜこの仕様は却下されたのか」という質問や、テックリードの「なぜこの技術選定を行なったのか」という質問に対して、蓄積された一次情報に基づいて回答します。<br /> このような合意の形成と参照という側面は、これまで必要だった「開発者↔開発者」の同期的なコミュニケーションを、「開発者↔Agent」+「Agent↔開発者」のコミュニケーションへと分解し、合意の形成・参照を非同期的に実現可能にします。すなわち、各職能の人が同じ時間・同じ場所に集まらなくても、合意の確認や意思決定をできるようになります。</p> <p>(3) AIに任せる</p> <p>このように合意形成が行われたら、あとはプロジェクトの機能要求を実現するための仕事をAIに移譲するだけです。例えばここで生成されるプロダクト仕様書や技術設計書は、全て過去の意思決定に基づくため、開発者がその生成意図を吟味したり、Hallucinationを精査する雑務が発生しません。またASDDの本質的な付加価値であるAgenticな実装生成も、高い確度で任せることができます。QAも自動で実行されるため、デグレの心配もありません。あとは、安心して実装の完了を待つだけです。</p> <p>この世界観のもとで、PMや開発者はAIによって拡張された思考や調査能力を存分に使いながらプロダクトのアイデアを考え進めていき、アイデアが煮詰まったと思ったら即座にそれが形になります。試行と学習のサイクルが圧倒的な速度で回り、より多くのアイデアを試し、より速く学ぶことができるようになるでしょう。</p> <p>また職能の拡張は一人一人の役割やプロジェクトの体制にも変化を与えるはずです。開発者がAIによって拡張されることで、専門知識が必要な調査や業務が自動化され、PM、アナリスト、Designer、エンジニア、QAといった個人間の職能の差異が薄れていき、結果として1人が担う役割も大きくなるはずです。またそのもとで、プロダクトデリバリーは少人数の自律的なチームによってEnd-to-Endに実施されるようになります。これまではOutputに目が向きがちだった開発者も、よりお客さまへの価値提供との距離が近づくことで、OutcomeやImpactに責任を持つようになっていくでしょう。</p> <p>さらに、薄れるのは職能の境界だけでなく、サービスの境界にも広がりそうです。ドメイン知識が形式知化されることで、プロダクト開発のために要求されるドメイン知識の総量も減少し、サービスの境界と認知負荷の境界が等価ではなくなるかもしれません。すなわち、逆コンウェイの法則に基づくような組織体制ではなく、よりビジネスを加速させるために最適な組織構造へと変化するかもしれません。例えば、1つの少人数開発チームが複数のサービスをまたいで1つの施策を実現するような体制はその候補の1つです。</p> <p>こうした要件定義からQAまでをAI-Nativeに再設計するための取り組みの効果は、開発生産性の向上だけに留まらず、本質的なビジネスの強度向上に貢献するとも考えています。これまでは各工程で担当者が異なるため知識の分断が、各工程でプラットフォームが違うためデータの分断が発生することがありました。プロジェクトを通したより多くの知識が、またより多くの職能ごとの専門知が集積されるようになることで、市場やお客さまのOutcome・Impactがフィードバックされて、それをもとにプロダクトを改善・拡張するというループが自己完結化するようになれば、施策の精度や戦略の練度の向上にも寄与できると信じています。</p> <p>pj-doubleの取り組みやそこで得た学びは、知的活動を中心とする他のあらゆる領域にも拡大できるかもしれません。「AIと考える」「AIと決める」「AIに任せる」の3つを使い分けながらプロセスを再設計することが、プロダクト開発に留まらない「AI-Nativeな働き方」の重要な要素になると考えています。</p> <h2>おわりに</h2> <p>以上、メルカリにおけるpj-doubleを通した、AI-Native化への挑戦とその学びについての紹介でした。本記事が、Human CentricからAgent Centricへと向かうこの過渡期において、AIとの新しい協業の形を模索する一助となれば幸いです。長文にお付き合いいただき、ありがとうございました。</p> <p>明日の記事は hokaoさんによる「Kubeflow PipelinesとPydantic Settingsを活用してMLパイプラインを型安全かつシンプルに実装する」です。引き続きお楽しみください。</p> 2025年のメルペイの取り組みを振り返ってhttps://engineering.mercari.com/blog/entry/20251201-looking-back-merpay-initiatives-in-2025/https://engineering.mercari.com/blog/entry/20251201-looking-back-merpay-initiatives-in-2025/<p>はじめに こんにちは。メルペイVPoEの@keigowです。 この記事は、Merpay &amp; Mercoin Advent Calendar 2025 の1日目の記事です。 2025年も色々なことがありました。年末 [&hellip;]</p> Mon, 01 Dec 2025 10:00:49 GMT<h2>はじめに</h2> <p>こんにちは。メルペイVPoEの<a href="https://x.com/keigow">@keigow</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/">Merpay &amp; Mercoin Advent Calendar 2025</a> の1日目の記事です。</p> <p>2025年も色々なことがありました。年末ということで、2025年にメルペイとして、或いはメルカリグループとして取り組んだことをこの機会に振り返られればと思います。</p> <h2>mercari GEARS 2025</h2> <p>11月13日に「メルカリエンジニアリングの今」をテーマにテックカンファレンスを開催しました。リアルな場でのイベントは実に7年ぶりの開催となりましたが、社内外含め沢山の方にご来場いただき、盛り上がることができました。キーノートを始めとして各発表の<a href="https://speakerdeck.com/mercari">スライド</a>や<a href="https://www.youtube.com/playlist?list=PLgznQFGqsAWD4Amf_scEA8XwIfg25aLu7">動画</a>も公開されておりますので、ぜひ御覧ください。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/d0cf5413-gears-scaled.jpg" alt="mercari GEARS 2025" /></p> <h2>AIによって進化する開発</h2> <p>ちょうど半年前に同じくブログの連載企画で<a href="https://engineering.mercari.com/blog/entry/20250602-merpay-ai-utilization/">メルペイにおけるAI活用の取り組み</a>についてご紹介しました。当時は<a href="https://www.claude.com/product/claude-code">Claude Code</a>が流行り始めたぐらいだったことを考えると、凄い速度の変化が起きていると改めて感じます。当時はエンジニアのAI Coding Toolの利用率を追いかけていましたが、今ではほぼ全てのエンジニアが何らかのAI Coding Toolを利用するようになりました。</p> <p>グループ全体として大きな取り組みとなったのは、<a href="https://careers.mercari.com/mercan/articles/53708/">こちらの記事</a>でも取り上げているAI Task Forceです。目標としてAI Nativeな会社を目指し、エンジニアの部署に限らず、カスタマーサービスやマーケティングなど全社33の部署を対象に、専任のエンジニアを含めた100名規模の組織を組成し、業務の棚卸しと、AI Nativeなワークフローとはそもそもどうあるべきかを考え、見直しを行っています。まだ道半ばではあるものの、各部署でロードマップを作成し、着実に成果が出てきています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/99f9724d-ai_task_force.png" alt="AI Task Force" /></p> <h2>Project Double</h2> <p>AI Task Forceの活動と並行して、メルペイにVPoE Officeという新しい箱を作り、AIによる生産性向上の取り組みとしてProject Doubleをスタートしました。その名の通り生産性を2倍にしようという取り組みで、当時一部のエンジニアたちが行っていたAgenticなCoding手法を、Agent Spec Driven Development(ASDD)として標準化することで誰もがそれを利用可能にしようとする試みです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/6e5dae32-asdd.png" alt="Overview of ASDD" /></p> <p>初期はBackendのみを対象としたProjectでしたが、7-9月の四半期で一定の成果を出すことができたため、その範囲をClientの開発、Backend/ClientそれぞれのQA領域、仕様書や設計などのPlanning領域へと広げることを決めました。また対象となるCompanyもFinTechの一部のPilot Projectから、グループ全体へと広がり、現在はEngineeringの中でも最優先の取り組みとして進行しています。</p> <p>内容の詳細や得られた学びについてはこのProjectをリードしているnakai-sanの記事をご確認ください。</p> <p><a href="https://engineering.mercari.com/blog/entry/20251201-pj-double-towards-ai-native-development/" title="pj-double: メルカリの開発生産性向上に向けた挑戦 — AI-Native化が辿り着いたASDDとプロセス変革の全貌">pj-double: メルカリの開発生産性向上に向けた挑戦 — AI-Native化が辿り着いたASDDとプロセス変革の全貌</a></p> <h2>Project Doubleが実現した世界で</h2> <p>Agentic Codingの推進において、乗り越えるべき課題は精度やイテレーションにかかる時間などたくさんありますが、日々起きるモデルの進化やツール、環境の整備によっていずれ問題としては解決されていくと思っています。</p> <p>一方で、1人のエンジニアが企画から設計、開発、テスト、リリースまでを一貫して行える世界が来たときに備えて、いくつか解決しなければいけない部分もあります。オンコール体制の整備、問い合わせ対応などの運用業務の最適化など、Agentic Codingが中心となった時代に最適な組織設計です。こちらについては一定の仮説はあるものの、まだ明確な応えが見えていない領域であり、Project Doubleの推進と合わせてトライアルをしていきたいと思っています。</p> <h2>しゃべるおさいふ</h2> <p>AIの活用はEngineeringだけでなく、プロダクトへの活用もPoCという形でトライしています。メルペイで現在取り組んでいるのがこちらの「しゃべるおさいふ」という機能で一部のお客さまを対象に試験的に導入しています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/889504b5-osaifu.jpg" alt="Osaifu" /></p> <p>内容は非常にシンプルで、利用データを元にAIがコメントをしてくれるという機能です。ちょっとした日常の利用に対してコメントを貰えるというのは思ったよりも嬉しい体験で社内テストは想定していたよりも好評でした。そこまで複雑な機能では無いものの、コストの観点や安全性、倫理的な観点も含め、AIをプロダクトで活用するとはどういうことなのか、という学びを得ることができました。12/20のkobaryo-sanの記事ではこちらの機能のバックエンドの設計について紹介する予定なのでお楽しみに。</p> <p><a href="https://engineering.mercari.com/blog/entry/20251219-f15f686553/" title="LLMを用いたおしゃべり機能「しゃべるおさいふ」のバックエンド設計">LLMを用いたおしゃべり機能「しゃべるおさいふ」のバックエンド設計</a></p> <h2>開発合宿</h2> <p>1月の事になりますが、数年ぶりにメルペイで一泊二日の開発合宿を行いました。普段業務に追われて取り組めないような新しい技術へのチャレンジ、OSSの開発、やりたいと思っていた新機能の開発などに取り組みました。この開発合宿をきっかけとして、<a href="https://careers.mercari.com/mercan/articles/54652/">有志による開発プロジェクト</a>がスタートし、実際にリリースまでつながることもありました。コロナ禍で普段直接顔を合わせることが少なくなっていたエンジニア同士のコミュニケーションの機会にもなり、参加してくださった皆様の満足度もかなり高かったので、また来年もトライできればと思っています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/80898002-development_camp-scaled.jpg" alt="Development Camp" /></p> <p>開発合宿の様子はMercari GearsのYouTubeに動画も上がっているので、気になった方はぜひご覧いただければと思います。</p> <p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/Aa5-5s7wi_4?si=clSs_xJygwVOxWOg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p> <h2>おわりに</h2> <p>今年は全社のテーマとしてAI Nativeを掲げ、Engineering組織としてもAI中心の一年となりました。半年前も同じようなことを書いた気がしますが、目まぐるしい変化の中で働けることを楽しんでいます。</p> <p>明日の記事はその中でも中心的な取り組みとなる、nakaiさんによる「pj-double: メルカリの開発生産性向上に向けた挑戦 — AI-Native化が辿り着いたASDDとプロセス変革の全貌」です。引き続きMerpay &amp; Mercoin Advent Calendarをお楽しみください。</p> 「Merpay &#038; Mercoin Advent Calendar 2025」開催のお知らせhttps://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025/<p>こんにちは。メルペイ Engineering Engagement チームの mikichin です。 Advent Calendarの季節がやってきます!今年も、メルカリグループは Advent Calendar を実 [&hellip;]</p> Wed, 26 Nov 2025 11:00:26 GMT<p>こんにちは。メルペイ Engineering Engagement チームの <a href="https://twitter.com/chida_miki" title="mikichin">mikichin</a> です。<br /> Advent Calendarの季節がやってきます!今年も、メルカリグループは Advent Calendar を実施します!<br /> <br /> ▶<a href="https://engineering.mercari.com/en/blog/entry/20251126-mercari-advent-calendar-2025">Mercari Advent Calendar 2025 はこちら</a><br /> </p> <h1>Merpay &amp; Mercoin Advent Calendar とは?</h1> <p>Advent Calendar の習慣にもとづいて、メルペイ・メルコインのエンジニアがプロダクトや会社で利用している技術、興味のある技術分野やちょっとしたテクニックなど知見をアウトプットしていきます。このAdvent Calendarを通じてクリスマスまでの毎日を楽しく過ごしていただければと思っています。</p> <h3>2024年のMercari / Merpay &amp; Mercoin Advent Calendar はこちら</h3> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20241125-mercari-advent-calendar-2024/">Mercari Advent Calendar 2024</a></li> <li><a href="https://engineering.mercari.com/blog/entry/20241125-merpay-mercoin-advent-calendar-2024/">Merpay &amp; Mercaoin Advent Calendar 2024</a></li> </ul> <h1>公開予定表 (こちらは、後日、各記事へのリンク集になります)</h1> <table> <thead> <tr> <th style="text-align: left;">Date</th> <th style="text-align: left;">Theme / Title</th> <th style="text-align: left;">Author</th> </tr> </thead> <tbody> <tr> <td style="text-align: left;">12/1</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251201-looking-back-merpay-initiatives-in-2025/" title="2025年のメルペイの取り組みを振り返って">2025年のメルペイの取り組みを振り返って</a></td> <td style="text-align: left;">@keigow</td> </tr> <tr> <td style="text-align: left;">12/2</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251201-pj-double-towards-ai-native-development/" title="pj-double: メルカリの開発生産性向上に向けた挑戦 — AI-Native化が辿り着いたASDDとプロセス変革の全貌">pj-double: メルカリの開発生産性向上に向けた挑戦 — AI-Native化が辿り着いたASDDとプロセス変革の全貌</a></td> <td style="text-align: left;">@nakai</td> </tr> <tr> <td style="text-align: left;">12/3</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251128-c46fa95168/" title="Kubeflow PipelinesとPydantic Settingsを活用してMLパイプラインを型安全かつシンプルに実装する">Kubeflow PipelinesとPydantic Settingsを活用してMLパイプラインを型安全かつシンプルに実装する</a></td> <td style="text-align: left;">@hokao</td> </tr> <tr> <td style="text-align: left;">12/4</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251202-5f1fdbecce/" title="メルコインとメルカリNFTのフロントエンド開発のインターンシップで取り組んだこと">メルコインとメルカリNFTのフロントエンド開発のインターンシップで取り組んだこと</a></td> <td style="text-align: left;">@Sakamoto</td> </tr> <tr> <td style="text-align: left;">12/5</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251205-a-pragmatic-approach-to-ai-powered-documentation-generation/" title="A Pragmatic Approach to AI-Powered Documentation Generation">A Pragmatic Approach to AI-Powered Documentation Generation</a></td> <td style="text-align: left;">@Fab</td> </tr> <tr> <td style="text-align: left;">12/6</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251206-yapc-fukuoka-2025/" title="YAPC::Fukuoka 2025に参加しました!! #yapcjapan">YAPC::Fukuoka 2025に参加しました!! #yapcjapan</a></td> <td style="text-align: left;">@Sakabe</td> </tr> <tr> <td style="text-align: left;">12/7</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251207-61e24d343e/" title="本番環境でしか起こらない Goroutines 急増問題と解決までの道のり">本番環境でしか起こらない Goroutines 急増問題と解決までの道のり</a></td> <td style="text-align: left;">@goccy</td> </tr> <tr> <td style="text-align: left;">12/8</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251205-finally-mercari-japan-in-english-our-road-to-cross-platform-i18n/" title="Finally, Mercari Japan in English! Our Road to Cross-Platform i18n">Finally, Mercari Japan in English! Our Road to Cross-Platform i18n</a></td> <td style="text-align: left;">@fenomas</td> </tr> <tr> <td style="text-align: left;">12/9</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251209-d0de07214d/" title="Agent Specで小さく素早く回すメルカリモバイル開発現場">Agent Specで小さく素早く回すメルカリモバイル開発現場</a></td> <td style="text-align: left;">@seitau</td> </tr> <tr> <td style="text-align: left;">12/10</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251210-ai-tf-facing-uncertainty/" title="AI Task Forceで学んだ「不確実性との向き合い方」">AI Task Forceで学んだ「不確実性との向き合い方」</a></td> <td style="text-align: left;">@panorama</td> </tr> <tr> <td style="text-align: left;">12/11</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251211-cf3b67a5a7/" title="理想の Workflow Platform という“聖杯”に、n8n でついに手が届くかもしれない">理想の Workflow Platform という“聖杯”に、n8n でついに手が届くかもしれない</a></td> <td style="text-align: left;">@ISSA</td> </tr> <tr> <td style="text-align: left;">12/12</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251211-6fe0bc0838/" title="Making n8n Enterprise-Ready: 企業向けn8nの導入と運用の取り組み">Making n8n Enterprise-Ready: 企業向けn8nの導入と運用の取り組み</a></td> <td style="text-align: left;">@T</td> </tr> <tr> <td style="text-align: left;">12/13</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251211-580dc508a7/" title="n8nの静的解析CLIツールをOSS化 – JSON解析とDAGで実現するセキュリティチェックの自動化">n8nの静的解析CLIツールをOSS化 – JSON解析とDAGで実現するセキュリティチェックの自動化</a></td> <td style="text-align: left;">@mewuto</td> </tr> <tr> <td style="text-align: left;">12/14</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251213-862c05648c/" title="n8n – 乱立するAIツール中でPoCを成功させるために考えたこと・実際に取り組んだこと">n8n – 乱立するAIツール中でPoCを成功させるために考えたこと・実際に取り組んだこと</a></td> <td style="text-align: left;">@abcdefuji</td> </tr> <tr> <td style="text-align: left;">12/15</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251212-extending-the-balance-service-challenges-in-implementing-multi-currency/" title="Extending the Balance Service: Challenges in Implementing Multi-Currency">Extending the Balance Service: Challenges in Implementing Multi-Currency</a></td> <td style="text-align: left;">@Timo</td> </tr> <tr> <td style="text-align: left;">12/16</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251214-supercharging-user-engagement-how-mercari-is-using-server-driven-ui-to-reduce-time-to-market/" title="Supercharging User Engagement: How Mercari is Using Server-Driven UI to Reduce Time-to-Market">Supercharging User Engagement: How Mercari is Using Server-Driven UI to Reduce Time-to-Market</a></td> <td style="text-align: left;">@Stefan_droid</td> </tr> <tr> <td style="text-align: left;">12/17</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251216-mandates-for-recurring-payments/" title="多様な支払い手段と継続課金を安全に扱う「Mandate」の設計">多様な支払い手段と継続課金を安全に扱う「Mandate」の設計</a><br /><a href="https://engineering.mercari.com/en/blog/entry/20251216-mandates-for-recurring-payments/" title="Designing “Mandates” for Safe and Flexible Recurring Payments">Designing “Mandates” for Safe and Flexible Recurring Payments</a></td> <td style="text-align: left;">@tomo</td> </tr> <tr> <td style="text-align: left;">12/18</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251217-ce2057d0ad/" title="メルペイでの3ヶ月間:非同期処理への移行とASDD開発の学び">メルペイでの3ヶ月間:非同期処理への移行とASDD開発の学び</a></td> <td style="text-align: left;">@Minato</td> </tr> <tr> <td style="text-align: left;">12/19</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251219-payment-platform-2025/" title="Payment Platform の 2025 年: メルカリグループを支える決済基盤のこれまでとこれから">Payment Platform の 2025 年: メルカリグループを支える決済基盤のこれまでとこれから</a></td> <td style="text-align: left;">@komatsu</td> </tr> <tr> <td style="text-align: left;">12/20</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251219-f15f686553/" title="LLMを用いたおしゃべり機能「しゃべるおさいふ」のバックエンド設計">LLMを用いたおしゃべり機能「しゃべるおさいふ」のバックエンド設計</a></td> <td style="text-align: left;">@kobaryo</td> </tr> <tr> <td style="text-align: left;">12/21</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251219-nine-months-of-devex-improvement-at-mercari-group/" title="メルカリグループのDevEx改善、9ヶ月間の実践">メルカリグループのDevEx改善、9ヶ月間の実践</a><br /><a href="https://engineering.mercari.com/en/blog/entry/20251219-nine-months-of-devex-improvement-at-mercari-group/" title="Nine Months of DevEx Improvement at Mercari Group">Nine Months of DevEx Improvement at Mercari Group</a></td> <td style="text-align: left;">@ntk</td> </tr> <tr> <td style="text-align: left;">12/22</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251219-029c76e123/" title="インターン生が挑んだ認証方式のマイグレーション──メルペイ加盟店管理画面へのOAuth 2.0導入">インターン生が挑んだ認証方式のマイグレーション──メルペイ加盟店管理画面へのOAuth 2.0導入</a></td> <td style="text-align: left;">@taki</td> </tr> <tr> <td style="text-align: left;">12/23</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251223-ai-driven-learning/" title="Cursorでプログラミング言語を学び直す方法——AI駆動学習の4ステップ">Cursorでプログラミング言語を学び直す方法——AI駆動学習の4ステップ</a></td> <td style="text-align: left;">@kubomi</td> </tr> <tr> <td style="text-align: left;">12/24</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251224-mercari-cast/" title="なぜ再発防止は、思ったように機能しないのか。メルカリのプロダクト開発でCAST分析が必要だった理由">なぜ再発防止は、思ったように機能しないのか。メルカリのプロダクト開発でCAST分析が必要だった理由</a></td> <td style="text-align: left;">@pooh</td> </tr> <tr> <td style="text-align: left;">12/25</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251225-building-egp-cards-at-merpay/" title="メルペイ インターンでの挑戦と学び:EGP Cardsと向き合った3ヶ月間">メルペイ インターンでの挑戦と学び:EGP Cardsと向き合った3ヶ月間</a><br /><a href="https://engineering.mercari.com/en/blog/entry/20251225-building-egp-cards-at-merpay/" title="Building EGP Cards at Merpay: Lessons from a Frontend Internship">Building EGP Cards at Merpay: Lessons from a Frontend Internship</a></td> <td style="text-align: left;">@Yusaku</td> </tr> <tr> <td style="text-align: left;">12/25</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251225-mercari-ai-native-company/" title="AI-Nativeという選択 ー 正解のない時代に、メルカリが選んだ指針">AI-Nativeという選択 ー 正解のない時代に、メルカリが選んだ指針</a></td> <td style="text-align: left;">@kimuras</td> </tr> </tbody> </table> <p>初日の記事は keigowさんです。引き続きお楽しみください。</p> 「Mercari Advent Calendar 2025」開催のお知らせhttps://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/https://engineering.mercari.com/blog/entry/20251126-mercari-advent-calendar-2025/<p>こんにちは。メルカリ Engineering Officeのyasu_shiwakuです。 今年もメルカリとメルペイ・メルコインで2本のAdvent Calendarを実施します! ▶Merpay &amp; Merco [&hellip;]</p> Wed, 26 Nov 2025 11:00:20 GMT<p>こんにちは。メルカリ Engineering Officeのyasu_shiwakuです。<br /> 今年もメルカリとメルペイ・メルコインで2本のAdvent Calendarを実施します!<br /> <br /> ▶<a href="https://engineering.mercari.com/blog/entry/20251126-merpay-mercoin-advent-calendar-2025">Merpay &amp; Mercoin Advent Calendar 2025 はこちら</a><br /> </p> <h1>Mercari Advent Calendar とは?</h1> <p>メルカリグループのエンジニアがプロダクトや会社で利用している技術、興味のある技術分野やちょっとしたテクニックなど知見をアウトプットしていきます。このAdvent Calendarを通じてクリスマスまでの毎日を楽しく過ごしていただければと思っています。</p> <h3>2024年のMercari / Merpay Advent Calendar</h3> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20241125-mercari-advent-calendar-2024/">Mercari Advent Calendar 2024</a></li> <li><a href="https://engineering.mercari.com/blog/entry/20241125-merpay-mercoin-advent-calendar-2024/">Merpay &amp; Mercoin Advent Calendar 2024</a><br /> </li> </ul> <h1>公開予定表 (こちらは、後日、各記事へのリンク集になります)</h1> <table> <thead> <tr> <th style="text-align: left;">Date</th> <th style="text-align: left;">Theme / Title</th> <th style="text-align: left;">Author</th> </tr> </thead> <tbody> <tr> <td style="text-align: left;">12/1</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251127-websocket-xss-vulnerability-discovery-my-security-journey-at-mercari/">Websocket XSS vulnerability discovery: My security journey at Mercari</a></td> <td style="text-align: left;">@philolo1</td> </tr> <tr> <td style="text-align: left;">12/2</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251202-llm-key-server/">LLM Key Server: Providing Secure and Convenient Access to Internal LLM APIs</a></td> <td style="text-align: left;">@Hiroki Akamatsu</td> </tr> <tr> <td style="text-align: left;">12/3</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251202-shops-monorepo-five-years-later-a-tale-of-bazel-and-cursor/">Shops Monorepo Five Years Later: A Tale of Bazel and Cursor</a></td> <td style="text-align: left;">@Jazz</td> </tr> <tr> <td style="text-align: left;">12/4</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251204-enhancing-developer-experience-through-mercaris-unified-platform-interface/">Enhancing DX through Mercari&#8217;s Unified Platform Interface</a></td> <td style="text-align: left;">@whygee</td> </tr> <tr> <td style="text-align: left;">12/5</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251205-mercari-hallo-frontend-improvements/">メルカリ ハロ Web フロントエンドの1年間の改善と学び</a></td> <td style="text-align: left;">@mattsuu</td> </tr> <tr> <td style="text-align: left;">12/6</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251206-engineering-the-semantic-layer-principles-for-data-at-scale/">Engineering The Semantic Layer: Principles for Data at Scale</a></td> <td style="text-align: left;">@sathiya</td> </tr> <tr> <td style="text-align: left;">12/7</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251203-46bf6511f3/">QAエンジニアがAIで日々の課題を解決した話</a></td> <td style="text-align: left;">@yuga</td> </tr> <tr> <td style="text-align: left;">12/8</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251202-navigating-change-learning-to-reinvent-in-an-unstable-world/">Navigating Change: Learning to Reinvent in an Unstable World</a></td> <td style="text-align: left;">@Antony Chane-Hive</td> </tr> <tr> <td style="text-align: left;">12/9</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251208-search-results-quality-monitoring-with-llms/">Search Results Quality Monitoring with LLMs</a></td> <td style="text-align: left;">@otter</td> </tr> <tr> <td style="text-align: left;">12/10</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251208-b7adaa9b98/">LiveContactToolにおける機微情報の取り扱い~CloudDLPを使ったマスキング</a></td> <td style="text-align: left;">@sters</td> </tr> <tr> <td style="text-align: left;">12/11</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251211-c73c2b1747/">OpenID Connect Core 1.0 の Claims パラメーターの利用</a></td> <td style="text-align: left;">@kgoro</td> </tr> <tr> <td style="text-align: left;">12/12</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251212-7fe4c31bf4/">Adsシステムの急成長を支える技術:信頼性と収益性を取り戻した「PJ-MARP」の全貌</a></td> <td style="text-align: left;">@tokku</td> </tr> <tr> <td style="text-align: left;">12/13</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251213-96e00d1d91/">メルカリが、AI時代にナレッジマネジメントに投資したわけ</a></td> <td style="text-align: left;">@t-hiroi</td> </tr> <tr> <td style="text-align: left;">12/14</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251204-563130cd63/">メルカリAdsが広告を届けるまでの話</a></td> <td style="text-align: left;">@yanap</td> </tr> <tr> <td style="text-align: left;">12/15</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251211-3846ed440d/">TiDB Resource Groupでワークロードを制御する</a></td> <td style="text-align: left;">@ogataka50</td> </tr> <tr> <td style="text-align: left;">12/16</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251215-the-cost-of-speed-a-battle-against-cost-debt-and-diverging-systems/">The Cost of Speed: A Battle against Cost, Debt, and Diverging Systems</a></td> <td style="text-align: left;">@Sneha</td> </tr> <tr> <td style="text-align: left;">12/17</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251216-building-a-learning-culture-with-devdojo/">Building a Learning Culture with DevDojo</a></td> <td style="text-align: left;">@mariz</td> </tr> <tr> <td style="text-align: left;">12/18</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251218-capturing-network-packets-in-kubernetes/">Kubernetes環境におけるパケットキャプチャ</a></td> <td style="text-align: left;">@mshibuya</td> </tr> <tr> <td style="text-align: left;">12/19</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251211-4cfd1db1bf/">AI-Native 開発を加速する AWS Kiro の導入と、Okta を活用したアカウント管理の自動化</a></td> <td style="text-align: left;">@amenbo &amp; @siroken3</td> </tr> <tr> <td style="text-align: left;">12/19</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251218-26bcec59ba/">メルカリ内部の Dynamic Client Registration 活用事例</a></td> <td style="text-align: left;">@task</td> </tr> <tr> <td style="text-align: left;">12/20</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251220-jamf-terraform-gitops/">PR駆動の変更、CI/CDでOS設定を自動反映 — Terraformで実現するJamf ProのIaC+GitOps基盤</a></td> <td style="text-align: left;">@yu</td> </tr> <tr> <td style="text-align: left;">12/21</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251217-2204b3261b/">Non-AI tasks in the AI task force:AIツール開発の現場でこそ必要な「AI以外の」技術選定</a></td> <td style="text-align: left;">@akkie</td> </tr> <tr> <td style="text-align: left;">12/22</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251221-tales-of-oidc-oauth-security-what-it-takes-to-trust-a-token/">Tales of OIDC &amp; OAuth Security: What It Takes to Trust a Token</a></td> <td style="text-align: left;">@Kahla</td> </tr> <tr> <td style="text-align: left;">12/23</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251223-when-speed-wasnt-about-coding-faster-our-journey-to-one-person-one-release/">When Speed Wasn’t About Coding Faster: Our Journey to ‘One Person One Release’</a></td> <td style="text-align: left;">@Sneha &amp; @Yu</td> </tr> <tr> <td style="text-align: left;">12/24</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251224-mercariadventcalendar/">「AIが学習しやすいナレッジ基盤」メルカリが全社で導入したNotion Architecture ver1.0</a></td> <td style="text-align: left;">@kiko &amp; aisaka</td> </tr> <tr> <td style="text-align: left;">12/25</td> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251225-mercari-ai-native-company/">AI-Nativeという選択 ー 正解のない時代に、メルカリが選んだ指針</a></td> <td style="text-align: left;">@kimuras</td> </tr> </tbody> </table> <p>最初の記事は、「<a href="https://engineering.mercari.com/en/blog/entry/20251127-websocket-xss-vulnerability-discovery-my-security-journey-at-mercari/">Websocket XSS vulnerability discovery: My security journey at Mercari</a>」です。</p> <p>どうぞお楽しみに!</p> 【mercari GEARS 2025】セッション以外の楽しみ方をご紹介https://engineering.mercari.com/blog/entry/20251105-mercarigears2025-enjoy-besides-sessions/https://engineering.mercari.com/blog/entry/20251105-mercarigears2025-enjoy-besides-sessions/<p>こんにちは!メルカリ Engineering Office の@mikichinです。 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます! 2018年に実施し [&hellip;]</p> Wed, 05 Nov 2025 10:00:10 GMT<p>こんにちは!メルカリ Engineering Office の<a href="https://x.com/chida_miki" title="@mikichin">@mikichin</a>です。<br /> 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます!</p> <p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/TDXzEjwqbaw?si=QJTLP0JGhJtu2kIP" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p> <p>2018年に実施した「Mercari Tech Conf 2018」から7年の時を経て、久しぶりのオフラインでの開催となります。<br /> テーマは「メルカリエンジニアリングの今」。<br /> 今年の全社的なテーマでもある「AI-Native」についてはもちろん、2018年以降メルカリグループのエンジニアリングがどのように変化してきたかを、技術・組織・カルチャーの観点からご紹介します。<br /> オンライン配信はありませんので、ぜひ会場でご自身の目と耳で確かめてください!!</p> <p>セッションの紹介記事はこちらをご確認ください。<br /> PASSION Stage のURL:<a href="https://engineering.mercari.com/blog/entry/20251008-mercarigears2025-passion-stage/">https://engineering.mercari.com/blog/entry/20251008-mercarigears2025-passion-stage/</a></p> <p>GROW Stage のURL:<a href="https://engineering.mercari.com/blog/entry/20251009-mercarigears2025-grow-stage/">https://engineering.mercari.com/blog/entry/20251009-mercarigears2025-grow-stage/</a></p> <p>MECHANISM Stage のURL:<a href="https://engineering.mercari.com/blog/entry/20251010-mercarigears2025-mechanism-stage/">https://engineering.mercari.com/blog/entry/20251010-mercarigears2025-mechanism-stage/</a></p> <p>本記事では、オフラインイベントならではのセッション以外の楽しみ方をご紹介します!</p> <h2>フロアマップ</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/2f7425a0--2025-11-04-20.50.35-1024x623.png" alt="" /></p> <p>会場は、メルカリのエンジニアリング組織における信念や行動の基盤となる共通認識を明文化した「<a href="https://engineering.mercari.com/culture/" title="Mercari Engineering Principles">Mercari Engineering Principles</a>」をモチーフにした「PASSION Stage」「GROW Stage」「MECHANISM Stage」の3つのステージがあり、ここでプレゼンテーションを聞くことができます。<br /> その他、Ask the SpeakerやTech Quizを実施している「COLLABORATION Lounge」、「Unconference」ルーム、「Break Area」があります。</p> <h2>スタンプラリー</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/416f9499-img_9799-1024x768.jpg" alt="" /></p> <p>会場に到着したら、名札を受け取ります。会場で参加者同士が話しかけやすいように、お名前や技術領域、所属など記載してくださいね。</p> <p>受付時に、名札とあわせてスタンプラリーのカードをお渡しします。スタンプラリーの詳細はカードに説明文がありますが、セッションに参加したり、Tech Quizに回答したりするとステッカーをもらえるほか、参加者同士で交換しながら集めていきます。集めたステッカーの枚数でもらえるグッズが異なりますので、ぜひ全部集めてみてください!</p> <h2>ポスターセッション</h2> <p>本イベントではプレゼンテーションだけではなく、ポスターセッションを準備しています。今回はEngineering組織だけではなく、メルカリR4Dラボでの研究も含め14つの発表があります。<br /> ポスターセッションの詳細は、下記ブログ記事をご確認ください。<br /> <a href="https://engineering.mercari.com/blog/entry/20251029-mercarigears2025-poster/">https://engineering.mercari.com/blog/entry/20251029-mercarigears2025-poster/</a></p> <p>ポスターセッションでは、発表者の方が掲示されているポスターの前に立っているので、質問をしたり、情報交換をしたりできます。ぜひ、お気軽にお立ち寄りください。<br /> ※常に、発表者がポスター前に立っているわけではありません。いない時間帯もありますこと、ご了承ください。</p> <h2>Ask the Speaker</h2> <p>セッションは聞いて終わりではありません。各セッション終了後には、登壇者と直接お話しいただける時間を設けております。セッション内では触れられなかった詳細な内容から、率直なご質問まで、疑問を解消し理解を深めていただける貴重な機会です。<br /> 「COLLABORATION Lounge」までお越しください。</p> <h2>Unconference Area</h2> <p>事前に用意されたテーマを待つ必要はありません。ご自身が話したいトピックを持ち込み、その場で議論を始めてみませんか。メルカリメンバーと意見を交わすのはもちろん、当日に提示されるテーマをきっかけに知見を交換することも可能です。知識や経験が交差する場を、ぜひご活用ください。<br /> 「Unconference」ルームまでお越しください。</p> <h2>Tech Quiz</h2> <p>メルカリのスペシャリストが用意したクイズに挑戦してみませんか。Backend、Clientなど各技術領域ごとにクイズを準備しています。普段あまり触れることのない分野でも大丈夫です。<br /> Tech Quiz AreaにはQuiz作成者もいる予定なので、お気軽にお声がけください。また、参加者同士で意見を出し合って一緒に考えてみましょう!</p> <h2>オリジナルグッズやお菓子</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/89420f47-img_9949-1024x768.jpg" alt="" /></p> <p>本イベント用に準備している「mercari GEARS 2025」オリジナルグッズです。<br /> ぜひ、ステッカーを集めて「Stamp Rally Kiosk」でGETしてくださいね!</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/3bb5e1df--2025-11-04-20.55.19-1024x395.png" alt="" /></p> <p>参加者同士でお話をするときのお供に、Coffee Standにて、オリジナルのお菓子やコーヒーを受け取ってください。</p> <p>「mercari GEARS 2025」では、単なる情報伝達の場ではなく、オフラインイベントならではの経験を共有し、交流を通じて新たな機会が生み出されることを期待しています。<br /> プレゼンテーション以外にもさまざまなコンテンツを準備していますので、ぜひ積極的にご参加ください。</p> <p>「mercari GEARS 2025」のお申し込みは<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="こちらから">こちらから</a>。</p> <h2>イベント詳細</h2> <p>開催日時:<br /> 2025年11月13日(木) 11:00-18:00</p> <p>概要:<br /> mercari GEARS 2025は、メルカリグループのエンジニアリング組織の技術への挑戦と、カルチャーを体感する技術イベントです。<br /> 本イベントは、単なる情報伝達の場ではなく、エンジニアたちが出会い、経験を共有し、交流を通じて新たな機会が生み出されることを目的としています。</p> <p>参加費:無料<br /> 会場:TODA HALL &amp; CONFERENCE TOKYO<br /> 参加方法:<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="こちらのページ">こちらのページ</a>にてお申し込みください。<br /> 【<a href="https://gears.mercari.com/" title="公式サイト">公式サイト</a>】</p> <p>本イベントに関する追加情報があれば、随時 <a href="https://x.com/MercariGears" title="@MercariGears">@MercariGears</a> でお知らせしますので、気になる方はぜひフォローをお願いします。</p> iOSDC Japan 2025に参加しました #iosdc #iwillbloghttps://engineering.mercari.com/blog/entry/20251031-iosdc-2025-report/https://engineering.mercari.com/blog/entry/20251031-iosdc-2025-report/<p>こんにちは。株式会社メルカリ iOSエンジニアのkntkです。 9月19日から9月21日にかけて開催された「iOSDC Japan 2025」にメルカリはゴールドスポンサーとして参加しました。 本記事では、その参加レポー [&hellip;]</p> Tue, 04 Nov 2025 10:00:20 GMT<p>こんにちは。株式会社メルカリ iOSエンジニアの<a href="https://x.com/kntkymt">kntk</a>です。<br /> 9月19日から9月21日にかけて開催された「<a href="https://iosdc.jp/2025/">iOSDC Japan 2025</a>」にメルカリはゴールドスポンサーとして参加しました。<br /> 本記事では、その参加レポートをお届けします!</p> <h1>Swiftコードバトル</h1> <p><strong>私kntkはiOSDC Japan 2025のday 0に開催された企画、<a href="https://fortee.jp/iosdc-japan-2025/proposal/7a9ef46c-e16d-469e-bd90-028e3ca69f5b">Swiftコードバトル2025</a>で優勝しました。</strong><br /> iOSDC Japan 2025の運営の皆さんには貴重な体験をさせていただき、本当にありがとうございました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/01af2c15-img_3384-768x1024.jpg" alt="" /></p> <p><strong>後日iOSDC Japan 2025運営から頂いた優勝記念品</strong></p> <h2>Swiftコードバトルとは</h2> <blockquote> <p>Swiftコードバトルはお題で指示された動作をするSwiftコードをより短く書けた方が勝ち、という競技です。 お題は決して難しいものではなく、少し練習すればSwiftプログラマであればどなたでも参加できる難易度を目指しています。<br /> お題例1: 入力された文字列を逆順にして出力するプログラムを書いてください。<br /> お題例2: 与えられた整数リストの要素の合計を計算するプログラムを書いてください。</p> </blockquote> <p><a href="https://iosdc.connpass.com/event/365136/">Swiftコードバトル2025予選</a>の説明文から引用</p> <p>一般的にはコードゴルフと呼ばれる競技になります。<br /> 昨年のiOSDC Japan 2024から開催されている企画で、今年が第2回目となります。<br /> 第1回目は私も参加し準優勝しました。<br /> <a href="https://engineering.mercari.com/blog/entry/20240925-iosdc-2024-report/">iOSDC Japan 2024に参加・登壇しました #iosdc #iwillblog</a></p> <p>昨年からの変化として、問題の複雑度が上がったように感じました。その結果<strong>回答が多様化し、戦略的な立ち回りが求められるようになった</strong>と思います。<br /> その影響もあってか、今年は問題文と一緒にサンプルコードが提供されるようになりました。</p> <h2>感想</h2> <p>去年は決勝で敗退して悔しかったのですが、今年は問題との相性などが巡って無事優勝することができて非常に嬉しかったです。予選から接戦が多く、Swiftコードバトルの環境が成熟して来ているのを感じました。</p> <p>また、<strong>参戦することで対戦中により良い解法を思いついた際の爽快感や、逆に思い付けなかった際の悔しさを味わうことができ、Swiftコードバトルをより深く楽しむことができます。</strong>是非皆さんも来年(開催された場合は)参戦してみてはいかがでしょうか。</p> <p>Swiftコードバトルの詳しい解説や私の戦略を書いた記事もありますので、ぜひ興味があればご覧ください!</p> <p><a href="https://engineering.mercari.com/blog/entry/20251031-swift-code-battle-2025-report">Swiftコードバトル2025で優勝しました #iosdc #iwillblog</a></p> <h1>登壇</h1> <p>株式会社メルカリからは私がLTで登壇しました。</p> <h2><a href="https://fortee.jp/iosdc-japan-2025/proposal/5d8aba2e-2123-4a63-bda8-74f68a40e64b">“奇妙”なSwift</a>(LT5分): <a href="https://x.com/kntkymt">kntk</a></h2> <p>Swiftにはさまざまな文法があり、それらは明示的・直感的で読みやすいことに定評がある一方で、Swiftの特定の文法を応用すると一見&quot;奇妙&quot;なプログラムも記述できます。<strong>”奇妙”なプログラムの背景にはさまざまなSwiftのテクニックが含まれており、解き明かすことで新たな発見を得ることができます。</strong></p> <p>このLTでは、”奇妙”なSwiftの例とそれらの背景にあるテクニックを3つ紹介しました。特に2つ目の例で紹介した、Switch文の内部で利用されているパターンマッチング演算子は利用することで冗長な表現を減らせる実用的なテクニックだと考えています。<a href="https://github.com/swiftlang/swift-syntax/blob/ef367f7370c51200ed95b384e03d545f59c7fc98/Sources/SwiftParser/TokenSpec.swift#L137-L143">実際にSwiftSyntaxなどの1st party libraryでも利用されています</a>。</p> <p>また、最後の例ではSwiftコードバトルで利用できる関数呼び出しの文字数短縮テクニックを紹介しました。この構成はプロポーザル提出時に考えたものなのですが、day0のSwiftコードバトルで優勝したことによって綺麗なオチになって良かったなと思います。</p> <h1>メルカリスポンサーブース</h1> <p>メルカリスポンサーブースではAIツールに関するポストイット企画を実施しました。1日1問、合計3問の質問を用意し実施しました。このセクションでは、ポストイットの回答結果を共有します!</p> <p>※目視で回答の集計を行っている関係上、集計結果はおおよその値となります。</p> <h2>Day 0: 普段iOS開発で使っているAI Coding Assistant Toolは何ですか?</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/11/c8f8984e-img_1931-1-754x1024.jpg" alt="" /></p> <p>合計回答数: 約90件(1件のポストイットに対して複数回答あり)</p> <p>回答数上位のツールから順に列挙すると以下の様になります。</p> <ul> <li>Claude Code <ul> <li>月定額で利用でき、利用上限も多くコスパが良いと感じているため。</li> </ul> </li> <li>GitHub Copilot <ul> <li>企業が導入しているため。</li> <li>学生は無料で利用できるため。</li> </ul> </li> <li>Gemini <ul> <li>学生は無料で利用できるため。</li> </ul> </li> <li>Cursor</li> <li>ChatGPT</li> <li>Codex</li> <li>Devin</li> <li>Amazon Q Developer</li> <li>Codeium</li> <li>Alex Sidebar <ul> <li>Xcode用のAI Coding Assistant。</li> </ul> </li> <li>Zed</li> <li>Grok</li> </ul> <p>私が初耳のツールも含まれており、新しいツールを知る良い機会となりました。</p> <h2>Day 1: iOSアプリの開発で、生成AIが最も役立つと思う作業は何ですか?</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/abb98608-img_1907-680x1024.jpeg" alt="" /></p> <p>合計回答数: 約180件<br /> 次の回答が上位となりました。</p> <ul> <li>テストコード生成</li> <li>プロトタイプ作成</li> <li>UI実装</li> <li>コードレビュー</li> <li>調査・コードリーディング</li> <li>リファクタリング</li> </ul> <p>また、来場者の方から具体的なプラクティスも聞くことができました。</p> <ul> <li>実装より先にテストコードを生成することで、AIが特定の実装に依存したテストコードを書くのを防いでいる。(テストコード生成)</li> <li>AIの生成したコードは品質に疑問があるので、主にコードを読ませる用途で利用している。(調査・コードリーディング)</li> </ul> <p>全体的にAIの使い方を試行錯誤しているお話が多く、さまざまな取り組みや現状の感触を共有いただきました。今後AI Coding手法が発展していくのが楽しみですね!</p> <h2>Day 2: 生成AIがアプリ開発者の作業をどう効率化していると思いますか?</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/32e843f6-img_1926-801x1024.jpg" alt="" /></p> <p>合計回答数: 約100件</p> <p>全体的に作業効率化に関する回答が多く、具体的には次の項目の効率化が大きいという声がありました。</p> <ul> <li>アイデア出し・壁打ち</li> <li>調査</li> <li>プロトタイプ</li> <li>実装</li> <li>レビュー</li> </ul> <p>効率化の結果、「設計に集中できる」「意思決定が早くなった」というお話もありました。<br /> 私も業務でAIを使い冗長な作業が減った結果、設計に集中できるようになったと感じており、共感できるお話が多くありました。</p> <h1>まとめ</h1> <p>メルカリブースに来てくださった参加者の皆さん、AIツールに関するノウハウを共有していただきありがとうございました!私自身も参加者の方とAIツールのノウハウについて話す中で大変勉強になりました。</p> <p><strong>最後に、iOSDC Japan 2025 の運営の皆様お疲れ様でした&amp;ありがとうございました!また来年も参加したいなと思います! #iosdc #iwillblog</strong></p> Swiftコードバトル2025で優勝しました #iosdc #iwillbloghttps://engineering.mercari.com/blog/entry/20251031-swift-code-battle-2025-report/https://engineering.mercari.com/blog/entry/20251031-swift-code-battle-2025-report/<p>こんにちは。株式会社メルカリ iOSエンジニアのkntkです。 私kntkは9月19日から9月21日にかけて開催された「iOSDC Japan 2025」のday 0に開催された企画、Swiftコードバトル2025で優勝 [&hellip;]</p> Tue, 04 Nov 2025 10:00:13 GMT<p>こんにちは。株式会社メルカリ iOSエンジニアの<a href="https://x.com/kntkymt">kntk</a>です。<br /> <strong>私kntkは9月19日から9月21日にかけて開催された「<a href="https://iosdc.jp/2025/">iOSDC Japan 2025</a>」のday 0に開催された企画、<a href="https://fortee.jp/iosdc-japan-2025/proposal/7a9ef46c-e16d-469e-bd90-028e3ca69f5b">Swiftコードバトル2025</a>で優勝しました。</strong><br /> iOSDC Japan 2025の運営の皆さんには貴重な体験をさせていただき、本当にありがとうございました。</p> <p>せっかくの機会なので、この記事では<strong>私がどのような戦略やプロセスで参戦したのかを言語化</strong>しようと思います。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/01af2c15-img_3384-768x1024.jpg" alt="" /></p> <p><strong>後日iOSDC Japan 2025運営から頂いた優勝記念品</strong></p> <p>また、メルカリは「<a href="https://iosdc.jp/2025/">iOSDC Japan 2025</a>」にゴールドスポンサーとして参加しており、その様子はこちらをご確認ください!<br /> <a href="https://engineering.mercari.com/blog/entry/20251031-iosdc-2025-report">iOSDC Japan 2025に参加しました #iosdc #iwillblog</a></p> <h1>Swiftコードバトルとは</h1> <blockquote> <p>Swiftコードバトルはお題で指示された動作をするSwiftコードをより短く書けた方が勝ち、という競技です。 お題は決して難しいものではなく、少し練習すればSwiftプログラマであればどなたでも参加できる難易度を目指しています。<br /> お題例1: 入力された文字列を逆順にして出力するプログラムを書いてください。<br /> お題例2: 与えられた整数リストの要素の合計を計算するプログラムを書いてください。</p> </blockquote> <p><a href="https://iosdc.connpass.com/event/365136/">Swiftコードバトル2025予選</a>の説明文から引用</p> <p>一般的にはコードゴルフと呼ばれる競技になります。<br /> 昨年のiOSDC Japan 2024から開催されている企画で、今年が第2回目となります。<br /> 第1回目は私も参加し準優勝しました。<br /> <a href="https://engineering.mercari.com/blog/entry/20240925-iosdc-2024-report/">iOSDC Japan 2024に参加・登壇しました #iosdc #iwillblog</a></p> <p>昨年からの変化として、問題の複雑度が上がったように感じました。その結果<strong>回答が多様化し、戦略的な立ち回りが求められるようになった</strong>と思います。<br /> その影響もあってか、今年は問題文と一緒にサンプルコードが提供されるようになりました。</p> <h1>何をする競技なのか</h1> <p>Swiftコードバトルの問題には文字数短縮余地のある<strong>「最適化ポイント」</strong>が複数個存在し、我々選手は<strong>個々の最適化ポイントに対して文字数短縮方法を考え、文字数短縮を行っています。</strong><br /> 問題が複数個の小問で構成されており、小問の合計スコアで競っている、というイメージを私は持っています。(実際には小問が相互に関係しあう場合があり、単純に可分できるわけではない。)</p> <p>最適化ポイントについて、昨年の決勝問題を例に紹介します。</p> <blockquote> <p>標準入力の各行に、ちょうど5文字からなる英単語が一つずつ並んでいます。<br /> 与えられた単語と「iOSDC」のハミング距離を出力してください。大文字と小文字は区別します。<br /> 「iOSDC」なら「0」、「CDSOi」なら「4」、「iosos」なら「4」を出力します。<br /> すべての行についてこの手順を繰り返してください。</p> </blockquote> <pre><code class="language-swift">// 回答a while let a = readLine() { print(zip(&quot;iOSDC&quot;, a).filter { $0 != $1 }.count) } // 回答b while let a = readLine() { print(zip(a, &quot;iOSDC&quot;).reduce(0) { $0 + ($1.0 != $1.1 ? 1 : 0) }) }</code></pre> <p>この問題には結果的に以下の最適化ポイントがありました。</p> <ol> <li><strong>“iOSDC”と入力にどのようにアクセスするか?</strong> <ul> <li>a. それぞれにインデックスアクセス</li> <li>b. <code>zip</code>でまとめてからアクセス (最短解)</li> </ul> </li> <li><strong>異なる文字数をどの様にカウントするか?</strong> <ul> <li>a. カウンタを持ってfor文でインクリメント</li> <li>b. <code>reduce</code>でカウンタをインクリメント</li> <li>c. <code>filter.count</code> (最短解)</li> </ul> </li> </ol> <p><strong>これらの最適化ポイントは問題に明示されていないので、まず最適化ポイントを発見する必要があります。</strong>「ここは複数の書き方ができるな」と、一目見ただけで発見できるわかりやすいポイントもあれば、問題を解く中で問題の理解度が上がることで発見できるポイントや、間違い探しのようにじっくり考え込んでやっと発見できるポイントもあります。</p> <p>最適化ポイントを発見した後は文字数短縮方法を考えて実装します。<strong>Swiftの知識と競プロ的な知識を総合的に活用して短縮方法を考える必要があります</strong>(詳しくは後述)。</p> <p>実際の回答を見てみると、回答aだと1, 2両方のポイントで最短解を採用できている一方で、回答bだと2で非最短解であるreduceを採用しており、2.の短縮方法で勝敗が決まった結果になっています。</p> <p>このように、<strong>問題に存在する最適化ポイントを発見し、最適な短縮方法を考える</strong>ことがこの競技の基本的な作業になると考えています。問題が複雑な場合は全ての最適化ポイントを発見・回答しきれず時間切れになることもあり、<strong>最適化ポイントを発見する速さと短縮方法を考える速さも重要になってきます。</strong></p> <h1>サンプルコード vs 0から実装</h1> <p>問題文と一緒にサンプルコードが提供されるようになったことにより、<strong>大きな方針として二つの作戦</strong>が存在していました。(コードバトル予選でも参加者間で話題になっていました。)</p> <p><strong>サンプルコードを元に文字数短縮を行う作戦</strong></p> <p>初期実装コストがなく、個々の最適化ポイントに順に対処していけば安定してスコアを伸ばせる利点があります。その一方で、サンプルコードに存在する冗長な表現の修正に時間を要したり、既存のロジックや構造に縛られて大幅な変更をしにくいなどの欠点があります。</p> <p><strong>サンプルコードを用いず0からコードを実装する作戦</strong></p> <p>サンプルコードの構造やロジックに縛られないため、最初から一気に短いプログラムを記述して高いスコアを狙える利点があります。その一方で、初期実装コストが必要な上、テストケースが通らない間違ったコードを書いてしまい、時間を消費する危険性があります。</p> <p><strong>実際に取った作戦</strong></p> <p>私はサンプルコードを元に文字数短縮を行う作戦で進めました。理由としては後者に必要となる競プロ的な能力に自信がなかったというのと、前者の安定してスコアを取れる点が複数試合を行う上で有利だと考えたためです。</p> <h1>対戦前の準備</h1> <p>競プロ等でも一般的に有効な手法だと思いますが、<strong>傾向を把握し、その対策を行っていました。</strong>事前に頻出パターンとその短縮方法(テクニック)調査して知識化し、対戦中はそれらの知識を適用できるようにしていました。これによって、問題に存在する複数の最適化ポイントのうち<strong>8-9割程度を事前知識で瞬時に発見して解くことができ、対戦時間を残りの1-2割(初見のパターンや一般化が難しい複雑なパターン)への対処に集中できていました。</strong></p> <p>具体的にどのような短縮テクニックを調査・利用していたのか、実際の準決勝の問題を例に紹介します。</p> <blockquote> <p>標準入力の各行に、英字のみからなる文字列が与えられます。キャメルケースをスネークケースに変換し、改行区切りで出力してください。</p> </blockquote> <p><strong>出題側から提供されるサンプル回答(読みやすさのため一部変更)</strong></p> <pre><code class="language-swift">while let line = readLine() { var result = &quot;&quot; for (index, character) in input.enumerated() { if character.isUppercase &amp;&amp; index &gt; 0 { result.append(&quot;_&quot;) result.append(character.lowercased()) } else { result.append(character.lowercased()) } } print(result) }</code></pre> <p>この問題では次のような文字数短縮テクニックが利用できます。</p> <ul> <li><strong><code>String.append(Character)</code>は<code>+=</code>で代替可能</strong> <ul> <li><code>RangeReplaceableCollection</code>の実装由来。<code>String</code>は<code>RangeReplaceableCollection</code>に準拠しているため。</li> </ul> </li> <li><strong><code>If aaa &amp;&amp; bbb</code>は<code>if aaa, bbb</code>に代替可能</strong></li> <li><strong>変数名の一文字化</strong></li> <li><strong>共通項の抜き出し(<code> r += c.lowercased()</code>)</strong></li> </ul> <p><strong>それらのテクニックを利用したプログラム</strong></p> <pre><code class="language-swift">while let l = readLine() { var r = &quot;&quot; for (i, c) in l.enumerated() { if c.isUppercase, i &gt; 0 { r += &quot;_&quot; } r += c.lowercased() } print(r) }</code></pre> <p>その他、多数のテクニックをドキュメントにまとめておき、対戦前に眺めていました。<br /> また、<strong>「この手法では短縮できない」という知識も持っておくことで、対戦中の試行錯誤時におけるノイズを減らしていました。</strong></p> <p>例を一つ紹介すると</p> <p><strong><code>for</code>文より<code>.forEach</code>で書いた方が短い?-&gt; for文の方が常に短い。</strong></p> <p><code>for i in a {</code>と<code>a.forEach { i in</code>を比較すると後者の方が5文字多くなります。<br /> また、<code>forEach</code>のクロージャーで省略引数名<code>$0</code>を使った場合<code>i in</code>を省略できますが、ここでの省略数は3文字なのでメソッド呼び出し部分だけで結果に2文字多くなり赤字です。</p> <pre><code>// 8文字 for i in a { // 13文字 a.forEach { i in // 10文字 a.forEach {</code></pre> <h1>対戦中</h1> <p>先述の通り、対戦中は冒頭に事前知識を用いて頻出のパターンの文字数短縮を行い、<strong>ほとんどの時間を初見パターンや一般化が難しい複雑なパターンの文字数短縮方法を考えることに集中していました。</strong></p> <p>具体的に文字数短縮方法を考えるプロセスについて紹介すると、<br /> 各最適化ポイントについて、<strong>文字数短縮方法には主に二つの側面</strong>があると考えていました。</p> <ul> <li><strong>A: 文法・記法を工夫する</strong></li> <li><strong>B: ロジックを工夫する</strong></li> </ul> <h2>A: 文法・記法を工夫する</h2> <p><strong>既存の記述の文法や記法を工夫することで文字数短縮を行う側面</strong>です。先述の準決勝での例にあるように変数名を一文字にしたり、<code>String.append(Character)</code>を<code>+=</code>に置き換えたりするなどが当たります。</p> <p>また、予選の問題ではSwiftの型推論や標準ライブラリの関数・メソッドを応用して文字数を減らす場面がありました。(Swiftらしさが強く現れる問題で特に記憶に残っています。)</p> <p><strong>コードバトル予選、ROT13変換問題の私の回答</strong></p> <pre><code class="language-swift">while let p = readLine() { print(String(p.map { a in let k = a.asciiValue! &amp;- 52 return a.isLetter ? .init( .init( 13...38 ~= k ? k % 26 + 65 : (k - 32) % 26 + 97 ) ) : a })) }</code></pre> <ul> <li><strong><code>Character.init(UnicodeScalar.init(...))</code>を<code>.init(.init())</code>で代替</strong> <ul> <li>型推論によって型名を省略</li> <li><code>String.init</code>の期待する型(<code>[Character]</code>)が<code>p.map</code>の帰り値の型を経由してクロージャーのreturn statementの型推論まで伝播</li> </ul> </li> <li><strong><code>Range.contains</code>を<code>~=</code>で代替</strong> <ul> <li>標準ライブラリに演算子定義が存在</li> </ul> </li> <li><strong>オーバーフロー演算子<code>&amp;-</code>を利用して事前計算</strong> <ul> <li>通常の演算子だとランタイムエラーになるテストケースが存在</li> </ul> </li> </ul> <p>このように、<strong>Swiftの知識(型推論・文法・言語機能・標準ライブラリなど)が活かされる側面</strong>です。Appleの公式ドキュメントや<a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/">TSPL (The Swift Programming Language)</a>を読むことでこの側面の思考力が鍛えられると思います。</p> <h2>B: ロジックを工夫する</h2> <p><strong>既存のロジックをより短い記述のロジックに変えることで文字数短縮を行う側面</strong>です。こちらは平たく言えば競プロ的な側面です。ただし、実行速度向上ではなく文字数短縮が目的です。</p> <p>実は先述した準決勝の私の回答は最短解ではなく、さらにロジックを工夫することで短縮できる余地がありました。(前回優勝者にご指摘いただきました。)</p> <pre><code class="language-swift">while let l = readLine() { var r = &quot;&quot; for c in l { if c.isUppercase, r != &quot;&quot; { r += &quot;_&quot; } r += c.lowercased() } print(r) }</code></pre> <p><code>index &gt; 0</code>というのは、つまり「最初以外の要素の時」を意味する条件なので、こちらは<code>r != &quot;&quot;</code>で代替可能なわけです。また、コードバトル予選では有名な「番兵法」を利用することで文字数を大幅に減らすシーンもありました。</p> <p>このように、<strong>問題・プログラムの意図を読みとるのがこちらの側面になり、競プロ的な力が活かされる側面</strong>です。競技プログラミングサイト等で過去問を解くことで、この側面の思考力を鍛えることができると思います。</p> <p>また、AとBの側面は排他的ではなく、Bを適用した後の記述に新たにAが適用できる場合もあり、<strong>AとBの組み合わせによって文字数短縮方法が多数存在していました。</strong></p> <h2>戦略</h2> <p>A・Bの例で示したように、<strong>より短い解に辿り着くにはAとB両方の側面を組み合わせて解法を考えることが必要になってきます。</strong><br /> しかし、解法を考える際はコードを書いてみないと実際に文字数短縮が可能か分からないことが多く、制限時間の中で試せる解法の候補数も限られてくるため、<strong>ある程度解法に”あたり”を付ける必要があります。</strong>ここで、<strong>私は迷ったらAを優先して攻める戦略を立てました。</strong>理由としては私がAの方が得意というのと、「Aの側面を極めた回答の方がSwift特有のテクニックが現れて面白いだろう」と考えたからです。</p> <p>結果的に決勝でもSwiftの文字列展開を利用した手法を選択し、Swiftらしいコードで優勝ができたのは良かったなと考えています。</p> <p><strong>アスキーアートを出力する決勝問題の私の回答(見やすさのため一部スペースを省略)</strong></p> <pre><code class="language-swift">let a = &quot;######&quot;, i = a + a, f = &quot;\(i)##&quot;, j = f + f (中略) print( &quot;&quot;&quot; \(f) \(j) \(f) \(j) ## \(a) \(a) ## \(a) \(a) \(a)#### ## \(a) \(f+a) \(a) \(a)#### ## \(a) \(f+a) \(a) (中略) &quot;&quot;&quot; )</code></pre> <h1>まとめ</h1> <p>Swiftコードバトルは問題に存在する最適化ポイントを発見し、Swiftの知識と競プロ的な知識を総合的に活用して最適な短縮方法を考える競技です。制限時間があるため、事前に頻出パターンの対策を行うことによって対戦時間を有効に活用することができます。</p> <p>Swiftコードバトルのテクニックは一見奇妙で役に立たなそうに見えるかもしれません。しかし<strong>その背景にはSwiftの文法・言語機能の知識やロジックを読みとる力などが隠れており、それらは日々の開発でも有益なものだと考えています。</strong>自分も参戦する中で多くを学ぶことができました。</p> <p>また、参戦することで対戦中により良い解法を思いついた際の爽快感や、逆に思い付けなかった際の悔しさを味わうことができ、Swiftコードバトルをより深く楽しむことができます。是非皆さんも来年(開催された場合は)参戦してみてはいかがでしょうか。</p> <p><strong>最後に、改めてiOSDC Japan 2025・Swiftコードバトル2025 の運営の皆さん、予選・本戦で対戦してくれた選手の皆さん、本当にありがとうございました! #iosdc #iwillblog</strong></p> 【mercari GEARS 2025】ポスターセッションをご紹介https://engineering.mercari.com/blog/entry/20251029-mercarigears2025-poster/https://engineering.mercari.com/blog/entry/20251029-mercarigears2025-poster/<p>こんにちは!メルカリ Engineering Office の@mikichinです。 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます! 2018年に実施し [&hellip;]</p> Wed, 29 Oct 2025 10:00:49 GMT<p>こんにちは!メルカリ Engineering Office の<a href="https://x.com/chida_miki" title="@mikichin">@mikichin</a>です。<br /> 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます!</p> <p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/TDXzEjwqbaw?si=QJTLP0JGhJtu2kIP" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p> <p>2018年に実施した「Mercari Tech Conf 2018」から7年の時を経て、久しぶりのオフラインでの開催となります。<br /> テーマは「メルカリエンジニアリングの今」。<br /> 今年の全社的なテーマでもある「AI-Native」についてはもちろん、2018年以降メルカリグループのエンジニアリングがどのように変化してきたかを、技術・組織・カルチャーの観点からご紹介します。<br /> オンライン配信はありませんので、ぜひ会場でご自身の目と耳で確かめてください!!</p> <p>また、今回はプレゼンテーションだけではなくポスターセッションもあります。<br /> ポスターセッションでは、発表者の方が掲示されているポスターの前に立っているので、質問をしたり、情報交換をしたりできます。ぜひ、お気軽にお立ち寄りください。<br /> ※常に、発表者がポスター前に立っているわけではありません。いない時間帯もありますこと、ご了承ください。</p> <p>本記事では、会場でしか見れないポスターセッションを一挙ご紹介!</p> <p>プレゼンテーションの紹介は下記記事をご参照ください。<br /> PASSION Stageのセッション紹介は<a href="https://engineering.mercari.com/blog/entry/20251008-mercarigears2025-passion-stage/" title="こちら">こちら</a>。<br /> GROW Stageのセッション紹介は<a href="https://engineering.mercari.com/blog/entry/20251009-mercarigears2025-grow-stage/" title="こちら">こちら</a>。<br /> MECHANISM Stageのセッション紹介は<a href="https://engineering.mercari.com/blog/entry/20251010-mercarigears2025-mechanism-stage/" title="こちら">こちら</a>。</p> <p>まだ申し込みをされていない方も、興味のあるセッションがあるはずです。お申し込みは<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="こちら">こちら</a>からお願いします。</p> <h3>メルカリグループにおけるAI-Nativeなインシデント管理の全貌と未来像</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/be3ee47d-ogp_poster-1_ja-1024x538.png" alt="" /></p> <p>LLMの普及により、インシデント対応・管理のあり方も大きく変わりつつあります。<br /> メルカリグループでは、複雑で負担の大きいインシデント管理を「AI-Native」に進化させることを決定しました。</p> <p>すでに導入している「IBIS」をはじめ、その周辺の仕組みや他のAI活用事例も紹介します。<br /> AIを取り入れることで、MTTRの短縮だけでなく、対応者の負担・ストレス軽減やコスト削減、さらにサービス信頼性の向上が期待できます。</p> <p>ただし、人間が担うべき領域も残ります。本発表では、メルカリグループの現在の取り組みと今後の展望をお伝えします。</p> <h3>The 3A’s: Simple Steps For Clean Unit Tests</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/2c4e1b08-ogp_poster-2_en-1024x538.png" alt="" /></p> <p>ソフトウェア開発のスピードが加速する中、新機能の追加や修正には、既存の機能を意図せず壊してしまうリスクが常に伴います。適切な安全策がなければ小さなミスが本番環境にリリースされ、多くのお客さまに影響を及ぼす可能性があります。 だからこそ、ユニットテストは極めて重要です。優れたユニットテストはコードの動作を検証するだけでなく、プロダクトの安定性と信頼性を確保し、開発チームが自信を持って変更を行える環境を支えます。 では、どうすればテストをシンプルでクリーン、かつ効果的に保つことができるのでしょうか。 そのための実績あるアプローチのひとつが、Arrange(準備)、Act(実行)、Assert(検証)の3Aフレームワークです。この3つのステップに従うことで、明確で保守性が高く、信頼できるユニットテストを簡単に書けるようになります。</p> <h3>Autonomous Support &#8211; Leveraging AI Bots for Scalable and Intelligent Operational Assistance</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/2a6e10ea-ogp_poster-3_en-1024x538.png" alt="" /></p> <p>現在、エンジニアはSlack上での場当たり的かつ反復的な質問対応や、絵文字によるトリアージといった非効率なワークフローに多くの時間を費やしており、チームによっては業務時間の10〜20%以上が問い合わせ対応に割かれるという課題があります。私たちはこの課題に対し、Slack上の雑多な問い合わせを迅速で信頼性の高い回答と標準化されたチケットに変換する、AI支援の自律型サポートシステムを構築しています。このbotはSlack上で直接対応を行い、適切なJIRA/GitHubチケットのトリアージや共有ナレッジベース(ドキュメント、過去のチケット、Slack、ソースコード)の検索を行い、回答を提案します。人間の対応が必要な場合は担当者へ引き継ぎ、自己完結可能な場合はその場で対応を完了し、そこから学習を重ねます。これにより、応答と解決の迅速化、中断の削減、そして自律解決率、エスカレーション率、削減できたエンジニア工数、CSAT(顧客満足度)、特定されたナレッジギャップといった指標を通じて効果を測定します。また、既存のプラットフォームツールを再利用することで、迅速な開発を可能にしています。</p> <h3>Toward a Global Identity Platform</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/5b41532a-ogp_poster-4_en-1024x538.png" alt="" /></p> <p>メルカリは2019年に越境EC事業をスタートしました。開始当時、海外のお客さまは機能の限られた代行業者の購入ページを使って商品を検索し、購入する必要がありました。その後、海外のお客さまにより良い購買体験を提供するため、メルカリはシステムを拡張し、海外でのサービス展開を始めました。この拡大において重要な要件がグローバルアカウントの導入です。本セッションでは、これまでの取り組みで達成した成果を共有するとともに、複数の国のお客さまをサポートするためにアイデンティティ基盤をさらに拡張していく今後の計画についてお話しします。</p> <h3>非エンジニア組織のAI-Native化に伴走した実践知</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/8b0ae0b6-ogp_poster-5_ja-1024x538.png" alt="" /></p> <p>非エンジニア組織が「AI-Native」へと変革する過程には、エンジニアの伴走が不可欠です。<br /> 本セッションでは、そのリアルな経験を紹介します。</p> <p>(1) AI活用で期待する結果を得るために入出力形式を工夫した事例<br /> (2) AIワークフローが突然停止した事例から学ぶライフサイクル管理の教訓<br /> (3) AI生成アプリをGASを使って安全にデプロイする手法</p> <p>といった、AI活用をプロトタイプから実運用への導くための実践知を共有します。</p> <h3>From Cluttered to Clear: Improving the Web Accessibility Design for Screen Reader Users in E-commerce With Generative AI</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/2dbc23f8-ogp_poster-6_en-1024x538.png" alt="" /></p> <p>視覚障がい者や弱視のユーザーは、スクリーンリーダーを使ってオンラインショッピングサイトを利用する際に、多くの障壁に直面しています。複雑なレイアウト、不明確なコンテンツ構造、視覚重視のデザインにより、特に初めて利用するサイトではストレスが多く非効率な体験となっています。これまでの支援ツールは商品説明や画像の代替テキストなど、個別の要素に焦点を当てられてきましたが、ページ全体におよぶ構造やナビゲーションの課題には十分対応できていませんでした。本研究では、生成AIを活用してショッピングサイトのHTMLコンテンツを自動的に再構成し、アクセシビリティを改善する方法を検討しました。研究は3段階の構成で行い、まずスクリーンリーダーのユーザーにインタビューを行い、次に生成AIを搭載したブラウザ拡張機能を開発し、最後に自動監査と実際の使用で評価を行いました。このツールはスクリーンリーダーの操作パターンに合わせてウェブコンテンツを動的に整えます。実験の結果、生成AIで再構成されたページではナビゲーション効率、コンテンツの分かりやすさ、全体的な使いやすさが大きく改善することが分かりました。参加者からは「セクションの順序がより論理的になった」「閲覧による疲労が減った」といった声が寄せられました。この成果は生成AIが既存サイトの構造そのものに作用することで、包括的かつユーザー中心のアクセシビリティ改善を実現できる可能性を示しています。</p> <h3>量子インターネット ー安心・安全でサステナブルなオンライン社会の実現に向けてー</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/87d5e951-ogp_poster-7_ja-1024x538.png" alt="" /></p> <p>メルカリの研究開発組織「R4D」では、近い将来に到来が予想されている「量子前提時代」を生き抜くメルカリを作るために、量子情報通信技術に関する研究を行っています。本ポスターでは、R4Dの量子チームが国内の研究機関と共同で行なっている量子インターネットに関する研究開発の全体像について紹介します。</p> <h3>中性原子量子コンピュータにおける表面符号を用いた消失エラー耐性プロトコル</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/fcba554e-ogp_poster-8_ja-1024x538.png" alt="" /></p> <p>光ピンセットによる中性原子アレイは、その優れた特性から量子コンピュータの有望な候補とされていますが、非パウリ誤り、消失誤り、リーケージ誤りが大きな課題となっています。従来の研究では、リーケージ誤りを消失誤りに変換できることは示されていましたが、変換された消失誤りが継続的に発生・蓄積する問題が残っていました。本研究では、この課題に対して、脱分極誤りと消失誤りを含む回路ベースのモンテカルロシミュレーションにより、消失誤りが平面コードに与える影響を評価しました。さらに、消失誤りへの耐性を高めるための新しいプロトコルを提案します。このプロトコルでは、オンラインでのコード変形を利用して、消失誤りが蓄積した「トラップ」から論理量子ビットを新しいトラップへと転送します。</p> <h3>「使い続ける力、未来をつなぐ」</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/87175dc2-ogp_poster-9_ja-1024x538.png" alt="" /></p> <p>人が使わなくなった製品を一度で使い終えず、次の誰かへ繋ぐ「リユース」は、私たち消費者が持続可能な社会のために選べる、最も身近で実践しやすい選択肢の一つです。本プレゼンテーションでは、リユースによって製品の使用期間がどれほど延長され、新品の購入をどれだけ代替できるのか、そのインパクトを明らかにします。</p> <p>このお話を通して、皆様がモノを手放すとき、あるいは新しいモノが欲しくなったときに、リユースを当たり前の選択肢として生活に取り入れ、「モノに隠された新たな価値」を実践していただくためのきっかけをご提供します。</p> <h3>オンラインフリマアプリにおける商品説明の人間–AI協働執筆:売り手および買い手の視点からの考察</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/79409173-ogp_poster-10_ja-1024x538.png" alt="" /></p> <p>オンラインのフリマアプリは、個人が中古品を他の個人に販売する手段として、特に日本で人気を博しています。出品のプロセスには、出品者が写真をアップロードし、潜在的な購入者が閲覧するための商品説明を書く作業が含まれます。近年は、大規模言語モデル(LLM)を用いた商品説明作成を通じて出品時の出品者の負担を軽減する、人間とAIの協働の応用領域が注目されています。本研究では、LLMに基づく支援が出品者の体験や商品の出品価格に与える影響に加え、人間とAIが協働して作成した商品説明の魅力に対する買い手の主観的な印象や選好にどのように影響するかを検討します。本研究の結果は、LLMベースのツールがオンライン中古市場に及ぼし得る影響の理解に寄与するとともに、フリマアプリ向けの人間—AI協働執筆システムの設計上の考慮点や今後の研究の方向性に関する示唆を提供します。</p> <h3>メルカリAdsにおけるAIを活用した広告審査の取り組み</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/dfb7883d-ogp_poster-11_ja-1024x538.png" alt="" /></p> <p>2024年9月に開始したメルカリAdsは、当初広告主から入稿いただいている広告に対しての審査を手動運用にて審査していました。<br /> 効率的な審査を模索し、AIを活用した広告審査システムを構築することで運用コストを削減しつつ、多くの広告素材に対して審査することが可能となりました。<br /> そのAI活用事例を共有します。</p> <h3>BFF保守の課題とgRPC Federationによる解決アプローチ</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/67c69848-ogp_poster-12_ja-1024x538.png" alt="" /></p> <p>マイクロサービスアーキテクチャにおけるBFF開発では、複数サービス間の型変換や依存関係管理により保守コストが増大します。本発表では、この課題に対してgRPC Federationを採用し、Protocol BuffersにDSLで定義することによってBFFを自動生成し、保守コストを大幅に削減した実例を紹介します。また、AIを活用したDSL記述支援の取り組みについても報告します。</p> <h3>Engineering Office is a Hub, connecting Engineering together</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/61df5dd2-ogp_poster-13_en-1024x538.png" alt="" /></p> <p>Engineering Officeはエンジニアリング部門のあらゆる部分を繋ぐハブの役割を持ちます。共通のオンボーディングからプロジェクトのサポート、エンジニアリングに関する情報まで、さまざまな要素を通してグループが方向性を合わせ、集中できるようにします。</p> <p>本発表では、自動化やAI、継続的なサービスライフサイクルを活用してビジネスニーズに迅速に対応し、メルカリ独自のエンジニアリング文化を維持するための、私たちのプロジェクトの一部をご紹介します。</p> <h3>Mercari における Recommendation System の概要</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/f0522edc-ogp_poster-14_ja-1024x538.png" alt="" /></p> <p>メルカリではホームスクリーンや商品詳細画面など様々な所で recommendation が行われており、その裏側にはそれぞれの特性に応じた技術が使われています。</p> <p>本発表ではメルカリにおける様々な recommendation とその裏で使われている技術について概要を共有し、議論を通じて情報交換したいと考えています。</p> <p>「mercari GEARS 2025」のお申し込みは<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="こちらから">こちらから</a>。</p> <h2>イベント詳細</h2> <p>開催日時:<br /> 2025年11月13日(木) 11:00-18:00</p> <p>概要:<br /> mercari GEARS 2025は、メルカリグループのエンジニアリング組織の技術への挑戦と、カルチャーを体感する技術イベントです。<br /> 本イベントは、単なる情報伝達の場ではなく、エンジニアたちが出会い、経験を共有し、交流を通じて新たな機会が生み出されることを目的としています。</p> <p>参加費:無料<br /> 会場:TODA HALL &amp; CONFERENCE TOKYO<br /> 参加方法:<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="こちらのページ">こちらのページ</a>にてお申し込みください。<br /> 【<a href="https://gears.mercari.com/" title="公式サイト">公式サイト</a>】</p> <p>本イベントに関する追加情報があれば、随時 <a href="https://x.com/MercariGears" title="@MercariGears">@MercariGears</a> でお知らせしますので、気になる方はぜひフォローをお願いします。</p> モジュラモノリスの品質を支えるリーダビリティチーム ― AI時代のスケーラブルなコード管理https://engineering.mercari.com/blog/entry/20251021-scaling-code-quality-modular-monolith-readability-team-ai-era/https://engineering.mercari.com/blog/entry/20251021-scaling-code-quality-modular-monolith-readability-team-ai-era/<p>1. イントロダクション こんにちは、Cross Border(XB) Engineeringのバックエンドエンジニアのosari.kです。本日は私が所属するリーダビリティチームの活動と、具体例として開発したバックエンド [&hellip;]</p> Tue, 21 Oct 2025 10:43:01 GMT<h2>1. イントロダクション</h2> <p>こんにちは、Cross Border(XB) Engineeringのバックエンドエンジニアのosari.kです。本日は私が所属するリーダビリティチームの活動と、具体例として開発したバックエンドの共通パッケージについて紹介します。</p> <p>メルカリグローバルアプリは、開発複雑性を抑えながら拡張性を保つためモジュラモノリスアーキテクチャを採用しています。モジュール間の依存関係を厳格化するため、システムはBFF層とTier1-4の階層構造で構成され、リクエストは上位から下位Tierへ流れます。モジュール間通信はProtocol Buffer + gRPCで標準化されています。詳しくは<a href="https://engineering.mercari.com/blog/entry/20251003-mercari-crossborder/" title="ブログシリーズ">ブログシリーズ</a>をご参照ください:</p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20251007-a09afcd49b/" title="グローバル展開にむけたアプリと基盤の再構築">グローバル展開にむけたアプリと基盤の再構築</a></li> <li><a href="https://engineering.mercari.com/blog/entry/20251007-behind-the-infrastructure-powering-global-expansion/" title="グローバル展開を支える基盤の裏側">グローバル展開を支える基盤の裏側</a></li> </ul> <p>しかし、モジュラモノリスを採用するだけでは、マイクロサービス開発で発生した課題を解決できません。サービス間の差異によるメンテナンスコストやオンボーディングコストの増加は、モジュール間でも同様に発生する可能性があります。これらを解決するには、コードのリーダビリティと一貫性の維持が不可欠です。</p> <p>そこで、グローバルアプリ開発の当初から<strong>リーダビリティチーム</strong>が組成されました。このチームは、モジュラモノリスの利点を最大限活かすため、Backendシステム全体のコードのリーダビリティを改善・維持する役割を担っています。メンバーは、アーキテクト、SREメンバー、バックエンドエンジニアで構成され、日本とインドの両拠点から参加しています。</p> <p>一貫性のあるコードを維持することで、開発者を柔軟にアサインでき、効率的な配置が可能になります。また、AI Agentを活用した開発においても、明確なガイドライン、自動チェック、一貫したコードベースがAIの活用効果を高めます。</p> <h2>2. リーダビリティチームの役割と活動</h2> <h3>2.1 目的</h3> <p>リーダビリティチームの主な目標は、コードの可読性を向上させることです。可読性が高く、一貫性のあるコードは、以下の効果をもたらします:</p> <ul> <li>新しいエンジニアの学習曲線を緩和し、オンボーディングを加速</li> <li>組織内での担当変更やチーム間の貢献を円滑化</li> <li>バグの検出や修正を容易にし、開発の品質とスピードを向上</li> </ul> <h3>2.2 活動内容</h3> <p>リーダビリティチームは以下の活動を通じて、コードの品質向上と開発スケールを支援しています:</p> <ul> <li><strong>コードレビュー</strong>: 複雑度の高いPRを中心にレビュー</li> <li><strong>ガイドラインの作成と維持</strong>: コードおよびPRガイドラインをGitHubリポジトリに集約</li> <li><strong>自動化ツールの開発</strong>: CIによる自動チェックを実装</li> <li><strong>ワークショップの開催</strong>: ベストプラクティスの共有</li> <li><strong>隔週ミーティング</strong>: ガイドライン、課題、改善点の議論</li> <li><strong>メトリクス駆動の改善</strong>: 開発ボトルネックの特定と継続的な改善</li> </ul> <p>中でもメトリクス駆動の改善については、GitHubのメトリクスを活用して目標を設定し、定期的にボトルネックを分析しています。特に以下の2つのメトリクスの改善に取り組んできました:</p> <ul> <li>一人あたり週何個のPull Requestをマージできたか</li> <li>Pull RequestをOpenしてからマージまでの時間</li> </ul> <p>これらを可視化することで、開発プロセスのボトルネックを特定し、継続的な改善につなげています。</p> <h2>3. AI時代のスケーラブルな品質管理</h2> <p>開発初期はメンバーが少なく、リーダビリティチームがほぼ全てのPull Requestをレビューできていました。しかし、開発が本格化し、開発メンバーの増加やAI Agent活用の普及により、PRのボリュームが急増し、従来の方法では対応できなくなってきました。</p> <p>幸い、ガイドラインをGitHubリポジトリに集約・維持していたため、LLMを活用したレビューの自動化基盤は整っていました。課題は、この基盤でどうスケーラブルに品質を担保するかでした。以下、具体的な取り組みをいくつか紹介します。</p> <p><strong>Claude活用</strong><br /> ClaudeでPull Requestを複雑性とサイズの観点から自動分類し、ラベル付けを行っています。Claudeは、PRをより小さい単位に分割する助言も提供します。</p> <p>このラベルを使い、複雑度が高いPull Requestを優先的にレビューできるようになりました。</p> <p>また、GitHubに集約したガイドラインを活用したClaudeによるレビューの自動化で、コードレビューとガイドラインの適用を効率化しています。</p> <p><strong>効率的レビュー</strong><br /> リーダビリティチームのレビューは必須ではありません。少なくとも一人のコードオーナーからレビューをもらえばPRはマージできます。これにより、リーダビリティチームが開発のボトルネックになりません。</p> <p>一方で、マージされたPRの中で複雑度が高いものは後追いでレビューします。こうした複雑なPRから課題を検知し、新しいパターンやライブラリを導入することで、今後の開発を改善していきます。このアプローチで、効率的にレビューリソースを配分し、最も重要な部分に注力できています。</p> <p><strong>自動化</strong><br /> ガイドラインを全て把握するのは困難なため、機械的にチェック可能なルールはCIに組み込んでいます。</p> <p>例えば、データベースのスキーマ設計では、社内のDatabase リライアビリティエンジニアと相談し、<a href="https://future-architect.github.io/arch-guidelines/documents/forDB/postgresql_guidelines.html" title="PostgreSQL ガイドライン">PostgreSQL ガイドライン</a>などを参考にガイドラインを策定しました。これを<a href="https://www.sqlfluff.com/" title="SQL Fluff">SQL Fluff</a>のプラグインとして実装し、自動チェックを行っています。</p> <p>これにより、レビュアーの負担を軽減し、人間のレビュアーはより高度な設計判断やアーキテクチャの妥当性に集中できます。</p> <h2>4. Feature Flag伝搬システムの設計</h2> <p>ここからは、リーダビリティチームが設計した品質管理の具体例として、Backendシステム全体で利用されるFeature Flag伝搬の仕組みを紹介します。</p> <h3>4.1 背景: Experiment Platformとモジュラモノリスアーキテクチャ</h3> <p>グローバルアプリでは、新機能のリリースを安全に行うため、メルカリの基盤システムである<strong>Experiment Platform</strong>を利用しています。この中でもFeature Flagを活用しており、お客さまごとに各種機能のオン・オフを管理できます。</p> <p>Feature Flagを使うことで、Web UI上で機能をオンにするお客さまを段階的に増やせるため、新機能のリリースを様子を見ながら進めることができます。問題が発生した場合も、システム自体を再デプロイすることなく、Web UI上で機能をオフに設定し、即座に無効化できます。Experiment Platformは割当(Assignments)を管理し、システム側でこの割当を取得して実装に組み込みます。</p> <p>Feature FlagはMobileアプリやWebアプリでも利用されています。これらのクライアントアプリでは、1人のお客さまが継続して利用するため、アプリ起動時などにFeature Flagを取得してキャッシュできます。一方、バックエンドシステムではリクエストごとにお客さまが異なるため、Experiment PlatformのAPIを毎回呼び出してデータを取得する必要があります。</p> <p>前述の通り、グローバルアプリのBackend systemではモジュラーモノリスを採用しており、クライアントアプリからの1リクエストが内部的には複数のモジュールへのリクエストを発生させます。</p> <div style="text-align: center"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/8eabf989-fig1-product-page-flow.png" alt="図1. 単純化した商品ページの例" width="800" class="size-medium wp-image-29132" /><br /> <br />図1. 単純化した商品ページの例 </div> <p>全てのモジュールがFeature FlagのAssignmentsを必要とする可能性があります。 このようなアーキテクチャのため、もし各モジュールが毎回Experiment Platformからデータを取得する場合、APIのレイテンシがその分増加してしまいます。</p> <div style="text-align: center"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/2ecb117a-fig2-latency-issue.png" alt="図2. 各モジュールがExperiment Platformを呼び出すとレイテンシが増加" width="800" class="size-medium wp-image-29132" /><br /> <br />図2. 各モジュールがExperiment Platformを呼び出すとレイテンシが増加 </div> <p>グローバルアプリの開発が進み、Feature Flagを利用する状況になってきた中で、レイテンシの増加を防ぐため、効率的にAssignmentsを参照するメカニズムとガイドラインが必要になりました。</p> <h3>4.2 設計要件と実装</h3> <p>Assignmentsの効率的な参照メカニズムを設計するにあたり、以下の要件を定義しました:</p> <ul> <li>Experiment Platformを一度だけ呼び出す</li> <li>ネットワークトラフィックを抑える</li> <li>割当に型安全なアクセス手段を提供する</li> <li>既存のコードが割当を使いたくなった場合のコードの変更量を最小限にする</li> </ul> <p>これらの要件を満たすために、 最終的に採用した解決策は、以下の3つの要素を組み合わせたものです:</p> <ol> <li><strong>gRPC metadataを使ってモジュール間のAssignments伝搬</strong>: シリアライズしたAssignmentsをgRPC metadata(HTTP Header)で運ぶ <ul> <li>BFF層のServer InterceptorがExperiment PlatformのAPIを呼び出し、Assignmentsを取得</li> <li>各モジュールのClient interceptorがAssignmentsをシリアライズしてgRPC metadataに設定</li> <li>BFF層以外のServer InterceptorがgRPC metadataから取得し、デリリアライズしてAssignmentsを取得</li> </ul> </li> <li><strong>context.Contextを使ってモジュール内のAssignments伝搬</strong>: <ul> <li>Server InterceptorがAssignmentsを<code>context.Context</code>に格納</li> <li>アプリケーションロジックはAssignmentsを<code>context.Context</code>から取得</li> </ul> </li> <li><strong>Protocol Bufferでの型定義</strong>: Assignmentsを明示的に定義し、型安全性を確保かつコンパクトなシリアライズを実現</li> </ol> <p></p> <div style="text-align: center"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/62997b06-fig3-solution.png" alt="図3. BFFがExperiment Platformを呼び出し、AssignmentsをgRPC metadataで伝搬" width="800" class="size-medium wp-image-29132" /><br /> <br />図3. BFFがExperiment Platformを呼び出し、AssignmentsをgRPC metadataで伝搬 </div> <p>BFFモジュールがExperiment platformのAPIを呼び出しAssignmentsを取得し、それを下位Tierのモジュールを呼び出すときにgRPC metadataで伝搬しています。 先程の図と異なり、BFFのみがExperiment platformを呼び出すため、レイテンシへの影響を抑えることができています。</p> <p>モジュール内の伝搬に<code>context.Context</code>を利用しているので、アプリケーションロジックがAssignmentsを参照したくなった場合も、コードの変更は最小限に抑えられます。</p> <p>一方でAssignmentsをリクエストに付与することでネットワークトラフィックには影響があります。そこで効率的なシリアライズ方法が必要です。</p> <p>Experiment PlatformのAssignmentsは、パラメータ名と値のkey-value形式です。Feature Flagの値は<code>&quot;true&quot;</code>、<code>&quot;false&quot;</code>、または未割り当ての3つの状態を取ります。未割り当ては、段階的リリースでお客さまの一部のみが割当対象になっている場合に発生します。</p> <p>例えば以下のような形式です(&quot;feature_flag1&quot;は未割り当てのため含まれていない):</p> <pre><code class="language-json">{ &quot;feature_flag2&quot;: &quot;false&quot;, &quot;feature_flag3&quot;: &quot;true&quot;, &quot;foo&quot;: &quot;10&quot;, &quot;bar&quot;: &quot;OK&quot; }</code></pre> <p>当初は<code>map[string]string</code>のJSON文字列としてシリアライズする案を検討しました。しかし、この方法では<strong>パラメータ名の長さがシリアライズ結果に影響し</strong>、数百〜数千のパラメータをサポートする場合に破綻します。<br /> そこで、<strong>AssignmentsをProtocol Bufferで明示的に定義し、シリアライズする</strong>ことにしました。<br /> 上記の例をProtocol Bufferで定義するとこのようになります:</p> <pre><code class="language-proto">message Assignments { optional bool parameter1 = 1; optional bool parameter2 = 2; optional bool parameter3 = 3; optional int64 foo = 4; optional string bar = 5; }</code></pre> <p>この方法により、以下の利点が得られます:</p> <ul> <li><strong>コンパクトなシリアライズ結果</strong>: パラメータ名はシリアライズ後のバイト長に影響を与えることがなくなり、Experiment Platform利用者はわかりやすい名前を使うことができます</li> <li><strong>型安全性</strong>: <code>optional</code>をつけることで、未割り当ての場合とfalseを区別できます。<code>*bool</code>型とすることで、Experiment Platformの利用時に陥りやすい罠である、未割り当て(<code>nil</code>)と<code>false</code>の区別が明示的になりました</li> </ul> <p>Protocol Bufferで明示的に定義する方法は、新規パラメータ追加時にProtoファイルの変更が必要という欠点があります。しかし、どちらの方法でもGoコードの変更とデプロイは必要です。一方で、<strong>どのパラメータがどこで利用されているか明確に把握できる</strong>利点を我々は重視しました。</p> <h3>4.3 検討の詳細</h3> <p>ここでは設計で候補案として上がったものとの比較内容を通じて検討の詳細の一部を紹介します。</p> <h4>4.3.1 in-memory cache案</h4> <p>gRPC metadataを使ったモジュール間の伝搬以外の方法として、in-memory cacheを利用したデータ共有という案がありました。 Experiment Platform ClientをProxyしてin-memory cacheのデータを返すことで、API呼び出しのレイテンシを削減する方法です。</p> <p>in-memory cacheの場合、モジュール間でAssignmentsの伝搬をする必要がないため、ネットワークトラフィックを抑えることも可能です。 特に、多くのモジュールがAssignmentsを利用しない場合にgRPC metadataを使う方法に対して優位性があります。</p> <div style="text-align: center"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/059170eb-fig4-alt-cache.png" alt="図4. in-memory cacheを利用した方法(BFFとTier2 ProductだけがAssignmentsを利用する場合)" width="800" class="size-medium wp-image-29132" /><br /> <br />図4. in-memory cacheを利用した方法(BFFとTier2 ProductだけがAssignmentsを利用する場合) </div> <p>最終的には、Protocol bufferを利用したassignmentsのシリアライズによりネットワークトラフィックの優位性が小さいこと、 <strong>分割して運用が可能なモジュラモノリス</strong>を実現していく中で、モジュール間でcacheが共有されないケースが増えていくことが予想されるため gRPC metadata を利用した方法を選択しました。</p> <h4>4.3.2 明示的なリクエストフィールド案</h4> <p>gRPC metadataではなく、Protocol Bufferで明示的なリクエストフィールドとして定義する案も検討しました。グローバルアプリBackendでは、API仕様の可視性や型安全性の観点から、一般的には明示的なフィールドを選択しています。</p> <p>しかし、この方法には全てのモジュールの全てのエンドポイントにAssignmentsフィールドを定義しなければならないという問題があります。Tier2のProductモジュールがAssignmentsを必要とする場合、中継するだけのTier1のエンドポイントにも同様のフィールドが必要になります。すでに多くのエンドポイントが開発されており、全てに修正を加えるのは現実的ではありませんでした。</p> <p>また、Protocol Bufferを利用することでgRPC metadataの長さも十分小さくなることが分かったため、gRPC metadataによる伝搬を選択しました。</p> <h3>4.4 将来対応</h3> <p>gRPC metadataを使うため、Headerのサイズ制限に引っかからないよう対策を講じています。ユニットテストで最大バイト長になりうるシリアライズ結果がしきい値を超えていないか検知できるようにしています。</p> <p>現在は全てのパラメータが<code>optional bool</code>型であるため非常にコンパクトですが、しきい値を超えた場合の対応策として、どのモジュールがどのパラメータを参照しているかを静的に解析し、必要なパラメータだけを伝搬するように機能を拡張する予定です。Protocol Bufferで明示的にパラメータを定義しているため、Assignmentsの参照の解析が容易になります。これも、明示的な定義を選択した利点の一つです。</p> <h2>5. まとめ</h2> <p>本記事では、メルカリグローバルアプリのリーダビリティチームの活動と、Feature Flag伝搬システムの設計について紹介しました。</p> <p>リーダビリティチームは、モジュラモノリスアーキテクチャの利点を最大限活かすため、コードの可読性と一貫性を維持し、開発をスケールさせる役割を担っています。GitHubメトリクスを活用した継続的な改善、Claudeによる複雑度分析、CIによる自動チェックなど、様々な活動を通じて品質を担保しています。</p> <p>Feature Flag伝搬システムでは、以下を重視しました:</p> <ul> <li><strong>Protocol Bufferによる明示的な型定義</strong>: 型安全性と利用状況の可視化</li> <li><strong>gRPC metadataとcontext.Contextによる透過的な伝搬</strong>: 既存コードへの影響を最小限に</li> </ul> <p>これらの設計判断は、技術的な実装の問題だけでなく、開発チーム全体のスケーラビリティを考慮したものです。</p> <p>AI時代において、ガイドラインの明文化、自動チェック、一貫したコードベースの維持は、AI Agentを活用した開発の重要な基盤となります。リーダビリティチームは、開発の品質とスピードの両立を目指して活動を続けていきます。</p> <p>最後までお読みいただき、ありがとうございました。</p> グローバルなメルカリの検索バックエンド設計と検索基盤拡充https://engineering.mercari.com/blog/entry/20251016-50fb7b8c1a/https://engineering.mercari.com/blog/entry/20251016-50fb7b8c1a/<p>こんにちは。検索領域でエンジニアをやっております、shinpeiです。 本記事は連載企画:メルカリ初の世界共通アプリ「メルカリ グローバルアプリ」の開発舞台裏の一環として、メルカリグローバルアプリの検索バックエンドをスク [&hellip;]</p> Thu, 16 Oct 2025 16:23:35 GMT<p>こんにちは。検索領域でエンジニアをやっております、shinpeiです。</p> <p>本記事は<a href="https://engineering.mercari.com/blog/entry/20251003-mercari-crossborder/">連載企画:メルカリ初の世界共通アプリ「メルカリ グローバルアプリ」の開発舞台裏</a>の一環として、メルカリグローバルアプリの検索バックエンドをスクラッチで開発することに伴い、大事にした設計のポイントをご紹介します。また今回の新たな要求を契機に既存の検索基盤の拡充が必要だったのでそれについても書かせていただきました。</p> <h1>グローバルアプリでの検索の要件と課題</h1> <p>先日、弊社からの発表の通り、メルカリはグローバルアプリの提供を開始しました。これに合わせて、検索もグローバルな検索を作るべく準備をすすめてきました。<a href="https://engineering.mercari.com/blog/entry/20251007-a09afcd49b/">グローバル展開にむけたアプリと基盤の再構築</a>で紹介したようにグローバルアプリを提供するにあたりバックエンド基盤の再構築を行っています。基盤を活かしながら必要な部分は新しく作り直すアプローチをとっており、検索バックエンドもそれに合わせて再設計を行いました。最低要件は、3年で50カ国展開。出品数は累計40億です(2025年9月時点)。日本事業(日本のお客さま向けメルカリアプリ)の検索との違いは複数ありますが、主なものは以下です。</p> <ul> <li>複数国展開をするための多言語対応</li> <li>検索対象として、アイテムモデルとプロダクトモデルとの両立</li> <li>越境事業においてフォーカスしていく際のエンタメホビー領域に特化できるような独自の検索結果チューニング</li> </ul> <p>1.は日本語以外を扱うための要件です。世界展開となれば、言語の壁は避けては通れない課題です。全文検索はそれぞれの言語自体を処理する必要があります。中国語のクエリ、英語のクエリ、フランス語のクエリ。これらを商品情報とマッチさせる必要があります。現在は日本で出品された日本語の商品をメインに扱っていますが、長期的には日本語以外の商品を扱う可能性も考慮する必要がありました。</p> <p>2.は検索エンジンで検索対象として扱うデータモデルの話です。アイテムとは、現在のメルカリの主流である、単一の商品が検索対象となるようなデータモデルです。一方、プロダクトモデルとは、一つの検索対象だけど、例えば色違い(バリアント)が複数存在させることができるようなデータモデルです。C2Cの商品はアイテムモデルで対応できるのですが、B2Cの商品の一部はバリアントを持つため、プロダクトモデルが必要になります。メルカリはC2Cマーケットプレイスとしてスタートしたため、日本事業の検索では今もなお、アイテムモデルで動いていますが、グローバルアプリではアイテム、プロダクト双方を同等に扱える必要があります。</p> <p>3.はグローバルアプリは検索結果を独立してコントロールしたいという意味です。グローバル展開を考えたときに国ごとのチューニングや事業の方向性に合わせた柔軟性が必要になります。例えば、クエリを自分たちで組み立てて、自由に検索結果をチューニングもできるように作る必要があります 。検索は日本事業と越境事業両方で重要な機能であり、開発の優先度やリソースなどい違いに影響を受けないようにすることも重要です。</p> <h1>Vertex AI Search for Commerce の採用</h1> <p>日本事業の検索ではElasticsearchを使っています。グローバルアプリでは日本にある商品全体を扱う予定だったので、売上に比べてデータ量が桁違いに大きい状態です。売上とコストを両立させるにはElasticsearchの再利用くらいしか思いつきませんでしたが、多言語対応や独立性の確保、リリースまでの時間と開発リソースを考えると、どうしてもその前の交通整理に長い時間がかかります。どうやるか思案していた時に、思いがけず案に上がってきたのがGoogleが提供してるVertex AI Search for Commerce(以下、VAIS:C)です。今回の例では以下の点で魅力的でした。</p> <ul> <li>フルマネージドな小売サイト向け検索サービス</li> <li>リクエストベースの課金モデル(トラフィックがなければ課金されない)</li> <li>多言語対応あり</li> <li>入力するデータはプロダクトモデル(バリアントあり)</li> <li>ある程度の検索結果チューニングが可能</li> <li>ユーザーイベントベースの検索結果最適化あり</li> <li>グローバルエンドポイント(世界中から使える)</li> </ul> <p>これだ!と思いました。要件とVAIS:Cができることを見比べていただければわかりますが、グローバルアプリにとって必要な要求をほぼ満たしており、ネックなのはコストだけです。そこで我々が立てた戦略は以下です。</p> <ul> <li>VAIS:Cを導入し、素早くスモールスタートする</li> <li>トラフィックが伸びてきて、ペイできないレベルになれば、Elasticsearchに切り替える</li> </ul> <p>この方法であれば、Elasticsearch側をグローバルアプリに適用するために必要な準備期間も稼げると思いました。一方で、ビジネス側の懸念は現在の日本事業の検索と、同様の売り上げを得る検索結果をだすことができるかどうか、でした。そこはやってみなければわからなかったので、A/Bテストを行うことになりました。</p> <p>簡単にVAIS:Cを説明しますと、商品情報と、ユーザーイベントを入れると、最適な検索結果が返ってくるというものです。最適化に必要となるユーザーイベントは、Google Analytics 4(以下、GA4)で収集したものが前提となってましたが、スキーマさえ合わせれば独自イベントの入力も可能でした。メルカリはGA4ではなく、独自のイベント基盤を持っているため、なんとか変換して動かしました。(Google Cloud Japanの皆様には多大なるご協力をいただきまして、大変お世話になりました。)</p> <p>結果的には、我々の商品情報と、なんとか変換したユーザーイベントでは、日本事業の検索よりも結果が劣ることを確認できました。また、後述するいくつかの要件を満たすことが難しいこともわかりました。ですが、このリスクを補ってあまるメリットがあるのも事実です。(これを全部、私一人でできてるのですから!)検索エンジンが変わることでのリスクは合意した上で、グローバルアプリの初期リリースはVAIS:Cで行う、と決めました。</p> <h1>新しい検索バックエンドの設計</h1> <p>検索エンジンの目処が立ったので、次はバックエンドの話です。ここでは、特にこだわった点をご紹介します。<br /> 私は7年前、現在の日本事業の検索システムの設計にも関わっていました。当時は理想や期待を込めて設計しましたが、実際に運用を続ける中で「これは設計から見直した方がいい」と感じる部分も多く、今回はそれらを改善する形にしています。</p> <ul> <li> <p>データモデルに依存したAPI</p> <p>データを取得するタイプのインターフェースは”どんなデータ”を対象とするかでガラッと設計が変わります。汎用的になものを作ろうとすると、抽象化が進みすぎて、余計わかりにくくなってしまいがちです。対象が無限に増えるわけではないので、汎用化はせずに、たとえばProductをSearchするためのエンドポイント、というふうに割り切りました。Productのデータモデルは決まっています。タイトルがあって、商品の説明があって、値段があって、、、と、どこに行っても同じです。</p> </li> <li> <p>柔軟な処理を可能にするプラグイン機構<br /> ElasticsearchやSolrなどにもあるように、検索クエリや結果に対して独自の処理を加えたいというニーズはどの検索システムにもあります。日本事業の検索開発でも、これまで多くの条件分岐(if文)がコードに埋め込まれ、整理が難しくなっていました。そこで、今回のグローバルアプリ向け検索バックエンドでは、クエリの前処理・後処理をフックできるプラグイン機構を導入しています。検索機能の開発者は、必要な処理をプラグインとして追加するだけで拡張が可能です。多くのソフトウェアでも似たような仕組みが使われていますが、結局この方法に行き着くよな、という実感があります。</p> </li> <li> <p>検索エンジン機能の実装と抽象をわける<br /> 基本的なロジックは抽象層にまとめ、実装部分は切り替え可能な構成にしています。もともと、VAIS:CとElasticsearchの両方で動く必要がありましたが、この要件がなくても同じ設計にしたと思います。というのも、検索エンジンの世界はまさに“戦国時代”で、Vector検索に強い新しいエンジンなど、次々と登場しています。現在はElasticsearchを使っていますが、将来的に他の選択肢が有力になる可能性もあります。このため、抽象化しておくことで、後々の移行や比較コストを大きく減らせると考えてます。</p> </li> <li> <p>その他<br /> グローバルアプリでは、もともとバックエンド全体に関する設計方針が明確にあります (<a href="https://engineering.mercari.com/blog/entry/20251007-behind-the-infrastructure-powering-global-expansion/">グローバル展開を支える基盤の裏側</a>を参照)。このおかげで、何がどこにあるかが、ある程度明確になってます。特に大事だと思うのは、BFFの切り分けです。BFFはここにあるよ(逆にいうと、ここにしかないよ)というのは、見る箇所が限定できるので探索のコストが減るのです。これまでのマイクロサービスでは、どこの誰が検索APIを叩いて、それをお客さまに出しているかをこちらから把握するのは困難でした。検索バックエンドでも、ドメイン特化のノウハウがあります。何をどこに実装するかを明確に提示してるのは新しい検索バックエンドの特徴です。バックエンド全体の設計でも、期せずして同じようなことをしているのは、これまでの苦痛が似ているからだと思います。だれでもなんでもどこにでも作れるというのは、メンテナンスコストを上げる結果になっているという経験則だと思います。</p> </li> </ul> <h1>プロダクトオーナーからの想定外の要求で冷や汗</h1> <p>最初はVAIS:Cを使うとはいえ、長期的にはElasticsearchに切り替えなければなりません。私はこれまで、Elasticsearch as a Service (以下、EaaS) という、<a href="https://github.com/elastic/cloud-on-k8s">ECK</a>などをベースに作った社内のElasticsearchホスト基盤を開発&運用してきました。チームの活躍で、グループ内のほぼすべての検索サービスはEaaSの上で動いてると言えるところまで来ています。関連するエコシステムも充実させてきました。カスタマイズ性も高く、実際、メルカリの複数のビジネスで使われていることがその証左です。グローバルアプリの準備期間中、責任者である@Suwenさんと直接話す機会が多く、EaaSはなんでもできるので安心してください、と伝えてました。そこで何度もフィードバックされたのが、以下のような点です。</p> <p>「日本事業のこの検索機能、グローバルアプリでも使いたいな」「日本事業のこの機能、グローバルアプリだったらこういうふうにカスタマイズしたらいいよね」</p> <p>さぁ、ここで私の過去の記事を読んでいただけたみなさまなら、私が冷や汗を流した理由がおわかりですね?EaaSは、Elasticsearchの提供が主であり、その上に作られた機能の再利用までは責任を持っていません。もちろん、プラットフォームとして、機能の再利用性を無視していたわけじゃありません。Elasticsearchにはプラグイン機構があり、実際にそのプラグインを用いた機能なら再利用できます。しかしメルカリではJavaエンジニアは少数派で、Elasticsearchプラグインの開発はほぼ進みませんでした。機能のほぼ全てはGoのマイクロサービスに作り込まれています。以下に、課題感を説明するのに使ったスライドの一部を晒します。日本事業の作り込んだ検索機能資産が使いたいけど、使えないという状態です。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/b3e2bd1d-screenshot-2025-10-10-at-21.29.05.png" alt="" /></p> <p>実はこの問題は以前にもぶつかってました。広告事業からも、EaaSを使ってもらっていたのですが、ビジネス側からの期待値は、EaaSを使えば、日本事業の検索と同じクオリティの検索結果がでてくると思われていたのです。実際には、EaaSは日本事業の検索と同じ検索結果を提供するわけではないので、この時は日本向けに作られたシノニムや日本語形態素解析などのライブラリを広告チームのバックエンドから再利用してもらう形で落ち着きました。この頃にスライドを書き、日本の検索チームへ、Goで作られた検索機能の再利用性を高めるべきだとの問題提起はしてきました。が、当時の日本事業の優先度的には、再利用性にリソースを割いてもらうことはなかなか難しかったのです。</p> <h1>All for oneで検索基盤の拡充へ</h1> <p>私はここからは迷走しました。日本事業の開発や優先度を邪魔しない範囲で、なんとか機能の再利用化を進める案を捻り出そうとしました。一時は、必要な機能をElasticsearchプラグインで書き直すか、とまで考えていました。ここで肝となった問題点を整理します。</p> <ul> <li>EaaSは最初から複数事業での利用を見据えた設計であるが、その上に作った日本事業の検索サービスは日本事業に強く結合しているため、他事業からの再利用が困難である</li> <li>日本事業の検索は7年運用してきたコードベースであり、前述のとおり、うまく整理されていないなどの負債がある</li> <li>日本事業の検索は引き続き重要であり、このコードベース自体を再利用できるように改変しつつ、日本事業向けの開発を同時並行するのは困難</li> </ul> <p>グローバルアプリの技術責任者である@deeeeetとも議論を重ねて、以下のような案を出しました。いまあるEaaSの上に、再利用可能な機能を一個ずつ切り出して使用するというものです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/e37dbfed-screenshot-2025-10-10-at-21.29.30.png" alt="" /></p> <p>日本事業側にも問題と案を再掲示し、議論を重ねました。最終的に、グローバルアプリの優先度が上がったこともあり、日本事業側からも積極的に動いてくれて、日本事業の検索機能を再利用するための新しい層を作ることが決まりました。この層に関しては、主導権があるわけではないので、私からはEaaSというプラットフォームが大事にしているところをなるべく共有して、いまのところ共通した方針のもと一緒に作っています。具体的には以下です。</p> <ul> <li> <p>Platformがその下で使われてるオープンソースソフトウェアに必要以上の制約を加えないこと<br /> ここでいうオープンソースソフトウェアとは、我々の場合はElasticsearchです。Elasticsearchには我々がただ使ってないだけで、まだまだたくさんの可能性があります。これを現時点では使わないからという理由で上のプラットフォームを経由すると使えなくなるのは本末転倒だと思ってます。実はここは議論になりやすい点で、制約を加えた方が不可測事態が避けられて、メンテが楽になるという話もありますので、慎重な説得と周りの理解が必要です。</p> </li> <li> <p>各ビジネスドメインから独立して使えること<br /> 共通基盤を作って、それを複数のオーナーから利用する時に問題になるのは機能の開発優先度だと思います。事業Aは規模が小さいから後回し。事業Bのオーナーは発言力があるから、優先。。などなど、いわゆる”政治”になりやすいところだと思います。完全な独立は不可能ですが、これをなるべく避けるためには、基盤への開発が開かれていることが重要です。開かれていると言っても、自由に作れるとはまた違います。ブログの前段で説明した通り、どこでもだれでもいじれると収拾がつかなくなってしまいます。それを解決する一つの手はプラグイン機構です。どこをどうフックするかはまだまだ議論していく必要があるのですが、基本的な方針に関しては同意できています。</p> </li> </ul> <p>こうして、現在のグローバルアプリの検索は、VAIS:CとEaaSの上に作られた新しい仕組みとのハイブリッドで稼働しています。</p> <h1>まとめ</h1> <p>今回はグローバルアプリでの新たな検索要求に対してどのような方針で対処したのかについて書かせていただきました。従来の日本事業の検索とは毛色の違う要求をこなすために、フルマネージドの検索サービスである、VAIS:Cを一時的に導入しました。また、グローバルアプリの開発要求を起点に議論が広がり、社内検索基盤を拡充させることで、今後の要求についても対処できるように基盤を作っていることについても触れさせていただきました。具体的な多言語対応方法など、今回、書ききれなかった部分もあるので、また次回以降でご紹介できればいいなと思ってます。</p> <h2>参考</h2> <ul> <li>”メルカリの検索基盤の変遷について” <a href="https://engineering.mercari.com/blog/entry/20220207-776318b784/">https://engineering.mercari.com/blog/entry/20220207-776318b784/</a></li> <li>月間2000万人が使う メルカリ検索機能のアーキテクチャ <a href="https://findy-tools.io/articles/mercari-elasticsearch/38">https://findy-tools.io/articles/mercari-elasticsearch/38</a> </li> </ul> 開発者全員が書けるE2Eテスト ─ 普通のgo testで実現するテスト基盤https://engineering.mercari.com/blog/entry/20251016-e2e-tests/https://engineering.mercari.com/blog/entry/20251016-e2e-tests/<p>はじめに こんにちは、Cross Border (XB) EngineeringでSRE &amp; Enablingを担当している@ryotaraiです。 本記事は連載企画:メルカリ初の世界共通アプリ「メルカリ グロー [&hellip;]</p> Thu, 16 Oct 2025 12:00:06 GMT<h2>はじめに</h2> <p>こんにちは、Cross Border (XB) EngineeringでSRE &amp; Enablingを担当している<a href="https://twitter.com/ryot_a_rai">@ryotarai</a>です。</p> <p>本記事は<a href="https://engineering.mercari.com/blog/entry/20251003-mercari-crossborder/">連載企画:メルカリ初の世界共通アプリ「メルカリ グローバルアプリ」の開発舞台裏</a>の一環として、このプロジェクトのバックエンドAPIのE2E(End-to-End)テストについて深掘りします。特に、開発者全員がメンテナンスできるE2Eテスト基盤をどのように実現したのか、その設計思想と実装について紹介します。</p> <h2>なぜE2Eテストの改善が必要だったのか</h2> <h3>従来のE2Eテストが抱えていた課題</h3> <p>バックエンドAPIのE2Eテストは、システム全体が正しく動作することを確認する重要な役割を担っています。しかし、多くのプロジェクトで以下のような課題に直面します:</p> <ol> <li>セットアップの複雑さ: テスト環境の準備に時間がかかり、開発者が気軽に実行できない </li> <li>並列実行の難しさ: テスト間でリソースの競合が発生し、実行時間が長くなる </li> <li>属人化: QAチームが主にメンテナンスし、開発者が触りにくい </li> <li>学習コストの高さ: 専用のフレームワークやDSLを学ぶ必要がある</li> </ol> <p>このプロジェクトでも当初、これらの課題に直面していました。特に、QAチームのみがE2Eテストのメンテナンスを担当する状況では、以下の問題が発生します:</p> <ul> <li>APIの変更時に、E2Eテストの更新が後回しになる </li> <li>テストの追加に時間がかかり、カバレッジが低下する </li> <li>開発者がテストの実装を理解せず、デバッグが困難になる</li> </ul> <h3>目指した姿:開発者全員が参加できるE2Eテスト</h3> <p>私たちが目指したのは、QAチームだけでなく、APIのコードを書いている開発者全員がE2Eテストのメンテナンスを行える体制です。</p> <p>これを実現するためには、以下の要件を満たす必要がありました:</p> <ul> <li>開発者が日常的に使っている技術でテストを書ける </li> <li>学習コストが低く、すぐに書き始められる </li> <li>IDEの補完やリファクタリング機能が使える </li> <li>ローカルでもCIでも同じように実行できる</li> </ul> <h2>フレームワークの設計思想</h2> <h3>「普通のgo testで書ける」という哲学</h3> <p>メルカリ グローバルアプリのバックエンドAPIは、Goで実装されています。そのため、最終的に私たちは普通のGoコード(go test)でE2Eテストを書く方式を選択しました。その理由は:</p> <ol> <li>学習コストがゼロ: 開発者は既にgo testの書き方を知っている </li> <li>型安全性: <a href="https://connectrpc.com/docs/introduction/">Connect</a>のクライアントコードをそのまま使え、コンパイル時に型をチェックできる </li> <li>IDEの恩恵: 補完、リファクタリング、定義ジャンプなどが使える </li> <li>デバッグのしやすさ: 通常のGoプログラムとしてデバッグできる </li> <li>既存コードの活用: テストヘルパー関数やモックなどを再利用できる</li> </ol> <p>この決定により、E2Eテストを特別なものではなく、日常の開発フローの一部にすることができました。</p> <p>私たちが実装したE2Eテストフレームワークの核心は、「普通のgo testで書ける」という設計思想です。</p> <p>実際のテストコード例を見てみましょう:</p> <pre><code class="language-go">func TestUpdateNickname(t *testing.T) { t.Parallel() tests := []struct { name string userID int64 nickname string wantCode connect.Code }{ { name: &quot;Success&quot;, userID: createTestUser(t).ID, nickname: &quot;NewNickname&quot;, wantCode: connect.CodeOK, }, { name: &quot;Blank nickname returns error&quot;, userID: readonlyUser().ID, nickname: &quot;&quot;, wantCode: connect.CodeInvalidArgument, }, { name: &quot;Non-logged in user returns error&quot;, userID: 0, nickname: &quot;TestNickname&quot;, wantCode: connect.CodeUnauthenticated, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() testenv.Run(t, func(params env.RunParams) { client := accountv1connect.NewBFFAccountServiceClient( http.DefaultClient, params.Server.URL, ) req := connect.NewRequest(&amp;accountv1.UpdateNicknameRequest{ Nickname: tt.nickname, }) if tt.userID != 0 { // 認証ヘッダーを設定 setAuthHeader(t.Context(), req.Header(), tt.userID) } _, err := client.UpdateNickname(t.Context(), req) if connect.CodeOf(err) != tt.wantCode { t.Errorf(&quot;error code = %v, want %v&quot;, connect.CodeOf(err), tt.wantCode) } }) }) } }</code></pre> <p>このコードは、go testのテーブル駆動テストという標準的なパターンで書かれています:</p> <ul> <li><code>t.Parallel()</code>で並列実行を指定(通常のgo testと同じ) </li> <li>テストケースを構造体のスライスで定義 </li> <li><code>t.Run()</code>でサブテストを実行し、各サブテストも並列実行 </li> <li><code>testenv.Run()</code>の中でテストサーバのURLを取得 </li> <li>Connectの自動生成されたクライアントをそのまま使用 </li> <li>通常のgo testと同じアサーション</li> </ul> <p>E2Eテスト特有の複雑な記述はほとんどなく、普段のユニットテストと同じように書けることが分かります。</p> <p>さらに、普通のGoコードで書けるということは、Claude CodeなどのAIコーディングツールも効果的に活用できるということです。テストケースの追加やエッジケースの洗い出しなど、AIの支援を受けながら効率的にテストを書くことができます。<br /> QAチームなど、バックエンドエンジニア以外でGo言語に慣れていないメンバーもいますが、AIを活用することでテストコードを作成できるようになっています。</p> <p>実際に、既存のJestで実装されていたE2Eテストをこのフレームワークに移行する際にも、AIを大いに活用しました。既存のテストコードを参照しながら、AIがGoのテストコードを生成し、開発者がレビュー・調整することで、移行作業を効率的に進めることができました。</p> <h3>全体アーキテクチャ</h3> <p>E2Eテストの実行方法として、全員がアクセスできる共有のdevelopment環境にデプロイしたアプリケーションに対してテストを実行するという選択肢もあります。しかし、この方法では開発中のバックエンドの変更に対してすぐにテストを実行できないという問題があります。</p> <p>私たちは、アプリケーションコードを変更しながらE2Eテストを通したり、追加・修正できたりする環境を重視しました。そのため、テストごとにサーバを動的に起動する設計を採用しています。これにより、開発者は自分の変更をすぐにE2Eテストで検証でき、テスト駆動での開発が可能になります。</p> <p>フレームワークの主な責務は:</p> <ol> <li>テストサーバの自動起動と管理: 必要に応じてサーバを起動し、プールで管理 </li> <li>データベースの自動準備: AlloyDB Omniを起動し、論理データベースを作成してマイグレーションを実行 </li> <li>並列実行のサポート: 複数のテストが同時に実行できるようリソースを管理 </li> <li>クリーンアップの自動化: テスト終了時に自動的にデータをクリーンアップし、リソースをプールに返却</li> </ol> <p>開発者から見ると、これらの複雑な処理は完全に隠蔽されており、<code>testenv.Run()</code>を呼ぶだけでテスト環境が整う仕組みになっています。</p> <h2>内部実装の工夫</h2> <p>ここからは、フレームワークがどのように並列実行とリソース管理を実現しているか、内部実装を見ていきます。</p> <h3>リソースプールによる並列実行</h3> <p>E2Eテストの並列実行を実現するために、サーバをプール管理しています。</p> <p>重要なのは、<code>testenv.Run()</code>に渡した関数が終了すると、自動的にサーバがプールに返却される仕組みです。開発者は明示的にリソースを返却する必要がなく、通常のテストと同じように書くだけで、フレームワークが自動的にクリーンアップとプールへの返却を行います。</p> <p>この仕組みにより:</p> <ul> <li>並列実行時にリソースの競合が発生しない </li> <li>サーバの起動コストを最小化(プールから再利用) </li> <li>テスト間のデータ汚染を防ぐ(TRUNCATEで初期化) </li> <li>リソース管理が透明(開発者は意識する必要がない)</li> </ul> <h3>データベースの管理</h3> <p>データベースについては、AlloyDB Omniのコンテナは1つだけ起動します。その中で、テストごとに論理データベースを自動的に作成し、マイグレーションを実行します。</p> <p>この設計により:</p> <ul> <li>データベースコンテナの起動コストを削減(1つだけ起動すればよい) </li> <li>並列実行でもデータが分離される(論理DBごとに独立) </li> <li>マイグレーションの実行も自動化(開発者は意識不要)</li> </ul> <p>論理データベースもプールで管理されており、テスト終了後はTRUNCATEでデータをクリーンアップしてから再利用されます。</p> <h3>コードカバレッジの収集</h3> <p>このフレームワークは、Go 1.20以降で導入された<a href="https://go.dev/doc/build-cover"><code>go build -cover</code></a>をサポートしています。</p> <p>通常のテストカバレッジ(<code>go test -cover</code>)はテストコード内での実行しか計測できませんが、E2Eテストではサーバプロセスとして実行されているコードのカバレッジを計測する必要があります。これを実現するのが<code>go build -cover</code>機能です。</p> <p>フレームワークの実装では:</p> <ol> <li>サーバごとに独立したカバレッジディレクトリを自動作成 <ul> <li>各サーバ起動時に一時ディレクトリを作成 </li> <li><code>GOCOVERDIR</code>環境変数を自動設定 </li> </ul> </li> <li>並列実行でもカバレッジが正確に収集される <ul> <li>各サーバが独立したディレクトリに書き込むため、競合しない </li> </ul> </li> <li>テスト終了時に自動マージ <ul> <li>すべてのサーバのカバレッジデータを<code>go tool covdata merge</code>で統合 </li> <li>最終的に1つの統合されたカバレッジデータが生成される</li> </ul> </li> </ol> <p>開発者は特定の環境変数を設定するだけで、複数サーバのカバレッジを自動的に収集・マージできます:</p> <pre><code class="language-shell"># カバレッジ付きでサーババイナリをビルド go build -cover -o server ./server # カバレッジを収集しながらテスト実行 GLOBAL_GOCOVERDIR=/tmp/coverage go test ./e2etest/... # カバレッジレポート生成 go tool covdata percent -i /tmp/coverage</code></pre> <p>この仕組みにより、E2Eテストでのコードカバレッジを正確に計測し、APIの品質を定量的に評価できるようになりました。</p> <h2>Kubernetes上での実行</h2> <p>E2Eテストは、開発環境ではローカルで実行し、CIではKubernetes上で実行します。ここでは、Kubernetes上での実行方法について、いくつかの興味深い工夫を紹介します。</p> <h3>go test -c を使った高速なデプロイ</h3> <p>通常、Kubernetes上でテストを実行する場合、以下の手順を踏むかと思います:</p> <ol> <li>コンテナイメージをビルド </li> <li>イメージをレジストリにプッシュ </li> <li>Kubernetes Podでイメージをプル </li> <li>コンテナを起動</li> </ol> <p>しかし、この方法には各ステップに時間がかかるという問題があります。E2Eテストでは実行速度が重要なので、私たちは異なるアプローチを取りました:</p> <pre><code class="language-shell"># テストバイナリをビルド go test -c \ -o package/e2etest \ ./path/to/e2etest # サーババイナリをビルド go build \ -o package/server \ ./path/to/server # tarでアーカイブしてkubectl execで転送 tar -czf - -C ./package . | \ kubectl exec -c main -i -n ${POD_NAMESPACE} ${POD_NAME} -- \ tar xzf - -C /tmp/e2e # Pod内で直接実行 kubectl exec -c main -it -n ${POD_NAMESPACE} ${POD_NAME} -- \ /path/to/entrypoint.sh</code></pre> <p><code>go test -c</code>を使うことで、テストコードを実行可能なバイナリにコンパイルできます。これにより:</p> <ul> <li>コンテナイメージのビルドが不要 </li> <li>レジストリへのプッシュ・プルが不要 </li> <li>kubectl execで直接ファイルを転送</li> </ul> <p>この方法により、テスト実行までの時間を大幅に短縮できました。具体的には1分半程度でビルドからテストの開始ができています。</p> <p>なお、Kubernetes上で実行する理由は、並列実行に必要な十分なリソースを確保するためです。並列度を上げるとレースコンディションを防ぐためにその数だけサーバが必要となり、必要なリソースが線形に増えるため、Kubernetesクラスタを使用しています。</p> <h2>まとめ</h2> <p>本記事では、メルカリ グローバルアプリのバックエンドAPIのE2Eテストについて紹介しました。<br /> このアプローチにより、E2Eテストは特別なものではなく、日常の開発フローの一部となります。開発者はAPIを変更した際に、躊躇なくE2Eテストを追加・修正できるようになっています。</p> <p>もちろん、まだ改善の余地はあります。例えば:</p> <ul> <li>テストの実行時間のさらなる短縮 <ul> <li>AIを利用して、変更箇所に関係するテストのみを実行する取り組みを進めています </li> </ul> </li> <li>テストデータのセットアップの簡略化 </li> <li>テスト結果のレポーティング</li> </ul> <p>しかし、開発者体験を最優先にした設計により、持続可能なE2Eテスト基盤を構築できたと考えています。</p> <p>同じような課題を抱えているプロジェクトの参考になれば幸いです。</p> グローバルWebを支えるSREの裏側 — 開発を加速させるための改善アプローチhttps://engineering.mercari.com/blog/entry/20251013-behind-the-scenes-of-sre-supporting-the-global-web/https://engineering.mercari.com/blog/entry/20251013-behind-the-scenes-of-sre-supporting-the-global-web/<p>Cross Border (XB) EngineeringでSRE &amp; Enablingをしているhatappiです。私たちのチームはSREのみではなく、Team TopologiesにおけるEnablingチー [&hellip;]</p> Mon, 13 Oct 2025 10:00:45 GMT<p>Cross Border (XB) EngineeringでSRE &amp; Enablingをしている<a href="https://x.com/hatappi">hatappi</a>です。私たちのチームはSREのみではなく、<a href="https://teamtopologies.com/">Team Topologies</a>におけるEnablingチームとしての役割を持っています。SREとして信頼性の向上やパフォーマンスの改善を行うだけではなく、XBのプロダクトチームの開発者体験と効率を向上させ、よりスムーズかつ高速に価値提供できるよう、技術的な課題解決や環境整備を通じて支援 (Enable) します。 </p> <p>2025年7月にPlatform NetworkチームからXB SRE &amp; Enablingチームへ異動し、最初の仕事としてメルカリ グローバルWebの立ち上げに携わっています。本記事は<a href="https://engineering.mercari.com/blog/entry/20251003-mercari-crossborder/">連載企画:メルカリ初の世界共通アプリ「メルカリ グローバルアプリ」の開発舞台裏</a>の一環として、アプリと同時に開発が進んでいるグローバルWebをテーマに、新しい環境でSREとして価値を出すために私が実践しているアプローチをご紹介します。</p> <h2>Enablingのための課題発見アプローチ</h2> <p>7月にXB SRE &amp; Enablingチームへ異動してきて、私の最初のミッションは「グローバルWeb の立ち上げをEnablingすること」でした。しかし異動して間もないためどのような課題や改善ポイントがあるか分からない状態でした。そこでまずは現状を正しく理解することが不可欠だと考え、私は2つのアプローチをとりました。</p> <h3>1. まずは自分でやってみる</h3> <p>グローバルWebのメンバーがどういった過程で困っていて何を改善できるかを知り、共感するためには、自分で体験してみるのが早いです。そこで1つの機能開発タスクに取り組みました。これによりただローカル環境をセットアップして動作確認するという点だけでなく実装をしてプルリクエストを作成して、レビューを受け、マージするまでの一連の開発サイクルを体験しました。</p> <p>このアプローチによって「CIの実行が遅く、フィードバックを得るまでの待ち時間が長い」「ローカルサーバーの起動が遅い」など様々な改善点を特定できました。</p> <h3>2. 現場の声を聞く</h3> <p>自分1人の体験だけでは、どうしても視野が狭くなってしまいます。特に日常業務としてグローバルWebを開発をしているメンバーの声を聞くのはとても重要です。Slackで改善点を聞いたりプランニングやレトロスペクティブの会議に参加するなどして情報を収集しました。</p> <p>これらにより最初のアプローチでは発見できなかった課題を見つけられただけでなく優先度の選定にもつながりました。 例えばCIの実行時間が遅いという改善点を最初のアプローチで特定しました。これは間違いなく改善点です。しかし実際の開発でCIの実行時間が気になるのは、他のメンバーからレビューを受け取る最後の段階です。それよりも、時々不安定になって失敗するCIやローカルサーバーの起動時間の遅さのほうが、より大きな課題だと感じているようでした。</p> <h3>Platform Engineeringの経験から得た気づき</h3> <p>この2つのアプローチを実践する中で、以前のPlatform Networkチームでの経験を振り返る機会となりました。</p> <p>Platform NetworkチームではPlatform Engineeringとして、メルカリの複数のプロダクトで横展開できる共通基盤やツールを提供してきました。メルカリには複数のプロダクトがあり、それぞれが異なるコンテキストやドメイン知識を持っています。そのためPlatform側からすべてのプロダクトの現場に深く入り込んで理解することは難しいという課題がありました。</p> <p>今回、XBのSRE &amp; Enablingとして2つのアプローチを実践することで、現場に深く入り込む重要性を再認識しました。一方で、Platform Engineeringの経験から、メルカリ全体を考えたときの横展開の重要性も理解しています。</p> <p>メルカリでも、両方の経験を持つエンジニアはまだ多くありません。だからこそ、今はXBで得た経験、例えば今回のグローバルWeb関連の改善内容を、Platformチームに積極的にフィードバックしながら一緒に改善を進めています。</p> <h2>AIを活用した課題解決</h2> <p>前のセクションの2つのアプローチによって、取り組むべき課題の解像度が大きく上がりました。しかし、改善すべき課題が分かっても、まだ異動して間もない私はグローバルWebやCross Borderのコンテキスト、Web関連の技術など多くの情報をキャッチアップする必要がありました。グローバルWebの立ち上げをスムーズにEnablingするためにもAIを活用することでこのプロセスをスムーズに進められないか試みました。</p> <h3>学習・調査</h3> <p>例えば CIの実行時間が遅い、という課題に取り組むとします。これを理解するためにはそもそもどのような仕組みで動いているのかを理解する必要があります。そこで下記に説明するように<a href="https://claude.com/product/overview">Claude</a>、<a href="https://claude.com/product/claude-code">Claude Code</a>を活用しながら必要な情報や改善策を検討していきました。</p> <p>まず初めに、Claude Codeを使用して既存のCI関連の設定を調査します。メルカリではGitHub Actionsを使用しているためActionの目的を尋ねたり、Job間の依存関係を確認するなどソースコードを読みながら理解を深めます。調査を進めていると、自分の知らない技術が出てきます。例えば<a href="https://turborepo.com/">Turborepo</a>などです。</p> <p>知らない技術が出てきた時はそれを理解するためにも一次ソースである公式のドキュメントを読みます。その際にドキュメントの要約などのためにClaudeを活用します。それだけであればClaude Codeをそのまま使うこともできます。Claude を使用した理由は ClaudeのArtifacts機能を利用するためです。(Fig1) ArtifactsはClaudeとの会話中に作成・編集できる独立したコンテンツを作成するための機能です。これを使用することで知らなかった技術を深堀りながら最終的にまとまったドキュメントが完成するため後から見返しやすくなりました。</p> <p><a href="https://storage.googleapis.com/prd-engineering-asset/2025/10/045563ff--2025-10-10-19.51.07.png"><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/045563ff--2025-10-10-19.51.07.png" alt="" /></a><br /> Fig1: Claude Artifacts</p> <p>最後のステップは改善のための調査です。改善方法の検討のための第一歩として一般的な改善策を収集するためにClaude Researchを活用しました。例えば「Turborepoを使ったリポジトリでCIを高速化する一般的な方法を調査」といった内容です。これにより、キャッシュ戦略の改善や並列実行の最適化など、複数のアプローチを短時間でリストアップでき、改善策の仮説を効率的に立てることが可能になりました。またArtifactsを活用することで調査した内容をもとに具体的な案など最終的な実装へ向けた情報を一緒にまとめることも可能でした。</p> <h3>実装・レビュー</h3> <p>改善の仮説が立ったら、次に行うのは実装です。調査でまとめた情報はArtifactsに存在しておりMarkdownとして出力できます。それをClaude、 GPT、 Geminiなど任意のツール・モデルで使用できます。</p> <p>私はメインでClaude Codeを利用していますが、その理由は<a href="https://docs.claude.com/en/docs/claude-code/slash-commands">Slash commands</a>があるからです。Slash commandsはClaude Code上で<code>/</code>から始まる特殊なコマンドで、Claude Codeで特定の操作を実行することができます。私は自分が開発の中で行うプロセスをこのSlash commandsに移行しています。 例えば変更内容からプルリクエストを作成するSlash commandがあります。このSlash commandには単なるプルリクエストの作成だけでなく変更内容からコミットメッセージを検討してコミットするなど私がよく行うステップを定義しています。</p> <p>実装が終わったらレビューです。 Claude Code 自身にもレビューはさせるのですが自分でもレビューします。今までは <code>git diff</code> やエディターについている diff viewer などを使用していました。しかしローカルでレビューしたときは問題ないと思ったのにGitHubでプルリクエストを作成して再度レビューすると改善点に気づくということがよくありました。変更を加えて毎回pushしてプルリクエスト上で確認するのは時間がかかります。この問題を解決するために<a href="https://www.npmjs.com/package/difit">difit</a>を使いはじめました。</p> <p>difitは、GitHub ライクなビューをローカル環境で実現してくれる CLI です。(Fig2) npm パッケージとして追加されているのでインストールも簡単ですぐ使い始められます。GitHubライクなビューにより今までプルリクエスト上で行っていたことをローカルで行えるようになりました。またdifitはコメント機能がついており、追加したコメントはAIにプロンプトとして渡せるようなコピー機能がついています。おかげでClaude Codeで開発しながらレビューして改善というサイクルをローカルで完結するようになりました。</p> <p><a href="https://storage.googleapis.com/prd-engineering-asset/2025/10/fe9cd280-screenshot.png"><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/fe9cd280-screenshot.png" alt="" /></a><br /> Fig2, difit (<a href="https://github.com/yoshiko-pg/difit">https://github.com/yoshiko-pg/difit</a>)</p> <h3>デバッグ</h3> <p>最後はデバッグです。 私は普段 Chromeを使用することが多いです。Chromeを用いたデバッグではChrome DevToolsが欠かせません。しかし、様々な機能があるため、どの機能を使ってどこを見たら自分が知りたい情報を見ることができるのか毎回苦労していました。</p> <p>そこで最近リリースされた<a href="https://www.npmjs.com/package/chrome-devtools-mcp">Chrome DevTools MCP</a>を活用してみました。これは、自然言語で指示するだけでMCP Serverを介してDevToolsを操作して必要な情報を引き出してくれる機能です。例えば、「グローバルWebのこのページのパフォーマンスをチェックして」と入力するだけで、関連する指標を分析してくれます。</p> <p>これにより毎回苦労していたDevToolsの操作をスムーズに行うことができ、問題発見までの時間を短縮することができました。</p> <h2>Enabling活動から得られた学び</h2> <p>今回のグローバルWebのEnablingを通じて学んだことが2つあります。</p> <h3>現場に入り一次情報に触れることの重要性</h3> <p>1つ目は、現場の一次情報に触れることで初めて、本当に解くべき課題とその優先順位が見えてくるという点です。もし私が客観的なメトリクスだけを見て判断していたら開発メンバーが感じていたCIの実行時間よりも「CIが時々不安定になること」や「ローカルサーバーの起動時間」がより問題であることに気づくのは難しかったでしょう。</p> <h3>新しい技術領域へ挑戦する際のAIの有効性</h3> <p>2つ目は、AIが新しい技術領域へ挑戦する際のハードルを下げてくれる、ということです。</p> <p>1つ目の学びの重要性を理解していても、実践するまでのハードルが高いと躊躇してしまいます。しかしAIを活用することでこの「最初の壁」を乗り越えやすくなったと感じました。もちろんAIがすべてを解決してくれるわけではないですが、まずは動くものを作ってから、その仕組みを深く理解していくという私の好きなアプローチが高速でできるようになったと感じています。</p> <h2>今後の展望</h2> <p><a href="https://engineering.mercari.com/blog/entry/20251007-a09afcd49b/">グローバル展開にむけたアプリと基盤の再構築</a>でも触れられていたように、今後3年以内に50カ国・地域への展開が予定されており、これは技術的にも非常にチャレンジングです。このスピードで世界に展開するためには、どのような実装や設定が必要か、どのように効率化できるか、データはどこに配置するか、Webサーバーはどこに置くのか、CDNはどのように活用できるかなど、考えるべきことが多数あります。考えることが多いからこそ面白く、SRE &amp; Enablingの腕の見せ所になると感じているので、非常に楽しみです。</p> <h2>おわりに</h2> <p>本記事では、Platform Networkチームから異動して新しい環境でグローバルWebのEnablingをどのように進めているかを紹介しました。</p> <p>2025年11月13日に、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます。私は異動前の Platform Networkチームの時に行ったCDNマイグレーションについて話します。 他にも面白そうなセッションが沢山あるのでぜひお越しください!</p> <p>参加登録はこちら 👉 <a href="https://gears.mercari.com/">https://gears.mercari.com/</a></p> <p>明日の記事は @gia さんです。引き続き<a href="https://engineering.mercari.com/blog/entry/20251003-mercari-crossborder/">連載企画:メルカリ初の世界共通アプリ「メルカリ グローバルアプリ」の開発舞台裏</a>をお楽しみください。</p> 【mercari GEARS 2025】MECHANISM Stageのセッションをご紹介https://engineering.mercari.com/blog/entry/20251010-mercarigears2025-mechanism-stage/https://engineering.mercari.com/blog/entry/20251010-mercarigears2025-mechanism-stage/<p>こんにちは!メルカリ Engineering Office の @mikichin です。 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます! 2018年に実 [&hellip;]</p> Fri, 10 Oct 2025 11:00:21 GMT<p>こんにちは!メルカリ Engineering Office の <a href="https://x.com/chida_miki" title="@mikichin">@mikichin</a> です。<br /> 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます!</p> <p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/TDXzEjwqbaw?si=QJTLP0JGhJtu2kIP" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p> <p>2018年に実施した「Mercari Tech Conf 2018」から7年の時を経て、久しぶりのオフラインでの開催となります。<br /> テーマは「メルカリエンジニアリングの今」。<br /> 今年の全社的なテーマでもある「AI-Native」についてはもちろん、2018年以降メルカリグループのエンジニアリングがどのように変化してきたかを、技術・組織・カルチャーの観点からご紹介します。<br /> オンライン配信はありませんので、ぜひ会場でご自身の目と耳で確かめてください!!</p> <p>会場は、メルカリのエンジニアリング組織における信念や行動の基盤となる共通認識を明文化した「Mercari Engineering Principles」をモチーフにした「PASSION Stage」「GROW Stage」「MECHANISM Stage」の3つのステージがあります。</p> <p>本記事では、「MECHANISM Stage」のセッションをご紹介!<br /> まだ申し込みをされていない方も、興味のあるセッションがあるはずです。お申し込みは<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="こちらから">こちらから</a>お願いします。</p> <h3>13:00 &#8211; 13:20 メルカリハロでのLLM活用</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/df42ca46-ogp_mechanism-1_ja-1024x538.png" alt="" /></p> <p>メルカリハロはスキマバイト領域におけるメルカリの新規事業です。2024年の3月にサービスがローンチされて以来、成長を続けており、先日登録者数1200万人を突破しました。</p> <p>メルカリハロのMLチームはサービスローンチから約半年が経過した2024年10月のタイミングで始動し、以来、AI/MLを用いた多くのプロダクト改善に取り組んできました。</p> <p>本セッションではその中でも特にメルカリハロのLLMを用いた機能やそれらを支えるLLMOps基盤について紹介を行います。具体的にはLLMを用いた求人の自動作成機能である「かんたん求人作成」やLLMを用いた求人の掲載前リスク予測等についてです。メルカリハロでは既に多くのLLMを用いた機能をリリースしており、用途の異なる50種類以上のプロンプトを本番で運用しています。そのためプロンプトの品質を管理するためのLLMOps基盤も非常に重要です。</p> <p>本セッションを通して、メルカリハロでLLM実装を進める中で得られたLLMのプロダクト実装のための勘所や、プロンプト管理・自動評価基盤などの実践的なLLMOpsのためのTipsといった知見を共有できればと思います。</p> <h3>13:30 &#8211; 13:50 FastlyからCloudflareへ 100%移行を達成したMercariのアプローチ</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/2f89dc7c-ogp_mechanism-2_ja-1024x538.png" alt="" /></p> <p>メルカリでは、2023年よりCDNプロバイダーをFastlyからCloudflareへ段階的に移行を開始し、2025年現在、移行が完全に完了しました。<br /> このセッションでは、安全かつスムーズに移行を進めるために実施したアプローチと、その過程で得た学びを共有します。<br /> CDN プロバイダーの比較ではなく、移行プロセスを主に話すため、CDNプロバイダーの変更を考えていない方にも、移行に関する考え方やプロセスのヒントを持ち帰っていただけると考えています。</p> <h3>14:15 &#8211; 14:35 The Invisible Backbone: AI-Native Observability for Modern Platforms</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/9d241e46-ogp_mechanism-3_en-1024x538.png" alt="" /></p> <p>もし、自動で設定され、変化にもシームレスに適応し、多すぎるアラートのノイズを一掃してくれるオブザーバビリティがあったら?本セッションでは、メルカリがどのようにAI-Nativeなプラットフォームを構築し、設定不要のモニタリング、一貫した可視性、そしてAI活用型のアラートを標準機能として実現したのかをご紹介します。</p> <p>信頼性が高く開発者にやさしいクラウドプラットフォームの未来を、自律型オブザーバビリティがどのように形作るのか。ぜひご参加のうえ、その目でご覧ください。</p> <h3>14:35 &#8211; 15:05 Running 1000 End-To-End Web Tests Daily</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/68f5950d-ogp_mechanism-4_en-1024x538.png" alt="" /></p> <p>Mercari USでは非常に多くの E2E Webテスト を実行していますが、それらを素早く、かつ本当に役立つテストにすることが大きな課題となっています。本セッションでは、プルリクエストごとにテストを実行する、新しいテストを追加する、各機能領域を対象にしたテストを実行するなど、私たちが行っている工夫をご紹介します。毎日何千件ものE2Eテストをどのように回しているのかを知りたい方には、まさにぴったりのセッションです。</p> <h3>15:15 &#8211; 15:35 Mercari&#8217;s Internationalization Journey</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/8bb9742c-ogp_mechanism-5_en-1024x538.png" alt="" /></p> <p>この2年間、私たちは海外のお客さまがメルカリのマーケットプレイスで商品を購入することを可能にしてきました。<br /> 本セッションでは、プロダクトを海外展開していくための道のりを、とくにユーザー生成コンテンツ(UGC)の翻訳に焦点を当ててご紹介し、翻訳コストを100分の1に削減するうえでLLM(大規模言語モデル)がどのように役立ったかについてもお話しします。</p> <h3>16:00 &#8211; 16:20 EGP &#8211; Mercari’s CRM Platform: Built Once, Powering Many</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/276f4570-ogp_mechanism-6_en-1024x538.png" alt="" /></p> <p>もともとシンプルなハードコードのCRMとして生まれたEGPは、マーケター向けのスケーラブルでUI主導のプラットフォームへと進化してきました。システムが拡大するにつれ、とりわけEGPの利用規模が大きくなると、その複雑さが使いやすさや運用面での課題を引き起こすようになりました。本セッションでは、私たちがシステム設計やAI活用によるUI改善を通じてこれらの課題にどのように取り組んできたのか、そしてその過程でどのような学びを得たのかを共有します。</p> <h3>16:30 &#8211; 16:50 Securing the Future of Workflow Automation and AI Agents</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/d89e574e-ogp_mechanism-7_en-1024x538.png" alt="" /></p> <p>企業がワークフローの自動化やAIエージェントを積極的に活用するにつれ、孤立したシステム、過剰な権限を持つエージェント、複雑に絡み合った権限モデルといった新たなリスクが生じています。本セッションでは、これらの課題をどう解決し、自動化とAIが持つ潜在能力を安全かつ最大限に引き出す方法を探ります。さらに、セキュアでスケーラブルな導入を実現しつつ、ユーザーが安心してイノベーションに取り組めるようにするための実践的なアプローチをご紹介します。</p> <h3>17:00 &#8211; 17:20 AI/LLMが拓くデータ活用の新時代:人間とデータ分析AI エージェントが協業する分析基盤へ</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/49b455af-ogp_mechanism-8_ja-1024x538.png" alt="" /></p> <p>私たちは、自然言語での対話を通じてデータ分析を行えるAIエージェント「Socrates」を開発・提供しています。Socratesの登場は、SQLクエリの生成から実行、結果の可視化や解釈までを誰でも簡単に実行できるような変革をもたらし、データ活用のハードルを大きく下げました。本セッションでは、Socratesが誕生した背景やSocratesを支える技術、そしてAIとの協業によってもたらされるデータ活用体験の未来像についてお話しします。</p> <p>「mercari GEARS 2025」のお申し込みは<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="こちらから">こちらから</a>。<br /> また、その他のセッション紹介は下記をご確認ください。<br /> PASSION Stageのセッション紹介は<a href="https://engineering.mercari.com/blog/entry/20251008-mercarigears2025-passion-stage" title="こちら">こちら</a>。<br /> GROW Stageのセッション紹介は<a href="https://engineering.mercari.com/blog/entry/20251009-mercarigears2025-grow-stage" title="こちら">こちら</a>。</p> <h2>イベント詳細</h2> <p>開催日時:<br /> 2025年11月13日(木) 11:00-18:00</p> <p>概要:<br /> mercari GEARS 2025は、メルカリグループのエンジニアリング組織の技術への挑戦と、カルチャーを体感する技術イベントです。<br /> 本イベントは、単なる情報伝達の場ではなく、エンジニアたちが出会い、経験を共有し、交流を通じて新たな機会が生み出されることを目的としています。</p> <p>参加費:無料<br /> 会場:TODA HALL &amp; CONFERENCE TOKYO<br /> 参加方法:こちらの<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="ページ">ページ</a>にてお申し込みください。<br /> 【<a href="https://gears.mercari.com/" title="公式サイト">公式サイト</a>】</p> <p>本イベントに関する追加情報があれば、随時 <a href="https://x.com/MercariGears" title="@MercariGears">@MercariGears</a> でお知らせしますので、気になる方はぜひフォローをお願いします。</p> 【mercari GEARS 2025】GROW Stageのセッションをご紹介https://engineering.mercari.com/blog/entry/20251009-mercarigears2025-grow-stage/https://engineering.mercari.com/blog/entry/20251009-mercarigears2025-grow-stage/<p>こんにちは!メルカリ Engineering Office の @mikichin です。 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます! 2018年に実 [&hellip;]</p> Thu, 09 Oct 2025 11:00:30 GMT<p>こんにちは!メルカリ Engineering Office の <a href="https://x.com/chida_miki" title="@mikichin">@mikichin</a> です。<br /> 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます!</p> <p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/TDXzEjwqbaw?si=QJTLP0JGhJtu2kIP" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p> <p>2018年に実施した「Mercari Tech Conf 2018」から7年の時を経て、久しぶりのオフラインでの開催となります。<br /> テーマは「メルカリエンジニアリングの今」。<br /> 今年の全社的なテーマでもある「AI-Native」についてはもちろん、2018年以降メルカリグループのエンジニアリングがどのように変化してきたかを、技術・組織・カルチャーの観点からご紹介します。<br /> オンライン配信はありませんので、ぜひ会場でご自身の目と耳で確かめてください!!</p> <p>会場は、メルカリのエンジニアリング組織における信念や行動の基盤となる共通認識を明文化した「Mercari Engineering Principles」をモチーフにした「PASSION Stage」「GROW Stage」「MECHANISM Stage」の3つのステージがあります。</p> <p>本記事では、「GROW Stage」のセッションをご紹介!<br /> まだ申し込みをされていない方も、興味のあるセッションがあるはずです。お申し込みは<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="こちらから">こちらから</a>お願いします。</p> <h3>13:00 &#8211; 13:40 Leader’s Talk: Moving Fast Without Breaking Things</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/238b2e00-ogp_grow-1_en-1024x538.png" alt="" /></p> <p>メルカリのバリューのひとつに「Move Fast(はやく動く)」があります。<br /> その一方で、開発者が柔軟に複数のサービスを行き来できるようにすることも必要です。では、その両方をかなえるにはどうすればよいのでしょう?</p> <p>本セッションでは、AI時代においてメルカリのエンジニアリング組織がいかに開発プロセスを進化させ、スピードとレジリエンスのバランスを取ろうとしているのかを、リーダーたちが自身の視点から紹介します。<br /> 発表は主に英語で行いますが、日本語での質問も大歓迎です!</p> <h3>14:15 &#8211; 14:35 Google Customer Engagement Suiteを使った顧客エンゲージメント変革</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/0ce25121-ogp_grow-2_ja-1024x538.png" alt="" /></p> <p>2025年に行われたGoogle Cloud Next Tokyoにて、メルカリは基調講演とブレイキングセッションを通し、Google社が提供するCustomer Engagement Suiteを活用した顧客エンゲージメントの変革について発表しました。このセッションでは、その発表で紹介されたプロダクトがどのような方法で構築されたのかについて紹介します。</p> <h3>14:45 &#8211; 15:05 PJ-Auroraが描く未来と、UI品質評価を自動化するエージェント開発</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/8db52528-ogp_grow-3_ja-1024x538.png" alt="" /></p> <p>メルカリのモノづくりのアプローチを変えることを使命とする「PJ-Aurora」。本セッションではその未来像を紹介し、あわせてUI品質評価を自動化するAIエージェント開発の現在地をお話しします。AI-Native時代における品質保証の可能性を探る試みを共有します。</p> <h3>15:15 &#8211; 15:35 なぜ、メルカリはノーコードを選ばなかったのか? 社内問い合わせ工数を60%削減したLLM活用の裏側</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/4e3430e8-ogp_grow-4_ja-1024x538.png" alt="" /></p> <p>生成AIブームの中、多くの企業がノーコードツールを試す一方で、メルカリは既存の生成LLMを社内データで徹底的にチューニングすることで、高い精度と柔軟性を追求してきました。本セッションでは、社内問い合わせ工数を60%削減したLLM活用事例「HiYo-Chan」を中心に、ノーコードツールでは実現できないメルカリ独自の技術的な工夫、そしてその裏側にあるビジョンまで、赤裸々にお話しします。</p> <h3>16:00 &#8211; 16:40 AI Native への道のり ― 数字でみる全社推進と現場の実践</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/67235f9b-ogp_grow-5_ja-1024x538.png" alt="" /></p> <p>メルカリグループは「AI-Native」を掲げ、AI を前提とした新しい開発の型づくりに挑戦しています。<br /> AI を単なる効率化ツールではなく、プロセス設計の根幹に組み込み、企画から実装までの一連の開発を再構築する取り組みを全社横断で進めています。</p> <p>本セッションでは、その全体像をデータと実例で解像度高く共有します。<br /> まず、AI 活用の広がりと生産性の変化を DX の定量データをもとに可視化し、組織カルチャーとしての AI 浸透の進化を振り返ります。<br /> 次に、AI Agent を前提とした開発を再現性高くスケールさせるための組織設計と運用、既存事業の開発体制を AI-Native へ進化させる実践、そしてその中で得た知見と課題を共有します。<br /> 最後に、新規事業開発を題材に、PM とエンジニアが生成 AI を活用して要件定義から実装までを一気通貫で進める最新の開発プロセスと、標準化ドキュメント「Agent Spec」の中身を紹介します。</p> <p>AI と共に開発の在り方を変革し、再現性ある生産性向上を実現する——メルカリの AI-Native な挑戦の最前線をお届けします。</p> <h3>17:00 &#8211; 17:30 LTセッション</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/30dd4698-ogp_lightning-talks_en-1024x538.png" alt="" /></p> <p>6人のLTを行います。</p> <ul> <li>未定 / kuu</li> <li>GitHubリポジトリの調査から分かったマイクロサービス開発でのデータベースの課題 / Tomoyuki Koyama</li> <li>Claude Codeで仕様駆動開発をする中で考えたエンジニアの役割 / Toshiki Kawamura</li> <li>Mercari Ads Optimizations For Profitable Revenue Stream / Kumar Abhinav</li> <li>Exploring LLM-Driven Formal Verification for Robust Continuous Integration of Services / Cheng-Hui Weng</li> <li>Evaluations for LLM Apps / jd</li> </ul> <p>「mercari GEARS 2025」のお申し込みは<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="こちらから">こちらから</a>。<br /> PASSION Stageのセッション紹介は<a href="https://engineering.mercari.com/blog/entry/20251008-mercarigears2025-passion-stage/" title="こちら">こちら</a>。<br /> MECHANISM Stageのセッション紹介は<a href="https://engineering.mercari.com/blog/entry/20251010-mercarigears2025-mechanism-stage/" title="こちら">こちら</a></p> <h2>イベント詳細</h2> <p>開催日時:<br /> 2025年11月13日(木) 11:00-18:00</p> <p>概要:<br /> mercari GEARS 2025は、メルカリグループのエンジニアリング組織の技術への挑戦と、カルチャーを体感する技術イベントです。<br /> 本イベントは、単なる情報伝達の場ではなく、エンジニアたちが出会い、経験を共有し、交流を通じて新たな機会が生み出されることを目的としています。</p> <p>参加費:無料<br /> 会場:TODA HALL &amp; CONFERENCE TOKYO<br /> 参加方法:こちらの<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="ページ">ページ</a>にてお申し込みください。<br /> 【<a href="https://gears.mercari.com/" title="公式サイト">公式サイト</a>】</p> <p>本イベントに関する追加情報があれば、随時 <a href="https://x.com/MercariGears" title="@MercariGears">@MercariGears</a> でお知らせしますので、気になる方はぜひフォローをお願いします。</p> グローバル展開を支える基盤の裏側https://engineering.mercari.com/blog/entry/20251007-behind-the-infrastructure-powering-global-expansion/https://engineering.mercari.com/blog/entry/20251007-behind-the-infrastructure-powering-global-expansion/<p>Cross Border(XB) EngineeringでArchitect兼SREをしている yanolab です。 ブログシリーズ初日では、グローバル展開にむけた基盤の再構築としてメルカリにおける取り組みの遷移の紹介 [&hellip;]</p> Wed, 08 Oct 2025 12:00:18 GMT<p>Cross Border(XB) EngineeringでArchitect兼SREをしている <a href="https://x.com/yanolab">yanolab</a> です。</p> <p>ブログシリーズ初日では、<a href="https://engineering.mercari.com/blog/entry/20251007-a09afcd49b/" title="グローバル展開にむけた基盤の再構築">グローバル展開にむけた基盤の再構築</a>としてメルカリにおける取り組みの遷移の紹介がありましたが、本記事ではグローバル展開を支える基盤の裏側と題して、バックエンドシステムのアーキテクチャーやフレームワーク、取り組みなどを少し掘り下げて紹介したいと思います。</p> <h2>Background</h2> <p>メルカリにおいては長らくMicroserviceアーキテクチャを採用して運用し、そのエコシステムにも投資をしてきました。echoサービスと呼ばれるMicroserviceのテンプレートや、GoでMicroserviceの開発を行うためのSDK、基本的なインフラ関係の設定をまとめたスターターキットと呼ばれるTerraformのモジュール、Kubernetesの設定を抽象化し、少ない記述でDeploymentを管理できるSDKなどがあります。また、Microserviceのリリースに際してはProduction Readiness Check(PRC)と呼ばれるプロセスがあり、新しく開発されたプロダクトやMicroserviceはこのチェックリストに合格する必要があります。これらのエコシステムやプロセスは成熟してきたものの、複雑化したエコシステムは学習コストを高め、肥大化したPRCによって、1つのMicroserviceを立ち上げるのには最低でも3ヶ月かかるようになってしまっていました。また、新しくビジネスを立ち上げる際は構築初期の人数は少ないにもかかわらず、数十のMicroserviceを立ち上げなければならない場合が多く、そのような場合においてMicroservice数×3ヶ月という工数は現実的ではなく、直近のメルカリの新規ビジネスはMonolith的なアプローチを採用することが多くなってきています。(ref: <a href="https://engineering.mercari.com/blog/entry/20240529-mercari-hallo-tech-stacks/" title="メルカリ ハロの技術スタックとその選定理由">メルカリ ハロの技術スタックとその選定理由</a>)<br /> グローバル展開にむけた基盤の再構築においては将来的に現在のメルカリMarketplaceと同じような規模になることが想定されるため、単純なMonolithではなく、既存のエコシステムを最大限活用しつつ、Microservice的な運用ができるような特殊なModular Monolithを設計し、運用しています。</p> <h2>分割して運用が可能なModular Monolith</h2> <p>メルカリのMicroservicesを想定したエコシステムは1レポジトリ、1サービスが基本となっており、大規模かつ複雑な構成のシステムは想定されていません。例えばCI/CDでは1バイナリ、1コンテナ、1Deploymentを想定しています。このような環境から逸脱する場合、システムの実装側で独自にワークフローを作成しメンテナンスを行う必要があります。Cross Borderチームでは独自でメンテナンスし続けるコストを回避するため、この方針に従いつつ、将来のビジネスの成長に伴う運用負荷の分散のため、Microservice的な運用ができるようにシステムを一つのバイナリにコンパイルするが、設定ファイルでモジュールの有効・無効を切り替えられるようにしています。また、モジュール間のインタフェースはProtocol Bufferで定義し、その通信はgRPCを利用するようにすることで、同じインスタンス内の通信にとらわれず、運用の自由度を高めています。これによって、1バイナリ、1コンテナとして既存のCIビルドシステムを利用しつつ、設定ファイルでモジュールをオン・オフしたり、モジュール間の通信相手を任意に設定したMicroservices的な運用を可能にしています。また、モジュール間のインターフェースをProtocol Bufferにしたことで、モジュールの独立性を高めつつモジュールのインタフェースの設計からチームで連携しながらモジュールの開発を行えるようになっています。(Fig. 1)</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/1e5201e8-modular-monolith-with-flexible-deployment-1024x399.png" alt="" /></p> <p style="text-align:center;">Fig.1 Modular Monolith with Flexible Deployments</p> <p>新基盤のデータベースにはAlloyDBを用いています。過去のメルカリのMonolithにおいては、システム全体で共有のデータベースを用いており、ドメインごとのテーブルの連結や権限に制限はありませんでした。そのため、サービスが成長するにつれてドメイン間の依存度は高まり、運用コストは増大していきました。それに対してMicroserviceへ移行した際には多くのサービスやチームで、SpannerやCloudSQLが採用されてきました。サービス毎に独自にデータベースを持ってチームで運用することは、ドメインやサービスの独立性が高く、オーナーシップやメンテナンスの面でとても優れている選択でした。しかし、それぞれのチームで独自にデータベースを持ち、少ないリクエストでも安定運用のためにHA構成としなければならないということはコストの面から見ると非効率で、リクエストの少ないサービスでは特にコスト的に無駄が多い状態となっていました。そこで、Cross Borderチームでは、コスト節約のためなるべく同じクラスタを利用するが、モジュールごとにサービスアカウントを分けアクセスできるデータベースを制限し、データベースもモジュール単位で分割することとしました。これによって、コストを抑えつつ将来の分割やスケールに備えています。(Fig. 2)</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/b71fa8d2-db-isolation-1024x479.png" alt="" /></p> <p style="text-align:center;">Fig.2 DB Isolation</p> <p>従来、メルカリではMicroserviceの設定を環境変数にて行ってきましたが、Monolithになった場合は設定が非常に多くなり、環境ごとの設定の管理なども煩雑になってしまうことが予想されました。そこで、設定ファイルには<a href="https://cuelang.org/">CUE lang</a>を採用し、デフォルトの設定をシングルソースで管理できるようにし、開発環境や本番環境など、環境ごとに違う値のみを差分管理できるようにしています。これらの設定ファイルはコンテナのビルド時にコンテナに同梱され、環境に応じて、ローカル環境であればローカル用の設定、開発環境・本番環境であればそれに応じた設定が自動的に使われるようになっています。また、実行時にCUE/YAMLで標準の設定を上書きをできるようにすることで、Deploymentごとに違う設定を行うことも可能にしています。(Fig. 3)</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/3ec88abb-difference-managemnt-of-config-1024x480.png" alt="" /></p> <p style="text-align:center;">Fig. 3 Difference management of config</p> <p>例えば開発環境と本番環境の標準の設定を標準のコンフィグとして下記(Fig. 4)のように定義します。この場合、ProductモジュールのProductInventoryアプリケーションはSearchモジュ ールのアドレスとしてlocalhostを利用します。</p> <pre><code class="language-cuelang"> #GRPCClientConfigSpec: { address: string | *&quot;localhost:\(#HTTPPort)&quot; timeout: =~&quot;^([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$&quot; | int | *&quot;3s&quot; retry: int &amp; &gt;=0 | *3 } components: &quot;layers/tire2/product/applications/productinventory&quot;: enabled: bool | *false search_module: #GRPCClientConfigSpec &quot;layers/tire3/search/applications/productsearch&quot;: enabled: bool | *false ... </code></pre> <p style="text-align:center;">Fig. 4 Common part of development and production</p> <p>開発環境の共通の設定を下記(Fig. 5)のように定義したとします。この場合、開発環境の一部であるGKEの環境でも、ローカル環境でも全ての機能が有効となり、すべてのモジュールはローカルホストのモジュールを利用します。</p> <pre><code class="language-cuelang"> components: &quot;layers/tire2/product/application/productinventory&quot;: enable: true &quot;layers/tire3/search/applications/productsearch&quot;: enabled: true ... </code></pre> <p style="text-align:center;">Fig. 5 Development specific configuration(Enabled all of modules)</p> <p>本番環境でGKEのDeploymentを分ける場合は、コンテナに同梱しているものとは別にConfigMapをYAMLとしてマウントし、これを読み込ませます。例えばDeploymentAのProductモジュールのInventoryアプリケーションの接続先をDeploymentBとし(Fig. 6)、DeploymentBではSearchモジュールのProductSearchアプリケーションのみを有効にする(Fig. 7)ことでSearchモジュールのみを独立して運用することが可能となっています。</p> <pre><code class="language-cuelang"> components: &quot;layers/tire2/product/applications/productinventory&quot;: enable: true search_module: address: deploymentB.xxxx.svc.local &quot;layers/tire3/search/applications/productsearch&quot;: enable: false ... </code></pre> <p style="text-align:center;">Fig. 6 The Search module used by the Product module can be switched to a different Deployment</p> <pre><code class="language-cuelang"> components: &quot;layers/tire2/product/applications/productinventory&quot;: enable: false &quot;layers/tire3/search/applications/productsearch&quot;: enable: true ... </code></pre> <p style="text-align:center;">Fig. 7 Deployment with only the Search module enabled</p> <p>これらの柔軟なアーキテクチャはローカル開発、開発環境ではシングルバイナリで運用し、本番環境においては適切な単位でモジュールを切り分け運用を行うことを可能にしています。これは特にローカル開発において非常に強力で、Microserviceの開発時の課題である、依存するMicroserviceを含めた実行環境を用意する必要がなくなり、開発環境の構築とメンテナンス効率を劇的に向上することができます。ただし、今回の基盤再構築においては、すべてのMicroservicesを置き換えるわけではなく、現存するメルカリのMicroservicesへの依存も存在しています。これらの依存関係に対応するために<a href="https://metalbear.com/mirrord/">mirrord</a>というプロダクトを用いて、ローカル環境からリモートのKubernetes環境に繋いで開発を行っています。また、<a href="https://github.com/air-verse/air">air</a>というプロダクトも利用しており、変更の動的リロードができるようになっており、Webアプリを開発するようなモダンな開発環境を実現しています。</p> <h2>モノレポによる変化への対応</h2> <p>メルカリのMicroserviceではサービス毎にレポジトリを作成し、Protocol Bufferの定義、Terraformを用いたインフラストラクチャの管理やKubernetesへのデプロイ環境のレポジトリのみ、それぞれ全員が共有するモノレポとして運用しています。このアプローチは有効ですが、メインで利用するレポジトリと異なっていることで、レポジトリ間を移動する必要があります。このコンテキストスイッチが頻繁に発生することは開発者にとって非常にストレスとなります。また、レポジトリをまたいだ自動化は、個別に動作するCIによって処理時間が長くなるだけでなく、問題が発生した場合にどこで何が起きているかを把握することが難しく、開発者の体験を悪くする要因となっています。今回基盤再構築にあたり、これら開発者の体験を見直すため、この構成も見直し、Backendプロジェクト、Frontendプロジェクト、Protobufの定義やTerraformを一箇所に集めて、極力モノレポで開発が完結するような試みを行っています。(Kubernetesへのデプロイのみエコシステムの都合上既存のモノレポを利用しています。)</p> <p>Modular Monolithで境界を明確にしつつ、モノレポでBackendプロジェクトのみならずFrontendプロジェクトも管理することで、アプリケーションやアーキテクチャやフレームワークを揃えつつ、言語や役割を超えた貢献をしやすくしています。また、メンテナンスの面においても、スクリプト、Workflow、CIなど一箇所をメンテナンスすれば良く、メンテナンス効率が高いと考えています。メルカリでは長らく組織やチームの生産性を可視化できておらず、開発者の生産性を正確に測定する方法が課題となっていました。2024年より、開発者の生産性を可視化し、改善することを目的に<a href="https://getdx.com/">DX</a>を導入しています。DXではサーベイを用いた定性的データとGitHubなどの生産性に関わるメトリクスなどの定量データを合わせて、効率、スピード、品質、新規性の4点を可視化しています。モノレポを用いたアプローチはこれらの値でメルカリ全体のスコアよりも良い結果が出ていることがわかりました。</p> <p>今回構築したモノレポにおいて少しユニークな点としては、インフラストラクチャの管理にTerraformとCUE langを用いているところです(従来通りtfフォーマットも利用可能です)。CIにてCUEからjsonに変換して適用しています。インフラストラクチャの定義をCUEにすることで、上で紹介したModular Monolithの設定管理のように差分を意識した環境構築が可能になります。CUEはYAMLやJSONとマージして利用することが可能なので、自動化の面で非常に有効だと感じています。今後、モノレポのすべてのデータが同じレポジトリにあるというメリットを活かして、Modular Monolithの設定やフレームワークから自動的にインフラストラクチャの構成ファイルを生成するFramework defined Infrastructureに取り組みたいという野望もあります。(Fig. 8)</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/3a21ceeb-framework-defined-infrastructure-1024x845.png" alt="" /></p> <p style="text-align:center;">Fig. 8 Framework Defined Infrastructure</p> <h2>複雑化するドメインや依存関係に対するアプローチ</h2> <p>現在、メルカリではMarketplace事業のほか、Merpayも含めて数百規模のMicroserviceが稼働しています。これらのサービスは必要以上に細かく分割されていたり、相互に依存し合っていたりしてメンテナンスを困難にするだけでなく、新規に機能を作成しようと思ったときに、どのMicroserviceに機能を追加するべきか、またどのMicroserviceの機能を利用できるのか、そもそも新規にMicroserviceを作成するべきなのかなどの判断を非常に困難にしています。そこで、Cross Borderでは新規にMarketplaceの基盤を再構築するに当たって、Tierという概念と依存関係マップを導入しつつ、Likeサービスのように特定の機能にフォーカスし、小さく分けすぎたサービスをSocialモジュールにまとめるなど、ある程度まとまった大きめのドメインに再集約するなど、ドメインや役割を整理しながら進めてきました。</p> <p>このTierコンセプトでは、BFF(Backend for Frontend)/Gateway、Tier 1、Tier 2、&#8230;Tier 4の5つの層に役割を分割し、それぞれの層の役割と制限を追加しました。</p> <h3>BFF/Gateway層</h3> <p>BFFはよく知られていますが、この層ではMobileやWeb画面に最適化したAPIを定義しすべてのリクエストはBFFを通してから下位の層へ送られます。お客さまに応じた言語の変換や通貨の変換もこちらの層で担当します。Mobileエンジニア、Webエンジニア、バックエンドエンジニアで共同で所有しメンテナンスを行っています。</p> <h3>Tier 1</h3> <p>主にリクエストオーケストレーションやビジネスフローを担当します。Tier 1の責任は、Tier 2以下のモジュールを使用してビジネスプロセスを構築することです。イメージとしてはMarketplaceの様々な機能を利用してプロセスを構築するので、水平方向の処理を担当する領域です。</p> <h3>Tier 2</h3> <p>主にMarketplaceのコアの機能を実現するドメイン特化の層になります。ProductモジュールやOrderモジュールなどが該当します。イメージとしては該当のドメインに特化した垂直方向の処理を担当する領域です。</p> <h3>Tier 3</h3> <p>基本的にMarketplaceに依存しないより汎用的な機能を提供する層になります。SearchやNotificationなどが該当します。</p> <h3>Tier 4</h3> <p>この層は少し特殊で、特定の特殊な要件を満たさなければならないモジュールや、Tier 1 〜 Tier 3に属することが難しい機能を提供する層になります。他のモジュールとは適用されるセキュリティーや運用要件などが異なる個人情報を専門で取り扱うモジュールをこの層に配置しています。</p> <p>リクエストは常に上から下へと流れ、同じTier同士の通信は禁止するという制約を設けています。ただし、上位Tierから下位Tierにアクセスする場合、中間Tierは飛ばして良いというルールを設けており、BFFからNotificationへのアクセスは許容しています。(Fig. 9)データベースもモジュール単位で分かれており、モジュールをまたいでトランザクションを張るということもできません。これらのルールにより、モジュールの独立性が非常に高まるとともに小さなモジュールが乱立するといったことを防いでいます。もし、同じTier同士のモジュールの通信が必要になった場合、そのモジュール同士のドメインが非常に近しいことを意味し、ドメインの境界の見直しの良いシグナルとして捉えています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/5243b7bd-tier-concept-1024x468.png" alt="" /></p> <p style="text-align:center;">Fig. 9 Tier Concept</p> <p>基盤の再構築はまだまだ始まったばかりですが、PaymentやIdPといったまとまったドメインかつ、環境が安定しているサービス群を活用しつつ、このデザイン手法を用いてMarketplaceのドメインを再整理し実装することで、2025年10月の現時点で18モジュールに留めることができています。</p> <h2>現在の課題</h2> <p>現状ではモジュール単位でのデプロイを可能にするために、モジュールごとにバージョンをファイルで管理し、リリース時にはそのバージョンをインクリメントすることで、モジュールごとのバージョンアップを検知しています。しかし、この方法はmainブランチをリリース用とするGitHub Flowとは相性が悪く、意図しない変更がリリースに含まれてしまうおそれがあります。現在この問題を解決するために試行錯誤をしています。</p> <h2>今後の展開</h2> <p>AIによる開発が主流になってきている昨今、競争力確保のためには新規にビジネスを素早く立ち上げる必要があります。今回紹介したCross BorderチームのMonorepo、Modular Monolithアプローチは初期の構築コストがそれなりに高いため、メルカリの今後の新規ビジネスに適用できるようにPlatformチームと連携して、もっと簡単に素早く構築できるように挑戦中です。今後何処かで機会があれば、これらの結果をまた記事にしたいと思います。</p> <h2>最後に</h2> <p>2025年11月13日に、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます。こちらにもぜひお越しください!</p> <p>参加登録はこちら 👉 <a href="https://gears.mercari.com/">https://gears.mercari.com/</a></p> <p>明日の記事は @Garyさんです。引き続き「<a href="https://engineering.mercari.com/blog/entry/20251003-mercari-crossborder/" title="連載企画:メルカリ初の世界共通アプリ『メルカリ グローバルアプリ』の開発舞台裏">連載企画:メルカリ初の世界共通アプリ『メルカリ グローバルアプリ』の開発舞台裏</a>」をお楽しみください。</p> 【mercari GEARS 2025】PASSION Stageのセッションをご紹介https://engineering.mercari.com/blog/entry/20251008-mercarigears2025-passion-stage/https://engineering.mercari.com/blog/entry/20251008-mercarigears2025-passion-stage/<p>こんにちは!メルカリ Engineering Office の @mikichin です。 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます! 2018年に実 [&hellip;]</p> Wed, 08 Oct 2025 11:00:38 GMT<p>こんにちは!メルカリ Engineering Office の <a href="https://x.com/chida_miki" title="@mikichin">@mikichin</a> です。<br /> 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます!</p> <p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/TDXzEjwqbaw?si=QJTLP0JGhJtu2kIP" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p> <p>2018年に実施した「Mercari Tech Conf 2018」から7年の時を経て、久しぶりのオフラインでの開催となります。<br /> テーマは「メルカリエンジニアリングの今」。<br /> 今年の全社的なテーマでもある「AI-Native」についてはもちろん、2018年以降メルカリグループのエンジニアリングがどのように変化してきたかを、技術・組織・カルチャーの観点からご紹介します。<br /> オンライン配信はありませんので、ぜひ会場でご自身の目と耳で確かめてください!!</p> <p>会場は、メルカリのエンジニアリング組織における信念や行動の基盤となる共通認識を明文化した「Mercari Engineering Principles」をモチーフにした「PASSION Stage」「GROW Stage」「MECHANISM Stage」の3つのステージがあります。</p> <p>本記事では、「PASSION Stage」のセッションをご紹介!「PASSION Stage」は同時通訳の提供があります。<br /> まだ申し込みをされていない方も、興味のあるセッションがあるはずです。お申し込みは<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="こちらから">こちらから</a>お願いします。</p> <h3>12:15 &#8211; 12:45 Keynote</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/475313d0-ogp_passion-1_en-1024x538.png" alt="" /></p> <h3>13:00 &#8211; 13:20 Techniques for Reliable Code Generation Using AI Agents</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/7852fb7e-ogp_passion-2_en-1024x538.png" alt="" /></p> <p>今年、コードの書き方は大きな変化が見られました。コード変更は主にAIエージェントが行うようになり、私たち人間の仕事は全体的な調整や成果物の修正が中心となってきています。しかし大規模かつレガシーなコードベースを扱う場合、AIエージェントがどこまで自律的に作業できるかには明確な限界があります。プロジェクト全体の文脈を十分に理解できていない、ガイドラインが守られていないという理由から、生成されたコードはマージ前に大幅な手直しが必要となることも少なくありません。<br /> 本セッションでは、AIエージェントが自律的にコード変更を行えるように設定する方法について、特にマイグレーションや定型処理の多いコードを扱う場面で有効なテクニックを紹介します。</p> <h3>13:30 &#8211; 13:50 AIの礎 ——プロダクトを支える、目に見えない力をつくる。</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/a8633258-ogp_passion-3_ja-1024x538.png" alt="" /></p> <p>「見た目が似た商品」から始まった画像埋め込みの小さな実験は、やがて“Embeddings”革命へと発展し、メルカリのプロダクト、カルチャー、そしてビジネスに大きな変革をもたらしました。本講演では、その歩みを振り返りながら、画像検索からAI Listing、セマンティック検索に至るまで、埋め込み技術がいかにブレークスルーを実現してきたのかを紐解きます。また、プロトタイプから堅牢なインフラへと拡張していく過程で直面した課題や、そこから得られた学びについてもご紹介します。</p> <h3>14:15 &#8211; 14:55 Building Foundation for Mercari’s Global Expansion</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/11ef9b1f-ogp_passion-4_en-1024x538.png" alt="" /></p> <p>メルカリは創業当時よりグローバルなマーケットプレイスを実現することをビジョンとして掲げてきました。これまでの挑戦から得られた学びや反省を踏まえ、現在は“Global One Product”というよりグローバルへの展開を加速させるための新たな共通基盤の構築に取り組んでいます。本セッションではなぜこのアプローチに至ったのか、どのようなアーキテクチャや実装で支えているのか、組織的なチャレンジと技術の両面から詳しく紹介します。複数リージョン展開における開発・運用上のチャレンジや、組織横断での意思決定の工夫についても共有します。</p> <h3>15:15 &#8211; 15:35 メルカリにおけるフィッシング対策の軌跡と未来</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/75dad922-ogp_passion-5_ja-1024x538.png" alt="" /></p> <p>フィッシング攻撃は進化を続け、サービスやユーザーを狙う手口は年々巧妙化しています。メルカリでも、その進化に対抗するために多様な防御策を講じてきました。そしてパスキー導入を契機に、戦いの焦点は大きく変わり、「いかにフィッシングを防ぐか」から、「いかに守れるユーザーや機能を広げるか」、「いかに強固でありながらUXを損なわない認証体験を実現するか」へとシフトしてきました。本セッションでは、攻撃手法の変遷と、それに呼応して発展してきたフィッシング対策や認証・リカバリー施策の歩みを振り返ります。</p> <h3>16:00 &#8211; 16:40 The Future of Platform in the Age of AI</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/26858497-ogp_passion-6_en-1024x538.png" alt="" /></p> <p>本セッションでは、私たちが現在AIを社内でどのように活用しているか、社内のエンジニアリング組織のニーズがどのように進化すると考えられるか、そしてAIエージェントを正規のユーザーとしてサポートできるプラットフォーム構築とは何なのかについてお話しします。 AI時代におけるプラットフォームエンジニアリングの姿や、今後3年から5年の間に取るべき大胆な一手について一緒に探っていきましょう。</p> <h3>17:00 &#8211; 17:40 Backend Standardization with MCP</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/99fb0ced-ogp_passion-7_en-1024x538.png" alt="" /></p> <p>他のチームのサービスを理解するのに頭を抱えたことはありませんか?各チームがそれぞれ異なるコード構造を使っていたり、部門ごとに分断されていたりして、作業がなかなか進まない。そんな状況をAIとModel Context Protocol (MCP) でどう変えられるのかをご紹介します。本セッションではまずMCPとは何かを説明し、なぜこれが社内のバックエンド開発を標準化し、投資対効果(ROI)を高める「ゲームチェンジャー」になり得るのかをお話しします。その後、実際にMCPが動くデモをご覧いただき、現在直面している課題や今後の設計の可能性についても探っていきます。</p> <p>「mercari GEARS 2025」のお申し込みは<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="こちらから">こちらから</a>。<br /> GROW Stageのセッション紹介は<a href="https://engineering.mercari.com/blog/entry/20251009-mercarigears2025-grow-stage" title="こちら">こちら</a>。<br /> MECHANISM Stageのセッション紹介は<a href="https://engineering.mercari.com/blog/entry/20251010-mercarigears2025-mechanism-stage/" title="こちら">こちら</a></p> <h2>イベント詳細</h2> <p>開催日時:<br /> 2025年11月13日(木) 11:00-18:00</p> <p>概要:<br /> mercari GEARS 2025は、メルカリグループのエンジニアリング組織の技術への挑戦と、カルチャーを体感する技術イベントです。<br /> 本イベントは、単なる情報伝達の場ではなく、エンジニアたちが出会い、経験を共有し、交流を通じて新たな機会が生み出されることを目的としています。</p> <p>参加費:無料<br /> 会場:TODA HALL &amp; CONFERENCE TOKYO<br /> 参加方法:こちらの<a href="https://www.eventbrite.com/e/mercari-gears-2025-tickets-1637585555479" title="ページ">ページ</a>にてお申し込みください。<br /> 【<a href="https://gears.mercari.com/" title="公式サイト">公式サイト</a>】</p> <p>本イベントに関する追加情報があれば、随時 <a href="https://x.com/MercariGears" title="@MercariGears">@MercariGears</a> でお知らせしますので、気になる方はぜひフォローをお願いします。</p> 初挑戦のインターンで得た学び – メルカリ『お問い合わせ対応システム』開発の1ヶ月https://engineering.mercari.com/blog/entry/20251007-553f1424b6/https://engineering.mercari.com/blog/entry/20251007-553f1424b6/<p>自己紹介 こんにちは、@KiKiと申します。今年9月に1ヶ月間、メルカリのインターンに参加させていただきました。 大学では情報系を専攻していて、大学の授業ではハードウェアからアプリケーションに至るまで幅広い分野について学 [&hellip;]</p> Tue, 07 Oct 2025 10:59:36 GMT<h2>自己紹介</h2> <p>こんにちは、@KiKiと申します。今年9月に1ヶ月間、メルカリのインターンに参加させていただきました。<br /> 大学では情報系を専攻していて、大学の授業ではハードウェアからアプリケーションに至るまで幅広い分野について学んでいます。<br /> 今回のメルカリのインターンは自分にとって初めて参加するインターンでしたが、多くのことを学びながら大きく成長することができたと感じています。本記事では実際にインターンで取り組んだ内容と学んだことをご紹介できればと思います。どうぞよろしくお願いいたします。</p> <h2>参加したインターンについて</h2> <p>私が今回参加したのは「Build@Mercari」というプログラムの一部であるインターンシップです。<br /> なお、Build@Mercariのプログラム自体の詳しい内容については、他の記事でとても詳しく紹介されていますので、そちらをご覧ください。<br /> <a href="https://careers.mercari.com/mercan/articles/40098/">https://careers.mercari.com/mercan/articles/40098/</a></p> <h2>どうしてBuildインターンに申し込んだのか</h2> <p>一般的なインターンに応募する際、技術要件や事前知識の高さにハードルを感じる方も多いのではないでしょうか。特に、情報系を専攻していない方はもちろん、専攻している方でも、インターンの応募時に求められる技術要件や知識に不安を感じることはあるかもしれません。</p> <p>大学では、アルゴリズムやハードウェア、OSの基本原理など基礎的な内容が中心で、Webアプリケーション開発などの実践的なスキルを学ぶ機会は限られることもあります。</p> <p>私自身、フロントエンドやバックエンドといったWeb関連技術は全くの未経験で、「どこから始めればいいのかわからない」と感じていました。そんな中、STEM分野・IT分野におけるマイノリティである女性や、LGBT+コミュニティの方を対象にトレーニングとインターンシップの機会を提供する「Build@Mercari」というオンラインプログラムを知りました。<br /> このプログラムは「Web関連技術の知識は全くないけれど、この業界に興味がある!」という気持ちひとつで応募できる懐の深さが魅力でした。「これなら私にも挑戦できるかもしれない」と思い、思い切って応募することにしました。</p> <h2>配属されたチームについて</h2> <p>私が配属されたのは「Contact Center」という、メルカリでのお問い合わせ対応をサポートする社内システムを開発しているチームです。</p> <p>現在、メルカリのお問い合わせ対応は、お客さまがフォームからお問い合わせし、お客さまからいただいたお問い合わせ内容などの情報を元にCS(カスタマーサービス)オペレーターが対応する、という流れになっています。ただし、このCSオペレーションではお問い合わせの解決までに時間がかかりすぎる、という課題がありました。</p> <p>このチームではそういったCSオペレーションを抜本的に再構築するプロジェクトを進めていました。具体的には、お客さまのお問い合わせにリアルタイムでBotが対応し、Botで解決が難しい場合は、有人チャットを通じてCSオペレーターが対応にあたる、という方式への移行を目指しています。インターン期間中は、この新しいチャット体験への移行を進めているフェーズだったので、メルカリの未来のCS体験を支える重要な仕組みに関われるのは、とても魅力的なポイントでした。</p> <h2>実際に取り組んだ内容</h2> <p>プロダクトに関わる業務は大きく分けて、バックエンドとフロントエンドに分かれます。<br /> バックエンドはサーバー側やデータベースの処理を担い、フロントエンドはユーザーに直接触れる画面や操作部分を担当します。今回私は、バックエンドとフロントエンドのタスクを1つずつ担当させていただきました。</p> <h3>バックエンド開発</h3> <h4>使用した言語・ツール</h4> <p>言語: Go, SQL<br /> ツール: GCP, Spanner, Kubernetes, BigQuery, Spinnaker, <a href="https://github.com/cloudspannerecosystem/yo" title="yo">yo</a></p> <h4>背景</h4> <p>お問い合わせ対応システムの開発を進める中で、会話履歴や関連データに紐づく識別情報をスムーズに取得することが、調査や分析作業を円滑に進める上での課題となっていました。</p> <p>このチャットのシステムは、GCPのサービスを使った実装になっています。個々のチャットを特定するためのIDは連携・保存していましたが、ConversationIDと呼ばれる、Botが会話したIDはそれとは異なるIDとなっており、これはシステム上では保存していませんでした。</p> <p>そのため、これまでの運用では、対象となる会話データから、ConversationIDを取得するまでに、複数の手順を踏む必要がありました。例えば、会話の記録から情報を一つひとつ検索したうえで、目的のデータを特定するといったプロセスが発生します。このような手間は、迅速な問題解決が求められる場面では特に大きな障壁となっていました。</p> <h4>実装したこと</h4> <p>こうした課題を解決するため、ConversationIDを自動的に収集・格納する仕組みを検討しました。具体的には、会話終了時に必要なデータを自動的に取得し、これまでテーブルに保存していなかったConversationIDも、会話終了時にテーブルへそのまま保存するように変更しました。この仕組みを活用することで、調査プロセスを大幅に簡略化し、より効率的な対応を目指しました。</p> <h4>結果</h4> <p>その結果、インターンの期間を通して、メンターさんをはじめとしたチームの方の助力もいただき、この仕組みを実際のシステムに反映させることができました。現場では「調査の高速化に役立っている」といった声もいただいており、自分が関わった仕組みが実際に使われていると実感でき、とても嬉しく感じています。この経験を通じて、開発したものが誰かの作業を少しでも助けられることのやりがいを改めて感じました。</p> <h3>フロントエンド開発</h3> <h4>使用した言語・ツール</h4> <p>言語: TypeScript, GraphQL<br /> ツール: Ant Design</p> <h4>背景</h4> <p>お問い合わせに関する情報にお客さまのメールアドレスが登録されていない場合、そのお問い合わせにはダミーのメールアドレスが登録されます。ただしそのような場合でも、CSオペレーターが操作する画面上には、「お客様のメールアドレス宛にメッセージを送信するボタン」が表示されていました。<br /> しかし、有効なメールアドレスではないため、このボタンを押してもメールの送信は実行されません。それにもかかわらず、CSオペレーターの画面上にはその旨が表示されないため、メール送信が完了したという誤認識を招く可能性があるUIとなっていました。</p> <h4>実装したこと</h4> <p>この問題を解決するため、ダミーのメールアドレスが設定されている場合に送信ボタンが押されたら、エラーメッセージをモーダルで表示する処理を追加しました。</p> <p>この実装を行うために、送信先メールアドレスの情報を取得できるように、データ取得クエリの一部を変更しました。この変更により、画面初期読み込み時に必要な情報が整う仕様に改めました。</p> <h4>結果</h4> <p>画面上にエラーメッセージが正しく表示されることを確認できました。これにより、CSオペレーターが誤認識をするリスクが減少し、日々の業務をより正確に進めることに役立つ改善が実現しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/d58a92ab-contact_tool.png" alt="contact tool screen" /></p> <h2>チーム開発ならではの学び</h2> <p>プライベートの個人開発では、自分の思いつくままに自由に実装することが多いかと思います。しかし、個人では達成が難しい大きな目標も、チームであれば実現できることがあります。一人では膨大な時間と労力がかかる作業も、チームで取り組むことで、それぞれの得意分野を活かし、知識やスキルを共有しながら効率よく進めることができます。</p> <p>さらに、チーム開発では単なる作業の分担にとどまらず、互いにフィードバックを与え合うことでプロダクトの可能性を広げていきます。</p> <p>今回のインターンは、私にとって初めて「お仕事でのチーム開発」や「大規模な開発」に触れる機会となりました。私がここで得た学びを、次にご紹介します。</p> <h3>プロダクトへの携わり方は業種によってさまざま</h3> <p>今回、Contact Centerチームに配属させていただき、チームの方々にサポートしていただいたり、働く様子を間近で見る中で、チームメンバーそれぞれの役割や業務内容について理解を深めることができました。<br /> 私が今回のインターンにおいては、以下のようなポジションの方と関わりがありました。</p> <h4>Product Manager</h4> <p>プロダクトを使うお客さま(私達の場合Customer Serviceのメンバー)からのニーズを取りまとめて、最適な形で実装できるように仕様を決定する役割です。今回のインターンでは、フロントエンドの実装を行う際、エラーメッセージの内容やデザインについて相談させていただきました。</p> <h4>Engineering Manager</h4> <p>エンジニアの意見をチームやプロジェクトに反映させるため、多くの会議に出席し関係者と調整を行ったり、他のエンジニアが意思決定に迷った際に相談に乗るのが主な役割です。また、チームのエンジニア一人ひとりと毎週1on1を行い、困りごとや課題に耳を傾けるなど、コードを書くこと以外にもチームメンバーとのコミュニケーションを重視している印象を受けました。<br /> 私もインターン期間中に何度か1on1を設定していただき、直接お話をする機会がありました。</p> <h4>Frontend Engineer</h4> <p>フロントエンドエンジニアはユーザーが直接目に見える部分を実装するエンジニアです。チームでの会議の際に、実装が出来上がった部分を紹介する時間があるのですが、フロントエンドのデモンストレーションは華があって見応えがあるので、いつも私も楽しみにしていました。</p> <h4>Backend Engineer</h4> <p>フロントエンドエンジニアとは逆に、表からは見えない部分を担当するエンジニアです。プロダクトの機能のに関わる裏側の処理を行うことができるという点が魅力です。適切なデータ構造やAPIの決定、システムのパフォーマンスに関わる仕事もできるのが個人的に面白いと考えています。縁の下の力持ちという印象です。</p> <h3>開発の流れ</h3> <p>チーム開発では、メンバーと協力して働くからこそ、個人開発にはないステップが必要になります。</p> <p>ここでは、そのリリースまでの流れを簡単にご紹介します。</p> <h4>Planning (何をするか決める)</h4> <p>解決すべき課題と作るものを明確にする段階です。Product Managerの方を中心に議論し、作業の方向性を定めます。</p> <h4>Spec作成 / チケット起票</h4> <p>Planningで決まった内容をもとに、仕様を具体化し、タスク管理ツールに登録します。この時点でレビューを一度受けることもあります。</p> <h4>Technical Spec / 詳細設計</h4> <p>技術的な詳細設計を行い、データの流れやAPI選定など、具体的な実装内容を詰めていきます。今回のインターンでは、チームの方がすでにチケット起票までを終えてくださっていたため、詳細設計の作成から作業を引き継ぎました。</p> <h4>開発</h4> <p>設計に基づいてコードを実装します。</p> <h4>Pull Request(PR)作成</h4> <p>GitHub上でコードを共有し、実装の意図やテスト内容を説明します。扱ったリポジトリでは同時に、PR作成時には自動的にCIツールが実行され、コードに対してlintや単体テスト(unit tests)が走る仕組みになっていました。</p> <h4>レビュー</h4> <p>チームメンバーがコードの品質や設計の意図を確認します。レビューの結果次第では4の開発に戻ってコードを書き直し、再度レビューを受け、承認が得られるまで繰り返すことになります…。</p> <h4>リリース準備</h4> <p>環境設定や実行権限の取得を行い、開発用の環境でテストを経たうえで本番リリースに備えます。</p> <h4>リリース</h4> <p>完成したコードを本番環境にリリースし、ユーザーが利用できる状態にします。</p> <h3>大規模開発に向けた設計思想「Clean Architecture」について</h3> <p>今回、バックエンド開発で触ることになったリポジトリは、「Clean Architecture」に基づいて設計されていました。</p> <p>コードの設計思想とは、特に大規模な開発において重要となる概念です。これは、「コードをどのように整理し、配置するか」を決める際に参考にするポリシーのことで、チーム全体での効率的な作業を支えます。たとえば、「このコードはここに置かれているに違いない」とチーム全員が共通認識を持てることで、開発効率が大きく向上します。</p> <p>Clean Architectureでは、プログラムの役割や責任に応じてコードがレイヤーに分かれています。それぞれのレイヤーは、独立して役割を果たせるように設計されており、異なるレイヤー間の依存関係を最小限に抑えることが特徴です。この設計により、変更や拡張がしやすくなるという利点があります。</p> <p>大規模開発に触れたことがなかった私にとって、コードの設計思想という概念に触れること自体、非常に大きな学びとなりました。</p> <h2>生成AIを用いた開発について</h2> <p>メルカリでは積極的に業務に生成AIを導入しています。</p> <p>今回のインターンを通じて、ソフトウェア開発の現場では「言語の文法を覚えてスラスラと書くだけがスキルではないんだ」と実感しました。生成AIの進化によって、コードを書く作業がかなり効率化されており、大規模なプロジェクトのコードを理解する際にも非常に有用だということを学びました。</p> <p>一方で、それ以上に重要だと感じたのは、大規模な開発に適した設計思想や、将来的に仕様変更がしやすい設計、自分以外の人にも分かりやすいコードを書くことの大切さです。また、モジュール化やメンテナンス性を意識した開発の考え方が、現場では重視されていることを強く感じました。</p> <p>これらの経験から、「知らない言語を使うプロジェクトだから…」と機会を逃すのは、少しもったいないと気付かされました。言語自体の知識は必要に応じて身につけていけばよく、現場で学べる設計や開発の考え方こそが、より長く自分の糧になり、生成AIに取って代わられることのない人材へと成長することにつながるのではないかと思います。</p> <h2>終わりに</h2> <p>この記事では、私が初めてのインターンを通して学んださまざまなことについて、紹介させていただきました。</p> <p>1ヶ月という期間はとても短く感じましたが、フルタイムで社員の方々に混ざって働き、たくさんお話をさせていただく中で、この記事には書ききれないほどの大きな学びを得ました。なにより、本当に楽しかったです。メンターをしてくださった<a href="https://engineering.mercari.com/blog/author/Peranikov/" title="Peranikov">Peranikov</a>さんをはじめ、Contact Centerチームの皆様方、ありがとうございました。</p> グローバル展開にむけたアプリと基盤の再構築https://engineering.mercari.com/blog/entry/20251007-a09afcd49b/https://engineering.mercari.com/blog/entry/20251007-a09afcd49b/<p>Cross Border (XB) Engineeringの @deeeeeeeeeet です. 先日の事業戦略発表会において共有しましたが,今後更にメルカリの海外展開を加速させるためにグローバル版のメルカリアプリを先日 [&hellip;]</p> Tue, 07 Oct 2025 09:29:53 GMT<p>Cross Border (XB) Engineeringの <a href="https://www.deeeet.com/">@deeeeeeeeeet</a> です.</p> <p>先日の事業戦略発表会において共有しましたが,今後更にメルカリの海外展開を加速させるためにグローバル版のメルカリアプリを先日リリースしました.</p> <p>このアプリは現在提供してる日本版・アメリカ版のメルカリとは異なる新しいアプリであり,またアプリだけではなくその裏側のバックエンド基盤も新たに再構築しています.本記事では,エンジニアリングの観点からメルカリ グローバルアプリ(以下、グローバルアプリ)とその基盤の戦略やアーキテクチャーをこれまでのメルカリの挑戦から得られた学びを振り返りつつ紹介します.</p> <h1>メルカリにおける越境取引</h1> <p>「メルカリ」に出品したことがあるみなさんの中には,自分の商品が一般のお客さまではなく事業者によって「代理購入」された経験がある方もいらっしゃるかもしれません.これは,海外のお客さまが日本の「メルカリ」に出品されている商品を購入できる越境 (Cross-Border, XB) 取引という仕組みによるものです. </p> <p>メルカリにおける越境取引は代理購入パートナーとの連携によって実現されています.海外のお客さまは,まず提携パートナーのWebサイト上で「メルカリ」の商品を注文します.すると,パートナーが購入代行者として「メルカリ」上で商品を購入し,支払い手続きを行います.国内の出品者は,この代理購入者であるパートナーの指定する日本の倉庫へ,通常の国内取引と同じように商品を発送します.商品が倉庫に到着後,パートナーが検品や海外向けの梱包を行い,海外のお客さまの元へ国際発送するという流れで実現されています.</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/f31f0b65-dairikounyuu.png" alt="" /></p> <p>この仕組みは,海外と国内のお客さまの双方にメリットがあります.海外のお客さまにとっては,言語の壁や通貨の違いを気にすることなく,日本のユニークな商品を手軽に購入できます.一方で,国内のお客さまにとっては,海外のお客さまとの直接的なコミュニケーションや国際発送といった複雑な手続きは一切不要で,国内取引と同じように販売の機会を世界中に広げることができます.</p> <p>越境取引事業は2019年に始まり,近年さらに成長しており, GMVとしては過去3年で15倍に成長しています.特に,アニメ,コミック,ゲーム,エンタメ関連グッズのカテゴリーが取引全体の多くを占めており海外のお客さまからの強い需要があります.</p> <p>このような強い需要と成長を顧みて,代理購入パートナーのサイトを通じた仕組みに加え,<a href="https://jp.mercari.com/">日本のメルカリのWebサービス</a>を通じて代理購入を可能にする取り組みも開始しました.この仕組みにより,海外のお客さまは直接「メルカリ」上でアカウントを作成し,「メルカリ」が提供する体験を通じて商品の検索と購入を行うことが可能になりました (引き続きパートナー企業を間に挟む形式は継続しています).この取り組みは2024年にリリースし, 現在台湾と香港から利用可能で利用者数を伸ばしています.</p> <p>こうして越境取引事業は順調に成長してきましたが,同時にいくつかの重要な課題も見えてきました.以下で説明するように既存の日本のシステムは日本市場に特化して作られており,単一通貨・単一言語を前提とした設計になっています.越境取引機能はこの上に追加的に実装したため,複数国への展開や各国固有の商習慣への対応を実現していくには限界がありました.特にアジア市場ではEC利用の多くがモバイル経由という状況において,Web版のみの提供では競争力に欠けるという問題もありました.</p> <p>このような課題を抱えながらも,海外市場からの需要は確実に存在しており,特にアニメ・ゲーム関連商品への関心は非常に高いことがわかっています.現在は台湾と香港の2か所のみですが,東南アジアや欧米にも同様の潜在的な需要があることは明らかでした.この機会を最大限に活かすためにはより早く展開国を拡大していく新たなアプローチが必要でした.</p> <p>そこで私たちは,単に既存システムを拡張するのではなく,グローバル展開を前提とした新たなアプリケーションとその基盤を構築するという決断に至りました.これは越境取引から始めて,将来的には各国でのローカルマーケットプレイスの立ち上げ,そして最終的には国境を越えたグローバルなマーケットプレイスの実現を見据えた戦略的な判断でした.</p> <h1>海外展開のアプローチ</h1> <p>グローバルなマーケットプレイスの実現は,メルカリ創業当時からのビジョンであり,海外展開への挑戦は今回が初めてではありません.これまでにもアメリカでの事業展開に挑戦し,現在もその成長に注力しています.過去にはイギリスへの展開を試みた経験もあります.</p> <p>これまでの海外展開では,それぞれの国において,日本と同様のローカルなC2Cマーケットプレイスをゼロから立ち上げるというアプローチを取ってきました.しかし,今回のグローバル展開は越境取引の成功と課題から学んだ新たなアプローチを取っています.日本から海外へ商品を届ける「越境取引」を事業の軸に据え,そこで構築した顧客基盤を活かしながら段階的にサービスを拡大していく戦略です.また展開エリアも3年以内に50カ国と地域を目指しており,スピード感も従来とは大きく異なります.これは日本のお客さまや事業者に出品していただいたユニークで豊富な商品を世界中に届けることを起点とし,そこから更なる可能性を模索していく戦略への転換を意味しています.</p> <p>この事業戦略の転換によりエンジニアリング戦略も大きく変えました.</p> <p>これまでの日本とアメリカ,そしてイギリスへの展開はそれぞれ独立した異なるシステムにより実現してきました.もちろん当初は共通のコードベースを各国にデプロイする方式 (ただしデータは分離) を取っていました.しかし,日本向けに作られたシステムを各国の事情に対応させていくことによるコードベースの複雑化 (e.g., 国のスイッチのためのif文が多くの場所で書かれることになった) や,国間での方針のアラインが必要であるために各国の意思決定のスピードの低下といった課題にぶつかりました. 最終的にはフォークを決定し,それぞれ独立したシステムとなり,開発運用の体制も分離していくことになりました.アメリカはその後アプリ自体も現地のUI/UXに合わせて刷新を行い,独自の機能をその上に実装していくことになり,日本とアメリカのシステムは今日でも分離されています.</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/6e6ea979-fork.png" alt="" /></p> <p>この方法は,迅速に事業を立ち上げ,各国の市場に深く最適化できる点では有効なアプローチでした.各国の事業をそれぞれで伸ばしていくために独立した組織作りとシステムを開発していくのも重要だったと思います.一方でより長期的な視点に立ったときに以下のような課題があり,次の展開へと繋げることが困難になっていました.</p> <ul> <li><strong>展開のコストとスピード</strong>: 展開国を増やすという観点での共通の基盤の整備はできておらず,次の国を考えたときに新たなアプリケーションとバックエンド基盤を構築し直すこと,もしくは既存のシステムの大規模な改修を考える必要がある.</li> <li><strong>開発リソースの非効率性</strong>: 同じような機能がそれぞれの国で個別に実装され,各基盤に専任のチームが必要となるため,開発リソースの重複や運用の非効率性が発生する.</li> </ul> <p>現状の「越境取引」自体は既存の日本のシステム上に構築できています.しかし,以下でより詳しく述べるように既存のシステムは複雑化しており,今後のより高速に展開国を増やしていく,グローバルに向けたより良いUI/UXの提供を行っていくのは限界がきていました.そして「越境取引」の次,例えば新たな国でローカルのマーケットプレイスを展開するといったことに繋げることは非常に困難です.</p> <p>このような課題を根本的に解決し,そして「越境取引」を中心とした新たな海外展開を効率的に加速させるためには新たな戦略が必要でした.そこで「国や地域ごとに個別のシステムを構築するのではなく,単一のグローバルな基盤で全ての国や地域をサポートする」という新たなビジョンを打ち立てその基盤の開発を始めました.</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/aad7b50d-global-be.png" alt="" /></p> <h1>グローバル基盤の開発戦略</h1> <p>この単一のグローバル基盤の開発の戦略にはいくつかのアプローチが考えられますが「拡張と再構築のハイブリッドなアプローチ」を選択しています.このアプローチに至った背景をこれまでのメルカリのバックエンドシステムの変遷から説明します.</p> <h2>メルカリのバックエンドシステムの変遷</h2> <p>メルカリのバックエンドシステムはMonolithアーキテクチャ (単一コードベースに全ての機能を実装する方式) として始まっています.アメリカ事業やイギリス事業を開始するときにフォークという選択肢をとることができたのはこのためです (それぞれの国のスケールを支えるために裏側のインフラやツールとして多くの仕組みがありそれらを複製するのは容易ではなかったはずですが).</p> <p>2017年あたりから特に日本の組織の規模は急激に拡大を始めます.組織の拡大により単一の巨大なコードベースに多くの人が同時に開発を行うことが困難になり,また一部の機能のバグでサービス全体に障害が波及する事も多く発生しました.加えてほとんどのシステムがオンプレ上に構築されており,その運用や拡張がボトルネックになっていました.このような問題を解決するためにMicroservicesアーキテクチャ移行とクラウド移行 (それに伴うDevOps化への移行) を開始します.私自身が入社したのはこの直前で,移行プロジェクトの推進とMicroservices開発の基盤やツールを整える<a href="https://engineering.mercari.com/blog/entry/2020-07-16-083548/">Platform Engineeringチームの立ち上げと拡大</a>を担ってきました.</p> <p>Microservicesアーキテクチャ移行のアプローチとしては<a href="https://microservices.io/patterns/refactoring/strangler-application.html">Stranglerパターン</a>を採用しました.これは既存のシステムの前段にGatewayを置き,そのGatewayを軸にトラフィックを徐々に新しいシステムに移行していくという方針です.より具体的には,(1) 既存システムに実装されている機能群をMicroservicesとして切り出し (2) Gatewayからその機能の利用トラフィックを徐々にMicroservices側に流す,を繰り返すことで段階的に新システムに移行していくアプローチです.移行開始から数年が経過しましたが,多くの機能をMonolithから切り出し,またその上で新しい機能を開発してきました.またほぼ全てのサービスのクラウド移行も完了しています (サービス数でいうと100を超えています).</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/1be7ea5b-strangler.png" alt="" /></p> <p>Microservices化以降では日本ではメインとなるC2Cマーケットプレイス事業に加えて複数の新規事業の立ち上げが始まることになります.フィンテック事業のメルペイ,暗号資産のメルコイン,B2C事業のメルカリShops,そしてスキマバイトサービス事業のメルカリハロです.メルペイはメルカリの決済システムを切り出しており,MicroservicesアーキテクチャとしてC2Cと同じインフラ基盤上に構築しています.メルコインはセキュリティのためにインフラは大きく分離していますが,基本的には同様のアーキテクチャパターンで開発しています.ShopsはMicroservicesアーキテクチャですがC2Cとは切り離した独立したシステムになっています (モバイルアプリとしては一つですが,裏側のバックエンドは分離しています).</p> <p>この数年に渡るMicroservices移行と複数事業の立ち上げに合わせて推進してきたのは共通基盤の整備です.自分がリードしてきたPlatform Engineeringのレイヤーとしての開発基盤やツールだけではなく,<a href="https://engineering.mercari.com/blog/entry/20221018-mtf2022-day2-5/">ID基盤</a>や<a href="https://engineering.mercari.com/blog/entry/20250605-bf42ce60cf/">決済基盤</a>,マーケティング基盤のような複数事業にまたがって利用できる基盤も開発してきました.これらが創業以来メルカリのバックエンドシステムの変遷です.</p> <h3>既存システムの課題</h3> <p>2025年の現在,既存のシステムを俯瞰したときにいくつかの構造的な課題を抱えています.</p> <p>最も大きな問題は,C2Cマーケットプレイスとして重要な機能がMonolith基盤に残っているという点です.Stranglerパターンとしていくつかの機能をMicroserviceとして抜き出すことはできてはいますが,この方式はProxy的に上物の機能を抜き出すに止まりデータ移行まで進まなかった部分も多くあります.特に「トランザクション管理」「配送」といった機能をMonolithとそのDBから抜き出すことができていません.これらはロジックとして密結合が強くうまく分離を進められなかったというのも大きな理由です.そのためMonolithへの強い依存が未だに残っています.この部分は今でも多くの開発と変更が必要な一方で複雑なコードベース上に残ってるために,日本事業の継続的な改善においても早急な対処が必要です.Microservices移行の初期から関わってきた人間としては,この重要部に初期から挑まなかったのは大きな反省です.</p> <p>グローバル展開を見据えてもこれは大きな課題になります.Monolithに残るトランザクション管理と配送システムは日本市場に特化した設計になっています.トランザクション管理は日本円のみを前提としており,複数通貨での取引,為替処理,各国異なる税制への対応を追加することは非常にコストは高いです.配送システムも日本国内の配送業者のシステムと密結合しており,各国のローカル配送業者,異なる配送オプションへの対応は根本的な作り直しなしには実現は難しくなっていました.</p> <p>また,C2CマーケットプレイスとB2C Shopsのシステム乖離問題があります.現状は別々のトランザクション,配送システムをそれぞれが持っているだけでなく,プロダクトの管理も分かれており,結果として日本のお客さまに対しても統一的な体験を提供できていません.これは,もともとのビジョンとして独立したサービスが考慮されていたこと,方向性が変わり統合しようと思っても上のMonolith問題により実行が難しかったことが原因として挙げられます.</p> <p>Microservicesアーキテクチャ自体にも課題があります.各サービスのオーナーシップと自由度を重視し,サービス間で十分な抽象化を行えていなかったこと,適切なドメイン分離を行えておらず分割の単位も非常に小さくしてしまったことが原因で,多くの小さな,作り方が微妙に異なるMicroservicesが数多く構築されてしまいました.このためMicroservicesの運用のコストが非常に高くなってしまっています.メルカリはスピード感を持って物事を進めるため組織変更も頻繁に行いますが,そのたびにMicroservicesのオーナーシップの移管が必要になり,実装の差異によりオンボードのコストも高くなっています.</p> <p>これらの制約により,既存システムの延長線上でグローバル展開を進めることは,技術的にもビジネス的にも限界があることが明確になりました.</p> <h3>グローバル基盤の方向性</h3> <p>このような変遷と現状の課題をベースにグローバル基盤の開発方針としていくつかのアプローチを考慮しました.まず,過去のアメリカ展開のようにフォークという選択肢を取ることは非常に難しくなっています.Microservices化された多くのシステムを複製していくのは現実的ではありません.全てをゼロから再構築することも考えましたが,これもコストと効率の観点から選択肢から外しました.結論として「既存のシステムの拡張と再構築のハイブリッドなアプローチ」を選択しています.</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/f80e787f-rebuild.png" alt="" /></p> <p>このアプローチでは,どこまでを拡張とし,どこまでを再構築するか? のラインを決めるのが重要でした.既存のシステムの多くは日本の市場に特化したものになっており,また多くのサービスがMicroservices化されています.それら全てを拡張していくのは現実的ではありませんし,日本事業は引き続き重要であるため,グローバル展開はそこから独立して進められることも重要でした.また未だ残るMonolithにグローバルから依存することも避けたいという強い気持ちもありました.</p> <p>「拡張」としては複数事業の立ち上げとともに発展した共通基盤を主に活用することにしました.特に強い専門性が求められる,そして拡張性を考慮して設計されてるサービスを選定しています.以下で詳しく述べますがMicroservicesからの脱却も同時に考えており,小さな細かなサービスには依存するのではなく,十分な大きさかつ独立した「ドメイン」(SaaSとして置き換えられるレベル) に依存することも決めました.この基準により,例えば,ID基盤はグローバルで共通に,また決済基盤はメルペイ基盤を通じてStripeに接続しグローバルの通貨やローカルの決済手段に対応していく,といったことを進めています.他にも検索基盤,マーケティング基盤なども既存のシステムを拡張することで活用しています.</p> <p>それ以外の部分は「再構築」の選択肢をとっています.特に上述したC2Cサービスとしての「トランザクション管理」「配送」「アイテム・プロダクト管理」は作り直すしかありませんでした.日本と同じ問題を避けるために,(1) それぞれを疎に長期的な拡張性を容易にする (2) CとBの商品を同等に扱い,統一したUI/UXを提供することができることを考慮し,また複数カ国展開や別の国において新たなローカルマーケットプレイスを実現できるようにするために (3) 各国の通貨,言語,税制・関税,法規制に柔軟に対応できる (複数であることを前提にする.以下のTenetsを参照) (4) 日本以外の国の商品や配送手段を扱うことになっても対応できるようにする,を前提として構築しています.</p> <p>また単に作り直すだけではまた新たな別基盤が誕生するだけです.初期はグローバルでの成功をメインとしつつも,最終的には日本のC2CとB2Cの基盤も置き換えていく,という想定で動き始めています (実際にリリースまで達成したのでこの基盤を日本でも活用していくためのプロジェクトを始動しています).</p> <p>モバイルアプリとWebに関してもグローバルでは異なるUI/UXは必須なので作り直しの選択になっています.加えてバックエンドを刷新することでAPI自体も切り替えることもでき,実装自体もより良くできます.</p> <h2>MicroservicesからModular Monolithへ</h2> <p>上述したMicroservicesアーキテクチャの抱える課題に取り組むため「再構築」したバックエンド基盤はModular monolithアーキテクチャとして開発しています.</p> <h3>Microservicesの課題</h3> <p>メルカリにおいてMicroservicesアーキテクチャの運用コストが高くなってしまった大きな理由は,各サービスの開発の自由度を高めてしまったところにあります.サーバー実装はGoで,データベースとしてSpanner/CloudSQL (MySQL),インフラとしてKubernetesを利用する,という最低限の技術スタックの統一は進めてきました.一方で,レポジトリ戦略はPolyrepo (1サービス = 1 GitHub レポジトリ)として,基準となるテンプレートや最低限の共通ライブラリはあれど,レポジトリの構成や実装方針は各チームに委ねる形になっていました.そのため,マクロで見ると同じGoのMicroservicesですが,ミクロでみるとかなり異なるサービスが量産されました.一つ一つのサービスの運用のコストは小さくても,異なるサービスを複数面倒見る必要があるとその差異により共通化ができず,コストが高くなるということが起こっています.</p> <p>これに加えてメルカリはとにかくスピード感を持って物事を進め方向性を転換していくため組織変更も頻繁に行います.そのためMicroservicesのオーナーシップの移管も頻繁に行う必要があります.移管のたびにオンボードが必要ですが実装の差異によりそのコストも高くなります.また共通化を進めるのも難しいです.</p> <p>また特にMonolithから移行を進めたC2C側はドメインの適切な分離ができていないところも多く,サービスの凝集度が低いところが多くあります.これにより機能追加のために複数のサービス,チームに跨った変更が必要になり,コミュニケーションコストの増大にも繋がっています.サービスごとのオーナーシップを強化するという方向性は逆に外からの変更を受け入れにくくするということにも繋がっています.</p> <p>このような課題に対して上手くMicroservicesアーキテクチャによる実装を進めたのが<a href="https://engineering.mercari.com/blog/entry/20210810-mercari-shops-tech-stack/">メルカリShopsによるMonorepoを使ったアプローチ</a>です.この方式ではShopsに関わる全てのMicroservicesを1つのRepoにまとめ,サービス間の実装を抽象化・統一化するということを実現しており,複数サービスによる運用のコストを削減しています.開発体験としてはMonolith的に,裏ではサービスが分離されてデプロイされる(これにより耐障害性のメリットを得られる),という両方の良い部分を取り入れることができています.</p> <p>一方でこのアプローチであっても課題はあります.こうしたMonorepoのためのインフラや自動化の仕組みを管理維持していくのは非常にコストが高いです (既存のPlatformと大きく分けて構築されたため共通基盤チームとの連携が難しくなっていたことも原因として挙げられます).そもそもMicroservicesのテスト,デプロイ,開発環境の構築は複雑にならざるを得ません.例えばテスト環境は全部のサービスをPRごとに複製するという富豪的なアプローチをとっています.またサービスごとにDBを分けるなどを厳密に行なっており,インフラのコストも高くなってしまっています.</p> <h3>Modular Monolith</h3> <p>このような背景もあり,新しい基盤の構築にはModular monolithアーキテクチャーを選択しています.単なるModular monolithではなく特定のModuleを必要とあらば独立してデプロイ可能な形にしています (Service Weaverのコンセプトに近い).</p> <p>初期のメルカリのMonolithでは適切なドメイン分離・モジュール分離ができなかったためにコードの密結合とそれによる複雑化が発生したと思っています.サービスの境界,依存関係をモジュールごとに明確に整理することで同様の問題に当たらないようにしています.Microservicesのように細かく分離しすぎることで複雑になるのを避け,十分に機能が凝縮されたモジュールを作るようにしています.また必要なときに独立したデプロイを可能にすることでMicroservices的な耐障害性の利点も可能にしています.</p> <p>初期の開発フェーズでは人数も多くないので基本は特定のモジュールにオーナーシップを限定することはしていません (もちろん特定の領域に強い人はいる).皆がコードベース全体にオーナーシップを持ってもらうようにしています.これにより,プロダクト開発の優先度によってモジュールのアサインが動的に決まり,Microservicesで発生した無駄なコミュニケーション調整コストをなくしています.一方で,今後組織が拡大したとしても,モジュール単位でのアサインは可能であり,かつてのMonolithでハマった問題も解決できる余地もあります.</p> <p>Monolithであることで,ワンバイナリによるローカル開発環境の構築は容易になり,またテストやデプロイもシンプルになり,Microservicesによって生じていた開発負荷もなくすことでより良い開発体験を作れています.インフラやCI/CD基盤もPlatform Engineeringチームが提供するものをそのまま使うことができ,ShopsのMonorepoアプローチで陥った基盤運用コストを抑えることができています (より詳細は次のポストで @yanolabより紹介します).</p> <p>一方でこの方針は組織全体の中では新しく,既存のMicroservicesアプローチとどう共存していくかという課題があります.現実的に一度分離したMicroservicesを全てMonolithへと戻していくことは簡単ではありません.そのためMicroservices自体は今後も残ることになると思います.Microservices開発と運用のコストを減らしていくために,サービスの分割単位をより適切なレベルに合わせていくことや,さらに言えばShopsで実現したようなMonorepoアプローチによる統一化を高めていくことが重要だと思っています.そして将来的な新規事業でこれからMicroservicesアーキテクチャーを初手で選択することは,特別な理由がない限りは,しない方がいいと思っています.このグローバル基盤のModular monolith構築パターンを横に展開し,実装パターンを共通化していくことも考えています.</p> <h3>技術スタック</h3> <p>以下はこの基盤の構築に利用している技術スタックの一覧になります.基本的にはメルカリがこれまで培ってきたスタックを前提に大きく変えないでそれらをうまく活用するようにしています.</p> <ul> <li>インフラ: 引き続きメインのクラウドにはGoogle Cloudを採用しています.メインのリージョンは東京を使っていますが,将来的には(特にパフォーマンスの観点から) 別のリージョンを利用する可能性も考慮しています.アプリケーションの実行基盤にはPlatform Engineeringチームが管理するKubernetes (GKE) を利用しています.</li> <li>データベース: データベースにはAlloyDBを選択しました.これまではメルペイを中心にSpannerを選定してきましたが, (1) 長期的な展開を考慮した時にGoogle Cloud 全てで担えない可能性も考慮し,なるべくロックインを避けること (2) PostgreSQLによるより良い開発体験エコシステムを利用すること,を考慮してAlloyDBを選定しました.他にもCockroachDBも考えており,今後の展開によっては乗り換えも考慮する可能性があります</li> <li>言語・フレームワーク: サーバーはGo,iOSはSwift,AndroidはKotlin,WebはNext.js (TypeScript)としています.ここは大きく変えていません</li> <li>Monorepo: より詳細は別のブログがこのあと書かれますが,iOS, Android,Webはそれぞれ日本のサービスのレポジトリをMonorepoとして拡張することで開発されています.日本とグローバルで共有可能なモジュールを切り出し,CI/CDを共通化することで,開発と運用の効率化をしています.</li> </ul> <h2>Tenets</h2> <p>この新たな基盤とアプリの開発には日本のみではなくインド拠点のメンバーを含めて多くが参加しています.さまざまな背景のメンバーが参加しても,これまで上で紹介してきたような方向性を実現するには,皆が同等の指針にしたがって意思決定を行えることが重要です.</p> <p>これを実現するために「Global Engineering Tenets」を策定しました.TenetsはAmazonの<a href="https://aws.amazon.com/blogs/enterprise-strategy/tenets-supercharging-decision-making/">Tenets: supercharging decision-making</a>を参考にしています.</p> <p>主なTenetsをいくつか紹介します: </p> <ul> <li><strong>Design for two</strong>: ソフトウェア開発においてある機能のサポートを1から2に増やすよりも,2から3に増やす方が容易であることは実感としてわかると思います.例えば,アプリケーションが既に2つの言語をサポートしている場合,3つ目の言語を追加するのは簡単です.一方、アプリケーションが1つの言語しかサポートしていない場合、2つ目の言語を追加するにはi18nの仕組みなど多くの準備が必要になります.これはグローバル展開においても同様のことが言えます.既に複数国・地域に対応している基盤に新規の地域や国を追加するのは、単一地域/国向けのアプリケーションを拡張するよりもはるかに容易です.機能やシステム設計においては常に2カ国・地域以上を想定するようにしています.</li> <li><strong>Global by default but enable localization</strong>: グローバル利用に向けたシステム開発を進める一方で,単に複数国へ事業を拡大するだけでなく,主要市場ではローカライゼーション施策を実施します.そのため,システムは複数の国々へ迅速かつ容易に拡張できつつ,かつ特定の国の要件をサポートする柔軟性も考慮する必要があります.長期的にはローカライゼーションのため現地にエンジニアリングチームを設置する可能性もあり,彼らが独立してローカライズ機能を開発できるようにする必要があります.</li> <li><strong>Learn and unlearn from the past experience:</strong> 今回新規で「再構築」する部分が多くあります.ただし,これは完全に新規であるべきではなく,上で紹介したような過去の学びを重要な資産として活用するべきです.自分は概要を説明しましたが,それぞれの領域,モバイル開発,Web開発,プロダクト開発などさまざまな領域で見直すべき課題があります.新しく採用したメンバーに関してもこれらの活用は強くお願いしました.</li> <li><strong>Keep each country&#8217;s business isolated</strong>: 既存の基盤やプラットフォームを活用する場合でも、相互に影響を与えないようにすべきです.例えば,グローバルで発生したバグやインシデントが日本の事業に影響を与えたり,その逆が生じることを避ける必要があります.</li> </ul> <p>これらはデザインドキュメントを書く時など多くの場面で指針として利用されています (特にDesign for Twoは各所で言われた).もちろん,多くの人が参加してる,長期的なプロジェクト、である以上は細かな部分ではズレは発生していますが,大きな方向性としては皆が同じ方向を向けたのではないかと思っています.</p> <h2>今後の展望</h2> <p>今回のリリースでは基本となる機能の実装が完了した状態です.今後はこの上に越境取引にとって重要になる機能,例えば事業者商品の予約販売機能や鑑定機能,などを実装しつつ,展開国をどんどん増やしていくことを目標としています.横展開だけではなく,特定の国へのローカライズとグロースを行っていく必要もあり,さらに基盤を活用していくフェーズになります.また上で紹介したように基盤自体は日本でも使えることを想定しており,その置き換えのプロジェクトも始めています.これによりこれまで抱えていた技術的な負債も同時に返済していくことを考えています.</p> <h2>mercari Gears 2025</h2> <p>2025年11月13日に、実に7年ぶりとなるメルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます.@yanolob とともに「Building Foundation for Mercari’s Global Expansion」と題して登壇いたします.</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/0883bd6a-ogp_passion-4_en.png" alt="" /></p> <p>グローバルアプリを開発するにあたり、なぜこのアプローチに至ったのか,どのようなアーキテクチャや実装で支えているのか,組織的なチャレンジと技術の両面から詳しく紹介します.複数リージョン展開における開発・運用上のチャレンジや,組織横断での意思決定の工夫についても共有します.</p> <p>ぜひ,セッションを聞きに来てください! 参加登録はこちら 👉 <a href="https://gears.mercari.com/">https://gears.mercari.com/</a></p> <p>明日の記事は @yanolobさんです。引き続き<a href="https://engineering.mercari.com/blog/entry/20251003-mercari-crossborder/">連載企画:メルカリ初の世界共通アプリ『メルカリ グローバルアプリ』の開発舞台裏</a>をお楽しみください。</p> 連載企画:メルカリ初の世界共通アプリ「メルカリ グローバルアプリ」の開発舞台裏https://engineering.mercari.com/blog/entry/20251003-mercari-crossborder/https://engineering.mercari.com/blog/entry/20251003-mercari-crossborder/<p>こんにちは。Cross Border (XB) Engineeringの @deeeeet です。 先日、2025年9月30日に越境取引事業の新戦略を発表し、メルカリ初の世界共通アプリ「メルカリ グローバルアプリ」(以下 [&hellip;]</p> Mon, 06 Oct 2025 09:27:32 GMT<p>こんにちは。Cross Border (XB) Engineeringの <a href="https://x.com/deeeet">@deeeeet</a> です。</p> <p>先日、2025年9月30日に越境取引事業の新戦略を発表し、メルカリ初の世界共通アプリ「メルカリ グローバルアプリ」(以下、グローバルアプリ)の提供を開始しました。<br /> そこで今回は、グローバルアプリの開発プロジェクトの開発舞台裏をご紹介する連載企画をスタートいたします。<br /> トピックはバックエンド開発のみではなく、モバイル開発、Web開発、SRE &amp; Enablingなどなど多岐にわたるのでお楽しみに。</p> <h2>「メルカリ グローバルアプリ」の概要 </h2> <p>メルカリ初となる世界共通アプリで、 海外の購入者は「グローバルアプリ」を通じて日本の「メルカリ」と「メルカリShops」の商品を閲覧・購入することができます。言語や決済、複雑な手続きなどの課題が解消され、海外の購入者は日本の「メルカリ」と同様、かんたんかつ安心・安全にお買い物を楽しめます。2025年9月30日より台湾・香港で提供を開始し、今後、展開する国や地域を順次拡大する予定です。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/10/d14b0526--1024x373.png" alt="" /></p> <h2>公開予定表</h2> <p>こちらは、後日、各記事へのリンク集になります。</p> <table> <thead> <tr> <th style="text-align: left;">Title</th> <th style="text-align: left;">Author</th> </tr> </thead> <tbody> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251007-a09afcd49b/" title="グローバル展開にむけたアプリと基盤の再構築">グローバル展開にむけたアプリと基盤の再構築</a></td> <td style="text-align: left;">@deeeet</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251007-behind-the-infrastructure-powering-global-expansion/" title="グローバル展開を支える基盤の裏側">グローバル展開を支える基盤の裏側</a></td> <td style="text-align: left;">@yanolab</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251009-from-local-to-global-building-seamless-b2c-product-integration-at-mercari/" title="From Local to Global: Building Seamless B2C Product Integration at Mercari">From Local to Global: Building Seamless B2C Product Integration at Mercari</a></td> <td style="text-align: left;">@ahsun</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251010-order-management-in-mercari-global-marketplace/">Order management in Mercari Global Marketplace</a></td> <td style="text-align: left;">@takady</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251012-the-journey-of-user-generated-content-translation/">The Journey of User-Generated Content Translation</a></td> <td style="text-align: left;">@aymeric</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251013-behind-the-scenes-of-sre-supporting-the-global-web/">グローバルWebを支えるSREの裏側 — 開発を加速させるための改善アプローチ</a></td> <td style="text-align: left;">@hatappi</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251014-toward-a-global-identity-platform/">Toward a Global Identity Platform</a></td> <td style="text-align: left;">@gia</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251016-e2e-tests/">開発者全員が書けるE2Eテスト ─ 普通のgo testで実現するテスト基盤</a></td> <td style="text-align: left;">@ryotarai</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251016-50fb7b8c1a/">グローバルなメルカリの検索バックエンド設計と検索基盤拡充</a></td> <td style="text-align: left;">@shinpei</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251018-global-web-app/">Building a region‑aware, SEO‑friendly global web app</a></td> <td style="text-align: left;">@gary</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20251021-scaling-code-quality-modular-monolith-readability-team-ai-era/">モジュラモノリスの品質を支えるリーダビリティチーム ― AI時代のスケーラブルなコード管理</a></td> <td style="text-align: left;">@osari.k</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251022-how-we-deliver-mobile-app-updates-faster/">How We Deliver Mobile App Updates Faster</a></td> <td style="text-align: left;">@manoj</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251024-evolving-mercaris-ios-codebase-into-a-multi-product-monorepo/" title="Evolving Mercari’s iOS codebase into a multi-product monorepo">Evolving Mercari’s iOS codebase into a multi-product monorepo</a></td> <td style="text-align: left;">@shingt</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251025-internationalization-in-web-monorepo/">Enabling internationalization in our web Turbo monorepo</a></td> <td style="text-align: left;">@gary</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251028-the-ai-lied-to-me-and-thats-when-i-learned-how-to-use-it/">The AI Lied to Me — And That’s When I Learned How to Use It</a></td> <td style="text-align: left;">@andrei</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251030-taming-agents-in-the-mercari-web-monorepo/">Taming Agents in the Mercari Web Monorepo</a></td> <td style="text-align: left;">@maxi</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251117-benchmarking-databases-for-global-app/">BenchMarking Databases For Global APP</a></td> <td style="text-align: left;">@amit</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251120-behind-the-global-launch-decoding-the-android-engineering-strategy-for-our-new-app/">Behind the Global Launch: Decoding the Android Engineering Strategy for Our New App</a></td> <td style="text-align: left;">@Karthi</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20251120-data-fetching-strategy-for-mercari-global-marketplace-web-app/">Data-fetching strategy for Mercari Global Marketplace Web App</a></td> <td style="text-align: left;">@vb</td> </tr> <tr> <td style="text-align: left;">TBD: How we overcome Project management challenges (How to plan a product launch in 6 months)</td> <td style="text-align: left;">@g-bansal</td> </tr> <tr> <td style="text-align: left;">Guest post from FT payment platform — Engineering for Multi-Currency and Multi-Provider Payments</td> <td style="text-align: left;">@ryuyama</td> </tr> <tr> <td style="text-align: left;">TBD</td> <td style="text-align: left;">@manas</td> </tr> <tr> <td style="text-align: left;">TBD: distributed transactions on checkout flow, specially error handling, retry</td> <td style="text-align: left;">@ahsun</td> </tr> <tr> <td style="text-align: left;">Something about global payment and checkout</td> <td style="text-align: left;">@huhu</td> </tr> <tr> <td style="text-align: left;">TBD: Ops development with AI</td> <td style="text-align: left;">@waiting.lau</td> </tr> <tr> <td style="text-align: left;">Sync Saga</td> <td style="text-align: left;">@Shishir</td> </tr> <tr> <td style="text-align: left;">TBD: High output teams</td> <td style="text-align: left;">@Atif</td> </tr> <tr> <td style="text-align: left;">TBD: Ordering Features</td> <td style="text-align: left;">@Shreyasi</td> </tr> <tr> <td style="text-align: left;">TBD</td> <td style="text-align: left;">@Chong (チョン)</td> </tr> <tr> <td style="text-align: left;">TBD</td> <td style="text-align: left;">@chris</td> </tr> </tbody> </table> <p> ひとつでも気になる記事がある方は、この記事をブックマークしておくか、 <a href="https://x.com/mercaridevjp" title="エンジニア向け公式X">エンジニア向け公式X</a> をフォロー&チェックしてくださいね!</p> メルカリ ハロのインターンで求人のリスク予測モデル作成・比較してみたhttps://engineering.mercari.com/blog/entry/20250912-d05fee6c7e/https://engineering.mercari.com/blog/entry/20250912-d05fee6c7e/<p>はじめに 2025年度のBuild@Mercariに参加し、メルカリ ハロのMLチームでインターンをしている@Ariaと@Ririkoです。私たちはメルカリ ハロの求人のリスク予測に取り組みました。この記事では、インター [&hellip;]</p> Fri, 12 Sep 2025 12:00:29 GMT<h1>はじめに</h1> <p>2025年度のBuild@Mercariに参加し、メルカリ ハロのMLチームでインターンをしている@Ariaと@Ririkoです。私たちはメルカリ ハロの求人のリスク予測に取り組みました。この記事では、インターンで取り組んだこと・感想などについて書いていきたいと思います!</p> <h1>自己紹介</h1> <p>@Aria</p> <p>こんにちは!大学1年の@Ariaです。私は高校生の時Build@Mercariに参加し、夏休みでBuildインターンをしています!機械学習・AIについて学んでみたいと思い、メルカリハロのMLに応募しました。</p> <p>@Ririko</p> <p>大学の学部3年の@Ririkoです!大学では電子情報工学を専攻しています。春休みにBuild@Mercariに参加しました。機械学習やAIに以前から興味があり、今回のBuildインターンに応募しました。本格的にインターンに参加するのはこれが初めてです。</p> <h1>背景</h1> <p>メルカリ ハロでは「求人の内容が適切か」「不適切な表現が含まれていないか」などといったリスクのある求人がないかを全件チェックしています。</p> <p>今回、私たちは、様々な機械学習手法で求人のリスク予測モデルを作成し、それぞれのコスト・精度・管理のしやすさなどの検証を行いました。</p> <h1>やったこと</h1> <p>以下のモデルを試し、コスト・精度・管理のしやすさなどの比較を行いました。</p> <ul> <li>統計モデル <ul> <li>ロジスティック回帰</li> <li>LightGBM</li> </ul> </li> <li>NNモデル <ul> <li>BERT</li> </ul> </li> <li>LLM</li> </ul> <h1>各モデルの構築について</h1> <h2>概要</h2> <p>求人のリスク予測においては、全体数に対してリスクのある求人数が少ないです。モデル作成の際に最も重要なことはリスクのある求人を取りこぼさずに検知することです。モデルが誤ってリスクありと判断した求人の数(False Positive数)がある程度増えてしまってでも、モデルが誤ってリスクなしと判断した求人の数(False Negative数)をゼロにすることが重要になります。</p> <p>また、統計モデルやニューラルネットワークモデルを扱う際、訓練時に愚直に学習させると、データに不均衡があるためにすべての求人をリスクなしと判断するようになってしまいます。そのため、正例(リスクがある求人データ)の数を拡張したり、損失関数に重みづけをしたりするなどして学習を工夫する必要があります。</p> <h2>統計モデル</h2> <p>ロジスティック回帰とLightGBM、どちらのモデルを用いる場合でも、コンピューターが文章を理解できるよう前処理が必要です。まず、文章のテキストデータを形態素解析によって単語に分解します。例えば、「猫はコタツで丸くなる」という文は、「猫」「は」「コタツ」「で」「丸く」「なる」といった単語に分けられます。</p> <p>次に、TF-IDF(Term Frequency Inverse Document Frequency)という手法を用います。TFは文書内での単語の頻出度を表し、IDFは単語の希少度を表します。TFとIDFを掛け合わせることで、単語を数値化し、文書全体をベクトルとして表すことが可能になります。このベクトル化の際には、正例に特徴的な単語をFeature(ベクトルの基底)として抽出しました。TF-IDFを用いることで、「は」や「で」のように頻繁に出現するため重要度が低い単語ではなく、特定の文章にのみ現れる重要度の高い特徴的な単語を際立たせることができます。</p> <p>このようにして作られたベクトルを入力として、各モデルは、求人にリスクがある場合は1、ない場合は0のラベルを出力するよう学習させました。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/09/4f0dc22f--2025-09-11-12.47.37.png" alt="テキストデータの前処理" /></p> <h3>ロジスティック回帰</h3> <p>ロジスティック回帰は、機械学習において最も基本的な分類モデルで、結果が0か1かといった二値である事象を予測するための統計的手法です。特定の事象が起こる確率を、説明変数を使って計算します。このモデルは、確率を0から1の間に収めるためのシグモイド関数という特殊な関数を用いるのが特徴です。</p> <p>訓練データを愚直に学習させると、データに不均衡があるために、うまく学習できずすべてリスクなしと判断するモデルになってしまいます。そのため、今回のロジスティック回帰では損失関数の計算時に、数少ない正例のペナルティが大きくなるように重み付けをしながら計算して学習を回しました。また、ハイパーパラメータのグリッドサーチなどによりモデルの性能向上を目指しました。</p> <h3>LightGBM</h3> <p>LightGBMは決定木をベースとする機械学習アルゴリズムです。特に、大規模なデータセットを扱う際に、その高速性と高い予測性能から、データサイエンスの分野で広く利用されています。</p> <p>LightGBMには、ハイパーパラメータと呼ばれる設定項目がたくさんあります。これらのパラメータを適切に設定することで、モデルの性能は大きく変わります。そのため、グリッドサーチという手法を使って、最適なパラメータの組み合わせを自動的に探索し、モデルの改善に取り組みました。</p> <h2>BERT</h2> <p>BERTは、2018年にGoogleにより開発されたTransformerのエンコーダー部分を基盤とする事前学習済み言語モデルです。大量のテキストデータから単語の文脈を双方向で学習するため、文全体の意味を深く理解できます。これにより、質問応答や文章要約など、さまざまなNLPタスクで高い性能を発揮します。</p> <p>BERTは正例と負例の数の不均衡なデータセットの学習が苦手です。そのため、LLMを用いて合成データを作成し、正例の数を増やしました。データ拡張の際にLLMに渡したプロンプトには元の正例の中からランダムに選んだデータをfew-shotとして組み込み、生成されたデータの文体に多様性が生まれるように工夫しました。さらに、BERTの学習において重要なパラメータ(トークン化の際の最大文字数、バッチサイズ、エポック数、学習率など)をグリッドサーチにより探索し、モデルの性能向上に取り組みました。</p> <h2>LLM</h2> <p>LLMに学習は必要ないので、データの前処理なしでプロンプトを工夫して、精度改善をしました。単に「リスクがあるか、ないか」のラベルだけでなく、なぜそのように判断したのかという理由も出力させました。LLMの出力から、誤った判断を下した理由や、不足している情報がないかを分析し、足りない指示をプロンプトに追加し改善しました。また、求人審査にはルールがあるため、そのルールをしっかり理解し、テキストデータを見直すことも精度改善につながりました。</p> <h1>実用上の観点から考えた各モデルの長所・短所</h1> <table style="width:100%;"> <thead> <tr> <th style="width: 30%;"></th> <th style="width: 35%;">長所</th> <th style="width: 35%;">短所</th> </tr> </thead> <tbody> <tr> <td><strong>ロジスティック回帰/</p> <p>Light GBM</strong></td> <td>2値のクラスに分類するための境界線である閾値の調整によってどこまで検知するか決められる</p> <p>処理が非常に早い</p> <p>求人件数が増えても運用コストがあまり変わらない</td> <td>説明性が低い</p> <p>ルールが改定されたときに新たな学習データの用意、学習に手間がかかる</td> </tr> <tr> <td><strong>BERT</strong></td> <td>2値のクラスに分類するための境界線である閾値の調整によってどこまで検知するか決められる</p> <p>文脈の理解が得意</td> <td>説明性が低い</p> <p>ルールが改定されたときに新たな学習データの用意、学習に手間がかかる</p> <p>推論にGPU、または多くのCPUを使うので統計モデルに比べて運用コストが高い</td> </tr> <tr> <td><strong>LLM</strong></td> <td>管理がプロンプトの更新のみで簡単</p> <p>ルールが改定された時も更新が容易</p> <p>過去にデータがないものへの対応が可能</p> <p>なぜそのように判断したかの理由が自然言語で説明できる</td> <td>件数が増えると、処理時間・運用コストが線形的に増える</td> </tr> </tbody> </table> <h1>比較結果 </h1> <p>統計モデルは複雑な文脈の問題に関しての求人のリスク検知は苦手であるものの、今回取り組んだタスクにおいては、学習方法を工夫することにより、LLMと同等の精度を出すことができました。</p> <p>BERTについても、学習に使うデータを拡張して正例・負例の数を同程度にすることにより、LLMと同等の精度を出すことができましたが、運用コストが統計モデルに対して高いので、今回の求人のリスク予測に関してはBERTを選択するメリットがないという結果になりました。</p> <p>運用コスト面で比較すると、統計モデルは小さなインスタンスで動くので、冗長性を考慮しても運用コストが低く、また求人件数が増えてもコストが変化しにくいです。BERTは統計モデルに比べてCPUがたくさん必要なので、元々のコストが高くなります。一方、LLMはトークンごとの課金のため、求人件数が増えるごとにコストも線形的に高くなります。</p> <p>これらの結果から、求人件数が少ない段階ではLLMが柔軟に活用できる一方、求人数が増えるとLLMではないモデルへの切り替えがコスト削減になることが分かります。</p> <h1>インターンでの学び・気づいたこと</h1> <p>今回のインターンを通して、テキストデータを前処理して統計モデルに適応する手法や今まで学ぶ機会がなかったBERTなどのモデルについて理解を深めることができました。モデルの性能を向上させるためにやるべき手法についても実際に手を動かしながら学ぶことができました。また、モデルに変更を加えて性能向上を目指すだけでなく、与えられたデータを自分の目でよく確認してデータの特徴を掴むことも非常に重要であることも学びました。</p> <p>実際の業務においては、自分が考えていることや試してみようと思っていることを他の人が確認できる形で言語化しておくことで、コミュニケーションがスムーズになるということを認識しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/09/a784698f-image.png" alt="" width="1720" class="size-full wp-image-34581" srcset="https://storage.googleapis.com/prd-engineering-asset/2025/09/a784698f-image.png 1720w, https://storage.googleapis.com/prd-engineering-asset/2025/09/a784698f-image-300x200.png 300w, https://storage.googleapis.com/prd-engineering-asset/2025/09/a784698f-image-1024x683.png 1024w, https://storage.googleapis.com/prd-engineering-asset/2025/09/a784698f-image-768x512.png 768w, https://storage.googleapis.com/prd-engineering-asset/2025/09/a784698f-image-1536x1024.png 1536w, https://storage.googleapis.com/prd-engineering-asset/2025/09/a784698f-image-1200x800.png 1200w" sizes="(max-width: 1720px) 100vw, 1720px" /> 左から@Aria, @Ririko</p> <h1>終わりに</h1> <p>本記事では、インターンで取り組んだタスク、感想についてお話しさせていただきました。今回のインターンを通して、開発に必要な知識、またキャリア面での知識など様々な学びを得ることができました。一ヶ月という時間はあっという間でしたが、とても濃い時間を過ごせました。</p> <p>メンターの@ku-muさん、アドバイスをくださった@arr0wさん、ML teamの皆さん、本当にありがとうございました!</p> 文系の私がBuild@Mercariでエンジニアへの第一歩を踏み出せた話https://engineering.mercari.com/blog/entry/20250910-c647c9a463/https://engineering.mercari.com/blog/entry/20250910-c647c9a463/<p>はじめに はじめまして!8月の1ヶ月間、Buildインターンに参加したkyoroです。 文系の私にとって、Build@Mercariは「エンジニアへの第一歩」となった大変貴重な成長機会でした。私がBuild@Mercar [&hellip;]</p> Wed, 10 Sep 2025 14:13:45 GMT<h2>はじめに</h2> <p>はじめまして!8月の1ヶ月間、Buildインターンに参加したkyoroです。</p> <p>文系の私にとって、Build@Mercariは「エンジニアへの第一歩」となった大変貴重な成長機会でした。私がBuild@Mercariで学んだことや経験したことを共有することで、同じように「エンジニアになりたいけど、非STEM領域出身で自信がない」や、「成長の機会に恵まれていない」と感じている方々に、Build@Mercariという選択肢があることを知ってほしいと思いました。<br /> 私に似たバックグラウンドをお持ちの方や、これから参加を検討されている方々の参考になれば幸いです。</p> <h3>なぜBuild@Mercariに参加したか</h3> <p>私は大学1年生で受講したデータサイエンスの授業をきっかけにプログラミングに興味を持ちました。しかし、私の学部は完全に文系でCS関連の授業もなかったため、それ以降は独学で学習を進めていました。<br /> 独学で学習を進めていたものの、実践の機会が少なく、あまり成長を実感できていない部分がありました。インターンシップを通して実践的に成長できたら、と思っていましたが、応募時点で一定の開発経験や制作物を求められることが多く、なかなか受け入れてもらえない状況が続いていました。そんな時、テックコミュニティ経由でBuild@Mercariの存在を知りました。現時点での経験が浅くても参加できるエンジニア育成プログラムと聞いてすぐに応募を決めました。</p> <h3>Build@Mercariってどんなプログラム?</h3> <p>性自認が女性である方を対象に、ソフトウェアエンジニアリングのスキルトレーニングとインターンシップの機会を提供するプログラムです。<br /> 現在STEM領域では女性がマイノリティとなっています。業界全体のD&amp;Iを推進するべく、メルカリではこうした学習機会を私たちに提供してくれています。</p> <h2>Buildトレーニングについて</h2> <p>2週間で、メルカリを想定した簡易出品アプリを個人で構築します。<br /> この課題を通じて、Gitの使い方からAPI開発、フロントエンドの実装、Dockerによるコンテナ化まで、Webアプリケーション開発の基本を一通り学ぶことができました。アプリ構築以外にも「アルゴリズムとデータ構造」や「データ分析」を学ぶステップも用意されており、非常に内容が充実しています。</p> <h3>選考の話</h3> <p>選考では、志望動機とコーディングテストを提出します。コーディングテストは、コンピュータサイエンスに関する基礎知識を確認するためにオンラインで実施されます。Buildトレーニングプログラムは、通常のインターンシップ選考とは異なり、多くの方にチャレンジしていただきたいという想いから、コーディングテストの難易度は低めに設定されています。そのため、現時点でのスキルに自信がない方も応募をためらう必要はありません。</p> <h3>このトレーニングの良かった点</h3> <p>1つ目は「数人のチームに分かれて、各自課題に取り組む」ことです。平日は毎日、グループ内で進捗共有の時間があり、メンターさんに質問したり、他のメンバーの進み具合を知ることができました。ここで自分の遅れを認識し、良い意味で焦れたことが、トレーニングを完遂できた大きな要因でした。初学者が一人で学習していると、わからないところで立ち止まってしまい、そのまま学習を中断してしまうことも多いです。そういった意味でも、このように仲間やサポートがある環境は、とても心強かったです。</p> <p>2つ目は、「参加者は1年間無料でUdemyの講義を受講できる」ことです。<br /> 参加時点では周辺知識がほとんどなかったため、とにかくトレーニング中はコードを動かすことで精一杯でした。しかし、トレーニング終了後にUdemyを大いに活用し、点と点だった理解を少しずつつなげて腹落ちさせ、「なぜそのコードが必要なのか」「どんな仕組みで動いているのか」を理解することができました。</p> <h3>トレーニングの成果</h3> <p>参加前の私は git と github の違いすらわからないレベルでした。しかし、トレーニング終了の2ヶ月後に1人で参加したハッカソンで企業賞をもらえるレベルに成長していました。Buildトレーニングは独学で伸び悩んでいた私に大きな成長を与えてくれました。</p> <h2>Build インターンについて</h2> <h3>選考の話</h3> <p>構築した出品APIが動作確認のテストに通ったトレーニング参加者は、1ヶ月間のBuildインターンに進むことができます。<br /> 基本的には、トレーニング終了後に提出するアンケートに回答した希望ポジションと部署を前提に選考が進みます。<br /> また、就業型インターンに応募して不合格となった場合でも、再度Buildインターンへ応募することも可能だそうです。<br /> Buildインターンの選考では30分程度の面接が1回設けられます。この面接は開発経験などの深掘りというよりも、配属先のエンジニアマネージャーさんとインターンへの参加意欲の確認を行いました。</p> <h3>配属先</h3> <p>私はメルカリのお客さまのお問い合わせ管理システムを構築しているContact Centerチームにバックエンドエンジニアとして配属されました。</p> <h3>取り組んだこと</h3> <p>現状のシステムでは、お問い合わせに対する自動返信や通知メールの文言がハードコードされており、文言の変更コストがかかることが課題でした。私は、その文言をデータベースから取得できるようにするタスクに取り組みました。<br /> 将来的にこの機能を拡張させ、管理画面から文言に変更を加えられるようにすることで、実際にお問い合わせに対応する非エンジニアの方でも文言の変更が可能となり、運用コスト削減や、変更スピードの向上が期待できます。</p> <p>大きく分けて3つのフェーズでタスクに取り組みました。</p> <h4>1. DB設計</h4> <p>どの文言をデータベースに移行するかを、文言が使われる場面や、その変更可能性も考慮しながら検討しました。<br /> 最初は深く考えずに、既存の他のDB設計を参考に設計していました。チームの方に設計をレビューしていただいた際、なぜそこでこの制約を入れたのかという質問に対してすぐに答えられなかった経験から、自分の設計根拠の甘さに気づくことができました。<br /> DB設計のみならず、設計や方針に唯一の正解はなく、エンジニア間で議論する中で自分一人では気付けなかった点に気づくことで、最適な解が導き出されることを学びました。議論を円滑に進めるためにも、常になぜ自分がその方針が良いと思ったのか、他に考えられる方針はないのか比較検討した上で、設計の意図や根拠を明確にもつ重要性を学びました。</p> <h4>2. DBへの文言挿入</h4> <p>実際にデータベースへ文言を挿入する段階では、移行する大量の文言を既存の文言と一言一句のずれも起きないよう、正確なクエリを作成する作業が発生しました。<br /> また、Spanner CLIの仕様上、文言に含まれる空行をSQL文の終了と認識してしまうため、全ての空行を改行文字に変更する必要があり、非常にミスが発生しやすい作業でした。作成したSQL文の最終確認は大変骨の折れる作業でした。最終確認をしていただいたチームの方々、日常の忙しい業務の最中、時間を割いていただいてありがとうございました。</p> <h4>3. 文言取得の実装</h4> <ul> <li> <p><strong>詳細設計の話</strong></p> <ul> <li>実装時の大幅な手戻りを回避するため、詳細設計を詰めてから実装に入りました。詳細設計を立てるには、5年分のコンテキストがある既存コードの流れを短期間で把握する必要があり、最も時間がかかりました。また、プロジェクト初期ほど見積もりの誤差が大きくなるという、「<a href="https://en.wikipedia.org/wiki/Cone_of_uncertainty" title="不確実性コーン">不確実性コーン</a>」のお話が興味深かったです。<br /> 最初に私が立てた詳細設計は見積もりが甘く、もう少し内容を詰めるようレビューをいただきました。その結果、後の実装工程では迷わず手を動かすことができました。開発プロセスの中で設計を疎かにするとその分の見積もりの誤差が実装に引き継がれてしまうということを身をもって学ぶことができました。<br /> チームでは「Architecture Decision Record」と呼ばれる意思決定記録に設計方針を残していました。「なぜ、そういう設計になったのか」や「他の案はあったのか?」などの議論や決定事項を記録しておくことで、今後新しい機能を実装しようとした時に、過去の設計をそのまま使用できたり、参考にして拡張することができる利点があると教えていただきました。ハッカソンのチーム開発経験とは比べものにならない「現場のチーム開発」を学ぶことができました。</li> </ul> </li> <li> <p><strong>テストコードの話</strong></p> <ul> <li>テストコードを組み立てるのは今回が初めてでした。今までは正常に動作していることを確認するだけで満足していましたが、コードの品質や保守性を高める重要な手段であることを学びました。Go特有の「テーブル駆動テスト」や「テスト駆動開発」、正常に値が返されるかだけではなく、渡す値などを変えた時、エラーがきちんと返されるかなど、「幅広いテストケースの想定」が必要であることを学び、テストコードの奥深さを知りました。</li> </ul> </li> <li> <p><strong>Goの話</strong></p> <ul> <li>Contact Centerチームではバックエンド開発言語にGoが採用されていました。Goに触れるのは今回のインターンが初めてだったため、開発を進めながら多くのGo独自の構文や考え方を学べました。</li> </ul> </li> </ul> <h2>全体を通して学んだこと</h2> <h3>エンジニアの仕事</h3> <p>インターンを通して実務に入ったことで、エンジニアに求められることはコードを書く力だけではないことを強く実感しました。特に印象的だった学びが、以下の3つです。</p> <p>1つ目は、「プログラミングは、コードを”書く”時間よりもコードを”読む”時間の方が多い」だということです。機能を新しく追加するにしても、まずは既存のコードを読み解いて、どこをどう変更すべきかを理解する必要があります。</p> <p>2つ目は、開発には期限があり、その中でいかに優先順位をつけてタスクを進めていくかも仕事では求められるということです。チームに進捗を伝える際も、「まだ終わってません」と伝えるのではなく、「現状で〜%進んでいます。ここまでに〜日かかったので、残りも同じくらいのペースで進めば、あと〜日程度かかりそうです」というように、進捗と予測をセットで伝える報告の仕方がチームとしての動きやすさにもつながるというお話もお聞きし、勉強になりました。</p> <p>3つ目は、仕事をチームで進める上で技術力と同じくらい、「伝える力」が大切ということです。PRを作成する際もレビューする側の目線に立って、意図や背景を丁寧に書くことであったり、1on1での質問においても、ただ「ここがわかりません」と言うのではなく、「〜について調べたり、〜を試したけれど、この時点で詰まっています」というふうに、自分の思考プロセスを整理して伝えることで、より的確なアドバイスをもらえると感じました。</p> <h3>アーキテクチャ</h3> <p>アーキテクチャに関する話の中で、メルカリがモノリシックな構成からマイクロサービス化へと移行していった背景や、その過程で起きた技術的・組織的な変化について伺いました。<br /> 特に印象的だったのは、マイクロサービス化によって各モジュールが自律的に機能するようになったことで、開発チームもそれぞれが独立して動けるようになり、組織構造そのものにも変化があったという点です。<br /> また、既存コードの流れを把握する際に、「ドメイン駆動設計」や「クリーンアーキテクチャ」といった考え方も教えていただきました。<br /> こうした設計思想は、単に「きれいなコードを書く」ためではなく、長期的に安定したシステムを作るための考え方であることを学びました。</p> <h3>メルカリ文化</h3> <h4>社内勉強会</h4> <p>社内勉強会も活発で、私はOpenAI社の講師によるトレーニングプログラムに参加しました。会話をするAIを、「指示、知識、アクション」の観点から細かく設定できるカスタムGPTの活用事例を実演形式で学びました。<br /> 普段からノンカスタムGPTは回答の情報量が多く、何が質問に対する回答の本質なのか見失いがちだったため、「なるべく不必要な部分を削ぎ落とし、シンプルで初学者にとってわかりやすい説明を心がけること」、「より人間らしく、後輩から慕われるようなエンジニアとして振る舞うこと」を指示したカスタムGPTを作成しました。インターン期間中このGPTへの質問でよりスピーディーに疑問を解消しながら開発できたこともあり、実際に社内勉強会での学びの恩恵を受けました。こうした様々な勉強会に参加できる環境が非常に魅力的であると感じました。</p> <h4>開発手法</h4> <p>チームでは「スクラム」という開発手法が取り入れられていました。スプリントと呼ばれる数週間の単位で開発期間を短く区切り、毎回「今回のスプリントではこれをやる」という目標をチームできめてから開発に入る形でした。<br /> スプリント期間中は毎朝のDaily Scrum(朝会)でチーム内で進捗や課題を共有しあい、タスクの進行を常に可視化しながらチーム全体の開発を前に進めていたことが印象的でした。</p> <h4>その他</h4> <p>インターン期間中、Contact Centerチームの方々やトレーニングで担当していただいたメンターさんと何度かランチに行かせていただきました。「キャリアの話」、「日本人チームと外国人チームの違い」、「メルカリの昔と今」、「エンジニアの成長ステップ」といったテーマについて、実際に働くエンジニアの方々から直接お話を伺うことができました。普段なかなか聞くことができないような、リアルな現場の話や考えに触れることができたことで、自分自身のキャリアや働き方を考えるきっかけにもなり、大変貴重な学びとなりました。</p> <h2>終わりに</h2> <h3>メルカリと配属先チームの皆さんへ</h3> <p>Build@Mercariという成長機会を提供してくれたメルカリ、そしてインターンで受け入れてくださったContact Centerチームのメンターさんをはじめとするメンバーの皆さんに心から感謝の気持ちを述べたいと思います。短い期間でしたがここには書ききれないほどたくさんのことが学べました。本当にありがとうございました。</p> <h3>これから参加しようと考えている方へ</h3> <p>ほぼゼロだった私がここまで多くのことを学び成長することができたのはBuild@Mercariのおかげです。もし今の自分のスキルに自信がなくても、エンジニアとして成長したいと考えているなら、ぜひBuild@Mercariへ参加して欲しいです。また、Buildインターンや就業型インターンで実際にメルカリのサービスに触れることで得られる学びは非常に大きいです。ぜひBuildトレーニングで終わらずに、有給インターンシップまで進んで欲しいです。</p> <p><strong>※この体験記は2025年度(今年度)のプログラム内容です。来年度以降のプログラムにおいては内容が変更になる可能性がありますので、ご了承ください。</strong></p> オフサイトで実践したふりかえりワークショップ設計の工夫と学びhttps://engineering.mercari.com/blog/entry/20250807-16d7cf7b4b/https://engineering.mercari.com/blog/entry/20250807-16d7cf7b4b/<p>メルカリハロで QA Engineering manageをしている @____rina____です。 本記事では、プロジェクトチームで実施したオフサイトについて、スクラムマスターとしてワークショップデザインを担当した経 [&hellip;]</p> Tue, 12 Aug 2025 08:00:54 GMT<p>メルカリハロで QA Engineering manageをしている <a href="https://engineering.mercari.com/blog/author/underscore42rina/" title="@\_\_\_\_rina\_\_\_\_">@____rina____</a>です。</p> <p>本記事では、プロジェクトチームで実施したオフサイトについて、スクラムマスターとしてワークショップデザインを担当した経験を共有します。</p> <p>リモートワークも継続する中で、対面でのオフサイトをどのように設計し、初回参加者への配慮をどのように実践したかについて詳しく解説します。</p> <h4>この記事から読者が学べること:</h4> <ul> <li>長期プロジェクトの効果的なふりかえり手法(タイムラインふりかえり)</li> <li>AIを活用したワークショップデザインの実践例</li> <li>初回参加者への配慮と心理的安全性の確保方法</li> <li>5グラウンドルールを活用した質の高い議論の実現方法</li> <li>対面でのチームビルディングの重要性と効果</li> <li>リモートワーク環境でのコミュニケーション技術的課題と解決策</li> <li>アナログ手法による対面ワークショップの効果と重要性</li> </ul> <h4>執筆者自身の学び:</h4> <p>スクラムマスターとしてワークショップデザインを担当した経験を通じて、参加者の心理的安全性を確保することの重要性を改めて実感しました。特に、初回参加者への丁寧な説明や視覚的な資料の活用、段階的な進行が、ワークショップの成功に直結することを学びました。また、AIを活用した効率的なワークショップ設計の可能性も実感でき、人間ならではの創造性や配慮と組み合わせることで、より効果的なワークショップを設計できることを確認しました。</p> <h2>開催概要</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/08/882ea1af-member-1024x437.jpg" alt="" /><br /> 今回実施したのは、メルカリハロで事業者向けサービスを開始するにあたり、事業者から手数料を徴収する仕組みを構築するプロジェクトチーム向けのオフサイトです。このチームのメンバーが、福岡市内の会場に集まり、5時間にわたってオフサイトを開催しました。普段はリモートで業務を進めているメンバーですが、この日は全国からメンバーが一堂に会し、対面ならではの熱量と一体感を感じながら、プロジェクトのこれまでとこれからについてじっくりと語り合う貴重な時間となりました。</p> <p>参加者はPM、エンジニア、EM、デザイナー、QAから10名を超えるメンバーが参加し、初回参加者も含めて多様なバックグラウンドを持つメンバーが集まりました。</p> <h2>背景・目的</h2> <p>今回のオフサイトを企画した背景には、長期にわたる手数料プロジェクトをふりかえり、今後の改善やチームの連携強化を図りたいという思いがありました。リモートワークも続く中、日々のコミュニケーションはどうしてもテキストやオンライン会議に偏りがちです。だからこそ、対面で集まり、普段は話せないような深い議論や、カジュアルな交流を通じて、チームとしての結束力を高めることが不可欠だと考えました。</p> <p>手数料プロジェクトでは私がスクラムマスターを務めており、今回のオフサイトはワークショップデザインから全体の進行まで、一貫して設計・運営を担当しました。特に意識したのは、初回参加者が安心して参加できる環境を整えることでした。</p> <h2>ワークショップデザインの工夫</h2> <h3>AIを活用したアジェンダ作成とアイスブレイク設計</h3> <p>今回のオフサイトの準備段階では、AIを積極的に活用しました。まず、ワークショップ全体のアジェンダ作成において、AIに手数料プロジェクトの特性や参加者の構成、目的などを入力し、最適な進行スケジュールの提案を受けました。AIが提案した時間配分やセッション構成をベースに、実際の参加者数や会場の制約を考慮して調整を加えることで、効率的で効果的なアジェンダを作成できました。</p> <p>特に印象的だったのは、アイスブレイク用のクイズ作成です。AIに福岡の文化や目にする予定の建築物に関する問題を生成してもらい、参加者の滞在をもっと楽しくする内容にしました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/08/7dfcd268-quiz-1024x582.jpg" alt="" /></p> <h3>初回参加者への配慮:丁寧なチェックインと説明</h3> <p>ワークショップデザインにおいて最も重要視したのは、初めてワークショップに参加するメンバーへの配慮でした。参加者の中には、付箋を使ったワークショップや、グループディスカッションに不慣れな方もいました。そのため、各セッションの開始時には必ず丁寧な説明を行い、参加者が迷わないよう配慮しました。</p> <h3>各セッションでの具体的な配慮</h3> <p>タイムラインふりかえりでは、具体的な手順を視覚的な資料とともに説明しました。グルーピング作業では、抽象的な指示ではなく具体的な例を示すことで、参加者が迷わずに作業を進められるよう工夫しました。</p> <p>各セッションの開始時には必ず目的、手順、期待する成果物を明確に伝え、質問しやすい雰囲気を作ることで、参加者全員が安心してワークショップに参加できる環境を整えました。</p> <h2>当日の流れ</h2> <p>オフサイトは、参加者全員が最大限に集中し、活発な議論ができるよう、綿密にデザインされたプログラムで進行しました。</p> <h3>アイスブレイクの意味と目的の説明</h3> <p>ワークショップの冒頭では、まず「アイスブレイクとは何か」「なぜアイスブレイクが必要なのか」について、参加者全員に丁寧に説明しました。この配慮を特に重視したのは、私自身が過去にワークショップに参加した際、突然ゲームが始まって「これをする意味がよくわからない」と混乱した経験があったからです。</p> <p>アイスブレイクが単なる場を和ませるための時間ではなく、その後の議論の質を左右する重要な要素であることを理解してもらうため、以下の4つの目的を明確に伝えました:</p> <ol> <li><strong>参加者同士の緊張をほぐす</strong>: 初対面や久しぶりの対面で生じるぎこちなさを解消し、心理的安全性を高める</li> <li><strong>場の雰囲気を明るくする</strong>: ポジティブな空気を作り出し、その後の議論が活発になる土台を築く</li> <li><strong>親近感を高める</strong>: 共通の体験を通じて、お互いへの理解を深め、チームとしての繋がりを強化する</li> <li><strong>集中力を高める</strong>: 軽いアクティビティを通じて、参加者の意識をオフサイトのテーマへと自然に引き込む</li> </ol> <p>この説明により、参加者はアイスブレイクの重要性を理解し、積極的に参加することができました。この事前の配慮が、次のアイスブレイクセッションでの自然な参加につながりました。</p> <h3>アイスブレイク:緊張をほぐし、一体感を育む時間</h3> <p>アイスブレイクとして、事前にAIを活用して作成した福岡にちなんだクイズからスタートしました。アイスブレイクの目的は、参加者同士の緊張をほぐし、場の雰囲気を明るくし、親近感を高めることです。お互いの意外な一面を知るような質問を投げかけることで、笑い声が絶えない和やかな雰囲気を作り出すことができました。</p> <p>特に、ワークショップ形式に不慣れなメンバーからも自然な笑い声が聞こえ、緊張していた表情が和らいでいく様子が印象的でした。このアイスブレイクを通じて、参加者全員が同じ土俵に立ち、次のセッションに向かう準備が整いました。</p> <h3>5グラウンドルールの共有</h3> <p>アイスブレイクの後、ワークショップを円滑に進めるための「<a href="https://biztool.jp/updates/updates_detail/fivegroundrules" title="5グラウンドルール">5グラウンドルール</a>」を参加者全員で共有しました。このルールは、今年私が参加した社外イベントで講師の方が使用していたもので、運営者として参加した私がその効果を実感し、今回のオフサイトで採用することにしました。</p> <ol> <li>ほめる</li> <li>聴く</li> <li>受けとめる</li> <li>待つ</li> <li>愉しむ</li> </ol> <p>実際に体験者として参加したことで、これらのルールが参加者の心理的安全性を高め、質の高い議論を生み出す効果を実感できました。今回のオフサイトでも、参加者全員が同じ価値観でワークショップに臨むことができました。</p> <h3>タイムラインによるプロジェクトのふりかえり</h3> <p>アイスブレイクで場が温まり、「5グラウンドルール」で参加者全員の認識がそろったところで、メインコンテンツである「タイムラインを使ったプロジェクトのふりかえり」へと移りました。手数料プロジェクトは長期にわたるため、過去の出来事を時系列で整理し、共通認識を持つことが非常に重要だと考えました。</p> <h4>ステップ1:できごとと感じたことを書く</h4> <p>まず、ロール上の模造紙にプロジェクトのタイムラインを引き、各メンバーが印象に残っているできごとや感じたことを付箋に書き出し、該当する時期に貼り付けていきました。このプロセスでは、まず事実としての「できごと」を書き出し、それに対して「感じたこと」を複数書き出すという明確な手順を示しました。</p> <p>事前準備として、これまでの議事録をNotebookLMに読み込み、音声出力サマリーを作成しました。このサマリーを参加者全員で聞くことで、プロジェクトの全体像を共通認識として持つことができ、より具体的で深い議論につながりました。</p> <p>付箋には色分けを採用し、できごとは黄色、感じたことはその他の色で分類することで、視覚的に情報を整理しやすくしました。アナログな手法だからこそ、参加者が直接手を動かして情報を整理でき、デジタルでは得られない物理的な体験を通じて、より深い議論が生まれました。</p> <h4>ステップ2:付箋を貼る</h4> <p>タイムライン上に、重要なマイルストーンを設定し、各参加者が該当する時期の付箋を貼り付けていきました。</p> <p>このステップでは、できごとについて簡単に説明する時間も設けました。これにより、他のメンバーが知らなかった出来事や、異なる視点での捉え方を共有することができ、プロジェクトの多面的な理解が深まりました。</p> <p>特に印象的だったのは、書いた人が読み上げることで、「これもあった」「これもあった」と次々と思い出が湧き上がり、付箋がどんどん増えていったことです。この段階で参加者全員の熱量が一気に上がり、ワークショップの雰囲気が大きく変わりました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/08/41ebff3b-retro1-1024x261.jpg" alt="" /></p> <h4>ステップ3:グルーピングする</h4> <p>タイムラインに沿って書き出された付箋を、関連性のあるもの同士でまとめる作業を行いました。同じような課題や、同じ時期の出来事、同じチームに関連するものをグループ化することで、プロジェクト全体の課題がより明確になりました。</p> <h4>ステップ4:話を深掘りしたい付箋にシールを貼る</h4> <p>グルーピングされた付箋の中から、さらに詳しく議論したいトピックや課題にシールを貼ることで、優先順位付けや深掘りの対象を明確にしました。このプロセスを通じて、プロジェクトの成功体験や課題、転換点などが視覚的に明確になり、参加者全員が同じ視点でプロジェクトの全体像を把握できるようになりました。</p> <h3>グループディスカッション</h3> <p>タイムラインのふりかえり後、抽出されたトピックを基にグループに分かれ、グループディスカッションを実施しました。各グループには45分間の時間を設け、特定の課題やテーマについて深掘りし、具体的な課題の抽出や次のネクストアクションを導き出すことに注力しました。</p> <h3>ネクストアクションの抽出と優先度決め</h3> <p>グループディスカッションで出された多くのアイデアや課題の中から、最も重要で実行可能な「ネクストアクション」を特定し、その優先順位を決定しました。この段階では、各アクションの実現可能性や影響度を考慮し、チーム全体で合意できる優先順位を設定することができました。</p> <h3>オフサイト自体のふりかえり</h3> <p>今回のオフサイトがどれだけ効果的だったかを評価するため、「ふりかえりのふりかえり」も実施しました。早めに全員がパソコンを閉じて、オフサイトに集中できる環境を作ったことや、福岡という場所を選んだことで普段参加が難しいメンバーも参加できたことが、特に良かった点として挙げられました。長時間の開催にも関わらず、議論が途切れることなく活発に進行できたのは、リモートワークでは実現困難な対面ならではの集中力と一体感の賜物でした。</p> <h2>まとめ</h2> <p>今回のオフサイトは、手数料プロジェクトの現状や課題を整理し、今後のアクションにつなげるだけでなく、チームとしての結束力を高める貴重な機会となりました。スクラムマスターとしてワークショップデザインを担当した経験は、今後のプロジェクト運営にも大きな学びとなりました。</p> <h4>おもな成果:</h4> <ul> <li>長期プロジェクトの効果的なふりかえり手法(タイムラインふりかえり)の実践</li> <li>AIを活用したワークショップデザインの効果検証</li> <li>初回参加者への配慮と心理的安全性の確保による質の高い議論の実現</li> <li>5グラウンドルールを活用したチーム全体の価値観統一</li> <li>対面でのチームビルディングによる信頼関係の強化</li> </ul> Agile Testing Days 2024参加記:探索的テストとテスト自動化を学んだ海外ワークショップ体験https://engineering.mercari.com/blog/entry/20250806-bafba5b2e8/https://engineering.mercari.com/blog/entry/20250806-bafba5b2e8/<p>メルカリハロで QA Engineering manageをしている @____rina____です。 昨年2024年11月に開催されたAgile Testing Daysとそこで参加したワークショップについて紹介します [&hellip;]</p> Fri, 08 Aug 2025 08:00:13 GMT<p>メルカリハロで QA Engineering manageをしている <a href="https://engineering.mercari.com/blog/author/underscore42rina/" title="@\_\_\_\_rina\_\_\_\_">@____rina____</a>です。<br /> 昨年2024年11月に開催された<a href="https://agiletestingdays.com/" title="Agile Testing Days">Agile Testing Days</a>とそこで参加したワークショップについて紹介します。</p> <p><!--more--></p> <h2>Agile Testing Daysとは何か</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/08/e52e448e-agiletestingdays-1024x383.jpg" alt="" /></p> <p> <a href="https://agiletestingdays.com/atd-experience/2024/" title="Agile Testing Days">Agile Testing Days</a>とは、ドイツのポツダムで毎年開催されているカンファレンスです。参加者層はテスター、アジャイルテスター、QAエンジニア、テストリード、テストオートメーションエンジニアといったQAやテストに関するエンジニアに加え、ディベロッパー、ソフトウェアエンジニア、アジャイルコーチ、スクラムマスター、プロダクトオーナーやチームリードなど様々な方が対象のカンファレンスです。今回参加した日本語話者の参加者は数名だったこと、ヨーロッパの参加者が多かったようで、コミュニケーションは英語でおこないました。</p> <p>Agile Testing Daysでは、1日のワークショップと3日間のカンファレンスで構成されています。カンファレンスでは、キーノートをはじめ、セッション、パネルディスカッション、ワークショップなどが開催されます。また、セッションの合間にはコーヒーブレイクやランチなどもが提供され、朝のジョギングから夜の音楽イベントまで、丸一日イベントを楽しむための仕掛けがたくさん用意されていました。</p> <p> カンファレンス会場はアジャイルテストの第一人者とも言える<a href="https://lisacrispin.com/" title="Lisa Crispin">Lisa Crispin</a>に会うことができたり、たくさんの有識者に会えたり、同じ悩みを持ったエンジニアと交流できたりし、とても刺激的なイベントでした。<br />  スピーカーはシニアエンジニアに限らす、はじめて登壇される方のセッションなど、幅広い登壇者のたくさんの発表を聞くことができました。</p> <h2>Getting a grip on exploratory testing with test charters</h2> <p> Ewald WassinkSconewile氏とRob van Sttenbergen氏による、探索的テストのワークショップです。探索的テストのワークショップは国内でも時々見かけるようになりました。<br />  このワークショップはいくつかのテーブルに分かれてグループワークをしました。私のグループは4名で、2名はドイツからの参加で、英語でコミュニケーションをしました。<br /> 次にワークショップの流れを紹介します。</p> <h3>ワークショップの流れ</h3> <p>以下のような流れでワークショップをデザインしていました。</p> <ol> <li>講師の自己紹介とワークショップの説明</li> <li>フリースタイルで探索的テストをする</li> <li>2で見つけた不具合の紹介</li> <li>テストチャーターを利用した探索的テスト</li> <li>グループディスカッションと発表</li> <li>登壇者の考案したフレームワークを利用した探索的テスト</li> <li>グループディスカッションと発表</li> </ol> <h4>1. 講師の自己紹介とワークショップの説明</h4> <p>講師2人の自己紹介がありました。海外のキーノートなどは国内でも見る機会がありましたが、自己紹介をしない印象があったので、少し意外でした。彼らのバックボーンなどを知ることができました。</p> <h4>2. フリースタイルでテストを実行する</h4> <p>以下のURLにアクセスして、個人でフリースタイルでテストをしました。<br /> <a href="https://www.eviltester.com/page/tools/thepulper/">https://www.eviltester.com/page/tools/thepulper/</a></p> <p>題材はWebサイトで、個々に不具合を出していきます。テスト設計はせずに、経験ベースでテストを実行します。</p> <p><figure id="attachment_34505" aria-describedby="caption-attachment-34505" style="width: 500px" class="wp-caption aligncenter"><img src="https://storage.googleapis.com/prd-engineering-asset/2025/08/2d902f41-test1-300x111.jpg" alt="" width="500" class="size-medium wp-image-34505" srcset="https://storage.googleapis.com/prd-engineering-asset/2025/08/2d902f41-test1-300x111.jpg 300w, https://storage.googleapis.com/prd-engineering-asset/2025/08/2d902f41-test1-1024x378.jpg 1024w, https://storage.googleapis.com/prd-engineering-asset/2025/08/2d902f41-test1-768x283.jpg 768w, https://storage.googleapis.com/prd-engineering-asset/2025/08/2d902f41-test1-1200x443.jpg 1200w, https://storage.googleapis.com/prd-engineering-asset/2025/08/2d902f41-test1.jpg 1252w" sizes="(max-width: 300px) 100vw, 300px" /><figcaption id="caption-attachment-34505" class="wp-caption-text">HTMLエンコードが行われていないため、入力された値がそのままHTMLとして表示される不具合</figcaption></figure></p> <hr /> <p><figure id="attachment_34508" aria-describedby="caption-attachment-34508" style="width: 500px" class="wp-caption aligncenter"><img src="https://storage.googleapis.com/prd-engineering-asset/2025/08/e7b3ee21-test2-300x207.jpg" alt="" width="500" class="size-medium wp-image-34508" srcset="https://storage.googleapis.com/prd-engineering-asset/2025/08/e7b3ee21-test2-300x207.jpg 300w, https://storage.googleapis.com/prd-engineering-asset/2025/08/e7b3ee21-test2-1024x707.jpg 1024w, https://storage.googleapis.com/prd-engineering-asset/2025/08/e7b3ee21-test2-768x530.jpg 768w, https://storage.googleapis.com/prd-engineering-asset/2025/08/e7b3ee21-test2-1200x829.jpg 1200w, https://storage.googleapis.com/prd-engineering-asset/2025/08/e7b3ee21-test2.jpg 1254w" sizes="(max-width: 300px) 100vw, 300px" /><figcaption id="caption-attachment-34508" class="wp-caption-text">HTMLエンコードをしていないために、入力値とinput formの表示が変わってしまう不具合</figcaption></figure></p> <hr /> <p><figure id="attachment_34509" aria-describedby="caption-attachment-34509" style="width: 500px" class="wp-caption aligncenter"><img src="https://storage.googleapis.com/prd-engineering-asset/2025/08/0825129e-test3-300x93.jpg" alt="" width="500" class="size-medium wp-image-34509" srcset="https://storage.googleapis.com/prd-engineering-asset/2025/08/0825129e-test3-300x93.jpg 300w, https://storage.googleapis.com/prd-engineering-asset/2025/08/0825129e-test3-1024x318.jpg 1024w, https://storage.googleapis.com/prd-engineering-asset/2025/08/0825129e-test3-768x239.jpg 768w, https://storage.googleapis.com/prd-engineering-asset/2025/08/0825129e-test3-1200x373.jpg 1200w, https://storage.googleapis.com/prd-engineering-asset/2025/08/0825129e-test3.jpg 1230w" sizes="(max-width: 300px) 100vw, 300px" /><figcaption id="caption-attachment-34509" class="wp-caption-text">日付を入力する欄に大きな数字をいれたためにNumber Format Exceptionが発生してしまった。これを不具合とするかどうかは仕様次第</figcaption></figure></p> <h4>3. 発見された不具合の共有</h4> <p> 参加者全員で、探索的テスト中に発見した不具合について発表しました。私は自身が報告しようとしていた不具合が、既に他の参加者によって報告済みであるか確信が持てませんでした。そこで、念のため隣席の方に不具合の画面を見せながら、「このバグについて報告しようと思っているのですが、既に発表された方はいますか?」と確認しました。すると、その方は私の発表をフォローしてくださり、安心して発表に臨むことができました。</p> <h4>4. 探索的テストとテストチャーターの解説</h4> <p>ここでは、探索的テストとは何か、そしてテストチャーターとは何かについて解説がありました。講師からは、著名なソフトウェアテスト研究者である<a href="https://en.wikipedia.org/wiki/Cem_Kaner" title="Cem Kaner">Cem Kaner</a>氏の言葉を引用しつつ、「テストチャーターは、テストのゴールを明確にするための計画である」という説明がありました。</p> <p>以下はテストチャーターの例です。<br /> <code></p> <pre> define your goal; charter template<br> &nbsp;&nbsp;target<br> &nbsp;&nbsp;&nbsp;&nbsp;where are you exploring?<br> &nbsp;&nbsp;resources<br> &nbsp;&nbsp;&nbsp;&nbsp;what resources do you need ?<br> &nbsp;&nbsp;information<br> &nbsp;&nbsp;&nbsp;&nbsp;what kind of information do you want to discover<br> </pre> <p> </code></p> <h4>5. テストチャーターを用いた探索的テスト</h4> <p>隣の席の方とペアになり、先ほどのテスト対象のWebアプリケーションに対して、付箋を使ってテストチャーターを作成しました。ペアワーク後、グループ全体でそれぞれのペアがどのような考えでテスト項目を選び、テストチャーターを作成したかを共有し、議論しました。</p> <h4>6. BRIEFフレームワークの提案</h4> <p>講師から、Elizabath Hendricson氏が提唱するテストチャーターについての説明がありました。私はテストチャーターについて、彼女の著書で知ってはいたものの、改めて説明を受けることで、自身の抱いていた違和感が明確になりました。それは、受け入れ条件(Acceptance Criteria)を書く際、つまりテスト設計時に、情報やテスト観点から記述している点でした。</p> <p>そして、講師らが提唱する新しいフレームワーク「BRIEF」の説明がありました。<strong>BRIEFは、Behavior(行動)、Result(結果)、Impediments(障害)、Expectation(期待)、Feeling(感情)</strong>の頭文字を取ったものです。このフレームワークを用いることで、振る舞いを軸としたテストチャーターを作成することができます。最後に、このBRIEFフレームワークを使ってペアワークを行い、その後、他のグループと入れ替わってディスカッションを行いました。</p> <p><figure id="attachment_34510" aria-describedby="caption-attachment-34510" style="width: 296px" class="wp-caption aligncenter"><img loading="lazy" src="https://storage.googleapis.com/prd-engineering-asset/2025/08/205afa60-test4-296x300.jpg" alt="" width="296" height="300" class="size-medium wp-image-34510" srcset="https://storage.googleapis.com/prd-engineering-asset/2025/08/205afa60-test4-296x300.jpg 296w, https://storage.googleapis.com/prd-engineering-asset/2025/08/205afa60-test4.jpg 712w" sizes="(max-width: 296px) 100vw, 296px" /><figcaption id="caption-attachment-34510" class="wp-caption-text">ペアワークで使った付箋</figcaption></figure></p> <p>このワークショップで特に素晴らしいと感じたのは、講師自身が考案したフレームワーク「BRIEF」を活用していた点です。探索的テストのワークショップでは、手を動かす演習や参加者同士の意見交換、テストチャーターの作成などはよく行われます。しかし、今回のように新しいチャーターのフレームワークを実際に試す機会は初めてで、非常に有意義でした。個人的にも、BRIEFのフレームワークは普段私がテストを考える際の思考回路に近く、とてもしっくりきました。</p> <h2>Journey From Manual to Automation Pythonic Tester</h2> <p>続いて、Mateusz Adamczak氏とMichal Pilarski氏による、テスト自動化の初学者を対象としたワークショップについてご紹介します。このワークショップはハンズオン形式で行われ、参加者は自身のPCを使って実際に自動テストを作成しました。</p> <h3>ワークショップの流れ</h3> <p>ワークショップでは以下のような流れでワークショップをデザインしていました。</p> <ol> <li>必要なツールとリポジトリのダウンロード</li> <li>Scrachを使ってアニメの作成</li> <li>Pythonのコードにコンバートする</li> <li>テストコードの作成</li> <li>グループディスカッションと発表</li> <li>登壇者の考案したフレームワークを利用した探索的テスト</li> <li>グループディスカッションと発表<br /> こちらもひとつずつ紹介します。</li> </ol> <h4>1. 必要なツールとリポジトリのダウンロード</h4> <p>GitLabのリポジトリ、Python、JetBrainsをダウンロードします。</p> <ul> <li>GitLab<br /> &#8211;&nbsp; <a href="http://gitlab.com/" title="GitLab.com">GitLab.com</a><br /> &#8211;&nbsp; <a href="https://gitlab.com/michpil/python_kids/-/tree/ATD_workshop_manual2auto" title="Files · ATD_workshop_manual2auto · Michal Pilarski / python_kids · GitLab">Files · ATD_workshop_manual2auto · Michal Pilarski / python_kids · GitLab</a></li> <li>Python.org<br /> &#8211;&nbsp; <a href="https://www.python.org/" title="Welcome to Python.org">Python.org</a></li> <li>JetBrains<br /> &#8211;&nbsp; <a href="https://www.jetbrains.com/pycharm/download/download-thanks.html?platform=macM1&amp;amp;code=PCC" title="PyCharm">PyCharm</a><br /> &#8211;&nbsp; <a href="https://www.jetbrains.com/pycharm/download/download-thanks.html?platform=macM1&amp;amp;code=PCC">https://www.jetbrains.com/pycharm/download/download-thanks.html?platform=macM1&amp;code=PCC</a></li> </ul> <h4>2.Scratchを使ったアニメーション動画作成</h4> <p>Scratchを使ってアニメーション動画を作成しました。最初に講師が画面共有しながら作り方を説明し、参加者はそれに倣って作業を進めました。Scratchは、子供向けのプログラミング学習ツールとして日本でも人気があり、直感的な操作でアニメーションを作成できます。そのため、プログラミング初学者でも問題なく取り組むことができました。講師の指示に従い、キャラクターを前後に動かしたり、音を鳴らしたりといった簡単な動作を作成しました。</p> <h4>3. Pythonコードへの変換</h4> <p>Scratchで作成したアニメーションが完成したので、次はそれをPythonのコードに変換します<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/08/5cd367f5-test5-1024x324.jpg" alt="" /></p> <p>変換に必要なコードは事前に用意されていました。</p> <p>まずターミナルで<br /> <code>python -V </code><br /> を実行してPythonのバージョンを確認し、次に<br /> <code>pip install -r requirements.txt</code><br /> を実行して必要なライブラリをインストールします。その後、Scratchで作成した <code>kitty.sb3</code> ファイルを変換することで、Pythonのコードが生成されました。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/08/731f140b-test6-1024x428.jpg" alt="" /></p> <p>講師から基本的なPythonコードの説明があり、その後は参加者自身でコードの実装を行いました。私の作成したアニメーションには背景がなかったため、生成されたコードの6行目をコメントアウトする必要がありました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/08/16b1f59e-testkitty-1024x610.jpg" alt="" /></p> <h4>4. テストコードの作成</h4> <p>いよいよテストコードの作成です。先ほどコンバートしたコードに対してテストコードを実装します。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/08/119a79c6-test7-1024x565.jpg" alt="" /></p> <p> 講師が簡単なテストのサンプルコードを紹介してくれました。このサンプルコードは、Scratchで作成したアニメーションの動きをテストするもので、pytestライブラリを使用していました。例えば、キャラクターが指定された位置に移動することを検証するテストや、特定の音が鳴ることを検証するテストなどがありました。講師がコードを画面共有しながら説明してくれたので、私たちも自身のPCで同じようにコードを書き写しました。エラーが発生した箇所は、エラーメッセージを読みながら修正したり、講師に質問したりして解決しました。その後、ターミナルでpytestコマンドを実行してテストを実行し、テストが成功することを確認しました。詳細なテストコードについては、ぜひGitLabのリポジトリ(<a href="https://gitlab.com/michpil/python_kids/-/tree/ATD_workshop_manual2auto">https://gitlab.com/michpil/python_kids/-/tree/ATD_workshop_manual2auto</a>)<br /> にアクセスして確認してください。ワークショップで使用したすべてのテストコードや関連資料が公開されていますので、テストコードの全体像を把握したり、実際にテストを実行したりすることができます。</p> <p>このワークショップの特筆すべき点は、まず初学者の参加者が実際に動くプログラムを作成できたことです。Scratchを活用することで、参加者が動くプログラムを自ら作れるようにするというアイデアは非常に素晴らしいと感じました。通常の自動テストに関するハンズオンでは、多くの場合、事前に用意されたプログラムに対してテストコードを書くことが一般的です。しかし、今回のワークショップでは、動くものをゼロから自分で作り、そのテストコードまで書くという一連の流れを、数時間という短い時間で体験できる点が素晴らしいと思いました。ワークショップの時間内ですべてのテストコードを実装することはできませんでしたが、参加者全員が何らかの形でテストコードを実装することができました。初学者向けにScratchでアプリ作成を体験させ、それをPythonコードに変換し、自身が作成したプロダクトコードに対してテストコードを実装するというワークショップのデザインは、本当に素晴らしいと感じました。</p> <h2>おわりに</h2> <p>今回の記事では参加した2つのワークショップについてご紹介しました。どちらのワークショップも、短時間で成果を実感できるような工夫が凝らされており、その内容とともに大変勉強になりました。ワークショップのオーナーの方々には、直接お会いして感謝の気持ちをお伝えし、ぜひこの素晴らしいセッションを日本にいるみんなにも広めたいので、サイトのURLなどを公開しても良いか確認させていただきました。</p> <p> 私自身、長年英語に苦手意識があり、ワークショップへの参加は、他の参加者や講師の方々にご迷惑をかけてしまうのではないかという不安がありました。しかし、同じ志を持つ仲間たちと、互いの伝えたい内容を理解しようとする姿勢に触れ、そのうちの一人とは数週間後に私の住む街へ偶然旅行に来るとのことで、食事の約束までできました。また、参加者の中には第二外国語として英語を学んでいる方もいたようで、みんなが真剣に耳を傾けてくれる姿勢が印象的でした。<br />  結果として、私自身も非常に楽しく学習できる時間を過ごすことができました。同じグループになった参加者の方々も、とても親切で助けられました。私は英語が得意ではありませんが、Agile Testing Daysではキーノートをはじめ、数多くのセッションやワークショップ、パネルディスカッションが開催され、登壇者の経験に基づいた発表が多く、共感できる内容が数多くありました。<br />  セッション以外の時間にも食事が提供され、参加者同士が楽しく交流できる場が設けられており、3日間を通して多くの方々と話すことができました。<br />  今年の<a href="https://agiletestingdays.com/" title="Agile Testing Daysの参加受付">Agile Testing Daysの参加受付</a>も始まりました。このブログをきっかけに、読者のみなさまがワークショップを試してみたり、海外のカンファレンスへの参加に挑戦してみようと思っていただけたら、とてもうれしいです。</p> Build@Mercari 2025で圧倒的に成長できた話https://engineering.mercari.com/blog/entry/20250731-57864d7e6b/https://engineering.mercari.com/blog/entry/20250731-57864d7e6b/<p>はじめに こんにちは。2025年度のBuild@Mercariに参加して、現在はメルカリのCS Tool Teamでインターンをしている@Aokaと申します。この記事では、私がBuild@Mercariに参加した感想や成 [&hellip;]</p> Thu, 31 Jul 2025 14:00:12 GMT<h2>はじめに</h2> <p>こんにちは。2025年度のBuild@Mercariに参加して、現在はメルカリのCS Tool Teamでインターンをしている@Aokaと申します。この記事では、私がBuild@Mercariに参加した感想や成長したことについて書いていきたいと思います。</p> <h3>Build@Mercariって何?</h3> <p>Build@Mercariとは、これまでさまざまな事情で機会が巡ってこなかった方、特にSTEM分野・IT分野におけるマイノリティである性自認が女性の方々を中心として、スキルトレーニングとインターンシップの機会を提供するプログラムです。</p> <h2>トレーニングの話</h2> <h3>選考</h3> <p>Build@Mercariの選考は書類とコーディングテストの結果で進みます。他の多くのインターンシップとは異なり、面接がないのが特徴的でした。<br /> 書類審査では、これまでの自分の経験やBuild@Mercariに参加したい理由などを記入しました。コーディングテストについては、プログラミング言語の基礎文法を理解していれば解ける問題が中心で、競技プログラミングのような高度な問題ではありませんでした。<br /> 選考は志望理由とコーディングテストの結果を総合的に判断しますが、志望理由が重要視されているそうです。</p> <h3>初日のオリエンテーション</h3> <p>初日のみオフラインでの開催で、メンターや同じチームの参加者とオフラインで交流する機会がありました。また、GitHubを使った課題をみんなで進めて、使い方に慣れることができました。オフラインで交流できたことが二週間のトレーニング期間を通じて心の支えとなり、モチベーションを維持することができました。</p> <h3>トレーニングでやったこと</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/abf8f193-428333800-c776fd5d-01b5-412e-990c-4ab356a0e6ab-300x154.png" alt="Build@Mercari" /></p> <p>トレーニングではソフトウェアエンジニアリングに必要な基礎知識を一通り学びました。<br /> トレーニング期間中は、5〜6人のチームにそれぞれメンターの方がついてくださる形で、各自が課題に取り組みました。課題の内容は以下の通りで、各STEPを進めながら、最終的にはメルカリのような商品を登録できるWebアプリを作りました。</p> <ul> <li>STEP1 Git</li> <li>STEP2 Setup environment</li> <li>STEP3 Algorithms and Data Structures</li> <li>STEP4 Develop API</li> <li>STEP5 Database</li> <li>STEP6 Writing Tests</li> <li>STEP7 Docker</li> <li>STEP8 Continuous Integration(CI)</li> <li>STEP9 (Stretch) Frontend</li> <li>STEP10 (Stretch) Run multi service</li> <li>EXTRA1 (Stretch) Data Analysis</li> </ul> <p>トレーニング内容は公開されており、以下のレポジトリから確認できます。<br /> <a href="https://github.com/mercari-build/mercari-build-training/tree/main">https://github.com/mercari-build/mercari-build-training/tree/main</a></p> <p>つまづいた時にメンターの方々にSlackで質問したり、課題の各STEPごとにレクチャーをしてくださったりと、手厚いサポートのおかげで、最後まで楽しく開発を続けることができました。</p> <h3>学んだこと</h3> <p>プログラム期間中はたくさんのことを学びましたが、特に以下の知識がついたと思います。</p> <ul> <li>Git, Githubを使ったチーム開発の基礎</li> <li>Backend開発の流れ</li> <li>DockerやCIなどインフラの知識</li> </ul> <p>このトレーニングを通じての一番の大きな成長は、開発することに対する心理的なハードルが下がったことだと感じています。参加する前は、フロントエンドの開発しかしたことがなかったのですが、フロントエンドからインフラまでの基礎知識を身につけたことにより、新しい技術について学習する際や、開発でつまづいた時にどの領域の知識を深めていけばいいのか判断できるようになりました。</p> <p>また、プログラムの参加者はUdemy Businessを1年間無料で使うことができます。はじめて学ぶ知識が多いなか、基礎から体系的に学習することができるので、非常に役にたちました。</p> <h3>難しかったこと</h3> <p>実装を始める前に、新しい概念を理解する必要があり、なぜこの技術が必要なのかというところから理解しなければならなかったことが大変でした。理解が浅いまま進めてしまうと、後でつまずくことが多かったため、基礎知識を身につけるためにUdemyの動画を視聴したり、AIとの壁打ちを通じて概念を整理したりして理解しながら進めました。</p> <p>特に、STEP4のAPI開発では、Python言語を使用してRESTful APIを実装したのですが、デバッグ作業に苦労しました。エラーが発生した際に、どの部分でなぜエラーが起きているのかを特定するのが難しく感じました。しかし、この経験を通じてデバッグスキルの重要性を痛感するとともに、バグに対する耐性もついたと思います。</p> <p>また、STEP7のDockerの部分が大変でした。初めてコンテナ技術に触れたため、仮想化の概念から始まり、イメージとコンテナの概念、Dockerfileの書き方まで、すべてが新しい知識でした。特に、依存関係の管理は理解するのに時間がかかり、何度もエラーと向き合いながら少しずつ実装を進めました。また、公式ドキュメントを読む大切さも学びました。はじめはとても読みづらく感じましたが、理解するのに大変役に立つことを実感しました。</p> <h3>自身の変化</h3> <p>プログラムを通して、技術力の向上はもちろんのこと、様々な方との関わりを通じて自分自身に大きな変化があったと感じています。</p> <p>参加者の方々のバックグラウンドは多様で、情報系学部の人に限らず、文系学部や美術系学部の人、また高校生から社会人まで幅広い年齢層の方がいらっしゃいました。私のようにWeb開発が初心者の方も多かったです。</p> <p>私がこのプログラムを修了できた最も大きな要因は、一緒に切磋琢磨し合える仲間がいたからだと感じています。お互いに教え合いながら頑張る雰囲気がとても心地よく、他の参加者の方々の進捗は良い刺激になりました。</p> <p>また、プログラムの初日には社員の方々のキャリアに関するプレゼンテーションがあり、直接質問できる機会もいただきました。出産や子育てと仕事の両立、転職に関するお話など、貴重なお話を聞くことができ、自分自身のキャリアについて深く考える時間となりました。</p> <h2>Buildインターンの話</h2> <h3>選考</h3> <p>トレーニング期間終了後、一定の基準を満たした参加者は、インターンシップへの応募資格を得ることができます。<br /> 選考プロセスでは面接が実施され、主にトレーニングを通じて得た学びや気づき、これまでの技術的な経験、そしてメルカリのバリューに対する理解や共感について質問されました。<br /> 選考通過後には、実際のインターンシップ開始前に事前面談の機会が設けられています。この面談では、配属予定のチームについての説明や、不安な点の相談などができるため、安心してインターンシップをスタートすることができました。</p> <h3>やったこと</h3> <p>タスクは、カスタマーサポート業務で使用される社内ツールであるCS ToolのPHPで記述されている既存エンドポイントをGo言語とマイクロサービスアーキテクチャで再構築するというものでした。</p> <p>開発の背景として、CS Toolの多くの機能は古いモノリスなアプリケーションとして運用されています。古いBackendはPHPで書かれており、現在これを保守性の観点などから、新しいマイクロサービスアーキテクチャに移行する取り組みが進められています。</p> <p>現在CS Toolでは、開発方針として既存機能の移行作業を進めながら、新機能については新しいサービスで開発するというルールがあり、今回私は前者を担当しました。</p> <h3>学んだこと</h3> <ul> <li> <p>新しい技術(Go, Kubernetes, gRPC)<br /> アーキテクチャの概念が実際のサービス運用でどのように活用されているかを理解することができました。</p> </li> <li> <p>Test QA Releaseの工程<br /> 実際の開発現場での品質管理プロセスを経験できました。単体テストの書き方から、QAのケースの作成と実施、リリースまで一連の流れを学ぶことができました。</p> </li> <li> <p>コードの可読性を考える<br /> 自分だけが理解できるコードではなく、チームメンバー全員が理解できるコードを書くことの重要性を学びました。変数名や関数名の命名、レポジトリ内でコードの一貫性を保つことなど実務で重要なポイントを学ぶことができました。</p> </li> <li> <p>コードの拡張性を意識する<br /> 将来的な機能追加や仕様変更に対応しやすいコード設計について学びました。将来的に、同じエンドポイントに新しいフィルターを追加するなどする時に、コードが書きやすいか考えました。<br /> また、自身が書く部分が既存のエンドポイントの実装と関わっていたり、使う関数が似ていたりするときに、コードを分割したり、まとめたりすることの重要性を学びました。</p> </li> <li> <p>AIの活用<br /> メルカリでは、AIの活用も積極的に進められており、開発にはCursorなどのAIコーディングツールを使うことができました。ただし、やみくもにAIに頼るのではなく、知識をもった上で、生成されるであろう答えを予想してから入力することで、より効果的にツールを活用することが大切だと学びました。</p> </li> </ul> <p>インターンを通じて、今まで触れる機会のなかった大規模開発ならではの知識や技術を多く学ぶことができました。特に、前述のコードの可読性の点では、コードのみならず、PRやQAシートを作成する際にも、チームの人が理解しやすく、レビューをしやすいかどうかを意識して書くことの重要性を学びました。</p> <p>またコードレビューを通じて、開発に必要なコードの書き方を具体的に学ぶことができました。自分の実装がベストプラクティスかどうか、様々な視点から評価していただけました。</p> <p>特に印象的だったのは、単にこの書き方の方が良いと教えていただくだけでなく、なぜその書き方が推奨されるのかという理由まで丁寧に説明していただいたことです。例えば、エラーハンドリングの実装では、ただ起こりうるエラーを処理するだけでなく、適切なログ出力やユーザーが分かりやすいエラーメッセージを考慮する必要があることを学びました。</p> <h3>交流を通じた学び</h3> <p>チームでは定期的に勉強会が開催されており、チームに役に立ちそうな技術や知見を積極的に共有する文化がありました。難しい内容も多かったですが、CursorのMCP serverについての回は特に興味深かったです。</p> <p>チームには地方に住んでいるメンバーも多くいましたが、普段はSlackで通話を繋いでコミュニケーションを取り、定期的に出社日を設けてオフラインでの交流機会を作ったり、チームビルディングイベントを開催したりと、チームのコミュニケーションを積極的に取ろうとされていることが印象的でした。</p> <p>困った時はいつでも相談に乗っていただき、ペアプログラミングも積極的にしてくださいました。特に、課題がある時に手取り足取り教えるのではなく、私にとってよい学習機会になるようにサポートしてくださったのがとても印象的でした。</p> <p>また、メンターランチや1on1での社員の方々との交流を通じ、自身のキャリアについて考えるとてもよい機会になりました。新卒入社の方のお話から、他の会社も経験された方のお話まで色々なバックグラウンドをお持ちの方との会話を通じて視野が広がりました。また、これからの大学生活に関して具体的なアドバイスをいただき、残り2年半の大学生活をどのように過ごすのか考える機会になりました。参加してくださった社員の方々に改めて感謝申し上げます。</p> <h2>終わりに</h2> <p>今回のBuild@Mercariとインターンでの経験を通じ、開発に必要な幅広い知識を身につけることができました。また、技術的な面以外でも多くの学びがありました。この半年間で得た学びを糧に、一流のエンジニアを目指して、さらに力をつけていきたいです。</p> <p>このプログラム期間中、多くの方々にサポートしていただいたおかげで、ここまで成長することができました。Build@Mercariのメンターの皆さんや、インターン期間中にメンターをしてくださった<a href="https://x.com/ukiuki_cherry" title="@a-uki">@a-uki</a>さんをはじめとするCS Toolチームの皆さんに本当に感謝の気持ちでいっぱいです。ありがとうございました。</p> <p><strong>※この体験記は2025年度(今年度)のプログラム内容です。来年度以降のプログラムにおいては内容が変更になる可能性がありますので、ご了承ください。</strong></p> Locked Shields 2025 参加レポートhttps://engineering.mercari.com/blog/entry/20250728-ceec77c0d4/https://engineering.mercari.com/blog/entry/20250728-ceec77c0d4/<p>はじめに 5月上旬、NATO Cooperative Cyber Defence Centre of Excellence(CCDCOE)が主催する世界最大級のサイバー防衛演習 Locked Shields 2025 が [&hellip;]</p> Tue, 29 Jul 2025 10:00:41 GMT<h2>はじめに</h2> <p>5月上旬、NATO Cooperative Cyber Defence Centre of Excellence(CCDCOE)が主催する世界最大級のサイバー防衛演習 Locked Shields 2025 が開催されました。今年は 約40 か国・約 4,000 名が参加し、17 の多国籍ブルーチームが国家レベルのICTインフラを防御するシナリオに挑みました。<br /> 昨年に引き続き、メルカリは今年もLocked Shieldsに参加しました。今回はセキュリティチームから3人のメンバーが参加しました。本記事では国際共同演習の最前線で得られた知見を共有します。</p> <h2>チーム構成と参加概要</h2> <p>今回次の3名が参加しました。</p> <ul> <li>Yuto Iso:主に日本の防衛対象の全情報システムの保全および重要システムの侵害防止を担当しました。</li> <li>Hiroki Akamatsu: プラットフォームおよびWebアプリケーションの脆弱性ハンティング・修正を担当しました。</li> <li>Sana Okumura: 侵害兆候の分析、証跡の確認・報告を担当しました。</li> </ul> <p>メルカリのメンバーは攻撃の兆候の検知・証跡確認、そして脆弱性の特定・修正を行いました。</p> <h2>取り組み内容</h2> <p>Locked Shieldsでは防衛対象の多数の情報システムが高度なサイバー攻撃を受けます。保護対象の全ての情報システムを自動的に調査する仕組みをIsoが開発し、各システムの保護・復旧にかかる労力を大きく軽減させました。これにより、脆弱性の事前特定・攻撃を受けたシステムの迅速な回復に貢献しました。</p> <p>また、Locked ShieldsではAI機能を含む様々なサービス・認証基盤・ネットワーク、そしてそれらを運用するためのプラットフォームが存在していました。AIやWebアプリケーションおよびコンテナ技術に対応できる人材としてAkamatsuが複数のWebアプリケーションの堅牢化・コンテナの安全なデプロイ構築などを支援しました。</p> <p>そして、攻撃者は様々な攻撃パターンで情報システムへの侵害を試みます。Okumuraは複数の証跡を確認することで正確に攻撃の影響範囲を特定・報告し、攻撃の検出から封じ込めに貢献しました。</p> <h2>得られた学びと成果</h2> <p>演習内ではそれぞれが自身の専門領域を活かしながら取り組みましたが、Locked ShieldsではOT(Operational Technology)系のシステムなど経験のない領域の攻撃に直面することもあり、学びつつ多数の攻撃からシステムを防衛しました。</p> <p>また技術面以外でも、様々な専門分野の演習参加者とともに、それぞれの知識や経験を活かしながら、サイバーセキュリティ防衛における円滑なコミュニケーションと協力関係を築く方法を実践的に学ぶことができました。</p> <h2>さいごに</h2> <p>Locked Shieldsは他に類を見ないほどの大規模な演習であり、今回の参加もメルカリのメンバーにとって非常に貴重な経験となりました。各メンバーがそれぞれの専門性を最大限に発揮し、各システムへの技術的な支援を横断的に行いました。自動化によるシステム調査・保全、そして複雑な環境下での迅速な脆弱性対応や影響範囲特定といった実践的なスキルを磨くとともに、新たな攻撃手法や防御戦略についても多くの学びを得ました。</p> <p>特に、国境を越えた多様な専門家との連携を通じて、サイバー防衛におけるコミュニケーションと協力体制の重要性を再認識しました。刻一刻と変化する状況の中で、迅速かつ正確に情報を共有し、共通の目標に向かって協力することの難しさと、それを乗り越えた際の達成感を肌で感じることができました。</p> <p>本演習で得た知見や経験は、メルカリのサービス全体のセキュリティ強化、インシデント対応能力の向上、そして将来のサイバー脅威への備えに大きく貢献するものと確信しています。メルカリは今後も、このような国際的な取り組みへ積極的に参加し、サイバーセキュリティ技術の向上と、安全・安心なサービス提供に努めてまいります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/4386a252-lockedshields-ja.jpeg" alt="" /><br /> 出典:<a href="https://x.com/ModJapan_jp/status/1920770496632627647">https://x.com/ModJapan_jp/status/1920770496632627647</a></p> SRE NEXT 2025参加レポートhttps://engineering.mercari.com/blog/entry/20250723-srenext-2025-report/https://engineering.mercari.com/blog/entry/20250723-srenext-2025-report/<p>こんにちは。SREチームの @foostan です。 弊社は2025年7月11~12日に開催されたSRE NEXT 2025に、PLATINUMスポンサーとして協賛し、ブース出展およびセッション発表を行いました。本記事で [&hellip;]</p> Wed, 23 Jul 2025 11:29:27 GMT<p>こんにちは。SREチームの @foostan です。</p> <p>弊社は2025年7月11~12日に開催された<a href="https://sre-next.dev/2025/">SRE NEXT 2025</a>に、PLATINUMスポンサーとして協賛し、ブース出展およびセッション発表を行いました。本記事では当日の様子とアンケートの収集結果をご紹介します。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/0cbc52ad-image.jpg" alt="sponsors" /></p> <h2>ブース出展</h2> <p>弊社としては久しぶりのブース出展であり、私個人としては初めての経験となりました。2日に渡り200人以上の来訪者にお越しいただき、SREに関するお話をたくさんさせていただきました。ありがとうございました。このように共通の話題で盛り上がれる機会は非常に貴重であり良い経験となりました。なお我々のブースでは自由記述形式で以下の2種類のアンケートを行いました。</p> <ul> <li>SREをやっていて良かった瞬間は?</li> <li>SREに関する業務でAIに任せたいことは何ですか?</li> </ul> <p>詳細は後述しますが、昨今のAI利活用の盛り上がりはSREの領域でも大きな影響を受けており、当日はAIに関する話題が尽きることはありませんでした。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/6a6bb5a5-image-1.png" alt="booth" /></p> <p>来訪者やアンケートに答えていただいた方向けにノベルティの配布も行っていました。特にロゴ入りのキーキャップは今回のイベント向けに用意したものでしたが高評をいただけて何よりでした。今後のイベントでも機会があれば配布しようと思いますのでその際は受け取っていただけると幸いです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/c4edeb1c-image-2.png" alt="keycaps" /></p> <h2>セッション発表</h2> <p>弊社の yakenji より、「複雑なシステムにおけるUser Journey SLOの導入」を発表させていただきました。我々がどのような経緯でUser Journey SLOを導入し、これをどのように運用しているのか、また今後の展望について共有しております。</p> <p><script defer class="speakerdeck-embed" data-id="97cd14ef91ec4fc193a4e2bbb86212f0" data-ratio="1.7777777777777777" src="//speakerdeck.com/assets/embed.js"></script></p> <p>なお本発表の内容は2024年末のブログ <a href="https://engineering.mercari.com/blog/entry/20241204-keeping-user-journey-slos-up-to-date-with-e2e-testing-in-a-microservices-architecture/">https://engineering.mercari.com/blog/entry/20241204-keeping-user-journey-slos-up-to-date-with-e2e-testing-in-a-microservices-architecture/</a> にも記載がありますので、よろしければこちらもご覧になっていただけると幸いです。</p> <h2>アンカンファレンスへの参加</h2> <p>当日のコンテンツはどれも興味深くさまざまな方の発表を聞いたり、各ブースを巡ってお話を伺うことでSREチームとして今後どうしていくか考えたり、またどのような技術の進歩があるのか考えたりと非常に楽しかったです。特に1日目の最後に行われたアンカンファレンスでは、具体的なテーマごとにグループに分かれてディスカッションを行うことで、各社のこれまでの経験や思いを知る良い機会となりました。</p> <p>私は「SREとその組織類型」のグループに参加させていただきました。普段はエンジニアリングマネージャーとしてチームや組織編成について考える機会もあるため、SREが組織にどう組み込まれるべきか、過去の事例や現在抱えている問題など、自分の視点からでは見れなかった意見を知ることができました。組織の規模やフェーズ、置かれている状況に応じてSREの組織もそれぞれにあるべき姿が存在することを改めて認識しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/90401b88-screenshot-2025-07-22-at-22.44.08.png" alt="unconference" /></p> <h2>アンケート結果</h2> <p>我々のブースで行ったアンケート結果をまとめましたのでご参照いただければ幸いです。各結果を同じような内容のグループに分類しそれぞれの割合を出しています。なお記載している内容は公開用に文章を変更しておりますのでご了承ください。</p> <h3>SREをやっていて良かった瞬間は?</h3> <p>合計で53件の意見をいただきました。</p> <ul> <li> <p><strong>トラブルシューティング: 24.6%</strong><br /> 大規模アクセスとなるイベントを無事乗り切った時や障害を迅速に解消できた時の達成感が最も多く挙げられました。難しい障害やボトルネックの特定・解消、原因不明のバグ修正などの技術的な課題を解決した時の喜びも大きいようです。また、インシデント対応フローの改善や開発チームの意識向上など、組織全体の成長を実感できる瞬間も良かった点として挙げられています。</p> </li> <li> <p><strong>自動化/トイル削減: 21.1%</strong><br /> インフラや運用の自動化によって手動作業が不要になったり、高速にデプロイできるようになったことに達成感を感じたという声が多く見られました。コードによるインフラ管理やオートスケーリングの導入、リリースフローの改善などを通じて、生産性向上や自律的なシステム構築を実現できた点が良かったこととして挙げられています。</p> </li> <li> <p><strong>開発体験向上: 14.0%</strong><br /> 開発者から「楽になった」といった声があった時や、生産性の向上を実感できた時にやりがいを感じたという意見が多く寄せられました。開発チーム全体のキャパシティを高める取り組みや、継続的に楽しく働ける環境づくりができていることも、良かった点として挙げられています。</p> </li> <li> <p><strong>ビジネス貢献: 12.3%</strong><br /> コスト削減やリソースの最適化によって、事業への具体的な貢献を実感できたという声が挙げられました。また、ビジネスへの関心・意識の高まりなど、技術面だけでなく事業視点での行動や成果を評価する動きが広がっていることも印象的です。</p> </li> <li> <p><strong>データ活用: 10.5%</strong><br /> トレースやメトリクスの導入により、システムの状態を可視化・定量化できるようになったことが大きな成果として挙げられました。オブザーバビリティの強化やSLI/SLOの導入などを通じて、計測や統計的な判断を行う文化が広がり、データに基づいた改善や意思決定が可能となってきているようです。</p> </li> </ul> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/41df16e2-image-3.png" alt="question result 1" /></p> <h3>SREに関する業務でAIに任せたいことは何ですか?</h3> <p>合計で100件の意見をいただきました。</p> <ul> <li> <p><strong>インシデントレスポンス: 22.2%</strong><br /> インシデントの一次対応やエラー調査の自動化をAIに任せたいという声が多く見られました。またログの収集・要約や過去のポストモーテムとの類似ケースの検索、初動対応の判断支援、インシデントレポートの下書き作成といった業務など、人手による作業の負荷の軽減に期待が寄せられています。</p> </li> <li> <p><strong>分析/予測: 16.0%</strong><br /> 障害やエラーの原因調査、影響範囲の把握、再発防止策の提案といった分析・改善フェーズにおいてAIの支援が期待されているようです。また、アップデートや構成変更に伴う影響調査、さらには事業計画やBillingデータからの将来予測といった業務にもAIを活用したいというニーズが見られました。</p> </li> <li> <p><strong>アップデート: 11.1%</strong><br /> システムやコードの保守や改善に関してAIの活用を望む声が多く挙げられました。特にEOL対応や、アップデート作業の自動化・効率化に対する期待が高まっています。</p> </li> <li> <p><strong>ドキュメンテーション: 10.5%</strong><br /> ポストモーテムや障害レポートなど、定型的かつ情報整理を要するタスクに対してAIの支援を求める声が挙げられました。また、社内向けの説明資料やダッシュボードの自動生成、ドキュメントの検索や要約といった情報の可視化・再利用性の向上に関してもAIへの期待が寄せられています。</p> </li> <li> <p><strong>モニタリング/アラート: 9.9%</strong><br /> アラートのトリアージや原因調査、優先度の判断、リリース後の自動モニタリングや外部サービスの障害速報の把握など、AIによるリアルタイム性と網羅性が期待されています。判断や対応をより迅速かつ確実にし、システムの安定性を高めるための基盤が整えられることが望まれているようです。</p> </li> </ul> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/23830139-image-4.png" alt="question result 2" /></p> <h2>おわりに</h2> <p>改めて来場してくださった皆様、また弊社のブースに立ち寄っていただいた皆様に感謝を申し上げます。また本イベントを開催し、運営していただいた皆様も本当にありがとうございました。弊社としても今後の発展に貢献していきますのでまた来年もよろしくお願いいたします。</p> <h2>おまけ: ノベルティーキーキャップの発注方法</h2> <p>今回作成したノベルティーキーキャップについてはクオリティの高いものを製作していただきました。需要は不明ですが、ノベルティーキーキャップの発注については直近ではあまり情報がないようでしたのでこちらにまとめておきます。</p> <p>今回発注したのはYUZU Keycapsさん( <a href="https://yuzukeycaps.com/">https://yuzukeycaps.com/</a> )です。こちらでは昇華印刷と呼ばれる方法を利用しており、熱と圧力によって浸透させて色をつけるため耐久性が高く剥がれることもありません。この方法は境界が滲んてしまうことがあるのですがとてもきれいな仕上がりでした。</p> <p>発注は以下の画面から行えます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/6288e824-image-5.png" alt="uzu keycap step1" /></p> <p>まずはレイアウトを選びます。ノベルティーキーキャップ用のレイアウトは用意されていないので、適当なものを選びカスタマイズしていきます。1Uサイズのものが一般的なのでOrtholinearのものを選ぶとあとの作業が多少楽になります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/aa1bc96d-image-6.png" alt="uzu keycap step2" /></p> <p>「Options」 からレイアウトをカスタマイズできます。今回はすべて同じキーにしたいので 「Add/remove keys」 を選択して詳細の編集画面に移ります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/468eb83f-image-7.png" alt="uzu keycap step3" /></p> <p>ここではすべてのキーを自由に選択することができます。各行 R1 ~ R4 も自由に選べます。ノベルティーキーキャップはEscとしてつけることを想定するためかR1であることが多いです。なお今回配布したメルカリのノベルティーキーキャップはMにつけることを想定してR4にしました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/76fb161a-image-8.png" alt="uzu keycap step4" /></p> <p>説明用に作成したR1 10&#215;5のものをこちらに置いておきます。<br /> <a href="https://yuzukeycaps.com/keyboards/121dfa73-a55e-492c-870a-2b94e490d040">https://yuzukeycaps.com/keyboards/121dfa73-a55e-492c-870a-2b94e490d040</a></p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/d03e1e03-image-9.png" alt="uzu keycap step5" /></p> <p>次にキーキャップのテンプレートを作成していきます。ひとつひとつ設定することもできますが、テンプレートを作成しておくと後の作業が楽になります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/139add2d-image-10.png" alt="uzu keycap step6" /></p> <p>後でロゴを配置できるように ICON タイプでテンプレートを作成します。なお、デフォルトのアイコンとしてアップロードした画像を選択することはできないようです。なのでここでは適当なものを選択しておきます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/22d25ae0-image-11.png" alt="uzu keycap step7" /></p> <p>次にキーを選択して、詳細の編集画面を開きます。「Select icon」を選択するとアイコンを選択する画面になるのでセレクトフォームの右側のアップデートボタンから利用するロゴファイルを選択します。画像の作成方法については下記に説明があります。基本的には2色のみしか使えないので注意が必要です(選べるカラーは豊富にありますが自分で自由に色を作ることもできません)。</p> <p><a href="https://www.notion.so/e5e214dc80c047ada20d97d3418eb2de?pvs=21">https://fkcaps.notion.site/Custom-image-upload-e5e214dc80c047ada20d97d3418eb2de</a></p> <p>なおこの画面からロゴの位置やサイズの微調整はできますが、同じ作業をすべてのキーに対してすることになるので、ここでは何もせずに済むようにテンプレートを調整することをおすすめします。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/29167e67-image-12.png" alt="uzu keycap step8" /></p> <p>あとはひたすらこの繰り返しです。アップロードしたデータはMy Iconsから選択できるようになっているので2回目以降は多少は楽です。なお私が発注したときはjsonファイルで入出力することができたので、jsonファイルを編集してこの繰り返し作業をもっと楽にすることはできましたが、この記事の執筆段階ではそれができなくなっていました。</p> <p>データが完成したら「ADD TO CART」に進み発注して完了です。データチェック後に製造され、手元に届くのには2~3週間ほどかかりますので余裕を持って発注することをおすすめします。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/44e12ff6-image-13.png" alt="uzu keycap step9" /></p> <p>以上です。ノベルティーキーキャップ作成の参考になれば幸いです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/49a9b094-image-14.png" alt="novelty keycap" /></p> QAはAIをどのように捉えるべきか &#8211; QAカンファレンス参加レポートhttps://engineering.mercari.com/blog/entry/20250718-e92e0e5563/https://engineering.mercari.com/blog/entry/20250718-e92e0e5563/<p>こんにちは。メルペイQAチームの@uni0110です。 私は6月にスコットランドのエディンバラで開催されたEuroSTARカンファレンスに参加しました。EuroSTARは世界的に有名なQAカンファレンスの一つで、今年は4 [&hellip;]</p> Tue, 22 Jul 2025 10:00:40 GMT<p>こんにちは。メルペイQAチームの@uni0110です。</p> <p>私は6月にスコットランドのエディンバラで開催された<a href="https://conference.eurostarsoftwaretesting.com/conference/2025/programme/" title="EuroSTARカンファレンス">EuroSTARカンファレンス</a>に参加しました。EuroSTARは世界的に有名なQAカンファレンスの一つで、今年は4日間にわたり60以上のチュートリアル、セッション、キーノートが行われました。約350社から1000人以上が参加した大規模なカンファレンスです。</p> <h2>テーマはAI on Trial</h2> <p>今年のカンファレンスで最も注目されたテーマはAIでした。参加者との会話でも、「あなたの会社ではAIをどのように活用していますか?」という質問が最も多く、議論が盛り上がりました。</p> <p>当時、メルペイQAチームは自動化にAIを活用して、それ以外の工程ではさまざまなツールを試している段階でした。そのため、私自身もAIに関するトピックに最も期待していました。</p> <p>全セッションの半分以上がAI関連のトピックで、内容はそれぞれ異なりましたが、どのセッションも共通して強調していたのは「AIがもたらす効率性や利便性よりも、AIの誤用や不確実性に対する注意」でした。初日のチュートリアルでこれを実際に体験できたので、簡単に共有したいと思います。</p> <h2>Test by Human vs. by AI</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/491f1158-photo2.png" alt="" /></p> <p>初日のチュートリアルでは、人間とAIがそれぞれ同じ内容のテストを実施しました。その結果、人間によるテストではシステムに潜在するバグが発見されましたが、AIが作成・実行したテストケースではバグを見つけることができませんでした。</p> <p>この違いは、人間だからできる批判的な思考です。人がテストケースを作成する際には、まず仕様を把握し、「どのような改修が行われたか」、「特定のケースではどうなるか」といった疑問があったらチューターに質問し、解決しました。このプロセスを通じて、不要なケースを削除し、必要なケースを追加することで、テストケースを完成させました。</p> <p>しかし、AIの場合は、どのAIツールを使用しても入力された指示に従ってテストケースを作成するだけで、バグを発見するケースは作成できませんでした。</p> <p>このことから、優れたQAエンジニアになるためには、クリティカルシンキングに基づいた積極的なコミュニケーション能力が必要であると痛感しました。</p> <h2>QAが見るAI</h2> <p>このチュートリアル以外にもAIが持っている以下の弱点のため、AIを使う時は十分気をつけないと行けないという内容のセッションが多かったです。</p> <ul> <li>プライバシー&セキュリティ</li> <li>バイアス</li> <li>ハルシネーション</li> <li>不慣れな人による誤用</li> <li>過度な自動化</li> </ul> <p>ここまでの内容だと、カンファレンス全体がAIに対して批判的な見方をしているように思われるかもしれませんが、どのセッションもAIが作業に役立つツールであることを前提としていたため、Anti-AI的な雰囲気ではありませんでした。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/733dc170-photo3.png" alt="" /></p> <p>ただ、警戒心を何度も与えた理由は、私達のロールがQAだからです。QAは他のエンジニアロールとは異なり、問題やリスクを発見する役割を担っています。そのため、AIに対しても厳しく警戒心を持たなければ、品質が損なわれる可能性があるからです。</p> <h2>終わりに</h2> <p>AIに関するベストプラクティスを期待して参加したカンファレンスでしたが、最後には自分は良いQAエンジニアなのか、もっとできることはないか、といった課題ばかり持ち帰ることになりました。また、QAエンジニアとしてAIに負けない私だけの価値について考え続けています。</p> <p>しかし、さまざまな場所から参加した多様なQAエンジニアと話す中で、皆が同じ悩みを抱えていることが分かり、良い刺激を受けました。</p> <p>特にAIについては、単なる便利なツールであり、silver bulletではないことを念頭に置いて活用していきたいと改めて感じました。</p> Elasticsearchのベクトル検索におけるフィルター使用時のパフォーマンス改善https://engineering.mercari.com/blog/entry/20250718-03e1dbe909/https://engineering.mercari.com/blog/entry/20250718-03e1dbe909/<p>Search Infra Teamのmrkm4ntrです。 画像検索にElasticsearchのベクトル検索(kNN検索)を活用しています。しかし、従来のキーワード検索と比較して、同等のリソースで処理できるQPS(Qu [&hellip;]</p> Fri, 18 Jul 2025 10:10:08 GMT<p>Search Infra Teamのmrkm4ntrです。</p> <p>画像検索にElasticsearchのベクトル検索(kNN検索)を活用しています。しかし、従来のキーワード検索と比較して、同等のリソースで処理できるQPS(Queries Per Second)が大幅に低いという課題がありました。そこで、Elasticsearch 8を基に、kNN検索のパフォーマンスをどこまで改善できるのかを調査しました。</p> <h2>kNN検索の構成と課題</h2> <p>今回の検証で使用したkNN検索のクエリ構成は以下の通りです。</p> <pre><code class="language-json">{ &quot;size&quot;: 100, &quot;query&quot;: { &quot;knn&quot;: { &quot;image_embedding&quot;: { &quot;vector&quot;: [ 0.1, 0.2, ... (128次元のベクトル) ], &quot;k&quot;: 100, &quot;num_candidates&quot;: 100, &quot;filter&quot;: { &quot;term&quot;: { &quot;status&quot;: &quot;on_sale&quot; } } } } } }</code></pre> <p>このクエリは、「status」フィールドが「on_sale」に一致するドキュメントの中から、与えられたベクトル(image_embedding)に類似した上位100件のドキュメントを検索するものです。ベクトルの次元数は128です。</p> <p>検証当初はElasticsearch 8.12.1を使用しており、async-profilerを用いてCPUプロファイルを取得した結果、以下の箇所がボトルネックとなっていることが判明しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/2cb0ad58-8.12.1.png" alt="" /></p> <p>特に目立つのは、赤枠で囲まれた<code>jint_disjoint_arraycopy</code>と<code>jlong_disjoint_arraycopy</code>です。これらのメソッドは、<code>OffHeapQuantizedByteVectorValues.vectorValue</code>から呼び出されており、JVMのヒープ外(つまりファイルシステムキャッシュ上)に格納されているベクトルデータをJVMのヒープ内にコピーする処理を行っています。</p> <p>Luceneでは高速なkNN検索を実現するためにPanama Vector APIを活用しています。このAPIはベクトル計算でプロセッサのSIMD命令(AVX命令)を使用し、演算効率を向上させるものです。しかし、ベクターデータを一度JVMヒープ内にコピーしてからPanama Vector APIに渡す無駄が発生しているため、パフォーマンスが大きく制約されています。</p> <p>このボトルネックを軽減する可能性がある修正がLuceneに施されていることが分かりました。具体的にはhttps://github.com/apache/lucene/pull/13339 の変更で、JVMヒープ外メモリから直接Panama Vector APIに渡す実装に改善されています。</p> <h2>Elasticsearch 8.17.1へのアップデートとパフォーマンスの変化</h2> <p>上記の改善が既に含まれるElasticsearch 8.17.1にアップデートし、パフォーマンスを検証しました。</p> <p>検証環境</p> <ul> <li>Elasticsearch Version: 8.17.1</li> <li>k: 100</li> <li>num_candidates: 100</li> <li>ベクトルの次元数: 128</li> <li>リアルタイムなドキュメントの追加/更新/削除</li> </ul> <p>フィルタリングの有無と度合いがパフォーマンスに与える影響を評価するため、以下の3つのケースで同一のスペックで捌けるQPSをパフォーマンスとして計測しました。</p> <ul> <li>フィルタなし: フィルタリングなし</li> <li>緩いフィルタ(loose filter): 約50%のドキュメントがフィルタ条件に一致</li> <li>絞り込みフィルタ(selective filter): 約10%のドキュメントがフィルタ条件に一致</li> </ul> <p>フィルタなしのケースにおいて、async-profilerでCPUプロファイルを再取得したところ、flame graphからPanama Vector APIの痕跡が消え、代わりに赤枠で囲んだ<code>dot7u</code>というメソッドが表示されるようになりました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/a551abd1-8.17.1-no-filter.png" alt="" /></p> <p>これは、Elasticsearch 8.15から、LuceneのPanama Vector APIではなく、より効率的なElasticsearch独自のベクトル化モジュールが使用されるようになったためです (<a href="https://www.elastic.co/search-labs/blog/vector-similarity-computations-ludicrous-speed">https://www.elastic.co/search-labs/blog/vector-similarity-computations-ludicrous-speed</a>) 。この独自実装では、不要なデータコピーはそもそも発生しません。</p> <p>8.12.1と8.17.1のパフォーマンスの比較は以下です。</p> <table> <thead> <tr> <th></th> <th>8.12.1</th> <th>8.17.1</th> </tr> </thead> <tbody> <tr> <td>フィルタなし</td> <td>350</td> <td>450</td> </tr> <tr> <td>緩いフィルタ(loose filter)</td> <td>50</td> <td>70</td> </tr> <tr> <td>絞り込みフィルタ(selective filter)</td> <td>30</td> <td>40</td> </tr> </tbody> </table> <p>Elasticsearch 8.17.1にアップデート後、全体的にパフォーマンスが改善しましたが、フィルタありのケースでは依然としてフィルタ無しのケースと比べるとパフォーマンスが低い状態です。</p> <h2>絞り込みフィルタリング時のHNSWグラフ探索の最適化</h2> <p>絞り込みフィルタ(selective filter)を適用した場合のCPUプロファイルを確認したところ、赤枠で囲まれた部分の<code>HNSWGraphSearcher.search</code>の処理時間が大幅に増加していることがわかりました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/6ad279c8-8.17.1-selective-filter.png" alt="" /></p> <p>これは近傍のノードをチェックする際に、類似度の計算を行わず、諦めて次をチェックする回数が増加していることを意味します。 つまり、HNSWグラフの構造上、フィルタ条件に合致するドキュメントが少ない場合、グラフのリンクを効率的に辿ることができないために発生する問題です。類似度が高い方向にグラフを探索しても、フィルタ条件に合致するドキュメントが見つからない場合、大きく迂回したり、戻ったりする必要が生じ、探索効率が低下します。</p> <p>この問題を解決するために、ACORNと呼ばれるアルゴリズム(<a href="https://arxiv.org/pdf/2403.04871">https://arxiv.org/pdf/2403.04871</a>) が提案されており、様々なベクトル検索エンジンが採用しています。Luceneのupstreamでも、ACORN風のアルゴリズムが実装されているため、このPR (<a href="https://github.com/apache/lucene/pull/14160">https://github.com/apache/lucene/pull/14160</a>) をcherry-pickして試したところ、特に絞り込みフィルタの場合に大きなパフォーマンス改善が見られました。</p> <table> <thead> <tr> <th></th> <th>8.17.1</th> <th>8.17.1 + ACORN</th> </tr> </thead> <tbody> <tr> <td>フィルタなし</td> <td>450</td> <td>450</td> </tr> <tr> <td>緩いフィルタ(loose filter)</td> <td>70</td> <td>80</td> </tr> <tr> <td>絞り込みフィルタ(selective filter)</td> <td>40</td> <td>120</td> </tr> </tbody> </table> <p>とはいえ、緩いフィルタ(loose filter)の場合はあまり改善されていません。</p> <h2>緩いフィルタリング時のBitSet合成の最適化</h2> <p>緩いフィルタ(loose filter)を適用した場合のCPUプロファイルを確認したところ、赤枠で囲んだ部分である<code>AbstractKnnVectorQuery.createBitSet</code>の処理時間が大部分を占めていることがわかりました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/faf66afc-8.17.1-acorn-loose-filter.png" alt="" /></p> <p>フィルタが固定で、kNN検索に指定するベクトル値のみを変更している場合、フィルタの結果のBitSetはクエリキャッシュに保存されるため、フィルタ自体のコストはほぼ無視できるはずです。</p> <p>コードを解析した結果、createBitSetメソッド内で、フィルタのBitSetとliveDocsのBitSetを合成した新しいBitSetを作成していることが判明しました。LuceneのSegmentはimmutableであるため、削除されたドキュメントを管理するために別のデータ構造(liveDocs)が必要になります。liveDocsもBitSetで表現されており、フィルタのBitSetとliveDocsのBitSetを合成する際に、BitSetの中身をiterateしていました。緩いフィルタの場合、フィルタ条件に合致するドキュメントが多いため、この処理に大きなコストがかかっていました。</p> <p>しかし、グラフを辿る際に、見つけたドキュメントがフィルタに合致するかをチェックするだけであれば、BitSetを合成する必要はありません。また、カーディナリティ(BitSet内で1が立っているビット数)を計算する場合も、iterateして合成する必要はなく、FixedBitSetのintersectionCountメソッドを使用することで高速に計算できます。</p> <p>これらの点を修正した結果、特に緩いフィルタを使用した場合のパフォーマンスが大幅に改善しました。</p> <table> <thead> <tr> <th></th> <th>8.17.1 + ACORN</th> <th>8.17.1 + ACORN + BitSet合成最適化</th> </tr> </thead> <tbody> <tr> <td>フィルタなし</td> <td>450</td> <td>450</td> </tr> <tr> <td>緩いフィルタ(loose filter)</td> <td>80</td> <td>200</td> </tr> <tr> <td>絞り込みフィルタ(selective filter)</td> <td>120</td> <td>170</td> </tr> </tbody> </table> <p>この修正はLuceneのupstreamにPRとして送りました(<a href="https://github.com/apache/lucene/pull/14771">https://github.com/apache/lucene/pull/14771</a>) 。しかし、その少し前に追加されていた修正 (<a href="https://github.com/apache/lucene/pull/14674">https://github.com/apache/lucene/pull/14674</a>) にて改善されていたためこのPRは不要でした。正確には、このPRそのものはcherry pickして試していましたが、<code>applyMask</code>の非default実装は別PRで対応されていたため、それを見落としていました。</p> <h2>FieldExistQueryによるパフォーマンス低下の解消</h2> <p>改善後のCPUプロファイルを確認したところ、依然としてcreateBitSet内の<code>Lucene99ScalarQuantizedVectorsReader$QuantizedVectorValues.advance</code>の処理時間が残っていました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/5fe9d775-8.17.1-acorn-bitset-loose-filter.png" alt="" /></p> <p>これは、フィルタにLucene内部で追加されるFieldExistQueryによるものであることがわかりました。インデックス内のすべてのドキュメントにkNNの対象となるベクトルフィールドが存在するわけではない場合、その存在をチェックする追加の処理が必要になります。今回のケースでは、埋め込み処理でエラーが発生した場合にベクトルが存在しないドキュメントが存在していました。</p> <p>これらのドキュメントをインデックスに含めないように修正したところ、パフォーマンスがさらに改善しました。</p> <table> <thead> <tr> <th></th> <th>8.17.1 + ACORN + BitSet合成最適化</th> <th>8.17.1 + ACORN + BitSet合成最適化 + FieldExistQueryの除外</th> </tr> </thead> <tbody> <tr> <td>フィルタなし</td> <td>450</td> <td>450</td> </tr> <tr> <td>緩いフィルタ(loose filter)</td> <td>200</td> <td>250</td> </tr> <tr> <td>絞り込みフィルタ(selective filter)</td> <td>170</td> <td>200</td> </tr> </tbody> </table> <p>一般にFieldExistQueryは対象のドキュメントのfieldを全てチェックする必要があるため高コストです。FieldExistQueryと今回のフィルタはQuery Cacheの対象のため全てのドキュメントをチェックしているわけではないはずですが、Query Cacheの対象とならないようなサイズのセグメントのみが対象だったとしても、緩いフィルタの場合は高コストであったと考えられます。</p> <h2>さいごに</h2> <p>Elasticsearch 8のkNN検索において、フィルタリング時のパフォーマンスを改善するために、以下の施策を実施しました。</p> <ol> <li>Elasticsearch 8.17.1へのアップデート: Elasticsearch独自のベクトル化モジュールを使用することで、フィルタなしのケースにおけるパフォーマンスを向上させました。</li> <li>ACORN風アルゴリズムの導入: 絞り込みフィルタ適用時のHNSWグラフ探索を最適化しました。</li> <li>BitSet合成の最適化: 緩いフィルタ適用時のBitSet合成処理を効率化しました。</li> <li>ベクトルフィールドの存在チェックの排除: ベクトルフィールドが存在しないドキュメントをインデックスから排除することで、不要な存在チェックを削減しました。</li> </ol> <p>これらの改善により、フィルタリングの有無に関わらず、kNN検索のパフォーマンスを大幅に向上させることができました。以下が最終的な結果です。</p> <table> <thead> <tr> <th></th> <th>改善前</th> <th>改善後</th> </tr> </thead> <tbody> <tr> <td>フィルタなし</td> <td>350</td> <td>450</td> </tr> <tr> <td>緩いフィルタ(loose filter)</td> <td>50</td> <td>250</td> </tr> <tr> <td>絞り込みフィルタ(selective filter)</td> <td>30</td> <td>200</td> </tr> </tbody> </table> <p>ベクトルフィールドの存在チェック以外の改善は将来のElasticsearchのリリース(9.0.4以降?)に含まれる予定です。</p> <p>これらの改善は、ElasticsearchのkNN検索をHybrid Searchなどに活用し、より高度な検索サービスの提供に繋がるものと考えています。</p> メルペイ Payment Coreチームで学んだ2ヶ月間の振り返りhttps://engineering.mercari.com/blog/entry/20250716-paymentcore-internship/https://engineering.mercari.com/blog/entry/20250716-paymentcore-internship/<p>こんにちは。メルペイ Payment Core Teamで2ヶ月間インターンシップをした@taichiです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の22 [&hellip;]</p> Wed, 16 Jul 2025 10:03:55 GMT<p>こんにちは。メルペイ Payment Core Teamで2ヶ月間インターンシップをした@taichiです。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の22日目の記事です。</p> <h1>はじめに</h1> <p>私は4月の中旬から6月の中旬の間、 バックエンドエンジニアとしてメルペイのインターンシップに参加しました。今回はインターン期間中に取り組んだタスクを振り返り、 そこで得た学びをまとめたいと思います。<br /> この記事が、 メルペイのインターンに挑戦してみたいと考えている未来のHackerの参考になれば幸いです。</p> <h2>取り組んだタスク</h2> <p>私が担当したタスクは大きく分けると3つあります。</p> <ul> <li>外部パートナーへの接続に関するCredentialの管理方法の変更</li> <li>Re-arch中のソースコードへのPub/Sub基盤統合</li> <li>個人情報難読化ポリシーの実装</li> </ul> <p>以下で1つずつ話していきます。</p> <h1>外部パートナーへの接続に関するCredential管理方法の変更</h1> <h2>背景</h2> <p>私が所属するPayment Coreチームでは、決済基盤マイクロサービスである『Payment Service』の開発を担当しています。このサービスは、メルカリ、メルペイ、メルコインが展開する多数のマイクロサービス群から参照されており、グループ全体の決済ドメインにおけるコアな責務を担っています。</p> <p>メルペイでは決済機能の一部で外部パートナーさまのAPIを活用しているため、そのCredentialを適切に管理する必要があります。</p> <p>私がタスクに取り組むまでの運用では、 新しく追加したCredentialを暗号化しSpannerにSQL raw queryを叩くことで保存していました。</p> <p>従来の運用方法は以下に示す危険性と面倒さを有しています。</p> <ul> <li>加盟店追加のたびにSQLクエリを叩く必要がある</li> <li>加盟店追加後にSQLクエリを発行し忘れる可能性がある</li> </ul> <h2>やったこと</h2> <p>上記の課題を解決するために、 以下のような設計と実装を行いました。</p> <ol> <li>外部パートナーの加盟店さまのパスワードの格納先をGoogle Secret Managerに変更</li> <li>Spannerに直接パスワードを保存せず, SecretのKeyとVersionだけを格納する</li> <li>Secretの情報を入力として渡すだけで, SQLが走ってSecretのKeyとVersionをSpannerに保存するK8sのJobテンプレートを作成</li> <li>SecretManagerを通して加盟店さまのパスワードを取得できるようにClientCodeを修正</li> </ol> <p>最初はCLIを作ってチームに提供することも考えましたが、 メンテナンスの負荷が新たに発生すること、 K8sのJob基盤がすでにチームに存在することを理由にCLIは避けました。<br /> K8sのJobテンプレートはこちらが参考になると思います。<br /> インターンに参加して1週間くらいで設計を行いドキュメントを作成したのですが, 設計書のレビューが手厚くチームメンバーと技術的な議論を繰り返すことでタスクやPayment Serviceの全体像を掴むことができました。</p> <h1>Re-arch中のソースコードへのPub/Sub基盤統合</h1> <h2>背景</h2> <p>私が所属していたPayment Coreチームが開発しているPayment Serviceは、そのソースコードが非常に複雑なため、大規模な再構築プロジェクト、通称「Re-arch(リアーキテクチャ)」が進められていました。</p> <p>このRe-archプロジェクトの目的は、既存のPayment ServiceのソースコードをClean Architectureのような設計思想に基づいて書き直すことです。</p> <p>現状のPayment Serviceでは、非同期処理のためにGoogle Cloud Pub/Subが利用されています。しかし、Re-arch後の新しいソースコードには、Pub/Subを利用するための基盤がまだ整備されていない状況でした。</p> <h2>やったこと</h2> <p>私はRe-arch後のソースコードのコンテキストにマッチするようにPub/Subの基盤を統合しました。<br /> Pub/Sub基盤の設計は、 Payment ServiceがPub/SubからSubscribeしか行わないことを前提にメンターと進めました。<br /> Subscriberに渡すHandlerのInterface設計や、 Usecaseに渡す依存関係を一括で管理するContainerとの親和性を考慮して設計する経験は非常に勉強になりました。<br /> Re-archのPRは変更が大きいものもあるので、 Conflictの解消やContextを理解し直すのに苦労したこともありましたが、 あれほど大規模なソースコードを読むこともないのでとても良い経験になりました。<br /> 開発体験としても、 Payment ServiceではMockの生成は<a href="http://moq.go" title="moq.go">moq.go</a>を使っているので、 interfaceだけ設計すればMockを簡単に生成できるので、 素早くTestableなコードを書くことができました。</p> <h1>個人情報難読化ポリシーの実装</h1> <h2>背景</h2> <p>個人情報保護法の改正を受け、メルペイ全社で「PII Deletion(個人情報難読化)プロジェクト」が進行中です。これに伴い、Payment Serviceもこの対応を行う必要がありました。<br /> 個人情報の難読化はメルペイ全体で取り組むべき課題であるため、すでにそのためのマイクロサービス「PII Deletion Service」が構築されていました。<br /> しかし、Payment ServiceからPII Deletion Serviceを叩きに行くことができない状態でした。</p> <h2>やったこと</h2> <p>PII Deletion Serviceのアーキテクチャは下図のようになっています。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/b2f49001--2025-07-16-9.46.53.png" alt="" /></p> <p>上図を解釈すると, 処理の流れとしては以下になります。(Payment Serviceは上図における右端に位置すること, 各マイクロサービスはgRPCで通信を行うことを念頭においてください)</p> <ol> <li>PII Deletion Managerから難読化すべき個人情報の情報がPub/SubにPushされる </li> <li>Payment ServiceがPub/SubからPullして難読化する対象を見つける</li> <li>難読化する</li> <li>難読化が成功したかのステータスをPII Deletion Managerに返す</li> </ol> <p>1はすでに仕組みとして存在するので、 私は2-4を実装すれば良いことに気づきます。<br /> 以下ではステップ2についてどのように対応したかを述べます。(3、 4はそこまで難しいことがないのでスキップします。)</p> <h2>Pub/SubからPullして難読化対象を見つける</h2> <p>PII DeletionのHandlerはRe-arch後のソースコードに実装するので、 私が統合したPub/Sub基盤を使用すればすぐに実現できるので簡単に思えます。<br /> しかし、 Pub/SubのSubscriberが行う処理をそのまま記述しようとすると2点好ましくない点があります。<br /> 普段慣れ親しんでいるgRPCエンドポイントと異なる体験で開発しないといけない<br /> Subscriberのロジックが他のAPIのロジックから独立しがち<br /> これらの課題を避けるために、 メルペイはPub/Sub gRPC Pusherという内製化サービスを持っています。Pub/Sub gRPC Pusherの仕組みは簡単で、 以下のようなアーキテクチャになります。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/07/afa3c4c3--2025-07-16-9.48.57.png" alt="" /></p> <p>gRPC Pusherはマイクロサービスの代わりにPub/SubからPullし, gRPCリクエストに変換してマイクロサービス側のエンドポイントを叩いてくれます。<br /> gRPC Pusherを使うことでメルペイのエンジニアは、Pub/SubのSubscriberロジックを慣れ親しんだgRPCエンドポイントとして実装でき、Pub/Subという特定のInfrastructureを意識しなくて良くなります。</p> <p>今回はこちらのgRPC Pusherを利用するためのInfrastructureリソースをTerraformで作成し、 個人情報難読化を行うロジックはgRPCエンドポイントとして実装を行いました。<br /> ただ、gRPC Pusherを使うならいろいろと考えるべきことがあります。<br /> Pub/SubのPull型Subscriptionの大きな利点は、Pullする側のスケールやワークロードの都合に合わせて処理を実行できる点にあります。gRPC Pusherはその都合をPullするマイクロサービスの代わりに受け持っていると考えることもできます。</p> <p>Kubernetesをはじめとしたスケーリング技術は、必要になった時にすぐにスケールするわけではないので、ワークロード量によってはgRPC Pusherの使用は適切でない場合もあります。しかし、PII Deletionのリクエストはスケールが追いつかないほど大量のリクエストが飛んでくることは想定し難いため、gRPC Pusherの使用を決断しました。<br /> 全ての個人情報を難読化するところまでは完了できませんでしたが、 基本的なロジックは全て実装し終えました。</p> <h1>学んだことと感想</h1> <h2>ハード面</h2> <p>使用した技術としては以下です。</p> <ul> <li>Kubernetes</li> <li>Terraform</li> <li>Google Cloud (Pub/Sub, Secret Manger, Spanner)</li> <li>gRPC</li> <li>Go</li> </ul> <p>どの技術も触ったことはあったものの深く触ったことはなかったので勉強になりました。<br /> 特にTerraformには興味があるので、 大学院の研究が落ちつく7月は何かしらのProviderを自前で実装するつもりです。Kubernetesに関しても基本的な概念やリソースの役割のみならず、 カスタムコントローラの実装等、 面白い部分はたくさんあるのでこれからさらに深く勉強していこうと思います。<br /> また、 Payment Serviceのソースコードは大規模かつ複雑だったので読み解くのに苦労しましたが、 パフォーマンスと冪等性を意識した設計になっているのでとても勉強になりました。</p> <h2>ソフト面</h2> <p>インターンを通して自身の英語スキルが向上したと感じました。<br /> Payment Coreチームでは、 チームのStandupも月曜日から木曜日は全て英語で行われます。<br /> また、私が参加していたPII Deletion ProjectのMeetingやGitHub上のやり取りも基本全て英語ですし、最終成果発表も全て英語で行いました。普段の業務から英語と身近に触れられたことは自身の成長に大きく繋がったと感じています。<br /> それから, チームメンバーはもちろん、他のチームの方々ともコミュニケーションを積極的にとることも心がけました。チームメンバーはもちろんのこと、他チームの方々も私に親切にしてくださり最終成果発表にも参加してくださいました。<br /> インターンシップを通じてメルカリのカルチャーを存分に味わえて本当に楽しかったです。</p> <p>メルカリグループは全社的にAI活用を推進しており、私も積極的にAIを活用しました。私は以前からAIを使ってソースコードを生成することに違和感を覚えていました。<br /> Junior EngineerはIntermediate、 Seniorとタイトルを上げていく必要があります。タイトルはエンジニアの各ソースコードのレベルだけで決まるわけではないですが、当然相関はあります。Juniorである私がAIを使ってソースコードを書いてしまったら、成長する機会がなくなりいつまで経っても自らのスキルが伸びないのではないか?と考えていました。</p> <p>ですが、Payment Serviceの莫大なドメインと複雑なソースコードを理解するには到底2ヶ月では足りないため、効率の良いキャッチアップが必要です。チームメンバーに相談してみると、彼らは皆AI(Cursor)を使って効率よくドメイン知識を吸収したり、UnitTestを書いたりしていました。彼らの助言を受けて私もインターン中にCursorを徹底的に活用し、AIと協調して、今までよりもより本質的な作業に時間を使えるようになりました。</p> <p>AIを使用しても出力されるコードや価値は本人の能力にキャップされるため、依然として強くなるために勉強は必要です。<br /> 私が最初に抱いていた違和感は今でも間違っていないと思います。<br /> しかし、成長する機会は自分でいくらでも作り出せるので、仕事と成長をイコールで結ばず両方全力で取り組めば、より早く、より大きな価値を届けられるエンジニアになれると考えを改めました。<br /> インターンを通して最も大きく変わった部分はここだと思います。</p> <h1>最後に</h1> <p>チームメンバーをはじめ、たくさんの方々にお世話になりました。<br /> メルカリのカルチャーを存分に体感しながら技術的に難しい課題に取り組まさせていただき、本当に感謝しています。<br /> 2ヶ月間ありがとうございました。</p> KubeCon + CloudNativeCon 2025 Japan 参加レポート(おまけ:Envoy拡張のWasmフィルタのデモ実装)https://engineering.mercari.com/blog/entry/20250630-1224ec5881/https://engineering.mercari.com/blog/entry/20250630-1224ec5881/<p>こんにちは。メルカリモバイルのソフトウェアエンジニアの@keiitajです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の21日目の記事です。 概要 本記事は [&hellip;]</p> Mon, 30 Jun 2025 18:07:44 GMT<p>こんにちは。メルカリモバイルのソフトウェアエンジニアの<a href="https://x.com/keiitaj">@keiitaj</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の21日目の記事です。</p> <h2>概要</h2> <p>本記事は、2025年6月16-17日に日本で初開催されたKubeCon + CloudNativeCon 2025 Japan の参加レポートです。</p> <p><strong>この記事の内容:</strong></p> <ul> <li>日本初開催のKubeConイベントレポート</li> <li>注目セッション</li> <li>Envoy拡張用Wasmフィルタのローカル環境上のデモ実装</li> </ul> <p><strong>執筆者の学び:</strong><br /> EnvoyとWebAssemblyを活用したAPI管理手法を実際に実装することで、セッションで得た知識をより深く理解できました。また、参加前は技術的ハードルが高いと感じていましたが、実際は職種やレベルを問わず楽しめるイベントであることを発見しました。</p> <h2>目次</h2> <ol> <li><a href="#はじめに">はじめに</a></li> <li><a href="#参加の動機">参加の動機</a></li> <li><a href="#イベントの規模と熱気">イベントの規模と熱気</a></li> <li><a href="#注目したkeynote--session">注目したKeynote &amp; Session</a></li> <li><a href="#参加して感じたこと">参加して感じたこと</a></li> <li><a href="#おまけenvoy--wasmフィルタの実装とローカル環境のデモ">おまけ:Envoy + Wasmフィルタのデモ実装</a></li> <li><a href="#まとめ">まとめ</a></li> </ol> <h2>はじめに</h2> <p>私は普段、Platform Engineerではなく、KubernetesやCloud Nativeの技術で構成された基盤の上でサービス開発を行うエンジニアとして、Tekton、Argo、Istio、Envoyなどを使って仕事をしています。</p> <h2>参加の動機</h2> <h3>技術的な興味</h3> <p>業務でKubernetesやCloud Nativeの技術(Tekton、Argo、Istio、Envoy、Spinnakerなど)に日々触れる中で、これらの技術の最新動向やコミュニティの活動に強い興味を持つようになりました。<br /> また、AI技術の進化により、サービス開発エンジニアがPlatform構築により深く関わる機会が増えていくと感じました。<br /> そのため、Cloud Nativeエコシステムの全体像を把握しておきたいという思いもありました。</p> <h3>日本初開催の歴史的なイベント</h3> <p>CNCF(Cloud Native Computing Foundation)の主要イベントとして世界各地で開催されてきたKubeConが、日本で初めて開催されることも参加の大きな動機となりました。</p> <p>実は、この日本開催実現までには長い道のりがあったそうです。<br /> 以前、5月に開催されたCloudNative Daysで、Linux FoundationのNoriaki Fukuyasu氏による<a href="https://event.cloudnativedays.jp/cnds2025/talks/2633">KubeConを日本に招致するまでの経緯</a>についての講演を聞く機会があり、その内容がとても印象的で、今回のKubeCon参加のきっかけの一つにもなりました。</p> <p>Fukuyasu氏の話によると:</p> <ul> <li>2023年以前、日本はクラウドネイティブ技術後進国と言われていた <ul> <li>大企業での採用実績が少ない</li> <li>アジャイル、マイクロサービス、コンテナを知らない人が多数</li> </ul> </li> <li><a href="https://community.cncf.io/cloud-native-community-japan/">Cloud Native Community Japan</a>を立ち上げ、月に複数回のmeetupを開催</li> <li>継続的なロビイング活動を通じて、ついに日本への招致に成功</li> </ul> <p>このような背景を知ったことで、日本初開催のKubeConに参加することの意義をより深く感じるようになりました。</p> <h2>イベントの規模と熱気</h2> <h3>会場と参加者</h3> <p>会場となったヒルトン東京お台場で開催されたこのイベントは、1,500枚のチケットが完売し、日本のCloud Nativeコミュニティの盛り上がりを肌で感じることができました。</p> <p>ちなみに、今年ロンドンで開催されたKubeConは12,418人が参加する<a href="https://www.cncf.io/wp-content/uploads/2025/06/KubeCon_EU_25_TransparencyReport.pdf">大規模なイベント</a>でしたが、日本開催はコンパクトながらも内容の濃いイベントでした。</p> <h3>イベントの楽しみ方</h3> <p>KubeConの魅力は、単にセッションを聞くだけではありません。初参加で感じた楽しみ方をご紹介します:</p> <ol> <li><strong>セッション参加</strong>:<a href="https://kccncjpn2025.sched.com">スケジュール</a>を見て、興味のあるKeynoteやセッションに参加</li> <li><strong>ブース巡り</strong>:セッションの合間に、OSSパビリオンや企業ブースを訪問 <ul> <li>OSSプロジェクトの開発者と直接話す貴重な機会</li> <li>最新プロダクトのデモを体験</li> <li>各社のノベルティグッズ収集</li> </ul> </li> </ol> <h4>企業ブースでの発見</h4> <p>各企業ブースを回ることで、最新のプロダクトやサービスの動向を直接確認できました:</p> <ul> <li> <p><strong>PagerDuty</strong> AIエージェントが過去の対応履歴を基に、インシデントの原因分析や重要度判定を支援</p> </li> <li> <p><strong>Splunk</strong> Observability Cloudの新機能を展示、AIチャット機能による差別化を強調</p> </li> <li> <p><strong>Toyota</strong> コネクテッドカーの研究開発でCloud Native技術を活用し、自動車業界でもCloud Nativeが浸透していることを実感</p> </li> </ul> <h2>注目したKeynote &amp; Session</h2> <h3>Opening Keynote: Community Opening Remarks</h3> <p><a href="https://www.youtube.com/watch?v=mh7Cmei3pmI">Community Opening Remarks</a> &#8211; Chris Aniszczyk氏(CNCF CTO)</p> <p><a href="https://www.youtube.com/watch?v=mh7Cmei3pmI" title="Community Opening Remarks"><img src="https://img.youtube.com/vi/mh7Cmei3pmI/0.jpg" alt="Community Opening Remarks" /></a></p> <p>開会の挨拶では、以下の印象的な発表がありました:</p> <ul> <li>1,500枚のチケットが完売したことへの感謝</li> <li>CNCF、Kubernetesへのコントリビューションで日本がTOP10入り</li> <li><strong>2026年も日本でKubeConを開催することが決定!</strong></li> </ul> <p>日本のCloud Nativeコミュニティの成長が認知されていることを実感しました。</p> <h3>技術セッション: Full Lifecycle API Management in Kubernetes With Envoy and WebAssembly</h3> <p>特に印象に残ったのは、<a href="https://www.youtube.com/watch?v=McMBwWPlYTE">Full Lifecycle API Management in Kubernetes With Envoy and WebAssembly</a>というセッションです。</p> <p><a href="https://www.youtube.com/watch?v=McMBwWPlYTE" title="Full Lifecycle API Management in Kubernetes With Envoy and WebAssembly"><img src="https://img.youtube.com/vi/McMBwWPlYTE/0.jpg" alt="Full Lifecycle API Management in Kubernetes With Envoy and WebAssembly" /></a></p> <h4>セッションの概要</h4> <p>KubernetesにおけるAPI管理の課題に対して、EnvoyとWebAssembly(Wasm)を活用した革新的なアプローチが紹介されました:</p> <ol> <li><strong>L3/L7プロキシ機能の統合</strong> <ul> <li>JWT認証とルーティングによる高度なAPIトラフィック管理</li> <li>eBPFとOpenTelemetryを活用したオブザーバビリティの向上</li> </ul> </li> <li><strong>WebAssemblyフィルタの活用</strong> <ul> <li>複数言語での開発が可能</li> <li>より高速な配布時間</li> <li>ランタイムでのセキュリティロジックの実装</li> </ul> </li> <li><strong>実践的なデモ</strong> <ul> <li>Authorizationヘッダーのチェック機能をWasmで実装</li> <li>認証なしのトラフィックをブロックする仕組みの構築</li> </ul> </li> </ol> <h4>技術的な洞察</h4> <p>このセッションで特に興味深かったのは、WebAssemblyの用途が拡大していることです。元々はブラウザ上でのパフォーマンス向上のために開発されたWebAssemblyが、今やサーバーサイドのプラグイン機構として活用され始めています。</p> <h2>参加して感じたこと</h2> <h3>技術トレンドの観察</h3> <p>イベント全体を通じて感じた技術トレンド:</p> <ul> <li><strong>OpenTelemetry + eBPF</strong>:オブザーバビリティ関連のセッションで頻繁に言及</li> <li><strong>WebAssembly</strong>:サーバーサイドでの活用事例が増加</li> <li><strong>AI統合</strong>:各種ツールにAI機能が標準装備</li> </ul> <h3>コミュニティの多様性</h3> <p>印象的だったのは、専門的な内容から初心者向けまで幅広いセッションが用意され、ライトニングトークやパネルディスカッションでは専門以外の話題(コミュニティでの友達作りやコントリビューションのモチベーションなど)も語られていたことです。</p> <p>参加のハードルが高いイメージがありましたが、実際は職種やエンジニアのレベルを問わず、誰でも楽しめるイベントであると感じました。</p> <h3>言語の壁と対策</h3> <p>Keynoteやセッションは全て英語で行われるため、英語が苦手な方には少しハードルが高いかもしれません。</p> <p>私がとった対策は、Google Meetのマイクでスピーカーの音声を拾い、Geminiに議事録を作成してもらって内容を把握することでした。</p> <p>Google Docsの音声入力は途中で音声が途切れると入力がストップしてしまったり、AppStoreに公開されている幾つかの音声の書き起こしアプリは有料でさらに時間制限があったりしたので、色々試行錯誤した結果、この方法が一番良かったと感じました。</p> <p>また、参加した仲間とセッションの内容を共有し合うことで、理解を深めていました。</p> <h3>効率的な参加のコツ</h3> <p>同一時間帯に複数のセッションが開催されるため、私は興味のあるセッションを絞って参加しましたが、複数人で参加する場合はより効率的な立ち回りができると感じました:</p> <ul> <li>手分けして異なるセッションに参加</li> <li>Coffee Breakで情報交換したり、お互いにセッション内容のメモを共有</li> </ul> <h2>おまけ:Envoy + Wasmフィルタの実装とローカル環境のデモ</h2> <p>KubeConで紹介されたEnvoyとWebAssemblyによるAPI管理の技術についてより深く理解するため、実際にEnvoyを拡張するGolang製のWasmフィルタを実装し、ローカル環境上で動作確認を行いました。</p> <p>実装の詳細やソースコードは、<a href="https://github.com/keitaj/envoy-wasm-sample">GitHub</a> で公開しています。</p> <h3>デモの概要</h3> <p>このデモでは、Bearer トークン認証を行うWasmフィルタを実装し、以下の機能を備えています:</p> <ul> <li>Bearer トークンによる認証機能</li> <li><code>/health</code> エンドポイントの認証スキップ</li> <li>認証成功時のカスタムヘッダー追加</li> <li>認証失敗時のJSONエラーレスポンス</li> </ul> <h3>実装のポイント</h3> <h4>1. 使用技術・ツール</h4> <ul> <li><strong>Envoy</strong>: v1.34-latest以降</li> <li><strong>Go</strong>: 1.24以降 (wasip1/wasm target)</li> <li><strong>proxy-wasm/proxy-wasm-go-sdk</strong>: Wasm plugin development SDK for Envoy</li> </ul> <h4>2. シンプルな認証ロジック</h4> <pre><code class="language-go">package main import ( &quot;github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm&quot; &quot;github.com/proxy-wasm/proxy-wasm-go-sdk/proxywasm/types&quot; ) func main() {} func init() { proxywasm.SetVMContext(&amp;vmContext{}) } type vmContext struct { types.DefaultVMContext } func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext { return &amp;pluginContext{} } type pluginContext struct { types.DefaultPluginContext } func (p *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus { proxywasm.LogInfo(&quot;plugin started&quot;) return types.OnPluginStartStatusOK } func (*pluginContext) NewHttpContext(contextID uint32) types.HttpContext { return &amp;httpAuthContext{contextID: contextID} } type httpAuthContext struct { types.DefaultHttpContext contextID uint32 } func (ctx *httpAuthContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { // Get path path, err := proxywasm.GetHttpRequestHeader(&quot;:path&quot;) if err != nil { proxywasm.LogErrorf(&quot;failed to get path: %v&quot;, err) path = &quot;/&quot; } proxywasm.LogInfof(&quot;Processing request to path: %s&quot;, path) // Skip health check if IsHealthCheckPath(path) { proxywasm.LogInfo(&quot;Health check endpoint, allowing request&quot;) return types.ActionContinue } // Get Authorization header authHeader, err := proxywasm.GetHttpRequestHeader(&quot;authorization&quot;) if err != nil { authHeader = &quot;&quot; } // Validate token authResult := ValidateToken(authHeader) if !authResult.IsValid { proxywasm.LogWarnf(&quot;Authentication failed: %s&quot;, authResult.Reason) return ctx.denyRequest(authResult.Reason) } // Add user type header proxywasm.LogInfof(&quot;Valid %s token&quot;, authResult.UserType) proxywasm.AddHttpRequestHeader(&quot;x-auth-user&quot;, authResult.UserType) return types.ActionContinue } func (ctx *httpAuthContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action { proxywasm.AddHttpResponseHeader(&quot;x-wasm-filter&quot;, &quot;go-auth&quot;) return types.ActionContinue } func (ctx *httpAuthContext) denyRequest(reason string) types.Action { body := CreateErrorResponse(reason) err := proxywasm.SendHttpResponse(401, [][2]string{ {&quot;content-type&quot;, &quot;application/json&quot;}, {&quot;x-wasm-filter&quot;, &quot;go-auth&quot;}, }, []byte(body), -1) if err != nil { proxywasm.LogErrorf(&quot;failed to send response: %v&quot;, err) } return types.ActionPause }</code></pre> <h3>ローカル環境でのデモ</h3> <p>Docker Composeを使用してローカル環境で動作確認を行えるようにしました:</p> <pre><code>Client → Envoy Proxy (Wasmフィルタ) → Backend Service</code></pre> <p>以下の3つのシナリオで動作を確認できます:</p> <ol> <li><strong>認証成功</strong>: 正しいBearerトークンでのリクエスト</li> <li><strong>認証失敗</strong>: 無効なトークンでのリクエスト</li> <li><strong>認証スキップ</strong>: ヘルスチェックエンドポイントへのアクセス</li> </ol> <h3>学びと今後の可能性</h3> <p>このデモ実装を通じて、Envoy Wasmフィルタの実用性と多くのメリットを実感できました:</p> <h4>Wasmフィルタの主要メリット</h4> <p><strong>言語の自由度</strong> &#8211; Go、Rust、C++など慣れ親しんだ言語で開発でき、既存のツールチェーンを活用可能</p> <p><strong>安全性</strong> &#8211; Wasmのサンドボックス環境で実行されるため、Envoyプロセスをクラッシュさせるリスクが低く、メモリ安全性も保証</p> <p><strong>パフォーマンス</strong> &#8211; ネイティブコードに近い実行速度を実現</p> <p><strong>配布とバージョニング</strong> &#8211; 単一の.wasmファイルとして配布でき、バージョン管理やデプロイメントパイプラインへの組み込みが簡単</p> <p>特に実際のプロジェクトでは、既存のGo/Rustコードベースがある場合、同じ言語でプロキシレイヤーのロジックを実装できることが大きな価値となります。</p> <p>認証、ログ処理、メトリクス収集などのビジネスロジックを統一言語で管理でき、JWT検証やレート制限、OpenTelemetry連携など、より高度な機能への拡張も現実的です。</p> <h2>まとめ</h2> <p>初参加したKubeCon + CloudNativeCon 2025 Japanは、日本のCloud Nativeコミュニティの盛り上がりを実感できる貴重な体験となりました。</p> <p>技術面では、EnvoyとWebAssemblyを活用したAPI管理手法が特に印象深く、実際にWasmフィルタを実装してローカル環境でデモを動かすことで、セッションで学んだ概念をより深く理解できました。また、OpenTelemetryとeBPFを組み合わせたオブザーバビリティ技術や、WebAssemblyのサーバーサイド活用、AI統合の進展など、Cloud Native技術の進化を直接体感することができました。</p> <p>また、参加前は技術的なハードルが高いイメージがありましたが、実際は職種やエンジニアレベルを問わず楽しめるイベントであることも発見できました。多様な参加者との交流や企業ブースでの最新技術の体験も、魅力だと感じました。</p> <p>来年2026年の日本開催も決定しており、Cloud Native技術に興味がある方にはぜひ参加をお勧めします。</p> <hr /> <p><em>本記事で紹介したセッションの動画は、<a href="https://www.youtube.com/@cncf">CNCFの公式YouTubeチャンネル</a>で公開されています。興味のある方はぜひご覧ください。</em></p> PJ-Aurora:メルカリにおけるUI生成・評価の取り組みhttps://engineering.mercari.com/blog/entry/20250630-94c2e6b283/https://engineering.mercari.com/blog/entry/20250630-94c2e6b283/<p>こんにちは。メルペイで機械学習とAIのチームのEMをしている@hiroです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の21日目の記事です。 メルカリ、メル [&hellip;]</p> Mon, 30 Jun 2025 10:00:10 GMT<p>こんにちは。メルペイで機械学習とAIのチームのEMをしている@hiroです。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の21日目の記事です。</p> <p>メルカリ、メルペイでは生成AIの活用を非常に積極的に推進しています。今回の「Merpay&Mercoin Tech Openness Month 2025」においても、自然発生的に多くのメンバーが生成AIをテーマに選択しており、これは会社全体でのAI活用の機運の高まりを示していると感じています。従前からAIの取り組みはありましたが、会社としてのコミットメントの深まりとエンジニアを含むメンバーたちの熱量が高くなっており、数々のプロジェクトが生まれています。 この記事ではその中から、CXOの@naricoと共に推進している「PJ-Aurora」(プロジェクトオーロラ)について共有します。</p> <h2>Design/Creative x AI</h2> <p>2024年のメルカンの記事『<a href="https://careers.mercari.com/mercan/articles/47129/" title="「Must」を「Fun」に!メルカリCPOとCXOが語る、“AI-Led Growth Company”としてのAI活用の未来">「Must」を「Fun」に!メルカリCPOとCXOが語る、“AI-Led Growth Company”としてのAI活用の未来</a>』にも書かれていますが、以前より「pj-ai-creator」を立ち上げ、デザイン領域における生成AIの活用について実験的な取り組みを行ってきました。PJ-AuroraはAI Creatorを経て正式に立ち上がったプロジェクトです。<br /> PJ-Auroraは生成AIの活用を通じて、メルカリ、メルペイにおけるものづくりのアプローチを変革し、よりMove Fastにアイデアを実現してお客さまに届けることができるようになることを目指しています。あしもとでは特に、アイデアからのUI生成や評価、UXシミュレーション工程のプロセスイノベーションに挑戦しています。<br /> とはいえ、基盤モデルの性能が日毎月毎に改善されていく中、生成AIを使って達成できることは何か、という問いの答えも変わり続けています。私たちも常に学習し続け、期待値とスコープを更新し続ける必要があると認識しています。</p> <h2>アイデアからUIを生成する</h2> <p>プロンプトからUIを生成するタスクは生成AIの発展とともに流行しているタスクの一つで、Figma社のFigma Makeをはじめさまざまなサービスが生まれています。メルカリでは各サービスの試験的な利用もしつつ、Agentic Workflow(複数のAIエージェントが連携して作業を進める仕組み)を構築し、独自のUI生成プロセスを構築する営みも並行して進めています。<br /> このタスクは昨今、精度向上が目覚ましく、正直なところ、最終的には内製のシステムではなく外部のサービスを使う形になるかもしれません。しかし、この取り組みを通じて得られる知見と資産は、AI時代における競争優位性の源泉になると考えています。</p> <h2>AI時代のデザイン資産</h2> <p>たとえば、デザインシステムは人間のデザイナー/エンジニアが理解し活用するために作られていることが多いですが、AI時代においては「AIが理解し活用できる形」での資産化が重要になってくると思います。</p> <ul> <li><strong>構造化されたデザイン言語</strong>: ブランドアイデンティティやデザイン原則をAIが解釈可能な形で体系化</li> <li><strong>AI-Friendlyなデザインシステム</strong>: コンポーネントやパターンをMCP(Model Context Protocol)等を通じてAIが参照・活用できる状態に整備</li> <li><strong>ドメイン特化の知識ベース</strong>: 特有のUXパターンやユーザー行動をAIが理解できる形で蓄積</li> </ul> <p>これらの資産は、外部サービスだけでは実現困難な「メルカリらしさ」を保ちながらAI活用を進めるための基盤となります。仮に将来的に外部サービスを利用することになったとしても、これらの構造化された資産は他のAIツールとの連携や、独自のワークフロー構築において価値を持ち続けると想定しています。<br /> 一方、基盤モデルを利用するだけで高精度のUI生成が可能になった場合、外部サービスを利用するのではなく、メルカリ内部のシステムやワークフローに接続しやすいようにあえて「基盤モデルのAPIを使った内製のUI生成システム」に着地する可能性もありえるかもしれません。</p> <h2>UI生成の実装の概要</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/d613b138-aurora-demo.png" alt="Aurora Agent Demo" /></p> <p>いくつかのコンセプトから構成されていますが、ここでは2つのAgentを紹介します。Agentic Workflowの実装は現時点では、GoogleのADK(Agent Development Kit)を使っています。</p> <ul> <li>コンセプトリファインAgent</li> <li>UI生成Agent</li> </ul> <h3>コンセプトリファイン</h3> <p>UI生成ツールのユーザーが記述した機能やサービスのアイデアを、UI生成Agentに入力するためのデータ(プロンプト)に変換するための機能です。基盤モデルの性能向上によって簡単な指示でもある程度の品質のUIを生成してくれるようになりつつあります。一方で、実現したいことや作りたいもの、制約等をよく言語化し、構造化することの価値は変わらず高く、生成されるものの品質を大きく左右すると感じています。</p> <p>以下は実装のイメージです。</p> <pre><code class="language-python">def create_concept_refinement_agent() -&gt; LlmAgent: &quot;&quot;&quot; ユーザーのコンセプト案をブラッシュアップするためのシンプルなLlmAgentを作成します。 &quot;&quot;&quot; return LlmAgent( name=&quot;ConceptRefinementAgent&quot;, model=&quot;gemini-2.5-pro-preview-05-06&quot;, description=&quot;ユーザーのデザインコンセプト案を受け取り、より詳細で具体的なアイデアにブラッシュアップするエージェント。&quot;, instruction=( &quot;あなたはデザインコンセプトを具体化・洗練させるAIアシスタントです。\n&quot; &quot;ユーザーから与えられたコンセプト案を読み、**それを元に最大限具体的で魅力的なコンセプト案を生成してください。**\n&quot; &quot;生成する際は、以下の点を考慮・推測し、具体的に記述してください:\n&quot; &quot;- **ターゲットユーザー:** (例: 20代後半のテクノロジーに関心のある都市部在住者)\n&quot; &quot;- **解決する課題/提供価値:** (例: 煩雑なスケジュール調整をAIで自動化し、自由な時間を創出する)\n&quot; &quot;- **コア機能/体験:** (例: 自然言語でのイベント登録、参加者の都合の良い時間を自動提案、ビデオ会議連携)\n&quot; &quot;- **差別化要因:** (例: 業界特化のテンプレート、独自のレコメンデーションエンジン)\n&quot; &quot;- **雰囲気・トンマナ:** (例: ミニマルで洗練されたデザイン、直感的でスムーズな操作感)\n&quot; &quot;- **具体的なUI構造:** (例: イベント登録するためのフォーム、参加者の都合の良い時間を提案するカレンダー)\n&quot; &quot;**ユーザーに質問を返さないでください。\n&quot; &quot;**与えられた情報からコンセプト案を構築し、完成したコンセプト案のテキストのみを返してください。\n&quot; &quot;**前置きや挨拶、説明は不要です。&quot; ), tools=[], sub_agents=[], )</code></pre> <h3>UI生成</h3> <p>以下がプロンプトをもとにHTMLでUIを生成するAgentのサンプルコードです。 既出のセクションにも書きましたがUI生成はさまざまなサービスが生まれており、群雄割拠です。前述の通り最終的に内製のAI Agentをさらに作り込んでいくかどうかはわかりません。一方、生成ロジック以外の要素、例えば自社のデザインシステムをAIからReadableな状態にすること、デザインのアイデンティティやコンセプトを言語化・構造化すること、AIを前提としたアプリ制作のワークフローを発明すること等は、組織の資産であり、独自性であり、差別化要因になりうる点だと想像しています。</p> <pre><code class="language-python">def create_page_generate_agent_v0_3(model_name: str) -&gt; LlmAgent: &quot;&quot;&quot;HTML生成エージェントを構築します。&quot;&quot;&quot; mcp_toolset = get_ds_mcp_tools() html_generation_agent = LlmAgent( model=model_name, instruction=&quot;&quot;&quot; ユーザーのアイデアを基にデザインを新規生成してください。 特に指示されない限りはスマホアプリのデザインを生成してください。 メルカリのデザインシステムに厳密に従った高品質なHTMLを生成するため、以下のステップを順守してください。 **ステップ1: デザインシステム参照画像の確認** メルカリのデザインシステムに準拠した表現を行うため、関連するコンポーネントやパターンの参照画像を必ず確認してください。 - `mcp_get_ds_master_image_map` ツールを呼び出し、利用可能なDS Master Imageのカテゴリと画像のリストを取得します。 - ユーザーの要求や生成する画面のコンテキストに合致する画像があれば、その画像の `category` と `filename` を引数として `mcp_get_ds_master_image_details` ツールを呼び出し、詳細な説明と画像URLを取得します。 - 取得した画像URLは、必ず `download_and_save_image_as_artifact` ツールを使用してその内容をシステムに読み込ませ、実際に画像を確認してください。 - これらの参照画像を十分に確認し、スタイル、レイアウト、インタラクションなどがメルカリのデザインシステムに沿っているかを確認した上で、HTML生成に活かしてください。 **ステップ2: アイコン・ロゴ素材の利用** アプリケーションで使用する汎用的なアイコンやロゴは、必ず `mcp_get_image_asset_map` ツールを使用して画像アセットのURLリストを取得し、そこから適切なものを選択してHTMLに埋め込んでください。 **ステップ3: HTML生成と出力** 上記のステップで得られた情報と、以下の「基本デザイン情報」を総合的に判断し、HTMLを生成します。 - **出力形式:** 純粋なHTMLコードのみを出力し、他の説明やコードブロックは含めない。完全な HTML コードのみを出力してください。説明や ```html ... ``` は不要です。 - **単一ファイル:** CSS/JavaScriptは外部参照せず、`&lt;style&gt;`タグや`&lt;script&gt;`タグで内部に埋め込む。 プレースホルダー画像 ユーザーアイコンや商品画像のプレースホルダーが必要な場合は、`get_random_sample_image_url` ツールを使ってください。 **画像のURLをmcpやツールで取得した場合:** 1. mcpやツールから取得したURLの画像を処理する必要がある場合 (例: 画像の内容を説明する、画像から情報を抽出する、画像に基づいて何かを生成する)、まず `download_and_save_image_as_artifact` ツールを使用して画像を取得し、システムに保存してください。 2. このツールを実行すると、システムが自動的に画像を読み込み、あなたがその内容を理解できるようになります。 3. もし画像URLへのアクセスにMCPの認証が必要だと判断される場合は、ツールの `use_mcp_auth` 引数を `True` に設定してください。それ以外の場合は `False` に設定してください。 &quot;&quot;&quot;, name=&quot;HtmlGeneratorAgent&quot;, description=&quot;Generates a single HTML page based on user idea.&quot;, output_key=&quot;generated_html&quot;, tools=[ get_random_sample_image_url, check_link_status, download_and_save_image_as_artifact, mcp_toolset, ], before_model_callback=before_model_load_artifact, ) return html_generation_agent</code></pre> <h2>UI/UX評価の取り組み</h2> <p>以上はUIを生成する仕組みの一端ですが、並行して、生成されたものをUI/UXの観点で評価する仕組みの構築にも取り組みはじめています。品質評価観点を定め、仮想的なペルソナを生成し、自動生成されたUIにフィードバックをします。<br /> 静的な画面の評価に加えて、Browser Use等を用いて画面操作をさせつつ、フィードバックを獲得していく仕組みです。全体のイメージとしては、Amazon社の「<a href="https://arxiv.org/abs/2502.12561" title="UXAgent: An LLM Agent-Based Usability Testing Framework for Web Design">UXAgent: An LLM Agent-Based Usability Testing Framework for Web Design</a>」のような構成で、AIによるUX評価の概念実証に着手しており、納得性の高いフィードバックを得られるケースも確認できています。<br /> UI生成については内製のフロントエンドがあるのですが、UI/UX評価に関してはCursor等のエージェントを使いながら小さく開始しています。有用性が確認できたら、どういう実装であるべきか、アプリ開発のワークフローの中でどう使っていくのが有効かを検討していきたいと考えています。</p> <h2>おわりに</h2> <p>PJ-Auroraの取り組みを通じて、生成AIの可能性と課題の両面を実感しています。技術の進歩は目覚ましく、UI生成の精度は日々向上していますが、同時に「何を作るか」「どう作るか」「私たちの仕事の仕方はどう変わるか」という本質的な問いに向き合うことがより重要になってきていると感じます。<br /> メルカリでは引き続き、お客さまにより良いサービスと体験を提供するために、新しい技術と向き合いながら開発をしています。生成AIという新しい道具を使いこなすために、私たち自身も学び続けていきます。</p> AI時代の組織変革:エンジニアリングマネージャーが見たメルカリグループの半年間の軌跡https://engineering.mercari.com/blog/entry/20250625-acfc60bbea/https://engineering.mercari.com/blog/entry/20250625-acfc60bbea/<p>こんにちは。メルペイ Payment &amp; Customer Platform Manager of Managers の@abcdefujiです。 この記事は、Merpay &amp; Mercoin Tech [&hellip;]</p> Thu, 26 Jun 2025 10:00:31 GMT<p>こんにちは。メルペイ Payment &amp; Customer Platform Manager of Managers の<a href="https://twitter.com/_abcdefuji">@abcdefuji</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の19日目の記事です。</p> <h2>要旨</h2> <p>2024年末から2025年6月の半年間で、メルカリではAIツールの導入において劇的な変化を遂げました。数十名から始まったパイロットプロジェクトが、わずか4ヶ月で1,100アカウントを超える全社規模の導入に成功しました。エンジニアの9割以上がAIコーディングアシストを活用する組織へと変貌しています。</p> <p>この記事では、エンジニアリングマネージャーの視点から、組織変革の成功要因を分析し、技術負債解消への新しいアプローチや個人の開発体験の変化について紹介します。特に、トップダウンのビジョン、ボトムアップの自発性、環境整備、そして可視化による推進力の4つの要素がどのように組み合わさって変革を実現したかを詳しく解説します。</p> <h2>始まりは一つのリクエストから</h2> <h3>2024年末の状況</h3> <p>全ての始まりは、同僚からの「Cursorを使ってみたいのですが、導入可能でしょうか?」というシンプルなリクエストでした。</p> <p>当時のメルカリには、既にAI Code AssistsツールとしてGitHub Copilotが導入されていましたが、実際の利用状況は以下の通りでした:</p> <ul> <li>一部のアーリーアダプターである数十名〜100名(weekly active user)規模で利用されている</li> <li>しかし、多くのエンジニアが「まだ使うには早い」と感じている状況</li> <li>高度なコンテキスト理解が不十分で、単一ファイルや行単位の修正提案が主流</li> <li>プロジェクト全体を理解した提案には至らず、AIアウトプットの質がプロダクション開発に適応できていない状況</li> </ul> <p>正直、私自身もGithub Copilotを使っていましたが、開発の現場で本格的にAIを活用するレベルには程遠いと感じていました。</p> <h3>転換点:2025年2月の承認</h3> <p>Cursorは2024/04〜07頃に一度検討されましたが、当時は導入の判断には至りませんでした。<br /> しかし、2025年2月、会社として本格的なAI導入の承認が下りました。数十名のパイロットプロジェクトとしてCursor導入がスタートしました。</p> <p>初めてCursorを触った時の感動は今でも鮮明に覚えています。特に印象的だったのは、Cursorの「コードのインデックス化」機能でした:</p> <ul> <li>プロジェクト全体のコンテキスト理解</li> <li>既存のパターンを学習した文脈に沿った提案</li> <li>大規模リポジトリでのコード理解スピードの向上</li> </ul> <p>まだ数万行を超える巨大なリポジトリでは不安定さや上手く機能しない場面もありましたが、主にオンボーディングの側面でスピードが爆速になりました。マネージャーである自分はコードへのコミット機会が減りつつありましたが、Cursorを使ってどの機能がどのように実装されているか非常に容易に特定できるようになりました。</p> <h2>爆発的普及の4ヶ月間</h2> <h3>驚異的な成長</h3> <p>2月から6月現在までの数字を見ると、その変化の大きさが分かります:</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/11856ff3--2025-06-25-15.30.48-1024x121.png" alt="" /><br /> (図: Cursor Usage Summary)</p> <ul> <li><strong>アカウント数</strong>:数十名 → 1,100アカウント超</li> <li><strong>エンジニア利用率</strong>:9割以上が何らかのAIコーディングアシストを活用(Cursor以外にもJetBrains AI、Google Code Assist、Claude Codeなど)</li> <li><strong>普及範囲</strong>:エンジニアからPdM、デザイナーまで拡大</li> </ul> <h3>社内の熱量の変化</h3> <p>数字以上に驚いたのは「熱量」でした。3月から5月の間、社内で起こっていたことは以下の通りです:</p> <ul> <li>毎週複数のAI関連イベントが社内で開催</li> <li>一つのイベントに100名以上の参加者がいるケースも</li> <li>エンジニアだけではなく、PdMなどの別職種への広がり</li> </ul> <p>社内のオフライン、Slack上で聞こえる会話がAI一色に染まっていきました。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/53cf1200--2025-06-23-16.30.52-1024x804.png" alt="" /></p> <p>(図: 社内AI開発支援ツールについて語ろう企画)</p> <p>「隣のチームでCursorの勉強会やるらしい」<br /> 「今度MCPについて話すイベントやります」<br /> 「AIで○○を楽にしました」</p> <p>エンジニアから始まった波は、さまざまな職種の人たちにも広がっていきました。マネージャーとして、この自発的な学習意欲の高まりを目の当たりにできたのは本当に感動的でした。</p> <h2>成功の4つの要因</h2> <p>この爆発的な普及を振り返ると、4つの要因が絶妙に組み合わさっていたことが分かります。</p> <h3>1. トップダウンの明確な意志</h3> <p>経営層からの「AIを活用していく」というメッセージは、単なる推奨ではありませんでした。会社の未来への投資であり、明確なビジョンの表明でした。トップが本気だからこそ、現場も本気になれる。その土台があったからこそ、後に続く変化が可能になったのです。</p> <p>そしてそれを推進するリーダーがいました。私はCursor導入を担当しましたが、Cursorだけではなく、エンジニア職種を超えて生成AIを導入する非常に強いリーダーシップが社内にモメンタムを生み出しました。</p> <h3>2. ボトムアップの自発的な情熱</h3> <p>トップダウンが生み出したモメンタムをさらに加速・継続させたのは「Move Fastな人たち」の存在でした。通常、組織変革では推進役を各チームに配置し、計画を立て、ロードマップを作成しますが、今回のCursorに関しては以下のような声が、あちこちから自然発生的に生まれました:</p> <p>例として</p> <ul> <li> <p><a href="https://engineering.mercari.com/blog/entry/20250613-64e628404a/">メルカリモバイルのAI Hackathon</a></p> </li> <li> <p><a href="https://engineering.mercari.com/blog/entry/20250604-pcp-llm-week/">PCP LLM Week</a></p> </li> </ul> <p>アーリーアダプターがいる。挑戦する文化がある。そして何より、学んだことを共有したがる人たちがいたことにより推進が大きく加速しました。</p> <h3>3. 環境の整備</h3> <p>このモメンタムの維持には環境整備を支えてくれたチームも大きな要因です。CursorだけではなくさまざまなAIツールが登場し、社内で利用したい声が多く上がりました。Cursorもここまでの人数規模が利用できる状態にするために社内のプロセス整備が行われました。ワンボタンでアカウント申請できる仕組みや、新規ツール導入時のセキュリティレビュー・予算レビューなどのさまざまなプロセスを、一体となって環境整備をしてくれました。</p> <p>Cursorにおいては、Slackから簡単にアカウント発行までできるように調整していただきました。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/435d6e92--2025-06-25-15.18.35-1024x235.png" alt="" /><br /> (図: Cursorアカウント発行アナウンスメッセージ)</p> <h3>4. 可視化という触媒</h3> <p>最後の要素は、「ダッシュボード」による可視化です。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/9c56c906--2025-06-23-23.39.29-1024x583.png" alt="" /><br /> (図: Cursor Dashboardn)</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/3b360113--2025-06-23-23.50.31-1024x212.png" alt="" /><br /> (図: Devin Dashboard)</p> <p>CursorをはじめDevin、Claude Code with LiteLLM、GitHub Copilotのダッシュボードを用意し、チームがどのくらいAIを使っているかを可視化しました。これにより、使いこなしているチームとまだ利用頻度が高くないチームを把握し、それぞれの背景を深掘りしていくことで、さらなる浸透のためのアクション定義につなげました。</p> <p>可視化による競争ではなく、「触発」が生まれました。同僚の活用方法を見て学び、自分なりの使い方を発見する。そんなポジティブなサイクルが会社全体に広がりました。</p> <h2>チームレベルの開発における変革</h2> <p>ここからは組織ではなく1チームの開発状況の変化に関して話します。</p> <h3>技術負債への新しいアプローチ</h3> <p>AIの導入が進む中で、私たちのチームに予想外の変化が起こりました。</p> <p>私たちPayment &amp; Customer Platformチームは、リリースから6年以上が経つシステムです。6年間、さまざまなプロダクトチームからの要求に応え、機能を追加し、時には妥協しながら成長を続けてきました。その結果、技術負債が蓄積されていました:</p> <p><strong>従来の課題:</strong></p> <ul> <li>yak shaving状態:積み重なった実装・複雑な仕様により、一つの小さな改善に大きなコストが伴う課題</li> <li>改善系のタスクの優先度が低く、時間が確保できず放置された課題</li> </ul> <p><strong>AI(Cursor / Claude Code)による変革:</strong></p> <ul> <li>大規模な内部リファクタリング・リアーキテクチャのような優先度が低く設定されやすいタスクの解消スピードが向上</li> <li>ルール・コンテキストの共有による一貫性のある修正が可能</li> </ul> <p>これまで「いつかやろう」で終わってしまいがちだった大規模な改修プロジェクトに、以前よりも継続的かつ素早く技術負債解消できる可能性が出てきたと感じています。</p> <h2>今後への展望</h2> <ol> <li><strong>さらなる活用領域の拡大</strong> &#8211; 開発だけではなく、開発プロセス全体でAIを活用していく仕組み作り(<a href="https://engineering.mercari.com/blog/entry/20250611-one-person-one-release/">One Person, One Release</a>)</li> </ol> <blockquote> <p>PMもEngineerも壁を越えていきます。一人の人が企画から開発、QA、リリースまで一気通貫で出来ることを目指します。<br /> 技術の壁を越え、ドメイン知識を越え、役割を越えて行くためのAIの活用とし、それらを使い熟すのです。</p> </blockquote> <ol start="2"> <li><strong>標準化</strong> &#8211; 個人やチームの知見や開発手法の横展開していく仕組みづくり</li> </ol> <p>例えば、CLAUDE.mdをどのように作成し、どのようなルールを記載しているか、どのようにドメインを表現しているか、チームによって独自に進化が進んでいます。それぞれのチームがAIによってさらなる生産性を得るために、導入や利用のプロセス自体の標準化を行いたいと考えています。</p> <p>このAIトレンドのスピードの中で、さまざまな手法が即座に古くなっていくと思います。古くなったものを即座に捨て去る覚悟を持ちつつ、AIを活用し生産性を向上させていく未来への道のりを作り始めています。</p> <h2>まとめ</h2> <p>この半年間で、メルカリはAIツールの導入において以下を実現しました:</p> <ul> <li><strong>組織的な成功</strong>:1,100アカウントを超えるユーザーへのCursor導入</li> <li><strong>文化的な成功</strong>:自発的な学習・普及文化の醸成</li> </ul> <p>成功の鍵は、トップダウンのビジョン、ボトムアップの自発性、環境整備、そして可視化による推進力の組み合わせにありました。AI時代の組織変革において、技術導入だけでなく、文化と人の変革が重要であることを改めて実感しています。</p> <h2>最後に</h2> <p>この目まぐるしい変化に楽しく向き合えているのは、周りの同僚たちの存在です。</p> <p>毎日のように新しいツール、開発体制、ユースケースなどのインプットとアウトプットする機会に溢れており、AI関連の情報交換は純粋に楽しく刺激的でした。この「集合知」でAIに対して前向きに挑めています。</p> <p>以上、ありがとうございました。</p> <p>明日の記事は cyanさんです。引き続きお楽しみください。</p> メルカリの Design System をリニューアルしましたhttps://engineering.mercari.com/blog/entry/20250624-the-story-behind-mercari-design-system-rebuild/https://engineering.mercari.com/blog/entry/20250624-the-story-behind-mercari-design-system-rebuild/<p>Design System チームの engineering manager をしている vwxyutarooo です。 私達はメルカリのアプリ・ウェブ開発に利用している Design System をフルリニューアルし [&hellip;]</p> Wed, 25 Jun 2025 12:00:12 GMT<p>Design System チームの engineering manager をしている vwxyutarooo です。<br /> 私達はメルカリのアプリ・ウェブ開発に利用している Design System をフルリニューアルしました。<br /> この記事で Design System に抱えていた問題とそれをどのように解決しようとしているのか、そのコンセプトを紹介していきます。</p> <h2>既存の Design System に抱えていた課題</h2> <p>既存の Design System は社内で 3.0 と呼ばれており、<a href="https://engineering.mercari.com/blog/entry/20221213-ground-up-app/">GroundUp</a> と呼ばれるメルカリのアプリとウェブを刷新するプロジェクトの一部として2020年頃からデザイン・開発が始まりました。<br /> 3.0 と聞くと随分進んでいるように見えますが、様々な開発背景により特定プラットフォームを対象にしたものや、日の目を浴びることのなかった過去のバージョンなどが含まれており、実質 3.0 が全社的に取り組んで開発された最初の Design System v1 となっている背景があります。</p> <p>おおよそ 5 年の運用期間を経て、3.0 で作られたコンポーネントは当初の利用想定ケースを大きく超える状況に対処する場面が多く見られるようになりました。その結果、多数の新規機能開発で Design System のコンポーネントでは表現できず、シンボルをディタッチして変更を加えたコンポーネントや社内でカスタムコンポーネントと呼んでいる非公式のコンポーネントが多数作成される事態に陥っていました。</p> <p>なぜこのようなことが起こったかを、ItemObject と呼ばれているコンポーネントの例を用いながら簡単に解説します。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/c5beb314-transition.png" alt="" /></p> <p>これは複数のスクリーンで頻繁に使用されるコンポーネントです。3.0開発時は共通と思われるパーツだったため単一のコンポーネントとして切り出され、いくつかのユニークな要素をプロパティによって表示・非表示を切り替えることで対応していました。社内ではこれを polymorphic API と呼んでいます。</p> <p>しかし 3.0 リリース後の継続した機能開発により必要な要素は増え続け、必要とされる表示パターンは増え続けました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/138817e2-frame-2607.png" alt="" /></p> <p>この方式の難しいところは個別の UI 最適が進むほど考慮すべき組み合わせパターンが倍に増えていく点です。さらに、特定の要素 A が表示されているときに出現する要素 B or C のように構造が深くなっていき、複雑さが増していきます。私達はこの構造をコンポーネントの Polymorphic API と定義し避けるべきコンポーネントデザインと考えています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/47827abb-frame-2602.png" alt="" /></p> <p>この状況を打開するため、コンポーネントの定義を刷新し異なるコンセプトで Design System を4.0として再構築することにしました。</p> <h2>Atomic Design Methodology</h2> <p>新しいコンポーネントの設計指針として Atomic Design を採用することにしました。古くから存在した概念で、2013年に Brad Frost によってそのアイディアが初めて提唱されたものです。<br /> <a href="https://bradfrost.com/blog/post/atomic-web-design/">Atomic Design &#8211; Brad Frost</a></p> <p>Atomic Design は旬をすぎたものとして扱われるようになって久しいですが、これは多くの場面で誤解のもとに運用されたり、拡大解釈されたりすることで、本来意図していない利用をされていることが大きいと考えています。<br /> <a href="https://www.youtube.com/watch?v=PK_PICNTgAg">Brad Frost: Is Atomic Design Dead? – Hatch Conference Berlin 2023</a></p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/df433f33-screenshot-2025-06-22-at-16.22.57.png" alt="" /></p> <p>よくある誤解として Atomic Design を実装リードで適用しようとしてしまう、或いは実装でのみ実現しようとしてしまう例がよく見られます。<br /> 私達の解釈では、Atomic Design は Design System を開発・運用するデザイナーとエンジニアのためのコンポーネント設計フレームワークであり共通言語です。実装が Atomic Design を強く意識する必要はなく、利用者に強調すべき情報でもありません。</p> <p>Atomic Designでは、UIの部品を最小単位の「atoms(原子)」に分解し、それらを組み合わせて「molecule(分子)」のようなより大きな部品を構成します。以前は一つの部品として扱っていたものを、複数の小さな部品に分割して組み立て直す考え方です。<br /> Atomic design によるコンポーネントの分解・設計手法に関しては Brad Frost 本人を含む多くの解説記事や動画が存在するため詳細は省略しつつ、先程紹介した Item Object を 4.0 の考え方で構築した例で簡単に紹介します。</p> <p>まずセオリー通り各コンポーネントをその役割の最小単位にまで分割していきます。<br /> 以下の画像の例も、3.0 では1つのコンポーネントとして扱われていましたが、4.0 は 2 つの atoms と呼ばれる最小単位のコンポーネントになります。そしてこれらのパーツを組み合わせてさらにパーツを構成します。この atoms から構成されるコンポーネントを molecule と呼びます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/c1247611-frame-2610.png" alt="" /></p> <p>これを繰り返し、最終的にバラバラのパーツから ItemObject などのよりハイレベルなパーツを構築可能にします。前提として UI をパーツで組み立て可能にするという点を念頭に置きつつ、組み立て後のパーツが汎用的なコンポーネントであるものを molecules や organisms として提供します。</p> <p>ItemObject のようにユースケースが細かく別れているコンポーネントに関しては使用頻度の高い汎用的なものを優先的に Design System のコンポーネントとして管理しつつ、利用シーンが多くないものや僅かな要素の違いを持つユースケースにはあえて organisms として完成形を提供せず、利用シーンで組み立てるようにしています。<br /> コンポーネントを利用時に組み立てる、というのも場合によっては利用者の負担になります。そのため、組み立て方法の例をレシピ/設計図として配布し補助的に活用しています。</p> <p>レシピ/設計図を提供するかどうかは、コンポーネントの利用頻度やコンテンツ/コンテキスト依存度から判断しますが、レシピや設計図 (Blueprint) に関しては Atomic Design とは異なる概念となるため次の節でもう少し詳しく紹介します。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/7847379a-frame-2609.png" alt="" /></p> <h2>Component Design Strategy</h2> <p>Atomic design は Design System のコンポーネントの分解・構築のためのフレームワークを提供しますが、なにがコンポーネントであるべきか、どんなコンポーネントが Design System として管理されるべきかその境界を示してはいません。</p> <p>私のチームでは Design System から内向きのレイヤーを Atomic Design で、外向きのレイヤーを自分たちで独自に設計しました。次の図はそのレイヤーを簡易的に表現したものです。内側に行くほど Design System で、外側に行くほど Design System ではなくなります。厳密に Design System チームの持ち物として責任を追うのは青の領域ですが、現実的にはっきりとした境界線を引けることは稀で、その境界はグラデーションになっていることが多いため、そのグラデーションを意図してこのような図で表現しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/61e66f52-frame-2627.png" alt="" /></p> <p>1つ1つのレイヤーを順番に解説していきます。</p> <p><strong>Snowflakes</strong></p> <ul> <li>ワンオフコンポーネント。コンテンツやコンテキストに依存しているなどの理由から Design System としては考慮されない</li> <li>控えめな使用を推奨<br /> <strong>Custom Component</strong></li> <li>Design System のコンポーネントスペックでは表現できない UI を構成するため、シンボルからディタッチされたり、stroke など Figma 上で制約を設けることができないプロパティをコンポーネントのスペックを超えて改造されたものを指す</li> <li>Design System としては非常に不本意なコンポーネントであるため将来的にそのスペックが Design System でサポートされるか、或いは UI の仕様を調整することで薄くなっていくべきレイヤー<br /> <strong>Blueprint</strong></li> <li>直訳すると青写真という意味になりますが、設計図や完成予想図の意味で使用される</li> <li>Blueprint は、Figma のデザインデータから iOS, Android, Web のソースコードまで包括的にその設計図が提供される</li> <li>主に Design System Component とするにはコンテンツ/コンテキスト依存が強いが頻繁に活用されるもの、或いは snowflakes のようなワンオフに近い用途を持つが、その組み立て方法が複雑なときに活用する<br /> <strong>Design Recipes</strong></li> <li>Figma のデザインファイルでのみ設計図が提供されるコンポーネント。ソースコード上では提供されない</li> <li>フレームワークの恩恵を受けるなど実装上コンポーネントとして定義する必要性が低いものに対し、デザイン効率化のため Figma のデザインファイルでのみコンポーネントとして利用 (レイアウト系のコンポーネントに多い)</li> <li>Blueprint がデザイン (Figma) とソースコード両方のレシピを提供するのに対し、Design Component はデザイン (Figma) のレシピのみが提供される<br /> <strong>Design System</strong></li> <li>コンテンツ/コンテキスト非依存で再利用可能な独立したコンポーネント</li> </ul> <p>実はこれらのレイヤーは Brad Frost により提唱されている vocabulary に深く影響を受けているため、彼に詳しい人にとっては既視感のあるものになっています。<br /> ただこれらには Atomic Design のような明示的な名前はついていないため、単に記事中の表現から component vocabulary と呼ぶことにします。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/5bc44e43-screen-shot-2021-02-03-at-10.48.35-am-1024x833-1.png" alt="" /><br /> <a href="https://bradfrost.com/blog/post/design-system-components-recipes-and-snowflakes/">Design system components, recipes, and snowflakes</a></p> <p>すべての UI コンポーネントが Design System で完結するデザイン組織が最も strict なデザイン組織と言えるかもしれません。実現は難しいですが、そのような組織も少なからず存在しているようです。</p> <p>このモデルは、もう少し合理的な妥協ラインを求めた場合にとてもフィットします。プロダクト開発でどうしても発生するコンテンツ依存なコンポーネントをワンオフとして一定数許容しつつ、そこにボキャブラリーとレイヤーを与えることで管理対象とし、薄く維持するためのマインドセットを生み出すことができます。そして、Design System と Snowflakes の間を埋める再利用可能だが Design System として管理するには十分な動機が (まだ) ないものをレシピとすることで、全体のコンポーネントレイヤーにグラデーションを与え、メンテナンスコストとリターンの最適化を図る意図があります。</p> <h2>コンポーネント設計・分割指針</h2> <p>次に Design System コンポーネントの設計・分割指針を見ていきます。冒頭で紹介した通り、以前のシステムでは最終的に1つのコンポーネントに振る舞いや variant を持たせ過ぎたことで利便性やメンテナンス性の低下を招きました。<br /> これらの教訓を踏まえ、新しいシステムではセマンティックでシンプルな分解を重視し、以下の4つをコンポーネント設計の指針としました。</p> <p><strong>Semantic</strong><br /> “ビジュアル的に近いものをコンポーネントとするのではなく、挙動や意味的な分類によってコンポーネントを定義/分割するし常に一貫した振る舞いを提供します。”</p> <p>例としてメルカリにはチップと呼ばれているラウンド状のクリッカブルなコンポーネントがあります。<br /> 3.0 では全て1つのコンポーネントとして定義されていましたが、以下のようによく似た見た目を持つコンポーネントに対して大きく異なる振る舞いをすることが分かります。</p> <ul> <li>トグル: タップする事にステートの変化</li> <li>リムーバブル: タップすると消える</li> <li>文字入力: タップが別のアクションのトリガーとなる</li> </ul> <p>一見、共通コンポーネントの異なる状態を利用しているだけに見えますが、タップ可能領域やタップ時、およびホバー時 (Web) のスタイルなども違ってきます。1つのコンポーネントで表現するには不要な依存関係を考慮する必要が出てくるため、コンポーネントの分割対象とすることで依存関係がシンプルになりメンテナンス性が向上します。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/412fe869-frame-2616.png" alt="" /></p> <p><strong>Properties</strong><br /> “異なる色、角の丸みや角ばっているなどに基づいてわずかな視覚的バリエーションを持つことができます。但しコンポーネントの形や振る舞いを変えることはできません。”</p> <p>先に紹介したチップコンポーネントでは、ストロークのスタイルを solid/dotted のようなプロパティを持たせています。これは視覚的なバリエーションであり形や振る舞いを変えることはないため、1つ目の Semantic 指針を侵害しません。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/2b9f717c-frame-2617.png" alt="" /></p> <p><strong>Optional Elements</strong><br /> “コンポーネントはオプショナルな要素を持つことができる (オプションのアイコンやテキストなど)”</p> <p>ボタンの prefix/suffix アイコンのような子要素を持たせることができます。<br /> 次の4つ目の指針で紹介する No polymorpihc API と相反することがないよう注意する必要があります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/995a589d-frame-2618.png" alt="" /></p> <p><strong>No polymorphic API</strong><br /> “一貫したAPIを持つべきである (必須となるプロパティが別のプロパティの存在の有無に基づいて変更されるべきではない)”</p> <p>画像とコードの例を用いて解説します。次の画像は、3.0の古い Design System で定義されていた ItemThumbnail というコンポーネントで、3.0 では Large size のみに割引や値段の要素が許可されていましたが、これは polymorphic API とみなし、新しい指針では避ける設計としています。<br /> “特定の条件の時に発生するネストされた条件”には、最終的に冒頭で紹介したような管理の複雑性を生じます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/3c5df672-frame-2620.png" alt="" /></p> <p>Polymorphic API を含むコード例:</p> <pre><code class="language-kotlin">ItemThumbnail( size = Medium ) ItemThumbnail( size = Large( discountPrice = 900¥, price = 1,000¥ ) )</code></pre> <p>4.0 ではコンポーネントの分解と再構築により、これらの問題を回避しています。ItemTile という Organism コンポーネントを用意し、構成要素として ItemThumbnail を含む Atoms, Molecules を持たせています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/267374d3-screenshot-2025-03-13-at-17.02.16.png" alt="" /></p> <p>Polymorphic API を含まないコード例:</p> <pre><code class="language-kotlin">ItemThumbnail( leftBottomContentSlot = &lt;other atoms/molecules/organism&gt; )</code></pre> <p><strong>結果</strong><br /> Atomic Design を採用した私達の Design System は、最終的に150弱の数のコンポーネントに再分解され、以下のようなコンポーネント分布の構成になりました。これが適切なのか過不足あるのかは現時点で判断することはできませんが、今後の運用で明らかになっていくはずです。<br /> Atoms: 50<br /> Molecules: 60<br /> Organisms: 40</p> <p>また、冒頭でから例として上げている ItemObject はそのレイアウトだけを提供する ObjectLayout と、パーツを組み上げる blueprint に分かれて提供する方法に着地しました。<br /> <strong>ObjectLayout:</strong><br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/3d3ca79a-screenshot-2025-06-13-at-17.51.17.png" alt="" /><br /> <strong>ItemObject (blueprint):</strong><br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/0a38c1e3-screenshot-2025-06-13-at-17.57.56.png" alt="" /></p> <p>条件分岐などで膨れ上がったコードも、iOS (Swift) で700行あったものが30行弱にまで削減されました。実際組立時に発生するコードもあるため純粋な削減とはなりませんが、コンポーネントの抽象化や汎用化に失敗していた部分が単純化できたと考えられます。</p> <h2>まとめ</h2> <p>今回の Design System 4.0 刷新プロジェクトを通じて、私達は過去の課題と向き合い、より柔軟かつ持続可能なシステムへと進化させるための重要な学びを得ました。</p> <p>コンポーネントの過度な汎用化が複雑性を生み、メンテナンス性を著しく低下させる教訓から、Atomic Design の原則に立ち返り、コンポーネントを最小単位に分割し、再利用性を高める設計へと移行しました。これにより、各コンポーネントが単一の責任を持つようになり、変更やテストが容易になりました。<br /> 同時にコンポーネントがどうあるべきかを考え直しゼロから組み直すことで 3.0 で得た知識と経験を新しいシステムに反映することができました。</p> <p>今後 Figma AI や Figma MCP をはじめとするデザイン及びコーディングの自動化において、ブランディングコンセプトを反映し、かつセマンティックな意味を持つ Design System コンポーネントはハブとしての役割や、AI に対してのコンテキスト提供者としてその重要性を増していくと考えています。</p> <p>また続報があればお伝えしていきます。<br /> 最後まで読んでいただきありがとうございました。</p> Web版メルカリにメルコインの機能を組み込む検証をした話https://engineering.mercari.com/blog/entry/20250620-31f2852f9b/https://engineering.mercari.com/blog/entry/20250620-31f2852f9b/<p>こんにちは。メルコイン フロントエンドエンジニアの@y-arimaです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の18日目の記事です。 本記事では、Web [&hellip;]</p> Wed, 25 Jun 2025 10:00:04 GMT<p>こんにちは。メルコイン フロントエンドエンジニアの@y-arimaです。</p> <p>この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の18日目の記事です。</p> <p>本記事では、Web版メルカリからメルコインAPIへの疎通確認を行ったPoC(Proof of Concept)について、技術的な課題と解決策、そして得られた知見を紹介します。</p> <h2>背景と目的</h2> <p>現在、メルコインの機能を利用できるWebサービスは存在しません。そこで、技術的な検証として、Web版メルカリからメルコインAPIにアクセスできるかどうかを試してみることにしました。</p> <p>今回のPoCでは、<strong>Web版メルカリからメルコインAPIへの疎通確認</strong>を主な目標とし、技術的な実現可能性を検証しました。</p> <h2>技術的な課題と解決策</h2> <h3>1. 認証設計の複雑さ</h3> <p>既存のWeb版メルカリの認証システムは独自のユーザーID体系を使用していましたが、メルコイン側のマイクロサービスは異なるクラスタに存在しており、セキュリティ上の理由からそのIDをそのまま受け付けない仕様となっていました。</p> <p>この問題を解決するため、プライバシーを考慮した識別子(PPID: Pairwise Pseudonymous Identifier)を利用する新たなOIDC(OpenID Connect)クライアントを作成する必要がありました。PPIDは、異なるサービス間でお客さまを安全に識別するための仕組みです。PPIDの詳細については、以下の記事をご参照ください。</p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20231023-mmtf2023-day3-3/">メルコインにおけるシステム間のデータ分離を実現するための通信アーキテクチャ</a> </li> <li><a href="https://engineering.mercari.com/en/blog/entry/20230130-applying-oauth-2-0-and-oidc-to-first-party-services/">Applying OAuth 2.0 and OIDC to first-party services</a></li> </ul> <h3>2. インフラ周りの設定</h3> <p>メルコインAPIへのアクセスを可能にするためには、Gatewayやネットワーク周りなどのインフラ設定が必要でした。普段フロントエンドエンジニアとしての業務がメインの私にとって、この辺りは馴染みの薄い領域でした。</p> <p>しかし、アーキテクトチームやSREチームなどさまざまな方にサポートいただき、必要な設定を進めることができました。この経験を通じて、マイクロサービス間の連携には多くのチームの協力が必要であり、またフロントエンドエンジニアとしても、インフラストラクチャーの知識の重要性を実感しました。</p> <h3>3. 未知のコードベースでの開発</h3> <p>今回のPoCでは、普段触れることのない複数のリポジトリでの作業が必要でした。大規模なコードベースを短期間で理解し、必要な修正を加えていく必要があり、これは大きなチャレンジでした。</p> <p>この課題に対しては、最新のAIツール(特にCursorなどのAI搭載エディタ)を積極的に活用することで対応しました。特に新たなOIDCクライアントの設定では、普段触れることのないTerraformのコードを修正する必要がありました。しかしCursorの機能を活用して既存のOIDCクライアントの設定を分析し、その構造や仕組みを理解した上で、新しいクライアントの設定を進めることで、開発効率を向上させることができました。</p> <h2>実装の成果</h2> <p>今回のPoCでは、以下の2つの機能を簡易的に実装することで、Web版メルカリからメルコインAPIへの疎通確認を行いました:</p> <ol> <li><strong>ビットコインの価格をチャートで表示する機能</strong> </li> <li><strong>取引報告書をダウンロードする機能</strong></li> </ol> <p>これらの機能実装を通じて、認証やAPI通信、ファイルのダウンロードなど、さまざまなパターンでの疎通確認を行うことができ、<strong>Web版メルカリからメルコインAPIへのアクセスは技術的に実現可能である</strong>ことを確認できました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/1b0f2646--2025-06-05-10.15.21.png" alt="" /></p> <h2>PoCを通して学んだこと</h2> <h3>一人での限界と効率的な進め方</h3> <p>PoCを進める中で、フロントエンドエンジニア一人でこのような大規模な検証を完遂することの難しさに直面しました。このような状況を受け、多くのチームの方々に協力いただきましたが、PoCという性質上、本番開発に比べて優先度が低く、各チームへの依頼が完了するまでに時間を要することも少なくありませんでした。そこで、未経験の領域にも果敢に挑戦し、「まずは自分でできることを探る」という姿勢で取り組むことで、チーム間のコミュニケーションコストを抑えつつ、効率的な開発を進めることができました。</p> <h3>AIツールの活用</h3> <p>前述した未知のコードベースの理解促進はもちろん、チャート機能や取引報告書のダウンロード機能の実装においても、AIツールは大きな効果を発揮しました。</p> <p>開発の流れとしては、以下のプロセスを繰り返しました。</p> <ol> <li>Cursorに要件を伝えてコードを生成 </li> <li>細部を自分で調整 または Cursorを活用して修正 </li> <li>動作確認 </li> <li>問題があれば2に戻る</li> </ol> <p>この方法により、極めて短期間で機能を完成させることができました。</p> <p>また、これらの機能の実装では、フロントエンドエンジニアとして普段から慣れ親しんでいる領域だったことが、AIツール活用の大きなアドバンテージになりました。<br /> 正しいコードの形が頭の中にあるため、Cursorが生成したコードの良し悪しを即座に判断でき、適切な修正指示を出すことができたのです。この既存知識とAIツールの組み合わせにより、開発スピードは格段に向上しました。</p> <h3>複数のチームとの効率的なコミュニケーション</h3> <p>メルカリグループのエンジニアリング組織は大規模であり、複数のチームから構成されています。<br /> PoCを進める上で「誰に質問すれば良いか」が最初は不明でした。この問題に対しては、メルカリ全体のアーキテクチャを横断的に把握していアーキテクトチームに最初に相談し、必要なタスクと担当チームを特定しました。その後は、複数のチームに並行して質問や相談を行うことで、開発のブロッカーを最小限に抑えながら効率的に進めることができました。</p> <h2>まとめ</h2> <p>今回のPoCでは、Web版メルカリからメルコインAPIへのアクセスが技術的に実現可能であることを確認できました。この検証を通じて、フロントエンドエンジニアとしても認証やインフラなど、システム全体への理解を深めることの重要性を改めて実感しました。</p> <p>また、AIツールの活用や効率的なチーム連携の方法など、今後のPoC開発にも活かせる知見を得ることができました。</p> <p>この記事が、同様の技術的挑戦に取り組む方々の参考になれば幸いです。</p> <p>明日の記事は @abcdefujiさんによる「<a href="https://engineering.mercari.com/blog/entry/20250625-acfc60bbea/" title="AI時代の組織変革:エンジニアリングマネージャーが見たメルカリグループの半年間の軌跡">AI時代の組織変革:エンジニアリングマネージャーが見たメルカリグループの半年間の軌跡</a>」です。引き続きお楽しみください。</p> メルカリグループの全社的なDevEx改善の仕組みづくりhttps://engineering.mercari.com/blog/entry/20250624-building-a-company-wide-framework-for-improving-devex-in-mercari-group/https://engineering.mercari.com/blog/entry/20250624-building-a-company-wide-framework-for-improving-devex-in-mercari-group/<p>Merpay &amp; Mercoin Tech Openness Month 2025の第17回目のブログ投稿です。 ntk1000 です。MerpayでKYCチームとPartner Platformチームのエンジニ [&hellip;]</p> Tue, 24 Jun 2025 10:00:18 GMT<p><a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/" title="Merpay &amp; Mercoin Tech Openness Month 2025">Merpay &amp; Mercoin Tech Openness Month 2025</a>の第17回目のブログ投稿です。</p> <p><a href="https://x.com/ntk1000" title="ntk1000">ntk1000</a> です。MerpayでKYCチームとPartner Platformチームのエンジニアリングマネージャーを務めています。本日は特定のチームについて話すのではなく、開発者体験(Developer Experience)を向上させるための会社全体のエンジニアリングOKRイニシアチブについて共有したいと思います。</p> <h2>1. なぜDevExなのか?</h2> <p>Developer Experience(以下、DevEx)は、開発者が仕事においてどれだけスムーズに、ストレスなく、価値ある仕事に集中できるかを示す概念です。</p> <p>Nicole Forsgrenらが提唱した研究では、&quot;良いDevExは、開発者の満足度と効率性を高め、生産性と定着率を向上させることで、ビジネス成果にもつながる&quot; とされています(参考: <a href="https://queue.acm.org/detail.cfm?id=3454124" title="The SPACE of Developer Productivity">The SPACE of Developer Productivity</a>)。</p> <p>また、Googleも、&quot;開発者が実際にどれだけの時間を本質的な価値創出に費やせているか&quot; を重視しており、DevExの改善をプロダクトの品質とスピード向上の重要な要素として扱っています(参考:<a href="https://getdx.com/blog/how-google-measures-developer-productivity/" title="How Google Measures Developer Productivity">How Google Measures Developer Productivity</a>)。</p> <p>このように、DevExは単なる開発効率の指標ではなく、チームの健全性とプロダクトの競争力に直結する、戦略的なテーマです。</p> <p>AIの台頭、事業の多角化、グローバル展開など、エンジニアリング組織の複雑性が増す中で、エンジニアの日々の業務には、集中時間の確保や自律的な判断の難しさといった新たな課題が生まれています。複雑性が高まるにつれて、個人の努力や善意だけでは対応しきれない構造的な摩擦が目立つようになってきているのです。</p> <p>たとえば、私が担当しているKYCおよびPartner Platformチームは、社内の他チームやプロダクトに必要な共通機能を提供するプラットフォームとしての役割を担っています。そのため、私たちは多様化・グローバル化するサービスの要求に応える開発と、自チームのプロダクト自体の改善を並行して行う必要があります。しかし現実には、前者への対応に時間とリソースの大半を割かれてしまい、後者の改善が後回しになり、結果として前者の対応にも時間がかかってしまうというジレンマが存在しています。これは構造的な負債であり、個人やチーム単体の努力だけで解決できる問題ではありません。</p> <p>だからこそ、私たちはDevExを単なる業務効率やスコア改善の話ではなく、開発チームの持続可能性とプロダクトの競争力を両立させるための戦略的な取り組みと位置づけました。複雑な環境の中で、自律的に動けるチームを育てるには、構造的な課題に対して全社的に向き合う必要があります。そのため、私たちはEMや開発チームだけにその責任を任せるのではなく、組織全体でDevEx改善に取り組む体系的なアプローチを選択しました。</p> <h2>2. 測るのは、行動と対話の出発点</h2> <p>私たちは<a href="https://getdx.com/" title="DX">DX</a>という、サーベイベースの定性データとデリバリースループットのような定量データを組み合わせたDevEx可視化ツールを採用しました。目的はスコアを生成することではなく、チームが自分たちの働き方を客観的に見つめ直し、課題を言語化し、改善に向けた行動を起こすきっかけを生み出すことです。定量と定性を合わせて可視化することで、エンジニアやEMが感覚的に持っていた課題認識をチーム全体で共有できるようになり、そこから建設的な会話が始まります。</p> <p>「測って終わり」にしないために、私たちは四半期ごとの改善サイクルを設計しました。サーベイは単なる数字の羅列ではなく、チーム内の声を可視化し、EMやチームメンバーがその背景にある課題を言語化するための出発点です。そこで得られた定量・定性のデータは、対話のきっかけとなり、チームが納得感を持って改善に向けたアクションを検討するプロセスを支えています。こうした仕組みによって、計測→判断→行動→振り返りというサイクルが継続的に回るようになっています。</p> <h2>3. 組織全体で機能する改善サイクルの設計</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/5f48a5e3-20250617_1557_continuous-improvement-cycle_remix_01jxyat5hafgy8j3m2hb8jf8kd-1024x683.png" alt="DevEx Cycle" /><br /> 改善サイクルの詳細は以下の通りです:</p> <ul> <li><strong>計測</strong>:四半期毎に15分前後の匿名サーベイの実施</li> <li><strong>判断</strong>:EMがサーベイ結果を確認、チームとも議論して、改善に注力するエリアを判断</li> <li><strong>行動</strong>:EMは判断結果を元に、具体的なアクションプランを作成し、チームとして実行</li> <li><strong>振り返り</strong>:チームのレトロスペクティブや次のサーベイ結果を元にアクションの効果を確認</li> </ul> <p>このプロセスの主体はチームのエンジニアおよびEMです。Manager of ManagersやDirector、VPは各チームの実施状況や、チームからエスカレートされた課題の確認と解決に責任を持ちます(これによって、EMの改善努力が組織全体に反映される構造が保たれます)。</p> <p>このプロセス設計には、セクション2で触れた「データをきっかけとした対話と行動」の考え方が反映されています。単にスコアを確認するだけでなく、数値とコメントから文脈を読み取り、現場で実行可能な改善策へと落とし込むことが重要です。そのために、各チームが自律的に進められるよう、プロセス自体はシンプルかつ反復可能な形で整備されています。</p> <p>また、このサイクルは四半期単位で繰り返すものであり、定常業務と並行しながらも継続的に改善が進むよう設計されています。過剰な負荷を避け、着実な実行と振り返りを促すために、各チームが取り組む改善アクションは一つか二つに絞ることが推奨されています。具体的には、サーベイ結果を元に、Vote数、コメント数、業界や会社平均とのスコアの乖離などの要素から複合的に判断し、チームで対話を行いながら優先度の高い課題を特定します。その中から、現実的に取り組めるものを選定します。アクションの量よりも、実行可能性とチームの納得感を重視しています。</p> <p>今回の取り組みでは、DevEx改善を個人やチーム単体の工夫ではなく、組織の仕組みとして整え、継続可能な文化として根付かせることを目指しています。実際に、今回のサーベイでは対象となるエンジニア100%からの回答を得ることができ、同じく100%全てのEMが改善アクションの提出・実行に参加しています。</p> <p>高い参加率を確保できたポイントとしては以下の通りです:</p> <ul> <li>このプロセス構築をエンジニア組織全体のOKRとして横断的に取り組んだこと</li> <li>なぜDevEx改善に取り組むのか、背景とその狙いをエンジニアだけでなく組織全体にも継続的に発信したこと</li> <li>サーベイ実施やEMによる改善の検討期間中はLunch&amp;Learn(ランチをとりながら学び、質疑応答ができる会)を積極的に開催し、接点を増やしたこと</li> <li>DXやサーベイに関する質問を受け付けるオープンドアセッションを複数回開催し、疑問や不安の解消につなげたこと</li> <li>プロセス開始前にAll Handsで改善サイクル全体を紹介し、意義や進め方への納得感を醸成したこと<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/8d8b4597-screenshot-2025-06-19-at-14.16.34.png" alt="DX Snapshot" /></li> </ul> <h2>4. チームを越えて見えた構造的課題</h2> <p>このように、改善サイクルはチーム単体の実行だけでなく、組織全体での振返りや支援を通じて持続的に機能する設計になっています。その結果、私たちはチーム単体では捉えきれない構造的な課題にも気づくことができました。</p> <p>内部スコアは公開できませんが、全社共通で明らかになった課題は次のようなものです:</p> <ul> <li><strong>Deep Work(集中できる時間)の不足</strong>:エンジニアが集中を要する複雑な作業に没頭する時間が不足しているという課題です。会議・割り込み・不明瞭な優先順位により、多くのチームで集中が妨げられており、投票数が最も多かった項目でした。複雑な問題解決のためには集中した時間が必要ですが、絶え間ないコンテキストスイッチによってその時間は奪われてしまいます。これは単なる時間管理の問題ではなく、組織の設計や業務の優先順位づけが関係する構造的な課題です。</li> <li><strong>チーム横断連携における摩擦</strong>:プロダクト開発はエンジニアリング部門だけで完結はせず、プロダクト・法務・CSなどさまざまな関連部署との連携が必要不可欠です。そして事業の多角化、組織の拡大によってチーム数・組織構造は複雑化していきます。この課題は業界平均との差が最も大きかった項目でした。これは私が担当しているKYCおよびPartner Platformチームでも自覚があり、本来は他チームが必要とする共通機能をスムーズに提供したいのですが、整備が間に合っておらず、他チームからの問い合わせ対応に多くの時間を要してしまっているのが現状です。</li> </ul> <p>このような課題はいずれも、個々のチームやEMだけでは解決できない、より上位の構造や仕組みの見直しが必要な領域です。したがって、全社的な文化と仕組みの転換、たとえば集中時間を保護する働き方のルール整備や、チーム間連携をスムーズにするセルフサービス化の推進といった取り組みが求められます。</p> <h2>5. 現時点で見えてきたこと</h2> <p><strong>実例:2つのドメインを持つチームからの学び</strong></p> <p>私が担当しているKYCおよびPartner Platformチームのサーベイ結果と改善アクションについて共有します。両チームをあわせて分析すると「ドキュメント」に関する課題が共通して浮かび上がりました。一方で、KYCチーム単体では「本番環境でのデバッグの難しさ」や「開発環境の整備不足」が強く指摘されるなど、ドメイン固有の課題も明確になりました。</p> <p>特にドキュメントに関しては、最新情報の所在が不明確であることや、過去の経緯に関するナレッジが分散していることが要因で、問合せ対応や仕様確認に多くの時間を要しているという声を普段からも聞いていました。これは、プラットフォームチームとしての提供価値を最大化するうえで重要な改善領域です。</p> <p>従来のドキュメント整備だけでは限界があると判断し、以下のようなアクションを早速進めています:</p> <ul> <li>AI/LLMを活用した過去の問合せやナレッジの検索・再利用ができる仕組みの構築</li> <li>過去の設計ドキュメントやコードベースをもとに、自然言語で仕様を検索・確認できる内部ポータルの構築</li> </ul> <p>更新頻度が高く、非構造的な情報も多い中で、LLMの柔軟性は有効だと考えています。まだ実験段階ではありますが、情報アクセスのしやすさはDevExに直結するため、引き続き取り組んでいきたいテーマです。</p> <h2>6. 最後に:DevExはプロダクト体験そのもの</h2> <p>良いプロダクトを作りたいなら、それを作る人たちにとって良い環境が必要です。DevExは単なるスピードや効率の話ではなく、明確さ・集中・流れの話です。</p> <p>今回は初回の改善サイクルでしたが、高い関心と参加率をもって全社的に取り組むことができました。対象エンジニアの100%からの回答と、すべてのEMによるアクション提出という結果は、今後に向けた大きな一歩です。一方で、この取り組みを一過性のプロジェクトで終わらせず、疲弊することなく習慣として定着させていくことが次の課題です。</p> <p>そして、DevEx改善はエンジニアリング組織の効率化にとどまるものではなく、提供するプロダクトそのものの体験価値の向上につながるものです。エンジニアが安心して集中できる環境を整えることが、結果的にユーザーにとっても価値ある機能や品質につながるという視点を忘れずに、今後も取り組んでいきたいと考えています。</p> <p>私たちもまだ試行錯誤中です。同じような取り組みを進めている方がいれば、ぜひ一緒に学び合いましょう。</p> <p>より良い開発体験を、一緒に育てていきましょう。</p> <p>明日の記事は @y-arimaさんの「<a href="https://engineering.mercari.com/blog/entry/20250620-31f2852f9b/" title="Web版メルカリにメルコインの機能を組み込む検証をした話">Web版メルカリにメルコインの機能を組み込む検証をした話</a>」です。引き続きお楽しみください。</p> 初めてのWWDC25に現地参加!Apple Parkで体験した特別な数日感https://engineering.mercari.com/blog/entry/20250622-1c1848f606/https://engineering.mercari.com/blog/entry/20250622-1c1848f606/<p>こんにちは。メルペイ iOSエンジニアの@shuntaです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の16日目の記事です。 今回は、WWDC25に現地参加 [&hellip;]</p> Mon, 23 Jun 2025 11:00:37 GMT<p>こんにちは。メルペイ iOSエンジニアの<a href="https://twitter.com/shunta_dev">@shunta</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の16日目の記事です。</p> <p>今回は、WWDC25に現地参加してきたので現地の雰囲気やイベントなどについてご紹介します。<br /> 私は、今回が初めての参加なのでとてもワクワクしました!</p> <h2>WWDCとは</h2> <p>WWDCは、Appleが毎年開催している開発者向けのカンファレンスです。最新のiOSやmacOSなどの新機能が発表される他、さまざまなセッションやラボで直接Appleのエンジニアや各国のiOSエンジニアと話せる貴重な機会でもあります。今回初めて現地に行くことができました。</p> <h2>準備</h2> <p>WWDCはコロナ後からチケットが無料・抽選制になり、事前の応募が必要です。<br /> チケットを確保したら、次はホテル・航空券の手配です。チケットを入手した時点で2ヶ月ほどしか猶予がないので、早めに取りました。<br /> また、忘れずにESTAを申請しておきましょう。</p> <h2>Day -1(6月7日): アメリカ到着</h2> <p>時差ボケを考慮して前日の6月7日にアメリカに到着しました。<br /> Apple Visitor Centerに行きました。WWDC期間中は混むとのことなので、当日着ていくAppleのTシャツやグッズを調達しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/3a1180fb-1-1024x768.jpg" alt="" /></p> <p>夜にはtry! Swiftコミュニティの飲み会に参加してきました。なんと日本から20~30人くらいの方々が集まっていました。<br /> 今までにWWDCに参加したことがある人が多かったので、やっておいたほうがいいことなどを教えていただき、とても役立ちました!<br /> Day 0 (6月8日): 前夜祭</p> <p>WWDCの前夜祭イベントは夕方からだったので、それまでの時間を使ってGoogle Plexに寄ったり現地のFarmer&#8217;s Marketに行ってみたりと観光を楽しみます。</p> <p>夕方からはAppleの旧本社であるInfinity LoopでWelcome Receptionに参加しました。</p> <p>空港みたいな厳しい手荷物検査(WWDC期間中は毎日検査があります)を通過したら本人確認をして、WWDCのグッズなどをもらえます。<br /> 今年はタンブラーと、ピンズ、トートバッグ、キーアクセサリーです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/fc046230-2-848x1024.jpg" alt="" /></p> <p>その後は参加者同士の交流会があり、<br /> ・世界地図にピンを刺して出身地を示すコーナー<br /> ・DJブース<br /> ・大きいジェンガなどのボードゲームコーナーなど<br /> ・美味しいドリンクとフード<br /> などが用意されていて、世界のエンジニアと交流が盛んになるような仕組みが素晴らしかったです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/97df9338-3-1024x1024.jpg" alt="" /></p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/0e98dc25-4-1024x768.jpg" alt="" /></p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/8716242b-5-1024x768.jpg" alt="" /></p> <h2>Day 1 (6月9日): Keynote当日</h2> <p>時差ボケで朝4時に目が覚めてしまい、早く行ってみようかなと思い、朝5時には会場に到着しました。Apple Visitor Center付近に集合です。着いたら誰もいなくて、一番先頭になりました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/edeef130-6-1024x768.jpg" alt="" /></p> <p>待機列では6:30頃にドーナッツやコーヒーなど軽食が配られました。</p> <p>8:00頃から入場を開始しました。先頭で待機していたのでTim Cook氏のXにも載っています。 <a href="https://x.com/tim_cook/status/1932275973606834627">https://x.com/tim_cook/status/1932275973606834627</a><br /> 席を確保した後は、Apple Park内のCaffè Macsで朝ごはんが用意されていました。ホテルのような高クオリティのご飯が提供されていてとても美味しかったです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/2a3c6809-7-1024x935.jpg" alt="" /></p> <p>Keynoteの開始時刻になるとTim Cook氏やCraig Federighi氏が登場。その後大きなスクリーンでKeynoteが始まりました。<br /> 最初のF1カーの場面では笑いが起こったり、Liquid Glassや会場が沸いたりと現地でしかできない体験をしました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/56176dbc-8.jpg" alt="" /></p> <p>Keynote後はお昼ご飯が用意されています。行列ができてしまったので全部の写真を撮れなかったのですが、いろいろな国の料理が提供されていました。これもとても美味しかったです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/e2f235f8-9.jpg" alt="" /></p> <p>Apple Park内に設置されたDownload Stationでは高速のネットが使えて、ベータ版をダウンロードできます。iOS 26を試したり、近くにいた人とVisionOS 26を入れて空間体験を共有できる機能などを試しました。</p> <p>昼食後はDeveloper向けのKeynoteとも言えるState of the Unionを見た後、In-Person LabsというAppleのエンジニアに直接質問できるアクティビティがあります。<br /> さまざまなジャンルのコーナーが用意されていますが、Design Labなどの人気なLabは事前予約制でWWDCに参加する前に予約が必要です。<br /> 私は、iOSのシミュレーターについて気になる点があったのでシミュレーターやXcodeなどデベロッパーツールに関して質問できるラボに行きました。<br /> 他にも、Keynoteに登場したF1カーと写真を撮ったり、Apple Park内を散策したりと盛りだくさんの一日でした。ディナーまで用意されていて一日中Apple Parkで過ごしました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/f19c7beb-10.jpg" alt="" /></p> <h2>Day 2 (6月10日): セッション参加とスペシャルイベント</h2> <p>2日目はApplePark近くのDeveloper Center Cupertinoで行われた、夕方のDeveloper Activityに参加しました。公式サイトのセッションビデオでは見ることができないオリジナルセッションのようで、AppleのエンジニアにLiquid Glassをデザイン・実装する方法を実際にデモやスライドを用いて説明していただける貴重な機会でした。</p> <p>夜にはSteve Jobs Theaterで「F1: The Movie」の試写会に参加しました。<br /> WWDCのKeynoteで一番初めに紹介された映画です。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/c167ac97-11.jpg" alt="" /></p> <p>シアター内の撮影はできなかったのですが、Steve Jobs Theaterは普通の映画館に比べてディスプレイの発色や明るさが綺麗だったり、音響がとても良かったりシアターの設備にも感動しました。<br /> もちろん映画の方もとても良かったので、公開されたら是非観てください!</p> <h2>おわりに</h2> <p>初めてのWWDC現地参加でしたが、本当に濃い数日間でした。オンラインでは得られない現地ならではの体験や、世界中の開発者との交流、Appleのコミュニティへの力の入れ方などを肌で感じることができました。</p> <p>他にも追加で3日ほど滞在し、現地のAI企業に行ったり、Waymoに乗ったりと最新のテクノロジーに触れてきたので別の機会に紹介できればと思います。</p> <p>やはり現地でしか得られない経験は多いと思うのでiOSエンジニアの方でもそうでない方でも、一度訪れてみてはいかがでしょうか!</p> <p>明日の記事は ntkさんによる「<a href="https://engineering.mercari.com/blog/entry/20250624-building-a-company-wide-framework-for-improving-devex-in-mercari-group/" title="メルカリグループの全社的なDevEx改善の仕組みづくり">メルカリグループの全社的なDevEx改善の仕組みづくり</a>」です。引き続きお楽しみください。</p> 「自分ができる領域が増えた」-Cursorを使って未経験のKotlinコードレビューに挑戦https://engineering.mercari.com/blog/entry/20250620-018cdf7e2b/https://engineering.mercari.com/blog/entry/20250620-018cdf7e2b/<p>こんにちは。メルカリモバイル iOSエンジニアでTech Leadをしています@takeshiです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の16日目の記 [&hellip;]</p> Mon, 23 Jun 2025 10:00:27 GMT<p>こんにちは。メルカリモバイル iOSエンジニアでTech Leadをしています<a href="https://x.com/hatakenokakashi">@takeshi</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の16日目の記事です。</p> <p>今回は私が業務中に利用したAIエージェントの経験を紹介します。<br /> Cursorを使って未経験のKotlinコードをレビューして、iOS/Androidの実装差分をなくした話です。</p> <h2>メルカリモバイルチームについて</h2> <p>まず私のチームであるメルカリモバイルチームの説明をさせてください。<br /> <a href="https://about.mercari.com/press/news/articles/20250304_mercarimobile/">メルカリモバイル</a>は2025年3月4日にローンチした新しいサービスです。<br /> iOSとAndroid両方提供しています。<br /> 現在は2つのチームに分かれていて、両チームともに少数精鋭です。</p> <h2>OS間の実装差分</h2> <p>メルカリモバイルの開発を進めていく中で、課題になっているのがOS間での実装差分です。リリース前のDogfoodingや各プロジェクトのQAフェーズでiOSとAndroidで挙動が違うことがしばしば見つかりました。</p> <ul> <li>ギガの残り残量が切り捨てなのか切り上げか</li> <li>バリデーションチェックの方法</li> <li>画面ロジックのエラーハンドリング</li> </ul> <p>これらの差分は、Specに記載があるがその記述が曖昧で実装者に委ねられていたり、そもそも情報が不足していることに起因しています。<br /> これらの差分は見つけ次第修正していますが、そもそも起こらないようにしたいとも思っていました。</p> <h2>Androidのコードレビューに挑戦</h2> <p>今回、自分がTech Leadとして担当するプロジェクトがあり、技術的なSpecの作成やDesign Docを作りました。実装自体はチーム内の他メンバーが担当です。<br /> Tech Leadとして、コードの品質を守る目的としても「コードレビューをしよう」と思ったわけです。<br /> しかし、私は10数年のエンジニアのキャリアでiOS/Swiftの開発がメインです。<br /> Androidのコードレビューができません。KotlinのコードがSwiftとあまりにも隔たりがあり、コードを読んでもどんな動作になるかの理解が難しいのです。</p> <p>ただ、メルカリ社内ではAIエージェントの導入が積極的に推奨されていて、私もCursorを使える状態でした。CursorはAnysphere社が提供するAIコードエディタです。<br /> これを使えばなんとかなるんじゃないかと思い試してみました。</p> <h2>Cursor でKotlinレビュー</h2> <p>まずはCursor Rulesで次のようなルールを作りました。</p> <pre><code class="language-text">あなたはシニアAndroidエンジニアです。 私はSwift開発者でKotlinの文法に慣れていません。 KotlinやAndroidの質問にはSwiftやiOSで同様の概念を用いて説明してください</code></pre> <p>そして、AndroidのPull Requestをチェックアウトし、差分に書かれているコードの文法でわからないことがあったらひたすらCursorに聞くというのを繰り返しました。</p> <p>例えばこんなプロンプトを打ちました。</p> <pre><code class="language-text">packageとimportってkotlinだとどんな意味?</code></pre> <p>するとこんな回答が返って来ました。</p> <pre><code class="language-text">Kotlinのpackageとimportについてですね。Swiftと比較しながら説明します。 **package** Kotlinのpackageは、クラスや関数などのコードをグループ化し、名前の衝突を避けるための仕組みです。Swiftで言うところのモジュール(module)や、より具体的にはXcodeプロジェクト内のターゲット(target)に近い概念と捉えることができます。 **import** Kotlinのimportは、他のpackageで定義されたクラスや関数などを、現在のファイルで手軽に利用できるようにするためのものです。これはSwiftのimportと全く同じ機能と考えていただいて大丈夫です。 まとめると、 - package: コードの所属グループを宣言する(Swiftのモジュールやターゲットのようなもの) - import: 他のグループのコードを自分のファイルで使いやすくする(Swiftのimportと同じ)</code></pre> <p>Kotlinを知っている方からすれば何でもないことですが、私はこのレベルで何も知らない状態でした。その他スコープ関数(letやapply)、data classなど、Swiftにはない文法などを聞いてました。</p> <p>コードを指定して文法含めたコードの処理をCursorに質問すると、それに合わせた解説をしてくれるのでコードの理解が深まりました。<br /> 昔だったら文法のキーワードをググって解説のサイトを読み込んでからコードに戻るのを繰り返さないといけないところです。これではいくら時間があっても最終的にしたい「差分コードの理解を深める」に到達できません。<br /> Cursorに聞くことで、時間をかけず、既存のSwiftでの知識を活用してKotlinの概念を理解しレビューを進めることができました。</p> <h2>発見した実装差分の具体例</h2> <p>レビューの過程で、APIのリクエストパラメーターがiOSと異なることに気がつきました。<br /> 他の類似した処理とパラメーターをまとめていたのですが、今回のプロジェクトにおけるBE要件としては不要なパラメーターが含まれていました。iOSではすでに、パラメーターを分けて実装していたので、Androidもそのように指摘をし、無事に分けてリクエストを送るように修正されました。</p> <p>この指摘で、各画面で必要最小限のパラメーターのみを送信するようになり、iOS側の実装と整合性が取れるようになりました。</p> <h2>AIエージェントでレビューをする上でのポイント</h2> <p>レビューもAIエージェントでやればいいんじゃないかという意見があるかもしれませんが、私は反対です。コードレビューはコードの品質を保つ重要な活動で、まだチームのナレッジを100%AIエージェントに伝える手段が確立してないからです。<br /> 単純なコードの書き方ならリンターを使えばよくて、それ以上を求めるなら、チームのナレッジを知っている人間がやったほうが早いのが現状です。</p> <p>また自分のレビューのスタンスとして、Specをコードがちゃんと表現しているかは重視しています。QAで見つかるよりは、レビューで指摘するほうが手戻りがなくて早いでしょう。</p> <h2>チーム内での反応</h2> <p>今回私がAndroidのレビューをAIエージェントを使って行ったことに対して、チームからは好意的なフィードバックをもらいました。Androidメンバーからは「自分もiOSに挑戦したい」という声が上がりました。ゆくゆくは実装も含めて、自分の領域を超えて担当できればいいなと思っています。</p> <h2>まとめ</h2> <p>今回の経験は私の中で、AIエージェントが自分のできる領域を増やせるツールであることを知るきっかけになりました。これまでは全く手が出なかったAndroidのコードレビューに対して、時間をかけず、やりたい成果をあげられたのは大きな進歩です。<br /> 「未経験の技術領域は手が出しにくい」と感じているエンジニアの方は多いと思います。しかし、AIエージェントという強力なツールを活用することで、これまで諦めていた領域にも挑戦できるようになります。小さいところから徐々に始めると良いと思います。<br /> 私も、次はレビューだけでなく実装にも挑戦したいと思います。</p> <p>明日の記事は ntkさんによる「<a href="https://engineering.mercari.com/blog/entry/20250624-building-a-company-wide-framework-for-improving-devex-in-mercari-group/" title="メルカリグループの全社的なDevEx改善の仕組みづくり">メルカリグループの全社的なDevEx改善の仕組みづくり</a>」です。引き続きお楽しみください。</p> <p>この記事の画像に利用されたAndroid ロボットは、Google が作成および提供している作品から複製または変更したものであり、<a href="https://creativecommons.org/licenses/by/3.0/">クリエイティブ・コモンズ</a>表示 3.0 ライセンスに記載された条件に従って使用しています。</p> gRPC Federationを使った3rd party API開発事例:マネーフォワード連携から学ぶ実装ノウハウhttps://engineering.mercari.com/blog/entry/20250618-27069c49cc/https://engineering.mercari.com/blog/entry/20250618-27069c49cc/<p>こんにちは。メルペイ Credit &amp; Payment Service / Engineering Headの@fivestarです。 この記事は、Merpay &amp; Mercoin Tech Openne [&hellip;]</p> Fri, 20 Jun 2025 11:00:57 GMT<p>こんにちは。メルペイ Credit &amp; Payment Service / Engineering Headの<a href="https://x.com/fivestr">@fivestar</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a>の20日目の記事です。</p> <p>今回はメルペイで主にBFF向けに採用しているgRPC Federationという仕組みを使って、3rd party向けのAPIを実装する取り組みの事例紹介になります。</p> <h2>BFF(Backends For Frontends)開発に導入されているgRPC Federation</h2> <p><a href="https://github.com/mercari/grpc-federation">gRPC Federation</a>は、Protocol Buffers上にDSL(Domain-Specific Language)を記述することでコードを書かずにBFF(Backends for Frontends)を作成できるフレームワークです。現在<a href="https://github.com/mercari/grpc-federation">OSS</a>として公開しています。</p> <p>gRPC FederationはBFFに限らずあらゆるマイクロサービス開発において、サービス間の依存関係をProtocol Buffers上で表現することを目指して社内で開発が進められていました。</p> <p>この仕組みはメルペイのBFFのリアーキテクチャに先行導入されていましたが、IDP(ID Platform)チームが用意しているメルカリID連携の仕組みやAPI Gatewayと組み合わせることで3rd party向けのAPI開発もスムーズに実現することができました。</p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20240401-4f426bd460/">gRPC Federation: gRPC サービスのための Protocol Buffers を進化させるDSL</a></li> <li><a href="https://engineering.mercari.com/blog/entry/20231023-mmtf2023-day3-9/">【書き起こし】gRPC Federation を利用した巨大なBFFに対するリアーキテクチャの試み – goccy【Merpay &amp; Mercoin Tech Fest 2023】</a></li> <li><a href="https://engineering.mercari.com/blog/entry/20221018-mtf2022-day2-5/">【書き起こし】メルカリグループの認証基盤における理想と現状、今後の取り組み – kokukuma 【Merpay Tech Fest 2022】</a></li> </ul> <h2>メルペイのアーキテクチャ</h2> <p>メルペイではマイクロサービスアーキテクチャを前提とした4レイヤーアーキテクチャを採用しています。API Gatewayを経由してAPI = BFFレイヤーが、Backendのマイクロサービスを束ねるという構成です(図1)。</p> <p>API GatewayはAPIサーバーをexposeするための処理を行います。メルペイではAPIサーバーはgRPCのエンドポイントを提供しており、API GatewayがgRPCのAPIをJSONのHTTP APIに変換します。他にもアクセストークンの検証等も行います。</p> <p>APIレイヤーにはMerpay APIという集約的なBFFが提供されていました。しかしサービス拡大に伴って保守コストの増加とオーナーシップの問題が出てきたことを受け、BFFをドメインごとに分割するMerpay APIリアーキテクチャプロジェクトが立ち上がります。このプロジェクトでgRPC Federationが採用されました。</p> <p>図1 メルペイの4レイヤーアーキテクチャ<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/a485883c-screenshot-2025-06-16-at-11.40.54-copy.png" alt="メルペイの4レイヤーアーキテクチャ" /></p> <h3>gRPC Federationの導入</h3> <p>gRPC Federationの利用者として感じる利点は、Protobuf上のDSLの記述のみで完結するため運用時の認知負荷が低いこととパフォーマンス最適化、品質安定性です。</p> <p>BFFの主な役割は大雑把に言えば「Frontendのために必要なデータをBackendのサービス群から取得して返す」ことです。gRPC Federationは <strong>DSLの記述順序に関わらずAPIコールの順序・並列化を最適化</strong> してくれるため、特に複雑なデータ取得が要求される画面で力を発揮します。</p> <p>複雑な実装が求められる場合は個別にGoのコードを直接記述することもできるのですが、これまでgRPC Federationを用いた開発上はDSLのみで完結しています。そのため自動生成されたGoのコードのみで運用することになり、バグが埋め込まれにくく品質が安定します。</p> <h2>メルペイにおける3rd party向けAPI</h2> <p>現在メルペイでは主に次のような3rd party向けのAPIを提供しています。</p> <ul> <li>ネット決済加盟店向けAPI</li> <li>PFMサービス連携向けAPI</li> <li>メルカリポイント交換API</li> </ul> <p>このうちPFM(Personal Financial Management)サービス連携向けAPIとメルカリポイント交換APIについてはgRPC Federationで実装されており、メルカリID連携を用いた認証・認可を採用しています。社内のアセットを最大限に活用して短期間・低コストでAPIを提供する手段が確立でき、外部システムとの連携において非常に前向きな意思決定ができるようになりました。</p> <h2>メルカリID連携</h2> <p>APIを外部に公開するうえで最も重要な要素が認証・認可です。メルカリにはIDPチームがあり、既にメルカリIDを用いたOAuth / Open ID Connect(OIDC)を実現する基本的な仕組みが整っていたので、この点は既存の資産を活用することで実現できました。</p> <p>これまでAuthentication Code FlowだけでなくClient Credentials Flowを採用したケースもあり、このあたりOAuth 2.0の基本的な機能はおおよそカバーされているため、ユースケースに応じた柔軟な対応が可能となっています。</p> <p>またメルカリ内部ではお客さまのIDはPII(個人識別用情報)として扱うため慎重に取り扱う必要があるのですが、OIDCを用いたときにPPID(Pairwise Pseudonymous Identifier)として変換されるため、安全かつシームレスに扱える仕組みが整っています。</p> <p>実際にこれまで3rd party向けのAPIを用意するときは都度IDPチームに相談して適切な選択肢を一緒に考えてもらっているのですが、プラットフォームとして確かな機能が提供されており、またそれらが正しく使えるように毎回丁寧に相談にのってくれるため、大変心強いです。</p> <ul> <li><a href="https://engineering.mercari.com/en/blog/entry/20230130-applying-oauth-2-0-and-oidc-to-first-party-services/">Applying OAuth 2.0 and OIDC to first-party services</a></li> </ul> <h2>gRPC Federationを用いた3rd party向けAPI提供</h2> <p>最初に3rd party向けAPIに導入したのがPFMサービスである<a href="https://jp.merpay.com/news/2024/08/20240805moneyforward/">マネーフォワードとのシステム連携プロジェクト</a>でした。gRPC Federationが導入されてまもなくの頃で、まだ実際にプロダクション環境での実績がない状況でしたが、前述のような利点があることからSolutionsチーム・Architectチームと相談の上で採用を決定しました。</p> <p>実はメルカリ全体としてオープンなPublic APIを提供するアイディアもありましたが、意思決定のコストや要求されるスピード感を考えたときに今それを目指すのはToo muchでした。 <strong>gRPC Federationを使うことでBFFの立ち上げコストが劇的に下がった</strong> ことからも、一旦はドメインごとにAPIを用意していく方が合理性があると判断しています。</p> <p>このあとに実装したポイント交換APIはgRPC Federationへの習熟度が上がってきたこともあり、おおよそ1-2週間程度で基本的なAPIの用意ができています。gRPC FederationのDSLを記述するのに多少の学習コストが必要でしたが、<a href="https://github.com/mercari/grpc-federation/blob/main/cmd/grpc-federation-language-server/README.md">Language Server</a>の他、執筆時点では<a href="https://github.com/mercari/grpc-federation/pull/303">MCPサーバーの実装</a>も進められており、効率的な開発が行える様々な支援が提供されています。</p> <h3>新規開発の流れ</h3> <p>3rd party向けのAPIをgRPC Federationで実装する場合、次のような手順で進めています。</p> <ol> <li>要件定義</li> <li>Design Doc作成</li> <li>API仕様書作成</li> <li>API仕様に合わせてProtocol Buffers上でスキーマ定義</li> <li>Protocol Buffers上でDSLを書いてAPI実装</li> </ol> <p>メルペイの場合、要件定義はプロダクトマネージャーが中心となって進めるケースが多いですが、3rd party向けAPIの場合は画面仕様が明確でないケースもあるため、エンジニアがオーナーシップを発揮する必要があります。マネーフォワード連携の場合、私自身がマネーフォワードの利用者で連携を待ちわびていたこともあったので喜々としてやった覚えがあります。</p> <h3>Design Doc作成</h3> <p>Design Docはステークホルダとの合意形成のためにさまざまな観点から情報を整理するために記述します。特にメルカリ・メルペイにおいてはドメインに関係があるチームはもちろん、Architect、SRE、IDP、SecurityといったEnabling方面との合意を早期に得ることが最終的な成果物を早く提供することにつながるため、迅速にまとめることを意識しています。</p> <ul> <li>API提供の背景、目的、スコープ、ゴール</li> <li>APIの名称、ドメイン名</li> <li>アーキテクチャ図、依存関係</li> <li>外部システムを含めたシーケンス図</li> <li>どのチームにどのような作業を要求するか</li> <li>エンドポイント</li> <li>メルカリID連携のクライアントの単位</li> <li>認可スコープ</li> <li>提供環境、特に外部向けの開発環境をどうするか</li> </ul> <p>こういった情報は要件定義以前から関係チームを巻き込みながら進めておくことで、そこまでにおおよそ決まった方針をDesign Docとして清書し、不確実な要素をつぶしていく作業になります。自分は1度やって勘所を掴んだこともあり要件次第ですがおおよそ1日程度で最低限はまとめられるため、とにかく1度やってしまえば意外と難しいことはなかったりします。</p> <h3>API仕様書作成</h3> <p>3rd partyにAPI仕様を伝える上で当然API仕様書を用意する必要があります。リクエストやレスポンスのヘッダーやパラメーター情報はもちろん、エラーの種別や実際のレスポンスのパターンも用意する必要があります。ただし、特にAPIを新規で開発するタイミングでは、実際にAPIクライアント側が想定するAPI構成になっているかを早めに揃えることが手戻りを抑えるために重要なため、基本的なAPIの単位と主要なパラメーターを整理して早期にすり合わせるように心がけています。</p> <p>またこの時メルカリID連携のフローも含めたシーケンス図を用意しておくことで、想定されるAPIの呼び出し方法や、お客さまがどこで操作を行うのかといったUX面でもよりイメージを揃えることができたため、シーケンス図もあわせて用意するようにしています。</p> <h2>gRPC Federationを用いたAPI定義の例</h2> <p>gRPC Federationを用いてProtocol Buffers上でAPIを作成するサンプルコードを用意しました。リスト1で3つのエンドポイントを内包する <code>APIService</code> サービスを、リスト2はそのうちの <code>CreateCharge</code> メソッドをそれぞれ定義したものです。(なおコードは実際のものを模したダミーです)</p> <h3>gRPCサービス定義</h3> <p>リスト1にはAPIのアウトラインとなる定義が記載されています。 <code>APIService</code> にはまず <code>mercari.api.gateway.spec</code> オプションで、API Gateway向けの設定を行っています。exposeするドメインや、内部的なAPI区分などを指定します。</p> <p><code>APIService</code> には <code>CreateCharge</code> <code>GetCharge</code> <code>CaptureCharge</code> という3つのgRPCメソッドが定義されています。各エンドポイントにはAPI GatewayでHTTPのエンドポイントで変換するために <code>google.api.http</code> オプションを設定しています。今回の例ではREST形式のパスを採用しているため、クライアントからするとREST APIとして操作しているように見えるでしょう。</p> <p>gRPC Federationでは <strong><code>option</code></strong> キーワードを使って <code>.proto</code> ファイル上にannotateします。gRPCサービスに対しては <code>.grpc.federation.service</code> をマッピングするだけで基本的には十分です。もしgRPC FederationのDSL内で環境変数にアクセスしたい場合 <code>env</code> キーで設定することができます。</p> <p><code>mercari.api.jp.authority.scopes</code> は必要なスコープを定義するメルカリ独自のオプションです。これはAPI Gatewayによって必要な権限のないリクエストを弾く処理が行われているため、どのようなスコープ単位の定義と、メソッドごとの必要スコープの設定にのみフォーカスできます。</p> <p>なお、 <code>mercari.</code> で始まっているオプションは基本的にメルカリ社内用のアノテーションで、あくまでメルカリ・メルペイ内部において、API GatewayやIDPとの連携についてアノテーションベースで設定できる仕組みがある、程度の理解で大丈夫です。</p> <p>リスト1: gRPC サービス定義</p> <pre><code class="language-proto">service APIService { option (mercari.api.gateway.spec) = { domain : &quot;example-api.merpay.com&quot; endpoint_prefix : &quot;&quot; api_type : API_TYPE_OPEN }; option (.grpc.federation.service) = {}; rpc CreateCharge(CreateChargeRequest) returns (CreateChargeResponse) { option (google.api.http) = { post : &quot;/v1/charges&quot; body : &quot;*&quot; }; option (mercari.api.jp.authority.scopes) = SCOPE_MERPAY_EXAMPLE_API_CHARGE_READWRITE; } rpc GetCharge(GetChargeRequest) returns (GetChargeResponse) { option (google.api.http) = { get : &quot;/v1/charges/{charge_id}&quot; }; option (mercari.api.jp.authority.scopes) = SCOPE_MERPAY_EXAMPLE_API_CHARGE_READWRITE; } rpc CaptureCharge(CaptureChargeRequest) returns (CaptureChargeResponse) { option (google.api.http) = { post : &quot;/v1/charges/{charge_id}:capture&quot; body : &quot;*&quot; }; option (mercari.api.jp.authority.scopes) = SCOPE_MERPAY_EXAMPLE_API_CHARGE_READWRITE; } }</code></pre> <h3>gRPCメソッド定義</h3> <p>実際にgRPC FederationでDSLを記載するのは主に各メソッドの戻り値に設定したメッセージになります。リスト2では <code>CreateCharge</code> メソッドの戻り値である <code>CreateChargeResponse</code> に、Backendサービスの <code>merpay.payment.v1.PaymentService/CreateCharge</code> を呼び出して、そのレスポンスから <code>res.charge</code> を取り出して <code>CreateChargeResponse.charge</code> に詰めて返す、という記述をしています。</p> <p>gRPC Federationでは <code>def</code> キーワードを用いて変数を定義しながら、その中で <code>call</code> キーワードを用いることでBackendサービスのAPIコールを行い、レスポンスに必要なデータを形成していくというフローです。 <code>$.amount</code> のように <code>$</code> を用いてリクエストパラメータに直接アクセスできます。</p> <p>今回は省略しましたが <code>validation</code> キーワードを用いてバリデーションしたり、 <code>error</code> キーワードを用いてエラーを返したりといった操作も可能です。</p> <p><code>(.grpc.federation.message).alias</code> を用いて、パッケージが異なるがスキーマが同じ場合に自動的にプロパティを詰め替えてくれる機能もあります。レイヤードアーキテクチャを採用しているとこういったレイヤーをまたいだ際のペイロードの詰め替えが発生しがちですが、名前を見て適切にマッピングしてくれるためとても簡潔に扱えます。</p> <p><code>customer_id</code> 変数に <code>mercari.grpc.federation.authority.pat().customerId()</code> という値を設定していますが、これはgRPC FederationのDSL(CEL API)を<a href="https://github.com/mercari/grpc-federation/blob/main/docs/cel_plugin.md">プラグインによって拡張</a> したもので、IDPが発行した内部用のアクセストークンからカスタマーIDを取得するメルカリ固有のデータアクセスをDSLに組み込んでいます。</p> <p>なお、このサンプルコードではAPI設計でよく使われる冪等性の担保についても実装例を示しています。昨今ではHTTPリクエストに <code>Idempotency-Key</code> ヘッダーで「冪等キー」を指定する手法が一般的で、サンプルコードでもこの指定に則っています。 <code>grpc.federation.metadata.incoming()[&#039;idempotency-key&#039;][0]</code> のようにHTTPヘッダーの値が取得できます。従来メルペイでも<a href="https://engineering.mercari.com/blog/entry/2019-06-07-155849/">決済や残高のデータ整合性担保</a>のために冪等キーを導入していましたが、gRPCのリクエストパラメータに指定する手法を採用しているため、BFFレイヤーで詰め替えを行っています。</p> <p>リスト2: CreateCharge RPCの定義</p> <pre><code class="language-proto">option (grpc.federation.file) = { import : [ &quot;proto/merpay/payment/v1/payment.proto&quot; ] }; message CreateChargeRequest { uint64 amount = 1; } message CreateChargeResponse { option (.grpc.federation.message) = { def[ { name : &quot;customer_id&quot; by : &quot;mercari.grpc.federation.authority.pat().customerId()&quot; }, { name : &quot;idempotency_key&quot; by : &quot;grpc.federation.metadata.incoming()[&#039;idempotency-key&#039;][0]&quot; }, { name : &quot;res&quot; call { method : &quot;merpay.payment.v1.PaymentService/CreateCharge&quot; request : [ {field : &quot;customer_id&quot;, by : &quot;customer_id&quot;}, {field : &quot;amount&quot;, by : &quot;$.amount&quot;}, {field : &quot;idempotency_key&quot;, by : &quot;idempotency_key&quot;} ] } } ] }; Charge charge = 1 [(grpc.federation.field).by = &quot;res.charge&quot;]; } message Charge { option (.grpc.federation.message).alias = &quot;merpay.payment.v1.Charge&quot;; string id = 1; uint64 amount = 2; }</code></pre> <h3>gRPC FederationでAPIサーバーが動くまで</h3> <p>実際にはこのProtobufの変更をマージした上で、gRPC Federationを用いてビルドされたGoのコードにテストコードを書いていきます。またAPI Gatewayに対してこのAPIをexposeするための設定も必要です。とはいえおおよそはgRPC Federationによってレールが敷かれるため、1からサービスを作成することに比べると非常にシンプルな工数で実現が可能となっています。</p> <h3>複雑なエンドポイントの実装</h3> <p>先ほど示したサンプルコードはスキーマ自体もかなり簡略化していますが、実際にマネーフォワードとの連携においてはお客さまの残高やメルカードの利用状況など複雑なスキーマやパターンを持つエンドポイントを複数実装しています。</p> <p>gRPC FederationでDSLを記述する際、次のような操作が基本機能として提供されています。</p> <ul> <li>ネストしたメッセージの中でもAPI call含めた定義が可能</li> <li>四則演算や型変換が可能</li> <li><code>if</code>キーワード、あるいは<code>by</code> キーワード内で三項演算子を用いた条件分岐が可能</li> <li><code>call</code> キーワードの中で <code>timeout</code> を用いたタイムアウト時間の設定、及び <code>retry</code> を用いたリトライの指定が可能</li> </ul> <p>複雑なスキーマにおいてはDSLもそれなりの記述量となりますが、基本的には処理結果を変数に詰めるということを繰り返していくため、処理がネストするような書き方にはならず複雑さは比較的抑えられるかと思います。</p> <p>またスキーマ自体が適切に正規化されていることでメッセージのまとまり単位でDSLの記述も整理できるため、特に新規にスキーマ定義する際は <strong>正規化を意識する</strong> ことで全体の見通しがよくなると思います。</p> <h2>まとめ</h2> <p>現時点におけるベストプラクティスの1つとして、gRPC Federationを採用して3rd party向けAPIを提供する流れをざっくりと紹介してきました。</p> <p>gRPC FederationはBFFのような仕組みを簡単に作成でき、運用負荷も少ないため、低コストでAPIを立ち上げることができます。もちろん複雑なスキーマにも対応できますし、サーバーを分割したい場合にも適しており、プロダクトの規模に応じて様々な状況に対応できる非常に実用的なソリューションです。</p> <p>もちろんこれはgRPC Federationを導入しただけで作れるものではなく、API GatewayやBackendサービスといったアーキテクチャのレイヤー化や、IDPのようなプラットフォームがあるからこそ、BFFレイヤーの拡張だけで新しいAPIの導入が実現できています。ですのでアーキテクチャの全体像を踏まえて参考にしていただければ幸いです。</p> <p>今回の記事用に書いたサンプルコードの作成にあたってClaude Codeを用いてDSLを生成しましたが、多少のやり取りでおおよそ期待通りのアウトプットが出来上がりました。今回の<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a>でもAI関連の記事が非常に多く出ていますが、本当に目まぐるしい速度で環境が変わっていっていますよね。ぜひ他の記事も目を通してみてください!</p> <p>明日の記事はtakeshiさんによる「<a href="https://engineering.mercari.com/blog/entry/20250620-018cdf7e2b/" title="「自分ができる領域が増えた」-Cursorを使って未経験のKotlinコードレビューに挑戦">「自分ができる領域が増えた」-Cursorを使って未経験のKotlinコードレビューに挑戦</a>」とShuntaさんによる「<a href="https://engineering.mercari.com/blog/entry/20250622-1c1848f606/" title="初めてのWWDC25に現地参加!Apple Parkで体験した特別な数日感">初めてのWWDC25に現地参加!Apple Parkで体験した特別な数日感</a>」の2本です。引き続きお楽しみください。</p> Mercari Pipeline (旧Mercari Dataflow Template) v1(β版)を公開しましたhttps://engineering.mercari.com/blog/entry/20250620-mercari-pipeline-beta1/https://engineering.mercari.com/blog/entry/20250620-mercari-pipeline-beta1/<p>はじめに こんにちは。メルペイ Solutionsチーム所属のデータエンジニア @orfeon です。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の15日目の [&hellip;]</p> Fri, 20 Jun 2025 10:00:46 GMT<h2>はじめに</h2> <p>こんにちは。メルペイ Solutionsチーム所属のデータエンジニア <a href="https://twitter.com/orfeon">@orfeon</a> です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の15日目の記事です。</p> <p>2020年にデータパイプラインをJSONで定義して実行することができるツールとしてmercari/DataflowTemplateを開発して<a href="https://engineering.mercari.com/blog/entry/mercari-dataflow-template/">OSSとして公開</a>しました。<br /> 最近このツールに大幅な機能追加を行い、 <a href="https://github.com/mercari/pipeline/">mercari/pipeline</a> と名前を変更してv1.0.0(β版)をリリースしました。</p> <p>この記事では今回開発を行った以下の機能について紹介していきます。</p> <ul> <li>運用の容易化 <ul> <li>YAML対応</li> <li>パイプライン構成管理の強化</li> <li>dead-letter設定の追加</li> </ul> </li> <li>パイプライン定義の容易化 <ul> <li>checkerツールの提供</li> </ul> </li> </ul> <h2>運用の容易化</h2> <p>多くのデータパイプラインの開発・デプロイを進めていくと、極力少ない工数で多くのパイプラインを運用していく必要性が高まります。<br /> ここではデータパイプラインのプロダクション環境での運用負荷を軽減するために追加された以下の機能について紹介します。</p> <ul> <li>YAML対応</li> <li>パイプライン構成管理強化</li> <li>dead-letter設定の追加</li> </ul> <h3>YAML対応</h3> <p>パイプラインの定義を行うconfigファイルのフォーマットは、これまでJSON形式のみサポートしていたのですが、YAML形式でも定義できるようになりました。<br /> JSONでは、改行やダブルクオートを含むようなパラメータがあった場合にサニタイズする手間が発生したり、コメントを書けなかったり、可読性が落ちたりするなどconfigファイルの保守に問題もありました。YAMLで定義することでこうしたパラメータでも直接指定できるようになり、configをよりシンプルに定義して保守できるようになりました。</p> <p>YAML定義によるbigquery sourceの定義例</p> <pre><code class="language-yaml">sources: - name: bigquery_source module: bigquery parameters: query: |- WITH subquery AS ( -- some comment SELECT user_id, MIN(timestamp) AS first_timestamp FROM `mytataset.mytable` GROUP BY user_id ) SELECT format(&#039;%d#g&#039;, user_id) as row_key, first_timestamp FROM subquery </code></pre> <h3>パイプライン構成管理強化</h3> <p>さまざまなデータパイプラインを運用していると、別々のパイプラインで共通する処理や設定を使いまわしたいケースがあります。</p> <ul> <li>起動時に変数を指定してパイプラインのパラメータを変更する</li> <li>パイプラインの中で指定したパスのみ実行する</li> <li>複数のパイプライン定義を一つのパイプラインにマージして動かす</li> </ul> <p>本来共通する部分を別々で定義してしまうと、変更時にそれぞれ修正する必要があり、データパイプラインの保守性が落ちてしまいます。<br /> 今回configのsystemの項目で新たに以下のパラメータが追加されました。これらを指定することで、上記のようなケースに対応するためのパイプラインの構成制御ができるようになりました。</p> <ul> <li>system <ul> <li>args</li> <li>context</li> <li>imports</li> </ul> </li> </ul> <p>以降の節でこれらのパラメータによる構成の制御方法について説明します。</p> <h4>argsによる起動時のパイプラインのパラメータの変更</h4> <p>system.argsパラメータを使うことで、パイプラインの起動時のオプションに指定した変数を使ってモジュールのパラメータを書き換えることができるようになります。<br /> 実はこれまでのmercari/DataflowTemplateでもパイプラインの起動時の変数指定はできたのですが、複数手段があったり、パイプライン実行時の動的な変数指定(データの値に応じて宛先のtopicをスイッチするなど)と競合したりするなど、いろいろと問題があったため今回argsパラメータとして整理をしました。</p> <p>args機能を利用するユースケースとしては、通常起動時はcronの起動時の条件でデータを読み込み、問題発生時のデータのバックフィルで読み込み元のテーブルやフィルタ条件の日付を起動時に指定する例などが挙げられます。</p> <p>以下はargsでパイプラインの起動時に変数を指定して、パラメータの値を書き換えるconfigの例です。<br /> argsでは起動時の指定が無い場合の変数のデフォルト値を設定しています。<br /> デフォルト値では固定値だけでなくTemplate Engineを使って動的に生成することもできます。<br /> target_tableではクエリで参照するテーブル名、current_dateではクエリのフィルタ条件として使うための日付として起動時の日付を生成しています。<br /> bigqueryモジュールのqueryパラメータでこれらの変数の値を埋め込むことでクエリの条件を起動時に制御できます。</p> <pre><code class="language-yaml">system: args: target_table: &quot;myproject.mydataset.mytable&quot; current_date: &quot;${utils.datetime.current_date(&#039;Asia/Tokyo&#039;)}&quot; sources: - name: bigquery_source module: bigquery parameters: query: |- SELECT * FROM `${target_table}` WHERE created_date &gt;= DATE(&quot;${current_date}&quot;)</code></pre> <p>パイプライン起動時に以下のようにparameters=args.{変数名}を指定すると、argsで定義した変数のデフォルト値を指定した値で置き換えることができます。</p> <pre><code class="language-bash">gcloud dataflow flex-template run sample-job \ --project=myproject \ --region=asia-northeast1 \ --template-file-gcs-location=gs://xxx/yyy/zzz \ --parameters=config=&quot;$(cat path/to/config.yaml)&quot; \ --parameters=args.target_table=myproject2.mydataset2.mytable2</code></pre> <h4>contextによるパイプラインのパスの指定</h4> <p>単一の目的のためのデータパイプラインではあるものの、状況により処理を派生させたいケースがあります。<br /> こうした派生する処理ごとに別々のconfigファイルを定義すると管理が煩雑になってしまいます。<br /> contextとtagsパラメータを使うことで、派生する処理も含めて単一のconfigファイルで定義しておき、状況に応じて一部の処理を切り替えることができます。<br /> 具体的にはconfigファイルで各モジュールにtagを設定して、起動時にcontextで指定したtagのモジュールだけでパイプラインを構成して実行することができます。<br /> contextとtagsを使う例として、機械学習の学習用パイプラインと予測用パイプラインを単一のconfigファイルで定義してcontextで切り替える構成を紹介します。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/9b2f8d38--2025-06-18-10.37.25-1024x805.png" alt="" /></p> <p>機械学習では予測モデルを構築する際に、学習用と予測用で別々のパイプラインを作ることがあります。データのソースは学習時と予測時で別々だが特徴量を生成する処理は共通というケースを想定します。<br /> 以下のconfigファイルでは、特徴量生成は共通ですが、学習用にはBigQueryのデータソース/結果シンクを用い、予測用にはPub/Subのデータソース/結果シンクを用いています。</p> <pre><code class="language-yaml">system: context: train sources: - name: ml_source module: bigquery tags: - train parameters: table: xxx timestampAttribute: timestamp_field - name: ml_source tags: - prediction schema: avro: file: xxx parameters: format: avro subscription: xxx transforms: - name: feature inputs: - ml_source tags: - train - prediction parameters: groupFields: - user_id select: - name: moving_avg field: amount_field func: avg range: count: 10 sinks: - name: feature_sink module: bigquery tags: - feature inputs: - feature parameters: table: xxx - name: feature_sink module: pubsub tags: - prediction inputs: - feature parameters: topic: xxx</code></pre> <p>sourcesとsinksにそれぞれml_sourceとfeature_sinkという同じnameを持つモジュールがあります。ただしtagsではtrain、 predictionと異なるtagを持っています。<br /> transformsでは特徴量を生成するselectモジュールとして直近の指定した個数の移動平均を計算する設定をしています。バッチでもストリーミングでも同じ特徴量を生成します。<br /> tagsではtrainとpredictionの両方を指定しています。<br /> system.contextでtrainを指定した場合、sourcesとsinksではtagsでtrainが指定されたモジュール(この場合はbigquery)のみでパイプラインが構成されます(contextでpredictionを指定した場合はsourcesとsinksでpubsubのみ)。<br /> transformのselectモジュールはtagsでtrainとprediction両方指定されているのでどちらのコンテキストでも利用されます。<br /> (なおcontextで何も指定しない場合は全てのモジュールが使われ、同じnameでコンフリクトが発生してエラーになります)</p> <p>contextにより、複数のコンテキストに応じたモジュールの設定を単一のconfigファイルに定義しておき、起動時にcontextを指定することでパイプラインの処理を簡単に切り替えられるようになります。</p> <h4>importsによる複数configファイルのマージ</h4> <p>パイプラインのためのインフラや運用のコストを減らすために複数の処理を単一のパイプラインにまとめたい場合があります。<br /> 一方で、一つのパイプラインに複数の処理をまとめるとconfigファイルが肥大化してパイプライン定義の見通しが悪くなります。<br /> そこでconfigファイルは用途に応じて別々に定義しておいて、importsパラメータでそれらのconfigファイルを指定することでパイプラインを一つにまとめることができるようになりました。</p> <p>以下ではimportsパラメータを利用したconfigファイルの例を説明します。<br /> この例のconfigファイルではsystem.importsパラメータのみ指定されています。実際の処理はimportsのfilesで指定されたconfigファイルで定義されており、起動時にこれらのファイルを読み込んで一つのパイプラインとして構成・実行します。<br /> (baseパラメータでconfigファイルのパスのprefixを指定しています)</p> <pre><code class="language-yaml">system: imports: - base: gs://example-bucket/configs/ files: - pipeline_1.yaml - pipeline_2.yaml - subdir/pipeline_3.yaml</code></pre> <p>このimports機能は、単純に複数のconfigファイルで定義されたモジュールをマージしているだけなので、各configファイルではnameが重複しないように注意が必要です。<br /> (imports時の重複チェックなどの機能は今後改善予定です)</p> <h3>dead-letter設定の追加</h3> <p>運用のためには処理の途中でエラーが発生した場合は原因を特定したりリカバリを行うために、問題のあったデータを切り分けて保持する必要があります。またデータパイプラインの要件によっては問題が発生した場合でも処理を正常に続ける必要があります(streaming処理や処理全体をやり直すコストが非常に大きい場合など)。<br /> 今回のバージョンアップではほぼ全てのモジュールで修正を行い、処理に問題が発生した場合も極力処理を正常に続行できるようにしました。また問題のあったデータを切り分けて指定したdead-letterに簡単に出力できるようになりました。</p> <p>以下、dead-letterのconfigファイルの設定例になります。<br /> このconfigではfailuresの項目が新たに追加されています。<br /> failuresのモジュールでは通常のsinkとは異なりinputsを指定する必要はありません、パイプラインの全てのモジュールで処理に失敗したデータはこのfailuresで指定されたモジュールに送られます。</p> <pre><code class="language-yaml">system: failure: failFast: false sources: - name: pubsub_source module: pubsub parameters: format: avro subscription: xxx sinks: - name: pubsub_sink inputs: - pubsub_source parameters: format: avro topic: xxx failures: - name: pubsub_failure_sink parameters: format: avro topic: xxx</code></pre> <p>処理に失敗したエラーデータは共通のスキーマでfailuresで定義したモジュールに送られます。<br /> あらかじめBigQueryでこのスキーマに準じたテーブルを作っておき、Pub/SubのBigQuery subscriptionを通じてBQに連携・保持することもできます。</p> <h2>パイプライン定義の容易化</h2> <p>パイプラインの定義を作って動作確認する際に、これまでは実際にJobを実行してうまくいくか確認する必要がありました。<br /> しかしこれは手間がかかる作業であり、パイプラインの定義自体が非常に時間の掛かるプロセスでした。<br /> ここではパイプラインの定義をもっと手軽に試行錯誤できるようにするために追加した以下の機能を紹介します。</p> <ul> <li>checkerツールの提供</li> </ul> <h3>checkerツールの提供</h3> <p>ブラウザ上で簡単にconfigファイルの内容をチェックするためのツールを同梱しました。<br /> これまでのmercari/DataflowTemplateのビルド成果物は基本的にDataflow Flex Templateのためのコンテナイメージのみでした。<br /> mercari/pipelineではビルド時のプロファイルを切り替えることで複数のビルド成果物を生成することができるようになりました。<br /> 現在では以下のプロファイルがサポートされています。</p> <ul> <li>dataflow <ul> <li>Cloud Dataflow Flex Template用のコンテナイメージを生成</li> </ul> </li> <li>direct <ul> <li>パイプラインのローカル実行用のコンテナイメージを生成</li> </ul> </li> <li>server <ul> <li>パイプラインのローカル実行機能をAPIとして提供するサーバ用のコンテナイメージを生成</li> </ul> </li> </ul> <p>プロファイルでserverを指定して生成されたコンテナイメージは、ローカルにpullして起動、利用することもできますし、Cloud Runなどにデプロイして使うこともできます。<br /> APIだけでなくチェック用のUIも備えているのでブラウザ上で操作できます。</p> <p>以下はこのserverのコンテナイメージを起動してブラウザで開いた画面の例になります。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/58ad9cda--2025-06-18-10.05.07-1024x761.png" alt="" /></p> <p>画面の左側がconfigの内容を記述するテキストエリアになります。<br /> 右側はconfigの実行結果を表示するエリアになります。<br /> 右上のヘッダーには定義したconfigを実行するために以下の2つのボタンが並んでいます。</p> <ul> <li>Dry Run <ul> <li>定義したパイプライン処理の実行グラフを生成する</li> <li>各モジュールのパラメータのチェック</li> <li>各モジュール間の関係整合性のチェック</li> <li>各モジュールの出力スキーマの確認</li> </ul> </li> <li>Run <ul> <li>定義したパイプライン処理をローカルで実行する</li> </ul> </li> </ul> <p>Dry Runボタンでは、定義したconfigの内容に問題がないか確認できます。<br /> 問題があった場合は右側にエラー内容が表示されるので、それを確認して修正することができます。<br /> 問題がなかった場合は右側に各モジュールの出力のスキーマが表示されるので、処理内容が想定した通りか確認することができます。</p> <p>Runボタンでは、定義したconfigの内容で実際にパイプラインをローカルで実行します。<br /> パイプラインでクラウドリソースにアクセスする場合(BigQueryのクエリ結果を取得するなど)はローカル実行しているサービスアカウントに必要な権限が付与されているか注意してください。</p> <p>以下のコマンドは、利用者のローカルマシン(MacOS)で自分の権限でserverコンテナを起動する例です。</p> <pre><code class="language-bash">docker run \ -p &quot;8080:8080&quot; \ -v ~/.config/gcloud:/mnt/gcloud:ro \ --rm asia-northeast1-docker.pkg.dev/{deploy_project}/{template_repo_name}/server:latest</code></pre> <p>Cloud Runにデプロイして使うこともできます。以下Cloud Runにデプロイするためのコマンド例です。<br /> (データ処理でBQ等の外部リソースにアクセスする場合はCloud Runのサービスアカウントに権限が必要です)</p> <pre><code class="language-bash">gcloud run deploy {service_name} \ --project={project} \ --image=asia-northeast1-docker.pkg.dev/{deploy_project}/{template_repo_name}/server:latest \ --platform=managed \ --region=asia-northeast1 \ --execution-environment=gen2 \ --port=8080 \ --no-allow-unauthenticated</code></pre> <p>ローカルであっても規模の小さいデータ処理であれば特に問題なく実行できるはずです。バッチでちょっとしたデータの加工や移動をするための便利ツールとしても利用することができます。<br /> なお現在このcheckerツールではstreamingモードでのRun実行はサポートしていないので、streamingモードでローカル実行する場合はdirectのコンテナイメージをコマンドラインで起動して使うことを推奨しています。<br /> directコンテナを動かすのは基本的にserverイメージをdirectに差し替えるだけです。<br /> ただしconfigファイルを起動時のパラメータに指定する必要があります。<br /> 以下のコマンドは、利用者のローカルマシン(MacOS)で自分の権限でdirectコンテナをstreamingモードで起動する例です。</p> <pre><code class="language-bash">docker run \ -v ~/.config/gcloud:/mnt/gcloud:ro \ --rm asia-northeast1-docker.pkg.dev/{deploy_project}/{template_repo_name}/direct:latest \ --streaming=true \ --config=&quot;$(cat path/to/config.yaml)&quot;</code></pre> <p>ちなみにこのUIの部分の開発はClaude Codeを使いながら作りました。<br /> 自分はフロントエンド開発の経験はほとんどないのですが、ちょっとしたUIを持ったサービスをサクッと作れてとても便利でした。</p> <h2>今後の開発</h2> <p>今後のmercari/pipelineの開発としては大まかに以下のような項目について開発を進めていきたいと考えています。</p> <ul> <li>checkerツールの拡張</li> <li>streaming処理機能の強化</li> <li>Apache Flink, SparkなどCloud Dataflow以外のRunnerへの対応</li> </ul> <h3>checkerツールの拡張</h3> <p>checkerツールは現在はまだシンプルなconfigファイルの簡易チェックや簡易な動作確認しかできませんが、非エンジニアでもデータパイプラインを手軽に利用できるように機能を拡張していきたいと考えています。<br /> 将来は自然言語で処理内容を指示するとエージェントがドキュメントや過去のconfigファイルの履歴などを参照して利用者とインタラクティブにパイプラインを構築できるようにしていきたいと考えています。<br /> 今回のリリースには間に合いませんでしたが、エンジニアがインタラクティブにconfigファイルの定義をできるようにcheckerツールをMCPサーバとして利用できるように準備を進めています。<br /> エージェントの連携を強化するためにも、リポジトリのドキュメントの整備やconfigファイルのexamplesの拡張も進めていきたいと思います。</p> <h3>streaming処理機能の強化</h3> <p>Google Cloudにおいてバッチ処理についてはBigQueryでかなりのことができるようになってきました。例えば <a href="https://cloud.google.com/bigquery/docs/federated-queries-intro">Federated Query</a> や <a href="https://cloud.google.com/bigquery/docs/export-to-spanner">Reverse ETL</a>を使うことで、Cloud Spanner や Cloud Bigtable などの外部のデータソースから取得したデータをBigQueryのクエリエンジンで処理して結果を書き戻すことも手軽にできるようになりました。<br /> <a href="https://cloud.google.com/bigquery/docs/generative-ai-overview">BigQuery ML</a>で機械学習モデルやLLMの推論結果を手軽にクエリの中で付与することもできます。<br /> またちょっとしたリアルタイム処理も、BigQueryの<a href="https://cloud.google.com/bigquery/docs/continuous-queries-introduction">Continuous queries</a>や、Cloud Pub/Sub の <a href="https://cloud.google.com/pubsub/docs/smts/smts-overview">Single Message Transforms</a> などを使うことで手軽に実現できるようになってきました。<br /> Google CloudにおいてCloud Dataflowは、大規模データに対する複雑なstreamingデータ処理を担うことを役割として期待されているように思います。<br /> streaming処理の中でも、Apache Beamの特徴であるbatchとstreamingで同じ処理をするユースケースに対して特に集中して機能開発をしていきたいと考えています。</p> <h3>Cloud Dataflow以外のRunnerのサポート予定</h3> <p>名前を mercari/DataflowTemplate から mercari/pipeline に変更した理由でもあるのですが、mercari/pipeline を Cloud Dataflow 以外のデータ処理基盤でも動かせるようにしていきたいと考えています。<br /> Google CloudでもApache Spark, Flink, Kafkaなどの人気でオープンなビッグデータのフレームワークやそのエコシステムとの連携にも力を入れていこうとしているように思います。<br /> こうしたフレームワークとの連携も進めていき、Cloud Dataprocなどでもパイプラインを動かせるように機能を拡張をしていきたいと考えています。</p> <p><a href="https://github.com/mercari/pipeline">mercari/pipeline</a> は大幅に変更があり、まだβ版でのリリースのため、機能が不足していたり一部バグがあったりするかもしれません。もし問題に気付かれた方がおられましたら、お知らせいただけますと助かります。<br /> またフィードバックやコントリビュータも随時募集しているので、こんな機能があったら嬉しいといった要望などありましたら、気軽にIssueで相談いただいたり、PRを送っていただけたりすると嬉しいです。</p> <p>明日の記事はtakeshiさんによる「<a href="https://engineering.mercari.com/blog/entry/20250620-018cdf7e2b/" title="「自分ができる領域が増えた」-Cursorを使って未経験のKotlinコードレビューに挑戦">「自分ができる領域が増えた」-Cursorを使って未経験のKotlinコードレビューに挑戦</a>」とShuntaさんによる「<a href="https://engineering.mercari.com/blog/entry/20250622-1c1848f606/" title="初めてのWWDC25に現地参加!Apple Parkで体験した特別な数日感">初めてのWWDC25に現地参加!Apple Parkで体験した特別な数日感</a>」の2本です。引き続きお楽しみください。</p> メルペイにおける6年間のインシデント対応・管理で直面した課題と改善https://engineering.mercari.com/blog/entry/20250617-56adf5904e/https://engineering.mercari.com/blog/entry/20250617-56adf5904e/<p>こんにちは。メルペイSREの@foostanです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の14日目の記事です。 皆さんはインシデント対応は好きですか。多 [&hellip;]</p> Thu, 19 Jun 2025 10:00:25 GMT<p>こんにちは。メルペイSREの<a href="https://twitter.com/foostan">@foostan</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の14日目の記事です。</p> <p>皆さんはインシデント対応は好きですか。多くの方はこの答えにNoと答えるかもしれません。ただこの業界にいるとYesと答える方もいてなかなか楽しい気分になることがあります。ちなみに私はインシデントの非日常感に少し高揚するタイプではありますが、同僚がたくさんいる昼間に限ります。夜はできる限り携帯電話をスリープ状態にしたいものです。</p> <p>さて、今回ご紹介するのはメルペイがローンチしてからの約6年間で培ってきたインシデント対応や管理のノウハウです。また実際に直面した課題を例としていくつか取り上げ、その改善をどのようにしてきたか共有します。</p> <p>なお内容は以前登壇させて頂いた <a href="https://incident-response.connpass.com/event/343419/">Incident Response Meetup vol.2</a> のものを少しアップデートしたものになります。</p> <h2>サービスについて</h2> <p>最初にインシデントに関わる話をするにあたり我々がどのような事業を展開し、どのようなデータや規模感でサービスを運用しているのか簡単に紹介させてください。</p> <p>メルペイはメルカリアプリで使用できるスマホ決済サービスであり、iDやコード決済、メルカードを利用してお店やネットサイトで利用できます。サービスのローンチは2019年2月なので6年と少し経ちました。なおFintechの領域のサービスであり、金融情報や与信などを扱っているためサービスには高い信頼性が求められます。もし不具合が発生しサービスが中断してしまった場合は、速やかに関係各所への連絡と事後対応が求められます。</p> <p>またメルペイの規模感は</p> <ul> <li>150以上のマイクロサービス</li> <li>40以上のチーム</li> <li>1900万人以上の利用者※</li> </ul> <p>と、国内ではそれなりの規模のサービスとなります。</p> <p>※ メルペイ「電子マネー」の登録、「バーチャルカード」の設定、「メルカード」の発行、暗号資産取引口座開設を行ったユーザと「メルペイコード決済」「ネット決済」「メルペイスマート払い(翌月払い・定額払い)」等の利用者の合計(自主退会・重複を除く)2025年3月末時点</p> <h2>システム / 組織構成</h2> <p>我々のサービスはマイクロサービスアーキテクチャを採用しており、小さな独立したサービスの集合体になっています。チームごとにサービスの開発や運用を分離できるため、他のサービスに依存することなく変更や拡張が可能です。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/041a2da8-screenshot-2025-06-16-at-18.32.26.png" alt="Microservice Architectureの概要図" /><br /> ロゴ出典:<br /> <a href="https://www.cloudflare.com/ja-jp/press-kit/">https://www.cloudflare.com/ja-jp/press-kit/</a><br /> <a href="https://docs.google.com/presentation/d/1fD1AwQo4E9Un6012zyPEb7NvUAGlzF6L-vo5DbUe4NQ/edit?slide=id.g1a96c6729e_19_0#slide=id.g1a96c6729e_19_0" title="Google Cloud Official Icons and Solution Architectures">Google Cloud Official Icons and Solution Architectures</a></p> <p>チームと責任範囲の例を以下に示します。API GatewayサービスやAuthorityサービス、またCDNなどの共通コンポーネントやネットワーク関連はPlatformチームが担い、ビジネスロジックを持つサービスをProductチームが担います。またPlatformチームは各Productでサービスが運用できるようにサービスのインフラの提供や運用に必要なエコシステムの提供を行っています。開発や運用は基本的にこの責任範囲のもと行っているため、なにか不具合が起きたときはそれぞれの責任範囲で復旧を行います。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/62e94cda-screenshot-2025-06-16-at-18.32.45.png" alt="Team and Responsibilityの説明図" /><br /> ロゴ出典:<br /> <a href="https://www.cloudflare.com/ja-jp/press-kit/">https://www.cloudflare.com/ja-jp/press-kit/</a><br /> <a href="https://docs.google.com/presentation/d/1fD1AwQo4E9Un6012zyPEb7NvUAGlzF6L-vo5DbUe4NQ/edit?slide=id.g1a96c6729e_19_0#slide=id.g1a96c6729e_19_0" title="Google Cloud Official Icons and Solution Architectures">Google Cloud Official Icons and Solution Architectures</a></p> <p>エンジニアリング組織と内部統制の概略図を以下に示します。3線モデルに従い、プロダクト提供を行う1線、リスクやコンプライアンス管理する2線、内部監査の3線の大きく3つに分類されます。また1線についてProductチームが効果的に動けるようにSREチームやPlatformチームが存在します。その他にも複数のチームが存在しますが今回は省略しています。</p> <p>私が所属するSREチームは大きく2つの役割があり、一つはプロダクトに近い立場で各領域の信頼性の向上や課題解決などを通してビジネスの成長に貢献するProduct SRE、もう一つはグループ全体にプラットフォームを提供してビジネスの成長を支えるPlatform SREです。インシデントに関してはProduct SREが実際の対応やサポート、ポストモーテムのレビューなどを行っており、Platform SREがインシデント管理のためのツールの選定や導入等を行っています。またインシデント管理のポリシーや対応フローの作成などインシデントに関わる統制はIT Riskとともに行っています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/2893791a-screenshot-2025-06-16-at-18.33.01.png" alt="Engineering Organizationの概要図" /></p> <h2>インシデント対応・管理</h2> <p>続いて我々が行っているインシデント対応や管理方法について紹介します。<br /> そもそも「インシデント」と言ってもいくつか意味を持ちますが、我々は以下のように分類しています。</p> <ul> <li><strong>システムインシデント</strong>: システムトラブル等による予期せぬサービス中断や品質の低下など</li> <li><strong>セキュリティインシデント</strong>: サイバー攻撃、システムの脆弱性による情報漏洩など</li> <li><strong>事務事故</strong>: 事務作業によるミスや不正による個人情報漏洩など</li> <li><strong>不正や犯罪</strong>: アカウントの不正利用など</li> </ul> <p>なお本記事は基本的にシステムインシデントについての話です。特に言及がなく「インシデント」と記載する場合はシステムインシデントを指しています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/dfb5f64f-screenshot-2025-06-16-at-18.33.13.png" alt="Incident Managementの概要図" /></p> <p>インシデント管理は準備、対応、学びのサイクルを繰り返します。各フェーズについて我々が実際に取り組んでいることの例をいくつかご紹介します。</p> <h3>インシデントへの備え</h3> <h4>SLO / アラートの整備</h4> <p>システムの異常を検知して対応に移れるようにモニターを整備します。どのようなモニターを用意するかはサービスや組織によってさまざまかと思いますが、我々はSLOをベースとしたものを用意しています。また異常を検知した後に原因を追求できるようにオブザーバビリティを確保したり、Dashboardを整備してシステムが正常に動いているかどうか確認できるような体制を取っています。</p> <h4>オンコールの整備</h4> <p>インシデントはいつ発生するかわかりません。仕事をしている日中かもしれないし、休日かもしれない。または夜中に発生する可能性もあります。なのでどのようなときでもアラートを受け取って対応できるようにローテーションを組んで待機します。なお弊社では社内規定を決めて手当が出るようにしています。異常にそなえて休日や深夜にも直ぐに行動ができるように待機するので精神的にも肉体的にも負荷がかかります。またこれは業務なので公平性を保つためにもこのような社内規定は重要です。</p> <h4>マニュアルの整備</h4> <p>緊急の対応は誰が実施するかわからないのでマニュアルを作成して誰でも対応できるように備えておくのが理想的です。前回の対応ログ等を残しておき、参照しやすいようにしておくのも効果的でしょう。最近だとAIの技術が急激に発展してきているので過去の実績をRAGなどによって与えられればAI Opsも現実味を帯びてきます。</p> <h3>インシデントへの対応</h3> <h4>異常検知</h4> <p>サービスの異常を即座に捉えるために、アラートでオンコールのメンバーに連絡を送ります。監視と通知の概略は以下のとおりです。我々はGoogle CloudやCloudflareを利用しており、それをDatadogでモニタリングしています。異常を検知するとPagerDuty経由で電話を鳴らしたりSlack上に通知を行います。なお社内にはインシデント情報を共有するMercariグループ共通のSlackチャンネルがあり、一次情報はそこに集約されます。お客さまからのお問い合わせなどの情報もCS経由でここに集まります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/b3125a1c-screenshot-2025-06-16-at-18.33.36.png" alt="Application Monitoringの概要図" /><br /> ロゴ出典:<br /> <a href="https://www.cloudflare.com/ja-jp/press-kit/">https://www.cloudflare.com/ja-jp/press-kit/</a><br /> <a href="https://www.datadoghq.com/about/resources/">https://www.datadoghq.com/about/resources/</a><br /> <a href="https://brandguides.brandfolder.com/pagerduty/logo">https://brandguides.brandfolder.com/pagerduty/logo</a><br /> <a href="https://slack.com/intl/ja-jp/media-kit">https://slack.com/intl/ja-jp/media-kit</a><br /> <a href="https://docs.google.com/presentation/d/1fD1AwQo4E9Un6012zyPEb7NvUAGlzF6L-vo5DbUe4NQ/edit?slide=id.g1a96c6729e_19_0#slide=id.g1a96c6729e_19_0" title="Google Cloud Official Icons and Solution Architectures">Google Cloud Official Icons and Solution Architectures</a></p> <h4>インシデントの識別</h4> <p>発生した内容や影響度からSEV(重大度)を見積もり対応を行います。SEVのレベルごとにポリシーを設けており、その後の対応方針が決まります(詳細は後述)。ただしいずれのレベルにおいても最優先でインシデントの緩和や解決に動き始めます。</p> <h4>エスカレーション / 通知</h4> <p>影響が社外に及ぶ場合は、並行してステークホルダーへの周知を行います。インシデントコマンダーもしくは連絡役を専任し、社内外の連絡窓口を一本化。お客さま、VPs、パートナー企業へ影響範囲・暫定対応・次回更新予定を通知します。</p> <h4>インシデントの緩和 / 解決</h4> <p>インシデントの対応は関連するチームが主体となって進めます。影響度が単一のプロダクトチームに閉じる場合はそのチームのPdMやEM、テックリードなどがインシデントコマンダーとなりインシデントの緩和や解決、その後の処理を行います。また規模が大きく複数のプロダクトチームをまたぐ場合はSREやPlatformチームなど組織横断で動きやすいチームが中心となって対応します。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/230e1ddb-screenshot-2025-06-16-at-18.33.57.png" alt="Incident Responseの概要図" /></p> <h3>インシデントからの学び</h3> <h4>ポストモーテム</h4> <p>インシデントが解決したらなるべく早くポストモーテムを実施します。社内ではポストモーテムのガイドやテンプレートを用意して効果的に振り返りが行えるような体制を整えています。最近ではSlackの会話を要約したり時系列の情報を収集したりするためにAIの活用も進めています。</p> <h4>恒久対応および再発防止</h4> <p>ポストモーテムで特定された根本原因に対しては、一時しのぎではなく恒久的な対策を計画します。コードの修正だけでなく、運用プロセスや組織構造に起因するケースも多いため、変更管理やレビュー体制まで含めて見直します。また対応策は実現可能なものを策定し完了日を設定することを重要視しています。</p> <h4>レポート作成 / 共有</h4> <p>社内で整備しているテンプレートを利用してインシデントレポートを作成します。インシデントが発生してから解決するまでの時系列情報、被害情報、発生原因、対応内容、根本原因、事後改善策などの項目が含まれます。また後に分析できるように内容や原因はいくつかのカテゴリに分類して記録しています。レポート作成においても最近はAI活用の試みを始めており、できる限り早く情報共有ができるように改善を行っています。</p> <h4>インシデント分析</h4> <p>過去インシデントのデータを集約し、ダッシュボードで傾向を可視化しています。たとえばチーム、原因、SEV、MTTRなどをグラフ化し特定の領域の増加傾向の識別やプロセス等を改善した際の効果測定などに利用しています。また未解決インシデントや未実施のポストモーテムの数を可視化することでインシデントの管理プロセスが正常に動いているかどうかを定期的に監視し、悪化傾向にあればプロセス全体の見直しをするなどの判断も行っています。</p> <h3>インシデント識別と対応ポリシー</h3> <p>重大度を示すSEVの定義と対応ポリシーは以下のとおりです。なお公開用に抽象度が高い表現をしていますが、社内ではもっと詳細な定義があります。</p> <table> <thead> <tr> <th>SEV</th> <th>概要</th> <th>対応ポリシー</th> </tr> </thead> <tbody> <tr> <td>SEV1</td> <td>極めて重大なインシデント</td> <td>全社で最優先に対応 <br /> プロジェクト化して継続的に改善</td> </tr> <tr> <td>SEV2</td> <td>多くのお客さまに影響を与える重大なインシデント</td> <td>関係チームで最優先に対応 <br /> 恒久対応・再発防止策の完了をIT Riskチームでトラッキング</td> </tr> <tr> <td>SEV3</td> <td>お客さまに影響を与えるインシデント</td> <td>関係チームで優先的に対応 <br /> 恒久対応・再発防止策の完了をチームでトラッキング</td> </tr> <tr> <td>SEV4</td> <td>お客さまに影響はないが対応が必要なインシデント</td> <td>関係チームで対応 <br /> 恒久対応・再発防止策の完了をチームでトラッキング</td> </tr> <tr> <td>SEV5</td> <td>お客さまに影響はなく対応が不要と判断したインシデント</td> <td>対応不要</td> </tr> </tbody> </table> <p>SEVの定義は日々運用していく中で見直しを行っています。SEVの運用にはいくつかの課題がありますが、その一つは正しく選べないというものです。選択するための基準はありますが、機械的に完璧に定義することは難しく最終的にはどうしても人の判断が入ります。そこで最近ではSLOの毀損度に応じて自動的に判断するなど、わかりやすくかつ即座に判断できる新しい基準を設けるための議論も行っています。</p> <h2>課題と改善</h2> <p>数年運用する中で発生した課題やその解決方法、また現在抱えている課題とそれに対して今取り組んでいること、これから取り組もうとしていることなどをご紹介します。一部既に上述したものも含まれています。</p> <h3>大量のアラート</h3> <p>インシデントまたはその予兆を知らせるアラートも大量に発生すると対応しきれません。また不要なアラートに埋もれてしまい、重要なアラートを見逃してしまうことで、インシデント対応の初動が遅れるリスクもあります。更にこのようなアラートを放置すると割れ窓理論によって状況が悪化する恐れがあります。</p> <h4>SLOアラート</h4> <p>この問題を解決するために、CPUやメモリの使用率といったシステム内部のメトリクスではなく、お客さまへの影響度を指標化した SLO(Service Level Objective)ベースのアラートを採用しました。SLI(Service Level Indicator)はお客さまへ影響が出たときに変化するメトリクスを選ぶ必要があり、現在はエラー率とレイテンシを広く利用しています。「ページャーから呼び出しがある = お客さまへの影響が実際に発生している」という状態が理想です。なおこのSLOアラートは何度かアップデートを繰り返しており我々も試行錯誤を続けている最中です。最近では<a href="https://engineering.mercari.com/blog/entry/20241204-keeping-user-journey-slos-up-to-date-with-e2e-testing-in-a-microservices-architecture/">E2E Testを用いたマイクロサービスアーキテクチャでのUser Journey SLOの継続的最新化</a>で取り上げたCritical User Journeyに基づいたSLOアラートの仕組みが軌道に乗り始めており、運用のさまざまな場面での活用を進めています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/17a73f8c-screenshot-2025-06-16-at-18.34.17.png" alt="SLO Monitoringの概要図" /><br /> ロゴ出典:<br /> <a href="https://www.datadoghq.com/about/resources/">https://www.datadoghq.com/about/resources/</a><br /> <a href="https://brandguides.brandfolder.com/pagerduty/logo">https://brandguides.brandfolder.com/pagerduty/logo</a><br /> <a href="https://slack.com/intl/ja-jp/media-kit">https://slack.com/intl/ja-jp/media-kit</a><br /> <a href="https://brand.hashicorp.com/product_logos">https://brand.hashicorp.com/product_logos</a></p> <h3>第一報の遅延</h3> <p>インシデントを検知したあとの情報共有は、社内だけでなく社外に対しても迅速に行う必要があります。第一報が遅れると対応そのものが遅延するだけでなく、現場や関係者を混乱させ、さらなる被害拡大を招くおそれがあります。</p> <h4>自動報告システム</h4> <p>SLO を基準とし、一定以上毀損した場合にあらかじめ登録された連絡先へ自動で第一報を送信する仕組みを導入しています。誤報を恐れずとにかく素早く第一報を送ることをコンセプトとしていますが、SLO をベースにしているため一定の精度も担保できます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/0934202c-screenshot-2025-06-16-at-23.09.33.png" alt="Auto Escalationの概要図" /><br /> ロゴ出典:<br /> <a href="https://www.datadoghq.com/about/resources/">https://www.datadoghq.com/about/resources/</a><br /> <a href="https://slack.com/intl/ja-jp/media-kit">https://slack.com/intl/ja-jp/media-kit</a><br /> <a href="https://aws.amazon.com/jp/architecture/icons/">https://aws.amazon.com/jp/architecture/icons/</a></p> <h3>情報過多</h3> <p>インシデント対応中は現場が混乱し、情報が過剰に流れてきます。また、途中から参加したメンバーが状況を瞬時に把握するのは困難です。熟練したインシデントコマンダーであればこうしたケアも行えますが、実際にはうまく機能しないことのほうが多いです。</p> <h4>インシデントサマライザー</h4> <p>Slack の会話内容を自動で要約し、影響を受けたサービス、その被害状況、原因などをまとめて表示できるようにしています。LLM を利用しているため、プロンプトを変更するだけでさまざまなフォーマットへ容易に拡張できます。外部向け報告資料やポストモーテム資料の作成にも活用可能です。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/e3a6eb2f-screenshot-2025-06-16-at-18.34.30.png" alt="Incident Summarizerの概要図" /></p> <h3>インシデント管理</h3> <p>ガイドが浸透しない、ポリシーが守られない、ポストモーテムがいつまでも終わらない、恒久対応・再発防止策の実施が進まないなど決められたインシデント管理プロセスの統制を取るのが難しいという問題があります。</p> <h4>コミュニティの形成</h4> <p>インシデント対応・管理を向上させるためには、関係者全員の理解が欠かせません。まずは「自分ごと」として捉え、主体的に対応することが第一歩です。そのために各チームから代表者を選出してコミュニティを組織し、インシデント管理状況の共有、分析結果の報告、ナレッジ共有、課題の整理や解決策の立案・実施などを自主的に進められる体制を構築しました。ただし長年運用をしていると活動が少なくなってしまう時もあったため、SREやIT Riskの責務として継続することが重要だと感じています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/cb41afd8-screenshot-2025-06-16-at-18.34.37.png" alt="Incident Management Committeeのドキュメント" /></p> <h3>インシデント分析</h3> <p>インシデントに関するデータは順調に蓄積されていますが、現状を手軽に可視化・分析したいというニーズが高まっています。</p> <h4>インシデントダッシュボード</h4> <p>インシデントの発生状況や要素別の集計結果、未対応タスクなどを可視化し、状況を迅速に把握できるダッシュボードを整備しました。しかし、現時点では詳細な分析まで踏み込めておらず、類似インシデントの検出や共通原因の特定といった高度な分析およびデータ活用は今後の課題です。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/c8967781-screenshot-2025-06-16-at-18.34.46.png" alt="Incident Dashboardの概要図" /></p> <h3>AI活用</h3> <p>最近の AI 技術の発展により、インシデント管理で有効に活用できる場面が急速に増えていると感じます。たとえば、不要なアラートが増えすぎている問題に対しては AI に分析させて削減案を自動で提案させたり、インシデント対応中に過去の類似インシデントを検索して対応の補助に利用できます。また、インシデント管理ツールの MCP サーバーを用意し、AI 経由でレポートの作成やデータ入力を依頼するなど、さまざまなアイデアが提案され実装が進んでいます。以前公開した<a href="https://engineering.mercari.com/blog/entry/20250206-llm-sre-incident-handling-buddy/">LLM x SRE: メルカリの次世代インシデント対応</a>で紹介したIBISもその一例です。</p> <p>AI 関連領域は目覚ましいスピードで進化しており、IBIS もアーキテクチャのアップデートや他の社内ツールとの連携を継続的に進めています。さらに SaaS でも<a href="https://www.datadoghq.com/blog/bits-ai-sre/">Bits AI SRE</a>のような AIを 活用したインシデント対応機能が登場しつつあります。</p> <h2>最後に</h2> <p>本記事では、6年間にわたる試行錯誤を通じて得られたインシデント対応・管理に関する知見をご紹介しました。SLO を利用した異常検知、コミュニティ形成によるポリシー適用と運用促進、ポストモーテムの徹底、AI 活用による効率化など、継続的に改善を進めています。インシデントを完全にゼロにすることはできませんが、仕組みに落とし込み、改善サイクルを回し続ければ、サービスの安定化とチームの強化につながると考えています。</p> <p>本記事が皆さまの運用を一歩前に進めるヒントになれば幸いです。AIがもたらす大きな変化とカオスを楽しみながら運用を楽にし、安眠を勝ち取りましょう。</p> <p>明日の記事はorfeonさんの「<a href="https://engineering.mercari.com/blog/entry/20250620-mercari-pipeline-beta1/" title="Mercari Pipeline (旧Mercari Dataflow Template) v1を公開しました">Mercari Pipeline (旧Mercari Dataflow Template) v1を公開しました</a>」とfivestarさんの「<a href="https://engineering.mercari.com/blog/entry/20250618-27069c49cc/" title="gRPC Federationを使った3rd party API開発事例:マネーフォワード連携から学ぶ実装ノウハウ">gRPC Federationを使った3rd party API開発事例:マネーフォワード連携から学ぶ実装ノウハウ</a>」の2本です。引き続きお楽しみください。</p> メルカリモバイル開発チームでAI Hackathonをした話https://engineering.mercari.com/blog/entry/20250613-64e628404a/https://engineering.mercari.com/blog/entry/20250613-64e628404a/<p>こんにちは。メルカリモバイル Backend チームでエンジニアリングマネージャーをしている @k_kinukawaです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2 [&hellip;]</p> Tue, 17 Jun 2025 10:00:17 GMT<p>こんにちは。メルカリモバイル Backend チームでエンジニアリングマネージャーをしている <a href="https://x.com/k_kinukawa">@k_kinukawa</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の12日目の記事です。</p> <p>2025年4月21日にメルカリモバイル開発チームでオフサイトミーティングを実施し、その中でAI Hackathonを開催しました。チーム内でのAI活用を促進することを目的とし、約20名のソフトウェアエンジニア・PM・QAエンジニアが参加しました。</p> <h2>なぜAI Hackathonを実施したのか</h2> <p>メルカリモバイル開発チームでは、2025年3月に社内の生成AI開発ツールのPoCに参加する形でCursor、Devin、Gemini Code Assistのアカウントをソフトウェアエンジニアに付与し、積極的な活用を推奨していました。しかし、4月中旬の時点で実際の活用は十分に進んでいませんでした。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/be442f95-mobile-ogp-1024x538.png" alt="" /></p> <p><a href="https://about.mercari.com/press/news/articles/20250304_mercarimobile/" title="メルカリモバイルは3月4日にローンチしたばかりの新サービス">メルカリモバイルは3月4日にローンチしたばかりの新サービス</a>で、やりたいこともやるべきことも山積している状況でした。一方で、生成AI開発ツールを取り巻く状況は日々ものすごいスピードで変化しており、忙しい中で最新の情報をキャッチアップして業務で活用するのはなかなかハードルが高い状況でした。</p> <p>そんな中、エンジニアリングマネージャーの週次定例ミーティング内でAI Labs TeamのマネージャーからCursor bootcampを実施したという共有がありました。Cursor bootcampでは以下のような取り組みが行われたとのことです。</p> <ul> <li>Cursorのリファレンスを読み合わせる(1時間)</li> <li>各々、取り組むことを決めて個別に作業する(2時間)</li> </ul> <p>結果として参加者間での知識レベルとCursorに対する期待値が揃い、Cursor活用や議論が活発になったとのことでした。</p> <p>また、<a href="https://engineering.mercari.com/blog/entry/20250604-pcp-llm-week/">PCP LLM Week</a>のような大規模な取り組みの話も耳にしていました。1週間という期間をかけて組織全体でAI活用に取り組むという非常に興味深い試みでしたが、メルカリモバイルの現状では同規模の時間を確保することは難しい状況でした。</p> <p>これらの事例を参考に、現実的に実行可能な形でチームのAI活用を推進するために<strong>1日という限られた時間で集中的にAI活用を体験する</strong>オフサイトイベントを企画しました。<br /> 普段の忙しい業務の中では断片的にしか触れないAIツールについて、まとまった時間を確保して一気にキャッチアップし、Hackathon形式で実際に楽しく手を動かして理解を深めることを目的としました。また、全員が同じタイミングで同じ体験をすることで、その後のチーム内での情報共有や議論の土台を作ることも狙いでした。</p> <h2>オフサイトの実施内容</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/96ace9ec--2025-06-11-14.07.04-1024x766.jpg" alt="" /></p> <p>オフサイトミーティングは社外の貸し会議室を利用しました。<br /> 参加対象者はソフトウェアエンジニアだけでなく、PM、QAエンジニアも含めました。<br /> 当日までに参加者全員に対してCursorのアカウント発行とダウンロードを実施しました。</p> <p>時間割は以下の通りです。</p> <ul> <li><strong>午前</strong>: アイスブレイク、相互理解セッション(偏愛マップ、ドラッカー風エクササイズ)</li> <li><strong>午後前半</strong>: AIキャッチアップセッション(0.5時間)</li> <li><strong>午後後半</strong>: AI Hackathon(2.5時間 + 発表1時間)</li> </ul> <p>余談ですが、このオフサイトではAI Hackathonだけでなく、相互理解のためのセッションとして偏愛マップ、ドラッカー風エクササイズも実施しました。<br /> このセッションを通じて、チームメンバーのパーソナルな一面を知ることができたり、お互いの期待値のズレを認識することができたりと非常に有意義でした。</p> <h2>AIキャッチアップセッション</h2> <p>Hackathonに入る前に、全員の認識を揃えるためのキャッチアップセッションを実施しました。<br /> 事前に私が社内外の情報を取りまとめて、スライドで発表しました。<br /> 内容は以下の通りです。</p> <ul> <li><strong>組織目標の共有</strong>: エンジニアリング組織として設定された重要な指標「全てのエンジニア100%が何らかのAIコーディングアシスタントツールを活用し生産性を高める」を改めて確認</li> <li><strong>AIツール利用ガイドライン</strong>: 社内におけるAIツールの基本的な利用方法が記載されたガイドラインの紹介、AIツールにどんな情報を入力してよいかの確認</li> <li><strong>社内のCursor利用ガイドライン</strong>: コードのindexingについての理解、.cursorindexingignoreファイルの理解</li> <li><strong>MCP Serverとセキュリティ</strong>: MCP Serverの説明、使用可能なMCP Serverの紹介、MCP Server利用時のセキュリティに関する注意点</li> <li><strong>社内のAIに関する取り組み、活用事例紹介</strong>: 社内で利用可能なAIツールとその活用事例の紹介、AI活用をしているプロジェクトの紹介</li> <li><strong>社外の事例紹介</strong>: 社外のAI開発ツール活用事例紹介</li> </ul> <p>このセッションを通じて、メルカリ社員として社内業務でAI開発ツールを利用するために必要な基礎知識のキャッチアップと、AI開発ツールを使ってできることの認識を合わせることができました。</p> <h2>AI Hackathon</h2> <p>2.5時間の時間を使って、各々が事前に準備したアイデアの実現に取り組みました。<br /> Hackathonを開催した4月末は、世の中的にMCP Serverが非常に注目されていたタイミングだったため、多くのメンバーがMCP Serverのセットアップとそれを活用したアイデアの検証にチャレンジしていました。<br /> 私は以下のようなことに取り組みました。</p> <ol> <li> <p><strong>MCP Serverセットアップ支援</strong></p> <ul> <li>PMとQAエンジニアに対して、CursorとMCP Server(Confluence、Jira、Figma)のセットアップをサポート</li> </ul> </li> <li> <p><strong>Terraform編集タスクをAIに任せる実験</strong></p> <ul> <li>あるメンバーへの権限付与タスクをCursorのAgentに依頼</li> <li>Terraformコードの変更からプルリクエスト作成まで、一切手動でコードを書かずに実施</li> </ul> </li> <li> <p><strong>自作MCP Server開発チャレンジ</strong></p> <ul> <li>Cursor Agentを使って、自作のMCP Server作成に挑戦(時間切れで未完成)</li> </ul> </li> </ol> <p>チームメンバーがチャレンジしたAIを使ったアイデアも一部紹介します。</p> <ul> <li>MCP Server開発・活用 <ul> <li>Cursor と Jira、Figma、Confluence、Spanner、BQとのMCP Server 連携そその活用方法の模索</li> <li>Proto MCP Server 開発</li> <li>GitHub mermaid sequence diagram 生成</li> </ul> </li> <li>AI開発支援ツール活用 <ul> <li>AI Reviewer 作成</li> <li>Cursorでのリファクタリングとテスト 生成</li> <li>GitHub MCP Server 連携</li> </ul> </li> <li>デザイン・プランニング領域 <ul> <li>FigmaAI調査</li> <li>新機能のプランニング(1時間で設計からリソース計画まで作成)</li> <li>テキストからワイヤーフレーム作成</li> </ul> </li> <li>業務改善 <ul> <li>便利ツールの開発</li> <li>Test Case自動生成</li> <li>アンケート分析</li> </ul> </li> </ul> <p>私が今回のHackathonで最も印象的だったのは、PMがCursorとMCP Serverを活用して、メルカリモバイルの新機能プランニングに取り組んでいたことでした。<br /> Hackathonの最初、Cursorにメルカリモバイルの最新のソースコードを読ませMCP ServerでConfluenceに接続できる状態までサポートしたのですが、そこからたった1時間程度で新機能開発に関する仕様作成、主要開発項目の洗い出し、既存機能を踏襲した設計、開発・QA工数とスケジュール算出を行いました。もちろんこれを使っていきなり開発に入れるわけではないのですが、叩き台としては十分使えるものが作れてしまったことに本人含めて皆驚いていました。</p> <h2>AI Hackathonを終えて</h2> <p>事後アンケートでは「CursorなどのAIツールをしっかり触る時間を取れて新しい技術をキャッチアップすることができてよかった」「業務から離れる時間を取ってAI活用にフォーカスする時間を取ることができて良かった」といった回答を頂きました。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/654d7fac--2025-06-11-10.21.17-1024x409.png" alt="" /><br /> ↑は<a href="https://getdx.com/" title="DX">DX</a>というツールを使ってメルカリモバイルBackendチーム内のCursorによるline changesを集計したグラフです。4月21日以降、Cursorが日常的に活用されるようになったことが数字からも読み取れます。</p> <p>また、チームのエンジニアによる<a href="https://speakerdeck.com/fumiyakume/cursorwo-dao-ru-dakeziyanaku-huo-yong-made-merukari2000ren-zhan-kai-noriaru?slide=8">メルカリ内製MCP Server リポジトリ</a>へのコントリビュート(いくつかのサービスの追加)も行われました。</p> <p>興味ある方は是非こちらも御覧ください。</p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20250609-4660151b47/">Sourcegraph × 自作MCP Serverによる社内コード検索連携の取り組み</a></li> <li><a href="https://speakerdeck.com/fumiyakume/cursorwo-dao-ru-dakeziyanaku-huo-yong-made-merukari2000ren-zhan-kai-noriaru">Cursorを&quot;導入&quot;だけじゃなく&quot;活用&quot;まで メルカリ2000人展開のリアル</a></li> </ul> <p>現在ではCursorだけでなく、DevinやClaude Code Agentの利用も徐々に増えています。</p> <h2>まとめ</h2> <p>今回のHackathonを通じて、以下のような学びがありました。</p> <p><strong>1. まとまった時間確保の重要性</strong></p> <ul> <li>環境は整っていても、忙しい日常では新しいツールを試す時間が取れない</li> <li>目的のために強制的に時間を確保することで、全員が「最初のハードル」を超え同じスタートラインに立てる</li> </ul> <p><strong>2. 全員参加による学習効果</strong></p> <ul> <li>皆で一緒に作業することで、その場で疑問を解消できる</li> <li>情報共有により全員の理解を一致させることができる</li> <li>ソフトウェアエンジニア以外のメンバーも効果的にAI開発ツールを活用できる可能性を確認することができた</li> </ul> <p>個人の自発的な学習だけでなく、組織として意図的に学習機会を創出することの有用性を実感することができました。これからも組織としてAI-Nativeになるための機会や仕組みを継続的に作っていきたいと思います。</p> <p>一方で、日々のミーティングや業務の量を見直していくことで余裕が生まれ、日常的に新しいことにチャレンジできる状態も作り出せるのではないかと考えています。これは今後の大きな課題だと考えています。</p> <p>明日の記事は@David, @anzaiで「<a href="https://engineering.mercari.com/en/blog/entry/20250617-building-a-flexible-checkout-solution-frontend-architecture-for-multi-service-integration/" title="Building a Flexible Checkout Solution: Frontend Architecture for Multi-Service Integration">Building a Flexible Checkout Solution: Frontend Architecture for Multi-Service Integration</a>」です。引き続きお楽しみください。</p> SRE2.0: LLMサービスの信頼性を測る新しい評価指標の紹介https://engineering.mercari.com/blog/entry/20250612-d2c354901d/https://engineering.mercari.com/blog/entry/20250612-d2c354901d/<p>こんにちは。Fintech SREの佐藤隆広(@T)です。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の11日目の記事です。 Google社が提唱し、Site [&hellip;]</p> Mon, 16 Jun 2025 10:00:35 GMT<p>こんにちは。Fintech SREの佐藤隆広(@T)です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の11日目の記事です。</p> <p>Google社が提唱し、<a href="https://sre.google/books/">Site Reliability Engineering Book</a>によって広く知られるようになったSREの信頼性マネジメントは、開発と運用の関係性を再定義し、SLI/SLOとエラーバジェットに始まり、Availability・Latency・エラーレート・トラフィック・リソース飽和度・耐久性といったような指標で補強されてきました。 ところが近年、大規模言語モデル(LLM)の進歩が著しく、サービスにLLMを利用する機会が増えることによって、</p> <ul> <li>プロンプトを数行変えただけで回答品質が変動する</li> <li>Latencyやエラーレートが良好でも幻覚(ハルシネーション)が急増する</li> <li>モデルの軽微なアップデートで回答スタイルが激変する</li> </ul> <p>といった、従来指標では見落としがちな事象に遭遇することが多くなりました。 つまり <strong>「LLMサービスの信頼性」</strong> を守るには、クラシックなインフラ指標の他に<strong>LLMサービス固有の品質指標</strong> を重ね合わせてモニタリングする必要性が迫られています。</p> <p>本記事では、LLMサービスの信頼性評価に不可欠な指標の選定から、具体的な測定・評価方法までを、DeepEvalライブラリを用いたデモを交えて紹介します。</p> <h2>1. LLMサービスの一般的評価指標</h2> <p>LLMサービスの信頼性を測る上で、どのような指標に着目すべきでしょうか? <a href="https://www.confident-ai.com/blog/llm-evaluation-metrics-everything-you-need-for-llm-evaluation">LLM Evaluation Metrics: The Ultimate LLM Evaluation Guide</a>では、下記の評価観点の代表例が挙げられていました。</p> <table> <thead> <tr> <th style="text-align: left;">指標名</th> <th style="text-align: left;">説明</th> </tr> </thead> <tbody> <tr> <td style="text-align: left;">回答の関連性 (Answer Relevancy)</td> <td style="text-align: left;">質問に対して、どれだけ適切に答えているかを測る指標</td> </tr> <tr> <td style="text-align: left;">タスク完遂度 (Task Completion)</td> <td style="text-align: left;">与えられたタスクをどれだけ正確にやり遂げられたかを測る指標</td> </tr> <tr> <td style="text-align: left;">正確さ (Correctness)</td> <td style="text-align: left;">事前に用意された正解とどれだけ一致しているかを測る指標</td> </tr> <tr> <td style="text-align: left;">幻覚の有無 (Hallucination)</td> <td style="text-align: left;">事実に基づかない内容や、デタラメな情報が含まれていないかを測る指標</td> </tr> <tr> <td style="text-align: left;">ツール使用の正確さ (Tool Correctness)</td> <td style="text-align: left;">タスクを達成するために正しいツールを選び、実行できたかを測る指標</td> </tr> <tr> <td style="text-align: left;">文脈適合性 (Contextual Relevancy)</td> <td style="text-align: left;">検索された情報が質問に対してどれだけ適切かを測る指標</td> </tr> <tr> <td style="text-align: left;">責任あるAI指標 (Responsible Metrics)</td> <td style="text-align: left;">差別的な表現や攻撃的な内容を含んでいないか、特定の属性に対して偏見を持っていないかなどを測る指標</td> </tr> <tr> <td style="text-align: left;">タスク固有指標 (Task-Specific Metrics)</td> <td style="text-align: left;">要約や翻訳など、「特定のタスク」においてLLMの性能を測るための指標</td> </tr> </tbody> </table> <p>従来のサービスの代表的な指標として、AvailabilityやLatencyなどといったインフラ系SLIを監視すれば、ユーザージャーニーと関連付けてお客さま満足度を把握することができました。 しかしLLMサービスでは、「応答が意図に沿い、事実に基づいているか」「タスクを正しく完遂できたか」といった生成品質そのものがお客さま満足度に直結します。 そのため、従来のAvailabilityやLatencyに加え、LLMサービス特有の生成品質を捉えるSLIを設計し、お客さまが「意図どおりの正しい回答を迅速に得られるか」を定量的に示せる指標体系を整える必要があります。 では、具体的にLLMサービスの指標を設計する上で、どの指標を選定するべきでしょうか。</p> <h3>1.1. 一般的評価指標の落とし穴</h3> <p>上記の表にある、回答の関連性、正確さ、幻覚の有無といった一般的な評価観点は骨格ですが、すべてのLLMサービスのユースケース固有の成功条件をキャッチアップできるとは限りません。 たとえば要約サービスなら「網羅性」や「矛盾の有無」、RAGなら「検索文脈の適合度」といった独自指標がなければ、お客さまが得る価値を測り切れないことが多いです。 <a href="https://medium.com/%40edgar_muyale/the-accuracy-trap-why-your-models-90-might-mean-nothing-f3243fce6fe8">The Accuracy Trap: Why Your Model’s 90 % Might Mean Nothing</a>という記事では、顧客離反(churn)予測モデルがテスト精度92%を達成したにもかかわらず、実運用では解約防止どころか誤警告と取りこぼしが発生し、結果として離反率が増えたことを解説しています。</p> <p>教訓としては、お客さま視点のエンドツーエンド評価を最優先にする、ということだと思われます。 LLMサービスはRAGやエージェント機構など複雑な内部構造を持ちますが、中間コンポーネントをいくら改善しても、お客さまが受け取る回答が向上しなければROIは上がりません。 ブラックボックスとしての最終出力を計測し、エンドツーエンドで測った結果が、サポート工数削減や売上向上と相関することが、そのLLMサービスの選定すべき評価指標でしょう。</p> <h3>1.2. 優れた評価指標とは?</h3> <p><a href="https://www.confident-ai.com/blog/the-ultimate-llm-evaluation-playbook">The Complete LLM Evaluation Playbook: How To Run LLM Evals That Matter</a> では、優れた評価指標の条件として、下記3点が挙げられていました。</p> <ul> <li><strong>定量的であること(Quantitative)</strong> <ul> <li>評価結果として数値スコアを算出できること。数値で評価できれば「合格ラインとなるしきい値」を設定したり、スコアの時系列変化を追ってモデル改善の効果を測定したりできることが望ましいです</li> </ul> </li> <li><strong>信頼性が高いこと(Reliable)</strong> <ul> <li>常に安定した評価結果が得られること。LLMの出力に予測不能な揺らぎがある以上、評価指標まで不安定では困ります。例えばLLMを用いた評価手法(後述のLLM-as-a-judgeなど)は従来手法より高精度な反面、評価結果にばらつきが出やすい傾向があるため注意が必要です </li> </ul> </li> <li><strong>正確であること(Accurate)</strong> <ul> <li>LLMモデルの性能を実際の人間の評価と近い基準で的確に反映できること。評価スコアが高い出力=人間にとって良好と感じられる出力、となるのが理想であり、そのためには人間の期待と整合した基準で評価する必要があります</li> </ul> </li> </ul> <p>また、評価指標値がいくら高いスコアを叩き出しても、売上やお客さま満足度などのビジネス成果につながらなければ意味がありません。 同記事では、これを <strong>Metric Outcome Fit(指標と成果のつながり)</strong> と呼んでおり、「現場で行われるLLMの指標評価の95%は、このつながりがなく価値を生まない」とまで言及されていました。ビジネス上「良い結果」とみなされるケースを指標が確実に“良い”と判定できるか、上記を確認・調整し続けることが、指標を外さない唯一の方法、と紹介されています。</p> <h2>2. 指標の評価方法の全体像</h2> <p>次に、指標を実際に評価する手法の種類について紹介します。大別すると、下記の4つが存在し、それぞれに長所・短所があります。</p> <ul> <li>統計的手法 (string-based / n-gram based / surface base)</li> <li>LLM以外のモデルを用いる手法 (classifier / learned metrics / small-LM metrics)</li> <li>統計的手法、LLM以外のモデルを同時に用いるハイブリッドな手法 (embedding-based metric)</li> <li>LLMそのものを用いる手法 (LLM based / generative evaluator)</li> </ul> <h3>2.1 統計的手法</h3> <p>人手で作成した正解データと出力テキストを文字列レベルで比較し、類似度を測って評価する手法です。</p> <ul> <li>BLEU <ul> <li>モデル出力と期待される正解文との1〜4-gram 精度を平均し、brevity penalty を乗法して精度ベースで算出し、長さの過不足に対するペナルティも加味したスコアを与えます</li> </ul> </li> <li>ROUGE <ul> <li>要約評価によく用いられ、ROUGE-Lは LCS(最長共通部分列)ベースで再現率と精度の F1を取り、ROUGE-1/2 が n-gram再現率に基づき要約が元文書をどれだけカバーしているかを測ります</li> </ul> </li> <li>METEOR <ul> <li>精度と再現率の両面から評価し、語順の違いや同義語のマッチングも考慮する指標です。(最終スコアは精度・再現率の調和平均に語順ペナルティを乗法して算出)</li> </ul> </li> <li>編集距離(<a href="https://note.com/noa813/n/nb7ffd5a8f5e9">レーベンシュタイン距離</a>) <ul> <li>出力と正解の文字列差分そのものを測定する指標。実務では複数文長の比較にそのまま使うことは稀で、キャッチアップコストの割には使用されていないケースが多いようです</li> </ul> </li> </ul> <p>ref: <a href="https://avinashselvam.medium.com/llm-evaluation-metrics-bleu-rogue-and-meteor-explained-a5d2b129e87f">LLM evaluation metrics — BLEU, ROGUE and METEOR explained</a></p> <p>これら統計的指標は計算が単純で再現性(一貫性)は高いですが、テキストの意味や文脈を考慮しないためLLMが生成するような長文回答や高度な推論を要する出力の評価には不向きです。事実、純粋な統計手法では出力の論理的整合性や文意の正しさまでは評価できず、複雑な出力に対しては精度が不十分だとされています。</p> <h3>2.2. LLM以外のモデルを用いる手法</h3> <p>評価専用の機械学習モデルを用いて、分類モデルや埋め込みモデルなど、比較的軽量な自然言語処理モデルを使って評価する手法です。</p> <ul> <li>NLI(自然言語推論)モデル <ul> <li>LLMの出力が与えられた参照テキスト(事実情報など)に対して、整合しているか(Entailment)/ 矛盾しているか(Contradiction)/ 無関係か(Neutral)を分類できます。この場合、モデルの出力スコアは「論理的にどれだけ一貫しているか」を表す0.0~1.0の確率値になります</li> </ul> </li> <li>Transformer型の言語モデル(NLI, BLEURTなど)をベースに学習した専用モデル <ul> <li>LLMの出力と期待される正解との類似度をスコアリングして計測する手法で、モデルベース手法では、テキストの意味をある程度考慮した評価が可能になりますが、評価モデル自体に不確実性があるため、スコアの一貫性(安定性)に欠ける場合があります。例えば、NLIモデルは入力文が長大になるとうまく判断できなかったり、BLEURTは学習データの偏りに影響を受け評価が偏る可能性が指摘されています</li> </ul> </li> </ul> <h3>2.3. 統計的手法、LLM以外のモデルを同時に用いるハイブリッドな手法</h3> <p>上記の中間に位置する手法で、事前学習済み言語モデルの埋め込んでベクトル化した値と、統計的な距離計算を組み合わせて評価する手法です。</p> <ul> <li><a href="https://openreview.net/pdf?id=SkeHuCVFDr">BERTScore</a> <ul> <li><a href="https://en.wikipedia.org/wiki/BERT_\(language_model\)">BERT</a>などで求めた各単語の文脈ベクトル同士の<a href="https://atmarkit.itmedia.co.jp/ait/articles/2112/08/news020.html">コサイン類似度</a>を計算し、出力文と参照文の意味的な重なり度合いを測定します</li> </ul> </li> <li><a href="https://arxiv.org/abs/1909.02622">MoverScore</a> <ul> <li>出力文と参照文それぞれについて単語埋め込みを用いた分布を作成し、そこから<a href="https://zenn.dev/derwind/articles/dwd-optimal-transport01#%E6%9C%80%E9%81%A9%E8%BC%B8%E9%80%81%E8%B7%9D%E9%9B%A2">Earth Mover’s Distance(最適輸送距離)</a>を計算して両者の差異を測定します</li> </ul> </li> </ul> <p>これらの手法は単語レベル・表面レベルを超えて意味的な近さを捉えられる点で統計的手法で挙げたBLEUなどより優れていますが、結局は元となる埋め込みモデル(BERT等)の性能やバイアスに影響されるという弱点があります。例えば専門領域の文脈や最新の知識について、事前学習モデルが適切なベクトル表現を持っていなければ正確な評価はできません。また評価モデルが内包する社会的バイアスがスコアに現れるリスクもあります。</p> <h3>2.4. LLMを用いた手法(LLM-as-a-judge)</h3> <p>評価手法の中でも近年注目されているのが、LLM自体に計測させて出力品質を評価させる手法、LLM-as-a-judgeです。 高度なLLMに「与えられた回答が基準を満たすか評価してください」と指示を与え、モデルから評価スコアや判定を引き出すアプローチになります。 LLMは文章の意味理解や複雑な判断ができるため、人間の主観に近い評価を自動化できる点が大きな長所です。 実際、GPT-4を評価者に用いる<a href="https://arxiv.org/abs/2303.16634">G-Eval</a>という手法では、評価スコアと人間評価との相関が従来の自動評価よりも大幅に向上することが、<a href="https://www.confident-ai.com/blog/g-eval-the-definitive-guide">G-Eval Simply Explained: LLM-as-a-Judge for LLM Evaluation</a>という記事でも紹介されています。 一方で、LLMベースの評価はそのモデルの応答次第で結果が変動しうるため、スコアの安定性(信頼性)に課題があります。 LLMに同じ回答を再評価させても毎回まったく同じスコアが得られる保証はなく、モデルのランダム要素や出力の揺らぎが評価結果にも影響を及ぼすためです。</p> <p>下記に、代表的なLLM-as-a-judgeの手法をピックアップしてみます。</p> <ul> <li><a href="https://arxiv.org/abs/2303.16634">G-Eval</a> <ul> <li>評価基準を1~5段階スケールで採点し、LLMが評価スコアと評価結果の理由(Chain of Thoughtの結果)を返す仕組み </li> </ul> </li> <li><a href="https://arxiv.org/abs/2210.04320">QAG Score</a> <ul> <li>出力からQA(Yes/No/Unknown)を自動生成し、原文で同じQAを解き、両者の一致率をスコアにする </li> </ul> </li> <li><a href="https://arxiv.org/abs/2303.08896">SelfCheckGPT</a> <ul> <li>同じプロンプトでN回サンプリングし、生成文同士の一貫性(例:N-gram・QA・BERTScoreなど複数の比較モード)を測って事実性を推定する。ばらつきが大きいほど幻覚の可能性が高くなる </li> </ul> </li> <li><a href="https://deepeval.com/docs/metrics-dag">DAG(deep acyclic graph)</a> <ul> <li>DeepEval が提供する決定木型メトリック。各ノードはLLM判定(Yes/No)で、経路によって固定スコアを返すため LLM-as-a-judgeなのにブール判定ノードを決定木で束ね、部分点を決定論化する </li> </ul> </li> <li><a href="https://arxiv.org/abs/2405.01535">Prometheus2 Model</a> <ul> <li>GPT-4を含む高品質ジャッジのフィードバックと多数の評価トレースで蒸留した7B/8×7Bの評価モデル。人間/GPT-4との一致率0.6〜0.7(直接採点), 72–85%(ペアワイズ比較)で立証済み</li> </ul> </li> </ul> <p>最後に、ここまで挙げた指標の計測評価方法をまとめてみたのが下記の表になります。</p> <table> <thead> <tr> <th style="text-align: left;">種類</th> <th style="text-align: left;">具体的な手法</th> <th style="text-align: left;">長所</th> <th style="text-align: left;">短所</th> </tr> </thead> <tbody> <tr> <td style="text-align: left;"><strong>統計的手法</strong></td> <td style="text-align: left;">BLEU / ROUGE / METEOR / 編集距離(レーベンシュタイン距離)</td> <td style="text-align: left;">・計算が単純で高速・再現性が高い<br /> ・追加学習が不要で実装が容易</td> <td style="text-align: left;">・意味・文脈を考慮せず表層一致のみを評価 <br />・論理整合性や高度な推論が必要な出力には不向き</td> </tr> <tr> <td style="text-align: left;"><strong>LLM 以外のモデルを用いる手法</strong></td> <td style="text-align: left;">NLI(自然言語推論)モデル / BLEURT / Transformer ベースの専用評価モデル</td> <td style="text-align: left;">・意味理解や論理的一貫性をある程度評価できる <br />・LLM より計算コストが低く、自前で fine-tune 可能</td> <td style="text-align: left;">・評価モデル自体の不確実性<br />・バイアスに依存 ・長文・専門領域で精度が低下しやすい</td> </tr> <tr> <td style="text-align: left;"><strong>ハイブリッド手法</strong></td> <td style="text-align: left;">BERTScore / MoverScore</td> <td style="text-align: left;">・埋め込みで語義的近さを捉え、統計指標より高精度<br /> ・決定論的で再現性を保ちやすい</td> <td style="text-align: left;">・埋め込み元モデルの学習範囲・バイアスに左右される <br />・最新知識や狭い専門領域では適合しにくい</td> </tr> <tr> <td style="text-align: left;"><strong>LLM を用いる手法(LLM-as-a-judge)</strong></td> <td style="text-align: left;">G-Eval / QAG Score / SelfCheckGPT / DAG (Deep Acyclic Graph) / Prometheus2 Model</td> <td style="text-align: left;">・人間評価に近い複雑な判断を自動化できる<br /> ・回答の多面的品質を一括で評価可能</td> <td style="text-align: left;">・出力が確率的でスコアに揺らぎが出やすい<br /> ・モデル利用コストが高く、プロンプトに敏感</td> </tr> </tbody> </table> <p>これら評価手法を実際に計測するには、効率的に測定するためのツールが必要です。 そこで、今回はLLM評価ライブラリの中から参考記事で垣間見ていたDeepEvalについて紹介したいと思います。</p> <h2>3. DeepEval</h2> <p><a href="https://github.com/confident-ai/deepeval">DeepEval</a> は、LLMサービスを評価するためのPythonライブラリです。 テストケースの作成、評価指標の定義、評価の実行を行うためのフレームワークを提供します。 DeepEvalは、応答の関連性、忠実性、文脈の精度など、さまざまな側面を評価する指標をサポートしており、カスタム指標や評価データセットの自動生成、Pytestのようなテストフレームワークとの統合もサポートしています。 <a href="https://deepeval.com/docs/getting-started">公式ドキュメント</a>には、詳細なインストール手順、基本的な使用方法、各種評価指標の設定方法、カスタム指標の作成方法などが詳しく解説されています。</p> <p>それでは、簡単な要約サービスを元に、評価手順を実践してみようと思います。</p> <h3>3.1 実践例: 要約サービスでの指標決定と測定方法</h3> <p>ここで想定する要約サービスは、記事やドキュメントなどの長文を入力として受け取り、その内容を短くまとめた要約文を生成するサービスです。 LLMの仕組み的に得意分野として真っ先に思いつくサービスだと思います。 今回は、グリム童話を要約して、子供でもわかるような文章で要約してくれるサービスを考えてみたいと思います。</p> <h3>3.2 指標の選定</h3> <p>要約という観点から、一般的な評価指標として思いつく指標は、<strong>回答の関連性 (Answer Relevancy)</strong>, <strong>正確さ (Correctness)</strong>, <strong>幻覚の有無 (Hallucination)</strong> です。 Deepevalの<a href="https://deepeval.com/docs/metrics-llm-evals">G-Eval</a>を利用して、上記3つの指標に対応することができますが、今回のケースでの <strong>1.2. 優れた評価指標とは?</strong> に該当するか調査する必要があります。</p> <ul> <li>定量的であること(Quantitative) <ul> <li>G-Evalは0〜1の連続スコアを返すので、評価結果として数値スコアを算出できると言えます</li> </ul> </li> <li>信頼性が高いこと(Reliable) <ul> <li>G-Evalは本来確率的ですが、LLMモデルに渡す<code>temperatureのオプションを0で呼び出し</code>、<code>evaluation_stepsを固定しCoT生成処理をスキップ</code>、<code>Rubricを指定して評価スコアを一定にする</code>という3点を実行すれば、同じ入力で同じスコアがほぼ再現させることができるので、常に安定した評価結果が得られそうです(厳密には、OpenAI側の sampling noise、 system randomness が残っており完全再現には至りません。top_p=0, seed 固定可能な API/backend を使うか,最終的には majority vote/ensemble 評価が推奨されます)</li> </ul> </li> <li>正確であること(Accurate) <ul> <li>G-Evalは参照(expected_output、今回のケースの場合、グリム童話の原文や正解データです)付きの評価であり、事実照合を中心とするタスクではG-Evalは人間判定との相関が高いことが論文・実運用の両方で示されています。</li> </ul> </li> </ul> <p>よって、今回のケースでは、<strong>回答の関連性 (Answer Relevancy)</strong>, <strong>正確さ (Correctness)</strong>, <strong>幻覚の有無 (Hallucination)</strong> の指標について、DeepEvalのG-Evalでの指標評価を使用することは妥当だと言えそうです。</p> <h3>3.3 評価観点の分解</h3> <p>次に、ピックアップした指標をどのような手順で評価させるのか、評価するために必要な観点やステップを列挙していきます。 幸いなことに、評価観点を分解する上で、参考になりそうな文献が、Google Cloudの<a href="https://cloud.google.com/vertex-ai/generative-ai/docs/models/metrics-templates">Vertex AIのドキュメント &#8211; モデルベース評価の指標プロンプト テンプレート</a>にありましたので、今回はそちらを参考に評価観点を分解していきたいと思います。</p> <ul> <li>回答の関連性 (Answer Relevancy) <ul> <li>STEP1. Identify user intent – List the explicit and implicit requirements in the prompt.</li> <li>STEP2. Extract answer points – Summarize the key claims or pieces of information in the response.</li> <li>STEP3. Check coverage – Map answer points to each requirement; note any gaps.</li> <li>STEP4. Detect off-topic content – Flag irrelevant or distracting segments.</li> <li>STEP5. Assign score – Choose 1-5 from the rubric and briefly justify the choice.</li> </ul> </li> <li>正確さ (Correctness) <ul> <li>STEP1. Review reference answer (ground truth).</li> <li>STEP2. Isolate factual claims in the model response.</li> <li>STEP3. Cross-check each claim against the reference or authoritative sources.</li> <li>STEP4. Record discrepancies – classify as omissions, factual errors, or contradictions.</li> <li>STEP5. Assign score using the rubric, citing the most significant discrepancies.</li> </ul> </li> <li>幻覚の有無 (Hallucination) <ul> <li>STEP1. Highlight factual statements – names, dates, statistics, citations, etc.</li> <li>STEP2. Compare with provided context and known reliable data.</li> <li>STEP3. Label claims as verified, unverifiable, or false.</li> <li>STEP4. Estimate hallucination impact – proportion and importance of unsupported content.</li> <li>STEP5. Assign score following the rubric and list specific hallucinated elements.</li> </ul> </li> </ul> <h3>3.4 評価スコアの算出</h3> <p>では、実際に評価測定をして評価スコアを算出してみます。 まず、要約させる題材とプロンプトを用意します。 今回、グリム童話の原文は<a href="https://ja.wikipedia.org/wiki/%E8%B5%A4%E3%81%9A%E3%81%8D%E3%82%93">赤ずきん</a>を使用し、プロンプトは下記を用意してみました。</p> <pre><code>以下のグリム童話の内容の要約を作成してください。 要件: 1. 主要な登場人物や重要な要素を特定して含める 2. 内容の流れを論理的に整理する 3. 重要な出来事や転換点を含める 4. 原文の内容に忠実であること 5. 要約の長さは500文字以内に収める グリム童話の内容: {赤ずきんの原文} 要約:&quot;&quot;&quot;</code></pre> <p>使用した評価スクリプトは下記になります。</p> <pre><code class="language-py">import asyncio import openai from deepeval.metrics.g_eval.g_eval import GEval from deepeval.metrics.g_eval.utils import Rubric from deepeval.test_case.llm_test_case import LLMTestCase, LLMTestCaseParams async def evaluate_comprehensive_metrics(client: openai.AsyncOpenAI, test_case: LLMTestCase, prompt_name: str, original_text: str) -&gt; dict: &quot;&quot;&quot;G-Evalメトリクス評価を実行&quot;&quot;&quot; # 回答の関連性評価 (Answer Relevancy) geval_answer_relevancy = GEval( name=&quot;Answer Relevancy&quot;, evaluation_steps=[ &quot;STEP1. **Identify user intent** – List the explicit and implicit requirements in the prompt.&quot;, &quot;STEP2. **Extract answer points** – Summarize the key claims or pieces of information in the response.&quot;, &quot;STEP3. **Check coverage** – Map answer points to each requirement; note any gaps.&quot;, &quot;STEP4. **Detect off-topic content** – Flag irrelevant or distracting segments.&quot;, &quot;STEP5. **Assign score** – Choose 1-5 from the rubric and briefly justify the choice.&quot;, ], rubric=[ Rubric(score_range=(0, 2), expected_outcome=&quot;Largely unrelated or fails to answer the question at all.&quot;), Rubric(score_range=(3, 4), expected_outcome=&quot;Misunderstands the main intent or covers it only marginally; most content is off-topic.&quot;), Rubric(score_range=(5, 6), expected_outcome=&quot;Answers the question only partially or dilutes focus with surrounding details; relevance is acceptable but not strong.&quot;), Rubric(score_range=(7, 8), expected_outcome=&quot;Covers all major points; minor omissions or slight digressions that don’t harm overall relevance.&quot;), Rubric(score_range=(9, 10), expected_outcome=&quot;Fully addresses every aspect of the user question; no missing or extraneous information and a clear, logical focus.&quot;), ], evaluation_params=[LLMTestCaseParams.INPUT, LLMTestCaseParams.ACTUAL_OUTPUT, LLMTestCaseParams.RETRIEVAL_CONTEXT], model=&quot;gpt-4o&quot; ) # 正確さ評価 (Correctness) geval_correctness = GEval( name=&quot;Correctness&quot;, evaluation_steps=[ &quot;STEP1. **Review reference answer** (ground truth).&quot;, &quot;STEP2. **Isolate factual claims** in the model response.&quot;, &quot;STEP3. **Cross-check** each claim against the reference or authoritative sources.&quot;, &quot;STEP4. **Record discrepancies** – classify as omissions, factual errors, or contradictions.&quot;, &quot;STEP5. **Assign score** using the rubric, citing the most significant discrepancies.&quot;, ], rubric=[ Rubric(score_range=(0, 2), expected_outcome=&quot;Nearly everything is incorrect or contradictory to the reference.&quot;), Rubric(score_range=(3, 4), expected_outcome=&quot;Substantial divergence from the reference; multiple errors but some truths remain.&quot;), Rubric(score_range=(5, 6), expected_outcome=&quot;Partially correct; at least one important element is wrong or missing.&quot;), Rubric(score_range=(7, 8), expected_outcome=&quot;Main facts are correct; only minor inaccuracies or ambiguities.&quot;), Rubric(score_range=(9, 10), expected_outcome=&quot;All statements align perfectly with the provided ground-truth reference or verifiable facts; zero errors.&quot;) ], evaluation_params=[LLMTestCaseParams.ACTUAL_OUTPUT, LLMTestCaseParams.RETRIEVAL_CONTEXT], model=&quot;gpt-4o&quot; ) # 幻覚の有無評価 (Hallucination) geval_hallucination = GEval( name=&quot;Hallucination&quot;, evaluation_steps=[ &quot;STEP1. **Highlight factual statements** – names, dates, statistics, citations, etc.&quot;, &quot;STEP2. **Compare with provided context** and known reliable data.&quot;, &quot;STEP3. **Label claims** as verified, unverifiable, or false.&quot;, &quot;STEP4. **Estimate hallucination impact** – proportion and importance of unsupported content.&quot;, &quot;STEP5. **Assign score** following the rubric and list specific hallucinated elements.&quot;, ], rubric=[ Rubric(score_range=(0, 2), expected_outcome=&quot;Response is dominated by fabricated or clearly false content.&quot;), Rubric(score_range=(3, 4), expected_outcome=&quot;Key parts rely on invented or unverifiable information.&quot;), Rubric(score_range=(5, 6), expected_outcome=&quot;Some unverified or source-less details appear, but core content is factual.&quot;), Rubric(score_range=(7, 8), expected_outcome=&quot;Contains minor speculative language that remains verifiable or harmless.&quot;), Rubric(score_range=(9, 10), expected_outcome=&quot;All content is grounded in the given context or universally accepted facts; no unsupported claims.&quot;) ], evaluation_params=[LLMTestCaseParams.ACTUAL_OUTPUT, LLMTestCaseParams.RETRIEVAL_CONTEXT], model=&quot;gpt-4o&quot; ) await asyncio.to_thread(geval_answer_relevancy.measure, test_case) await asyncio.to_thread(geval_correctness.measure, test_case) await asyncio.to_thread(geval_hallucination.measure, test_case) # Rubricスコアを推定する関数(表示用) def extract_rubric_score_from_normalized(normalized_score, rubric_list): &quot;&quot;&quot;正規化されたスコア(0.0-1.0)からRubricの範囲を特定&quot;&quot;&quot; scaled_score = normalized_score * 10 for rubric_item in rubric_list: score_range = rubric_item.score_range if score_range[0] &lt;= scaled_score &lt;= score_range[1]: return { &#039;scaled_score&#039;: scaled_score, &#039;rubric_range&#039;: score_range, &#039;expected_outcome&#039;: rubric_item.expected_outcome } return None answer_relevancy_rubric_info = extract_rubric_score_from_normalized( geval_answer_relevancy.score, geval_answer_relevancy.rubric ) correctness_rubric_info = extract_rubric_score_from_normalized( geval_correctness.score, geval_correctness.rubric ) hallucination_rubric_info = extract_rubric_score_from_normalized( geval_hallucination.score, geval_hallucination.rubric ) return { &quot;answer_relevancy_score&quot;: geval_answer_relevancy.score, &quot;answer_relevancy_rubric_info&quot;: answer_relevancy_rubric_info, &quot;answer_relevancy_reason&quot;: geval_answer_relevancy.reason, &quot;correctness_score&quot;: geval_correctness.score, &quot;correctness_rubric_info&quot;: correctness_rubric_info, &quot;correctness_reason&quot;: geval_correctness.reason, &quot;hallucination_score&quot;: geval_hallucination.score, &quot;hallucination_rubric_info&quot;: hallucination_rubric_info, &quot;hallucination_reason&quot;: geval_hallucination.reason, } async def generate_summary(client: openai.AsyncOpenAI, prompt_template: str, full_story: str, model: str = &quot;gpt-4o&quot;) -&gt; str: &quot;&quot;&quot;LLMを使って要約を生成&quot;&quot;&quot; prompt = prompt_template.format(context=full_story) try: response = await client.chat.completions.create( model=model, messages=[{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: prompt}], max_tokens=300, temperature=0.0, top_p=0, logit_bias={} ) content = response.choices[0].message.content return content.strip() if content else &quot;&quot; except Exception as e: return f&quot;Error: {str(e)}&quot; async def process_prompt(client: openai.AsyncOpenAI, prompt_info: dict, full_story: str, context: list) -&gt; dict: model = prompt_info.get(&quot;model&quot;, &quot;gpt-4o&quot;) # 要約生成 summary = await generate_summary(client, prompt_info[&quot;template&quot;], full_story, model) # テストケース作成 test_case = LLMTestCase( input=prompt_info[&quot;template&quot;], # プロンプト actual_output=summary, # 要約結果 retrieval_context=context # 要約対象(童話)の原文 ) # 評価実行 metrics_result = await evaluate_comprehensive_metrics(client, test_case, prompt_info[&#039;name&#039;], full_story) return { &quot;prompt_name&quot;: prompt_info[&#039;name&#039;], &quot;model&quot;: model, &quot;summary&quot;: summary, **metrics_result } async def main(): # 童話の原文を読み込み with open(&#039;little_red_riding_hood.txt&#039;, &#039;r&#039;, encoding=&#039;utf-8&#039;) as f: full_story = f.read().strip() context = [full_story] prompts = [ { &quot;name&quot;: &quot;prompt-01&quot;, &quot;template&quot;: &quot;&quot;&quot;以下のテキストを読んで、内容の要約を作成してください。 要件: 1. 主要な登場人物や重要な要素を特定して含める 2. 内容の流れを論理的に整理する 3. 重要な出来事や転換点を含める 4. 原文の内容に忠実であること 5. 要約の長さは250文字以内に収める テキスト: {context} 要約:&quot;&quot;&quot;, &quot;model&quot;: &quot;gpt-4o&quot; }, ] async with openai.AsyncOpenAI() as client: tasks = [ process_prompt(client, prompt_info, full_story, context) for prompt_info in prompts ] all_results = await asyncio.gather(*tasks) # 結果表示処理 ... if __name__ == &quot;__main__&quot;: asyncio.run(main())</code></pre> <p>実行した要約結果は下記になりました。</p> <pre><code> 昔、赤ずきんちゃんという愛らしい女の子がいました。彼女はおばあさんから赤いずきんをもらい、それをいつもかぶっていました。 ある日、病気のおばあさんにお菓子とぶどう酒を届けるため、森を通っておばあさんの家に向かいます。 途中で狼に出会い、行き先を教えてしまいます。狼は先回りしておばあさんを飲み込み、赤ずきんちゃんも騙して飲み込みます。 しかし、通りかかった狩人が狼のお腹を切り開き、赤ずきんちゃんとおばあさんを救出します。赤ずきんちゃんは教訓を得て、二度と森で道を外れないと心に誓いました。</code></pre> <p>G-Evalが評価した結果は下記になります。(1回目を抜粋)</p> <pre><code>- 回答の関連性 (Answer Relevancy): 0.912 - Expected Outcome: Fully addresses every aspect of the user question; no missing or extraneous information and a clear, logical focus. - Reason: The summary includes key characters like Little Red Riding Hood, her grandmother, the wolf, and the hunter. It logically organizes the flow of events, such as the journey through the forest, the encounter with the wolf, and the rescue. Important events like the wolf&#039;s deception and the rescue by the hunter are covered. The summary is faithful to the original text and concise, with no extraneous information. - 正確さ (Correctness): 0.901 - Expected Outcome: All statements align perfectly with the provided ground-truth reference or verifiable facts; zero errors. - Reason: The main facts in the Actual Output align well with the Retrieval Context, including the characters, events, and moral of the story. Minor details like the specific dialogue and actions are slightly condensed but do not affect the overall accuracy. - 幻覚の有無 (Hallucination): 0.903 - Expected Outcome: All content is grounded in the given context or universally accepted facts; no unsupported claims. - Reason: The output closely follows the context with accurate details about Little Red Riding Hood, her grandmother, the wolf, and the hunter. The sequence of events and character actions are consistent with the context, with no unsupported claims.</code></pre> <p>スコアを決定した評価理由を見ますと、各指標に対して的確に評価しているようにみえます。 <strong>3.2. 指標の選定</strong>で、G-Evalは評価に揺らぎがあることを紹介しました。よって、上記のスクリプトを50回実行して、計測した評価数値の散布図は下記になります。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/a918ef06-newplot.png" alt="スクリプトを50回実行して、計測した評価数値の散布図" /></p> <p>結果的には、すべての指標でスコア値が概ね<strong>0.9以上</strong>になりましたが、これで各指標のSLI値を概ね0.9としてSLOを0.9以上として目標値に掲げることはできるでしょうか?</p> <h3>3.5. 評価指標のレビュー</h3> <p>上記で紹介したとおり、このサービスは、<strong>グリム童話を要約して、子供でもわかるような文章で要約してくれるサービス</strong> です。 上記の要約結果を <strong>子供でもわかるように</strong> するには、下記の指標も考慮しないといけないでしょう。</p> <ul> <li><strong>可読性 (Readability):</strong> 子供が読めない難しい漢字、表現が使われていないか? <ul> <li>騙して?、教訓?、ぶどう酒?</li> </ul> </li> <li><strong>安全性・有害性 (Toxicity / Safety):</strong> 現代のコンプライアンスと照らし合わせて、子供には過激な表現が使われていないか? <ul> <li>お腹を切り開き?</li> </ul> </li> </ul> <p>評価指標はお客さま価値とビジネスKPIと密接に関連付けることを意識して評価指標を選定する必要があります。 今回の要約サービスの場合、一般的評価指標より、対象者を考慮して上記の指標をタスク固有指標(Task-Specific Metrics)として最優先に考える指標にするべきです。 また、それに伴い、プロンプトも修正しなければならないでしょう。</p> <p>とはいえ、初回から完璧な指標セットを作るのは困難です。 <a href="https://www.confident-ai.com/blog/the-ultimate-llm-evaluation-playbook">The Complete LLM Evaluation Playbook: How To Run LLM Evals That Matter</a> では、<strong>評価指標はまず1つから始め、最終的には5つに絞る指標設計が望ましい</strong>とありました。 評価指標のスコアが、<strong>Metric Outcome Fit &#8211; 指標と成果のつながり</strong> (子どもたちに頻繁に利用されること)と、どれだけ一致しているか、意識しながら指標を選定、計測、評価する必要があります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/87da3515-image2.png" alt="Summary of Red Riding Hood" /></p> <p>(実サービスだった場合、ビジネスKPIとしては、文章より画像で提供した方が、良い成果が得られるかもしれません)</p> <h3>3.6. 自動化の可能性を探る</h3> <p>今回の例では、人間が指標の選定、評価スコアの算出、評価スコアの算出、指標評価のレビューを実施しました。 G-EvalはGPT-4クラスのモデルに「評価手順を自分で分解して考えさせ、最終スコアだけを返させる」仕組みをとるため、人間の代わりに 評価基準の適用・スコアリング・集計までをワンショットで自動化できます。 以下はその手順例です。</p> <ol> <li>評価タスクの提示: 評価に使うLLMに対し、「これから提示する生成文をある評価基準に従って1〜5点で採点して下さい」といったタスク説明を与える。その際に、その評価基準の定義も明示してLLMに文脈を教える(例えば、LLMサービスの一般的評価指標にあった指標一覧を提示する)</li> <li>評価観点の分解: 1.でLLMが選定した指標に対して、必要な観点やステップを自ら列挙させる</li> <li>スコア算出: 続いてモデルに、先ほど生成した評価ステップに従い、実際の入力・出力を評価させる</li> </ol> <p>注意点として、LLMが評価者だと“LLMらしい”出力を過大評価し、数語仕込むだけでスコアを操作される脆弱性があります。そのため、別系列のLLMモデルで評価してみることや、2つの回答を並べてどちらが良いか比べるペアワイズ比較、異常検知などで緩和を試みても、完全な中立性は保証できません。 また、<strong>3.2. 指標の選定</strong>でも紹介しましたが、G-Evalは確率的評価手法が故に、同じ回答でも評価が揺らぐという再現性に問題があり、評価プロンプトやシードを固定するなどの工夫が必要です。 これらの理由から、最終判断は必ず人間のレビューを併用して補正・検証する二段構えを取ることが不可欠です。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/810c3274-image1.png" alt="Automated metric evaluation cycle" /></p> <h2>4. まとめ</h2> <p>LLMサービスの信頼性評価に不可欠な指標の選定から、具体的な測定・評価方法までを、DeepEvalライブラリを用いたデモを交えてご紹介しました。 従来のAvailabilityやLatencyといった指標だけでは測りきれない『LLMサービスの信頼性評価』の指標をSLIとしてどう定義するかは、SREにとっても新しい分野だと思います。 本記事で試したDeepEvalなどの評価ツールのアプローチも、数ある選択肢の一つに過ぎません。LLMの評価指標は現在も絶賛研究中の分野であり、LLMサービスの信頼性をどう測るか、という問いに、まだ唯一の正解はなさそうです。ただ、この先、新しい評価指標や新しい測定手法が発見されたとしても、<strong>『この指標は本当にお客さま満足度を表しているのか?』</strong>という問いは、変わることのない本質的な問いかけだと思います。 技術の進歩とともに、この問いかけを忘れず、日々のSRE業務に取り組んでいければ幸いです。</p> <p>明日の記事は @k_kinukawaさんの「<a href="https://engineering.mercari.com/blog/entry/20250613-64e628404a/" title="メルカリモバイル開発チームでAI Hackathonをした話">メルカリモバイル開発チームでAI Hackathonをした話</a>」です。引き続きお楽しみください。</p> <h4>References</h4> <ul> <li>Site Reliability Engineering Book: <a href="https://sre.google/books/">https://sre.google/books/</a></li> <li>LLM Evaluation Metrics: The Ultimate LLM Evaluation Guide: <a href="https://www.confident-ai.com/blog/llm-evaluation-metrics-everything-you-need-for-llm-evaluation">https://www.confident-ai.com/blog/llm-evaluation-metrics-everything-you-need-for-llm-evaluation</a></li> <li>The Accuracy Trap: Why Your Model’s 90 % Might Mean Nothing: <a href="https://medium.com/%40edgar_muyale/the-accuracy-trap-why-your-models-90-might-mean-nothing-f3243fce6fe8">https://medium.com/%40edgar_muyale/the-accuracy-trap-why-your-models-90-might-mean-nothing-f3243fce6fe8</a> </li> <li>The Complete LLM Evaluation Playbook: How To Run LLM Evals That Matter: <a href="https://www.confident-ai.com/blog/the-ultimate-llm-evaluation-playbook">https://www.confident-ai.com/blog/the-ultimate-llm-evaluation-playbook</a> </li> <li>レーベンシュタイン距離: <a href="https://note.com/noa813/n/nb7ffd5a8f5e9">https://note.com/noa813/n/nb7ffd5a8f5e9</a></li> <li>LLM evaluation metrics — BLEU, ROUGE and METEOR explained: <a href="https://avinashselvam.medium.com/llm-evaluation-metrics-bleu-rogue-and-meteor-explained-a5d2b129e87f">https://avinashselvam.medium.com/llm-evaluation-metrics-bleu-rogue-and-meteor-explained-a5d2b129e87f</a></li> <li>BERTScore: <a href="https://openreview.net/pdf?id=SkeHuCVFDr">https://openreview.net/pdf?id=SkeHuCVFDr</a></li> <li>BERT: <a href="https://en.wikipedia.org/wiki/BERT_\(language_model\)">https://en.wikipedia.org/wiki/BERT_(language_model)</a></li> <li>コサイン類似度: <a href="https://atmarkit.itmedia.co.jp/ait/articles/2112/08/news020.html">https://atmarkit.itmedia.co.jp/ait/articles/2112/08/news020.html</a></li> <li>MoverScore: <a href="https://arxiv.org/abs/1909.02622">https://arxiv.org/abs/1909.02622</a></li> <li>Earth Mover’s Distance(最適輸送距離): <a href="https://zenn.dev/derwind/articles/dwd-optimal-transport01#%E6%9C%80%E9%81%A9%E8%BC%B8%E9%80%81%E8%B7%9D%E9%9B%A2">https://zenn.dev/derwind/articles/dwd-optimal-transport01#%E6%9C%80%E9%81%A9%E8%BC%B8%E9%80%81%E8%B7%9D%E9%9B%A2</a></li> <li>G-Eval (Paper): <a href="https://arxiv.org/abs/2303.16634">https://arxiv.org/abs/2303.16634</a></li> <li>G-Eval Simply Explained: LLM-as-a-Judge for LLM Evaluation: <a href="https://www.confident-ai.com/blog/g-eval-the-definitive-guide">https://www.confident-ai.com/blog/g-eval-the-definitive-guide</a></li> <li>QAG Score: <a href="https://arxiv.org/abs/2210.04320">https://arxiv.org/abs/2210.04320</a></li> <li>SelfCheckGPT: <a href="https://arxiv.org/abs/2303.08896">https://arxiv.org/abs/2303.08896</a></li> <li>DAG(deep acyclic graph): <a href="https://deepeval.com/docs/metrics-dag">https://deepeval.com/docs/metrics-dag</a></li> <li>Prometheus2 Model: <a href="https://arxiv.org/abs/2405.01535">https://arxiv.org/abs/2405.01535</a></li> <li>DeepEval: <a href="https://deepeval.com/docs/getting-started">https://deepeval.com/docs/getting-started</a></li> <li>Vertex AI &#8211; モデルベース評価の指標プロンプト テンプレート: <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/models/metrics-templates">https://cloud.google.com/vertex-ai/generative-ai/docs/models/metrics-templates</a></li> <li>赤ずきん: <a href="https://ja.wikipedia.org/wiki/%E8%B5%A4%E3%81%9A%E3%81%8D%E3%82%93">https://ja.wikipedia.org/wiki/%E8%B5%A4%E3%81%9A%E3%81%8D%E3%82%93</a></li> </ul> メルペイインターンで機能の改修・設計からLLM AgentベースのSlack Bot開発まで色々やった話https://engineering.mercari.com/blog/entry/20250612-b1aee5118d/https://engineering.mercari.com/blog/entry/20250612-b1aee5118d/<p>はじめに こんにちは!メルペイのBalanceチームの中にあるSettlementチームでインターンをしていました、somaです。 この記事は、Merpay & Mercoin Tech Openness Month 2 [&hellip;]</p> Fri, 13 Jun 2025 11:00:16 GMT<h2>はじめに</h2> <p>こんにちは!メルペイのBalanceチームの中にあるSettlementチームでインターンをしていました、somaです。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay & Mercoin Tech Openness Month 2025</a> の10日目の記事です。<br /> 本記事では、インターンでやったことやその感想などを書いていこうと思います。</p> <h2>チームについて</h2> <p>Settlementチームは、主にメルペイの加盟店さまの売り上げを集計し、振り込みの指示を行うといった役割を担当しています。<br /> Settlementのシステムについて詳しく知りたい方は、<a href="https://engineering.mercari.com/blog/entry/20231023-mmtf2023-day2-5/">こちらの記事</a>が参考になると思います。</p> <h2>取り組んだこと</h2> <p>私が担当したタスクは大きく分けて4つあります。</p> <ul> <li><strong>既存のAPIの分割</strong></li> <li>決済トランザクションのハンドラの改修</li> <li><strong>新規機能の設計</strong></li> <li><strong>お問い合わせ対応の補助をするSlack Botの開発</strong></li> </ul> <p>今回は、太字の3つについて話していこうと思います。</p> <h2>既存のAPIの分割</h2> <h3>背景</h3> <p>Settlementサービスには加盟店さまの</p> <ul> <li>月に何回売り上げの入金を行うか</li> <li>売り上げの入金を次の月に繰り越すかどうか、繰り越すのであればいくら以下の場合か</li> </ul> <p>などの設定を行うためのAPIが存在します。</p> <p>しかし、これらの設定がすべて1つのAPIで行われているため、リクエストが重なると片方のリクエストの結果だけが反映されてしまうリスクがあります。そこで、このAPIを各フィールドを変更する複数のAPIに分割することにしました。</p> <h3>やったこと</h3> <p>各マイクロサービス間の通信にはgRPCが使われているため、まずはProtocol BuffersでAPIのインターフェースを定義しました。社内でProtocol Buffersは共通のリポジトリで管理されており、そこにマージすると、Protocol Buffersの内容に基づいて自動生成されるクライアントライブラリにGoのinterfaceなどのコードが自動生成されます。<br /> その後、その中身をマイクロサービスのリポジトリで実装しました。<br /> また、モックも自動生成されてE2Eテスト上で動作確認できるため、わざわざ自分でAPIを叩いて挙動を確かめなくてもテストができるのは開発がしやすいなと思いました。</p> <h2>新規機能の設計</h2> <h3>やったこと</h3> <p>新規機能を追加するにあたって、</p> <ul> <li>どの部分にどのような変更が必要か</li> <li>その変更の実現方法の選択肢と、それぞれのメリットデメリットを考慮した上でどの方法を選択するのが望ましいか</li> <li>タスクへの切り分けと、それぞれのタスクの見積もり</li> <li>QAで確認して欲しいポイント</li> </ul> <p>などを考え、設計書を作成しました。</p> <p>最初に丁寧に説明をしてもらえるわけではなく、仕様書を読みながら自分でプロジェクトについて理解しながら調査をして、設計に取り掛かりました。その中で、どうしてもわからないことは質問して理解しました。それにより、わからないことを整理し、仮説を立て、調査をして検証するような働き方を身につけることができました。</p> <h2>お問い合わせ対応の補助をするSlack Botの開発</h2> <h3>背景</h3> <p>最近のメルカリエンジニアリングブログを見てもわかるように、現在メルカリ社内ではAIをどんどん活用していこうという動きがあります。そこで、Settlementチームでも何かできることはないかと話し合った結果、他部署からのお問い合わせに対する調査や回答をしてくれるBotを作りたいという話になりました。<br /> Settlementチームには、加盟店さまから主に振込に関するお問い合わせが届きます。それに対してエンジニアが調査を行い、回答をしたり追加で質問をしたりするなど、対応を決定します。しかし、毎回データベースやコードから手作業で調査を行って対応をするのはコストが高いです。そこで、お問い合わせに対して、データベースやコードを参照して調査を行い、回答をしてくれるようなBotを作って対応コストを削減しようという試みです。<br /> ただ、システム内部を理解していない人でも簡単にお問い合わせに対する正しい回答を得られるようなBotを作るのは難しいです。また、回答に対してそれが本当に正しいのかを判断できる人が使わないと、誤った回答をしてしまう恐れがあります。そのため、まずはシステムをよく理解しているエンジニアが、調査をSlack上で簡単に行えるようにするBotを開発することになりました。</p> <h3>挑戦前の不安</h3> <p>このプロジェクトを計画した段階で、すでに残りのインターン期間は3週間ほどで、開発に使える日数は10日前後しかありませんでした。さらに、自分はAI Agentの構築, GKE(Google Kubernetes Engine)へのデプロイ, Slack Botの開発などの経験がありませんでした。それによって、開発の全体像が見えず、10日前後で本当に終わらせることができるのか見積もりが困難でした。また、思わぬ困難なポイントが出現する可能性も考えられました。そこで、このプロジェクトに取り組むのではなく、先ほど紹介した新規機能のプロジェクトに取り組んだ方が確実なのではないか?とも思いました。<br /> しかし、2日ほど調査した段階で実現できるだろうと判断したため、不安はありましたがGo Boldにこのプロジェクトに取り組むことに決めました。</p> <h3>やったこと</h3> <h4>AI Agentの構築</h4> <ul> <li>簡単にAI Agentが作れる</li> <li>Googleが開発しており、今後BigQueryが組み込みtoolとして導入される動きがあるなど、Google Cloudとの相性が良い</li> </ul> <p>という利点があり、</p> <ul> <li>MCPサーバと連携できる</li> <li>Claudeなどさまざまなモデルを使うことができる</li> </ul> <p>という要件も満たしていたため、GoogleのADK(Agent Development Kit)を使うことにしました。<br /> また、AI Agentがプロダクションデータが同期されているBigQueryとGitHubリポジトリを参照できるようにするために、それらのMCPサーバを使うことにしました。GitHubのMCPサーバは公式のものを使用しましたが、BigQueryには公式のものがなかったので社内で実装されているものを利用しました。<br /> コンテキストは、Markdown形式で</p> <ul> <li>BigQueryのクエリの例</li> <li>用語と、その情報がSettlementのリポジトリのどのファイルにあるのか</li> <li>リポジトリのディレクトリ構造と、それぞれのディレクトリにどのようなファイルがあるのか</li> <li>回答に使用したクエリやファイルを添付するようにするなど、回答のフォーマットの指定</li> </ul> <p>のような情報を与えました。<br /> また、Claudeなどのモデルを使えるようにするために、社内に用意されているLiteLLMのプロキシサーバを使用しました。<a href="https://www.litellm.ai/">LiteLLM</a>とは、AnthropicのClaudeやGoogleのGeminiなど、さまざまなモデルをOpenAIのAPIのフォーマットで呼び出すことができるライブラリです。ADKでもLiteLLMがサポートされているため、これを使ってClaudeなどのモデルを使えるようにしました。</p> <h4>GKE上にデプロイ</h4> <p>開発したアプリからコンテナを作成するためのDockerfile, GKEにデプロイするためのKubernetesのマニフェストを作成しました。<br /> ここでは、AI AgentのサーバとGitHubとBigQueryのMCPサーバ、MCPサーバを動かすのに必要なコマンドをコンテナに取り込み、AI Agentが標準入出力を使ってMCPサーバを呼び出せるようにしました。</p> <h4>Slack Botとの連携</h4> <p>次に、Slack BotからのリクエストをGKE上のサーバで受け取り、LLMからの返答をSlack Botにメッセージとして投稿させるようにします。<br /> そのために、APIサーバに以下のようなミドルウェアを実装しました。</p> <ol> <li>Slackからのリクエストであることを確かめるために、署名による検証を行う</li> <li>Slackからのリクエストを受け取り、メッセージ部分を取り出す</li> <li>ADKが本来受け取るはずだったbodyの形式にリクエストを変換し、処理を行う</li> <li>レスポンスからLLMからのメッセージ部分を取り出し、Slackのメッセージとして送信する</li> </ol> <p>また、インフラ部分も、IngressでHTTPS通信を受け付け、署名検証をすることでセキュアにSlackからのリクエストを受け付けるようにしました。</p> <h3>結果</h3> <p>Slack Botの導入に関する社内の審査を通さなければならないため、インターン期間中に導入まで持っていくことはできませんでしたが、代替としてWeb UIで動作確認をしたところ、お問い合わせに対して以下のように期待通りの調査を行ってもらうことができました。DBの中では数値で管理されているステータスを、きちんとソースコードを読んだ上でその意味まで説明してくれていたり、内部のプロンプトでソースを提示するように指示すると、きちんと回答に使用したSQLクエリやファイルを教えてくれています。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/bb0fce86-screenshot-2025-06-10-at-16.13.03-1024x481.png" alt="" /><br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/9de079b0-screenshot-2025-06-10-at-16.13.13.png" alt="" /></p> <h3>今後の課題</h3> <p>コンテキストとしてより多くのドメイン知識を与えることで、さらに多くのケースに対応できるようにしたいです。また、現在は内部のプロンプトをgitで管理しているため、より簡単に修正を行えるような仕組みにしたいです。</p> <h2>学んだこと</h2> <h3>技術面</h3> <ul> <li>Kubernetes</li> <li>gRPC</li> <li>マイクロサービスアーキテクチャ</li> <li>Pub/Sub</li> <li>Spanner</li> </ul> <p>のような、今まで深く触れてこなかった技術を使えたのでとても勉強になりました。<br /> 特に、Kubernetesは、ずっと勉強したいと思いつつできていなかったので、これを機に本格的に勉強を開始できてよかったです。<br /> また、Slack Botの開発を通して、今まで自分の中でブラックボックスになってしまっていた企業のインフラにしっかり触れられたのはすごく良い経験になりました。</p> <h3>経験面</h3> <p>技術選定や設計、実装の方針などを決める際に、調査〜意思決定まで自分に任せていただけたのは、すごく良い経験になりました。メルペイのインターンではタスクを任された後、基本的には自走して、わからないところがあれば質問をするような方針だったため、自分がタスクやプロジェクトを握っているんだという実感がありました。その中で、技術選定や設計におけるトレードオフなどを考慮して提案し、レビューをもらうようなプロセスを踏むことは1人前に近づくための成長につながりました。また、QAの調整やリリースの周知、他チームへの質問なども自分が行うよう求められ、本当にチームの一員として働くことができました。</p> <h2>タスク以外で取り組んだこと</h2> <h3>英語</h3> <p>SettlementチームではGitHub上の会話は基本的に英語で、Slackのやり取りでも英語で話すことがあります。 そのように、インターンを通して英語を使う場面があったため、自分もSlackで英語で話しかけてみたり、オンライン英会話を始めてみたりなど、日常的に英語を使う機会を作る工夫をしてみました。インターン終了後も継続していきたいです。</p> <h3>1on1</h3> <p>キャリアについて考えたり視野や知見を広げる上で、人とたくさん話すのはとても重要だと考えています。そこで、一緒にランチに行った方や、インターンの一次面接をしてくださった方、そうやって話した方のお知り合いなど、いろいろな方に1on1を申し込んで、どのように技術のキャッチアップをしているのかや、なぜ今のキャリアを選んだのかなど、ざっくばらんにお話しました。自分にはなかった考え方や知らなかった知識を身につけることができ、今後の成長のヒントになりました。<br /> 突然DMで1on1を申し込んだにもかかわらず、みなさん快く受けて下さりとても感謝しています。</p> <h3>AIの活用</h3> <p>会社がAIの活用を推進しているということで、自分もCursorを使ってみました。特に既存のAPIを分割するタスクでは、基本的に元の実装に倣うため、ほぼ全ての実装をプロンプトを入力するだけで行うことができました。一方で、決済トランザクションのPub/Subハンドラの処理を改修するタスクでは、改修やテストをAgentが正しく実装することはできませんでした。ただ、テストの骨組みさえ作ってしまえば、適切なテストケースの追加はAgentに任せることができました。<br /> また、自分もプライベートでもっとAIを活用できないか?と考えるようになりました。<br /> 技術のキャッチアップでNotebookLMを使うようになり、<br /> 今まで「名前は結構聞くけど、本腰入れて勉強するほどじゃないんだよな〜」という技術<br /> ドキュメントがあまり好みではない技術<br /> 技術的な論文<br /> などを効率的に学習できるようになりました。革命ですね。<br /> また、活用だけでなく、AI関連の技術についても積極的にキャッチアップするようになりました。</p> <h3>社内のコードを漁る</h3> <p>コードを読み進めることで、開発基盤やソフトウェアアーキテクチャなどについて理解を深めることができました。マイクロサービスなので、各サービスの全体像の把握がしやすく、学びやすかったです。インターンではせっかく実際に会社で働けるので、タスクに取り組むだけでなく、自分がこのインターンで学べることは何か?を積極的に考えていくと良いと思いました。</p> <h2>さいごに</h2> <p>以上が、私がインターンで行ったことやその感想でした。<br /> メルペイでのインターンを通して、自分の中でできることや今後のキャリアに向けた視野がグッと広がったと感じています。<br /> 2ヶ月間ありがとうございました!!</p> Devin にE2Eテストの実装を任せるhttps://engineering.mercari.com/blog/entry/20250612-a80033f0b8/https://engineering.mercari.com/blog/entry/20250612-a80033f0b8/<p>こんにちは。メルペイ Payment &amp; Customer Platform / Client EM の@anzaiです。 この記事は、Merpay &amp; Mercoin Tech Openness Mon [&hellip;]</p> Fri, 13 Jun 2025 10:00:06 GMT<p>こんにちは。メルペイ Payment &amp; Customer Platform / Client EM の@anzaiです。<br /> この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 10日目の記事です。<br /> E2E テスト実装におけるDevin活用の現状について紹介します。</p> <h2>はじめに</h2> <p>モバイルアプリケーション開発において、テスト自動化の重要性は言うまでもありません。特に、メルカリアプリのような大規模かつ複雑なプロダクトでは、品質保証の観点からリグレッションテストは欠かせないプロセスであり、その自動化は非常に重要な課題となっています。</p> <p>本記事では、AIソフトウェアエンジニアである「Devin」を活用してE2E(End-to-End)テスト実装の課題に取り組んだ事例を紹介します。テスト自動化の効率化やAI技術の導入に関心のある開発者の方々の参考になれば幸いです。</p> <h2>メルカリのE2Eテスト実装における課題</h2> <p>メルカリのリグレッションテストとして実施しているE2Eテストは、お客さま体験を保証しつつ毎週行っているリリースを担保する上で欠かせない要素[^1]ですが、その実装には以下のような課題があります。</p> <ul> <li><strong>コンテキストスイッチの負荷</strong>: 通常の開発業務からE2Eテストの実装に切り替える際に、大きな負担が発生します。</li> <li><strong>実装・実行時間の長さ</strong>: E2Eテストを正確に実装するには時間がかかり、テストケースが増えるにつれて実行時間も長くなる傾向があります。</li> <li><strong>コードリーディングの困難性</strong>: メルカリアプリのリグレッションテストとしてのE2Eテスト実装では、アプリの機能を横断的に理解する必要があります。メルペイには非常に多くのコードが存在するため、エンジニアは自分の担当ドメイン外のコードも読み解く必要がありました。これには多くの時間と、コードの実装意図や仕様の理解が困難な時に担当のチームに確認するコミュニケーションコストがかかります。</li> <li><strong>テストケース判断の複雑性</strong>: テストケースで書かれている手順や期待値はどのように実装すれば満たしたといえるのか、といった判断には詳細なコード解析や事例の確認が必要になり、多くの時間がかかります。</li> </ul> <p>これらの課題により、開発者の時間が圧迫され、本来注力すべき機能開発に遅れが生じる可能性があります。</p> <h2>AIソフトウェアエンジニア「Devin」によるアプローチ</h2> <p>このような背景から、私たちはDevinに注目しました。</p> <h3>Devinとは何か</h3> <p>Devinは、完全自律型AIソフトウェアエンジニアです。従来のコード補完ツール(GitHub Copilotなど)とは大きく異なり、自然言語による指示だけで、ソフトウェアの設計からコーディング、デバッグ、デプロイまで、開発プロセス全体を自律的に実行できます。</p> <h3>なぜDevinがE2Eテスト実装に適していると考えたのか</h3> <p>今回のE2Eテスト実装において、Devinが特に有効だと考えた理由は以下の通りです:</p> <p><strong>1. パターン化された実装環境</strong><br /> メルカリアプリのE2Eテストは「ページオブジェクトモデル[^2]」で実装されており、ある程度決まった形で書けるようになっています。また、すでに十分な量の参考テストケースが存在していたため、Devinが学習しやすい環境が整っていました。</p> <p><strong>2. コード理解の負担軽減</strong><br /> 前述のコードリーディングの困難性でお話しした通り、リグレッションテストとしてのE2Eテストの場合、様々なドメインを横断的に理解する必要があります。<br /> しかし、DevinならACU(計算リソース)が許す限り、どこまでも深くコードを読み続けることも可能です。人間のように疲れることがないのは大きなメリットでした。</p> <h2>eKYC画面におけるE2Eテスト実装事例</h2> <p>具体的な検証として、メルペイのeKYC(オンライン本人確認)機能のリグレッションテストのE2Eテスト化のタスクをDevinに任せてみました。</p> <h3>実施方法</h3> <p>メルカリのテストケースはTestRailで管理されています。そのため、以下のフローでDevinにタスクを渡すフローを構築しました。</p> <p>① Cursor上からREST APIでTestRailのテストケースを取得<br /> ② 取得したテストケースから実装可能なようにタスクを詳細化<br /> TestRail上の情報は人間が直接操作して実行する前提で記載されていますが、Devinが処理可能なフォーマットに変換する必要があり、それにはアカウントのコード上における作成方法や操作手順が重要になります。<br /> ③ 公式のAtlassian MCP Server経由でJIRAチケットを作成<br /> ④ そのJIRAチケットをDevin が読み、実装作業を開始</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/869b259f--2025-06-12-12.16.21.png" alt="" /><br /> <em>TestRailからJIRA、Devinへのデータフロー構成</em></p> <div style="text-align: right"> <p>ロゴ出典:<br /> https://lobehub.com/ja/icons/cursor<br /> https://www.testrail.com/<br /> https://atlassian.design/foundations/logos<br /> https://deepwiki.com/ </p></div> <p>このフローにした理由は以下の通りです:</p> <ol> <li><strong>セッション管理の最適化</strong>: 一つのセッションが大きくなりすぎるとDevinの能力が低下してしまうため、テストケース単位で作業を分割することで品質を保つ</li> <li><strong>実装タイミングの制御</strong>: 開発チームのスケジュールに合わせて、実装開始のタイミングをコントロールできる</li> <li><strong>継続的な改善</strong>: 試験的な取り組みだったため、Cursor上で作成したJIRAチケットのフォーマットや内容をみながら逐次JIRAチケット生成プロンプトを改善したい</li> </ol> <p>Devinにテスト実装を任せる際、特に意識した情報は以下の2つです:</p> <h4>前提条件の詳細化</h4> <p>メルペイではさまざまなアカウント状態(年齢、本人確認ステータス、銀行接続状況など)が存在します。そのため、各テストに必要なテストアカウントの作成方法やセットアップ手順について詳しく記載しました。<br /> 例)<br /> eKYC未実施の状態 -&gt; XXX() という関数を呼び出してアカウントを生成してください<br /> eKYC実施済みかつ銀行口座接続済みの状態 -&gt; YYY() という関数を呼び出してアカウントを生成してください</p> <h4>完了条件(assertion)のパターン化</h4> <p>テストの操作内容は多様でしたが、完了条件については一定のパターンがありました。そこで以下のような内容を事前に詳しく記載しました:</p> <ul> <li>「画面遷移が成功したかどうかの判定方法」</li> <li>「エラーメッセージが正しく表示されているかの確認方法」</li> <li>「各UI要素が期待通りに表示されているかの検証方法」</li> </ul> <p>これらの情報はTestRailsに記載されているテストケースには記載がないため、Cursor上でJIRAチケットに情報を追加するプロンプトを設定しています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/a326c6f2-prompt_test_rails.png" alt="" /><br /> <em>JIRA チケット作成プロンプト一部抜粋</em></p> <h3>結果</h3> <p>DevinはJIRAチケットの内容に対応するテストコードを含むPull Requestを自動生成しました。生成されたコードを人間がレビューしたところ、以下のような品質でした:</p> <ul> <li><strong>コード品質</strong>: 参考にできる水準で、実装内容も妥当</li> <li><strong>テストロジック</strong>: 期待していた通りの流れで実装されていた</li> <li><strong>ページオブジェクトパターンの適用</strong>: 既存のコードスタイルに合致した実装</li> </ul> <p>特に印象的だったのは、複雑な前提条件についてもしっかりと理解し、適切なテストアカウントのセットアップコードを生成できていた点です。現状はシンプルなテストケースの依頼のみですが、試したほとんどのテストケースについて人間の修正なく実装ができました。</p> <p>さらに、GitHub上で行われたレビューコメントに対して、Devinが自動でコードを修正するといった挙動も確認できました。これは、開発プロセスにおけるコミュニケーションコスト削減への寄与も期待させるものです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/2e236084--2025-06-09-18.46.06.png" alt="" /><br /> <em>Devinがcommentを確認し、リアクションをつけている</em></p> <h2>Devin活用の評価:メリットと今後の課題</h2> <p>今回の検証を通じて、Devinを活用するメリットをまとめました。</p> <h3>メリット</h3> <ul> <li><strong>コンテキストスイッチの軽減</strong>: E2Eテスト実装にかかる細切れの作業時間を大幅に削減できました。例えば、「業務開始時にDevinに実装タスクを指示し、夕方にレビュー、翌日に結果確認と再レビュー」といった効率的なワークフローを作ることができます。これにより、開発者は他の重要なタスクに集中しやすくなります。</li> <li><strong>反復的な作業の委譲</strong>: Devinは、人間なら多大な集中力が必要なコードの追跡や全体像の把握といったタスクを、粘り強く実行してくれます(ACUというリソース制限は存在します)。このような作業をDevinに任せることで、開発者はより創造的な問題解決に時間を使うことができます。</li> </ul> <h3>今後の展望</h3> <p>今回は開発者が手元のCursorでタスク生成を行いながらフローを構築しましたが、今後はより完全な自動化を目指しています。</p> <p>具体的には、以下のような仕組みを検討しています:</p> <ol> <li><strong>TestRailのMCPサーバー構築</strong>: TestRailの変更を監視して自動的にJIRAチケットを生成</li> <li><strong>JIRAチケット監視機能</strong>: チケットの更新を検知して、自動的にDevinのセッションを開始</li> <li><strong>完全な無人運用</strong>: 人間の介入なしに、TestRailの更新からテストコード実装まで自動実行</li> </ol> <p>これにより、テストケースの追加や変更があった際に、開発者が意識することなく自動的にE2Eテストが実装される環境を実現したいと考えています。</p> <h2>まとめ</h2> <p>Devinを使ったE2Eテスト実装の取り組みは、開発効率向上の可能性を示してくれました。特に、コンテキストスイッチの削減や、定型的で負荷の高い作業の自動化は、開発者の生産性向上に貢献する重要なポイントです。</p> <p>AI技術はまだ発展途上で、Devinも例外ではありません。しかし、その可能性は非常に大きく、今後の技術進歩に期待が持てます。</p> <p>本記事が、開発現場におけるE2Eテストの課題解決や、AI技術活用の検討に役立てば幸いです。Devinのような新しい技術を効果的に取り入れ、より良い開発プロセスの実現を目指していきたいと思います。</p> <p>次の記事は @Tさんの「<a href="https://engineering.mercari.com/blog/entry/20250612-d2c354901d/" title="SRE2.0: LLMサービスの信頼性を測る新しい評価指標の紹介">SRE2.0: LLMサービスの信頼性を測る新しい評価指標の紹介</a>」です。引き続きお楽しみください。</p> <h3>参考資料</h3> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20211210-merpay-android-test-automation/">メルペイのAndroidアプリにおけるテスト自動化</a></li> <li><a href="https://www.selenium.dev/ja/documentation/test_practices/encouraged/page_object_models/">ページオブジェクトモデル</a></li> </ul> GASで効率化!MVNOの動作検証仮事業者&Jira issue作成 with AIhttps://engineering.mercari.com/blog/entry/20250606-37101db087/https://engineering.mercari.com/blog/entry/20250606-37101db087/<p>こんにちは。メルカリモバイル フロントエンドエンジニアのtoshickです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の9日目の記事です。 この記事は以下の [&hellip;]</p> Thu, 12 Jun 2025 10:00:29 GMT<p>こんにちは。メルカリモバイル フロントエンドエンジニアのtoshickです。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の9日目の記事です。</p> <p>この記事は以下の二部構成となっています。</p> <ul> <li>(その1)GAS(Google Apps Script)に助けられたMVNO開発の話</li> <li>(その2)Jiraのissue作成のためのGAS</li> </ul> <h2>(その1)GAS(Google Apps Script)に助けられたMVNO開発の話</h2> <p>わたくしtoshickは2025年<a href="https://about.mercari.com/press/news/articles/20250304_mercarimobile/" title="今年リリースされたメルカリモバイル">今年リリースされたメルカリモバイル</a>というプロジェクトのフロントエンド担当として開発をしています。<br /> メルカリモバイルとはメルカリがMVNO事業者としてモバイル通信事業に参画したものです。<br /> 当然MNP(Mobile Number Portability)に対応しているため、お客さまが他のモバイル事業者から転入してきたり、逆にメルカリモバイルから他事業者へ転出されたりするようなケースにも対応しておく必要があります。<br /> これらをポートイン(転入)、ポートアウト(転出)と呼びます。<br /> 他事業者との連携なのであらかじめやりとりのためAPIを決めておき、エンドユーザーが任意のタイミングでポートインやポートアウトを実行できるようにしておきます。</p> <h3>MNPワンストップ方式とMNPツーストップ方式</h3> <p>MNPの手続きの大まかな流れは以下です。</p> <ol> <li>お客さまは今自分が契約している通信事業者に対してMNP予約番号の発行を要請しこれを取得する。</li> <li>お客さまはその予約番号の有効期限内に乗り換え先となる通信事業者にて予約番号を入力して新規契約を行う。</li> </ol> <p>このようにお客さまが自ら予約番号を取得して乗り換え先にもっていくMNPの方法をMNPツーストップ方式と呼びます。<br /> 一方で最近はその予約番号発行手続きをAPIから行うようにした、手順が簡略化されたフローが主流となっています。<br /> これがMNPワンストップ方式と呼ばれる方法です。<br /> お客さまからすると特にデメリットはないので、もし利用できる場合には多くのお客さまはこちらのMNPワンストップ方式で事業者変更の手続きを行っているはずです。</p> <h3>MNPワンストップ方式での動作テスト</h3> <p>メルカリモバイルもこのMNPワンストップ方式に対応している事業者となります。<br /> ここで問題になるのがワンストップ方式時のテストになります。<br /> ワンストップ方式はメルカリ内部だけで完結するものではないので、ワンストップ時のAPIの受け口となる本番と同様のふるまいをする別MVNO事業者のようなものが必要になります。<br /> これを動作検証仮事業者と呼ぶことにします。(以降 仮事業者) </p> <p>メルカリと仮事業者のAPIでのやりとりを模したテストのためのダミーの事業者としてふるまいます。<br /> 開発もかなりすすんできていた段階で、仮事業者も静的なページを用意すればなんとかなると考えていたのですがよく考えると問題がありました。</p> <p>静的なWebページだとpostのリクエストを処理できないのです。<br /> その事実に気づいた時にやっと「仮事業者はWebサーバ上で起動されたWebアプリケーションである必要がある」ということに気づきました。</p> <h3>動作検証仮事業者の開発</h3> <p>社内でカジュアルにWebアプリケーションを立ち上げて任意のjsを実行できるような環境をスピーディに用意する必要にかられていろいろ相談しましたが、今から新しくマイクロサービスを作成するのは時間的に不可能だということがわかりました。<br /> 素早く、新規マイクロサービス作成とかいう高コストでもなく、社内QAからアクセス可能で、postリクエストを処理可能な、社内向けのWebアプリケーションを作成する方法を見つける必要があります。</p> <p>この問題をチームに相談したところ</p> <p>「GASでできるかもしれない」</p> <p>というアドバイスをもらいました。<br /> 自分はいままでほとんどGASに触ってこなかったのでそのアイデアは目からウロコです。<br /> 早速サンプルのGASを作成してpostのリクエストを送信したところちゃんとリクエストパラメータを取得して画面に出力することができました。<br /> さらにjs実行によりform post(同期post)の実行もできることがわかったのです。<br /> これなら仮事業者としてメルカリモバイルのAPIにむけて特定のパラメータ付きでpostリクエストをコールしたり、メルカリモバイル側からpostリクエストをうけとってから任意のタイミングでコールバックをコールしたりするような本番の外部MVNO事業者と同じふるまいをするアプリケーションが用意できます。</p> <h3>GASによる仮事業者の作成</h3> <p>本格的にGASでアプリケーションを作成してみたところいろいろ課題や混乱が発生しました。<br /> まず最初はコード管理の問題でした。<br /> 通常の開発はGitを利用した差分管理およびGitHubへのpush、review、mergeといった手順をふむのが普通ですが、GASの場合はなんとブラウザ上でコードを修正し、ブラウザ上でデプロイを行うものでした。<br /> これだとGit開発でうけている恩恵がうけられません。コードの差分のreveiewも不可能です。</p> <h4>claspの導入</h4> <p>みなさんがどうやってGASを開発しているのか調べたところ、claspというcliツールがgoogleから提供されていることがわかりました。<br /> <a href="https://github.com/google/clasp?tab=readme-ov-file">https://github.com/google/clasp?tab=readme-ov-file</a><br /> claspはGAS開発のための便利なコマンドを提供しており、ローカルから自分のGASアプリへコードをpushすることが可能になります。<br /> これでコードをGit上で管理しつつ、GASへの反映もブラウザ上のファイル編集ではなくローカルのファイルのpushにより可能になるのでより安全な開発になります。</p> <h4>GASの癖</h4> <p>GASのWebアプリケーション作成は少し違和感を覚えるかもしれません。<br /> まず、ランタイム時の依存ファイルのインポートが不可能です。<br /> 通常のJavaScript(js)を利用したWebアプリケーションはimport文(もしくはrequire文)を利用して依存するファイルをロードするのが普通ですが、GASだとそれができません。<br /> アプリのアクセス時のSSR処理時にコードをinjectするしか方法がないということです。<br /> PHPのようなやり方ですね。</p> <pre><code class="language-html">// これでjsの書かれたhtmlをinjectできる &lt;script&gt; &lt;?!= includeFile(&#039;jsUtil&#039;) ?&gt; &lt;/script&gt;</code></pre> <p>もし関数を定義してそれをコールしたい場合、それはグローバルに定義してあらかじめhtmlのヘッダで読み込んでおく必要があります。</p> <p>次に、GAS上で取り扱うファイルは拡張子がhtmlである必要があります。<br /> 上記のコード includeFile(&#8216;jsUtil&#8217;) は jsUtil.html をここに読み込んでくださいという命令になっています。</p> <p>htmlとなっていはいますが、中身はjsです。<br /> GASが「htmlしか動的に読み込ませない」というルールにしているためにこのようなヘンテコなことになっているようです。</p> <ul> <li>開発側は定義した関数をjsとしてファイルに書き込み、デプロイ時はその拡張子をhtmlとしてpushする。</li> <li>さらに実行html内には includeFile関数をつかってヘッダからその関数をグローバルに読み込む。</li> </ul> <p>といった工夫が必要になります。<br /> 別に直接実行htmlに関数を書いてもよいですが、複数htmlに同じ関数は書きたくないでしょう。</p> <p>GASアプリからURLのクエリに含まれた値やpostでリクエストされた値を取得する場合built-inで用意された変数があるので、以下のような関数をhtmlに定義すればjs側でランタイム時に利用できるようになります。</p> <pre><code class="language-js">&lt;script&gt; &lt;!-- postパラメータをinject --&gt; function getHtmlPostParams() { return { &lt;? for (let key in postParams) { ?&gt; &lt;?= key ?&gt;: &lt;?= postParams[key] ?&gt;, &lt;? } ?&gt; }; } &lt;/script&gt; &lt;!-- クエリパラメータをinject --&gt; &lt;script&gt; function getHtmlGetParams() { return { &lt;? for (let key in getParams) { ?&gt; &lt;?= key ?&gt;: &lt;?= getParams[key] ?&gt;, &lt;? } ?&gt; }; } &lt;/script&gt;</code></pre> <p>これ以外にも新規デプロイするたびにURLが変わるけど、URLが変わらないデプロイ手順があったりと癖の多いツールですが理解さえしてしまえばなんとかなりました。</p> <h3>動作検証仮事業者の画面サンプル</h3> <table> <thead> <tr> <th>仮事業者ポートアウト</th> <th>仮事業者ポートイン</th> <th>仮事業者ポートイン完了</th> </tr> </thead> <tbody> <tr> <td><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/d3e6427b--2025-06-06-13.29.11.png" alt="" /></td> <td><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/53a2f098--2025-06-06-13.24.15.png" alt="" /></td> <td><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/e31bd6ad--2025-06-06-13.28.05.png" alt="" /></td> </tr> </tbody> </table> <p>ポートインとポートアウトという言葉はわかりやすいようでわかりづらいです。<br /> なぜなら一方でのポートインは、同時に他方からするとポートアウトだからです。<br /> ここは混乱しないように仮事業者のタイトルにはわかりやすく主語を明記しておきました。</p> <h3>結論その1</h3> <p>無事メルカリモバイルの動作チェックのための仮事業者のアプリケーションを立ち上げることができました。<br /> 仮とはいってもQAのためのアプリなので、メルカリモバイルの堅牢性確認のため様々なパターンのリクエストを発生させる機能も入っています。</p> <p>このように、claspとGASを利用して手軽にpostリクエストを処理できるWebアプリケーションを社内に用意することができました。<br /> post処理可能なWebアプリケーションを簡単に作成できるツールがこんなに身近にあるとは発想としてありませんでした。</p> <p>みなさんもテストなどの際に社内におけるpostやgetを処理するための受け皿となるWebサービスが必要になるときが来るかもしれません。<br /> そのとき、これほど簡単に自前サーバーも必要なく立ち上がってくれるこのツールを思い出すと役に立つかもしれません。</p> <h2>(その2)Jiraのissue作成のためのGAS</h2> <p>もうひとつGASで効率をあげた話をします。<br /> メルカリは開発のサポートとしてAIを積極的に導入しています。<br /> その一環でMCPサーバ経由からのJira issueの作成を試してみました。</p> <blockquote> <p>MCPサーバとはAI経由でサードパーティのツール(Jiraやfigma等いろいろ)を操作するための仕組みです。<br /> 今まではAIに操作のやり方をおしえてもらって自分でツールを操作していたと思いますが、これを使うとAIが直接ツールを操作することができるようになります。つまり、Jiraのissue作成もやってもらえます。</p> </blockquote> <p>CursorというAIツール上のプロンプトから日本語で、issue作成を依頼してみました。</p> <pre><code>XXXプロジェクトに以下のJira issueを作成してください タイトル: MCP経由からのチケットさくせい (AIからの回答) Jiraチケットが正常に作成されました! チケット番号: XXX-0000 タイプ: タスク ステータス: To Do URL: https://******</code></pre> <p>なんと簡単にissueが作成されてしまいました。<br /> しかしこれだけではわたくしは満足せず、どうせならGoogleスプレッドシートに属性をすべて定義しておいてそれからissueを作成してほしいなと考えました。</p> <pre><code>// シートのイメージ サマリー | issueのリンク | Epic | 担当 | ラベル | スプリント | ストーリーポイント</code></pre> <p>rowごとにissueの属性を定義し、isseの生成が完了するとリンクがシートに追加されるというものです。<br /> 前からJiraのissue作成後の手動属性追加は面倒だと考えていたため、属性をシートでまとめて管理できるのはよいアイデアだと考えました。</p> <p>Jiraのissue管理は便利な反面、指定すべき項目が多いです。<br /> 手動で付与していると設定忘れが発生したりします。</p> <ul> <li>タイトル</li> <li>説明</li> <li>担当</li> <li>複数ラベル(Frontendラベルおよびチームのラベル等)</li> <li>スプリントへの追加</li> <li>ストーリーポイントの追加</li> <li>親Epicの追加</li> <li>リリースバージョンの追加</li> </ul> <h3>ローカルnodeアプリケーションからのJira issue作成</h3> <p>最初は対象のスプレッドシートを読み込んでJiraのAPIをコールするようなローカルのnodeアプリケーションを考えたので、Cursorに聞きながらnodeのexpressアプリケーションを作成しました。<br /> expressである必要もなかったかもしれないですが、即座にコードが出力されて割と簡単にアプリを作成してJira issueが追加できるようになりました。<br /> ほぼ想定どおりの動きをするアプリケーションができあがったあと、いちいちローカルのWebアプリを起動するのも面倒だなと考えてしまいました。</p> <h3>GASによるスプレッドシートからのJira issue作成</h3> <p>ということで、結局はこのローカルコードをクラウド上で実行すれば一番楽だということに気づいたのでCursorに依頼をしました。</p> <pre><code>この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の実装(ボタンの追加) ...</code></pre> <p>素晴らしいです。ものすごくわかりやすく段階的な手順を明示してから具体的なコードを提示してもらえました。<br /> これにより無事スプレッドシート上で動くGASアプリを使って簡単にJira issueを作成することができるようになりました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/8c645166--2025-06-06-13.45.57.png" alt="" /></p> <p>いままでどちらかというとJiraのissue作成は面倒に感じていたのですが、せっかく自分でツールを作成したとなるとそうは言っていられません。<br /> シートのタブごとに実行できるので文脈をかえてどんどんissueを作成していけます。<br /> リリースバージョンごと、アサイニーごと、エピックごとというようにさまざまな文脈でシートを作成してissueを作成していけます。<br /> もちろん属性のつけ忘れは存在しません。</p> <p>ボタンを押した後にissue作成が成功するとIssueのカラムに自動的にリンクが付与されます。<br /> すでにリンクが存在している行は無視されて作成からは除外されるようになっています。<br /> ラベルも任意の数紐づけが可能です。<br /> 自分で作成したJira issueのヒストリーも見られるのがよいなと感じています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/a4bdb224--2025-06-06-13.46.35.png" alt="" /></p> <h3>結論その2</h3> <p>このように、GASを利用して効率をあげることができた事例を紹介しました。<br /> 他にも気づいていないだけでさまざまな業務を要所要所で自動化できるかもしれません。<br /> ひらめいたアイデアをツールに落とし込むためのAIの存在も見逃せないところです。<br /> いままでアイデアはあったけれどもどうやってツールにすればよいかわからなくてあきらめていたという人は、ぜひともAIに問いかけてアイデアを実現してみることをおすすめします。</p> <hr /> <p>以上でわたくしからの話は終わりです。<br /> 読んでいただきありがとうございました。<br /> 明日の記事は @anzaiさんの「<a href="https://engineering.mercari.com/blog/entry/20250612-a80033f0b8/" title="Davin にE2Eテストの実装を任せる">Davin にE2Eテストの実装を任せる</a>」と @somaさんの「<a href="https://engineering.mercari.com/blog/entry/20250612-b1aee5118d/" title="メルペイインターンで機能の改修・設計からLLM AgentベースのSlack Bot開発まで色々やった話">メルペイインターンで機能の改修・設計からLLM AgentベースのSlack Bot開発まで色々やった話</a>」です。引き続きお楽しみください。</p> メルカリAdsにおけるAIを活用した広告審査の取り組みhttps://engineering.mercari.com/blog/entry/20250611-e2e046c2db/https://engineering.mercari.com/blog/entry/20250611-e2e046c2db/<p>目次 はじめに 背景 広告審査における課題 AI審査の概要とシステム構成概要 プロンプトの検証 まとめ はじめに こんにちは。メルカリAdsのバックエンドエンジニア、chapaと申します。 今回は、メルカリAdsがOpe [&hellip;]</p> Wed, 11 Jun 2025 18:00:19 GMT<h2>目次</h2> <ul> <li>はじめに</li> <li>背景</li> <li>広告審査における課題</li> <li>AI審査の概要とシステム構成概要</li> <li>プロンプトの検証</li> <li>まとめ</li> </ul> <h2>はじめに</h2> <p>こんにちは。メルカリAdsのバックエンドエンジニア、chapaと申します。</p> <p>今回は、メルカリAdsがOpenAIを活用して実現した広告審査プロセスについて解説します。<br /> 本記事を通じて、AI活用による課題解決について具体的にお伝えできればと思います。</p> <h2>背景</h2> <p>メルカリAdsでは多くの広告主様にご利用いただいており、さまざまな広告素材が日々入稿されています。その中には、残念ながら不適切な表現を含むものもあり、それらがメルカリのお客さまの目に触れることは、お客さま体験を損ねるリスクとなります。</p> <p>これまではサンプリングした広告素材を人の目で確認していましたが、広告主様の増加に伴い、手動での対応に限界が見えてきました。そこで、審査の自動化に取り組む必要性が生じました。</p> <h2>広告審査における課題</h2> <p>広告事業をおこなうにあたって、入稿されている広告素材全てに対して審査を行う必要があります。<br /> ただ入稿される広告素材は非常に多く、手動審査での対応では難しい状況です。<br /> そのため、自動判定を取り入れることで基準を満たすかを効率的にチェックして、手動審査するべき数を減らし、運用の効率化を進めています。その手法の一つがAI審査です。</p> <h2>AI審査の概要とシステム構成概要</h2> <p>AI審査では、以下の機能を実現しています。</p> <ul> <li>広告画像やタイトルテキストなどに、不適切な表現が含まれていないかをチェック</li> <li>禁止表現が検出された場合は、広告配信を即座に停止</li> </ul> <hr /> <p>システム構成については以下のようになっています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/85c8e059-20250611152803-1024x490.png" alt="" /></p> <p>広告主様が更新するすべての広告をリアルタイムでOpenAIへ送信するとコストが非常に大きいため、以下の工夫を採用しました。</p> <ul> <li>リアルタイムチェックは行わず定期的にBatchAPIを用いてOpenAIへ送信</li> <li>ライトなモデル(gpt-4o-mini)を利用することでOpenAIへのコストを削減</li> <li>OpenAIのオプション設定も以下を採用 <ul> <li>結果のブレをなくすため、<code>Temperature</code> は<code>0.01</code>に設定</li> <li>結果はJSON形式にするため <code>ResponseFormat</code> は <code>json_object</code> に設定</li> </ul> </li> </ul> <h2>プロンプトの検証</h2> <p>審査を実施するためのプロンプトを作成し、さまざまなパターンを用意して検証しました。<br /> Playgroundでプロンプトと質問を送信した際には、期待通りに結果が返ってきましたが、BatchAPIを使用した際にいくつかの課題がありました。<br /> システム的な課題と要件的な課題をいくつかサンプルとともにご紹介します。</p> <h3>① 期待しているJSON形式で返却されない</h3> <p>返却値はOpenAIのオプション設定にてJSON形式と定義し、プロンプトに例を記載しました。しかし、以下のようなケースが見られました。</p> <ul> <li>JSON形式として正しいものの、期待している統一フォーマットではない場合がある</li> <li>JSON形式そのものが正しくない場合がある</li> </ul> <p>期待される返却値の一例:</p> <pre><code class="language-json">// 期待される返却値の一例: { &quot;id&quot;: &quot;test-id-00001&quot;, &quot;result&quot;: &lt;審査結果値&gt;, &quot;reasons&quot;: [{&quot;type&quot;: 1, &quot;value&quot;: &quot;審査理由詳細・・・&quot;}] // 審査理由があれば配列で設定される } // 実際の返却値の例: { &quot;id&quot;: &quot;test-id-00001&quot;, &quot;result&quot;: &lt;審査結果値&gt;, &quot;reasons&quot;: [&quot;&quot;] // 不正な形式(配列が空欄の際にダブルクォートのみが入っている) }</code></pre> <p>このケースの場合はプロンプトに出力形式を厳守するようプロンプトに記載し解決しました。<br /> これによりほぼ不正な形式で返却されることは解消されました。</p> <pre><code class="language-json">// 省略 ## 出力形式 **必ず以下の出力フォーマットを厳守** { &quot;id&quot;: &lt;id&gt;, &quot;result&quot;: &lt;int&gt;, &quot;reasons&quot;: [{type: &lt;int&gt;, value: &lt;string&gt;}] }</code></pre> <hr /> <h3>② 期待しているID値が返却されない</h3> <p>質問で送信したIDをキーとして返却されることを期待していましたが、以下のようなケースが見られました。</p> <pre><code class="language-json">// 送信値の例: { &quot;id&quot;: &quot;test-id-00001&quot;, &quot;text&quot;: &quot;test-text&quot; } // 期待される返却値の一例: { &quot;id&quot;: &quot;test-id-00001&quot; &quot;result&quot;: &quot;&lt;審査結果値&gt;&quot;, &quot;reason&quot;: [] } // 実際の返却値の例: { &quot;id&quot;: &quot;test-id-00&quot;, &quot;result&quot;: &quot;&lt;審査結果値&gt;&quot;, &quot;reason&quot;: [] }</code></pre> <p>このケースはBatchAPIへリクエストを送信する際にユニークなID(<code>custom_id</code>)を設定することができるので、返却後もそのIDを正として利用することで解決しています。</p> <p>参照: <a href="https://platform.openai.com/docs/api-reference/batch/request-input">https://platform.openai.com/docs/api-reference/batch/request-input</a></p> <hr /> <h3>③ 性的な表現と下着広告の判別</h3> <p>広告素材の中には性的表現を含む画像と下着広告が混在しています。これにより、下着広告が誤って性的表現と判定される問題がありました。</p> <p>入稿されたデータから無作為にデータを取得して、期待値を設定し正答率を取りながらプロンプトの改善を行いました。</p> <ol> <li>初期プロンプト <ul> <li>正答率: 70.76%</li> <li>「性的表現」と「下着広告」を区別する定義が曖昧</li> </ul> </li> <li>改善プロンプト <ul> <li>改善後の正答率は<strong>77.33%</strong>に向上しました。これは下着広告に関するプロンプトの条件追加が要因と考えられます。</li> <li>しかし、「性的表現」と「下着広告」の詳細定義が曖昧なままでした。</li> </ul> </li> <li>最終プロンプト <ul> <li>「性的表現」と「下着広告」の詳細に定義することで不明瞭な基準に基づく誤判定が大幅に減少し、正答率が<strong>13.37ポイント</strong>向上し<strong>90.67%</strong>となりました。 <ul> <li>例としては、以下のような記載を追加しました。<br /> 性的表現: 過度に性的な印象を与える画像の構図・演出等を明確定義<br /> 下着広告: 通常の下着製品画像は適切なファッション広告として区別</li> </ul> </li> </ul> </li> </ol> <p>こうしたプロンプトの改善にもAIを活用しており、OpenAIにプロンプト、質問内容、返却された結果を伝えて、改善提案してもらい結果の正答率を上げることができました。<br /> プロンプトの書き方の改善が最も大事だと改めて認識しました。<br /> とうとう有識者とではなくとも、AIと壁打ちできるようになってしまったと感じました。</p> <h2>まとめ</h2> <p>この記事では、メルカリAdsにおけるOpenAIを活用した取り組みを紹介しました。<br /> 特別な工夫を施さなくても、基本的なAI活用によって大量のデータを効率的に処理できる可能性をお伝えすることができたと思います。</p> <p>今後も引き続き改善を行い、より安心で安全なお客さま体験を提供できるよう努めていきたいと考えています。</p> One Person, One Release &#8211; AI Nativeの夜明けhttps://engineering.mercari.com/blog/entry/20250611-one-person-one-release/https://engineering.mercari.com/blog/entry/20250611-one-person-one-release/<p>はじめに こんにちは。メルペイVPoEの@jorakuです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 8日目の記事です。 AI Agent / AI Cod [&hellip;]</p> Wed, 11 Jun 2025 11:30:48 GMT<h1>はじめに</h1> <p>こんにちは。メルペイVPoEの@jorakuです。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/" title="Merpay &amp; Mercoin Tech Openness Month 2025">Merpay &amp; Mercoin Tech Openness Month 2025</a> 8日目の記事です。<br /> AI Agent / AI Code AssistなどのAI Toolsが日々リリースされており、めまぐるしい時代を楽しく過ごしています。ここで記載されているものも1ヶ月後には古くなる可能性もありますが、現時点での情報を残しておきます。</p> <h1>目次</h1> <ul> <li>AI 2027 シナリオ</li> <li>AI Code Assist / AI Agentの登場</li> <li>One Person, One Release</li> <li>役割の再定義</li> <li>AI Nativeの夜明けに求められるもの</li> </ul> <h1>AI 2027 シナリオ</h1> <p>一時期話題になった非営利団体 AI Futures Projectが作成した<a href="http://ai-2027.com" title="レポート">レポート</a>です。元Open AIの方などが著者です。<br /> レポート内容は衝撃的でシンギュラリティについても触れられています。賛否両方のコメントもあり、正確性については敢えてここでは言及しません。というか私には分かりません。</p> <h1>AI Code Assist / AI Agentの登場</h1> <p>ただ、現実的に今、ソフトウェア開発に大きな変化が生まれています。</p> <p>ソフトウェア開発の風景は、AIコードアシスタントやAIエージェントの登場により、根本的な変革の岐路に立っています。これらの技術は、単に「何を作れるか」だけでなく、「どのように作るか」を根本から変えつつあります 。マイクロソフト社のKevin Scott氏が「我々の生涯で起こった最も重要な技術プラットフォームのシフトかもしれない」と<a href="https://technologymagazine.com/ai-and-machine-learning/microsofts-cto-and-evp-on-the-need-for-ai-speed" title="述べる">述べる</a>ように、この変化の大きさは計り知れません 。AIは単なる技術ではなくプラットフォーム、ソフトウェア開発ライフサイクル(SDLC)における協調的なパートナー、場合によっては自律的なエージェントへと進化しています 。<br /> 20年以上前の話になりますが、Public Cloudという概念を作ったAWSの登場も衝撃的でした。<br /> AWS/Azure/Google Cloudの登場により、私たちはより安全に、より安定した、拡張性の高いプロダクトを顧客にいち早く提供できるようになりました。ただ、今回のAI Code Assist / AI Agentは、コード生成だけでなく、テスト、デプロイ、保守に至るまでソフトウェア開発ライフサイクル(SDLC)全体に影響を与えており、その進化のスピードは目を見張るものがあります。</p> <h1>One Person, One Release</h1> <p>これまでの技術的な専門性や、業界の知識などの専門性を活かしながら価値貢献してきている時代でした。ただ、これからはそれが両極端になっていくと考えます。より専門性の深い分野に特化していくのか、それとも、幅広い領域で素早く価値提供するのか。</p> <p>これまでのAI Toolsの登場でプロンプトだけでアプリケーションが作れる時代になりました。プロンプトベースでアプリケーションを素早くプロトタイピングし、PoCレベルまで到達できる時代になっています。非エンジニア職種の方でも、一定のアウトプットが可能な環境が整ってきました。逆にエンジニアだとしても良い体験設計できるデザインをできるようになりました。<br /> 誰もがプロダクトを作れる時代に入り、“プロダクトをつくる会社”と“プロダクトを利用する会社”の境界が曖昧になりつつあります。もはや「プロダクトカンパニー」というのは古い時代の言葉になるのかもしれません。</p> <p>そこで期待されるのは、起業したばかりのスタートアップの様に、一人の人がより多くの役割をスピード感もって価値提供していくことになると考えます。</p> <p>次年度の<a href="https://engineering.mercari.com/blog/entry/20231225-creating-mercari-engineering-roadmap/" title="Engineering Roadmap">Engineering Roadmap</a>を思案している所ですが、Visionとして「One Person, One Release」を入れていきたいと考えています。<br /> PMもEngineerも壁を越えていきます。一人の人が企画から開発、QA、リリースまで一気通貫で出来ることを目指します。</p> <p>技術の壁を越え、ドメイン知識を越え、役割を越えて行くためのAIの活用とし、それらを使い熟すのです。</p> <h1>役割の再定義</h1> <p>それでは今後それぞれの役割はどのように変化するのでしょうか。より多くの方がこれまでの役割を越えていけると考えます。越えて行かねばならない。とも言えるかもしれません。<br /> 専門性がより深化していく流れは今の専門性と変わらない点があるかもしれませんが、より影響範囲を広げてお客さまへの価値提供が出来る役割です。仮にここではAI Agent Orchestration Engineerと名付けます。(<a href="https://note.com/r_kaga/n/n99eb7e549819" title="参考記事">参考記事</a>)<br /> もちろん、AIを駆使することで各職種がコードに触れることも可能になりますが、求められる責任や精度はそれぞれの職能に応じて変わります。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/ea5ca9e0--2025-06-11-11.14.07.png" alt="" /></p> <table> <thead> <tr> <th style="text-align: left;">役割</th> <th style="text-align: left;">責任</th> </tr> </thead> <tbody> <tr> <td style="text-align: left;">AI Expert</td> <td style="text-align: left;">AIの専門家としてAI/LLM製品やツールの開発</td> </tr> <tr> <td style="text-align: left;">System / Domain Architect</td> <td style="text-align: left;">AI/LLMでは補えない技術的難易度の高いものやPlatformなどの基盤を整備する役割。また、法令要件や求めるべき倫理など業界の専門性が高い要件を構築・監査する役割</td> </tr> <tr> <td style="text-align: left;">Agent Developer</td> <td style="text-align: left;">自社の業務や運用に合わせてデータの整備やMCPの構築、業務に合わせた自動化/省人化するAgentの開発。また、Agent to Agentのような基盤の開発も担う</td> </tr> <tr> <td style="text-align: left;">AI Agent Orchestration Engineer</td> <td style="text-align: left;">AIエージェント同士を組み合わせ、お客さまの課題に対して機能や体験を統合的に設計・実装・提供する役割。One Person, One Release を実現する役割</td> </tr> </tbody> </table> <h1>AI Nativeの夜明けに求められるもの</h1> <p>新しい時代の夜明けです。地球が回る限り後戻りすることはありません。<br /> 最後に新しい時代に応じて何が求められてくるのか、あくまで個人的な意見ですが、夜が明けた今、個人・組織とって大切にしていきたいマインド・スキルセットを記載します。</p> <h2>組織</h2> <ul> <li>セキュリティと利便性の両立 <ul> <li>AIは利便性高く、生産性にも大きく寄与しますが、自社の大事な情報の流出や新たなハッキングのリスク、また倫理的な観点のリスクも発生します。守るべき点ももちろん大事ですが、攻めと守り両方を求めていく姿勢が大事だと考えます。</li> </ul> </li> <li>情報管理/情報戦略 <ul> <li>これまでの情報資産や、情報化されていない経験知見をどのように蓄積していくのか、組織においてはこれまで以上に重要な要素になります。</li> </ul> </li> <li>AI活用の定量評価 <ul> <li>自社でどのようにAIが使われているのか、それを定量的に観測し、評価にも組み込んでいく事が必要になってきます。もちろんお客さまにどのような価値を提供できたのかとても大事な部分ですが、エンジニア含めたプロダクト開発する組織の一つの指標としては追うべき数値と捉えています。</li> </ul> </li> </ul> <h2>個人</h2> <ul> <li>言語化能力 <ul> <li>AIに理解させるためには、正しく言葉で伝えていく必要がありますし、それらを学習してもらう必要があります。何がしたいのか、どうしたいのか、誰にでも分かるように論理的に記載して良く必要があります。</li> <li>メルカリ入社後に驚いた事の一つとしてドキュメント文化があります。今後の時代にはこのドキュメントが功を奏するように持って行きたいと考えています。</li> </ul> </li> <li>好奇心 <ul> <li>個人のマインドセットとしてとても大切だと考えるのが好奇心です。wakuwakuする心ですね。好奇心を持つのではなく、それを自ら作り出せる事がとても大事です。時代の変化は激しいですし、自分の領域を一部奪われてしまうのではないかという猜疑心も生まれます。止まっていては何も始まらないので進むしかありません。前に進むための動力としての好奇心を持つことが大切です。</li> </ul> </li> </ul> <p>もし、まだAIを活用しきれていないという課題感を感じている方、大丈夫です。今はAI Agent / AI Code Assistの勃興時代です。日々変わりますし、1ヶ月前の情報や経験がもはや古くなる時代です。<br /> ですから、今までのアドバンデージは無く、今から始めても直ぐに先頭を走ることが出来ます。みなさま「好奇心」をもってこの変化を楽しんでいきましょう!</p> <p>明日の記事は @toshickさんによる「<a href="https://engineering.mercari.com/blog/entry/20250606-37101db087/" title="GASで効率化!MVNOの動作検証仮事業者&Jira issue作成 with AI">GASで効率化!MVNOの動作検証仮事業者&Jira issue作成 with AI</a>」です。引き続きお楽しみください。</p> Sourcegraph × 自作MCP Serverによる社内コード検索連携の取り組みhttps://engineering.mercari.com/blog/entry/20250609-4660151b47/https://engineering.mercari.com/blog/entry/20250609-4660151b47/<p>こんにちは。メルカリモバイル Tech Leadの@_seitauです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の7日目の記事です。 今回は、Cursor [&hellip;]</p> Tue, 10 Jun 2025 10:00:31 GMT<p>こんにちは。メルカリモバイル Tech Leadの<a href="https://x.com/_seitau">@_seitau</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の7日目の記事です。</p> <p>今回は、CursorをはじめとするAIコーディング支援ツールに、社内コードの検索能力を持たせるための取り組みをご紹介します。</p> <h2>はじめに</h2> <p>メルカリでは、社内向けのマイクロサービス開発フレームワークや、ScenarigoというE2Eシナリオテストツールなど、独自の技術基盤が整備されており、これらが開発速度を大きく向上させています。</p> <p>一方で、社内に類似した実装が豊富にあるにもかかわらず、Cursorがそれを認識できず、実装内部を自律的に把握できないという新たな課題も生まれていました。</p> <h2>社内基盤の充実がCursorの制約になっていた背景</h2> <p>メルカリでは、メルカリモバイルチームを含む複数のチームで共通のマイクロサービス開発フレームワークが使われており、構成や実装パターンが似ているため、他チームの実装を参考にすることは日常的です。ScenarigoによるE2Eテストのシナリオ記述も同様に、既存のパターンを参照する場面が多くあります。</p> <p>しかし、これらの実装は複数のプライベートリポジトリに分散しており、通常のGitHub検索では、求めるコードを効率的に見つけることが難しい状況でした。単一リポジトリに閉じた実装や、広く知られた著名なライブラリを使用した実装であれば、Cursorは十分にその能力を発揮できていましたが、社内独自のライブラリやツールを参照している箇所では、Cursorが期待通りに機能していない状態でした。</p> <h2>解決策:Sourcegraph MCP Server</h2> <p>既存のGitHubのMCP Serverも存在しますが、GitHubのAPIは社内のコードを横断的に検索するには限界がありました。特にメルカリのように複数のマイクロサービスが多層的に構成され、リポジトリが分割管理されている場合、単一リポジトリ検索に依存するGitHub APIでは、Cursorにとって十分な参照性を持たせることが困難です。</p> <p>そこで、Sourcegraphのクロスリポジトリ検索機能と、構造化された検索性を活かす形で、SourcegraphのMCP Serverを社内向けに実装しました。</p> <p>このMCP Serverにより、Cursorにプロンプトの中でSourcegraphを使用するように指示することで、必要な社内コードを自発的に検索し、その結果を回答やコード生成に反映することが可能になります。</p> <h2>自作Sourcegraph MCP Serverのインターフェース</h2> <p>今回私が開発したSourcegraph MCP Serverは、Cursorが社内コードにアクセスするための非常にシンプルなAPIインターフェースを提供しています。主に以下の2つのメソッドで構成されています。</p> <h3>1. コード検索ツール (<code>mcp_sourcegraph_search_code</code>)</h3> <p>このツールはSourcegraphの強力な検索機能を利用し、メルカリの全社内リポジトリを横断してコードを検索できます。</p> <h4>パラメータ</h4> <ul> <li><code>q</code> (required): Sourcegraphのクエリ構文に従った検索クエリテキスト</li> </ul> <h4>リクエストサンプル</h4> <p>基本的な検索:</p> <pre><code class="language-json">{ &quot;q&quot;: &quot;PubSubLogWithFieldsInterceptor&quot; }</code></pre> <p>言語を指定した検索:</p> <pre><code class="language-json">{ &quot;q&quot;: &quot;import React language:typescript&quot; }</code></pre> <p>関数名での検索:</p> <pre><code class="language-json">{ &quot;q&quot;: &quot;func main language:go&quot; }</code></pre> <p>ファイル名を含む検索:</p> <pre><code class="language-json">{ &quot;q&quot;: &quot;handleUserLogin file:*.go&quot; }</code></pre> <h4>レスポンスサンプル</h4> <pre><code class="language-json">{ &quot;results&quot;: [ { &quot;repository&quot;: &quot;github.com/mercari/service-a-repo&quot;, &quot;file&quot;: &quot;internal/interceptor/logging.go&quot;, &quot;lineNumber&quot;: 45, &quot;content&quot;: &quot;func PubSubLogWithFieldsInterceptor() {...}&quot;, &quot;url&quot;: &quot;https://sourcegraph.com/github.com/mercari/service-a-repo/-/blob/internal/interceptor/logging.go#L45&quot; }, { &quot;repository&quot;: &quot;github.com/mercari/shared-lib&quot;, &quot;file&quot;: &quot;pkg/logging/interceptor.go&quot;, &quot;lineNumber&quot;: 23, &quot;content&quot;: &quot;type PubSubLogWithFieldsInterceptor struct {...}&quot;, &quot;url&quot;: &quot;https://sourcegraph.com/github.com/mercari/shared-lib/-/blob/pkg/logging/interceptor.go#L23&quot; } ], &quot;totalCount&quot;: 12, &quot;hasNextPage&quot;: true }</code></pre> <h3>2. ファイル内容取得ツール (<code>mcp_sourcegraph_get_file_content</code>)</h3> <p>このツールは、指定したリポジトリの特定のファイル内容を直接取得します。</p> <h4>パラメータ</h4> <ul> <li><code>repository</code> (required): github.com/を含む完全なリポジトリパス</li> <li><code>filePath</code> (required): リポジトリ内のファイルパス</li> <li><code>commitID</code> (optional): 特定のコミットIDまたはブランチ名</li> </ul> <h4>リクエストサンプル</h4> <p>基本的なファイル取得:</p> <pre><code class="language-json">{ &quot;repository&quot;: &quot;github.com/mercari/service-a-repo&quot;, &quot;filePath&quot;: &quot;src/main.go&quot; }</code></pre> <p>特定ブランチのファイル取得:</p> <pre><code class="language-json">{ &quot;repository&quot;: &quot;github.com/mercari/service-a-repo&quot;, &quot;filePath&quot;: &quot;config/app.yaml&quot;, &quot;commitID&quot;: &quot;develop&quot; }</code></pre> <p>特定コミットのファイル取得:</p> <pre><code class="language-json">{ &quot;repository&quot;: &quot;github.com/mercari/service-a-repo&quot;, &quot;filePath&quot;: &quot;README.md&quot;, &quot;commitID&quot;: &quot;a1b2c3d4e5f6789&quot; }</code></pre> <p>深いパスのファイル取得:</p> <pre><code class="language-json">{ &quot;repository&quot;: &quot;github.com/mercari/service-a-repo&quot;, &quot;filePath&quot;: &quot;internal/services/user/handler.go&quot; }</code></pre> <h4>レスポンスサンプル</h4> <pre><code class="language-json">{ &quot;content&quot;: &quot;package main\n\nimport (\n \&quot;fmt\&quot;\n \&quot;log\&quot;\n \&quot;os\&quot;\n)\n\nfunc main() {\n if len(os.Args) &lt; 2 {\n log.Fatal(\&quot;Usage: program &lt;arg&gt;\&quot;)\n }\n \n fmt.Printf(\&quot;Hello, %s!\\n\&quot;, os.Args[1])\n}&quot;, &quot;repository&quot;: &quot;github.com/mercari/service-a-repo&quot;, &quot;filePath&quot;: &quot;src/main.go&quot;, &quot;commitID&quot;: &quot;main&quot;, }</code></pre> <h3>主要メリット</h3> <p>これらのツールが提供する主なメリットは以下の通りです。</p> <h4>1. リポジトリの横断検索</h4> <p>メルカリ組織の全リポジトリを検索対象にできるため、広範囲なコード探索が可能です。<br /> 共有ライブラリの実装詳細を効率的に見つけるのに役立ちます。</p> <h4>2. 高度なクエリ構文</h4> <p>Sourcegraphの強力なクエリ構文をサポートしており、柔軟な検索が可能です。<br /> 言語、ファイル名、パスなどで検索を絞り込むことができます。</p> <h4>3. 直接的なファイルアクセス</h4> <p>リポジトリをクローンすることなく、ファイル内容を直接取得できます。<br /> 特定のコミットやブランチのファイルにアクセスできるため、過去の履歴や開発中のブランチのコードも確認できます。</p> <h3>Cursor Ruleによる自発的な検索を実現</h3> <p>Sourcegraph MCP Serverを利用すると、Cursorに明示的に「Sourcegraphを利用して」と指示するだけで必要な時にコードベースを検索してくれます。<br /> さらに、以下のようにCursor用のルールを設定することによって、明示的に指示を与えずとも、Cursorが自発的にMCPを利用して検索を行うようになります。</p> <pre><code class="language-md"># 社内コード検索のためのルール メルカリ組織内のリポジトリでコードを検索する際は、search_code MCPツールを使用してください。これにより、すべての社内リポジトリを横断的に検索できます。 使用例 mcp_sourcegraph_search_code(q=&quot;PubSubLogWithFieldsInterceptor&quot;) 利点 - 社内すべてのリポジトリを横断的に検索可能 - 共有ライブラリの実装詳細を特定できる - 通常のリポジトリ閲覧では見つけにくいコードにもアクセス可能 - 社内フレームワークの実装やパターンをより深く理解できる</code></pre> <p>一例として、以下のようにCursorに「〜のリポジトリの実装を参考にして」と伝えるだけで、Sourcegraphを通じて社内のコードベースを検索し、実装を進められるようになっていることがわかります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/7da69f24-cursor_sourcegraph_mcp_chat-1024x1018.png" alt="" /></p> <h2>まとめ</h2> <p>Cursorに対して必要な情報を与えるだけでなく、自ら必要な実装を探しにいける環境を整えることが重要でした。GitHub APIよりも社内実装の検索に適したSourcegraph APIを採用し、それをMCP Server経由でCursorから能動的に利用できるようにしたことで、より実用的なコード生成パートナーとして活用できるようになりました。</p> <p>今回の取り組みが、Cursorを活用した開発体験の向上や、社内における生産性のさらなる向上につながれば幸いです。<br /> 明日の記事は メルペイ VPoE @Jorakuさんによる「<a href="https://engineering.mercari.com/blog/entry/20250611-one-person-one-release/" title="One Person, One Release – AI Nativeの夜明け">One Person, One Release – AI Nativeの夜明け</a>」です。引き続きお楽しみください。</p> チェックアウトソリューションのバックエンドアーキテクチャhttps://engineering.mercari.com/blog/entry/20250606-7afd82e718/https://engineering.mercari.com/blog/entry/20250606-7afd82e718/<p>はじめに こんにちは。メルペイ Payment Coreチームの @susho です。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の6日目の記事です。 我々の [&hellip;]</p> Mon, 09 Jun 2025 10:00:53 GMT<h1>はじめに</h1> <p>こんにちは。メルペイ Payment Coreチームの <a href="https://x.com/susho0220">@susho</a> です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/" title="Merpay &amp; Mercoin Tech Openness Month 2025">Merpay &amp; Mercoin Tech Openness Month 2025</a> の6日目の記事です。</p> <p>我々のチームでは、メルペイにおける各決済手段に応じた決済処理を提供しています。今回、新しくチェックアウトソリューションという、決済処理の実装と画面を一括で提供するソリューションを提供することにしました。 ( <a href="https://stripe.com/jp/payments/checkout">Stripe Checkout</a> の内製版のようなものをイメージしてもらえると良いと思います。)<br /> 詳細は <a href="https://engineering.mercari.com/blog/entry/20250605-bf42ce60cf/">決済基盤の新たな挑戦: 決済チェックアウトソリューションの開発</a> をご参照ください。</p> <p>この記事では、そのソリューションのバックエンドに着目し、アーキテクチャを紹介したいと思います。</p> <h1>これまでの課題</h1> <p>これまでメルカリグループでは、決済処理が必要な新規サービスを立ち上げる際、提供する決済手段に応じて各サービス提供者で決済処理を実装する必要がありました。単純な同期処理だけの場合であればそこまで難易度は高くないですが、3DSを利用したクレジットカード決済など、リダイレクトが必要になる非同期処理が含まれる場合、実装コストは格段に高くなります。<br /> また、決済処理における画面も実装する必要がありますが、基本的にはどのサービスも必要な画面の部品は共通のものが多くなります。<br /> 車輪の再発明のように、新規でサービスを立ち上げる際これらの実装をしなければならず、爆速にサービスを立ち上げることが難しくなっていました。<br /> そこで、これらの機能を備えてソリューションを提供することで、これらの課題を解決できるのではないかと考えました。</p> <h1>アーキテクチャ概要</h1> <p>まずはアーキテクチャの概要を紹介します。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/26f31c39--2025-06-03-12.35.38-e1749182519538.png" alt="" /></p> <ul> <li> <p>Checkout Solution Service<br /> 決済処理の一連のフローを管理するリソースを管理し、決済処理を実行するマイクロサービスです。<br /> 技術スタックとして、主にSpannerやPub/Subを利用しています。また、安全に分散トランザクションを管理するために、内製のWorkflow Engineを利用しています。Workflow Engineに関しては <a href="https://engineering.mercari.com/blog/entry/20231023-mmtf2023-day3-7/">こちら</a> を参照してください。</p> </li> <li> <p>Checkout Frontend<br /> 決済画面を提供し、BFFを経由してCheckout Solutionの決済処理を呼び出します。</p> </li> <li> <p>Checkout BFF<br /> Frontendで決済画面を提供するために必要なAPIを提供します。<a href="https://github.com/mercari/grpc-federation">grpc-federation</a> を利用してBFFを構築しています。</p> </li> <li> <p>Processing Tracer<br /> データの整合性が担保されているかどうかをチェックをするためのリコンサイル処理をキックし、その成否を管理するマイクロサービスです。詳細は <a href="https://engineering.mercari.com/blog/entry/20231023-mmtf2023-day3-7/">こちら</a> を参照してください。</p> </li> <li> <p>Payment Service<br /> さまざまな決済手段を提供するための各種APIを提供しているマイクロサービスです。Checkout SolutionはこれらのAPIを組み合わせて決済処理を提供します。</p> </li> </ul> <p>利用者であるClient Servicesはお客さまへ決済画面を提供するために、Checkout SolutionへAPIを呼び出し、そのレスポンスに含まれるURLへ遷移させることで決済機能を提供することができます。</p> <h1>アーキテクチャ詳細</h1> <p>Checkout Solution Serviceの詳細を説明します。<br /> ベースとなる部分は <a href="https://stripe.com/jp/payments/checkout">Stripe Checkout</a> を参考にしています。</p> <h2>API</h2> <p>まず、Checkout Solution Serviceで提供するAPIの機能について説明します。</p> <h3>CreateCheckoutSession API</h3> <ul> <li>決済処理のフローを開始するためにCheckoutSessionを作成するAPIです。決済画面の一意なURLが払い出され、Client ServicesはそのURLへ遷移することで、お客さまへ決済機能を提供することができます。</li> <li>IdempotencyKeyを受け取り、冪等性を担保します。</li> </ul> <h3>ConfirmCheckoutSession API</h3> <ul> <li>決済処理を実行するためのAPIです。お客さまが決済ボタンを押下することでFrontendから呼び出されます。ある決済手段で決済が失敗した場合でも別の決済手段で決済できるように、このAPIは決済が成功するまで有効期限が切れるまで呼び出すことができます。</li> <li>IdempotencyKeyを受け取り、冪等性を担保します。</li> </ul> <h2>リソース</h2> <p>次に、Checkout Solution Serviceで管理する主なリソースについて説明します。</p> <h3>CheckoutSession</h3> <p>決済処理の一連のフローを管理するリソースです。</p> <ul> <li>状態遷移 <ul> <li>open<br /> 初期状態。CreateCheckoutSession APIが呼び出されることでこの状態になります。</li> <li>complete<br /> 後述するPaymentIntentがrequires_captureになるとこの状態になります。</li> <li>expired<br /> 有効期限が切れるとexpiredになります。</li> </ul> </li> </ul> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/cb9f6acc-checkoutsession_status.jpeg" alt="" /></p> <h3>PaymentIntent</h3> <p>実際の決済処理を管理するためのリソースです。</p> <ul> <li>状態遷移 <ul> <li>requires_payment_method<br /> 初期状態。決済処理を実行するまで待っている状態です。</li> <li>processing<br /> 決済処理の実行中に必ず遷移する状態です。この状態にある場合、他のリクエストによってこのリソースを操作することはできず、ロックされます。</li> <li>requires_capture<br /> 決済処理のオーソリが完了したら遷移する状態です。</li> <li>requires_action<br /> 決済処理のオーソリを実行するために追加のアクションが必要になる場合に遷移する状態です。例えば、3DSの認証などが必要な場合はこの状態に遷移します。</li> <li>succeeded<br /> オーソリが確定したら遷移する状態です。</li> <li>canceled<br /> オーソリがキャンセルされたり、追加のアクションを待っている場合に有効期限が切れてキャンセルされた場合に遷移する状態です。</li> </ul> </li> </ul> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/0cbf6683-paymentintent_status.jpeg" alt="" /></p> <h3>Charge</h3> <p>単一の決済処理を管理するリソースです。PaymentIntentから作成されます。各決済手段の機能を提供しているPaymentのリソースと対応する形で状態を管理します。</p> <h3>CheckoutConfig</h3> <p>Clientでカスタマイズしたい設定を管理するリソースです。例として、レイアウトの設定などを管理しています。</p> <h2>シーケンス</h2> <p>次に、実際にこれらのリソースがどのように関連して処理を実行するかのシーケンスを紹介します。</p> <h3>CreateCheckoutSession API</h3> <p>リソースを作成するためにDBへINSERTし、Clientへリソースを返します。ここに決済画面へのURLが含まれます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/420b1c72-createcheckoutsession_sequence.jpeg" alt="" /></p> <h3>ConfirmCheckoutSession API</h3> <ul> <li>PaymentIntentを作成し、決済処理を実行します。冪等かどうかをチェックし、冪等であれば処理を進め、そうでない場合にすでにロックされていたらリクエストをエラーで終了させます。また、決済処理が失敗した場合、その失敗理由をDBに保存します。</li> <li>オーソリが失敗した場合、状態をprocessingからrequires_payment_methodへ戻すようにすることで、再度APIが呼ばれてもまた別のオーソリを実行できるようにしています。</li> <li>APIの処理全体をWorkflow Engine経由で実行するため、途中でタイムアウトエラーになった場合、非同期でリトライされます。</li> </ul> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/741bbddc--2025-06-03-17.08.24.png" alt="" /></p> <h2>冪等性の担保</h2> <p>ここで、APIの共通機能である、冪等性の担保についてどのように実装しているかについて紹介します。</p> <ol> <li>APIでIdempotencyKeyを受け取ります。</li> <li>同じIdempotencyKeyでDBに保存されているレコードがあるかどうかをチェックします。<br /> 存在していない場合は、DBに保存し処理を進めます。</li> <li>存在している場合は、リクエストフィールドのハッシュ値から計算されたFingerprintと、保存されていたFingerprintが一致しているかどうかをチェックします。</li> <li>一致している場合は、レスポンスを返すか、処理を進めます。</li> <li>一致していない場合は、エラーを返します。</li> </ol> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/1a80239d-idempotency_flowchart.jpeg" alt="" /></p> <p>このようにすることで、同じIdempotencyKey、リクエストフィールドであれば処理を続行、または即座にレスポンスを返せるようにでき、そうでない場合はエラーにすることが可能になります。</p> <h1>おわりに</h1> <p>この記事では、チェックアウトソリューションのアーキテクチャを紹介させていただきました。</p> <p>3DS認証が必要な場合など、リダイレクト処理が必要になるためまた複雑になるのですが、今回は1番シンプルなケースを書きました。内部の状態遷移や冪等性の担保など、少しでも参考になれば幸いです。</p> <p>明日の記事は seitauさんの「<a href="https://engineering.mercari.com/blog/entry/20250609-4660151b47/" title="Sourcegraph × 自作MCP Serverによる社内コード検索連携の取り組み">Sourcegraph × 自作MCP Serverによる社内コード検索連携の取り組み</a>」です。引き続きお楽しみください。</p> 決済基盤の新たな挑戦: 決済チェックアウトソリューションの開発https://engineering.mercari.com/blog/entry/20250605-bf42ce60cf/https://engineering.mercari.com/blog/entry/20250605-bf42ce60cf/<p>この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の5日目の記事です。 この記事では、Payment &amp; Customer Platform (PCP) Vi [&hellip;]</p> Fri, 06 Jun 2025 10:00:51 GMT<p>この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/" title="Merpay &amp;amp; Mercoin Tech Openness Month 2025">Merpay &amp; Mercoin Tech Openness Month 2025</a> の5日目の記事です。</p> <p>この記事では、Payment &amp; Customer Platform (PCP) Vision 2.0の一環として進行中の、決済チェックアウトソリューション開発に関する背景、プロダクトビジョン、全体設計、そして現在の状況について紹介します。</p> <h2>はじめに</h2> <p>こんにちは。メルペイPayment &amp; Customer Platform(PCP)チームのEngineering Headの<a href="https://x.com/foghost" title="@foghost">@foghost</a>です。</p> <p>PCPの各ドメインチームが現在メルペイの事業だけでなく、メルカリグループが展開するすべての事業を支えるための決済、KYC、加盟店管理の社内共通ビジネス基盤(Foundation)の開発を行っています。 (現状をVision 1.0と定義します)</p> <p>しかし、現状複数の事業に利用可能な共通機能の提供はできてるとはいえ、機能の拡張性が不足してたり、導入時のコストがかかったりする課題があり、各事業の立ち上げやグロースを爆速させる武器にはまだなりきれていない状況です。</p> <p>昨年(2024年)から、今後の10年を見据えたTech Roadmapとして、新たな<strong>PCP Vision 2.0</strong>を策定し、事業拡大のスピードを大幅に加速させる共通ビジネス基盤への進化を目指して取り組んできました。</p> <p>PCP Vision 2.0では、「<strong>Functionalities Evolution(機能の進化)</strong>」と「<strong>Domain Architecture Evolution(アーキテクチャの進化)</strong>」と2つの側面から、現在の課題を整理しながら、各ドメイン領域において考えられる将来の姿を定義し、取り組みを進めています。</p> <p>今回ご紹介する<strong>決済チェックアウトソリューション</strong>は「Functionalities Evolution(機能の進化)」を実現するため、決済基盤の領域でチャレンジしている取り組みの一つになります。</p> <h2>課題定義</h2> <p>現在、メルペイは決済事業だけでなく、メルカリやメルカリShopsなどのEC事業にも同じ決済基盤を提供する決済APIを使用しています。</p> <p>共通の決済APIソリューションの提供のみでも外部決済手段の接続、複合決済含めた決済トランザクションの管理、不正検知、会計連携など含めて共通化ができて各EC事業における決済機能の導入の開発負担を大きく削減できています。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/ee417b90-checkout_background.png" alt="" /><br /> しかし、決済APIのみでは、新しい決済手段(例:コンビニ支払い、ビットコイン支払い)に対応するには、各プロダクトごとに個別の実装が必要になります。このため、画面の改修やバックエンド処理のフローを含めた実装作業が必要です。プロダクトの企画、要件定義、機能設計、開発などをすべて含めると、数ヶ月かかる場合もあります。</p> <p>また、プロダクトにおけるチェックアウトのコンバージョン率を向上させるためのチューニングや、チェックアウトのUX改善においても、複数のプロダクトで重複した開発労力が発生しています。</p> <p>このような課題を解決するために、Stripeなどの海外の決済事業者が提供してるLow Code Checkout Solutionのような、社内向けの<strong>Low Code Solution</strong>を開発できないかについて、2024年6月頃から検討を始めていました。</p> <h2>プロダクトビジョン</h2> <p>ソリューションの企画段階では、解決したい課題に向けて、US事業を含むEC事業におけるチェックアウトのユースケースを調査し、PMを含めて以下のようにソリューションのプロダクトビジョンを明確にしました。</p> <ul> <li>各プロダクトにチェックアウトソリューションのClient/Frontendの画面実装を<strong>シームレスに組み込められる</strong> <ul> <li>Low Codeで簡単にインテグレーションできる</li> <li>社内統一したDesign Systemに基づいた統一感のあるUXの提供</li> </ul> </li> <li><strong>Configurable</strong> <ul> <li>利用可能な決済手段や、クーポンなどの共通要素はConfigでカスタマイズできる。設定すれば、すぐにその決済手段を利用できるようになる</li> <li>チェックアウト画面についてもConfigでカスタマイズが可能であり、プロダクト側の独自な画面要素も簡単に拡張することができる</li> </ul> </li> <li>プロダクトを横断して、利用者の<strong>決済設定を共通化</strong>することができる <ul> <li>利用者が一度決済設定(クレジットカード情報など)をすれば、複数のプロダクトを横断して利用することができる</li> </ul> </li> <li><strong>国内事業とGlobal事業</strong>、両方サポートできる  <ul> <li>国内事業に向けて自社決済手段のサポート</li> <li>台湾や香港の越境取引を行っているGlobal事業に向けて現地決済手段のサポートや、General Data Protection Regulation(GDPR)などの現地の法的規則に準拠するシステム設計</li> </ul> </li> </ul> <h2>ソリューション設計</h2> <p>既存のAPIソリューションに加えて、Low Codeのソリューションを検討する際に考えられる実現方法はいくつかあります。以下の観点からそれぞれ5段階評価して選定しました。</p> <ul> <li><strong>インテグレーションコストの削減効果</strong> <ul> <li>これは最も解決したい課題であり、プロダクトに最小限のインテグレーションコストでチェックアウト機能を組み込むことを目指したい</li> </ul> </li> <li><strong>ガバナンスの容易さ</strong> <ul> <li>複数のプロダクトを横断してUX体験のガバナンスが可能</li> <li>共通のチェックアウト機能・体験を横断的に最適化しやすい状態を目指したい</li> </ul> </li> <li><strong>フレキシビリティの高さ</strong> <ul> <li>プロダクトごとに独自の画面設計や要素を拡張したいニーズが必ず存在するため、トレードオフが発生することもあるが、拡張性の高い仕組みを提供することを目指したい。</li> </ul> </li> </ul> <table> <thead> <tr> <th>パータン</th> <th>手法</th> <th>インテグレーションコスト削減</th> <th>ガバナンスの容易さ</th> <th>フレキシビリティーの高さ</th> <th>チェックアウト画面のオーナーシップ</th> </tr> </thead> <tbody> <tr> <td>A</td> <td>決済APIのみ提供(比較のため)</td> <td>1</td> <td>1</td> <td>5</td> <td>プロダクトチーム</td> </tr> <tr> <td>B</td> <td>共通のチェックアウト画面を提供し、画面要素ごとに一定のカスタマイズ性を提供する</td> <td>5</td> <td>5</td> <td>2</td> <td>決済基盤チーム</td> </tr> <tr> <td><strong> C</strong></td> <td><strong>チェックアウト画面を自由に組み立てる共通の仕組みを提供する。画面要素について共通の画面要素(Core Element)とプロダクト特有画面要素(Flex Element)どちらも組み込み可能にする</strong></td> <td><strong>4~5</strong></td> <td><strong>4</strong></td> <td><strong>4</strong></td> <td><strong>決済基盤チーム </strong></td> </tr> <tr> <td>D</td> <td>画面の組み立てはプロダクトに任せる、共通の画面要素(決済手段など)をSDKなどで提供する</td> <td>&lt;4</td> <td>3</td> <td>4.5</td> <td>プロダクトチーム</td> </tr> </tbody> </table> <p>最終的にソリューションの立ち上げ時の実装方法を「<strong>C</strong>」に決めました。また、タイミングもよく社内で一緒に共同開発できる新規事業のプロジェクトがあり、このプロジェクトはWebサービスであるため、最初は<strong>決済基盤側でself-hostedしたWebのチェックアウトソリューション</strong>の開発からスタートしました。将来必要があれば、蓄積された共通の画面要素をパターンDのようにSDKとしてプロダクトへ提供することも可能だと考えています。</p> <p>プロダクト視点からチェックアウトソリューションを導入するときの処理フローが以下のようになります。初期のセットアップ、インテグレーションの実装が必要になるが、チェックアウトにおける各種共通機能(例: 決済手段、クーポン)を利用するにはチェックアウトの設定だけで対応できるようになります。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/0818a0db-checkout_flow.png" alt="" /></p> <ol> <li>プロダクトのチェックアウト要件に応じてチェックアウトの設定情報を作成する</li> <li>購入処理をトリガーにバックエンド経由でチェックアウトのセッションを発行する</li> <li>チェックアウトセッションに含まれるチェックアウトのトップページの遷移URLへ遷移すれば、決済基盤が提供するチェックアウト画面が表示される</li> <li>それ以降プロダクトの利用者がチェックアウトが提供する各種決済手段を利用して決済処理できる</li> <li>決済の結果についてCallback経由、もしくは非同期イベントの通知から受け取ることができる</li> <li>受け取った決済結果に基づいて、プロダクトのバックエンド側で最終的なバリデーションを行い、決済を確定したり、キャンセルしたりすることができる</li> </ol> <h2>プロダクト特有画面要素のサポート</h2> <p>ソリューションとして一見簡単に見えますが、各プロダクトで共通化がまだ難しい画面要素をどのようにサポートすれば良いか、非常に悩ましい課題でした。</p> <p>この課題を解決するために以下のように共通の画面要素とプロダクト特有の画面要素を分けて、それぞれ決済基盤とプロダクトサイドで開発できるためのフレームワークを開発してます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/a6a17917-checkout_framework.png" alt="" /></p> <ul> <li>複数のプロダクトで利用可能な共通の画面要素(例:決済手段、クーポン)については、「<strong>Core Element</strong>」として基盤チームが担当して開発を行う。</li> <li>プロダクト固有の画面要素については、「<strong>Flex Element</strong>」としてプロダクト側のエンジニアが独自で開発できる。</li> <li>プロダクトは、ゼロから自前で実装するのではなく、提供される共通の画面要素(Core Elements)と、独自で開発した画面要素(Flex Elements)を組み合わせて、製品固有のレイアウトを作成する。そうすることで、独自のカスタマイズされたチェックアウト体験を実現できるようになる。</li> </ul> <h2>開発の現状と今後の展望</h2> <p>現在、チェックアウトソリューションはメルカリの新規サービス「<a href="https://nft.jp.mercari.com/" title="メルカリNFT">メルカリNFT</a>」のリリースと共にすでに本番で機能提供し始めています。また他の新規サービスのリリース向けにも共同開発を進めており、既存プロダクトのチェックアウト機能のリプレースも現在検討しています。</p> <p>今後は冒頭でもお伝えしたように、各事業の立ち上げや成長を加速させるための強力な武器となるよう、以下の観点からチェックアウトソリューションをさらに成熟させていきたいと考えています。 </p> <ul> <li>決済手段を含む共通画面要素の拡充</li> <li>即時決済だけでなく、継続払いなど多様なユースケースへの対応</li> <li>プロダクトを横断した一貫性のあるチェックアウト体験の継続的な改善</li> </ul> <p>チェックアウトソリューションに関連する記事が公開予定なので、あわせてご確認ください。<br /> 「<strong><a href="https://engineering.mercari.com/blog/entry/20250606-7afd82e718/" title="チェックアウトソリューションのバックエンドアーキテクチャ">チェックアウトソリューションのバックエンドアーキテクチャ</a></strong>」<br /> 「<strong><a href="https://engineering.mercari.com/en/blog/entry/20250617-building-a-flexible-checkout-solution-frontend-architecture-for-multi-service-integration/" title="Building a Flexible Checkout Solution: Frontend Architecture for Multi-Service Integration">Building a Flexible Checkout Solution: Frontend Architecture for Multi-Service Integration</a></strong>」</p> <p>明日の記事は sushoさんの「<a href="https://engineering.mercari.com/blog/entry/20250606-7afd82e718/" title="チェックアウトソリューションのバックエンドアーキテクチャ">チェックアウトソリューションのバックエンドアーキテクチャ</a>」です。引き続きお楽しみください。</p> PCP LLM Week: How We Become AI-Nativehttps://engineering.mercari.com/blog/entry/20250604-pcp-llm-week/https://engineering.mercari.com/blog/entry/20250604-pcp-llm-week/<p>この記事は Merpay &amp; Mercoin Tech Openness Month 2025 の 4 日目の記事です。 こんにちは、Merpay の Payment Core チームでエンジニアリングマネージャ [&hellip;]</p> Thu, 05 Jun 2025 10:00:45 GMT<p><em>この記事は <a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の 4 日目の記事です。</em></p> <p>こんにちは、Merpay の Payment Core チームでエンジニアリングマネージャーをしている komatsu です。<br /> 普段は決済基盤を開発するチームのマネージャーをしており、最近では社内で AI/LLM 関連の導入や登壇などもしています。</p> <p>この記事では、私たちの組織で実施した「PCP LLM Week」という取り組みについてのレポートと、イベントを通して得られた知見についてご紹介します。<br /> PCP LLM Week は、50 人程度のエンジニア組織で一週間にわたって一切の手動コーディングを禁止し、AI/LLM のみを使用した開発を強制的に行うという、かなりチャレンジングな実験でした。</p> <h2>PCP LLM Week とは</h2> <p>2025/05/08 から 2025/05/14 までの 1 週間にわたって、Merpay の Payment &amp; Customer Platform (PCP; Payment Core チームを含む、決済基盤や KYC、パートナー向けの基盤機能の開発をするチームが所属する領域) 内で実施しました。<br /> 対象となったのは約 50 名のメンバーで、バックエンドエンジニア、クライアントエンジニア、QA エンジニア、そして各チームのエンジニアリングマネージャーを対象に開催しました。</p> <p>このイベントの最大の特徴は、期間中は手動でのコーディングを基本的に禁止し、LLM のみを使用して開発を行うという厳格なルールを設けたことです。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/ee00849e-pcp-llm-week-coding-ban.png" alt="" /></p> <h2>なぜ始めたのか: 組織の課題と解決策</h2> <p>この取り組みを始めた背景には、メンバーとの 1on1 や普段のコミュニケーションの中で上がっていた以下の課題がありました。</p> <ol> <li><strong>学習機会の不足</strong>: 多くのエンジニアが AI/LLM に興味を持っていたものの、日々の業務が忙しくまとまった学習時間を確保できない</li> <li><strong>組織的な情報格差</strong>: 全社的に進めているライセンスやセキュリティの整備状況が全員に伝わっておらず、何を使ってよいのか分からない</li> <li><strong>高度な活用方法の未習得</strong>: MCP (Model Context Protocol) を活用したドキュメント連携や Slack 連携などの応用的な使い方を試せている人が少ない</li> <li><strong>情報交換の機会不足</strong>: AI/LLM についてエンジニア間で気軽に情報交換する機会が限られている</li> </ol> <h3>参考事例: 強制的な学習環境の効果</h3> <p>今回の PCP LLM Week は、<a href="https://note.com/_mkazutaka/n/n43d3c91ac6b7">ある企業の CTO が「エンジニアのコーディングを禁止する」という指令を出した事例</a>と<a href="https://www.itmedia.co.jp/aiplus/articles/2504/07/news064.html">その結果</a>を参考にして企画しました。<br /> この事例では、短期的には生産性が約半分に低下したものの、AI の得意・不得意分野が明確になり、組織全体の AI 活用能力向上という長期的価値を確認できたと報告されていました。<br /> 私たちも同様のアプローチを取ることで、個人の自発的な学習に頼るのではなく、組織として確実に AI/LLM と向き合う機会を作ることができると考えました。</p> <h3>ツール選択: なぜ Cursor なのか</h3> <p>今回のイベントでは以下の理由から主に Cursor エディタを推奨しました。</p> <ul> <li><strong>学習環境の共有</strong>: 全員が同じツールを使うことで、知見の共有やサポートがしやすい</li> <li><strong>高度な機能</strong>: MCP ツールとの連携など、より発展的な使い方を学べる</li> <li><strong>実用性</strong>: (企画時点において) 実際の業務で継続的に使用できるレベルの機能を持っている</li> </ul> <p>Kotlin や Swift を主に扱うクライアントエンジニアにとっては制約が大きくなりますが、全員が同じツールを使うことで統一された学習体験を提供し、知見の共有やサポートをより効果的に行うことができると判断しました。<br /> また、当時社内では GitHub Copilot も利用可能でしたが、社内での Cursor への注目や性能を加味して原則 Cursor に寄せました。</p> <h2>設計思想: 目標とルール</h2> <p>イベントを有意義なものにして、同じ方向に向かっていくためには適切な目的と目標設定が必要です。<br /> そのため、イベントを企画したタイミングで考え、モチベーションやゴールを記載したドキュメントを作成し、事前に参加メンバーに共有しました。</p> <h3>目的と目標</h3> <p><strong>Motivation</strong></p> <ul> <li>AI/LLM がエンジニアの開発スタイルを大きく変革している昨今に、「実際の開発現場でどの程度活用できるのか」を組織全体で実践的に検証する</li> <li>日常の開発業務を LLM のみで実行することにより、AI との効果的な協働方法や、人間が担うべき領域との適切な境界線を、チーム全体で発見・共有する</li> </ul> <p><strong>Goals</strong><br /> 以下の 5 つをイベントの目標として設定しました。</p> <ol> <li><strong>LLM の能力と限界を直接体験する</strong>: LLM によって何ができて何ができないのかを肌で感じる</li> <li><strong>開発ワークフローの最適化戦略を構築する</strong>: LLM を現在の開発プロセスに効果的に統合し、持続可能な生産性向上を実現するための実践的な活用戦略を学ぶ</li> <li><strong>AI コラボレーションスキルを習得する</strong>: プロンプトエンジニアリングや LLM による問題分解のスキルを身につける</li> <li><strong>開発パラダイムの再考</strong>: 従来のコーディングアプローチをゼロベースで考え直し、新しい問題解決アプローチを探求する</li> <li><strong>AI 拡張時代への適応</strong>: 開発者の役割がどのように変化していくかの洞察を得る</li> </ol> <p>LLM は開発プロセスにおける大きなパラダイムシフトですが、愚直に適用すると既存のプロセスに LLM を上乗せするだけになってしまいます。<br /> LLM を体験して既存の業務プロセスに適用するだけでなく、ゼロベースで開発プロセスやエンジニアのあり方を見つめ直すことが近年の AI/LLM 時代に必要だと考え、このような構成にしました。</p> <p><strong>Non-Goals</strong><br /> 一方で、以下は今回のイベントの目標ではないことを明確にしました。</p> <ul> <li><strong>イベント期間内の生産性向上</strong>: あくまで中長期的な生産性向上の一環であり、短期的な生産性低下は気にしない</li> </ul> <p>この取り組みを実現するために、Director、VPoE レベルでの組織的な意思決定を行い、短期的な生産性低下を許容して学習投資として位置づけることで、エンジニアが安心して実験できる環境を整備しました。</p> <h3>ルール設計</h3> <p>厳格なルールを設けることで、全員が LLM と向き合う環境を作りました。</p> <p><strong>基本原則</strong></p> <ul> <li><strong>手動コーディングの完全禁止</strong>: 一行のコード修正であっても、必ず LLM を通して行う</li> <li><strong>小さな編集も例外なし</strong>: 変数名の変更、コメントの追加など、些細な変更も LLM で実施する</li> <li><strong>普段の業務での実践</strong>: 特別なタスクを用意するのではなく、日常業務を LLM で行う</li> </ul> <p><strong>許可される例外</strong></p> <ul> <li><strong>緊急対応</strong>: インシデント対応やシステム障害への対処</li> <li><strong>締め切り間近のタスク</strong>: リリース直前など、時間的制約が厳しい場合</li> <li><strong>環境設定</strong>: LLM ツール自体のセットアップや設定変更</li> </ul> <p><strong>ドキュメント作成</strong></p> <ul> <li><strong>LLM による作成を推奨</strong>: 技術仕様書、設計書、README 等は LLM で作成することを推奨</li> <li><strong>手動修正は許可</strong>: LLM が生成した内容の事実確認や微調整は人間が行ってもよい</li> </ul> <h3>なぜ日常業務で実践するのか</h3> <p>今回のイベントでは、特別なタスクやサンプルプロジェクトを用意するのではなく、これらの理由から、あえて普段の業務に LLM を適用することにしました。</p> <p><strong>現実的な活用可能性の検証</strong></p> <ul> <li>実際の業務環境で LLM がどの程度役立つのかを正確に把握するため</li> <li>理想的な条件ではなく、制約のある現実の中での効果を測定するため</li> <li>既存のコードベースや技術スタックとの相性を確認するため</li> </ul> <p><strong>真の課題と限界の発見</strong></p> <ul> <li>サンプルプロジェクトでは見えない、実業務特有の困難さを体験するため</li> <li>レガシーコードや複雑な依存関係がある環境での制約を理解するため</li> <li>ドメイン知識が必要な場面での LLM の限界を実感するため</li> </ul> <p><strong>継続可能性の評価</strong></p> <ul> <li>イベント終了後も継続して使えるかどうかの判断材料を得るため</li> <li>日常的なワークフローに LLM を組み込む際の現実的な課題を把握するため</li> <li>チーム開発や既存プロセスとの統合における問題点を発見するため</li> </ul> <p><strong>組織全体での実用性確認</strong></p> <ul> <li>個人の実験レベルではなく、チーム・組織レベルでの実用性を検証するため</li> <li>異なる役割 (バックエンド、フロントエンド、QA、マネージャー) での効果の違いを確認するため</li> <li>実際のプロダクト開発における生産性への影響を測定するため</li> </ul> <p>これらの方針によって、より実用的で価値のある知見を得ることを狙いとしました。</p> <h3>実施スケジュール</h3> <p>イベントは参考にした事例と同じく 1 週間の構成で実施しました。<br /> 初日 (5/8) にイベント紹介、他部署の AI 活用事例紹介、Cursor ハンズオン、設定・開発時間を行い、その後 1 週間 (5/8-5/14) 各自で普段の開発業務に AI/LLM を活用し、最終日 (5/14) に成果発表会を開催しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/06/a778c677-pcp-llm-week-hands-on.png" alt="" /></p> <h2>成功例と課題: 実践から見えた現実</h2> <p>イベント後にアンケート調査を行ったりメンバーとの 1on1 を通してさまざまなフィードバックを得たりしました。</p> <h3>全体的な満足度と効果</h3> <p>まず、メンバーのイベントに対する満足度について、多くの (92%) メンバーがイベントを効果的に活用し、その機会に満足していました。<br /> 特に初日に細かい設定について話し、まとまった準備時間を取ることで社内で開発されている MCP ツールの導入など、発展的な設定までできたことが良い体験だったという声もありました。</p> <p>また、アンケート結果から、参加者のスキルレベルによって異なる効果が見られました。<br /> このイベントによって、多数の「初心者」だったメンバーが「中級者」へとレベルアップしました。<br /> LLM ツールを初めて使う人にとって、強制的に使用する環境が効果的な学習機会となったようです。<br /> 基本的な使い方から応用的な活用方法まで、短期間で幅広く体験できたことが、さまざまな知見の習得につながったと思われます。</p> <p>一方で、既に「上級者」レベルの参加者については、さらなるレベルアップは限定的でした。<br /> これらのメンバーには、より高度な学習機会や異なる学習スタイルが必要であることが判明しました。<br /> ただし、上級者には他のメンバーへの指導やベストプラクティス共有という重要な役割があり、組織全体の底上げに大きく貢献していました。</p> <p>アンケート結果では、ほぼすべて (96%) のメンバー が「今後も使い続けたい」と回答しており、スキルレベルに関わらず継続的な活用への意欲が高いことが確認できました。<br /> この記事の執筆時点の統計を見ると、使用頻度の差はありますが、PCP における Cursor の使用率は非常に高い水準になっていました。<br /> このイベントが多くのメンバーがツールを習得し、スキルレベルの底上げに寄与したことが一つの要因だと思うので、主催者としてはとても満足しています。</p> <p>期間については、ほとんどの参加者が「1 週間が適切」と回答しており、学習効果と業務への影響のバランスが良好であることが確認できました。<br /> 短すぎず長すぎない、集中して取り組める期間として評価されています。<br /> 実際、普段のアサインもある中でこれ以上長いと多少支障が出てきますし、短すぎても消化不良になる可能性もあるので、適切な期間設定だったと感じます。</p> <p>実際の参加者の声もいくつか紹介します。</p> <blockquote> <p>全く触ったことがない状態だったが、Copilot のときと同様に無くてはならないものになった。簡単な仕事なら AI で完結できる感覚がある。</p> <p>どのように指示するかでアウトプットのクオリティは変わるものの、もう開発に使用できるレベルまで LLM の信頼性があったことに驚いた。</p> <p>社内のドキュメントや repository をワークスペースに追加することで、社内の技術基盤や事情に沿ったコーディングを LLM がしてくれた体験が良かった。</p> <p>元々かなり有用だという噂を聞いていた程度だったが、実際に使ってみてその効果を実感できたため、どのように活用できるかをタスクごとに考えるようになってきた。</p> <p>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&#8217;s also able to help refactor and find unused code as well.</p> <p>曖昧な指示だとずれた変更が行われるのでコンテキストを明確にする必要があり、自分がやろうとしていることをコンテキストなしの状態から言語化するところに慣れが必要だと感じた。また、一見自然言語でも通じるように見えるので素朴に質問してしまいがちだが、特に MCP server などでは内部でどのような問い合わせが行われるのかを理解した上での利用が必要といった難しさがあると感じた。</p> <p>コード生成にはまだ一定の限界がある。一発ではできない。k8s や tf のレポジトリはファイルが多すぎるため LLM にとってはノイズになることもある。</p> </blockquote> <p>多くのメンバーの LLM に対する信頼度が向上し、その活用方法についても考える良い機会になったことが伺えます。<br /> また、同時に現状の LLM の性能の限界を理解する良い機会にもなり、どのようなタスクに適用していくかの洞察を得ることができました。</p> <h3>成功例: LLM の可能性を実感した瞬間</h3> <p><strong>開発効率の大幅向上</strong></p> <ul> <li>テスト作成、API 修正などの定型的なタスクで劇的な時間短縮</li> <li>既存コードの解析や大規模リファクタリングでの威力</li> <li>ボイラープレートコード生成による生産性向上</li> </ul> <p><strong>高度なコーディング支援</strong></p> <ul> <li>Cursor や GitHub Copilot による的確な修正提案</li> <li>複雑なコード生成による協働的な開発体験</li> <li>一発で修正箇所を発見できる精度の高さ</li> </ul> <p><strong>ワークスペース統合とドキュメント活用</strong></p> <ul> <li>社内ドキュメントやリポジトリとの連携による組織固有の技術基盤に沿ったコード生成</li> <li>既存データソースを基にした技術仕様書の自動生成</li> <li>MCP を活用した Confluence の仕様から Jira チケットの自動生成</li> </ul> <p><strong>プロンプトエンジニアリングの重要性の理解</strong></p> <ul> <li>明確で詳細な指示の重要性 (人間への指示と同様)</li> <li>プロンプトの品質が出力品質に直結することの実感</li> </ul> <p>当初の狙い通り、多くのメンバーが直近の LLM の性能について理解し、活用できる開発プロセスを発見していました。<br /> また、MCP や <a href="https://www.cursor.com/changelog/0-50">v0.50</a> でちょうど追加されたワークスペース機能なども駆使し、複数マイクロサービスに跨った開発なども可能となり、高度な活用をしているメンバーも多々いました。</p> <h3>課題: 直面した限界と困難</h3> <p>また、イベントを通して現状の AI/LLM の課題も見えてきました。</p> <p><strong>精度と品質の問題</strong></p> <ul> <li>不正確な出力や意味不明な結果の生成</li> <li>複雑で非標準的な概念への対応困難</li> <li>コード品質の一貫性の欠如</li> </ul> <p><strong>効率性とワークフローの課題</strong></p> <ul> <li>AI との反復的なやり取りによる時間コスト</li> <li>特定タスクでは人間の方が依然として高速</li> <li>既存ワークフローとの統合やツール切り替えの煩雑さ</li> <li>AI 生成コンテンツの検証時間が手動作業と同等かそれ以上</li> </ul> <p><strong>プロンプトとコンテキストの難しさ</strong></p> <ul> <li>曖昧な指示に対する AI の対応困難</li> <li>大規模で複雑なコンテキストの処理限界</li> <li>自分の要求を詳細に言語化することの困難さ</li> </ul> <p><strong>知識ギャップと限界</strong></p> <ul> <li>ドメイン固有知識やインターネット上にない情報への対応困難</li> <li>暗黙知やコードで明示されていない側面の理解不足</li> </ul> <p><strong>技術領域による制約</strong></p> <ul> <li>iOS/Swift 開発など、特定の技術スタックでの効果的な活用の困難さ</li> </ul> <p>当然ではありますが、コードや仕様には載っていない各メンバーが持っているドメイン知識を適切に伝えるには一定の負荷がかかり、そのことが生成されるコードの限界になることがわかりました。<br /> これを機に私のチームではドキュメンテーションを自動化したり不足しているコードコメントを追加したりするといった次のアクションにつながっており、良い学びができたと感じています。<br /> また、強制的な機会だったからこそ、AI でできることの限界を知ることができました。</p> <h3>開発に対する考え方の変化</h3> <p>イベントを通じて、メンバーの開発に対する考え方に以下のような変化が見られました。</p> <p><strong>実用性への確信の高まり</strong></p> <ul> <li>「思っていたより実用レベルに達している」という認識の変化</li> <li>従来の開発手法を置き換える可能性への確信</li> <li>コード編集などの特定領域での代替可能性の実感</li> </ul> <p><strong>戦略的活用の理解</strong></p> <ul> <li>全てのタスクの代替ではなく、適材適所での活用の重要性</li> <li>設計ドキュメントやコードレビューなど特定用途での高い効果</li> <li>人間の専門知識を最終段階に残すプロセスの有効性</li> </ul> <p><strong>継続学習の必要性</strong></p> <ul> <li>効果的な使用方法を見つけるための実験の重要性</li> <li>他者の経験から学ぶことの価値</li> <li>ベストプラクティスの継続的な学習の必要性</li> </ul> <p><strong>組織的なシナジー効果</strong></p> <ul> <li>全員が LLM を使うことで生まれる相乗効果への気づき</li> <li>チーム間での活用度合いの差とベストプラクティス共有の重要性</li> </ul> <p>実際に全員で使うことで、どのように LLM を活用していくのか、どのようにエンジニアの役割が変わっていくのかといった当初の目標を考える有意義な機会になりました。<br /> また、継続的に知見の共有や学習を続けていく必要性も再度実感できたと思います。</p> <h2>マネージャー視点: 組織運営への影響</h2> <p>EM の観点でも多くの学びがありました。</p> <h3>組織運営に関する学び</h3> <p>まず、<strong>強制力の重要性</strong>を痛感しました。<br /> 普段から AI/LLM という声は多く聞いていましたが、実際には日々の業務に追われて学習時間を確保できないメンバーがほとんどでした。<br /> 今回のように組織全体で取り組む期間を明確に設けることで、全員で LLM に真剣に向き合う機会を作ることができました。<br /> PCP のメンバーは自走力の高いエンジニアが多いですが、そのような環境だとしても自発的な学習だけに頼らず、組織全体としてのスキルアップの機会を提供することの重要性を再確認しました。</p> <p>次に、<strong>情報共有の活性化</strong>が想定以上の効果を生みました。<br /> イベント用に作成した専用 Slack チャンネルは、当初は質問や困りごとを共有する場として考えていましたが、実際には知見の共有やちょっとした発見の報告など、非常に活発なコミュニケーションの場となりました。<br /> イベント終了後も継続的な学習コミュニティとして機能しており、組織の学習文化醸成に大きく貢献しています。</p> <p>そして、<strong>スキルレベルの標準化</strong>という予想外の効果も得られました。<br /> これまでは AI/LLM の活用レベルに個人差が大きく、チーム間での知見共有も限定的でした。<br /> しかし、全員が同じ体験をすることで、組織全体の AI リテラシーが底上げされ、共通言語で議論できるようになったのは大きな収穫でした。</p> <h3>生産性への現実的な影響</h3> <p>短期的には実際に生産性が多少低下しましたが、これは組織として予想し、受け入れていた結果でした。<br /> また、個人的には予想していたほどの生産性の低下は見られず、直近の LLM の性能向上による恩恵が大きいと感じています。</p> <p>同時に生産性観点でも長期的な価値を確認できました。</p> <ul> <li><strong>適材適所の理解</strong>: どのタスクに AI が向いているかの判断力向上</li> <li><strong>開発スタイルの変化</strong>: プロンプトエンジニアリングを通じた問題分解能力の向上</li> <li><strong>AI ファーストな思考習慣</strong>: 課題に直面した際に AI による解決を当然の選択肢として考える習慣の定着</li> <li><strong>個別最適化されたワークフロー</strong>: 各メンバーが自身の開発スタイルに最適化された AI ツールの組み合わせと活用方法を確立</li> </ul> <p>特に AI による解決策を常に考える習慣ができたことは、ゼロベースでアプローチを考え直す上で非常に価値ある体験だったと感じています。<br /> また、この結果は、組織のリーダーシップが「短期的なコストを払ってでも、長期的な AI 活用能力を獲得する」という明確な意思決定を行ったからこそ実現できたものであり、組織として成長していくことへの重要性を再度確認しました。</p> <h3>マネージャーアンケートから見えた現実</h3> <p>マネージャー向けのアンケートでは、AI 導入による生産性向上とリソース計画への影響について以下のような現実的な見解が得られました。</p> <p><strong>生産性向上への見通し</strong><br /> 生産性向上については、短期的な劇的な変化ではなく、中長期にわたって徐々に向上していく見込みであることが分かりました。<br /> 現在はツールの乱立や性能差の変化により、短期では一長一短の状況が続いており、これから模索や選定を継続的に行っていくことが重要です。<br /> また、AI の効果は作業の種類と開発者の LLM 経験に大きく依存することも明らかになりました。<br /> 適切なタスク選択と生産性維持のためのトレーニングが重要であり、特に問い合わせ対応や運用系など、直接的な生産性に結びつかないタスクでの効率化に期待が寄せられています。</p> <p><strong>リソース計画への影響</strong><br /> 現時点では従来のリソース計画手法を大幅に変更するレベルの変化は見られませんでした。</p> <p>また、短期で AI によってリソースに大幅な余裕が出るには依然として壁がありますが、組織横断の開発スタイルは導入しやすくなったという手応えを感じています。<br /> 段階的な環境整備が現実的なアプローチであることが確認できました。</p> <p><strong>マネージャーとしての学び</strong><br /> 組織的な取り組みの価値として、IC からの結果を聞いて想定よりも課題が多いことを実感しました。<br /> 一方で、全員が使うことで生まれるシナジー効果を確認でき、組織として LLM にどう寄り添っていくかを考える機会の重要性を認識しました。</p> <p>現実的な期待値設定については、短期的な劇的な生産性向上への過度な期待は禁物であることが分かりました。<br /> 中長期的な投資として捉え、継続的な学習と改善が必要であり、ツールの組み合わせや切り替えの最適化が今後の課題となります。</p> <h2>その後の展開: 継続的な取り組み</h2> <p>イベント最後には以下の項目でメンバーを表彰し、効果的な活用方法を共有しました。</p> <ul> <li><strong>LLM Code Generation Champion</strong>: 最も多くのコードを生成した人</li> <li><strong>LLM Refactoring Champion</strong>: 最も多くのコードを削除 (リファクタリング) した人</li> <li><strong>Precision Prompter</strong>: 最も高い Accept Rate を達成した人</li> </ul> <p>同僚がどのように活用できているかを間近でシェアすることで、全員がより自分ごととして AI スキルセットに対する理解を深めたり、期ごとの個人目標に追加したりと、全体的な AI に対する視座向上ができたと思います。</p> <h3>継続的な取り組み</h3> <ol> <li><strong>情報共有チャンネルの継続</strong>: イベント用 Slack チャンネルを汎用的な名前に変更し、継続的な情報交換の場として活用</li> <li><strong>ベストプラクティスの共有</strong>: 効果的な活用方法を組織内で継続的に共有</li> <li><strong>新機能のキャッチアップ</strong>: AI/LLM ツールの新機能を組織全体で迅速に取り入れる体制構築</li> </ol> <h2>まとめ</h2> <p>PCP LLM Week は、短期的な生産性低下というコストを払いながらも、全員で体験することで組織全体の AI 活用能力を大幅に向上させる貴重な機会となりました。</p> <p>特に重要だったのは、「全員で同じ体験をする」ことで生まれた学習効果と、その後の継続的な情報共有文化の醸成です。<br /> AI/LLM の活用は個人のスキルに依存する部分が大きいですが、組織として取り組むことで、より大きな価値を生み出せることを実感しました。<br /> 特に昨今のモデルやエコシステムの進化は個人でキャッチアップしていくにはあまりに膨大なため、組織として方向性を示し、スキルアップの機会を提供することでモチベーションを獲得し、各メンバーの自走力に繋げる良いサイクルが生まれると感じました。</p> <p>今後も組織として AI-Native になるための機会や仕組みを継続的に作っていきたいです。</p> <p>明日は同じく PCP の foghost さんによる「<a href="https://engineering.mercari.com/blog/entry/20250605-bf42ce60cf/" title="決済基盤の新たな挑戦: 決済チェックアウトソリューションの開発">決済基盤の新たな挑戦: 決済チェックアウトソリューションの開発</a>」です。<br /> 引き続き <a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> をお楽しみください。</p> メルコインでSystem Design Interviewを導入しましたhttps://engineering.mercari.com/blog/entry/20250530-introduction-sdi-mercoin/https://engineering.mercari.com/blog/entry/20250530-introduction-sdi-mercoin/<p>こんにちは。メルコイン バックエンドソフトウェアエンジニアの@toshinaoです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の3日目の記事です。 これまで [&hellip;]</p> Wed, 04 Jun 2025 10:00:56 GMT<p>こんにちは。メルコイン バックエンドソフトウェアエンジニアの<a href="https://twitter.com/toshinao_">@toshinao</a>です。 この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の3日目の記事です。</p> <h2><strong>これまでの採用フロー</strong></h2> <p>メルコインはメルカリグループですが、メルペイやメルカリとは別に採用を行っています。これまでメルコインのソフトウェアエンジニア採用(バックエンド)は、「技術課題 → 1次面接 → 2次面接 → 最終面接」という流れでした。技術課題はGoまたはJavaで出題され、応募者は1週間以内に提出します。1次面接はエンジニア、2次面接はマネージャー、最終面接は役員が担当します。</p> <h3><strong>技術課題の問題点</strong></h3> <p>技術課題は、応募者の経験している技術言語をもとにご自身で選択していただいています。従来の技術課題は、GoとJavaで内容が異なっていました。Goでは課題プログラムの修正や機能追加を行う形式で、応募者のプログラミングスキルやコードの理解力、バグ修正・実装力などを評価していました。一方、Javaではアプリケーションを1から開発する課題が出され、設計・実装・テストまで一連の開発プロセスを通じて、総合的な開発力や設計力を評価していました。 メルコインのBackend開発はGoで書かれており、Java経験者はポテンシャルによる採用となるため、Javaの課題はアプリケーション全体の設計能力が強く要求され、応募者にとってハードルが高いという課題がありました。 また、技術課題をGoだけにすると応募者が減ってしまう懸念がありました。</p> <p>また、応募者に大きな時間的負担がかかっていました。応募者は想定回答時間が5時間\~10時間の課題を1週間以内に提出する必要がありました。実際に課題を受けて入社した社員に聞いたところ10時間以上かかった社員が多くいました。課題に加えて、1次面接も1時間程度必要で、全体として多くの時間をいただいていました。</p> <h3><strong>導入の経緯</strong></h3> <p>こうした課題を解決するため、技術課題と1次面接をSystem Design Interview(以下、SDI)を導入しました。すべての採用フローがSDIになったわけではなく、主にGo未経験者を対象に導入しています。SDIの導入にあたっては、他社の事例を参照したり、社内メンバーに何回も試し、ブラッシュアップを繰り返しました。</p> <p>開発経験、とくにシステム設計ができる方であれば、Go未経験でもメルコインで活躍できると考えています。実際、Javaの課題で合格した方もメルコインで活躍しています。Goのスキルも必要ですが、金融システムとしてスケールや耐障害性を考慮した設計力も重要です。設計力があれば、既存コードを参考にしつつ、メンバーのサポートを受けてGoでの開発も可能だと考えています。</p> <h2><strong>System Design Interviewとは</strong></h2> <p>System Design Interviewは、ソフトウェアエンジニアの採用面接でシステム設計能力を評価する手法です。GoogleやAmazon、Microsoftなどの大手テック企業で広く採用されており、バックエンドエンジニアやシステムアーキテクトの重要な評価基準となっています。</p> <h3><strong>SDIの進め方</strong></h3> <p>SDIでは、面接官がシステムの要件を提示し、応募者は要件を確認しながら対話形式でアーキテクチャを設計していきます。例えば「決済サービスの設計」や「大規模なログ収集基盤の設計」など、実際の業務に近い課題が出されます。応募者は、要件のヒアリングから始め、システムの全体像をホワイトボードやオンラインツールで図示しながら説明します。</p> <p>一般的なSDIでは、YouTubeやX(旧Twitter)のような大規模サービスや、検索機能・レコメンデーションシステムなどの設計課題が出されます。応募者は、要件定義・スコープの明確化、データモデル設計、コンポーネント分割、インターフェース設計、スケーラビリティ・パフォーマンス・可用性・耐障害性・セキュリティ・コスト・運用性など、さまざまな観点から設計を進めます。</p> <p>メルコインのSDIでは、より実務に近いバックエンドシステム設計の課題を出題しています。Product Managerから作りたいシステムの要件を教えてもらい、システム設計をしていく形になっています。</p> <h3><strong>評価基準</strong></h3> <p>SDIで重視しているのは、与えられた要件の理解、適切な技術選定、スケーラビリティ・セキュリティ・パフォーマンスなどを考慮した設計力です。この過程で、技術的知識だけでなく、問題解決力やコミュニケーション能力など、実務で必要なスキルも評価できます。特に、設計の根拠を論理的に説明できるか、トレードオフを意識した提案ができるか、チームでの議論を想定したコミュニケーションができるかを重視しています。</p> <p>SDIの面接は90分で、最初の70分がSDI、次の10分が過去の開発経験の質問、最後の10分が応募者からの質問時間です。面接官は、応募者が本質的な課題に集中できるよう、適宜ヒントを出したり、議論の方向性を調整したりしています。</p> <h2><strong>SDIの効果</strong></h2> <p>SDI導入により、当初想定していた以上の効果が得られました。 まず、技術課題でいただいていた応募者の時間が削減されたことで、選考にかかる期間を短くすることができました。さらに、評価の質も向上し、システム設計能力だけでなく、コミュニケーション力や問題解決アプローチも評価できるようになりました。これにより、実際の業務での活躍イメージをより正確に把握できるようになっています。</p> <p>また、特定のプログラミング言語経験に依存しない評価方法となったことで、多様なバックグラウンドを持つ人材の発掘にもつながっています。これにより、技術チームに多様な視点や経験を持つエンジニアを迎え入れることができています。</p> <p>さらに、SDIを通じて応募者の「考え方」や「価値観」も把握しやすくなりました。例えば、障害発生時の対応方針や、セキュリティリスクへの意識、コストとパフォーマンスのバランス感覚など、実際の業務で重要となる観点を深掘りできるようになりました。</p> <h2><strong>SDIの課題</strong></h2> <p>一方で、SDIにも課題があります。 元々の問題が70分で最後まで回答するのが難しいため、設計の本題と関係ない部分に時間を取られると、ほとんど進まないまま終わってしまうことがあります。そのため、面接官が軌道修正する必要があり、話を遮る場面も増えます。応募者に不快な思いをさせないよう配慮が必要です。</p> <p>また、応募者からの質問にどこまで答えてよいかの判断も難しいです。正解をそのまま伝えてしまうことを避けるため、曖昧な回答になりがちです。特に、設計の根幹に関わる部分はどこまで答えるか非常に難しいです。</p> <p>これらの課題を解消するため、面接ごとに曖昧さを減らし、「この試験で見ないこと」や「SDIの進め方の補足」などを追加し、本題から逸れないよう問題をブラッシュアップし続けています。面接官同士での振り返りや、応募者からのフィードバックも積極的に取り入れています。</p> <h2><strong>今後の展望</strong></h2> <p>今後は、SDIの課題バリエーションを増やすなど、SDIの質を上げていくことや、面接官のトレーニングや評価基準のさらなる明確化にも力を入れ、より公平で納得感のある選考プロセスを目指します。</p> <p>また、SDIの内容や運用ノウハウを社内外に発信し、他社やコミュニティとの情報交換も積極的に行っていきたいと考えています。</p> <h2><strong>まとめ</strong></h2> <p>SDIの導入により、メルコインの採用プロセスは大きく改善されました。時間的な効率化だけでなく、より実践的な評価が可能となり、多様な人材の発見にもつながっています。一方で、面接の進め方や質問対応など課題もありますが、継続的な改善を通じて解消を図っています。</p> <p>今後も、SDIを通じて実践的なシステム設計能力を持つエンジニアを発見・採用し、より強固なシステム開発チームの構築を目指していきます。</p> <p>メルコインにご興味ある方は、下記よりご応募ください。<br /> <a href="https://apply.workable.com/mercari/j/756A4E7B6D/">Product Engineer,Backend &#8211; Mercoin</a></p> <p>明日の記事はkomatsuさんによる「<a href="https://engineering.mercari.com/blog/entry/20250604-pcp-llm-week/" title="PCP LLM Week: How We Become AI-Native">PCP LLM Week: How We Become AI-Native</a>」です。引き続きお楽しみください。</p> メルペイインターン体験記:実務の中での学びと気付きhttps://engineering.mercari.com/blog/entry/20250529-f73ce3fe90/https://engineering.mercari.com/blog/entry/20250529-f73ce3fe90/<p>こんにちは!メルペイ Growth Platform Frontend チームのインターン生の@utaです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の2日 [&hellip;]</p> Tue, 03 Jun 2025 11:00:34 GMT<p>こんにちは!メルペイ Growth Platform Frontend チームのインターン生の<a href="https://twitter.com/uta1018_">@uta</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の2日目の記事です。</p> <h2>はじめに</h2> <p>私は3月から5月末までの3ヶ月間、フロントエンドエンジニアとしてメルペイのインターンに参加しました。今回は、インターン期間中に取り組んだタスクについて振り返り、そこで得た学びや気づきについて以下の内容をまとめたいと思います。</p> <ul> <li>取り組んだタスク <ul> <li>チームについて</li> <li>Engagement Platformカレンダーの開発</li> <li>クーポン検索機能の開発</li> </ul> </li> <li>インターンで得た学びと気付き <ul> <li>メルカリカルチャーの体験</li> <li>初めての実務から学んだエンジニア像</li> </ul> </li> </ul> <p>この記事が、メルペイのインターンに挑戦しようと考えている方や、興味を持っている方の参考になれば幸いです!</p> <h2>取り組んだタスク</h2> <h3>チームについて</h3> <p>私が配属された Growth Platform Frontend チームは、Engagement Platform(通称EGP)という社内向けマーケティングツールを開発しています。このツールを使うと、マーケターや PM(プロジェクトマネージャー)がポイントやクーポンなどのインセンティブ配布、LP(ランディングページ)の作成・公開、キャンペーン作成といった CRM業務をコーディング不要で簡単に行えます。</p> <p>EGPについての詳細は下記ブログもあわせてご確認ください。</p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20241210-f7c478382a/">WYSIWYGウェブページビルダーを支える技術とSever Driven UIへの拡張</a></li> <li><a href="https://engineering.mercari.com/en/blog/entry/20231207-enhancing-collaboration-and-reliability-the-journey-of-version-history-in-our-page-editor-tool/">Enhancing Collaboration and Reliability: The Journey of Version History in our Page Editor Tool</a></li> <li><a href="https://engineering.mercari.com/blog/entry/20231023-mmtf2023-day1-8/">【書き起こし】WYSIWYGウェブページビルダーを支える技術的マジックの裏側 – Hal Amano / Arvin Huang / Ben Hsieh / Jas Chen【Merpay &amp; Mercoin Tech Fest 2023】</a></li> </ul> <h3>Engagement Platformカレンダーの開発</h3> <p>インターン期間中、最も注力したタスクがEGPカレンダー画面の開発です。</p> <h4>問題点</h4> <p>EGPではキャンペーンの作成および管理を行うことができます。これまでは、作成されたキャンペーンの確認のために検索機能を備えたテーブルが提供されていました(図1.1)。</p> <figure style="text-align: center; margin-top: 12px"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/6e0e5b8d-screenshot-2025-05-28-at-18.42.07.png" alt="キャンペーンリストテーブル" /><figcaption>図1.1 キャンペーンリストテーブル</figcaption></figure> <p>しかし、この表示方法ではキャンペーンのスケジュールを一元的に把握することができません。キャンペーンに伴う通知の重複や、システムのキャパシティを超える可能性が可視化されていないという問題がありました。</p> <h4>改善策</h4> <p>そこで、私はキャンペーンのスケジュールを可視化するカレンダーの開発に取り組みました(図1.2)。このプロジェクトはPMの@ChloeさんがPRD(プロダクト要求仕様書)に起こしたもので、@Chloeさんやチームメンバーにサポートをいただきつつ、要件の確認・Figmaによるデザインの作成から実装・リリースまで取り組みました。</p> <figure style="text-align: center; margin-top: 12px"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/a23dd1b7-screenshot-2025-05-28-at-18.46.45.png" alt="キャンペーンカレンダー" />図1.2 キャンペーンカレンダー<br /> </figure> <h4>苦労した点</h4> <p>このプロジェクトの中で最も苦労した部分が、キャンペーンの仕様理解とUIの考案です。<br /> キャンペーンには作成画面からもわかるように、さまざまな設定事項があります(図1.3)。これらの変数がどのような操作を決定しているのか、また、それをどのようにカレンダー上のUIに落とすかという部分に悩みました。</p> <figure style="text-align: center; margin-top: 12px"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/e2e3acd6-crm.dev_.citadelapps.com_ep-ui_campaigns_new_modebatch.png" alt="キャンペーン作成画面" /><figcaption>図1.3 キャンペーン作成画面</figcaption></figure> <h4>キャンペーンの仕様理解</h4> <p>キャンペーンには大きく分けて、real-timeとbatchの2種類が存在し、それぞれで配布条件やタイミングが異なります(図1.4)。</p> <figure style="text-align: center; margin-top: 12px"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/e1f45ec7-frame-29.png" alt="キャンペーンの種類" /><figcaption>図1.4 キャンペーンの種類</figcaption></figure> <p>また、キャンペーンの配布タイミングを決定づける重要な変数が2種類存在します。1つ目は配布期間を定めるcampaign schedule、2つ目は配布対象を評価するクエリに関する条件を定めるsegmentationsです。さらに、キャンペーンの種類によって、これらの変数が実際の配布スケジュールにどのように関与するかも、それぞれ異なります(図1.5)。</p> <figure style="text-align: center; margin-top: 12px"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/49623884-frame-32-1.png" alt="キャンペーンの配布タイミング例" /><figcaption>図1.5 キャンペーンの配布タイミング例</figcaption></figure> <h4>カレンダーUIの考案</h4> <p>このような仕様の違いを、次のようにUIに落とし込みました(図1.6)。real-timeキャンペーンは期間中に配布資格を満たしたタイミングで配布されるキャンペーンであるため、キャンペーン期間を1日単位で可視化しました。一方、batchキャンペーンは単発もしくは定期的に配布されるキャンペーンです。そのため、キャンペーン期間はラベルに記載するのみに留め、カレンダーでは時間単位で実際に配布されるタイミングを可視化しました。</p> <figure style="text-align: center; margin-top: 12px"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/19d5ea18-frame-31-3.png" alt="キャンペーン種別のカレンダーへの表示方法" /><figcaption>図1.6 キャンペーン種別のカレンダーへの表示方法</figcaption></figure> <h4>学んだこと</h4> <p>このcampaign scheduleとsegmentationsの複雑さは、campaign scheduleが過去のインシデントを受けて後から追加された機能であることに由来していると伺いました。これまで私は、このような歴史的経緯を持つプロジェクトに取り組んだ経験がなかったため、大規模なプロジェクトにおけるコードや仕様の理解の難しさを実感しました。また、このような状況で自ら質問することの重要性を学びました。キャンペーンの理解からデザイン考案、実装に至るまで、チームメンバーをはじめ、PMの方や他のチームの方々から大変貴重なサポートをいただきました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/419fe4ac-screenshot-2025-05-28-at-19.45.07.png" alt="" /></p> <h3>クーポン検索機能の開発</h3> <p>インターン期間中、最も技術的に挑戦したタスクがクーポン検索機能の開発です。</p> <h4>問題点</h4> <p>EGPではクーポンの作成および管理を行うことができます。これまでは、作成されたクーポンを確認するためのテーブルが提供されていましたが、検索機能は存在していませんでした(図2.1)。また、キャンペーンのリワードとしてクーポンを選択する際にも、検索機能がないことで効率が悪く、この機能は多くの場面で長らく待望されていました。</p> <figure style="text-align: center; margin-top: 12px"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/897e729d-2.1.png" alt="クーポンリストテーブル" /><figcaption>図2.1 クーポンリストテーブル</figcaption></figure> <h4>データフロー</h4> <p>これまでのクーポンテーブルでは、他のチームが開発したAPIからデータを取得していました。検索機能の実装において、まずそのAPIを利用することを考えます。しかし、クーポンは歴史的に古いページであり、APIにもフィルターや検索機能が実装されていないという問題がありました。</p> <p>そこで、既存のデータベースに加えてSpannerを用いる新しいデータフローを採用しました(図2.2)。新たにクーポンのデータを既存のデータベースとSpannerの両方に保存し、SpannerとGraphQLを活用して検索機能を実装しました。これにより、より効率的で拡張性の高いデータ取得が可能となりました。</p> <figure style="text-align: center; margin-top: 12px"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/a1ef2b1a-data-flow.png" alt="クーポン検索機能のデータフロー" /><figcaption>図2.2 クーポン検索機能のデータフロー</figcaption></figure> <p>データの移行やリリースはインターン期間内に間に合いませんでしたが、テキストによるクーポン名の検索機能や、リターンタイプに基づく絞り込み機能を実装することができました(図2.3)。</p> <figure style="text-align: center; margin-top: 12px"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/57e4de40-2.2.png" alt="クーポン検索画面" /><figcaption>図2.3 クーポン検索画面</figcaption></figure> <h4>学んだこと</h4> <p>SpannerやGraphQLといったバックエンド領域の技術に挑戦する機会を得られたことは、大きな学びとなりました。私はこれまで主にフロントエンドの技術を扱ってきましたが、バックエンド領域にも触れることで、自分の視野を広げることができました。また、チーム全体を見渡しても、フロントエンドチームでありながら、必要に応じてバックエンド領域のタスクにも積極的に取り組んでおり、その姿勢にプロフェッショナル性を強く感じました。こうした環境でインターンができたことは非常に刺激的で、自分自身の成長につながったと思います。</p> <h2>インターンでの学びと気付き</h2> <h3>メルカリカルチャーの体験</h3> <p>このインターン期間中には、メルカリカルチャーを感じられる機会がたくさんありました。<br /> まず驚いたことは、 大量の情報にアクセスできる環境です。ほぼ全てのSlackチャンネルやドキュメントへのアクセスが許可されており、それらの情報を自由に閲覧できることに驚きました。こうしたアクセスの範囲は、社員とほとんど同じであり、インターン生であっても「会社の一員」として扱われているように感じました。</p> <p>また、印象的だったのがAll Hands です。これは各部署が定期的に開催するミーティングで、インターン生も自由に参加できるものでした。この場では、チーム外の取り組みや会社全体の目標について詳しく知ることができ、普段接する機会の少ない他部署の活動にも触れることができました。ミーティング中にはSlackの random チャンネルが活発に使われており、メンバーが所属部署や役職に関係なく意見を交換し合ったり、気軽にリアクションを飛ばし合う姿が非常に印象的でした。</p> <p>これらの体験を通じて、メルカリが複数の事業領域を抱える大規模な組織であるにもかかわらず、一体感のあるワンチームとして進んでいる文化を強く感じました。また、インターン生であっても社員と同じ情報にアクセスし、実際にメルカリで働くイメージをリアルに描くことができました。企業の現実的な働き方や意思決定のプロセスに触れることができ、非常に貴重な経験でした。</p> <h3>初めての実務から学んだエンジニア像</h3> <p>今回のインターンは、私にとって初めての実務経験でしたが、エンジニアとしての価値を考える大きなきっかけとなりました。これまでは、エンジニアとしてキャリアを築くためには、機能を実装するコーディングスキルを高めることが重要だと考えていました。しかし、実務を通じて、技術力だけではなくチーム全体への貢献が不可欠であることを学びました。</p> <p>特に印象に残っているのは、EM(エンジニアリングマネージャー)の@ben.hsiehさんとの関わりです。@ben.hsiehさんは定期的に1on1を実施し、私の状況や目指したい方向性について丁寧に聞いてくださいました。そして、それらを踏まえた上で適切なタスクを割り振っていただきました。例えば、EGP内で使用されるLP作成ツールの使い方を知りたいと相談した際、そのツールを活用した実際のタスクを割り当てていただき、実践的な学びを得る機会となりました。</p> <p>こうした環境の中で私は、ただコードを書く力を高めるだけでなく、チームの一員としてプロダクトや目標に貢献できるエンジニアでありたいと考えるようになりました。エンジニアとして目指すべき在り方や方向性をより明確にすることができたと感じています。</p> <p>EM の@ben.hsiehさんが記事を公開していますので、あわせてご確認ください。<br /> 「<a href="https://engineering.mercari.com/en/blog/entry/20250527-rethink-tools-ui-ux-human-centric-to-ai-driven/" title="Rethink Tool&#039;s UI/UX - Human-Centric to AI-Driven">Rethink Tool&#8217;s UI/UX &#8211; Human-Centric to AI-Driven</a>」</p> <h2>おわりに</h2> <p>本記事では、メルペイのインターンで取り組んだタスクや、そこから得た学びと気づきについてお話しました。技術的なスキルを磨くだけでなく、メルカリグループの文化に触れ、エンジニアとしての価値を考えるきっかけを得ることができた、とても貴重な3ヶ月間でした。</p> <p>このような充実した経験が得られたのも、メンターの@togamiさんをはじめ、チームメンバーや関わってくださった全ての方々の手厚いサポートのおかげです。この場をお借りして改めて感謝を申し上げます。ありがとうございました!</p> <p>現在、メルカリではインターンを募集しています。このブログを読んで「自分も挑戦してみたい!」と思った方は、ぜひ一歩を踏み出してみてください。きっと素晴らしい経験が待っていると思います!</p> <p><a href="https://careers.mercari.com/jp/students/?_gl=1*p858hc*_ga*MTE5NTEwNDYuMTc0MDk3ODkyNw..*_ga_01RNCQSJ52*czE3NDc5MDA1OTIkbzIzJGcxJHQxNzQ3OTAwNzA4JGowJGwwJGgw">Students | 採用情報</a></p> <p>明日の記事は メルコイン Opsチーム @toshinaoさんによる「<a href="https://engineering.mercari.com/blog/entry/20250530-introduction-sdi-mercoin/" title="メルコインでSystem Design Interviewを導入しました">メルコインでSystem Design Interviewを導入しました</a>」です。引き続きお楽しみください。</p> メルペイにおけるAI活用の取り組みhttps://engineering.mercari.com/blog/entry/20250602-merpay-ai-utilization/https://engineering.mercari.com/blog/entry/20250602-merpay-ai-utilization/<p>はじめに こんにちは。メルペイVPoEの@keigowです。 この記事は、Merpay &amp; Mercoin Tech Openness Month 2025 の初日の記事です。 これまでもメルペイ及びメルカリグル [&hellip;]</p> Mon, 02 Jun 2025 10:00:20 GMT<h2>はじめに</h2> <p>こんにちは。メルペイVPoEの<a href="https://x.com/keigow">@keigow</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/">Merpay &amp; Mercoin Tech Openness Month 2025</a> の初日の記事です。</p> <p>これまでもメルペイ及びメルカリグループでは、社内向けChatGPTとも言える<a href="https://careers.mercari.com/mercan/articles/49488/">Ellie</a>の取り組みや、LLMをプロダクトや業務効率化に活かすためのハッカソンイベント<a href="https://careers.mercari.com/mercan/articles/39144/">ぐげん会議</a>の開催、プロダクトへのLLM利用も含め、AI/LLMの活用を推進してきました。</p> <p>AI/LLMの進化のスピードは想像以上に早く、毎日のように新しいアップデートがあります。最近ではこれまで以上にAI Nativeな組織、プロダクトに生まれ変わっていくべくさまざまな取り組みを進めており、その一部をご紹介できればと思います。</p> <h2>AI Coding Assistant Toolの活用</h2> <p>元々2023年の6月に<a href="https://github.com/features/copilot">GitHub Copilot</a>の利用を開始し、社内での利用も徐々に増えていましたが、<a href="https://www.cursor.com/">Cursor</a>の利用開始に伴い、社内での活用が大きく進みました。現在CopilotとCursorだけでも、Engineering組織全体の約8割のメンバーが利用しています。またCoding Agentとして<a href="https://devin.ai/">Devin</a>の利用も広がっています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/ba681d76-cursor-active-user.png" alt="Cursor Active User" /></p> <p>Engineering組織としても今QのOKRの中で一番重要なKR1として、全てのエンジニアが100%何らかのAI Coding Assistant Toolを活用し、生産性を高めることを目標に設定しました。生産性を計測する仕組みとしてこれまで社内では独自で<a href="https://cloud.google.com/blog/ja/products/gcp/using-the-four-keys-to-measure-your-devops-performance?hl=ja">Four Keys</a>などを取得していましたが、新たに<a href="https://getdx.com/">DX</a>を全社で導入しました。こちらの詳細については17日目のntkさんの記事で紹介予定です。</p> <p>目標の設定と全社員向けにツールを提供する予算の確保、世の中の盛り上がりも相まって急速に導入が進み、各チーム単位でAI活用のオフサイトを実施したり、一週間Vibe Codingのみの期間を設定するなど社内でも盛り上がりを見せています。それぞれのイベントについての取り組みの様子も今後の記事で紹介予定なので、ぜひ御覧ください。</p> <h2>MCPサーバによる社内ツール連携</h2> <p><a href="https://modelcontextprotocol.io/introduction">Model Context Protocol</a>(MCP)を活用した社内ツールの連携も急速に進んでいます。MCPサーバについてはセキュリティの観点から安全性が確認されているものを活用するため、社内でMCPサーバの実装をまとめたRepositoryが作られています。JIRA、Confuluence、Slack、Google Driveを始めとした3rd Party製のツールや、内製のMicroservicesの管理ツール、Google SpannerやBigQueryなどのMCPサーバが作られ、Cursorなどを利用して主にLocal環境で活用されています。これらのツールの利用者はエンジニアに限らないため、さまざまな職種のメンバーが自身の業務に合わせてツールの活用方法を検討するようになりました。</p> <p>並行して各ツール自体にもEmbedされたAI Chat機能が導入されてくるようになっているため、今後もベストプラクティスの検討をしていきます。MCPサーバの活用については7日目のseitauさんの記事でも触れられる予定です。</p> <h2>Merpay AI Labsの取り組み</h2> <p>業務フローの改善におけるAI/LLMの活用を推進するため、メルペイにはAI Labsという専門のチームがあります。</p> <p>日毎にアップデートされるモデルやツールの進化に合わせて、各チームでのAI活用ニーズが増すなか、AI活用による業務フローの改善を進めたいが、どのように進めればよいのかわからないといった声を聞く機会が増えてきました。</p> <p>実際に部署内で行われている業務のフローは複雑かつ、ステークホルダーも多く、さまざまな課題があったとしても、それが業務フローを改善すべき問題なのか、単にシステム化をすればいいだけなのか、あるいはAIを活用することで大きなアウトカムを埋める領域なのかの判断自体も難しいという問題がありました。</p> <p>Engineeringの部署としてもそれをサポートし、AI活用を推進することが組織としてのインパクトが大きいと考え、今年の1月に改めてチームのMissionやVisionを設定しました。以下は抜粋になりますが、会社全体のAI推進を行うことをMissionとして定義しています。</p> <blockquote> <p><strong>Mission</strong></p> <ul> <li>AI LabsはFintechのAI活用と開発をEnable/Driveします。AI Labsはそのための、実践者であり、伝道者であり、触媒になります。</li> <li>FintechにおけるAI Nativeなアプリケーション開発/業務設計のCenter of Excellence(中核となる組織)としての能力を確立し、AI Nativeな事業推進に貢献します。</li> </ul> </blockquote> <p>このチームで取り組んだプロジェクト例としては以下のようなものがあります。成果が出たもの出なかったものなど含め、結果はさまざまですが、着実に知見を貯めることができていると思います。</p> <ul> <li>コンセプトからのアプリのデザインの自動生成</li> <li>仕様からのQAのテストケースの自動生成</li> <li>画像生成AIを用いたキャンペーンのキービジュアル生成</li> <li>お客さま問い合わせの要因分析</li> <li>専門チームの問い合わせ工数削減のための対応効率化Botの作成</li> </ul> <p>画像生成の例<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/e5884c17-image38.png" alt="メルカード画像" /></p> <p>特にアプリのデザインの自動生成については、現在進行系で面白い取り組みになってきています。こちらは18日目のhiroさんの記事で詳細をご紹介予定です。</p> <h2>おわりに</h2> <p>これまで取り組みの一部をご紹介してきましたが、世の中の盛り上がりと勢い同様に、社内でも各チーム、各メンバーが次々と新しくAI活用に取り組み、さまざまなツールが作られるという状況が続いており、その全てを把握することも困難な状態になっています。キャッチアップだけでも大変な時代になってきましたが、この熱狂の中で仕事に取り組めていることは幸せな状況だなと感じており、この目まぐるしい変化を楽しんでいければと思います。</p> <p>明日の記事はbenさんによる「<a href="https://engineering.mercari.com/en/blog/entry/20250527-rethink-tools-ui-ux-human-centric-to-ai-driven/" title="Rethink Tool&#039;s UI/UX - Human-Centric to AI-Driven">Rethink Tool&#8217;s UI/UX &#8211; Human-Centric to AI-Driven</a>」とutaさんによる「<a href="https://engineering.mercari.com/blog/entry/20250529-f73ce3fe90/" title="メルペイインターン体験記:実務の中での学びと気付き">メルペイインターン体験記:実務の中での学びと気付き</a>」の2本です。引き続きお楽しみください。</p> 「Merpay&Mercoin Tech Openness Month 2025」開催のお知らせhttps://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/https://engineering.mercari.com/blog/entry/20250528-merpay-mercoin-tech-openness-month-2025/<p>こんにちは。メルペイ Engineering Engagement チームの @mikichin です。 メルカリグループは「あらゆる価値を循環させ、あらゆる人の可能性を広げる」をミッションに、さまざまなサービスを展開し [&hellip;]</p> Wed, 28 May 2025 10:00:52 GMT<p>こんにちは。メルペイ Engineering Engagement チームの <a href="https://x.com/chida_miki">@mikichin</a> です。</p> <p>メルカリグループは「あらゆる価値を循環させ、あらゆる人の可能性を広げる」をミッションに、さまざまなサービスを展開しています。<br /> メルペイは単なる決済サービスではなく、新しい「信用」を基盤として、それに基づく循環型社会、なめらかな社会を創ることを、メルコインはテクノロジーによって、さまざまな価値観の境界線を打ち破り、誰もが暗号資産・デジタル資産などあらゆる価値を簡単に交換できる世界の実現を目指しています。<br /> そのためには、お客さま・企業・金融機関など、さまざまなステークホルダーに対して「OPENNESS」な姿勢で向き合うことで、もっと身近なものに変えていきたいと考えています。</p> <p>本企画は、技術も「OPENNESS」にしていこうという考えのもと、2019年にスタートしました。<br />今回から「Merpay &amp; Mercoin Tech Openness Month」とリニューアルし、よりパワーアップした形でお届けします。<br /> 「Merpay &amp; Mercoin Tech Openness Month 2025」では、メルペイ・メルコイン・メルカリモバイルの開発をしているエンジニアたちの取り組みをご紹介します。<br /> 各エンジニア組織がテクノロジーでお客さまの課題解決を実現することを大切にし、その挑戦の中で得た知見を6月2日から約1ヶ月間に渡り毎日公開していきます!技術、開発設計や思想、組織ストラクチャー、Tips、その他最近の取り組みなど、幅広くお伝えします。</p> <p> 2019年は<a href="https://engineering.mercari.com/blog/entry/2019-05-17-100000/" title="こちら">こちら</a><br /> 2020年は<a href="https://engineering.mercari.com/blog/entry/notice-merpay-tech-openness-month-2020/" title="こちら">こちら</a><br /> 2021年は<a href="https://engineering.mercari.com/blog/entry/20210825-notice-merpay-tech-openness-month-2021/" title="こちら">こちら</a><br /> 2022年は<a href="https://engineering.mercari.com/blog/entry/20220401-notice-merpay-tech-openness-month-2022/" title="こちら">こちら</a><br /> 2023年は<a href="https://engineering.mercari.com/blog/entry/20230531-notice-merpay-tech-openness-month-2023/" title="こちら">こちら</a></p> <p>▼公開予定表 (こちらは、後日、各記事へのリンク集になります)</p> <table> <thead> <tr> <th style="text-align: left;">Title</th> <th style="text-align: left;">Author</th> </tr> </thead> <tbody> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250602-merpay-ai-utilization/" title="メルペイにおけるAI活用の取り組み">メルペイにおけるAI活用の取り組み</a></td> <td style="text-align: left;">@keigow</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20250527-rethink-tools-ui-ux-human-centric-to-ai-driven/" title="Rethink Tool&#039;s UI/UX - Human-Centric to AI-Driven">Rethink Tool&#8217;s UI/UX &#8211; Human-Centric to AI-Driven</a></td> <td style="text-align: left;">@ben.hsieh</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250529-f73ce3fe90/" title="メルペイインターン体験記:実務の中での学びと気付き">メルペイインターン体験記:実務の中での学びと気付き</a></td> <td style="text-align: left;">@uta</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250530-introduction-sdi-mercoin/" title="メルコインでSystem Design Interviewを導入しました">メルコインでSystem Design Interviewを導入しました</a></td> <td style="text-align: left;">@toshinao</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250604-pcp-llm-week/" title="PCP LLM Week: How We Become AI-Native">PCP LLM Week: How We Become AI-Native</a></td> <td style="text-align: left;">@komatsu</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250605-bf42ce60cf/" title="決済基盤の新たな挑戦: 決済チェックアウトソリューションの開発">決済基盤の新たな挑戦: 決済チェックアウトソリューションの開発</a></td> <td style="text-align: left;">@foghost</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250606-7afd82e718/" title="チェックアウトソリューションのバックエンドアーキテクチャ">チェックアウトソリューションのバックエンドアーキテクチャ</a></td> <td style="text-align: left;">@susho</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250609-4660151b47/" title="Sourcegraph × 自作MCP Serverによる社内コード検索連携の取り組み">Sourcegraph × 自作MCP Serverによる社内コード検索連携の取り組み</a></td> <td style="text-align: left;">@seitau</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250611-one-person-one-release/" title="One Person, One Release – AI Nativeの夜明け">One Person, One Release – AI Nativeの夜明け</a></td> <td style="text-align: left;">@Joraku</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250606-37101db087/" title="GASで効率化!MVNOの動作検証仮事業者&Jira issue作成 with AI">GASで効率化!MVNOの動作検証仮事業者&Jira issue作成 with AI</a></td> <td style="text-align: left;">@toshick</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250612-a80033f0b8/" title="Devin にE2Eテストの実装を任せる">Devin にE2Eテストの実装を任せる</a></td> <td style="text-align: left;">@anzai</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250612-b1aee5118d/" title="メルペイインターンで機能の改修・設計からLLM AgentベースのSlack Bot開発まで色々やった話">メルペイインターンで機能の改修・設計からLLM AgentベースのSlack Bot開発まで色々やった話</a></td> <td style="text-align: left;">@soma</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250612-d2c354901d/" title="SRE2.0: LLMサービスの信頼性を測る新しい品質指標の紹介">SRE2.0: LLMサービスの信頼性を測る新しい品質指標の紹介</a></td> <td style="text-align: left;">@T</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20250612-d2c354901d/" title="SRE2.0: No LLM Metrics, No Future: Why SRE Must Grasp LLM Evaluation Now">SRE2.0: No LLM Metrics, No Future: Why SRE Must Grasp LLM Evaluation Now</a></td> <td style="text-align: left;">@T</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250613-64e628404a/" title="メルカリモバイル開発チームでAI Hackathonをした話">メルカリモバイル開発チームでAI Hackathonをした話</a></td> <td style="text-align: left;">@k_kinukawa</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20250617-building-a-flexible-checkout-solution-frontend-architecture-for-multi-service-integration/" title="Building a Flexible Checkout Solution: Frontend Architecture for Multi-Service Integration">Building a Flexible Checkout Solution: Frontend Architecture for Multi-Service Integration</a></td> <td style="text-align: left;">@David, @anzai</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250617-56adf5904e/" title="メルペイにおける6年間のインシデント対応・管理で直面した課題と改善">メルペイにおける6年間のインシデント対応・管理で直面した課題と改善</a></td> <td style="text-align: left;">@foostan</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250620-mercari-pipeline-beta1/" title="Mercari Pipeline (旧Mercari Dataflow Template) v1(β版)を公開しました">Mercari Pipeline (旧Mercari Dataflow Template) v1(β版)を公開しました</a></td> <td style="text-align: left;">@orfeon</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250618-27069c49cc/" title="gRPC Federationを使った3rd party API開発事例:マネーフォワード連携から学ぶ実装ノウハウ">gRPC Federationを使った3rd party API開発事例:マネーフォワード連携から学ぶ実装ノウハウ</a></td> <td style="text-align: left;">@fivestar</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250620-018cdf7e2b/" title="「自分ができる領域が増えた」-Cursorを使って未経験のKotlinコードレビューに挑戦">「自分ができる領域が増えた」-Cursorを使って未経験のKotlinコードレビューに挑戦</a></td> <td style="text-align: left;">@takeshi in iOS</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250622-1c1848f606/" title="初めてのWWDC25に現地参加!Apple Parkで体験した特別な数日感">初めてのWWDC25に現地参加!Apple Parkで体験した特別な数日感</a></td> <td style="text-align: left;">@Shunta</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250624-building-a-company-wide-framework-for-improving-devex-in-mercari-group/" title="メルカリグループの全社的なDevEx改善の仕組みづくり">メルカリグループの全社的なDevEx改善の仕組みづくり</a></td> <td style="text-align: left;">@ntk1000</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20250624-building-a-company-wide-framework-for-improving-devex-in-mercari-group/" title="Building a company-wide framework for improving DevEx in Mercari Group">Building a company-wide framework for improving DevEx in Mercari Group</a></td> <td style="text-align: left;">@ntk1000</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250620-31f2852f9b/" title="Web版メルカリにメルコインの機能を組み込む検証をした話">Web版メルカリにメルコインの機能を組み込む検証をした話</a></td> <td style="text-align: left;">@y-arima</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250625-acfc60bbea/" title=" AI時代の組織変革:エンジニアリングマネージャーが見たメルカリグループの半年間の軌跡"> AI時代の組織変革:エンジニアリングマネージャーが見たメルカリグループの半年間の軌跡</a></td> <td style="text-align: left;">@abcdefuji</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/en/blog/entry/20250625-integration-of-appintents-to-a-project-that-uses-bazel-build-system/" title="Integration of AppIntents to a Project That Uses Bazel Build System">Integration of AppIntents to a Project That Uses Bazel Build System</a></td> <td style="text-align: left;">@cyan</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250630-94c2e6b283/" title="PJ-Aurora:メルカリにおけるUI生成・評価の取り組み">PJ-Aurora:メルカリにおけるUI生成・評価の取り組み</a></td> <td style="text-align: left;">@hiro</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250630-1224ec5881/" title="KubeCon + CloudNativeCon 2025 Japan 参加レポート(おまけ:Envoy拡張のWasmフィルタのデモ実装)">KubeCon + CloudNativeCon 2025 Japan 参加レポート(おまけ:Envoy拡張のWasmフィルタのデモ実装)</a></td> <td style="text-align: left;">@keitasuzuki</td> </tr> <tr> <td style="text-align: left;"><a href="https://engineering.mercari.com/blog/entry/20250716-paymentcore-internship/" title="メルペイ Payment Coreチームで学んだ2ヶ月間の振り返り">メルペイ Payment Coreチームで学んだ2ヶ月間の振り返り</a></td> <td style="text-align: left;">@taichi</td> </tr> </tbody> </table> <p>どんな知見が得られるのか、毎日が楽しみです。<br /> Merpay &amp; Mercoin Tech Openness Month 2025 の1日目は、メルペイ VPoE @keigow が執筆予定です。<br /> <br /> ひとつでも気になる記事がある方は、この記事をブックマークしておくか、 <a href="https://x.com/mercaridevjp">エンジニア向け公式Twitter</a>をフォロー&チェックしてくださいね!</p> メルカリインターンで体感した、「Move Fast」を支えるアジャイル開発https://engineering.mercari.com/blog/entry/20250514-0d49feabb2/https://engineering.mercari.com/blog/entry/20250514-0d49feabb2/<p>はじめに こんにちは!メルカリのヘルプセンターチームで、2025年の2月中旬から5月中旬までの3か月間インターンをしていた@markunです。私は普段、討論の構造を可視化するシステムについて研究しているのですが、システム [&hellip;]</p> Wed, 14 May 2025 14:51:04 GMT<h2>はじめに</h2> <p>こんにちは!メルカリのヘルプセンターチームで、2025年の2月中旬から5月中旬までの3か月間インターンをしていた@markunです。私は普段、討論の構造を可視化するシステムについて研究しているのですが、システムの使いやすさを改善するなかで、ユーザーのニーズを的確に反映するスキルを伸ばしたいと感じていました。そこで今回、お客さまの体験を重視し、大規模なユーザーを抱えているメルカリでどのようにプロダクト開発が行われているかを学ぶべく、フロントエンドエンジニアとしてインターンに参加しました!</p> <h3>チームについて</h3> <p>ヘルプセンターチームは、お客さまが疑問や問題を自己解決するためのヘルプコンテンツの提供と、解決が難しい場合のメルカリ事務局への問い合わせのためのプラットフォームの構築を行っています。また、ガイドコンテンツやお問い合わせフォームを管理する社内向けシステムの開発・運用を通じて、お客さまのサポート体制を支えています。</p> <h3>アジャイル開発について</h3> <p>アジャイル開発とは、ユーザーのニーズに柔軟に対応しながら迅速に改善を重ねる開発スタイルの一種です。といっても、コーディング規約などの具体的なルールがあるわけではありません。ここで鍵となるのは、チームの一人ひとりが「<a href="https://agilemanifesto.org/iso/ja/manifesto.html" title="アジャイルソフトウェア開発宣言">アジャイルソフトウェア開発宣言</a>」に代表される数々の原則を実践する意識を持つことです。</p> <p>アジャイル開発はお客さまの満足度を効果的に高められますが、経験がない方にとっては中々イメージが掴みにくいと思います。そこで本記事では、インターンで体験したヘルプセンターチームでのアジャイル開発の実践例をご紹介したいと思います。アジャイル開発への理解を深められたら幸いです!</p> <h2>ガイド記事編集用の社内ツールの多言語対応</h2> <p>メルカリは、今年3月に<a href="https://about.mercari.com/press/news/articles/20250307_mercariglobalshipping/" title="国際メルカリ便">国際メルカリ便</a>という海外のお客さまが日本国内で出品されている商品を購入できるサービスをリリースしています。これを受けて、ヘルプセンターでは海外のお客さま向けのガイドの提供が始まりました。そこでメンターと相談して、社内向けの記事編集ツールの多言語対応の改善点について、実際にツールを使用している方に直接話を聞くことにしました。</p> <p>話し合いの結果、日本語話者も多言語に対応した記事一覧ページをよく使うこと、その際に記事一覧が常に中国語で表示され、目的の記事を探しにくいということが明らかになりました。この課題を解決するため、フロントエンドに記事一覧画面の表示言語を切り替える機能を追加することにしました。</p> <h3>取り組んだこと</h3> <p>いざ実装に取り掛かると、大きな課題に直面しました。記事一覧を表示する際、指定された言語の翻訳データが用意されていない記事は取得されない仕様だったことが判明したのです。そのためフロントエンド側で記事一覧の表示言語に日本語を指定するだけでは、一部の記事が欠けてしまうという問題が生じました。そこで、翻訳が存在しない場合は他の言語の記事を埋めて返す、言語フォールバックと呼ばれる処理(下図参照)をバックエンド側に実装することで対応しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/17f20e02-markun-fallback-language-1024x303.png" alt="" /></p> <h3>アジャイルなポイント</h3> <p>アジャイル開発には、ユーザーを第一に考えるという原則があります。この原則に沿って、実際にツールを使う人たちに直接話を聞いたり、担当していたフロントエンドだけでなくバックエンドにも積極的に挑戦したことで、最初に仕様が決まった時からの状況や要望の変化に柔軟に対応できました。また、ヒアリングから約1か月という短期間で、今必要とされている機能をリリースすることができました!この成果は、まさにメルカリのバリューである「Move Fast」を体現したものだったと感じます。</p> <p>この経験を通じて、開発者の主体性とユーザーとの継続的な対話を重視するアジャイル開発の強みを実感しました。また、自分がこれまでに経験してきたことに縛られずにユーザーを最優先する姿勢が、メルカリの特徴であるGo Boldなプロダクト開発に繋がることが分かりました!</p> <h2>お困りの商品選択画面のリファクタリング</h2> <p><a href="https://help.jp.mercari.com/" title="ヘルプセンター">ヘルプセンター</a>では、商品を選択すると関連するガイドのページに遷移する機能があります。サービスの運用を続ける中でこの仕組みに関する処理が複雑化していたのですが、ページ遷移を制御するロジックがフロントエンド側に実装されていたため、今後拡張する際に開発のボトルネックになるのではないかという懸念がありました。そこで、そのロジックをバックエンドへ移行することを検討しました。</p> <h3>取り組んだこと</h3> <p>この機能を実装するうえで、様々な選択肢がありました。例えば、データベースに新たなテーブルを追加する、テーブル構造は変えずにバックエンドのロジックのみ変更する、あるいはそもそも変更を見送るという選択肢もありました。そこで私は、考えられる全ての選択肢とそのメリット・デメリットを詳細に言語化してドキュメントに整理し、それをもとにチームで実装の方針を議論しました。最終的に、バックエンドのロジックのみを変更する方針で実装を進めました。</p> <h3>アジャイルなポイント</h3> <p>アジャイル開発では、課題解決に主体的に取り組むことが重視されています。このケースでは、APIのインターフェースやテーブル構造の変更などのあらゆる可能性を徹底的に議論し、実装の方針決めからリリースまで裁量を持って推し進めることができました。</p> <p>この経験を通じて、自分で最初から最後までやり遂げるアジャイル開発の難しさを実感すると同時に、責任を持ってやり通す楽しさとやりがいを身に染みて感じられました。また、同じ処理でも書き方は想像以上に数多く存在すること、その中で広い視野を持って何故その処理をそこに書くのかを常に考えることが大切であると学びました。</p> <h2>インターンで得た学び</h2> <p>これまで紹介したこと以外にも、ソフトスキルとハードスキルの両面で大きく成長することができました。</p> <p>ソフトスキルの面では、相手の意見をうまく聞き出すには質問の範囲を絞った方がよいということを学びました。私は最初、タスクの方針について相談する際に「どんな機能があると良いですか?」「解決策は三つありますが、どれが良いですか?」といった漠然とした聞き方をしていました。これは一見幅広く意見を引き出せるようにみえますが、実際には混乱を招いてしまうことがよくありました。相手の意思を尊重しつつ、自分の考えや質問の目的を明確にした上で話を聞いた方が円滑に議論を進めやすいということが分かりました。</p> <p>また、チームワークの大切さを再認識しました。私たちのチームでは2週間に1回、レトロスペクティブと呼ばれる振り返り会を行っています。このミーティングでは毎回感謝のコメントが枠をはみ出すぐらい盛り上がり、そこで生まれたモチベーションや連帯感が私のインターンでの取り組みの原動力になっていました。チームワークの重要性は当たり前すぎて見過ごしがちですが、その価値を改めて深く実感しました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/c2addabf-markun-restrospective-jp-1024x816.png" alt="" /></p> <p>ハードスキルの面では、レイヤーごとの役割を把握することの重要性を学びました。取り組んだタスクの中には、機能自体はスムーズに実装できたものの可読性や拡張性などの観点からレビューを受け、関数の設計から見直すといったこともよくありました。インターンであっても対等に接してもらえたおかげで、何故そのコードをそこに書いたのかを明確にする、様々な職種の方に伝わるような変数名を考えるなどの基礎的な工夫に一切の妥協を許さないことがいかに重要かということが分かりました。</p> <p>また、テストに関しても大きな学びがありました。私は、インターンに参加するまでテストを書いたことがなく、単にバグを発見するための手段だと思っていました。もちろんそれも重要ですが、特にユニットテストのコードは仕様書としての役割も果たしており、どの関数に何が期待されているかが一目でわかるよう徹底的にシンプルに書くべきだということを学びました。また、そのためにfor文やif文さえ可能な限り使わないようにしていることを知り、驚くと同時にテストコードの奥深さに感銘を受けました。</p> <p>こうした経験を通じ、コードの質を高めるには高度なテクニックを使いこなすことよりも、シンプルさや目的に即した書き方になっているかを考える力を磨くことが重要だとわかりました。実際の現場では何が求められるのかを身をもって知ることができ、自分のコードの保守性や可読性だけでなく、システム開発の姿勢そのものへの意識も大きく変わりました。</p> <h2>福岡合宿の話</h2> <p>インターン開始から1か月が経った頃、福岡で開催されたオフサイトに参加しました。ヘルプセンターチームは福岡や大阪など日本各地を拠点としているメンバーが多く、普段はオンラインでのコミュニケーションが中心なため、対面での顔合わせも兼ねて企画されました。ミーティングでは、現状の共有やヘルプセンターの今後の展望について議論が白熱しました!</p> <p>その後はみんなで夕食をとり、普段なかなか顔を合わせられないチームでの親睦を深められました!!とりまぶし、絶品でした。ただ、残念ながらとりまぶしは気がついたら食べ終わっていて写真を撮り忘れてしまったので、代わりに締めで頂いたラーメンを載せます。こちらも同じぐらい美味でした!</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/05/3a5c89ce-markun-ramen-test-1024x639.jpg" alt="" /></p> <h2>おわりに</h2> <p>本記事ではメルカリのインターンで取り組んだことと、アジャイル開発の経験を通して学んだことをご紹介しました。フロントエンドエンジニアとして参加しながら、バックエンドのコーディングにも積極的に取り組み、課題を見つけては素早く改善するというサイクルを繰り返し回すことができました!</p> <p>このように大胆に挑戦できたのは、お客さまのニーズを徹底的に考え抜き、チームとの綿密な議論を通じて何故その課題に取り組むのかを明確にできたこと、その中で責任と裁量を持ってタスクをやり遂げるアジャイル開発の原則を実践できたこと、そして何より挑戦を歓迎してくれる環境があったからだと強く感じます。</p> <p>メンターの@monkukuiさんはじめ、お世話になった皆さまに、この場を借りて感謝させていただきます。ありがとうございました!</p> <p>本ブログがメルカリのインターンや入社を検討している皆様の参考になれば幸いです。この記事を読んで興味が湧いた方は、ぜひメルカリにチャレンジしてください!!</p> <p><a href="https://careers.mercari.com/jobs/">https://careers.mercari.com/jobs/</a></p> メルペイインターンで通知をいい感じにした話https://engineering.mercari.com/blog/entry/20250313-2dfe9c5828/https://engineering.mercari.com/blog/entry/20250313-2dfe9c5828/<p>はじめに こんにちは!北陸先端科学技術大学院大学修士1年の@midorinです。 メルペイのBalanceチームにて1月から3月にかけての2ヶ月間、バックエンドのインターンに参加しました。 今回は、インターンで主に取り組 [&hellip;]</p> Fri, 14 Mar 2025 11:00:40 GMT<h2>はじめに</h2> <p>こんにちは!北陸先端科学技術大学院大学修士1年の@midorinです。<br /> メルペイのBalanceチームにて1月から3月にかけての2ヶ月間、バックエンドのインターンに参加しました。<br /> 今回は、インターンで主に取り組んだ通知の改善とメルペイで学んだことを本ブログに記載します。</p> <h2>メルペイ Balanceチームについて</h2> <p>メルペイはメルカリの売上金や銀行口座のお金が使える決済サービスです。Balanceチームでは売上金などの残高やポイント、債権を管理するサービスを開発しています。</p> <h2>ポイント失効通知</h2> <p>メルペイではポイントの失効期限が近づいた際に通知が届くようになっています。<br /> 具体的には当日から起算して1日後、7日後、30日後のいずれかに失効するポイントがあれば通知が届く仕様となっています。この通知は毎朝11時に実行されるバッチ処理により実現されており、このバッチ処理はBalanceチームが管理しています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/03/80e2fcdd-notification_logo_changed.png" width="min(100vw, 1159px)" aspect-ratio="1159/250" alt="notification"></p> <p>現行のポイント失効通知は付与されたポイント量に関わらず通知が届くため、1Pの付与のみでも失効期限の30日前、7日前、1日前の計3回届く煩わしさがありました。これにより、少額付与のキャンペーンが実施しづらいという弊害が生じていました。<br /> 本インターンでは主にこの通知の改善に取り組みました。</p> <h2>改善の流れ</h2> <p>今回の改善は主に以下のような流れで行いました。<br /> 仕様の確認<br /> 改善案の提案<br /> 実装〜リリース<br /> 順に説明します</p> <h3>仕様の確認</h3> <p>実装を見ながら現状の仕様を確認し、ドキュメントにまとめました。結果、以下のような仕様であることがわかりました。</p> <ul> <li>1, 7, 30日後に失効期限がくるポイントが存在するか確認する</li> <li>あれば、それらのうち最も直近の日付について失効するポイントの通知を送る <ul> <li>たとえば、1, 7日後それぞれに失効期限がくるポイントが存在すれば1日後が優先される</li> </ul> </li> </ul> <h3>改善案の提案</h3> <p>仕様を確認したのち、現状の通知の総数、消費されたポイント額が通知のタイミングでどれだけ増えているか、参考となる他社の事例などを調べました。調査結果として、現状の仕様における通知の効果はそこまで大きくなく、お客さま体験向上のために減らしても良いだろう、という結論に至りました。</p> <p>チームの方と協力して改善案を考えました。ミーティングを重ねる中で「少額のポイントに関しては通知の価値が低く煩わしく感じているのではないか」という仮説が浮かび上がりました。これを受け、以下のような案を候補とし、PMなどが参加している、プロダクトとしての仕様を決定するミーティングにて話し合いました。</p> <ul> <li>各日に閾値を設け、失効するポイントが閾値を超えない場合は通知しない</li> <li>定期便のように決まった期間の失効するポイントをまとめて通知する<br /> ミーティングにて合意がとれ、リーガルから法的な問題がないことも確認いただけたため、閾値を設ける案で実装を進めることにしました。</li> </ul> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/03/d0f63d44-legal_request_comment.png" width="min(100vw, 1992px)" aspect-ratio="1992/564" alt="legal_request_comment"></p> <p>現状とのギャップが小さいところから段階的に導入するために、1日後の通知には閾値を設けず、7日後には100、30日後には500の閾値を設けました。閾値の根拠として、付与しているポイントは100ポイントと500ポイントが特に多いという点があげられます。傾向として額が大きくなるほど付与数は減っているのですが、100ポイントと500ポイントの付与だけがキャンペーンで付与することが多く、例外的に増えていました。ここがお客さまの体感が変わる境界であり、効果的に煩わしさが解消できると考え、ここに閾値を設けることにしました。<br /> この仕様により、以下の図のように1P付与の際は通知が一回のみになる改善がなされます。予測として、ポイントの失効に関する通知が30%以上減少することを見込んでいます。</p> <figure><img src="https://storage.googleapis.com/prd-engineering-asset/2025/03/762194bb-graph3.png" width="min(100vw, 2620px)" aspect-ratio="2620/1204" alt="notification"><figcaption style="font-size: min(3vw, 1.8rem);">△保有ポイントが1Pのお客さまが受け取る失効通知(閾値導入後)</figcaption></figure> <h3>実装〜リリース</h3> <p>ここまでで提案した仕様を実装しました。単純に閾値の追加をするだけでなく、新たにテストを記述し、QAを経てリリースへ持ち込みました。<br /> テストの記述ではテストケースの追加しやすさや意味など、さまざまな指導をいただきながら実装を進めました。<br /> QAでは担当の方に仕様を伝えテストケースの漏れがないかを確認して、リリース時に問題が起きないように努めました。<br /> インターン期間の最終週にリリースをし、問題なく通知量を減少させることができていることを確認しました。<br /> 今後は失効総額が上がらないことを目標に掲げ、リリース後も通知数や消費タイミングなどのデータを見ながら継続的に改善していくのがベスト、という話に落ち着きました。</p> <h2>学び</h2> <p>今回のインターンではデータを元に仕様を策定し、実プロダクトに適用するという貴重な経験をさせていただくことができました。この経験を通していくつかのことを学びました。</p> <h3>コード品質を担保する仕組み</h3> <p>テストをただ書くだけでなく、追加しやすく品質を保証するようなものを書いたり、バグを生みにくいコードを書いたりなど、普段書いているようなコードからさらに深く考えて進められており、さまざまな発見がありました。Balanceチームが管理しているサービスは一つバグが起きるだけでも致命的になり得るため他よりもコード品質に重点が置かれているという話を伺い、サービスの目的によってコードの良さの指標が変わるという学びを得ました。</p> <h3>効率的にReviewを進める心がけ</h3> <p>いくつかのタスクを進める中で速度は良いが見落としがある旨のフィードバックがありました。自身でも課題に感じており、改善方法を相談したところ他の人のPRをReviewしたり、Self-Reviewしたりするのが良さそう、という一つの解決策を提示いただけました。これを実践したところ、見落としは少なくなり、新たな知識を得られるような本質的な指摘、議論へと素早く移動できるようになり、有意義に時間を使うことができるようになったと感じています。</p> <h3>データ駆動の改善プロセス</h3> <p>ポイント失効通知の改善は以前から課題として認識されていましたが、具体的なデータが不足しており、着手できずにいました。今回、チーム全体でデータを用意しPMらに提案することで実装を進めることが可能になりました。<br /> 用意したデータについて、Balanceチームで管理しているサービスではDBにSpannerを利用しており、このデータは定期的にBig Queryに同期されています。今回はBig QueryのデータをLooker Studioにて可視化し、分析しました。これにより、PMらにもわかりやすいデータの提示をすることができ、スムーズに議論を進めることができました。客観的な議論、方針の策定をするためにデータが役立つことを改めて実感しました。</p> <h3>お客さま体験の考え方</h3> <p>今回の改善は通知を減らすものであるため、短期的に見るとアプリを開く確率が減り、利用率などに影響してしまう可能性はあります。しかし、お客さま体験を向上させることで長期的には利用を継続していただいたり、価値の高い通知に絞る事で通知をオンのままにしていただいたりというデータに現れにくいメリットも存在します。お客さま体験を上げるビジネス的な良さを話し合えたのはとても良い経験だったと感じています。</p> <h2>インターン実務以外の話</h2> <p>今回のインターンではタスクを進める以外にもいろいろな経験があり、どれも素晴らしい体験だったため共有します。</p> <h3>開発合宿</h3> <p>インターン開始後すぐに合宿があり、さまざまな方と交流したり、Swiftを書いたりしました。業務的なコミュニケーションを始める前に社員さんのあたたかい雰囲気をしることができたため、とても良い経験でした。<br /> 開発合宿初日に財布を落としたのですが、期間中に戻ってきて日本のあたたかさを感じることもできました。届けてくださった方、ありがとうございます。。。</p> <p>開発合宿の様子は、こちらをご確認ください。</p> <p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/Aa5-5s7wi_4?si=0izmVWIVdq_gsQya" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p> <h3>勉強会</h3> <p>チーム内だけでなく、有志で集まった勉強会が定期的に開催されており、技術に対するモチベの高さを伺うことができました。<br /> Tech Talkと呼ばれるゆるめのLTも存在しており、こちらでは業務内外のさまざまな知見を得ることができました。</p> <h3>mertip</h3> <p>メルカリではmertipと呼ばれるピアボーナスの仕組みが存在します。インターン生でもこれを利用することができ、自身も積極的に利用していました。他の方に貢献することが奨励されている、というのが目に見えてわかること、質問などをする際に気後れしないことなどがとてもよかったと感じています。 </p> <h2>まとめ</h2> <p>本ブログではメルペイのインターンで取り組んだポイント失効通知の改善とその経験を通して学んだことを記述しました。今回の経験で、バグの生みにくいコードの書き方などのようなハードスキルと、いかにしてタスクを進めていくかなどのようなソフトスキルを得ることができ大きく成長できたと感じています。<br /> また、インターン参加前後で会社の印象に変化がありました。参加前は技術力が強い方々が黙々と作業をしている印象でした。インターンを通して、技術力が高いという印象は変わらず、技術、プロダクトに対する興味が高い方々が楽しく仕事を進めている印象に変わりました。肩肘はらず、モチベの高い環境でとても刺激を受けることができました。とても貴重な経験ができ、メンターの@kobaryoさんはじめ、チームの方々、関わった皆様方にすごく感謝しています。ありがとうございました!<br /> 本ブログがインターンを検討している皆さんの参考になれば幸いです。<br /> 記事を見ていいなと思ってくれた方はBalance teamに応募してみてください!Balance teamに限らずメルペイは<a href="https://apply.workable.com/mercari/j/29F7FC9266/">インターンを募集してます</a>!</p> Cloud SQL for PostgreSQL のインスタンス統合https://engineering.mercari.com/blog/entry/20250220-8685a1def6/https://engineering.mercari.com/blog/entry/20250220-8685a1def6/<p>こんにちは。Mercari DBRE(Database Reliability Engineer) チームのエンジニアの @takashi-kun です. 今回私達のチームでは複数の Cloud SQL for Post [&hellip;]</p> Thu, 20 Feb 2025 15:18:39 GMT<p>こんにちは。Mercari DBRE(Database Reliability Engineer) チームのエンジニアの <a href="https://www.linkedin.com/in/takashi-honda-646a5889/">@takashi-kun</a> です.<br /> 今回私達のチームでは複数の Cloud SQL for PostgreSQL インスタンス(以下 Cloud SQL インスタンス)の一部を一つのインスタンスに統合するという作業を行いました. 本記事ではその作業の詳細やダウンタイムを短くするために選択した方法などを紹介します.</p> <h2>はじめに</h2> <p>今回の対象サービスのメルカリ Shops では microservice アーキテクチャを採用(ref: <a href="https://engineering.mercari.com/blog/entry/20210806-3c12d85b97/">メルカリShopsはマイクロサービスとどう向き合っているか</a>)しており, それぞれの microservice 毎にデータベースが存在します. スペックやデータサイズは大小様々ですが, おおよそ 50 程度のインスタンスが稼働しています. これらのインスタンスは運用効率化のためすべて Enterprise Plus Edition で稼働しています. 先のブログでも紹介されていますが, データベースが microservice 毎に別となっているため, 障害の局所化や他 microservice のデータベースを意識せずに開発できるなど, 開発からリリースまでを迅速に行えるという多くのメリットを享受しました. 一方でサービスの利用が拡大していき, それらをそのまま運用していくうえでいくつかの課題がありました.</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/02/64f0b16a-elephant-8359382_1280.jpg" alt="" /></p> <p><a href="https://pixabay.com/photos/elephant-herd-animals-trunk-safari-8359382/">https://pixabay.com/photos/elephant-herd-animals-trunk-safari-8359382/</a></p> <h2>メルカリ Shops DB の課題</h2> <p>この章では現在メルカリ Shops において直面していた DB 運用の課題についていくつか紹介します.</p> <h3>コスト</h3> <p>まず1点目はコスト(サーバ費用)です. microservice 毎に DB が存在しているため, 利用が少なく最低スペックにも関わらずインスタンスを起動させ続けなければならない, という問題があります. インスタンス1台の費用は小さいですが, microservice の数が多いため “チリツモ” で費用が膨れていくにも関わらず, インスタンス Tier は最低で運用しているため <a href="https://cloud.google.com/sql/cud">CUD</a> 以外のコスト削減の手段を取れていませんでした.</p> <h3>管理</h3> <p>microservice 毎にインスタンスがあるため, <a href="https://cloud.google.com/sql/docs/postgres/maintenance#nearzero">near-zero downtime</a> といえどメンテナンスが大量に一度に通知されてしまいます. またIaC で管理されているとはいえ, 数が多いため現在のチーム体制ですべてを網羅的に管理運用することが難しくなっていました. 性質上, それぞれの microservice を廃止/統合するといったことは難しく, かといってインスタンスを減らすということはできず限られたメンバーで多くのインスタンスの対応が必要となっていました.</p> <h3>余剰ストレージ</h3> <p>Cloud SQL にはストレージの自動拡張機能と PITR がありますが, メルカリ Shops ではかつてこの機能を全てのインスタンスで有効化していました. 特に更新(INSERT/UPDATE/DELETE)が多く行われる系統のインスタンスでは, PITR のために保存している WAL のサイズが多く, それが要因でインスタンスのストレージサイズを拡張し続ける, という問題が発覚しました. 止血対応として PITR を無効化したものの, Cloud SQL では<a href="https://cloud.google.com/sql/docs/postgres/instance-settings#storage-capacity-2ndgen">一度拡張したストレージサイズは縮小できない</a>ため, 添付のよう余剰なストレージサイズに対して課金し続けていました.</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/02/417cb151-browsinghistory_storage.png" alt=""> </p> <p>4.5TB のディスクに対し実データは 250GB 程度</p> <h2>対策の手法</h2> <p>メルカリ Shops では上記のような運用/インスタンス費用の課題に対する対策として, Cloud SQL インスタンスの統合を行うことを決定しました. 単純にインスタンスを統合するにもいくつか方法があり, それぞれについて簡単に解説し, 今回メルカリ Shops がどのような手法で統合を行ったかについて説明していきます.</p> <h3>前提</h3> <h4>サービス要件</h4> <p>まず, 統合作業時のサービス側の要件について紹介します. メルカリ Shops は利用者数が店舗・購入者ともに拡大しており, かつメルカリからの導線もあるため, 長時間(1時間以上)の停止メンテナンスを行うことはできませんでした. また作業時の停止についてもメルカリ Shops の DB は基本的に read heavy な構成のため, 書き込み(INSERT/UPDATE/DELETE)は停止しても読み込み(SELECT)については停止しないように進める必要がありました.<br /> 加えて統合対象の中には決済やレポート(店舗側に売上内容を表示する)機能といったサービス的にクリティカルな機能も含まれていたため, ダウンタイムをできるだけ短くしたい要望がありました.<br /> 一方, 今回の統合作業において重要な観点として <span style="color: red"><strong>切り戻しの準備は不要</strong></span> ということで合意をしました. つまり仮に切り替え後に何らかの問題があって切り戻した場合には, 切り戻し完了までに書き込まれたデータは欠損しても問題ない, ということです. これによって構成が少しシンプルになります.</p> <h4>システム</h4> <p>メルカリ Shops ではインスタンスはすべて各 microservice 毎に論理データベース, クレデンシャル(user/password), 権限(GRANT)が分離されています. また, 接続方法については各インスタンスの持っている Private IP に接続する方式となっており, <a href="https://cloud.google.com/sql/docs/postgres/sql-proxy">Cloud SQL Auth Proxy</a> や <a href="https://cloud.google.com/sql/docs/postgres/samples/cloud-sql-postgres-databasesql-connect-connector">Cloud SQL Go Connector</a> などを利用していませんでした. そのため, インスタンス統合後の切り替えのために Instance Connection Name(<code>${project}:${region}:${instance}</code>)などを変更する必要がなく, アプリケーション側での作業が不要で切り替えを実施できます.</p> <h3>統合方法案</h3> <p>上記の前提を踏まえて, インスタンス統合で検討した方法と実際に行った方法を紹介します.</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/02/3759dfde-screenshot-2025-02-20-at-13.22.55.png" alt="" /></p> <p><a href="https://wiki.postgresql.org/wiki/Logo">https://wiki.postgresql.org/wiki/Logo</a></p> <h4>DMS</h4> <p>まず, Google Cloud 上で DB の移行が可能な managed service で <a href="https://cloud.google.com/database-migration">Data Migration Service(DMS)</a> があります. 詳細については省略しますが, これは Cloud SQL(source) を primary とする external replica の作成と切り替えをフォローする managed service です. DMS は現時点では統合はサポートしていないこと(destination のインスタンスに DB があると実行できない), 切り替えは DB の切り替えのみサポートしていてアプリケーションの変更は別途必要ということで今回は要件に見合いませんでした.</p> <h4>export/import</h4> <p>当初やろうとしていた方法はこれで, source インスタンスで書き込みだけを停止し該当 DB を export , そしてそれを destination へと import し, 完了したらアプリケーションを destination へと変更する方法です. この方法は最も手順がシンプルかつ事前準備もほとんどいらないため, 最も楽な方法ではある一方, 書き込みの停止時間が長くなります. 特にメルカリ Shops では source インスタンスを参照している分析用バッチなどが多く動いていて, それらを import 後に切り替えるなどをするとどうしても 1 時間以上書き込み停止発生してしまい, その停止時間がサービスにクリティカルな機能の要件に見合わないということで, この方法も断念しました. </p> <h4>Logical Replication(manual)</h4> <p>最後に検討した方法が PostgreSQL の <a href="https://www.postgresql.org/docs/current/logical-replication.html">Logical Replication</a> を利用する方法です. Cloud SQL では <a href="https://cloud.google.com/sql/docs/postgres/replication/configure-logical-replication">Logical Replication をサポートしている</a>ため, これらを利用してデータの同期と切り替えを行います. </p> <p>Logical Replication は, primary を source としないといけない, 同期レプリケーションはサポートしてない(設定できない), ConnectorEnforcement が有効化されている場合は機能しないなどの <a href="https://cloud.google.com/sql/docs/postgres/replication/configure-logical-replication#limitations-general">Cloud SQL としての制限</a>や, DDL は伝播しない, large object は伝播しないなど <a href="https://www.postgresql.org/docs/current/logical-replication-restrictions.html">Logical Replication としての制限</a>がいくつかありますが, 今回のケースではいずれも大きな問題とはなりませんでした. 加えて export/import で問題になった分析用バッチなどもこの方法だと同期が完了したタイミングで参照先をアプリケーションより前に変更しておくなど, 切り替え方法を工夫することで停止時間を短くできるため, 今回はこの方法をとることにしました.</p> <h2>統合手順</h2> <p>Logical Replication を構成し統合するにはこのような流れで進めていきます:</p> <ol> <li>インスタンスで <code>cloudsql.logical_decoding</code> を有効化</li> <li>schema dump &amp; restore</li> <li>Logical Replication を設定</li> <li>同期完了を待つ</li> <li>アプリケーションの接続を DNS ベースにする</li> <li>source 側で書き込み block(downtime 開始)</li> <li>ブロックする前までのデータ更新が追いついたことを確認</li> <li>DNS 変更</li> <li>source 側で既存の接続を kill</li> </ol> <p>大きく分けて Logical Replication 準備(1-4), 切り替え前作業(5-7), 切り替え(8-9)の3つに分けて説明していきます.</p> <h3>Logical Replication 準備</h3> <p>ここではインスタンスで Logical Replication を構成するための設定をします. 以下, 統合先インスタンスを destination, 統合元インスタンスを source とします. </p> <p>まず最初に source に対して <code>cloudsql.logical_decoding</code> を有効化する必要がありますが, これには再起動が必要となります. データ量や TPS などによって異なるかと思いますが, 大体 30s ~ 60s 程度で起動が完了しました. </p> <p>続いて destination に source と同じ PostgreSQL user, database を作成します. <a href="https://cloud.google.com/sql/docs/postgres/create-manage-users">user</a>, <a href="https://cloud.google.com/sql/docs/postgres/create-manage-databases">database</a> を作成したら destination に source の schema をリストアします. 実データは Logical Replication の <code>COPY</code> によって同期されていくため単に空のテーブルとアクセス権のみ設定するだけでよいです. インスタンスに接続するのは前述の Cloud SQL Auth Proxy 経由で接続しています(この後の Cloud SQL Auth Proxy の起動は省略). </p> <p>また, この後何度もインスタンスに接続をするので, このように <code>/etc/hosts</code> や <code>~/.pgpass</code> を設定しておいて機械的にアクセスできるようにしておくと良いでしょう.</p> <pre><code>## /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}</code></pre> <pre><code>$ cloud-sql-proxy \ ${PROJECT}:${REGION}:source-001 \ --address $(grep source-001 /etc/hosts | cut -d&#039; &#039; -f1) $ cloud-sql-proxy \ ${PROJECT}:${REGION}:destination-001 \ --address $(grep destination-001 /etc/hosts | cut -d&#039; &#039; -f1)</code></pre> <p>なお Logical Replication は DDL を伝播しないという制約があり, dump/restore やLogical Replication 中に誤って DDL を実行してしまわないようにスクリプト側でもブロックするなどしてそれに対応しました.</p> <pre><code>## dump $ pg_dump \ -U ${USER} \ -h source-001 \ --schema-only &gt; src.$(date &#039;+%Y%m%d-%H%M%S&#039;).sql ## restore $ psql -h destination-001 \ --user src \ --dbname src &lt; src.xxxxxx.sql</code></pre> <p>Logical Replication を構成するためには publication を source 側に, subscription を destination 側に作成する必要があります. それぞれ以下のような形で作成可能です:</p> <pre><code>## publication の作成 CREATE PUBLICATION pub FOR ALL TABLES; SELECT * FROM pg_publication_tables;</code></pre> <p>subscription でつなぐユーザーは, 区別できるようにアプリケーションが利用しているものとは別のものを用意するとよいでしょう.</p> <pre><code>## subscription の作成 CREATE SUBSCRIPTION sub_src CONNECTION &#039;host=xxxxxxx port=5432 dbname=src user=replication password=xxxxxx&#039; PUBLICATION pub; SELECT * FROM pg_stat_subscription;</code></pre> <p>これで Logical Replication が開始し, <code>COPY</code> コマンドが source 側で実行され, データの初期同期(既存のデータのコピー)が行われます. <code>COPY</code> が終わったらデータの差分同期が行われるようになります. データサイズの大きいテーブルだとかなり時間がかかるため, <code>COPY</code> 作業が終わったかどうかは source 側で pg_stat_activity を見るか, destination 側で <code>COPY</code> 対象テーブルにロックが掛かってるかを見るとわかります:</p> <pre><code>## source SELECT * FROM pg_stat_activity WHERE query LIKE &#039;%COPY%&#039;; ## destination SELECT * FROM xxx LIMIT 1;</code></pre> <p><code>COPY</code> が終わり差分同期が始まったら source と destination の差はこのようにして求められます. 両方 0 だった場合は追いついています.</p> <pre><code>## 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;</code></pre> <p><code>COPY</code> が終わりそれぞれの diff が 0 となっていたら差分同期も追いついているため, 切り替え前作業に進みます.</p> <h3>切り替え前作業</h3> <p>ここでは書き込みを停止して実際に切り替えを実行する前までの作業を説明します. 前述しましたがメルカリ Shops の DB 構成は</p> <ol> <li>DB への接続は直接 IP ベースで接続している</li> <li>write/read ユーザーを分けていない, <code>cloudsqlsuperuser</code> Role を持つ</li> </ol> <p>ということがあります. 書き込み停止を確実に行い, かつ短時間で済むようにするにはいくつかの工夫をする必要があります.</p> <h4>接続方法の変更</h4> <p>IP ベースでの接続となると, アプリケーションを統合先インスタンスへと向き先を変更する必要があり, そのためにはデプロイが必要となります. 書き込み停止時間中にデプロイをすると数十分かかるため, 事前にアプリケーションの接続を FQDN を使うように変更することにしました. 例えば DSN では</p> <pre><code>postgres://src:xxxxxx@10.0.0.1:5432/src</code></pre> <p>と <code>10.0.0.1</code> に向いていたものを <code>src.db-consolidation.mercari.internal</code> と FQDN に変更します:</p> <pre><code>postgres://src:xxxxxx@src.db-consolidation.mercari-shops.internal.:5432/src</code></pre> <p>こうすることによって書き込み停止した後に <code>src.db-consolidation.mercari.internal</code> を source から destination に変更することで, アプリケーションのデプロイを伴わずに切り替えが可能となります.</p> <h4>書き込み停止</h4> <p>書き込みの停止については write/read ユーザーが分かれておらず同一ユーザーでクエリが実行されていたため, 少し工夫が必要です. もしユーザーが分かれていれば write ユーザーの RENAME などでブロックすることも可能ですが, そうなってはいないためユーザーの権限を剥奪するかデータベースごと read only にするかの 2 通りの方法が考えられます. ユーザーは<code>cloudsqlsuperuser</code> Role を持っているため, REVOKE を実行して更新権限剥奪するよりも, 有効/無効が単純なデータベースを read only にすることにしました:</p> <pre><code>ALTER DATABASE xxx SET default_transaction_read_only TO on;</code></pre> <p>ただし, この ALTER 文は default を変更するのみで transaction で明示的に <code>SET TRANSACTION</code> などが実行されている場合は書き込み可能となってしまうため, それが利用されていないことを確認する必要があります. メルカリ Shops では DB 周りは <a href="https://github.com/ent/ent">https://github.com/ent/ent</a> を利用しており, <code>SET</code> するには <a href="https://github.com/ent/ent/blob/master/dialect/sql/driver.go#L95">WithVar</a> を実行するか, 直接 <code>SET XXX</code> を実行する必要がありますが, そのどちらも実行されていないため <code>ALTER DATABASE</code> を採用しました.</p> <p>実際の流れですが, まずアプリケーションの DSN を FQDN(向き先は source-001) に変更しデプロイします. デプロイ完了し, 実際に FQDN が利用されていることが確認できたら書き込みを停止させるために source 側で ALTER 文を打ちます. この ALTER 文は Connection Pooling などすでに接続されているクライアントには有効ではないため, 一度すべての接続を切断して再接続を促します.</p> <pre><code>## 書き込みを停止 ALTER DATABASE src SET default_transaction_read_only TO on; ## 自分以外の接続をすべて KILL SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid &lt;&gt; pg_backend_pid() AND datname = current_database() AND usename = CURRENT_USER;</code></pre> <p>このあとの接続はすべて <code>default_transaction_read_only = ON</code> となっているため, (明示的に指定しない限り)書き込みは失敗します.</p> <h3>切り替え</h3> <p>切り替えは FQDN の向き先を destination の IP に変更することで切り替え可能です. その前にデータが同期されているかなどを確認しておく必要があります. Cloud SQL での Logical Replication は同期レプリケーションではないため, 厳密にはすべての更新が伝播したかをチェックする必要がありますが, 前述の通りメルカリ Shops は read heavy なユースケースであって差分同期は十分早いため, シンプルな確認だけするようにしました.</p> <pre><code>## 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;</code></pre> <p>これらの source/destination での WAL の同期状況, 実行状況を何度か確認しそれぞれ 0 のままだった場合は切り替え可能として進めます. また, Logical Replication は仕様として sequence は同期されないため, もし利用している場合はそれもここで合わせておく必要があります:</p> <pre><code>## source SELECT * FROM pg_sequences; SELECT max_value FROM pg_sequences WHERE sequencename = &#039;${SEQUENCE_NAME}&#039;; ## destination SELECT setval(&#039;${SEQUENCE_NAME}&#039;, 99999); SELECT max_value FROM pg_sequences WHERE sequencename = &#039;${SEQUENCE_NAME}&#039;;</code></pre> <p>これで source と destination のデータが揃っている状態となるため, FQDN の向き先を destination の Private IP に変更し, DNS が伝播した後に再度 source 側の接続を <code>KILL</code> します. これで source 側に残っている接続がすべて destination に切り替わります.</p> <pre><code>$ dig +short src.db-consolidation.mercari-shops.internal. 10.0.0.2 ## source SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid &lt;&gt; pg_backend_pid() AND datname = current_database() AND usename = CURRENT_USER; ## destination SELECT * FROM pg_stat_activity WHERE pid &lt;&gt; pg_backend_pid() AND datname = current_database() AND usename = CURRENT_USER;</code></pre> <p>FQDN を変更し source への接続を <code>KILL</code> しすべての接続が destination に向くことが確認できたら, downtime は終了となります.</p> <h2>統合した結果</h2> <p>上記の手順で統合作業を行うことで, 長くても 2~3 分程度の停止時間で切り替えが完了し, 実作業はメンテナンスなどを設けずにすべてオンラインで日中に完了させられました. エラーも切り替え作業前後の接続を <code>KILL</code> することによる再接続でエラーとなる程度で, 瞬断程度の問題で済みました. </p> <p>また統合による効果として</p> <ul> <li>コスト効果: 25%程度削減</li> <li>余剰なインスタンス削除による運用負荷軽減</li> </ul> <p>など, 当初の課題をいくつか解消することができました.</p> <p>メルカリ Shops では IP ベースでの接続をしていたためこのような手法で統合/切り替えを行ってきましたが, Cloud SQL Auth Proxy などではまた別のアプローチ(<a href="https://github.com/GoogleCloudPlatform/cloud-sql-proxy/releases/tag/v2.15.0">v2.15.0</a> の DNS ベースを利用するなど)を取る必要があるかと思います. また, より強い整合性が求められるケースや切り戻し用インスタンスを準備して切り戻しに備えるなど, 今回の統合手法よりも難しいケースも考えられます. 普段チームでよく運用している MySQL とは勝手が違うことが多くかなり勉強になりました.</p> <h2>最後に</h2> <p>現在, メルカリでは学生インターン/エンジニアを積極的に募集しています. ぜひ <a href="https://careers.mercari.com/jobs/">Job Description</a> をご覧ください。</p> LLM x SRE: メルカリの次世代インシデント対応https://engineering.mercari.com/blog/entry/20250206-llm-sre-incident-handling-buddy/https://engineering.mercari.com/blog/entry/20250206-llm-sre-incident-handling-buddy/<p>株式会社メルカリのPlatform Enablerチームで新卒エンジニアとして働くTianchen Wang (@Amadeus)です。今回は、Large Language Model (LLM)を利用してフリマアプリ「 [&hellip;]</p> Thu, 06 Feb 2025 14:41:37 GMT<p>株式会社メルカリのPlatform Enablerチームで新卒エンジニアとして働く<a href="https://www.linkedin.com/in/tianchen-amadeus-wang/?originalSubdomain=jp" title="Tianchen Wang (@Amadeus)">Tianchen Wang (@Amadeus)</a>です。今回は、Large Language Model (LLM)を利用してフリマアプリ「メルカリ」の次世代インシデント対応を構築した事例を共有します。</p> <p>今日の急速に進化する技術環境において、堅牢なオンコール体制を維持することは、サービスの継続性を確保するために重要です。インシデントは避けられないものですが、迅速に対応し解決する能力は、お客さまに安心・安全の体験を提供するために不可欠です。これは、メルカリのすべてのSite Reliability Engineer(SRE)と従業員が共有する目標です。</p> <p>この記事では、Platform Enablerチームが生成AIを活用して開発したオンコールバディであるIBIS (Incident Buddy &amp; Insight System) の紹介をします。IBISは、エンジニアのインシデント解決を迅速化し、MTTR(Mean Time to Recovery)を短縮することで、組織やエンジニアが負担するオンコール対応コストを削減することを目的として設計されています。</p> <h2>課題の認識と解決のモチベーション</h2> <p>メルカリでは、お客さまが安心・安全に製品を利用できることがすべての従業員によって共有される優先の目標およびビジョンです。このために、異なる部門が協力し、オンコールチームを設立しました。毎週、オンコールメンバーには多くのアラートが発生し、その多くは実際にお客さまに影響を与えたインシデントとして扱われます。これらのインシデントはお客さま体験の悪化をもたらすため、インシデントが回復するまでの平均時間(MTTR)を短くすることが被害を最小化する上で重要です。</p> <p>さらに、オンコールメンバーはこれらのインシデントに対処するために多大な時間を割かなければならず、新しい機能を開発するために利用できる時間が間接的に削減され、ビジネス目標の達成能力に影響を及ぼします。</p> <p>結果として、<strong>インシデント発生時にMTTRを短縮し、オンコールメンバーへの負担を軽減</strong>することが、プラットフォームチームにとって重要な課題となっていますが、Large Language Model (LLM)の登場により、これらのインシデント対応を自動化することが可能な解決策として浮上しました。</p> <h2>深掘り:IBISのアーキテクチャ</h2> <p>インシデント対応システム「IBIS」のアーキテクチャを詳しく見ていきましょう。</p> <div align="center"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/02/e7c669d7-screenshot-2025-02-05-at-17.18.05.png" alt="図1. IBISのアーキテクチャ" width="800"></p> <p>図1. IBISのアーキテクチャ</p> </div> <p>高レベルの視点から、過去のインシデントについての振り返りレポート情報をインシデント管理ツール<a href="https://www.blameless.com/" title="Blameless">Blameless</a>から抽出します。これらのレポートには、暫定措置、根本原因、障害による損害などのデータが含まれています。これらのデータはクレンジング、翻訳、および要約のプロセスを受けます。その後、OpenAIの埋め込みモデルを使用して、これらのデータソースからベクターを作成します。</p> <p>ユーザーが自然言語でSlackボットに質問を投げかけると、これらのクエリもベクターに変換されます。その後、会話コンポーネントが質問に関連するベクター埋め込みを検索し、関連する言語構造を整理してユーザーに応答を形成します。</p> <p>アーキテクチャ全体を「データの前処理」と「会話機能」の2つの主要コンポーネントに分けて詳しく説明します。</p> <h3>データの前処理</h3> <p>以下はIBISがインシデントデータを前処理する方法です。</p> <div align="center"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/02/3e92db5b-screenshot-2025-02-05-at-17.14.42.png" alt="図2. IBISのデータ処理プロセス" width="500"></p> <p>図2. IBISのデータ処理プロセス</p> </div> <h4>データ抽出</h4> <p><a href="https://www.blameless.com/" title="Blameless">Blameless</a>には各インシデントのプロセス詳細、インシデントSlackチャンネルからのチャットログ、振り返りおよびフォローアップアクションなど重要なインシデント関連情報が含まれています。Google Cloud Schedulerを活用し、Blamelessの外部APIから最新のインシデントレポートを定期的にGoogle Cloud Storageバケットにエクスポートします。このプロセスはサーバーレスの原則に基づいて設計され、Google Cloud Run Jobs内で実行されます。</p> <h4>データクレンジング</h4> <p>Blamelessから取得したデータを無差別にLarge Language Model (LLM)に送信することはできません。それは、データに多数のテンプレートが含まれており、ベクター検索(<a href="https://ja.wikipedia.org/wiki/%E3%82%B3%E3%82%B5%E3%82%A4%E3%83%B3%E9%A1%9E%E4%BC%BC%E5%BA%A6">コサイン類似度</a>)の精度に大きく影響を与える可能性があるだけでなく、膨大な量の<a href="https://www.igi-global.com/dictionary/personal-identifiers-information-piis/60620">個人識別情報(PII)</a>が含まれているためでもあります。潜在的な情報漏洩のリスクを軽減し、生成される結果の精度を高めるため、データクレンジングは必要なプロセスです。</p> <p>データからテンプレートを除去するため、データがMarkdown形式であることを利用し、LangChainが提供する<a href="https://python.langchain.com/docs/how_to/markdown_header_metadata_splitter/" title="Markdown Splitter">Markdown Splitter</a>機能を使って関連するセクションを抽出します。PIIに関しては、種類が多いため、<a href="https://spacy.io/" title="SpaCy">SpaCy</a> NLPモデルを使用してトークン化し、語の種類に基づいて潜在的に存在するPIIを削除します。</p> <p>データクレンジングコンポーネントはGoogle Cloud Run Functionsで実行されます。このステージ以降は、Google Cloud Workflowを使用してシステム全体を管理します。Google Cloud Storage Bucketに新しいファイルが追加されると、Eventarcが自動的に新しいワークフローをトリガーします。このワークフローはHTTPを使用してデータクレンジング用のCloud Run Functionを起動し、完了するとプロセスの次のステージに進みます。クラウドワークフローを導入することで、ETLプロセス全体のコードメンテナンスが容易になります。</p> <h4>翻訳、要約、エンベディング</h4> <p>クリーンになったデータはプロセスの次の段階に進みます。データクレンジングのおかげで、LLMモデルを利用して、データをよりスマートに処理することができます。メルカリでは、インシデントレポートが日本語と英語で書かれているため、これらのレポートを英語に翻訳することは、検索精度を向上させるために重要なステップです。翻訳ステップをGPT-4oベースのLangChainに依頼しています。また、多くのレポートが長文であるため、内容の要約もベクター検索精度を向上させるために重要です。GPT-4oがデータの要約を支援します。最後に、翻訳された要約済みのデータはエンベディングを経て、ベクターデータベースに格納されます。</p> <p>翻訳、要約およびエンベディングプロセスはGoogle Cloud Run Jobsで実行されます。データクレンジングが完了すると、Cloud WorkflowがCloud Run Jobを自動的にトリガーします。図2に示されているように、エンベディングされたデータはLangChainが提供する<a href="https://python.langchain.com/docs/integrations/vectorstores/google_bigquery_vector_search/" title="BigQuery vector store">BigQueryベクターストア</a>パッケージを使用して、BigQueryテーブルに格納されます。</p> <h3>会話機能</h3> <p>Slackベースの会話機能はIBISのコア機能です。私たちの設計では、ユーザーはSlackでボットに言及することで、自然言語でIBISに直接質問を投げかけることができます。この機能を実現するために、Slackからのリクエストを常時受信し、ベクターデータベースに基づいて応答を生成できるサーバーが必要です。</p> <div align="center"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/02/85ffa738-screenshot-2025-02-05-at-17.12.26.png" alt="図3. IBISの会話システム" width="600"></p> <p>図3. IBISの会話システム</p> </div> <p>図3に示すように、このサーバーはGoogle Cloud Run Service上に構築されています。ベクターDBとして機能するBigQueryから関連情報を取得し、それをLLMモデルに送信して応答を生成します。</p> <p>クエリの処理に加えて、会話コンポーネントは、短期記憶などの他の機能もサポートしており、インタラクティブな体験を向上させます。</p> <h4>短期記憶</h4> <p>エンジニアがインシデントの理解を時間とともに深めることを考慮し、同一スレッド内で記憶機能を取り入れることは、インシデントの解決策を提供するIBISの能力を強化するために重要です。図4に示されているように、LangChainのメモリ機能を使用して、同じチャンネルからのユーザーのクエリとLLMの応答を保存します。同じチャンネルで追加のクエリが投げかけられる場合、スレッド内の以前の会話がLLMに送信される入力の一部として付加されます。</p> <div align="center"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/02/264475bd-screenshot-2025-02-05-at-16.49.24.png" alt="図4. 短期記憶の設計" width="450"></p> <p>図4. 短期記憶の設計</p> </div> <p>このストレージソリューションは、メモリをCloud Run Serviceインスタンスのメモリ内に配置するため、新しいバージョンのIBISを再デプロイしてCloud Run Serviceを更新すると、メモリが消失します。詳細については、<a href="https://python.langchain.com/docs/how_to/chatbots_memory/" title="LangChain&#039;s memory documentation">LangChainのメモリドキュメント</a>を参照してください。</p> <div align="center"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/02/19346e12-screenshot-2025-02-05-at-17.24.56.png" alt="図5. 短期記憶のケース" width="400"></p> <p>図5. 短期記憶のケース</p> </div> <h4>インスタンスをアクティブに保つ</h4> <p>短期記憶機能のメモリデータが現在インスタンスに保存されているため、コールドスタート時にメモリが失われないようにこのインスタンスをアクティブに保つ必要があります。これを達成するために、この<a href="https://knmts.com/as-a-engineer-223/" title="document">ドキュメント</a>のガイダンスに基づいた戦略を実施しました。Cloud Run Serviceインスタンスに定期的にアップタイムチェックを送信して、アクティブな状態を維持します。このアプローチはシンプルで、コストも最小限です。また、このサービスのスケールアップを制限し、インスタンスの最大数と最小数の両方を1に設定しました。</p> <h2>今後の展望</h2> <ol> <li> <p>正確に<strong>ユーザーフィードバックを収集する</strong>ことが主要な目標の一つです。自動評価のためのヒューマン・イン・ザ・ループアプローチを採用し、ユーザーの調査応答をデータポイントとして収集し、IBISを継続的に改善する計画です。</p> </li> <li> <p>従来の言及ベースのクエリ方法から<strong>Slackフォームベースの質問アプローチ</strong>に移行する予定です。この変更は、ユーザーのクエリを精緻化することにより、応答の精度を向上させることを目的としています。</p> </li> <li> <p>社内ツールの継続更新を考慮し、会社のドキュメントに基づいて<strong>LLMモデルをfine-tuningする</strong>計画です。これにより、モデルが最新で関連性のある回答を提供することを確実にします。</p> </li> </ol> <h2>まとめ</h2> <p>このプロジェクトは2024年12月末に初期バージョンをリリースしました。このブログを書いている時点までで(2025年1月)、IBISはメルカリのインシデント対応用のいくつかのslackチャンネルで使用可能になりました。このツールを利用するユーザーの数は増え続けているので、継続的にユーザーフィードバックを収集し、回復までの平均時間(MTTR)への影響を監視していきます。</p> <h2>さいごに</h2> <p>現在、株式会社メルカリでは学生インターン・新卒エンジニアを積極的に募集しています。ぜひ<a href="https://careers.mercari.com/jobs/?employment_type=new-graduate-hiring+internships&quot;">Job Description</a>をご覧ください。</p> メルカリ ハロのインターンに2ヶ月参加してみてhttps://engineering.mercari.com/blog/entry/20250128-15cddb7f50/https://engineering.mercari.com/blog/entry/20250128-15cddb7f50/<p>こんにちは、九州大学大学院1年の@masaと申します。 私は、2024年11月から12月末までの2ヶ月間、メルカリ ハロのフロントエンドエンジニアとして、インターンに参加しました。 左からインターンの@masa、メンター [&hellip;]</p> Wed, 29 Jan 2025 12:00:46 GMT<p>こんにちは、九州大学大学院1年の@masaと申します。<br /> 私は、2024年11月から12月末までの2ヶ月間、メルカリ ハロのフロントエンドエンジニアとして、インターンに参加しました。</p> <figure style="text-align: center"> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/01/ebd7c9f9-image4.jpg" /><figcaption>左からインターンの@masa、メンターの@d&#8211;chanさん</figcaption></figure> <p>今回は、その中で特に注力したインテグレーションテスト戦略と、メルカリでの学びについてお話しします。</p> <h2>なぜ「メルカリ ハロ」のインターンに参加したのか</h2> <p>インターンに参加した主な目的は、大規模サービス、特にtoC向けのサービス開発を体験することでした。メルカリのサービスの中でもメルカリ ハロは、リリースして1年も経っていない新規のプロダクトであり、スピードと品質が求められる現場で、実践的な開発プロセスを学ぶ絶好の機会だと考えました。<br /> また、メルカリという会社の雰囲気やカルチャーを直接体験して解像度を上げてみたいという興味も、参加の大きな動機の一つでした。</p> <h2>インテグレーションテストへの取り組み</h2> <p>インターン期間中、大小様々なタスクに取り組みましたが、中でも注力したのが事業者向け画面のインテグレーションテストです。私がジョインした時点で、テックリードの@ryotahさん主導で技術選定と環境構築は完了しており、テストカバレッジ向上に取り組む段階でした。</p> <p>メルカリ ハロでは、インテグレーションテストに関して、メルペイのフロントエンドテスト方針も踏襲して、仕様書(Spec)に準拠した、ページ単位のインテグレーションテストを実装しました。この過程で以下の2点に関して、改良に取り組みました。</p> <ol> <li>冗長なコードの回避</li> <li>バリデーションテストの最適化</li> </ol> <h3>冗長なコードの回避</h3> <p>Specに従ってテストを記述することで、チーム内でのテスト粒度や方針の一貫性を保てます。しかし、Specに厳密に従いすぎると、異なる画面で同じフォームコンポーネントを使う場合などに 検証内容が重複してしまい、コードが冗長になりがちです。<br /> この問題に対し、以下の3つのアプローチを検討しました。</p> <ol> <li>共通コンポーネントにテストを記述<br /> メリット:テストコードの重複を解消できる。1つの共通コンポーネントに対するテストを集約できるため、同じ検証ロジックを何度も書く必要がなくなる。<br /> デメリット:インテグレーションテストとしては「実際のアプリケーションに近い形でテストしたい」という方針とやや乖離する。複雑な部分をコンポーネント化してしまうと<strong>「人によってテストの書き方が異なってしまう」</strong>という懸念もある。</li> <li>全ての画面でテストを記述:<br /> メリット:上記二つの中間的アプローチで、各ページでの実際のユーザー操作を想定したSpecに基づいたテストを忠実に書くことになるため、微妙に異なるユースケースやバグを見落としにくい。<br /> デメリット:同様のテストロジックを大量に書くことになり、変更があった際の修正も多岐にわたってしまいメンテナンスが大変。</li> <li><strong>代表的な1画面でのみ共通コンポーネントのテストを記述</strong>:<br /> メリット: 上記2つの中間的アプローチで、テストの冗長性をある程度抑えつつ、基本機能の担保が可能。<br /> デメリット: 完全な網羅性はないものの、必要に応じて各ページごとに追加テストを柔軟に書くことで補える。</li> </ol> <p>最終的には、<strong>「代表的な1画面でのみ共通コンポーネントのテストを書く」</strong>方式をベースに、<strong>ページ固有のロジックがある場合だけテストを追記する</strong>という運用に落ち着きました。現状のチームリソースや開発速度を考慮すると、これが最も<strong>現実的かつ柔軟</strong>なアプローチだと判断しました。</p> <h3>バリデーションテストの最適化</h3> <p>フォームライブラリ(react-hook-form)の標準的なバリデーションはユニットテストでカバーし、インテグレーションテストでは、ユニットテストでは検証しにくいバリデーションに集中しました。<br /> たとえば、以下のように<strong>submit時にエラーがあった場合にモーダルを表示</strong>するロジックは、react-hook-formのschemaテストだけではカバーしにくいケースです。</p> <pre><code>const onSubmit = (value) =&gt; { // 入力項目に誤りがある場合 if (value.name !== &#039;hoge&#039;) { setShowModal(true) } // データの送信など }</code></pre> <p>ここのような部分をPlaywrightを使ったインテグレーションテストで検証します。</p> <pre><code>// Playwright を使用したインテグレーションテストの例 test(&#039;入力項目に誤りがある場合にモーダルを表示する&#039;, async({page}) =&gt; { // 省略 // ... await page.getByLabel(&#039;名前&#039;).fill(&#039;foo&#039;); await page.getByRole(&#039;button&#039;, {name: &#039;送信&#039;}).click(); await expect( page.getByRole(&#039;dialog&#039;, { name: &#039;名前にはキーワードを含めてください&#039; }).toBeVisible(); });</code></pre> <p>テストを書くコストとリターンのバランスを意識しつつ、後々の技術負債にならないよう、意味のあるテストコードを心がけました。</p> <p>また、開発プロセスの透明性と効率性を高めるため、インテグレーションテスト用のSlackチャンネルを作成しました。背景としては、フロントエンド領域で気軽に技術的な相談ができる場が十分に整備されていなかったことや、別チームのエンジニアとコミュニケーションを取る機会が少なかったことが挙げられます。そこで、実装中に直面した課題や疑問点を具体的なケースとともに共有できるようにしたことで、<strong>チーム全体で問題意識を共有することができ、より良いソリューションを見出す</strong>ことができました。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/01/27dd8438-image3_2-1024x905.png" alt="" /></p> <h2>その他の活動と経験</h2> <p>インターン期間中に、生成AIを活用した業務効率化アイデアソンにも参加しました。</p> <p>90分という限られた時間内で、チームビルディングからアイデア出し、プロトタイプ作成まで行うというかなり濃密なスケジュールでしたが、とても刺激的で楽しかったです。<br /> アイデア選定時は「共感が得られるか」と「短時間で成果を出せるか」を重視しました。最終的には、「カレンダーたのんだ〜」という、参加者の空き状況や入れたい予定の性質をもとにGoogleカレンダーの予定調整を効率化するアイデアに取り組むことにしました。</p> <p>チームの方々が優秀すぎて、自分の役割を見出すのに最初は戸惑いましたが、自分にできる貢献を考え、ワークフローの設計および実装を担当しました。Zapierを使用してカレンダー情報を取得する部分も実装したかったのですが、時間の制約で叶いませんでした。<br /> <img src="https://storage.googleapis.com/prd-engineering-asset/2025/01/fe002fad-image2-1024x576.jpg" alt="" /></p> <p>そして結果はなんと、優勝することができました🎉<br /> (チームメンバーのみなさんに感謝です🙇‍♂️)</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2025/01/0701f401-image1-1024x526.png" alt="" /></p> <h2>英語でのコミュニケーションでの苦労</h2> <p>インターン選考時の面接では、所属予定のチームは英語の使用頻度が高くないので、英語が苦手でも大丈夫というお話を伺っていました。しかしチームの状況が変わり、私が参加した初週から、週に1回フロントエンドのMTGは英語になりました。英語でのコミュニケーションには正直不安があり、特に司会役を担当しながら英語で進行しなければいけない回は、大変苦労したことを覚えています。事前にチートシートを用意するなどして、なんとか乗り切りました…。</p> <p>さらに、オフィスには多くの外国籍社員が在籍しており、社内イベントへ参加することで自然と英語を使う機会が増えました。また、Pull Requestのレビューも英語でやりとりするため、日常的に英語に触れられる環境だと実感しました。</p> <p>予想以上に英語を使うシーンが多かったことで最初は戸惑いましたが、そのおかげで英語学習のモチベーションが格段に上がりました。技術的スキルだけでなく、グローバルなコミュニケーション能力も磨ける環境は、エンジニアとしての成長に大きな価値があると感じています。</p> <h2>さいごに</h2> <p>今回、メルカリ ハロのインターンを通して、大規模サービス開発の現場で多くの貴重な経験を積むことができました。特に、インテグレーションテストの実装を通じて、効率的で品質の高いテストコードを書く考え方や、チームコミュニケーションの大切さを深く学んだと感じています。<br /> この2ヶ月間で得た知識や経験を、今後の学びやキャリアに活かしていきたいと思います。最後になりましたが、メンターのd&#8211;chanさんをはじめ、あたたかく迎えてくださった皆さまに心より感謝申し上げます。</p> メルカリのEngineering Roadmapの具体的な運用についてhttps://engineering.mercari.com/blog/entry/20241225-engineering-roadmap/https://engineering.mercari.com/blog/entry/20241225-engineering-roadmap/<p>はじめに こんにちは、メルカリでJapan RegionのCTOを担当している木村です。僭越ながら今年も最後のアドベントカレンダーの投稿を担当させていただきます。 昨年投稿した開発組織にとってのEngineering R [&hellip;]</p> Wed, 25 Dec 2024 10:00:41 GMT<h2>はじめに</h2> <p>こんにちは、メルカリでJapan RegionのCTOを担当している木村です。僭越ながら今年も最後のアドベントカレンダーの投稿を担当させていただきます。</p> <p>昨年投稿した開発組織にとっての<a href="https://engineering.mercari.com/blog/entry/20231225-creating-mercari-engineering-roadmap/" title="Engineering Roadmapの必要性">Engineering Roadmapの必要性</a>についての記事では、「開発スケジュールの期待値調整」が容易になったり、「将来を見越したアーキテクチャ」を作ることができたり、「Visionを組織に浸透させやすくなるメリット」があることなどをご紹介しました。しかし、昨年は実際にEngineering Roadmap(以下Roadmapと呼ぶ)にどのようなアイテムがあるのか、あるいはどのように運用されているのかといった具体的なご説明までには至ることができませんでした。本稿では、運用上難しかった話なども含めて、より実践的な内容をお話ししたいと思います。</p> <h2>昨年のRoadmapを振り返る</h2> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2024/12/f6ab2d54--2024-12-24-20.09.05.png" alt="" /></p> <p>まずは、前回のRoadmap作成時の狙いと実際に1年後どのような結果になったのかをご説明したいと思います(まだビジネス上オープンになっていないものもあるため、公表されているものに絞ってお話しします。Roadmapに関連するビジネス的なイベントとしては<strong>2024年3月6日に新規事業であるメルカリ ハロをリリース。2024年8月29日に、台湾のお客さまがWeb版「メルカリ」を通じて日本で出品された商品を購入できる「<a href="https://about.mercari.com/press/news/articles/20240829_crossborder/" title="越境取引">越境取引</a>」</strong>の展開をスタートしたほか、<strong>2024年9月10日には生成AIを活用した「AI出品サポート」の提供</strong>を開始しました。</p> <p>これらを技術的に実現させるため、23年12月の段階で、Roadmapの大きな方向性として以下のように定めていました。</p> <p><strong><em>① 3つの領域の&quot;開発コストの低下&quot;と&quot;Enabling&quot;を実現する</em></strong><br /> 中長期的なビジネスの拡張を実現するために、以下の3つの事項のバランスを保ちつつ、Biz Enablingと開発コストの低減を実現する。</p> <ul> <li>既存サービス開発簡易化</li> <li>新規事業展開簡易化</li> <li>国際展開</li> </ul> <p>メルカリグループではメルペイやメルコイン、今回のハロのように継続的に新規事業を提供しています。このような新規事業を立ち上げるたびにすべてを0から開発するのではなく、既存のプラットフォームを拡張・活用することによって、新規事業の展開をより高速かつ低コストで実現することをゴールに掲げました。この方針を応用することで、国内の新規事業の立ち上げのみではなく、多国展開もより効率的に実現することも目指しています。当然ながら新規のものだけでなく、既存サービスへの改善もあるので、既存サービス開発の効率化も同時に目標として掲げていました。</p> <p>これらを実現するためにRoadmapの中にアクションアイテムとして定義していたものを一部抜粋してご紹介します。</p> <h3>Golden Path</h3> <p>これまで、メルカリグループの組織としてどの技術を標準と位置付けるのかは特に明文化されていませんでした。もちろん言語やデータベースの選定などにおける暗黙的なコンセンサスは組織のなかにありましたが、基本的には各事業が必要な技術の選定をそれぞれで行ってきました。これらは柔軟性や自由度の高さという観点ではうまくワークすることもありましたが、一度使い始めると長期的なメンテナンスコストが発生したり、事業の立ち上げ時にゼロから投資を行う必要があったり、 選定のために同じような議論を繰り返すことになるなど、スピード面で課題がありました。</p> <p>Golden Pathはグループ内での技術の標準化やフレームワークを作ることによって、開発と運用コストを落とすことと、同時に新規サービスを作る際の効率化を狙いました。アクションアイテムとしては以下のように設定していました。</p> <p><strong><em>技術標準をベースとしたアプリケーション構築領域(Web, Mobile, Backend)での Bootstrapping tool の開発を進め、Global Expansion への活用が可能な状態になっている。</em></strong></p> <p>これは言い換えると、アプリケーションを作る際に、メルカリの環境に適した効率的で標準化されたフレームワークを提供することを目的としています。1年後どのようになったかというと、残念ながらすべての領域でこれを実現することはできなかったのですが、WebについてはBootstrapping toolが完成されて、新規事業を作る際のWeb開発を大きく効率化することができました。また、改めてADRや<a href="https://engineering.mercari.com/blog/entry/20241216-mercari-tech-radar-initiative/" title="Tech Radar">Tech Radar</a>の仕組みが整備され、BackendやMobile開発においても開発に使われるTech Stackを改めて標準化することによって、関係者にコンセンサスを取る手間が省けるようになり、技術選定のコストを低減させることができました。</p> <p>また、新しい microservice や Webアプリケーション を本番環境で運用する前の基準を定めたチェックリストである <a href="https://engineering.mercari.com/blog/entry/20241213-new-production-readiness-check-experience-in-mercari/" title="Production Readiness Check (PRC) の効率化・短縮化">Production Readiness Check (PRC) の効率化・短縮化</a>も実施しました。これまで2ヶ月以上かかってしまっていた PRC を自動化することによって効率化する試みです。</p> <p>技術の標準化というものは、ごく当たり前のことに聞こえてしまうかもしれませんが、弊社でも継続的な新規事業の創出や技術的なトレンドの変化によって、全社でのコンセンサスを取ることが難しくなってきていました。ここで、この課題をそのままにせず、一度立ち止まり、全社で標準的に用いるTech Stackを再整理することで、改めて開発の高速化を狙う決断をしました。</p> <h3>IDP</h3> <p>いわゆるアカウントIDに関するプラットフォームの改善です。IDはビジネス戦略に合わせて先行して技術基盤を用意しなければならず、Roadmapの中でも最重要な項目です。PassKeyに関する開発や普及に関してもこの項目に含まれます。こちらも、国際展開に向けて以下のようなアクションアイテムを設定していました。</p> <p><strong><em>新しくなったアカウント登録・ログインプロセスが、実際に日本以外のregionから利用される状態になっている。</em></strong></p> <p>計画的に開発を行い、こちらも1年後の現在、台湾で提供されているサービスでのアカウント作成に活用されています。上記にも述べましたように、IDはビジネス戦略の根幹になる技術と言っても過言ではありません。<a href="https://engineering.mercari.com/blog/entry/20231225-creating-mercari-engineering-roadmap/">昨年の記事</a>にも記載しましたように、お客さまへ新しい価値を提供するには、開発組織としてビジネスの方向性にアラインしつつ、先行してプラットフォームを用意する必要があります。まさに台湾での越境取引の件は昨年の時点で、他国へのビジネス展開が概ね決まっていたので、先行してそれを実現するためのアクションプランを計画的に実装し、提供することができました。また、これまでにもメルコインやメルカリ ハロを提供する中で、メルカリのIDとeKYCさえ完了していればとてもシームレスに新規サービスをご利用いただける仕組みができあがったのも、継続的にIDPが先を見越した開発ができていたことに起因しています。</p> <h3>AI出品サポート</h3> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2024/12/bfa2a977--2024-12-24-20.08.49.png" alt="" /></p> <p>この時期に生成AIの活用の推進も強化しており、9月にリリースされた<a href="https://about.mercari.com/press/news/articles/20240910_aisupport/" title="AI出品サポート">AI出品サポート</a>のアクションアイテムも設定してありました。</p> <h4>AI出品サポート(出品補助)</h4> <p>生成AIのポテンシャルの大きさは明らかではありましたが、これをいち早くビジネスに導入して、特にメルカリにおける出品の利便性を向上させてお客さまに早く価値を提供したいと考えていました。この段階ではまだ生成AI社内でも検証段階でありましたが、早い段階でサービス活用の指針を定められていたことで、業界でも比較的早い段階で生成AIを実際にサービスに活用することができました。</p> <p>Roadmapのアクションアイテムは実際にはより多くのものがあるのですが、雰囲気を掴んでいただくために公開できる範囲で一部のみ抜粋させていただきました。</p> <p>この先にも述べますが、Roadmap作成の最大のメリットは、Visionを示すだけに留まらず、やること・やらないことを明確に意思表示できることだと感じています。特に大きなリアーキテクチャが伴うものについては「やらないといけないと思っていた」や「やろうと思っていた」ことは、なるべく早くに意思決定して、早く取り掛からなければ、後々解決するのが困難になってしまうことが多々あります。私たちは継続的に解決しなければならない課題について議論し、ビジネスとの方向性とアラインしながら技術的な投資の決定を継続的に行っています。</p> <h3>Next level of Scalability and Resiliency</h3> <p>今後のサービスの成長をより堅牢にするために、Infrastructureレベルでの改善もRoadmapに設定していました。これまでも私たちはInfrastructureのResiliencyやSalabilityの改善を行ってきましたが、今後の国際展開によるお客さまの増加や金融事業を提供しているメルペイのResiliencyを改善するためには抜本的な仕組みの改善が必要でした。</p> <p>Scalabilityの改善に関して、特に大きな進歩は、大規模なコアなデータをMySQLの物理サーバに保存していたものを、<a href="https://qiita.com/official-columns/event/202402-pingcap-02/" title="慎重な検証を重ねた上">慎重な検証を重ねた上</a>で、TiDB Cloudへのmigrationを始めたことです。これによりSclabilityの改善と運用コストの大幅な低減の実現を狙っています。</p> <p>そして、国際展開するための基盤の準備として、複数拠点でサービスを運用するためのMulti RegionでのInfrastructureの構築も進めています。こちらについては、現在も進行中であり、まとまった形で発表できる状態になったら再度詳しくご説明したいと思います。しかし、並行してInfrastructureのコストを最適化しつつも、Multi Regionでのサービス運用を実現するためにはコストの増加は避けられません。したがって、<a href="https://engineering.mercari.com/en/blog/entry/20240329-finops-at-mercari/" title="FinOpsの文化醸成を継続的に行い">FinOpsの文化醸成を継続的に行い</a>、具体的なコストの低減を全社の目標として共有しています。今年は、特にCUD採用率、Spot VM採用率を全社で上げていき、コンピュートリソースの最適化を実現することができました。</p> <h2>Roadmapの活用と運用</h2> <p>この作成したRoadmapをどのように活用、運用しているのかについてご説明します。</p> <h3>Visionの浸透に活用する</h3> <p>Engineering組織で重要なことについてVisionの浸透があります。「私たちは今後何を実現したいのか」、そして「どのような過程を経てこれを実現させるのか」を一人一人のエンジニアに理解してもらうことが大事です。浸透について、私たちも特別なことをやっているわけではないのですが、Roadmapが完成したら、Engineerが全員参加するAll Handsで作成したRoadmapを使って説明し、Visionの浸透を図っています。まさに、年末の今も来年のRoadmapを作っているところであり、年明けに来年からの3ヵ年に実現したいVisionとRoadmapを全社で説明することになっています。なかなか一度の説明では浸透しないので、プレゼンテーション資料だけではなく、言語化されたRoadmapの文章をいつでも誰でも見られる状態にすることや、誰でもこれに対してFeedbackできる仕組みを築くことが重要です。それによって、一人でも多くのエンジニアがRoadmapを自分事として捉えて、Roadmapについて真剣に考えてくれることを目指しています。</p> <h3>OKRの設定に活用する</h3> <p>私たちはクォーターごと、つまり3ヶ月ごとにOKRを設定しています。OKRを3か月ごとに考えるのは計画性がないととても大変な作業ですし、OKRの設定に時間がかかってしまうと、設定と同時にすぐにまた次のOKRを考えなければならないといった悪循環となってしまいます。Roadmapで年間の計画が決まっていればOKRに設定しなければいけないオブジェクトの多くをRoadmapから転用することができるので、とてもスムーズに作成することができます。</p> <h3>運用について</h3> <p>当然ながら、Roadmapは掲げたままにしないこと、形骸化させないことが非常に重要です。そのために継続的に進捗を確認することやRoadmap自体をメンテナンスすることが重要です。これが完璧な運用方法というわけではないですし、将来変わることもあると思いますが、参考までにわたしたちの現在の運用方法をお話ししたいと思います。</p> <p>基本的には以下のイテレーションでロードマップの作成とアップデートを行なっています。</p> <ul> <li>12月にRoadmapのメジャーアップデートを行い(前年のRoadmapをリバイズして1年-3年の計画を作成する)</li> <li>その後はクォータ末に毎回マイナーアップデートを行う(3月、6月、9月)</li> </ul> <p>プログレスのチェックは月に1回各アクションアイテムのプログレスを確認しています。当然ながらプログレスの確認は必ずやったほうが良く、やりながら方向性をアジャストすることもできますし、この継続的な運用によってさらにVisionの浸透が強化されます。</p> <h2>Engineering Roadmapを運用する上で難しい点と工夫</h2> <h3>ビジネスプライオリティの影響</h3> <p>1-3年間のRoadmapを立てて、着々と開発を進めても、ビジネス上のプライオリティが下がってしまったり、方向性が変わることはどうしても発生します。むしろ、そういう変化は受容できる仕組みにしなければ現実的な運用は厳しいと考えています。そのため、私たちは月1回のプログレスチェックでの方向性のアジャストや3か月ごとのマイナーアップデートを行って、なるべくフレキシブルにビジネスの要求に応えられる運用を目指しています。</p> <h3>アイテムが多くなりすぎる問題</h3> <p>どうしてもやりたいことを整理するとRoadmapに追加したい項目が多くなりがちになってしまいます。しかし、項目が多くなればなるほど、エンジニアをはじめとする現場のメンバーの理解を得ることが難しくなりますし、現実的には全てに手をつけられなくなってしまうリスクがあります。実際に私も「やらないことを決める」努力をして項目を減らす努力はしているのですが、まだまだ多い状態です。毎年Roadmapを洗練させていくなかで、少しずつ数を絞ってはいますが、実際に運用してみると「少し少ないかな」と思うくらいの方がいいと個人的には思います。</p> <h2>最後に</h2> <p>今回は少し具体的にRoadmapの内容や運用についてご説明させていただきました。本当はすべてのRoadmapを公開して、それぞれの狙いや振り返り、改善点などもお話することができるとよりイメージをお伝えしやすいのですが、まだまだ世に公開できていないものもありますので、それはまた来年末にとっておき、ご容赦いただけたら幸いです。Roadmapの設定と運用において、当たり前の内容ではあるのですが、せっかく作成したRoadmapを形骸化させないためには、議論を重ねて極力正確なものを作り、継続的に見直していくことがポイントとなります。そして、作成したものをそのままにせずに、いつでも誰でもアクセスできて、Feedbackを提供できる仕組みと雰囲気づくりをすることによって、血の通ったRoadmapを作成することができます。Roadmapは方向性を言語化することで、ビジネスとエンジニアリングの間の理解の差を埋めて、方向性の不確実性を減らすことができ、自信を持って開発し続けるために欠かせないツールだと考えています。ご一読くださったみなさんにとって、少しでも手助けになったら幸いです。</p> Spanner Data Boostを活用したリアルタイムなリコンサイルエラーの検出https://engineering.mercari.com/blog/entry/20241224-spannar-data-boost/https://engineering.mercari.com/blog/entry/20241224-spannar-data-boost/<p>こんにちは。Mercari Corporate Products Teamのエンジニアの@yuki.watanabeです。 この記事は、Mercari Advent Calendar 2024 の21日目の記事です。 は [&hellip;]</p> Tue, 24 Dec 2024 11:00:17 GMT<p>こんにちは。Mercari Corporate Products Teamのエンジニアの<a href="https://github.com/yuki0920">@yuki.watanabe</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20241125-mercari-advent-calendar-2024/">Mercari Advent Calendar 2024</a> の21日目の記事です。</p> <h2>はじめに</h2> <p>現在、内製の会計仕訳システムの開発に携わっています。このシステムには様々なバッチ処理が実装されているのですが、BigQueryへクエリしデータを抽出するためのバッチで誤検出の問題がありました。本記事ではこの問題に対して検討した複数のソリューションと結果的にどの方法を採用したのかについて紹介します。<br /> バッチ処理の課題を解決する際の参考にしていただけると幸いです。</p> <h2>リコンサイルエラー検出のバッチについて</h2> <h3>会計仕訳システムにおけるデータの流れ</h3> <p>まず、会計仕訳システム(図のAccounting System)におけるデータの流れを紹介します。お客さまがメルカリやメルペイを使用した場合、取引内容に応じて様々なMicroservicesが処理を行い、金銭に関わるデータがある場合は、会計仕訳システムのPub/Subに送信します。会計仕訳システムではCloud Functionsでバリデーションを行い、Spannerのaccounting_dataテーブルへ登録します。<br /> 次に、各Microservicesは会計仕訳システムのPub/Subに送信済みのデータについて、会計仕訳システムのリコンサイル用APIへ送信します。このAPIは後述するリコンサイルと呼ばれる突合処理を行い、結果をSpannerのreconciliationテーブルへ登録するもので、Kubernetes上のgRPC ServerのAPIとして実装されています。<br /> Spannerへ登録されたデータ(accounting_data、 reconciliation)は、Cloud ComposerとDataflowを用いて、BigQueryへ1日に1度差分を同期しています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2024/12/9c15d849-golduck_architecture-2024-12テックブログ用.png" alt="" /></p> <h3>リコンサイルは会計データの確からしさを検証する仕組み</h3> <p>リコンサイルとは、会計仕訳システムと会計データの送り元となるMicroservice間のデータの突合処理のことを指します。Microserviceはデータベースに登録した会計データをリクエストデータに含め、リコンサイル用APIへリクエストします。APIでは、リクエストデータと会計仕訳システムに登録された会計データ(accounting_data)を突合し、突合結果をreconciliationテーブルのstatusカラムに保持して登録します。このリコンサイルを通じて、Microservice側のデータと会計仕訳システム側のデータが一致していることを保証しています。<br /> 以下はreconciliationに登録されるstatusカラムの値のイメージです。</p> <ul> <li>突合成功: status=’success’</li> <li>突合失敗: status=’failed’</li> </ul> <h3>リコンサイル検証用バッチでリコンサイルのエラーがないかを確認する</h3> <p>突合が失敗したデータについてはリコンサイルのエラーと考えられます。そこで、リコンサイルエラー検出用のバッチをCronJobを用いて実装しています。このCronJobでは1日に1度BigQueryへクエリし、リコンサイルエラーのデータを抽出します。エラーのデータが存在する場合は、Microservice Teamへ共有し、再度のリコンサイルAPIへのリクエストを依頼しています。</p> <h3>SpannerとBigQueryの同期タイムラグによる誤検出</h3> <p>しかし、上記のバッチには課題が存在しました。Spannerにはリアルタイムにリコンサイル結果が登録されていますが、バッチが参照しているBigQueryには1日に1度しか同期されません。このSpannerとBigQueryの同期タイムラグにより、バッチの実行結果には誤検出である偽陽性のデータが含まれていました。「Spannerには突合済みのデータが存在するが、BigQueryには未同期」のデータは本来は突合が成功していますが、バッチでは突合が失敗したデータとして検出されていました。<br /> このため、バッチによってリコンサイルエラーとして抽出されたデータについて、エンジニアが「Spannerにクエリをして本当にリコンサイルのエラーがあるのかどうかを調べる」という手動の運用作業が発生していました。<br /> 会計仕訳システムでは、会計データを扱っているという特性上、毎月の月初に前月分のデータを確定する、いわゆる「締め」が必要になります。月末付近に発生したリコンサイルエラーは速やかに送り元であるMicroservice Teamにリコンサイル依頼をし、リコンサイルエラーを解消しなければなりません。しかし、上記の運用作業が発生する場合、リコンサイルエラーの検出から解消までに日をまたいでしまうこともあり、会計業務への影響が出てしまうこともありました。<br /> これらの運用課題の解消のためには、「リコンサイルエラー検出のバッチの誤検出をゼロにする」ということが必要でした。</p> <h2>Spanner Data Boostの採用</h2> <h3>検討したソリューション</h3> <p>運用課題の解消のため、複数のソリューションを検討しました。</p> <h4>1. StreamingでSpannerからBigQueryへ同期する方法</h4> <p>まず、Spannerに登録されたデータをStreamingでリアルタイムにBigQueryへ同期する方法を検討しました。Dataflowの<a href="https://cloud.google.com/dataflow/docs/guides/templates/provided/cloud-spanner-change-streams-to-bigquery">Spanner change streams to BigQuery template</a>などを利用し同期用のJobを作成することで、技術的には実現可能な方法ではあります。Streamingでリアルタイムに同期できると、上記以外の課題の解消にも役立てられるため、大きな恩恵を得られたでしょう。一方で、Stremingの同期用のJobを採用する場合は、同期の不具合がある場合にも備えなければなりません。例えば、同期用Jobが停止する、BigQueryへ二重でデータが登録される、BigQueryへの一部のデータの登録が失敗するなどが考えられます。こうした不具合が発生した場合には、手動運用でリカバリするか、もしくはリカバリ用のシステムの実装が必要になりますが、初期の実装とその後の運用まで含めた工数を考慮すると、既存の課題に対するソリューションとしては過大だと考え、採用を見送りました。</p> <h4>2. SpannerとBigQueryの同期頻度を増加する方法</h4> <p>次に、SpannerからBigQueryの同期頻度を増加する方法を検討しました。現状1日に1度行っている同期を2〜3回に増加させ、その後にリコンサイルエラー検出のバッチを実行する方法です。これまでの方法と比較し、偽陽性のデータを減らすことは可能だったかと思います。しかし、Spannerに登録されているBigQuery未連携のデータは多少なりとも存在するため、リコンサイルエラー検出のバッチの誤検出をゼロにすることには向かないと考え、採用を見送りました。</p> <h4>3. Spanner federated queriesとSpanner Data Boostを利用する方法</h4> <p>最終的に、Spannerの<a href="https://cloud.google.com/spanner/docs/databoost/databoost-overview">Data Boost</a>を活用することにしました。上述の通り、リコンサイルエラー検出のバッチではBigQueryにクエリをしていますが、このクエリを修正し、BigQueryの<a href="https://cloud.google.com/bigquery/docs/spanner-federated-queries">Spanner federated queries</a>の機能を使い、Spannerへのクエリもしています。Spannerへクエリする際に「Spannerには登録済みだがBigQueryには未同期のデータ」も併せて取得することで、BigQueryとSpannerのデータをどちらも考慮して、リコンサイルエラーのあるデータのみを抽出することができるようになっています。<br /> 重要な点としては、Spanner federated queriesを利用する際に、Data Boostを有効化するということです。Data BoostはSpannerのPrimary Instanceへ負荷を与えることなくSpannerへクエリすることができる非常に便利な機能です。リコンサイルエラー検出のバッチでは、Spannerへ登録された1日分のデータを取得しますが、1日分でもかなりのレコード数となるため、もしPrimary Instanceへクエリした場合はパフォーマンスへの影響は避けられません。そこで、Data Boostを有効化しこの問題を回避しています。<br /> この方法を採用した理由は、「リコンサイルエラー検出のバッチの誤検出をゼロにする」という目的を達成可能であり、かつ実装工数の観点でも既存のクエリの改修の範囲で早急に実現できることが見込まれたためです。</p> <h3>BigQueryへのクエリ改修前後のサンプルコード</h3> <p>リコンサイルエラー検出のバッチで使用しているBigQueryへのクエリの改修前後のサンプルコードを記載します。</p> <h3>改修前のクエリサンプル</h3> <pre><code class="language-sql">SELECT * FROM example_dataset.reconciliation WHERE status != &#039;success&#039;</code></pre> <p>まず、改修前のクエリではBigQueryのDatasetであるexample_datasetのreconciliationテーブルをFROM句に指定し、statusがsuccess以外のレコードを抽出していました。</p> <h3>改修後のクエリサンプル</h3> <pre><code class="language-sql">WITH spanner_reconciliation AS ( SELECT * FROM EXTERNAL_QUERY(&#039;spanner_connection_example&#039;, &quot;&quot;&quot; SELECT * FROM reconciliation WHERE created &gt;= TIMESTAMP(CURRENT_DATE(&quot;Asia/Tokyo&quot;), &quot;Asia/Tokyo&quot;) &quot;&quot;&quot;) ) SELECT * FROM example_dataset.reconciliation LEFT JOIN spanner_reconciliation ON reconciliation.id = spanner_reconciliation.id WHERE status != &#039;success&#039; AND (spanner_reconciliation.id IS NULL OR spanner_reconciliation.status != &#039;success&#039;)</code></pre> <p>改修後のポイントは2点あります。1点目は、WITH句でEXTERNAL_QUERYの関数を利用している点です。第1引数にSpannerを指定したBigQueryのConnection IDを指定し、第2引数には、クエリ実行日に登録されたreconciliationテーブルのレコードを抽出するクエリを指定しています。<br /> 2点目は、メインのクエリのWHERE句の絞り込みです。FROM句にreconciliationテーブルを指定することは改修前と同様ですが、加えてWITH句で定義したspanner_reconciliationテーブルをLEFT JOINし、WHERE句で利用しています。これにより、「BigQueryのreconciliationテーブルのレコードでエラーが発生している」かつ「実行日に登録されたSpannerのreconciliationテーブルのレコードでエラーが発生している、またはレコードが存在しない」条件に該当したレコードのみを抽出できるようになっています。</p> <h2>まとめ</h2> <p>会計仕訳システムのリコンサイル検証用バッチには、SpannerとBigQueryの同期タイムラグによる誤検出の課題が存在しました。そこで、バッチで実行しているBigQueryへのクエリを改修し、Spanner federated queriesとData Boostを利用しSpannerへもクエリすることで、BigQueryへ未同期のデータも抽出するようにし、同期タイムラグによる課題を解決しました。<br /> 本記事執筆時点で、改修版のリリースから2ヶ月程度が経過しています。改修前と比較し、月に10件程度発生していたSpannerの手動クエリによる運用作業がほぼゼロになるなどの効果が出ています。<br /> また、この経験が早速別の機会にも役立ちました。あるバッチでSpannerへクエリする処理が、とある変更をきっかけにインデックスが効かなくなり大幅にパフォーマンスが悪化してしまう問題があったのですが、Spanner federated queriesとData Boostを利用することで、これを解決することができました。<br /> 今後もSpannerとBigQueryをしばらく使い続けることが予想されるため、Spanner federated queriesとData Boostを利用したアプローチを様々な場面で活用できるかと思います。<br /> 明日の記事は kimras さんです。引き続きお楽しみください。</p> <h2>参考資料</h2> <ul> <li><a href="https://cloud.google.com/spanner/docs/databoost/databoost-overview">Data Boost Overview | Spanner | Google Cloud</a></li> <li><a href="https://cloud.google.com/bigquery/docs/connect-to-spanner">Connect to Spanner | BigQuery | Google Cloud</a></li> <li><a href="https://cloud.google.com/bigquery/docs/spanner-federated-queries">Spanner federated queries | BigQuery | Google Cloud</a></li> <li><a href="https://www.oreilly.co.jp/books/9784873118703/">データ指向アプリケーションデザイン ―信頼性、拡張性、保守性の高い分散システム設計の原理</a></li> <li><a href="https://www.oreilly.co.jp/books/9784814400652/">データエンジニアリングの基礎 ―データプロジェクトで失敗しないために</a></li> </ul> メルペイの数百画面をSwiftUI/Jetpack Composeに移行するプロジェクトを推進するhttps://engineering.mercari.com/blog/entry/20241221-leading-a-project-to-migrate-hundreds-of-screens-to-swiftui-jetpack-compose-from-uikit-androidview-in-merpay/https://engineering.mercari.com/blog/entry/20241221-leading-a-project-to-migrate-hundreds-of-screens-to-swiftui-jetpack-compose-from-uikit-androidview-in-merpay/<p>こんにちは。メルペイ Engineering Managerの@masamichiです。 この記事は、Merpay &amp; Mercoin Advent Calendar 2024 の記事です。 メルペイのモバイルチ [&hellip;]</p> Tue, 24 Dec 2024 10:00:51 GMT<p>こんにちは。メルペイ Engineering Managerの<a href="https://x.com/masamichiueta">@masamichi</a>です。<br /> この記事は、<a href="https://engineering.mercari.com/blog/entry/20241125-merpay-mercoin-advent-calendar-2024/">Merpay &amp; Mercoin Advent Calendar 2024</a> の記事です。</p> <p>メルペイのモバイルチームでは現在、メルカリアプリ内に存在するメルペイの数百画面をSwiftUI/Jetpack Composeに移行するプロジェクトを推進しています。<br /> この記事では、プロジェクトの経緯とその進め方について紹介します。</p> <h1>メルペイのリリース</h1> <p>メルペイが搭載されたメルカリアプリがリリースされたのは2019年2月です。初期の開発は主に2018年に進めていましたが、当時はSwiftUIやJetpackComposeは発表されておらず、メルペイを含むメルカリアプリはUIKit/Android Viewで開発していました。<br /> その後、2019年内にiOS/Android共に宣言的UIフレームワークであるSwiftUI/Jetpack Composeが発表されました。</p> <h1>GroundUP Appプロジェクト</h1> <p>一方2020年ごろから、母体となるメルカリアプリは長年の開発で積み重なってきた課題を解決するために コードベースを刷新するGroundUP Appプロジェクトが立ち上がりました。<br /> GroundUP AppプロジェクトではSwiftUI/Jetpack Composeが全面採用され、2022年にリリースすることができました。</p> <p>プロジェクトの詳細についてはコアメンバーの記事を参照ください。</p> <ul> <li><a href="https://careers.mercari.com/mercan/articles/35887/">メルカリの事業とエコシステムをいかにサステナブルなものにするか?かつてない大型プロジェクト「GroundUp App」の道程</a></li> <li><a href="https://careers.mercari.com/mercan/articles/36183/">これからメルカリのエンジニアリングはもっと面白くなる──iOS&Androidのテックリードが振り返る、すべてがGo Boldだった「GroundUp App」</a></li> </ul> <p>メルペイの各種機能はモジュール化してある程度疎結合な状態でメルカリアプリに組み込んでいたため、新しいアプリにも組み込まれた状態を実現し、GroundUP Appプロジェクトと並行しながら新機能の開発を続けていきました。</p> <p>メルペイの移行についてはこちらの記事を参照ください。</p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20221213-ground-up-app/">メルカリアプリのコードベースを置き換える GroundUP App プロジェクトの話</a></li> <li><a href="https://engineering.mercari.com/blog/entry/20231023-mmtf2023-day1-4/">【書き起こし】Merpay iOSのGroundUP Appへの移行 – kenmaz【Merpay &amp; Mercoin Tech Fest 2023】</a></li> </ul> <h1>DesignSystem</h1> <p>メルカリではDesignSystemを定義し画面デザインおよび開発を行っています。メルカリでは2019年ごろから段階的にアプリへの導入を進めてきました。</p> <p>特にGroundUPプロジェクト後の新しいアプリではSwiftUI/Jetpack ComposeのUIコンポーネントに刷新され、DesignSystemの全面的な採用によって画面のUI/UXの統一、ダークモード対応やアクセシビリティの向上が実現しました。</p> <p>一方で、メルペイは前述の通り初期から開発してきたモジュールをそのまま新しいアプリに統合しました。それらの画面はUIKit/Android Viewで作られており、DesignSystemについてもUIKit/Android Viewの旧バージョンの実装となっていました。それによって、UI/UXの差分、ダークモード非対応、UIフレームワークが違うことによるアーキテクチャの差分といった課題がありました。<br /> GroundUPプロジェクトで得た効果を最大限活用するため、2023年よりメルペイの既存画面のマイグレーションを進めるプロジェクトを開始しました。</p> <h1>Engineering ProjectsとGolden Path</h1> <p>メルペイの数百画面をマイグレーションしていくには、長期的な取り組みが必要となります。メルペイではこういった長期的なエンジニアリングへの投資を推進するためにEngineering Projectsという枠組みを構築してきました。<br /> Engineering Projectsの詳細については、VP of Engineeringの@keigowさんの記事をご覧ください。</p> <ul> <li><a href="https://engineering.mercari.com/blog/entry/20241204-merpay-engineering-investment/">メルペイのエンジニアリングへの投資を推進する仕組み</a></li> </ul> <p>また、現在メルカリグループ全体で標準的な技術スタックをGolden Pathとして定義し、開発効率の向上や技術資産の再利用を目指しています。DesignSystemで採用されているSwiftUI/Jetpack ComposeはGolden Pathとして定義されており、メルペイのマイグレーションプロジェクトは社内ではわかりやすくDesignSystemマイグレーションプロジェクトと呼んでいます。</p> <ul> <li><a href="https://careers.mercari.com/mercan/articles/40891/">グローバル展開を推進する開発組織をつくる——Meet Mercari’s Leaders:木村俊也(CTO)</a></li> </ul> <p>実際にマイグレーションを実施していくには工数が必要であり、優先度の議論も必要となります。本プロジェクトを立ち上げるにあたってプロジェクト計画書を作成し、背景やアクション、体制やマイルストーンを明確にしました。Golden Pathのような会社の長期的な方針やEngineering Projectsの取り組みもあり、本プロジェクトをEngineering Projectsの1つとして推進しています。</p> <h1>プロジェクト体制と進め方</h1> <h2>体制</h2> <p>メルペイでは、プログラムという大きなドメイン毎にプロダクトマネージャーやエンジニアを含めたクロスファンクショナルなチーム体制で事業を推進しています。<br /> Design System マイグレーションを進めるには全プログラムのモバイルチーム、デザイナーとの連携が必要不可欠です。モバイルチームのリーダーとデザイナーで隔週の定例ミーティングをセットし、進捗やブロッカーの共有、マイルストーンの設定を定期的に行っています。<br /> プロジェクトの立ち上げ期は週次で集まって進め方の型を作っていくのが良いと思いますが、ある程度固まってくると隔週がちょうどいいと感じています。</p> <p>プロジェクトの情報を全て集めたページを社内のConfluenceに作っています。ここでプロジェクト計画書や体制図、機能ごとのSlackのコミュニケーションチャンネル、デザインや開発のノウハウ、QAのテストケース、機能のリリース状況、定例のミーティング議事録などプロジェクトに必要な情報を俯瞰して見ることができるようにしています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2024/12/034b65f3-tableofcontents.png" alt="tableofcontents" /><br /> <em>Table of Contentsの一部抜粋</em></p> <p>マイグレーションを進めるには工数とタイミングが重要です。プロダクトの新規施策を導入するタイミングで同時にマイグレーションできれば、効率的に進めることができます。一方、それだけでは変化の少ない機能のマイグレーションが進められません。また、緊急度の高い開発に関してはスピードを優先して一旦既存の画面への開発を行うケースもあります。既存の機能をそのまま移行するケース、プロダクトの新規施策を導入するタイミングで同時に移行するケース、どちらもバランスよく進められるように、各プログラムのデザイナーおよびモバイルチームリーダーと密に連携をとりながら進めています。</p> <h2>Screen Listと進捗の追跡</h2> <p>画面のマイグレーションをしていくにも、まずどれくらいの機能および画面があるのかをできるだけ正確に把握する必要があります。 メルペイではプロジェクトを立ち上げる際に全ての画面一覧をスプレッドシートにまとめたスクリーンリストを作成しました。これによって画面数や画面パターンを正確に把握したり、機能のオーナーシップを持つチームや開発・デザイン担当者を一元化して把握することができるようになりました。全ての画面にIDを振ってチーム内で対象とする画面の認識齟齬がないようにもしています。</p> <p>各画面には以下のような進捗ステータスも付けてグラフにすることで、全体の進捗を視覚的に追跡できるようにしています。</p> <ul> <li>TODO</li> <li>Design In Progress</li> <li>Design In Review</li> <li>Design Done</li> <li>Dev in Progress</li> <li>In QA</li> <li>Done</li> </ul> <p>隔週の定例ミーティングでマイグレーションに取り組んでいる機能の進捗状況を更新しています。</p> <p>各画面の状況を正確に把握することで、Engineering Projectsの定例ミーティングでもCTO, VPoEに対して透明性高く正確な情報をレポートすることができています。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2024/12/da9b8b04-screenlist.png" alt="screen list" /><br /> <em>Screen Listのシート一部抜粋</em></p> <h2>Strategy Sharing</h2> <p>メルペイでは四半期の後半に一度Strategy Sharingという、次の四半期の施策の優先順位の決定や戦略・ロードマップのレビューを行い全社的に共有するタイミングを設けています。その中で、Engineering Projectsとしても次の四半期にターゲットとする機能と進捗率を定義し、全社的にプロジェクトのマイルストーンを共有しています。これによってエンジニアリング部門以外の方々でも進捗を把握することができ、全社的な認知を得ることができています。</p> <h2>現在の進捗状況</h2> <p>これまで2023年から2024年にかけて約2年間プロジェクトを推進してきましたが、2024年12月現在、Androidは約65%、iOSが約60%のマイグレーションを完了してリリースできています。開発中のものも含めると70% ~ 80%のマイグレーションが進んでいます。</p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2024/12/9543c8b4-android.png" alt="Android" /><br /> <em>Android Progress</em></p> <p><img src="https://storage.googleapis.com/prd-engineering-asset/2024/12/9f6b7bb3-ios.png" alt="iOS" /><br /> <em>iOS Progress</em></p> <p>今後もメルペイのモバイルエンジニアリングをアップデートすべく、チーム一丸となって100%を目指してプロジェクトを推進していきます。</p> <h1>終わりに</h1> <p>この記事では、メルカリアプリ内に存在するメルペイの数百画面をSwiftUI/Jetpack Composeに移行するプロジェクトプロジェクトの経緯とその進め方について紹介しました。<br /> プロジェクト規模も大きく長期的な取り組みで困難なことも多いですが、テックカンパニーとしてこのような取り組みに挑戦できていることはメルカリグループのエンジニアリング組織としての強みだと思います。<br /> SwiftUI/Jetpack Composeへ移行を検討しているチーム、移行を進めているチームの皆さまの参考になれば幸いです。</p> <p>次の記事は @kimuras さんです。引き続きお楽しみください。</p>