有名テック企業の技術ブログを、ひとつのフィードで。
フィード
112件
こんにちは、メルペイiOSエンジニアのkubomiです。 この記事は Merpay & Mercoin Tech Openness Month 2026 の 10日目の記事です。 生成AIによって、エンジニアが短時間でプロトタイプをつくれる場面はかなり増えました。最近、小規模なプロジェクトで「初回ミーティングの前に、動くものをつくり切ってしまう」という進め方を試したところ、意思決定のスピードが劇的に変わりました。私はこのやり方を "Build First, Discuss Later(まずつくる、議論は後)”と呼んでいます。この記事では、その具体的な進め方と、実践を通じて私自身に起きたマインドセットの変化を紹介します。 よくある開発フローと、その課題 私たちの現場では、開発に取りかかる前に、まず関係者の認識をそろえておくのが一般的です。具体的には、最初にプロダクトマネージャー(PdM)が大まかな仕様を用意し、それをもとにキックオフミーティングを開いて詳細を議論します。議論を経てPdMが仕様を固め、エンジニアはその仕様をもとに見積もりを出して実装に入ります。 ただ、議論して仕様を固めたつもりでも、いざつくり始めると「あれ、ここの挙動どうするんだっけ?」という疑問が次々と出てくることがあります。そのたびにPdMへ確認したり、追加のミーティングを開いたりすると、少しずつコミュニケーションの往復が増えていきます。 "Build First, Discuss Later" という提案 そこで私が実践したのが、プロセスの順番をあえて逆転させる "Build First, Discuss Later" です。仕様が固まる前に、まず動くプロトタイプをつくってしまうという発想で、ミーティングはその動くものを土台に議論を進めます。 従来は「議論して仕様を固めてからつくる」流れでしたが、これを「先につくり、その動くものを見ながら議論する」へと入れ替えます。実際に触れる画面があると、抽象的な仕様書をめぐる議論よりもはるかに早く、関係者の認識がそろっていきます。 ただし、何にでもこの進め方を使うわけではありません。何日もかかるような大きな実装でこれをやると、方針が変わったときの手戻りが大きすぎます。私の場合は、数時間から1日以内でつくれるくらいの小規模な施策に限定しています。そのくらいの規模なら、悩んで待つより、とりあえずつくってしまったほうが圧倒的に速い、という実感があります。 ミーティングにプロトタイプを持ち込む3つのステップ 私は "Build First, Discuss Later" を、ミーティングの前・中・後という3つの場面に分けて実践しています。ここでは、アプリの画面にバナーを追加した事例を例に、それぞれの場面で意識していることを順に紹介します。 ミーティング前:自分のベスト案でつくり切る ミーティング前は、手に入る計画書や仕様書をAIと一緒に読み込み、「なぜつくるのか(Why)」「何をつくるのか(What)」を自分なりに解釈します。この段階で最も大事なのは、完璧な実装をつくることではなく、どこが曖昧なのかを目に見える形にすることだと考えています。実際、詳細が決まっていないことがほとんどですが、曖昧な点にぶつかっても立ち止まりません。PdMに質問する代わりに、いったん自分が考えるベストな案でつくり切り、迷ったポイントはミーティングのアジェンダに整理しておきます。 たとえば、バナー追加の事例では、リリースに必要な最小限の機能に絞って早くリリースするか、将来使い回せる再利用性を優先するか迷いながらも、まず最小限の機能で動くプロトタイプをつくりました。UIデザインがまだない場合も、既存の画面部品を組み合わせた仮の見た目で形にしました。 ミーティング中:動くものを見ながら論点を解消する ミーティング中は、その動くプロトタイプを見せながら議論し、可能な限りその場で論点を解消します。意見が分かれそうな箇所には、あらかじめA案とB案を用意し、「私はこういう理由でA案を推します」と推奨案まで添えておきます。バナーの例では、期日を踏まえて、リリースに必要な機能に絞った設計プランと、将来の再利用まで見据えた設計プランを提示し、PdMはその場で前者のプランに合意できました。細かな仕様も、プロトタイプを見ながらサクサクと決まっていきました。判断の材料がそろっているため、議論は驚くほど早く前に進みます。 ミーティング後:決まった内容をすぐ反映する ミーティング後は、決まった内容を仕様書に反映し、実装を微修正したうえで品質保証(QA)のテストに回します。大きなつくり直しが起きにくく、初回ミーティングの直後にはリリースが見えている、という状態になりました。バナーの例では、私がつくった仮の見た目をデザイナーが本番デザインへブラッシュアップし、実装側はそれを反映する微修正で済みました。 "Build First, Discuss Later" で起きた3つの変化 この進め方を試してみると、ミーティングの進み方やPdMとのやり取りがかなり変わりました。特に大きかった変化は、次の3つです。 1つ目は、ミーティングがほぼ1回で完結するようになったことです。うまくいけば、初回ミーティングが終わった時点で仕様も実装もほぼ固まっており、開発見積もりすら不要になることもあります。その後の往復も大きく減りました。 2つ目は、議論が速く、かつ正確になったことです。実際に動くものを見せながら「この画面の挙動はこれでよいですか」と確認できるため、言葉だけのやり取りで生じがちな認識のズレが起きにくくなりました。 3つ目は、PdMの負担が軽くなったことです。エンジニアが具体的な仕様の案まで持っていくので、PdMは方針を確認するだけで済みます。特にPdMが複数プロジェクトを兼務しているような状況では、その確認コストを減らせるだけでも大きな価値があります。 「待つエンジニア」から「提案するエンジニア」へ こうした変化は、単に開発プロセスやコミュニケーションを効率化しただけでなく、私自身のエンジニアとしてのマインドセットにも影響を与えました。 以前の私は、決まった要件を正しく実装することがエンジニアの主な役割だと思っていました。けれど、PdMが持つWhyと大まかなWhatを起点に、まず動くものをつくってみると、「これは要らないかもしれない」「こっちの方がお客さまに価値を届けられるのでは」といった議論を、自分から持ち込めるようになりました。 いちばん大きかったのは、「私はこれがいいと思う」というアイデアを持ってミーティングに参加できるようになったことです。ただ仕様を待つのではなく、要件定義の段階から意見を出し、仕様を決めていく側に少しずつ入っていけるようになりました。 そうやって関わっていると、「この機能は今いちばん自分が詳しい」というオーナーシップも自然と生まれてきます。自分の提案が仕様に反映され、動くものを通じてプロダクトの方向性が決まっていく。その過程に関われるようになって、プロダクトづくりが前より一層楽しくなりました。 生成AIによって「まずつくってみる」ハードルが下がったことで、エンジニアが上流の議論に入りやすくなったと感じています。プロトタイプをつくってミーティングに持ち込むことは、単に開発を速くするだけではなく、エンジニアがより主体的にプロダクトづくりに関わるためのきっかけにもなるのだと思います。 まとめ "Build First, Discuss Later" は、先に動くプロトタイプをつくり、それを見ながら議論することで、意思決定を速くする進め方です。みなさまも、「仕様待ちで開発が始められない」「仕様が曖昧で手戻りが多い」と感じたら、自分なりのプロトタイプを会議に持ち込んでみてください。会話が前に進むだけでなく、プロダクトづくりの楽しさも少し違って見えてくると思います。 次の記事は mewutoさんです。引き続きお楽しみください。
こんにちは。メルペイのフロントエンドエンジニアの @mattsuu です。この記事は「Merpay & Mercoin Tech Openness Month 2026」の7日目の記事です。 EGP Code は、ランディングページ(LP)を AI と作る社内向けのエディタです。作成背景については AI と作る HTML ベースの LP エディタ EGP Code を内製した理由 という記事で紹介しました。本記事では、その内部で動いている 4 つの仕組みを紹介します。 EGP Code が扱うのは、HTML と状態や動きを担う少数の <egp-*>(独自の Web Components)を混ぜた 1 枚のページです。 <section class="p-6 text-center"> <h1>春のキャンペーン</h1> <p>応募受付中</p> <egp-button>応募する</egp-button> </section> この HTML を AI エージェントとの対話やエディタで編集し、プレビューで確認して公開します。紹介する 4 つの仕組みは、(1) エージェントの再帰ループ、(2) Firestore を介したリアルタイム反映、(3) ブラウザだけで完結するテストランナー、(4) プレビューと HTML を結ぶ対応表です。 1 つの指示がユーザーから届いてプレビューに反映されるまでの流れと、それぞれの仕組みが効く場所は次のとおりです。 仕組み 1: 文脈とツールを束ねるエージェントの再帰ループ 「フォントサイズを 24px にして」「文字を太くして」のように特定の要素への簡単な指示なら、その要素を特定して CSS を更新したり文言を調整したりするだけなので、ほぼ 1 回の操作で終わります。一方で複数の要素にまとめて指示したり、API を使った画面やテストを作ったり、Lint・テストのエラーを直したりする場合は、推論とツール実行を何度か往復することになります。 エージェントは「推論 → ツール実行 → 結果を会話履歴に追加 → 再び推論」という再帰ループで動きます。ここでいうツールとは、AI が必要と判断したときに呼べる関数の定義と説明のことです。HTML を書き換える、ファイルを読む、Lint で検証する、テストを走らせる、といった操作をツールとして用意しておき、モデルがその中から必要なものを選んで呼び、戻り値を見て次の手を考えます。 1 回の入力に対して 4 つの情報をまとめて渡します。システムプロンプトで AI エージェントに役割やコード生成のルールといった前提を与え、それにユーザーの指示と選択要素と現在の HTML、これまでの会話履歴、そして使えるツールの一覧を教えます。このうち現在の HTML・仕様・テストといったページの状態は、種類ごとに XML タグで区切ってまとめます。 会話履歴の持ち方は、開発当初 OpenAI API 側に任せていました。しかし全社的に ZDR(Zero Data Retention) を適用することになり、プロバイダ側に会話を残せなくなったため、いまは履歴をすべて自前で記録し、毎回のリクエストに載せて送るステートレス方式にしています。履歴の肥大化を防ぐため、一定量を超えたら要約させてコンテキストを圧縮しています。 // 1 ラウンドの推論 const stream = await client.responses.stream({ ...args, input: sessionBuffer.getItems(), // 自前で管理している履歴全体を送る previous_response_id: undefined, store: false, // プロバイダ側に会話を保存させない }); ループの工夫をいくつか紹介します。 1 つ目は、ツールの失敗の扱いです。ここでいう失敗とは、apply_patch の差分が当たらない、Lint がエラーを返す、テストが落ちるなど、ツールが期待どおりに完了しなかった状態を指します。こうした失敗ではループを止めず、エラーの内容をそのまま結果としてエージェントに返すことで自己修正させています。 2 つ目は、find_skill ツールによる情報の出し分けです。社内 API の使い方などのドキュメントは、最初は ID と一行説明の一覧だけを見せておき、本文は必要になったタイミングで読み込みます。たとえば「商品一覧を表示したい」という指示が来ると、エージェントはまず一覧から関連しそうなものを探し、find_skill でそのドキュメント本文を取得します。エージェントは取得したドキュメントを読み、正しい引数で API を呼びます。 この推論とツール実行の往復を繰り返し、最後にエージェントが apply_patch で HTML を差分更新すると 1 つの指示が編集として完成します。 ループの途中で方向を変える Real-time steering ここまでは、1 つの指示を最後まで処理してから次を受け取る前提でした。ですが実際には、処理の途中で「やっぱり色は青にして」と方針を変えたり、「ついでにフッターも直して」と指示を足したくなることがあります。完了を待たずに割り込みで指示を足し、走っているループに後から反映する仕組みを用意しています。こうした仕組みは Real-time steering とも呼ばれます。 ユーザーが処理中に指示を足したときの流れは、次のようになります。 エージェントが処理中(画面がローディング中)にユーザーがメッセージを送ると、クライアントはそれを通常の指示ではなく、割り込みメッセージとして送信します。サーバは受け取った割り込みメッセージを、 Firestore の通常の会話履歴とは別のサブコレクションに、いま走っているリクエストの ID を添えて書き込みます。エージェントは、自分が処理しているリクエストの ID に一致する割り込みだけを読み取ります。 // 自分のリクエスト宛の割り込みメッセージだけを読み、読んだら消す const consumeSteeringMessages = async (conversationId, requestId) => { const snapshot = await steeringRef .where('requestId', '==', requestId) // このリクエスト宛だけを対象にする .orderBy('timestamp', 'asc') .get(); const messages = snapshot.docs.map((doc) => doc.data()); await deleteDocs(snapshot.docs); // 読んだら消す return messages; }; ID で絞るので別のリクエスト宛の割り込みを拾うことはなく、読んだら消すので同じ指示が二重に効いたり取りこぼしたりすることもありません。処理のループに差し込むため、割り込みが来たときにエージェントが何をしようとしていたかで、対応が 2 つに分かれます。 1 つ目は、ツールを呼ぼうとしていた場合です。そのツールを実行せず、戻り値の代わりに「ツールは実行していません。処理中に新しい指示が届きました」という内容を返します。 // ツール実行の直前に割り込みを確認する const steering = await consumeSteeringMessages(conversationId, requestId); if (steering.length > 0 && toolCalls.length > 0) { for (const toolCall of toolCalls) { // ツールは実行せず、戻り値の代わりに割り込みを差し込む buffer.pushMessage({ role: 'tool', tool_call_id: toolCall.id, content: '[CONVERSATION_STEERING] 処理中に新しい指示が届きました ...', }); } continue; // 計画を立て直すため、もう一度推論へ } 2 つ目は、ツールを呼ばずに、ユーザーへの返信メッセージを作り終えていた場合です。本来ならこれを見せてループが終わるところですが、割り込みが届いたので止めるべきツールがありません。この返信はまだ画面に出していないので破棄し、直前のユーザーの指示に割り込みメッセージを足して送り直すことで、続きの作業を依頼します。 // ツールがない場合は、画面に出していない返信を捨てて指示を足す if (steering.length > 0 && toolCalls.length === 0) { buffer.pop(); // まだ画面に出していない返信を捨てる const priorUserTurn = buffer.pop(); // 直前のユーザーの指示を取り出す buffer.pushMessage({ role: 'user', content: `${priorUserTurn.content}\\n[CONVERSATION_STEERING] 処理中に新しい指示が届きました ...`, }); continue; } いずれの場合も、すでに実行した副作用を巻き戻すわけではなく、これから実行するはずだったツールを止めたり、まだ画面に出していない返信を捨てたりして、計画を組み直しています。 仕組み 2: Firestore を指示の受け渡し場所にしたリアルタイム反映 仕組み 1 で見たように、エージェントは複数のツールを往復させて指示に応えるため、編集には時間がかかることがあります。処理が終わるまで画面が何も更新されないと、利用者は反映されたかどうか分からないまま待つことになります。 そこで Firestore SDK を利用して、変更をリアルタイムに受け取れるようにしています。サーバ側は 1 つの操作が終わるたびにその内容を Firestore へ書き込み、ブラウザ側はそれをサブスクライブして即座に検知・反映します。これで自前で WebSocket を張らずに、編集の途中経過をそのままプレビューへ反映できます。 エージェントが何かを書き換えると、サーバは会話ごとのコレクションに、次のようなドキュメントを 1 件追加します。 { "status": "PENDING", "requests": [ { "action": "setHtmlSchema", "payload": "<body> ...更新後の HTML... </body>", "reason": "ユーザーの依頼を反映" } ] } ブラウザ側の JS は、Firestore の SDK(onSnapshot)でこのコレクションをサブスクライブしており、PENDING のドキュメントが届くと requests の各操作をエディタの状態へ反映します。たとえば setHtmlSchema なら、エディタが表示している HTML を新しいものに置き換えて、プレビューを再描画します。 ブラウザが requests の操作を反映し終えると、その JS が status を COMPLETED に書き戻します。HTML 差し替えなどの「反映だけ」のアクションは、投げたら終わりで結果を待ちません。一方でテスト実行のように結果が必要なアクションでは、ドキュメントが COMPLETED になるまで一定間隔で読み直して、書き込まれた結果を取り出します。取り出した結果はツールの戻り値としてエージェントに返り、それを見て次のツール呼び出しを決めます。 仕組み 3: ブラウザだけで完結するテストランナー 応募ボタンを押したときの API 呼び出しやその結果に応じた表示の切り替え、リンクによる画面遷移といった LP の動作を手動で確認するのは手間がかかります。そこで EGP Code では、こうした動作をテストで確かめられるようにしています。 テストはブラウザ上のエディタで直接書いたり、AI に書かせたりできます。これらのテストは、サーバや CI ではなく、プレビューと同じブラウザの中で実行します。ただし Jest や Vitest は Node.js 上で動くツールなので、そのままブラウザには読み込めません。そこで test / it / describe / expect を提供する小さなランナーを自作しました。アサーションは単体で使える @vitest/expect、DOM 操作は Testing Library をそのまま利用しています。 // 自作の test 関数でテストを定義する test('エントリーボタンで API が呼ばれる', async () => { // Testing Library でユーザーのクリックを再現する await userEvent.click(screen.getByText('エントリー')); // @vitest/expect で結果を検証する expect(mockEntry).toHaveBeenCalledWith({ campaign: 'X' }); }); テストが社内 API を呼ぶこともありますが、本番に飛ばすわけにはいきません。そこで iframe 内で window.fetch を差し替え、リクエストはすべてモック関数に通します。モックしていない呼び出しはエラーになるので、本番へ漏れることはありません。 テストの実行は、この iframe を作るエディタのページ(ホスト)と iframe の postMessage のやり取りで進みます。 ホストは iframe を作ってテスト用 HTML を流し込み、iframe 側の初期化(window.fetch の差し替えなど)が済むのを待ってから実行を指示します。先に指示が届くと取りこぼすため、必ず「準備完了」を待つようにします。結果は仕組み 2 のアクションでエージェントへ戻ります。失敗していれば、仕組み 1 で触れた自己修正がここで働き、内容を読んで実装を直してもう一度走らせます。 仕組み 4: プレビューの要素と HTML の位置を結ぶ対応表 ここまではエージェント主導の編集を見てきましたが、人が直接手を入れる場面もあります。Code タブを開くと Monaco エディタで HTML を直接編集でき、プレビューでリアルタイムに確認できます。プレビューの要素をクリックしてエージェントへ指示を出したり、Monaco の対応行へジャンプするには、「プレビューの要素と HTML 上の位置」を対応づける仕組みが必要です。 <img src="https://storage.googleapis.com/prd-engineering-asset/2026/06/1aa073c8-preview-jump-ja.png" alt="EGP Code のエディタ。左
こんにちは。メルペイのフロントエンドエンジニアの @mattsuu です。この記事は「Merpay & Mercoin Tech Openness Month 2026」の 2 日目です。 私たちのチームは、マーケターや PM 向けの社内ツール群 Engagement Platform (EGP) を開発しています。ランディングページ (LP) の作成・公開もその一機能で、過去に WYSIWYG コンポーネントエディタ EGP Pages について同じチームから紹介記事を出しています。 今回はその後継としてゼロから作り直した EGP Code を紹介します。AI エージェントと対話しながら LP を作るための、HTML ベースの社内向け LP エディタです。見た目を生成するだけでなく、本番運用に必要なところまで踏み込んで同じ編集体験の中に組み込んでいるのが特徴で、すでに 10 件以上の本番 LP がこの仕組みで作られています。 v0、Gemini Canvas、Claude Design、Figma Make など、AI で UI を作れるツールはすでに数多くありますが、見た目は作れても本番 LP として運用するには、API 連携・品質保証・Native 連携といった社内固有の課題が残ります。EGP Code は、このギャップを埋めるために内製しました。 EGP Pages と AI 編集における課題 EGP Pages は、ブロックを選んで組み合わせるノーコードの WYSIWYG コンポーネントエディタです。ドラッグ&ドロップで Layout や Text といった 40 種類以上のコンポーネントから、ページを組み立てます。マーケターがエンジニアの手を借りずに LP を作れるという目的に対して非常によく機能しており、いまも多くの LP が EGP Pages で作られています。 転機になったのは、AI でページを編集したいというニーズが出てきたことです。EGP Pages は人がドラッグ&ドロップで組み立てる前提で設計されており、AI が扱う際にデータ構造が問題になります。例えば、ボタンを押すと数字が増えるページの JSON ツリーは次のようになります。 { "components": [{ "id": "root", "elements": [ { "id": "1", "tagName": "Context", "props": { "value": [ { "name": "count", "type": "code", "value": "0" }, { "name": "increment", "type": "code", "value": "(count) => count + 1" } ]}}, { "id": "2", "tagName": "Layout", "props": { "children": [":=element.3", ":=element.4"] }}, { "id": "3", "tagName": "Text", "props": { "value": "Count: ${context.count}" }}, { "id": "4", "tagName": "Action", "props": { "label": "+1", "onTriggerAction": [{ "type": "SET_CONTEXT", "payload": { "count": "${context.increment(context.count)}" }}]}} ] }] } 人がエディタ越しに触る分には、この構造でも問題ありませんが、LLM に直接編集を任せようとすると課題が見えてきます。 ツリー構造が独自: ":=element.3" のような独自記法を AI に都度教える必要があり、プロンプトが長くなります。 ロジックが分散: 状態・条件・動作・表示が Context / When / Action / Text に散らばり、挙動の把握にツリー全体を辿る必要があります。 JSON 文字列の中に JavaScript が埋まっている: テンプレートリテラルか eval される式かが描画コンポーネント次第で、正しい解釈が難しくなります。 さらにこの JSON ツリーは等価な HTML のおよそ 2 倍のトークンを消費し、編集のたびに API コストとコンテキスト消費が膨らみます。加えてテスト基盤がなく、AI の編集結果を公開前に機械的に検証する手段がありませんでした。これは EGP Pages の設計が悪かったわけではなく、ノーコード時代に最適化された正しい設計でした。ただ AI に編集させるという前提が加わったことで設計を問い直す必要が出てきたのです。 HTML ベースで作り直す 選択肢は2つありました。既存の JSON 表現を AI 向けに改善するか、AI 前提でゼロから作り直すかです。私たちは後者を選び、ページの表現を HTML ベースにしました。HTML は人にも LLM にも馴染みがあり、独自の JSON ツリーや参照記法を毎回プロンプトで教える必要がないからです。 先ほどのカウンターページは、EGP Code では次にようになります。 <body> <egp-script timing="page-loaded"> rx.count = 0; </egp-script> <egp-script> rx.increment = () => { rx.count = (rx.count ?? 0) + 1; }; </egp-script> <p><egp-text>Count: {{rx.count}}</egp-text></p> <egp-button :onclick="rx.increment">+1</egp-button> </body> 機能は同じですが、コード量もトークン消費もおおよそ半分です。ただし、素の HTML だけでは、状態管理や条件分岐といった動的な振る舞いは表現できません。かといって <script> で自由に JavaScript を書かせると、ページの挙動を追いづらくなります。 そこで、状態管理・条件分岐・繰り返しといった動的な部分だけを、少数の Web Components (<egp-*>) に閉じ込めました。「静的な部分は普通の HTML、動的な部分だけ Web Components」という切り分けによって、EGP Pages のように状態・条件・動作が散らばらない構造になっています。 見た目のスタイリングには Tailwind CSS を採用しています。Web 開発者に馴染みのある書き方に寄せることで、人も AI も独自の作法を覚えずに済みます。また、副次的な効果として、外部ライブラリへの依存が最小限になり、LP ごとに独自パッケージが混ざりません。ランタイムは中央で管理する少数のものだけで動くため、npm パッケージ起因のサプライチェーンリスクが問題になる中でも安全面で利点があります。 使い方 EGP Code では、ほとんどの操作を AI エージェントとのチャットで進めます。「LP を作りたい」のように要件が固まっていない依頼では、エージェントはいきなり作り始めるのではなく、文脈に応じた質問を返してくれます。対象デバイス、カラーテーマ、入れたいセクションといった項目を選択肢から答えていきます。 回答を送ると、エージェントが HTML やテストをまとめて生成し、たたき台となる LP ができあがります。 大まかな見た目ができたら、ボタンやテキストなどの要素を直接選んで仕上げます。対象をクリックして「文字サイズを大きくして」「文言を〜に変えて」のように頼めば、位置を説明しなくてもエージェントが直す箇所を正確に把握します。複数箇所にまとめてコメントを付けたり、参考にしたい画像を貼って渡すことも可能です。 実運用に必要な 3 つの仕組み LP と聞くと、文章と画像が並んだ静的なページを思い浮かべるかもしれませんが、実際には、エントリー状況で CTA ボタンを切り替えたり、お客さまの属性で見せ方が変わったりと動きを伴う LP も少なくありません。 そのため、見た目だけが整っていれば十分というわけではなく、社内 API との連携、タップ時の分析ログ送出、公開前に表示や挙動を検証する仕組み、アプリと Web の遷移差を吸収する仕組みなど、周辺の仕組みもあわせて必要になります。 EGP Code では、こうした仕組みを編集体験の中に組み込んでいます。以降では、この 3 つの仕組みを順に紹介します。 社内 API 連携と Logging LP からは、商品一覧の取得やエントリー状況の確認といった社内 API への呼び出しが頻繁に発生します。しかし、社内 API は AI ツールの学習データに含まれていません。EGP Code では、このギャップを「使い方をその場で AI に教える」仕組みで埋めています。 例えば「商品一覧を出す API は?」と聞くと、エージェントは候補の社内 API を用途つきで挙げてくれます。 このやり取りの裏側では、エージェントが「関連する API を探す → 使い方を理解する → 型付きで実装する」という流れで動いています。 この流れを実現するために、いくつかの工夫をしています。まず、社内 API の使い方を、API ごとに Markdown ファイルに記載しています。実際には、次のようなファイルが並んでいます。 api-searchExampleItems.md api-postExampleEntry.md api-getExampleSegment.md api-getExampleRecommendations.md runtime-event-log.md ... すべての API の説明をプロンプトに乗せてしまうと不要にトークンを消費してしまいます。そこで各ドキュメントのタイトルと用途だけを渡しておき、AI が必要と判断したときにだけその本文を読み込ませる形にしています。 次に、社内 API は型付きの薄いラッパー関数越しに呼び出します。LP から見えるのは関数呼び出しだけで、認証ヘッダ・サービス分岐・パスの違いはラッパーが吸収します。誤った使い方があれば Lint が検知します。 分析ログの送出も同じドキュメント参照のしくみで扱っています。 ログが必要になったタイミングで、エージェントが対応するドキュメントを読み込み、API 呼び出しと同じ流れでログ用のコードを生成します。このしくみによって、AI に「商品一覧を出して」と頼むだけで、社内 API を正しく呼んだ動的な LP が組み上がります。 エディタ内で完結するテストと品質保証 AI が生成した API 呼び出しが正しく動いているか、ボタンが期待どおりに反応するかを、変更のたびに人が目視で確認するのは現実的ではありません。そこで EGP Code は、エディタ内にテストの仕組みを内蔵しています。 エンジニア以外にとってテストは馴染みがなく、自分で作成するのはハードルの高い作業です。そこで、テストを直接書く代わりに、実現したい振る舞いを自然な言葉で書ける Spec タブを用意しています。ここに、LP の仕様を書き残していきます。 あとは AI とのチャットで「@SPEC.md を元にテストを書いて」と頼むと、文脈に応じてテストが自動で生成されます。テストはエディタ向けに自作した Jest 風の API で書いており、内蔵のモックサーバで fetch を差し替えられるので、本番 API がなくても動的ページの挙動をエディタ内で再現できます。 // ブラウザ上でそのまま実行される test('エントリーボタンで API が呼ばれる', async () => { render(html); await userEvent.click(screen.getByText('エントリー')); expect(mockEntry).toHaveBeenCalledWith({ campaign: 'X' }); }); テストが失敗すると、その結果は AI にフィードバックされ、AI が自己修正します。仕様を書く → AI が実装とテストを生成する → ブラウザで挙動を確かめるという流れがエディタ内で完結するため、静的な LP から API を使った動的な LP まで、同じ仕組みで品質を担保しながら作ることが可能です。 アプリと Web の差を吸収する Native 連携 キャンペーンページなどの LP は、Web ブラウザだけでなく、メルカリアプリ内の WebView でも開かれます。このとき通常のリンクのままだと、アプリ内では外部ブラウザが開いてしまい、アプリのネイティブ画面へ遷移できません。これを LP ごとに userAgent 判定や Native bridge の呼び出しで書くのは現実的ではありません。そこで、その差をプラットフォーム側で吸収し、LP を作る側は専用の Web Components を使うだけで済むようにしました。 <egp-link href="https://jp.mercari.com/search?keyword=camera"> カメラを探す </egp-link> このリンクをクリックすると環境を自動で判定して、アプリ内なら Native bridge 経由でネイティブ画面へ、Web なら通常のリンクとして開きます。 まとめ さまざまな AI ツールによって UI を作りやすくなっていますが、実運用の LP に乗せるには、API 連携・品質保証・Native 連携といった作り込みが必要になります。EGP Code は、そこをプラットフォーム側に組み込むことで、UI づくりから運用までを同じ編集体験の中でつなげようとしています。 実際に次のような新しい進め方が出てきています。 PM とフロントエンドエンジニアだけで、仕様策定から実装・リリースまでを完遂 バックエンドエンジニアが API から LP まで 1 人で構築 マーケターが静的な LP を 1 人で制作 テストや API 連携を含む動的な LP は、まだ非エンジニアだけで完結させるには難しい部分が残っています。それでも、誰がどこまで担えるかの境界は少しずつ動いていて、いずれはこうした動的な LP も非エンジニアだけで作れるようになると考えています。 次の記事は @sinmetalさんの「MySQLからSpannerに移行した時のQueryチューニング」です。引き続きお楽しみください。
こんにちは。メルペイ Engineering Engagement チームの @mikichin です。 メルカリグループは「あらゆる価値を循環させ、あらゆる人の可能性を広げる」をミッションに、さまざまなサービスを展開しています。 メルペイは単なる決済サービスではなく、新しい「信用」を基盤として、それに基づく循環型社会、なめらかな社会を創ることを、メルコインはテクノロジーによって、さまざまな価値観の境界線を打ち破り、誰もが暗号資産・デジタル資産などあらゆる価値を簡単に交換できる世界の実現を目指しています。 そのためには、お客さま・企業・金融機関など、さまざまなステークホルダーに対して「OPENNESS」な姿勢で向き合うことで、もっと身近なものに変えていきたいと考えています。 本企画は、技術も「OPENNESS」にしていこうという考えのもと、2019年にスタートしました。 「Merpay & Mercoin Tech Openness Month 2026」では、メルペイ・メルコイン・メルカリモバイルの開発をしているエンジニアたちの取り組みをご紹介します。 各エンジニア組織がテクノロジーでお客さまの課題解決を実現することを大切にし、その挑戦の中で得た知見を6月1日から約1ヶ月間に渡り公開していきます!技術、開発設計や思想、組織ストラクチャー、Tips、その他最近の取り組みなど、幅広くお伝えします。 2019年はこちら 2020年はこちら 2021年はこちら 2022年はこちら 2023年はこちら 2025年はこちら ▼公開予定表 (こちらは、後日、各記事へのリンク集になります) Title Author メルカリのグローバル展開を支えるPayment Platformの進化 @ryuyama AI と作る HTML ベースの LP エディタ EGP Code を内製した理由Why we built EGP Code, an HTML-based AI editor for landing pages @mattsuu MySQLで実装されたクエリをSpannerに移行した時に行ったパフォーマンスチューニング @sinmetal 決済プラットフォームと経理を繋ぐ MoneyFlow @komatsu 会計システムにおける訂正機能の設計と実装 @hokao 事業者請求払いのための与信管理マイクロサービスの設計 Designing a Credit-Management Microservice for Partner Invoice Payment @imamu カーソルベースAPIのデータをマージするページネーション設計 @nanacom AI と作る LP エディタ EGP Code を支える 4 つの仕組み Four Mechanisms Behind EGP Code, an AI-Powered Landing Page Editor @mattsuu Pub/Sub drivenなmicroserviceにPR単位の検証環境を導入した話 @mikupo 内製ワークフローエンジンの設計とメルカリでの活用事例 @sapuri Build First, Discuss Later|初回ミーティングに動くプロトタイプを持ち込んだら、意思決定が爆速になった Build First, Discuss Later: How Prototypes Speed Up Product Decisions @kubomi Cloud Functions 世代移行で発生した1000万件のメッセージ滞留:Pub/Sub × Cloud Run × Spanner のチューニング @mewuto 修正PRを食べてレビュースキルが賢くなる:Claude Codeによる自己改善サイクル @um(うめ) メルペイのキャンペーン基盤をルールベース汎用システムに書き直して Otoku Revolutionするまで — Santa Service の Rulebase 移行の話 @hasegway TiDB – BQ連携データパイプライン @orfeon About my AI work setup @cyan Product Engineerとして働く @anzai Otoku Revolutionのすべてをお話します @yutaro 任意の単位での債権の色付けと与信の分割を実現するDebt View @kobaryo TBD @abcdefuji Growth Platform体制の振り返り @yo-gawa Next Payment @haoyu TBD @becosuke どんな知見が得られるのか、毎日が楽しみです。 Merpay & Mercoin Tech Openness Month 2026 の1日目は、メルペイ Payment Platform @ryuyama が執筆予定です。 ひとつでも気になる記事がある方は、この記事をブックマークしておくか、 エンジニア向け公式Xをフォロー&チェックしてくださいね!
はじめに こんにちは。MercariでPMインターンをしている菊池翔吾です。 インターン期間中に mercari-pm-agent というClaude CodeのSkillを開発しました。PMが行う「問題の発見→データ収集→PRD作成→UIモック」の一連のワークフローを、1つのセッション内で処理するエージェントです。 この記事では、PMのワークフローをClaude Code上でどのように実装したか——Skillの設計と、MCP(Model Context Protocol)を使ったNotion・Slack・Looker・Figmaとの接続方法——を中心に紹介します。 背景:メルカリPMの情報収集ワークフローと課題 メルカリのPMが意思決定を行うには、複数のツールを横断して状況を把握する必要があります。 Notionで中期戦略・KPI目標の方向性を確認する Slackで社内の改善要望やフィードバックを検索する Lookerでユーザー行動の定量指標を確認する Figmaで対象画面の現状デザインを確認する これらを統合してPRD(製品要求仕様書)に落とし込む 各ツールへのアクセス自体は難しくありませんが、ツールを横断しながら「どのデータが今の判断に関係するか」を整理する作業には一定の時間がかかります。PMが本来時間を使うべきは、集めた情報をもとに深く考え、意思決定し、関係者と対話することのはずです。情報収集にかかる時間を、思考と意思決定に充てられるようにしたい——それがこのツールを作った動機です。 mercari-pm-agentの概要 mercari-pm-agent は、Claude CodeのSkillとして実装したPM支援エージェントです。 PMがプロダクト上のビジネス課題を自然言語で入力すると、以下のステップが自動的に進みます。 処理の流れ 実装:Claude Code SkillsでPMワークフローを定義する Claude Code Skillsとは Claude Code Skillsは、Claude Codeの振る舞いをMarkdownファイルで定義する仕組みです。SKILL.md にエージェントの動作手順・制約・ツールへのアクセス方法を記述することで、特定の業務フロー専用のエージェントを構築できます(公式ガイド)。 コードを書かずにエージェントの振る舞いを定義できる点が特徴です。PM向けSkillの実装例としては phuryn/pm-skills も参考にしました。ただし、後述するように「Markdownを書くだけ」では精度は出ません。振る舞いの制約設計と評価サイクルが重要です。 ファイル構成:関心の分離をプロンプト設計に適用する mercari-pm-agent/ ├── [SKILL.md](http://skill.md/) # エージェントの振る舞い定義(英語) └── references/ ├── [prd-template.md] # PRDテンプレート ├── [prd-checklist.md] # PRD品質チェックリスト(9項目) ├── [ui-and-figma.md] # UI Spec・Figma Makeプロンプトテンプレート ├── [laplace-guide.md] # データ解釈ガイド ├── [data-sources.md] # データソース一覧・使い方 └── [quick-reference.md] # 出力チェックリスト 初期は全ての定義を SKILL.md 1ファイルに集約していましたが、後述する評価スキルによるスコアリングを通じて、ファイルが長くなるほど出力精度が低下するという問題を確認しました。 これはLLMの特性と関係しています。コンテキストが長くなると、モデルが文脈の中で関連情報に適切に注目できなくなる現象(いわゆる「Lost in the Middle」問題)が知られており、Anthropicのプロンプトエンジニアリングガイドでもプロンプトを簡潔に保つことが推奨されています。 対応として、振る舞いの定義(SKILL.md本体)と参照データ・テンプレート(references/)を分離しました。ソフトウェア開発における「関心の分離(Separation of Concerns)」をプロンプト設計に適用したアプローチです。SKILL.mdはエージェントが「何をどの順序でするか」のみを保持し、具体的なデータやテンプレートは必要なタイミングでreferencesから参照する設計です。この構造変更だけでスコアが明確に改善しました。 なお、SKILL.mdは英語で記述しています。Claudeへの指示として英語の方が精度が高いためです。 MCP接続:複数ツールをエージェントに繋ぐ mercari-pm-agent の中核的な価値は、Step 2のデータ収集を自動化する点にあります。ここではMCP(Model Context Protocol)を使ったツール接続の設計について説明します。 MCPとは MCPはAnthropicが策定したオープンプロトコルで、LLMアプリケーションが外部ツールやデータソースに接続するための標準仕様です。MCPサーバーを通じて、Claude CodeからNotion・Slack・Lookerなどの外部サービスをツールとして呼び出せるようになります。 接続しているMCPサーバー MCPサーバー 種別 取得できる情報 用途 Notion MCP 公式(Notion提供) 戦略ドキュメント・KPIダッシュボード 中期戦略との整合性確認 Slack MCP 社内独自実装 社内フィードバックチャンネルの投稿 改善要望・現場の声の収集 Socrates 社内独自実装(BigQuery・Lookerベース) CVR等の指標データ 定量的な課題の裏付け Figma MCP 社内独自実装 デザインファイルのコンポーネント情報 既存デザインの取得・UI Specへの反映 並列クエリと堅牢性の設計 Step 2(データ収集)では、これら複数のMCPを並列でクエリします。data-sources.md に以下のルールを記述しています。 - Pull in parallel during Data Enrichment — do not wait for one source before querying another. (データ収集フェーズでは並列で参照する。1つのソースの完了を待たないこと) - If a source is unavailable, skip silently and mark it in the output. (ソースが利用不可の場合は、出力にその旨を明記してスキップする) 直列での順次参照に比べてユーザーの待ち時間を削減するためです。また、いずれかのMCPが利用不可の状態でも処理が止まらないようフォールバック設計を入れています。 セキュリティ上の考慮 Slack MCPのセットアップには社内VPN接続とUser Tokenによる認証が必要です。トークンはClaude Codeの設定ファイルに環境変数として渡す形にしており、チャット上でトークン文字列が露出しない設計にしています。また、SlackのUser Tokenは7日で失効するため、更新用のスクリプトを別途用意しています。 開発で大事にしたこと 評価基準を先に決める——プロンプトのTDD 実装を始める前に、まず「エージェントの出力をどう評価するか」の基準を定義しました。 課題の理解精度(問題の本質を正しく捉えているか) 仕様の具体性(実装可能なレベルで記述されているか) 実現可能性(技術的・リソース的に妥当か) UXの妥当性(お客さまにとって使いやすいか) これはソフトウェア開発におけるテスト駆動開発(TDD)に近い発想です。LLMベースのエージェントは「動くかどうか」より「正しく動くかどうか」の判定が難しい。評価軸を先に定義することで、プロトタイプの改善サイクルを感覚ではなく基準で回せるようになりました。実際のWeb改善課題を収集して評価データセットを作り、反復的に精度を上げていきました。 LLMの「それらしい嘘」を制約として防ぐ LLMを業務フローに組み込む上で最も危険なのは、「根拠のないそれらしい情報」の生成です。データが存在しない状況でも、モデルは自然に「それっぽい数値」を出力します。PMがその数値を信じてPRDに記載してしまうと、意思決定の根拠がフィクションになります。 これは「嘘をつくな」とプロンプトで命令するだけでは解決しません。モデルがデータ不足を認識したとき、どう振る舞うかを制約として設計する必要があります。 Data integrity rules: Unconfirmed data must be labeled "Not provided" or "To be validated" (未確認のデータは "Not provided" または "To be validated" とラベルすること) Never fabricate numbers or sources (数値や出典を捏造しないこと) さらに、PMの確認なしに次のステップへ自動的に進むことを禁じました。 You are NOT allowed to infer completeness. Only explicit confirmation from the PM allows progression. (完了を推測して次へ進むことを禁じる。PMの明示的な確認があった場合のみ次へ進める) これにより、エージェントが「それらしい流れ」で自動進行するのではなく、常にPMが意思決定のドライバーである状態を維持します。 スキルをスキルで評価する——自動評価パイプライン 設計したルールが実際に機能しているかを検証するため、評価専用のスキル(skill-creator-max)を別途作成しました。mercari-pm-agent に対してテストケースを投げ、出力の品質をスコアリングして返すエージェントです。このスコアを使った反復改善の中から、前述の「SKILL.mdは短いほど精度が上がる」という知見が得られ、ファイル分割の設計変更につながりました。 まとめ mercari-pm-agent の開発を通じて得た、Claude Code Skillsを使ったエージェント設計の主な知見をまとめます。 Skillの設計は「振る舞いの仕様書」を書くことに近い。 命令ではなく制約の設計が重要で、LLMが「どう振る舞うべきでないか」を明示することが精度に直結する。 MCPによる外部ツール接続は並列設計で。 直列参照はユーザー体験を悪化させる。フォールバック設計とあわせて、接続の堅牢性を考慮する必要がある。 プロンプト設計にも関心の分離が有効。 コンテキストが長くなるほど精度が下がる。振る舞い定義と参照データの分離は、ソフトウェア設計の原則をLLM設計に適用した結果として機能した。 評価基準は実装より先に作る。 LLMエージェントの品質評価は主観に陥りやすい。評価軸を先に定義し、評価専用のエージェントを作ることで客観的な改善サイクルが回せる。 mercari-pm-agent はClaude CodeのSkillとして実装しているため、MCP設定が済んでいれば /mercari-pm-agent のコマンド1つで起動できます。 PMの業務効率化やClaude Code Skillsを使ったエージェント設計に興味のある方の参考になれば幸いです。