BASE Departmentでバックエンドエンジニアをしている、かがの( @ykagano )です。 2026年9月30日(水)に合同会社DMM.com 二次元コンテンツ開発部(以降DMM社と表記)とBASE株式会社(以降BASE社と表記)にて合同の勉強会を開催しました。この記事では当日の様子をレポートします! 開催概要 日時: 2026年9月30日(水)18:00〜 場所: BASE社オフィス内のイベントスペース 参加者: 14名 テーマ: 「カートの課題と取り組みを語り合おう」 DMM社、BASE社ともに、カート領域に関わるメンバーが参加しました。 開催のきっかけ 以前私がPHP Conference Japan 2026に参加した際に、DMM社のOzakiさんの下記登壇を拝聴しました。その後Ask the Speakerのブースでお話しさせていただいたところ、Ozakiさんから合同勉強会をしませんかとお誘いいただいたのがきっかけです。 fortee.jp 登壇 まずBASE社から私が登壇し、BASEの購入ドメインで感じている課題についてお話ししました。 speakerdeck.com 続いてDMM社のOzakiさんに登壇いただき、DMMのECで感じている課題として、技術的負債やAI活用の課題などをお話しいただきました。 ワークショップ ビクトリア大学のMargaret-Anne Storey教授が2026年の論文『 From Technical Debt to Cognitive and Intent Debt | arXiv 』で公開された内容を受けて、ソフトウェアの3つの負債である「技術的負債」「認知的負債」「意図的負債」について考えるワークショップを行いました。 論文の最後の一文を引用します。 Maintaining healthy software systems in the age of AI may depend on preserving alignment among intent, code, and understanding, and software teams should manage understanding with the same care and urgency as they manage code. 引用: From Technical Debt to Cognitive and Intent Debt | arXiv この一文では、AI時代のソフトウェア開発において、コードだけでなく、チームの理解や設計意図との整合も同じように管理することの重要性が述べられています。 以下は、ワークショップで紹介した負債の定義です。 ■ 技術的負債 コードや設計の問題が蓄積し、変更や保守が難しくなっている状態 例:重複した処理や複雑な依存関係により、小さな変更にも手間がかかる ■ 認知的負債 システムに対するチームの共有理解が不足し、安全な変更が難しくなっている状態 例:コードは動くが、仕組みや変更の影響範囲をチームで説明できない ■ 意図的負債 目的・制約・設計判断の理由が十分に記録・維持されず、判断のよりどころが失われている状態 例:「なぜこの仕様なのか」が分からず、本来の目的から外れた変更をしてしまう 「技術的負債」はこれまでよく知られた負債ですが、「認知的負債」「意図的負債」という2つの負債はAIを用いた開発でより顕著になってきていると感じます。 ワークショップでは参加者に次のお題を出しました。 負債による現実の課題とその取り組み方を考えてみましょう 本日お話しした課題がどの負債にあたるかを書き出してみてください 書き出すのは全く別の課題でもいいですし、過去に経験した課題でも構いません 課題の中の一部を切り出したり、粒度を絞ってもいいです またその取り組み方を考えてみましょう 4〜5人ずつの3チームでテーブルごとに分かれ、以下のような構成の模造紙に付箋を貼る形式で進めました。 題材 現実の課題(10分) 取り組み方(10分) 技術的負債 認知的負債 意図的負債 模造紙に書き出した後は、各チームから内容を発表してもらいました。 各テーブルの周りに集まって、模造紙の内容を見ながら、こういう課題があるのでこう取り組めばいいのではと考えた内容を発表いただきました。 ワークショップのチーム発表の様子 課題を認知的負債や意図的負債として分類し、その取り組み方について考えてもらうことで、普段の業務ではなかなか考えない視点で課題を捉えることができたのではないかと思います。 懇親会 ワークショップ後はアンケートに回答いただいた後、懇親会を行いました。 BASE社から飲食を提供させていただき、それぞれグループに分かれて会話を楽しんでいました。 懇親会で歓談している様子 参加者からのコメント Capi(@You_saku98) 自分は普段購入ドメインではなく発送ドメインのチームに所属しているのですが、2026年に入ってから決済機能の改修を経験し、「決済の関わる購入ドメインにもっと詳しくなりたい!」と思い参加させていただきました。 勉強会ではドメイン関係なく開発現場で悩んでいる話題について他社のエンジニアさんを交えて議論ができてよかったです。"どの会社も似た課題に直面していること"、"課題に対して取り組んでいること"が知れてよかったです。 また、AIコーディングエージェントが当たり前になった現代で技術的負債、認知的負債、意図的負債がどんな形でどんな影響を自分たち開発エンジニアにもたらすのかを考え直す良い機会になりました。勉強会の中で自分が最近考えていた仮説がそこまで的外れじゃなさそうなことに気づけました。開発環境、自分の考え方は絶えず変化していくものなのでまた半年後くらいに同じテーマで勉強会をやりたいです笑。 懇親会では勉強会中に触れられなかった発送ドメインの話やバッチ処理の運用、監視体制についてお話しさせていただきました。また、実務の話しだけでなく "直近趣味でどんな技術を触っているのか"、"この技術に最近興味があるんですが知ってますかね" など広い範囲でお話もできました。 おわりに DMM社の皆さまにはBASE社まで来ていただきありがとうございました! 社外の方を含めた勉強会を主催させていただいたのは初めてでしたが、アンケートの結果を見ると、参加者の皆さんにも満足していただけたようで、うれしく思います。 またよければ2回目の合同勉強会の開催なども行っていければと思います。 BASE社では現在エンジニアを積極採用中です。興味があれば採用情報もぜひご覧ください! binc.jp
はじめに こんにちは、BASE DepartmentでPHPerをしている @meihei です。 長く運用しているサービスでは、機能の追加や変更を重ねるうちに、「もう使っていなさそうなデータ」が残ることがあります。以前は必要だった配列のキーが残り、その値を取得して受け渡す処理も動き続けている(ただし描画では使っていない)、といった具合です。 使われているデータは残し、不要なものだけを安全に削除したい。そのためには、まず本当に使われていないのかを確かめる必要があります。 この記事では、 ArrayAccess と trigger_error() を使い、廃止候補のキーが読まれたら警告を出す仕組みを紹介します。 背景 パフォーマンス改善のため、無駄な処理をなくしたい APMで分析していたところ、カテゴリ情報に含める商品数を専用のAPIリクエストで取得していることが分かりました。この商品数がテンプレート側で使われていなければ、取得処理を削除し、API通信にかかる時間を削減できると考えました。 ショップごとに異なるテンプレートがある BASEでは、ショップの世界観に合わせてデザインを選び、カスタマイズできます。無料・有料のさまざまなテンプレートに加えて、HTML編集によるカスタマイズも提供しています。 thebase.com そのため、PHPから渡したデータを受け取る側には、数多くのカスタムテンプレートがあります。例えば同じカテゴリ情報を渡していても、カテゴリ名だけを表示するテンプレートもあれば、カテゴリに属する商品数まで表示するテンプレートもあり得ます。 PHP側のコードを見ればデータを渡していることは確認できても、各ショップのテンプレートで実際に参照されているかまでは分からない、という課題がありました。 誤って削除しても表示崩れに気づきにくい ただし、実際には表示に必要だった場合、削除するとテンプレートの表示が壊れます。Twigのデフォルト設定( strict_variables が false )では、存在しないキーの値をそのまま表示しようとしてもエラーにならず、その部分が空になります。こうしたバグは気づきにくく、原因の特定や修正も大変です。 カテゴリ情報の受け渡しをTwigで書くと、次のようになります。 echo $twig- > render('category.twig', [ 'category' = > [ 'id' = > 12, 'name' = > 'トップス', 'count' = > $this- > getCountCategory(id: 12), // 数百ミリ秒かかるので消したい処理 ], ]); <h2>{{ category.name }}</h2> <span>{{ category.count }}件</span> このテンプレートが使われているなら、 count はまだ削除できません。 配列からDTOへ移行したい また、データの受け渡しも、配列からDTO(Data Transfer Object)へ移行したいと考えていました。最終的には、必要な項目とその型を明示した、POPO(Plain Old PHP Object)なDTOでデータを渡す形を目指しています。 Twigの category.name という書き方は、配列のキーだけでなく、オブジェクトのpublicプロパティにも使えます。そのため、この参照方法を保ったまま、PHP側で渡す値を配列からDTOへ置き換えられます。 twig.symfony.com ただし、今の配列をそのままDTOにすると、不要な項目まで引き継いでしまいます。そこで、まずは既存の読み方を保ちながら廃止候補のキーの利用状況を確認し、安全に削除できることを目指しました。 配列アクセスを観測しながらDTOへ移行する ArrayAccessで読み取り専用のDTOを作る まずは、配列として読める、読み取り専用のDTOを用意します。PHPの ArrayAccess を実装すると、オブジェクトに対する $dto['name'] のようなアクセスをメソッドで受け取れます。 以下のコードはPHP 8.2以降を対象にしています。最初に読み取りと書き込み禁止を実装し、その後で警告処理を追加します。 abstract readonly class AbstractDeprecatedKeyProbe implements \ ArrayAccess { public function __construct (private array $values) { } final public function offsetExists(mixed $offset): bool { return isset ($this- > values[$offset]); } final public function offsetGet(mixed $offset): mixed { return $this- > values[$offset] ?? null; } final public function offsetSet(mixed $offset, mixed $value): void { throw new \ LogicException (static::class . ' は書き換えられない'); } final public function offsetUnset(mixed $offset): void { throw new \ LogicException (static::class . ' は削除できない'); } } readonlyにする必要がなければ、 ArrayObject を使うのもよいと思います。配列として扱うための実装が用意されているので、そのまま利用できます。今回はDTOをreadonlyにし、配列アクセス経由の書き込みも明示的に禁止したかったため、 ArrayAccess を直接実装しています。 次に、この基底クラスを継承してカテゴリのDTOを作ります。readonlyクラスを継承する子クラスもreadonlyにします。 final readonly class CategoryDto extends AbstractDeprecatedKeyProbe { public function __construct ( public int $id, public string $name, public int $count, ) { parent:: __construct ([ 'id' = > $id, 'name' = > $name, 'count' = > $count, ]); } } コンストラクタでは、通常のDTOと同じように、項目ごとに型を指定して受け取ります。引数をpublicプロパティとして宣言しているので、 $dto->name と読めます。そのうえで、同じ値を parent::__construct() に配列として渡し、既存の $dto['name'] という読み方にも対応させています。 $dto = new CategoryDto( id: 12, name: 'トップス', count: 34, ); echo $dto- > name; // トップス echo $dto['name']; // トップス Twigへ渡す場合は、 category の値をこのDTOに置き換えます。 echo $twig- > render('category.twig', [ 'category' = > new CategoryDto( id: 12, name: 'トップス', count: $this- > getCountCategory(id: 12), ), ]); Twig側の category.name という書き方はそのままです。利用状況を調べる段階なので、 count を取得するAPIリクエストもまだ残しています。 廃止候補のキーへのアクセスを検知する 続いて、安全にキーを削除するために、廃止候補のキーが読まれたら警告を出すようにします。対象のキーはDTOごとに異なるので、基底クラスに次の抽象メソッドを追加します。 abstract protected function deprecatedKeys(): array; CategoryDto では、このメソッドを実装して廃止候補を列挙します。 protected function deprecatedKeys(): array { return ['count']; } 次に、基底クラスの offsetGet() と offsetExists() を変更し、共通の警告処理を呼びます。 final public function offsetExists(mixed $offset): bool { $this- > warnIfDeprecated($offset); return isset ($this- > values[$offset]); } final public function offsetGet(mixed $offset): mixed { $this- > warnIfDeprecated($offset); return $this- > values[$offset] ?? null; } これらのメソッドを final にしているのは、子クラスで上書きした際に警告処理が抜け落ちるのを防ぐためです。廃止候補は各DTOで宣言し、観測方法は共通に保ちます。 基底クラスに追加する warnIfDeprecated() は、次のようになります。 private function warnIfDeprecated(mixed $offset): void { static $warnedKeys = []; static $deprecatedKeyMaps = []; $deprecatedKeys = $deprecatedKeyMaps[static::class] ??= \array_fill_keys($this- > deprecatedKeys(), true); if (! isset ($deprecatedKeys[$offset])) { return; } $warnedKey = static::class . '::' . $offset; if ( isset ($warnedKeys[$warnedKey])) { return; } $warnedKeys[$warnedKey] = true; \ trigger_error ( \sprintf( '%s: 廃止検討中のキー "%s" が参照された。まだ利用者がいるため削除できない。', static::class, $offset, ), E_USER_WARNING, ); } E_USER_WARNING は、PHPのコードから発生させる非致命的な警告です。標準の動作では処理を停止しないので、警告を出した後も元の値を返せます。 trigger_error() でこの警告を出し、既存のエラーハンドラを通じて外部の監視ツールへ送ることで、「このキーにはまだ利用者がいるので、削除を保留する必要がある」と判断する材料にします。 *1 ただし、警告を例外に変換するエラーハンドラでは処理が止まります。導入時には、表示を維持できることと、警告が監視ツールへ届くことを確認します。 カテゴリ一覧をループすると、同じキーへのアクセスが何度も発生します。知りたいのは利用の有無なので、警告は「クラス名とキー名」の組み合わせごとに一度だけにしています。 抑制状態はインスタンス間で共有し、通常はリクエストごとにリセットされます(常駐ワーカーでは持続)。廃止候補の一覧もクラスごとにキャッシュするため、 deprecatedKeys() は同じクラスのどのインスタンスでも同じ配列を返すようにします。 readonlyクラスではstaticプロパティを宣言できないため、警告済みの情報はメソッド内のstatic変数に保持します。これにより、DTOのデータを変更せずに、呼び出しをまたいで警告の重複を抑制できます。 なお、フレームワークなどが用意するヘルパー関数で配列を扱っている場合は、置き換える際に確認が必要です。引数が array 型ならDTOは渡せませんし、内部で配列へキャストする場合は、このアクセスの観測を通りません。 Twigはpublicプロパティより先に ArrayAccess 経由で値を読むため、このDTOの category.count も offsetExists() と offsetGet() を通ります。これで、テンプレートの category.count が参照されたら警告が出るようになります。 *2 廃止できると分かったら、普通のDTOへ戻す 必要な利用経路を確認し、 count を廃止できると判断したら、プロパティと対応する配列キー、その値を取得するAPIリクエストを削除します。コンストラクタの呼び出しからも count の引数を外します。 PHPの $dto['name'] やTwigの category['name'] が残っている場合は、PHPの $dto->name やTwigの category.name に変更します。読み方を揃えたら、 AbstractDeprecatedKeyProbe の継承と parent::__construct() も外せます。最後に残るのは、次のような普通のDTOです。 final readonly class CategoryDto { public function __construct ( public int $id, public string $name, ) { } } Twigへは引き続き category として渡せるので、 {{ category.name }} はそのまま使えます。観測のために用意したDTOを捨てる必要はなく、必要な項目だけを持つPOPOなDTOとして使い続けられます。 おわりに 今回は、 ArrayAccess と trigger_error() を使い、既存の動作を保ちながら廃止候補のキーへのアクセスを観測する方法を紹介しました。 観測の仕組みを外したあとには、必要な項目だけを持つPOPOなDTOが残ります。不要な処理を減らせて、データの受け渡しも型付きのDTOに移行できる。この両方を進められるのが、今回の仕組みのうれしいところです。 突然ですが私は、デッドコードを削除したり、無駄な処理を消してパフォーマンスを改善したりするのが大好物です。AIによってコードをどんどん増やせるようになった今だからこそ、安全にコードを消せる仕組みを用意して、積極的に不要なコードを消していくことが大事だと思っています! こうした日々の改善を通じて、サービスを支えるコードを一緒に育てていきたい方は、ぜひ採用情報もご覧ください! binc.jp *1 : この方法は、 NewRelicと戦うレガシーPHP を参考にしました。 *2 : この実装では、Drupalの DeprecatedArray も参考にしました。ArrayObjectを継承し、アクセス時にE_USER_DEPRECATEDを出しながら元の処理を続ける、近い発想の実装です。Drupalでは配列全体を非推奨として扱いますが、今回は廃止候補のキーに絞って観測しています。
はじめに こんにちは。BASE Departmentでバックエンドエンジニアをしている白数( かじゅ )です。 私たちのチームでは今年の夏、カートの表示速度を改善するプロジェクトに取り組みました。 ここでいう「速度」は、サーバーの処理時間だけを指すものではなく、購入者がボタンを押してから次の画面が表示されるまで、実際に待っている時間を指します。 計測を始めた時点で特に表示に時間がかかっていたのは、PAY IDアプリで「購入手続きに進む」を押してから購入確認画面が表示されるまでの経路でした。 一度に大きく作り替えず、小さく変えては数字を見る、を繰り返しフロントエンドとバックエンド合わせてマージ(リリース)したプルリクエストは約140件にもなりました。 その積み重ねの結果、最も効果が大きかったWebでカートを開く操作では、ボタンを押してからカートが表示されるまでの待ち時間がプロジェクト開始前の半分以下になりました。 この記事では、表示速度を改善するまでの過程と実施した対応の一部をご紹介します。 「ボタンを押してから画面が出るまで」を一つの指標にした BASEのカートでは、商品ページの「カートに入れる」や、アプリの「購入手続きに進む」を押すと、次の画面に遷移する前に中継用のページに入ります。 このページの役割は、カートの状態を確認し、カート編集画面と購入確認画面のどちらへ進むかを決めることです。購入者に見えるのはローディング表示だけなので、以下では「振り分けページ」と呼びます。 購入者は振り分けページの読み込み、APIの処理、次画面の読み込みが終わるまで待つことになります。 複数のページをまたぐためAPIの応答時間だけを見ても、この待ち時間は分かりません。 これまでAPIのレイテンシは日々計測している状況でしたが、画面表示までの時間は計測できていませんでした。 まずは振り分けページのナビゲーション開始から、次画面の表示が完了するまでを一つのカスタムイベントにまとめ、ブラウザからNew Relicへ送るようにしました。 この値をプロジェクトの主要指標としたうえで関連データを含む形でダッシュボード化しp50(中央値)とp90を中心に日々の推移を手軽に確認できるようにしました。 詳細はお見せできませんが作成したダッシュボード 内訳を見るとページ全体を2回読み込んでいた 処理の内訳は振り分けページ、バックエンド、購入確認画面の3区間に分かれます。 処理の内訳 区間ごとに計測すると、p50ではバックエンドのAPIが全体に占める割合は非常に小さく、待ち時間の大半は振り分けページと購入確認画面という、2回のページ全体の読み込みでした。 不要なJavaScriptを読まないようにした 改修前の振り分けページは、ローディングを表示するだけにもかかわらず、13本のJavaScriptファイルを読み込んでいました。 中身はReactや状態管理ライブラリ(XState)などで、振り分けの処理はこれらの読み込みと初期化が終わるまで始められませんでした。 そこで、振り分けの処理を始めるまでに読み込むJavaScriptを減らすことを方針にしました。 先に、改修前後を図に示します。 改修前後 まずはフロントエンドの軽量化を行いました。主な対応は以下です。 サポートブラウザの見直しにより不要になった古いpolyfillを削除 OpenAPI定義から自動生成していたAPIクライアントを最小実装へ見直し 実際には使用していない処理まで含まれており、ファイルサイズが大きくなっていたため自動生成された型定義のみを利用し必要最小限の実装へ変更 次に「カートに入れる」などのアクションをサーバー側でも実行できる構成へ変更しました。 これまでXStateやReact hooksに依存していた処理を、Reactに依存しない素の関数へ置き換えたうえで、BFF(Backend for Frontend)であるNext.jsのAPIルートへ移しています。 これらの対応により、 ブラウザからバックエンドAPIを順番に呼び出す必要がなくなった BFFへの1回のリクエストで処理が完結 ブラウザ側に残る処理は、警告表示・計測・画面遷移のみ という構成になりました。 Reactに依存する処理がなくなったため、Next.jsのページとしてリクエストを受けつつもJSXを使用せずに、ローディング表示と振り分け用のインラインスクリプトのみの必要最小限なHTMLを文字列で組み立てて直接レスポンスすることで、振り分けページのみはReactを使用しない構成に変更しました。 その結果、約353KB(gzip圧縮後)のJavaScriptが全て不要となり、このページで実行するJavaScriptファイルは0本となっています。 ここまでの対応に要したプルリクエストは、polyfillの削除が7件、APIクライアントの見直しが25件、素の関数への置き換えが37件、BFFへの移設が13件、Reactの削除が6件です。 最も件数が多かった素の関数への置き換えは、単体ではプロジェクト開始前の表示時間(p50)の約4%にあたる短縮にとどまりましたが、BFFへの移設とReactの削除を可能にする下準備となりました。 最終的には、プロジェクト開始前の表示時間(p50)に対して「カートに入れる」経路では合わせて約19%、カートを開く経路では約13%にあたる時間を短縮できています。 次の画面で使う情報を先に取得して渡すようにした 購入確認画面で使うカート・購入手続き・決済情報は、振り分けページのSSRで取得しても使われなかったり、購入確認画面のSSRでバックエンドから取り直されたりと、無駄な待ち時間の原因になっていました。 これらの情報はBFFで振り分けの処理を実行する際に並行して取得し、短い有効期限でRedisへ保存する形とすることでバックエンドからの取り直しを不要にしました。 この対応でPAY IDアプリの経路では、プロジェクト開始前の表示時間(p50)の約14%にあたる時間を短縮できています。 また、振り分けページでローディングを表示している間に、次画面で使うJavaScriptを先読みしておくことで遷移後の読み込み時間の短縮にもつながっています。 バックエンドはp90を中心に改善した 上述したとおり、p50では全体に占めるバックエンド処理の割合が小さかったものの、p90ではバックエンド処理の占める割合が大きかったため改善を行いました。 バックエンドの対応に要したプルリクエストは14件です。 1リクエストの中で外部APIを複数回実行しており処理時間の大半は、この外部APIからの応答を待つ時間です。 そこで不要な外部APIの呼び出しを経路から外しました。 次画面では参照せず、表示後に別のAPIで取り直す値を計算していた。同じ処理を別画面向けのAPIと共有し、そちらでは値を使っていたため、不要な経路でだけ計算をスキップした ある判定のために2種類の外部APIを呼んでいたが、判定に必要な情報は片方のAPIのレスポンスにも含まれていたため、1回の呼び出しにまとめた また、ループの中にN+1問題がありました。 バックエンドはレイヤーを分けたモジュラーモノリスの構成で実装されています。 DBアクセスや他機能への問い合わせがループの中に隠れていてコード上では気付きにくく、N+1の逐次問い合わせが発生していたため、事前に一括取得するようにリファクタリングを行いました。 AIを使って調査と改善のサイクルを速めた ここまでに紹介したボトルネックの調査や改善内容の検討にはAIを活用しています。 たとえば、New Relicのログや計測結果を調べる際には、確認したい内容をもとにクエリのたたき台を作らせたり、複数の計測結果から次に確認すべき箇所を整理させたりしました。また、コードを調査する際には、処理の呼び出し関係を追い、不要なAPI呼び出しやループ内の問い合わせが発生している可能性がある箇所を洗い出すために利用しました。 AIの出力をそのまま採用するのではなく、実際のログやコード、計測結果と照らし合わせて確認し、修正後はダッシュボードで数値が改善していることを検証しました。 このように、仮説を立て、コードを調査し、小さく修正して計測するサイクルを繰り返すことで、多数の改善を継続的にリリースできました。 表示時間が階段状に下がった 上記以外の対応も含め、プロジェクト開始前の1週間と全施策完了後の1週間を比べたときの表示時間の変化は次のとおりです。 経路 p50(中央値) p90 PAY IDアプリ: 購入手続きに進む → 次画面 -35% -40% Web: カートに入れる → 次画面 -42% -46% Web: カートを開く → 次画面 -53% -51% p50では最大53%、p90では最大51%の短縮です。 速度改善期間中のPAY IDアプリにおける購入手続きに進む→次画面のp50 約140件のプルリクエストを施策別に見ると、不要なJavaScriptを読まないようにする対応が88件、次の画面で使う情報の先取りが20件、バックエンドの改善が14件、計測の仕組みづくりや本記事で紹介していない改善が20件です。 フロントエンド、バックエンドともに効果が大きかったのはAPIの呼び出し回数を減らす施策でした。 改善の効果をチームで喜んだ ダッシュボードの数字だけでなく、実際に画面を操作して体感でも確かめていました。 速度が改善するたびに盛り上がり、良い雰囲気の中でプロジェクトを進めることができました。 改善の効果があったときの会話 おわりに 過去にも速度改善の対応経験はありましたが、AIの利用によりボトルネックの分析や特定が非常に楽になりました。 断片的に散らばったログを自力でかき集め、分析や対応内容を検討していたとすると、これだけの数のプルリクエストをリリースするのは難しかったと思います。 また、今回はプロジェクトとして対応しましたが、AIによって分析のハードルが大きく下がるので、日常的に分析させることで、処理を効率化できる箇所に早く気付けそうだと感じました。 BASEでは様々な職種で一緒にプロダクトを作り上げていくメンバーを募集しています。興味のある方は、ぜひお気軽に採用情報をご確認ください! binc.jp
PAY ID Departmentで検索・レコメンドエンジニアを担当している津川( @KeiT )です。 2026年9月5日(土)に中野セントラルパークカンファレンスで開催された Product Engineering Conference 2026(PdE Conf) に参加してきました。BASE社はシルバースポンサーとして協賛し、弊社執行役員の柳川が「エンジニアリングは、どこまで拡大解釈できるか 心技体をつないだままで事業責任者になった話」というタイトルで登壇しました。この記事では登壇者のコメントと、現地参加したメンバーの感想をお届けします! 登壇の事前告知記事は こちら です。 Product Engineering Conference 2026 とは product-engineering.jp 「職能の壁を越え、プロダクト価値を最大化させるための『技術』と『知恵』を共有し、議論し、高め合う場」として今年初めて開催されたカンファレンスです。チケットは事前に完売しており、会場も終日にぎわっていました。 タイトルの「プロダクトエンジニアリング」という言葉どおり、自分が聞いたセッションには「職能の境界をどう越えるか」「越えた先で何に責任を持つか」という問いが共通していて、1日を通して同じテーマを角度を変えて考え続ける時間になりました。 登壇者コメント 柳川 慶太( @gimupop ) speakerdeck.com 一言で表すなら、コミュニティの熱量を感じました。 この時代に、言語のカンファレンスとも違う新たなカットで、コミュニティ発信のカンファレンスが生まれるということ。とても素晴らしく、当たり前のことではないと思います。 その熱に応えられるように、思い悩みながら資料を作り練習し当日に臨みました。結果として、このカンファレンスに相応しい登壇内容になったと自負しています。 プロダクトエンジニアという言葉が着目されてる事象を見ると時代は巡ってるなと思います。 プロダクトエンジニアなんて当たり前でしょ、というご意見もわかりますが、当たり前とは言えない状況があればこそ、この言葉が生まれたのだと思います。そんな中でも「やっぱりプロダクトでしょ!」ってなってるのは素直に嬉しいので、個人的にはこれからもコミュニティに参加し後押ししていきたいなと考えています。 登壇のもとになったnoteの記事もあわせてどうぞ。 note.com 現地で見たセッションから得た学び・感想 印象に残ったセッションを3つ紹介します。いずれも自分が聞いて受け取った内容なので、正確な内容は各登壇資料をご覧ください。 より使われるサービスに。行政プロダクトのグロース戦略(岡田 康豊さん / デジタル庁) speakerdeck.com 招待講演は、デジタル庁の岡田さんによる、行政サービスをどうグロースさせるかという話でした。ジョブ理論(Jobs to be Done)を軸にした「ユーザーのJobを完了させようとすると、職能や組織の境界を越えざるを得ない」という指摘が特に印象に残っています。それが官公庁という環境で実際に実現されている実績と相まって、越境は理想論ではなく、組織の制約が大きい環境でも現実に起こせることなのだと実感しました。 「誰に、どんな価値を、どの順番で届けるか」を決めるために、自分自身がそのプロダクトに一番詳しくなる。与益者と受益者を分けて考える。体験ベースで組織を作る。成果への責任を広げていく。行政という文脈で語られていましたが、そのまま事業会社にも当てはまる内容で、非常に学びが多かったです。 KPIだけでは運用できないプロダクトが考えるべき「Evals」という第2の評価系(飯沼 亜紀さん) speakerdeck.com 検索やレコメンドを担当している身として、自分の仕事に一番近い話でした。KPIだけでは測れない「良い仕事とは何か」を決めるのがEvalsで、KPIとEvalsの両方で評価することでKPIハックを防ぐ、という整理でした。 「良い仕事とは何か」を決められない理由として紹介されていた例も具体的で、「状況依存性が高すぎてルールが書けない」「線引きが暗黙知になっていて明文化できない」といったものでした。その暗黙知を形式知へ落としていくフローそのものがEvalsを作る仕事なのだと受け取りました。これは検索やレコメンドの品質評価をどう作るかに、そのまま持ち帰れそうです。 越境負債:称賛されるほど、負債は返済されない(shopetanさん) fortee.jp この日最後に聞いたのは、「越境」そのものを問い直すセッションでした。「〇〇部の責任じゃない」を減らすために境界を引き直す。では、その組織再編は何を解消して、何を最適化しているのか。そんな問いから始まりました。 印象に残ったのは次のような指摘でした。 AIは能力の境界を広げたが、責任の境界は広げてくれない お客様から見たプロダクトと自社から見たプロダクトは異なる。お客様の世界に境界は存在しない 越境行為そのものがお客様に付加価値を与えているわけではない。組織の問題を越境者に押し付けているとも言える 本来解決すべき境界のズレが、越境によって解消されないまま放置されている 「なぜ越境が必要なのか、何がズレているのか、本来どうなっているべきなのか」という締めの問いは、越境がテーマの1日の最後に置かれたからこそ深く考えさせられました。 ワークショップに参加しました セッションの後には「プロダクトエンジニアリングの行動特性をみんなで探ろう」というテーマのワークショップがありました。グループに分かれて、まずブレストで具体的な行動をひたすら出して発散し、そこから共通する行動特性へ抽象化して収束させる、という流れでした。 同じテーマに関心のある人が集まっているだけあって、どのテーブルも議論が白熱していました。カンファレンスで他の参加者とここまで意見を交わす機会はなかなかないので、1日かけてセッションを聞いたあとに自分の考えを言葉にして出す、という時間そのものがとても刺激になりました。 参加メンバーのコメント 植田 いくつかのセッションを聴講させていただきましたが、共通するテーマは「AIを活用した越境の壁と、その乗り越え方」だったように感じています。 AIによって、職種・職能の垣根を越えるきっかけは各社に生まれています。一方で、実際に越境しようとすると、熱量や意識だけでは乗り越えられないハードルがいくつも存在します。そのため、越境を個人の頑張りに委ねるのではなく、それを後押しするための設計や仕組み化が今後の重要なテーマになる――そうした示唆をいただける登壇が多くありました。 弊チームにとっても「越境」は大きなテーマの一つです。ただのスローガンで終わらせることなく、実際の行動につながる設計と仕組み化を進めていきたいと、改めて思わされる一日でした。 運営スタッフ、登壇者の皆様、有意義なカンファレンスを企画・開催いただき、ありがとうございました。 かがの( @ykagano ) 初開催のカンファレンスでしたが、どのセッションも熱量が高く、学びの多い1日でした。 特に越境することで生まれる価値について考えさせられるセッションが多くありました。 スポンサーブースも全て回らせていただき、スタンプラリーを制覇することができました! 各社のロゴを模した手書きのスタンプで、集めるのも楽しかったです! ワークショップでは、同じテーブルの皆さまから過去の大変だった経験を伺えたことがとても刺激になりました! 懇親会には、おいしーいごはんがてーんこもりに用意されていて、参加者同士で交流を深めながら、楽しい時間を過ごせました! 運営スタッフのみなさま、登壇者のみなさま、貴重な機会をありがとうございました! おわりに 「職能の壁を越える」というテーマを掲げたカンファレンスでしたが、実際に聞いてみると、越境を称えるだけではなく、越境のコストや、そもそもなぜ越境が必要になるのかまで踏み込んだセッションが多く、想像以上に骨太な1日でした。 自分自身、職種を問わず誰もが向き合う問いについて考えるきっかけをもらえました。ここで持ち帰ったことは、これからの仕事の中で少しずつ形にしていければと思います。 初開催とは思えない充実した内容で、来年もぜひ参加したいです。運営・登壇者のみなさま、素晴らしい1日をありがとうございました! BASE社では現在エンジニアを積極採用中です。興味があれば採用情報もぜひご覧ください! binc.jp
はじめに ネットショップ作成サービス「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