TECH PLAY

BASE株式会社

BASE株式会社 の技術ブログ

581

はじめに こんにちは、Checkout Reliabilityチームでバックエンドエンジニアをしているかがの( @ykagano )です! Checkout Reliabilityチームはカートの信頼性を向上させるためのチームです。 今回、BASEのカート機能を安定的に提供するために、継続的な負荷テスト環境を構築しましたので第1回として、本記事では全体像を紹介します。 全3回の記事を予定していますので、よろしくお願いします。 BASEの負荷テスト BASEではこれまで負荷テストは必要に応じて都度実施していました。 k6 というオープンソースソフトウェア(以降OSS)の負荷テストツールを使って、BASEのカートへの商品追加から購入完了までのシナリオの負荷テストを行い、負荷が高まっても機能的な問題がないことを確認していました。 しかし、この都度実施の負荷テストには以下の課題がありました。 負荷テストの課題 1. 以前作成したテストシナリオがシステムの仕様変更で動かない状態になっている k6のテストシナリオはJavaScriptで書けるのですが、例えば購入可能な状態にするために必要なAPIやパラメータが一つ増えていたりすると修正が必要になります。 2. 外部APIの仕様変更で、外部APIに見立てたモックのコード修正が必要になっている 商品を購入する際には、BASEから社外の決済APIを呼び出しているのですが、開発環境とはいえ、社外の決済APIに負荷をかけるわけにはいかないため、決済APIの応答に見立てたモックを用意しています。そのため、この社外APIの仕様が変更になるとモックの方もコード修正が必要となります。 3. 新機能を作成した後、改修の度に負荷テストを行っておらず、現在のキャパシティ(負荷に対する許容量)が分からない 当時行った負荷テストの記録は残っているのですが、仕様変更があった今も同じキャパシティを維持できているのかは負荷テストをしてみないと分かりません。しかし、再度負荷テストをするためには、前述のハードルがあります。 これらの課題を解決して、継続的に負荷テストを行えるようにしよう、というのが今回の取り組みです。 負荷テスト環境の要件 継続的な負荷テスト環境として求めた要件は以下となります。 1. 負荷テストを自動で定期実行し、結果をレポートできる 定期的に負荷テストを行うことで、仕様変更によるエラー等を早期に検知できることを求めました。 2. 負荷生成ツールもモックもスケールアウトできる k6のようなシステムに負荷を与えるためのツールを負荷生成ツールと呼びます。この負荷生成ツールとモックの両方を今後システム規模が大きくなっても追従して高負荷を与えられるように、負荷生成ツールやモックの台数を増やす機能を求めました。 3. モックがノーコードで更新できる これまで使っていたモックはPHPによる独自実装をしていました。これだと仕様が変わるとコードの変更が必要です。今回はOSSのモックツールを使用することで、コードの変更をせずにモックの仕様を更新できる機能を求めました。 ベストプラクティスの調査 これらの要件に加え、負荷テスト環境としてのベストプラクティスを調査したところ、下記の記事を見つけました。 引用: 負荷テストの全体像を理解しよう|AWS(第1回/全3回) ベストプラクティスの部分を抜粋します。 実際のワークロードを使用して負荷テストを行い、自身のワークロードが本番環境でどう動作するのかを確認すること 本番環境同等のサイズの環境を利用して試験を行うこと。また、本番環境同等の量・バリエーションのデータを、本番環境データを匿名化したり模擬的に作成したりして用意すること ユーザーの実際の行動をリプレイ、もしくはプログラム的に再現することで、大規模にアーキテクチャ全体に対して負荷をかけること デリバリーパイプラインの一環として負荷テストを自動的に実行し、事前に定義した性能目標と比較して評価し、要求性能を満たせるかどうかを継続的に保証すること ベストプラクティスに書かれていることは、私が負荷テスト環境に求めた要件とほとんど同じですが、負荷テストの考え方が間違っていない裏付けとして非常に参考になりました。 負荷テストツールの選定 こうした考え方を元に、継続的な負荷テスト環境の要件を満たすための負荷テストツールを選定しました。 負荷生成ツールには Locust を選定しています。 また外部APIのリクエストを受けるために WireMock を選定しました。 それぞれの詳細については第2回以降の記事で説明したいと思います。 負荷テスト環境の構成 最終的に負荷テスト環境の構成は下図となっています。 左が負荷生成ツールとモックツールを使用する負荷テストツール。 右が既存のBASEシステムと同等の負荷検証システム(簡略)となります。 負荷テスト環境のシステム構成図 実際のリクエストと同様にインターネットを跨いでリクエストを処理するようにしています。 ここからは負荷テストの構築にあたり進めた内容についてお話しします。 キャパシティプランニング 負荷テスト環境を構築するために、キャパシティプランニングを行いました。 まず以下の2点を確認しました。 BASEのGMV(流通取引総額)がこの先どれぐらい増加する想定なのか 現時点での本番環境での最大負荷はいくつなのか 1に関しては事業計画を参照すれば分かりますが、2に関しては、どこの負荷を基準とするかを検討しました。 BASEのカート機能には注文APIがありますので、そのAPIに対する負荷を基準にすることにしました。 またBASEではこれまでrpm(requests per minute)を基準にしていましたが、今回は高負荷状態を基準にするため、rps(requests per second)を基準にすることにしました。 ここでは注文APIの過去の最大負荷を仮に1,000rpsとします。 また事業計画として2年後のGMVが仮に1.5倍になるとします。 そうすると、2年後にはrpsも1.5倍の1,500rpsが最大負荷として想定されます。 しかし、あくまで成り行きで増える見積もりとなるため、上ブレすることも考えて多めに見積もりを行いました。 増加分の500rpsを2倍にして上振れ含め1,000rpsになるため、 2,000rpsを目標値 とします。 このような考え方で、今回構築するシステムの負荷テスト可能なキャパシティとして設定しました。 負荷テスト環境の構築 キャパシティプランニングでは、注文APIの負荷を仮に2,000rpsと設定しましたが、2,000rpsはあくまで注文API単体の負荷となります。 今回購入のワークロードを対象としているため、そのワークロードでは20を超えるAPIを利用しており、全体としては数万rpsの負荷が想定されました。 こうした要素を加味して、負荷テスト環境のシステム要件を決めていきました。 Gold環境 本番と同等の負荷検証システムは、既に以前から使っているGoldという名前の環境があったため、それを今回も利用することにしました。 しかしこのGold環境は、本番と同等のDBを使用するため、本番相当のデータ量の準備に数時間かかるという課題がありました。 負荷テスト環境は最終的に負荷テストの定期実行までを見据えていました。 負荷テスト環境の起動後、負荷テストを実行し、負荷テスト環境の破棄まで行う想定です。 しかし、定期実行が準備だけで数時間かかるとなると、コンテンツのデリバリー途中でタイムアウトする可能性もあります(実際にこのデータ準備の処理が何度か止まっていることがありました)。 再実行が頻繁に必要になってくると定期実行に支障が出ます。 DBが巨大すぎるため、Gold環境での定期実行は難しいことが分かってきました。 巨大なDBは存在するだけで多大なコストがかかるため、定期的に数時間利用するだけでも多大なコストがかかります。 定期実行ではこの課題を解決する必要がありました。 Bronze環境 本番同等の環境で負荷テストを行うというベストプラクティスには反するのですが、Gold環境での定期実行の課題を解決するため、負荷テストを自動で定期実行し、結果をレポートするための環境として本番よりスペックの低い縮小環境も今回用意することにしました。 この縮小環境には Bronze と名付けました。 命名理由としてはすでにSilver環境を別用途で使っていたためです。 このBronze環境があれば、テストシナリオの修正と確認もBronze環境で容易に行えます。 最終確認としての負荷テストはあくまでGold環境を使用しますが、定常的に必要最小限のキャパシティを担保するには、Bronze環境でも十分要件を満たせると考えました。 Bronze環境では開発環境のデータベースをそのまま流用することにしました。システムの仕様としては、本番環境の構成を縮小した形で仕様を決めていきました。 Gold環境とBronze環境の比較 項目 Gold環境 Bronze環境 目的 本番想定の最終的な負荷検証 継続的な負荷テスト・定点観測 スペック 本番同等 本番より縮小 データ量 本番同等(大規模) 開発環境のデータを流用 起動時間 数時間(データ準備に時間がかかる) 比較的短時間 コスト 高い 低い 実行頻度 必要に応じて実施 定期実行(週次) 主な用途 最終確認・性能限界の検証 早期検知・継続的な品質担保 メリット 本番に近い精度の検証が可能 手軽に何度でも実行できる デメリット コスト・準備時間が大きい 本番との差異がある Bronze環境で継続的に異常検知を行い、最終的な性能検証はGold環境で行う、という役割分担にしています。 チームでの構築 今回、チームで負荷テスト環境の構築を進めていきました。 草案は私の方で作成しましたが、設計は全員で行いました。 その後、Bronze環境の構築、AWSインフラとLocust環境の構築、WireMock環境の構築と3つに分かれて構築を行いました。 私は主にBronze環境の構築を行ったのですが、BASEのテスト環境を1セット作成する作業のため、BASEのシステムに対する解像度を上げることができ、非常に良い経験を得られました。 Bronze環境での継続的負荷テスト 毎週月曜に以下のスケジュールで負荷テストを自動実行しています。 時刻 内容 AM3:00 アプリケーションの自動デプロイ AM7:00 負荷テストの自動実行 負荷テストの自動実行はGitHub Actionsのワークフローで行っています。 以下のジョブが40分程度で実行されます。 負荷テスト開始の通知 Bronze環境の起動 負荷テストツールの起動 負荷テストの実行(メインシナリオ1) 負荷テスト結果の通知 負荷テストの実行(メインシナリオ2) 負荷テスト結果の通知 負荷テストツールの停止 Bronze環境の停止 負荷テスト完了の通知 負荷テスト結果はSlackに投稿されます。 以下はレポート内容の例ですが、注文API(post_orders)に対するrpsとAPI全体(aggregated)のrpsを見れるようにしています。 post_orders: requests=2000 failures=0 avg_time=20ms avg_rps=8.0, aggregated: requests=80000 failures=400 avg_time=500ms avg_rps=300.0 これで毎週最新のアプリケーションでキャパシティの定点観測ができる状態になりました。 本番と同等の挙動ではないですが、もしパフォーマンスの低下があれば週次で検知できます。 また今後負荷テストを実施したい場合にも、すぐに使える環境があるため、負荷テスト実施のハードルも下がってくれるのではないかと期待しています。 Gold環境での本番想定の負荷テスト Gold環境での負荷テストは当初パフォーマンスが想定したよりも出ませんでした。 本番と同等の環境ではありますが、本番と仕様や設定が一部異なっていたため、それらを本番と揃える作業が必要でした。 また本番と挙動が異なる部分もありました。 これは外部APIの通信時間を細かく本番と合わせたり、外部APIが本来持っているRate Limit(一定時間内にリクエスト回数の上限を設ける機能)の機能を再現するための仕組みを入れることで徐々に解決していきました。 負荷テストは実行してパフォーマンスが出ない場合にどこが原因か調査しボトルネックを見つけることも大事ですが、パフォーマンスをもっと出すためにはどうすればいいかは、仮説を立てる必要があります。 そのため、負荷テストの実施前に負荷改善の仮説DBをNotionに作成し、この辺りを改善すればより早くなるのではないか、といった仮説をチーム内で思い付いたものがあれば、確度が低くても良いのでどんどんDBに入れていくことにしました。 今後はこの負荷テストの結果と仮説を元に、BASEのカートの安定性をより高めるための施策を実施していく予定です。 おわりに 今回の記事では、継続的な負荷テスト環境の全体像と設計方針について紹介しました。 第2回では負荷生成ツールの構築と運用について、第3回ではモックツールの構築と運用についてお話しする予定です。 順次公開しますので、ご期待いただければと思います。 このようなシステム環境で働きたいエンジニアの方はぜひ採用情報をご覧ください! binc.jp
はじめに こんにちは!Pay ID Engineering Sectionの岡部( @rerenote )です。 今回はBASEが竹スポンサーとして協賛しているカンファレンス、 PHPカンファレンス小田原2026 のご紹介となります。 phpcon-odawara.jp PHPカンファレンス小田原2026 概要 PHPカンファレンス小田原は「小田原の地でつながる、気張らないカンファレンス」をスローガンに、参加したエンジニアが新たな知識を共有し合い、互いに学び、成長できる場所をつくれるイベントです。 2026/4/11(土)、おだわら市民交流センター「UMECO」で開催されます。 昨年の模様はこちらの記事をご覧ください。 devblog.thebase.in 登壇メンバーのご紹介 BASEで活躍しているメンバーのうち、2名が登壇予定です。ぜひ聞きにいらしてください。 レガシーPHP転生 〜父がドメインエキスパートだったのでDDD+Claude Codeでチート開発します〜 プログラミングをするパンダ ( @Panda_Program ) 会場:かま 14:45〜 fortee.jp スポンサーLT 川口将貴( @dmnlk ) 会場:かま 15:35〜 ブースのご紹介 当日はBASE株式会社としてスポンサーブースの出展も行います。 今年は「UMECO周辺のBASEショップを巡ろう! 小田原ショップスタンプラリー」と題した企画を実施予定です。 BASEをご利用のショップオーナーの中には、ネットショップだけでなく、並行して実店舗を運営されている方もいらっしゃいます。会場であるUMECO周辺にも実店舗を構えているショップがありますので、カンファレンスで小田原にお越しの際は、ぜひ足を運んでみてください。 BASEのブースでお会いできることを楽しみにしております。 おわりに PHPカンファレンス小田原 2026 のチケットはこの記事掲載時点ですでに完売しているそうです。後日こちらのブログでも参加レポートをお届けする予定です。楽しみにお待ちください。 PHPカンファレンス小田原 2026で、みなさまにお会いできることを楽しみにしております! 発表を見てBASEで働くことに興味を持った方は、ぜひ採用情報もご覧ください。 binc.jp
はじめに Architecture Design Grp で エンジニア をしている大塚です。 New Relic Advance: Tokyoというイベントに参加してきました。 New Relicのこれからについて、さまざまな発表がありましたので、簡単にまとめさせていただきました。 今回はCEOなどの登壇もあり、見応えのあるイベントでした! TL;DR New Relicが日本リージョン(国内データセンター)を追加予定で、データ保管要件とレイテンシ面でメリット 生成AI(Analyzer + MCPなど)でアラート調査の初動を短縮し、運用定着を加速する事例が紹介された New Relic Lensで外部DBやDWHなどにクエリし、NRQLで既存データと結合して分析できる イベント概要 日程:2026/03/12 16:00 ~ 18:00 会場:八芳園 全体の所感 New RelicのCEOなどが登壇するようなイベントで、今まで参加したイベントとは雰囲気がだいぶ違いました。 生成AI時代なので、AIの活用を加速させる内容が多かったです。 AI for New RelicとしてAIアシスタントやSRE Agentなどの紹介があり、インシデントなどの対応フローの自動化などが大きなトピックでした。 New Relic for AIとしてMCPの紹介や、ほかプラットフォームとの統合についての紹介もありました。 基調講演でもAIとNew Relicのデータを用いてインシデントやアラート対応を効率化させるという話があったので、BASEでも取り込んでいこうと感じました。 セッション / トピック 1) 日本リージョン追加! newrelic.com 日本にNew RelicのDCを設立するというお話です。 セキュリティやプライバシー要件的にNew Relicに保存できなかった(海外リージョンのため)データも日本国内にデータが保存されるようになることで、より幅広いデータを集約することが可能になります。 レイテンシなどのパフォーマンスにも寄与するような大きなトピックでした。 2) 生成AIで加速させるNew RelicのEnabling speakerdeck.com blog.kinto-technologies.com New Relicを組織に浸透させるための取り組みにAIを組み合わせて、活用を加速させるというお話でした。 勉強会やドキュメントなどのコンテンツをいくら揃えても、それぞれのエンジニアにはそれぞれのタスクがあるので、時間を使って活用してもらうのは難しいという課題が挙げられていました。 New Relicを用いて、計装→検知→調査→解消までのサイクルは回せるようになるが、結局は人の目や手で対応をする必要があるので非効率という課題が残ってしまっていました。 その課題に対してNew Relic Analyzerという仕組み(AIによる分析)を導入することで、初期調査の時間が大幅に短縮され、MCPを用いた対話型にすることで日常業務への定着も進んだとのことでした。 BASEではアラートはSentryで調査はNewRelicという構造になっているので、現状を維持しても良いがNew Relicを活用できていない部分も大きいと感じました。 インシデントやアラートの対応サイクルを改めて見直し、AIを導入した新しい仕組みづくりをしてみても良いかもしれない。。。 3) New Relic Lens docs.newrelic.com New Relic外のデータソースに対してクエリを実行することが可能になりました。 クエリ結果に対してNRQLでNRDBのデータとjoinして分析・可視化できるようです。 現状対応しているのは以下 クラウド データ ウェアハウス : Snowflake 、 Redshift 、 ClickHouse リレーショナルデータベース : PostgreSQL 、 MySQL 、 SQL Server ドキュメントデータベース : MongoDB 、 Elasticsearch スプレッドシート : Google Sheets データレイク : Iceberg メトリクスと監視 : Prometheus 、 AWS CloudWatch BASEでの活用の今後の展望 アラート対応やインシデント対応の実態調査 New Relic Analyzer的な機能の実装が可能かどうか New Relic Lensを利用してNew RelicからMySQLのデータをクエリできるようにする(可能か?の調査から) 最後に 生成AIの活用が進む中で、アプリケーション監視についてもこれらの技術を用いる必要性を感じました。 今回のイベントの内容はBASEでも活用できる内容が多かったので、積極的に取り入れることで社内のAI Observabilityを向上させていきたいと思います。 こうした生成AIを活用した取り組みは各社行っていると思いますが、BASEでも積極的に開発やサービスに取り入れています。 binc.jp
はじめに BASE Order Section でWebアプリケーションエンジニア をしている Capi(かぴ) です。 2026/3/20(金)- 3/22(日)の3日間、BASE株式会社もゴールドスポンサーとして協賛した PHPerKaigi 2026 が開催されました。今回はPHPerKaigi 2026に参加したメンバーのコメントや感想をお届けします! PHPerKaigiとは PHPerKaigiは、オープンソースのスクリプト言語 PHP (正式名称 PHP:Hypertext Preprocessor)を使用している方、過去にPHPを使用していた方、これからPHPを使いたいと思っている方、そしてPHPが大好きな方たちが、技術的なノウハウとPHP愛を共有するためのイベントです。コミュニティ貢献活動の一環として、今年もゴールドスポンサーとして協賛しました。 登壇者コメント 川島慧 モジュラモノリスを導入してから4年が経ち、「そろそろ答え合わせができるのでは」という気持ちで今回の発表に臨みました。 「何を解決しようとしているのか分からないままマイクロサービス(orモジュラモノリス)を開始しようとするのはほとんど悪いアイディアだ」という、技術主導は大抵誤りであるという意識を4年前からずっと持って挑んできました。結果的に思いがけないつまづきもありましたが、無事に解決するべきIssueの解像度が上がった4年間でした。 スライド中では「AIに要約させながら読むことを推奨」と書いたのですが、アップロード後に試してみると、意外なことにChatGPTやGeminiでは何度訂正させても全く違う内容が返ってきました。どうやら100枚超のスライドをAIに要約させるのはまだ難しいようです。ClaudeとNotebookLMだとある程度うまくやってくれます。 個人的にはかなり痛手です。これほどの文量を読むエンジニアは今の時代にまずいないので、ほとんどの人に届かない内容になってしまったと思います。スライドを読む際はご自身の目でしっかり読んでいただくか、動画を見るか、ClaudeまたはNotebookLMあたりににお願いするのがおすすめです(個人の感想です)。 speakerdeck.com プログラミングをするパンダ セール開始のバッチのパフォーマンスをチームで改善したという話を共有しました。発表後に「うちでも遅いバッチがある」という話を聞いたり、「しっかり遅いことに気づいて改善に持っていける体制が作られているのが素晴らしい」とお褒めの言葉をいただいたりしました。 特にポストモーテム共有会をやっているということが今回のバッチの改善の起点になったのですが、その取り組みがとても良いと言ってもらうことが多く、ポストモーテム共有会も別のメンバーが始めていたので大変ありがたいなと思っています。 スライドの最後の方でも触れましたが、今回の発表を機にもう一段改善できるところを見直そうと思うので、さらに早くできるのではないかなと思っています。当日発表を聞きに来てくださった方、ありがとうございました。そうでない方もぜひスライドを一度ご覧ください。皆様のお役に立てれば幸いです。 speakerdeck.com 02 PHP8.5に追加されたarray_first / array_lastの歴史的背景について話しました。5分という短い時間で7年分の議論の流れを追いましたが、重要な話はできるだけ削らずに伝えられたと思います。 さまざまな観点から議論を重ねて未解決の疑問をなくす必要がある一方で、発散した議論はスコープを絞ったり、特定の観点を重視して仕様を決めたり・・・仕事の進め方やファシリテーション、マネジメントなど、普段のプロダクト開発で重要な力が、RFCの採択においても重要だという所感を感じました。 PHP Internalsの議論の流れを追う中で学びが多く、今回のarray_first / array_last以外の議論も追ってみたいと思いました。興味を持った皆さんも、まずは参考文献のURLからarray_first / array_lastの議論を追ってみてください! speakerdeck.com パンフレット記事執筆者コメント meihei 「カンファレンスが終わったあとに」というパンフレットを寄稿しました。先日のPHPerKaigiでは多くの学びがあったかと思います。それらを自分の中で、またチームメンバーとともに知識として再構築し、今後さまざまな機会で活かせる武器にしてもらえたら嬉しいです。 パンフレットはマンガで解説していますので、お気軽にお読みください! 現地で見たセッションを一部紹介 当日イベントに参加した自分含む弊社メンバーが現地で見たセッションのうち特に気になったセッションのレポートです! 1. 「接続」—パフォーマンスチューニングの最後の一手 〜点と点を結ぶ、その一瞬のために〜 by 武田 憲太郎さん 高トラフィックを捌く技術的な解決を自信持って行うためにできることを紹介していただきました。アプリケーションコードよりもミドルウェアの話が中心でした。 Keep Alive、Persistent Connection、コネクションプール… 単語は知っているけれど自分が使いこなせていないものがたくさん出てきて非常に勉強になりました。スライドには図やグラフ、コードが添付されていたので難しい内容でも理解しやすかったです。興味のある方はぜひスライドだけでもみていただきたいです。オススメです。 自分は登壇の中で武田さんがおっしゃっていた「計測は仮説を持って行う」という言葉が印象に残っています。また、登壇を聞きながらシステムの接続はとても奥が深く技術者の腕の見せ所なのではないかと考えました。 (BASE Order Section @Capi) 2. プログラミング言語論から覗くPHPの正体 by うさみけんたさん 「PHPがどんな歴史を積み重ね、今の書き方になったのか」を紐解いていきました。PHPがゆるふわ言語というのは自分もなんとなく理解していますが、想像以上にゆるふわ言語であることを知り、PHPらしさを再度認識する良い機会になりました。 また、登壇の中ではPHP作者Rasmus Lerdorf(ラスマス・ラードフ)氏の発言を取り上げていました。言語作者の人となりを知るのも面白いなと登壇を聞きながら考えました。なぜ言語が作られたのか、どんな思想で言語が成長してきたのかを知ることでその言語への愛着が強まります。 登壇で出てきたこのスライドが自分は好きです笑 (BASE Order Section @Capi) 3. 俺の/私の最強アーキテクチャ決定戦開催 ― チームで新しいアーキテクチャに適合していくために by 髙橋直規さん 持続可能なチーム開発を目指す中で、「アーキテクチャを刷新する必要があるが、トップダウンで決めるのは避けたい」という背景から、「最強アーキテクチャ決定戦」を開催したという内容のライトニングトークでした。 2人程度で似たようなイベントを開催したことはありましたが、経験値に差があるチームで実施したことはなかったため「すべての処理を神メソッドで実現する」という提案のハードルを下げるための工夫や、イベントの流れなど開発チームの運営において大変参考になる内容が多かったです。(Pay ID Engineering Section 岡部 @rerenote) 4.AI時代の脳疲弊と向き合う ~言語学としてのPHP~ by さくらいさん 日々の業務の中で、何かしらAIとやりとりする機会が増えています。その中で、抽象的な内容を具体的な表現に落とし込む場面が以前より多くなっていると感じていました。また、AIと長時間やりとりした後に強い疲労感を覚えることも増えてきています。 このライトニングトークでは、こうした疲れがどこから生じるのか、そしてその対策について解説されていました。対策の中でも「意識して時間を使い分ける」はすぐに実践できそうだと感じ、早速取り入れています。以前と比べて、疲労感を覚える場面が少なくなってきたように感じています。(Pay ID Engineering Section 岡部 @rerenote) おわりに 去年に引き続きレポーターとしてPHPerKaigiに参加させていただきました。 今年も協賛活動、社員のスピーカー参加を通して PHPコミュニティの盛り上がりに貢献でき、弊社としても大変有意義な時間となりました。 スタッフの方々には業務でお忙しいにも関わらず、多くの時間をイベント準備へ注いでいただいたかと思います。この場を借りて御礼申し上げます。
はじめに こんにちは!Pay ID Engineering Sectionの岡部( @rerenote )です。 今回はBASEがゴールドスポンサーとして協賛しているカンファレンス、 PHPerKaigi 2026 のご紹介となります。 phperkaigi.jp PHPerKaigi 2026 概要 PHPerKaigi(ペチパーカイギ)は、PHPer、つまり、現在PHPを使用している方、過去にPHPを使用していた方、これからPHPを使いたいと思っている方、そしてPHPが大好きな方たちが、技術的なノウハウとPHP愛を共有するためのイベントです。 PHPerKaigi 2026 公式サイト 2026/3/20(金)- 3/22(日)の3日間、中野セントラルパークカンファレンス&ニコニコ生放送で、オフライン及びオンラインのハイブリッド開催となります。チケットは下記よりお買い求めいただけます。 チケットショップ | PHPerKaigi 2026 #phperkaigi - fortee.jp 昨年の模様はこちらの記事をご覧ください。 devblog.thebase.in 登壇メンバーのご紹介 BASEで活躍しているメンバーのうち、3名が登壇、1名がパンフレット記事掲載予定です。ぜひ聞きにいらしてください&パンフレットもぜひご覧ください。 モジュラモノリス導入から4年間の総括:アーキテクチャと組織の相互作用について 川島慧( @nazonohito51 ) 3/21(土)Track C 15:35 登壇者メッセージ モジュラモノリスを題材にしていますが、主題は組織とアーキテクチャの力学の分析です。ただの苦労話や事例紹介ではなく、背後にある構造を読み解きます。 fortee.jp 大規模ECサイトのあるバッチのパフォーマンスを改善するために僕たちのチームがしてきたこと プログラミングをするパンダ( @Panda_Program ) 3/22(日)Track C 11:00 fortee.jp なぜarray_firstとarray_lastは採用、array_value_firstとarray_value_lastは見送りだったか 02( @cocoeyes02 ) 3/22(日)Track A 16:05 fortee.jp カンファレンスが終わったあとに meihei( @app1e_s ) パンフレット掲載 fortee.jp PHPerトークン ここで、PHPerKaigiで行われる「PHPerチャレンジ」のPHPトークンをお知らせします。BASEからは3つのPHPerトークンを用意しました。今回はBASEのサービスに関する内容からピックアップしています。 #ネットでお店を開くならベイス♪ thebase.com 誰でもかんたんに使えて、自分だけのネットショップを開設できるネットショップ作成サービス「BASE(ベイス)」のCMで使用されているフレーズです。 はじめての方でもかんたんに自分だけのネットショップがすぐに作成でき、ネットショップに必要な決済手段も、多様な決済方法をスピーディーに導入可能。 #ECのカクシゴト www.youtube.com 「ECのカクシゴト」は「BASE」公式YouTubeチャンネルの1つ。“ひとり開業の裏側”をテーマに、個人やスモールチームでネットショップを運営するショップオーナーの開業ストーリーや人生、日々の暮らしに密着したドキュメンタリー形式の動画を配信しています。 実際にネットショップを運営する個人やスモールチームのオーナーたちに密着し、「なぜECという働き方を選んだのか」「副業から始まり、起業に至るまでの過程」「ものづくりと暮らしのバランス」など、表からは見えないさまざまな価値観や選択肢に光を当てていきます。 毎週金曜日に新しい動画が配信されていますので、ぜひご覧ください。 #好きが、買える。ペイアイディー payid.jp 「BASE」をご利用のショップと購入者をつなぐ、BASE唯一の購入者向けショッピングサービス「Pay ID(ペイ アイディー)」。「Pay IDアプリ」を通じて、ショップは集客・販促をおこない、購入者はお気に入りショップのフォローや商品通知など、スムーズな購入体験を得ることができます。 お気に入りショップでのお買い物をサポートする「ショッピングアプリ」、スムーズでクイックなお支払いを実現する「決済」の2つの領域でサービスを提供しています。 おわりに PHPerKaigi 2026で、みなさまにお会いできることを楽しみにしております! 発表やパンフレットを見てBASEで働くことに興味を持った方は、ぜひ採用情報もご覧ください。 binc.jp
はじめに こんにちは! BASEでバックエンドエンジニアをしている、かがの( @ykagano )です。 昨年まではCartチームにいたのですが、今年から部署名が変わりまして、現在はCheckout Reliabilityチームにいます。 これはBASEのECカートの信頼性を守るためのチームです。 決済の安定性を高めるために日々活動をしていますが、それについてはまた別の記事で書かせてください。 さて、QRコード決済サービスとして日本で一番シェアの大きいPayPayといえば、皆さまもご存知かと思います。 今回は、2026年1月28日にサービスリリースしたこのPayPay導入についてお話しさせていただきます。 BASEはなぜ決済手段を増やすのか まずBASEがなぜ決済手段を増やすのかですが、そもそも決済手段が増えるとどういう良いことがあるのかからお話ししたいと思います。 ECにおいて購入体験の中で最も重要なポイントのひとつが「決済」です。 商品を見つけてカートに入れても、最後の支払いでつまずいてしまうと、その時点で購入は完了しません。 特に近年はクレジットカードだけでなく、QRコード決済や後払いなど、ユーザーごとに「普段使っている支払い方法」が多様化しています。 そのため、ショップ側が対応している決済手段が少ないと、それだけで購入機会を逃してしまう可能性があります。 SBペイメントサービス株式会社による「決済手段のEC利用実態調査結果」でも、「よく利用する決済手段がない場合、男女ともに約6割が購入せず離脱する」という結果になっています。 www.sbpayment.jp ECサイトで物品もしくはデジタルコンテンツ・サービスを支払う際に、よく利用する決済手段がない場合どうするかを尋ねたところ、男女ともに56.5%以上が、そのECサイトでは購入せず離脱する傾向にあることが分かりました。また、そのうち44.8%以上※2の人は他のECサイトもしくは実店舗で購入する意向があることから、購買意欲が高い人でも決済手段の不足が要因で離脱しており、ユーザーのニーズに応じた決済手段を取りそろえることは購買率を上げる重要な要素であると言えます。 つまり、決済手段を増やすことは単に「選択肢を増やす」というだけではなく、ユーザーが安心して購入を完了できる環境を整えることにつながります。 これはショップオーナーにとっては購入率の向上につながり、BASEにとってもより多くの取引を支える基盤を強化することになります。 今回PayPayがBASEに導入されたことは、こうした背景からも多くのショップ様にとって望まれていた機能追加だったと考えています。 BASEでのPayPay導入方法 PayPayは「BASEかんたん決済」に追加される形で提供されます。 PayPayを利用するには、この「BASEかんたん決済」の利用申請が必要です。 「BASEかんたん決済」の利用申請をもとに、PayPayで審査が行われます。 その後、可決された場合はPayPayを利用開始いただけます。 なお決済手数料は 4.6% + 40円 です(グロースプランの場合、決済手数料は 3.9% のみ)。 これは既存のAmazon Pay や PayPal決済と同じ決済手数料となります。 その他、詳細については下記ヘルプページをご参照ください。 help.thebase.in BASEでの支払い方法 BASEのカート画面で支払い方法に「PayPay」を選択して、購入完了ボタンを押していただく形となります。 その後、「PayPay」の手続き画面に遷移しますので、「PayPay」アプリで認証・支払いすると、BASE側に自動で戻り、購入が完了します。 PayPayは現在、BASEのWebショップのみが利用可能となっていまして、Pay IDアプリには未対応となります。 またPayPayで支払いが可能な商品は通常商品のみとなっており、デジタルコンテンツや予約販売や抽選販売など、通常商品以外の商品には利用できないようになっていますのでご注意ください(取り扱いについては今後変更になる場合があります)。 その他、詳細については下記ヘルプページをご参照ください。 help.thebase.in PayPay導入の開発について PayPayの導入はService Reliabilityチーム(以降SRチーム)にて開発いただいてます。 私自身はPayPay導入の開発に直接関わっていたわけではないのですが、導入後のカートシステムの運用を行う前提で設計や実装レビューに入らせていただきました。 今回BASEにPayPayが導入されることになり、システムの開発が始まったのが、2025年10月8日になります。この日に開発メンバーでキックオフを行い、設計を始めていきました。 実装にはPayPayのスマートペイメント機能を利用しています。 初回はユーザーの連携が必要ですが、2回目以降は同じ端末からの操作であれば再度連携せずに支払いをすることができます。 詳細はPayPayの公式ドキュメントをご参照ください。 developer.paypay.ne.jp PayPayはクレジットカード決済とは異なり、ユーザー操作後にBASEとは非同期で決済結果が確定するフローを含んでいます。 そのため「決済をいつ確定させるか」「どの状態をもって成功とみなすか」を慎重に設計する必要がありました。 今回は「注文が完了した時には決済も確定している」というBASEの体験を維持するために、PayPayの画面で決済をしたあと、BASEの画面に戻ってきた後にPayPayの決済結果が正常に完了していることを確認してからBASEに注文を作成するようにしました。 BASEでは複数の決済手段を扱っているため、特定の決済に依存しすぎない形での実装が求められます。 PayPayも既存の決済と同じインターフェースに乗せることで、カート・注文・発送といった関連機能への影響を最小限に抑えています。 私がレビューをしている中で特に意識したのは、「PayPay側で一時的な障害が発生しても、注文や決済状態が不整合を起こさないこと」です。 この辺りは特に注意してレビューをさせていただきました。 もし何らかの原因によって決済が失敗した場合も不整合が起こらない仕組みとなっておりますので、安心してご利用いただければと思います。 おわりに 開発期間としては約3ヶ月でリリースすることができたのですが、これほど早くリリースできたのもSRチームの皆さまの頑張りのおかげです。 本当にありがとうございました。 PayPay決済はまだBASEで開始したばかりですが、今後利用数、利用率ともに高くなるものと思いますので、より安定的に運用できるように改善を重ねていきます。 BASEではこうした決済の仕組みにも関わっていけますので、ご興味がありましたら採用情報をぜひご覧ください。 binc.jp
はじめに 先日、New Relic 社主催の New Relic Meet Up for Digital に弊社のメンバー 6 人が参加してきました。 以前も New Relic 社主催の FutureStack Tokyo 2023 でも Game Day がありましたが、今回はこちらに参加していないメンバーでの参加となりました。 前回 BASE で Game Day に参加したブログはこちら! devblog.thebase.in コンテンツ この Meet Up では基調講演、GameDay Lite、懇親会という流れで進行されていました。 AI 時代の「オブザーバビリティ」戦略 AI時代には開発スピードがどんどん上がり、それに伴いコードの大規模化や不確実性などの課題が新たに生まれ、オブザーバビリティの重要性がもっと上がるというお話しでした。 New Relic パスポイントやセッションリプレイなど、まだ使えていない便利な機能を知れたのでどんどん使っていきたいです。 GameDay Lite 3~4人のシャッフルチームで、障害が発生した想定のWebアプリケーションに対して New Relic を使って解決していくゲームを行いました(回答式)。 高得点を叩き出した上位チームには景品が渡されていて、弊社の @takashi.nohara さんのチームが見事 3 位に入賞していました🎉 参加レポート @yunamiyaa Product dev Order Section Architecture Design の宮坂 @yunamiyaaです。 今回のミートアップでは、4人1組のチームでNew Relicを使ったトラブルシューティングに挑戦しました。 私たちのチームは、普段から業務でNew Relicを使い込んでいるメンバー3名と、ちょうど会社で導入が決まったばかりの未経験メンバー1名という構成でした。初心者のメンバーに「調査のコツ」や「画面の見方」を教えながら進めることで、自分たちにとっても基本に立ち返る良い機会になり、改めて「なぜこの手順で調査するのか」を言語化できました。 同じNew Relicというツールを使っていても、他社のエンジニアとディスカッションする中で、エラーを探す手順やダッシュボードの使い方が会社ごとに異なることに驚きました。それぞれの組織の文化やシステム構成に合わせた工夫があり、「うちではこうやっている」という情報交換がとても刺激的でした。 さらに、New Relic社の方から具体的なケースに対する追跡方法を直接教えていただけたことが大きな収穫でした。 「あ、そんな追い方があるんだ!」という発見が多く、普段の業務では気づけなかった機能の活用法や、より効率的な調査パターンを知ることができました。自社の中だけでは得られなかった新しい視点をたくさんもらえた、非常に有意義なミートアップでした。 @Torata BASE ProductDev / Item Section でエンジニアをやっているTorataです。 自分は直近で New Relic に触れる機会があり、今後も New Relic を継続して学んでいく必要があると感じていたタイミングで、今回の Game Day に参加しました。 初めて見る機能や、見たことはあるものの目的がよく分かっていなかった機能に、ゲーム感覚で触れられたのがとても楽しかったです。 また解説では、「どの意図を持って何を見るのか」という観点から、調査を深掘りしていくプロセスまで聞けたのが大きな学びになりました。 賞を勝ち取ることはできませんでしたが、今回学んだことを業務で活用し、賞を狙えるようになるくらい使いこなしていきたいです💪 @meihei BASE の Item Scalability Group でエンジニアをしている江間 @meihei です。 Item Scalability Group では、アプリケーションからインフラストラクチャ・データベースまで横断的に監視してボトルネックを探すため、日頃から New Relic を使っています。チョットワカルつもりで GameDay に参加しましたが、初めて触る機能や UI が結構ありました。 また、チームが3人だったこともあり、横に並んで New Relic を触っていました。はじめましての方もいる中、とても良い形で協力プレーが出来てとても楽しかったです。 最初の講演も GameDay Lite も非常に学びが多かったです。New Relic の設定でまだやっていない事がいくつか見つかったので、ここで得た知識を活用していきたいと思います。 GameDay中のチームWの様子。中央の一人がNew Relicの画面を触りながら、横二人が問題や資料を開き、限られた環境の中で効率化をしていた。 @Otsuka BASEのOrder Secの大塚です。 GameDay形式でのイベントへの参加は初めてでした。 New Relicは普段から触っており正直自信があったのですが、なかなか苦戦してしまいました。 その分普段活かしきれていない機能についての活用方法なども学ぶことができたので、今後に活かしていきたいと思います。 @wakana Product Dev/Checkout Section の @wakana です。 私は普段の業務中から New Relic を使用しており、主にAPM, Service Level, Dashboard, Browser, Logs などの機能を用いて、監視業務や可観測性の向上に取り組んでいます。 今回、New Relic Game Day への参加は初めてでしたが、事前にグループ分けされたほかの参加者の方と4人1組でチームを組み、クイズ形式の課題にNew Relicを活用して答えていく協力型の形式で、とても取り組みやすく感じました。 内容としても全て実践的で充実した内容となっており、参加前から知っていた・使ったことのある機能は4割ほどで、残りは初めて見るページなどが多かったため、とても刺激になりました。 一部、とても魅力的でありながらBASEでまだ導入ができていない機能などもあったため、今後よりオブザーバビリティの民主化を社内で進めていくうえでぜひ導入を進めていきたいと感じました。 @nohara BASEのCheckout Sectionのエンジニア、野原です。普段の業務では New Relic を利用しています。今回は理解を深めるために、New Relic の GameDay に参加しました。 今回が初めての参加だったのですが、想定とは少し違い、他社の方々とランダムに4人チームを組んで課題に挑戦する形式で、とても新鮮でした。特に、初対面のメンバーと短時間でチームワークを築いていく体験が印象に残っています。まず最初に、4人チームの中で誰が回答を記録・提出するかを決め、その後はそれぞれの知見を持ち寄りながら課題に取り組みました。 初めて目にする機能やUIも多くありましたが、メンバー同士で相談しながら進めることで、なんとか回答することができました。最初は順位をあまり意識していなかったのですが、第1ステージで2位だったのは意外でした。第2ステージでは一部、回答が間に合わなかった問題もあったため、最終順位が3位だったのは正直驚きでした。今回学んだ内容は、ぜひ今後の業務にも活かしていきたいと思います。 また、最初の発表もとても勉強になりました。これからはAIの時代ということで、今後はAIの活用も積極的に取り入れていきたいと感じています。業務の中でも、引き続き New Relic を活用していく予定です、これからもどんどん勉強します。 GameDayのチーム賞で見事二位に入賞。左から二人目がBASEメンバーのnohara。 おわりに AI時代でのオブザーバビリティへの向き合い方や、New Relicの機能に関する新たな知識を沢山学べた1日でした。 当日のイベント進行や準備をしていただいたNew Relic社の皆様、ありがとうございました! これからもどんどん New Relic を使っていきたいと思います!
CTOの川口 ( id:dmnlk ) です。 これはBASE Advent Calendar25日目の記事です。 毎年ながら僕は立候補してないのに勝手に日程が組み込まれてました。 BASE社では2027年卒の学生を対象に新卒採用を始めました。 今まで基本的に行っていなかったことです。対象はエンジニア職、デザイナー職、ビジネス職です。 2025年中には就活を終えている大学生が多いということも知り、自分が新卒だった頃と比べててだいぶ進行が早いことに驚いています。 採用面接を行っていく中で必ず聞かれることとして「どうしてこのタイミングで新卒採用を始めたのか?」というものがあります。 特にエンジニア職においてはAIによるコーディングが大きな流れとなっており、インターン経験があれど実務経験が乏しい所謂ジュニアエンジニアをなぜこのタイミングで採用を始めるかというのは当然の疑問だと思いますし学生の方には不安も大きいのかもしれません。 もちろん面接中にそれを同じように答えているのですが来年以降の方も踏まえてここで明文化しておこうと思い記事にしています。 コロナ禍が起きた2020年以降、BASE社ではリモートワークを主としたハイブリッドワークを行ってきました。 ここでリモートワークの是非を言及するつもりはなく各社ごとに合わせた働き方や組織があるかと思います。 自分はBASEにかれこれ8年ほど在籍していますが、最近自社がどういう文化を持った組織かといったことを認識しづらくなったと感じています。 特にこの部分は中途面接でよく聞かれており、今のところ答えられている部分ではあるのですがどうしても文化面といった部分に希薄化を感じています。 もちろん文化が色濃ければ濃いほどいいというものではありませんが、開発組織のトップであるCTOたる自分がその組織について自分の言葉で話せなくなっていることには危機感があります。 中途採用したメンバーも定期的に入社していただいていますが、中途で入ってきたメンバーが文化を作っていくといった力学は働きづらいように思いますしそれを求めすぎるのは何か違うのではないかと思います。 自分が新卒で入った会社は大きなインターネット企業の子会社ですが、そこグループ全体で反対に企業文化が外から見ても中から見ても非常に強い会社です。 斜に構えた新卒であった自分はその空気感といったものに怪訝な気持ちでいたものですが、今思い返すと朱に交われば赤くなるといったように一定その会社のマインドや風土といったものに影響され今の考え方があります。 もちろんその企業もコロナ禍でリモートワークをやっていたようですし、内部では文化は変わったり薄まったりしたのかもしれませんが外から見る限りは未だに強い流れがあると思います。 思い返しその源泉はなんだったのだろうと考えると、もちろん社長や経営陣の強いリーダーシップもありますが新卒を毎年積極的に採用し全社がその採用に協力し入社した新卒を全社で育て早い段階から抜擢し伸ばしていたところにあったのではないかと考えています。 そのような点も踏まえ、BASEの開発組織として新卒を採用しボトムアップで文化醸成を図っていきたいというのが自分の考えとなります。 実務能力という点においてはあまり自分ではジュニアという括りで捉えてはいません。 インターン経験を一旦除いて考えると大規模Webサービスや数十人規模での組織での開発の仕方など経験が不足しているのは当然です。 ですが、それは単純な経験の差というだけであり1~2年もあれば優秀な方であれば容易に習得できうるものですし、事実自分もそうでした。 AIによるバフがあることによりジュニアエンジニアの能力向上のチャンスはより強くなっています。優秀な人物であればそのバフは数倍以上の効果を見せるはずです。 逆にいうとその変化を緩やかにしか利用できないシニアエンジニア達はすぐに追いつかれてしまうかもしれませんし、組織コミットも希薄になっている以上企業や組織としてどちらが重用されるかは明白です。 これからのジュニアエンジニアは、いかに変化に柔軟に適応できるかが重要なのではないかと思います。 受け入れる私たち企業側も、AIを前提とした開発が主流になっていくなかでどのように能力を引き上げていくかはまだ手探りの課題があるでしょう。 基礎となる技術が変わるわけではないのでその部分をしっかり理解したメンバーがきちんと何が重要でどこをAIに移譲していくか考慮した研修などを設計していく必要があります。 かのRuby on Railsの作者DHHもブログエントリで、「常にジュニアの開発者を採用し続ける」と明言しています。 We'll always need junior programmers AIを前提としていく開発業界の中で優秀な開発者の需要は否応なく上がり続けるのは自明であり、AIが開発者の全ての仕事を奪い廃業になっていく未来は見えません。 それはOpenAIやAnthorpicのようなモデルプロバイダーだけでなく、私たちWebサービスの開発でも同様です。 そうした中でBASE社として、ユーザーの経済活動を支え続けるプラットフォームを開発運用して成長していきたい新卒の方に期待を持って今後も新卒採用を続けていく予定です。 もちろん中途採用の方も募集していますので、気になる方はいつでもお声がけください。 採用情報 | BASE, Inc. では、皆様良いお年を。
この記事はBASEアドベントカレンダーの24日目の記事です。 はじめに こんにちは!BASEプロダクト開発チームにて責任者(エンジニアリングマネージャー)をしている 植田 です。アドベントカレンダーも残すところあと2日ですね。 今回は エンジニア組織の「組織デザイン」 をテーマに、BASEの開発組織でこれまで実際に行ってきた組織設計と、その変遷をご紹介します。 組織論やチームトポロジーに関する書籍や記事は数多くありますが、 理論としては理解できるが、自分の組織にどう当てはめればいいかわからない 他社の開発組織が、実際にどう悩み、どう変えてきたのかを知りたい と感じたことがある方も多いのではないでしょうか。 この記事では、「この形が正解です」という話ではなく、なぜその組織にして、どこに課題を感じ、次に何を選んだのかという意思決定の変遷をリアルに書いています。 想定読者 エンジニアリングマネージャー、開発組織の責任者の方 組織設計・組織デザインに興味があるエンジニアの方 前提:BASEの全社組織と、今回扱う範囲 まず前提として、BASEの全社組織の概要を簡単に説明します。 BASEでは事業部制を採用しており、各事業部の中にプロダクト開発・マーケティング・CSなどの機能が配置されています。一方で、SREや情シスなどの全社横断の技術組織は、事業部とは別の技術本部として存在します。こうすることで各事業部の開発組織は事業・プロダクトにおける機能開発や運用といったことに集中しやすい環境が整備されています。 この記事で扱うのは、BASE事業における「Product Dev(プロダクト開発組織)」 の組織設計です。 組織設計の観点とその難しさ 具体的な組織図の話に入る前に、そもそもなぜ組織設計が難しいのかを整理しておきます。 組織設計で考慮すべき論点は非常に多岐にわたります。 事業戦略・プロダクト戦略との整合性 開発組織としてのビジョン、方向性とのアライン 開発PJをパフォーマンス高く推進できるか 開発チームへのPJアサインをスムーズにできるか プロダクトマネージャー、デザイナーとの協業 現状の開発組織の課題を解消できるか 開発組織における各機能をどのように持たせるか 1チームあたりのメンバー人数は何名が適正か 採用と育成との相性 運用体制・フローをどうするか メンバー同士の相性 コードベースと組織境界の関係 専門領域と属人性の落とし所 ざっと挙げただけでもこのような観点が想定されます。1つずつ論点として取り上げ全体の整合性を設計する作業は多くの時間を要します。 しかも厄介なのは、 組織設計は試行回数を簡単に増やせない という点です。組織を変えても、その良し悪しが見えるまでには時間がかかります。頻繁に組織を変えれば現場は疲弊しますし、一方で変えなければ歪みは蓄積していきます。 この前提を踏まえたうえで、ここから実際の組織変遷を紹介します。 フェーズ1:事業戦略ミラーリング型(2022〜2023) 1つ目は「事業戦略ミラーリング型」です。 背景と狙い 当時採用していたのは、 事業戦略と開発組織をミラーリングする構成 でした。BASEは、スモールチーム向けのEC・決済という変化が激しく、事業戦略のスピードが求められる領域です。事業・プロダクトマネージャー・デザイナー・エンジニアが同じ方向を向き、同期的に動けることを重視していました。具体的には”Value Creation Sec”はテイクレート成長に責任を持つ戦略推進、”Core & Capacity Sec”はGMV成長に責任を持つ戦略推進というようにミッションが分かれていました。 得られたメリット 事業戦略の変更に追従しやすい チームごとの守備範囲が明確 特定領域の知見が溜まりやすい 見えてきた課題 一方で、次第に以下のような問題が顕在化しました。 事業戦略の変更に追従するために組織改編が頻発する チームの耐用年数が短く、チームビルディングが進まない サイロ化が進み、隣のチームへの関心が薄れる チームにおいて忙しさの濃淡が生まれる 実際にメンバーからは、「またすぐ組織が変わると思うと、チームを良くしようとするモチベーションが湧かない」という声も上がり、 「強いチームが良いプロダクトを作る」 という前提が揺らぎ始めました。 フェーズ2:Feature Dev型(2024〜2025) 次に選んだのが、 担当領域を固定しない Feature Dev チームを複数置く構成 です。 開発領域を持たない Feature Dev チーム リアーキテクチャを推進する Module Development チーム 狙い 事業戦略変更への耐性を高める PJの割当柔軟性を上げる メンバーの経験領域を広げる モジュラーモノリスへの移行(リアーキテクチャ)に継続投資する 実際どうだったか 一定の成果はありました。 機動力の向上 マンネリ化の軽減 チーム間の負荷は平準化 広くプロダクトを知る機会の創出 しかし2年ほど運用する中で、別の歪みも見えてきました。 誰がどの領域を「守る」のかが曖昧 特定領域を守る・成長させる・身につけるという力学が働かない リリース後の運用責任が分散する パフォーマンス改善など中長期テーマに継続投資し辛い メリットとデメリットは表裏一体ではありますが、2年程度の間にデメリットが少しずつ顕在化するようになってきました。またこの2年の間にリアーキテクチャが前進し、 新たなアーキテクチャ上でスピーディに開発していくことの重要性 が増してきました。 さらに、新機能開発だけでなく、 事業成長とともにサービスレベルを継続的に高めていく必要性 も高まり、専任チームを作る機運が高まってきました。 フェーズ3:ECコア機能領域型(2026〜) そして2026年からは、 ECのコア機能を軸にした組織設計 に踏み切ります。 Item / Order / Checkout はECのコア機能になりリアーキテクチャ境界に近い Feature Dev…当該Sectionにおける開発PJを担う Item Scalability…商品領域のパフォーマンス改善を担う Architecture Design…中期的なアーキテクチャ設計を担う Checkout Reliability…購入に関するスケーラビリティ向上を担う(高負荷対応) この形に込めた意思 耐用年数が長い領域を組織境界にする サービスレベル改善を「片手間」にしない 新たなアーキテクチャに取り組みやすい体制にする 期待するメリット 担当領域に詳しくなり責任を持つ文化の醸成 近い領域を重点的に開発することによるスピードアップ 機能開発以外の、サービスレベル改善の継続的な投資 もちろん、この形も完成形ではありません。サイロ化やマンネリ化のリスクは理解した上で、ローテーションや横断OKRなどで緩和する前提にしています。 まとめ 振り返って強く感じているのは、 組織設計に最終解はない ということです。事業・プロダクト・人が変われば、最適な組織も変わります。 大事なのは、 今の課題は何か 何を優先し、何を捨てるか 次に変える前提で、今をどう設計するか を言語化し続けることだと思っています。 この記事が、どこかの開発組織で「組織を考えるきっかけ」になれば嬉しいです。 BASE では、今後のプロダクト成長を支える技術戦略や開発基盤の構築をリードしていただけるエンジニアを募集しています。 ご興味があれば、ぜひ採用情報をご覧ください。 binc.jp
この記事はBASEアドベントカレンダー 2025 の 23 日目の記事です。 エンジニアの右京です。今年後半になって、主に Web ブラウザ上での AI との対話型 UI の利用シーンに、インタラクティブな UI を提供するという流れが注目されています。 この記事はそれを実現するために現在提案されている MCP Apps について簡単に紹介するものです。 対話型 UI の拡張 今年 10 月に OpenAI が Apps in ChatGPT として Apps SDK を発表しました。これは MCP のレスポンスとして ChatGPT の対話型 UI にアプリケーションを組み込むことができる SDK です。 これにより、ChatGPT の対話中に外部のアプリケーションを呼び出して、よりインタラクティブな体験を提供できることが期待されています。 https://developers.openai.com/apps-sdk/concepts/ui-guidelines より引用 developers.openai.com これまでテキストベースのみで行われていた対話に、HTML、CSS、JavaScript を用いたコンポーネントとしてアプリケーションを提供できるようになり、表現の幅が広がります。 さらに、MCP を用いて提供することで、それに対応するプラットフォームであれば恩恵を受けることができるようになります。 OpenAI の発表以前から MCP UI というものも存在していて、こちらも MCP を用いて対話型 UI を拡張するための仕組みです。 また、技術仕様は大きく異なりますが、同時期に OpenAI と Stripe から発表された Agentic Commerce も購買フローを対話的に完了することができ、対話型 UI を拡張する流れの一つだと言えるでしょう。 mcpui.dev www.agenticcommerce.dev 一方で Apps SDK や MCP UI の仕様はもちろん統一されているわけではなく、プラットフォームごとに互換性があるような状態にはなっていません。 そこで、この仕様を標準化できるようにと現在提案されているのが MCP Apps です。 MCP Apps blog.modelcontextprotocol.io github.com ここでは、Apps SDK の Quickstart でサンプルとして登場する Todo アプリを参考に、MCP Apps として実装する場合のイメージを紹介していきます。 サンプルコードと解説は @modelcontextprotocol/sdk と zod を用いた MCP サーバーの実装をある程度把握していることを前提に記述しています。 また、 Apps は提案段階で現時点では動作する実装は試験的なものに限られていることに注意してください。あくまで実装のイメージとして捉えていただけると幸いです。もし、なにかしら動くものが欲しい場合は Apps SDK を利用するのが現実的です。 MCP Apps (と Apps SDK / MCP UI) は以下の 3 つの要素を中心に構成されます: ui:// URI Scheme を利用した MCP Server への Resource 定義 サンドボックス化された iframe でのコンポーネント実行 postMessage を使った JSON-RPC での通信 MCP Apps では、まず MCP サーバーに対して ui:// URI Scheme で Resource を定義します。 @modelcontextprotocol/sdk を使う場合は server.registerResource を利用します。 server . registerResource ( "todo-widget" , "ui://example/todo" , { title : "Todo Widget" , mimeType : "text/html;profile=mcp-app" , } , async () => { return { contents : [ { uri : "ui://example/todo" , text : `<!DOCTYPE html><html>...Todoアプリの実装...</html>` , mimeType : "text/html;profile=mcp-app" , } , ] , } ; } ) ; mimeType が text/html;profile=mcp-app となっているのが特徴的ですね。ここで配信される HTML の全文は以下に折りたたんでいますが、全体を見なくとも雰囲気は掴めるかと思います。 Todoアプリの実装 <!DOCTYPE html> < html > < body > < input id = "input" placeholder = "Add task" > < button onclick=" addTodo ()" > Add </ button > < ul id = "list" ></ ul > < script > let tasks = [] ; window . addEventListener ( "message" , ( event ) => { if ( event . data . method === "ui/tool-result" ) { tasks = event . data . params ?. structuredContent ?. tasks || [] ; render () ; } }) ; function addTodo () { const title = document . getElementById ( "input" ) . value ; window . parent . postMessage ({ jsonrpc : "2.0" , method : "tools/call" , params : { name : "add_todo" , arguments : { title } } , id : Date . now () } , "*" ) ; } function render () { document . getElementById ( "list" ) . innerHTML = tasks . map ( t => `<li> ${ t . title } </li>` ) . join ( "" ) ; } </ script > </ body > </ html > 次にこの Resource を server.registerTool で _meta.ui.resourceUri として Tool に関連付けします。 server . registerTool ( "add_todo" , { title : "Add todo" , description : "Creates a todo item with the given title." , inputSchema : { title : z . string () . min ( 1 ) } , _meta : { ui : { resourceUri : "ui://example/todo" , } } , } , async ( args ) => { todos = [ ... todos , { id : uuid () , title : args . title , completed : false }] ; return { content : [{ type : "text" , text : `Added ${ args . title } ` }] , structuredContent : { tasks : todos } , } ; } , ) ; これで add_todo が呼び出されると、MCP は ui:// URI Scheme で定義されたリソースを取得し、iframe へロードします。この iframe がチャット UI の中に表示されることで、インタラクティブな UI が提供されることになります。そして、Tool の実行結果が ui/tool-result として postMessage で JSON-RPC 形式で iframe 内に送信されます。 window . addEventListener ( "message" , ( event ) => { if ( event . data . method === "ui/tool-result" ) { tasks = event . data . params ?. structuredContent ?. tasks || [] ; render () ; } }) ; この場合は add_todo で返された tasks がそのままメッセージに含まれているので、それを受け取って UI を更新しています。これで UI とチャットの内容が同期されるということですね。 アプリ側から Tool を呼び出すようなことも可能で、以下はタスクを追加するためのボタンをクリックすると Tool を呼び出す例です。 function addTodo () { const title = document . getElementById ( "input" ) . value ; window . parent . postMessage ({ jsonrpc : "2.0" , method : "tools/call" , params : { name : "add_todo" , arguments : { title } } , id : Date . now () } , "*" ) ; } method には専用のものがいくつか定義されており、リンクを開くよう要求したり、チャット UI へメッセージを返すことができるようです。 より詳しい現状の仕様は以下で確認できます: github.com また、今回は素の HTML でアプリを作成していますが、 React コンポーネントとして実装できるような仕組みも提供されています。 この辺りだったり、より詳しい日本語の情報として azukiazusa さんの記事が参考になるかと思います。この記事を書く際にも大変参考にさせていただきました。 azukiazusa.dev 所感 来年以降でこの辺りの整備が一気に進み、実用化されることでビジネスサイドからの注目度もより高まるのではないかと考えています。個人的には(少なくともCoding Agentの文脈の) MCP についてはコンテキストの圧迫やその費用対効果から少し懐疑的なのですが、チャット UI へのサービスの統合は一定のビジネスインパクトがあるだろうなとも感じています。特に Agentic Commerce は EC 領域において大きな影響を与えそうです。MCP が登場した当初から、「これからはチャットでタスクが完結するのでサービスごとのフロントエンドは不要(意訳)」というような意見もありましたが、それが少し現実味を帯びてきたのかもしれません。 ここまでに紹介したように MCP Apps や Apps in GPT は既存の Web 技術の上に成り立っています。これが将来的にどうなるかはわかりませんが、少なくとも数年の間は新しい Web フロントエンドの形として付き合っていく必要がありそうです。これまで一つのドメインに対して一貫性のあるデザインや操作を提供することが中心だった Web フロントエンド開発ですが、MCP Apps のような仕組みが普及することで、サービスの機能をコンポーネントとして切り出していく方向へシフトするかもしれません。サービスの利用方法の一つとして Remote MCP Server が提供されているのが当たり前になり、さらに複数の形で同一のサービスを提供することが一般的になる可能性もあります。 そのためには BFF のような役割を持つレイヤーの重要性が増しそうです。一方で UI Component や Design Token に関してはどの提供方法でも統一したかったりと、全体のアーキテクチャやコード管理の方法にも影響がありそうですし、API を整えたりということも必要でしょう。さらに未来に登場するであろう汎用プラットフォームに向けても対応できるような備えを、なんもわからんなりに考えておきたいですね! おわりに ざっくりとした紹介になってしまいましたが、MCP Apps のイメージが伝われば幸いです。 今回のアドベントカレンダーでは前半でも記事を書いているのでよかったらそちらもご覧ください! devblog.thebase.in devblog.thebase.in 明日は @UedaHayato です!お楽しみに!
この記事は BASE アドベントカレンダー22日目 の記事です。 いよいよ年の瀬も近くなってきました。マネージャーの松原( @simezi9 )です。 この時期になるとアドベントカレンダーとして大量のアウトプットが世に公開されるのもすっかり毎年の恒例となりました。 そこで改めて「出版バイアス」という現象とそれを起点とした情報との向き合い方を考えてみよう、というのが本エントリの趣旨となります。 出版バイアスとは何か? 「出版バイアス」という単語は耳にしたことがあるでしょうか? この概念は科学論文の世界、とくに医療関係の論文においてよく話題に登場するものです。 その意味とは、肯定的な結果を持つ研究や革新的で独創的であると主張する論文ばかりが世の中に公開される傾向にあり、 そうでない論文、つまり仮説に対してネガティブな結果に終わった研究やはっきりとした結論が出せない研究が世の中に出てこないというバイアスです。 これによって研究の成果が成功例ばかり報告されてしまい、本来よりも過大な評価をうけてしまうという現象が起こります。 これにたいして肯定的な結果を持つ論文しか出版されないことから「出版」バイアスという名前がついています。 これは「生存者バイアス」や「サンクコストの誤謬」といった概念とも相通ずる物があるかと思います。 このバイアスが生まれる理由はとても明快です。 研究者たちからすれば論文として成果を上げるプレッシャーにさらされている中で自分の功績として華々しく主張できない結果をいちいち論文として公表するような手間を取らないし、 読み手側としても刺激的な結果を求めるために否定的な論文は需要が少ないためです。 このバイアスに対抗するための手法というものも様々考案されているようで、統計的手法( メタアナリシスにおけるファネルプロット など)を用いて、報告されている結果に不自然な偏りがないかをチェックしてみたり、 研究のプロトコルそのものを事前に信頼できる外部機関に登録(研究に際しての仮説、データの取り方や取ったデータの分析手法などを事前に決めておく)したうえで、 結果がどうであれ必ず公表することにする(=研究の途中に発生するバイアスの除外)といったことが行われているようです。 ファンネルプロットの例 世の中にある出版バイアス この出版バイアスという現象から考えさせられることは多いと私は思っています。 つまり、科学の世界では論文の価値を高めることに非常に重点が置かれるため先述のようなバイアスを自覚し、それを極力排除するような取り組みが行われるわけですが、 そうでない自由な出版物に対してはその動機が存在しないためよりこのバイアスがひどくなるのではないか、と考えているためです。 出版物というのは論文や書籍に限らず、ブログのエントリであったり登壇スライドであったりSNSの投稿であったり、様々なアウトプットに当てはまります。 より具体的に言うと、「〇〇という仕組みを導入したらめちゃくちゃ成功した」という共有は行うにあたって非常にハードルが低いのに対して、「〇〇はダメ」と主張するのは非常に大変ですし(かつ炎上しがちでリスクが高い)、 「〇〇には効果があるのかなんともわからなかった」などという結論がぼんやりした投稿を行うことはさらに難しいです。 結果的に世の中には「〇〇は効果がある」という共有ばかりが残ってしまい、その価値が実態よりも過大に評価されていくという傾向があるように思います。 その〇〇とはもしかしたら例えば「React」だったり「マイクロサービス」だったり「Kubernetes」だったりはたまた「1on1」だったりするのかもしれません(実際にそれらがそうであるという話ではなく、あらゆるトピックが入りうるという例です)。 バイアスと向き合う このバイアスに対抗するというのは実際の問題としてかなり難しいもので、 耳目を集めるアウトプットを出したい著者と刺激的なコンテンツを求める読者、という構図がある限り世の中には自然とそうしたアウトプットが増えていくことになります。 実際にこの文章を書いているさなかでも、もっとかっこいいパンチの効いた主張ができないか、などと考えている自分がいます。 あるいは、アドベントカレンダーで特に動きが盛んになる企業のテックブログなどでは失敗を公表することにより組織のレピュテーションを汚す結果になることを恐れますし、そもそも失敗を認めたくないから書きたくないことも多いでしょう。 本当はもっと様々なアウトプットがまんべんなく公表されていく世の中が理想なのかも知れませんが、 残念ながらこうした出版バイアスというのは避けられずに存在しています。 あらゆる意見表明の場において「共有されずに終わった結果・意見が存在している」ということを意識の片隅に置いておくだけでも物事を冷静に捉える助けになるのではないかと思います。 最後に、出版バイアスの話をきちんと知りたい方は以下の書籍などに詳しいので関心があればぜひ年末年始の課題図書にいかがでしょうか Science Fictions あなたが知らない科学の真実 作者: スチュアート・リッチー ダイヤモンド社 Amazon 明日は@yaakaitoによる記事です、お楽しみに!
はじめに この記事は BASE アドベントカレンダー21日目の記事です。 devblog.thebase.in こんにちは、バックエンドエンジニアの小笠原( @yukineko_819 )です。 今回は、私がこの2年間ほどをかけて取り組んできたBASEにおけるサービスレベルマネジメントの取り組みの歩みと、今後の展望についてお話しようと思います。 始まり 最初のきっかけは、New Relic社主催のFutureStack Tokyo 2023に参加したことでした。 devblog.thebase.in BASEではこれより以前からNew Relicを導入して活用していましたが、イベントの参加を通じてまだまだNew Relicを十分に活かしきれていなかったことを知りました。 そして、ここではじめてサービスレベルマネジメントという概念を知り、私たちも取り組んでみようか、という話になりました。 最初の一歩 まずは小さく身近なところから始めてみようということで、当時の所属チームが担当していたCRM領域、特に当時開発中であったメンバーシップAppにおけるSLI/SLOを考えるところからスタートしました。 とは言っても最初は何もわからない状態からのスタートだったため、New RelicやGoogleが公開しているSLOに関する資料や、他社事例のテックブログなどを参考にインプットを重ねながら、およそ2ヶ月をかけて以下のような議論を進めていました。 メンバーシップAppにおける絶対に毀損してはいけない体験とは何か? フロントエンド、ALB、バックエンドAPI、どのレイヤーのイベントをSLIとするのか? SLIにおける「成功のイベント数」はどのようなイベントを成功とみなすのか? SLOは何を基準に定めれば良いのか? 活動の中で「購入者がメンバーシップ会員として商品を購入することができる」という体験をCritical User Journey(CUJ)の一つとして定めてSLOを設定しようと議論を重ねていきましたが、残念ながら結局SLOを設定するところまで至ることはできませんでした。 しかし、この2ヶ月間の活動を通して取り組んだメンバーの間では、CUJを検討する過程でそのサービスが提供している価値をより深く理解する必要があり、加えて適切なSLIを選定するためにはそのサービスがどのような機能やアーキテクチャ、インフラストラクチャで構成されているのかを理解する必要がある、ということを強く認識することができた活動でした。 ある1領域のSLMからサービス全体のSLMへ SLMとは何なのか、そしてSLOを設定することの難しさを痛感した我々ではありましたが、次の一歩を踏み出しました。 一般的なECサービスが提供しているようなECサイトを公開したり商品を購入したりといった体験は、ショップオーナーや購入者から見ても最もコアとなる体験であり、BASEとしてもそれらの体験は重要であるはず、という考えのもと、「ショップを開設して商品を登録して出品し、その商品が実際に購入された後、発送を完了してショップに売上が立つ」という一つのサイクルをBASEにおける最も重要な体験であると定義して、このサイクルを構成するCUJ群に「Tier1 CUJ」と名付けてSLOを設定することにしました。 SLOの設定にあたっては、最初から良いものを作ろうと深く悩みすぎたことでなかなかSLOを設定するところまで辿り着けなかった前回の反省を踏まえ、ひとまずは計測することを第一目標として掲げ、集まった有志数人でこれらのCUJに対してSLOを設定していきました。 これまでの「商品ページが重い」「機能がうまく動いていないというお問い合わせが何件か来ている」といった定性的な指標に加えて、この水準でこのくらいの人が使えている、のようにSLOという定量的な指標で表現できるようになった瞬間でした。 ここまで取り組んできたサービスレベルマネジメントの取り組みとして、一つの大きな成果です。 惜しむらくは、それを知っているのが設定を行った数名の有志だけであること、質より速度を優先したことで設定したメンバー達ですらSLOの妥当性に対する信頼性に不安があったこと、そしてSLOが未達でもそれを改善するという活動に繋げるまでのフローをまだ整備できていないためにSLO未達の状態が続いてもアクションを取れずにいる、ということでした... 一般的なSLOからBASEのSLOへ さて、いろいろ課題が残るもののTier1 CUJをカバーするようにSLOを設定することはひとまずできたので、次の段階としてよりSLOの精度をあげる取り組みをすることにしました。 サービスレベルマネジメントとしては運用・監視・改善のサイクルを回すことが大事であり、改善の部分が抜け落ちてしまっている状態ではありましたが、SLOの精度を上げてアラートがなった場合はなおさねばならない状況を作った方が運用を軌道に乗せるにはスムーズだろう、と判断してのことです。 購入にまつわる決済や発送の領域に詳しいエンジニア、インフラストラクチャに詳しいSREチーム、アーキテクチャに詳しいplatformチームといったメンバーに声をかけて総勢12名の「サービスレベル定点観測会」という会議体を組成しました。 BASEのサービスやTier1 CUJの体験を実現する各種機能に対して深い知識と理解を持っているメンバーでSLO検討を実施することで、より実態に即した質の高いSLOを設定できると考えたためです。 この取り組みはうまくいった部分もあれば、失敗した部分もありました。 成功した点は以下が挙げられます。 参加したメンバーにサービスレベルマネジメントの意義を理解してもらうことができた CUJの検討からSLOを設定するところまでの1サイクルを経験してもらうことができた 実際に幾つかのCUJに対して、より検討を深めた上でSLOを追加したり置き換えたり、そのままで良いという確認が取れたりといったSLOの品質向上が実現できた 一方で失敗した点としては、 メンバーをいきなり数倍に増やしたがそれに対応した会議体のマネジメントは私のキャパシティを超えていた 二週間に一度の開催枠の中で活動をしていたので都度どこまでやったかの思い出し作業が発生した といった点が反省点でした。 SLI/SLOワークショップの開催 また、サービスレベル定点観測会の取り組みとは別に、New RelicのBASEのサポート担当の方に支援いただき、BASE社内向けのSLI/SLOワークショップを開催しました。 参加するメンバーが多く2回に分けて開催したのですが、1回目はNew Relicの方が、2回目はBASE社内のエンジニアが進行を担当して、どちらの回も大変好評で良い結果となりました。 ワークショップによって一定の認知を広げることができたことで、これ以降開発チームが自発的にリリース予定の機能についてSLOを設定してみるといった事例も見受けられ、BASEにサービスレベルマネジメントの種をまいたとても重要な施策だったと思います。 SLOの再定義とエンジニア以外との協力 いきなり少し規模を大きくしすぎたという前回の反省から、メリットの部分は維持しつつ1チームとして機動力高く動けそうな人数として4人にまで絞り込み、改めてスタートしました。 そしてある程度品質も向上したSLOが揃ってきたため、次のフェーズとしてこのSLOをエンジニアが活用する運用指標の一つというだけではなくBASEとして共通の守るべきサービス品質基準として整えていくために、改めてTier1 CUJを以下のように整理しなおしました。 区分 内容 Revenue Critical 商品の購入や注文の発送など、BASEの売上に直結する最重要のCUJ群 EC Service Core ECサービスとして一般的に備えているCUJのうち、Revenue Criticalに該当しないCUJ群 Engagement Support ショップオーナーの集客をサポートするCUJ群 その上で、この区分やCUJ群がBASEとして重要であるとすることに違和感がないか、不足がないか、といった観点でエンジニア以外のチームとも相談を実施し、ここに違和感がないと合意した上でまだ設定されていない箇所のSLOの設定を進めました。 特にRevenue Criticalについては事業KPIに強く関連するCUJに焦点を当てて、事業KPIがどのような要素から構成されているのかを紐解き、そこからよりビジネスと相関を持ちうるSLOを選定して設定することを進めていきました。 エンジニアの指標からみんなで見る指標へ 同時に、New Relic上で管理しているSLOを誰でも見ることのできる指標として整備し、事業KPIをはじめとする各種データと絡めて分析ができる環境を整える取り組みにも着手しました。いわゆるビジネスオブザーバビリティと言われる取り組みで、具体的には以下の2点を実施しました。 毎月、先月のSLOが達成できていたか、できなかった場合どのような要因があったのか、という点をサービスベルサマリーレポートとしてスライドにまとめSlack上で全社共有する New Relic上で設定・管理しているSLOを、MetricデータをNerdGraph APIを用いてBigQueryに溜め込むことでLookerで閲覧できるようにする 2のSLOをLookerで閲覧できるようにした理由は、エンジニア職以外はLookerを使ってデータの分析や事業KPIの分析などを行なっていたためです。BASEではエンジニアは全員がNew Relicのアカウントを付与されているのですが、エンジニア職以外はLookerを使ってデータの分析や事業KPIの分析などを行なっており、New Relicのアカウントを持っている方はほぼいませんでした。 データ資産という意味でもLookerで分析するために整えられた各種データが揃っている状態だったため、改めてNew Relicにそれらのビジネスメトリクスを取り込むよりは、New Relic上にしかないSLOのデータもLooker上で分析できるようにして統一する方が良い、と考えたためです。 これらの取り組みはすぐに効果が出て全員がSLOに興味を持って活用してくれるようになる類のものではありませんが、それでも今まで見ることのできなかったデータを公開したこと、定期的にそのデータの見方や分析内容を発信すること、には意義があったと思っています。 データを公開したとして本当に興味を持ってもらえるのだろうか、データの公開よりも先にやった方がいいことがあるのではないか、という点はサービスレベル定点観測会のメンバーとしても不安を抱えながら作業を進めていましたが、いざ公開してみると詳しく話を聞いてみたい、この応答速度のSLOは維持できているとしても遅いと思うので改善したほうがいいのではないか、といったような声を何件かかけてもらうことができるようになっていきました。 SLOでサービスの品質を定量的に観測して維持するための改善をしていく、というサービスレベルマネジメントを全社で実施していくことの芽が出始めたことを実感しました。 全体を俯瞰するSLOから、どこに影響があったのかわかるSLOへ SLOを設定する上で難しいなと感じるのは、どこをSLIとして定義するか、という選定の部分だと思います。例えば一つ一つのAPI単位で細かく設定することもできてしまいますし、あるいはもっと大雑把にフロントエンドの画面描画を指標として設定することもできます。 サービスレベル定点観測会では、購入体験を支えるカート機能はとりわけ重要なサービスであると考え、次の観点としてこのカートでの購入体験をより詳細にSLOでカバーしていくことを目標としました。 ビジネス面でも大量のトラフィックを高速に処理できるかどうかは事業KPIであるGMVの増加に影響がありますし、ユーザー体験の面でも商品を購入するというECサービスを使用する購買層のユーザーとしては最もコアとなる価値体験を司る部分です。 単に購入が正常に高速に完了できたのか、という指標だけではなく購入者のカート体験として「カートに商品を追加する」「カート内の送り先情報を編集する」「確認画面で内容が正しいかを確認する」といった、購入を行う上での一連の体験を全て洗い出し、必要な箇所を選定してSLOを設定していきました。 これにより、購入体験に関してより具体的にどのような影響が出ていたのかを把握できるようになりました。 ここから、さらに一歩先へ このように、手探りで模索を続けながらも、サービス全体を俯瞰してどこに何が起こっているかを把握する、ビジネス戦略に強く影響がある箇所に問題が起きていないかを把握する、コアとなるユーザー体験で快適なサービスを提供できている、といった様々な角度観点からアプローチをかけてBASEとして重要なサービス価値とは何か、どこにSLOを設定していくべきかを考えて進めてきました。 ここまでの活動を通して、サービスレベル定点観測会の中にも、それぞれ自発的にSLOの設定にチャレンジしてくれたエンジニア達の中にも、その知見がだいぶ溜まってきたのではないかと思います。 次なるチャレンジとして、来年からは今までサービスレベル定点観測会が推進してきたサービスレベルマネジメントを、いよいよ開発組織全体で分担して担当していくということに挑戦していきたいと考えています。 おわりに 改めて振り返ってみると、上長や同僚に大いに助けてもらいながら、なんとかここまで形にしようと賛同し協力してくれた皆さんと一緒に一歩一歩、少しずつ進んできた2年間だったと思います。 特に印象深いのは、誰に相談を持ちかけてもサービスレベルマネジメントの意義を理解していただけて、その上で「ぜひ一緒にやりましょう!」という言葉をいただけたことです。 より良いユーザー体験を提供するため、安定してサービスを提供していくためには協力を惜しまない、という温かい後押しが私の背中を常に支えてくれていました。 ようやくここまで辿り着き、そしてまだまだここからという状態ではありますが、引き続きユーザーに安定してサービスを提供していけるようにサービスレベルマネジメントの取り組みを推進していきます。 最後に、BASEでは今後の事業成長を一緒に支えてくれるエンジニアを募集しています。 ご興味があれば、ぜひ採用情報をご覧ください。 binc.jp 明日のBASEアドベントカレンダーは松原(@simezi9)さんの記事です。お楽しみに!
BASE ADVENT CALENDAR 2025 DAY.20 はじめに この記事はBASE アドベントカレンダー 2025の20日目の記事です。 Pay ID Platform Group の 大木です。 本記事では、Feature Flag(aka Feature Toggles)の標準化仕様及びSDKである OpenFeature と、Feature Flag As A Service(以下FFaaS)である AWS AppConfig を利用したサービスを約1年間運用してきたため、OpenFeatureを中心にFeature Flagの現在とAppConfigの運用に関してをお話しします。システムの開発言語はGoを利用しているため、主にそれをベースにお話しします。 Feature Flag概要 Feature Flagと聞いて何を思い浮かべるでしょうか? On/Offのスイッチのようなもので、段階的機能公開を行うカナリアリリースで利用したり、問題があった場合にロールバックできる A/B テストで、ユーザセグメントごとに異なる機能やUIを動的に振り分けたりするなどに使う コード差分を頻繁にmainブランチへマージするコードのフレッシュさを保つトランクベース開発 具体的なケースを思い浮かべると、複数の機能を持っているように思えますが 、簡単に言えば、 Feature Flagとは、コードを変更することなくシステムの動作を変更できる手法 で、システムへのコード反映(デプロイ)と機能公開(リリース)を分離することができるものです。 その活用方法として、大きく4つのカテゴリに分類することができ、設計上の制約、管理方法がそれぞれ異なります。カテゴリの分類目安として、フラグをどのくらいの期間利用するかという存続期間と、フラグの評価ロジックや値の決定をどの程度動的に行うかによって分類できるようです。 Release Flag: 開発中の未完成な機能を本番環境から隠すための、比較的短命なフラグ。 Ops Flag: システムの運用担当者が、パフォーマンスの最適化や機能の緊急停止のために使用するフラグ。比較的長命になることがあります。 Experiment Flag: A/Bテストなど、ユーザーの振る舞いを比較するために使用するフラグ。実験期間中のみ存在します。 Permission Flag: 特定のユーザーグループに機能へのアクセス権を与えるための、永続的または非常に長命なフラグ。 引用: https://martinfowler.com/articles/feature-toggles.html 我々は主に、中長期運用するOps FlagやPermission Flagに関して、OpenFeature SDKとAppConfigを利用した運用管理を行なっており、OpenFeatureの主要機能や予備知識を交えながら説明したいと思います。 OpenFeatureについて OpenFeature とは、コミュニティ主導のFeature Flag標準化プロジェクトですが、標準化仕様の策定とSDKを提供しています。LaunchDarklyやAppConfig等フラグ管理を行うベンダーが提供するAPIや独自SDKを直接利用するのではなく、OpenFeatureを利用する利点はどのようなところにあるのでしょうか? 例えば、以下のような点が挙げられると思います。 どのフラグ管理バックエンドを選択しても、アプリケーションからは、OpenFeature SDKの評価APIにより、統一的なインタフェースでフラグ評価ロジックを実装可能 FFaaSやDB、ローカルファイルなど、フラグ管理するバックエンドは、それぞれに対応するOpenFeature Providerを利用することで、アプリケーション側のフラグ評価ロジックを、ほぼ変更せずに差し替えが可能 Hooksを利用し、フラグ動的評価を行うための評価コンテキスト(Evaluate Context)を編集したり、ログ出力、Telemetryの実施など機能拡張を行うことが可能 以下の図は、OpenFeatureが、任意の仮想的なフラグ管理システム「Flags-R-us」と統合されることを示しています。あたかもクラウド上のシステムと統合されそうな印象を受けますが、3rd partyベンダーが提供する独自SDKや、DB、ローカルファイルなどフラグ管理が行える仕組みと、それに対応するProviderを用意できれば、共有の標準化されたフラグクライアント(SDK)を利用し、一貫性のある統合APIを利用して実装することが可能です。 引用: https://openfeature.dev/docs/reference/intro アプリケーションで、OpenFeature SDKを利用して、Feature FlagをGoで扱うには以下のような実装となります。もしフラグ管理バックエンドを差し替えたい場合は、1.でフラグ管理バックエンドに対応するProviderに差し替えれば、フラグ評価や利用する箇所の修正は不要です。 package main import ( "fmt" "context" "github.com/open-feature/go-sdk/openfeature" ) func main() { // 1. フラグ管理バックエンドを扱うProviderを登録 openfeature.SetProviderAndWait(openfeature.NoopProvider{}) // 2. そのProviderを、アプリケーションから利用するためのクライアントを作成 client := openfeature.NewClient( "app" ) // 3. フラグの評価実行 v2Enabled := client.Boolean( context.TODO(), "v2_enabled" , true , openfeature.EvaluationContext{}, ) // 4. 評価されたフラグ値を使う if v2Enabled { fmt.Println( "v2 is enabled" ) } } これで終わるなら、直接フラグ管理システムが提供するAPIやSDKを使えば良いかと思いますが、他にもたくさん仕様が考えられていますので、いくつか紹介します。 1. 共通のI/Fで実装したいが、アプリ制御とA/Bテストは、別々のフラグ管理システムを利用したい A/Bテストを行う際にもFeature Flagを利用することができます。我々はAWSのAppConfigをフラグ管理システムとして利用していますが、A/Bテストでは、分析基盤が用意されているかどうかがとても重要です。現在、 Amazon CloudWatch Evidently というモニタリングや分析を行える機能はサポートを終了しており、分析基盤を別途用意するのも大変です。そういった場合、以下のように複数Providerを登録することが可能です。それぞれのProviderに対応するクライアントを作成することで複数のフラグ管理システムを、用途ごとに使い分けることができます。 import "github.com/open-feature/go-sdk/openfeature" // アプリ制御用Providerを登録 openfeature.SetProviderAndWait(NewLocalProvider()) // 名前付きでProviderを登録 openfeature.SetNamedProvider( "abtesting" , NewABProvider()) // デフォルトのProviderを利用するクライアントを作成 clientWithDefault := openfeature.NewDefaultClient() // 名前付きで登録したProviderを利用するクライアントを作成 clientForABTesting := openfeature.NewClient( "abtesting" ) 2. フラグ管理システムを別システムに移行したい 例えば、サービス終了で別システムに移行しなければならないというケースを考えてみましょう。 実験的機能となりますが、 Multi-Provider というものを利用すれば、複数のProviderをまとめて登録し、一つのクライアントを使い並行して実行できます。これにより、先に新システムに対応するProviderを追加登録しておいて、後からゆっくりと新しいシステムにフラグを登録するといったことが可能になります。 import ( "github.com/open-feature/go-sdk/openfeature" "github.com/open-feature/go-sdk/openfeature/multi" "github.com/open-feature/go-sdk/openfeature/memprovider" ) mprovider, err := multi.NewProvider( multi.StrategyFirstMatch, multi.WithProvider( "providerA" , memprovider.NewInMemoryProvider( /*...*/ )), multi.WithProvider( "providerB" , myCustomProvider), ) if err != nil { return err } openfeature.SetNamedProviderAndWait( "multiprovider" , mprovider) 3. 複数フラグ管理システムを組み合わせてシームレスに利用したい 2.のケースと同じように複数のProviderを利用するケースなのですが、組み合わせて使うケースを考えてみましょう。FFaaSを利用する場合、ネットワーク越しにフラグデータセットを取得してこなければならないため、システムがダウンしている場合、アクセスできないことがあります。そこで、バックアップとして、環境変数やローカルファイルを利用するProviderを併せて登録しておけば、その間は、フォールバックしてフラグを利用するといったことが可能になります。以下の中からProvider利用戦略を指定することで、柔軟に結果を利用することができます。 First Match: Providerを順番に呼び出し、最初にフラグが存在するものを返す First Success: Providerを順番に呼び出し、エラーが発生しなかったProviderの結果を返す Comparison: 並列にProviderを呼び出し、結果を比較し一致した場合はその結果を、そうでない場合はFallback Providerの結果またはデフォルト値を返す Custom: 自前で利用ロジックを実装 OpenFeatureでは、ターゲティングと呼ばれる仕組みがあり、アプリケーションやユーザーに関する情報を利用して、動的に評価するルール設定を定義し、フラグのステータスを制御します。 AppConfigの場合、 マルチバリアントフラグ というものを利用する必要があります。その場合、AppConfigからAPIで直接取得する方式は利用できず、AppConfig AgentをSidecarコンテナで立てて利用するのが必須となっています。AgentがAppConfigから、設定値やターゲーティングに利用するルールセットをポーリングにより定期的にダウンロードするため、アプリケーションからは、ローカルホストへの通信により、Agentでフラグ評価を実行できます。 しかし、 Agentのイメージ が壊れた場合、正しくフラグを取得できなかったり、リソース枯渇や過負荷になった場合、Agentへの接続エラーになることが極まれに発生します。そうした場合、バックアップとして、ローカル設定からフラグの結果を返せるProviderを併せて登録しておくことで、問題が起きにくくなるかなと思います。 引用: https://docs.aws.amazon.com/ja_jp/appconfig/latest/userguide/appconfig-agent.html 4. ターゲティングをサポートする評価コンテキストを柔軟に構築できる OpenFeatureは、ターゲティングを行う入力値として、評価コンテキストというコンテナを利用します。ここに、ユーザのIPやメールアドレス、サーバの位置情報(AWS Regionなど)を載せてリクエストすることで、柔軟にフラグ評価を行えます。 フラグ管理システムがフラグを動的に評価し結果を返す仕組みを提供していればの話ですが、それによって、柔軟に結果を変化させることができます。これはグローバルに設定したり、特定クライアント、実行時にも設定できます。様々なフェーズで設定されたコンテキストのデータはマージされて、評価リクエストに乗ります。 これを使った実装として、カートのCheckout時とPay IDアプリの非Checkout時にリクエストする同じHTTP APIがあり、そこでこのターゲティングを利用したフラグを利用しています。Permission Flagの一種の使い方ですね。 Checkout時には、前段で独自に審査ロジックが動作しているため、無駄に審査や料金が発生しないように審査ロジックをスキップするが、Pay IDアプリの非Checkout時には何も審査していないため、APIで必ず審査ロジックを実行する必要があります。その制御にこの仕組みを使っています。 // OpenFeature全体で利用する評価コンテキストを設定 openfeature.SetEvaluationContext(openfeature.NewTargetlessEvaluationContext( map [ string ]any{ "region" : "us-east-1-iah-1a" , }, )) // クライアントに設定(これによって、利用するProviderのみに伝搬される) client := openfeature.NewClient( "my-app" ) client.SetEvaluationContext(openfeature.NewTargetlessEvaluationContext( map [ string ]any{ "version" : "1.4.6" , }, )) // 実行時に評価コンテキストを設定 evalCtx := openfeature.NewEvaluationContext( "user-123" , map [ string ]any{ "company" : "Initech" , }, ) boolValue, err := client.BooleanValue( "boolFlag" , false , evalCtx) また、開発言語によっては、トランザクションコンテキストを利用できる場合があり、リクエストスコープなデータをそこに載せて伝搬することが可能です。 goの場合は、 context packag e を使って実現しています。これによって、HTTPリクエスト受信時にミドルウェアでIPやユーザIDを得られたら、とりあえずトランザクションコンテキストを利用し、評価コンテキストにマージするといった使い方が可能です。 import "github.com/open-feature/go-sdk/openfeature" // set the TransactionContext ctx := openfeature.WithTransactionContext(context.TODO(), openfeature.EvaluationContext{}) // get the TransactionContext from a context ec := openfeature.TransactionContext(ctx) // merge an EvaluationContext with the existing TransactionContext, preferring // the context that is passed to MergeTransactionContext tCtx := openfeature.MergeTransactionContext(ctx, openfeature.EvaluationContext{}) // use TransactionContext in a flag evaluation client.BooleanValue(tCtx, ....) 5. Hooksで機能拡張できる Hooksは、アプリケーション開発者が、フラグ評価に任意の動作を追加できる機能です。 以下の4つのステージでロジックを追加できます。 before: フラグ評価の直前 after: フラグ評価が成功した直後 error: フラグ評価が失敗した直後 finally: フラグ評価後に無条件に これは、評価コンテキストと同様、グローバルや特定クライアント毎や、評価実行時のいずれかで実行するように設定可能です。 ユースケースとしては、評価コンテキストへの編集や追加、評価後のフラグ値の検証、 Telemetryデータ計測 、ログ記録に利用できます。 OpenFeature Hooks LifeCycle 評価コンテキストへの編集に利用するフックの例としては、以下のようなものです。 AppConfigの場合、Contextはリクエストヘッダで送信します。提供されているProviderで、評価リクエストのフォーマットに合うように大体は変換されると思いますが、一部データ型の変換が、評価ルールが想定しているデータ型に合わないケースが、発生することがあります。 変換ロジックを、アプリケーション側のコードで実装することも可能ですが、フラグ管理システムを差し替えた場合、フォーマットが一致しない可能性があるため、その部分を書き直す必要が生じるかもしれません。フックを実装しOpenFeature SDKに登録すれば、アプリケーションロジックに影響を与えず、データ変換ロジックを実行することが可能となります。 package featureflag import ( "context" "maps" "strings" "time" "github.com/open-feature/go-sdk/openfeature" ) // AppConfigのContextに合うように変換するHookを実装します。 type AlterAppConfigContextHook struct { openfeature.UnimplementedHook } // AppConfigでは、HTTP Header: Contextに複数値を詰め込むため、カンマ区切りはそれらの値を分けるために使用されます。 // そのため、カンマ区切りの値を持つ配列値等の場合は、別の区切り文字に置き換える必要があります。 // そもそも配列もサポートしていなそうなため、スペース区切りに変換するようにします。 // // See https://docs.aws.amazon.com/ja_jp/appconfig/latest/userguide/appconfig-creating-multi-variant-feature-flags-rules-operators.html // // Example: // // HTTP Header: Context: "targetingKey=1qaz2wsx3edc,machineId=1qaz2wsx3edc,region=us-east-1-iah-1a,tenant=e1,service=payid-api,scope=payid-app account,accessDate=2025-03-31T04:00:16" func (h *AlterAppConfigContextHook) Before(ctx context.Context, hookContext openfeature.HookContext, hookHints openfeature.HookHints) (*openfeature.EvaluationContext, error ) { oldECtx := hookContext.EvaluationContext() newAttrs := map [ string ]any{} maps.Copy(newAttrs, oldECtx.Attributes()) // 記載されている評価ルールとして利用可能なオペランドのフォーマットに合わせる for k, v := range maps.All(newAttrs) { if t, ok := v.([] string ); ok { newAttrs[k] = strings.Join(t, " " ) } if t, ok := v.(time.Time); ok { newAttrs[k] = t.Format(time.RFC3339) } } newECtx := openfeature.NewTargetlessEvaluationContext(newAttrs) return &newECtx, nil } func NewAlterAppConfigContextHook() *AlterAppConfigContextHook { return &AlterAppConfigContextHook{} } サーバサイドにおけるFeature Flag評価と取得に関する課題 これまで、フラグ管理システムがどこにあるかを意識せずに話していました。しかし、FFaasの場合、クラウド上のどこかに存在するため、どこかの段階でフラグデータセットをネットワーク越しに通信して、取得する必要があります。 その際に考えることはいくつかあるかと思います。フラグが必要になるたびに、リモートに通信してその都度評価してもらうのか、データセットを取得しローカルで評価するのかがありそうです。 Different approaches for server-side SDK architectures という記事によると、サーバーサイドSDKが一般的に採用しているアーキテクチャ上のアプローチは3つあるといいます。 Different approaches for server-side SDK architecture アーキテクチャ 仕組み 利点 欠点 "Direct" API Bridge フラグ評価のたびに、SDKがフラグ管理サービスのAPI(REST/gRPC)を直接呼び出す。 SDKの実装がシンプル。評価ロジックをサービス側に集約できる。 ネットワークのオーバーヘッドが大きく、パフォーマンスが低い。ネットワーク障害の影響を受けやすい。 API with Cache APIからのレスポンスをSDKがキャッシュし、後続の同じリクエストにはキャッシュから応答する。 ネットワークトラフィックを削減できる。 初回リクエストは遅い。キャッシュされていない動的な評価には不向き。キャッシュの更新ラグがある。 Local Evaluation SDKがフラグの全設定データをローカルに保持し、評価をメモリ上で完結させる。設定の更新はストリーミング(SSE, WebSockets)や定期的なポーリングで行う。 非常に高速でネットワークの影響を受けない。耐障害性が高い。 SDKの実装が複雑。全設定データを保持するため、メモリ使用量が増加する可能性がある。 これらのうち、Local Evaluationが最も高速かつ効率的で耐障害性に優れています。しかし、フラグ管理システムが、単純にAPIしか提供していない場合、OpenFeature Providerでこれらの仕組みを頑張って実装する必要があります。 また、独自SDKを提供している場合、そのSDKに対して呼び出しを行うOpenFeature Providerを利用もしくは作成すれば、実装コストを下げることが可能です。 さて、AppConfigの場合、どうなるのでしょうか? 先ほどお見せしたAppConfigの図を再掲します。 引用: https://docs.aws.amazon.com/ja_jp/appconfig/latest/userguide/appconfig-agent.html Agentとアプリケーションを一つのシステムとするなら、Local Evaluationと言えそうです。ただし、1の部分でローカルホスト宛に通信が発生しており、この通信も過負荷時、極まれに失敗することがあります。また、AppConfigでターゲティングを行う場合、このアーキテクチャは変更できません。 このAppConfig Agentを利用するProviderを実装する場合、最も単純な仕組みとするなら、対Agent向けに通信を行い、そちらでフラグ評価を実行する"Direct" API Bridge のアプローチとなります。我々は、OpenFeature Providerとして、 https://github.com/Arthur1/openfeature-provider-go-aws-appconfig を利用しています。 AppConfigにフラグを追加更新、デプロイするには? AppConfigでは、マネージメントコンソールで、直接設定データを作成し、デプロイを実行するケースのほか、 https://docs.aws.amazon.com/ja_jp/appconfig/latest/userguide/appconfig-type-reference-feature-flags.html のJSONスキーマに基づいた設定データを作成し、AWS SDKやCLIを使ってフラグデータセットの更新とデプロイによる設定反映が行えます。デプロイの際、デプロイ戦略を使うことにより、段階的リリースも行うことが可能です。データセットの定義は、コードベースでGit管理しており、CI/CDにより、AWS環境に反映しています。 以下の図の通り、GitHub Actionsでマージ時に差分を検知し、データセットをS3にアップロードすると、Step functionsの各ステップでLambdaが実行され、AppConfigへの設定反映とデプロイが実行されます。 このワークフローでは、定義済みデプロイ戦略: AppConfig.AllAtOnceと同様の戦略を使用しているため、段階的ではなくデプロイ完了後即時リリースとなります。また、手動トリガーでのワークフローも用意しています。 appconfig workflow フラグデータセットについて コードベースにある設定ファイルの形式は、JSONではなくYAMLにしています。ダブルクォートのエスケープなど色々考慮しないといけないためです。 AWS.AppConfig.FeatureFlags のJSONスキーマに準拠したデータ構造で、YAMLファイルに記述して管理しています。これをAppConfigへ反映する際には、JSONに変換します。 version: "1" flags: sampleflag: description: 説明 attributes: attribute_name: description: 属性説明 constraints: type : number minimum: 1 values: sampleflag: enabled: false # または _variants また、ターゲティングでマルチバリアントフラグを利用するためには、以下のように _variants にそれぞれの評価ルールを定義したデータセットを定義する必要があります。 OpenFeatureの評価コンテキストに格納された値を利用し、ruleに一致するかを判定します。以下の設定例だと、評価コンテキストに、 environment=dev と格納された場合は、一番目が選択されます。 values: sign: _variants: - name: High cache enabled: true rule: (eq $environment "dev" ) attributeValues: cache_interval: 3600 expires_in: 3600 - name: default enabled: true attributeValues: cache_interval: 300 expires_in: 3600 attributeValues を併せて定義することにより、そのフラグに関連づけられる属性を追加することができます。OpenFeatureでは、それをMetadataとして取得することが可能です。 AppConfigのフラグ属性は、Boolean形式のFeatureFlagの場合、enableの時のみ利用でき、またデータセット定義で、 _variants を使った場合でないと利用できません。 それにより、アプリケーションに様々な特性を付与することもできます。 上記のデータセット例は、AWS KMSを利用した署名リクエストを制御するフラグを想定しているのですが、毎回署名するのはコストがかかるため、署名をキャッシュしたり有効期限を設定するのに利用したりしています。 ちなみに、OpenFeature Go SDKだと、Metadataは型 map[string]any となっており、フラグ管理システムの定義または、Providerのパース処理によっては、型情報が失われてしまいます。 以下のように、ヘルパー関数を実装するのが良いでしょう。 func GetInt(metadata openfeature.FlagMetadata, key string ) (result int64 , err error ) { // map[string]any なので、int64, float64 の両方を試す intVal, err := metadata.GetInt(key) if err == nil { result = intVal return } floatVal, err := metadata.GetFloat(key) if err == nil { result = int64 (floatVal) return } return } func GetStringArray(metadata openfeature.FlagMetadata, key string ) ([] string , error ) { v, ok := metadata[key] if !ok { return nil , fmt.Errorf( "key %s does not exist in FlagMetadata" , key) } s, ok := v.([]any) if !ok { return nil , fmt.Errorf( "key %s is not an array" , key) } var strArr [] string for _, v := range s { if str, ok := v.( string ); ok { strArr = append (strArr, str) } } return strArr, nil } 注意点 FeatureFlagやFFaaSを利用すると、色々いいことづくめのように見えますが、必ずしも万能ではありません。 1. 後方互換性を保てないリリースをした場合、問題が見つかった後にさっと前の状態に戻すのは難しい コードベースをできるだけ最新に保つトランクベース開発には便利ですが、時には、後方互換性を損なう機能をリリースしないといけない場合があります。 問題が見つかった場合、フラグの切り替えで、コードパスの切り替えはできるかもしれませんが、データ構造が変わってしまった場合、緊急メンテナンスを実施し、データの更新が必要になるかもしれません。 そのため、時にはFeatureブランチで、mainブランチとは長期間切り離した状態で、開発した方が安全な可能性はあります。ただし、その戦略をとった場合、合流時にいわゆるビックバンリリースとなり、作業は大変になるかと思います。 2. 不特定多数のターゲティングには向いているが、特定ユーザの認可の代わりにはならない 認可は、ユーザーが特定のアクションを実行したり、リソースにアクセスしたりする 権限があるかどうか を検証するセキュリティメカニズムのため、目的が異なります。 何でもかんでも、FeatureFlagで制御するのはよろしくない です。 3. 不要になったRelease Flagのお掃除が面倒 リリース後安定運用でき、ロールバックしないとわかった場合、そのフラグは不要となります。ただ、FFaaSの設定を気軽に触りたくないし、そのままにした場合、もうフラグ設定を参照する必要がないのに、不要なアクセスで課金されてしまいます。 削除するにしても、併せてコードも書き換える必要があります。フラグを使わずに済むのならば、その方がリリース後に修正が不要なため、簡潔に実装できます。また、リリース後にも、無駄に課金されることを考えると、場合によっては、環境変数やローカル設定経由の定義で済ませた方が良いということもあります。 一方で、高負荷時やアラートをトリガーに機能オフにするキルスイッチとして、リリース後にもOps Flag的に利用するのなら、FFaasを利用する価値はあります。 おわりに OpenFeatureを中心に、FeatureFlagの現在についてお話ししました。参考になれば幸いです。 BASEでは、今後の事業成長を支えるエンジニアを募集しています。 ご興味があれば、ぜひ採用情報をご覧ください。 採用情報 | BASE, Inc. - BASE, Inc. 明日のBASEアドベントカレンダーは小笠原さんの記事です。お楽しみに!
はじめに この記事はBASEアドベントカレンダー2025の19日目の記事です。 こんにちは。BASEのプロダクト開発チームでバックエンドエンジニアをしている大塚です。 この記事ではNew Relicのダッシュボードを「動く仕様書」にするために、機能ごとに標準化されたダッシュボードをTerraformで手軽に出来るようにする取り組みを紹介させていただきます。 まだまだ構想と検証段階なので、こんなことしようとしているよというニュアンスで紹介させていただきます! New RelicとTerraformについて 取り組みについての紹介に入る前に、New RelicとTerraformについて簡単に紹介させていただきます。 New Relicとは New Relicは、アプリケーションパフォーマンス監視(APM)やインフラストラクチャ監視を提供するクラウドベースの可観測性プラットフォームです。 主な特徴: リアルタイム監視: アプリケーションやインフラストラクチャのパフォーマンスをリアルタイムで可視化 分散トレーシング: マイクロサービスアーキテクチャにおけるリクエストの流れを追跡 カスタムダッシュボード: NRQLというクエリ言語を使って、独自のダッシュボードを作成可能 アラート機能: 異常検知時に通知を送信し、迅速な対応をサポート Terraformとは Terraformは、HashiCorpが開発したInfrastructure as Code(IaC)ツールです。コードでインフラストラクチャを定義し、バージョン管理や自動化を実現してくれます。 主な特徴: 宣言的な記法: HCL(HashiCorp Configuration Language)を使って、あるべき状態を定義 マルチクラウド対応: AWS、GCP、Azureなど、様々なクラウドプロバイダーに対応 状態管理: インフラの現在の状態を追跡し、差分を検出 豊富なプロバイダー: New Relicを含む多くのサービスに対応したプロバイダーが存在 New Relic × Terraformの組み合わせ TerraformのNew Relicプロバイダーを使用することで、ダッシュボード、アラートポリシーなどをコードで管理できます。 これにより以下のようなことが実現できます。 ダッシュボードの構成をGitで管理し、レビューや変更履歴の追跡が可能に 環境ごと(開発、ステージング、本番)に同じ構成のダッシュボードを簡単に展開 チーム全体で標準化されたダッシュボードを共有 手動操作によるミスを削減 これらのメリットを活かして、BASEではNew Relicのダッシュボードを「動く仕様書」として機能させる取り組みを進めています。 ただ、メリットがある一方、Terraform化には以下のような課題もあります。 全リソースを管理できないため、手動での管理とTerraformでの管理のリソースが混在 Terraform管理のリソースを手動で変更することによる競合 これらの課題は、Terraform管理するリソースを絞ることで回避しています。 今後Terraform管理のリソースを増やしていく想定なので、これらの課題に対しての対応も合わせて検討を進めていく予定です。 なぜやるのか 課題 BASEでは各機能のパフォーマンスや挙動を監視するためにNew Relicのダッシュボードを活用していますが、以下のような課題がありました。 ダッシュボードの属人化: 個々のエンジニアが必要に応じて手動でダッシュボードを作成するため、レイアウトや粒度がバラバラになりがち メンテナンスの困難さ: 機能追加や変更があった際、ダッシュボードの更新が漏れたり、誰がメンテナンスすべきか不明確になっている 標準化の欠如: 新しい機能を追加する際に「どんなメトリクスを見るべきか」の指針がなく、監視の抜け漏れが発生しやすい 解決策 これらの課題を解決するために、Terraformを使ってダッシュボードをコード化することで、以下を実現できると考えました。 課題の解決に加えオブザーバビリティを向上させるためにも有効だと考えています。 1. ダッシュボードを「動く仕様書」に 各機能に対して標準化されたダッシュボードレイアウトを定義することで、そのダッシュボード自体が機能の仕様や監視すべきポイントを示す「動く仕様書」として機能します。新しくジョインしたメンバーも、ダッシュボードを見れば「この機能で何を監視すべきか」や「どのように動作しているか」が一目でわかる。 2. コードレビューによる品質担保 ダッシュボードの構成をコードで管理することで、Pull Requestを通じたレビュープロセスを導入できます。これにより、チーム全体で監視項目の妥当性を検討し、知見を共有できる。 構成 New RelicのダッシュボードをTerraform管理する用のリポジトリを用意してコードを管理しています。 構成としてはざっくり以下のようになっています。 ├── main . tf # New Relic provider の設定だけを持つルートモジュール ├── variables . tf # New Relic アカウント情報など共通変数 ├── modules / # 再利用可能な Terraform module 群 │ └── dashboard / │ └── standard_feature_dashboard / │ ├── main . tf # ダッシュボード共通レイアウトの実装 │ └── outputs . tf # 作成したダッシュボードの URL / ID を出力 │ ├── dashboards / # ダッシュボード定義 │ └── ⚪︎⚪︎⚪︎_dashboard . tf # ⚪︎⚪︎⚪︎ダッシュボード │ └── .github / └── ISSUE_TEMPLATE / └── standard_feature_dashboard . yml # ダッシュボード作成依頼用の Issue Form ルートには New Relic provider と共通変数だけを置き、実際のダッシュボード定義は modules/dashboard/... の共通 module dashboards/... 配下の各tfファイルから呼び出す構成にしています。 これにより、ダッシュボードのレイアウトを 1 箇所で統一しつつ、機能単位では URL やトランザクション名などの差分だけを記述すればよい運用になっています。 運用 BASEの機能開発は複数ある開発チームが並行で進めています。 開発の後半でNew Relicのダッシュボードや監視項目の検討、アラートの設定などを実施している開発チームが多いので、その際に標準ダッシュボードを作成するフローを検討しています。 現状、各チームのエンジニアがダッシュボード用のtfファイルをいじるのではなく、知見を持っているメンバーが開発チームの依頼を受けてtfファイルを作成するというフローを採用しています。 GitHub Issue Form と組み合わせることで、「依頼 → Terraform 化 → New Relic 反映」までをスムーズに回せるようにしているのもポイントです。 成果物 Terraformで出力されるダッシュボードは以下のようなダッシュボードです。 複数ページに分かれていますが、例としてbackendアプリケーションのページを紹介します。 New Relicダッシュボード 汎用的なダッシュボードにするために、グラフの種類などはかなり少なめで、アプリケーションの動態が分かる最低限の内容にしています。 トランザクション一覧 スループット エラーレート など Markdownで自由に記載できる項目も用意し、機能のページや仕様書へのリンクなどを置けるようになっています。 おわりに New Relicのダッシュボードを機能ごとの汎用的な「動く仕様書」としてTerraformで出力し、組織のオブザーバビリティを高める活動を紹介させていただきました。 まだ検証段階ですが、こちらの取り組みを組織に広げることで組織のオブザーバビリティを向上させていきたいと思っています。 New RelicとTerraformはダッシュボード以外にも色々なことが出来るので、皆さんもぜひ試してみてください。 BASEでは組織のオブザーバビリティを向上させる様々な取り組みを実施中です。 ご興味のある方は以下のリンクから採用情報などもみていただけると幸いです。 binc.jp
BASE ADVENT CALENDAR 2025 DAY.18 はじめに こんにちは!Data Strategy teamでデータエンジニアをしているshota.imazekiです。 昨今、業務の中でLLMを活用する場面が増えてきており、その流れを受けて弊社でもさまざまな取り組みを進めています。本記事では、その中の一つとして今年挑戦した「SQL自動生成」について紹介します。 SQL自動生成のスコープ 読み進めるにあたって誤解が生じないよう、本記事における「SQL自動生成」のスコープをあらかじめ整理しておきます。 分析基盤上で、分析者が分析目的で実行するSQLを自動生成の対象とします 主に SELECT文の生成を対象とし、CREATE / DELETE / UPDATE といったDDL・DMLは対象外とします LLMを用い、自然言語を入力としてSQLを生成することを前提とします。BIツールのように、UI操作による分析体験を目指すものではありません 取り組んだ背景 BASEでは、分析基盤としてBigQueryを採用し、BIツールにはLookerを利用しています。Lookerは社内で広く活用されており、現在では社員のおよそ3分の1が、毎週Lookerを通じて何らかのデータを確認している状況です。 一方で、SQLを直接書いて自由に分析できるユーザーはごく一部に限られているという課題も抱えていました。Looker上の既存のExploreやダッシュボードでは十分でないケースにおいても、SQLを書くハードルの高さから、簡単なデータ抽出依頼であってもData Strategy teamに来ることが度々ありました。 この状況は、分析者の裾野を広げたいという観点だけでなく、Data Strategy teamの負荷軽減という点でも、改善の余地があると感じていました。そこで今回、「SQLを書く」というボトルネックをLLMでどこまで解消できるのかを検証する取り組みとして、SQL自動生成に挑戦しました。 SQL自動生成における課題 SQL自動生成における課題は大きく分けて2つあると考えてます。 1. 膨大なテーブル構成に対する理解 BASEの分析基盤には数百個のテーブルが存在しており、分析を行う際には次のような知識が求められます。 ある指標や事象を分析する際に、どのテーブルを参照すべきか 複数のデータを組み合わせて分析したい場合に、どのようなキーでテーブルを結合するのか これらの前提をLLMが理解できていない場合、関係のないテーブルを参照したり、そもそも存在しないテーブルを用いたSQLを生成してしまうことがあります。そのようなSQLは、当然ながら分析に利用することはできません。 2. KPIなどのビジネス指標への理解 もう一つの課題は、BASE固有のビジネス指標(KPI)に対する理解です。 例えば、BASEにおけるGMV(流通総額)は、生データとしてそのまま存在しているわけではなく、複数のテーブルを組み合わせた上で、特定の条件に基づいて集計することで初めて算出される指標です。そのため、事前情報がない状態で「GMVを出して」とLLMに指示した場合、BASEが定義するGMVとは異なる値が生成されてしまう可能性があります。 課題の本質 LLM自体はSQLを書くための一般的な知識を十分に備えています。しかし、BASE固有のテーブル構成や業務ドメイン、指標の定義については、LLMが知り得るものではありません。 この「ドメイン知識の欠如」こそが、SQL自動生成における最大の課題だと考えています。そして、これら2つの課題を解決するために、今回の取り組みではディメンショナル・モデリングを用いてテーブル構成を再整理するというアプローチを採用しました。 ディメンショナル・モデリングを用いたSQL自動生成 ディメンショナル・モデリングとは ディメンショナル・モデリングは、分析用途を主眼に置いたデータモデリング手法の一つです。一般に、業務システムで利用されるトランザクション処理向けのデータベースでは、リレーショナルデータモデリングが採用されることが多く、データは正規化された形で設計されます。 このような設計は、個々のトランザクションを正確かつ効率的に処理することを目的としており、その結果として、分析のしやすさよりも更新や整合性を重視した構造になりがちです。そのため、分析を行う際には多くのテーブルを結合する必要があったり、データの意味を理解するために多くの前提知識が求められるケースも少なくありません。 一方、ディメンショナル・モデリングは、ユーザーがデータを分析しやすいことを重視してテーブル構造を設計することを目的としています。 分析の軸となるディメンションと、数値を持つファクトを中心にデータを整理することで、クエリの記述や指標の理解がしやすい構造を実現することが、このディメンショナル・モデリングのゴールとなります。 スタースキーマについて ディメンショナル・モデリングを語る上で、代表的な構成として スタースキーマ があります。スタースキーマは、分析の中心となるファクトテーブルと、その周囲に配置されるディメンションテーブルによって構成されるシンプルなデータ構造です。 ファクトテーブルには、売上金額や注文件数といった集計対象となる数値データが格納され、ディメンションテーブルには、日付・商品・購入者など、分析の切り口となる属性情報がまとまった形で格納されます。これらが、ファクトテーブルを中心に放射状に結合されることから、スタースキーマと呼ばれています。 出典:スタースキーマとは - Power BI|Microsoft Learn https://learn.microsoft.com/ja-jp/power-bi/guidance/star-schema この構成の特徴は、 テーブルの役割が明確であること 結合パターンが限定されること 分析クエリを直感的に記述しやすいこと といった点にあります。そのため、分析者にとって理解しやすいだけでなく、どのテーブルをどのように結合すればよいかを推測しやすい構造になっています。 今回のSQL自動生成の文脈においても、スタースキーマのように構造が明確なデータモデルは、LLMがテーブル間の関係性を誤解しにくく、安定したSQLを生成しやすいという点で相性が良いと考えました。 出典:スタースキーマ (Star Schema)|Databricks https://www.databricks.com/jp/glossary/star-schema なお、分析用途においては One Big Table のように、あらかじめすべてを結合したテーブルを用意する選択肢もあります。一方で今回は、まずはテーブルの役割や関係性を明確にし、段階的にモデルを育てていくことを重視し、スタースキーマから取り組むことにしました。 ディメンショナル・モデリングの導入 BASEの分析基盤では、これまでディメンショナル・モデリングは採用されておらず、データレイクに近い生テーブルや、用途ごとに作成されたデータマートに対して直接クエリを実行するケースが多くありました。このような構成は柔軟性が高い一方で、どのテーブルをどのように使えばよいのかを理解するための前提知識が必要となり、SQLを直接書いて分析するハードルを高めていた要因の一つだと考えています。 今回のSQL自動生成の取り組みでは、この課題に向き合うと同時に、分析者にとっても、LLMにとっても理解しやすいデータ構造を用意することが重要だと考えました。そのため、SQL自動生成の検証と並行して、ディメンショナル・モデリングを導入することにしました。 過去にData Strategy teamへデータ抽出の依頼が集中していた時期があり、その過程で社内の主要なビジネス指標や集計ロジックについて一定の知見が蓄積されていたこともあり、モデリング自体は比較的スムーズに進めることができました。 ディメンショナル・モデリングを導入したことで、結果としてLLMにとっては次のようなメリットが得られました。 参照すべきテーブル数が、数百規模から数個にまで絞られた GMVのようなビジネス指標をあらかじめ集計済みのファクトテーブルとして定義することで、指標の算出ロジックを都度推論する必要がなくなった LLMの選定 ディメンショナル・モデリングを導入した後、次に検討すべきポイントとなったのが、どのLLMを用いてSQL自動生成を行うかという点でした。今回の対象ユーザーは、SQLに習熟していない社内の分析者全般を想定しています。そのため、GitHub Copilotのようにエディタ上での利用を前提とするものや、ローカルLLMのように各自のPCで環境構築が必要となる手法は、利用のハードルが高いと判断しました。 そこで、できる限り導入・利用のハードルが低く、日常業務の延長線上で使える選択肢として、 BigQuery上で直接利用できるGemini in BigQuery ブラウザから利用可能でプロンプトなどを柔軟にカスタマイズできるGPTs の2つに候補を絞って検討を進めることにしました。GPTsは有料プランの機能ではあるものの、社内の多くのユーザーがすでに利用可能な環境であったため、今回の取り組みにおける利用ハードルは低いと判断しています。 Gemini in BigQuery Gemini in BigQueryは、その名の通りBigQuery上で利用できるGeminiです。クエリエディタ内に自然言語をコメントとして記述することで、SQLを自動生成することができます。生成されたSQLはそのままBigQueryのコンソール上で実行されるため、利用ハードルという点では最も低い選択肢だと感じていました。検証時点では無料で利用できたことも、大きな魅力の一つでした。 しかし、今回のディメンショナル・モデリングを前提としたSQL自動生成という文脈では、以下の点から相性が悪いと判断しました。 クエリエディタで、最近表示またはクエリしたテーブルに関する SQL コメントを記述します。 ( 公式ドキュメント より) この仕様上、ユーザーが直前に閲覧・クエリしていたテーブルがディメンショナル・モデリングされたテーブル以外であった場合、意図しないテーブルまで参照してSQLが生成されてしまう可能性があります。 今回の取り組みでは、参照すべきテーブルを明確に制御することが重要でしたが、その制御方法が見つからなかったため、Gemini in BigQueryの採用は見送ることにしました。 GPTs GPTsは、ChatGPTを特定の用途や目的に合わせてカスタマイズできる機能です。指示(Instructions)や知識(Knowledge)を事前に与えることで、特定のドメインやユースケースに特化した振る舞いをさせることができます。以下のスクリーンショットのように、あらかじめ指示や知識を設定することで、SQL自動生成に特化したGPTを作成しました。 今回の取り組みでは、GPTsに対して主に 「指示」と「知識」 の2つを設定しています。 指示 指示には、GPTがどのような役割を担い、どのように振る舞うべきかをまとめました。具体的には、以下のような内容を記述しています。 あなたは BigQueryに精通したSQLエンジニア であること ディメンショナル・モデリングされたテーブル構成や、想定される結合方法を前提にSQLを生成すること ユーザーからのリクエストが、ディメンショナル・モデリングされたテーブル群では対応できない場合は、「Data Strategy teamに問い合わせてください」といった旨の文言を返すこと その他、補足しておきたいBASE固有のドメイン知識など ここで重要なのは、「何でもSQLを生成する」のではなく、対応できないケースでは無理に生成せず、適切にエスカレーションさせる振る舞いを明示している点です。 知識 知識には、GPTが参照できる具体的な情報として、以下の2種類のファイルを設定しています。 ディメンショナル・モデリングされたテーブル群のDDL 各カラムには必ず description を記載し、どのカラムが何を意味するのかをGPTが正確に把握できるようにしています サンプルクエリ ディメンショナル・モデリングだけでは意図が伝わりづらい集計については、実際のSQL例を2つほど与え、生成されるクエリの方向性を補強しています これにより、GPTはテーブル構造や指標の意味を単なるDDLの羅列としてではなく、「どのように使われるのか」という文脈込みで理解できるようになります。 検証結果 作成したGPTが実際の業務で利用可能かを確認するため、あらかじめ用意しておいた約10問の検証用SQL自動生成問題をGPTに解かせてみました。 なお、以下に掲載するSQLは、テーブル名やカラム名をマスク、もしくは実際とは異なる名称に置き換えています。 2025年4月にGMVが100万円以上あったショップの抽出 期間指定・集計・結合といった基本的な構文に加え、HAVING句を用いた条件指定まで含めたSQLを生成することができました。 SELECT u.shop_id, -- ショップID(user_idの文字列) SUM (f.gmv) AS total_gmv -- 合計GMV FROM ` xxx . xxx .fact_tables` f JOIN ` xxx . xxx .dim_users` u ON f.user_id = u.user_id WHERE f.ordered >= ' 2025-04-01 ' -- 2025年4月の開始 AND f.ordered < ' 2025-05-01 ' -- 2025年4月の終了 GROUP BY u.shop_id HAVING total_gmv >= 1000000 -- 100万円以上(単位:円) ORDER BY total_gmv DESC ; 都道府県別のユーザー数ランキング 単純な集計にとどまらず、ウィンドウ関数(RANK)を用いたランキング処理も正しく生成できていることが確認できました。 SELECT prefecture, -- 都道府県 COUNT (*) AS user_count, -- ユーザー数 RANK () OVER ( ORDER BY COUNT (*) DESC ) AS rank -- ユーザー数の多い順に順位付け FROM ` xxx . xxx .dim_users` GROUP BY prefecture ORDER BY rank ; 上記の例以外にも、CASE文、WITH句、各種ウィンドウ関数などを含むクエリについても検証を行いましたが、分析用途でよく使われるSELECTクエリについては、ある程度の複雑さまで対応できそうという印象を持ちました。また、事前に用意していた10問の検証ケースについては、すべて意図どおりのSQLを生成することができています。 そのため、本取り組みはPoCに留めるのではなく、社内向けに展開し、現在は分析者を中心に実際の業務で利用されています。 今後の展望 今回は、分析基盤におけるSQL自動生成というテーマで取り組みを紹介しました。ここでは、現時点で見えている今後の展望について整理します。 テーブルやカラムの追加による対応範囲の拡充 ディメンショナル・モデリングの導入によって、把握すべきテーブル数は大きく絞られました。一方で、その構成に含まれていないデータについては、現時点ではSQL自動生成の対象外となっているのも事実です。今後は、スタースキーマ型のテーブルを段階的に拡充していくことで、より多くの分析ニーズに対応できるようにしていきたいと考えています。 対応範囲を広げつつも、テーブル構造の分かりやすさを保つことを意識しながら、モデルを育てていく予定です。 ハルシネーション対策 LLMを利用する以上、ハルシネーションのリスクを完全に排除することはできません。 ディメンショナル・モデリングの導入や指示文の工夫によって、一定の抑制は可能ですが、常に正しいSQLが生成されることを前提にするのは現実的ではないと考えています。 そのため、利用者側にも最低限の前提知識は必要になります。高度なSQLを書くスキルまでは求めませんが、 ディメンショナル・モデリングされたテーブル構成の理解 生成されたSQLを読み、妥当性を判断できる力 は重要だと考えています。 今後は、社内勉強会などを通じてこれらの理解を深め、LLMを過信せず、うまく付き合っていくための土台作りにも取り組んでいきたいと思います。 おわりに BASEでは、LLM活用に限らず、分析基盤全体の改善に継続的に取り組んでいます。 こうした取り組みにご興味のある方は、ぜひお気軽にご応募ください! A-1.Tech_データエンジニア / BASE株式会社 明日のBASEアドベントカレンダーは大塚さんの記事です。お楽しみに!
はじめに この記事は BASE アドベントカレンダー17日目の記事です。 devblog.thebase.in こんにちは、BASE CSE Group のグループマネージャーをしている @izuhara です。 BASEは「誰でもかんたんにネットショップを開設できる」サービスとして成長し、多くのショップオーナーに利用されてきました。その裏側では、事業規模が拡大するにつれ、オペレーションも複雑さを増し、バックオフィスやオペレーションを行うチームに属人化や手作業が蓄積していくという課題が生まれていました。 こうした背景のもと、事業運営を技術で支えるために立ち上がったのが CSE(Corporate Solution Engineering)チーム です。 現在CSEでは、以下の3つを柱として業務改善・自動化を推進しています。 月次売上計上業務の自動化対応 決済を中心とした社内システムの構築 AI活用を前提とした業務の再構築 本記事では、BASEの裏側を支えるCSEチームの変遷をこの3フェーズに分けて紹介します。 フェーズ1:経理向け月次売上計上業務の自動化(2020年〜) CSEが最初に向き合ったのは、BASEの事業基盤となる月次売上計上業務でした。 当時、売上データの集計や修正作業はすべて手作業で行われており、月次締めの度に数日間にわたる作業が発生していました。 さらに、上場直後であったこともありJ-SOXへの対応強化が求められ、売上金の透明性や監査対応の厳密さが一段と必要とされるタイミングでもありました。 CSEの主な取り組み 売上データ集計・計上処理の自動化 BASE側の決済データと各決済サービスの入金データ、ショップ売上金の整合性チェック機能の構築 詳細はこちらをご覧ください。 devblog.thebase.in 売上計上の整合性チェック 成果 月次作業が数日→数時間に短縮 クエリの叩き間違いなどによる売上計上のヒューマンエラーが大幅に減少 経理チームが安心して業務を進められる安定したプロセスを提供 このフェーズは、CSEがまず「足元の重要業務を支えるエンジニアリングチーム」として役割を確立した時期でした。 フェーズ2:決済を中心とした社内システムの構築(2022年〜) 事業成長に伴い、経理領域以外にも自動化ニーズが急増したタイミングです。 また、このタイミングでIT統制領域が Product Governance チームとして分離され、CSEはより社内業務改善に特化した組織 へと方向転換しました。 社内ではEUC(End User Computing)によるスプレッドシート・手入力運用が多く、業務のブラックボックス化やミスの温床になりつつありました。これらを計画的にシステム化し、再現性と可視性の高い業務基盤へと移行していくことが求められました。 CSEの主な取り組み 請求書発行プロセスの自動化、債権回収モニタリング機能の開発 kintoneを活用した業務アプリケーションの高速構築 EUC依存からの脱却と、業務のシステム化の整備 インボイス制度への対応 成果 手運用で行われていた社内オペレーションの多くをシステム化 kintone等を活用し、小さく始めて早く改善する内製プロセスを社内に定着 請求や債権管理など、ミスが許されない領域で運用リスクを大幅に低減 このフェーズを通じて、CSEは「社内システムの開発パートナー」としての立ち位置を確立しました。 フェーズ3:AI活用を前提とした業務の再構築(2025年〜) 生成AIの登場により、業務改善は新たなステージへ入りました。 従来の「人がやっていた作業を自動化する」から一歩進み、業務プロセスそのものをAI前提で再設計するフェーズです。 まずPoCとして着手したのは、社内でも問い合わせが多い人事・労務領域のAI(RAG)による自動応答です。FAQの回答や書類手続きの案内など、繰り返し発生するコミュニケーションをAIで対応する仕組みづくりを進めました。 PoCで得たAIによる回答品質の高め方や、セキュアな情報をAIで取り扱うための基盤構築などは、その後のカスタマーサポート業務のAI導入にも活かされています。 CSEの主な取り組み 人事・労務・総務など、社内問い合わせのAI自動応答の構築 カスタマーサポート業務のAIによる業務置き換え 社内データを安全に扱うための基盤整備 詳細はこちらの記事をご覧ください。 devblog.thebase.in 社内問い合わせのAI自動応答 成果 問い合わせ対応のAIによる自己解決 AIを活用した業務改善の成功例が蓄積し、AI活用の窓口としての役割の拡がり AI活用はまだまだ始まったばかりで、改善途中にも新たな技術革新が繰り返されているところですが、このフェーズを通じて、CSEは「AI活用で業務を再構築」する新たな改善施策を進められるようになりました。 おわりに CSEチームは、BASEの事業成長に合わせて 「業務の自動化 → 社内システム構築 → AI活用で業務を再構築」 という進化を続けてきました。 今後も社内のあらゆる業務がAIで再構築されていく未来を見据え、BASEの事業を支える目に見えない基盤をつくり続けていきます。 BASEでは、今後の事業成長を支えるエンジニアを募集しています。 ご興味があれば、ぜひ採用情報をご覧ください。 binc.jp 明日のBASEアドベントカレンダーは @ImazekiShota さんの記事です。お楽しみに!
はじめに この記事はBASEアドベントカレンダー2025の16日目の記事です。 こんにちは。Pay ID プラットフォーム Group で エンジニアをしている noji です。最近は Pay ID の認証基盤のフロントエンド開発を担当しています。 本記事では BASE のショップや Pay ID アプリでの買い物時にカートでの Pay ID ログイン機能を提供している JavaScript(以後 payid-js)のビルド環境を webpack/Babel から esbuild に移行した話を紹介します。 payid-js について payid-js は Pay ID ログイン機能を提供している埋め込み用の JavaScript です。Pay ID ログインすることで、Pay ID に登録されている住所情報や決済手段情報を連携することで、ユーザーはスムーズに購入手続きを進めることができます。 BASE のカートのフロントエンドで payid-js を読み込み、用意された関数を呼び出すと画面上にログイン用の画面が iframe 上に表示され、Pay ID にログインできます。iframe 内でログイン処理を行い、結果を postMessage API を使ってカートのフロントエンドに通知します。 payid-js は iframe 内外でやり取りを行うインターフェースを提供しており、iframe 内でログインが完了すると、結果をカートのフロントエンドに返すようになっています。(iframe の内側の画面については別システム) 技術としては、TypeScript で実装されており、ビルドには webpack と Babel を使用していました。 移行背景 payid-js は BASE のカートと iframe で表示される Pay ID ログイン画面の橋渡しをするだけのコンポーネントなので、軽量な JavaScript です。 軽量であるので、webpack のビルドに時間がかかるとは感じていませんでしたが webpack 時代のバージョンアップや設定変更が大変 Babel を含む関連ライブラリの設定が複雑 依存関係の脆弱性が多い payid-js には webpack ほど高度な機能が不要である などの課題があり、よりシンプルなビルドツールへの移行を検討しました。 esbuild を選んだ理由 候補として esbuild、vite、Rollup などがありましたが、最終的に esbuild を選択しました。理由は以下です。 シンプルで高速なビルドが可能 Go 製でビルドが非常に速いのに加えて、設定もシンプルでわかりやすく、TypeScriptのトランスパイルも内蔵されていてBabelも不要 依存関係が少なく、メンテナンスコストや脆弱性リスクが低い webpack や Babel に比べて関連ライブラリ等の依存関係が少なく、アップデートや脆弱性の対応に追われる負荷が軽減されそう 他ツールとの比較 vite:SPA 向けの開発サーバーは強力だけど、payid-js のような埋め込み用 JavaScript にはオーバースペック Rollup:esbuild ほど高速ではなく、設定もやや複雑になる。ライブラリ向けには良いが、今回は見送り esbuild は HMR(Hot Module Replacement) をサポートしていないですが、payid-js は埋め込み用の JavaScript であり、開発時に HMR は必要ないため問題ありませんでした。 参考 esbuild vite Rollup 移行で詰まったポイント ローカルの 開発サーバーの構築 今までは webpack-dev-server を使用してローカル開発環境を構築していました。 webpack-dev-server はビルドしたアセットをメモリに保持し、変更があれば自動で配信内容を更新してくれる開発サーバーを内蔵しています。 Docker からのアクセスでも常に最新が返ってくるため、ビルド・配信・更新反映をひとまとめに解決してくれる優れた仕組みでした。 一方、esbuildにはwebpack-dev-serverのような開発サーバーは内蔵されておらず、あくまで”ビルド”のみの機能です。今回は serve で簡易的に http-server を立ち上げるスクリプトを用意しました。watch だけだと変更を検知して Docker コンテナに反映させることができなかったので、 chokidar も利用し、変更を検知して明示的に再ビルドできるようにしました。 #!/usr/bin/env node import path from "path" ; import { fileURLToPath } from "url" ; import * as esbuild from "esbuild" ; import { spawn } from "child_process" ; import chokidar from "chokidar" ; const __filename = fileURLToPath ( import . meta . url ) ; const __dirname = path . dirname ( __filename ) ; const outdir = path . resolve ( __dirname , "dist" ) ; // esbuild の watch 用コンテキストを作成 const ctx = await esbuild . context ({ entryPoints : [ path . resolve ( __dirname , "src" , "index.ts" )] , bundle : true , sourcemap : true , platform : "browser" , outdir , entryNames : "bundle" , minify : true , loader : { ".html" : "text" , } , }) ; await ctx . watch () ; console . log ( "esbuild: watching" , outdir ) ; // chokidar でファイル変更を監視して rebuild const watcher = chokidar . watch ([ path . resolve ( __dirname , "src" )] , { ignoreInitial : true , usePolling : true , interval : 100 , }) ; let rebuilding = false ; async function scheduleRebuild ( event , filePath ) { if ( rebuilding ) return; rebuilding = true ; console . log ( `change detected ( ${ event } ):` , filePath ) ; try { await ctx . rebuild () ; console . log ( "esbuild: rebuild complete" ) ; } finally { setTimeout (() => ( rebuilding = false ) , 50 ) ; } } watcher . on ( "add" , ( p ) => scheduleRebuild ( "add" , p )) ; watcher . on ( "change" , ( p ) => scheduleRebuild ( "change" , p )) ; watcher . on ( "unlink" , ( p ) => scheduleRebuild ( "unlink" , p )) ; // ローカルサーバー (npx serve) spawn ( "npx" , [ "serve" , "-s" , outdir , "-l" , "9000" ] , { stdio : "inherit" , shell : true , }) ; このスクリプトを実行すると、chokidar がソースコードの変更を監視し、変更があった場合に再ビルドを行います。また、 npx serve を使用してローカルサーバーを立ち上げ、ブラウザから埋め込み用 JavaScript を確認できるようにしています。 ビルドの成果物の違い 基本的に成果物はほぼ同じでしたが、loaderの指定によりHTML の import 部分で差異がありました。 webpack: HTML モジュールをオブジェクトとして扱う esbuild: HTML モジュールを文字列として扱う そのため後々の移行の手順にもあるように、一定期間同じコードベースで webpack/esbuild の両方をビルドする必要があったため、どちらのビルド方法でも動作するように、下記のようなユーティリティ関数を追加しました。 const rawModule = require( "./container.html" ); const html = getHtmlStringFromModule(rawModule); // `*.html` をバンドルする方法はバンドラによって異なります。 // - esbuild や rollup の一部設定では、インポートはそのまま文字列になります。 // - もしくは `{ default: string }` のようなオブジェクトを返す場合もあります。 // ここで形を正規化することで常に文字列として扱えるようにします。 const getHtmlStringFromModule = ( mod : unknown ): string => { if ( typeof mod === "string" ) { return mod; } if ( typeof mod === "object" && mod !== null ) { const maybeDefault = (mod as Record < string , unknown >). default ; if ( typeof maybeDefault === "string" ) { return maybeDefault; } } throw new Error ( "unexpected HTML module shape" ); } ; ビルド用の設定 #!/usr/bin/env node import path from "path" ; import { fileURLToPath } from "url" ; import { build } from "esbuild" ; const __filename = fileURLToPath ( import . meta . url ) ; const __dirname = path . dirname ( __filename ) ; const outdir = path . resolve ( __dirname , "dist" ) ; const outfile = path . join ( outdir , "bundle.js" ) ; // 本番ビルド await build ({ entryPoints : [ path . resolve ( __dirname , "src" , "index.ts" )] , bundle : true , sourcemap : true , platform : "browser" , outfile , minify : true , define : { API_BASE_URL : JSON . stringify ( process . env . API_BASE_URL || "" ) , } , loader : { ".html" : "text" , } , logLevel : "info" , }) ; console . log ( "esbuild: built" , outfile ) ; ビルド用のスクリプトも非常にシンプルです。esbuild の build 関数を使用して、エントリーポイントや出力先、バンドル設定などを指定しています。 ビルドされたファイルを CircleCI のジョブで S3 にアップロードし、CDN 経由で配信する仕組みは以前と同様に維持しています。 移行の手順 移行は段階的に行いました。 ローカル/dev 環境のみ esbuild に切り替え stg/本番も esbuild に切り替え webpack/Babel 関連の設定・依存関係を削除 結果として、問題なく移行でき、切り替えによる影響もありませんでした。 移行結果 元々軽量な JavaScript であったため、ビルド時間の劇的な改善はありませんでしたが、設定が大幅にシンプルになり、依存関係の脆弱性も出にくくなりました。 元々が CircleCI 上で 2 ~3秒程度のビルド時間でしたが、esbuild に移行したことで 1 秒未満に短縮されました!! おわりに payid-js のビルドを webpack/Babel から esbuild に移行したことで、設定のシンプル化と依存関係の削減が実現できました。 今後も payid-js の開発を続けていく中で、さらなる改善点が見つかれば積極的に取り組んでいきたいと思います。 BASE / Pay IDではエンジニアを募集しているので、興味ある方は以下からご連絡ください。 明日のBASEアドベントカレンダーはIzuharaさんの記事です。お楽しみに。 binc.jp
はじめに BASE Dept で アプリケーションエンジニア をしている Capi(かぴ) です。 BASEでは機能開発に加え、プロダクトの品質を向上させるため非機能要件の強化も行なっております。今回は自分が半年間ほど担当してきた SASTツールPoC についてお話ししていきます。PoCのプロジェクトが立ち上がり今日までに行なってきたことを可能な限り紹介していきます。 ※ SASTツールとは SAST (Static Application Security Testing) とはアプリケーションのソースコード、バイトコード、バイナリコードに対して脆弱性が内在するか否かを確認するテスト手法であり、ホワイトボックステストの一種である。 SASTは、アプリケーション機能をブラックボックステストするDAST (Dynamic Application Security Testing)と異なり、アプリケーションのコードコンテンツ、ホワイトボックステストに焦点を当てている。SASTツールは、関数レベル、ファイルまたはクラスレベル、アプリケーションレベルなどの分析レベルによりソフトウェアとアーキテクチャに潜在するセキュリティの脆弱性を特定する。 NECソリューションイノベータ, 「SAST (Static Application Security Testing)」, ( https://www.nec-solutioninnovators.co.jp/ss/insider/security-words/74.html ) 自分が所属する組織が抱えている「開発フローに組み込める実用的なSASTツールを評価・選定することで、セキュリティリスクの早期検知体制を確立したい」という課題に対して調査、提案、検証、まとめを一貫して行うことができたのは良い経験でした。 今回の記事でPoCプロジェクトのメンバーだけでなく他の部署、マネージャー陣も巻き込み、色々工夫しながら進めてきた記録が少しでも伝えられれば幸いです。また、SASTツールの導入はもちろん、SASTツール以外で新しいツールの導入を考えている方にこの記事が少しでも参考になれば幸いです。 業界トレンドと社内調査 「SASTツール入れたい!!」と言うだけではなかなか導入の舵は切れません。まずは業界のセキュリティトレンド、昨今のセキュリティ事例、社内のセキュリティ対応状況のインプットから始めました。 書籍でインプット(一部抜粋) 体系的に学ぶ 安全なWebアプリケーションの作り方 第2版[固定版] 脆弱性が生まれる原理と対策の実践 セキュアで信頼性のあるシステム構築 ―Google SREが考える安全なシステムの設計、実装、保守 組織、団体公開しているセキュリティ情報をインプット(一部抜粋) OWASP Top10 ECサイト構築・運用セキュリティガイドライン (IPAが公開している資料) セキュリティ事例をインプット 過去に社内インシデントがあるかどうか 外部が公開しているECサイトの情報漏洩、不正アクセス、不正決済事例の調査 セキュリティ対応状況をインプット これまで行ってきた社内のセキュリティ施策の洗い出し 社内セキュリティロードマップの確認 調査をもとに現在の課題と解決策に期待する効果を図解する インプットが終わり、自分たちが行っていきたいイメージがついてきたところで社内の開発フローと社内のセキュリティ対応を一枚の図に起こしました。 実際に図解するとセキュアな実装について考える頻度は少なく「現状どこが不足しているのか」、「今後SASTツールの導入でどこが強化できそうか」をわかりやすくすることができました。 なぜSASTツール導入をしたいのかをよりはっきりさせることができたと考えています。 図解の様子 ※ 付箋の色の意味(一部抜粋) 緑色: 現在の開発フローで行われていること 水色: セキュリティ対応に関すること 赤色: SASTツール導入で期待すること ツール調査と選定 ツール選定ではカバー株式会社さんの資料を参考にさせていただきました。 note.cover-corp.com まずは下記項目でツールを評価し、PoCで試したいものを選定しました。 我々の開発言語に対応しているか。ルールセットがどれだけあるのか 有料ツールを使った場合のコスト CI対応、IDE連携対応 ツール選定の様子(一部) 調査が進むにつれて「今回がPoCということもあり、最初から有料ツールを導入するよりもOSSで小さく始めるのが良いのではないか」という意見が多く出てきました。そして最終的にPoCで検証したいツールを SonarQube と Semgrep に絞りました。 コード品質、セキュリティ、および静的解析ツール(SonarQube) | Sonar Semgrep App Security Platform | AI-assisted SAST, SCA and Secrets Detection 課題の整理と提案 業界トレンドと社内調査、ツール選定を終えた段階で他部署やマネージャー陣に提案をしました。 OWASP Top10やIPAの資料など信頼できる情報源を参考にしたり、現状と理想を図解することでわかりやすくSAST導入の背景やSAST導入で期待する効果を伝えることができました。また、提案資料は全てドキュメントに残します。 ドキュメントに残す理由は意思決定の背景をのちほど参照できるようにするためです。当時の目標や当時の意思決定の根拠を残すことで振り返りの材料を用意しておきます。将来の改善に次に繋げることができると考えたためです。 検証実施 スキャン実施、IDE連携を試したりしました。 下記項目でツールを評価しました。 修正のしやすさ(検出された内容がわかりやすいか、直すのが容易か) 設定の容易さ(設定の手間) 誤検知率(多さ) CIへの組み込みやすさ(設定の手間、環境変数の設定忘れで起こることとその対応工数) ダッシュボードの使いやすさ etc… 定量面と定性面で評価を進めました。実際に使ってみると「想像してたのと違った」という気づきがありました。 検証の中で作ってみたデモ SonarQubeとSemgrepを検証した結果、プロジェクトチーム内で「Semgrepの方が適していそうだ」という判断をしました。そして実戦投入を想定した仕組みのデモ構築を進めました。 デモで構築したものはCIでSASTツールを使い定期スキャンを実行、スキャン結果で対応優先度が高いものはGitHubのIssuesに登録されるというものです。 GitHubのIssueに登録することでGitHub Copilotで修正してもらうことができるのではと考えました。修正の全てをCopilotに任せるだけでなく最初の修正案を出してくれる存在としても活用できると考えています。 下記はSASTツールに無料版のSemgrepを利用して構築したものの図です。 デモで作成したプロジェクトのIssuesです。Issue登録時にラベルを付与することで検索しやすくしています。 自動でIssueに登録されたもの デモを作る時に困ったことと解決方法 検証の中でこんなことしたいあんなことしたいがたくさん思いつきましたが、すんなりいかない時や悩みがたくさんありました。いくつか紹介させていただきます。 1. SASTツールのスキャン結果をGitHubの世界に持っていけない スキャン結果で対応優先度が高いものはGitHubのIssuesに登録 先ほど紹介したこの仕組み、自分が検証していた製品(Semgrep)には機能として存在していませんでした。そこでCIでスキャン結果をjsonファイルに出力し、そのjsonファイルを加工してIssueとして登録するシェルスクリプトを書きました。 このシェルスクリプトを使った解決方法は自分の同僚であり自分がとても尊敬している meiheiさん がPHPカンファレンス福岡2025で発表していた「隙間ツール開発」から着想を得ました。 speakerdeck.com 2. スキャン結果をどう加工して登録すればいいのか スキャン結果を出力したjsonファイルはそのまま使えませんでした。なのでjson結果を自然なIssueとして登録するための加工をしました。 次に話すのですが、GitHubの既存Issueを取得してすでに登録されているIssueを重複させない仕組みにしたかったのでタイトルを優先的に考えました。 最終的に下記にしました。 $severity='危険度' $path='対象ファイルの場所' $line='行' $col='列' $id='どんな脆弱性か' $TITLE="[$severity] in $path line: $line col: $col - Rule: $check_id" Issueのページで確認すると全体はこんな感じです。 Issueの例 「タイトルのファイルの行と列は不要じゃないか」と考える方がいらっしゃるかもしれませんが、1つのファイルで複数検出された場合、現状は1つしかIssueを作れません。ゆくゆくは「ファイルごとにIssueを作る」に改善した方が良いと考えています。 3. GitHub Issuesに同じスキャン結果が登録される GitHubに同じタイトルでいくつもIssueが作成可能です。これによって定期実行を行うと新規のIssueに加え、まだ対応し切れていないIssueを重複して作成してしまいます。 下記の画像は検証でデモを作成している間に生まれた同じ内容のIssue達です。 デモ開発中に見つけたIssueの重複 これを防ぐために既存のIssuesと同じタイトルかどうかを確認するようにしました。既存のIssues取得はGitHubのAPIでできます。 docs.github.com ※こちら2025年12月現在、注意書きがあるので気をつけてください。Issuesと一緒にプルリクエストも取得してしまうことがあるのでこれは利用者が対応する必要があります。 GitHub's REST API considers every pull request an issue, but not every issue is a pull request. For this reason, "Issues" endpoints may return both issues and pull requests in the response. You can identify pull requests by the pull_request key. 既存のIssuesと同じタイトルかどうかを確認 また上記ですが、これはSASTツールで検出された警告のリストの要素と既存Issuesのリストを突合する処理で実現しました。for文を2回まわして突合することも可能ですが、for文のネストは計算量が大きくなってしまうので 既存Issuesのリストを連想配列にして突合してみました。 for文を2回まわして突合 と 連想配列にして突合 のサンプルコードは下記になります。 #!/bin/bash list1 = ( " apple " " banana " " cherry " ) list2 = ( " banana " " cherry " " date " " fig " ) echo " === 二重ループ (O(n×m)) === " hit = 0 # リストに含まれているかのフラグ for a in " ${list1[ @ ]} " ; do for b in " ${list2[ @ ]} "; do if [[ " $a " == " $b " ]] ; then hit = 1 break fi done if [[ $hit -eq 1 ]] ; then echo " $a は list2 に含まれる " continue else echo " $a は list2 に含まれない " continue fi hit = 0 done # === 二重ループ === # apple は list2 に含まれない # banana は list2 に含まれる # cherry は list2 に含まれる #!/bin/bash list1 = ( " apple " " banana " " cherry " ) list2 = ( " banana " " cherry " " date " " fig " ) # list2 をハッシュセット化 declare -A set for b in " ${list2[ @ ]} " ; do set[" $b "]= 1 done echo " === 連想配列 === " # list1 の各要素がセットにあるか確認 for a in " ${list1[ @ ]} " ; do if [[ -n " ${set[$a]} " ]] ; then echo " $a は list2 に含まれる " continue else echo " $a は list2 に含まれない " continue fi done # === 連想配列 === # apple は list2 に含まれない # banana は list2 に含まれる # cherry は list2 に含まれる 4. 意思決定の情報が記録されず未来に情報を残せない これは実装に関係ない話しです。 PoCを進める中で追加で決めたい内容は出てきます。自分は導入することだけに集中してしまい、「なぜこの構成になっているのか?」、「これは誰が決めたのか?」の情報を残さないことに危機感を感じました。今回は広範囲に影響する変更になりうるのできちんと文章に残しました(検索ができればSlackに残しても良いと思います)。 意思決定ログ(Notionのテーブル) これは過去に別プロジェクトでやってよかったと感じたものを輸入しました。 おわりに 駆け足でしたが約6ヶ月のSASTツールPoCの内容を共有させていただきました。 振り返るとPoCの中で普段の機能開発とは違う能力が求められたこと、今まで触ってこなかったツールに詳しくなれたのは良い経験になったと考えています。 BASEでは機能開発はもちろん非機能要件について考えたり既存システムの改善について考える課題があります。また、その課題に挑戦する機会もあります。ご興味あればぜひ採用情報をご覧ください。 binc.jp 明日はPay ID所属の noji さんによるフロントエンドのビルド周りに関する記事です!
BASE ADVENT CALENDAR 2025 DAY.14 はじめに 本記事は BASE アドベントカレンダー 2025 の 14 日目の記事です。 BASE BANK Dept で フルサイクルエンジニア をしている 02 です。 2025年4月、BASEは新しい振込申請機能「最速振込」をリリースしました。最短10分、土日祝日を含む365日対応での入金が可能になり、ショップオーナーさんのキャッシュフロー改善に大きく貢献しています。 本記事では、最速振込の実装で使用したスキーマ変更とMySQL INSTANT DDLを活用したマイグレーションについて解説します。なお、テーブル名・カラム名は説明用に簡略化しています。 プロジェクト管理やリーダーシップの観点、振込申請や最速振込の詳細については、以前の記事「 最速振込の舞台裏:プロジェクトのリードの実践と学び 」で紹介しました。よろしければそちらもご覧ください。 既存のテーブル設計課題と新しいカラム 最速振込を追加する前、BASEの振込申請には通常振込、お急ぎ振込、定期振込の3種類がありました。当時、振込種別は複数のテーブルを参照して判定していました。 通常振込:振込手数料テーブルのお急ぎ振込フラグがOFF お急ぎ振込:振込手数料テーブルのお急ぎ振込フラグがON 定期振込:定期振込ログテーブルにレコードが存在 しかし、この設計には課題がありました。 振込種別を判定するために複数テーブルをJOINする必要がある フラグでは2種類しか表現できず、お急ぎ振込か否かしか判断できない 新しい振込種別を追加するたびに判定ロジックが複雑化する これらの課題を解決するため、振込申請テーブルに振込種別カラムを追加しました。 transfer_type varchar ( 20 ) COMMENT ' 振込種別(通常振込: normal、お急ぎ振込: express、定期振込: scheduled、最速振込: instant) ' これにより、振込種別の判定が振込申請テーブルの1カラムで完結します。カラム追加には、MySQLのINSTANT DDLを活用しました。 MySQL INSTANT DDLの活用 INSTANT DDLとは MySQL 8.0.12で導入されたALTER TABLEのアルゴリズムです。従来のCOPYやINPLACEと異なり、メタデータの更新のみで操作が完了するため、非常に高速です。 アルゴリズム 動作 特徴 COPY テーブルをコピーして入れ替え テーブルロックが発生、最も遅い INPLACE その場で変更(内部的には再構築) DML操作は可能だが、時間がかかる INSTANT メタデータのみ更新 非常に高速、テーブルサイズに依存しない INSTANT DDLは行データの再配置や再構築を行わず、メタデータだけを更新します。 結果として、アプリケーション視点では接続断やタイムアウト、強制リトライが生じず、読み書きトラフィックを止めないまま変更を完了できます。これが本記事で述べる「完全無停止」の根拠です。 MySQL 8.0.29以降、INSTANT DDLは以下の操作に対応しています。 カラムの追加(任意の位置に追加可能)、削除 カラム名の変更 ENUM/SETへの要素を追加 DEFAULTの追加、変更、削除 仮想カラムの追加、削除 テーブル名の変更 インデックスのデータ構造(BTREEやHASH)の変更 ただし、以下のような制限があります。 INSTANTのアルゴリズムに対応していない構文との併用はできない ROW_FORMAT=COMPRESSED のテーブルでは使用できない FULLTEXT インデックスを含むテーブルでは使用できない INSTANT DDLの操作回数には上限があり、テーブルごとに64回(超過時はOPTIMIZE TABLEでリセット)まで 詳細については、MySQLの公式ドキュメントをご参照ください。 https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html#online-ddl-column-operations なぜINSTANT DDLを選んだのか 振込申請テーブルは数百万件のレコードを持つ、追加・更新頻度の高いテーブルです。従来のINPLACE DDLでは長時間のロックが発生するため、メンテナンス時間の確保か、 gh-ost を使用したカラム追加が必要だと考えていました。 しかし、INSTANT DDLを使用すればサービスへの影響を最小限に抑えられ、メンテナンス時間も不要です。さらに、マイグレーション手順を適切に設計すれば、DDLの実行だけでカラム追加が完結します。 本番相当のデータ量を持つ検証環境で検証した結果、無停止で実現できると確信しました。 INPLACE DDL: 3分ほど INSTANT DDL: 374ms マイグレーション手順と安全なカラム追加の戦略 新しいカラムを追加する際、単純にカラムを追加してアプリケーションを更新するだけでは不十分です。過去のレコードとの整合性が取れないため、以下の手順で段階的にマイグレーションを行いました。 なお、今回出てくるクエリは、例として名称は一部置き換えています。 Step 1: DEFAULT句ありでカラム追加 まず、振込種別カラム transfer_type を DEFAULT句 'normal' (通常振込)で追加します。これにより、既存レコードの transfer_type には自動的に 'normal' が設定されます。 ALTER TABLE 振込申請テーブル ADD transfer_type varchar ( 20 ) DEFAULT ' normal ' COMMENT ' 振込種別 ' , ALGORITHM=INSTANT, LOCK = DEFAULT ; 'normal' (通常振込)をデフォルト値として選んだのは、通常振込が最も多く使われている振込種別だからです。Step 4で過去データをマイグレーションする際、更新するレコード数を最小限に抑えられます。最もレコード数の多い通常振込を更新対象から除外できるため、この方法が効率的だと判断しました。 Step 2: アプリケーションの対応 振込申請作成時に振込種別カラムを設定するようアプリケーションを更新します。 通常振込作成時: transfer_type = 'normal' お急ぎ振込作成時: transfer_type = 'express' 定期振込作成時: transfer_type = 'scheduled' Step 3: DEFAULTを検知用の値に変更する 次にDEFAULT句を検知用の値である 'unsupported' に変更します。 ALTER TABLE 振込申請テーブル ALTER transfer_type SET DEFAULT ' unsupported ' , ALGORITHM=INSTANT, LOCK = DEFAULT ; この変更後、 transfer_type = 'unsupported' のレコードが発生しないことを監視します。もし発生した場合は、対応漏れがあることを意味するため、アプリケーションコードを修正します。 Step 4: 過去データのマイグレーション レコードを監視して対応漏れがないことを確認したら、過去のレコードを関連テーブルの情報をもとに更新します。 -- お急ぎ振込の過去レコード(振込手数料テーブルのお急ぎ振込フラグを参照) UPDATE 振込申請テーブル LEFT JOIN 振込手数料テーブル ON 振込申請テーブル.振込申請id = 振込手数料テーブル.id SET 振込申請テーブル.transfer_type = ' express ' WHERE 振込手数料テーブル.お急ぎ振込フラグ = 1 ; -- 定期振込の過去レコード(定期振込ログテーブルの存在を参照) UPDATE 振込申請テーブル LEFT JOIN 定期振込ログテーブル ON 振込申請テーブル.id = 定期振込ログテーブル.id SET 振込申請テーブル.transfer_type = ' scheduled ' WHERE 定期振込ログテーブル.id IS NOT NULL ; Step 1でDEFAULT句を追加した際、既存レコードの transfer_type には自動的に 'normal' が設定されています。そのため、通常振込の過去レコードについてはUPDATE文を実行する必要はありません。 Step 5: 参照ロジックの切り替え 最後に、振込種別の判定ロジックを新しいカラム transfer_type を参照するように切り替えます。また、任意のタイミングでDEFAULT句を削除します。 ALTER TABLE 振込申請テーブル ALTER transfer_type DROP DEFAULT , ALGORITHM=INSTANT, LOCK = DEFAULT ; こういった手順で振込申請テーブルに振込種別カラムを追加しました。 おわりに 最速振込の実装では、MySQL INSTANT DDLを活用することで、更新頻度の高い数百万行のテーブルへ、メンテナンスなしでスキーマ変更を実現できました。 MySQL 8.0.29以降を使用している方は、ぜひINSTANT DDLの活用を検討してみてください。 BASE BANK Deptでは、プロダクト開発をリードできるエンジニアを募集しています。興味のある方は、ぜひ採用情報をご覧ください! binc.jp 明日は、BASEアドベントカレンダーは @capi さんです!お楽しみに!
こんにちは!CSE Group でエンジニアをしている上野です。 この記事は BASE AdventCalender の13日目の記事です。 12日目は kagano さんの GitHub Copilot の Custom Instruction でのコードレビューについての記事でした。この 1 年は AI に関する話題、特に Coding Agent の話題がたくさんありましたね。日々モデルも機能も進化していて、今どこの AI は何ができるんだっけ?と迷子になってしまっているので、私自身参考になりました。 さて、BASE AdventCalender 13日目のこの記事でも AI についての話ですが、開発業務以外の業務改善について、この1年間の取り組みをお話します。 はじまりから PoC 期 はじまり 2025年に入る前から各個人や部署毎で生成 AI のサービスを積極的に試したり導入してみたり、ということはされていましたが、2025年の頭から、会社としてしっかりと生成 AI を使っていこうという方針が打ち出されました。 私の所属する CSE は社内の業務改善をエンジニアリングで支援するチーム *1 ですが、このときの方針としては CSE に依頼しよう、ではなく各部門で業務を効率化できるようにしていこう、という流れでした。しかし、CSE としてなにかしようというものではなかったのですが、いずれはなにか依頼があるであろうということを見越し、CSE でも準備をしていくことにしました。 そこでまずは生成 AI を利用した簡単なアプリケーションを実装したり、SaaS などを検討したりしてみよう、という事になりました。 いくつか実装するアプリケーションの案はあったのですが、 ナレッジが整備されている 問い合わせの Slack チャンネルでも問い合わせが多く、ある程度の効果が見込める という2点から人事・労務の質問回答 Bot を作成することにしました。 AI Chat Bot の PoC 各種サービスの検討 作成するものが決まったため、次にいくつか SaaS などを比較検討をしました。 また、汎用的に使用できるということで Dify と、Amazon Bedrock も比較対象としました。 そこでのなかで挙がったそれぞれのメリット、デメリットなどを紹介します。 検討対象 メリット デメリット SaaS 製品 簡単な設定ですぐ構築できる ダッシュボードなど、必要な機能も利用可能 Chat Bot 以外でやりたいことがでてきた際にできないことも多い 価格は比較的高い Dify 色々な人がワークフローを構築できる 会社で使用する場合、SaaS版ではなくセルフホスト版を使用することになり、メンテナンスコストがかかる 社員全員に開放する場合、Notionにあるナレッジのセキュリティが課題 Amazon Bedrock Agent、KnowledgeBase 柔軟に構築が可能 コストも低い キャッチアップが必要 構築の工数は高くなる 上記のメリット・デメリットを勘案し、長期的な視点では技術力を持っている必要があること、単純な Chat Bot だけではなく今後業務に組み込まれていくであろうことをから、Amazon Bedrock で構築していくことに決定しました。また、Chat Bot への問い合わせのインターフェースは Slack としました。これは「ナレッジが Notion にあるのであれば、構築をせず NotionAI でよいのでは」という考えもあったものの、NotionAI で各個人が人事・労務の情報を確認してしまうと、ハルシネーションによる誤った情報だった場合人事担当がキャッチできないこと、オープンな場で質問することで他の人に見えないことで情報が個人で閉じてしまうことがあるため、Slack のオープンな場での問い合わせをするようにしました。(当然人事関連の問い合わせは非公開であるべきものもあるため、そういうものは既存のクローズドな問い合わせ窓口を利用してもらうよう案内しました。) 実装 実装するアプリケーションは以下のようなアーキテクチャとなりました。また、今回は Notion のデータを Bedrock KnowledgeBase に取り込みましたが、PoCということで継続的に更新するなどの仕組みは作らず、ダウンロードをして S3 に配置し、そのバケットを Bedrock KnowledgeBase で読み込むという単純なものにしました。 PoC 結果 PoCの結果、PoC 期間の約1ヶ月間で、問い合わせの件数が46件、正答率が 70%(32件/46件)、誤答の分類としては ナレッジの記事の内容が曖昧だった(ナレッジの課題) 質問内容について明記されていない、記事がなかった(ナレッジの課題) ナレッジの画像に情報がありAIが参照できなかった(技術的課題) 別の記事の内容が参照されてしまった(技術的課題) 古い記事の内容が参照されてしまった(ナレッジの課題) その他(ハルシネーションなど) 学び PoC の結果から以下のような教訓、学びが得られました。 少し曖昧であったり、抽象的な書き方の記事でも、人が読むとある程度行間を読んだり、入社時の研修で案内されていたり質問ができていたため補完されていたが、AI は前提知識がなにもないため誤った解釈をしてしまう事がある。 プロンプトや KnowledgeBase の RAG の設定はあまり凝ったことはしていないがある程度の正答率が得られ、生成 AI の力を改めて感じた。 過去の記事を参照してしまうなど、AI活用にはまずデータの整備が大事だと痛感した。 CS の問い合わせ改善プロジェクト はじまり PoC を進めていた頃と並行して、CS チームから「今後 CS の業務を AI で改善するだけでなく、AI 前提の業務に変革したい」という相談がありました。その中で、まずは工数がかかっている「問い合わせを一部委託しているパートナーからのエスカレーションの対応を AI で一次受けする AI」(以下エスカレーション AI) の依頼がありました。 こちらについても NotionAI などで代替できないかなどの検討を行ったところ、契約の関係上NotionAI を利用できず、また AI への一次エスカレーションの内容を弊社社員も確認できるようにしておきたいという要件、PoC で構築した仕組みと同等のものを転用できるということで、Slack をインターフェースに、ナレッジを参照して回答するAIを実装しました。 実装について Slack から AI への問い合わせ部分実装は PoC の仕組みとほぼ同じですが、PoC ではなく日々ナレッジが更新されていくため、Notion のデータを日次で S3 にアップロードし、その後 Bedrock KnowledgeBase のデータソースを再同期するという仕組みを構築しました。 また、当時 Notion のインテグレーションの作成はワークスペースオーナーのみができましたが、インテグレーションを記事にコネクトする操作 (つまりインテグレーションに読み込む許可をする操作) は任意のユーザーが、そのユーザーがアクセスできる任意のページ・データベースに可能でした。これにより、もし誤って社外秘の情報をコネクトしてしまった場合、AI がその情報を読んでしまい、意図せず情報が流出してしまうという課題がありました。( 現在はワークスペースオーナーのみがコネクトを許可する設定が実装され、この課題は解決されています。 ) そのため、Notion の記事連携機能部分では、Notion のホワイトリスト方式でデータベース ID、ページ ID を設定し、Pull Request でのレビューを必須とすることで意図しない記事の連携を制御するようにしました。 アーキテクチャは以下のようになりました。 効果 エスカレーション AI の実装により、以下のような効果が得られました。 約200件/週 のエスカレーションの20~30%削減ができた。 また、可能性として「BASE 側の工数が削減されたが、パートナー側でナレッジを確認したりなどの工数が発生しており、負担が移っただけではないか」という事も考えられましたが、CS のマネージャーに詳細をヒアリングをしたところ、そうではなく純粋にこの効果を得られた、とのことでした。 ヘルプページ改善プロジェクト はじまり 前述のエスカレーション AI が無事本番運用された後、次は CS で工数のかかっていたヘルプページの AI による改善の相談があり、そのプロジェクトが開始しました。 ヒアリングをする中で、以下のような状況や課題が見えてきました。 状況 PRD から仕様書や SOP の生成は NotionAI である程度自動作成ができていた 元々PRD、仕様書、SOP はすべて Notion で管理されており親和性が高かった しかしヘルプページは zendesk にしか存在していなかった 課題 ヘルプページが Notion になく、仕様書や SOP の作成後、どのヘルプページを更新すべきか検索が難しく、工数がかかり更新漏れもあった そこで、まずはAIで更新するなどの前に、Notion にヘルプページをもってきて、SSoT (Single Source of Truth) とする、その後 SOP や仕様書とヘルプページを紐づけることで、まずは管理ができるようにするよう提案し、その後紐づけたデータを NotionAI が参照し、内容を作成するという形で進めました。 技術的な部分 ここでは NotionAI を用いたため実装は zendesk の API を実行する Lambda など多くはありませんが、Notion 上での設定などを簡単に紹介します。 ドキュメントがすべて Notion にあるためすべて情報を Notion に集約し、 AI は NotionAI を使用 zendesk のヘルプページはセクション ID が必須なため、セクションの情報も同期するように構築 仕様書、SOP、ヘルプページのデータベースで、それぞれリレーションを設定し、紐づいているドキュメントを NotionAI が読み込み記事を生成 ヘルプページの完全自動公開は NotionAI の仕様上現状は難しいが、プロンプトを管理して最低限の操作で生成するように構築 ヘルプページについてはユーザーの目に触れる部分のため必ず人が最終チェックをしてから公開するようにしている 学び このプロジェクトは最近のもののため、具体的な数字としての効果はまだ得られていませんが、進める中で以下のような教訓や学びを得られました。 AI でなにかをやりたいという要望でも、しっかりと業務や課題を洗い出して課題を解決するというのは大事 AWS Bedrock Agent Core など、気になる技術はたくさんあるものの、それにこだわらずに、既存のツールで工夫することも時には大事 おわりに 大変だったこと この1年間業務改善に生成 AI を適用するという取り組みを進めてきましたが、(きっと同じことをしている多くの人が感じているであろう)大変なこともありました。そのうちの全てではないですがいくつか紹介します。 生成 AI のモデルや各社の機能拡充の対応速度が早く、実装してすぐ不要になる機能があったり、前提が変わったりしてしまうこと モデルだけでなく色々な機能(AI ワークフローや各種ドキュメントシステムとのナレッジ連携など)も次々でてきて、今実装しているこの機能は不要になってしまうのではないか、という不安はいつもつきまとっていました。 そのうえでどれにベットするのか、というのを考えるのが非常に難しかったです。(複数人が使用する業務システムなので、頻繁に「やっぱりこっちに変えます」ということはできないため。) この記事では、この1年間、BASE の CSE チームが生成 AI の業務改善への適用の取り組みを紹介しました。まだまだこの記事には書ききれなかった紆余曲折ややったことなどもありますし、もちろん昨日の記事のように BASE のアプリケーション開発での AI の取り組みなどもあります。興味を持っていただいたら、採用情報をご覧ください! 採用情報 | BASE, Inc. - BASE, Inc. 明日は 02 さんの「数百万行でも怖くない!MySQL INSTANT DDLで「完全無停止」カラム追加」の記事です!以前 gh-ost の記事 もありましたが、それとの違いや技術選定についてなど気になりますね!お楽しみに! *1 : CSE チームについての説明は こちらの記事 や、今年のアドベントカレンダー17日目の記事をご覧ください。