TECH PLAY

株式会社メルカリ

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

270

こんにちは。Backend Enablement Team のエンジニアの @goccy です。 この記事は、 Merpay & Mercoin Advent Calendar 2025 の7日目の記事です。 時は今から4ヶ月ほど前の8月下旬、とても暑い日でした。珍しくチームの On Caller の PagerDuty がアラートを上げました。内容は Gateway からあるマイクロサービスへの呼び出しが突然 Timeout するようになり、gRPC の Deadline Exceeded が急増しているというものでした。対象のマイクロサービスは gRPC Federation を利用した BFF ( Backend for Frontend ) で、基本的にはさらに後ろのマイクロサービスにプロキシするだけの単純なサービスです。少し調査すると、該当の gRPC メソッドには Timeout が設定されていなかったため、Gateway 側で設定した Timeout ( 30秒 ) を超過した際に、Gateway 側でリクエストをキャンセルするタイミングで出ているエラーだとわかりました。この間、BFF 側でエラーが発生していないにもかかわらず Goroutines は増え続けていることから、リクエストが処理できずにサーバー上で滞留しているような状況だと推測できました。しかし、それ以上の調査が困難なことと、問題が起きているのが一部の Kubernetes Pod だけだったため、該当の Pod を再起動することを試したところ、無事解決に向かいました。その日は営業時間が終了間際だったこともあり、もやもやしたものを抱えつつ、いったん解散して帰途につくことになりました。 このときはまだ、これから長い闘いが待っていることをチームの誰も知る由もありませんでした。 サービス間の関係図 それから二日後、先日の問題の原因究明もろくにできていないような状況で、再びアラートが上がりました。しかし今回は前回とは別のサービスで、かつ gRPC Federation を利用していました。現象は前回同様、突然該当のサービスの特定の Pod だけ Goroutines が急増し応答しなくなるという問題で、Pod を作り直すと改善するという現象も同様でした。 立て続けに gRPC Federation を利用する複数のサービスで同様の問題が起こったため、原因が gRPC Federation 側にありそうだという線が濃厚になってきました。これはとてもまずい状況です。gRPC Federation を利用するサービスは 20 を超えます。 突如として暗雲が垂れ込めます。この日から、チーム総出でこの問題に対処するようになりました。 Lock 範囲の最小化 まず、該当のサービスの直近のリリースに含まれる gRPC Federation の変更点などを調査しましたが、こちらは特に怪しいものはないという結論に至りました。次に、リクエストが滞留し続けることから、gRPC Federation 側で取得している Lock の待ち時間が長すぎたり、Dead Lock しているケースがないかを疑いました。該当のサービスでは Cloud Profiler を導入しており、その結果から Lock 待ちをしているスレッドが大量にあることがわかりました。Lock 箇所として複数の候補がありましたが、それらを Claude Code を使ってプロファイリング結果を参照しながら議論したところ、いずれも Dead Lock でないこともわかりました。次の可能性として、Lock を解放するまでの時間が長く、Lock を解放するよりも Lock の待ち行列に加わるスレッドの方が多くなり、結果 Goroutines が増え続けている可能性を考えました。そこで、まずは Cloud Profiler が挙げた Lock 箇所のうち、Lock 範囲を最小化することで不必要に長く Lock を取得している箇所をなくしました。( 該当のPR ) しかし数日後、また Goroutines が急増しアラートが上がりました。この時点から、一日に3〜 5回程度不定期にアラートが鳴り続けるようになってしまい、Pod を再起動するだけとはいえ、On Caller の負担が顕著に増えました。ただ、Lock 範囲を狭めたことで、Lock 待ちをしている箇所のほとんどが gRPC Federation のプラグインを利用している箇所であるとわかりました。 gRPC Federation のプラグインアーキテクチャ ここで、以降の説明を理解しやすくするために gRPC Federation のプラグインアーキテクチャについて解説します。gRPC Federation 自体の詳しい説明は 私が以前書いた別記事 をご参照ください。 簡単に説明すると、gRPC Federation は Protocol Buffers 上で記述できる型推論付き静的型付け言語であり、式の評価には CEL( Common Expression Language ) を利用しています。 OSS として開発しているため、Protocol Buffers 上で記述する CEL 式の中でドメイン固有のロジックを利用することは通常できません。例えば認証処理を例にとると、gRPC の metadata から 認証用の JWT を取り出してパースし、得られた値をバリデーションしたり、さらに別の値を取得するような処理は CEL の表現力の範囲を超えています。ですが、こういった処理は gRPC Federation を利用するすべてのサービスで必要になる共通処理でもあります。 gRPC Federation ではこのような要件のために、CEL 式の中で利用できる API を独自に定義し、その実装を Go で書いて WebAssembly に変換して利用する プラグインシステムを用意しています 。 Go でプラグインシステムを提供する場合、WebAssembly を利用するのが最善であることは、先日 Go Conference 2025 で発表してきた ( Go で WebAssembly を利用した実用的なプラグインシステムの構築方法 ) ので、詳しくはそちらを参照して欲しいのですが、重要なこととして、WebAssembly を利用する場合に次のような制約があることを知っておく必要があります。 WASI P1 という仕様に沿って動作するが、P1 の段階ではスレッドの仕様は存在しない スレッドの仕様がないため、必ずシングルスレッドで動作する WebAssembly を動作させるために作るインスタンスは、起動するだけで数十MBのメモリを消費するため、インスタンスを無制限に作るようなアーキテクチャは採用できない これらの制約のため、gRPC Federation ではプラグインを評価する際、gRPC Service ごとにプラグインに対する WebAssembly インスタンスを一つだけ作成し、評価の前後で Lock を取得して直列に処理します。これはつまり、サーバーに対してどんなに並行にアクセスしても、プラグインの処理の段階では直列実行になることを意味しています。 Cloud Profiler の結果、このプラグイン評価時に取得する Lock 待ちが大量に発生していることが原因だとわかりました。 また、以降ではプラグインの評価に際して、WebAssembly ランタイムを動かすGo 側をホスト、プラグイン側をゲストと呼称して説明します。 問題の再現に挑戦 具体的な原因がわかってきたところで、プラグインの評価を高頻度かつ大量に行うことで問題が再現するかを試し始めました。ローカルで立ち上げたサーバーに対して高負荷な状況を作ったり、開発環境にデプロイしているサーバーに対してリクエストするなどさまざまなアプローチをチーム総出で試しましたが、問題が起こった gRPC メソッドに対して 1000rps を超える負荷を 30分近くかけ続けても再現には至りませんでした。 ここで、問題を再現することが難しいことが分かったため、問題発生時に本番環境で取れる情報を増やす方向も考え始めました。具体的には対象のサービスに pprof による計測用のエンドポイントを仕込んでおき、問題が起きたときに対象の Pod のサーバーに対してプロファイリングを実行することで、 Cloud Profiler ではわからない情報をとれるようにしました。 pprof による調査 pprof を仕込んだ翌日、問題が再発しました。今回は対象の Pod を再起動するのではなく、Pod を Kubernetes Service から切り離し、切り離したサーバーの pprof のエンドポイントにリクエストを送ることで各種データを取得しました。 取得した結果を対象に、例えば go tool pprof -traces を実行すると、計測時に動いていたすべての Goroutines をスタックトレース付きで把握することができるのですが、プラグイン関連のスタックトレースを見ると、プラグイン評価に際してホストとゲストそれぞれが相手からのリクエストを待っている状態になっていました。これはありえない状況です。ホストは正しくゲストに要求を伝えたと思っているが伝わっていない、もしくはその逆の現象が発生している可能性があります。 それぞれが同時に相手からの要求を待ち受けているということは、永久にその要求は満たされません。そのため、デッドロックのような状況になってプラグイン評価が永遠に行われず、プラグイン評価は直列に動作するため、サーバーの処理が停止してしまう事態になっていることがわかりました。 しかしここから調査が難航します。そもそもなぜこのような事態になっているかがわからなかったのと、問題が起こるたびにプロファイリングをしても結果が毎回同じ内容だったため、新しい情報が得られませんでした。ローカルでも引き続き再現しません。 そこで、プラグインインスタンスに何らかの不調が起きて、突然ホストとゲスト間の疎通ができなくなると仮定し、ホストとゲスト間のメッセージのやりとりに利用するメモリの読み書き処理でエラーになった場合は、あらかじめ作っておいたバックアップインスタンスにフォールバックするような仕組みを作成しました ( https://github.com/mercari/grpc-federation/pull/327 )。もしどこかでエラーが返るようであれば、インスタンスをリフレッシュすることで復帰できるはずです。 しかし結果は期待とは異なり、エラーが発生することもなく、予兆なしに突然問題が発生してしまいました。 GC を疑い始める プラグインを評価する際、ホストからゲストへのリクエスト方法は大きく2通り存在します。 一つは認証処理の例で説明したような通常の API リクエストです。もう一つはGCの実行でした。 WebAssembly ランタイムではアロケーション戦略としてリニアメモリを採用しており、メモリが足りなくなったら倍のメモリを確保して対処します。ですが、メモリが十分でも確保した範囲を縮小することはしません。つまり、GC が走るタイミングが遅れるほど、メモリが不必要に多く確保されてしまうことになります。これを防ぐため、プラグイン側で細かく GC を実行する必要があり、gRPC Federation では定期的にホストからゲストに対して、強制的に runtime.GC() を実行するように要求する戦略をリリース初期から1年以上行ってきました。 このアーキテクチャをふまえ、本番環境でしばらく運用した際にエラーなく突然問題が起こることから、GC をトリガーした際に問題が起こるのではないかという仮説を立て、ホストからの GC 呼び出し処理をなくすことで、事態が好転するのではないかと考え始めました。仮に GC が関係なかったとしても、ホストからゲストにリクエストを投げる回数自体が減るため、問題が発生する頻度が下がるのではないかという期待もありました。 トレードオフとして、プラグイン側で GC が走る頻度が減るため OOM ( Out Of Memory ) になるリスクは上がりますが、まずはホストからゲストへの GC 要求をやめて様子を見てみることにしました。 事態が好転し始める GC の経路を削除した後、アラートが上がる頻度が目立って減りました。しかし、予想通り OOM が発生してしまったのと、頻度がゼロにはなっていないため、根本対策にもなっていません。 プロファイリングしても相変わらず同じ結果でした。 しかし、事態が今までと変わったことから、問題の切り分けのためにプラグイン側で自動で走る GC を疑ってみることにしました。プラグイン側で走る自動GCを GOGC=off 環境変数を指定することで止め、強制 GC のみで運用する方法です。このとき、より実装をシンプルにするため、今までホスト側で GC の終了を待っていなかったのを同期的に待つようにしました。補足として、今まで GC の終了を待っていなくて大丈夫なのかと疑問に思った方がいるかもしれませんが、ゲスト側で強制GCの場合を特別扱いして処理しているので非同期にすること自体は問題ありません。 この状態でしばらく運用したところ、問題が発生する時は、スタックトレースから必ず強制GCの経路になっていることがわかりました。つまり、ホストから強制GCを実行したところ、その結果が返らずに停止しているということになります。 ホスト・ゲスト間の通信方法の改善 突然 runtime.GC() から処理が返ってこなくなるとは考えにくかったので、GC が走った影響で、ホストとゲスト間の通信に利用する何かが壊れたと推測しました。そこで、両者の通信方式をよりシンプルなものに変更することで GC の影響を減らすことができるのではないかと考え、今まで利用していた STDIO を使った通信方法をやめ、ホスト関数と Go のチャネルを利用した通信方式に変更し、すべてをホスト上で行う標準的な Go のチャネルを介した処理に変更しました。 https://github.com/mercari/grpc-federation/pull/331 https://github.com/mercari/grpc-federation/pull/333 この処理は Go Conference 2025 で紹介した際のホスト・ゲスト間の通信処理を改善したものなので、興味のある方は見てみていただければと思います。 別件で、プラグイン上でネットワークソケットを扱うために導入した wasi-go というライブラリも疑い、ネットワークソケット関連の syscall 以外を wazero が提供する枯れた実装に置き換えようともしましたが、wazero がファイルシステムまわりの API を private にしているために、 wasi-go 側で作った ソケットディスクリプタ を wazero 側に伝える方法がなく断念しました。 さて、この改善を行ったことで事態がより進展すると思われましたが、問題は収束せず、プロファイリングすると、ホストとゲストそれぞれでチャネルの受信処理でブロックしているという結果になりました。ただし、いずれも強制GCの結果待ちでした。 ログを仕込む 今回の問題は本番環境で長時間運用しなければ発生しなかったため、コストの面からデバッグログを仕込むことを躊躇っていました。しかし、これ以上原因をしぼりこむことができなかったため、遂にログを追加することにしました。 あわせて Trace も追加しました 気をつけたこととして、問題が発生したらできるだけすぐにデバッグログを無効にしたかったため、環境変数を有効にしたときだけログを有効にするような Feature Flag 方式を採用し、環境変数の変更を巻き戻せばすぐに元に戻せるようにして運用しました。 ログを有効にして計測した結果、ゲストがGC 実行の要求を受け取ったというログは見つかりましたが、結果を送信するというログがなかったため、ないだろうと考えていたGC実行中にハングしている可能性が濃厚になりました。 また、ログを仕込んだ結果、ホストとゲストでどちらも受信待ちになっていたと思っていた不可思議な現象は、実は別のプラグインインスタンスだったことがわかりました。つまり、ホスト側とゲスト側のスタックトレースは、それぞれ別のインスタンスのものを表示していたのです。先に説明した通り、gRPC Federation は gRPC Service ごとに作るプラグインインスタンスは一つですが、 同一プロセス上で動く gRPC Service が複数ある場合はインスタンスも複数になります 。このことを考慮できていなかったのが悔やまれます。しかしこれで、問題がデッドロックではなくGC実行中のハングに収束したためとてもシンプルになりました。 インスタンスの自動復旧対策 気づくと10月も半ばに差し掛かっていました。振り返ると8月下旬から数えて2ヶ月近く格闘していたことになります。 ですが問題が明確になったことで、取れる策も具体的になりました。GC実行中に処理が返らなくなることから、まだその原因はわからないにせよ、ワークアラウンドを実装することはできます。強制GCにタイムアウトを設け、タイムアウトした場合はインスタンスが壊れたと判断し、前に実装していたインスタンスをバックアップに切り替える仕組みを使って自動的に復旧することができるはずです。 ついでに、インスタンスを切り替える際にログも出力してみます。無事復旧しているならばログが出るはずです。 この対応を行ってから、数日間様子を見ましたが、仕込んだログは定期的に出力されつつ、Goroutines が急増することもなくなりました。 ひとまず、ワークアラウンドはうまく動いていそうで、障害対応が落ち着いたといえる状態になりました。 このとき行った実装は こちらです 根本原因が判明 障害対応が落ち着いたので、今まで得られた情報からより再現しやすい環境で調査を続けたところ、長時間動作させる中でローカルで再現できるようになりました。依然として再現することは難しかったですが、それでも再現できたことは大きく、詳細なデバッグログを仕込んで複数回実行する中で、 ネットワークソケットを利用するために導入した wasi-go というライブラリの PollOneOff syscall 内で呼び出している unix.Poll から処理が返ってこなくなることがわかりました。デバッグログの結果から、どうやら通常は 0 になるはずの unix.Poll に渡している timeout 値が異常値(負数)になっており、永久にくることのないイベントを待ってブロッキングする挙動になっていることがわかりました。また、 PollOneOff と GC に因果関係があるかを調べるために Go 本体のソースコードを追ってみると、GC のシーケンスで PollOneOff が呼び出される経路があることもわかりました。 この問題を修正したときのPRは こちらです 。 ※ ライブラリは私が作ったもののように見えますが、実際には fork したものを使っています。 この修正を適用したあと、仕込んだインスタンス切り替え時のログは出力されなくなり、無事問題が解決しました。問題発生から2ヶ月ほどのことで、あの暑かった夏から少し肌寒くなってきていました。 反省点 あらためてプロファイリング結果を思い返すと、実は PollOneOff のスタックトレースは一番最初に計測したプロファイリング結果に含まれていました。勘がよければ、最初の時点で PollOneOff が原因だと気づくこともできたのです。 PollOneOff は epoll のようにファイルディスクリプタに対するイベントを監視する WASI 用の syscall で、スタックトレースは最初から目に入っていましたが、初期の実装ではホストとゲスト間の通信に STDIO 方式を採用していたため、通信時に利用する標準入出力のイベント待ちで呼び出されているのかと勘違いしていました。また、wazero ( WebAssembly ランタイム ) 側で特別に別スレッドでイベント待ちをしているのかとも予想しており、あまり気にしていなかったのが悔やまれます。 冷静に考えると、あとで Go のチャネルを利用した通信方式に切り替えてもスタックトレースに現れていたのはおかしいし、シングルスレッドアーキテクチャなのに別スレッドでイベント監視しているのもおかしいので、先入観で判断してはいけないと改めて気付かされました。 逆に、最初からプロファイリング結果に答えは書かれていたので、pprof が大事だとも言えます。 振り返り 本番環境でしか問題が起こらない場合、問題のある Pod を単に再起動するのではなく、サービスから切り離してプロファイリングするのは非常に重要なフローでした。 pprof は実行時にしか負荷がかからないので、本番用のビルドでも関係なく pprof の計測エンドポイントを持っておいた方がいいという気づきも得ました。 また、ちょっとしたことですが、デバッグログを無効化するために実装を巻き戻して image を作り直し、 hotfix を deploy する方法だと時間がかかってしまうので、環境変数によってデバッグログの出し分けを制御できたのは良かったです。 今回の対応では、問題のあるPodをサービスから切り離した後、pprof によるプロファイリングをメインに対応してきましたが、実はもうひとつ試したかったこととして、 Delve でのサーバープロセスへのアタッチによるデバッグがありました。しかし、いくつかの制約により実施には至らず、もし実行できていれば unix.Poll 時に timeout 値が変な値になっていることに気付いたかもしれないため、今後の課題です。 メルカリグループ全体の課題として、プロファイリング結果や Core ファイルなどを永続化する手段を標準化していないため、仮にそれらの情報をディスクに書き出したとしても、Pod が消えると情報が消えてしまう問題があります。Go 1.25 から入った Flight Recorder などを効果的に利用するためにも、解析情報を Pod の外に退避させ、デバッグ時に参照できるような仕組みを構築することが重要だと考えています。 今回修正を加えた https://github.com/goccy/wasi-go は、 https://github.com/dispatchrun/wasi-go を fork したものなのですが、fork 元のメンテナンスが停止していること、自分の方のライブラリでは今回の修正対応以外にも TLS 対応や exec.Command 対応など、実際のユースケースに応じたさまざまな改善が入っているので、これから Go が WASI P2 に対応するまでの間で WebAssembly を使いたいと思っている方には強い味方になってくれるはずです。ぜひご利用ください。 最後に、今回 Claude Code にプロファイリング結果の分析などの壁打ち相手になってもらいましたが、AI は答えを出そうとするあまり誤った答えを平気で出してくるので、嘘を嘘だと見抜く力が必要だと強く感じました。 明日の記事は fenomasさんです。引き続きお楽しみください。
Merpay KYC チームの Sakabe です。 この記事は、 Merpay & Mercoin Advent Calendar 2025 の6日目の記事です。 2025年11月14日〜15日に開催された「 YAPC::Fukuoka 2025 」へメルカリグループはスポンサーとして参加していました。今回はその参加レポートをお届けします! YAPC::Fukuoka 2025 について YAPC は Yet Another Perl Conference の略で、Perl を軸にしながらも、今では Web・インフラ・AI・SRE・フロントエンドなど幅広い技術者が集まる技術カンファレンスです。 今回のテーマは「きゅう」。探”求” や ”急”激な成長体験など、”九”州開催の YAPC らしいキーワードでした。 開催概要 日程: 2日開催 2025年11月14日(金) 2025年11月15日(土) 会場: 福岡工業大学 会場入口には YAPC の装飾が並び、到着した瞬間からイベントの熱気に包まれました。 福岡工業大学は駅を降りるとすぐに広いグラウンドが目に入り、学生の活気も合わさって “技術のお祭り” らしい雰囲気が一気に高まります。 Mercari メンバーの登壇 今回は Mercari から 「 iPhone のマイナンバーカードを使った本人確認の実装 」と題しeKYC に関するトークが採択されました。 iPhone のマイナンバーカードを使った本人確認の機能について、Verifier における技術的な観点や開発経緯などをkgoroさんに話していただきました。 最新の eKYC 手法ということもあり質問が非常に多く、Q&A 後も聴講者が集まって議論が続くほどの盛り上がりでした。 現地ならではの熱量を強く感じたセッションでした。 発表資料はこちらをご確認ください。 セッションの見どころ DBからエンジニアリング、AI まで幅広い発表があり、個人的に特に印象に残ったのはこちらです。 今、MySQLのバックアップを作り直すとしたら何がどう良いのかを考える旅 競馬で学ぶ機械学習の基本と実践 旧から新へ: 大規模ウェブクローラのPerlからGoへの置き換え YAPC の特徴として、特定の技術に閉じすぎず、技術と向き合う姿勢を語るセッションが多いのも魅力でした。 参加メンバーの感想 (kgoro) 今回は「iPhone のマイナンバーカードを使った本人確認」というテーマで採択いただき発表させていただきました。発表について多くのご質問をいただき、個人的にも iPhone のマイナンバーカードについて改めて深く考える貴重な機会になりました。 聴講した発表に関しては、「 クレジットカードの不正を防止する技術 」という発表が特に印象に残りました。こちらは普段扱う領域に少なからず関連する技術ですが、直接的に扱う機会が少なかったため、非常に学びになる内容でした。また、今後これらの技術を考えるうえでの後押しにもなったと感じています。 全体を通して、この度のカンファレンスは遠方かつトークの内容もさまざまなトピックがあったことから自分にとって新鮮な経験となりました。 (myuto) 普段は機械学習まわりの開発をしていますが、分野関係なくいろんなバックグラウンドの人が集まるイベントなので、終始ゆるく楽しく過ごせました。技術系のカンファレンスって専門領域に寄りがちな印象があったけど、YAPCは良い意味で雑多で、それがすごく心地よかったです。 最新の生成AIの動向に対しても、いかにAIを使った上で開発や制作をうまく回すかといった見解や価値観もいろいろ発表や懇親会を通して伺うことができたのが楽しかったです。 イベント全体の雰囲気が明るくて、参加者同士で気軽に話せるのが良かったです。普段あまり接点のない人たちとも技術の話で盛り上がれるのは、やっぱり現地イベントならではですね。スポンサーエリアも盛り上がっていて、各社の工夫が感じられて見て回るだけで楽しかったです。 まとめ YAPC は コミュニティ × 技術 × 熱量 が心地よく交わる、独特の魅力を持つイベントです。 福岡開催ということもあり、主催者の気合いの入り方も違いましたね。 運営・スポンサー・スピーカー・参加者のみなさん、本当にありがとうございました! 次回の YAPC も楽しみにしています! 明日の記事はgoccyさんです。引き続きお楽しみください。
こんにちは。メルカリのフロントエンドエンジニアの @mattsuu です。 この記事は、 Mercari Advent Calendar 2025 の5日目の記事です。 はじめに メルカリ ハロ Webフロントエンドの開発スピードと品質両立の取り組み という記事を公開してから約1年が経過しました。 前回の記事では、Next.js、Apollo Client、Tailwind CSS、そして monorepo 構成を採用し、MSW を使用したモック駆動開発などの技術スタックと開発手法について紹介しています。 この1年間、私たちは初回の技術選定を基盤としながら、パッケージ管理、テスト戦略、開発体験の3つの領域に焦点を当てて継続的な改善を進めてきました。 本記事ではそれらの取り組みと効果について紹介します。 パッケージ管理の改善 monorepo 環境での依存関係管理とセキュリティ対策として、pnpm catalog による一元管理、minimumReleaseAge によるサプライチェーン攻撃対策、Knip による未使用パッケージの検出を導入しました。これらにより、依存関係の更新作業の効率化とセキュリティリスクの低減を実現しています。 pnpm catalog による一元管理 monorepo 環境では、同じライブラリの異なるバージョンを管理する場合があります。バージョンによって I/F が異なったり、セキュリティ update の対応が複数箇所で必要になるなど、対応コストが発生します。これを防ぐために各ライブラリで単一のバージョンを利用する方針を採用しました。 pnpm の catalog 機能を使うことで、依存関係のバージョンを一箇所で管理することができます。 # pnpm-workspace.yaml catalog: '@apollo/client': 3.13.4 'next': 15.3.2 'react': 19.0.0 # ... その他の依存関係 pnpm-workspace.yaml にカタログを定義し、各パッケージの package.json で catalog: を参照する運用により、同一ライブラリのバージョン分散を防いでいます。 { "dependencies": { "@apollo/client": "catalog:", "next": "catalog:", "react": "catalog:" } } この仕組みにより、依存関係のバージョン更新は catalog 側を更新するだけで monorepo 全体へ反映されるため、個別のパッケージに対して複数 PR が発生する問題を解消できました。 導入手順については次の記事を参考にしました。 monorepo内でのパッケージのバージョンを1つだけに統一するOne Version Ruleをpnpm catalogで実装する Dependabot の活用 Dependabot を catalog と組み合わせることで、依存関係更新のワークフローが改善しました。Dependabot が catalog のバージョンを更新する PR を作成し、それをマージするだけで monorepo の依存関係を一括更新できます。 運用面では導入して間もなかったこともあり、ナレッジ共有も踏まえて週1で担当をローテーションしていました。CI で自動テストが通り、PR に対して E2E テスト手動でトリガーして通過したらマージするようにしています。major update など破壊的変更があるものは別途チケットを切って対応していました。 また、Dependabot の cooldown 機能 を利用して、既存ライブラリの更新に一定の待機期間を設けていました。これにより、公開直後の不具合やセキュリティリスクを回避しやすくなりました。 minimumReleaseAge によるセキュリティ対策 前述の Dependabot の cooldown 機能は既存のライブラリアップデートに対するセキュリティ対策ですが、手元で新規ライブラリのインストールやアップデートを行う際には pnpm の minimumReleaseAge が役立ちます。 # pnpm-workspace.yaml minimumReleaseAge: 10080 # 1 week in minutes minimumReleaseAgeExclude: - '@example/*' この設定により、公開されてから一定期間経過していないパッケージはインストールされません。 社内パッケージは minimumReleaseAgeExclude で除外し、開発の妨げにならないようにしています。また未導入ではありますが、pnpm の trustPolicy オプションを有効にすると、 サプライチェーン攻撃への対策 をさらに強化できます。 Knip による未使用パッケージの検出 プロジェクトが成長する中で、使われなくなった依存関係が残り続ける問題がありました。未使用のパッケージはバンドルサイズの増加やセキュリティリスクの原因になります。 Knip を導入することで、未使用のパッケージを自動検出できるようになりました。 CI では knip-reporter を利用していますが、現在は builtin の reporter が実装された ため、そちらを使うと良いかもしれません。また、Knip は未使用のファイルや、export しているがどこからも import されていない型や関数も検出できるため、コードベースを健全に保つ上で効果的です。 詳しくは次の記事が参考になります。 TypeScript/JavaScriptの不要なコードを削除するツール「Knip」の紹介 – ベースマキナ エンジニアブログ テスト基盤の整備 テスト戦略として、全画面への Playwright による Integration Test の導入、Component Test の最小化、VRT の活用を進めました。Code Coverage よりも Use Case Coverage を重視し、テストの種類を絞ることでメンテナンスコストを削減しながら、安定したテスト基盤を整えることができました。 Playwright を用いた Integration Test と VRT の導入 1年前の時点では Integration Test の基盤が十分に整っておらず、軽微な機能改修でも QA エンジニアに詳細な確認を依頼する必要がありました。また、機能自体に問題がなくとも UI のリグレッションが発生した際に気づきにくい体制でした。 この課題を解消するため、画面全体を対象とした Playwright による Integration Test を導入し、UI の差分検出には Playwright の VRT を活用しました。 Integration Test の方針は、メルペイでのテスト自動化方針を参考に次の考えをベースとしています。 基本的に、仕様書をベースにアプリケーションの振る舞いが期待通りかをテストします。実装の詳細は考慮しません。インテグレーションテストが仕様書と対応し、テストコードを見るとアプリケーション挙動がわかるように管理されると理想的です。 参考: メルペイフロントエンドのテスト自動化方針 仕様書とテストコードを完全に一致させることは難しいものの、基本方針として Code Coverage よりも Use Case Coverage を重視する姿勢で運用していました。 テスト構成 テストコードの大枠とトップレベルの describe について次のような構成にしています。 /** * Specs: * https://example.com/specs/feature-abc // ① */ test.beforeEach(async ({ page }) => { await mockLogin(page); await mockAllGraphQL(page); // ② }); // 画面のパス const url = '/sample-page'; test.describe('Rendering', () => { test('renders correctly', async ({ page }) => { await mockGraphQL( // ③ page, SampleQueryDocument, produce(mockSampleQueryResponse(), (draft) => { // ... 必要に応じてレスポンスを調整 }) ); await page.goto(url); // ④ // ... レンダリング確認 }); }); test.describe('Validation', () => {}); test.describe('Actions', () => {}); ① 仕様へのリンクを記述する ② mockAllGraphQL を利用してすべての API リクエストをモックする ③ 個別ケースは mockGraphQL でレスポンスを上書きする トップレベルの beforeEach 内で mockGraphQL を利用することは避け、対象のテストと近い場所で呼び出す 多少冗長でもモック内容とテスト内容の関連が明確になることを優先する ④ モック設定完了後に page.goto を実行し、テスト対象の画面に遷移する トップレベルの describe については次の3つに分類しています。 Rendering : レスポンスに基づいて情報を正しくレンダリングできるか Validation : 無効なリクエストが送信されるのを防げるか Actions : 利用者の操作に応じて、正しいリクエストを生成し、レスポンスを適切に処理できるか(更新、エラー、ページ遷移を含む) 記述ルールを明確にしたことで一貫した実装が可能になり、monorepo で複数メンバーがそれぞれのアプリケーションや機能を担当する場合でも、テストを書く負荷を減らすことができました。また、これらのルールを Claude.md にまとめておくことで、AI によるセルフレビューやテスト生成の精度も向上し、開発体験の改善につながりました。 Component Test 最小化戦略 テストの種類とバランスを考えたとき、私たちは次の方針を採用しました。 Integration Test (Playwright) : ブラウザ上で実際のユースケースを検証し、全画面をカバーする Component Test (React Testing Library) : DatePicker など複雑な UI コンポーネントのみに限定する Unit Test (Jest) : ロジック部分を対象にカバレッジ 80% 程度を維持する テストの種類を絞ることで、チームメンバーが学ぶべき範囲を限定し、テストの重複排除などメンテナンスを削減することができました。 Integration Test に寄せた構成では CI のパフォーマンスが気になるところですが、 Playwright の sharding で4並列にテストを実行させることで実行時間が10分程度に抑えられるようにしています。 また、Playwright にはテスト計画生成・テスト生成・自動修復を行う Test Agents が用意されており、将来的にテスト運用をより効率化できる可能性があります。QA チームが管理する E2E テストでも Playwright を採用していたため、チーム間でツールが統一されている点もメリットでした。 VRT (Visual Regression Testing) の活用 モバイル向け Web UI は事業者向け管理画面と比べて画面あたりの情報量が少なく、UI の変化を見つけやすいという特徴があります。また、UI 検知に必要なモックも少なくて済むため、実装やメンテナンスの負担を抑えられます。そのため、モバイル向け UI では Rendering のテストを VRT 中心にし、事業者向け管理画面では text や element の assertion を用いる構成にしていました。VRT には Playwright の screenshot 機能 を利用して全画面実装しました。 test('should render page correctly', { tag: '@vrt' }, async ({ page }) => { await mockGraphQL( page, ExampleDocument, produce(mockExampleResponse(), (draft) => { // UI の確認に使うデータは immer で上書きする draft.data.item.name = 'テストアイテム1'; }) ); await page.goto('/example'); await expect(page).toHaveScreenshot({ fullPage: true }); }); これまでは開発環境でデザイナーに UI を直接確認してもらう必要がありましたが、VRT の導入により特定条件の UI を Snapshot で比較・確認できるようになり、確認作業の工数を削減できました。 開発体験の向上 開発効率と品質を向上させるため、ESLint カスタムルールによる品質担保、Mock の自動生成、Claude Code Action によるセルフレビュー、Apollo MCP によるテストデータ作成の効率化といった取り組みを行いました。これらにより、レビュー負荷の軽減や開発スピードの向上につながりました。 ESLint カスタムルールによる品質担保 実装漏れが原因で QA 段階で不具合が見つかった場合には、修正と合わせて ESLint のカスタムルールを追加していました。チーム固有のルールをドキュメントや Claude.md / Agents.md にまとめる方法もありますが、静的解析によってルールを強制するほうがヒューマンエラーを防ぎやすく、品質担保に効果的です。 カスタムルールの追加には、次の記事が参考になります。 no-restricted-syntax でお手軽にカスタムルールを追加する – Zenn Ubie における ESLint 活用 – Zenn Mock 自動生成の仕組み GraphQL スキーマが更新されるたびに手動で Mock データを更新するのは負担が大きいため、 @graphql-tools/mock を利用した Mock データの自動生成を導入しました。スキーマが更新されたら、pnpm でスクリプトを実行するだけで Mock データを更新できます。 # GraphQL スキーマから型と Mock データを生成 pnpm generate:graphql GraphQL Code Generator のカスタムプラグインを作成し、 @graphql-tools/mock の addMocksToSchema を使って Mock データを生成しています。 import { addMocksToSchema } from '@graphql-tools/mock'; const mockedSchema = addMocksToSchema({ schema: graphqlSchema, mocks: { // 型に応じて初期値を設定 ID: () => 'mock-id-0001', String: () => 'Hello World', Int: () => 10, Boolean: () => true, Time: () => '2021-01-01T00:00:00.000Z', }, }); // スキーマに対してクエリを実行し、Mock データを生成 const result = executeSync({ schema: mockedSchema, document: queryDocument, }); この result を使って、次のように mock 関数を自動生成します: const mockFn = ` export function mockExampleResponse(): ExecutionResult<ExampleQuery> { return ${JSON.stringify(result, null, 2)}; } `; この仕組みにより、GraphQL スキーマから適切な Mock データが自動生成されるため、手動で値の整合性を確認する手間が大きく削減されました。 また、特定のケースを再現したい場合は、immer を使って必要な部分のみ上書きできます。 import { produce } from 'immer'; import { mockExampleResponse } from 'example-mockdata/base/gen/query'; const customMock = produce(mockExampleResponse(), (draft) => { // 特定の条件を再現するためにデータを上書き draft.data.items[0].status = 'active'; draft.data.items[0].count = 10; }); このように、意図的に上書きした箇所がそのまま Storybook やテストで確認したい条件として明確に残るため、Mock の管理や UI・テスト確認がシンプルになりました。 Claude Code Action によるセルフレビュー claude-code-action を導入することで、PR レビューを依頼する前の draft の段階で実装者自身が抜け漏れに気づけるようになりました。レビュワーが指摘する前に基本的なミスを修正できるため、レビュワーの負担削減や品質の向上につながりました。 Apollo MCP によるテストデータ作成の効率化 ローカルで開発する際は、API が未実装の場合を除き、基本的にバックエンドと実際に疎通しながら進めていました。モックで開発する手もありますが、実際の API と通信するほうが挙動の再現性や信頼性が高いためです。一方で、特定の条件を再現するためのテストデータ作成には課題がありました。開発環境の管理画面を操作したり、場合によっては DB を直接触る必要があり、準備に手間がかかりがちでした。 そこで Apollo MCP を導入し、テストデータを口語ベースで簡単に作成できるようにしました。Apollo MCP は GraphQL のスキーマやクエリを操作して実行できるため、次のようなことが可能になります。 「今日の18:00 ~ 20:00 で時給1200円の募集を北海道で作って」のような指示をすると、該当する mutation を実行しテストデータを作成 GraphQL スキーマを自動で読み取り、どんなクエリや mutation が使えるかをで一覧・検索 特定のクエリに必要な引数を口語形式で検索 これにより、ローカルやテスト環境でのデータ準備が大幅に効率化され、手動テストの工数削減や開発スピードの向上につながりました。 おわりに この1年間、日々の開発の中で出てきた課題に向き合いながら、少しずつ仕組みを整えてきました。All for One のカルチャーのもと、メンバーがそれぞれの専門性を活かしつつ、知見を共有しながら開発できたことが改善を進める上で良い循環になっていたと思います。本記事で紹介した取り組みが参考になれば幸いです。 明日の記事は sathiya さんです。引き続きお楽しみください。
はじめに こんにちは。 @Sakamoto です。 この記事は、 Merpay & Mercoin Advent Calendar 2025 の4日目の記事です。 2025年9月からメルコインでフロントエンドのインターンを始め、12月初めでちょうど3か月になりました。期間中はメルコインに加え、メルカリNFTの開発にも参加しました。 本記事では、インターン期間中に取り組んだタスクを振り返り、そこで得た学びや気づきをまとめます。 チームについて 今回のインターンではメルコインとメルカリNFTでフロントエンドエンジニアとして参加しました。それぞれ扱うプロダクトの特性が大きく異なるため、求められる視点や進め方にも違いがありました。 メルコイン 暗号資産交換業としてガバナンスやリーガルと関わりが深く、単に機能を実装するだけでなく、背景となる法律や制約を理解したうえで開発を進める必要があります。 メルコインのフロントエンドチームはお客さま対応のための社内ツールのフロントエンド開発、LP(ランディングページ) の作成などを担当しています。 メルカリNFT NFTの売買を可能にするプロダクトを持つチームで、2025年1月に提供を開始し、現在も毎週のように新機能が追加されています。 そのため、よりスピード感のある開発が求められる環境でした。 メルカリNFTのフロントエンドチームは主にメルカリNFTのフロントエンド開発を担当しています。 取り組み概要 メルコイン 社内ツールのフロントエンド改修 メルカリNFT 買取機能の実装・改善 メルカリNFTのくじをメルカリアプリ内から購入できるようにするための対応 リリース後のメモリ監視 その他 Web3領域のドメイン知識の習得 社内の多職種の方とのコミュニケーション エンジニア業務でのAI活用 ここでは太字の6つについてまとめようと思います。 社内ツールのフロントエンドの改修 背景 メルコインで進行しているプロジェクトの一部として、口座の状況、暗号通貨の配信価格など、お客さまからのお問い合わせに必要な情報を確認できる社内ツールの改修を担当し、フロントエンドの仕様整理から実装、QAチームとのテストケースの調整まで、一連の開発プロセスに関わりました。 やったこと 社内ツールの改修にあたって、 表記揺れがないか バリデーションに過不足はないか エラーハンドリングが適切か UIの整合性があるか API連携が正しいか 変更に柔軟に対応できる設計か といったポイントを確認し、後工程での手戻りが起きにくい状態まで仕様書のレビューを行いました。 その上で自分の実装スピードや改修範囲を考慮し、適切なバッファを含めて工数見積もりを行いました。 実装では、各マイクロサービス間の通信にはgRPCが使われているため、Protocol Buffersの定義からモックを作り、UIとの接続を進めると同時にCypressでのE2Eテストも作成・改修に取り組むことができました。 またQAチームとはテストケースや指摘内容を確認しながら、仕様と実装の齟齬をなくすことに努めました。 学び gRPCやProtocol Buffers、Cypressといったこれまで触れる機会の少なかった技術に取り組んだことで、フロントエンド以外の領域にも一部理解が広がりました。 あわせて、仕様書の精度をどう高めるか、どの程度のバッファを見込んで工数を組み立てるか、レビューされやすいコードにするにはどんな書き方がよいかなど、プロダクト開発全体を俯瞰する視点も身についたと感じています。 買取機能の実装・改善 背景 メルカリNFT内のGMV(流通取引総額)を伸ばすための施策として、NFTくじを購入したお客さまが買取依頼を出せる機能の追加をしました。 それに伴い、商品詳細ページに新しいUIや状態管理が必要となり、この部分の改修を担当しました。 また、このタスクがメルカリNFTのコードを触る最初の機能開発だったため、既存コードや周辺仕様のキャッチアップを素早く進める必要もありました。 やったこと 仕様書を確認し、商品詳細ページのどのタイミングで買取ボタンを表示するか、どの状態をどう見せるかといったUIの出し分けを整理しました。 その上で、実装した内容の品質を担保するためにVRT(Visual Regression Test)を活用し、Playwrightを用いたインテグレーションテストも追加・修正しました。 学び Playwrightを使ったインテグレーションテストやVRTの活用など、これまで触れる機会のなかったテスト手法を実際に使いながら理解を深められました。 また、買取前後でUIが複雑に変化する仕様だったこともあり、コンポーネントの責務をどう分けるか、保守性と可読性をどこまで両立できるか、といった設計面の学びも多かったです。 スピードが求められる環境の中でも、既存実装の調査や理解を並行して進めることの重要性を実感しました。 リリース後のメモリ監視 背景 メルカリNFTでは、サービスを安定して提供し続けるために、リリース後のリソース状況の監視や挙動の確認を継続的に行っています。 特にメモリ使用量は、トラフィックの増減や特定機能の利用状況によって変動しやすいため、定期的に観測し、必要に応じて挙動を調査する運用を行っています。 今回の取り組みでは、本番環境のメモリ使用状況をより細かく把握し、どのタイミングでどう増減しているのかを整理しながら、今後の安定した運用につなげるための調査を進めています。 やったこと まずは「いつ・どんな状況でメモリが増えるのか」を把握するために、各Podのメトリクスを確認し、傾向を調査しました。 その上で、なぜそのような増加が起こるのか仮説を立て、順に検証を進めました。 Datadogからログやメトリクスを細かく確認しつつ、他チームにも相談し、Node.jsやNext.jsの内部挙動、キャッシュやSSR周りの可能性など、さまざまな観点から情報を集めることで検証の方向性を調整しています。 学び 日々の調査の中で得られる学びは非常に多いと感じています。 Node.jsやNext.jsが内部でどのようにメモリを使うのか、CPU・GCの仕組み、キャッシュの扱いなど、技術的な理解が大きく深まりました。 同時に、進行中の問題を扱う際のコミュニケーションの取り方や調査内容のまとめ方、仮説の立て方と検証の進め方、進捗が見えにくいタスクをどうやって周囲と共有するかなど、機能開発とは異なるスキルも求められることを実感しています。 Web3領域のドメイン知識の習得 メルコインとメルカリNFTの両方に関わる中で、Web3に関する基礎知識を継続的に身につけることを意識してきました。 暗号資産やNFTの基本概念をはじめ、法規制やコンプライアンス、チェーンの仕組み、トランザクションの流れ、ウォレットの挙動など、業務に必要な知識は社内ドキュメントとして多く整理されています。 日々の開発と並行して、それらの資料を自分から探して読み、背景となるドメイン理解を深めるようにしてきました。 Web3は学ぶ範囲も広く、継続して取り組みたい領域です。 社内の多職種の方とのコミュニケーション もう一つ意識的に取り組んでいたのが、社内の多職種の方とのコミュニケーションです。 プロダクト開発では、さまざまな職種と連携しながら仕様の確定やリリースに向けた調整を進める必要があります。 そのため、日々のやり取りだけでなく、会社の施策として実施されているチービルやメンターランチの機会を活用しつつ、自分からも声をかけて1on1やランチの機会をつくるようにしていました。 チーム全体が話しかけやすい雰囲気を持っていたこともあり、積極的にコミュニケーションを取ることができました。 また、普段どのように技術をキャッチアップしているのか、どんな観点で仕様を見ているのか、なぜ今のキャリアを選んだのかといった話を聞くことで、自分の知らなかった考え方や知識に触れる機会も多くありました。 開発スキルだけでは得られない学びが多く、視野を広げるきっかけになったと感じています。 エンジニア業務でのAI活用 CursorなどのAIツールも積極的に活用し、コード全体の構造を素早く把握したり、関連部分の洗い出しを効率よく進めるようにしていました。 開発・実装では、メンターの方からのアドバイスも参考にしながら、どの情報を渡すと意図が正確に伝わるかといった点を意識するようにしました。 仕様を明確にまとめてコンテキストとしてAIに渡すことで、実装の方向性がぶれにくくなり、提案されるコードの品質も安定するようになりました。 また、AIから返ってくるコードはそのまま使うのではなく、自分の考えとの違いを確認したり、なぜその書き方になるのかを読み解くことで理解を深めるきっかけにもなりました。 結果として、実装を効率化するだけでなく、コードの設計方針を比較しながら学べる良い教材のような存在にもなっていたと思います。 まとめ 3か月を通して、メルコインとメルカリNFTのそれぞれ異なる特徴を持つプロダクトの開発に携わり、多くの学びを得ることができました。 機能実装だけでなく、仕様の詰め方、テストの考え方、多職種とのコミュニケーションなど、エンジニアとして必要なスキルを幅広く経験できた期間だったと感じています。 残りの1か月も、これまでの学びを活かしながら、より深い理解と確かなアウトプットを積み重ねていきたいと思います。 明日の記事は @Fabさんです。引き続きお楽しみください。
はじめに こんにちは。メルペイのGrowth PlatformでML Engineerをしている @hokao です。 この記事は、 Merpay & Mercoin Advent Calendar 2025 の3日目の記事です。 背景 Growth Platformでは、新たにGrowth MLというMLチームを立ち上げました。私は主にMLOpsを担当しつつ、チームの生産性向上に向けた基盤づくりにも取り組んでいます。 Growth MLチームでは、 Kubeflow Pipelines(KFP) でMLバッチ処理を実装し、 Vertex AI Pipelines 上で運用しています。KFPは、Kubernetes上でコンテナを使ってMLワークフローを構築・実行するためのプラットフォームであり、Vertex AI PipelinesはそれらをGoogle Cloud上でサーバーレスに運用・管理できるサービスです。KFPには Python SDK が用意されており、パイプラインをPythonコードとして記述できます。 KFPはPythonでMLパイプラインを実装できる一方で、コンポーネントの設計・実装の自由度が高く、適切に使いこなすのは容易ではありません。そこで、メンテナンス性と可読性を高め、チームの開発生産性を向上させるために、KFPを用いた実装パターンを整備しました。本稿では、その概要とポイントを紹介します。 KFPのコンポーネント実装パターン コンポーネントはパイプラインを構築する基本的な構成要素です。執筆時点では、コンポーネントの実装方法として次の3パターンが提供されています。 Lightweight Python Components Containerized Python Components Container Components Lightweight Python Components Lightweight Python Componentsは最も手早く実装できますが、関数スコープに完全に閉じた実装が求められるという制約があります。実装例は次のとおりです。 from kfp import dsl @dsl.component( base_image='python:3.14', packages_to_install=['numpy==2.3.0'], ) def sin(val: float = 3.14) -> float: import numpy as np return np.sin(val).item() packages_to_install で指定したライブラリはパイプライン実行時にインストールされ、その後に関数のコードが実行されます。依存ライブラリは関数ごとに管理する必要があり、MLバッチ全体で用いる特定ライブラリのバージョンを統一することが困難です。また、ライブラリのインポートや使用するシンボルの定義を関数内に閉じ込める必要があるため、関数の責務が容易に肥大化し、単体テストの記述も難しくなります。さらに、KFPが担うコンポーネント定義などのパイプラインのオーケストレーションと、ドメイン固有のビジネスロジックが密結合になりやすく、コードの見通しも損なわれます。これらの特性から、プロダクション用途には向きません。 Containerized Python Components Containerized Python Componentsは前述のLightweight Python Componentsに近い書き方ですが、関数スコープに閉じるという制約が一部緩和されます。コードと依存関係を含むコンテナイメージを事前にビルドすることで、関数外で定義したシンボルも参照できます。 しかし、関数ごとに依存ライブラリを定義する必要がある点は同じです。さらに、KFPのCLIが特定のディレクトリ構造に依存して自動生成する runtime-requirements.txt と Dockerfile を前提とするため、柔軟性に欠けます。したがって、これらの制約を踏まえた上で利用する必要があります。 Container Components 3つ目のContainer Componentsは、 docker run のようにイメージ、コマンド、引数を指定してコンポーネントを定義する方式です。 from kfp import dsl @dsl.container_component def say_hello(name: str) -> dsl.ContainerSpec: return dsl.ContainerSpec( image='gcr.io/my-project/my-base-image:latest', command=['python', 'main.py'], args=['--name', name], ) パイプラインで用いるコードとライブラリを含んだイメージを用意する必要がありますが、依存関係の管理がはるかに容易になります。実行時にライブラリをインストールする必要もありません。また、実行するPythonスクリプトはKFP特有の制約を受けない通常のスクリプトとして実装できるため、テストも書きやすくなります。 こうした理由から、Growth MLチームでは原則としてContainer Componentsでコンポーネントを実装することとしました。 Container ComponentsでのCLI実装とその課題 Container Componentsで実行コマンドを指定するため、処理ロジックはPythonのCLIとして実装するのが自然です。CLIコマンドは docker run のように実行されるため、引数としてリストや辞書などの複雑な引数を渡すときは注意が必要です。 たとえば、次のようなパイプラインを用意したとします。実行するコンポーネントは1つだけで、CLI引数としてPythonのリストを渡します。 from kfp import dsl, local @dsl.container_component def argparse_component( list_var: list, # KFP does not support the `list[str]` type annotation. ) -> dsl.ContainerSpec: return dsl.ContainerSpec( image='kfp-demo:latest', command=['python', '-m', 'cli.argparse_cli'], args=['--list-var', list_var], ) @dsl.pipeline def pipeline() -> None: argparse_component(list_var=['a', 'b', 'c']) def main() -> None: # Using local Docker runner for demonstration local.init(runner=local.DockerRunner()) pipeline() if __name__ == '__main__': main() 実行するCLIは次のとおりです。標準ライブラリの argparse で実装しており、リストを受け取り、 print 関数でそのまま出力するだけのシンプルなものです。 import argparse def main() -> None: parser = argparse.ArgumentParser() parser.add_argument('--list-var', nargs='+', required=True) args = parser.parse_args() print(args.list_var) if __name__ == '__main__': main() CLI引数として渡しているのは ['a', 'b', 'c'] なので、 print の結果も ['a', 'b', 'c'] となってほしいところです。しかし実際には、 ['["a", "b", "c"]'] と出力されます。 Container Componentsはほぼ docker run と同じ仕組みで動作するため、引数を受け渡す際に変数の値がシリアライズ(文字列化)されます。そのため、実際の docker run コマンドで渡される引数は文字列 '["a", "b", "c"]' になります。また、先ほどの argparse の実装では、スペース区切りで渡された複数の値をリストとして扱う仕様になっています。結果として、この文字列全体が1要素として扱われ、 ['["a", "b", "c"]'] というリストになってしまいます。この挙動は、 Click や Typer など他のCLIライブラリでも同様です。 この問題を単純に解決しようとすると、ひとまず引数を文字列( str )として受け取り、CLI側でデシリアライズするという実装が考えられます。ただ、それだと本質ではない処理が増えてコードの見通しが悪くなり、なにより型の安全性が担保されません。 引数が int や bool だけで済むならシンプルで理想的ですが、Pythonだけでパイプラインを書く以上、 list や dict といった構造化データを引数にしたいケースはどうしても発生します。 Pydantic Settingsで型安全かつシンプルにCLI引数を扱う いくつかのアプローチを検討・試行した結果、 Pydantic Settings を使う方法が、これらの課題を最もシンプルに解決できることが分かりました。 Pydantic Settingsは、 Pydantic の型定義を使って環境変数や .env などから設定を型安全に読み込めるライブラリです。さらに、 SettingsConfigDict(cli_parse_args=True) を有効にすればCLI引数もPydanticモデルでパースできます。 先ほど argparse で実装したCLIをPydantic Settingsで書き直すと次のようになります。 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='list-var') def main() -> None: settings = Settings() print(settings.list_var) if __name__ == '__main__': main() Pydanticは入力値を指定した型にパースし、その出力がその型に一致することを保証します。Pydantic SettingsでCLI引数をパースする場合、 list や dict などはJSON形式をサポートしているため、KFPのContainer ComponentsでシリアライズされたCLI引数(JSON文字列)も適切にパースしてくれます。その結果、わざわざデシリアライズ処理を書く必要がなく、型安全性も自然に確保されます。 複雑な構造体もPydantic Settingsで型安全に扱う argparse など標準的なCLIライブラリでは dict などをそのまま引数型として扱えませんが、Pydantic SettingsならJSON文字列をモデルにマッピングできるので、簡単かつ型安全に扱えます。 ここでは少し応用的な例として、KFPの dsl.PipelineTaskFinalStatus を使って実行タスクの最終状態を受け取る方法を紹介します。使い方はシンプルで、コンポーネントに PipelineTaskFinalStatus の型アノテーションを付けた引数を追加するだけです。変数に自動的に値が代入されるようになっているため、コンポーネントの関数を呼び出す際にこの引数を指定する必要はありません。公式ドキュメントにはContainer Componentsでの具体例はありませんが、Pydantic Settingsを使えば問題なく扱えます。 from kfp import dsl @dsl.container_component def pipeline_task_final_status_component( base_image: str, pipeline_task_final_status: dsl.PipelineTaskFinalStatus, ) -> 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=[ 'python', '-m', 'cli.pydantic_settings_cli', ], args=[ '--pipeline-task-final-status', pipeline_task_final_status, ], ) PipelineTaskFinalStatus の変数をCLIの引数に指定すると、他の値と同様にJSON文字列としてシリアライズされてコンテナに渡されます。シリアライズ後の実体は単なるJSON文字列なので、対応するPydanticのモデルを定義しておけば、CLI側でそのまま型付きオブジェクトとして安全に扱うことができます。 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['SUCCEEDED', 'FAILED', 'CANCELLED'] class Settings(BaseSettings): model_config = SettingsConfigDict(cli_parse_args=True) pipeline_task_final_status: PipelineTaskFinalStatus = Field( alias='pipeline-task-final-status', description='Pipeline task execution status (JSON format)', ) def main() -> None: settings = Settings() print(settings.pipeline_task_final_status) if __name__ == '__main__': main() 想定と異なる形式のデータが渡された場合、Pydanticは入力時点で検証し即座にバリデーションエラーを返します。そのため、ネストした dict や list などのやや複雑な構造のデータでも、Pydantic Settingsを使えば型に沿って安全に扱えます。 Pydantic Settingsを併用することで、KFPのContainer Componentsの開発体験が大きく向上します。 おわりに 本稿では、KFPのコンポーネント実装パターンを整理し、Container ComponentsとPydantic Settingsを組み合わせることで、開発者がより簡単かつ型安全にパイプラインを構築できるアプローチを紹介しました。 他にも、パイプラインの実行スケジュールを宣言的に記述し、 terraform apply のように差分を確認・適用できる仕組みなど、運用を支えるさまざまな取り組みをしています。これらについても、また別の機会に紹介できればと思います。 今回紹介した内容が、KFPを使った開発における設計で悩んでいる方の参考になれば幸いです。 明日の記事は @Sakamoto さんです。引き続きお楽しみください。
こんにちは。メルカリのAI Securityエンジニアの @hi120ki です。 この記事は、 Mercari Advent Calendar 2025 の2日目の記事です。 メルカリでは社内でのAI/LLM活用の拡大を目指し、様々な取り組みが行われています。これらの動きを後押しするために AI Securityチーム はLLM APIへのアクセスを安全でありながら便利に提供するためのサービスLLM Key Serverを開発しました。 これによりLLM APIへのアクセス申請を受け担当者が手作業でユーザーを登録していたプロセスがLLM Key Serverによって置き換えられ、LLM APIユーザーは申請の手間なく自身の社内アカウント経由で有効期限付きのLLM APIキーを取得できるようになりました。 また、GitHub ActionsやGoogle Apps Script上でLLM APIを利用するための共通テンプレートも提供し、ローカル環境のみならずCIやクラウドやノーコードツール等の複数のサービス上でのLLM利用促進にも貢献しました。 本記事ではLLM APIにおけるセキュリティ課題・プロセスの改善・LLM Key Serverのアーキテクチャ・実装におけるポイントについて解説します。 LLM APIにおけるセキュリティ課題 現在様々なLLMが各社から展開されており、メルカリでは複数のLLMをタスクごとの要求や従業員の好みごとに使い分けています。しかし、LLMへのアクセスを提供するAPIには多くの場合APIキーが必要となっています。 主要なLLMベンダーが提供するAPIへのアクセスに使われるAPIキーは、有効期限がなく、流出しそれが検知されなかった場合、長い期間にわたって情報漏洩・経済的損失を受ける恐れがあります。さらに現在のAI/LLM活用の流れの中で、多くのAPIキーが発行されており、管理があいまいになってしまうことも懸念されています。また、複数社のLLMを利用する中でユーザー・チーム・権限管理が複雑になることが想定されます。このような複雑な管理体制は定期的なアクセス権の監査を難しくします。 もちろんAPIキーを用いずWorkload Identityおよびクラウド間連携を用いてGoogle CloudやAzure経由でLLM APIにアクセスする方法が最も安全であり、推奨される方法として社内で提示しています。しかし一方で、設定の複雑さや、様々な外部のAI/LLMプロダクトがそれらをサポートせずリリースされることもあり、多くのLLM関連ツールを評価している状況下では別の方法が求められていました。 また追加の要件として、セキュリティポリシーによって煩雑なフローを強制することはかえってポリシーの逸脱を助長しかねないため、安全性を保ちながら利便性も追求することが必要でした。 安全かつ便利なLLM APIへのアクセス提供 安全かつ便利なLLM APIへのアクセス手段を提供するため、複数のモデルを一つのAPIから利用可能にするOSSの LiteLLM と、Google WorkspaceおよびGoogle Cloudの OIDC IDトークン発行機能 を用いることにしました。 LiteLLMは様々なモデルプロバイダーから提供されるLLMを一つのAPIから利用可能にするOSSで、基本的なLLM API呼び出しの他にClaude CodeなどのCoding Agentツールにも対応しています。 また、OIDC IDトークン発行機能はGoogleのOAuthやサービスアカウントの権限を利用することでGoogleによって署名されたIDトークンを取得できるというもので、これを利用することでユーザーの身元を確認することができます。 メルカリでは Google CloudからGitHubへのアクセスを有効期限が短い認証情報へ切り替えるためのToken Server を運用していますが、LLM Key ServerはこのアーキテクチャをベースにLLMへのアクセスへと発展させたものになります。 LLM Key Serverのアーキテクチャ LLM Key Serverの認証フローは以下のようになっています。 LLM Key Serverの認証フロー まず、Claude Codeを利用したいユーザーやLLMを利用したいアプリケーションは、GoogleのAPIから自身を証明するためのOIDC IDトークンを取得します。ここではGoogle Workspaceアカウントによる認証や、Compute metadataサーバーからのサービスアカウント認証などが利用されます。 次に、取得したOIDC IDトークンをLLM Key Serverに送信すると、LLM Key Serverはトークンの署名を検証し、トークン内の情報をもとにLiteLLMへアクセスするための一時的なAPIキーを発行します。このAPIキーは有効期限が短く設定されており、ユーザーはこのキーを用いてLiteLLM経由で様々なLLMへアクセスすることができます。 ローカル環境での利用においてGoogle Workspaceアカウントによる認証を利用する際は、社内公開のCLIツールを利用することで1つのコマンドでOAuth認可フローを開始し、OIDC IDトークンの取得からLLM APIキーの取得までを行うことができます。 またAPIキーはサービスアカウントに対しては1時間という短い有効期限が設定されています。しかし、クラウド上で動作しLLMを利用するアプリケーションが長時間動作することを想定し、自動的にキーを更新する仕組みも提供しています。これはGo言語のライブラリとして整備されており、自動的にキーの更新を行い、継続してLLM APIを利用することができます。 このようにしてGoogle Workspaceアカウントによる認証やGoogle Cloud上のサービスアカウント認証を利用することで、安全にLLM APIへのアクセスを提供しつつ、有効期限付きのキーを発行することで情報漏洩リスクを低減し、さらに自動更新ライブラリを提供することで利便性も確保しました。 LLM Key Serverの利用形態の拡張 LLM Key Serverはローカル環境やクラウド上で動作するアプリケーションの利用だけでなく、様々な社内ツールやサービス上での利用も想定しています。特に以下の2つの形態での利用をサポートしています。 GitHub Actions GitHub Actions上でLLM APIを利用するための共通テンプレートを提供しています。GitHub Actions上では GitHubから提供されるOIDC IDトークン を用いてLLM Key ServerからLLM APIキーを取得し、LiteLLM経由で様々なLLMへアクセスすることができます。これにより、CI/CDパイプライン上でのLLM活用が促進されており、Claude Codeを用いたコードレビューが自動化されたりしています。 - 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}&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('Failed to retrieve OIDC token from GitHub Actions'); } const res = await fetch('https://key-server.example.com/llm-key', { method: 'GET', headers: { 'Authorization': `Bearer ${oidc_token}`, 'Content-Type': 'application/json', } }); if (res.status !== 200) { core.setFailed(`LiteLLM API Error: HTTP ${res.status}`); } const body = await res.json(); core.setSecret(body.key); core.setOutput('token', body.key); このようなGitHub Actionsのactionを用意することで、開発者が直接LLM APIキーを管理することなく、CI/CDパイプライン上で安全にLLM APIを利用できるようになりました。 Google Apps Script Google Apps Script上でもLLM APIを利用するための共通テンプレートを提供しています。Google Apps Script上では OAuth scope設定 を用いてユーザーの認証を行い、OIDC IDトークンを取得することができます。 Google Apps Scriptの設定ページから appsscript.json ファイルを表示するようにした後、以下のようにOAuth scopeを追加します。 "oauthScopes": [ "openid", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/script.external_request" ], その後、Google Apps Script上で以下のようにOIDC IDトークンを取得し、LLM Key ServerからLLM APIキーを取得し、LiteLLM経由で様々なLLMへアクセスすることができます。 function getLLMToken() { try { const cache = CacheService.getUserCache(); const cacheKey = "llm_token"; const cachedToken = cache.get(cacheKey); if (cachedToken) { return cachedToken; } console.log("[+] Fetching new LLM token"); const token = ScriptApp.getIdentityToken(); const options = { method: "GET", headers: { Authorization: "Bearer " + token, }, }; const response = UrlFetchApp.fetch( "https://key-server.example.com/llm-key", 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("Key not found in response"); } cache.put(cacheKey, responseData.key, 50 * 60); // Cache for 50 minutes return responseData.key; } catch (e) { console.error("Error getting LLM token: " + e.toString()); return null; } } OIDCのIDトークンを検証する際には、ユーザーのメールアドレスだけでなく、Apps Scriptのバックエンドとして動作しているGoogle Cloudプロジェクトが、Google Cloud組織内の system-gsuite/apps-script フォルダに所属しているかどうかも確認しています。 これにより、信頼できるスクリプトからのアクセスのみを許可するようにしています。 これによりノーコードツール上での平文でのLLM APIキーの管理を避け、安全にLLM APIを利用することができます。 この仕組みにより社内でのLLM活用が促進されており、社内ドキュメントの要約や翻訳などに利用されています。 さいごに 組織としてLLM APIキーを安全に扱うための仕組みとしてLLM Key Serverを開発しました。これにより安全性と利便性の両立を実現し、社内でのLLM活用をポジティブにSecurityチームから促進することができました。今後もAI Securityチームは安全で便利なAI/LLM活用のための取り組みを続けていきます。 このようなメルカリでのAI/LLM活用やセキュリティに関する取り組みに興味がある方は、ぜひ メルカリの採用ページ をご覧ください。 明日の記事は@Jazzさんです。引き続きお楽しみください。
こんにちは。メルペイ VPoE室マネージャーの @nnaakkaaii です。 この記事は、 Merpay & Mercoin Advent Calendar 2025 の2日目の記事です。 はじめに この記事では、メルカリで2025年7月からスタートした取り組みであるProject Double(以下、pj-double)についてご紹介します。 「AIと協業する開発体験」「開発プロセスの再設計」「AI‑Nativeな組織づくり」に関心のあるエンジニア・PM・マネージャーの方にとって何かしらの参考になればと思い、本記事を執筆しました。 pj-doubleは、プロダクト開発における生産性を2倍に向上させることをミッションに掲げ、開発体制・プロセスをAI-Nativeに再設計する挑戦です。しかしその背景には、私たちがAIの導入を試みる中で経験した数々の試行錯誤と、そこから得た気づきがありました。 まずは、その前段として社内におけるAI活用の始まりと、初期フェーズでの課題から振り返ってみたいと思います。 2025年初頭: Vibe Codingの台頭と課題 2025年初頭は、AIを活用したコーディング手法の黎明期でした。社内でもさまざまなAIツールの活用、MCPサーバーの整備などがボトムアップに推進されました。それぞれの開発者が多種多様な方法論を模索する、いわゆる “Divergence” のフェーズでした。 このAI活用の初期段階において、私たちは大きな可能性を確認しました。設計・実装・テスト環境上の動作確認までを全てAIで行い、開発工数の見積もりに対して5分の1まで効率化を実現した事例も生まれました。 このようにAIを活用した開発手法が、一部の開発者の生産性を格段に向上した一方で、AIを使いこなしている開発者とそうでない開発者の間の溝も顕在化し始めていました。当時のAIを活用した開発手法の模索は、個人ごとに異なる個別最適化の域を出ず、組織全体としての生産性を向上するための組織的ムーブメントには至っていないという課題がありました。 実際に、こうした開発手法に再現性を与え、組織全体で共有知化するための試みもいくつか立ち上がったものの、局所的なプラクティスの共有・CLAUDE.mdやCursor Rulesの共有知化などに留まっていました。その理由として、当時主流であったVibe Codingに構造的な要因があったと振り返っています。Vibe Codingは、開発の状態に合わせてAIに都度指示を送って、その出力を都度確認しながら方向修正を行い、AIと並走しながら実装の完了まで導くような開発手法です。この同期的でインタラクティブなプロセスの性質上、開発者が直感的・経験的に行なっている状況判断の質が生産性を大きく左右します。このように、個々の状況判断はAIとの対話ログに閉じてしまって透明性が低く、また場面ごとの判断根拠を事後的に説明することも難しいため再利用性も低かったことが、再現可能な共有知に昇華する上での決定的な課題となっていました。 またベイエリアの新興企業を中心として、AIを前提とした開発プロセスや開発体制によって、数十人でユニコーン級のサービスを作る事例が知られるようになりました。このようなAI-Nativeな組織では、開発のプロセスも、そこにおける1人1人の役割や職能も、既存のそれらとは異なる形を取ると思います。社内においても、一部の開発者は「開発体験そのものが根底から変わる実感」を覚えていたものの、ボトムアップなAI-Native化の推進体制では、既存の開発プロセスや体制の上での局所最適なAI活用に留まってしまうという限界が見えていました。AIがない時代に最適化された開発プロセスや開発体制を、AI-Nativeな開発プロセスや開発体制へと変革することが、プロダクトデリバリーにおける持続的な競争力を有するための至上命題でした。 2025年7月: pj-doubleの発足 このような背景を受けて、AI-Nativeな開発プロセス・開発体制へのシフトを本格的に推進させるため、2025年の7月頃にメルペイのVP of Engineering Officeにて “pj-double” が発足されました。これは、さまざまな手法を模索して個人の個別最適化を達成する “Divergence” のフェーズから、全社として高生産性を再現度高く実現可能な開発プロセスのスタンダードを確立する “Convergence” のフェーズへの移行のスタートでした。私たちはこのスタンダードの確立により、社内のあらゆる知見やフィードバックがそこに集積され、持続的に組織として共有知を育てていけるような体制を実現することを目指しました。 時期を同じくして、技術環境のトレンドも変化しました。Prompt EngineeringからContext Engineeringへ、そしてVibe CodingからAgentic Codingへの変化です。Prompt Engineeringのもとでは「いかに良いプロンプトを与えるか」が重要でしたが、Context Engineeringのもとでは「いかに多くのデータソースと繋いで、いかに必要な一次情報を直接インプットするか」が重要視されるようになりました。また、同期的かつインタラクティブにAIを導く必要があったVibe Codingに対して、Agentic Codingでは、「最初に目的・ステップ・完了条件さえ与えれば、Agentが自律的にタスクを完遂するため、開発者はその結果のみを評価すれば良い」という非同期的かつスケーラブルな開発体験を可能にしました。 このようにAI活用は、個人のアートから、再現性のあるサイエンスへと変化し、pj-doubleが掲げる再現可能なAI-Native開発への挑戦を後押しする鍵となりました。 2025年7-9月: AIを活用した開発手法の効果の実証 pj-doubleではこの開発手法のスタンダードを確立することを、最初の3ヶ月の目標に置きました。メルペイやメルカリモバイルにおける30以上のバックエンド開発のプロジェクトと連携を開始し、社内で蓄積されたさまざまな開発手法に関する知見を集積することから始めました。 この連携を通して、「どのようなプロジェクトのどのような開発フェーズではどのようなAI活用が行われているのか」、「それらの手法には定性的にどのような利点・欠点があるのか」、「それらの手法が定量的にどのくらいの開発生産性向上の効果を出しているのか」、「それらの手法は他のプロジェクトにおいても同程度の効果を再現可能なのか」、などの観点について、各プロジェクトとの週例のミーティングを通して解像度高くトラックしていきました。 ここにおいて特に、開発生産性の測定のためにどのような指標を設定すべきかが議論に上がりました。 DX (メルカリで導入している開発者体験を可視化するツール)などで定量的に確認可能なメトリクスは既に複数あったものの、どれも一長一短で、また3ヶ月という期間の短さを踏まえて即効性も重視されました。結果として私たちは、プロジェクト開始時点における従来通りの開発工数の見積もりと、最終的な開発工数の実績値との間で、どれほどの開発速度向上の効果があったかを主観ベースで統計化することにしました。なるべくノイズや主観によるボラティリティを減らすため、週例のミーティングにおいて細かく開発フェーズごとに行なった作業と要した時間を報告してもらいました。これには、どの工程においてどのような課題があるのかを顕在化させる副次的な効果もありました。 そしてこの連携を通した定量的な調査の中で、最も開発生産性向上の効果が高かった手法が、Specドリブン開発(Spec-Driven Development; SDD)でした。SDDを採用した複数のプロジェクトは、見積比で平均して150%以上の開発速度向上の効果を実現しました。またSDD以外のAIを活用した開発手法を採用したプロジェクト群と比較しても、80%以上の開発速度向上の効果を実現しました。 また更に定性的な調査の中で、AIを活用した開発手法の効果を高く実感している開発者とそうでない開発者の間には、次のような観点で差があることがわかりました。 (1) AIのコンテキストに適切な一次情報を与えられているか 最初にAIに開発時の前提となる全ての一次情報を明示的に渡すことによって、AIの作業中に開発者がプロンプトから情報を補完する必要がなくなります。これにより、AIが要件を無視して実装したり、Hallucinationを起こすことを防止できます。 (2) AIにタスクの明確な目的・ステップ・完了条件などを明示することができているか 最初に開発のステップや完了条件などを明示することによって、AIの作業中に開発者が介入して方向修正する必要性が減ります。これにより、AIが全く意図しない方針で実装を進めてしまったり、タスクのゴールを都合良く解釈してしまうといったことを防止できます。また実装が終わるまでどのようなアウトプットになるのかわからないといった不確実性も解消されます。 (3) AIが作業中にコンテキストに保持する情報を最適化できているか AIが単一のコンテキストで「資料を読み、技術調査を行い、コードベースを解析し、実装を生成し、テストを実行し、不具合を修正し、…」というステップを全て進めようとすると、不要なドキュメントやコードの情報、テスト時のログなどが全てコンテキストに含まれることになってしまい、すぐに会話がcompactされてしまいます。このcompactionによって、AI自身が何のタスクを行なっていたのか忘れてしまうこともあります。実装を複数のステップで異なる会話のコンテキストに分離したり、Claude CodeのSubagent機能のようなコンテキストを部分的に分離するような機能を用いることが、これに対する解決策となります。 2025年9月: スタンダードとしてのAgent-Spec Driven Development(ASDD)の提案 このような結果を踏まえて、SDDの高い開発生産性への効果と、上記3つの観点に代表されるAIを活用した開発手法の効果を左右する不確実性や属人性を低減するために、メルカリ流に最適化された手法が、Agent Specドリブン開発(Agent-Spec Driven Development; ASDD)でした。 ASDDは、「資料の読解・技術調査・コードベースの解析を通して、実装計画(Agent Spec)を生成する」ステップと、「その策定されたAgent Specに基づいて実装を行う」ステップの、2つのステップから構成されます。このAgent Specには、タスクの一覧、タスクごとの詳細設計、タスクごとに「どの実装を参考に」・「どのファイルに」・「どのような変更を加えるか」などの詳細に記載されており、SDDのSpecにおいてAIにとっての明瞭性と人間にとっての可読性を両立したものになっています。ASDDは、SDDのSpecすらも自動生成することを目指した開発手法であるとも言えます。 1つ目のAgent Spec生成のステップでは、メルカリのナレッジ基盤に最適化されたエージェントが自動的に一次情報にアクセスし、詳細な実装計画を生成します。また別のエージェントが、実装計画がサービスのコーディング規約に沿っているか、計画が所定のセキュリティ観点をクリアしているかなどのさまざまな調査を行います。この2つのエージェントが交互に修正と評価を繰り返し、最後に要件や仕様における不確実要素が残った場合には、開発者に追加の質問を行います。このように、「AIのコンテキストに適切な一次情報を与える」ことをプロセスによって保証しました。 2つ目のAgent Specから実装を生成するステップでは、実装を行うエージェントと、テストや静的解析を行うエージェント、実装結果のAgent Specとの整合性を検証するエージェントが互いに協調しながら、タスクの完了まで自律的に実行されます。このように、「AIにタスクの明確な目的・ステップ・完了条件などを明示する」ことをプロセスによって保証しました。 このASDDには、次のような利点がありました。 AIは計画時・実装時のそれぞれで必要な情報のみをコンテキストに保持することができるため、コンテキスト中の情報密度が高い AIの実装計画をレビューできるため、実装結果の予測可能性が高い 生成される実装は実装計画に基づく宣言的なアプローチであるため、再利用性や透明性が高い 各ステップへのcontributionを通して、全社横断で知見の集約と持続的改善を行うことができる そして、ASDDによって実装全体がAIに移譲して非同期的に実行可能なプロセスになったため、高いスケーラビリティの実現が可能になりました。このスケーラビリティこそが、高い開発生産性の実現を可能にしたと考えられます。 2025年10月から現在: 開発プロセス全体のAI-Native化へ 2025年10月からは、1人から始まったpj-doubleも10人超のチームに拡大し、またその対象領域もメルペイの一部プロジェクトから、全社へとスケールしています。対象範囲も、これまでのバックエンドの詳細設計と実装のAI-Native化に加えて、要件定義や設計、iOS/Androidのクライアント開発やQAを含めた、デリバリーサイクル全体のプロセス再設計へと拡大しています。 要件定義・設計領域では、プロダクト仕様書と技術設計書を作成・合意するプロセスを再設計しています。そこでは、PMや開発者が簡単な要件や技術方針を伝えるだけで、AIが一次情報や過去のプロジェクトを参考にプロダクト仕様書と技術設計書を自動生成することを目指してきました。 クライアント実装の領域では、バックエンドの実装と同様にASDD開発の有効性を調査してきました。既にiOS開発では効果が検証され始めており、バックエンドとクライアントの一貫した実装体験として、ASDDの整備をさらに続けていく予定です。 QA領域では、バックエンドQAとクライアントQAのそれぞれにおいて、またテストケースの自動設計と自動実装のそれぞれについて、ASDDと同様にQAのワークフロー全体をAgenticに実行するための手法を模索しています。テストケース自動設計においては、Claude Code Commandによって、マイクロサービスの依存関係やテストケースのもとになる仕様書の解析・テストケースの設計などを自動化する試みが模索されており、現場のQAエンジニアからはポジティブなフィードバックをいただいています。またテストケース自動実装においては、ASDDと同様にテストの実装と評価のループをAgentが自律的に行うことによって、高い精度で正しいテストケースが実装されることを確認しています。 これらの各領域における取り組みは現在進行形で、現場のプロジェクトとの連携の中で、地に足つけた持続的な検証と改善を行いつつも、トップダウンで「プロセスがどう変化するか」「体制がどう変化するか」についてビジョンを打ち立てて、着実に一歩ずつ前進しています。 pj-doubleを通して得た学び 2025年7月から現在に至るまでのpj-doubleの道のりは、決して順風満帆なものではありませんでした。技術的な課題、組織的な摩擦、そしていわゆる「ビルドトラップ」など、多くの壁に直面しました。 しかし、それらを乗り越える過程で得られた学びこそが、pj-doubleの道筋や目指す開発プロセスの礎となっています。ここでは、特に重要だった4つの学びを共有します。 学び1: AI活用は品質・保守性とのトレードオフではない プロジェクト発足当初、多くの開発者が懸念していたのが、「開発速度を上げることで、品質や保守性が犠牲になるのではないか」というトレードオフでした。しかし、検証はこの直感に反する結果を示しました。 まず機能的な品質についてです。pj-doubleでは、QAワークフローの自動化などのGuardrail整備を進めることで、「デグレが発生していないか」「意図通りに動作するか」といった機能的な品質保証をプロセスとして組み込んでいます。 重要な視点は、これが「AIと開発者の責任分界点の再定義」であるということです。AIに実装を任せる(非同期化する)ことで、開発者はそこで浮いたリソースを、より本質的な品質担保や、開発者がリソースを責任を持つべき高度な検証作業に充てることができます。実際に、メルカリモバイル開発における事例では、実装の高速化によって創出された時間を動作検証やQAに充てることで、設計段階でのインシデントの未然防止に寄与するという成果が確認されました。 次に保守性の観点です。「AIが書いたコードが読みにくい」「拡張性が低い」といった懸念もよく聞かれます。しかし、これらの問題はAIの能力不足ではなく、コンテキストの欠如に起因することが多いです。「社内の共通ライブラリを使って欲しい」「特定のコーディング規約に従って欲しい」といった意図があるならば、そのドメイン知識や規約をコンテキストとしてAIに与えることが効果的です。実際にASDDでは、Agent Specの生成時に、社内フレームワークの参考実装などをコンテキストに与えることで、社内の実装における暗黙的なパターンを反映させており、開発者からも「自分で書くのと同じかそれ以上の品質の実装計画が生成される」というポジティブなフィードバックを受けています。 さらに、こうしたASDDなどの共通基盤を整備することで、組織として推奨する実装パターンや規約を誰もがコンテキストに注入できるようになります。個人のスキルに依存してバラバラに行われていたAIコーディングに対して、組織全体で品質のベースラインを引き上げることが可能になると期待しています。 もちろん、短期的な速度向上が長期的な技術的負債につながらないよう、私たちはDXツールを用いてRevert Rate(手戻り率)やMTTR(平均復旧時間)などの指標を常にモニタリングしています。定性的な感覚と定量的な計測の両軸で確認を続けていますが、現時点ではAI活用が品質を低下させるというシグナルは出ておらず、むしろ標準化の強力な武器になり得ると確信しています。 学び2: レビューのボトルネックはPull Requestの粒度で解消できる 「AI時代の開発は、人間によるコードレビューがボトルネックになる」。これはよく耳にする懸念であり、実際に社内のDX指標を見ても、レビューのリードタイムが開発生産性の向上を阻害している傾向が一部で見受けられました。しかし、現場への定性的なヒアリングと分析を通して、これは「AIコード特有の問題」ではなく、「プロセスの運用」によって解決可能な課題であることが見えてきました。 レビューが辛くなる原因を調査したところ、単に「AIが書いたコードだから」ではなく、AIの圧倒的な生産速度によって「1つのPR(Pull Request)に含まれる差分が膨大になっていたから」であることが判明しました。人間はインクリメンタルな情報の処理には長けていますが、一気に大量の情報を受け取ることで、認知負荷が限界を超えてしまったとも言い換えられます。 pj-doubleではこの課題に対して、仕組みによって解決することを目指しました。Agent Specを生成する段階でタスクを「合理的かつ動作可能な最小単位」に細かく区切り、そのタスクごとにPRを作成するという運用を徹底したのです。 実際にこのプロセスを採用した開発者からは、「PRが小さく保たれることで、AIが書いたコードであってもレビューのストレスを感じなくなった」などのポジティブなフィードバックが寄せられています。AIと人間のコードの間に、レビュー工数上の有意な差は生じないというのが、私たちの現在の結論です。 さらに私たちは、AI-Nativeな開発プロセスの再設計において、コードレビューという行為そのものの再定義も必要だと考えています。 一口にコードレビューと言っても、品質や保守性を守る側面、踏襲的なプロセスとして習慣化して手段と目的が逆転した側面、監査上の要求を満たすための側面など、その役割は複数の意味を持ちます。これら全てを「人間が目視で保証する」という形で行う必要があるのでしょうか。 pj-doubleでは、単に全ての生成コードに人間が目を通すという既存の形を超えて、より意義のある、本質的な価値提供にフォーカスした新しいコードレビューの体験を模索し続けています。 学び3: 現場に地に足つけたアジャイルな検証と改善のサイクルの難しさ 私たちがpj-doubleを通して度々実感したのは、現場での検証と改善のサイクル、いわゆるフィードバックループを回すことの難しさです。特にpj-doubleのQA領域での取り組みにおいて、私たちは典型的な「ビルドトラップ」を経験しました。 QA領域でのAgent活用において、私たちは当初、QAワークフローを自動化するためのリッチなUI付きのツールを開発・提供しました。プロンプトだけを配布するよりも、画面を提供したほうがデモがしやすく、配布も容易で、何より入力を制限することでAgentの挙動の再現性を担保できると考えたからです。 しかし、これは結果として検証の足枷となりました。本来、検証フェーズで最も重要なのは「プロンプトや手法の改善」です。しかし、アプリという堅牢な形にしてしまったことで、改善のためにはアプリ側のコード修正まで必要になり、コントリビューションのハードルを上げてしまいました。ASDDのように最小構成で始めていれば、アーリーアダプターたちが直接プロンプトを改善できたはずが、アプリのメンテナンスに時間を取られ、本来の目的である「課題解決の手法の確立」がおざなりになるという本末転倒な事態を招いたのです。 また、アジャイルな改善ループの実現も、想像以上にハードルが高いものでした。 当初は週に2〜3回程度の頻度でフィードバックをもらい、高速に改善を回す想定でした。しかし、現場の開発者にとって、検証内容を言語化してフィードバックを送る行為自体が大きなコストです。実際に届くフィードバックは非常に質が高く丁寧なものでしたが、それを高頻度で要求することは現実的ではありませんでした。 これらの経験から、私たちはアプローチを修正しています。 まずツールありきで考えるのではなく、最小限の構成で「手法の洗練」に集中すること。そして、週に1度などのミーティングにおけるカジュアルなフィードバック収集や、Slack上のフィードバックの自動収集、DXの機能を活用した集計値の自動算出などの仕組みを導入しています。 この学びを踏まえたアプローチの修正によって、pj-doubleのQA領域では提供する手法の洗練と現場からのフィードバックの収集のアジャイルなサイクルが実現されるようになりました。この2025年10月から始まったQA領域のAgentic化の取り組みは、今や急速なスピードで各領域におけるQAエンジニアの体験を塗り替えていっています。 この取り組みを通した最も重要な教訓は、現場に対して「良いツールがあるから使ってフィードバックして」という姿勢ではうまくいかないということです。 「開発プロセスを変える必要があり、そのために協力してほしい」というスタンスで巻き込み、ツールはそのプロセス変革を支援するための手段に過ぎないという合意形成を行うこと。ツールに依存しないプロセスを現場と共に見出し、泥臭く検証し続けることこそが、遠回りのようでいて最短の道であると痛感しています。 学び4: 混沌とした過渡期こそ、明確なビジョンが旗となる 上のビルドトラップの話とも重複しますが、pj-doubleにおいて私たちが最も警戒したのは、手段の目的化、すなわち「AIを使った便利ツールの開発」に陥ることでした。 日々新しい生成AIモデルやツールが生まれては消えるカオスの渦中で、今の技術スタックに合わせてツールを作り込んでも、それは数ヶ月後には陳腐化してしまいます。pj-doubleの模索においても、目前の課題解決に最適化したソリューションが、数ヶ月後には時代遅れになっているリスクと常に隣り合わせでした。取り組み自体を陳腐化させないためには、特定のツールや局所的な最適化ではなく、ツールに依存しない「プロセスの変革」に向き合う必要があります。しかし、私たちが今向き合っているものが、本当にツールに依存しない普遍的なプロセスなのか、それとも現在のツールの限界を補うための過渡的な対処療法に過ぎないのか、その見極めは非常に困難です。 このような「標準化と陳腐化のジレンマ」の中で、組織として価値あるアセットを積み上げ続けるために最も重要だった思考の転換こそが、現在の手元に見える課題から積み上げ式に考えるのではなく、来たる未来から逆算する「バックキャスティング」でした。 今のツールの限界を度外視し、技術のポテンシャルを前提に置くことから始めます。「Agentが自律的にコンテキストを収集し、コーディングルールに従って実装を完遂し、QAもAgenticに自動で行われる」。これらが当たり前に実現されたとき、プロダクトデリバリーはどのような形態を取るべきか? ボトルネックはどこに移るのか? この理想的な未来の体験から逆算して初めて、今なすべき投資が見えてくるものかと思います。 私たちが進めているナレッジフロー整備、非同期のエージェント実行基盤開発、リグレッションQAの自動化、テスト環境への自動デプロイを含むCI/CDの改善などは、単なるインフラ整備ではなく、この描いた世界観へシームレスに移行するための戦略的な布石です。 メディア理論家のマーシャル・マクルーハンはかつて、「私たちはバックミラー越しに現在を見ながら、未来に向かって後ろ向きに進んでいる("We look at the present through a rear-view mirror. We march backwards into the future.")」と言いました(出典:The Medium is the Massage by Marshall McLuhan)。 これは、私たちの「常識」や「思考様式」そのものが過去の技術環境によって形作られているという、構造的な限界への指摘です。私たちは今の常識(バックミラー)を通してしか世界を見ることができないため、Agentが当たり前になった新しいパラダイムにおいて、私たちがどのような思考や行動の原理を持つべきかを、現在の延長線上で想像することはできません。ゆえに、過去の成功体験や現在の思考の常識だけで将来を設計しようとすれば、それは局所最適化に留まり、本当に目指すべき世界観には辿り着けません。明確なプロセスが見えない過渡期だからこそ、ともすれば「ビルドトラップ」に陥りがちです。 だからこそ、pj-doubleでは既存の常識を意図的に崩し、統一された未来の体験を鮮明にイメージとして描き出し、それを旗として掲げ続ける能動的な態度が不可欠であると強く意識してきました。実際に、pj-doubleでは積極的に目指す世界観やその先の開発体験を社内で発信することで、先の見えない変革の中でも、組織全体が同じ方向を向いて進むことができてきたのだと振り返っています。 pj-doubleが上流プロセスで直面した課題 ASDDでは、Agent SpecというAIにとっての中間表現を統一することで、AIの実行計画への介入が可能になりました。すなわち、開発標準・ドメイン知識・職能の専門知識を強制的に注入することに成功しました。このようにASDDは、pj-doubleが当初目指していた集合知の共通基盤としての効果を発揮しています。 ASDDはこのように下流の「仕様が決まった後の実装」において大きな効果を発揮しているのと同時に、上流の「仕様が決まるまでのプロセス」において深刻な課題に直面しています。私たちは、開発現場における継続的な検証とフィードバックの中で、「コードを書く」速度は劇的に向上したものの、その前段である「何を作るか(What)」と「どう設計するか(How)」の合意形成プロセスにおいて、深刻なボトルネックに直面しました。 より具体的には、これまで開発者からはASDDについて次のようなフィードバックを受けました。 (1) Agent Spec以前に必要な合意形成が無視されている 「Agent Specは仕様や設計方針が確定していることを前提にしているが、そもそも関係者間の合意形成に最も多くの時間がかかっている」 「Agent Specのように自動生成されたドキュメントは判断根拠の説明が伴わないため、合意形成に用いる媒体としても適切ではない」 (2) Agent Specの生成とレビューのコストが高く、開発サイクルの速度が落ちる 「生成されたAgent Specや実装の意図が分からず、情報源を探すために技術設計書を遡り、プロダクト仕様書を遡り、Slackの議論を遡らないといけない」 「毎回0から書き換わるAgent Specを一行一行レビューして修正するくらいなら、自分で書いたほうが早い」 1つ目について、今のASDDでは、たとえ合意のない確度の低い要求でも、そのまま実装の自動生成まで行うことができてしまいます。結果として、AIは曖昧な部分を勝手に補完し、「技術的には正しいが、誰も合意していない成果物」が自動生成されます。立ち返って考えてみると、従来の上流から下流までの開発フローにおいては、仕様・設計・方針に関する段階的な合意の積み重ねこそが、開発フローのチェックポイントとなって手戻りを防止する機能を果たしていたはずです。またこのプロセスの本当のボトルネックは、これらの合意形成を同じ時間・同じ場所に集まって同期的に行うステップです。pj-doubleは要件定義段階で、プロダクト仕様書や技術設計書を自動で生成することを目指していましたが、このような一次情報が創出される創造的な議論の場や、そこにおける合意形成などの本質的なプロセスがそのスコープから抜け落ちてしまっていました。つまり、上流工程において私たちがフォーカスすべきは、一次情報を組み合わせてプロダクト仕様書や技術設計書に変換するプロセスではなく、その一次情報そのものを生成するプロセスだったという反省です。 また2つ目について、ASDDによって生成された長大なAgent Specや実装は開発者の認知負荷を遥かに凌駕し、真面目にレビューしようと思うと、膨大なレビューコストを強いることになってしまいます。さらにAgent Specには、一次情報と自動生成された真偽不明な情報とが入り混じることもしばしばありました。開発者は、生成された情報に心当たりがない場合、その情報の出自を遡って調査する必要があります。本質的に人間の認知モデルは、インクリメンタルな認識の更新を基本としていて、今の認識との差分として追加情報を期待するはずです。他方でpj-doubleで取ってきたドキュメント自動生成のアプローチは、膨大な情報源からかき集めた新情報を既知の情報と一緒くたにして提示するため、認知過負荷を引き起こしてしまっていました。 課題解決への系統的分析とアプローチ このような課題に対して、私たちはその原因が、「『AIと考える・AIと決める』べきことを、『AIに任せる』体験として設計してしまったこと」であったと総括しました。 AIと開発者の協業モデルは、大きく同期プロセス・非同期プロセスに分類できると考えています。(ここで暗黙知とは言語化されていない情報、形式知とは言語化・文書化された情報を指しています。) 同期的協業 — AIと考える・決める 非同期的協業 — AIに任せる 活動の様態 AIが情報収集・開発者が意思決定・AIが生成・開発者が評価 AIが自律的に情報収集・意思決定・生成・評価を行う 活動の役割 暗黙知と形式知の交換 / 一次情報の生成を伴う 形式知から形式知への転換 / 情報の変換に過ぎない 活動の意義 選択・意思決定・合意 アウトプットの機能的側面、実利的な要求の実現 機能やテストの実装は、動作するという機能的な要求を満足することが目標であるため、非同期的協業によって認知負荷を超えてスケールさせることが重要で、その点でAIへの移譲を促進することがAI-Native化の促進を意味します。 一方で上流プロセスにおいては、作成される仕様や設計そのものの価値は小さく、むしろそこに至るまでの比較検討・意思決定・合意などの、人間による承認の記録としての側面が大きいです。そして承認を要するという性質上、誰かの認知負荷の範囲内で運用される必要があり、その意味で同期的協業を必要とします。すなわち、上流フェーズにおける最重要事項は、生成されるアウトプットではなく、仕様や設計が検討され、レビューされ、合意され、それらが新たな一次情報として管理されるようになることです。 しかしpj-doubleでは、同期プロセスの意義を過小評価し、全てを非同期プロセスとして処理しようとしていました。一次情報は既知で既出の前提として、それらを用いた情報の変換(プロダクト仕様書・技術設計書・Agent Spec・実装生成)のAgentic化にフォーカスしてしまっていました。私たちは、「AIが最初から90%の完成度の設計を作る」ことと、「AIと一緒に考え、機微な方向性を決定しながら同じ設計に辿り着く」ことが、上流プロセスにおいては全く異なる意味を持つという観点を見落としていたということです。 すなわち、上流プロセスを非同期的協業とは区別された同期的協業として再設計することがpj-doubleにおける次の命題となるわけですが、このAIとの同期的協業も更に、「AIと考える」体験と「AIと決める」体験の2つに区別することで、更に見通しが良くなると考えています。 同期的協業 — AIと考える 同期的協業 — AIと決める 活動の様態 AIが情報を収集・提示し、開発者の思考を拡張する 思考や意思決定を通して新たな一次情報を生み出す 活動の役割 形式知の提示による暗黙知の拡大 一次情報の形式知への登録・参照 活動の意義 対話を通した認知の範囲・思考の深度・選択の質の向上 合意が形成され、蓄積されること 「AIと考える」プロセスは、開発者が1人では見つけられなかった情報にアクセスし、1人では実現し得なかった深度で検討することを可能にします。ここでは、AIとの自然な対話の中で、AIが設計・実装を前進させるためのガイドをしてくれることが理想的な体験です。例えば、ChatGPTやClaudeとの対話を通して、設計や実装の方針について議論するような体験です。ここでは、開発者自身への知の内面化(Internalization)の側面に価値があります。 そのためには、人の認知モデルに基づいて、開発者の現状理解からの差分として新たな情報が補完的に提示されるようなインタラクションの設計であるべきです。これは例えば、エンジニアがテックリードとの会話を通して、「こういう設計もあったか」「この設計はこの考慮が漏れていた」などの発見を伴うプロセスに対応します。AIと開発者の関係性は、AIが生成した情報を開発者が受動的に消費するという関係性ではなく、開発者が次に必要とする情報をAIが補完的に提示するという関係性を目指すべきであると考えます。 またこのような開発者とAIの思考の模索は、完全なフリーフォーマットで行うことも、完全なテンプレートに基づいて行うことも、どちらも適切ではないと考えています。過度な自由度は選択麻痺を引き起こし、過剰な制約は模索の余地を奪うためです。pj-doubleのプロセス再設計においても、プロセスを標準化することが何度か試みられましたが、これは思考の幅を制限してしまう過度な制約の典型例だったと言えそうです。AIが全体プロセスやそこにおけるステータスを見据えつつ、開発者を自然な対話の中で誘導できるような思考フローが理想的でしょう。 「AIと決める」プロセスは、開発者によるさまざまな選択肢の採用・不採用の意思決定とその理由を、新たな一次情報として蓄積するためのプロセスです。ここでは、建設的なプロセスのチェックポイントとしての意思決定やその蓄積、一次情報の生成という、知の表出化的側面に価値があります。 このように、AIと決めたことが蓄積し、それがAIと考えるために使われるという循環をデザインすることが、同期的協業の条件であると考えています。 私たちpj-doubleは現在、このような考えに基づいた課題解決方針の模索とその検証を実施するフェーズにあります。もしその取り組みを通して更なる発見や学びがあれば、またいつか別のテックブログなどで共有させてください。 pj-doubleが描く世界観 そしてこの体験が実現した世界観は、次のようにイメージできるかもしれません。 (1) AIと考える 開発者はAIとの話し合いを通じて、仕様や設計を考え進めていきます。そこにおいてAIは既存のサービスやドメイン知識を駆使しながら、思索や意思決定をサポートします。このような能力の拡張により、上流工程において開発者1人が担える役割も拡大します。 (2) AIと決める AIは対話の中で追加で発生する一次情報を蓄積します。PMの「なぜこの仕様は却下されたのか」という質問や、テックリードの「なぜこの技術選定を行なったのか」という質問に対して、蓄積された一次情報に基づいて回答します。 このような合意の形成と参照という側面は、これまで必要だった「開発者↔開発者」の同期的なコミュニケーションを、「開発者↔Agent」+「Agent↔開発者」のコミュニケーションへと分解し、合意の形成・参照を非同期的に実現可能にします。すなわち、各職能の人が同じ時間・同じ場所に集まらなくても、合意の確認や意思決定をできるようになります。 (3) AIに任せる このように合意形成が行われたら、あとはプロジェクトの機能要求を実現するための仕事をAIに移譲するだけです。例えばここで生成されるプロダクト仕様書や技術設計書は、全て過去の意思決定に基づくため、開発者がその生成意図を吟味したり、Hallucinationを精査する雑務が発生しません。またASDDの本質的な付加価値であるAgenticな実装生成も、高い確度で任せることができます。QAも自動で実行されるため、デグレの心配もありません。あとは、安心して実装の完了を待つだけです。 この世界観のもとで、PMや開発者はAIによって拡張された思考や調査能力を存分に使いながらプロダクトのアイデアを考え進めていき、アイデアが煮詰まったと思ったら即座にそれが形になります。試行と学習のサイクルが圧倒的な速度で回り、より多くのアイデアを試し、より速く学ぶことができるようになるでしょう。 また職能の拡張は一人一人の役割やプロジェクトの体制にも変化を与えるはずです。開発者がAIによって拡張されることで、専門知識が必要な調査や業務が自動化され、PM、アナリスト、Designer、エンジニア、QAといった個人間の職能の差異が薄れていき、結果として1人が担う役割も大きくなるはずです。またそのもとで、プロダクトデリバリーは少人数の自律的なチームによってEnd-to-Endに実施されるようになります。これまではOutputに目が向きがちだった開発者も、よりお客さまへの価値提供との距離が近づくことで、OutcomeやImpactに責任を持つようになっていくでしょう。 さらに、薄れるのは職能の境界だけでなく、サービスの境界にも広がりそうです。ドメイン知識が形式知化されることで、プロダクト開発のために要求されるドメイン知識の総量も減少し、サービスの境界と認知負荷の境界が等価ではなくなるかもしれません。すなわち、逆コンウェイの法則に基づくような組織体制ではなく、よりビジネスを加速させるために最適な組織構造へと変化するかもしれません。例えば、1つの少人数開発チームが複数のサービスをまたいで1つの施策を実現するような体制はその候補の1つです。 こうした要件定義からQAまでをAI-Nativeに再設計するための取り組みの効果は、開発生産性の向上だけに留まらず、本質的なビジネスの強度向上に貢献するとも考えています。これまでは各工程で担当者が異なるため知識の分断が、各工程でプラットフォームが違うためデータの分断が発生することがありました。プロジェクトを通したより多くの知識が、またより多くの職能ごとの専門知が集積されるようになることで、市場やお客さまのOutcome・Impactがフィードバックされて、それをもとにプロダクトを改善・拡張するというループが自己完結化するようになれば、施策の精度や戦略の練度の向上にも寄与できると信じています。 pj-doubleの取り組みやそこで得た学びは、知的活動を中心とする他のあらゆる領域にも拡大できるかもしれません。「AIと考える」「AIと決める」「AIに任せる」の3つを使い分けながらプロセスを再設計することが、プロダクト開発に留まらない「AI-Nativeな働き方」の重要な要素になると考えています。 おわりに 以上、メルカリにおけるpj-doubleを通した、AI-Native化への挑戦とその学びについての紹介でした。本記事が、Human CentricからAgent Centricへと向かうこの過渡期において、AIとの新しい協業の形を模索する一助となれば幸いです。長文にお付き合いいただき、ありがとうございました。 明日の記事は hokaoさんによる「Kubeflow PipelinesとPydantic Settingsを活用してMLパイプラインを型安全かつシンプルに実装する」です。引き続きお楽しみください。
はじめに こんにちは。メルペイVPoEの @keigow です。 この記事は、 Merpay & Mercoin Advent Calendar 2025 の1日目の記事です。 2025年も色々なことがありました。年末ということで、2025年にメルペイとして、或いはメルカリグループとして取り組んだことをこの機会に振り返られればと思います。 mercari GEARS 2025 11月13日に「メルカリエンジニアリングの今」をテーマにテックカンファレンスを開催しました。リアルな場でのイベントは実に7年ぶりの開催となりましたが、社内外含め沢山の方にご来場いただき、盛り上がることができました。キーノートを始めとして各発表の スライド や 動画 も公開されておりますので、ぜひ御覧ください。 AIによって進化する開発 ちょうど半年前に同じくブログの連載企画で メルペイにおけるAI活用の取り組み についてご紹介しました。当時は Claude Code が流行り始めたぐらいだったことを考えると、凄い速度の変化が起きていると改めて感じます。当時はエンジニアのAI Coding Toolの利用率を追いかけていましたが、今ではほぼ全てのエンジニアが何らかのAI Coding Toolを利用するようになりました。 グループ全体として大きな取り組みとなったのは、 こちらの記事 でも取り上げているAI Task Forceです。目標としてAI Nativeな会社を目指し、エンジニアの部署に限らず、カスタマーサービスやマーケティングなど全社33の部署を対象に、専任のエンジニアを含めた100名規模の組織を組成し、業務の棚卸しと、AI Nativeなワークフローとはそもそもどうあるべきかを考え、見直しを行っています。まだ道半ばではあるものの、各部署でロードマップを作成し、着実に成果が出てきています。 Project Double AI Task Forceの活動と並行して、メルペイにVPoE Officeという新しい箱を作り、AIによる生産性向上の取り組みとしてProject Doubleをスタートしました。その名の通り生産性を2倍にしようという取り組みで、当時一部のエンジニアたちが行っていたAgenticなCoding手法を、Agent Spec Driven Development(ASDD)として標準化することで誰もがそれを利用可能にしようとする試みです。 初期はBackendのみを対象としたProjectでしたが、7-9月の四半期で一定の成果を出すことができたため、その範囲をClientの開発、Backend/ClientそれぞれのQA領域、仕様書や設計などのPlanning領域へと広げることを決めました。また対象となるCompanyもFinTechの一部のPilot Projectから、グループ全体へと広がり、現在はEngineeringの中でも最優先の取り組みとして進行しています。 内容の詳細や得られた学びについてはこのProjectをリードしているnakai-sanから明日の記事で詳しく紹介していただく予定です。 Project Doubleが実現した世界で Agentic Codingの推進において、乗り越えるべき課題は精度やイテレーションにかかる時間などたくさんありますが、日々起きるモデルの進化やツール、環境の整備によっていずれ問題としては解決されていくと思っています。 一方で、1人のエンジニアが企画から設計、開発、テスト、リリースまでを一貫して行える世界が来たときに備えて、いくつか解決しなければいけない部分もあります。オンコール体制の整備、問い合わせ対応などの運用業務の最適化など、Agentic Codingが中心となった時代に最適な組織設計です。こちらについては一定の仮説はあるものの、まだ明確な応えが見えていない領域であり、Project Doubleの推進と合わせてトライアルをしていきたいと思っています。 しゃべるおさいふ AIの活用はEngineeringだけでなく、プロダクトへの活用もPoCという形でトライしています。メルペイで現在取り組んでいるのがこちらの「しゃべるおさいふ」という機能で一部のお客さまを対象に試験的に導入しています。 内容は非常にシンプルで、利用データを元にAIがコメントをしてくれるという機能です。ちょっとした日常の利用に対してコメントを貰えるというのは思ったよりも嬉しい体験で社内テストは想定していたよりも好評でした。そこまで複雑な機能では無いものの、コストの観点や安全性、倫理的な観点も含め、AIをプロダクトで活用するとはどういうことなのか、という学びを得ることができました。12/20のkobaryo-sanの記事ではこちらの機能のバックエンドの設計について紹介する予定なのでお楽しみに。 開発合宿 1月の事になりますが、数年ぶりにメルペイで一泊二日の開発合宿を行いました。普段業務に追われて取り組めないような新しい技術へのチャレンジ、OSSの開発、やりたいと思っていた新機能の開発などに取り組みました。この開発合宿をきっかけとして、 有志による開発プロジェクト がスタートし、実際にリリースまでつながることもありました。コロナ禍で普段直接顔を合わせることが少なくなっていたエンジニア同士のコミュニケーションの機会にもなり、参加してくださった皆様の満足度もかなり高かったので、また来年もトライできればと思っています。 開発合宿の様子はMercari GearsのYouTubeに 動画 も上がっているので、気になった方はぜひご覧いただければと思います。 おわりに 今年は全社のテーマとしてAI Nativeを掲げ、Engineering組織としてもAI中心の一年となりました。半年前も同じようなことを書いた気がしますが、目まぐるしい変化の中で働けることを楽しんでいます。 明日の記事はその中でも中心的な取り組みとなる、nakaiさんによる「pj-double: メルカリの開発生産性向上に向けた挑戦 — AI-Native化が辿り着いたASDDとプロセス変革の全貌」です。引き続きMerpay & Mercoin Advent Calendarをお楽しみください。
こんにちは。メルペイ Engineering Engagement チームの mikichin です。 Advent Calendarの季節がやってきます!今年も、メルカリグループは Advent Calendar を実施します! ▶ Mercari Advent Calendar 2025 はこちら Merpay & Mercoin Advent Calendar とは? Advent Calendar の習慣にもとづいて、メルペイ・メルコインのエンジニアがプロダクトや会社で利用している技術、興味のある技術分野やちょっとしたテクニックなど知見をアウトプットしていきます。このAdvent Calendarを通じてクリスマスまでの毎日を楽しく過ごしていただければと思っています。 2024年のMercari / Merpay & Mercoin Advent Calendar はこちら Mercari Advent Calendar 2024 Merpay & Mercaoin Advent Calendar 2024 公開予定表 (こちらは、後日、各記事へのリンク集になります) Date Theme / Title Author 12/1 TBD @keigow 12/2 pj-double: メルカリにおけるEngineeringのAI-Native化の取り組みと学び @nakai 12/3 Kubeflow PipelinesとPydantic Settingsを活用してMLパイプラインを型安全かつシンプルに実装する @hokao 12/4 TBD: mercoin internshipでの経験 @Sakamoto 12/5 TBD: Using AI for areas it is strong at. How we automated some aspects of technical documentation generation. @Fab 12/6 YAPC::Fukuoka参加しました @Sakabe 12/7 TBD: gRPC Federation の Incidentについて @goccy 12/8 Finally, Mercari in English! Our road to cross-platform i18n @fenomas 12/9 TBD: vibecheckerというlocalでreviewするtoolの話 + reviewのreviewの自動化? @seitau 12/10 TBD: AI関連技術 or AI Task Force/pj-doubleでの学び or 何か @panorama 12/11 TBD: n8n関連 @ISSA 12/12 Making n8n Enterprise-Ready: 企業向けn8nの導入と運用の取り組み @T 12/13 n8nの静的解析CLIツールをOSS化 – セキュリティチェックの自動化 @mewuto 12/14 TBD: n8n関連と生産性 @abcdefuji 12/15 Extending the Balance Service: Challenges in Implementing Multi-Currency @Timo 12/16 "Supercharging User Engagement: Use Server-Driven UI to reduce Time-to-Market" @Stefan_droid 12/17 Mandates for Recurring Payments @tomo 12/18 DebtAccountAsyncUpdateの設計 @Minato 12/19 2B 与信の世界 @komatsu 12/20 LLMを用いたおしゃべり機能「しゃべるおさいふ」のバックエンド設計 @kobaryo 12/21 getDX の活動・連携周りで何か @ntk 12/22 加盟店管理画面へのOAuth2.0の導入 @taki 12/23 障害振り返りにCAST(システム理論に基づく因果分析)を試してみた @pooh 12/24 AI駆動学習サイクル @kubomi 12/25 TBD @kimuras 初日の記事は keigowさんです。引き続きお楽しみください。
こんにちは。メルカリ Engineering Officeのyasu_shiwakuです。 今年もメルカリとメルペイ・メルコインで2本のAdvent Calendarを実施します! ▶ Merpay & Mercoin Advent Calendar 2025 はこちら Mercari Advent Calendar とは? メルカリグループのエンジニアがプロダクトや会社で利用している技術、興味のある技術分野やちょっとしたテクニックなど知見をアウトプットしていきます。このAdvent Calendarを通じてクリスマスまでの毎日を楽しく過ごしていただければと思っています。 2024年のMercari / Merpay Advent Calendar Mercari Advent Calendar 2024 Merpay & Mercoin Advent Calendar 2024 公開予定表 (こちらは、後日、各記事へのリンク集になります) Date Theme / Title Author 12/1 Websocket XSS vulnerability discovery: My security journey at Mercari @philolo1 12/2 LLM Key Server: Providing Secure and Convenient Access to Internal LLM APIs @Hiroki Akamatsu 12/3 Shops Monorepo 5 years later: a tale of Bazel and Cursor @Jazz 12/4 Enhancing DX through Mercari’s Unified Platform Interface @whygee 12/5 [TBD] ハロのフロントエンドアーキテクチャ振り返り or Apollo MCP でテスト工数削減 @mattsuu 12/6 Engineering the Semantic Layer: Principles for Data at Scale @sathiya 12/7 [TBD] QAエンジニアがAIで日々の課題を解決した話 @yuga 12/8 Navigating Change: Learning to Reinvent in an Unstable World @Antony Chane-Hive 12/9 Search Results Quality Monitoring with LLMs @otter 12/10 [TBD] DCR の話 @task 12/11 [TBD] claims parameter の話 @kgoro 12/11 LiveContactToolにおける機微情報の取り扱い~CloudDLPを使ったマスキング @sters 12/12 TBD @tokku 12/13 [TBD] メルカリのナレッジマネジメント戦略 @t-hiroi 12/14 メルカリAdsが広告を届けるまでの話 @yanap 12/15 TiDB Resource Groupでワークロードを制御する @ogataka50 12/16 [TBD] From brownfield to greenfield: Shops FinOps journey @Sneha & @Darius 12/17 Building a Learning Culture with DevDojo @mariz 12/18 [TBD] Kubernetes Packet Capture @mshibuya 12/19 AI-Native開発を加速する AWS Kiro の導入と、Okta を活用したアカウント管理の自動化 @amenbo & @siroken3 12/20 PR駆動の変更、CI/CDで自動反映——Terraformで実現するJamf ProのIaC+GitOps基盤 @yu 12/21 [TBD] Non-AI Tasks in the AI Task Force @akkie 12/22 What It Takes to Trust a Token: Tales of OIDC & OAuth Security @Kahla 12/23 [TBD] Pj-neco development @Sneha & @Yu 12/24 メルカリのNotion Architecture ver.1 の話 @kiko & aisaka 12/25 TBD @kimuras 最初の記事は、「 Google CloudからGitHub PATと秘密鍵をなくす – Token ServerのGoogle Cloudへの拡張 」です。 どうぞお楽しみに!
こんにちは!メルカリ Engineering Office の @mikichin です。 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます! 2018年に実施した「Mercari Tech Conf 2018」から7年の時を経て、久しぶりのオフラインでの開催となります。 テーマは「メルカリエンジニアリングの今」。 今年の全社的なテーマでもある「AI-Native」についてはもちろん、2018年以降メルカリグループのエンジニアリングがどのように変化してきたかを、技術・組織・カルチャーの観点からご紹介します。 オンライン配信はありませんので、ぜひ会場でご自身の目と耳で確かめてください!! セッションの紹介記事はこちらをご確認ください。 PASSION Stage のURL: https://engineering.mercari.com/blog/entry/20251008-mercarigears2025-passion-stage/ GROW Stage のURL: https://engineering.mercari.com/blog/entry/20251009-mercarigears2025-grow-stage/ MECHANISM Stage のURL: https://engineering.mercari.com/blog/entry/20251010-mercarigears2025-mechanism-stage/ 本記事では、オフラインイベントならではのセッション以外の楽しみ方をご紹介します! フロアマップ 会場は、メルカリのエンジニアリング組織における信念や行動の基盤となる共通認識を明文化した「 Mercari Engineering Principles 」をモチーフにした「PASSION Stage」「GROW Stage」「MECHANISM Stage」の3つのステージがあり、ここでプレゼンテーションを聞くことができます。 その他、Ask the SpeakerやTech Quizを実施している「COLLABORATION Lounge」、「Unconference」ルーム、「Break Area」があります。 スタンプラリー 会場に到着したら、名札を受け取ります。会場で参加者同士が話しかけやすいように、お名前や技術領域、所属など記載してくださいね。 受付時に、名札とあわせてスタンプラリーのカードをお渡しします。スタンプラリーの詳細はカードに説明文がありますが、セッションに参加したり、Tech Quizに回答したりするとステッカーをもらえるほか、参加者同士で交換しながら集めていきます。集めたステッカーの枚数でもらえるグッズが異なりますので、ぜひ全部集めてみてください! ポスターセッション 本イベントではプレゼンテーションだけではなく、ポスターセッションを準備しています。今回はEngineering組織だけではなく、メルカリR4Dラボでの研究も含め14つの発表があります。 ポスターセッションの詳細は、下記ブログ記事をご確認ください。 https://engineering.mercari.com/blog/entry/20251029-mercarigears2025-poster/ ポスターセッションでは、発表者の方が掲示されているポスターの前に立っているので、質問をしたり、情報交換をしたりできます。ぜひ、お気軽にお立ち寄りください。 ※常に、発表者がポスター前に立っているわけではありません。いない時間帯もありますこと、ご了承ください。 Ask the Speaker セッションは聞いて終わりではありません。各セッション終了後には、登壇者と直接お話しいただける時間を設けております。セッション内では触れられなかった詳細な内容から、率直なご質問まで、疑問を解消し理解を深めていただける貴重な機会です。 「COLLABORATION Lounge」までお越しください。 Unconference Area 事前に用意されたテーマを待つ必要はありません。ご自身が話したいトピックを持ち込み、その場で議論を始めてみませんか。メルカリメンバーと意見を交わすのはもちろん、当日に提示されるテーマをきっかけに知見を交換することも可能です。知識や経験が交差する場を、ぜひご活用ください。 「Unconference」ルームまでお越しください。 Tech Quiz メルカリのスペシャリストが用意したクイズに挑戦してみませんか。Backend、Clientなど各技術領域ごとにクイズを準備しています。普段あまり触れることのない分野でも大丈夫です。 Tech Quiz AreaにはQuiz作成者もいる予定なので、お気軽にお声がけください。また、参加者同士で意見を出し合って一緒に考えてみましょう! オリジナルグッズやお菓子 本イベント用に準備している「mercari GEARS 2025」オリジナルグッズです。 ぜひ、ステッカーを集めて「Stamp Rally Kiosk」でGETしてくださいね! 参加者同士でお話をするときのお供に、Coffee Standにて、オリジナルのお菓子やコーヒーを受け取ってください。 「mercari GEARS 2025」では、単なる情報伝達の場ではなく、オフラインイベントならではの経験を共有し、交流を通じて新たな機会が生み出されることを期待しています。 プレゼンテーション以外にもさまざまなコンテンツを準備していますので、ぜひ積極的にご参加ください。 「mercari GEARS 2025」のお申し込みは こちらから 。 イベント詳細 開催日時: 2025年11月13日(木) 11:00-18:00 概要: mercari GEARS 2025は、メルカリグループのエンジニアリング組織の技術への挑戦と、カルチャーを体感する技術イベントです。 本イベントは、単なる情報伝達の場ではなく、エンジニアたちが出会い、経験を共有し、交流を通じて新たな機会が生み出されることを目的としています。 参加費:無料 会場:TODA HALL & CONFERENCE TOKYO 参加方法: こちらのページ にてお申し込みください。 【 公式サイト 】 本イベントに関する追加情報があれば、随時 @MercariGears でお知らせしますので、気になる方はぜひフォローをお願いします。
こんにちは。株式会社メルカリ iOSエンジニアの kntk です。 9月19日から9月21日にかけて開催された「 iOSDC Japan 2025 」にメルカリはゴールドスポンサーとして参加しました。 本記事では、その参加レポートをお届けします! Swiftコードバトル 私kntkはiOSDC Japan 2025のday 0に開催された企画、 Swiftコードバトル2025 で優勝しました。 iOSDC Japan 2025の運営の皆さんには貴重な体験をさせていただき、本当にありがとうございました。 後日iOSDC Japan 2025運営から頂いた優勝記念品 Swiftコードバトルとは Swiftコードバトルはお題で指示された動作をするSwiftコードをより短く書けた方が勝ち、という競技です。 お題は決して難しいものではなく、少し練習すればSwiftプログラマであればどなたでも参加できる難易度を目指しています。 お題例1: 入力された文字列を逆順にして出力するプログラムを書いてください。 お題例2: 与えられた整数リストの要素の合計を計算するプログラムを書いてください。 Swiftコードバトル2025予選 の説明文から引用 一般的にはコードゴルフと呼ばれる競技になります。 昨年のiOSDC Japan 2024から開催されている企画で、今年が第2回目となります。 第1回目は私も参加し準優勝しました。 iOSDC Japan 2024に参加・登壇しました #iosdc #iwillblog 昨年からの変化として、問題の複雑度が上がったように感じました。その結果 回答が多様化し、戦略的な立ち回りが求められるようになった と思います。 その影響もあってか、今年は問題文と一緒にサンプルコードが提供されるようになりました。 感想 去年は決勝で敗退して悔しかったのですが、今年は問題との相性などが巡って無事優勝することができて非常に嬉しかったです。予選から接戦が多く、Swiftコードバトルの環境が成熟して来ているのを感じました。 また、 参戦することで対戦中により良い解法を思いついた際の爽快感や、逆に思い付けなかった際の悔しさを味わうことができ、Swiftコードバトルをより深く楽しむことができます。 是非皆さんも来年(開催された場合は)参戦してみてはいかがでしょうか。 Swiftコードバトルの詳しい解説や私の戦略を書いた記事もありますので、ぜひ興味があればご覧ください! Swiftコードバトル2025で優勝しました #iosdc #iwillblog 登壇 株式会社メルカリからは私がLTで登壇しました。 “奇妙”なSwift (LT5分): kntk Swiftにはさまざまな文法があり、それらは明示的・直感的で読みやすいことに定評がある一方で、Swiftの特定の文法を応用すると一見"奇妙"なプログラムも記述できます。 ”奇妙”なプログラムの背景にはさまざまなSwiftのテクニックが含まれており、解き明かすことで新たな発見を得ることができます。 このLTでは、”奇妙”なSwiftの例とそれらの背景にあるテクニックを3つ紹介しました。特に2つ目の例で紹介した、Switch文の内部で利用されているパターンマッチング演算子は利用することで冗長な表現を減らせる実用的なテクニックだと考えています。 実際にSwiftSyntaxなどの1st party libraryでも利用されています 。 また、最後の例ではSwiftコードバトルで利用できる関数呼び出しの文字数短縮テクニックを紹介しました。この構成はプロポーザル提出時に考えたものなのですが、day0のSwiftコードバトルで優勝したことによって綺麗なオチになって良かったなと思います。 メルカリスポンサーブース メルカリスポンサーブースではAIツールに関するポストイット企画を実施しました。1日1問、合計3問の質問を用意し実施しました。このセクションでは、ポストイットの回答結果を共有します! ※目視で回答の集計を行っている関係上、集計結果はおおよその値となります。 Day 0: 普段iOS開発で使っているAI Coding Assistant Toolは何ですか? 合計回答数: 約90件(1件のポストイットに対して複数回答あり) 回答数上位のツールから順に列挙すると以下の様になります。 Claude Code 月定額で利用でき、利用上限も多くコスパが良いと感じているため。 GitHub Copilot 企業が導入しているため。 学生は無料で利用できるため。 Gemini 学生は無料で利用できるため。 Cursor ChatGPT Codex Devin Amazon Q Developer Codeium Alex Sidebar Xcode用のAI Coding Assistant。 Zed Grok 私が初耳のツールも含まれており、新しいツールを知る良い機会となりました。 Day 1: iOSアプリの開発で、生成AIが最も役立つと思う作業は何ですか? 合計回答数: 約180件 次の回答が上位となりました。 テストコード生成 プロトタイプ作成 UI実装 コードレビュー 調査・コードリーディング リファクタリング また、来場者の方から具体的なプラクティスも聞くことができました。 実装より先にテストコードを生成することで、AIが特定の実装に依存したテストコードを書くのを防いでいる。(テストコード生成) AIの生成したコードは品質に疑問があるので、主にコードを読ませる用途で利用している。(調査・コードリーディング) 全体的にAIの使い方を試行錯誤しているお話が多く、さまざまな取り組みや現状の感触を共有いただきました。今後AI Coding手法が発展していくのが楽しみですね! Day 2: 生成AIがアプリ開発者の作業をどう効率化していると思いますか? 合計回答数: 約100件 全体的に作業効率化に関する回答が多く、具体的には次の項目の効率化が大きいという声がありました。 アイデア出し・壁打ち 調査 プロトタイプ 実装 レビュー 効率化の結果、「設計に集中できる」「意思決定が早くなった」というお話もありました。 私も業務でAIを使い冗長な作業が減った結果、設計に集中できるようになったと感じており、共感できるお話が多くありました。 まとめ メルカリブースに来てくださった参加者の皆さん、AIツールに関するノウハウを共有していただきありがとうございました!私自身も参加者の方とAIツールのノウハウについて話す中で大変勉強になりました。 最後に、iOSDC Japan 2025 の運営の皆様お疲れ様でした&ありがとうございました!また来年も参加したいなと思います! #iosdc #iwillblog
こんにちは。株式会社メルカリ iOSエンジニアの kntk です。 私kntkは9月19日から9月21日にかけて開催された「 iOSDC Japan 2025 」のday 0に開催された企画、 Swiftコードバトル2025 で優勝しました。 iOSDC Japan 2025の運営の皆さんには貴重な体験をさせていただき、本当にありがとうございました。 せっかくの機会なので、この記事では 私がどのような戦略やプロセスで参戦したのかを言語化 しようと思います。 後日iOSDC Japan 2025運営から頂いた優勝記念品 また、メルカリは「 iOSDC Japan 2025 」にゴールドスポンサーとして参加しており、その様子はこちらをご確認ください! iOSDC Japan 2025に参加しました #iosdc #iwillblog Swiftコードバトルとは Swiftコードバトルはお題で指示された動作をするSwiftコードをより短く書けた方が勝ち、という競技です。 お題は決して難しいものではなく、少し練習すればSwiftプログラマであればどなたでも参加できる難易度を目指しています。 お題例1: 入力された文字列を逆順にして出力するプログラムを書いてください。 お題例2: 与えられた整数リストの要素の合計を計算するプログラムを書いてください。 Swiftコードバトル2025予選 の説明文から引用 一般的にはコードゴルフと呼ばれる競技になります。 昨年のiOSDC Japan 2024から開催されている企画で、今年が第2回目となります。 第1回目は私も参加し準優勝しました。 iOSDC Japan 2024に参加・登壇しました #iosdc #iwillblog 昨年からの変化として、問題の複雑度が上がったように感じました。その結果 回答が多様化し、戦略的な立ち回りが求められるようになった と思います。 その影響もあってか、今年は問題文と一緒にサンプルコードが提供されるようになりました。 何をする競技なのか Swiftコードバトルの問題には文字数短縮余地のある 「最適化ポイント」 が複数個存在し、我々選手は 個々の最適化ポイントに対して文字数短縮方法を考え、文字数短縮を行っています。 問題が複数個の小問で構成されており、小問の合計スコアで競っている、というイメージを私は持っています。(実際には小問が相互に関係しあう場合があり、単純に可分できるわけではない。) 最適化ポイントについて、昨年の決勝問題を例に紹介します。 標準入力の各行に、ちょうど5文字からなる英単語が一つずつ並んでいます。 与えられた単語と「iOSDC」のハミング距離を出力してください。大文字と小文字は区別します。 「iOSDC」なら「0」、「CDSOi」なら「4」、「iosos」なら「4」を出力します。 すべての行についてこの手順を繰り返してください。 // 回答a while let a = readLine() { print(zip("iOSDC", a).filter { $0 != $1 }.count) } // 回答b while let a = readLine() { print(zip(a, "iOSDC").reduce(0) { $0 + ($1.0 != $1.1 ? 1 : 0) }) } この問題には結果的に以下の最適化ポイントがありました。 “iOSDC”と入力にどのようにアクセスするか? a. それぞれにインデックスアクセス b. zip でまとめてからアクセス (最短解) 異なる文字数をどの様にカウントするか? a. カウンタを持ってfor文でインクリメント b. reduce でカウンタをインクリメント c. filter.count (最短解) これらの最適化ポイントは問題に明示されていないので、まず最適化ポイントを発見する必要があります。 「ここは複数の書き方ができるな」と、一目見ただけで発見できるわかりやすいポイントもあれば、問題を解く中で問題の理解度が上がることで発見できるポイントや、間違い探しのようにじっくり考え込んでやっと発見できるポイントもあります。 最適化ポイントを発見した後は文字数短縮方法を考えて実装します。 Swiftの知識と競プロ的な知識を総合的に活用して短縮方法を考える必要があります (詳しくは後述)。 実際の回答を見てみると、回答aだと1, 2両方のポイントで最短解を採用できている一方で、回答bだと2で非最短解であるreduceを採用しており、2.の短縮方法で勝敗が決まった結果になっています。 このように、 問題に存在する最適化ポイントを発見し、最適な短縮方法を考える ことがこの競技の基本的な作業になると考えています。問題が複雑な場合は全ての最適化ポイントを発見・回答しきれず時間切れになることもあり、 最適化ポイントを発見する速さと短縮方法を考える速さも重要になってきます。 サンプルコード vs 0から実装 問題文と一緒にサンプルコードが提供されるようになったことにより、 大きな方針として二つの作戦 が存在していました。(コードバトル予選でも参加者間で話題になっていました。) サンプルコードを元に文字数短縮を行う作戦 初期実装コストがなく、個々の最適化ポイントに順に対処していけば安定してスコアを伸ばせる利点があります。その一方で、サンプルコードに存在する冗長な表現の修正に時間を要したり、既存のロジックや構造に縛られて大幅な変更をしにくいなどの欠点があります。 サンプルコードを用いず0からコードを実装する作戦 サンプルコードの構造やロジックに縛られないため、最初から一気に短いプログラムを記述して高いスコアを狙える利点があります。その一方で、初期実装コストが必要な上、テストケースが通らない間違ったコードを書いてしまい、時間を消費する危険性があります。 実際に取った作戦 私はサンプルコードを元に文字数短縮を行う作戦で進めました。理由としては後者に必要となる競プロ的な能力に自信がなかったというのと、前者の安定してスコアを取れる点が複数試合を行う上で有利だと考えたためです。 対戦前の準備 競プロ等でも一般的に有効な手法だと思いますが、 傾向を把握し、その対策を行っていました。 事前に頻出パターンとその短縮方法(テクニック)調査して知識化し、対戦中はそれらの知識を適用できるようにしていました。これによって、問題に存在する複数の最適化ポイントのうち 8-9割程度を事前知識で瞬時に発見して解くことができ、対戦時間を残りの1-2割(初見のパターンや一般化が難しい複雑なパターン)への対処に集中できていました。 具体的にどのような短縮テクニックを調査・利用していたのか、実際の準決勝の問題を例に紹介します。 標準入力の各行に、英字のみからなる文字列が与えられます。キャメルケースをスネークケースに変換し、改行区切りで出力してください。 出題側から提供されるサンプル回答(読みやすさのため一部変更) while let line = readLine() { var result = "" for (index, character) in input.enumerated() { if character.isUppercase && index > 0 { result.append("_") result.append(character.lowercased()) } else { result.append(character.lowercased()) } } print(result) } この問題では次のような文字数短縮テクニックが利用できます。 String.append(Character) は += で代替可能 RangeReplaceableCollection の実装由来。 String は RangeReplaceableCollection に準拠しているため。 If aaa && bbb は if aaa, bbb に代替可能 変数名の一文字化 共通項の抜き出し( r += c.lowercased() ) それらのテクニックを利用したプログラム while let l = readLine() { var r = "" for (i, c) in l.enumerated() { if c.isUppercase, i > 0 { r += "_" } r += c.lowercased() } print(r) } その他、多数のテクニックをドキュメントにまとめておき、対戦前に眺めていました。 また、 「この手法では短縮できない」という知識も持っておくことで、対戦中の試行錯誤時におけるノイズを減らしていました。 例を一つ紹介すると for 文より .forEach で書いた方が短い?-> for文の方が常に短い。 for i in a { と a.forEach { i in を比較すると後者の方が5文字多くなります。 また、 forEach のクロージャーで省略引数名 $0 を使った場合 i in を省略できますが、ここでの省略数は3文字なのでメソッド呼び出し部分だけで結果に2文字多くなり赤字です。 // 8文字 for i in a { // 13文字 a.forEach { i in // 10文字 a.forEach { 対戦中 先述の通り、対戦中は冒頭に事前知識を用いて頻出のパターンの文字数短縮を行い、 ほとんどの時間を初見パターンや一般化が難しい複雑なパターンの文字数短縮方法を考えることに集中していました。 具体的に文字数短縮方法を考えるプロセスについて紹介すると、 各最適化ポイントについて、 文字数短縮方法には主に二つの側面 があると考えていました。 A: 文法・記法を工夫する B: ロジックを工夫する A: 文法・記法を工夫する 既存の記述の文法や記法を工夫することで文字数短縮を行う側面 です。先述の準決勝での例にあるように変数名を一文字にしたり、 String.append(Character) を += に置き換えたりするなどが当たります。 また、予選の問題ではSwiftの型推論や標準ライブラリの関数・メソッドを応用して文字数を減らす場面がありました。(Swiftらしさが強く現れる問題で特に記憶に残っています。) コードバトル予選、ROT13変換問題の私の回答 while let p = readLine() { print(String(p.map { a in let k = a.asciiValue! &- 52 return a.isLetter ? .init( .init( 13...38 ~= k ? k % 26 + 65 : (k - 32) % 26 + 97 ) ) : a })) } Character.init(UnicodeScalar.init(...)) を .init(.init()) で代替 型推論によって型名を省略 String.init の期待する型( [Character] )が p.map の帰り値の型を経由してクロージャーのreturn statementの型推論まで伝播 Range.contains を ~= で代替 標準ライブラリに演算子定義が存在 オーバーフロー演算子 &- を利用して事前計算 通常の演算子だとランタイムエラーになるテストケースが存在 このように、 Swiftの知識(型推論・文法・言語機能・標準ライブラリなど)が活かされる側面 です。Appleの公式ドキュメントや TSPL (The Swift Programming Language) を読むことでこの側面の思考力が鍛えられると思います。 B: ロジックを工夫する 既存のロジックをより短い記述のロジックに変えることで文字数短縮を行う側面 です。こちらは平たく言えば競プロ的な側面です。ただし、実行速度向上ではなく文字数短縮が目的です。 実は先述した準決勝の私の回答は最短解ではなく、さらにロジックを工夫することで短縮できる余地がありました。(前回優勝者にご指摘いただきました。) while let l = readLine() { var r = "" for c in l { if c.isUppercase, r != "" { r += "_" } r += c.lowercased() } print(r) } index > 0 というのは、つまり「最初以外の要素の時」を意味する条件なので、こちらは r != "" で代替可能なわけです。また、コードバトル予選では有名な「番兵法」を利用することで文字数を大幅に減らすシーンもありました。 このように、 問題・プログラムの意図を読みとるのがこちらの側面になり、競プロ的な力が活かされる側面 です。競技プログラミングサイト等で過去問を解くことで、この側面の思考力を鍛えることができると思います。 また、AとBの側面は排他的ではなく、Bを適用した後の記述に新たにAが適用できる場合もあり、 AとBの組み合わせによって文字数短縮方法が多数存在していました。 戦略 A・Bの例で示したように、 より短い解に辿り着くにはAとB両方の側面を組み合わせて解法を考えることが必要になってきます。 しかし、解法を考える際はコードを書いてみないと実際に文字数短縮が可能か分からないことが多く、制限時間の中で試せる解法の候補数も限られてくるため、 ある程度解法に”あたり”を付ける必要があります。 ここで、 私は迷ったらAを優先して攻める戦略を立てました。 理由としては私がAの方が得意というのと、「Aの側面を極めた回答の方がSwift特有のテクニックが現れて面白いだろう」と考えたからです。 結果的に決勝でもSwiftの文字列展開を利用した手法を選択し、Swiftらしいコードで優勝ができたのは良かったなと考えています。 アスキーアートを出力する決勝問題の私の回答(見やすさのため一部スペースを省略) let a = "######", i = a + a, f = "\(i)##", j = f + f (中略) print( """ \(f) \(j) \(f) \(j) ## \(a) \(a) ## \(a) \(a) \(a)#### ## \(a) \(f+a) \(a) \(a)#### ## \(a) \(f+a) \(a) (中略) """ ) まとめ Swiftコードバトルは問題に存在する最適化ポイントを発見し、Swiftの知識と競プロ的な知識を総合的に活用して最適な短縮方法を考える競技です。制限時間があるため、事前に頻出パターンの対策を行うことによって対戦時間を有効に活用することができます。 Swiftコードバトルのテクニックは一見奇妙で役に立たなそうに見えるかもしれません。しかし その背景にはSwiftの文法・言語機能の知識やロジックを読みとる力などが隠れており、それらは日々の開発でも有益なものだと考えています。 自分も参戦する中で多くを学ぶことができました。 また、参戦することで対戦中により良い解法を思いついた際の爽快感や、逆に思い付けなかった際の悔しさを味わうことができ、Swiftコードバトルをより深く楽しむことができます。是非皆さんも来年(開催された場合は)参戦してみてはいかがでしょうか。 最後に、改めてiOSDC Japan 2025・Swiftコードバトル2025 の運営の皆さん、予選・本戦で対戦してくれた選手の皆さん、本当にありがとうございました! #iosdc #iwillblog
こんにちは!メルカリ Engineering Office の @mikichin です。 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます! 2018年に実施した「Mercari Tech Conf 2018」から7年の時を経て、久しぶりのオフラインでの開催となります。 テーマは「メルカリエンジニアリングの今」。 今年の全社的なテーマでもある「AI-Native」についてはもちろん、2018年以降メルカリグループのエンジニアリングがどのように変化してきたかを、技術・組織・カルチャーの観点からご紹介します。 オンライン配信はありませんので、ぜひ会場でご自身の目と耳で確かめてください!! また、今回はプレゼンテーションだけではなくポスターセッションもあります。 ポスターセッションでは、発表者の方が掲示されているポスターの前に立っているので、質問をしたり、情報交換をしたりできます。ぜひ、お気軽にお立ち寄りください。 ※常に、発表者がポスター前に立っているわけではありません。いない時間帯もありますこと、ご了承ください。 本記事では、会場でしか見れないポスターセッションを一挙ご紹介! プレゼンテーションの紹介は下記記事をご参照ください。 PASSION Stageのセッション紹介は こちら 。 GROW Stageのセッション紹介は こちら 。 MECHANISM Stageのセッション紹介は こちら 。 まだ申し込みをされていない方も、興味のあるセッションがあるはずです。お申し込みは こちら からお願いします。 メルカリグループにおけるAI-Nativeなインシデント管理の全貌と未来像 LLMの普及により、インシデント対応・管理のあり方も大きく変わりつつあります。 メルカリグループでは、複雑で負担の大きいインシデント管理を「AI-Native」に進化させることを決定しました。 すでに導入している「IBIS」をはじめ、その周辺の仕組みや他のAI活用事例も紹介します。 AIを取り入れることで、MTTRの短縮だけでなく、対応者の負担・ストレス軽減やコスト削減、さらにサービス信頼性の向上が期待できます。 ただし、人間が担うべき領域も残ります。本発表では、メルカリグループの現在の取り組みと今後の展望をお伝えします。 The 3A’s: Simple Steps For Clean Unit Tests ソフトウェア開発のスピードが加速する中、新機能の追加や修正には、既存の機能を意図せず壊してしまうリスクが常に伴います。適切な安全策がなければ小さなミスが本番環境にリリースされ、多くのお客さまに影響を及ぼす可能性があります。 だからこそ、ユニットテストは極めて重要です。優れたユニットテストはコードの動作を検証するだけでなく、プロダクトの安定性と信頼性を確保し、開発チームが自信を持って変更を行える環境を支えます。 では、どうすればテストをシンプルでクリーン、かつ効果的に保つことができるのでしょうか。 そのための実績あるアプローチのひとつが、Arrange(準備)、Act(実行)、Assert(検証)の3Aフレームワークです。この3つのステップに従うことで、明確で保守性が高く、信頼できるユニットテストを簡単に書けるようになります。 Autonomous Support – Leveraging AI Bots for Scalable and Intelligent Operational Assistance 現在、エンジニアはSlack上での場当たり的かつ反復的な質問対応や、絵文字によるトリアージといった非効率なワークフローに多くの時間を費やしており、チームによっては業務時間の10〜20%以上が問い合わせ対応に割かれるという課題があります。私たちはこの課題に対し、Slack上の雑多な問い合わせを迅速で信頼性の高い回答と標準化されたチケットに変換する、AI支援の自律型サポートシステムを構築しています。このbotはSlack上で直接対応を行い、適切なJIRA/GitHubチケットのトリアージや共有ナレッジベース(ドキュメント、過去のチケット、Slack、ソースコード)の検索を行い、回答を提案します。人間の対応が必要な場合は担当者へ引き継ぎ、自己完結可能な場合はその場で対応を完了し、そこから学習を重ねます。これにより、応答と解決の迅速化、中断の削減、そして自律解決率、エスカレーション率、削減できたエンジニア工数、CSAT(顧客満足度)、特定されたナレッジギャップといった指標を通じて効果を測定します。また、既存のプラットフォームツールを再利用することで、迅速な開発を可能にしています。 Toward a Global Identity Platform メルカリは2019年に越境EC事業をスタートしました。開始当時、海外のお客さまは機能の限られた代行業者の購入ページを使って商品を検索し、購入する必要がありました。その後、海外のお客さまにより良い購買体験を提供するため、メルカリはシステムを拡張し、海外でのサービス展開を始めました。この拡大において重要な要件がグローバルアカウントの導入です。本セッションでは、これまでの取り組みで達成した成果を共有するとともに、複数の国のお客さまをサポートするためにアイデンティティ基盤をさらに拡張していく今後の計画についてお話しします。 非エンジニア組織のAI-Native化に伴走した実践知 非エンジニア組織が「AI-Native」へと変革する過程には、エンジニアの伴走が不可欠です。 本セッションでは、そのリアルな経験を紹介します。 (1) AI活用で期待する結果を得るために入出力形式を工夫した事例 (2) AIワークフローが突然停止した事例から学ぶライフサイクル管理の教訓 (3) AI生成アプリをGASを使って安全にデプロイする手法 といった、AI活用をプロトタイプから実運用への導くための実践知を共有します。 From Cluttered to Clear: Improving the Web Accessibility Design for Screen Reader Users in E-commerce With Generative AI 視覚障がい者や弱視のユーザーは、スクリーンリーダーを使ってオンラインショッピングサイトを利用する際に、多くの障壁に直面しています。複雑なレイアウト、不明確なコンテンツ構造、視覚重視のデザインにより、特に初めて利用するサイトではストレスが多く非効率な体験となっています。これまでの支援ツールは商品説明や画像の代替テキストなど、個別の要素に焦点を当てられてきましたが、ページ全体におよぶ構造やナビゲーションの課題には十分対応できていませんでした。本研究では、生成AIを活用してショッピングサイトのHTMLコンテンツを自動的に再構成し、アクセシビリティを改善する方法を検討しました。研究は3段階の構成で行い、まずスクリーンリーダーのユーザーにインタビューを行い、次に生成AIを搭載したブラウザ拡張機能を開発し、最後に自動監査と実際の使用で評価を行いました。このツールはスクリーンリーダーの操作パターンに合わせてウェブコンテンツを動的に整えます。実験の結果、生成AIで再構成されたページではナビゲーション効率、コンテンツの分かりやすさ、全体的な使いやすさが大きく改善することが分かりました。参加者からは「セクションの順序がより論理的になった」「閲覧による疲労が減った」といった声が寄せられました。この成果は生成AIが既存サイトの構造そのものに作用することで、包括的かつユーザー中心のアクセシビリティ改善を実現できる可能性を示しています。 量子インターネット ー安心・安全でサステナブルなオンライン社会の実現に向けてー メルカリの研究開発組織「R4D」では、近い将来に到来が予想されている「量子前提時代」を生き抜くメルカリを作るために、量子情報通信技術に関する研究を行っています。本ポスターでは、R4Dの量子チームが国内の研究機関と共同で行なっている量子インターネットに関する研究開発の全体像について紹介します。 中性原子量子コンピュータにおける表面符号を用いた消失エラー耐性プロトコル 光ピンセットによる中性原子アレイは、その優れた特性から量子コンピュータの有望な候補とされていますが、非パウリ誤り、消失誤り、リーケージ誤りが大きな課題となっています。従来の研究では、リーケージ誤りを消失誤りに変換できることは示されていましたが、変換された消失誤りが継続的に発生・蓄積する問題が残っていました。本研究では、この課題に対して、脱分極誤りと消失誤りを含む回路ベースのモンテカルロシミュレーションにより、消失誤りが平面コードに与える影響を評価しました。さらに、消失誤りへの耐性を高めるための新しいプロトコルを提案します。このプロトコルでは、オンラインでのコード変形を利用して、消失誤りが蓄積した「トラップ」から論理量子ビットを新しいトラップへと転送します。 「使い続ける力、未来をつなぐ」 人が使わなくなった製品を一度で使い終えず、次の誰かへ繋ぐ「リユース」は、私たち消費者が持続可能な社会のために選べる、最も身近で実践しやすい選択肢の一つです。本プレゼンテーションでは、リユースによって製品の使用期間がどれほど延長され、新品の購入をどれだけ代替できるのか、そのインパクトを明らかにします。 このお話を通して、皆様がモノを手放すとき、あるいは新しいモノが欲しくなったときに、リユースを当たり前の選択肢として生活に取り入れ、「モノに隠された新たな価値」を実践していただくためのきっかけをご提供します。 オンラインフリマアプリにおける商品説明の人間–AI協働執筆:売り手および買い手の視点からの考察 オンラインのフリマアプリは、個人が中古品を他の個人に販売する手段として、特に日本で人気を博しています。出品のプロセスには、出品者が写真をアップロードし、潜在的な購入者が閲覧するための商品説明を書く作業が含まれます。近年は、大規模言語モデル(LLM)を用いた商品説明作成を通じて出品時の出品者の負担を軽減する、人間とAIの協働の応用領域が注目されています。本研究では、LLMに基づく支援が出品者の体験や商品の出品価格に与える影響に加え、人間とAIが協働して作成した商品説明の魅力に対する買い手の主観的な印象や選好にどのように影響するかを検討します。本研究の結果は、LLMベースのツールがオンライン中古市場に及ぼし得る影響の理解に寄与するとともに、フリマアプリ向けの人間—AI協働執筆システムの設計上の考慮点や今後の研究の方向性に関する示唆を提供します。 メルカリAdsにおけるAIを活用した広告審査の取り組み 2024年9月に開始したメルカリAdsは、当初広告主から入稿いただいている広告に対しての審査を手動運用にて審査していました。 効率的な審査を模索し、AIを活用した広告審査システムを構築することで運用コストを削減しつつ、多くの広告素材に対して審査することが可能となりました。 そのAI活用事例を共有します。 BFF保守の課題とgRPC Federationによる解決アプローチ マイクロサービスアーキテクチャにおけるBFF開発では、複数サービス間の型変換や依存関係管理により保守コストが増大します。本発表では、この課題に対してgRPC Federationを採用し、Protocol BuffersにDSLで定義することによってBFFを自動生成し、保守コストを大幅に削減した実例を紹介します。また、AIを活用したDSL記述支援の取り組みについても報告します。 Engineering Office is a Hub, connecting Engineering together Engineering Officeはエンジニアリング部門のあらゆる部分を繋ぐハブの役割を持ちます。共通のオンボーディングからプロジェクトのサポート、エンジニアリングに関する情報まで、さまざまな要素を通してグループが方向性を合わせ、集中できるようにします。 本発表では、自動化やAI、継続的なサービスライフサイクルを活用してビジネスニーズに迅速に対応し、メルカリ独自のエンジニアリング文化を維持するための、私たちのプロジェクトの一部をご紹介します。 Mercari における Recommendation System の概要 メルカリではホームスクリーンや商品詳細画面など様々な所で recommendation が行われており、その裏側にはそれぞれの特性に応じた技術が使われています。 本発表ではメルカリにおける様々な recommendation とその裏で使われている技術について概要を共有し、議論を通じて情報交換したいと考えています。 「mercari GEARS 2025」のお申し込みは こちらから 。 イベント詳細 開催日時: 2025年11月13日(木) 11:00-18:00 概要: mercari GEARS 2025は、メルカリグループのエンジニアリング組織の技術への挑戦と、カルチャーを体感する技術イベントです。 本イベントは、単なる情報伝達の場ではなく、エンジニアたちが出会い、経験を共有し、交流を通じて新たな機会が生み出されることを目的としています。 参加費:無料 会場:TODA HALL & CONFERENCE TOKYO 参加方法: こちらのページ にてお申し込みください。 【 公式サイト 】 本イベントに関する追加情報があれば、随時 @MercariGears でお知らせしますので、気になる方はぜひフォローをお願いします。
1. イントロダクション こんにちは、Cross Border(XB) Engineeringのバックエンドエンジニアのosari.kです。本日は私が所属するリーダビリティチームの活動と、具体例として開発したバックエンドの共通パッケージについて紹介します。 メルカリグローバルアプリは、開発複雑性を抑えながら拡張性を保つためモジュラモノリスアーキテクチャを採用しています。モジュール間の依存関係を厳格化するため、システムはBFF層とTier1-4の階層構造で構成され、リクエストは上位から下位Tierへ流れます。モジュール間通信はProtocol Buffer + gRPCで標準化されています。詳しくは ブログシリーズ をご参照ください: グローバル展開にむけたアプリと基盤の再構築 グローバル展開を支える基盤の裏側 しかし、モジュラモノリスを採用するだけでは、マイクロサービス開発で発生した課題を解決できません。サービス間の差異によるメンテナンスコストやオンボーディングコストの増加は、モジュール間でも同様に発生する可能性があります。これらを解決するには、コードのリーダビリティと一貫性の維持が不可欠です。 そこで、グローバルアプリ開発の当初から リーダビリティチーム が組成されました。このチームは、モジュラモノリスの利点を最大限活かすため、Backendシステム全体のコードのリーダビリティを改善・維持する役割を担っています。メンバーは、アーキテクト、SREメンバー、バックエンドエンジニアで構成され、日本とインドの両拠点から参加しています。 一貫性のあるコードを維持することで、開発者を柔軟にアサインでき、効率的な配置が可能になります。また、AI Agentを活用した開発においても、明確なガイドライン、自動チェック、一貫したコードベースがAIの活用効果を高めます。 2. リーダビリティチームの役割と活動 2.1 目的 リーダビリティチームの主な目標は、コードの可読性を向上させることです。可読性が高く、一貫性のあるコードは、以下の効果をもたらします: 新しいエンジニアの学習曲線を緩和し、オンボーディングを加速 組織内での担当変更やチーム間の貢献を円滑化 バグの検出や修正を容易にし、開発の品質とスピードを向上 2.2 活動内容 リーダビリティチームは以下の活動を通じて、コードの品質向上と開発スケールを支援しています: コードレビュー : 複雑度の高いPRを中心にレビュー ガイドラインの作成と維持 : コードおよびPRガイドラインをGitHubリポジトリに集約 自動化ツールの開発 : CIによる自動チェックを実装 ワークショップの開催 : ベストプラクティスの共有 隔週ミーティング : ガイドライン、課題、改善点の議論 メトリクス駆動の改善 : 開発ボトルネックの特定と継続的な改善 中でもメトリクス駆動の改善については、GitHubのメトリクスを活用して目標を設定し、定期的にボトルネックを分析しています。特に以下の2つのメトリクスの改善に取り組んできました: 一人あたり週何個のPull Requestをマージできたか Pull RequestをOpenしてからマージまでの時間 これらを可視化することで、開発プロセスのボトルネックを特定し、継続的な改善につなげています。 3. AI時代のスケーラブルな品質管理 開発初期はメンバーが少なく、リーダビリティチームがほぼ全てのPull Requestをレビューできていました。しかし、開発が本格化し、開発メンバーの増加やAI Agent活用の普及により、PRのボリュームが急増し、従来の方法では対応できなくなってきました。 幸い、ガイドラインをGitHubリポジトリに集約・維持していたため、LLMを活用したレビューの自動化基盤は整っていました。課題は、この基盤でどうスケーラブルに品質を担保するかでした。以下、具体的な取り組みをいくつか紹介します。 Claude活用 ClaudeでPull Requestを複雑性とサイズの観点から自動分類し、ラベル付けを行っています。Claudeは、PRをより小さい単位に分割する助言も提供します。 このラベルを使い、複雑度が高いPull Requestを優先的にレビューできるようになりました。 また、GitHubに集約したガイドラインを活用したClaudeによるレビューの自動化で、コードレビューとガイドラインの適用を効率化しています。 効率的レビュー リーダビリティチームのレビューは必須ではありません。少なくとも一人のコードオーナーからレビューをもらえばPRはマージできます。これにより、リーダビリティチームが開発のボトルネックになりません。 一方で、マージされたPRの中で複雑度が高いものは後追いでレビューします。こうした複雑なPRから課題を検知し、新しいパターンやライブラリを導入することで、今後の開発を改善していきます。このアプローチで、効率的にレビューリソースを配分し、最も重要な部分に注力できています。 自動化 ガイドラインを全て把握するのは困難なため、機械的にチェック可能なルールはCIに組み込んでいます。 例えば、データベースのスキーマ設計では、社内のDatabase リライアビリティエンジニアと相談し、 PostgreSQL ガイドライン などを参考にガイドラインを策定しました。これを SQL Fluff のプラグインとして実装し、自動チェックを行っています。 これにより、レビュアーの負担を軽減し、人間のレビュアーはより高度な設計判断やアーキテクチャの妥当性に集中できます。 4. Feature Flag伝搬システムの設計 ここからは、リーダビリティチームが設計した品質管理の具体例として、Backendシステム全体で利用されるFeature Flag伝搬の仕組みを紹介します。 4.1 背景: Experiment Platformとモジュラモノリスアーキテクチャ グローバルアプリでは、新機能のリリースを安全に行うため、メルカリの基盤システムである Experiment Platform を利用しています。この中でもFeature Flagを活用しており、お客さまごとに各種機能のオン・オフを管理できます。 Feature Flagを使うことで、Web UI上で機能をオンにするお客さまを段階的に増やせるため、新機能のリリースを様子を見ながら進めることができます。問題が発生した場合も、システム自体を再デプロイすることなく、Web UI上で機能をオフに設定し、即座に無効化できます。Experiment Platformは割当(Assignments)を管理し、システム側でこの割当を取得して実装に組み込みます。 Feature FlagはMobileアプリやWebアプリでも利用されています。これらのクライアントアプリでは、1人のお客さまが継続して利用するため、アプリ起動時などにFeature Flagを取得してキャッシュできます。一方、バックエンドシステムではリクエストごとにお客さまが異なるため、Experiment PlatformのAPIを毎回呼び出してデータを取得する必要があります。 前述の通り、グローバルアプリのBackend systemではモジュラーモノリスを採用しており、クライアントアプリからの1リクエストが内部的には複数のモジュールへのリクエストを発生させます。 図1. 単純化した商品ページの例 全てのモジュールがFeature FlagのAssignmentsを必要とする可能性があります。 このようなアーキテクチャのため、もし各モジュールが毎回Experiment Platformからデータを取得する場合、APIのレイテンシがその分増加してしまいます。 図2. 各モジュールがExperiment Platformを呼び出すとレイテンシが増加 グローバルアプリの開発が進み、Feature Flagを利用する状況になってきた中で、レイテンシの増加を防ぐため、効率的にAssignmentsを参照するメカニズムとガイドラインが必要になりました。 4.2 設計要件と実装 Assignmentsの効率的な参照メカニズムを設計するにあたり、以下の要件を定義しました: Experiment Platformを一度だけ呼び出す ネットワークトラフィックを抑える 割当に型安全なアクセス手段を提供する 既存のコードが割当を使いたくなった場合のコードの変更量を最小限にする これらの要件を満たすために、 最終的に採用した解決策は、以下の3つの要素を組み合わせたものです: gRPC metadataを使ってモジュール間のAssignments伝搬 : シリアライズしたAssignmentsをgRPC metadata(HTTP Header)で運ぶ BFF層のServer InterceptorがExperiment PlatformのAPIを呼び出し、Assignmentsを取得 各モジュールのClient interceptorがAssignmentsをシリアライズしてgRPC metadataに設定 BFF層以外のServer InterceptorがgRPC metadataから取得し、デリリアライズしてAssignmentsを取得 context.Contextを使ってモジュール内のAssignments伝搬 : Server InterceptorがAssignmentsを context.Context に格納 アプリケーションロジックはAssignmentsを context.Context から取得 Protocol Bufferでの型定義 : Assignmentsを明示的に定義し、型安全性を確保かつコンパクトなシリアライズを実現 図3. BFFがExperiment Platformを呼び出し、AssignmentsをgRPC metadataで伝搬 BFFモジュールがExperiment platformのAPIを呼び出しAssignmentsを取得し、それを下位Tierのモジュールを呼び出すときにgRPC metadataで伝搬しています。 先程の図と異なり、BFFのみがExperiment platformを呼び出すため、レイテンシへの影響を抑えることができています。 モジュール内の伝搬に context.Context を利用しているので、アプリケーションロジックがAssignmentsを参照したくなった場合も、コードの変更は最小限に抑えられます。 一方でAssignmentsをリクエストに付与することでネットワークトラフィックには影響があります。そこで効率的なシリアライズ方法が必要です。 Experiment PlatformのAssignmentsは、パラメータ名と値のkey-value形式です。Feature Flagの値は "true" 、 "false" 、または未割り当ての3つの状態を取ります。未割り当ては、段階的リリースでお客さまの一部のみが割当対象になっている場合に発生します。 例えば以下のような形式です("feature_flag1"は未割り当てのため含まれていない): { "feature_flag2": "false", "feature_flag3": "true", "foo": "10", "bar": "OK" } 当初は map[string]string のJSON文字列としてシリアライズする案を検討しました。しかし、この方法では パラメータ名の長さがシリアライズ結果に影響し 、数百〜数千のパラメータをサポートする場合に破綻します。 そこで、 AssignmentsをProtocol Bufferで明示的に定義し、シリアライズする ことにしました。 上記の例をProtocol Bufferで定義するとこのようになります: message Assignments { optional bool parameter1 = 1; optional bool parameter2 = 2; optional bool parameter3 = 3; optional int64 foo = 4; optional string bar = 5; } この方法により、以下の利点が得られます: コンパクトなシリアライズ結果 : パラメータ名はシリアライズ後のバイト長に影響を与えることがなくなり、Experiment Platform利用者はわかりやすい名前を使うことができます 型安全性 : optional をつけることで、未割り当ての場合とfalseを区別できます。 *bool 型とすることで、Experiment Platformの利用時に陥りやすい罠である、未割り当て( nil )と false の区別が明示的になりました Protocol Bufferで明示的に定義する方法は、新規パラメータ追加時にProtoファイルの変更が必要という欠点があります。しかし、どちらの方法でもGoコードの変更とデプロイは必要です。一方で、 どのパラメータがどこで利用されているか明確に把握できる 利点を我々は重視しました。 4.3 検討の詳細 ここでは設計で候補案として上がったものとの比較内容を通じて検討の詳細の一部を紹介します。 4.3.1 in-memory cache案 gRPC metadataを使ったモジュール間の伝搬以外の方法として、in-memory cacheを利用したデータ共有という案がありました。 Experiment Platform ClientをProxyしてin-memory cacheのデータを返すことで、API呼び出しのレイテンシを削減する方法です。 in-memory cacheの場合、モジュール間でAssignmentsの伝搬をする必要がないため、ネットワークトラフィックを抑えることも可能です。 特に、多くのモジュールがAssignmentsを利用しない場合にgRPC metadataを使う方法に対して優位性があります。 図4. in-memory cacheを利用した方法(BFFとTier2 ProductだけがAssignmentsを利用する場合) 最終的には、Protocol bufferを利用したassignmentsのシリアライズによりネットワークトラフィックの優位性が小さいこと、 分割して運用が可能なモジュラモノリス を実現していく中で、モジュール間でcacheが共有されないケースが増えていくことが予想されるため gRPC metadata を利用した方法を選択しました。 4.3.2 明示的なリクエストフィールド案 gRPC metadataではなく、Protocol Bufferで明示的なリクエストフィールドとして定義する案も検討しました。グローバルアプリBackendでは、API仕様の可視性や型安全性の観点から、一般的には明示的なフィールドを選択しています。 しかし、この方法には全てのモジュールの全てのエンドポイントにAssignmentsフィールドを定義しなければならないという問題があります。Tier2のProductモジュールがAssignmentsを必要とする場合、中継するだけのTier1のエンドポイントにも同様のフィールドが必要になります。すでに多くのエンドポイントが開発されており、全てに修正を加えるのは現実的ではありませんでした。 また、Protocol Bufferを利用することでgRPC metadataの長さも十分小さくなることが分かったため、gRPC metadataによる伝搬を選択しました。 4.4 将来対応 gRPC metadataを使うため、Headerのサイズ制限に引っかからないよう対策を講じています。ユニットテストで最大バイト長になりうるシリアライズ結果がしきい値を超えていないか検知できるようにしています。 現在は全てのパラメータが optional bool 型であるため非常にコンパクトですが、しきい値を超えた場合の対応策として、どのモジュールがどのパラメータを参照しているかを静的に解析し、必要なパラメータだけを伝搬するように機能を拡張する予定です。Protocol Bufferで明示的にパラメータを定義しているため、Assignmentsの参照の解析が容易になります。これも、明示的な定義を選択した利点の一つです。 5. まとめ 本記事では、メルカリグローバルアプリのリーダビリティチームの活動と、Feature Flag伝搬システムの設計について紹介しました。 リーダビリティチームは、モジュラモノリスアーキテクチャの利点を最大限活かすため、コードの可読性と一貫性を維持し、開発をスケールさせる役割を担っています。GitHubメトリクスを活用した継続的な改善、Claudeによる複雑度分析、CIによる自動チェックなど、様々な活動を通じて品質を担保しています。 Feature Flag伝搬システムでは、以下を重視しました: Protocol Bufferによる明示的な型定義 : 型安全性と利用状況の可視化 gRPC metadataとcontext.Contextによる透過的な伝搬 : 既存コードへの影響を最小限に これらの設計判断は、技術的な実装の問題だけでなく、開発チーム全体のスケーラビリティを考慮したものです。 AI時代において、ガイドラインの明文化、自動チェック、一貫したコードベースの維持は、AI Agentを活用した開発の重要な基盤となります。リーダビリティチームは、開発の品質とスピードの両立を目指して活動を続けていきます。 最後までお読みいただき、ありがとうございました。
こんにちは。検索領域でエンジニアをやっております、shinpeiです。 本記事は 連載企画:メルカリ初の世界共通アプリ「メルカリ グローバルアプリ」の開発舞台裏 の一環として、メルカリグローバルアプリの検索バックエンドをスクラッチで開発することに伴い、大事にした設計のポイントをご紹介します。また今回の新たな要求を契機に既存の検索基盤の拡充が必要だったのでそれについても書かせていただきました。 グローバルアプリでの検索の要件と課題 先日、弊社からの発表の通り、メルカリはグローバルアプリの提供を開始しました。これに合わせて、検索もグローバルな検索を作るべく準備をすすめてきました。 グローバル展開にむけたアプリと基盤の再構築 で紹介したようにグローバルアプリを提供するにあたりバックエンド基盤の再構築を行っています。基盤を活かしながら必要な部分は新しく作り直すアプローチをとっており、検索バックエンドもそれに合わせて再設計を行いました。最低要件は、3年で50カ国展開。出品数は累計40億です(2025年9月時点)。日本事業(日本のお客さま向けメルカリアプリ)の検索との違いは複数ありますが、主なものは以下です。 複数国展開をするための多言語対応 検索対象として、アイテムモデルとプロダクトモデルとの両立 越境事業においてフォーカスしていく際のエンタメホビー領域に特化できるような独自の検索結果チューニング 1.は日本語以外を扱うための要件です。世界展開となれば、言語の壁は避けては通れない課題です。全文検索はそれぞれの言語自体を処理する必要があります。中国語のクエリ、英語のクエリ、フランス語のクエリ。これらを商品情報とマッチさせる必要があります。現在は日本で出品された日本語の商品をメインに扱っていますが、長期的には日本語以外の商品を扱う可能性も考慮する必要がありました。 2.は検索エンジンで検索対象として扱うデータモデルの話です。アイテムとは、現在のメルカリの主流である、単一の商品が検索対象となるようなデータモデルです。一方、プロダクトモデルとは、一つの検索対象だけど、例えば色違い(バリアント)が複数存在させることができるようなデータモデルです。C2Cの商品はアイテムモデルで対応できるのですが、B2Cの商品の一部はバリアントを持つため、プロダクトモデルが必要になります。メルカリはC2Cマーケットプレイスとしてスタートしたため、日本事業の検索では今もなお、アイテムモデルで動いていますが、グローバルアプリではアイテム、プロダクト双方を同等に扱える必要があります。 3.はグローバルアプリは検索結果を独立してコントロールしたいという意味です。グローバル展開を考えたときに国ごとのチューニングや事業の方向性に合わせた柔軟性が必要になります。例えば、クエリを自分たちで組み立てて、自由に検索結果をチューニングもできるように作る必要があります 。検索は日本事業と越境事業両方で重要な機能であり、開発の優先度やリソースなどい違いに影響を受けないようにすることも重要です。 Vertex AI Search for Commerce の採用 日本事業の検索ではElasticsearchを使っています。グローバルアプリでは日本にある商品全体を扱う予定だったので、売上に比べてデータ量が桁違いに大きい状態です。売上とコストを両立させるにはElasticsearchの再利用くらいしか思いつきませんでしたが、多言語対応や独立性の確保、リリースまでの時間と開発リソースを考えると、どうしてもその前の交通整理に長い時間がかかります。どうやるか思案していた時に、思いがけず案に上がってきたのがGoogleが提供してるVertex AI Search for Commerce(以下、VAIS:C)です。今回の例では以下の点で魅力的でした。 フルマネージドな小売サイト向け検索サービス リクエストベースの課金モデル(トラフィックがなければ課金されない) 多言語対応あり 入力するデータはプロダクトモデル(バリアントあり) ある程度の検索結果チューニングが可能 ユーザーイベントベースの検索結果最適化あり グローバルエンドポイント(世界中から使える) これだ!と思いました。要件とVAIS:Cができることを見比べていただければわかりますが、グローバルアプリにとって必要な要求をほぼ満たしており、ネックなのはコストだけです。そこで我々が立てた戦略は以下です。 VAIS:Cを導入し、素早くスモールスタートする トラフィックが伸びてきて、ペイできないレベルになれば、Elasticsearchに切り替える この方法であれば、Elasticsearch側をグローバルアプリに適用するために必要な準備期間も稼げると思いました。一方で、ビジネス側の懸念は現在の日本事業の検索と、同様の売り上げを得る検索結果をだすことができるかどうか、でした。そこはやってみなければわからなかったので、A/Bテストを行うことになりました。 簡単にVAIS:Cを説明しますと、商品情報と、ユーザーイベントを入れると、最適な検索結果が返ってくるというものです。最適化に必要となるユーザーイベントは、Google Analytics 4(以下、GA4)で収集したものが前提となってましたが、スキーマさえ合わせれば独自イベントの入力も可能でした。メルカリはGA4ではなく、独自のイベント基盤を持っているため、なんとか変換して動かしました。(Google Cloud Japanの皆様には多大なるご協力をいただきまして、大変お世話になりました。) 結果的には、我々の商品情報と、なんとか変換したユーザーイベントでは、日本事業の検索よりも結果が劣ることを確認できました。また、後述するいくつかの要件を満たすことが難しいこともわかりました。ですが、このリスクを補ってあまるメリットがあるのも事実です。(これを全部、私一人でできてるのですから!)検索エンジンが変わることでのリスクは合意した上で、グローバルアプリの初期リリースはVAIS:Cで行う、と決めました。 新しい検索バックエンドの設計 検索エンジンの目処が立ったので、次はバックエンドの話です。ここでは、特にこだわった点をご紹介します。 私は7年前、現在の日本事業の検索システムの設計にも関わっていました。当時は理想や期待を込めて設計しましたが、実際に運用を続ける中で「これは設計から見直した方がいい」と感じる部分も多く、今回はそれらを改善する形にしています。 データモデルに依存したAPI データを取得するタイプのインターフェースは”どんなデータ”を対象とするかでガラッと設計が変わります。汎用的になものを作ろうとすると、抽象化が進みすぎて、余計わかりにくくなってしまいがちです。対象が無限に増えるわけではないので、汎用化はせずに、たとえばProductをSearchするためのエンドポイント、というふうに割り切りました。Productのデータモデルは決まっています。タイトルがあって、商品の説明があって、値段があって、、、と、どこに行っても同じです。 柔軟な処理を可能にするプラグイン機構 ElasticsearchやSolrなどにもあるように、検索クエリや結果に対して独自の処理を加えたいというニーズはどの検索システムにもあります。日本事業の検索開発でも、これまで多くの条件分岐(if文)がコードに埋め込まれ、整理が難しくなっていました。そこで、今回のグローバルアプリ向け検索バックエンドでは、クエリの前処理・後処理をフックできるプラグイン機構を導入しています。検索機能の開発者は、必要な処理をプラグインとして追加するだけで拡張が可能です。多くのソフトウェアでも似たような仕組みが使われていますが、結局この方法に行き着くよな、という実感があります。 検索エンジン機能の実装と抽象をわける 基本的なロジックは抽象層にまとめ、実装部分は切り替え可能な構成にしています。もともと、VAIS:CとElasticsearchの両方で動く必要がありましたが、この要件がなくても同じ設計にしたと思います。というのも、検索エンジンの世界はまさに“戦国時代”で、Vector検索に強い新しいエンジンなど、次々と登場しています。現在はElasticsearchを使っていますが、将来的に他の選択肢が有力になる可能性もあります。このため、抽象化しておくことで、後々の移行や比較コストを大きく減らせると考えてます。 その他 グローバルアプリでは、もともとバックエンド全体に関する設計方針が明確にあります ( グローバル展開を支える基盤の裏側 を参照)。このおかげで、何がどこにあるかが、ある程度明確になってます。特に大事だと思うのは、BFFの切り分けです。BFFはここにあるよ(逆にいうと、ここにしかないよ)というのは、見る箇所が限定できるので探索のコストが減るのです。これまでのマイクロサービスでは、どこの誰が検索APIを叩いて、それをお客さまに出しているかをこちらから把握するのは困難でした。検索バックエンドでも、ドメイン特化のノウハウがあります。何をどこに実装するかを明確に提示してるのは新しい検索バックエンドの特徴です。バックエンド全体の設計でも、期せずして同じようなことをしているのは、これまでの苦痛が似ているからだと思います。だれでもなんでもどこにでも作れるというのは、メンテナンスコストを上げる結果になっているという経験則だと思います。 プロダクトオーナーからの想定外の要求で冷や汗 最初はVAIS:Cを使うとはいえ、長期的にはElasticsearchに切り替えなければなりません。私はこれまで、Elasticsearch as a Service (以下、EaaS) という、 ECK などをベースに作った社内のElasticsearchホスト基盤を開発&運用してきました。チームの活躍で、グループ内のほぼすべての検索サービスはEaaSの上で動いてると言えるところまで来ています。関連するエコシステムも充実させてきました。カスタマイズ性も高く、実際、メルカリの複数のビジネスで使われていることがその証左です。グローバルアプリの準備期間中、責任者である@Suwenさんと直接話す機会が多く、EaaSはなんでもできるので安心してください、と伝えてました。そこで何度もフィードバックされたのが、以下のような点です。 「日本事業のこの検索機能、グローバルアプリでも使いたいな」「日本事業のこの機能、グローバルアプリだったらこういうふうにカスタマイズしたらいいよね」 さぁ、ここで私の過去の記事を読んでいただけたみなさまなら、私が冷や汗を流した理由がおわかりですね?EaaSは、Elasticsearchの提供が主であり、その上に作られた機能の再利用までは責任を持っていません。もちろん、プラットフォームとして、機能の再利用性を無視していたわけじゃありません。Elasticsearchにはプラグイン機構があり、実際にそのプラグインを用いた機能なら再利用できます。しかしメルカリではJavaエンジニアは少数派で、Elasticsearchプラグインの開発はほぼ進みませんでした。機能のほぼ全てはGoのマイクロサービスに作り込まれています。以下に、課題感を説明するのに使ったスライドの一部を晒します。日本事業の作り込んだ検索機能資産が使いたいけど、使えないという状態です。 実はこの問題は以前にもぶつかってました。広告事業からも、EaaSを使ってもらっていたのですが、ビジネス側からの期待値は、EaaSを使えば、日本事業の検索と同じクオリティの検索結果がでてくると思われていたのです。実際には、EaaSは日本事業の検索と同じ検索結果を提供するわけではないので、この時は日本向けに作られたシノニムや日本語形態素解析などのライブラリを広告チームのバックエンドから再利用してもらう形で落ち着きました。この頃にスライドを書き、日本の検索チームへ、Goで作られた検索機能の再利用性を高めるべきだとの問題提起はしてきました。が、当時の日本事業の優先度的には、再利用性にリソースを割いてもらうことはなかなか難しかったのです。 All for oneで検索基盤の拡充へ 私はここからは迷走しました。日本事業の開発や優先度を邪魔しない範囲で、なんとか機能の再利用化を進める案を捻り出そうとしました。一時は、必要な機能をElasticsearchプラグインで書き直すか、とまで考えていました。ここで肝となった問題点を整理します。 EaaSは最初から複数事業での利用を見据えた設計であるが、その上に作った日本事業の検索サービスは日本事業に強く結合しているため、他事業からの再利用が困難である 日本事業の検索は7年運用してきたコードベースであり、前述のとおり、うまく整理されていないなどの負債がある 日本事業の検索は引き続き重要であり、このコードベース自体を再利用できるように改変しつつ、日本事業向けの開発を同時並行するのは困難 グローバルアプリの技術責任者である@deeeeetとも議論を重ねて、以下のような案を出しました。いまあるEaaSの上に、再利用可能な機能を一個ずつ切り出して使用するというものです。 日本事業側にも問題と案を再掲示し、議論を重ねました。最終的に、グローバルアプリの優先度が上がったこともあり、日本事業側からも積極的に動いてくれて、日本事業の検索機能を再利用するための新しい層を作ることが決まりました。この層に関しては、主導権があるわけではないので、私からはEaaSというプラットフォームが大事にしているところをなるべく共有して、いまのところ共通した方針のもと一緒に作っています。具体的には以下です。 Platformがその下で使われてるオープンソースソフトウェアに必要以上の制約を加えないこと ここでいうオープンソースソフトウェアとは、我々の場合はElasticsearchです。Elasticsearchには我々がただ使ってないだけで、まだまだたくさんの可能性があります。これを現時点では使わないからという理由で上のプラットフォームを経由すると使えなくなるのは本末転倒だと思ってます。実はここは議論になりやすい点で、制約を加えた方が不可測事態が避けられて、メンテが楽になるという話もありますので、慎重な説得と周りの理解が必要です。 各ビジネスドメインから独立して使えること 共通基盤を作って、それを複数のオーナーから利用する時に問題になるのは機能の開発優先度だと思います。事業Aは規模が小さいから後回し。事業Bのオーナーは発言力があるから、優先。。などなど、いわゆる”政治”になりやすいところだと思います。完全な独立は不可能ですが、これをなるべく避けるためには、基盤への開発が開かれていることが重要です。開かれていると言っても、自由に作れるとはまた違います。ブログの前段で説明した通り、どこでもだれでもいじれると収拾がつかなくなってしまいます。それを解決する一つの手はプラグイン機構です。どこをどうフックするかはまだまだ議論していく必要があるのですが、基本的な方針に関しては同意できています。 こうして、現在のグローバルアプリの検索は、VAIS:CとEaaSの上に作られた新しい仕組みとのハイブリッドで稼働しています。 まとめ 今回はグローバルアプリでの新たな検索要求に対してどのような方針で対処したのかについて書かせていただきました。従来の日本事業の検索とは毛色の違う要求をこなすために、フルマネージドの検索サービスである、VAIS:Cを一時的に導入しました。また、グローバルアプリの開発要求を起点に議論が広がり、社内検索基盤を拡充させることで、今後の要求についても対処できるように基盤を作っていることについても触れさせていただきました。具体的な多言語対応方法など、今回、書ききれなかった部分もあるので、また次回以降でご紹介できればいいなと思ってます。 参考 ”メルカリの検索基盤の変遷について” https://engineering.mercari.com/blog/entry/20220207-776318b784/ 月間2000万人が使う メルカリ検索機能のアーキテクチャ https://findy-tools.io/articles/mercari-elasticsearch/38
はじめに こんにちは、Cross Border (XB) EngineeringでSRE & Enablingを担当している @ryotarai です。 本記事は 連載企画:メルカリ初の世界共通アプリ「メルカリ グローバルアプリ」の開発舞台裏 の一環として、このプロジェクトのバックエンドAPIのE2E(End-to-End)テストについて深掘りします。特に、開発者全員がメンテナンスできるE2Eテスト基盤をどのように実現したのか、その設計思想と実装について紹介します。 なぜE2Eテストの改善が必要だったのか 従来のE2Eテストが抱えていた課題 バックエンドAPIのE2Eテストは、システム全体が正しく動作することを確認する重要な役割を担っています。しかし、多くのプロジェクトで以下のような課題に直面します: セットアップの複雑さ: テスト環境の準備に時間がかかり、開発者が気軽に実行できない 並列実行の難しさ: テスト間でリソースの競合が発生し、実行時間が長くなる 属人化: QAチームが主にメンテナンスし、開発者が触りにくい 学習コストの高さ: 専用のフレームワークやDSLを学ぶ必要がある このプロジェクトでも当初、これらの課題に直面していました。特に、QAチームのみがE2Eテストのメンテナンスを担当する状況では、以下の問題が発生します: APIの変更時に、E2Eテストの更新が後回しになる テストの追加に時間がかかり、カバレッジが低下する 開発者がテストの実装を理解せず、デバッグが困難になる 目指した姿:開発者全員が参加できるE2Eテスト 私たちが目指したのは、QAチームだけでなく、APIのコードを書いている開発者全員がE2Eテストのメンテナンスを行える体制です。 これを実現するためには、以下の要件を満たす必要がありました: 開発者が日常的に使っている技術でテストを書ける 学習コストが低く、すぐに書き始められる IDEの補完やリファクタリング機能が使える ローカルでもCIでも同じように実行できる フレームワークの設計思想 「普通のgo testで書ける」という哲学 メルカリ グローバルアプリのバックエンドAPIは、Goで実装されています。そのため、最終的に私たちは普通のGoコード(go test)でE2Eテストを書く方式を選択しました。その理由は: 学習コストがゼロ: 開発者は既にgo testの書き方を知っている 型安全性: Connect のクライアントコードをそのまま使え、コンパイル時に型をチェックできる IDEの恩恵: 補完、リファクタリング、定義ジャンプなどが使える デバッグのしやすさ: 通常のGoプログラムとしてデバッグできる 既存コードの活用: テストヘルパー関数やモックなどを再利用できる この決定により、E2Eテストを特別なものではなく、日常の開発フローの一部にすることができました。 私たちが実装したE2Eテストフレームワークの核心は、「普通のgo testで書ける」という設計思想です。 実際のテストコード例を見てみましょう: func TestUpdateNickname(t *testing.T) { t.Parallel() tests := []struct { name string userID int64 nickname string wantCode connect.Code }{ { name: "Success", userID: createTestUser(t).ID, nickname: "NewNickname", wantCode: connect.CodeOK, }, { name: "Blank nickname returns error", userID: readonlyUser().ID, nickname: "", wantCode: connect.CodeInvalidArgument, }, { name: "Non-logged in user returns error", userID: 0, nickname: "TestNickname", 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(&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("error code = %v, want %v", connect.CodeOf(err), tt.wantCode) } }) }) } } このコードは、go testのテーブル駆動テストという標準的なパターンで書かれています: t.Parallel() で並列実行を指定(通常のgo testと同じ) テストケースを構造体のスライスで定義 t.Run() でサブテストを実行し、各サブテストも並列実行 testenv.Run() の中でテストサーバのURLを取得 Connectの自動生成されたクライアントをそのまま使用 通常のgo testと同じアサーション E2Eテスト特有の複雑な記述はほとんどなく、普段のユニットテストと同じように書けることが分かります。 さらに、普通のGoコードで書けるということは、Claude CodeなどのAIコーディングツールも効果的に活用できるということです。テストケースの追加やエッジケースの洗い出しなど、AIの支援を受けながら効率的にテストを書くことができます。 QAチームなど、バックエンドエンジニア以外でGo言語に慣れていないメンバーもいますが、AIを活用することでテストコードを作成できるようになっています。 実際に、既存のJestで実装されていたE2Eテストをこのフレームワークに移行する際にも、AIを大いに活用しました。既存のテストコードを参照しながら、AIがGoのテストコードを生成し、開発者がレビュー・調整することで、移行作業を効率的に進めることができました。 全体アーキテクチャ E2Eテストの実行方法として、全員がアクセスできる共有のdevelopment環境にデプロイしたアプリケーションに対してテストを実行するという選択肢もあります。しかし、この方法では開発中のバックエンドの変更に対してすぐにテストを実行できないという問題があります。 私たちは、アプリケーションコードを変更しながらE2Eテストを通したり、追加・修正できたりする環境を重視しました。そのため、テストごとにサーバを動的に起動する設計を採用しています。これにより、開発者は自分の変更をすぐにE2Eテストで検証でき、テスト駆動での開発が可能になります。 フレームワークの主な責務は: テストサーバの自動起動と管理: 必要に応じてサーバを起動し、プールで管理 データベースの自動準備: AlloyDB Omniを起動し、論理データベースを作成してマイグレーションを実行 並列実行のサポート: 複数のテストが同時に実行できるようリソースを管理 クリーンアップの自動化: テスト終了時に自動的にデータをクリーンアップし、リソースをプールに返却 開発者から見ると、これらの複雑な処理は完全に隠蔽されており、 testenv.Run() を呼ぶだけでテスト環境が整う仕組みになっています。 内部実装の工夫 ここからは、フレームワークがどのように並列実行とリソース管理を実現しているか、内部実装を見ていきます。 リソースプールによる並列実行 E2Eテストの並列実行を実現するために、サーバをプール管理しています。 重要なのは、 testenv.Run() に渡した関数が終了すると、自動的にサーバがプールに返却される仕組みです。開発者は明示的にリソースを返却する必要がなく、通常のテストと同じように書くだけで、フレームワークが自動的にクリーンアップとプールへの返却を行います。 この仕組みにより: 並列実行時にリソースの競合が発生しない サーバの起動コストを最小化(プールから再利用) テスト間のデータ汚染を防ぐ(TRUNCATEで初期化) リソース管理が透明(開発者は意識する必要がない) データベースの管理 データベースについては、AlloyDB Omniのコンテナは1つだけ起動します。その中で、テストごとに論理データベースを自動的に作成し、マイグレーションを実行します。 この設計により: データベースコンテナの起動コストを削減(1つだけ起動すればよい) 並列実行でもデータが分離される(論理DBごとに独立) マイグレーションの実行も自動化(開発者は意識不要) 論理データベースもプールで管理されており、テスト終了後はTRUNCATEでデータをクリーンアップしてから再利用されます。 コードカバレッジの収集 このフレームワークは、Go 1.20以降で導入された go build -cover をサポートしています。 通常のテストカバレッジ( go test -cover )はテストコード内での実行しか計測できませんが、E2Eテストではサーバプロセスとして実行されているコードのカバレッジを計測する必要があります。これを実現するのが go build -cover 機能です。 フレームワークの実装では: サーバごとに独立したカバレッジディレクトリを自動作成 各サーバ起動時に一時ディレクトリを作成 GOCOVERDIR 環境変数を自動設定 並列実行でもカバレッジが正確に収集される 各サーバが独立したディレクトリに書き込むため、競合しない テスト終了時に自動マージ すべてのサーバのカバレッジデータを go tool covdata merge で統合 最終的に1つの統合されたカバレッジデータが生成される 開発者は特定の環境変数を設定するだけで、複数サーバのカバレッジを自動的に収集・マージできます: # カバレッジ付きでサーババイナリをビルド go build -cover -o server ./server # カバレッジを収集しながらテスト実行 GLOBAL_GOCOVERDIR=/tmp/coverage go test ./e2etest/... # カバレッジレポート生成 go tool covdata percent -i /tmp/coverage この仕組みにより、E2Eテストでのコードカバレッジを正確に計測し、APIの品質を定量的に評価できるようになりました。 Kubernetes上での実行 E2Eテストは、開発環境ではローカルで実行し、CIではKubernetes上で実行します。ここでは、Kubernetes上での実行方法について、いくつかの興味深い工夫を紹介します。 go test -c を使った高速なデプロイ 通常、Kubernetes上でテストを実行する場合、以下の手順を踏むかと思います: コンテナイメージをビルド イメージをレジストリにプッシュ Kubernetes Podでイメージをプル コンテナを起動 しかし、この方法には各ステップに時間がかかるという問題があります。E2Eテストでは実行速度が重要なので、私たちは異なるアプローチを取りました: # テストバイナリをビルド 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 go test -c を使うことで、テストコードを実行可能なバイナリにコンパイルできます。これにより: コンテナイメージのビルドが不要 レジストリへのプッシュ・プルが不要 kubectl execで直接ファイルを転送 この方法により、テスト実行までの時間を大幅に短縮できました。具体的には1分半程度でビルドからテストの開始ができています。 なお、Kubernetes上で実行する理由は、並列実行に必要な十分なリソースを確保するためです。並列度を上げるとレースコンディションを防ぐためにその数だけサーバが必要となり、必要なリソースが線形に増えるため、Kubernetesクラスタを使用しています。 まとめ 本記事では、メルカリ グローバルアプリのバックエンドAPIのE2Eテストについて紹介しました。 このアプローチにより、E2Eテストは特別なものではなく、日常の開発フローの一部となります。開発者はAPIを変更した際に、躊躇なくE2Eテストを追加・修正できるようになっています。 もちろん、まだ改善の余地はあります。例えば: テストの実行時間のさらなる短縮 AIを利用して、変更箇所に関係するテストのみを実行する取り組みを進めています テストデータのセットアップの簡略化 テスト結果のレポーティング しかし、開発者体験を最優先にした設計により、持続可能なE2Eテスト基盤を構築できたと考えています。 同じような課題を抱えているプロジェクトの参考になれば幸いです。
Cross Border (XB) EngineeringでSRE & Enablingをしている hatappi です。私たちのチームはSREのみではなく、 Team Topologies におけるEnablingチームとしての役割を持っています。SREとして信頼性の向上やパフォーマンスの改善を行うだけではなく、XBのプロダクトチームの開発者体験と効率を向上させ、よりスムーズかつ高速に価値提供できるよう、技術的な課題解決や環境整備を通じて支援 (Enable) します。 2025年7月にPlatform NetworkチームからXB SRE & Enablingチームへ異動し、最初の仕事としてメルカリ グローバルWebの立ち上げに携わっています。本記事は 連載企画:メルカリ初の世界共通アプリ「メルカリ グローバルアプリ」の開発舞台裏 の一環として、アプリと同時に開発が進んでいるグローバルWebをテーマに、新しい環境でSREとして価値を出すために私が実践しているアプローチをご紹介します。 Enablingのための課題発見アプローチ 7月にXB SRE & Enablingチームへ異動してきて、私の最初のミッションは「グローバルWeb の立ち上げをEnablingすること」でした。しかし異動して間もないためどのような課題や改善ポイントがあるか分からない状態でした。そこでまずは現状を正しく理解することが不可欠だと考え、私は2つのアプローチをとりました。 1. まずは自分でやってみる グローバルWebのメンバーがどういった過程で困っていて何を改善できるかを知り、共感するためには、自分で体験してみるのが早いです。そこで1つの機能開発タスクに取り組みました。これによりただローカル環境をセットアップして動作確認するという点だけでなく実装をしてプルリクエストを作成して、レビューを受け、マージするまでの一連の開発サイクルを体験しました。 このアプローチによって「CIの実行が遅く、フィードバックを得るまでの待ち時間が長い」「ローカルサーバーの起動が遅い」など様々な改善点を特定できました。 2. 現場の声を聞く 自分1人の体験だけでは、どうしても視野が狭くなってしまいます。特に日常業務としてグローバルWebを開発をしているメンバーの声を聞くのはとても重要です。Slackで改善点を聞いたりプランニングやレトロスペクティブの会議に参加するなどして情報を収集しました。 これらにより最初のアプローチでは発見できなかった課題を見つけられただけでなく優先度の選定にもつながりました。 例えばCIの実行時間が遅いという改善点を最初のアプローチで特定しました。これは間違いなく改善点です。しかし実際の開発でCIの実行時間が気になるのは、他のメンバーからレビューを受け取る最後の段階です。それよりも、時々不安定になって失敗するCIやローカルサーバーの起動時間の遅さのほうが、より大きな課題だと感じているようでした。 Platform Engineeringの経験から得た気づき この2つのアプローチを実践する中で、以前のPlatform Networkチームでの経験を振り返る機会となりました。 Platform NetworkチームではPlatform Engineeringとして、メルカリの複数のプロダクトで横展開できる共通基盤やツールを提供してきました。メルカリには複数のプロダクトがあり、それぞれが異なるコンテキストやドメイン知識を持っています。そのためPlatform側からすべてのプロダクトの現場に深く入り込んで理解することは難しいという課題がありました。 今回、XBのSRE & Enablingとして2つのアプローチを実践することで、現場に深く入り込む重要性を再認識しました。一方で、Platform Engineeringの経験から、メルカリ全体を考えたときの横展開の重要性も理解しています。 メルカリでも、両方の経験を持つエンジニアはまだ多くありません。だからこそ、今はXBで得た経験、例えば今回のグローバルWeb関連の改善内容を、Platformチームに積極的にフィードバックしながら一緒に改善を進めています。 AIを活用した課題解決 前のセクションの2つのアプローチによって、取り組むべき課題の解像度が大きく上がりました。しかし、改善すべき課題が分かっても、まだ異動して間もない私はグローバルWebやCross Borderのコンテキスト、Web関連の技術など多くの情報をキャッチアップする必要がありました。グローバルWebの立ち上げをスムーズにEnablingするためにもAIを活用することでこのプロセスをスムーズに進められないか試みました。 学習・調査 例えば CIの実行時間が遅い、という課題に取り組むとします。これを理解するためにはそもそもどのような仕組みで動いているのかを理解する必要があります。そこで下記に説明するように Claude 、 Claude Code を活用しながら必要な情報や改善策を検討していきました。 まず初めに、Claude Codeを使用して既存のCI関連の設定を調査します。メルカリではGitHub Actionsを使用しているためActionの目的を尋ねたり、Job間の依存関係を確認するなどソースコードを読みながら理解を深めます。調査を進めていると、自分の知らない技術が出てきます。例えば Turborepo などです。 知らない技術が出てきた時はそれを理解するためにも一次ソースである公式のドキュメントを読みます。その際にドキュメントの要約などのためにClaudeを活用します。それだけであればClaude Codeをそのまま使うこともできます。Claude を使用した理由は ClaudeのArtifacts機能を利用するためです。(Fig1) ArtifactsはClaudeとの会話中に作成・編集できる独立したコンテンツを作成するための機能です。これを使用することで知らなかった技術を深堀りながら最終的にまとまったドキュメントが完成するため後から見返しやすくなりました。 Fig1: Claude Artifacts 最後のステップは改善のための調査です。改善方法の検討のための第一歩として一般的な改善策を収集するためにClaude Researchを活用しました。例えば「Turborepoを使ったリポジトリでCIを高速化する一般的な方法を調査」といった内容です。これにより、キャッシュ戦略の改善や並列実行の最適化など、複数のアプローチを短時間でリストアップでき、改善策の仮説を効率的に立てることが可能になりました。またArtifactsを活用することで調査した内容をもとに具体的な案など最終的な実装へ向けた情報を一緒にまとめることも可能でした。 実装・レビュー 改善の仮説が立ったら、次に行うのは実装です。調査でまとめた情報はArtifactsに存在しておりMarkdownとして出力できます。それをClaude、 GPT、 Geminiなど任意のツール・モデルで使用できます。 私はメインでClaude Codeを利用していますが、その理由は Slash commands があるからです。Slash commandsはClaude Code上で / から始まる特殊なコマンドで、Claude Codeで特定の操作を実行することができます。私は自分が開発の中で行うプロセスをこのSlash commandsに移行しています。 例えば変更内容からプルリクエストを作成するSlash commandがあります。このSlash commandには単なるプルリクエストの作成だけでなく変更内容からコミットメッセージを検討してコミットするなど私がよく行うステップを定義しています。 実装が終わったらレビューです。 Claude Code 自身にもレビューはさせるのですが自分でもレビューします。今までは git diff やエディターについている diff viewer などを使用していました。しかしローカルでレビューしたときは問題ないと思ったのにGitHubでプルリクエストを作成して再度レビューすると改善点に気づくということがよくありました。変更を加えて毎回pushしてプルリクエスト上で確認するのは時間がかかります。この問題を解決するために difit を使いはじめました。 difitは、GitHub ライクなビューをローカル環境で実現してくれる CLI です。(Fig2) npm パッケージとして追加されているのでインストールも簡単ですぐ使い始められます。GitHubライクなビューにより今までプルリクエスト上で行っていたことをローカルで行えるようになりました。またdifitはコメント機能がついており、追加したコメントはAIにプロンプトとして渡せるようなコピー機能がついています。おかげでClaude Codeで開発しながらレビューして改善というサイクルをローカルで完結するようになりました。 Fig2, difit ( https://github.com/yoshiko-pg/difit ) デバッグ 最後はデバッグです。 私は普段 Chromeを使用することが多いです。Chromeを用いたデバッグではChrome DevToolsが欠かせません。しかし、様々な機能があるため、どの機能を使ってどこを見たら自分が知りたい情報を見ることができるのか毎回苦労していました。 そこで最近リリースされた Chrome DevTools MCP を活用してみました。これは、自然言語で指示するだけでMCP Serverを介してDevToolsを操作して必要な情報を引き出してくれる機能です。例えば、「グローバルWebのこのページのパフォーマンスをチェックして」と入力するだけで、関連する指標を分析してくれます。 これにより毎回苦労していたDevToolsの操作をスムーズに行うことができ、問題発見までの時間を短縮することができました。 Enabling活動から得られた学び 今回のグローバルWebのEnablingを通じて学んだことが2つあります。 現場に入り一次情報に触れることの重要性 1つ目は、現場の一次情報に触れることで初めて、本当に解くべき課題とその優先順位が見えてくるという点です。もし私が客観的なメトリクスだけを見て判断していたら開発メンバーが感じていたCIの実行時間よりも「CIが時々不安定になること」や「ローカルサーバーの起動時間」がより問題であることに気づくのは難しかったでしょう。 新しい技術領域へ挑戦する際のAIの有効性 2つ目は、AIが新しい技術領域へ挑戦する際のハードルを下げてくれる、ということです。 1つ目の学びの重要性を理解していても、実践するまでのハードルが高いと躊躇してしまいます。しかしAIを活用することでこの「最初の壁」を乗り越えやすくなったと感じました。もちろんAIがすべてを解決してくれるわけではないですが、まずは動くものを作ってから、その仕組みを深く理解していくという私の好きなアプローチが高速でできるようになったと感じています。 今後の展望 グローバル展開にむけたアプリと基盤の再構築 でも触れられていたように、今後3年以内に50カ国・地域への展開が予定されており、これは技術的にも非常にチャレンジングです。このスピードで世界に展開するためには、どのような実装や設定が必要か、どのように効率化できるか、データはどこに配置するか、Webサーバーはどこに置くのか、CDNはどのように活用できるかなど、考えるべきことが多数あります。考えることが多いからこそ面白く、SRE & Enablingの腕の見せ所になると感じているので、非常に楽しみです。 おわりに 本記事では、Platform Networkチームから異動して新しい環境でグローバルWebのEnablingをどのように進めているかを紹介しました。 2025年11月13日に、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます。私は異動前の Platform Networkチームの時に行ったCDNマイグレーションについて話します。 他にも面白そうなセッションが沢山あるのでぜひお越しください! 参加登録はこちら 👉 https://gears.mercari.com/ 明日の記事は @gia さんです。引き続き 連載企画:メルカリ初の世界共通アプリ「メルカリ グローバルアプリ」の開発舞台裏 をお楽しみください。
こんにちは!メルカリ Engineering Office の @mikichin です。 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます! 2018年に実施した「Mercari Tech Conf 2018」から7年の時を経て、久しぶりのオフラインでの開催となります。 テーマは「メルカリエンジニアリングの今」。 今年の全社的なテーマでもある「AI-Native」についてはもちろん、2018年以降メルカリグループのエンジニアリングがどのように変化してきたかを、技術・組織・カルチャーの観点からご紹介します。 オンライン配信はありませんので、ぜひ会場でご自身の目と耳で確かめてください!! 会場は、メルカリのエンジニアリング組織における信念や行動の基盤となる共通認識を明文化した「Mercari Engineering Principles」をモチーフにした「PASSION Stage」「GROW Stage」「MECHANISM Stage」の3つのステージがあります。 本記事では、「MECHANISM Stage」のセッションをご紹介! まだ申し込みをされていない方も、興味のあるセッションがあるはずです。お申し込みは こちらから お願いします。 13:00 – 13:20 メルカリハロでのLLM活用 メルカリハロはスキマバイト領域におけるメルカリの新規事業です。2024年の3月にサービスがローンチされて以来、成長を続けており、先日登録者数1200万人を突破しました。 メルカリハロのMLチームはサービスローンチから約半年が経過した2024年10月のタイミングで始動し、以来、AI/MLを用いた多くのプロダクト改善に取り組んできました。 本セッションではその中でも特にメルカリハロのLLMを用いた機能やそれらを支えるLLMOps基盤について紹介を行います。具体的にはLLMを用いた求人の自動作成機能である「かんたん求人作成」やLLMを用いた求人の掲載前リスク予測等についてです。メルカリハロでは既に多くのLLMを用いた機能をリリースしており、用途の異なる50種類以上のプロンプトを本番で運用しています。そのためプロンプトの品質を管理するためのLLMOps基盤も非常に重要です。 本セッションを通して、メルカリハロでLLM実装を進める中で得られたLLMのプロダクト実装のための勘所や、プロンプト管理・自動評価基盤などの実践的なLLMOpsのためのTipsといった知見を共有できればと思います。 13:30 – 13:50 FastlyからCloudflareへ 100%移行を達成したMercariのアプローチ メルカリでは、2023年よりCDNプロバイダーをFastlyからCloudflareへ段階的に移行を開始し、2025年現在、移行が完全に完了しました。 このセッションでは、安全かつスムーズに移行を進めるために実施したアプローチと、その過程で得た学びを共有します。 CDN プロバイダーの比較ではなく、移行プロセスを主に話すため、CDNプロバイダーの変更を考えていない方にも、移行に関する考え方やプロセスのヒントを持ち帰っていただけると考えています。 14:15 – 14:35 The Invisible Backbone: AI-Native Observability for Modern Platforms もし、自動で設定され、変化にもシームレスに適応し、多すぎるアラートのノイズを一掃してくれるオブザーバビリティがあったら?本セッションでは、メルカリがどのようにAI-Nativeなプラットフォームを構築し、設定不要のモニタリング、一貫した可視性、そしてAI活用型のアラートを標準機能として実現したのかをご紹介します。 信頼性が高く開発者にやさしいクラウドプラットフォームの未来を、自律型オブザーバビリティがどのように形作るのか。ぜひご参加のうえ、その目でご覧ください。 14:35 – 15:05 Running 1000 End-To-End Web Tests Daily Mercari USでは非常に多くの E2E Webテスト を実行していますが、それらを素早く、かつ本当に役立つテストにすることが大きな課題となっています。本セッションでは、プルリクエストごとにテストを実行する、新しいテストを追加する、各機能領域を対象にしたテストを実行するなど、私たちが行っている工夫をご紹介します。毎日何千件ものE2Eテストをどのように回しているのかを知りたい方には、まさにぴったりのセッションです。 15:15 – 15:35 Mercari’s Internationalization Journey この2年間、私たちは海外のお客さまがメルカリのマーケットプレイスで商品を購入することを可能にしてきました。 本セッションでは、プロダクトを海外展開していくための道のりを、とくにユーザー生成コンテンツ(UGC)の翻訳に焦点を当ててご紹介し、翻訳コストを100分の1に削減するうえでLLM(大規模言語モデル)がどのように役立ったかについてもお話しします。 16:00 – 16:20 EGP – Mercari’s CRM Platform: Built Once, Powering Many もともとシンプルなハードコードのCRMとして生まれたEGPは、マーケター向けのスケーラブルでUI主導のプラットフォームへと進化してきました。システムが拡大するにつれ、とりわけEGPの利用規模が大きくなると、その複雑さが使いやすさや運用面での課題を引き起こすようになりました。本セッションでは、私たちがシステム設計やAI活用によるUI改善を通じてこれらの課題にどのように取り組んできたのか、そしてその過程でどのような学びを得たのかを共有します。 16:30 – 16:50 Securing the Future of Workflow Automation and AI Agents 企業がワークフローの自動化やAIエージェントを積極的に活用するにつれ、孤立したシステム、過剰な権限を持つエージェント、複雑に絡み合った権限モデルといった新たなリスクが生じています。本セッションでは、これらの課題をどう解決し、自動化とAIが持つ潜在能力を安全かつ最大限に引き出す方法を探ります。さらに、セキュアでスケーラブルな導入を実現しつつ、ユーザーが安心してイノベーションに取り組めるようにするための実践的なアプローチをご紹介します。 17:00 – 17:20 AI/LLMが拓くデータ活用の新時代:人間とデータ分析AI エージェントが協業する分析基盤へ 私たちは、自然言語での対話を通じてデータ分析を行えるAIエージェント「Socrates」を開発・提供しています。Socratesの登場は、SQLクエリの生成から実行、結果の可視化や解釈までを誰でも簡単に実行できるような変革をもたらし、データ活用のハードルを大きく下げました。本セッションでは、Socratesが誕生した背景やSocratesを支える技術、そしてAIとの協業によってもたらされるデータ活用体験の未来像についてお話しします。 「mercari GEARS 2025」のお申し込みは こちらから 。 また、その他のセッション紹介は下記をご確認ください。 PASSION Stageのセッション紹介は こちら 。 GROW Stageのセッション紹介は こちら 。 イベント詳細 開催日時: 2025年11月13日(木) 11:00-18:00 概要: mercari GEARS 2025は、メルカリグループのエンジニアリング組織の技術への挑戦と、カルチャーを体感する技術イベントです。 本イベントは、単なる情報伝達の場ではなく、エンジニアたちが出会い、経験を共有し、交流を通じて新たな機会が生み出されることを目的としています。 参加費:無料 会場:TODA HALL & CONFERENCE TOKYO 参加方法:こちらの ページ にてお申し込みください。 【 公式サイト 】 本イベントに関する追加情報があれば、随時 @MercariGears でお知らせしますので、気になる方はぜひフォローをお願いします。
こんにちは!メルカリ Engineering Office の @mikichin です。 来る11月13日、メルカリグループのテックカンファレンス「mercari GEARS 2025」が開催されます! 2018年に実施した「Mercari Tech Conf 2018」から7年の時を経て、久しぶりのオフラインでの開催となります。 テーマは「メルカリエンジニアリングの今」。 今年の全社的なテーマでもある「AI-Native」についてはもちろん、2018年以降メルカリグループのエンジニアリングがどのように変化してきたかを、技術・組織・カルチャーの観点からご紹介します。 オンライン配信はありませんので、ぜひ会場でご自身の目と耳で確かめてください!! 会場は、メルカリのエンジニアリング組織における信念や行動の基盤となる共通認識を明文化した「Mercari Engineering Principles」をモチーフにした「PASSION Stage」「GROW Stage」「MECHANISM Stage」の3つのステージがあります。 本記事では、「GROW Stage」のセッションをご紹介! まだ申し込みをされていない方も、興味のあるセッションがあるはずです。お申し込みは こちらから お願いします。 13:00 – 13:40 Leader’s Talk: Moving Fast Without Breaking Things メルカリのバリューのひとつに「Move Fast(はやく動く)」があります。 その一方で、開発者が柔軟に複数のサービスを行き来できるようにすることも必要です。では、その両方をかなえるにはどうすればよいのでしょう? 本セッションでは、AI時代においてメルカリのエンジニアリング組織がいかに開発プロセスを進化させ、スピードとレジリエンスのバランスを取ろうとしているのかを、リーダーたちが自身の視点から紹介します。 発表は主に英語で行いますが、日本語での質問も大歓迎です! 14:15 – 14:35 Google Customer Engagement Suiteを使った顧客エンゲージメント変革 2025年に行われたGoogle Cloud Next Tokyoにて、メルカリは基調講演とブレイキングセッションを通し、Google社が提供するCustomer Engagement Suiteを活用した顧客エンゲージメントの変革について発表しました。このセッションでは、その発表で紹介されたプロダクトがどのような方法で構築されたのかについて紹介します。 14:45 – 15:05 PJ-Auroraが描く未来と、UI品質評価を自動化するエージェント開発 メルカリのモノづくりのアプローチを変えることを使命とする「PJ-Aurora」。本セッションではその未来像を紹介し、あわせてUI品質評価を自動化するAIエージェント開発の現在地をお話しします。AI-Native時代における品質保証の可能性を探る試みを共有します。 15:15 – 15:35 なぜ、メルカリはノーコードを選ばなかったのか? 社内問い合わせ工数を60%削減したLLM活用の裏側 生成AIブームの中、多くの企業がノーコードツールを試す一方で、メルカリは既存の生成LLMを社内データで徹底的にチューニングすることで、高い精度と柔軟性を追求してきました。本セッションでは、社内問い合わせ工数を60%削減したLLM活用事例「HiYo-Chan」を中心に、ノーコードツールでは実現できないメルカリ独自の技術的な工夫、そしてその裏側にあるビジョンまで、赤裸々にお話しします。 16:00 – 16:40 AI Native への道のり ― 数字でみる全社推進と現場の実践 メルカリグループは「AI-Native」を掲げ、組織と現場の両輪で挑戦を進めてきました。本セッションでは、その全体像をデータと実例で解像度高く共有します。まず、社内の開発状況を可視化する取り組みを通じて、AI活用の広がりとインパクトを数値で示します。次に、全社でAIエージェントを使える組織づくり、既存事業を「AI-Native」な開発体制へ移行する設計と運用、そこで直面した課題と理想の姿を掘り下げます。最後に、新規事業開発を題材に、生成AIを活用してPMとエンジニアが要件定義から実装までをどう加速しているか、開発を支える標準化ドキュメント「Agent Spec」の中身と運用を具体的に紹介します。新規事業と既存事業にAIを組み込みながら、新しい開発の型をつくるための実践的な知見をお届けします。 17:00 – 17:30 LTセッション 6人のLTを行います。 未定 / kuu GitHubリポジトリの調査から分かったマイクロサービス開発でのデータベースの課題 / Tomoyuki Koyama Claude Codeで仕様駆動開発をする中で考えたエンジニアの役割 / Toshiki Kawamura Mercari Ads Optimizations For Profitable Revenue Stream / Kumar Abhinav Exploring LLM-Driven Formal Verification for Robust Continuous Integration of Services / Cheng-Hui Weng Evaluations for LLM Apps / jd 「mercari GEARS 2025」のお申し込みは こちらから 。 イベント詳細 開催日時: 2025年11月13日(木) 11:00-18:00 概要: mercari GEARS 2025は、メルカリグループのエンジニアリング組織の技術への挑戦と、カルチャーを体感する技術イベントです。 本イベントは、単なる情報伝達の場ではなく、エンジニアたちが出会い、経験を共有し、交流を通じて新たな機会が生み出されることを目的としています。 参加費:無料 会場:TODA HALL & CONFERENCE TOKYO 参加方法:こちらの ページ にてお申し込みください。 【 公式サイト 】 本イベントに関する追加情報があれば、随時 @MercariGears でお知らせしますので、気になる方はぜひフォローをお願いします。