はじめに ネットショップ作成サービス「BASE」のプロダクト開発チームでエンジニアリングマネージャー(EM)をしている髙嶋です。 BASE は組織としても個人としてもエンジニアイベントに積極的に関わっており、時折イベントの会場提供をさせていただくこともあります。2026/8/24(月)に「PHPerKaigi mini #4」を弊社オフィスにて開催したので、本記事ではその様子についてご紹介します。 phperkaigi.connpass.com PHPerKaigi mini とは PHPerKaigi mini は、技術カンファレンス「PHPerKaigi」のスポンサー企業およびスポンサーを検討中の企業がホストとなり、PHPer 向けに開催される勉強会です。トークテーマは必ずしも PHP 関連である必要はなく、「PHPer が聞いて面白ければ何でもアリ」というゆるやかなコンセプトで運営されています。 今回は第4回として、我々 BASE 株式会社がホストを務めました。 当日の雰囲気 登壇者・参加者あわせて20名程度にお集まりいただき、6本の LT と懇親会で大いに盛り上がりました。 ちなみに BASE からは、 @meihei が「CloudSearch から OpenSearch へ切り替えたけど質問ある?」と題して発表を行いました。詳細な内容については、いずれまた別の形でアウトプットされる予定なので楽しみにしていてください。 他には ADR の実践から Claude Code の個人開発活用、DI フレームワークの高速化まで、テーマが多岐に渡ったのも本イベントらしい点だったのではないかと思います。 おわりに 弊社では今後も機会があればエンジニアイベントの会場提供を行っていきたいと考えていますので、お気軽にお声がけください。 最後に、BASE はエンジニアイベントでわいわい楽しみたいという方に非常にマッチする環境です。ご興味のある方は、ぜひ採用情報もご覧いただけますと幸いです。 binc.jp
はじめに この記事は、夏のブログリレー2026 12日目、最終日の記事です。 こんにちは!PAY ID Product Divisionの岡部( @rerenote )です。 これまできちんとご挨拶する機会がありませんでしたが、今年の4月からこのブログの編集長を務めています。 8月下旬からスタートしたブログリレーも、ついに最終日となりました。 今回は、これまでの記事をあらためてご紹介しながら、ブログリレーを振り返っていきたいと思います。 夏のブログリレー2026 企画について 🍉 BASE PRODUCT TEAM 夏のブログリレー2026 🍉 今回のブログリレーは、「夏にもブログリレーをやってみませんか?」という企画を持ち込んでもらったところから始まりました。 最初にお話を伺った時は「お盆時期だし、執筆者集まるかな?」という若干の不安もありましたが、いざ募集してみるとあっという間に枠が埋まり、ほっとしました。こういう企画にフットワーク軽く参加してくれる方が多く、いつも非常にありがたみを感じております。 夏のブログリレー2026 振り返り AI活用に関する記事 問い合わせ調査AIエージェントcs_a導入の苦難とこれから AI時代のエンジニアに何が必要か、いま自分が考えていること iOSアプリ開発で プルリクの動作確認 スクショを自動化 SentryアラートをAIで自動調査するSlack Botを作った話 会社のはてなブログ運用をHatenaBlog WorkflowsとAIで効率化する 技術の使い方紹介に関する記事 Slack ワークフローから GitHub Actions を起動する 脅威モデリングはじめました フィーチャートグルをもっとEasyに使おう マルチモーダルな商品カテゴリの分類モデル BASEでの開発・運用に関する記事 「主要SLO」を半年運用して、得られた手応えと見直していること 顧客中心主義を開発の意思決定に組み込むために実践したこと 総括 最も多かった話題は、AI活用に関するものでした。 BASE社内でもAIの導入は進んでおり、開発に限らず業務のいろいろな場面で活用されています。今回のブログリレーでも、実際の業務の中でどのようにAIを取り入れているのか、さまざまな事例が集まりました。 一方で、技術の使い方紹介や開発・運用に関する記事も投稿されており、普段の業務の中で得られた知見や取り組みを、幅広く紹介できたのではと思います。 編集長としてこのブログについて感じていること このブログには、BASEでの仕事の中で生まれた知見や試行錯誤、プロダクトチームでの出来事などが記されています。 書く人にとっては自分の仕事の足跡を残す場所、読む人にとってはBASEのことが気になった時に知るための手がかり。このブログがそんな場所になっていくとよいなと感じています。 最近では「ブログというメディアが読まれなくなってきた」という話も耳にするようになりました。その背景には情報収集の手段の多様化も一因としてあるのだと思います。 情報の届け方や受け取り方が変わっていく中でも、あとから誰かがたどれる形で記録が残っていること自体に価値があると信じて、今後もいろいろなログを残していけたらと考えています。 おわりに 夏のブログリレー2026に参加してくださったみなさま、企画を持ち込んでくれたメンバー、ブログ運営を支えてくれている編集部メンバー、そしてこの記事を読んでくださっているみなさま、この場をお借りしてお礼申し上げます。ありがとうございました! 夏のブログリレー2026はこれで終了となりますが、これからもBASEでの日々の仕事をお届けしていきますのでよろしくお願いいたします。
はじめに この記事は、BASEテックブログ夏のブログリレー11日目の記事です。 BASE Dept. Product Divにてバックエンドエンジニアをしている オリバ です。 ショップオーナーの発送領域を担当しており、バックエンドを中心に、フロントエンドやNew Relicなども触っています。 2026年5月、担当する「送り状一括印刷 App」の一部機能をリリースしました。しかし、リリース後に分かったのは、想定していた使われ方をされていないということでした。機能自体は動いていますが、開発チームが想定していた操作と、ショップオーナーの実際の操作が違っていました。 Appの概要や使い方は以下をご覧ください。 送り状一括印刷 App(BASE Apps) apps.thebase.com 発送作業の時間を大幅削減!「送り状一括印刷 App」の使い方解説(BASE U) baseu.jp 本記事では、顧客の声を開発の意思決定に組み込むために実践した3つの取り組みと、その効果について共有します。 顧客中心主義とは 私は仕事の中で「顧客中心主義」という考え方を心掛けています。『みんなでアジャイル』で知った言葉です。 この本では、組織全体でアジャイルを実践するための原則として、次の3つが紹介されています。 「顧客から始める」「早期から頻繁にコラボレーションする」「不確実性を計画する」 引用:みんなでアジャイル ―変化に対応できる顧客中心組織のつくりかた|O'Reilly Japan www.oreilly.co.jp もともと私はDDDやクリーンアーキテクチャが好きで、設計に関心がありました。これらは変更容易性を高めるための手段でもあり、いわばHOWにあたります。ただ、HOWをいくら磨いても、何を作るかというWHATとなぜ作るのかというWHYが定まっていなければ成果にはつながりません。自分にはこのWHATが足りていないと感じ、顧客中心主義を実践しようと思いました。 とはいえ、言葉として知っていることと、日々の開発で実践できているかは別です。 なぜ顧客の声を聞きに行ったのか 以下の3点が理由です。 1. 開発チームだけではユースケースの想像に限界がある 新機能の開発時、その機能が使われるユースケースを開発チームだけで想像していました。しかし、ショップオーナーが発送作業の際にどの画面を見て、何を基準に判断しているのかは把握できていませんでした。 2. チームが向かう方向を決めるきっかけにしたかった 私は送り状一括印刷App開発のPJMを務めており、チームが向かっていく方向づけも担っています。「この機能を今すぐ開発すべきなのか、本来の趣旨から外れているから後に回すべきか」といった判断を、顧客の実態をもとに行いたいと考えました。 3. AIの導入で実装スピードが上がり、PDCAを回したかった 開発にAIを導入したことで、実装スピードが上がりました。ただし、作るスピードが上がっても、何を作るかの精度が上がらなければ効果は限定的です。リリース後の反応を早く拾い、次の改善につなげるサイクルを回したいと考えました。 実践① ユーザーインタビューに同席する PdM・デザイナーが実施するショップオーナーへのユーザーインタビューに、3〜4回同席しました。聞くだけではなく、私からもいくつか質問をしています。 想定していた動線と実際の動線が異なっていた 送り状一括印刷 Appでは、お届け先住所の初期値に購入者がカートで入力した住所情報が、送り主情報の初期値にAppの設定画面で登録した住所情報が設定されます。個別に変更したい場合は、注文画面の送り状印刷アイコンからモーダルを開いて変更できます。 しかしインタビューでは、このモーダルの存在に気づいてもらえておらず、「お届け先住所と送り主情報の初期値は変更できない」と認知されていることが分かりました。 注文画面の送り状印刷アイコン(2026年9月時点) 送り状印刷アイコンから開く「送り状内容」モーダル(2026年9月時点) 発送通知の失敗が把握しづらく、独自運用で補われていた 発送通知に失敗しても、ショップ側で後から把握しづらい状態になっていました。そのため、注文メモなどを使った独自運用で補っているケースがありました。 エラーの有無だけでなく、対象の注文・失敗理由・次に取るべき行動を後から確認できる必要があると考えています。 注文画面が発送判断の中心的な画面になっている インタビューを通じて、注文画面がショップの梱包・発送を判断するための中心的な作業画面になっていることが分かりました。メンバーシップ特典など、発送判断に必要な情報は注文画面上で確認・検索できるとよいと考えています。 発送判断の中心となる注文画面(2026年9月時点) 細かな改善でも現場の制約を解消できる 問い合わせで挙がっていた要望をもとに、送り状一括印刷 Appで印刷位置を指定できるようにしました。その後のユーザーインタビューでは、この改善についてショップオーナーから感謝の声をいただきました。大きな機能追加ではなくても、現場の制約を解消する改善には実用的な価値があると感じました。 同席して分かったこと 開発者には自明なアイコンや操作でも、初めて使うショップには意図が伝わらない場合がある UIだけでなく、ツールチップや画像付きヘルプなども含めて使い方を伝える必要がある ショップオーナーの利用実態や反応を直接知ることが、開発の目的と価値の再認識につながる 実践② 問い合わせ内容を定期的に確認する BASEのショップ管理画面には問い合わせフォームがあります。このショップオーナーから寄せられた問い合わせを、定期的に確認するようにしました。 開発中・リリース直後の機能は週に数回確認する 開発中の機能やリリースしたばかりの機能については、週に数回確認しています。ユーザーインタビューと違って日程調整が不要なため、リリース直後の反応をすぐに拾えます。 SLI/SLO振り返り会のアジェンダに組み込む 個人が気の向いたときに見るだけでは、見る人と見ない人が分かれ、チームの判断材料になりません。そこで、Order Sectionで月1回実施しているサービスレベル(SLI/SLO)振り返り会のアジェンダに、顧客の声を確認する時間を組み込みました。 SLI/SLOの数値は、システムがどれだけ安定して動いているかを示します。しかし、その数値が顧客にとって十分かどうかは、数値だけでは分かりません。同じ場で顧客の声を扱うことで、指標と実態を突き合わせて話せることを狙っています。 見つけた声はSlackでチームに共有する 送り状一括印刷 Appについては、PdMと私が問い合わせ内容を見て、気になった声をSlackでチームメンバーに共有しています。現状はこの形で回っており、メンバー全員がDBを見に行く状態にはなっていません。 実践して分かったこと 送り状一括印刷 Appにおいて、想定した使い方がされていないことをすぐに認知でき、改修からリリースまでのサイクルを短く回せた。アジャイルの利点が出た部分だと感じている SLI/SLOのエラーバジェットの数値が安定していても、ショップオーナーの希望を満たせているとは限らないことが分かった 実践③ CXチームにヒアリングする CXチームは、ショップオーナーと継続的にコミュニケーションを取りながら、ショップ運営の課題解決や「BASE」の活用を支援するチームです。このCXチームにMTGの時間をもらい、ヒアリングをしました。 非機能要件の課題を見つける目的で声をかけた CXチームが担当するショップオーナーの中には、規模の大きいショップも多く含まれます。注文・発送領域のパフォーマンスなど、非機能要件の課題を見つけられるのではないかと考え、声をかけました。 実践①のユーザーインタビューや、実践②の問い合わせ内容で拾えるのは、使い方や機能に関する内容が中心です。一方で、表示速度のような非機能要件の話は、問い合わせとして上がってこないこともあります。 開発チームからは見えていなかった課題を把握できた ヒアリングを通じて、開発チームからは見えていなかった非機能要件の課題を把握できました。具体的な内容はここでは割愛しますが、実践②で書いたとおり、SLI/SLOを見ているだけでは気づけない内容でした。 単発のMTGから継続的なやり取りへ ヒアリングのMTG自体は一度だけですが、その後もCXチームとのやり取りは続いています。 実践して分かったこと 非機能要件の課題は、ショップオーナーと日常的に接しているチームのほうが実態を把握している場合がある エンジニアから他部署に声をかけることで、一度のヒアリングだけでなく継続的な相談先ができる 何が変わったか 改善タスクの起票数が増えた Order Sectionには複数チームがあり、私が所属するチームでは、注文画面や発送領域に関する改善タスクの起票数は、以下のように変化しました。 2〜6月:月2〜4件 7月以降:月7〜8件 7月ごろにユーザーインタビューを実施し、そこで得た内容をもとに課題の洗い出しから起票、実装、リリースまでを行いました。顧客の声を聞くことで、これまで見えていなかった課題が可視化され、タスクとして起票できるようになったことが大きいと考えています。 エンジニアから提案してタスク化する場面が増えた これまでは、Bizチームや顧客問い合わせの多さを鑑みて、別部署から依頼された案件をタスク化していくことが多かったです。顧客の声を直接知るようになってからは、エンジニアからPdMやデザイナーに提案し、タスク化して改善につなげる場面が増えました。 職種を越境して、主体的に改善タスクを推進できるようになったことが、今回の一番大きな変化だと感じています。顧客の声が、何を作るかを決める際の材料として開発の意思決定に入るようになった、とも言えます。 うまくいかなかったこと 課題が可視化され、AIによって実装も速くなっていたため、タスクを並列で進めようとしてしまいました。結果、自身が疲弊しました。 顧客の声を聞くと、改善すべきことは確実に増えます。実装スピードが上がったことで、「やれるはず」と思ってしまったのも原因だと考えています。声を集める仕組みだけでなく、集まったものを絞る側の判断も必要だと感じています。 これからやりたいこと BASEコミュニティ Meetupに参加する 弊社ではショップオーナー向けのコミュニティポータルを運営しており、その一つの取り組みとしてBASEコミュニティ Meetupを開催しています。 BASEコミュニティポータル community.thebase.com 前回はこちら:BASEコミュニティ Meetup vol.15 大阪(2026年8月開催) community.thebase.com 今後は運営側としてこの場に関わり、ショップオーナーの声を直接聞いて発送領域の改善につなげていきたいです。 非機能要件の課題をもっと洗い出す CXチームへのヒアリングで、非機能要件の課題は開発チームから見えにくいことが分かりました。機能要件だけでなく、非機能要件の課題を洗い出して改善していきたいと考えています。 おわりに 本記事では、顧客中心主義を開発の意思決定に組み込むために実践した3つの取り組みを紹介しました。 開発チームの中だけで顧客の使い方を想像していた状態から、顧客の実態をもとに改善を提案できる状態に変わりました。ただし、可視化できてきたのは機能要件に関する要望が中心です。非機能要件の課題はまだ隠れているものが多く、これをどう見つけていくかが今後の課題です。 弊社では、顧客の声をもとにプロダクトを改善していくことに関心のあるエンジニアを積極採用しています。興味を持っていただけたら、ぜひ下記からご応募ください! binc.jp 明日は、BASEテックブログ夏のブログリレーのラストを飾る、rerenoteさんの記事です。お楽しみに!
はじめに こんにちは!PAY ID Product Divisionの岡部( @rerenote )です。 今回はBASEがシルバースポンサーとして協賛しているカンファレンス、 Product Engineering Conference 2026 のご紹介となります。 product-engineering.jp Product Engineering Conference 2026 概要 Product Engineering Conference(略称: PdEConf / PdE Conf)は、職能の壁を越え、プロダクト価値を最大化させるための「技術」と「知恵」を共有し、議論し、高め合う場を作ることを目的とします。 Product Engineering Conference 2026 公式サイト Product Engineering Conferenceは「プロダクトエンジニアリング」をテーマとして初開催されるカンファレンスです。 2026/9/5(土)、中野セントラルパークカンファレンスで開催されます。 登壇メンバーのご紹介 BASEで活躍しているメンバーのうち、1名が登壇予定です。参加される方はぜひ聞きにいらしてください。 エンジニアリングは、どこまで拡大解釈できるか 心技体をつないだままで事業責任者になった話 柳川 慶太( @gimupop ) Hall A 11:30 登壇者メッセージ 責任感を持て、と言うだけなら無責任です|柳川慶太 の内容をベースにお話する予定です。 fortee.jp note.com おわりに 参加チケットは完売していますが、後日イベントレポートをお届けできればと思っています。みなさまにお会いできることを楽しみにしております! 発表などを見てBASEで働くことに興味を持った方は、ぜひ採用情報もご覧ください。 binc.jp
はじめに この記事は、BASEテックブログ夏のブログリレー10日目の記事です。 こんにちは、Data Strategyチームの竹内です。 BASEでは日々数多くの多種多様な商品が新しく登録されています。それらの商品が「何のカテゴリの商品なのか」を機械学習モデルで自動的に推論する仕組みを、以前からバッチ処理基盤として運用してきました。 今回、そのモデルを 商品テキストと商品画像の両方を入力に取るマルチモーダルなモデル に置き換えたので、その経緯やモデルの中身について紹介します。 はじめに TL;DR なぜ商品カテゴリを推論するのか これまでの取り組みと、画像を使いたくなった理由 使用したモデル 1. 画像を3つのトークンに変える 2. 画像特徴を「BERTのトークン」に翻訳する 3. 1本のTransformerに流し込む 4. 分類と埋め込みの取り出し 結果 埋め込みベクトルの活用 おわりに ※ 記事内のコードはサンプルとして簡略化しています。 TL;DR 商品タイトル・説明文などのテキストと、商品画像の両方を入力とする商品カテゴリ分類モデルを作成しました モデルは MMBT(Supervised Multimodal Bitransformers)をベースに、画像側を Swin Transformer、テキスト側を日本語BERT( tohoku-nlp/bert-base-japanese-v3 )に置き換えたものです 学習データは約100万件の商品で、ラベル(498クラスの階層カテゴリ)はLLMによるアノテーションで付与しました なぜ商品カテゴリを推論するのか BASEには毎日さまざまな商品が登録されますが、「その商品がどのカテゴリに属するか」を横断的に把握することは困難です。ショップ側で設定されたショップカテゴリは任意項目であり、また「ショップのカテゴリ」と「そのショップが実際に売っている個々の商品のカテゴリ」は必ずしも一致しません。 そこで、商品ごとにカテゴリを機械学習で推論して付与しています。用途は大きく3つあります。 分析基盤としての提供 … カテゴリごとの流通額・登録数・不正決済の発生状況といった、商品全体像に対する解像度を上げるための分析軸 不正検知モデルの特徴量 … 不正決済の検知や不正な商品登録の検知など、各種モデルの特徴量として利用 プロダクトでの活用 … 推薦や検索など、アプリ側の機能での利用 これまでの取り組みと、画像を使いたくなった理由 商品カテゴリの推論そのものは新しい取り組みではなく、2022年にBERTを使ったモデルとその推論基盤について記事を書いています。 devblog.thebase.in このときのモデルは商品タイトルと説明文を結合したテキストのみを入力とするもので、記事でも画像の利用は今後の課題としていました。 商品によっては、説明が簡素で短いものや、購入者とのやり取りや注意事項のみを記載しているものなど、テキストだけでは何の商品か判断できない商品が一定数あります。一方で、そうした商品でも画像を見れば人間には一目で分かることが多くあります。 逆に、商品の外装やイメージだけのものなど、画像だけでは判断がつかない商品も存在します。テキストと画像は互いに補い合う関係にあり、両方をバランス良く扱えるようにしたい、というのがマルチモーダルモデルを使用する主な動機となります。 あわせて、分類先のカテゴリも見直しています。従来のモデルは100クラス程度の分類モデルであったのに対し、今回は階層構造を持つカテゴリマスタのフルパス(例: グルメ・飲料/スイーツ・お菓子/ケーキ )を1つのクラスとみなした 498クラスのシングルラベル分類にしています。 使用したモデル マルチモーダルなモデルには様々な選択肢がありますが、今回は MMBT (MultiModal BiTransformer) を採用しました。 github.com アイデアはとてもシンプルで、画像を数個の「単語」のようなトークンに変換して、テキストトークンと一緒に同じ1本のTransformerに流し込むというものです。画像とテキストをそれぞれ別のエンコーダに通して最後にベクトルを結合するのではなく、最初から同じself-attentionの中で混ぜてしまうアプローチになります。 https://arxiv.org/abs/1909.02950 より引用 その際、attention内で個々のトークンがどのモーダルに対応するのかを、Token Type Embedding(セグメント埋め込み)によって区別しています。こちらは従来、テキスト文が質問と回答のどちらに該当するかなどのマーカーとして使用していたものですが、MMBTではそのトークンが属するモーダルによって値を変えたEmbeddingを位置埋め込みなどと同様に、それぞれのトークンに加算しています。 また、本実装では、論文のオリジナル実装から次の2点を置き換えています。 画像エンコーダ: ResNet → Swin Transformer ( swin_base_patch4_window7_224.ms_in22k ) テキストエンコーダ: 英語BERT → 日本語BERT ( tohoku-nlp/bert-base-japanese-v3 ) 実装は責務ごとに4つのクラスを積み上げる形になっています。以下、内側から順に見ていきます。 1. 画像を3つのトークンに変える ImageEncoder は、224×224 の画像を Swin Transformer に通して 7×7 の特徴マップにし、それを3つの領域に平均プーリングして3本の特徴ベクトル(各1024次元)に要約します。この3本が「画像トークンの素」になります。 class ImageEncoder (nn.Module): def __init__ (self, num_image_embeddings: int = 3 ): super ().__init__() model = timm.create_model( "swin_base_patch4_window7_224.ms_in22k" , pretrained= True , num_classes= 0 , ) # avg poolingと最後のlinear層を除外 modules = list (model.children())[:- 2 ] self.model = nn.Sequential(*modules) self.pool = nn.AdaptiveAvgPool2d((num_image_embeddings, 1 )) def forward (self, x): out = self.model(x) out = out.permute( 0 , 3 , 1 , 2 ).contiguous() out = self.pool(out) out = torch.flatten(out, start_dim= 2 ) return out.transpose( 1 , 2 ).contiguous() 画像を何トークンに要約するかはハイパーパラメータで、ここでは3としています。トークン数を増やすほど画像の情報は細かく残せますが、そのぶんテキストに使えるトークン列が短くなります。 2. 画像特徴を「BERTのトークン」に翻訳する BERTのテキストトークンの埋め込みは、実際には 単語埋め込み + 位置埋め込み + セグメント埋め込み の足し算でできています。画像トークンもまったく同じ作り方にすることで、BERTは画像をテキストトークンと同様に扱うことができます。 ImageBertEmbeddings では、 Swin の出力(1024次元)を nn.Linear(1024, 768) でBERTの隠れ次元に射影し 位置埋め込みはテキスト側と同じものを共有し 画像かテキストかを区別するセグメント埋め込み ( nn.Embedding(2, 768) )を新たに定義して加算する という処理を行います。BERTの実装によってはそのまま使える token_type_embeddings が無いケースがあるため、こちらで定義しています。 さらに先頭に [CLS] 、末尾に [SEP] の単語埋め込みを足すので、画像トークンの個数が3の場合、常に 固定長5トークン の並びになります。 [CLS] 画像1 画像2 画像3 [SEP] テキスト1 テキスト2 ... テキストN [PAD] ... |___________________________| |_________________________________________| 画像トークン(固定5個) テキストトークン(可変長) テキスト側は日本語BERTのtokenizerでトークン化し、最大長は 512 - 画像トークン数 に切り詰めます。 3. 1本のTransformerに流し込む MultimodalBertEncoder は、画像トークン列とテキストトークン列を横に連結して1本のシーケンスにし、BERTのエンコーダに通します。 このとき attention mask は、 画像部分は常に1 (必ず全部見る)、テキスト部分は実トークンだけ1・パディングは0、という形で作っています。 attention_mask = torch.cat( [torch.ones(bsz, self.num_image_embeds + 2 ).long(), attention_mask], dim= 1 , ) extended_attention_mask = attention_mask.unsqueeze( 1 ).unsqueeze( 2 ) extended_attention_mask = extended_attention_mask.to(dtype= next (self.parameters()).dtype) # 1の部分は0に、0の部分は-10000.0に変換する extended_attention_mask = ( 1.0 - extended_attention_mask) * - 10000.0 あとは通常のBERTと同じで、全トークンが互いに attention を張り合い、最後に pooler が全体を768次元のベクトルに要約します。 4. 分類と埋め込みの取り出し 最後の MultimodalBertClf は、pooler の出力(768次元)を nn.Linear(768, クラス数) に通してカテゴリのスコアを出すだけの薄いクラスです。 class MultimodalBertClf (nn.Module): def __init__ (self, n_classes, model, vocab, hidden_size: int = 768 ): super ().__init__() self.enc = MultimodalBertEncoder(model=model, vocab=vocab) self.clf = nn.Linear(hidden_size, n_classes) def forward (self, txt, mask, segment, img): return self.clf(self.enc(txt, mask, segment, img)) def embeddings (self, txt, mask, segment, img): # 分類前の特徴量を返す return self.enc(txt, mask, segment, img) この768次元ベクトルは「商品テキストと商品画像の両方を要約したベクトル」なので、カテゴリ分類以外の下流タスクにも転用できます。推論時にはカテゴリと一緒にこの埋め込みも保存しています。 結果 学習はGeForce RTX 5090を積んだオンプレサーバーで数日程度行い、検証データ全体でのaccuracyは90%でした。推論バッチの作成後、新規登録された商品を対象に定性的な検証も実施したところ、498クラスのうち比較的少数のクラスに関しても、ある程度正確に推論できていました。 また、従来のテキストのみによる分類では判別が難しかった商品についても、適切に分類できていることが確認できました。 埋め込みベクトルの活用 先述の通り、このモデルからはカテゴリだけでなく、分類器手前の768次元の埋め込みベクトルも取り出せます。これは「テキストと画像の両方を踏まえた商品の表現」なので、カテゴリという498個の枠に丸める前の、より細かい情報を持っています。 手元で近傍探索を試すと、同じカテゴリの中でも見た目や商品の雰囲気が近いものが上位に並ぶことが確認できました。この埋め込みは、商品の推薦や検索、あるいは他の機械学習モデルの特徴量としての活用を想定しています。 おわりに 今回は、テキストと画像の双方を扱うマルチモーダルモデルによる商品カテゴリ分類の取り組みを紹介しました。 今回利用したMMBTは、それぞれのモーダルのエンコーダをある程度自由に選択できる点、クラス分類に特化しており実装がシンプルな点が魅力です。また、少し工夫を加えれば複数の画像への対応や画像以外のモーダルの利用もできそうであり、今後も活用の幅を広げられたらと思っています。 最後に、BASEでは様々な職種で一緒にプロダクトを作り上げていくメンバーを募集しています。 興味のある方は、ぜひお気軽に採用情報をご確認ください! binc.jp 明日はoliverさんによる「顧客中心主義を開発の意思決定に組み込むために実践したこと」に関する記事です!
はじめに こんにちは、バックエンドエンジニアのかがの( @ykagano )です。 2026/8/27(木)に「PHP Tech Talk Night ~ after phpcon 2026 ~」を合同開催しました。 本記事では、当日の登壇内容や会場の様子についてお届けします! イベント概要 「PHPカンファレンスの熱量を次につなげる」をテーマに、株式会社viviONさま・株式会社kubellさま・ピクシブ株式会社さま・BASE株式会社の4社合同で開催し、会場は株式会社viviONさまのオフィスをお借りしました。 vivion.connpass.com BASEからの登壇 人気商品が「ちゃんと買える」をつくる — ECの負荷改善(かがの) 今回の発表では、2026年1月からチームで負荷改善に取り組んだお話をさせていただきました。 3人のチームでしたが、まず負荷テスト環境の構築に全員で分担しながら取り組みました。 そして実際の負荷改善では、ログの計測を行い、分析して仮説を立案し、対策を行った上で、負荷テスト環境での検証を行います。 この一連の流れについてイメージをお伝えできていたら嬉しいです。 speakerdeck.com 参加したメンバーのコメント meihei( @meihei ) PHPカンファレンスの開催中にイベントの存在を教えてもらい、熱量そのままに参加させてもらいました。20分のトークが盛りだくさんで、どれも面白く、学びの多いものばかりでした。 特に、PHPカンファレンスで聞いた viviON さんのレガシーコードと向き合う話( 30年価値を出し続けているPHPプロダクト ── レガシーと向き合い成⻑する戦略 )が面白かったこともあり、同じ viviON さん(会場提供ありがとうございます)の竹下さんのお話も楽しく聞けました。片やレガシーと向き合いながらプロダクトを成長させる戦略の話、片やボトムアップで良い取り組みを取り入れていく話。目指すゴールが同じところにあるのが良かったです。 現地で見たセッションの感想 どのセッションも学びが多かったのですが、ここではゲスト登壇いただいたセッションを、現地で X に投稿した感想とあわせて紹介します。 ことみんの登壇資料の作り方〜登壇はいいぞ〜 / @kotomin_m speakerdeck.com スライドのデザイン作りすごい 😯 #php_night pic.twitter.com/VLGy7YBQ73 — ykagano (@ykagano) 2026年8月27日 PHPプロジェクトの結合バランスを可視化する / @kajitack speakerdeck.com 強度と距離と変動性によって改善したほうがいいか分かるんだ! #php_night pic.twitter.com/D27jAcJOcv — ykagano (@ykagano) 2026年8月27日 「楽にすること」と「楽しむこと」は違う / @soudai1025 soudai.hatenablog.com 楽したい 🫠 #php_night pic.twitter.com/oPPsjPURDN — ykagano (@ykagano) 2026年8月27日 会場・懇親会の様子 開始前の会場の様子です。とてもおしゃれなイベントスペースで、こちらの写真の右奥には仮眠スペースがありました。また後ろの方にはファミレス席やバーカウンターがありました。 各社のノベルティ置き場も用意されていました。 懇親会では軽食とドリンクを囲んで、参加者同士で交流を深めることができました。 おわりに 「PHPカンファレンスの熱量を次につなげる」というテーマのとおり、どの発表も熱量のある内容でとても楽しかったです。合同開催いただいた株式会社viviONさま、株式会社kubellさま、ピクシブ株式会社さま、そしてご参加いただいた皆さまありがとうございました! BASEではエンジニアを募集しております。よろしければ、採用情報もぜひご覧いただけますと幸いです。 binc.jp
はじめに この記事は、BASEテックブログ夏のブログリレー9日目の記事です。 こんにちは、BASE株式会社で PHPer をしている @meihei です。 フィーチャートグルはご存知で既に使っている、という方も多いと思います。BASEでもフィーチャートグルの仕組みを利用しています。 一方で、私が進めていたプロジェクトでは、既存の仕組みだけでは少し扱いづらい場面がありました。そこで、用途に応じて必要最低限のフィーチャートグルを追加していきました。 この記事では、フィーチャートグルを具体的にもっとEasyに取り入れていった事例を紹介します。 背景 フィーチャートグルとは フィーチャートグルは、コードを変更・デプロイすることなくシステムの振る舞いを切り替える手法です。新しい処理をコードとしては本番に入れておきつつ、実際に有効化するタイミングは別で制御できるので、「デプロイ」と「リリース」を分離できます。 このフィーチャートグル自体の話は多くの場所で語られているものなので、この記事では深入りしません。 この記事で扱うのはもっと素朴なものです。「特定のリクエストを旧経路と新経路のどちらに通すかを判断できる」ことさえできれば、フィーチャートグルとして十分に機能します。 PHPで書くとこれだけです。 if ($featureEnabled) { // 新しい処理 } else { // 今までの処理 } 「今までの処理」はリリースが終わったら消すので、消す対象をコメントでマークしておくと、後から機械的に探せて便利です。 // @delete start feature-a if (!$featureEnabled) { // 今までの処理 return; } // @delete end feature-a // 新しい処理 リリースが済んだら @delete start feature-a 〜 @delete end feature-a のブロックをまるごと消して、新しい処理だけを残します。 問題は「トグルの値」をどう決めるか BASE にある既存のフィーチャートグルは、ショップ単位 (特定のショップだけ、または全ショップ) の有効・無効の設定からトグルの値を決めています。 管理画面から操作でき、まず社内のテスト用ショップや一部のショップで有効にして様子を見て、問題なければ全ショップに広げる、という段階的なリリースに使われています。 一方で、リリースの手前には「本番で自分のリクエストだけ新経路を試したい」「ショップページ以外のページでも検証したい」といった、ショップ単位とは別の切り替えが欲しくなる場面があります。 もしちゃんと作るなら、既存の仕組みを拡張するか、OpenFeature のようなライブラリを入れて、汎用的な仕組みから入るところです。 今回はそうせず、Easyに、特定ケースにしか効かない汎用性のない最小のものを作りましょう、というお話です。 事例1:Query Parameter で特定リクエストだけ切り替える 対象: 社内向け管理画面 値の出どころ: クエリパラメータ 既存の仕組みで足りない理由: 社内向け管理画面にはショップという単位がない この画面にアクセスできるのは社内の人だけです。 公開前の機能が外に見える心配はなく、URL が利用者の履歴に残っても問題ありません。 なるべく簡単に切り替えられることだけを考えればよいので、クエリパラメータで判断することにしました。 function isFeatureAEnabled($request): bool { return ($request- > getQueryParams()['feature_a'] ?? '') === '1'; } URL の末尾に ?feature_a=1 を付けたリクエストだけが新経路を通り、それ以外は今まで通りです。DB もデプロイも要らず、状態も持ちません。 「このリンクを開いてください」と URL を渡すだけで、開発者以外の人にも確認を依頼できます。 画像は、チーム共有会で機能をオンにした状態にアクセスできるよう共有したときのものです。 クエリパラメータなので、ボタンで切り替えられるようにしました。 事例2:HTTP Header で特定の訪問者だけ切り替える 対象: ショップページと BASE API 値の出どころ: カスタムヘッダー 既存の仕組みで足りない理由: ショップ単位でしか切り替えられず、訪問者単位でオンオフできない ショップページと BASE API は、ショップの訪問者や外部のアプリが使う、社外に公開された経路です。 既存のショップ単位のトグルで切り替えると、そのショップを訪れる全員が一斉に新経路になります。しかし今回のユースケースでは、リリース前の確認として「特定のリクエストだけ」を新経路 (または旧経路) に通したいので、ショップ単位とは別に訪問者単位の切り替えが必要でした。 事例1で紹介したクエリパラメータの方式では、不本意に URL に乗ってしまうリスクがあり、クエリパラメータを受け取らない API へのリクエストでは使えません。そこで、クエリパラメータを使わずカスタムヘッダーで判断することにしました。 function isFeatureAEnabled($request): bool { return $request- > getHeaderLine('X-Feature-A') === '1'; } ショップページなどの多くの場所で判定が必要な場合は、Middleware を使うと便利です。 final class FeatureA { private static bool $enabled = false; public static function enable(): void { self::$enabled = true; } public static function isEnabled(): bool { return self::$enabled; } } class FeatureAMiddleware { public function process($request, $handler) { if (isFeatureAEnabled($request)) { FeatureA::enable(); } return $handler- > handle($request); } } Middleware がリクエストの冒頭でヘッダーを判定して FeatureA をオンにするので、新旧の分岐を書く場所では、リクエストを引き回さずに FeatureA::isEnabled() で判定できます。 QAなどで動作確認する人は、ヘッダーを書き換えるブラウザ拡張機能を使ったりして新経路に入ります。他の訪問者は今まで通りです。 事例3:Cookie + IP Address で社内アクセスだけ切り替える 対象: ショップページ 値の出どころ: Cookie と IP アドレス 既存の仕組みで足りない理由: 事例2と同様の要件だが、普段のブラウザ操作だけで使えて、時間が経てば自然に元へ戻る形で新経路に切り替えたい 社内の人に新経路を見てもらう機会が急遽発生し、突貫で用意しました。 見てもらう相手はヘッダーの書き換えに慣れた人ばかりではないので、事例2の方式は使えません。 そこで、ブラウザに Cookie を持たせて、普段の操作のまま新経路を使い続けられるようにしました。 Cookie には有効期限があるので、期限が切れれば自然に元の状態へ戻り、戻し忘れの心配もありません。 ただし Cookie だけでは外部の人も同じことができてしまうので、社内 (オフィスや VPN) の IP アドレスからのアクセスであることも条件に加えました。 function isFeatureAEnabled($request): bool { // 社内 IP かどうかの判定。中身はプロキシや LB の構成に依存するので省略 return isInternalAccess($request) && ($request- > getCookieParams()['feature_a'] ?? '') === '1'; } Cookie は、ブラウザ操作だけで「feature-a を有効にする」ことができる形でセットします。 このアプローチは、私が過去に書いた記事と同じものです。 developers.prtimes.com 事例4:Shop ID を使ってカナリアリリースする 対象: ショップページ、ショップ向けの管理画面 値の出どころ: ショップと1対1に対応する整数IDの剰余と、割合の設定値 (canary) 既存の仕組みで足りない理由: ショップ単位のトグルは対象を手で選ぶ必要があり、「全体のn%」という広げ方ができない 最初は1%、問題がなければ10%、50%、100%と段階的に上げていく、いわゆるカナリアリリースもEasyに実装できます。 要件は「既存のトグルと同じショップ単位のまま、全ショップのうちn%だけを新経路に切り替えたい」というものです。 そこで、ショップと1対1に対応する整数のIDを100で割った余りを、canary として設定した割合と比較します。 function isFeatureAEnabled(int $numericShopId): bool { // $numericShopId はショップと1対1に対応する整数のID $canary = (int) Config::get('feature_a_canary'); return $numericShopId % 100 < $canary; } 判定はショップごとに決まっているので、canary を上げれば対象が広がっていきます。 割合の変更にはデプロイが必要であり、厳密には「デプロイ」と「リリース」を分離できていませんが、触るのは Config の値だけで、戻すときも値を戻すだけです。今回はこれで十分と割り切りました。 おわりに この記事では、クエリパラメータやヘッダー、Cookie と IP アドレス、Shop ID といった手元にあるものだけで、トグルの値をEasyに決めていった事例を紹介しました。 最終的には、カナリアリリースやカスタムヘッダーなど複数の判定を1つの関数にまとめ、組み合わせて使っています。 リリースが終わったら、判定関数ごと @delete マークの旧経路と一緒に消します。 この記事で紹介したフィーチャートグルにはビジネスロジックが含まれず、決められた場所で決められた用途でしか使われません。 だからこそ、なるべく質素に、単純に、Easyに書く方が良いと思っています。 フィーチャートグルというと専用の基盤を思い浮かべがちですが、もっとEasyに始めることができます。ぜひ参考にしてください! またこうしたプロダクト開発で使える、簡単だけど強力なTipsに興味のある方は、ぜひ採用情報もご覧ください! binc.jp 明日は、Takeuchiさんの記事です。お楽しみに!
はじめに この記事は、BASEテックブログ夏のブログリレー8日目の記事です。 こんにちは! wakana です。 私はBASE株式会社でバックエンドエンジニアをしながらBASE事業部内のサービスレベル活動推進をしています。 BASEのサービスレベル活動では、「SLOを全社の共通言語にする」ことを目標に掲げています。その取り組みの一つとして、今年から 「主要SLO」の運用を始めました。 運用開始から半年が経ち、主要な機能のサービス品質を広く可視化し、事業部全体で品質を振り返る文化も定着しつつあります。一方、実際に運用したことで、次に取り組むべき課題も具体的に見えてきました。 この記事では、主要SLOの展開によって得られたことと、現在力を入れている点、さらには今後の展望についてご紹介します。 前提:主要SLOは、SLOを全社で共有するための指標 主要SLOは、エンジニアが運用するSLOを一段抽象化した指標です。エンジニア以外の人にもサービスの状態を捉えてもらいやすくすることを狙って設計しました。 主要SLOを作るまでの経緯や、そこに込めた意図については、過去の記事で紹介しています。あわせてご覧ください。 devblog.thebase.in devblog.thebase.in まずは「サービスの品質を可視化できる」ということを共有した 主要SLOを展開するにあたり、私たちは一つひとつのSLOを細部まで作り込むことよりも、まずは重要な機能を広くカバーすることを優先しました。 サービスレベル活動の最初の段階で目指していたのは、「サービス品質は数値として捉えられる」という考え方を事業部全体に広げることでした。 そのためには、一部の機能だけを精緻に計測するよりも、各担当領域の状態を主要SLOから確認できることが大切だと考えたためです。 この点については、着実に手応えを得られています。 主要SLOの状態を「月次サマリー」として毎月共有する運用も始まり、サービス品質を定期的に数値で確認するための土台ができてきました。 未達のSLOがあった場合にも、それに対する対応状況と同時に共有し、ただ計測しているだけではなく改善活動にまで繋げていることも、事業部全体で確認できるようになっています。 月次サマリーの一部イメージ。主要SLOの状態を事業部全体で定期的に確認できるようにしています。 次の課題:SLI/SLOの見直しが想定通りに進まなかった 網羅性を優先した展開と並行して進めたかったのが、SLIの計測方法とSLOそのものの継続的な見直しです。 個々のSLI/SLO については、まず計測を始め、運用しながら精度を高めていく方針でした。しかし、半年間の運用を振り返ると、見直しは想定していたペースでは進んでいませんでした。 見直しが必要だという認識はあったものの、それを継続的に行うための仕組みを、展開時の設計に十分組み込めていなかったためです。 最初に設定したSLIの計測方法は、その時点で妥当だと考えたものです。ただし、 実際のサービスの状態を捉えられているかは、運用を通じて継続的に確かめる必要があります。 良いレスポンス・悪いレスポンスの定義は適切か この計測方法は実態に合っているか 対象に含めるもの、含めないものは適切か etc... といった点を確認し、必要に応じて更新していく。そこまで含めて設計することで、SLIを信頼できる状態に保つことができます。 見直しの手順や、状態を定期的に確認する場が定まっていなかったことで、「継続的に見直していくものである」という意識付けが弱かったように感じます。 それにより、SLOが未達になった際に、 サービス品質の変化によるものなのか、計測方法を見直すべきなのか、 その切り分けに時間がかかってしまうこともありました。 この切り分けをしやすくするには、判断の出発点となるSLIを信頼できる状態にする必要があります。 SLI、SLO、エラーバジェットの関係は、書籍 SLO サービスレベル目標 | Alex Hidalgo 著 で「信頼性スタック」として整理されています。 この関係を踏まえると、SLIを優先して見直す理由が分かります。 信頼性スタックにおけるSLIの位置づけ SLIは、SLOとエラーバジェットを支える土台です。 まず品質の善し悪しを定義する SLIでサービスの状態を捉える それをもとにSLOの達成状況を判断する その結果をエラーバジェットの運用につなげる これらの要素は下から積み上がる関係にあるため、SLIの計測が実態とずれていると、その上にある判断の根拠も弱くなってしまいます。 この関係を踏まえ、私たちはまず、判断の出発点であるSLIの計測方法を見直すことにしました。 SLIの信頼性を高めるために取り組んだこと 週次会で担当者と一緒に見直す 当初は、各SLOの担当者が見直しを進めるための手順や、迷ったときに一緒に考える場を十分に用意できていませんでした。 そこで、サービスレベル活動の週次会で担当者と計測結果を一緒に確認し、数値と実態がずれる原因を調べました。 また、SLIの見直しを行なっていきたいことを改めて各担当者と共有し、対応優先度の相談などから一緒に考えました。 こうした取り組みにより、現在は各SLOの担当者全員がSLIの見直しを実施でき、見直しを完了したSLIも順調に増えています。 担当者が自ら見直しを進めた指標もあり、SLIの精度を高める動きが少しずつ広がってきました。 そして見直したSLIについては、「なぜこの計測方法でサービスの状態を捉えられるのか」を、以前より明確に説明できるようになりました。 実際に、計測対象から外れていた処理を見つけ、サービスの実態に合うよう計測方法を変更したSLIもあります。 ちょうど当記事を執筆中にも見直しが完了した報告をもらいました。ありがたい🙌 自分のチームで説明会を開く また、私自身もSLOの担当者の一人であるため、所属チームでサービスレベル活動について説明する会を開きました。 SLI, SLOは一人で使うものではありません。サービスを提供する全員で品質の定義を決定し、SLOの達成状況やエラーバジェットを見てリソース投資の判断など、次の行動につなげていく必要があります。 そのため、これまでの活動で得た知見を共有し、チームのメンバーがサービスレベル活動に関わりやすい状態を目指しました。 発表の後、チームメンバーから多くのコメント・感想をいただくことができました。ありがとうございます! 説明会後は、可観測性を向上させる施策のチームレビューがスムーズに進み、リリースまでつなげることができました。 これから:SLOと事業指標を共通の判断材料にする SLIの信頼性を高めた先では、SLOと事業指標を結びつけた運用へ進みたいと考えています。 BASEではすでに、SLOと流通総額(GMV)を並べて確認できる社内ダッシュボードを展開しています。 この基盤を活かし、SLOと事業指標を関連づけながら、プロダクトの状態をより具体的に捉えられる運用を目指しています。 エンジニアは事業指標まで視野を広げ、SLOは職種を問わず共通の判断材料として使う。 同じ情報を見ながら、全員でプロダクトに向き合える運用を作る。 その判断を支えるSLIについても、各SLOの担当者による見直しのサイクルが回り始めています。 現在は、この見直しを一度きりで終わらせず、継続的な運用として組織に定着させている段階です。 おわりに 主要SLOをBASE事業部に展開してから半年間の取り組みと、現在進めていることをご紹介しました。 網羅性を優先した最初のフェーズでは、主要な機能を広くカバーし、サービス品質を定期的に数値で確認するための土台を作ることができました。 現在は、各SLOの担当者がSLIを見直すサイクルが回り始め、継続的な運用として組織に定着しつつあります。 この運用を続け、SLOと事業指標を結びつけた判断につなげていきます。 BASEでは、SLOをはじめとしたサービス品質への取り組みを一緒に進めていく仲間を募集しています。興味のある方は、ぜひお気軽に採用情報をご覧ください。 binc.jp 明日は、emaさんの記事です。お楽しみに!
はじめに この記事は、BASE夏のブログリレー7日目の記事です。 BASE Dept Order SectionでエンジニアをしているCapi(かぴ)です。 本記事では有志でチームを組んで進めているBASEのWebアプリケーションセキュリティ施策の一環で脅威モデリングを行ったのでそのご紹介です。脅威モデリングに興味のある方の参考になれば嬉しいです。 最初にこの記事で伝えたいこと3つを事前に共有しておきます。 目的をもって脅威モデリングを導入する 小さく始めて継続することを意識する 脅威モデリングに時間はかかったが、得るものは多かった 脅威モデリング実施の背景 過去のインシデント履歴 BASEでも過去に決済周りのインシデントが発生しています。決済のインシデントはユーザーにも会社にも影響が大きく、恒久対応のプロジェクトが立ち上がったこともありました。ただ、いずれも「発生してから気づく」受け身の対応でした。そこで、自分たちで決済周りを事前に検査し、原因を特定し、修正まで行える能動的な体制を作りたいと考えました。 OWASP SAMMのスコア改善 以前OWASP SAMMというフレームワークを使い、ソフトウェア開発ライフサイクル全体におけるセキュリティ対策成熟度を数値化しました。その結果として、"セキュリティ基準の明文化"や"アプリケーションの継続的なセキュリティ評価実施"の項目のスコア改善を目指すことにしました。 今後も追加開発が行われシステムが複雑化していくことを予想し、早めの対策を打ちたいと考えました。 脅威モデリング実施前の準備 先行事例調査 脅威モデリングをどう行い、どう活かしていくのかの具体事例が知りたかったためまずは先行事例を調査しました。主に企業のテックブログを参考にさせていただきました。 脅威モデリングを始めてみました - セキュリティリスク分析の第一歩 Luup社 メルカリの脅威モデリングプロセス mercari社 事例を調査する中で気づいたのは「想像以上に日本語の事例がインターネットで公開されていないこと」です。脅威モデリングに関する情報はたくさん公開されているのですが、具体的な事例紹介は少なかったです。 手法を学ぶ STRIDE、Attack Treeを学びました。 STRIDEは脅威を6種類に分類して網羅的に洗い出す手法、Attack Treeは特定の攻撃目標に至る経路や条件をツリー状に分解する手法です。 最終的にはじめての脅威モデリングだったので多くに手を出さず、情報が手に入りやすい(インターネットでの情報が多い)ものを採用したいという理由で STRIDE を使うことに決めました。 STRIDEに関してはMicrosoft社をはじめ大手のサイバーセキュリティ企業が公開している資料がありました。 Microsoft Threat Modeling Tool の脅威 また、Attack Treeは今後導入していきたいものとして概要のみ押さえました。 Monthly Research 「脅威分析の役割と手法の紹介」 脅威モデリングに対して共通認識を持つ 前提として最初に「重要箇所から小さくはじめて少しずつ範囲を広げていく」をチームで合意しました。当時のドキュメントにある意思決定内容にも「小さくはじめる」、「できる範囲から実施し、継続する」というのを残しました。 当時の社内意思決定ドキュメントより引用 最初から完璧を目指さない dfdを完璧にしないと脅威モデリングができないわけではありません。わかる範囲で実施し、継続することが大事です。 引用文に出てくるdfdは Data Flow Diagram(データフロー図) のことで、システムにおけるデータの流れを表した図のことです。データの流れからシステムの機能を洗い出します。 脅威モデリングの設計 セキュリティインシデントが発生した場合にユーザー影響、会社への影響が大きい箇所から優先的に脅威モデリングを行いたいと考え、まず下記2シナリオを選びました。 カート機能のクレジットカード決済 カート機能で購入者アカウントにログインした状態でのクレジットカード決済 BASEにはいろんな決済手段、決済時のオプションがありますが今回は一番オーソドックスなものを選びました。「まずはシンプルな決済手段から」というのをチームで合意を取っていたためです。 また、洗い出す脅威を絞りました。今回はSTRIDEを選んだため最大6種類の脅威が存在します。今回はその中からS、T、Iに絞りました。チームでクレジットカード決済でよく発生しそうなものは何かを話し合い決めました。また、多くの脅威を洗い出そうとすることで脅威モデリングの時間が伸びてしまうことを防ぐためです。 略字 脅威 日本語 脅威の内容 侵害されるセキュリティ特性 今回の対象 S Spoofing なりすまし 他人や他システムになりすまして正規の利用者・サービスとして振る舞う 真正性(Authentication) ○ T Tampering 改ざん データや通信、コードを不正に書き換える 完全性(Integrity) ○ R Repudiation 否認 行った操作や取引を後から「やっていない」と否定できてしまう 否認防止(Non-repudiation) 次回 I Information Disclosure 情報漏洩 権限のない相手に情報が渡ってしまう 機密性(Confidentiality) ○ D Denial of Service サービス拒否 サービスを利用できない状態に追い込む 可用性(Availability) 次回 E Elevation of Privilege 権限昇格 本来持たない権限を取得して操作を行う 認可(Authorization) 次回 DFD作成 今回は脅威モデリングの対象範囲にしたカート機能のクレジットカード決済処理の範囲のみDFDを作成しました。 DFD作成が脅威モデリング準備で一番時間がかかりました。カート機能がBASEのコア機能でありたくさんのシステムコンポーネントが関わっているため図解するための現状理解が難しかったためです。 DFD作成では生成AI(Claude Code)を活用し、時間短縮を目指しました。 カート機能をClaude Codeに読み込ませDFDをMermaidで出力 MermaidをFigJamにインポート Mermaid Live Preview でもMermaidを表示し、見比べながらFigJam側の図を整える FigJamのインポートには Mermaid to FigJam というプラグインを使いました。 FigJamにインポートしたのは脅威モデリング中、付箋を貼るだけで脅威を残せる形にしたかったためです。 脅威モデリング実施 進め方 アプリケーションエンジニア2人で時間を合わせ、オンラインで行いました。 シナリオごとにDFDを追う シナリオを追いながら気になったところに付箋を貼る 貼った付箋に対して議論する、深掘って追加の付箋を貼る ※進め方の補足 付箋はSpoofing(なりすまし)を緑、Tampering(改ざん)を赤、Information Disclosure(情報漏洩)をオレンジにして貼りました DFDをAIに出力してもらった関係上、実際の動きと異なる部分がいくつか見つかりました。DFDの不安な箇所は既存のUMLを参照したり実際にコードを読みました。 脅威モデリングで出てきた脅威 詳細をご共有することはできないため割愛しますが、SとTとIどれも同じくらい見つかりました。 下の表にそれぞれいくつ付箋が貼られたのかを数えてまとめています。 脅威 数 Spoofing(なりすまし) 8 Tampering(改ざん) 9 Information Disclosure(情報漏洩) 7 システム間の接続部分に付箋が集中しました。 新しい決済手段を実装するときに意識すること、守るべきことを明文化していく素材を手に入れられました。 わかったこと 脅威モデリングの難易度 取り組み当初は「1シナリオ1時間あれば十分」と見積もっていました。しかし、実際に取り組むと、DFD作成に2時間半、脅威の洗い出しに4時間、合計6時間半かかりました。時間を食うのはシナリオ選定ではなく、図解と脅威の洗い出しでした。 はじめてで慣れていなかったとはいえ他の業務と兼務してやり切るのは難易度が高いことがわかりました。 シナリオ2つを選んだのも原因です。今後は余裕を持って1つずつ確実に進めます。 自分たちのシステム認識と実態のズレ 脅威モデリングを行う前、カート機能に関わるのはアプリケーション(フロントエンド)、アプリケーション(バックエンド)、外部サービス1つ(決済代行サービス)、DBくらいだと想定していました。 しかし、DFDを作って脅威モデリングを進めるうちに、想定外のものが次々と出てきました。 把握しきれていなかった内部システムとの接続 1つ 把握しきれていなかった外部システムとの接続 1つ アプリケーションが参照しているデータソース 2つ 想定していたコンポーネント数と同じだけ、知らないものがありました。 画面だけ見ると単純な決済処理でも、内部では多くのコンポーネントと通信が関わっています。脅威モデリングそのものも大事ですが、DFDを作成し正しく整理していくだけでも学びは多かったです。 継続するために行ったこと。今後やること。 DFD、脅威モデリング結果の記録 今回のDFD、脅威モデリング結果は社内の誰もが閲覧できるFigJamに残せました。 また、DFDの作り方や脅威の分類も図解することで今回脅威モデリングを行ったメンバー以外の人が脅威モデリングを始める土台を準備できました。 次の脅威モデリング対象を決める BASEのカートには他にもたくさん機能があります。今回の脅威モデリングや直近の開発プロジェクトを考慮し、今後下記シナリオの脅威モデリングをやりたいと考えています。 使用率の高いクレカ以外の外部決済手段 かんたん海外販売のカート機能 セキュリティ要件の作成 現在、脅威モデリングを元にセキュリティ要件を作成しています。今後、カートの追加開発をする時に「ここだけは守ってほしい」ことを明文化していきます。 セキュリティツール導入のPoC 脅威モデリングで出した脅威に対し、自分たちでテストを行い、テスト結果をレポートにして早期防止に繋げる仕組みを作りたいと考えています。 その仕組みを作るためにDASTツール、IASTツールの導入PoCを進めています。 おわりに 今回は脅威モデリング未経験者が調査から実践までやってみた経験、学びを共有させていただきました。 BASEでは機能開発はもちろん非機能要件について考える課題があり、その課題に挑戦する機会もあります。ご興味あればぜひ採用情報をご覧ください。 binc.jp 明日はwakanaさんによる社内のサービスレベル活動推進に関する記事です! 参考資料 OWASP SAMM OWASP Threat Modeling Process 脅威モデリングを始めてみました - セキュリティリスク分析の第一歩 メルカリの脅威モデリングプロセス 脅威モデリングをソリューション化させるまでの歩み Newton Consulting「STRIDEモデル」 Microsoft Threat Modeling Tool の脅威 脅威モデリングとは|実施手順と成功のためのポイントを解説 Monthly Research 「脅威分析の役割と手法の紹介」 DFD
この記事は、 BASE夏のブログリレー 6日目の記事です。 こんにちは、テックブログ編集委員の松原( @simezi9 )です。 BASE Tech Blog は、はてなブログで運用しています。 昨今ではブログを企業が運営すること自体の価値を改めて問われているようにも思いますが、BASEという会社の活動を世の中に発信していきたいという思いで引き続き活動を続けています。 そんなBASEのテックブログも、運用開始から10年が過ぎました。 その間、編集部員も入れ替わり運営のルールも様々に形を変えてきたわけですが、 ここにきて記事の執筆の仕組みに大きくテコ入れをして、AI時代準拠にアップデートをしました。 本記事ではどういう改善を行ったかを解説していきます。 従来の入稿の仕組み これまで記事を入稿する際には主にNotionを中心に据えたワークフローを利用していました。 具体的にはNotionのデータベースに記事を執筆し、編集部員がレビューして記事にコメントを残し、レビューが完了したら記事を執筆者がはてなブログに転載して公開するというシンプルなワークフローでした。 (執筆者ははてなブログ側に編集者としてはてなIDを登録する形) このワークフロー自体はそれなりに完成もされていました。 とはいえ細かい不便は色々とあり、具体的には以下のようなものです。 レビューの指摘が煩わしい :文章の修正案をそのまま渡す手段がなく、「ここをこう直してほしい」を逐一説明することになる 実際に記事が表示された状態でのプレビューが面倒 :実際のはてなブログ上でどのように表示されるかは、いちいちはてなブログ側に反映させた上でプレビューする必要があるため、面倒になって後回しにされがち 入稿が手作業 :公開のたびに本文を移す。画像ははてなフォトライフに上げ直して挿入し直さなければならない 執筆者ごとにアカウントがいる :はてなブログに執筆者を登録する必要があり、入社と退職に合わせてその管理もしなければならない Notion記法とはてな記法に互換性がない :段落や改行の揺れ、画像の挿入など、いちいち目視で確認して調整しなおさなくてはいけない はてなブログの管理画面でアップロードした画像が、執筆者のはてなフォトライフアカウントに紐づいてしまう :記事に挿入している画像は執筆者のはてなフォトライフアカウントになるため、退職後など不意のタイミングで画像が消えてしまったり、管理できない可能性がある はてなブログとNotion側で同期が取れている保証がない :公開直前直後ではてなブログ側のみ編集してしまった場合に内容がズレてしまい、同期する手段がなかった これらの問題が深刻になることはあまりなく一個一個は小さい不便ですし、高頻度に記事を入稿するような環境でもなかったためこれらは妥協の範囲で運営によってカバーされてきました。 仕組みを根本的に改善するには労力がいりますし、テックブログのためにそれだけの力をさける人は多くない、というのは多くのテックブログの悩みとしてあるのではないでしょうか。 HatenaBlog Workflowsの導入とその後の変化 そんな中で新しいワークフローの中心として注目したのが、 HatenaBlog Workflows です。 これははてなブログの基本的な操作をまとめて、GitHubからブログ管理を可能にするためのライブラリです。 このライブラリ自体は 2023年にBoilerplate経由で導入されていて 記事の入稿もGitHubから可能だったのですが、予約投稿に対応していなかったり細かく対応ができない部分がいくつかのこっていて、 かつ社内向けのマニュアルなどもあまり整備されていなかったため一時的に利用されてはいたものの、その後は普及していませんでした。 (少しわかりにくいのですが、基底となるユーティリティとしてHatenaBlog Workflowsがあり、そのサンプル実装として HatenaBlog Workflows Boilerplate(β) があり、BASEでは後者をカスタムして利用しています) このフローを利用した入稿が普及しなかったこと自体は仕方のない話で、ライブラリ自体は便利であったものの、ほとんどの執筆者にとってたまにしか執筆しない会社のブログシステムでGitHub Actionsを利用して記事を書く・投稿する、というのはいくら説明を受けても心理的な障壁が高いでしょうし、 扱いに慣れているNotionで手作業で執筆を進められるなら、そっちで済ませてしまうだろうと思います。 そうして時代は流れていったのですが、昨今のAI時代、BASE社内でも誰もが当たり前のようにClaude CodeやCodexを扱う中で、 HatenaBlog WorkflowsをAIで扱いやすくするだけでブログの執筆がGitHub上で完結するワークフローを簡単に整備できるのではないか?と考えました。 エージェントが手順を引き受けてくれれば、これまでGitHubベースでの執筆が普及しなかった理由を解消できるはずです。 さらにタイミングのいいことに、HatenaBlog Workflowsが内部で利用している blogsyncが4月にはてなブログの予約投稿に対応していた ため、 その更新を取り込んでもらうことができれば、「予約投稿できない」というGitHub管理での最大の課題も公式のワークフローで解消できそうだと判明したのも大きかったです。 (実際に blogsyncのバージョンを上げてほしいという要望をIssueで出したところ 、3日で対応版がリリースされました。 おかげでいまは、front matterに Scheduled: true と公開したい日時を書いてpushするだけで予約投稿が登録されるようになり、管理画面に触る必要はなくなりました。) 新しい入稿の流れ そして実際にリポジトリにエージェントのためのSkillやハーネスを用意して、以下のような執筆ワークフローを整備しました。 ハーネスの方にはHatenaBlog Workflowsに関する知識と、はてな記法に関する知識をリファレンスとして渡しています。 また、その過程で必要になるGitHub Actionsの生成とドキュメント整備を担ってもらいました。 執筆者はブログのリポジトリを用意して、Claude Codeを起動する 「記事を書きたい」と伝える エージェントにより、投稿に対する基本的な情報(タイトルなど)を聞かれた後、HatenaBlog Workflowsを利用してGitHub側に記事のPRが作成され、執筆・プレビューが可能な状態となる 以降はGitHubに原稿の更新をpushするたびにプレビューが自動で更新される 執筆する 画像の挿入をする場合にはPRに挿入予定の画像をコメントするとGitHub Actions経由で自動的にはてなフォトライフにアップロードされ、はてな記法でのコメントが返ってくる 記事が完成したらGitHub上でPRのレビューを受ける Approveされたら、エージェントに時間を指定して公開依頼を伝えれば予約投稿の設定が完了する という流れです。実際の参考イメージですが以下のようになります。 1. 記事を書き始めた様子 2. 記事に使う画像のアップロードをする様子 3. 社内向けの静的HTMLアップロードツールであるpon を利用して展開されたマニュアル 構築過程でいくつか苦労はあったものの、無事にワークフローは完成してNotionへの依存を一切なくしてGitHubだけで執筆が完結するようになりました。 執筆者側は何も知らない状態でも、マニュアルやSkillなどを手厚く用意しているのでAIエージェントと対話するだけで記事の公開にたどり着くことができます。 結果 これらのワークフローは実際に構築されて2ヶ月ほどですが、その間に公開された記事のほぼ全てがこの仕組みを利用して公開されています。 それまでのNotionによる手作業での入稿もバックアップとして残されてはいますが一気に切り替えが完了しました。 (この過程でいくつかHatenaBlog Workflows側にIssueを提出しましたが、いずれも迅速に対応いただきました。この場を借りて謝意を示します) そして、先述した不満点も全て解消され、さらに以下のような嬉しい点も生まれてきました。 執筆者のはてなIDが不要になった:執筆者がはてなブログにログインして記事の調整をする必要がなくなったため、はてなIDが不要になりました。それに伴ってブログ編集部による執筆者のアカウント管理も不要になりました 画像の管理がブログのアカウントに一元化された:上記の話と近い話ですが、執筆者のはてなフォトライフに依存しなくなったため責任を持って編集部が画像を一元管理できるようになりました GitHubのSuggested Changeを利用した明瞭なレビューのフィードバックが可能に GitHub Projectsを利用した執筆状態の管理とステータストラッキングの自動化が可能に GitHub Issuesを利用した記事ネタの管理の一元化 たまたま執筆の間隔が空いてしまったりすると、記事の入稿のフローはどうなってるんだっけ?とわからなくなることもありました。 それがエージェントとの対話だけで思い出して入稿まですべてできるようになったのは管理の手間や執筆の心理的な障壁を下げるというところで大きくメリットがあったと感じています。 まとめ 既存のHatenaBlog Workflowsの仕組みをAIエージェント向けに拡張することではてなブログの執筆運用をAI時代のワークフローに載せ替えてみました。 AIとHatenaBlog Workflowsの組み合わせは強力で、最小限の管理で多数の執筆者が参加する企業ブログをスムーズに運用することができるようになりました。 一方でそもそもの話として、AIによるアウトプットが世に溢れすぎて、ブログというメディアの価値自体が低下していくなかで編集部は何を大事にするべきか、一歩先の見えない時代ではあります。 ただ編集部員としてはブログを書く文化が風化しないように、企業の活動を広く発信していくプラットフォームが廃れてしまわないように、文章を書く以外の手間を極力排除してサポートしたいと思っています。 実際に、仕組みで雑務をスキップできるようになった分、本記事の執筆そのものにはAIを極力使わずに自分で文章を書いていたりします。 人間臭さというか人間の手触りがある文章ってなんだか素敵だよな、と思いつつまたブログ文化が盛り上がっていくことを期待しています。 明日のブログリレーはCapiさんの記事です。お楽しみに!
はじめに この記事は、BASE夏のブログリレー5日目の記事です。 こんにちは、BASE でバックエンドエンジニアをしている大塚です。 いきなりですが、エラーログアラート、通知チャンネルには流れてくるものの、日々の開発に追われて誰もすぐには見に行けない——そんな経験はないでしょうか? アラートに気づいた人がログを見にいき、該当コードを grep して……という初動調査は、慣れていても 30 分から 1 時間かかる作業です。 BASE ではエラーや例外を Sentry に集約しているのですが、この初動調査を AI エージェントに任せる Slack Bot「sentry-analyzer」を内製して運用しています。 Slack で Bot に調査を依頼すると、AI が Sentry・New Relic・アプリケーションコードを自動で調べて、原因の仮説と証跡をまとめたレポートをスレッドに返してくれます。 本記事では、sentry-analyzer の仕組みと設計上の工夫、運用して見えてきたことを紹介します。 sentry-analyzer とは sentry-analyzer とは、Sentry から通知されるエラーや例外の調査・修正をしてくれる Bot です。 使い方はシンプルで、Slack のエラー通知チャンネルで Bot に調査を依頼するだけです。さらに、Sentry のアラートが届いたスレッドには Bot が「🔍 調査を開始」ボタンを自動投稿するので、ボタン 1 つでも調査を始められます (工夫したポイントで後述)。 調査結果が Slack のスレッドに返信されてくる様子 すると Bot が裏側で次のような調査を自律的に行い、数分でレポートをスレッドに投稿します。 Sentry からイベント詳細 (スタックトレース、頻度、影響ユーザー数) を取得 New Relic に NRQL を発行して、エラーレート・レイテンシ・直近デプロイなどの関連メトリクスを確認 対象リポジトリのコードを Read / Grep して、スタックトレースが指す実装を読解 以上を突き合わせて、原因の仮説・影響範囲・修正の方向性をレポートにまとめる レポートは 2 層構成にしていて、要約は Slack のスレッドに、チャートやサマリーカード付きの詳細レポートは HTML に変換して社内ホスティングにアップロードし、URL をスレッドに添えます。 レポートを受け取って終わりではなく、そのまま 会話を続けられる のもポイントです。「この仮説の根拠をもっと詳しく」「別の時間帯も見て」とスレッドに返信すると、Bot は調査のコンテキストを保持したまま深掘りしてくれます。 さらに、調査の先にある「修正」まで踏み込んでいます。分析レポートの直後に表示されるボタンを押すか「修正して」とメンションすると、エージェントが分析コンテキストを引き継いでコードを修正し、 Draft PR の作成まで 行います (詳細は後述)。 アーキテクチャ sentry-analyzer は、社内のオペレーション向け AI エージェント群を集約したモノレポ base-operation-ai-agents の一員として、セルフホストの Coolify 上でコンテナとして稼働しています。エージェント本体は 1 つの Node.js プロセスで、構成要素は次のとおりです。 Slack 受け口 : Slack Bolt の Socket Mode。アウトバウンドの WebSocket だけで動くため、インバウンドのエンドポイント公開が不要 エージェント本体 : Claude Agent SDK。ただし SDK に直接依存するのはモノレポ共通の LLM 層 packages/llm だけで、エージェントはその薄いラッパー経由で実行する (後述) 外部データアクセス : Sentry / New Relic へのアクセスは in-process の自作 MCP ツール ( get_sentry_issue / execute_nrql など)、コード読解は SDK 組み込みの Read / Grep / Glob / Bash ツール コード参照 : 調査対象リポジトリのミラーは専用の同期サービス (repo-sync) が共有ボリューム上に定期同期しており、各エージェントはそれを参照してその場で grep できる セッション管理 : Slack のスレッド単位でセッション ID を保持 (TTL 1 時間)。スレッド返信時は SDK のセッション resume で会話を継続 エージェント基盤に Claude Agent SDK を選んだのは、 自前実装を最小にできる からです。LLM エージェントを作ろうとすると、ツール呼び出しのループ、会話履歴の管理、コンテキストの永続化と resume あたりを自分で書くことになりがちですが、SDK はこれらを丸ごと持っています。こちらで書いたのは Slack のハンドラ、MCP ツール、調査手順を記述したプロンプトが中心で、エージェントらしい部分のコードはほとんどありません。 調査の「賢さ」はモデルではなくプロンプト側に寄せています。調査手順はマークダウンのプロンプトテンプレートとして管理しており、「NRQL はこういうクエリをこの順で試す」「trace.id はこの点に注意」といった社内の可観測性ノウハウをここに蓄積しています。運用しながらこのファイルを育てることが、そのまま Bot の調査品質の改善になる構造です。 工夫したポイント 1. 修正 PR 自動作成 — エージェントの責務を「コード修正まで」に絞る 分析セッションは原因箇所と修正方針まで把握しているのに、そのコンテキストを捨てて人間がゼロから修正に着手するのはもったいない。そこで分析の延長で修正 Draft PR まで作れるようにしました。設計で特に意識したのは エージェントに渡す権限を最小にする ことです。 エージェントの責務は「使い捨ての git worktree 内でコードを修正し、PR タイトル・本文を生成する」まで git push と GitHub API 呼び出し (GitHub App 認証) は、エージェントではなくホスト側 (orchestrator) が実行する。認証トークンをエージェント環境に渡さない 修正は共有のコード参照ディレクトリではなく、依頼ごとに切る git worktree で行い、終わったら成功・失敗を問わず削除 さらに、自動生成されたコードがそのまま本番に向かわないよう、機械的なガードを何段か入れています。 ガード 内容 Draft PR 固定 自動マージ機構は持たない。人間がレビューして Ready 化する 変更量上限 一定のファイル数・行数を超える diff は PR を作らず報告のみ 保護パス CI 設定やインフラ定義、lockfile などへの変更はデフォルト拒否 出自明記 PR 本文に AI 生成である旨・元の Sentry Issue・依頼者を明記 トリガーは、曖昧なキーワード判定で自動発火させる形は採らず、 分析レポート直後に表示するボタン (確認ダイアログ付き) と、「修正して」「PR 作って」のような 明示的な依頼メンション の 2 つだけを入口にしています。言い回しの揺れによる誤爆を避けつつ、ボタンのおかげで機能の発見性も上がりました。 2. 使ってもらうための工夫 — アラートに Bot が先回りする 社内ツールは作っただけでは使われません。sentry-analyzer も当初は「メンションの書き方を知っている人だけが使える」状態で、アラートを見た人が Bot の存在を思い出せなければ、そこで初動は止まってしまいます。 そこで、Bot が参加しているチャンネルに Sentry のアラートが届いたら、Bot 自身がそのスレッドに使い方の案内と「🔍 調査を開始」ボタンを自動投稿するようにしました。調査の入口がアラートに必ず現れるので、メンションの書き方を覚えていなくてもボタン 1 つで初動が始まります。 地味ですが効いている配慮が 2 つあります。 分析できないアラートには案内を出さない : Issue ID が抽出できるアラートだけを案内対象にする。ボタンを押したのに「分析できませんでした」と返ってくる体験を作らない ボタン押下後はボタンを外し、「◯◯さんが調査を開始しました」に置き換える : 連打による二重実行を防ぎつつ、誰が調査を始めたかがスレッドに残る 修正 PR の作成をボタンにしたこと (前述) も同じ発想で、「機能があることに気づける導線を、使う場所のすぐそばに置く」ことを意識しています。 3. エージェントを単独で運用せず、社内共通基盤 (モノレポ) に乗せる 社内には sentry-analyzer 以外にも Slack で動くオペレーション向け AI エージェントがあり、それぞれが別リポジトリでデプロイ・環境変数・ログ・LLM 呼び出しを別々に作ると、運用も知見も分断されてしまいます。 実際、sentry-analyzer も過去には単独リポジトリ + EC2 (systemd 常駐) で運用しており、この分断を身をもって感じていました。そこでエージェント群は 1 つのモノレポ base-operation-ai-agents に集約していて、sentry-analyzer もその 1 エージェントとして動いています。 共通基盤に乗ることで得たものは大きく 3 つあります。 LLM 層の共有 : Claude Agent SDK への直接依存は共通パッケージ packages/llm の 1 箇所だけ、という境界規約を CI で検査。共通層は SDK をほぼ素通ししつつ、エラー分類 (型付き例外) と usage 記録だけを足す薄い設計で、SDK のバージョン追従や記録基盤の改善が全エージェントに一度に効く 可観測性 : 運用が軌道に乗ると「誰がどれくらい使っているのか」「1 回の調査に何ターン・いくらかかっているのか」が知りたくなりますが、以前はログが console.log のみで、これに答えられませんでした。いまは共通層が 1 実行ごとの usage (ターン数・トークン量・コスト)・モデル別内訳・ステップトレース (どのツールが何 ms 時点で動いたか) を共有 MySQL に記録し、社内の Web UI から閲覧できます。エラー時もメトリクスは取得されるので、失敗した調査のコストも記録に残ります デプロイの標準化 : push でコンテナイメージがビルドされ自動デプロイ。本番プロセス上でビルドしないので、EC2 時代に踏んだデプロイ事故 (後述) は構造ごと解消 調査対象リポジトリの同期 (repo-sync) のような周辺機能も共用になり、エージェントを増やすたびに作り直す必要がなくなりました。一方でエージェント同士は import し合わない疎結合を保っていて、各エージェントは独立したプロダクトとして開発できます。「共有するのは基盤と規約、プロダクトは独立」というバランスの良い運用ができていると感じています。 運用してみて 初動調査が「投げておけば進む」ものになった 一番大きい変化は、アラート対応の心理的なハードルが下がったことです。従来は「まとまった時間が取れたら見よう」と後回しになりがちだった調査が、Bot に投げておけばレポートが返ってくるので、とりあえず投げる → レポートを見て判断する、という流れになりました。レポートには NRQL の実行結果や該当コードの引用が証跡として付くので、そのままチームの議論の土台になります。 定量的な成果として、運用開始からの約 5 ヶ月で約 150 回の初動調査を実行しています(月 30 回ペース)。 人手なら 30 分〜1 時間かかっていた初動調査が、Bot なら数分でレポートが出てくるようになりました。調査時間そのものは計測していませんが、初動までの速さは体感でも大きく変わっています。 踏んだ罠: 本番サーバ上で npm ci をしてはいけない 順風満帆だったわけではなく、EC2 で運用していた時期にはデプロイ事故もやらかしています。dependabot の major バージョンアップ PR を短時間に連続マージした際、デプロイのたびに本番 EC2 上で npm ci + tsc を走らせる構成だったため、小さいインスタンスのメモリが枯渇。 npm ci が中途半端に死んで node_modules が壊れ、Bot が restart ループに陥りました。 さらに悪いことに、デプロイワークフローが SSM 実行結果のエラーを握り潰す実装になっており、GitHub Actions 上はすべて success 表示。Slack で Bot が無応答なことに気づくまで、数時間の停止を見逃しました。 学びはシンプルです。 本番サーバ上でビルドしない。ビルドして成果物だけ配る CI の success 表示は、実態を exit code に反映していなければ意味がない major バージョンアップの連続マージはそれ自体がリスク この事故は「独自構成の 1 台を独自運用し続けること」自体のリスクを実感させてくれて、共通基盤に乗せる判断を後押しする出来事にもなりました。 今後の展望 症状起点トリアージ : 実際の障害対応の入口は Sentry のアラートだけではなく、「商品ページちょっと重くない?」のような軽い違和感のつぶやきから始まることもあります。Sentry の Issue ID がなくても自然言語の症状記述から対象サービスを推定して初動調査を始められる「症状モード」を検討中です 品質評価の仕組み化 : プロンプトやモデルを変えたとき「調査品質が落ちていないか」を人の目視以外で判定できるよう、形式遵守 → 証跡との整合 (グラウンディング) → プロセス品質 → 結論の妥当性、と段階を分けた評価フレームワークを設計中です エージェント間のノウハウ共有 : モノレポに集まったことで、プロンプトの知見やサブエージェントのレシピをエージェント横断で共有する土台ができました。sentry-analyzer で貯めた調査ノウハウを他のエージェントにも還流させていきます 調査ノウハウの蓄積 : 調査手順のプロンプトテンプレートは、運用で得た知見を足すほど賢くなります。定期的な更新はできていませんが、長期で運用する上で必須の作業です おわりに sentry-analyzer は「エラーの初期調査のコスト削減」に大きく貢献していると感じています。 まだまだ改善の余地はありますが、我々と同じように「アラートは来るが初動が重い」チームの参考になれば幸いです。 明日は、matzzさんの記事です。お楽しみに!
この記事は、BASEテックブログ夏のブログリレー4日目の記事です。 BASE株式会社で PAY IDアプリのiOSアプリエンジニアをしている kakkki です。 最近、コーディングエージェントの進化によって開発のスピードが上がり、プルリクエスト(以下、プルリク)の数も大きく増えました。弊社のiOSアプリチームでも、git worktreeを活用して複数の開発を並行して進める場面が増えています。 開発が速くなるのはうれしい一方で、次のような課題も目立つようになってきました。 開発のたびに必要なシミュレーターや実機での動作確認の手間をどう減らすか 増えていくプルリクのレビュワーやデザイナーのUI確認の負担をどう軽くするか そんな中我々が工夫してる取り組みとして、次の2つのコーディングエージェント向けスキルについて紹介します。 iOSシミュレータを操作し、動作確認とスクリーンショット撮影を行うスキル 撮影したスクリーンショットをアップロードし、プルリクに貼り付けるスキル この2つを組み合わせ、UI変更の動作確認からプルリクへのスクリーンショット添付までを、コーディングエージェントに任せるフローを確立しています。 きっかけ:膨大なUIパターンのデグレチェック きっかけは、弊社のアプリ「PAY ID」の商品詳細画面の機能追加のための開発をしていたときのことでした。 商品詳細画面は、10年以上にわたって作り続けられてきました。機能追加や仕様変更を重ねた結果、機能や商品の状態に応じたUIパターンは膨大な数にのぼり、長年の仕様を支える条件分岐が積み重なっています。そのため、ちょっとした改修でも「他の表示パターンに意図しない影響が出ていないか」というデグレチェックが欠かせず、そのUI確認とレビューに大きなコストがかかっていました。 PAY IDアプリ 商品詳細画面のスクショです 同じ商品詳細画面でも、商品の状態によってバッジやボタンの構成が変わります(画面は開発環境のテストデータです)。 そんな中で思いついたのが、UI確認やスクショといった「証跡を残す作業」そのものを、コーディングエージェントに任せられないかというアイデアでした。 変更箇所に影響しそうなUIパターンをエージェントがひとつずつ洗い出し、シミュレーターで動作確認をして、スクリーンショットを集めてプルリクに貼ってくれる、といったフローです。 これができれば、影響しそうなパターンの洗い出しやそれぞれのパターンのUI変更の確認がとても楽になると考えました。 エージェントに動作確認をさせるのは、意外とあっさりできた 最近はコーディングエージェントがシミュレータを操作して、自律的に動作確認まで行えるようにするツールが活発に開発・公開されています。 私たちのiOSアプリチームでは現在、Software Mansionが開発するArgentを使って、エージェントが動作確認するためのスキルをリポジトリ内に置いています。Argentは、コーディングエージェントがiOSシミュレータを操作・デバッグできるためのツールキットで、タップやスクロールなどの操作をエージェントに提供します。 これにより、変更対象の画面にエージェントが自分で遷移したり、各UIパターンの画面を自律的に探索・修正した箇所までスクロールする、というところは意外と早く実現できました。 シミュレータ操作側のスキル(ios-simulator-controller)の中身は、こんな内容です(抜粋・一部一般化しています)。 --- name : ios-simulator-controller description : | Argentを使って、iOSシミュレータ上のアプリを コーディングエージェントが操作する。 画面遷移、UI操作、状態確認、スクリーンショット撮影までを扱う。 --- 【基本方針】 - 目的の画面には、可能であればdeeplinkを使って直接移動する - 操作前に現在のUI要素を取得し、アクセシビリティIDやラベルから対象を特定する - 固定座標による操作は、ほかの方法で要素を特定できない場合に限定する - 操作後は画面の状態変化を確認してから、次の操作へ進む 【操作できること】 - アプリの起動・再起動 - deeplinkによる画面遷移 - UI要素の取得 - タップ、スクロール、テキスト入力 - UI要素の表示・非表示の待機 - スクリーンショット・画面録画 【ワークフロー】 1. 操作対象のシミュレータを特定する 2. 目的の画面へ移動する 3. 現在のUI要素を取得する 4. アクセシビリティIDやラベルから操作対象を探す 5. タップやスクロールなどの操作を行う 6. 目的の状態へ変化したことを確認する 7. 必要な画面のスクリーンショットを保存する 【操作の定石】 - 画面遷移後は一定時間待つのではなく、目的のUIが表示されるまで待機する - スクロールは「スクロール → UIを確認」を、上限を決めて繰り返す - ダイアログなどが操作を妨げている場合は、先に閉じてから本来の操作へ戻る - 操作が反応しなかった場合は状態を確認し、別の候補や経路を試す 操作のたびに現在のUIを読み取り、画面遷移の完了や操作結果を確認しながら次へ進むことで、エージェントによる動作確認の再現性を高めています。また、運用中に見つかったアプリ固有の操作方法や例外パターンも、少しずつスキルへ蓄積しています。 スクショは撮れた。悩んだのは「置き場所」でした 悩んだのはその先です。 撮ったスクショをどこに置いて、レビュアーやデザイナーなど他のメンバーとどう共有するか。 2026年8月現在、GitHub APIやGitHub CLIには、プルリクのディスクリプションに画像ファイルをアップロードする公式な手段がありません。 人間が手操作で貼り付けることはもちろんできますし、Computer Useのような仕組みでエージェントに直接貼らせることもできなくはないのですが、どちらも人間の手間やトークンコストがかさんでしまい、やりたいことに対して大げさなフローになってしまいます。 そんなときに知ったのが、いわゆる「Companion Branch Approach」という方法でした。画像を置くための専用ブランチを別に用意して、そこに画像をコミットしてプッシュしつつ、プルリクのディスクリプションからはそのブランチ上の画像URLを参照する、というアプローチです。 たとえば、プルリクのディスクリプションに次のようなイメージタグを書くイメージです。 < img src = "https://github.com/sample-org/sample-app/blob/screenshots/pr-1234/item_detail.png?raw=true" width = "300" /> 画像用ブランチ上のファイルを ?raw=true 付きのURLで参照すると、ディスクリプション上でそのまま画像として表示されます。 この方法のいいところは、開発用のブランチに画像を混ぜなくて済むことです。専用ブランチに分けておけば、ソースコードの履歴を汚すことなく、GitHub上のURLで画像を参照できます。 私たちのチームではGitのorphanオプション( --orphan )で生成したブランチを利用しています。画像を置くだけのブランチに、ソースコードの履歴は要りません。orphanオプションのブランチなら、まっさらな履歴からスタートできます。 orphan The act of getting on a branch that does not exist yet (i.e., an unborn branch). After such an operation, the commit first created becomes a commit without a parent, starting a new history. — gitglossary - orphan | git-scm.com このorphanオプションのブランチを、私たちは _assets という名前で運用しています。中身は、プルリクの番号ごとにディレクトリを切って画像を置くだけの、とてもシンプルな構造です。 _assets ブランチ ├── README.md ├── pr-1234/ │ ├── item_detail_default.png │ ├── item_detail_soldout.png │ └── item_detail_campaign.png ├── pr-1235/ │ └── cart_badge_count.png └── pr-1250/ ├── search_result_grid.png └── search_result_empty.png _assets ブランチへの画像アップロードとディスクリプションへの画像貼り付けの処理は、以下のようなコーディングエージェントのスキルに落とし込みました。 画像アップロード側のスキル(pr-screenshot-uploader)の中身は、こんな内容です(抜粋・一部簡略化しています)。 --- name : pr-screenshot-uploader description : | スクリーンショットを永続的な _assets orphan branch にアップロードし、 PR description に markdown テーブルとして埋め込む。 フィーチャーブランチを汚さずに画像を GitHub 上で参照可能にする。 --- 【概要】 - ` _assets ` はプロジェクト全体で1つだけの永続 orphan branch。PR ごとにサブディレクトリで管理する - 一時 clone を使って操作するため、元のワーキングツリーには一切触らない - git 操作はすべてシェルスクリプト内で完結し、AI が直接 git コマンドを実行しない - PR description の更新は HTML コメントマーカーで境界を定義し、何度でも再実行できる 【ワークフロー】 1. スクショの保存先ディレクトリを確認する(表示速度のため必要に応じてリサイズ) 2. スクリプトで ` _assets ` ブランチへアップロードし、画像 URL の一覧(JSON)を受け取る 3. 画像 URL から markdown テーブルを組み立てる(カラム構成や行数は AI が柔軟に決める) 4. スクリプトで PR description の「スクリーンショット」セクションを更新する 5. 完了報告(ブランチ / PR / 画像数) スクリプト全文は長くなるため省略しますが、処理は大きく2つに分かれます。 画像を _assets ブランチへアップロードする _assets ブランチだけを一時ディレクトリに clone する pr-<番号> へ画像をコピーして commit ・ push する 参照する画像URLの一覧(JSON)を取得する push が競合した場合は rebase して再試行する プルリクの説明文を更新する 現在の説明文をバックアップする HTMLコメントで囲んだ「スクリーンショット」セクションに対して、取得した画像URLを含んだ内容で追記する 更新結果を取得し、正しく反映されたことを確認する git 操作やプルリクの更新のような「間違えたくない部分」はシェルスクリプトに閉じ込めています。どのようにスクショ画像を並べてテキストによる説明を追記するかはAIの判断に任せています。 今では、開発セッションの中で「プルリクのディスクリプションに、修正した範囲のスクショを貼っておいて」と自然言語で指示するだけで、エージェントが修正箇所のスクショをプルリクのディスクリプションに上げてくれます。 導入して何が変わったか これらのスキルをリポジトリに置いてから、プルリク作成時のスクリーンショット添付が劇的に楽になりました。今ではシミュレーターでのスクショの撮影からプルリクへの貼り付けまで、エージェントが一気通貫でやってくれます。 ただし、ログインが必要な画面など、エージェント単体ではたどり着きにくい画面もあるので、すべての画面で手動操作をゼロにできたわけではありません。 それでも、日々のiOSアプリ開発の大半でこの仕組みを活用できるようになり、いろいろな面で良い効果がありました。 デザイナーとのやりとりが速くなった たとえば何パターンものUIバリエーションがある画面をデザイナーが実機でチェックしようとすると、手動でひとつずつ画面遷移して目視確認していくことになり、とても労力がかかります。 最近は、変更したUIパターンを網羅したスクショ一覧を貼ったプルリクを共有することが多いです。デザイナーは開発アプリを直接操作しなくても、一覧化されたスクショを見るだけで手早くフィードバックできるようになりました。 もちろん今でも最終的には実機確認はしていただくのですが、「エンジニアが開発する → デザイナーがUIをレビューしてフィードバックする → エンジニアが修正する」というフィードバックループが、以前より速く回るようになりました。 データの状態に応じてUIのバリエーション数が多い画面ほど地味に効いてきます。 スクショがすぐに上げ直せるので修正サイクルが早くなった 手動でスクショを貼っていた頃は、実装を修正したら人力で画像を上げ直すのを頑張るか、古いスクショのまま放置されるかのどちらかになりがちでした。 今では、修正後に「もう一回動作確認して、スクショを上げ直しておいて」と1プロンプト投げるだけで、エージェントがすべてのスクショを自動でアップロードし直してくれます。 気軽に修正して上げ直せるので、開発したものをクイックに共有してフィードバックをもらって修正するというループが回しやすくなったと感じています。 スクショのあるプルリクが当たり前になってレビューコストが下がった プルリクの数が増えていく中で、レビューするコストの増大は見逃せません。UIの変更が含まれるプルリクであれば、極力修正箇所のスクショを添付することで少しでもレビュアーのコストを軽減できると考えています。 iOSアプリチーム内ではその共通認識があるのでもともとスクショを手動で上げることにも抵抗がなかったのですが、最近はiOSアプリエンジニア以外の方がiOSアプリのプルリクを上げてくれる機会も増えてきました。 このスキルがあれば気軽にスクショをプルリクに用意できるので、レビューコストの軽減に役立っています。 まとめ この仕組みを導入してから、4ヶ月ほど経ちました。今では「修正した範囲のスクショを貼っておいて」のひとことが、各エンジニアのコーディングエージェントとのやりとりの中に定着しています。 チーム全体として、「開発した変更を周りに伝えるハードル」が下がった と実感しています。 コーディングエージェントの進化で、コードを書くこと自体はどんどん速くなっています。一方で、「その変更が正しいかを確かめること」と「何をしたのかを周りに共有すること」は、まだまだチームごとの工夫が必要な領域だと感じています。 開発したものの確認や共有のやり方に似たような課題を感じている方の参考になれば嬉しいです。小さな仕組みですが、チームのフィードバックループは思った以上に速く回るようになるはずです。 BASEでは、一緒にプロダクトを作ってくれるエンジニアを募集しています。ぜひお気軽に採用情報をご確認ください。 open.talentio.com 明日は、Otsukaさんの記事です。お楽しみに!
この記事は、 BASE PRODUCT TEAM 夏のブログリレー2026 の3日目の記事です。 はじめに PAY ID Dept & Data Strategy Group で検索やレコメンドまわりを担当している持田です。 私たちのチームでは、大きなキャンペーンが実施されるたびに、検索やレコメンドといった API のスケールアウト・スケールインの設定を事前に入れる作業を手動で行っていました。PAY ID では月に何度かキャンペーンが行われることもあり、この設定作業が地味に手間でした。 社内にはキャンペーンの告知を流す Slack チャンネルがあり、告知は Slack ワークフローで投稿されています。そこで、この告知が作られたタイミングで Slack ワークフローを起点に GitHub Actions を動かし、設定変更の Pull Request を自動生成する 仕組みを作りました。 Slack ワークフローから GitHub Actions を起動する、というのは一見よくありそうな要求なのですが、公式に用意された素直な道がなく、実際にやってみるとハマりどころが多くありました。この記事では、その構成と、途中で踏んだ落とし穴を共有します。 この記事の対象読者 Slack ワークフローから GitHub の処理を起動したい方 定型的な申請作業を、中継サーバーを立てずに自動化したい方 前提 Slack の GitHub コネクタが利用できること GitHub Actions が使えるリポジトリがあること 記述している Slack / GitHub の挙動は、いずれも 2026 年 7 月時点で私たちの環境において確認したものです。プランや設定によって異なる可能性があります 何を自動化したかったのか 前提として、私たちのサービスでは大きなキャンペーンを実施する際、アクセス増に備えて事前にサーバーの台数を増やしておく必要があります。具体的には、ECS サービスや Aurora のリードレプリカに対する AWS Application Auto Scaling のスケジュールドアクション(指定した時刻に最小・最大キャパシティを変更する設定)を Terraform で管理していて、開始前にスケールアウト、終了後にスケールインする予定を cron 式で書いて反映する形です。 この作業には、いくつか面倒な点がありました。 キャンペーンの告知は Slack のワークフローで流れてくるが、設定変更の PR を作るのは人力 設定を書くファイルが複数に分かれていて、同じ日時を何度も転記する必要がある 日時を cron 式に変換する必要があり、間違えやすい つまり、入力(キャンペーンの日時)はすでに Slack 上に決まった形式でそろっているのに、そこから先が手作業、という状態でした。ここをつなげば自動化できそうだ、というのが出発点です。 Slack ワークフローから GitHub Actions を起動する 直接つなぐ方法がない まず考えたのは「Slack ワークフローから GitHub Actions の workflow_dispatch を叩く」という方法です。しかし Slack のワークフロービルダーには、 任意の HTTP リクエストを送るステップが用意されていません (Webhook は外部からワークフローを起動する受信側には用意されていますが、ワークフローから外部へ送る側にはありません)。 そうなると選択肢は次の2つです。 カスタム Slack アプリを作ってワークフローに独自のステップを追加し、そのステップを処理するサーバー側で GitHub API を叩く(ステップの追加は Slack 公式のカスタムステップ機能 で可能で、 Workflow Buddy のような先行事例もあります) Slack ワークフローの GitHub コネクタで issue を作成し、それをトリガーにする 1 は自由度が高い一方、アプリとサーバーの開発・運用がまるごと増えます。申請の記録も処理の起点も GitHub 側に寄せられる 2 を選びました。 issue を橋渡しに使う GitHub Actions には on: issues というトリガーがあり、issue が作成されたタイミングでワークフローを起動できます。全体の流れは次のとおりです。 issue 本文をインターフェースにする GitHub コネクタの issue 作成ステップでは、issue の本文を自由に組み立てられます。固定のテキストとフォームの回答(変数)を組み合わせたテンプレートを設定しておく形です。このとき、あとで機械的にパースできる形にしておくのがポイントです。 type: キャンペーン start: 2026/07/24 18:00 end: 2026/07/25 00:00 name: 〇〇キャンペーン requested_by: @xxxx announce_url: https://xxx.slack.com/archives/CXXXXXXXX/pXXXXXXXXXXXXXXXX パース側は、この程度の実装で済みます。 import re FIELD_RE = re.compile( r"^\s*(type|start|end|name|requested_by|announce_url)\s*[::]\s*(.*?)\s*$" , re.IGNORECASE) def parse_issue_body (body: str ) -> dict [ str , str ]: fields = {} for line in body.splitlines(): m = FIELD_RE.match(line) # 同じキーが複数あった場合は最初の行を採用する if m and m.group( 1 ).lower() not in fields: fields[m.group( 1 ).lower()] = m.group( 2 ) return fields 実際に運用する上では、次のような点も決めておく必要がありました。 対象の issue を絞る — 何もしないとリポジトリに作られたすべての issue が処理対象になってしまうので、タイトルが特定のプレフィックス(今回は [campaign-scale] )で始まる issue だけを処理します タイムゾーンを固定する — 日時は JST として解釈すると決め打ちし、cron 式に変換する際もタイムゾーン指定を明示します 想定外の入力はエラーにする — 日時としてパースできない、必須項目が空、といった場合は PR を作らずに処理を止め、理由を issue にコメントします 入力に不備があって PR が作られなかった場合は、issue 本文を手で直して復旧する運用にしています。あとで触れますが、フォーム側で「特定の選択肢のときだけ必須」といった制御ができないため、入力漏れは必ず起きます。issue を編集すると自動でリトライされるようにしてあるので、直して保存するだけで済みます。 最後の announce_url には、ワークフローが投稿した告知メッセージへのリンクが入ります。Slack のメッセージ送信ステップには「メッセージのリンク」という出力変数があり、これを後続の issue 作成ステップに渡せるので、どの告知に対するスケーリング設定なのかを issue や PR に残すことができます。 ハマったところ 1. コネクタのリポジトリ一覧が途中で切れる GitHub コネクタの「issue を作成する」ステップでは、対象リポジトリをドロップダウンから選びます。ところが私たちの環境では、このリストがアルファベット順に 300 件ほどで打ち切られていて、目的のリポジトリまでたどり着けませんでした。組織のリポジトリ数が多いと、ここで詰まります。検索窓もありますが、読み込み済みのリストを絞り込んでいるだけのようで、リストに載っていないリポジトリは検索しても出てきませんでした。 解決策は、 対象リポジトリにだけアクセスできる machine user(bot 用アカウント)でコネクタを接続し直す ことでした。ドロップダウンに出るのは接続したアカウントがアクセスできるリポジトリなので、権限を絞ればリストが短くなり、目的のリポジトリを選べるようになります。 副次的な効果として、個人アカウントへの依存もなくなりました。個人アカウントで接続していると、その人が異動・退職したときにワークフローが止まってしまいます。 2. 自動生成した PR では CI が起動しない 今回のリポジトリでは、PR を作ると自動で terraform plan が走る CI が設定されています。ところが、GitHub Actions が作成した PR ではこれが動きませんでした。 これは GitHub Actions のドキュメントに明記されている挙動で、 GITHUB_TOKEN を使って行った操作は、原則として別のワークフローを起動しません (ワークフローが無限に連鎖するのを防ぐための仕様です)。 When you use the repository's GITHUB_TOKEN to perform tasks, events triggered by the GITHUB_TOKEN , with the exception of workflow_dispatch and repository_dispatch , will not create a new workflow run. 引用: Triggering a workflow|GitHub Docs ここで一つ落とし穴がありました。この件でよく紹介される回避策に「PR を手動で Close して Reopen する」というものがあるのですが、今回のリポジトリでは使えませんでした。CI 側のトリガー定義が on : pull_request : types : [ opened, synchronize ] となっていて、 reopened が含まれていなかったためです。トリガー定義に reopened を足せば解決しますが、既存 CI の定義には手を入れたくなかったので、今回は PR のブランチへ手元から空コミットを push する(= synchronize を発火させる)回避策を取りました。 git commit --allow-empty -m "ci: trigger plan" && git push そうした事情がなければ、素直にトリガー定義へ reopened を足すのでよいと思います。恒久的に解決するなら、 GITHUB_TOKEN ではなく GitHub App のトークンで PR を作る方法もあります。 3. フォームの条件分岐が使えない 今回、フォームで「キャンペーンの場合は終了日時を必須にしたい」という要求がありました。ワークフロービルダーには「ブランチを追加する」(if/else 分岐)という機能があるのですが、これは上位プラン向けの機能で、私たちの環境では利用できませんでした。 この結果、 「特定の選択肢を選んだときだけ必須」という制御はできず 、フォーム上の必須設定は種別によらず一律になります。 そこで、次のような方針にしました。 ラベルとヒント文で伝える — 「終了日時(キャンペーンの場合は必須)」のように、条件をラベル自体に書き込む 入力漏れは受け入れて、後段で処理する — GitHub Actions 側で検知し、PR を作らずにエラーとして通知する 既定値で埋めない — 「未入力なら開始+1時間」のような補完はあえてしない 3つ目は意図的な判断です。既定値で埋めてしまうと、設定したつもりが実際には途中で元に戻る、という事故につながります。これは何も設定されていない状態より危険なので、明示的に止めて人間に判断してもらう方が安全と考えました。 その代わり、issue 本文を編集すれば自動でリトライされるようにしています。今回作った自動化ワークフロー側の on: issues トリガーに edited を足すだけです(先ほど触れた既存 CI とは別の、この仕組み用のワークフローです)。 on : issues : types : [ opened, edited ] 4. edited トリガーで PR が二重に作られる ところが、 edited を足したことで別の問題を踏みました。 PR を作成したあとも、その PR がマージされるまで issue は open のままです。この状態で issue 本文を編集すると(誤字修正でも)ワークフローが再実行されます。このときデフォルトブランチにはまだ変更が反映されていないため、「まだ設定されていない」と判定され、 同じ内容の PR がもう1本作られてしまいます 。 対策として、再実行してよいかを判定する軽量なジョブを前段に置きました(以下は要点だけの抜粋です)。 jobs : precheck : outputs : proceed : ${{ steps.check.outputs.proceed }} steps : - id : check run : | # 編集による起動でなければそのまま進む # 編集の場合、PR 作成済みかどうかを issue のコメントで判定する ... create-pr : needs : precheck if : needs.precheck.outputs.proceed == 'true' 作成済みかどうかの判定に GitHub の検索 API( Closes #N in:body など)を使う手もありますが、検索インデックスには遅延があるため、作成直後の編集を取りこぼす可能性があります。今回のワークフローは PR を作成すると issue に PR のリンクをコメントで残すので(冒頭の構成図にあるステップです)、そのコメントの有無を直接 API で確認する形にしました。こちらは検索インデックスの遅延に左右されません。 ただしこの方式は、短時間に連続で編集された場合の同時実行までは防げません。コメントの有無を確認してから、PR を作り、コメントを残すまでには数十秒かかります。その間に 2 回目の編集で起動した実行がチェックに到達すると、まだコメントが存在しないためすり抜けてしまいます。厳密に防ぐなら、issue 番号をキーにした concurrency を併用して実行を直列化します。 concurrency が保証するのは同時に走らないことだけなので、直列化した上で、順番が回ってきた実行がコメントの有無を確認して止まる、という組み合わせで初めて防げます。 concurrency : group : campaign-scale-${{ github.event.issue.number }} cancel-in-progress : false おわりに この仕組みによって、これまでキャンペーンのたびに手作業で行っていた設定変更が自動化されるので、今後は少し楽になりそうです。 明日は @kakkki さんの記事です。お楽しみに! BASE では一緒に働く仲間を募集しています。 binc.jp
はじめに BASEのProduct Divisionでエンジニアをしている Kondo と申します。 この記事は、BASE夏のブログリレー2日目の記事です。 生成AIの進化を実感しながら、これからエンジニアとしてどう生き残っていけば良いのだろうと考えることが増えました。ここでいう「生き残る」とは、AIに代替されることなく、変化の中でもチームやプロジェクトに必要とされ続けることです。生成AIでコードが書ける前提になり、「コードが書ける」こと自体が強みになりにくい場面が増えたと感じています。また技術的な調査やドキュメントを書くスキルも、AIに頼めば質の良いものがすぐに出てくるようになり、スキルの差別化という意味では難しくなったように思います。 一方で、AIに任せて生成を待っているだけでは、プロジェクトが思ったほど前に進まないことも、実際の仕事の中で分かってきました。 AI時代にエンジニアとして生き残るための正解、というものは私自身はまだ持ててはいません。しかし、このAI時代だからこそ、試行錯誤をしながら自分がどのように考えているか、書いてみたいと思います。 AIで大量のコードを生成しても、プロジェクトの開発が速くならなかった AIを使うことで、大量のコードをすぐに生成できるようになりました。しかし、そのコードを理解して、責任を持ってリリースするのは実装者とレビュアーです。AIは次々にコードを生成してくれる一方で、実装量が増えるほど、レビューで確認すべき変更も増えました。 困ったのは、出てきたコードが「それっぽいけれど、既存コードベースの文脈を拾いきれていない」ケースです。一見正しそうなのに実際には動かない、既存コードと設計や書き方がそろっていない、といったことが起きました。さらに、実装者が使うそれぞれのAIがコードベースの異なる前提を置いて実装し、全体としての統一感がなくなっていきました。これを人間がレビューで逐一指摘していたのですが、指摘の量はコードの生成量に比例して増えていきます。レビューで毎回言い続けるやり方は続かないと感じました。 もう一つ、より深い問題として、AIによる実装の生成速度に、人間の理解と検証が追いつかないことがありました。実装者は、生成された大量のコードを理解し、既存実装や外部APIの仕様と整合しているかを検証するために、多くの時間を要しました。同様に、レビュアーも変更内容や検証結果を確認し、責任を持ってリリース可否を判断する必要があります。最終的には自信を持ってリリースするのですが、そこに至るまでには時間がかかり、結果としてリリース速度はあまり上がりませんでした。 プロジェクトの開発は、ただコードを書くだけではなく、チームが変更を理解して、リリースしてもOK、その後の運用も自信を持って行える、という判断をレビューで下して、リリースして進むものです。ただコードを生成すれば良い、それが成果である、ということにはなりません。 そこで、コードを速く生成することよりも、チームが判断できる状態を作ることを優先しました。 まず、チームが適切にレビューできるプルリクエストの粒度になっているかを見直しました。また、AIが生成したコードをチームが理解し、その後の運用まで進められるかも確認するようにしました。必要であればチームで集まり、仕様とコードを同期的に確認しながら動作確認を行いました。また、レビュアーだけではなく、実装者自身が生成されたコードの細部まで理解できているかも確認するようになりました。AIがコードを素早く生成するだけでは、プロジェクト全体は速くなりません。レビューやその後の運用まで含めて、チームが判断できる状態を作ることを意識するようになりました。 資料をAIで迅速に用意しても、プロジェクトは前には進まなかった 同じ問題は、コード以外でも起きました。私たちは、プロダクトのある機能で利用している外部APIのサポート終了に伴い、後継となるAPIへ移行するプロジェクトに取り組んでいました。移行先APIの仕様をAIに調査させると、網羅的な項目をすぐに出してくれます。検証コードや資料も短時間で大量に作れます。しかし、そもそもプロジェクトで何を判断したいのかが決まっていないと、資料を作っても次の意思決定にはつながりませんでした。むしろ大量の資料を漫然と読んでいるだけで、認知負荷がただ増えただけのようにも感じました。 改めてプロジェクトの課題を考えると、現在の処理量を維持したまま次のAPIに移行できるかでした。そこから、移行可否を判断するために必要な現在の負荷、移行先APIのレートリミット、ピーク時のリクエスト数、制限を超えた場合の挙動などに絞ってAIに資料を作らせました。そうして初めてプロジェクトの意思決定に必要な資料を作成することができました。 AIは資料を一瞬で作ってはくれますが、ただAIに調査を依頼するだけでは、今のプロジェクトで解くべき課題や、意思決定に必要な情報までは定まりません。そこは人間がしっかりと考えた上でAIに指示を出すことで、初めてプロジェクトに必要な資料を迅速に用意して、次に進むということができました。 では、AI時代にエンジニアとしてどう生き残るのか 今回、AIを使って大量のコードを生成しても、人間が理解し、責任を持ってレビューできなければ、プロジェクト全体の開発は速くなりませんでした。同じように、AIを使って網羅的な資料を作っても、何を判断したいのかが決まっていなければ、次の行動にはつながりませんでした。どちらの経験でも、AIが作った量ではなく、その結果としてチームの理解や意思決定がどれだけ進んだかが重要でした。 これらの経験から、AIに任せることと、人間が考えるべきことを見極める必要があると感じました。また、AIの生成能力をプロジェクトの成果につなげることも、これからのエンジニアに必要な役割だと考えています。 そのために、まず重要だと考えているのが、プロジェクトの中にある曖昧な困りごとを、課題として特定して言語化することです。レビューが進まない、資料があるのに判断できない、同じ確認を何度もしている、といった状況は、最初から明確なタスクとして現れるとは限りません。誰が何に困っているのか、本来どのような状態になっていたいのか、その差を整理して、取り組める課題にする必要があります。 もう一つは、プロジェクトの不確定要素を特定することです。何が分かっていないために判断できないのか、どの不確定要素が大きなリスクなのか、何を検証すれば次に進めるのかを考えます。AIは検証コードや資料を作ってくれますが、今のプロジェクトでどの不確定要素を優先して解消するべきかは、プロジェクトの状況を踏まえて人間が考えなければなりません。 また、自分のタスクだけではなく、チームの中で困っているメンバーを見つけて助ける動きも重要になると考えています。AIによって個人がコードや資料を作る能力は高くなりました。その一方で、チームの認識のずれを見つける、レビューや意思決定の詰まりを解消する、相手が判断できる形に情報を整理する、といった動きは、AIに依頼して待っているだけでは起こりません。 AIを使って自分のタスクを早く終わらせるだけではなく、チームやプロジェクト全体を前に進めるにはどうすればよいのか。これからも実際の仕事の中で試行錯誤しながら、自分が組織やプロジェクトにどのような価値を出せるのかを考え続けていきたいと思います。 おわりに この記事では、AI時代にエンジニアとして生き残るために何が必要かを考えてみました。一緒にAI時代を生き残ってくれる方は、ぜひ採用情報をご覧ください。明日は、MochidaHarukaさんの記事です。お楽しみに! binc.jp
この記事は、BASE夏のブログリレー1日目の記事です。 こんにちは。BASE ProductDiv ItemSection の Torata です。 直近半年で私が作った問い合わせ調査AIエージェントの取り組みを紹介しようと思います。技術的にはそこまで難しいことをしていないので、技術構成の話ではなく、導入にあたってぶつかった壁とそれをどう乗り越えたかを中心に書いていきます。 前提:BASEの問い合わせ対応の流れ 前提としてBASEの問い合わせがどのような流れで、私が作成したAIエージェントがどのフェーズにフォーカスしたものかを説明します。 BASEではショップオーナーや購入者(以下、あわせてユーザーと呼びます)から受け取った問い合わせをCSチームが一次受けしてくれます。 技術的な知見や権限が必要でCSチームでは解決できないものがエンジニアにエスカレーションされます。 このエンジニアへエスカレーションされた問い合わせをBASEではcs_qと呼んでおり、後述するcs_aというAIエージェントも含めたフローは以下のようになっています。 cs_qの業務負荷を減らすためにcs_aという相棒を作った ユーザーからの問い合わせというのは日々発生するものであり、プロダクトの規模が大きくなれば問い合わせの数も自然と増えていきます。 しかし、その問い合わせに対応するエンジニアの数が比例して増えるわけではありません。自然とcs_qの業務負荷が増え、通常の開発業務を圧迫するシーンもみられるようになってきました。 そこでcs_qの業務負荷を軽減するAIエージェントを作成しました。 cs_qをアシストするAIなのでcs_aと名付け、Slack Botとして問い合わせチケットの投稿に応じて自動で調査を行い、回答案を作ってくれます。ただし、cs_aの回答はあくまで下書きであり、エンジニアが内容を確認してからCSチームへ返信する運用にしています。 実際のSlack上でのやりとりはこのような様子です。 cs_aを導入してからの課題と取り組み cs_aを導入してすぐに効果が出たというわけではありませんでした。 ここからはcs_aを運用する中で出てきた課題と、それを解決するために行った取り組みについて紹介します。 調べられる範囲を広げる 導入当初、cs_aはコードのみを参照して回答をしていました。 しかし、それだとプロダクトに対する事前知識やユーザーの現在の状況といった問い合わせ業務に必要なコンテキストを踏まえた上での調査ができていませんでした。 そこでマルチエージェント型にし、問い合わせ内容に応じてNew Relicのログ、問い合わせを受けた機能の仕様書、CSチームの利用しているSOP(Standard Operating Procedures)、過去問い合わせをもとにしたナレッジを取得するようにしました。さらに、エージェントが取得できないデータに関しては問い合わせ担当者に必要な情報を尋ねるようにしました。 これにより、プロダクトの知識やユーザーの行動をもとにした調査ができるようになりました。 特にログを参照することで、ユーザーがコードでいうどこの分岐に入ったのかを特定できるようになり、調査精度が上がりました。 回答精度の低下に気づけるようにする 調べられる範囲を広げたことでcs_aが参照できるものが増えた一方、LLMの出力が揺らぐ要因も増えてしまいました。 プロンプトを少し変えたり新しく参照できる要素を増やすたびに、精度が落ちていないかドキドキして見守らなければいけないのは開発者にとって優しい状態ではありません。 そこで過去のcs_qを利用して、cs_aの回答を評価(eval)する仕組みを作りました。 過去のcs_qには、エンジニアが実際に行った回答や調査経緯が残っているので、そのまま正解データとして使えます。 LLMの評価において正解が用意されているのはとてもありがたいことです。またcs_qのチケットには事前にラベル付けがしてあったので、ラベルごとにケースを用意し、特定の領域でのみ精度が落ちているのを見逃さないようにすることもできました。 調査の筋道を見せる cs_aはさまざまなコンテキストから調査ができるようになり、安心して変更を加えられる状態も整いました。 するとcs_aは期待通りの調査結果を返してくれるようになりました。大量の文章とともに... Slack上で動くBotという性質上、文量が多いとSlackで表現できるマークダウンでは可読性が落ちてしまいます。かといって文量を少なくしてしまうと、調査結果が何をもとにしたものなのかをエンジニアが知ることができず、結果的にエンジニアが裏付けのために調査し直すような二度手間が発生してしまいました。 これを解決するために、社内用のHTMLホスティング環境を利用して、調査の筋道をHTMLで見られるようにしました。 この環境についてはCTOの記事でも説明されています。 devblog.thebase.in これにより可読性高く大量の情報をエンジニアがcs_aから得られるようになり、結果的にcs_aの調査結果の信頼性を上げることに繋がりました。 回答の7割が一致するようになった これらの活動により、一部の領域やカテゴリを除いたcs_qの7割で、cs_aの回答がエンジニアの回答とほぼ一致するようになりました。 問い合わせのSlackスレッドの中では、エンジニアが「cs_aの回答の通りです。」と返すだけで対応が完了するケースもちらほら出てくるようになりました。 それでも、エンジニアの工数は減っていなかった 回答を考えて書く作業はたしかに楽になりました。しかし、ユーザーからの問い合わせ内容を確認し、CSチームへ返信するという業務自体は何も変わっていません。 cs_aへ作業を任せることはできるようになりましたが、判断を任せることはまだできていないのです。 結果として、業務負荷は多少減ったものの、「劇的に楽になった」と言える状況にはまだ持っていくことができませんでした。 これから目指すもの 今後は判断をcs_aに任せることでcs_q対応が劇的に楽になったという状態を目指そうと思っています。 判断をcs_aに任せるということはcs_aを信頼するということです。 そのためにはCSチームと連携しながら、どういうタイミングでエンジニアに調査を依頼したいか、どういう状態ならAIに任せてもいいかといった基準を決め、その基準を誰でも見られる形にすることで、安心してcs_aの回答をそのまま利用できる状態にする必要があります。 これらを実現してcs_aの回答をそのまま使える範囲を広げ、エンジニアが対応するcs_qの件数自体を減らしていくことで、エンジニアが問い合わせの根本対応やユーザー体験を考えることに集中しやすい世界を作りたいと考えています。 まとめ cs_aを運用していく中で、CSチーム、調査担当者、cs_aの開発者のそれぞれにとって価値のある運用を目指した取り組みを紹介しました。 今後はこの取り組みの価値をより大きくしていけたらと思っています。 BASEではcs_a以外にもさまざまな業務をAIに任せようという動きが広まりつつあります。 興味のある方は、ぜひお気軽に採用情報をご確認ください。 binc.jp 明日は、Kondoさんの記事です。お楽しみに!
こんにちは!BASE PRODUCT TEAM BLOG 編集部です。 お盆が過ぎてもまだまだ夏真っ盛りという今日この頃ですが、みなさまいかがお過ごしでしょうか。突然ですが、このたび「夏のブログリレー2026」と題して、ブログ記事の集中公開イベントを開催します! この記事では、その概要と見どころをご紹介します。 夏のブログリレーとは BASE では、これまで年の瀬のアドベントカレンダーを8年続けて開催してきました。 devblog.thebase.in 一方で、それ以外では集中連載企画といったようなものはあまり実施してこなかったこともまた事実です。 日々のプロダクト開発・運用の中では、1年を通してさまざまな知見や工夫が生まれています。そうした情報を鮮度の高いタイミングでカジュアルに発信できれば、読んでくださる方にとっても記事を書くメンバーにとっても、より良い機会にできるのではないかと考えました。 そこで夏にもアウトプットの場を作り、BASE のプロダクト開発における日々の取り組みやその魅力を年末に限らずどんどん発信していきたく、開催する運びとなりました。 公開予定一覧 現時点での予定は以下の通りです。内容や順番は変更になる場合があります。また、今後さらにラインナップが追加される可能性もありますので、ぜひ楽しみにお待ちください。 日付 執筆者 タイトル 2026-08-25 @Torata 問い合わせ調査AIエージェントcs_a導入の苦難とこれから 2026-08-26 @Hirotaka Kondo AI時代のエンジニアに何が必要か、いま自分が考えていること 2026-08-27 @ichimu_HarukaMochida Slack ワークフローから GitHub Actions を起動する 2026-08-28 @kakkki プルリクに動作確認スクショがあるのを当たり前にする、iOSアプリ開発でのAI活用 2026-08-29 @Hiroki Otsuka SentryアラートをAIで自動調査するSlack BotをClaude Agent SDKで作った話 2026-08-30 @matzz はてなブログの運用をシンプルにする取り組み 2026-08-31 @Capi 脅威モデリング、はじめました 2026-09-01 @wakana SLOを全社の共通言語にするための「主要SLO」の現在地と次のフェーズ 2026-09-02 @ema Claude Code で使っているツールをGo製CLIで作ってるよ 2026-09-03 @Sho Takeuchi マルチモーダルな商品カテゴリの分類モデル 2026-09-04 @oliver 顧客中心主義を開発の意思決定に組み込むために実践したこと 2026-09-05 @rerenote ブログ編集部員として おわりに 気になるテーマはありましたでしょうか?普段なかなか外に出せていない知見やちょっとした Tips といったものまで、幅広くお届けする予定です。期間中は毎日記事が公開されますので、ぜひ楽しみにしていてください! 初日となる明日は @Torata さんの記事です!お楽しみに! binc.jp
はじめに こんにちは、バックエンドエンジニアのかがの( @ykagano )です。 2026/8/6(木)に「PHPカンファレンス2026 リジェクトコン&アフターイベント」を共同開催しました。 本記事では、当日の登壇内容や会場の様子についてお届けします! イベント概要 2026/7/20に開催されたPHPカンファレンス2026のスポンサー3社(株式会社コドモン、BASE株式会社、株式会社クイック)による共同開催で、会場は株式会社コドモンさまのオフィスをお借りしました。 codmon.connpass.com BASEからの登壇 カートの信頼性を担保するWireMockを使ったe2eテスト(かがの) 今回コドモンさまからお話をいただき、共同開催することができて大変光栄でした。 実は10分話すつもりが当日に持ち時間8分だと分かり、結果少し早口に9分ほどでお話しさせていただきました(汗)。 Xの投稿など見るとご参加いただいた皆さまに楽しんでいただけたようでよかったです! speakerdeck.com エンジニアの地力の鍛え方:エラーチャンネルのボールを拾い続けること(meihei) PHPカンファレンスにプロポーザルを出したものの、惜しくも採択に至らなかったトークとして「エンジニアの地力の鍛え方:エラーチャンネルのボールを拾い続けること」をお話しさせていただきました。 限られた発表時間では喋りきれなかった部分も懇親会などで沢山お話しができて、とても楽しい発表になりました! ありがたいことに、11月に開催される PHPカンファレンス新潟 でもお話できる事となったので、今回のトークからより踏み込んだ内容で発表できたらなと考えております。 発表資料はそのタイミングで公開する予定ですので、ぜひお楽しみにお待ちください! 参加したメンバーのコメント wakana( @ wakanaction ) 今回は登壇する2人の応援としてゲスト参加しました。 どちらのセッションも、背景などを知っている私からしても非常に聴き応えがあり、また知見を得られる発表で、背筋が伸びる思いでした。 懇親会では2人とは別のテーブルにいたのですが、そこでも2人のトークや、そこから派生した話題で盛り上がっていました。会場の多くの方に響いた発表だったのではないかと思います。 そして、すてきな会場をお貸しくださり、当日の運営を取りまとめてくださったコドモンさま、司会進行をスムーズかつ陽気に盛り上げてくださったクイックさまに、この場を借りて感謝申し上げます。 会場・懇親会の様子 芝生の上にビーズクッションや座布団が敷かれていました。 後方にはアウトドアチェアも用意されていてとてもリラックスできる空間でした。 懇親会では、BASEから飲食を提供させていただき、参加者同士で交流を深めることができました。 おわりに 共同開催という形で、PHPコミュニティの盛り上がりに少しでも関われていれば嬉しく思います。会場をご提供いただいた株式会社コドモンさま、共同開催の株式会社クイックさま、そしてご参加いただいた皆さまありがとうございました! BASEではエンジニアを募集しております。よろしければ、採用情報もぜひご覧いただけますと幸いです。 binc.jp
こんにちは、バックエンドエンジニアのかがの( @ykagano )です。 BASEは2026年8月6日(木)に株式会社コドモン、株式会社クイックと共同で「PHPカンファレンス2026 リジェクトコン&アフターイベント」を開催します。 PHPカンファレンス2026 リジェクトコン&アフターイベントについて 2026年7月20日(月・祝)に開催されたPHPカンファレンス2026のスポンサーである、コドモン・BASE・クイックの3社合同で、リジェクトコン&アフターイベントを開催します。 リジェクトコンとはプロポーザルを提出したものの、惜しくも採択されなかったトークを発表するイベントです。 そのため、登壇者はPHPカンファレンス2026にプロポーザルを提出したメンバーです。 codmon.connpass.com 日時: 2026年8月6日(木) 19:00〜21:00 会場: コドモンオフィス(東京都品川区西五反田八丁目4番13号 五反田JPビルディング10階) 参加費: 無料 PHPカンファレンス2026へのBASEの協賛・登壇の様子はこちらをご覧ください! devblog.thebase.in セッションの紹介 当日は6本のLTが予定されており、BASEからは2名が登壇します。 「カートの信頼性を担保するWireMockを使ったe2eテスト」 19:20〜 LT2 私、かがのが発表させていただきます。ECサイトのショッピングカートでは、e2eテストを実施する際にさまざまな課題があります。WireMockを用いてそれらの課題をどのように解決しているかをご紹介します。 「エンジニアの地力の鍛え方:エラーチャンネルのボールを拾い続けること」 19:50〜 LT5 meiheiさんが発表されます。meiheiさんは私とは別のモジュールを担当されているのですが、私が担当しているモジュールでエラーが発生すると、meiheiさんがすぐに気付いて連絡をくださることがあり、いつも助かっています。 LTのあとには懇親会も予定されています。 登壇者や参加者のみなさまと、ゆっくりお話できるのを楽しみにしています! おわりに 参加費は無料で、connpassから先着順でお申し込みいただけます。 PHPカンファレンス2026に参加された方も、惜しくも参加できなかった方も、ぜひお気軽にご参加ください! BASEはカンファレンスやイベントへの参加や登壇を積極的に支援しています。 もしご興味がありましたら、採用情報もご確認いただけると幸いです。 binc.jp
はじめに こんにちは、BASE株式会社で PHPer をしている @meihei です。 直近で New Relic の Terraform 管理を改修し、AI を使いながら New Relic を利用しやすい環境を整えました。設計思想や具体的な取り組みなどをお話します。 背景 一度アーカイブされた Terraform 管理 BASE では以前から New Relic を導入しており、BASE BANK では New Relic を Terraform で管理する取り組みもありました。 devblog.thebase.in ただし、この取り組みで使っていたリポジトリは、現在アーカイブされています。運用を続けるには New Relic と Terraform の両方に詳しい人が継続的に関わる必要があり、メンテナンスをし続けていくには難しさがありました。 New Relic 活用の広がりと Terraform 化の再開 しかしその後、社内ではサービスレベルマネジメントの取り組みが少しずつ進み、New Relic を活用する場面も増えていきました。 devblog.thebase.in devblog.thebase.in また、New Relic のダッシュボードを Terraform で管理し、「動く仕様書」として扱う取り組みも始まりました。 devblog.thebase.in こうして New Relic の Terraform 化が再び始まりました。本格的に New Relic を活用するようになったが故に、その管理・保守運用をコードで管理するモチベーションが高まったことが理由の一つです。ただし、この時点で実装されていたのはダッシュボードのみであり、Service Level などの New Relic の他の機能まで広く Terraform 管理するには、まだ実装とメンテナンスのコストが大きい状態でした。 Agentic AI が変えたこと 時は過ぎ、現在は Agentic AI の広がりによって、Terraform の実装や変更案の作成が以前よりやりやすくなっています。New Relic Provider の仕様を調べたり、Terraform コードを読み解いたり、既存の New Relic リソースを import して Terraform 管理へ移す作業までも AI に支援してもらうことで進めやすくなりました。そして、これまでやりたかった New Relic の監視設定を Terraform で管理することの現実味が高まりました。 一方で、AI の支援によって Terraform が書けるようになったとしても、それを継続的に安全に運用できるとは限りません。むしろ、Terraform で管理する対象が増えるほど、コードの構造、所有権、レビューのしやすさ、安全性、設定意図の共有が重要になります。 特に New Relic の監視設定は、運用体制と強く結びつきます。どのチームがどの領域に責任を持つのかという監視設定の所有権をコード上で表現しやすいことが重要になります。 また、監視設定の所有権を 現在の 組織構造だけを前提に設計すると、将来の変更に弱くなります。組織は時間の経過とともに変化し、運用体制もそれに合わせて変わります。Agentic AI の普及によって事業環境の変化が速くなるでしょうし、組織や責任範囲の見直しも増える可能性があります。そのため、監視設定の所有権は、変化を前提に表現できる形にしておく必要があります。 今回目指したこと 今後は、New Relic や Terraform に詳しい一部のメンバーだけでなく、各領域のエンジニアや AI の支援を受けた開発者が、監視設定の変更に関わる場面が増えていくと考えています。 その前提では、Terraform を書きやすくするだけでは不十分です。誰がどの監視設定に責任を持つのか、どの経路でレビューするのか、危険な変更をどう検知するのか、そして設定の意図をどう共有するのかを、継続的に保てる構造が必要になります。 今回の取り組みでは、New Relic の Terraform 管理を「AIに書かせる」ことではなく、人間と AI が継続的に安全に変更できる構造へ整えることを目指しました。 課題 まず、再設計する前の New Relic のダッシュボードのみが管理された Terraform リポジトリは以下のような構成になっていました。 ├── .github/ ├── modules/ # 再利用を目的とした共通 module │ └── dashboard/ │ └── standard_feature_dashboard/ # 共通レイアウトのダッシュボードを生成する module ├── services/ # プロダクト・サービスごとの Terraform 設定 │ └── base/ │ ├── prod/ # 環境ごとの設定 │ │ ├── checkout/ # 決済領域 │ │ │ └── dashboards/ # 決済領域のダッシュボード設定。base/prod/main.tf からローカル module として読み込まれる │ │ │ └── hoge_dashboard.tf # 共通 module を呼び出して、1つのダッシュボードを定義するファイル │ │ ├── item/ # 商品領域 │ │ ├── order/ # 注文領域 │ │ └── main.tf # サービス x 環境単位の Terraform エントリポイント。各領域のローカル module を読み込む │ └── dev/ └── README.md # プロジェクト全体の説明 既存構成では、標準化されたダッシュボードを作成するための共通 module がありました。もともと、ダッシュボードが標準化されず乱立・属人化し、メンテナンスが困難になっていたため、共通 module によるアプローチは有効でした。また、領域ごとにディレクトリが分かれており、2026年以降の組織図( ECのコア機能を軸にした組織設計 )とマッチし、保守運用すべきコードも明確になっています。 課題1:ガードレール未整備 New Relic の Terraform 管理が始まってまだ間もないということもあり、Terraform 運用に必要なガードレールは十分に整っていませんでした。 今後、New Relic や Terraform に詳しくないエンジニアが変更に関わることを考えると、レビューだけに依存せず、品質とセキュリティを機械的に担保できる仕組みが必要でした。 課題2:共通 module の運用と開発速度のボトルネック 共通 module を使うと標準化や再利用性が高まる反面、利用者(このリポジトリでは module を呼び出す領域)ごとの差異を埋めるため複雑さが増したり、変更に対して柔軟さが欠けることがあります。 また、ある時点の共通 module で利用者が目的を達成できない場合「共通 module を改修する」「共通 module を使わない回避策で作成する」という判断が必要になります。これらはどちらも、変更したい領域の外側にある関心事を考える必要があります。 たとえ AI によって Terraform の実装や変更案の作成がしやすくなったとしても、共通 module の存在はこのような追加の判断が必要となり、結果として判断を行う人間がボトルネックとなってしまいます。 課題3:New Relic の UI 上でダッシュボードを変更できない ダッシュボードの共通 module では newrelic_one_dashboard を使っていました。Terraform のリソースとしては newrelic_one_dashboard の他にも newrelic_one_dashboard_json があります。この2つにはダッシュボードを HCL で書くか JSON を書くかという違いがあります。 New Relic の UI 上ではダッシュボードを GUI Editor か JSON で編集ができ、そのダッシュボードの Export は JSON 形式のみです。そのため HCL で書かれたダッシュボードは簡単に New Relic の UI 上で変更することができませんでした。 一方で newrelic_one_dashboard_json にして、Export したダッシュボードをそのまま使う運用だと、最初の目的のダッシュボードの標準化が難しいという課題がありました。 課題4:運用には New Relic の専門知識が必要 New Relic の設定では、システムの振る舞いや異常検知の条件を New Relic 側の表現に落とし込む必要があります。一方で、開発や事業の会話はユーザージャーニーやユビキタス言語を中心に行われます。この対応関係が暗黙知のままだと、New Relic に詳しい人以外が監視設定の意図を理解しづらくなりますし、AI による生成物の精度も悪くなります。 取り組み Terraform 管理の方針 課題には明確に答えられるものもあれば、トレードオフを伴うものもあります。そこでまずは以下の方針を決めました。 New Relic は UI での編集も許容する。Terraform は UI での変更もコードでの変更も両方を取り込む形で管理する。 サービスや組織構造の変化に追従しやすいことを優先し、なるべくフラットで質素な構成に寄せる。 1は、New Relic の UI Editor での編集のしやすさに加え、New Relic 最新機能もいち早く取り入れやすくするための判断です。UI 操作で機能を理解したうえで Terraform 管理に移す、という流れを想定しています。 2は背景でも述べた通り、Agentic AI の普及によって事業環境の変化が速くなることを前提に、組織や責任範囲の見直しが増えることを想定した判断です。 この方針の上で、課題に対して4つの取り組みを行いました。 取り組み1:ガードレールの整備 まずは、課題1を解決するために、Terraform の CI/CD を整備しました。 User Key を発行した New Relic ユーザーの権限を最小化 アクセストークンなどの Secrets を GitHub から AWS に移行 AWS へは OIDC で最小権限でアクセスするように変更 GitHub Actions のバージョンは SHA Pinning で固定 Dependabot での更新は cooldown 期間を設ける tflint と secretlint を導入 重要な部分は CODEOWNERS でレビュー必須に このうち SHA Pinning や Dependabot の cooldown は、 tj-actions/changed-files の侵害(CVE-2025-30066) のようなサプライチェーン攻撃への備えです。悪意あるバージョンの多くは公開から間もない時期に発見されるため、 新しいバージョンの適用をあえて遅らせる cooldown はシンプルですが有効な対策になります。 speakerdeck.com これは AI 専用の仕組みではなく、人間が変更しても AI が変更しても、危険な変更を検知しやすくするための土台となります。 取り組み2:共通 module を廃止 続いて課題2を解決するために、現時点では共通 module を廃止しフラットな構成に寄せることにしました。 ファイル名に prefix をつけるだけというシンプルな作りと CODEOWNERS で、責任範囲や所有者を表現するようにしました。 ├── services/ │ └── base/ │ ├── prod/ │ │ ├── main.tf # エントリポイント │ │ ├── item_alerts.tf # 商品領域のアラート │ │ ├── item_dashboards.tf # 商品領域のダッシュボード │ │ ├── item_service_levels.tf # 商品領域の Service Level │ │ ├── item_*.tf # 商品領域その他 │ │ ├── checkout_*.tf # 決済領域 │ │ ├── order_*.tf # 注文領域 │ │ ├── notifications.tf # 共通の通知設定 │ │ ├── ... # .github/CODEOWNERS services/base/*/item*.tf @baseinc/item-team services/base/*/checkout*.tf @baseinc/checkout-team services/base/*/order*.tf @baseinc/order-team ※ ディレクトリ構成中のファイル名やチーム名は例です。 各ファイルでは New Relic のリソースを定義するだけとなっています。 なお、module を全否定しているわけではありません。大量の同種リソースを扱う必要が出てきた時にはスコープを限定した状態で module の導入を改めて検討する余地を残しています。 取り組み3:標準化されたダッシュボード作成のスキル化 共通 module を廃止しようとすると「標準化されたダッシュボードを作成するための共通 module」をどう取り扱うかが関連して問題になります。こちらは AI が New Relic ダッシュボードを作成する時に、標準化されたダッシュボードを生成する Skill(Agent Skills)を用意して対応しました。 背景で紹介した「動く仕様書」の記事では標準化を共通 module というコードの制約で担保していましたが、これを Skill とレビューという生成時の規約で担保する形に置き換えたことになります。 そしてダッシュボード自体を HCL で書く必要性もなくなったため、JSON で書くようにし、 newrelic_one_dashboard_json を使うことで課題3も解決しました。JSON でダッシュボードを管理する方法は New Relic 公式ブログでも紹介されています 。 またダッシュボードの本体が JSON で書かれることで、フロントエンドのコードから呼び出しやすくなり、モックデータで HTML を生成するツールを作って CI から社内 HTML ホスティング環境にアップロードするようにしました。 devblog.thebase.in モックデータで HTML を生成して、それをベースに PR の議論が進んでマージされた例です。 取り組み4:ユビキタス言語と New Relic の監視設定をつなぐ Catalog file これはまだ PoC ですが、用語マッピングファイルとして Catalog file を用意しました。 Catalog file は、ビジネスやプロダクトの言葉と、New Relic の監視設定を対応づけるための翻訳レイヤーです。 イメージとしては、フロントエンドとバックエンドの間にあるインターフェースを理解するために OpenAPI を使うようなものです。 現時点では、OpenAPI のような厳密な契約を最初から目指すのではなく、ユーザージャーニーやユビキタス言語と、New Relic の監視設定を対応づけるための薄い翻訳レイヤーとして用意しました。 たとえば item(商品)領域の Catalog file では、次のような対応関係を表現します(クラス名などの値は例です)。 domain : item critical_user_journeys : - id : item_register name : 新しい商品を追加できること # 社内で管理している SLO 一覧と結合するためのキー code_paths : backend : - 'ItemController::register' service_levels : - tf : newrelic_service_level.item_register_success # Terraform の state アドレス 実は最初は、ユーザージャーニーの説明や目標値、オーナーなども Catalog file に持たせようとしていました。しかし実際に書いてみると、それらは社内の SLO 管理ドキュメントや Terraform 側にすでに存在する情報で、Catalog file に転記するとどちらかが必ず古くなります。 そのため現在は、Catalog file には他のどこにも存在しない情報(ユーザージャーニーとコードと New Relic リソースを結びつける対応関係)だけを持たせ、それ以外は結合キーと参照リンクで済ませる形を試しています。 これにより、AI に New Relic の Terraform 設定だけを読ませるのではなく、人間が普段使っているユーザージャーニーやユビキタス言語を起点に、New Relic で観測できる設定へ変換しやすくすることを目指しています。 とはいえ、これが翻訳レイヤーとして機能するのか、持たせる情報の粒度はこれで良いのかは、まだ検証の途中です。Catalog file は別ファイルとして存在する以上、現実のシステムや New Relic の設定と乖離するリスクも常にあり、今後は Terraform や New Relic の実態との差分を機械的に検査していくことも課題になります。 再設計後の構成 最終的に現在の Terraform リポジトリは以下のような構成になりました。 ├── .claude/ │ └── skills/ │ └── newrelic-dashboard # 標準ダッシュボードを作成するための Claude スキル ├── .github/ │ └── CODEOWNERS # 領域ごと担当チームをオーナーに設定する ├── services/ # プロダクト・サービスごとの Terraform 設定 │ └── base/ │ ├── prod/ # 環境ごとの設定 │ │ ├── item_alerts.tf # 商品領域のアラート設定を管理 │ │ ├── item_dashboards.tf # 商品領域のダッシュボードを管理 │ │ ├── item_service_levels.tf # 商品領域の Service Level を管理 │ │ ├── checkout_*.tf # 決済領域 │ │ ├── order_*.tf # 注文領域 │ │ ├── dashboards/ # ダッシュボード本体の JSON。領域ごとにディレクトリを分ける │ │ │ └── item/ │ │ │ └── hoge_dashboard.json │ │ └── main.tf # サービス x 環境単位の Terraform エントリポイント │ └── dev/ ├── observability-catalog/ │ ├── domains/ # ドメインごとのユビキタス言語と New Relic の監視設定の対応関係を定義する │ │ └── item.yml │ └── applications.yml # リポジトリと New Relic を対応付けるための定義書 ├── tools/ │ └── dashboard-viewer/ # ダッシュボード JSON をモックデータで描画するローカル viewer └── README.md # プロジェクト全体の説明 執筆時点では、ダッシュボード6枚に加えて、Service Level 15本とその burn-rate アラートなど、通知設定までが Terraform 管理下に入っています。ダッシュボードだけだった半年前と比べると、管理対象は New Relic の監視設定ひと通りに広がりました。 まとめ New Relic の Terraform 管理を再開するにあたり、AI に書かせることではなく、人間(組織)と AI が継続的に安全に変更できる構造を整えることを目指しました。 ガードレールで、誰の変更であっても機械的に検証される土台を作る 共通 module を廃止したフラットな構成と CODEOWNERS で、所有権を組織の変化に追従しやすい形で表現する ダッシュボードは JSON と Skill で、UI での編集しやすさと標準化を両立する Catalog file で、ユビキタス言語と監視設定の対応関係を明示する 組織も事業環境も変わり続ける前提に立つと、監視設定の管理は「一度作って終わり」にはなりません。変更に関わる人(と AI)が増えても壊れない構造を先に用意しておくことが、これからの New Relic 運用の土台になると考えています。 BASE では「オブザーバビリティ × AI」な取り組みにも挑戦しています。 一緒に進めていく仲間を募集しています。興味のある方は、ぜひお気軽に採用情報をご確認ください。 binc.jp
はじめに 2026/7/20(月・祝)、BASE株式会社から 登壇 & ゴールドスポンサーとして協賛した PHP Conference Japan 2026が開催されました。 今回はPHP Conferenceに初参加したメンバーが多かったため、初参加ならではの感想コメント、会場やスポンサーブースの様子についてお届けします! BASEのスポンサーブースの紹介 今年は Yell Bank と PAY ID がロゴをリニューアルしたため、それにちなんで「BASEロゴ検定〜あなたもBASEのGEEKになろう〜」と題して、BASEが展開するサービスのロゴにまつわるクイズ企画を実施しました! ↑ブログ投稿用に、ご回答いただいた中から厳選させていただいて貼っていますが、写真にない付箋が100枚くらいありました。 非常に多くの方にご回答いただき、ノベルティとして用意していました「BASEロゴ今治ミニタオル」も14時頃に配布終了してしまいました。(お渡しできなかった方、すみません…!) 今回作成したクイズのうち問1〜問3までは、イベント当日に受付で入場者に配られるチラシにロゴの説明として回答を掲載していました。 また、問4はエクストラ問題としてチラシには答えが載っていない問題を用意していました。そのように説明すると「我こそは」と真剣に考えてくださる方が多く、みなさん楽しんで挑戦してくださいました。(この問題の答えはこちらの PAY ID ロゴリニューアル のブログに掲載されています) ノベルティとしてお配りしていたBASEロゴ今治ミニタオル。こだわって作成しているため嬉しいお声をたくさんいただけてたいへん嬉しかったです。 登壇の紹介 かがの( @ykagano ) speakerdeck.com 今回は購入に失敗したインシデントについてお話しさせていただきました。 ポイントとしては以下の2点と考えています。 クラス名をシリアライズして保存する設計による、クラス削除の影響漏れ 擬似乱数の消費位置を変更したことによる、キー衝突の確率変動 発生原因を深掘りすると複数の要因が重なって発生した、学びのあるインシデントでした。 特に2は資料では割愛していますが、擬似乱数の消費位置を突き止めるために、AIを使って発生パターン(シード43億通り × 消費位置15まで)の総当たりを行い、エラーが発生した2つのキーの消費位置がどちらも5であることを確認しています。 インシデントはなるべく発生させないようにしたいですが、インシデントを通してPHPの擬似乱数の仕組みを知ることができたのはよかったと思います。 PHP Conferenceでの登壇は初めてで緊張しましたが、たくさんの方にお聞きいただきありがとうございました。また次回の開催時にも参加させていただきます。 現地で見たセッションから得た学び・感想( @wakana ) ブーススタッフとして従事していた時間が長かったため数個のセッションしか見ることができませんでしたが、その中でも特に印象に残ったセッションの感想です。 (登壇の詳細については、登壇者様からアップロードされている資料や、後日公開予定となっているイベント公式アーカイブをご覧ください) Laravelで学ぶ Webアプリケーションチューニング入門 Ryo Tomidokoro氏 speakerdeck.com こちらのセッションでは、Laravelでのアプリケーション開発・運用を例に取ったアプリケーションのチューニング方法の基礎、考え方について発見・再確認することができました。 印象に残った学びは以下です。 「なんか遅い気がする」ではなく、「APIのレスポンス返却が普段より500ms遅くなっている」などの定量的な数値に変換することで議論が行えるようになり、調査を開始することができる。 ブラウザに表示されるまでの経路のうち、Webアプリケーションが占める割合は一部でしかない。Webアプリケーションを改修すると決める前に、プロファイラーを使って本当にWebアプリケーションが遅いことを特定する。 Webアプリケーションが遅いことがわかったら、New Relicやdatadogなどのプロファイラーを使って遅い処理を特定する。ボトルネックは往々にして解決が難しい事が多いが、そこに向き合うことが重要。 ボトルネックから目を逸らすな。 ボトルネックの特定や、改修にあたっては日頃から自分の中に使える手札を増やしておくことが大事。(スライド内で紹介されていたものの一部抜粋) PHP で学ぶ Cache の距離の話 / study_cache_with_php PHPerのための計算量入門/Complexity101 for PHPer 達人が教えるWebパフォーマンスチューニング 〜ISUCONから学ぶ高速化の実践 個人的にも直近の業務ではプロファイラーツールを用いてチューニングのための分析を行うことが増えてきていたため、タイミングよく今の自分に必要な発表を聞くことができました。 日々の手札を増やしていけるよう、一層精進していきたいと思いました。 そして「ボトルネックから目を逸らすな」肝に銘じます 💪 セレンディピティ~AIで答えが出る時代に、なぜコミュニティへ行くのか? 小泉岳人 氏 www.docswell.com こちらのLTでは、「AI時代にわざわざコミュニティに足を運ぶ意義はどこにあるのか」という点を伺うことができました。 個人的に、別のカンファレンスやその後の懇親会でも耳にすることが増えてきたテーマだったため、視聴前から興味の強い発表でした。 こちらのLTでは、主に次のような学びを得ることができました。 問いに対する答えを知るだけならAIで行うことができてしまうが、コミュニティに来ると思いがけない出会い(人・情報・etc…)に心を動かされることがある 様々な発表を聞いて、すぐに答えの出ない問いや考えを得て、それを持ち帰ることで、 自分の中で問いを育てることができる 問いを育てながら何度も足を運ぶことで、仲間を作ることができる 特に印象的だったフレーズは「 問いを育てる 」というものでした。 AIを使った開発がスタンダードになり、なんでもすぐにAIに聞くのが当たり前になったいま、自分で考える力が落ちてきているのではと、個人的にも危機感を持っていたタイミングでした。 そんな中で聞いた「 問いを育てる 」というフレーズは、これまでの自分にはなかった視点で、シンプルながら強く心に残りました。 この発表が一日のタイムテーブルの最後に置かれ、直後に懇親会が控えていたことも、「コミュニティに集まる意義」を物語るような構成に感じられて感動しました。 実際、その後の懇親会では、普段よりもさらに深く交流を楽しむことができたと感じています。 参加メンバーからのコメント wakana 今回初めてPHP Conferenceに参加することができました! ブーススタッフとして朝早くから集まり、カンファレンス後の懇親会まで参加しました。1日を通してとても楽しく充実していて、帰る頃には学生時代の文化祭後のような清々しい疲労感がありました。 スポンサーブースではたくさんの来場者の方とお話することができ、「サービス使ってます!」「ノベルティかわいい!」といった嬉しいお声をいただいたり、さらには「今の仕事を進めるうえで困っていることがあり、BASEさんではどのように解決していますか?」といったディープな内容のお話まで楽しく交流させていただきました。 他のスポンサー企業様のブースにお邪魔した際も、近い悩みを共有できて立ち話が思わぬ盛り上がりを見せました。実は当日の朝までは夜の懇親会に参加しない予定だったのですが、「来られるならぜひお話ししましょう」とお声掛けいただいたのをきっかけに、その場でチケットを購入しました(笑)。おかげで、今後の共同イベントをご一緒する予定の会社様のエンジニアの方とも交流を深められ、思い切って参加して本当によかったと感じています。 セッションで得られる学びはもちろんですが、コミュニティの温かさやウェルカムな雰囲気は、これまで参加したイベントの中でもダントツでした。 ぜひまた参加したいです! 白数( かじゅ ) 今回、PHP Conferenceにスポンサーブーススタッフとして初めて参加しました。 スポンサーブースの企画段階から関わり、「あれはどう?」「これはどう?」とメンバーでわいわい意見を出し合いながら、楽しく準備を進めることができました。 実施した「BASEロゴ検定」をきっかけに、1人でも多くの方にBASEが運営するサービスについて知っていただけたら嬉しいです。 当日は、初めてお会いする方々と企画や会話を通じて交流し、セッションもいくつか聴くことができました。 PHPコミュニティの活気を直接感じられる貴重な機会となりました。 三佐和 初参加の PHP Conference でした! 普段 PHP をあまり書いていない身としては、「参加しても大丈夫かな…」と最初は少し不安もありましたが、スポンサーブースの企画から当日まで携わることができ、とても貴重な経験になりました。 当日はブースに来てくださった方々がクイズを通して BASE のサービスに興味を持ってくださる様子を見ることができたり、普段利用しているサービスの感想を直接聞けたりと、来場者の方々と雑談を交えながらお話しする時間がとても楽しかったです。 また、スタンプラリーにも参加してみましたが、各社それぞれ企画だけでなくノベルティにも個性や特色が表れていて、ブースごとに異なる雰囲気を楽しめたのが印象的でした。 今回のカンファレンスを通して、技術だけでなく、人と人、企業同士がつながることの魅力や、イベントづくりの面白さを感じることができました。 また機会があればぜひ参加したいです! 地引 初めてPHP Conferenceに参加しました! 私はスポンサースタッフではなく、インターン生として先輩(Capi(かぴ) さん)と一緒に一般参加させていただきました。 現地で参加したセッションはどれも刺激的で、ものづくりやコーディングを純粋に楽しんでいる熱量あふれる方々の発表がとても印象的でした。AI時代ならではの開発の工夫や考え方にも触れることができ、ものづくりの面白さを改めて実感しました。 懇親会では、BASEの先輩方が本当に温かく、他社のエンジニアの方や先輩のお知り合いの方をたくさん紹介してくださいました。周りに学生が少なく緊張していたのですが、いろいろな方と交流するきっかけを作っていただけたことがとても嬉しく、改めてBASEの温かさを感じました。 技術的な学びだけでなく、新しい人とのつながりやコミュニティの温かさも感じられるイベントで、参加してよかったと心から思える一日でした。 またぜひ参加したいです! おわりに カンファレンスのクロージング後、一日ブーススタッフお疲れ様、そしてかがのさんLTお疲れ様でした〜!ということでみんなでパシャリ。(一般参加として来ていた Capi(かぴ) さん 地引さん も加わってくれました!) ブーススタッフとして参加したメンバーの内半分以上が初参加というフレッシュな構成でしたが、全員の協力のおかげで何事もなく終えることができました。 また、PHP Conference の運営スタッフのみなさまも、お忙しい中、多くの時間をイベント準備に割いてくださったことと思います。 こんなに貴重な機会をいただけたことに、この場を借りて心より感謝申し上げます。 BASEではエンジニアを募集しています。よろしければ、採用情報もぜひご覧ください! binc.jp