基盤チームの右京です。 昨今はフロントエンドのアプリケーションもリッチになり、ブラウザ上で実行されるコードが行うことの範囲も増えてきました。一方で多くのことを実装できてしまうのはリスクでもあり、BASE でも問題となることがあります。 その中でも「開発環境の URL」や「デバッグ機能の存在」ような環境毎に異なる情報は、特に意図せずに漏れやすいものだと考えています。これらはコードを記述する際に、実装方法を知識として知っていればその多くが回避可能です。この記事ではその実装例を解説しています。 コードから漏れる情報 例えば、次のようなコードがあるとします。 function debug() { // 開発環境の host であればデバッグ機能を有効にする return location .host === 'dev.example.com' ; } なんの変哲もないようなコードに見えますが、ブラウザ上で実行されるコードとしては非常に良くないものだと言えます。通常 JavaScript をブラウザで実行する場合、ソースコードが直接取得されるため、その内容は利用者が閲覧することできます。つまり「秘匿情報の開発環境の URL が、比較的誰でも簡単に入手できるものに記載されてしまっている」ということになります。 Minifier や Compressor を用いて難読化を行ったとしても、(このままでは)この URL の情報が消えることはありません。 location.host という変数と比較をするため、 "dev.example.com" は実行時まで必要なコードだからです。 防ぐにはどうするか? 殆どの場合は次のうちいずれかの方法で解決できるでしょう。 情報をサーバーサイドのプログラムから受け取る 環境毎にビルドで使用する定数を入れ替える ビルドフェーズで不要になるコードを確定して削除する 前者の 2 つは馴染みのある方法で、最後の 1 つはフロントエンド特有のものです。 1. 情報をサーバーサイドのプログラムから受け取る HTML を配信するサーバーサイドのプログラムがある場合、環境による変化はそちらでコントロールします。今回の例では、デバッグ機能を利用したい場合にそのフラグを input で埋め込むような形にします。 < input type = "hidden" id = "debug" value = "1" > JavsScript からはこの値を取得してデバッグ機能を有効にするかを決定しますが、このままではデバッグ機能自体は残ってしまうので、API のエンドポイント URL などの単純に値を切り替えたいものに向いているでしょう。 2. 環境毎にビルドで使用する定数を入れ替える SPA などで HTML が静的な場合は、ビルド時に 1. と同等の置き換えを行うと良いでしょう。できることは 1. と同様なため、デバッグ機能そのものはコードとして残ってしまうことは変わりません。 環境毎に API の URL が変更されるこのようなコードがあるとします。 function baseURL() { return API_BASE_URL; } fetch( ` ${baseURL()} /api` ) 今回は esbuild の CLI を用いてこれをビルドします。esbuild には define オプションがあるのでこれを使って環境毎の値をコードへ埋め込みます。webpack を使用している場合は DefinePlugin など、それぞれに対応したものがあるはずです。 // $ esbuild index.js --define:API_BASE_URL=\"http://dev.example.com\" function baseURL() { return "http://dev.example.com" ; } fetch( ` ${baseURL()} /api` ); 環境によって define に渡す値を変更してビルドすることで、最終的なコードには開発環境の情報が残らなくなりました。 3. ビルドフェーズで不要になるコードを確定して削除する これは、2. の define を利用して意図的にデッドコードを生みだすことで、Minifier や Compressor でそのコードを削除する方法です。この方法はデバッグ機能そのものをコードから削除できます。 説明を簡単にするため、次のコードを例にしていきます。 function test() { if (APP_ENV === 'production' ) { return 'production' } else { return 'development' } } console.log(test()) APP_ENV はビルド時に決定される値で、それが置き換えられます。production を設定してビルドした場合 esbuild ではこのようになります。 // $ yarn esbuild index.js --define:APP_ENV=\"production\" function test() { if ( true ) { return "production" ; } else { return "development" ; } } console.log(test()); APP_ENV が "production" === "production" となり、 true となることが確実なのでそれに置き換わり else 側が完全なデッドコードとなりました。これに --minify オプションを追加すると、デッドコードが削除されます。 // $ yarn esbuild index.js --define:APP_ENV=\"production\" --minify function test() { return "production" } console.log(test()); 逆に APP_ENV が production 以外なら else 側だけが残ります。 // $ yarn esbuild index.js --define:APP_ENV=\"development\" --minify function test() { return "development" } console.log(test()); このようにしておくと else 側には安全に開発環境向けのデバッグ機能を記述できます。この方法は terser の README.md でも紹介されています 。 まとめ 情報を守るためのコードの記述方法や、ビルドツールの機能について紹介してきました。ここには書いていませんが、例えば CI で特定の URL を記述を検出するなども有効な手段の 1 つでしょう。フロントエンドのコードは「すべて公開状態」なことを常に忘れずコードを記述することを心がけましょう。
こんにちは。バックエンドエンジニアの髙嶋です。 今回は、私が BASE に転職するに至った動機の一つでもある「開発力をあげたい」ということについて、実際入社してどうだったかを半年が経過した今、あくまでも私個人の経験としてお話しさせていただこうと思います。 結論から言うと「開発力」が向上した実感は確かにあり、 DDD や TDD、あるいはスクラムといったものを使っていこうという機運が開発組織的にもあった ただしやり方がガチガチに決まっているということはなく、チームごとに合った方法を考えていく余白が十分にあった それをするための Be Hopeful(楽観的)で Move Fast(速く動く)かつ Speak Openly(率直)であるという、会社としての風土が自分に合っていた といったことが振り返ってみると挙げられると思っています。 先に結論を言ってしまったので、ここからは少し時間を巻き戻しながら順にお話ししていこうと思います。 なかなか一般化できるような内容ではありませんが、一つの参考として読んでいただけると幸いです。 そもそも開発力とは 「開発力」のまま話を進めるとスコープが広すぎるので、今回の話における定義を決めておこうと思います。 一言でいうと、「設計力」となります。 そして「設計」という言葉自体も非常に様々な捉えられ方をする、広い意味を持つ単語です。 そのためここでは 企画担当から提示されたやりたいこと(要件)の解像度を高め どう既存プロダクトの中に落とし込むかあらゆる選択肢を洗い出し その中から様々な要素を加味して現状における最適解と思われるものを一つ選び それをどのようにユーザーに見える形で実現するかを考え、実行する力 と表現しておきます。 なぜ開発力もとい設計力をあげたいと思ったか 前提として、私の転職理由をお話ししておかなければなりません。 前職では Web アプリケーションを開発するチームのグループマネージャーを任せていただいており、それ自体は非常によい経験ではありました。 一方で、エンジニアとして自信を持てるほどの知識や経験がないまま、その時の状況もあって役職に就かせていただいたという背景がありました。 その結果、いわゆるマネジメント業務に割く時間が多くなっていく中で、チームメンバーから技術的なアドバイスを求められても、引き出しが少ないがために確固たる根拠を持って助言できない自分に対してもどかしさを感じていました。 そういった背景もあり、一度環境を変えて技術的な幅を広げる経験を積みたいと思った際に、ご縁があって BASE に入社させていただく運びとなりました。 入社して何をやっているか ネットショップ作成サービス BASE の、ショップオーナー様向けの機能開発に主に取り組んでいます。 特にショップ開設直後、スムーズにショップ公開まで行うことができるよう、そのオンボーディングを手助けする領域を担当させていただくこととなりましたが これまで会社としても抜本的な施策をそれほど多くうってこなかったドメインである 歴史があり、技術的負債も少なくない 多くの機能に影響しうるため、開発上の影響範囲が広いというだけではなく関係各所との連携も必要になる という背景もあり、自分にとってもチャレンジングで面白みの感じられる業務と思っています。 加えてプロジェクトチーム自体も新たに発足したこともあって、そういった意味でも一から作り上げていくようなわくわく感がありました。 一方で開発の進め方にフォーカスすると、 プロダクト開発に取り組む、プロジェクトチームとしてのフレームワークとしては「スクラム」 いわゆるソフトウェア設計手法としては「DDD(ドメイン駆動設計)」 を採用しました。 これは既に社内でも活用事例があり、参考にしやすかったことが前提としてはあります。 そして自身としても今までやってみたいとは思いつつも、業務の中でしっかりと取り組んだ経験がなかったものなので非常に前のめりではありました。 冒頭でも触れましたが特に新参者の自分にとっては、オープンにコミュニケーションをとり、前向きにどんどん試していこうという雰囲気がベースにあったことは大きかったと思います。 新しいチームで新しいことに取り組むことのハードルがぐっと下がり、その結果様々な知識を吸収し、価値のある経験を多く積むことができていると感じています。 どんな成長があったか しかしながら、もちろん全てが最初から順調に進んだわけではありません。 特に印象的だったことがこの2つです。 抜本的に画面構成やユーザー導線を見直すような施策を打とうとしたが、レガシーなアプリケーションの作りが制約となり、リファクタリングから始めざるをえなかった スクラムで開発に取り組もうとしたものの、チームメンバー内の認識がバラバラで円滑に進まなかった 以下、それぞれについて簡単にその経緯を説明します。 まず1つ目のリファクタリング対応が必要になったことについて。 これは本当に想定外で、蓋を開けてみたら判明したことではありました。 プロダクトオーナーから「こんなことがやりたい」という企画があがってきて、最初のうちは開発チームとしても「多少大変だろうがまあできるだろう」という感覚ではありました。 ただ実際にソースコードを見てみると、現状の構成のままでは実現自体が難しい、あるいは実現できたとしても相当な工数がかかり複雑な対応になってしまう、ゆえに不具合につながる可能性も高まるといったところです。 そのためリファクタリングすることに関しての合意形成はそこまで難しくありませんでしたが、当然ながら無尽蔵にコストや期間をかけられるわけではありません。 どこまでやるかのスコープを明確にし、そのうえでボトルネックとなっている問題を解決できる設計方針を練ることには非常に頭を悩ませました。 やったことの全てを挙げるのは難しいですが、 Web API の構成見直し(OpenAPI を利用しており、その開発体験については こちらの記事 も参照ください) それに伴い、MVC 構成でバックエンド側に偏っていた処理の一部をフロントエンド側に委譲 ユニットテストが十分になかったこともあり、責務を適切に分解してテスタブルなコードに修正 といったことに取り組み、そのリリースを経て施策が打ちやすい状態にすることができました。 リファクタリングという性質上、いわゆる目に見えるような形での成果はなかなかありません。 ただ今後の改修を安心かつ高速に実施できるようになったことは非常に重要であり、エンジニアとしても一回り成長できた、非常に有意義な体験となりました。 そして2つ目、スクラムでの開発がうまくいかなかったことについて。 こちらについてはやり方うんぬんの問題はありつつも、結論としては最初にチームメンバーでスクラムに対する認識を揃えておけなかったことが一番の問題であったと思います。 まず起こっていたことしては、デイリースクラムやスプリントプランニング、ふり返り会(スプリントレトロスペクティブ)といったイベントについてもある程度形式に沿ってやってはみたものの、どこか進め方や生み出されるアウトプットがしっくりこない、結果として「なんかやりにくいなあ」といった状況が続いていました。 そしてスクラムマスターがいなかったこともあり、その軌道修正を図る動きも起こりづらくなっていたという背景もあったかと思います。 どう改善を試みたかというとトリガーとなったのはふり返り会で、上述したような「スクラムにおける動き方についてそれぞれの頭の中に正解があり、そもそもそのすり合わせができていない」といった課題が挙がったことでした。 そういった意味では、ふり返り会が一定機能していたことが最後の砦となり、そこから改善の動きにつながったことは重要なターニングポイントであったと思います。 その後の流れとしては以下のようなことをやりました。 対象図書を決めて読書会を開き、チームとしての認識を揃える 仕事の進め方やチームとしての考え方をドキュメントに明示し、各イベントの進行方法などについてもフォーマットを固める 少しでも疑問に思うようなことがあれば、タイムリーに相談して話し合う 話し合った結果変わったことがあれば、それをすぐドキュメントなどに反映し、実際に試してみる そして少しずつ改善を重ねながら、以前と比べるとチームとして同じ方向を向いてメンバーが自立してスムーズに動けるようになったことで、アウトプットの質と量の向上にもつながってきていると感じます。 ただ形式どおりにやるのではなく、自分たちのチーム状況にあったやり方を都度考えていく中で悩み抜いたことが、確かな財産となって今があるんだろうなあと実感しています。 いわゆる個に閉じた開発スキルといったものとは少し性質が異なりますが、チームとして開発を進めるという点において、これも非常に大きな学びとなりました。 おわりに とは言え、全てを自分たちで一から解決しなければならないということは決してなく、困ったことがあれば先輩社員が気軽に相談に乗ってくれたり、壁打ち相手になってくれたりといったことも支えになりました。 また Slack におけるコミュニケーションも活発であり、スクラムについてはアジャイル開発に関するあれこれを相談できる #iikanji-agile といったチャンネルでのアドバイスも参考になりました。 そういった中で失敗も成功も経験し、開発に取り組む際に半年前の自分では絶対に出てこなかったであろう選択肢が自然と出てくるようになったときに、少しは力がついてきたんじゃないかな?という自信が生まれてきていると今では感じます。 まだまだ貪欲にインプットとアウトプットを繰り返していく必要はありますが、 新しいフレームワークや開発手法に取り組むことでエンジニアとしての幅を広げることができた その前提として、本記事で挙げたものに限らず新しいことに取り組むことへの理解や活発なコミュニケーションがあった ということを、結びの言葉とさせていただきます。 BASE では、このように一緒に試行錯誤しながら、会社としても個人としても成長できる環境で、一緒にサービスを発展させていく仲間を募集しております。 カジュアル面談も実施しておりますので、ぜひお気軽にお問い合わせください。 binc.jp
PHPカンファレンス沖縄2022のOGP画像 この度は、8/27(土)に開催された PHP カンファレンス沖縄 2022 にゴールドスポンサーとして協賛し、また 3 名のメンバーが登壇しました。 登壇者 3 名からオンラインで参加した感想や発表内容の補足など、PHP カンファレンス沖縄 の参加レポートをお届けします! 発表内容と補足 プログラミングをするパンダ( @Panda_Program ) セッション内容について @Panda_Program です。「実践!ユニットテスト入門」というタイトルで、テストを書いたことがない方向けの発表をしました。 テストを書いたことがない方が、テストを書いてみようと思うことを狙いとしています。経験ゼロから入門者・初級者になれるような内容となっています。 これは @hgsgtk さんのスライド「『質』の良いユニットテストを書くためのプラクティス」がテストの初級者を中級者にすることを目的として作られているため、それならば自分はテスト経験ゼロの人が初級者になるために必要な知識を得られる資料を作ろうと思ったことがきっかけでした。 このような温かいコメントを頂いたので、狙いは一定程度成功したかなと思っております。 登壇おつかれさまでした! こちらこそ有難うございました🙏🙏 まさに入門者にもとても分かりやすかったです! — haruna akashi (@harutp1001) 2022年8月27日 上記の2つの資料でユニットテストを書いてみて、次に質の良いユニットテストを書くコツを知った後、ぜひTDDを試してみてもらえたら嬉しいですね。 panda-program.com そして ATDD へ...!開発者にとってもテストの世界は奥が深いですね。 イベントの感想 今回はオンライン参加でした。事前に登壇動画を収録するのは初めてでしたが、現地に行きたい気持ちと登壇は録画だと楽だな〜という気持ちがせめぎ合っています(笑)。 Twitter で感想を貰い、Discord では質問や議論をするというようにツールによって役割が分かれていたように思いました。反応を頂けることは嬉しいですし、自分としても別の方の登壇内容に Discord で反応できたのは Twitter でのやりとりより身近に感じてよかったです。 また、めもりー( @m3m0r7 ) さん、ひさてるさん( @tanakahisateru )と Twitter / Discord で会話する、 @tadsan さんとPHPer Roomで雑談をしてコードゴルフを実演してもらう、 @mpyw さんにリプを飛ばす、ゆうきゃん( @ucan_lab )さんのお顔を拝見するという実績を解除しました!次はどこかで @mpyw さんに感謝を伝えたいです。私はあなたの記事を読んで育ちました。 PHP カンファレンス沖縄では PHP の話題に限らず、DevOps やスクラムといった開発の話、HTTP を見直す話であったり Laravel のコードリーディングといった技術の話などとても勉強になりました。話題が広いのは PHP カンファレンス沖縄のいいところですね。 来年はぜひ現地で参加したいです! 02 ( @cocoeyes02 ) セッション内容について はいさい!02です! 今回は以前記事に書いた「 登壇するぞ!って決めてからトークするまでの流れ - BASEプロダクトチームブログ 」をベースにしつつ、マイナーアップデートをした内容になりました。 今回の内容は、自身の登壇の話や登壇応援活動をしている中でFBした内容をまとめた内容になります。登壇応援活動については以下の記事も読んでみてください。 devblog.thebase.in また「プロポーザルやトークをレビューして欲しいけど、レビューしてくれる人がいないよ!」という方は、ぜひ僕に声をかけてみてください!微力ながらお力添えできればと思っています! ご静聴ありがとうございました〜 ちなみに社内に限らず、登壇前にプロポーザルやトークをレビューして欲しい!と言う場合はいつでもレビューしますので、是非メンションツイートやDMへどうぞ〜 #phpcon_okinawa #track_b — 02 (@cocoeyes02) 2022年8月27日 イベントの様子について 今年も興味深い発表が多くて良かったです! 去年と同じくオンラインでの参加になったのですが、オフライン会場の様子を見るととても楽しそうで行きたかったな〜〜〜と強く思いました! 沖縄会場の様子 #phpcon_okinawa pic.twitter.com/07QeX3u1er — カンボ🏝沖縄 (@kanbo0605) 2022年8月27日 来年もプロポーザルを応募をして、現地で登壇を目指したいと思います! キュアセブン ( @cureseven ) セッション内容について 今回はゴールドスポンサーセッションとして、「Gather × Code With Me × ペアプロのお誘い で最高です」というテーマのLTをさせていただきました。 使ったスライドはこちらです。 リモート下での開発を成功させるために、私のチームで3つのツールを使いながらコミュニケーションしている様子とその方法についてお伝えさせていただきました。 DiscordやTwitterの実況を通して、Gather使ってるよ!の声や、タスクの重さでどう取り組むか選択できる良さへいいねの声をいただきました。 また、タイトルにも入れた「最高です」の言葉でチームの雰囲気についても感じてもらえて嬉しかったです。 鍵は振り返りだと思います。気になっていることややってみたいことをチームメンバーにオープンにしながら、よりよい開発体験をチームで作っていけると良いと思います。 私たちのリモート開発はまだまだ続きます。更にいい開発ができるように頑張ります! 最後に 今回計 3 名のメンバーが登壇する機会をいただき、とても充実した時間を過ごすことができました! また多くの発表を通して様々な知識にふれることができ、各々が新たな知見や視点を持ち帰って来れたと考えております。 それもひとえに PHP カンファレンス沖縄実行委員会の皆様のおかげです。心より感謝申し上げます。 それでは、来年もまた皆様にお会いできることを楽しみにしております!
基盤チームの右京です。 BASE ではショップのデザインを誰でも簡単にできるような、いわゆるノーコードな機能を提供しています。 デザイン | BASE (ベイス) ネットショップを無料で簡単に作成 この記事では、そんなノーコードなシステムの裏側について、簡単にですが解説しています。 ショップページ配信の基盤システム ノーコードの前にまず BASE のショップページ(ShopFront と呼んでいます)がどのようなシステムかを知っておく必要があります。BASE のショップページは特定の URL にアクセスすると必要なデータをデータベースから取得し、テンプレートエンジンを使ってサーバーのプログラムで HTML を生成して返す、よくある伝統的な Web ページとして実装されています。ただし、ショップ毎にページのデザインは全く異なるため、1 つの固定のテンプレートを利用しているわけではなく、ショップ毎に動的に切り替えるような仕組みになっています。 このショップ毎のテンプレートを作る手段の 1 つが今回紹介するノーコードでのデザイン編集機能になります。ノーコードの他にも HTML に近い形式でテンプレートが記述できる仕組みもあり、現在ではそのどちらかを選択できるような形になっています。いずれかの方法で生成された何かしらのデータは「変換エンジン」を通して、ShopFront で実行可能なテンプレートとブラウザから呼び出されるアセット類に変換されます。 複数の形式から実行可能なテンプレートを生成する「変換エンジン」 「変換エンジン」はショップの管理画面と ShopFront をつなぐ中核的な存在で、例えば BASE Template として記述されたショップページのテンプレートを ShopFront が実行可能な形式へ変換します。ShopFront 自体はここまでにあったようにテンプレート自体は持っておらず、テンプレートのレンダリングの際に使用される変数の生成のみを行っています。この変数は JSON で表すと以下のようになっており、URL 毎に共通部分以外の内容が異なります。 { // この URL で表示しているページ種別の識別子 "page": "item", // どの URL でも使用できる、ショップの基本的な情報 "shop": { "name": "テストショップ", "logo": "https://...", "description": "テストで作成したショップです。" }, // 商品ページの URL で使用できる、その URL の示す商品の詳細な情報 "item": { "title": "テスト商品", "image": "https://...", "stock": 10, "price": 500 } , // ... 他にも多くの変数が存在する } ShopFront で使用できるテンプレートエンジンは Twig に似た形式で、社内では Cot と呼んでいます。一方で BASE Template は独自の記法を採用しているため、ShopFront で実行するには Cot の形式へ変換する必要があります。そこで、次のような変換行うのがこの変換エンジンです。 BASE Template 変換後(Cot) < html > {LogoTag} {block:ItemPage} < h2 > {ItemTitle} </ h2 > < img src = "{ItemImage0URL-origin}" > < p > ¥ {ItemPrice} </ p > < p > 残り {ItemStock} 個 < p > {/block:ItemPage} {block:AboutPage} < p > {ShopIntroduction} </ p > {/block:AboutPage} </ html > < html > < img src = "{{ shop.logo }}" > {% if page == 'item' %} < h2 > {{ item.title }} </ h2 > < img src = "{{ item.image }}" > < p > ¥ {{ item.price }} </ p > < p > 残り {{ shop.stock }} 個 < p > {% endif %} {% if page == 'about' %} < p > {{ shop.description }} </ p > {% endif %} </ html > これによって BASE Template という形式から、ShopFront で実行可能な形式へ変化しました。これをストレージに保存し、ショップを表示する際に ShopFront がテンプレートして利用することでショップ毎のデザインの切り替えが実現されています。ようするに、とにかく Cot という形式にさえしてしまえば、元のデータはなんでもよいような設計になっています。ノーコードも変換エンジンを利用する実装の 1 つで、BASE Template とは全く異なるデータをもとにして Cot を生成しています。 1 ノーコードでのデザイン編集 コンセプトと概念 「デザインをかんたんに、もっと自由に。」というコンセプトで開発されたデザイン編集機能は、「パーツ」と呼ばれる小さな部品をショップページに配置することで、ノーコードで自由な構成のショップページを作成できます。これはページ全体を細かく GUI から変更できるような機能ではなく、ある程度デザイン済みのページに対して必要な要素を追加、組み合わせていくものです。 これを設計していく上で「全体を大きく編集可能な部分とそうではない部分に分離し、可能な部分に対して任意の子要素を追加できる」を最も基本的な考え方としています。これは右の図のような概念になっていて、赤い枠で囲われている編集可能な部分を「コンテナ」、そこへ追加される黄色い要素を「パーツ」と呼んでいます。 「パーツ」はそれぞれが独立してデザインされており、例えばリストのカラム数や画像の回り込みのようなスタイルを個別に変更することも可能です。「コンテナ」の配置パターンと灰色の編集不可能エリアのデザインを複数用意し、それにショップに合わせた必要なパーツを乗せることで様々なデザインのショップを実現しています。 レイアウト用テンプレートと複数のパーツ用テンプレートを組み合わせて表現 これの実現方法として 2 種類のテンプレートを用意して、その組み合わせを Web 上で編集したものを(ここがノーコードな部分)、変換エンジンで 1 つのテンプレートとしてまとめています。1 つは大枠のデザインとコンテナを定義する「レイアウト」、もう 1 つは「パーツ」でそれぞれは次のようなコードになっています。 レイアウトは HTML 全体と、編集可能な箇所とする「コンテナ」を独自で定義した <cot-container> というタグを使って表現しています。この <cot-container> の子要素として様々なパーツが追加されていきます。 < body > < h1 >< img src = "{{ shop.logo }}" alt = "{{ shop.name }}" ></ h1 > < div > < div class = "main" > < cot-container name = "main" /> </ div > </ div > </ body > パーツはメタ情報が含まれているため少し長めになっていますが、商品パーツの例です。Vue.js の SFC と似たような構造になっています。 props はこのパーツに渡すことができる変数で React や Vue.js でいう props と同等のものとイメージしてください。この例では layout に row を設定することで、縦方向と横方向でレイアウトを切り替えられるような実装をしています。 < script name = "metadata" > { name: "item" , props: { layout: { ui: 'select' } } } </ script > < template > < div class = "container" data -layout= "[[layout]]" > < div class = "head" > < h2 > {{ item.title }} </ h2 > < img src = "{{ item.image }}" > </ div > < div class = "body" > < p > ¥ {{ item.price }} </ p > < p > 残り {{ shop.stock }} 個 < p > </ div > </ div > </ template > < style > .container { display : flex ; } .container [ data-layout = "row" ] { flex-direction : column ; } </ style > < script > function main ( $el ) { $el.addEventListener ( ... ) } </ script > これらを GUI 上でどのように組み合わせているかについては後ほど記載するのですが、最終的には次のような JSON として管理しています。デザイン編集という機能は、この JSON のエディターだと考えると今後がイメージしやすくなります。 { " containers ": { " main ": [{ " name ": " image ", " props ": { " src ": " https://.... ", " fit ": " cover " } } , { " name ": " item ", " props ": { " layout ": " column " } }] } } そして、この JSON を変換エンジンに渡すことで ShopFront で利用可能な Cot を生成しています。 <cot-container> のあった場所に、 props を展開したパーツのテンプレート部分が追加されています。 コードで見る < style > .container { display : flex ; } .container [ data-layout = "row" ] { flex-direction : column ; } </ style > < body > < h1 >< img src = "{{ shop.logo }}" alt = "{{ shop.name }}" ></ h1 > < div > < div class = "main" > < div name = "main-container" > <!-- 画像パーツ --> < div id = "zzzz" data -fit= "cover" > < img src = "https://...." > </ div > <!-- 商品パーツ --> < div id = "xxxx" class = "container" data -layout= "column" > < div class = "head" > < h2 > {{ item.title }} </ h2 > < img src = "{{ item.image }}" > </ div > < div class = "body" > < p > ¥ {{ item.price }} </ p > < p > 残り {{ shop.stock }} 個 < p > </ div > </ div > </ div > </ div > </ div > < script > ( function ( $el ) { $el.addEventListener ( ... ) } )( document .querySelector ( '#xxxx' )) </ script > </ body > このテンプレートを ShopFront で変数を用いてレンダリングすると、デザイン編集の GUI で作ったデザインが反映される、という仕組みです。 コア部分以外のデザインやデータは変換時点で静的に解決する 1 つに結合されたテンプレートには、デザインに関する CSS と必要な JS の殆どが埋め込まれているような形になります。これは ShopFront 単体では変数は動的な必要があるものに絞る、特にデザインに関する変数をほとんど持たせないような設計を取っているためです。デザインに関する部分は変換エンジンの時点でほとんどが静的な HTML となるため、ShopFront の仕様を小さく保つことにも一役買っています。 例えば「リアル店舗の地図を表示するパーツ」を作りたい場合は、地図のウィジェット埋め込みまでをパーツとして完結させることで ShopFront に変数を追加することなく機能を増やしていく、という方針を取っています。 JSON エディターとしてのデザイン編集 先程、ノーコードでのデザイン編集はようするに JSON エディターだと説明しました。BASE のフロントエンドは現在主に Vue.js で実装されており、デザイン編集も例外ではありません。では、レイアウトやパーツといった独自の形式のものを Vue.js 上でどのように扱っているかといいますと、それぞれを Web Components (!) へ変換し、Vue.js を使ってそれを動的に組み合わせるようにして実装されています。Web Components として、実際のショップページで使用されるものと同等のものをショップ管理画面でもレンダリングしてしまうことで、実際のショップページに近い見た目を再現しています。(説明のしやすさのため、この JSON をデザインデータと呼ぶことにします。) Web Components の slot の内容を Vue.js で制御し、編集を可能に 実装がどうなっているかを少し解説しますと、コンテナへ動的にパーツを追加できるよう、コンテナを Web Components の slot に置き換えて、その中にパーツの Web Components を追加しています。 <cot-container> を slot に置き換えた Web Components を作成し( layout-component )、その slot の内容を Vue.js で生成しています。 <body> <h1><img src="{{ shop.logo }}" alt="{{ shop.name }}"></h1> <div> <div class="main"> - <cot-container name="main" /> + <slot name="main"></slot> </div> </div> </body> そしてこの Web Components をプレビュー兼編集可能な GUI としてそのまま表示します。少しややこしいのですが、次のような構造になっています。 < VueContainer > < layout-component > < div slot= "main" > < DraggableContainer > < component :is= "`${p.name}-component`" v- for = "p in designData.container.main" :props= "p.props" /> </ DraggableContainer > </ div > </ layout-component > </ VueContainer > DraggableContainer は DnD を実装するためのラッパーだとしてください。新しいパーツを左側のエリアからここへドラッグすると、デザインデータへパーツの情報が追加されます。追加されると再レンダリングが起こり、 v-for によってそのコンテナに追加されているすべてのパーツが Web Components で表示されます。 パーツの見た目の変更 props に変更があった場合も同じように再レンダリングが起こり、Web Components が更新されます。これは Vue.js の機能ではなく Web Components で attribute の変更を監視し、必要なら自身の内容をレンダリングしなおすように実装されています。 class extends HTMLElement { // ... static get observedAttributes() { return [ 'props' ] ; } attributeChangedCallback(attrName, oldVal, newVal) { if (attrName === 'props' && this ._shadow) { this ._props = newVal this ._shadow.innerHTML = this .render() } } } 最後にここで編集したデザインデータを保存すると、変換エンジンがデザインデータをもとに ShopFront 用の Cot を生成して ShopFront から参照できるストレージへ保存します。これで、ノーコードでのデザイン変更が完了します。 まだまだたくさんの課題があります ざっくりではありますが、BASE が提供するノーコードなデザイン編集機能ついて解説してきました。この機能はリリースされてからもうすぐ二周年となるのですが、実現していきたい機能や解決していきたい課題がまだまだあります。技術的なチャレンジで言えば..。 そもそも Web Components としてパーツを作ることができればよいのでは?しかし SSR も捨てがたい... 商品パーツのように、どうしても機能が山盛りになりがちなものをどう開発保守していこうか? 外部デベロッパー向けに仕様を公開して、よりカスタマイズやサービス連携がしやすいようなプラットフォームにできないか? などなど..。 サービスとしても自由度と制約による保守のしやすさのバランスが大事だったり、ショップのブランディングがより幅広く可能なものを考えたりと、非常にやりがいのある分野となっています。そんなわけで BASE ではあらゆる方面のエンジニアを募集していますので、興味を持っていただたら幸いです。 採用情報 | BASE, Inc. 今回は省略していますが、テンプレートエンジンだけでは補いきれないものに関しては WebAPI が ShopFront に実装されています。商品リストのページングや、関連商品、レビューの遅延取得などはこの WebAPI 群を使用しています。 ↩
2022/08/27(土)に開催される PHPカンファレンス沖縄 2022 に BASE がスポンサード及び所属するエンジニア 3 名が登壇いたします。 PHPカンファレンス沖縄 2022 は、新型コロナウィルス(COVID-19)感染症拡大防止のため、オンラインおよびオフラインでのハイブリット開催となります。 また、BASE に所属するエンジニアは、オンラインによる参加となります。 https://phpcon.okinawa.jp/ BASE は、ゴールドスポンサーとして当カンファレンスに協賛しています。 ゴールドスポンサーの一覧(下から2番目にBASEロゴ) セッションの内容について プログラミングをするパンダ( @Panda_Program ) 「実践!ユニットテスト入門」15:30~ Track A テスト書いてますか? テストを書く理由と実際のテストコードを紹介する実践編に分け、TDD を3年間実践してきた経験に基づいてお話しします。 テストを書いたことのない方が、テストを書いてみたいと思ってもらえることを目指します。 サンプルコードは PHP + PHPUnit ですが、他言語でも通用する考え方を紹介します。 ■ 概要 なぜテストコードを書くのか レガシーコードとは、テストのないコード テストはコストが安いフィードバックループである ■ 実践編 テストケースは日本語で書こう いろんな assertion を知ろう arrange / act / assertion のテストコード実装パターン set up / tear down を使って前処理/後処理をする dataProvider でテストをまとめる(ただし早すぎる抽象化に気をつけよう) 02 ( @cocoeyes02 ) 「カンファレンススピーカー入門〜登壇するぞ!って決めてからトークするまで〜」14:30~ Track B みなさんはカンファレンスで登壇したことがありますか? カンファレンスで登壇をしているスピーカーは、様々な過程を経てみなさんの前でトークをしています。 例えば採択前ならネタ決めやプロポーザル、採択後ならスライド作成・トーク練習などの準備・・・ このトークするまでの過程は、人によって違うところもあり暗黙知であることが多いように思えます。 そこで今回は、過去に私がPHP系カンファレンスにて登壇した内容を例にしつつ、自分がカンファレンスで登壇するまでに準備していることを話します。 まだ登壇したことがない方はもちろん、登壇したことがある人も良いところを取り入れられるきっかけになれば幸いです! 具体例を交えつつ、以下のことについて触れる予定です! プロポーザルを出す時にどんなことを考えているのか? トークするまでの準備はどういうことをしてるのか? キュアセブン ( @cureseven ) 「Gather x Code With Me x ペアプロのお誘いで最高です」13:50~ Track A ゴールドスポンサートーク 私が所属しているチームでは、リモートワークでペアプロ・モブプロによる開発を行っています。 隔週の振り返りでネクストアクションをみんなで考えて実践し続けた結果、リモートワークでもコミュニケーションが充実し、手戻りが減るなどの効果が現れました。 そんな私たちが半年かけて洗練したペアプロ・モブプロの運用方法をお伝えします! 具体的には以下のような話をします。 PhpStorm の Code With Me 機能の活用でリモートワークでも快適に Gather の活用でフィードバックを素早くもらえる 「ペアプロのお誘い」Slack ワークフローの活用で柔軟にペアを組む 最後に PHPカンファレンス沖縄 2022 の当日のチケットは下記よりお申し込みいただけます。 https://techplay.jp/event/866236 それでは、みなさまにお会いできることを楽しみにしております。
こんにちは!BASE BANK Sectionの松雪( @applepine1125 )と清水( @budougumi0617 )です。 去る2022/08/04(木)にオンラインで開催された株式会社UPSIDERさまと株式会社Kanmuさまとの合同Meetup 「Tech Meetup 〜Goで作る決済サービス〜」にBASE BANK Sectionから2名登壇しました。 upsider.connpass.com youtu.be どんなイベント? 今回のイベントはGo×決済というテーマで、開発時の工夫やこれまで直面した課題などについてGo言語を駆使してカード決済事業を行う3社がそれぞれ事例を紹介、ディスカッションするものでした。 ご一緒させていただいたUPSIDERさま、Kanmuさま改めて感謝申し上げます。ありがとうございました! 当日は参加した3社の発表と、Goや決済についてのパネルディスカッションが行われました。 当日のハッシュタグは #go_tech_meetup でした。当日のツイートを遡ると発表時の盛り上がりが伝わるかと思います。決済ならではのコアな質問も飛び交っていたのでぜひ眺めてみてください! LTした内容について 今回LTは BASE BANK Sectionの松雪( @applepine1125 ) がBASEカードというプロダクトで行っている決済シミュレーションテストについて発表しました。 basecard-lp.thebase.com speakerdeck.com 複雑なカード決済の仕様が存在する中で、質の高い決済機能を提供するためにどのようなテストを行っているかについて発表しました。 今回発表した決済領域に限らず、複雑なドメイン知識や仕様が存在する事業では、どのようにプロダクトの質を担保するのか、そのためにどういったテストを行うべきなのかについて熟考する機会が多いかと思います。 今回の発表では、シミュレーションテストの実現方法だけでなく、 なぜシミュレーションテストを行おうと思ったのか? について、その背景にある複雑な決済の仕様も踏まえて話をまとめました。 なぜテストをするのか?という点については決済領域に限らずどのプロダクトでも考える必要があると思っており、今回の発表を通してその1事例をご提供できればと思って発表をしました。 パネルディスカッションの内容について 各社の発表後、合同でパネルディスカッションが行われ、松雪( @applepine1125 )と清水( @budougumi0617 )が参加しました。 当日は以下の様なテーマでディスカッションが行われ、パネラーだけでなくtwitter上でも大きな盛り上がりを見せていました。 各社のアーキテクチャ紹介 Goを書いていてよかったと思うこと 残高管理や決済などコア機能のテスト戦略 開発チーム構成や開発プロセスで気をつけている事 最後に宣伝 BASE BANK Sectionでは、今回発表させていただいたBASEカードだけでなく、BASEを利用するショップオーナーさんのキャッシュフローを改善するための様々なプロダクト開発を行っています。 Goを中心にPython, PHPなどの言語を駆使し、フルサイクルエンジニアというスタンスでフロントからインフラまでを一気通貫に触るだけでなく、一連の開発プロセスにエンジニアが主体的に関わり質の高いスピーディなアウトプットを行っています。 basebook.binc.jp そんなエンジニアがオーナーシップをもったチームでプロダクトのグロースに挑戦したい!と思った方や今回の発表で金融決済領域に興味を持った方はぜひ下記の紹介資料や募集をご覧ください! 今回参加したメンバーへのtwitter DMからでもお待ちしております! speakerdeck.com open.talentio.com
2022/07/23(土)に開催された ISUCON 12 *1 予選に BASE から8名のエンジニアが6チームに分かれて参加しました。 参加者の感想をお届けします! isucon.net チーム「Speed of Sound」 最終スコア 4,711点 およそ210位でした。当日の作業リポジトリは以下です。 github.com 清水(@budougumi0617 ) BASE BANK Section テックリードの @budougumi0617 です。 ISUCON11に引き続き2回目の参加でした。前回同様環境構築、ミドルウェアの設定変更、監視用のツールの導入などを主に対応しました。 構成管理をしたい欲を出しすぎてかなり足を引っ張ってしまいました。 /etc ディレクトリ以下の設定ファイルのパーミッションを変更、シンボリックリンク化した副作用で PAM まわりの挙動をおかしくした sudo コマンドも動かなくなってCloudFormationをやり直すはめに MySQLの my.conf 周りのファイルをシンボリックリンク化したためスロークエリ設定の有効化に時間を溶かす AppArmorも切っていたはずなので謎 スロークエリログを確認してMySQLの PREPARE を抑制する お決まりの変更 をしたあとはほとんど有効な一手を打てませんでした。 alp コマンドや netdata の導入もしていたのにスロークエリ結果からしかアプローチを考えない狭窄的な行動になっていました。 また、最近のISUCONで多用されていた env.sh やsqlファイルに ALTER や INDEX を追記すれば initialize で毎回テーブルが初期化されるような構成に馴れていたため手間取っていたところも多かったです。 終盤ではベンチマークの検証が通らなくなってしまい復旧に手間取りました。シードデータファイルが見当たらなかったので安易にDB上のデータを消したのが原因でした。他のインスタンスからデータをダンプして復旧しましたが、競技とはいえ安易にデータベースのデータをふっ飛ばしてはいけないということを再確認しました。 sqliteを意識するところまでボトルネックの解消ができず非常に悔しい結果になったので来年はリベンジしたいです。 永野(@glassmonekey) こんにちは @glassmonekey です。今回初参加です。同僚に誘われたのでいい機会と思い参加してみました。 特に分担する予定はなかったのですが、MySQLだけとりあえず剥がすといった若干インフラ寄りの作業をしたりしました。 サーバーにsshして/etc以下を触る経験は久しくしてなかったので懐かしい気持ちになりました。 反省点としては、サーバー構成の反映漏れとかで時間が溶けるタイミングとかあったので、アプリケーションの改修にそこまで時間を取ることができなかったことがありました。インフラ構成はAnsible化するなりしていきたいなあと思ったりしました。 短期間でドメイン知識の吸収からサービス特性を読み解くところも求めれるので、楽しい時間でした。次回は予選突破を目指すぞ。 吉田 ( @yopidax ) 今回初参加でした。前々からISUCON自体は気になっていたのと同僚の清水さんに誘わたのがきっかけで参加してみました。 チームでの作業分担は特に無かったのですが、私はMySQLのスロークエリを見つつボトルネックを特定しindex貼ったりアプリケーション側の改善をしていました。対策として練習なしで過去問題と解説を眺めるくらいしか行わず挑んだのですが、結果あまり手を付けることが出来ず準備不足を痛感しました。 短い時間で仕様を読み解きつつ普段一緒に働く同僚とワイワイしながら進めること自体は楽しかったので、事前練習を行いつつ次回も是非参加したいと思います! チーム「ngsw」 最終スコア 3,614点 @ngsw インフラ側の問題に至る前のボトルネックをそもそもどうにもできませんでした。 なので以下を行なったのですが、点数的にはほぼ意味がありませんでした。 Dockerはがし sqliteを/dev/shm 上に配置(これしたからOS再起動試験が1.5hかかった) ISUCON参加(および前回予選問題を利用しての事前の社内練習会)で自身の苦手な領域が、目に見えてわかるのでそこが非常に面白かったです。 なので答え合わせ重要と思いました。予選通過者のエントリを読んだりしながら、ベンチマーカー公開されるのを待ちたいです。 dockerが起動してるEC2で、それをはがして云々みたいなのはパズル的な面白さがありました。 オペレーションが身体性を伴ってきたときに訪れる万能感があり、そのために素振り重要なんだろうなって感じました。 チーム「牡蠣食えば 金がなくなり リボ払い」 最終スコア 7,472点 @cureseven 今年もISUCON10で本戦に一緒に出場した社外の友人と参加しましたが、7,472点133位で敗退しました。 私は今年もアプリケーションを担当しました。今回の問題はsqliteをmysqlに置き換える作業が終わってからindexを貼り、その上でボトルネックの特定をしようと思い、N+1を潰したり早期returnさせ無駄な計算を省いていたりしていたら時間が来てしまいました。 練習の成果もあり初動は毎回上手くいき、数分は1位にいたりもしたのですが、sqliteの置き換えにおいてのinitializeが時間内に終わらない問題などにぶつかり後半は点数が伸び悩みました。 簡単に解決できないとき、別の手を考えたり一緒に方針を考えたりするなど、チームの柔軟な動き方が課題でした。個人的には去年よりも手数を打てて成長を感じました。 本戦問題として出てもおかしくないような難易度の高い問題で大変刺激になりました。運営の皆様ありがとうございました。 チーム「example.com」 最終スコア 3,119点 横山 今回で3回目の参戦です。 クエリ中心に最初は見ていきexplainなどのクエリ実行しながらindexの効果を確認していきました。 そのあとはN+1の実装部分を修正してループしたinsert処理をバルク処理に書き直したりしました。 結果としてスコアは伸びず3,119点となりました。 前回、前々回は3人で参加していましたが今回は諸事情で1人での参戦となりその面でモチベーションを保って対応するのが難しかったです。 次回はまた3人で参戦してワイワイしながらやりたいと思います。 チーム「今すぐダウンロード」 最終スコア 0点 (計測失敗前の最高スコアは1,400点台(1,466?) 田中 @tenkoma 初めてISUCONに参加しました。 まず、参考実装をPHPに変更後の初期スコアが1,000点くらいになりました。 その後、MySQLサーバをアプリケーションサーバと分けて1,400点くらいに。 SQLで複数回呼び出しているものをまとめたり、インデックスを追加してみたものの効果が出ず。 最後に、SQLiteファイルをMySQLに移行するために以下の作業をしましたが、ベンチマーク前の初期化が終わらなくなってしまい、0点で終了しました。 アプリケーションから tenant DBの参照先を admin DBと同じ接続に変更 SQLite ファイルをdump して1ファイルにし、初期化シェルスクリプトでMySQL DBに投入 また参加する機会があればチームで参加したいです。 チーム「IMS」 最終スコア 0点 炭田( @tac_tanden ) 今回、社外の友人と3人で初参戦でした。最終スコアは0点(0点になる前の最大スコアは4,821点でした)で非常に悔しく感じています。 競技中は主にクエリのチューニングとアプリケーションコードの変更、nginxのチューニングなどもトライしていました(が、全然スコア上がりませんでした...)。MySQLの移管に関しては試みましたが、時間内に完了させることができませんでした。 ボトルネックを冷静に見定めるためのツールを使えるようにするのを、序盤に素早く設定できるようにするのが次回の課題なので、来年出場する前に練習しようと思います! 最後に 今年も挑戦のしがいのある問題、当日の快適なコンテスト環境を準備していただいたISUCON運営の皆様に感謝いたします。 社内では #club-isucon チャンネルでISUCONに向けた情報交換や合同練習をしていました。 残念ながら今回の予選を突破できた弊社メンバーはいませんでしたが、『 達人が教えるwebパフォーマンスチューニング 』の勉強会などを企画して来年のリベンジに向け動き出したいと思います! 宣伝 BASEでは購入者やショップオーナーの皆様のことを第一に考え、パフォーマンスチューニングにも興味があるサーバーサイドエンジニアの仲間を募集しております。 カジュアル面談も実施しておりますので、ぜひお気軽にお問い合わせください。 open.talentio.com open.talentio.com *1 : 「ISUCON」は、LINE株式会社の商標または登録商標です。
2022/7/21(木曜日)にオンラインで開催された「 Looker User Meetup Online #8 」に BASE BANK Dev Groupの永野(@glssmonekey)が登壇しました。 looker-japan-user-group.connpass.com イベントについて 今回のテーマは「Lookerまでのデータデリバリー、みんなどうしてる?」でした。 各社のLookerまでのデータパイプラインの構成やチーム構成など、Lookerを中心にしたデータに関する話題でわいわいしました。 当日は弊社含めて次の4社(敬称略)の発表が行われました。 Unipos株式会社 株式会社タイミー 株式会社フィードフォース BASE株式会社 当日のTwitterハッシュタグは #lookermeetup でした。 登壇内容について 今回、永野( @glassmonekey ) が発表した内容は次のスライドです。 今回はアプリケーションエンジニアの視点から、「リリースサイクルにデータ分析基盤の構築を含めて行うこと」の重要性というコンセプトでトークしました。 日々変化するビジネス要件に応じて、見たいデータは変化していくことでしょう。 そのような要望があると以下のような方法が多いのではないでしょうか? データ基盤のスキーマを変更する 分析のためのSQLを変更する etc... 多くの場合で専門のチームがこれらをメンテナンスしていくケースが多いように思われますが、 その場合だとビジネスの変化という速度に適応することが難しいのではないでしょうか? そのため私達のチームでは、データ分析基盤の構築もリリースサイクルに含めて構築するようにしています。 この「リリースサイクルにデータ分析基盤の構築を含めて行うこと」の実現に、Lookerはとても役立っています。 現状は 派生テーブル を使うことで、日々変化するビジネス要件を我々自身で見たいデータを構築しています。これにより、リリースに応じた指標をスピーディーに分析する体制を実現しています。 感想 派生テーブルを活用しているものの、今後の展望やデータの粒度で迷ってる点が色々あったので、イベントにて dbt や PDT の活用事例を実際に聴くことができ、最高にわくわくする時間でした。 現状CDCにembulk+マージクエリで頑張ってたのですが、dbtを使うともう少し楽にできそうみたいなところを知ることができたので、実際に業務でも取り入れてみたいなと思いました。 他にも話題としてデータのテスト戦略が上がっていたので、改めて他社さんの事例をとても聞いてみたいなと思いました。 データ基盤のテスト設計は皆目検討についてない状態なので、ぜひ学びたい!! 宣伝 BASE BANKチームでは Go, Python, PHPを中心に、フロントからインフラまでを一気通貫で開発しています。 また開発だけでなく、機能をグロース・分析・サポートまで担当します。 basebook.binc.jp そんな開発スタイルのチームで新規事業開発に挑戦してみたいぞ!と思った方はぜひ下記の募集をご覧ください。 open.talentio.com
2022/6/28(火)にオンラインで開催された「 新規事業プロダクト開発時の技術選定どうやった? -カケハシ×LINE×BASEの開発者が振り返る技術選定プロセスと内省- 」にBASEから2名が登壇しました。 techplay.jp イベントについて 今回登壇させていただいたイベントは新規事業プロダクト開発時の技術選定について各社の事例を紹介するものでした。主催の株式会社カケハシさまお誘いありがとうございました。 当日は弊社含めて次の3社(敬称略)の発表とパネルディスカッションが行われました。 株式会社カケハシ LINE株式会社 BASE株式会社 当日のTwitterハッシュタグは #TechKKHS でした。 登壇内容について 今回、BASE BANKチームの柳川( @gimupop )と清水( @budougumi0617 )が合同で発表した内容は次のスライドです。 登壇内容について(柳川) 新規事業を行う上での心構えやマインドを中心に、経験から話しました。 そしてそれが現在にどうつながっているかの振り返りも含めて話させていただきました。 持ち帰っていいただきたいものは勇気という言い方をしました。新規事業のおいては、やはりある意味属人的な人の決断というものが必要です。 今振り返ってみると、技術選定時の決断が、その後のチームの精神の核になっていると気が付きました。 引き続きアジリティを落とさない形で、チーム拡大をし、アウトプットのクオリティをあげていくことに取り組んで行こうと思いました。 登壇内容について(清水) 新規事業を複数リリースしグロースさせつづけていくためにチームで大事にしていることとそのために選択した技術について話させていただきました。 技術選定はチームが何を大事にしているかによって大きく変わってくるかと思います。 今回の登壇では特定の技術の紹介というよりも 「何を基準に技術を選んだのか?」「リリースから4年間開発し続けていてその効果をどう感じているか?」 を中心に話をまとめました。 また、自分としても今使っている技術のどこに良さを感じて活用し続けているのか見つめ直すいい機会になりました。 これからも「プロダクトやドメインに集中する」という目的を忘れずに技術と向き合っていこうと思います。 パネルディスカッションについて 各個人の発表後は3社合同でパネルディスカッションが行われ、当日は次のような話題が挙がりました。 エンジニア以外も含めたらチームにはどんなロールの人が何人くらいいるのか 「やっぱりああしておけばよかったな」と後悔していること 新規開発に向いていそうな人 パネルディスカッションの内容はTECH PLAYさまのイベントレポートで確認できます。 techplay.jp 宣伝 BASE BANKチームでは Go, Python, PHPを中心に、フロントからインフラまでを一気通貫で開発しています。 また開発だけでなく、機能をグロース・分析・サポートまで担当します。 basebook.binc.jp そんな開発スタイルのチームで新規事業開発に挑戦してみたいぞ!と思った方はぜひ下記の募集をご覧ください。 open.talentio.com
こんにちはエンジニアリングマネージャーをしております植田です。4月18日に グロースプラン の提供が開始されました。今回この開発プロジェクトにて「リスク管理」に取り組んでみたのでそのお話をします。 Index リスク管理に取り組んだ背景 そもそもリスク管理とは 具体的なリスク管理の進め方 リスク管理はどのようなプロジェクトで実行すべきか 実際にどのように取り組んだか 取り組んで良かったことと、今後発展させたいこと リスク管理に取り組んだ背景 まず今回なぜリスク管理に取り組んだかをお話します。BASEでは現在大小様々な開発プロジェクトが同時進行していますが、日に日にその複雑性は増しています。年月を追うごとに積み重なる仕様、日に日に拡大していくリポジトリ(ソースコード群)…と、複雑性・難易度は増す一方です。その中で、開発プロジェクトもいわゆる「不確実性が高い」と言われることが当たり前の状況になっています。不確実性とはつまりリスクのことです。この不確実性・リスクに立ち向かうにはリスク管理を行っていくことが有効な手段であると自身の経験から考えました。冒頭に触れたグロースプランプロジェクトは 「ミッションクリティカルなプロジェクト」 であったこと、 「複数のチームからメンバーが集まり横断的に開発するタスクフォース型のプロジェクト」 であったこと、という点でリスク管理をするにはもってこいのプロジェクトであると考え、関係者に呼びかけリスク管理に取り組んでみることとなりました。 なお、私自身はPMBOKをベースとしてリスク管理を学びましたので、もしご興味がある方はPMBOKの書籍等もあわせてご覧いただけると嬉しいです。 そもそもリスク管理とは リスク管理とは、プロジェクトにおける潜在的なリスクを洗い出し、リスクが顕在化しないように先手を打つプロジェクト管理手法の1つです。また、仮にリスクが顕在化してしまった時のためにあらかじめ対処を決めておくことで問題の影響を最小限に抑える行為です。リスク管理を行うことで、問題を防ぐことができ、また問題に発展した場合も即座の対応が先回りしてできるため、言わば転ばぬ先の杖として機能することができます。プロジェクトマネジメントの格言として 「リスクを制する者はプロジェクトを制す」 といった言葉があるほどリスク管理は重要なプロセスですのでぜひご参考いただきたいです。 具体的なリスク管理の進め方 具体的なリスク管理表を見て頂いたほうが理解しやすいので下記のサンプル表をご覧ください。 ※サンプルのためグロースプランプロジェクトの内容とは関連ありません このようなリスク管理簿を作成し運用していきます。サンプルのためシンプル化していますが、実際には、起票者、起票日、備考欄など管理上必要だと思う項目は適宜追加ください。 続いて実際の進め方、サイクルについて解説します。 こちらの図のように1週間程度のサイクルで「リスクの特定→対策の立案→対策の実行→モニタリング」を繰り返すのが基本です。 ①リスクの特定と優先順位付け プロジェクトのキックオフが開始されたあとはできるだけ早期にリスク特定のための会議を開きましょう。リスクはプロジェクトが開始された時点から潜在的に存在するものでプロジェクトの脅威となるものです。1日でも早くリスクを特定し対策を実行することでプロジェクトの成功確度が高まります。1〜2時間ほど関係者にてリストアップを行いましょう。 リスクが洗い出せたあとは、リスクに対して優先順位を決めていきます。仮にリスクの洗い出しを行って20件のリスクがリストアップされたとしても、愚直にリストの上から順番にすべて対処をするのは効率的ではありません。発生確率と影響度合いを決定し、それらをかけ合わせた上で優先度を決定し、優先度が高いものから対処を検討していきましょう。なお、発生確率と影響度合いは3段階で決定しますが、こちらは当事者で相談して決定すればある程度感覚値であっても問題ありません。優先度が決まればよいので、この数値に厳密性は必要ありません。 ②リスクへの対応戦略(対策の立案) リスクには「リスクが顕在化しないようにする事前対策」を実行しモニタリングすることが有効な手段です。リスクへの対応戦略は以下の4種類に大別されます。 種類 説明 回避 リスクを避けたり、リスクの発生原因を取り除いたり、リスクの影響を避ける為にプロジェクトの計画を変更すること 転嫁 リスクの影響を責任とともに第三者へ移転すること 軽減 リスクの発生確率および影響度をプロジェクトが許容できる程度まで低減すること 受容 事前対策を講じないと決めること、リスクの除去が困難な場合や、万が一リスクが顕在化しても大きな影響を及ぼさないときに採択される これらをリスクごとの基本方針としプロジェクトメンバーと共に検討し、対応策を決定していきます。優先度の高いリスクへの対策に関しては第三者や上位層へレビューしてもらうことでより有効な対応策にブラッシュアップしていくこともできます。リスク管理を繰り返していくことで、対策を考えるコツも掴めてきますので、当事者でどんどんアイデアを出し合ってよりよい対策を立案していけるといいですね。 ③対策の実行 リスクのリストアップ、優先順位付け、対策が決まれば、優先順位の高いリスクから1つずつ対策を実行していきましょう。担当者(担当チーム)、対応期日を決めきちんと対応していくことでプロジェクトが実を結びます。 ④モニタリング 実行プランができあがり対策が順次開始されたあとは ”モニタリング” が重要となってきます。新たなリスクが生まれていないか、既存リスクが問題となって顕在化してこないか、リスクが顕在化した場合はすぐに課題の対策を実行する、といった動き出しが必要です。なお、プロジェクトが終了する時期にリスクをすべて解消・解決している必要はありません。優先度が低いもの、リリースに特段の影響を及ぼさないものは無理に解消する必要はありませんので、現実とも向き合いながらモニタリングしていきましょう。 リスク管理はどのようなプロジェクトで実行すべきか リスク管理が有効だからといって、動いているすべてのプロジェクトで実行する必要はないでしょう。ではどんなプロジェクトで活用すればよいかですが、例えば以下のような項目にあてはまるプロジェクトではリスク管理の適用を検討してみると良いと考えていますのでご参考ください。 期間的:3ヶ月以上かかるようなプロジェクト 技術的:技術的に難しい、新しい技術を導入するなど 人的:リソースが不安定、新規の顔ぶれ、ステークホルダーが多いなど その他:イレギュラー要素があるなど 実際にどのように取り組んだか 今回はトライアルでグロースプランプロジェクトにて取り組んでみたので、まずは関係するマネージャー数名で始めてみました。以下は具体的な経過です。 1回目(1時間):初回はまず想定されるリスクをみんなでリストアップし、結果20件ほどのリスクが洗い出されました。この日は発生確率と影響度合まで相談し優先度を決定しました。 2回目(1時間):翌週の2回目は優先度9〜6までのものに対してリスクの対策を検討しました。時間があればすべての対策を考えてもよいのですが限られた時間を有効活用し優先度の高いものにフォーカスして対策を決定しました。 3回目(1時間):さらに翌週の3回目は優先度9〜6までのリスクの対策の進捗確認と、優先度6未満の対策を検討しました。 4回目以降は基本的にリスクの対策の進捗確認がメインとなり、プロジェクトがリリースされるまで毎週30分程度集まりました。 大事なことはリスクの対策が進むに従い、それぞれのリスクのステータスを未着手→対応中→モニタリング→クローズ、と変更していくことだと思っています。こうすることで、プロジェクトにおけるリスク管理が進捗していることがステータスの遷移と共に把握できます。 今回は1週間に1度の頻度でリスク管理定例を開催しましたが、開催頻度は現場で相談の上決定いただければよいかと思います。また、今回のプロジェクトは3ヶ月程度の期間でしたので、リスク管理定例も合計10回程度開かれたのが実績となります。 取り組んで良かったことと、今後発展させたいこと 取り組んで良かったこと 20件程度のリスクがリストアップされ、リスク顕在化の抑制が出来たり、リスクが顕在化したときにあらかじめ想定した通りの対処が実行されたこと リスク管理が実施されていなければ、問題が発生してから慌てて解決に動いていたであろう事象が複数あったこと ステークホルダーに対してプロジェクトにおけるリスクの件数を定量的にレポートできたこと 今回初めて取り組んだマネージャーからリスク管理終盤に「やってよかったですね」と率直な感想をもらえたこと 今後発展させたいこと 開発チームメンバーとともにリスク管理に取り組みたい 今回はトライアルだったこともありまずマネージャーのみでリスク管理に取り組みましたが、開発プロジェクトのリスクを一番知っているのは開発に向き合っているメンバーだと思いますので、ぜひ今後はマネージャーとメンバーが一緒になってリスク管理をやっていけたらと思っています。 リスク管理の横展開 今後少しずつ社内で横展開を行いリスク管理をしているプロジェクトを増やしていきたいと考えています。 以上がBASEでリスク管理に取り組んだお話です。ぜひ皆様もリスク管理に挑戦いただけると嬉しいです。
はじめに 基盤チームでバックエンドエンジニアをやっている松田( @tadamatu )です。 以前にCTO川口が当ブログ内で公開した以下の記事があります。 devblog.thebase.in 新規接続の限界 BASE のアクセス量の伸びは凄まじくこの構成でも接続エラーが発生するようになってしまいました。 ピーク時に秒間 2 万もの新規接続が primary インスタンスへ行われているといった状態です。 この記事が公開されたのが約2年前で、 当時100万程度 だったショップ数は 170万を超え 、我々はまだまだ伸ばしたいと考えています。 これは、ショップ数の伸びとともに、指数関数的に増えていくユーザのアクセスを捌く必要があることを意味します。 ブログ公開当時、我々はさまざまな検討の末、以下のような対策を取りました。 残された手段は primary のインスタンスに対しての接続数を如何にして減らすか、ということのみです。 ここで出来ることアプリケーション側の接続をいかに reader インスタンスに行うか、ということです。 これはクエリを reader に向けるというよくある負荷対策ではなく、新規接続自体を reader に最初から向ける 対応後もメディア露出などによりアクセスが集中すると、たびたびMySQLへの新規接続がボトルネックとなり、接続しづらい状態に陥っていました。 そこでBASEでは、 RDS Proxy が2020/06にGAされた こともあり、検証を開始し、導入するまでに至りました。 aws.amazon.com 本記事では、そこで得た 知見 、 PHPとの相性はどうなのか 、 検証した方法 、 導入後どの程度効果があったのか 、 導入後発生した障害 、など、RDS Proxy導入における メリット・デメリット を公開させていただこうと思います。 RDS Proxy導入方法など基本的なことは他の記事に譲ります。この記事では 実際の導入を通して得たTips をできるだけ網羅しましたので、 RDS Proxyが気になっている方 や 導入を検討している方 のお役に立てれば嬉しいです。 RDS Proxyとは まず、 RDS Proxy を導入することで公式に記載されているメリットを簡単に書いておきます。 aws.amazon.com (1) 接続プーリングによるパフォーマンスの向上 (2) フルマネージド (3) フェイルオーバー発生時の時間短縮などによるアプリケーションの可用性向上 (4) IAM 認証を利用しセキュリティレベルの向上(オプション) などいくつかありますが、もちろん我々が一番得たかったメリットは (1) になります。 導入前の検証 さて、検証を始めていくわけですが、ポイントとなったものを記載していきます。 アプリケーション的な障壁はほぼなかった まず手始めに実際にDEV環境にRDS Proxyを構築してみて、アプリケーションからのDB接続をRDS Proxy経由に切り替えて検証をしてみます。 これに関してはQAチームに協力をしていただき、一通りリグレッションテストを通すことで検証、動作担保を行いました。 結果、あっさりリグレッションテストをパス! つまり、RDS Proxyの切り替えに関してコードの変更はほぼなしで可能で、 コード変更するのは接続先の切り替えくらいというライトな感覚で切り替えが可能 ということが分かりました。 (後述しますが、BASEではRDS Proxyの切り替えで、全くアプリのコードは変更しませんでした) 費用面について見積もり RDS Proxyは RDSインスタンスのvCPUの総数 に応じて費用がかかります。 aws.amazon.com BASEでは、過去に障害が発生していた経緯もあり、安定運用のため、少し力技でAuroraのインスタンスを増やし、インスタンスサイズも大きくしたものを利用していました。 そのままシフトしてRDS Proxy採用した場合で見積もると、 年間300万円程度の上乗せ費用が発生する ことが分かりました。 決して小さな額ではないですが、BASEでは将来の接続安定性が得れるのであれば導入しようということで、検証を続けました。 しかし導入後、力技分のインスタンスを減らし、インスタンスサイズも落とす、という調整ができたことで、結果的にRDSの削減分も含めると 年間960万円もの削減 ができる計算になります。 これは非常に大きなメリットでした。 ピン留めが心配 RDS Proxyを導入するにあたって、一番気になっていたのは ピン留め という現象です。 ピン留めを回避する より抜粋 RDS Proxy は、他のセッションに不適切なセッション状態の変化を検出すると、クライアント接続を特定の DB 接続に自動的にピン留めします。ピン留めにより、接続の再利用の有効性が低下します。 ピン留めは CloudWatch の DatabaseConnectionsCurrentlySessionPinned メトリクスによって確認ができます。 検証の結果、BASEのアプリケーションからの接続では ピン留めが全く発生しませんでした 。 BASEではCakePHPをフレームワークとして利用していますが、PHPは一般的に(ミドルウェアなどで接続プーリングを実装していない限り)リクエストのたびに接続・切断を繰り返すため、ピン留めの影響を全く受けなかったのではないかと推測しています。 ちなみに、この悪魔の証明とも言えそうなメトリクスに表示されないピン留めをどのように検証したかというと、まずRDS Proxy経由でmysqlクライアントにより接続し、 SET ステートメントや LOCK TABLE 操作をすることにより無理やりピン留めを発生させ、メトリクスとしては検知できることを確認します。 その後、QAのリグレッションテストを通したときにピン留めメトリクスが検出されないことを確認することで発生していないと結論づけました。 (実際、本番リリース後もこのメトリクスはほぼ検知されませんでした) 負荷試験について BASEでは、負荷試験用のDEV環境があります。 今回は、その環境で動かすアプリケーションからのDB接続を、 (A) RDS直接接続した場合(現状の環境想定) (B) RDS Proxy経由し接続した場合 で比較し、検証しました。 検証する軸としては以下の2軸 (1) インスタンスタイプによる調整 (2) スクリプトによりアクセス数を調整 まず (A) の限界値 を見極めます。 (1) を本番と同じものに固定し、 (2) のアクセス数を上げていき、 エラーが出る限界値 をだしました。 次に、 (B)の限界値 を各軸で上下させることで出していきます。 (2) を固定にして、 (1) を下げていきます。 結果、 インスタンスタイプを1/4 サイズに落としてもエラーが出ないという好成績 に。 これ以上インスタンスタイプを落とし続けても意味がないため、次に (1) インスタンスタイプを 1/4 の状態で固定し、 (2) のアクセス数を増やし限界値を見極めます。 こちらも結果として、 5倍以上のアクセス数にしてもDBのエラーが出ません でした。なんと、ボトルネックがRedisやApacheなど他に移ってしまい、限界値が計測できなかったくらい効果がありました。 接続切断を繰り返すというオーバーヘッドがそれほど大きかったのだろうという結論にいたりました。 PHPと相性がとても良かったという結果だと思います。 カスタムエンドポイント問題 RDS Proxyを導入すると、 DBへの接続はRDS Proxyがcluster単位でコントロール をするようになります。 BASEでは、DBレプリケーションをclusterに依存し、Writer/Reader構造を利用して運用をしていました。 少し特殊な部分として、調査や分析などでサービスへの影響を与えないよう、Readerのうちの1つを分析用として利用し、他のDBをカスタムエンドポイントによりDB接続をコントロールしていました。(下記イメージのAS-ISの部分) RDS Proxyを導入すると、すべてのDBがコントロール対象となってしまい、サービスに影響を与えず分析できるDBが確保できないという状態になるため少し問題です。 ここで私たちが取った解決策としては、 Binlogによるレプリケーションした別clusterを自分たちで構築 しました。(下記イメージのTO-BE) Binlogによるレプリケーション構築 余談ですが、実は我々がRDS Proxyの導入をする上で一番手を焼いたのが、この別clusterをつくるという部分でした。 スナップショットをとり、そのピンポイントのタイミングのポジションをとり、別clusterでBinlogのレプリケーションを開始する。この操作がダウンタイムなしでは難しく、メンテナンスコントロールなど必要でした。 ただこれは、BASEの独特な環境の問題なのでRDS Proxy導入で必須ではありません。 MySQLユーザのアクセスコントロール RDS Proxyを導入する場合、 RDS Proxyに対してMySQLへのアクセス情報(ユーザ/パスワード)をアタッチしておく必要があり ます。 これは、すべてのアクセスをRDS Proxy経由にした場合、MySQLに登録してあるユーザをすべてアタッチしておく必要があるということです。 ユーザを増やした場合、都度追加する必要があります。権限管理のためにユーザも増やすこともよくありますし、管理的にも煩雑でよくありません。 そこで私たちは小さくルールづけをして、コントロールすることにしました。以下のようなものです。 - SREがコントロールするユーザに関しては、RDS Proxyを経由してアクセスをする - 基本的にはサービスからのアクセス - それ以外のユーザに関してはRDS Proxyを経由せずアクセスする 前者のユーザを増やした場合のみ、アタッチが必要となります。たとえアタッチが漏れたとしても動作検証時に発見できます。 一般ユーザ追加などの頻度の高い操作ではアタッチが不要です。DB操作開発者が増えてユーザ作成したが、DBにアクセスできない、なんでだぁ、といった混乱が防げます。 いざリリース 接続エンドポイントの切り替え BASEでは、アプリケーションからのRDSへの接続は clusterのエンドポイント を利用せず、Route53経由することで行なっています。 リリースはこれを RDS Proxyのエンドポイント に Route53上で切り替えることで実施 しました。 実行はアクセスの少ない深夜帯に、ダウンタイムなし(ユーザサイドから見たメンテナンス状態にせず)で切り替え完了。 切り替え後、モニタリングでも逆に心配になるくらい何も問題なく、レイテンシーが少し発生しているなーくらいで完了しました。 リバート 実際はしなかったのですが、リバートについてもRoute53上でエンドポイントを戻すだけというライトなものでしたので、今回は思い切った実施ができました。 心の安心は大切ですね。 (余談)切り替え後、接続が増え続け不安に 切り替え直後、DBへの接続が24時間のあいだ一方的に増え続けモニタリングしていて不安になりましたが、原因は以下の通りでした。 MaxIdleConnectionsPercent より抜粋 RDS プロキシは、データベース接続が使用されなくなった 24 時間後にその接続を閉じます。プロキシは、アイドル状態の最大接続数の設定値に関係なく、このアクションを実行します。 以下は、 CloudWatch の DatabaseConnections メトリクスなのですが、切り替え直後は24時間で接続がガクンと落ち、その後すこしづつ平準化されていく動きになっていることがわかると思います。 RDS Proxy切り替え後のDatabaseConnectionsメトリクス 導入後の測定・効果 ConnectionAttemptsが無風に これまでの障害で接続サージが発生していた時は、 新規接続を試す数である ConnectionAttempts というメトリクスが異常な値を示して いました。 RDS Proxyを導入する前までは、それなりの数値を示していたのですが、 導入後は接続プールでコントロールされるため1桁 になりました。 これも1つの安定接続を得たという証拠となります。 (グラフではゼロに見えますが実際は少しあります。それだけ減ったということです。) RDS Proxy切り替え時のConnectionAttemptsメトリクス 接続が安定 前述しましたが、 DatabaseConnections が非常に安定 しました(下図)。 導入前に比べて若干増加してるのは、RDS Proxyがアイドル状態になったコネクションを開いたまま保持するためと考えられます。 アイドル状態のコネクション保持最大値については、 MaxIdleConnectionsPercent という値で設定できますので環境に合わせて調整すると良いと思います。 RDS Proxy切り替え後のDatabaseConnectionsメトリクス 下図は、最大接続数のグラフなのですが、多くても 1,200 程度にしかなっていません。 RDSに設定してある max_connections が 12,000 なので、現状は かなりの余裕がある結果 となっています。 RDS Proxy切り替え後の現在の最大接続数 Readerインスタンスが減らせた 上記の接続安定の結果を受け、接続に余裕をもてると分かったことで、 Readerインスタンスを1つ削除 できました。 削除した後も CPU使用量が少し増え 、 各DBのネットワークスループット量が増えた くらいで、想定通りの結果となりました。 メモリ使用量は増えませんでした。 下図は削除前後の CPU使用量 (CPUUtilizationメトリクス) なのですが、削除後は 30-35% で遷移しており、これまで接続安定化のために無駄にサイズを上げてましたが、むしろ最適化できたのではないかと考えています。 Readerインスタンス削除時のCPUUtilizationメトリクス 削除して以降、問題は発生しておらず、 費用削減に貢献 しました。 Writerインスタンスのサイズを下げることができた さらに接続安定の結果を受け、 Writerインスタンスを半分にする ことができました。 サイズを下げた後もReaderと同様で CPU使用量が少し増え たくらいで、問題は発生しておらず、 費用削減に貢献 しました。 ここで特筆すべきは、Writerインスタンスのサイズを下げたときの フェールオーバーの動き です。 サイズの変更は、まずcluster内に「切り替え先となるDB」を事前に作成し、フェールオーバーさせることで切り替え、切り替えた後に「切り替え元のDB」を削除したのですが、ここでもRDS Proxyのおもしろい動きを見てとることができました。 下図は、フェールオーバーさせたときの RDS Proxyのメトリクス です。 切り替えた瞬間、 ClientConnections が急激に増え 、クライアントからの接続をRDS Proxyが待機させていることが見て取れます。 それと同時に DatabaseConnections が急激に増えている ことが分かります。これはおそらく待機させた接続をいっきに捌くためだと考えられます。 Writerインスタンス変更時のRDS Proxyのメトリクス そして下図が QueryResponseLatency メトリクス です。 これを見ても最大で4秒ほど待機させていることが分かります。 つまりユーザから見ると、「あれ?ちょっと遅いな」程度で済んでいるということです。 Writerインスタンス変更時のQueryResponseLatencyメトリクス もちろんエラーが全くなかった訳ではありません。Sentryにエラー 2006 MySQL server has gone away が出力されていましたが、かなり少なく通常のフェールオーバーと比べるとかなり小さな影響で済んだと言えると思います。 そして下図が Database側から見た DatabaseConnections メトリクス(1日以上経過したもの) です。 前述しましたが、フェールオーバー直後リクエストを捌くため一気に増え、またアイドル状態で24時間経過したものから解放されていくというのが見て取れるメトリクスになっています。 24時間後は接続も必要分だけとなり、安定しています。 Writerインスタンス変更時のDatabaseConnectionsメトリクス RDS Proxyの特徴の一つである「(3) フェイルオーバー発生時の時間短縮などによるアプリケーションの可用性向上」を確認することができました。 レイテンシーが発生する AWS公式 では応答時間に 平均5ミリ秒/クエリ のネットワークレイテンシーが発生すると記載があります。 BASEでも例外なく、導入したことで APIのレスポンスタイムが 0.05〜0.1秒 程度増加 しました。 BASEではRDS Proxy により多段になったことによる、仕方のないコストとして、将来のアクセス安定性を取り許容することにしました。 RDS Proxy導入後のレイテンシー モニタリングの統合について BASEでは監視サービスとして Mackerel や New Relic を利用し、モニタリングしているのですが、RDS Proxyのメトリクスをそれらに 統合できませんでした。 ( 統合することはできます(下に追記あり) ) RDS Proxyは、まだまだ利用者が多いと言えず監視サービス側で適用されていないのだと思います。 今後、RDS Proxyの利用者が増えてくれば対応されてくる可能性もあるかもしれません。 以下が、各サービスのメトリクスになりますので、状況はこちらで確認ください。 Mackerel - AWSインテグレーション - RDS NewRelic - Amazon RDS monitoring integration 2023/03/10 追記 上記ページのメトリクスは旧来からのAPI Pollingの対応表であり、新規の対応をしていないためだそうです。 CloudWatch Metric Streams での対応をすることで、統合することが可能です。 (New Relic の担当者様からご連絡いただきました。ありがたい。) 導入後に発生した障害 ここは検証で発見できなかったBASEで発生した障害(失敗談)になります。 つまり環境に依存した内容も含まれますが、参考になればということで書いておきます。 長時間接続問題 BASEサービスには常時動作しているWorkerがあります。 こちらもPHPで実装されており、基本は プロセスが生きている間はDBへ接続されっぱなしになっているのですが、これが影響しエラーが発生 しました。 RDS Proxy の導入前は、 MySQLの設定値 wait_timeout = 28800 (8時間・デフォルト値) により長時間接続ができていた のですが、RDS Proxy の導入後は、 前段のRDS Proxyサイドでタイムアウトにより切断されてしまう ことが原因でした。 RDS Proxy には、 アイドルクライアントの接続タイムアウト(IdleClientTimeout) という設定があり、これが 30分 になっていたため 、タイムアウトがこれまでよりも短時間で頻発するようになっていました。 今回はこれをMySQLの時間と同じ 8時間 に設定する ことで解決しました。 デフォルト値が 30分 ですので、DB接続を長時間維持する部分がある場合は注意が必要です。 大量のレコードが発生するものは極端にレイテンシーが発生する RDS Proxy導入後、あるページが特定ユーザで表示できない現象が発生しました。 原因調査した結果、 SQLクエリの結果が大量レコードを返す ことが分かり、RDS Proxy導入後、そのクエリだけが異常に遅くなっていることが分かりました。(数秒程度だったレスポンスが、数分かかるようになっていた) 転送レコード量が適切になるよう修正する ことでこれを解決しました。 公式に記述はないのですが、多段になったことで転送が量に応じて時間がかかるようになったのではないかと想像しています。 実際、DBクライアントなどで、大量レコードを返す同じクエリを、RDS直接続と、RDS Proxy経由接続した場合で比較すると明らかにレスポンスタイムが変わって来ます。 大量レコードを返す処理がある場合には注意ポイントだと思います。 クエリ数の調査 障害ではないですが、前述した通りクエリ数に応じてレイテンシーが発生することが分かっていたので、後追いで1リクエストのクエリ数の調査を実施しました。 調査の結果、N+1問題がちらほら発生していることが確認できたので、ひどいものに関しては即時対応を実施しました。 ここはRDS Proxy関係なくケアしていきたいところです。 RDS Proxy 自体の設定変更は実行中も導入ユーザへの影響はない 上記に記載したような、いくつかのトラブルが発生し、RDS Proxyの設定を変更したい場合がありました。 アイドルクライアントの接続タイムアウト(IdleClientTimeout) の調整や、RDS Proxyの ログ記録を有効 にしたい場合などです。 AWSサイドにも確認をしましたが、これらの変更はダウンタイムなしで切り替わるため、気軽に変更しても大丈夫のようです。 まとめ BASE が RDS Proxy を導入することで得たメリット・デメリットをまとめると以下のようになります。 メリット MySQLへの接続が超安定 ボトルネックが別に移ったと言っても過言ではないほどの安定接続を獲得できた 費用的なメリットあるかも 本来、RDS Proxyにはコストがかかるが、RDSのインスタンスを減らすことができたため、実質コストダウンになった PHPとの相性が非常によかった 毎アクセスで接続切断を繰り返すような言語やアプリケーション( PHP 、 サーバーレスアプリケーション など)とは相性が良い可能性あり デメリット (今後の課題や許容した副作用) 若干のレイテンシーが発生する BASEではRDS Proxyを挟む分、仕方のないコストと考えた プロジェクトによっては許容できない可能性もあるので注意が必要 現状は監視サービスでモニタリング統合ができない BASEではRDS Proxyを導入することで、安定した接続ができるようになり、今後増えていくであろうアクセスを迎え入れる準備もでき、非常に大きなメリットがあったと考えています。 この記事が、皆様のRDS Proxy導入検討の参考になれば嬉しいです。 以上、BASEでのRDS Proxy導入レポートでした。 最後に BASEでは、このように提供するサービスを利用してくださる購入者やショップオーナーの皆様のことを第一に考えて、様々な角度からサービスを共に発展させていく仲間を募集しております。 カジュアル面談も実施しておりますので、ぜひお気軽にお問い合わせください。 open.talentio.com
2022/4/28(木)にオンラインで開催された「 \非公式/ Go Conference 2022 Spring スポンサー企業4社 アフタートーク 」にBASEからも2名が登壇しました。 andpad.connpass.com イベントについて 今回登壇させていただいたイベントは先日開催された「 Go Conference 2022 Spring 」にスポンサーとして協賛していた4社で行われたイベントです。主催のANDPADさまお誘いありがとうございました。 gocon.jp 当日は弊社含めて次の4社(敬称略)のGopherの発表とパネルディスカッションが行われました。 株式会社Showcase Gig BASE株式会社 株式会社LayerX 株式会社ANDPAD (順番は当日の発表順) 当日のTwitterハッシュタグは #gocon2022_4sponsor でした。 BASEとGo Conference 2022 Spring 先日行われたGo Conference 2022 SpringでBASEが行なったスポンサー内容、メンバーによる登壇内容については次の記事をご参照ください。 devblog.thebase.in 登壇内容について アフタートークではBASE BANKチームの清水( @budougumi0617 )と永野( @glassmonekey )が登壇しました。 当日はYouTubeで配信され、録画は次のとおりです。 www.youtube.com ANDPADさまの登壇内容については次の記事を参照ください。 \非公式/ Go Conference 2022 Spring スポンサー企業4社 アフタートーク LT内容の解説 〜ExcelとShift-JISとの闘争編〜 - ANDPAD Tech Blog New Relicを使った Observabilityの実現方法と活用例 by @budougumi0617 BASE株式会社BASE BANKチームにて、 テックリードをしている清水( @budougumi0617 )です。 アフタートークでは私たちBASE BANKチームでNew RelicをどのようにGoアプリケーションに導入しているかを発表させていただきました。 Goはアノテーションプログラミングなどが出来ず、明示的にSaaSのSDKの呼び出しをコードの中で実装する必要があります。そのため何も考えずにNew Relicの計装コード(計測用のコード)を適用していくと、次の課題が挙げられます。 新規機能・新規エンドポイントを開発するたびにNew Relicの計装コードが必要になる ビジネスロジックのレビュー以外にNew Relicの対応が漏れていないかレビューする必要がある 発表ではこれらの課題について我々がどのようにアプローチをしているのかサンプルコードと一緒に紹介させていただきました。New Relicを使っていない方にもひとつの設計パターンとして参考にしていただけるかと思います。 Goで始めるTDD by @glassmonkey おなじくBASE株式会社BASE BANKチームにて、 Engineering Program Manager をしていると永野( @glassmonekey )です。 今回のアフタートークではGoでTDDをしつつ小さく作っていくプロセスに関して話をさせていただきました。 TDDを”どう”やるのかという話はよく聞くと思いますが、TDDを"なぜ"やるのかの一例をお伝えできたなら幸いです。 今回は Go Conference 2022 Spring で発表の題材にした姓名分割ツールの glassmonkey/seimei を題材にしました。 github.com 他の方が作ったコードながらも、試しながら作ることで少しずつ理解を重ねつつコードを書くことができました。そのおかげか2~3日ぐらいの短期間ながらも正確に実装することができました。 まさに @t_wada さんが仰ってた 質とスピードの話 を体験することができました。 今回の場合だと質を 移植元のツール(rskmoi/namedivider-python) が質を担保してくれていたというのも作業としては進めやすくはありました。 github.com また、発表後に @serima さんが言ってた点はまさにこれで、昨今のDevOpsの文脈で言われるデプロイ頻度が重要と言われる点もそうですが、開発者個人にもフィードバックループの回数は品質において重要な要素だと再認識しました。 Goはそういった意味でもコンパイル言語ながらもコンパイル時間が短く最高です。 今日の @glassmonekey さんの発表を聞いてこれを思い出したんだった! いかに「小さく早く」失敗に気付ける仕組みをつくるか〜みたいな話ですね。OGP にもなっているこの図が秀逸! https://t.co/E1nXHVYsn1 — serima | LayerX (@serima) 2022年4月28日 最後に改めて元ツールの作者である @rskmoi さんには感謝を述べたいと思います。世界で2番目にアルゴリズムへの理解が深まったと自負してるので、今後フィードバックは適宜していきたいなと思ってはいます。 昨日の #gocon で、私が個人開発してるPythonのOSSをGoに移植したLTがありました。私の実装を読んで完璧に移植して頂けたこと、Goの特性を生かしてCLIとしての性能を格段に上げて頂けたことなど嬉しいことばかりです🙌 https://t.co/buFlswv6iV — Rei Sakamoto (@rskmoi) 2022年4月24日 パネルディスカッションについて 各個人の発表後は4社合同でパネルディスカッションが行われ、当日は次のような話題が挙がりました。 Go Conference 2022 Springで気になった発表 データベースパッケージの選定事情 アフタートークの発表内容について CSVを扱う際の苦労話 普段どのように登壇内容を決めているのか 他社の方々と普段なかなかお話しすることができない「これハマるよね」という話ができたり、スライド発表とは違った知見共有の時間になったと思います。 宣伝 BASE BANKチームでは Go, Python, PHPを中心に、フロントからインフラまでを一気通貫で開発しています。 また開発だけでなく、機能をグロース・分析・サポートまで担当します。 そんな開発スタイルに興味あるぞって方は永野( @glassmonekey )にDMを送っていただくか、 下記のリンクから気軽にご連絡ください。 open.talentio.com 「転職活動はしていないけど、Goの日々の開発の困りごとってどうやって解決しているの?」のような雑談がされたい方は @budougumi0617 のMeetyでお話しましょう。 meety.net
この度は、4/9(土)~4/11(日)に開催された PHPerKaigi 2022 にメンバーが登壇したり、プラチナスポンサーおよびスポンサーブーススポンサーとして協賛しました。 今回は、アンカンファレンスの様子やアンカンファレンスで登壇したメンバーからコメントをお届けします! PHPerKaigi 2022 とは 2022/04/09(土) ~ 2022/04/11(月) の 3 日間にわたって PHPerKaigi 2022 が開催されました。今年はオンラインとオフラインのハイブリット開催になります。 BASE はこれまでにも開催されている PHPerKaigi への登壇並びにスポンサードをコミュニティ貢献活動として行って参りました。 アンカンファレンスの様子 企業ブース横のコーナーでは、アンカンファレンスと称し、イベント中に募集したセッションが繰り広げられました。また、今回はオンラインオフラインのハイブリット開催だったため、オンラインで実施されたアンカンファレンスもありました。 【現地アンカンファレンスのお知らせ】 パブリックビューイング会場には「現地アンカンファレンス」エリアがあります。 オンライン(Track C)のアンカンファレンスとは違い、現地に居る人がその場で気軽にトークを開始できます。 現地に居る方はぜひチェック&ご利用ください! #phperkaigi pic.twitter.com/BqmMgWYI2M — PHPerKaigi 2025 @3/21-3/23 (@phperkaigi) 2022年4月10日 BASEからは、以下の発表を行いました。 BASE x メルカリ スピーカー アフタートーク by kawashima オブジェクト指向UIという考え方をエンジニアがチームに導入した話 by wakano 「PHP 8 で作る JSON パーサ」 (by @shin1x1 さん)を実装してみる by kushibiki 再演:リーダブルコミットのすゝめ by 02 無限LT - オンラインわいわい by cureseven はじまりました! #phperkaigi pic.twitter.com/E2CKMbsXh9 — Mercari_Dev (@mercaridevjp) 2022年4月10日 BASE x メルカリ スピーカー アフタートーク は、メルカリの方にお誘いいただき実現しました。両社モジュラーモノリスに挑戦しており、似たような悩みを持っていそうなので対談しませんかとお声がけいただき、開催に至りました。 kawashimaと登壇してくださった安達様をはじめ、メルカリのみなさまありがとうございました。 トークの中では、 モノリスとマイクロサービスの中間の選択肢ができたのは良いことだと思う 綺麗に境界を状態にするのは困難なので、関係ある場所まで誰でも触れる状態を作っておくことが大事 モジュラーモノリス化を進めるにあたり、組織改変も一緒に進める必要があると感じている といった話がありました。 オブジェクト指向UIという考え方をエンジニアがチームに導入した話 by wakano 「PHP 8 で作る JSON パーサ」 (by @shin1x1 さん)を実装してみる by kushibiki 再演:リーダブルコミットのすゝめ by 02 無限LT - オンラインわいわい by cureseven 登壇者のコメント 若野 ( @sam8helloworld ) BASEのお金周りのサービス開発をしている @sam8helloworld です。PHPerKaigi 2022では現地アンカンファレンスにて「オブジェクト指向UIという考え方をエンジニアがチームに導入した話」という発表を行いました。 自分自身このイベントは初参加であること、一緒に参加する同僚はほぼ全員リアルでは初めましてであること、発表内容が開発の文脈からは離れたものであることなどが理由で当日は朝からかなり緊張していました(笑) また自分の取った枠の開始時間はお昼のパブリックビューイングのセッションと被っていて、時間になっても誰も人が周りにいませんでした。緊張してたのも相まって「人いないしアンカンファレンスやめようかな?」と思ったりもしてました。 ただ、PHPerKaigiは凄く温かいものでだんだんと人が集まってきて気づいたら何人もの人がスライドを映したスクリーンを見てくれていました。この光景にとても嬉しくなり気づいたら挨拶して発表を始めてました。 発表中は参加者の皆さんが相槌を打ってくれてたり、メモしてくれているのが見えるので嬉しくなり、何とかしてこの人たちに自分の体験談を役に立ててもらいたいと思いました。なので用意してた原稿には書いてなかったことも含めて沢山話してしまいました。 当初20分ちょっとで終わる内容で作ったスライドだったのですが、気づいたら40分近く話してて終わる頃には声が少し枯れちゃってたのもいい思い出です。 発表後は前日までの疲れや緊張は吹き飛び、あまり話かけられなかった会場の参加者や一緒に参加してた同僚とたくさん話すことができました。これはカンファレンスハイ、とでも言うのでしょうか?私にとって緊張する久しぶりのオフラインカンファレンスは、アンカンファレンスのトークというリハビリを通して凄く楽しいカンファレンスになっていました。 今は来年のPHPerKaigi 2023が楽しみで仕方がないです! 櫛引 ( @Panda_Program ) @Panda_Program です。PHPerKaigi 2022 では 「PHP 8 で作る JSON パーサ」 (by @shin1x1 さん)を実装してみる というタイトルで発表をしました。 普段業務では TypeScript で Vue や React を書いているフロントエンドエンジニアです。ただ、元々キャリアを PHP のサーバーサイドエンジニアから始めた(本当の最初は WordPress の Plugin 開発)ため、PHPerKaigi の存在は知っていました。 社内でアンカンファレンスの枠が空いているからどうかとお声かけ貰い、とても嬉しかったので参加することにしました。 今回はアンカンファレンスということで双方向的なやりとりができることを前提にネタ探しをする間に、以前 PHP カンファレンスで新原さん(@shinx1x)が PHP で JSON パーサを実装するという発表をされていたこと、自分が PHP を書いていた頃は 7.4 が最新だったため PHP 8 の機能を学びたいという動機でこの題材を選びました。 本番はライブコーディングですが、流石にぶっつけ本番で上手くはいかないだろうと、準備をしました。準備内容は、前日と前々日に1回ずつ通しでスライドを見ながら実装することです。 準備ではコードを書くこと、パーサの仕組みに興味があったこと、また PHP の新機能を使えること(特に match 式が魅力的)から、心理的には発表用の資料作成より楽でした(笑)。 コードは GitHub に上げています。 オンラインでのアンカンファレンスであったことや本番当日の直前、自分の発表と同じ時間帯に、現地で BASE × メルカリ の最強アンカンファレンスの開催が突発的に決まったこともあり、Zoom の視聴者数は10名いるかいないか程度でした。 ただ、その中でも参加者の方が動画で顔出しして自分の解説にうなづいてくださっていたり、テストが落ちた時のデバッグをコメントで手伝ってくださったりと、オンラインでしたが交流ができてよかったです! 今回は引っ越し直後でバタバタしていたこと、ライブコーディングをやり切ることを目標としていたのでオフラインよりは緊張しないオンラインを選びました。 しかし、発表中に自分のエンジンがかかり、発表後もすぐには熱が冷めなかったため、ライブコーディングに失敗しても、オフラインで参加するのがよかったかなと思いました。オンラインだと「カンファレンスの廊下」(立ち話)もできないですしね。 また参加する機会があれば、次はオフライン参加して見たいと思います! 当日のライブコーディングの様子。現地メンバーが BASE のブースでも流してくれていました 最後に PHPerKaigiアンカンファレンスは、オフラインならではの交流がたくさん生まれました。カンファレンスと違い、ラフな空気で質問もたくさん飛び交っている様子でした。 そんな空気感の中で、発表を通じてたくさんの人と交流できたことを嬉しく思っています! これからもBASEはエンジニアコミュニティとのつながりを大事にしていきます。 私たちはサービスを共に発展させていく仲間を募集しております。 カジュアル面談も実施しておりますので、ぜひお気軽にお問い合わせください。 https://open.talentio.com/r/1/c/binc/homes/4380
スポンサーブースでの集合写真 この度、4/9(土)~4/11(日)に開催された PHPerKaigi 2022 にプラチナスポンサーおよびスポンサーブーススポンサーとして協賛しました。 今回は、スポンサーブースの様子やPHPerトークンの答え合わせなどをお届けします! PHPerKaigi 2022 とは 2022/04/09(土) ~ 2022/04/11(月) の 3 日間にわたって PHPerKaigi 2022 が開催されました。今年はオンラインとオフラインのハイブリット開催になります。 BASE はこれまでにも開催されている PHPerKaigi への登壇並びにスポンサードをコミュニティ貢献活動として行って参りました。今回はプラチナスポンサーおよびスポンサーブーススポンサーとして当カンファレンスに協賛しています。 プラチナスポンサーの一覧(2段目1番左にBASEアイコン) スポンサーブーススポンサーの一覧(1段目1番右にBASEアイコン) スポンサーブース出展 elePHPantぬいぐるみがたくさん並んだブースでみなさんをお出迎え パブリックビューイング会場の練馬区立区民・産業プラザ Coconeriホールでは、オフラインで参加されている皆さまと交流する場として、ブースを出展しました。 オフィスにたくさんいたelePHPantたちを総動員して、ブースを彩ってもらいました。 BASEからは合計6人のメンバーが登壇し、メインセッションやLT、オフライン・オンラインアンカンファレンスなどの様々な場所で発表を行っていたのですが、登壇後、トークの内容について質問や相談などのコミュニケーションを行う場としてたくさんの方にお立ち寄りいただき、大変賑わっておりました。 お立ち寄りいただいた皆さまありがとうございました! 実際に出展したスポンサーブースの写真 ブースコンテンツのBASE Appsクイズ また、ブースでは遊びに来てくださった皆さまによりBASEのことを知っていただくために、「BASE Appsクイズ」というものをご用意して遊んでいただいていました! ネットショップ作成サービス「BASE」には、どんな規模のショップでもショップ運営に必須な「基本機能」の他に 「BASE Apps」というプラグイン方式でショップ運営に必要な機能を必要になったタイミングで追加できる仕組み があり、今回はその「BASE Apps」のアイコンから機能の内容と名前を推測して答えていただくというクイズを作成しました。 BASE Appsクイズトップページ ショップオーナーさんとの出会い たくさんの方がブースに立ち寄っていただいたのですが、その中でも実際にBASEをご利用いただいている方々に出会い、サービスについての生の声をいただけて非常にありがたい機会にもなりました。 お隣の企業ブースにて参加されていた方が手芸作品などの販売に使っていただいていたり、ご家族が手作りのアクセサリーの販売にご利用くださっている方がいらっしゃったり、はたまた「こういう使い方ってできるかな?」と相談しに来てくださる方もいらっしゃり、BASEというサービスが色々な方に利用いただいていることを改めて気付かされて「これからも良いサービス作っていくぞ!!💪 」とモチベーションが高まるきっかけをいただきました。 また、お隣の企業ブースにいらっしゃった方はライブエンブロイダリングでPHPerKaigi 2022ロゴを刺繍し、ミニクッションを作られていて、「こうやってBASEショップで販売されている商品はつくられているのか…!」と、思わずまじまじと見てしまいました(笑) くるりとぬい終わったら、ひっくり返して綿を詰めて詰めて詰めて... #phperkaigi ロゴのミニクッションが出来ました! pic.twitter.com/UX6WTXfX7p — 枯白菜ちゃん/まりゐ (@marii_mikuriya) 2022年4月11日 PHPerトークンの解説 PHPerチャレンジ 今回のイベントではオンライン、オフラインどちらの参加者もゲーム感覚で楽しめる#から始まるトークンを探しポイントを競うPHPerチャレンジも併せて3日間のイベントの間みんなでランキングを競っていました! 全150トークンをパンフレットや記事に散らばっているのでみんなで色々と探して楽しんでおりました〜!隠れているトークンを探すのは楽しいですね! ランキングの結果は こちら !!! BASEからのトークンをお見せします! 「#今BASEに入社してやることあるの?」 パンフレットのBASEのコーナーのタイトルにありました! パンフレットのBASEのコーナー 「#BASEから4人登壇」 BASE Bookの記事の中に入っていました! BASE Bookの記事の写真 basebook.binc.jp 「#登壇応援中」 Youtube(People & Culture in BASE)の概要欄、ハッシュタグの中にありました! Youtubeの概要欄の写真 【前編】登壇するエンジニアを増やしたい。登壇支援活動をするエンジニアに話を聞きました - YouTube 最後に オフラインでのスポンサーブースの参加は久々なこともありオフラインならではのコミュニケーションも久しぶりにできたのかなと感じました。 BASEをもっと身直に感じていただけることができオーナーさんもとも実際にお話しができたりこれからショップを使ってみたい方のご相談に乗ることができました。 たくさんの参加者の方々や発表にふれることができ、とても充実した時間を過ごさせていただきました。 それも実行委員長である長谷川さんをはじめ、実行委員会の皆様のおかげです。心より感謝申し上げます。 来年もまた皆様にお会いできることを楽しみにしております。
はじめに こんにちは。Product Dev Division でエンジニアリングマネージャーをしている @tac_tanden です。 Docker Desktop 有料化の移行期間が終わって約 3 ヶ月が経ちましたが、皆さまいかがお過ごしでしょうか? 旬の時期は過ぎている気もしますが、BASE 社内で行った Docker Desktop の有料化移行をする中で得た知識や知見を改めてまとめたので、テックブログで公開させていただくことになりました。 有料アカウント購入の運用フローや社内の予算案の作成を担当した当時の自分が、最初から知っていればあんなに苦労しなかったのに!というのを主にまとめています。 なので、想定される読者の方としては以下の通りです。 Docker Desktop の有料プランを購入予定だが、どういう基準で選んでよいのかわからない 近い将来、有料プランの購入条件に該当しそうなので、予め準備をしておきたい 新入社員の方の Docker Desktop の有料プランへの追加運用フローをどうやっているのか気になる それでは、よろしくお願いします! Docker Desktopの利用が有料になる基準はどこから? Docker のプランごとの価格表が掲載されているページの一番下に以下のように書かれています。 Docker Desktop is free to use, as part of the Docker Personal subscription, for individuals, non-commercial open source developers, students and educators, and small businesses of less than 250 employees AND less than $10 million in revenue. Pricing & Subscriptions 特にスモールビジネスの部分ですが "small businesses of less than 250 employees AND less than $10 million in revenue"とあり、従業員数が 250 人未満かつ年間売上 1000 万ドル未満の企業のみ無料で利用可能と書かれています。 例えば BASE 株式会社は従業員数こそ 250 名以下ですが、年間売上高 1000 万ドル以下には該当せず、無料利用の対象外ということがわかります。 無料対象の企業 従業員数 250 人未満 年間売上 1000 万ドル未満 有料プランは全部で3種類 支払い方法により値段が変わってきます。 年払いをする場合は以下の値段になります。 また、月払いをする場合は以下の値段になります。 Pro と Team だと 1 ヶ月あたりの金額が 2 ドル違います(年間 24 ドル)。さらに、Business だと 1 ヶ月あたり 21 ドル(年間 252 ドル)となり一気に値段が上がります。GitHub Enterprise プランが年間 252 ドルと同じ金額なので、それを意識した値段設定になっているのかもしれません。"Buy Now"の上に目立つように"Contact Sales"のボタンがあるので、問い合わせをしてボリュームディスカウントやサポートなどで値段を調整する前提になっているようにも見えますね。 Team プランでは、年払いの場合は "Start with minimum 5 users for $25."とあり、最低 5 ユーザ分購入が必要ですが、最初の 5 ユーザまでは合わせて月額 25 ドルで利用できることがわかります。 月額 25 ドルなので年間 300 ドルとなり、通常だと 5 ユーザで月額 35 ドルなので年間 120 ドル安く購入できる計算です。 また、月払いの場合の同様の割引があり、最初の 5 ユーザはまとめて月々35 ドルで利用が可能です。 購入数 月払い 年払い 最初の 5 ユーザ合計 35 ドル/月 25 ドル/月 6 ユーザ目以降(1 ユーザあたり) 9 ドル/月 7 ドル/月 ※プランの価格は 2022/04/28 現在の価格になります。最新の情報は Pricing & Subscriptions を御覧ください。 プランごとの機能の差 BASE 社内でプランを検討したときに比較したポイントをいくつかピックアップします。 Docker Desktopの利用 BASE での一番の目的が Docker Desktop の利用でした(代替方法がいくつかありますが、それらとの比較は割愛します)。 すべてのプランに含まれているので、プラン間の比較で特に考慮すべき点はありませんでした。 有料アカウント管理 Pro プランの説明には "Includes pro tools for individual developers who want to accelerate their productivity." とあり、基本的には個人向けのプランで、購入も個人毎になってしまいます。対して、Team, Business は組織向け(会社など)のプランで、購入も「Organization」という単位で行われます。Docker Hub の Organization 内にユーザを招待して、有料アカウント枠(シート)に追加できます(GitHub の Organization とほぼ同じ役割ですね)。 会社の開発組織として購入する場合は、一括で管理できたほうが良いと思うので、基本的に Team プラン以上が選択肢になりそうです。 Audit Log Team 以上のプランで、Organization 内の変更ログを確認できるようになります。 Purchase via invoice 請求書による購入は Business プランのみ対応しているようです。会社として必須なケースがあるかもしれません。 Single Sign-On (SSO) BASE 社内で検討する際に、一番重要なポイントだったのが SSO でした。SSO は Business プランしか対応していません。SSO は会社として必須になっている場合もあるのではないでしょうか。 弊社の場合、社内のアカウントの管理も担当しているチームと確認し、導入するサービスが SSO に対応していることは現時点で必須にしていないというガイドラインのもと、SSO を理由に Business プランを選択しませんでした。SSO 以外にも細かい部分も含めて議論した結果、まずは Team プランを導入することに決まりました。 ※プラン内容は 2022/04/28 現在のものになります。最新の情報は Pricing & Subscriptions を御覧ください。 有料プランの予算を立てる上で考慮したいポイント ここでは「Team」プランを導入するときに、年間の予算を立てる上で調査してわかったポイントをまとめていきます。 年払い/月払い まず、年払い or 月払いで 1 ユーザあたり年間 24 ドル差があります。そして、最初の 5 ユーザまでは合算で 25 ドル(月払いの場合は 35 ドル)で利用できるので、計算する場合は少しややこしくなります(年間 120 ドルの差ですが)。 自分は、「最初 5 ユーザまで」のところを読み違い、5 ユーザ毎にディスカウントされると思い予算の計算をしてしまいましたが、120 ドルのディスカウントになるのは最初の 5 ユーザのみで、以降のユーザに対してはディスカウントはないのでご注意ください。 また、 Add seats to your subscription のページを確認したところ、年払いと月払いのユーザを混在させることはできませんでした。 When you add a monthly or annual subscription, it includes seats for that subscription only. 有料アカウントを増減するときにかかる料金 次に有料アカウント(シート)数の増減の部分を考えていきます。 こちらも Add seats to your subscription を確認すると、追加購入は次回の支払い期日までの日割り按分で支払うことが書かれています。 When you add seats to your subscription in the middle of your billing cycle, you are charged a prorated amount for the additional seats. そして、チームの有料アカウントは「シート(席)」という概念で、有料席が空いたら別のユーザを紐付けられる仕組みになっています。なので、もし退職者が出てしまった場合でも、使わなくなった有料アカウントを別のユーザに付け替えることが可能です。 逆に解約時ですが、 Remove seats from your subscription には、解約は次回の支払い時にその分を減額すると記載されていました。 You can manage your Docker Hub subscription anytime by removing seats from your subscription. If you remove seats in the middle of the billing cycle, changes are applied immediately and reflect in the next billing cycle. 上記の要素に加えて、会社の採用状況と今後の人員計画、過去の退職者/利用者の実績から 1 年間に支払う料金を比較して、年払いか月払いを検討するとよさそうです。 有料アカウントに新入社員の方を追加する運用方法 最後に BASE での、有料アカウントの管理方法を共有させていただこうと思います。とは言っても特別なことはしておらず、Google form とスプレッドシートを使ってアカウントの追加や付け替えの管理を行っています。 新しく入社された方に、オンボーディング期間に Google form から利用中の Docker ID を投稿していただくようにようにしています。Google form では回答がそのままスプレッドシートに反映されるように設定できますし、form の管理者は新しい回答がされた際にメールで通知が飛ぶようにすることもできるので、見逃してしまう心配も少ないです。 新しく入社された方がオンボーディング期間に忘れずに申請しなければいけない、という点はまだ改善の余地はありますが、現状は以上のような運用になっています。 ご参考になれば幸いです。 おわりに Docker Desktop 有料化対応を担当していた当時の自分が、最初から知っていれば苦労しなかったのになあ、というポイントを主にまとめてみました。少しでも参考になれば幸いです。 最後まで読んでいただき、ありがとうございました。 BASE ではユーザ第一にサービスを共に作っていく仲間を募集しております。 カジュアル面談も実施しておりますので、ぜひお気軽にお問い合わせください! https://open.talentio.com/r/1/c/binc/homes/4380
こんにちは。BASE株式会社 BASE BANKチームの前川、清水( @budougumi0617 )です。 3月上旬にNew RelicからCircleCI integrationが公開されました。 BASE BANKチームで使用してみたので、設定方法などをまとめてお送りいたします。 TL;DR 3月上旬にNew RelicのCircleCI Integrationが公開され、New Relic上でjobやワークフローの実行時間、ジョブヘルス、実行数などが取得できるようになった New Relic OneとCircleCIを連携し、ダッシュボードを作成する方法をご紹介する jobの状況だけでなく、workflowの状況もダッシュボードに表示する方法をご紹介する 背景 2022年3月に New Relic Instant Observability(New Relic I/O) の新たな連携先パートナーとしてCircleCI, Netlify, Jiraなどが発表されました。 docs.newrelic.com BASE BANKチームでは、自分たちの開発組織としてのパフォーマンスがどれくらいなのかを判断するために、ソフトウェア開発チームのパフォーマンスを示す 4 つの指標のうち手始めにデプロイ頻度を計測しています。 devblog.thebase.in これまではDeployment Markerを用いてNew Relic One上でデプロイ頻度を計測していましたが、この度CircleCI Integrationが登場したので、これを使ってデプロイ頻度などの情報をダッシュボードに表示してみました。 CircleCI Integration このIntegrationではCircleCI Webhookから取得できる情報をNew Relic Oneに連携することができます。 newrelic.com circleci.com CI上で実行するジョブやワークフロー、パイプラインのIDや名前、ステータス、実行開始時間、終了時間だけでなく、CircleCIと連携しているVCS(gitなど)のコミット時間や内容、authorなども取得することができます。 そのため、例えば 単位時間あたりのトータル実行ジョブ数、ワークフロー数 ジョブヘルス 直近失敗したジョブ プロジェクトあたりのジョブ実行数 コミットからリリースまでの所要時間 など、取得できるパラメータの使い方によって様々なデータを計測、表示することができます。 設定 ダッシュボードを作成する サンプルのダッシュボードはNew Relic I/OのCircleCIページの「Install quickstart」から簡単に作成できます。 New Relic上の Install quickstart ボタン Quick Installを完了すると、2022/04/06時点では次のようなダッシュボードが作成されます。 New Relic上に作成されたCircleCIのダッシュボード このクイックインストールではダッシュボードは作成されますがデータ連携の設定は行われません。 作成されたダッシュボードにCircleCIからCI/CDのデータを送信するにはCircleCI上での設定が必要になります。 CircleCIからNew Relicへデータを送信する Quick Installの手順には記載されていません 1 が、CircleCI上からNew Relicに送信するにはAPIキーを利用して後述のNew RelicのエンドポイントのURLをCircleCIのWebhookに登録する必要になります。 New RelicのAPI Keyを取得する 今回の連携で利用するNew RelicのAPIはLog APIですので、API Keyはライセンスキーになります。 docs.newrelic.com ライセンスキーは次のドキュメントに記載されている手順で確認できます。ライセンスキーによって連携するアカウントが区別されるので、アカウントを間違えないようにしてください。 docs.newrelic.com ライセンスキーが表示されない場合は権限不足なので組織のNew Relic管理者に問い合わせてください。 CircleCI上でWebhookを登録する CircleCIからNew Relicにワークフローの実行結果を表示するには、New Relic上でデータを表示したいプロジェクトごとにNew RelicのWebhookを登録する必要があります。 circleci.com まず、New Relicでパイプライン情報を確認したいプロジェクトの「Project Settings」を開きます。 GitHubリポジトリと連携したCircleCIプロジェクトの場合、「Project Settings」は以下のURLです. https://app.circleci.com/settings/project/github/${ORG_NAME}/${REPO_NAME} 「Project Settings」の中にある「Webhooks」をクリックすると「Add Webhook」ボタンがあるのでそれをクリックします。 CircleCIのWebhook設定画面 新しいWebhookを追加する画面になるので確認したAPI Keyを使って次のように設定します。 設定名 内容 Webhook name 任意の判別しやすい名前 Receiver URL https://log-api.newrelic.com/log/v1?Api-Key=${NEW_RELIC_API_KEY} Secret token 空欄のまま Certificate verification チェックを入れる Workflow Completed チェックを入れる Job Completed チェックを入れる これでCircleCIが実行されるたびにダッシュボードに情報が流れるようになりました。 複数のプロジェクトのパイプラインの実行結果をNew Relicで利用したい場合はこの操作をプロジェクトごとに繰り返します。 より価値のある情報を表示する NewRelic上でCircleCIのワークフローの実行情報を取得できるようになりました。 しかし、クイックインストールで作成したダッシュボードのサンプルグラフは我々の運用方法ではあまり意味のないものでした。 なぜかというと、複数プロジェクトのワークフローの実行結果を一つのグラフにすると、ジョブの数ではデプロイの実体がわからなくなるためです。我々のプロジェクトはプロジェクトごとにデプロイパイプラインが異なり、デプロイするまでに実行されるジョブの数が異なります。あるプロジェクトAは1回のデプロイでジョブを10個実行し、あるプロジェクトBが1回のデプロイでジョブを5個しか実行しない場合、ジョブの実行総数を可視化しても意味のある情報にはなりません。 ジョブの実行総数が100だった場合、すべてプロジェクトA起因ならば10回デプロイしたことが想定されますが、すべてプロジェクトB起因だった場合は20回デプロイしていたことになります。 そこで、ワークフロー単位のグラフを改めてつくることにしました。 CircleCI からどんな情報が取得できるのか? CircleCi からは現在以下の 2 種類のイベントを取得できます。 workflow-completed job-completed Webhook - CircleCI New Relic 上では type というパラメータで表されており、以下のクエリで 2 種類のイベントが取得できていることを確認できます。 FROM Log SELECT uniques( type ) since 30 days ago NRQLリファレンス | New Relic Documentation それぞれのイベントで取得できる情報を、 New Relic 上ではそれぞれ以下のクエリで確認することができます。 FROM Log SELECT keyset() WHERE type = ' workflow-completed ' since 30 days ago FROM Log SELECT keyset() WHERE type = ' job-completed ' since 30 days ago NRQLリファレンス | New Relic Documentation 情報の一部を確認すると、workflow-completed イベントではワークフローに関係する情報を取得でき、job-completed イベントではジョブに関係する情報と関連するワークフローの情報の一部が取得できていることがわかります。 また、ワークフローのステータスに関しては workflow-completed でのみ取得できるということが確認できます。 ワークフロー単位での情報の表示について ワークフローに関する情報は workflow-completed と job-completed 両方のイベントに含まれています。 また、New Relic 上の実際のデータを確認すると、workflow-completed イベントに関しては殆どが workflow.id でユニークでしたが一部重複がありました。 よって今回ワークフロー単位での情報の表示については、以下の方針に基づいて可視化を行いました。 uniqueCount 関数を利用してワークフローに関する属性のユニーク値の数を集計する workflow-completed イベントの情報を集計する NRQLリファレンス | New Relic Documentation まずはワークフローの数を表示してみましょう。 FROM Log SELECT uniqueCount(workflow.id) WHERE type = ' workflow-completed ' since 30 days ago ワークフロー数 同様に、日毎のワークフロー数の推移をプロジェクト別に可視化するクエリは以下のようになります。 FROM Log SELECT uniqueCount(workflow.id) WHERE type = ' workflow-completed ' FACET project.name TIMESERIES 1 days since 30 days ago 日毎のワークフロー数 次に、ワークフローのステータスについて可視化してみましょう。 workflow-completed イベントについて、 workflow.status の値でグループ化しカウントして表示するクエリは以下のようになります。 FROM Log SELECT uniqueCount(workflow.id) WHERE type = ' workflow-completed ' FACET workflow.status since 30 days ago ワークフローステータスの集計 最後に、今週のデプロイ総数を先週と比較してみましょう。 今回は成功ステータスである特定のワークフロー名のワークフローの数をカウントすることでデプロイの数を集計してみます。 FROM Log SELECT uniqueCount(workflow.id) WHERE workflow.name = ' test-build-deploy ' AND workflow.status = ' success ' SINCE this week COMPARE WITH 1 week ago 今週のデプロイ数と先週との比較 同様に、日毎のデプロイ数の推移をプロジェクト別に可視化するクエリは以下のようになります。 FROM Log SELECT uniqueCount(workflow.id) WHERE workflow.name = ' test-build-deploy ' AND workflow.status = ' success ' TIMESERIES 1 day FACET project.name SINCE 30 days ago プロジェクトごとのデプロイ数の推移 おわりに 今回、新しく登場したNew Relic OneのCircleCI Integrationの導入方法についてご紹介しました。 設定さえすれば最低限の情報はすぐに出力できるようになるため、読者の皆様が所属する開発組織のパフォーマンス計測の一助となれば幸いです。 New Relicを活用したサービス品質の向上や、開発組織のパフォーマンス改善に興味のある方はぜひカジュアルにお話しましょう! open.talentio.com どこかに書いてあるのかもしれませんが、我々の調査ではドキュメントが見つからずテクニカルサポートに問い合わせました。 ↩
2022/4/23(土)にオンラインで開催されるGo Conference 2022 Spring Onlineにシルバースポンサーとして協賛し、2名のメンバーが登壇します。 Go Conferenceとは https://gocon.jp/2022spring/ Go Conference 2022 Spring Online Go Conferenceは一般社団法人Gophers Japanが主催し半年に1回行われるプログラミング言語Goに関するカンファレンスです。 前回 に引き続き、オンライン開催です。 今回、弊社は前回に引き続きシルバースポンサーとして協賛します。 ※ The Go gopher was designed by Renee French . Illustrations by tottie . BASEとGo プロダクトの大半のサーバーサイドがPHPで実装されているBASEですが、BASE BANKチームが開発・運用している資金調達サービスである「YELL BANK」、ショップの売上金をVisa加盟店の決済で利用できる「BASEカード」はGoの分散サービスとして実装されています。 thebase.in https://cp.thebase.in/basecard cp.thebase.in 「YELL BANK」のリリースは2018年12月であり、Goを使ったプロダクト運用実績は3年以上になります。 登壇内容について testingパッケージを使ったWebアプリケーションテスト(単体テストからE2Eテストまで) by @budougumi0617 gocon.jp BASE株式会社BASE BANKチームにて、 テックリードをしている清水( @budougumi0617 )です。「testingパッケージを使ったWebアプリケーションテスト(単体テストからE2Eテストまで)」というタイトルでプロポーザルを提出し採択されました。 ソフトウェアとテストは切っても切れない関係です。 いっぽう、Webアプリケーション開発においてはDBなどのミドルウェア・外部API・永続化情報の状態など様々な依存関係が存在します。 本セッションではDBや外部APIに依存するコードの単体テストからテスト中にWebサーバやDBを起動するシミュレーションテストまで、 私がtestingパッケージを使って行なっている様々なレベルのテストについて紹介します。 前職の経験も合わせると業務でGoのWebアプリケーション開発に携わり始めて5年になります。 その中で行なった試行錯誤して書いたテストのアプローチを紹介します。 プロダクトによってテストで担保したい品質、仕様は異なります。私の発表を視聴していただいた方のテストに対する「手札」がひとつでも増えれば幸いです。 Python製の姓名分割ライブラリをGoに移植した話 by @glassmonkey gocon.jp 一般的にわかち書きでは無い日本語で姓名から「姓+名」の分割を行うことは困難です。 しかし、Python製の姓名分割ライブラリ( https://github.com/rskmoi/namedivider-python )を用いるとある程度精度良く分割は可能です。 そこでシングルバイナリで扱えるGoのメリットを活かして、Python製の姓名分割ライブラリをGoに移植した話をします。 その際移植で工夫した点や気をつけた点をお話します。 今回、他の方が書いたコードを他言語に移植することを初めてチャレンジしました。 今回のケースではLL言語故のruntime時の不安定さをGoに移植することで解消することができました。 ただ、一部numpyの実装を独自に行う必要があったなど、メリットばかりでは無いことも学びとしてありました。 別言語の実装をGoやさらに他言語へ移植する際の意思決定の参考になれば嬉しく思います。 オフィス・アワーについて 今回は我々BASEはシルバースポンサーとして協賛させていただくため、イベント当日はRemo上でオフィスアワーを開いて参加者のみなさんをお待ちしております。 オフィスアワー中の弊社ブースでは弊社が定期開催しているGophers Code Reading Partyを開催予定です。 同Partyはコードリーディングに限らず、その場で集まったメンバーが最近気になったGoのトピックを話す集まりで、社外ゲストも含めカンファレンスの廊下やオフィスの休憩スペースのような雰囲気で雑談する集まりです。 普段使っているGoのOSSのコードリーディング ちょっと挙動がわからなかった標準パッケージのコード 直近話題になったGo関連のトピック・ブログ記事について BASEでGoをどうやって使っているか etc... publicリポジトリのissueで毎回メモを取っています。 当日用はこちらのissueを使う予定なので事前にトピックをコメントしていただくのも大歓迎です。 github.com 普段の雰囲気やトピックについては過去の回のメモ(既存issue)を御覧ください。 https://github.com/basebank/gophers-code-reading-party/issues 宣伝 ANDPADさん主催のGoConスポンサー企業合同アフタートークイベントにも参加予定です。 andpad.connpass.com 株式会社Showcase Gigさん、株式会社LayerXさん、株式会社アンドパッドさんと4社合同で行ないます。 弊社からは @budougumi0617 , @glassmonekey が次の内容の発表とパネルディスカッションに参加する予定です。 New Relic Oneを使ったObservabilityの実現方法と活用例 by @budougumi0617 Goで始めるTDD by @glassmonekey 宣伝その2 BASE BANKチームでは Go, Python, PHPを中心に、フロントからインフラまでを一気通貫で開発しています。 また開発だけでなく、機能をグロース・分析・サポートまで担当します。 そんな開発スタイルに興味あるぞって方は永野( @glassmonekey )にDMを送っていただくか、 下記のリンクから気軽にご連絡ください。 open.talentio.com 「転職活動はしていないけど、Goの日々の開発の困りごとってどうやって解決しているの?」のような雑談がされたい方は @budougumi0617 のMeetyでお話しましょう。 meety.net 最後に、Go Conference 2022 Spring Onlineに参加するには次のイベントページより参加登録をお願いします。 gocon.connpass.com それでは4月23日にお会いしましょう!
メンバーが登壇している様子 この度は、4/9(土)~4/11(日)に開催された PHPerKaigi 2022 に4名のメンバーが登壇しました。 今回は、登壇者 4 名からコメントと、他のセッションの感想などをお届けします! PHPerKaigi 2022 とは 2022/04/09(土) ~ 2022/04/11(月) の 3 日間にわたって PHPerKaigi 2022 が開催されました。今年はオンラインとオフラインのハイブリット開催になります。 BASE はこれまでにも開催されている PHPerKaigi への登壇並びにスポンサードをコミュニティ貢献活動として行って参りました。 登壇者のコメント 川島 ( @nazonohito51 ) TechLeadの川島( @nazonohito51 )です。 今回はBASEがサービスとしても組織としても成長していく中で生産性を維持するためのアーキテクチャ戦略についての発表をさせていただきました。社内でこの戦略が打ち出されたのはかれこれ2年ほど前になるのですが、明確な形で社外に公表されたのは今回が初になります。 この戦略はアーキテクチャの本からチーム・組織・文化などの本から「学習する組織」といったテクノロジー系とは言えない本まであちこち読んだ末に考え出されました。実態としてはクリーンアーキテクチャやマイクロサービスなどのアーキテクチャパターンというより、進化的アーキテクチャ+DevOpsといった趣旨の内容であると言ったほうが近いと思います。中長期的な期間で考えればアーキテクチャに固定的な解は存在せず、システムを取り巻く環境の変化の中で常にバランスを取り続ける変化する動体である必要があります。そして変化の方向は、その時の目先の問題だけを反応的に局所最適で解決するのではなく、常に何かしらの目的を達成するような構造へ向かうような指向性が求められます。そして弊社における目的とは資料の前半で触れられていたようなものでした。 各所の反応を見る限り「モジュラモノリス」という単語に惹かれた方が多そうな印象ですが、趣旨としては中長期的にアーキテクチャに対してどんな姿勢で行くかの考えを整理したものがメインコンテンツになります。モジュラモノリスはその中の中心ではありますが一部に過ぎず、「モジュラモノリス」という単語そのものに「組織の生産性」という期待を寄せているならば何か見落としがあると思います。この資料で終始徹底したのは技術的な方法論からは入らない、という点で、事業と技術の整合性をどう取るのかについて一番エネルギーが使われています。 このアーキテクチャ戦略は未だに手探りの部分がほとんどの状況ですが、これから社内で少しずつ進めていく予定です。 Discordチャンネルに送られた質問について Discordに送られてきた質問はおそらく他の多くの方も持たれる疑問だと思われるのでこちらにも記述します。 モジュラモノリスの時点ではDB分割をしていない状態なのか? そうなります。理由はDB分割の境界はドメインが根拠であるべき、と考えているためです。ドメイン基準で分割したいけど境界線が分からない->DB設計の手戻りはコストが高い->アプリケーションの手戻りはDBよりも低コストなのでまずはアプリケーション(=モジュール)境界を安定させてからそれをDB設計に反映する、という戦略を立てているため、モジュラモノリス開始時点ではあえてDB分割していません。 漸進的にマイクロサービスへ向かっていくかどうかでモジュールをまたいだトランザクション境界について考え方が変わらないか? マイクロサービス化するならモジュールをまたいだトランザクションを許してはならないし(マイクロサービス化する時点でトランザクション分かれる)、マイクロサービス化しないならそういうトランザクションを許可する、という考え方にならないか、という質問でした。 結論としてはご指摘のとおりになります。マイクロサービス化する場合、CAP定理にもある通りCAPのいずれかが大きく損なわれます。スライド資料中にも赤文字で記述していますが、モノリスと分散システムにおけるデータ整合性に対する戦略は根本的に異なります。モジュラモノリス時点でやれていたことはマイクロサービス化しても全てが同じようにできるわけでは決してありません。トランザクション境界に対する明確な戦略は打ち出せてはいないのですが、少なくともモジュラモノリス時点で同一トランザクションで処理することもできれば別トランザクションに分けることもできるという選択肢を用意しています。もちろんイベントドリブンな結果整合性の処理を実現することはモジュラモノリスにおいても出来ます。モジュール境界線が明確ならはじめからトランザクションを分けたり、結果整合性の処理にしてしまうことが後のマイクロサービス化するときに有利になります。が、境界線が明確でないなら無意味に更新処理が複雑化したり、更新が反映されていない参照が発生する可能性をもたらしてしまったり、あるいは「モジュール境界線自体が後から見直しやすい」というモジュラモノリスのメリットを一部手放すことになる可能性もあります。トランザクション境界に対しては現状画一的な判断はできず、都度判断することになることになると考えています。 あと実は、発表中には触れませんが、BASEで実際にマイクロサービス化する箇所は極めて限定的になるのではないかと考えている背景もあります。少なくとも全モジュールがマイクロサービスになって動いているような未来はほとんどありえないだろうと考えています。 モジュラモノリスというパターンについて モジュラモノリスというパターン自体は「こう作れ」という明確な指示があるわけではないので、自社が「モジュール」という構造を通して何を実現したいかによってその姿は変わってくると思います。弊社はクリーンアーキテクチャをベースにしましたが、これは一例に過ぎません。「マイクロサービスアーキテクチャによって何を達成したいのか明確に把握していない場合には、マイクロサービスアーキテクチャは悪いアイディアである」という言葉はモジュラモノリスにおいてもそのまま当てはまるかと思います。 スライド資料だけを見て動画を見てない方には誤解を生みかねないのでこちらの記事でも触れさせていただきますが、モジュラモノリスは決して銀の弾丸ではありません。マイクロサービスとは別の形をした諸刃の剣です。振り方を誤ればきちんと怪我をしますのでご注意を。 永野 ( @glassmonekey ) BASE BANKチームでEngineering Program Managerをしている永野( @glassmonekey )です。 今回は個人開発や副業で扱ってるGraphQLに関して、普段業務で扱ってるPHPを通してどうなのかをトークしました。今回事前収録が個人的にも初めてで運営の皆様にはご迷惑をおかけしました。 弊社ではGraphQLに取り組んでいるわけではなかったのですが、改めて導入すべきかどうかを漠然と考えていたので、発表資料を作る過程を通して良い思考実験になりました。皆様も迷ったら登壇駆動はおすすめです。 発表後には、GraphQLの導入に迷ってたがかなり参考になったといった感想をいただく機会もありかなりの励みになりました。特にオフラインだったので直接感想を言い合えるという体験は最高でした。 これも運営の皆様の調整あってこそだったと思うので、改めてありがとうございました。 大津 ( @cocoeyes02 ) Product Dev Division / Service Dev Section に所属している02( @cocoeyes02 )こと大津です。 今回はコミットメッセージ軽量規約「Conventional Commits」の説明と関連ツールを使ってみた様子をトークしました。 PHP カンファレンス沖縄 2021でトークした リーダブルコミットのすゝめ でも少しだけ「Conventional Commits」について触れましたが、今回はガッツリ環境を用意して試すところまでやった他、 「Conventional Commits」の公式ドキュメントサイトにIssueやPRを出すところまでやってみました (とはいえ反応薄くてちょっと悲しい・・・) 今回使用した ramsey/conventional-commits ですが、導入の提案 Issue を laravel/framework や CakePHP など PHP フレームワークのリポジトリで出してみようかなと思っています! 導入そのものよりも、PHP OSS コミュニティ界隈の人がコミットメッセージについてどう思うか議論するというのが目的です。 分かりやすいコミットメッセージのメリットをもっと多くの人が享受できるよう、今回の発表以外でも動いていきたいと思います! 炭田 ( @tanden ) Product Dev Division / Service Dev Section に所属しているtanden( @tanden )です。 今回のLTでは「Webサービスのバウンスメール処理の事始め」ということで、そもそもバウンスメールとは何なのか、AWS SESをつかってバウンスメールをサービスにどのようにフィードバックするのかを簡単に発表させていただきました。 LTの発表は事前収録ではなかったので、ココネリホールの会場での発表でしたが、オンラインでの気軽さや視聴のしやすさはもちろんあるのですが、オフラインでの発表の雰囲気はやはりいいな、素敵だなと改めて思いました(その分緊張もすごいのですが)。 素敵な雰囲気の会場を作ってくださった、運営の皆さまに改めて感謝申し上げます。ありがとうございました。 個人的な心残りは、LTで笑いを全くとらない真面目な発表になってしまったことです。次回はもっとフランクなLTに挑戦してみたいと思います!(笑) 他のセッションについて 若菜 ( @ wakanaction ) Product Dev Division / Service Dev Section に所属しているwkです。 Day1とDay2の少しだけ、オンラインで視聴参加しました。 いくつかかいつまんで感想書きます 👨🍳 予防に勝る防御なし - 堅牢なコードを導く様々な設計のヒント @t_wada さん ついにt_wadaさんのセッションをリアルタイムで聞くことができ、感激...! PHPerのみならず、そして初学者から上級者まで広い範囲の方に刺さりそうな内容だった 中でも、以下を用いて堅牢な設計を考えていく運びが大変ためになった 型宣言 列挙型 モデリング 普遍性と等価性 完全性 責務の配置 メソッドに渡る値を型宣言によって絞る、さらに扱う内容が限られている値は列挙型で絞る、といった具合 度々、 プログラマが知るべき97のこと から引用されていたが、中でも以下を重点に置いていた。自分の業務でもぜひ参考にしたい考え方だった。 いいインターフェースの条件とは、正しく使用する方が 操作ミスをするより簡単 誤った使い方をすることが困難 「不安や疑念はテストに書いておく」 業務ロジックに限らず、FWの挙動、組み込み処理の動きなどもテストに残しておくことで、PHPの思わぬ落とし穴に気付けたり、一方を直したら一方が壊れた、なんてことに気づきやすくなっていて、精神衛生としてもすごく良かった。 総論:設計やコードレビューの際に常に念頭に置いておきたいような、即業務に活かしていける内容だった。型厳格な方向に進んでくれたPHPの恩恵に感謝しながら頑張ります コミットメッセージ規約「Conventional Commits」を導入してみよう! @02 さん Commitメッセージ、わかりやすく書きたい気持ちはあるものの、実際どうしたら良いかいまいちわかっていなかった 日本語で丁寧に書いてみたり、英語で統一して書いてみたり色々試した Commitメッセージに関する規約があるのは初めて知った フォーマットが決まっており、「Prefix (feat, fix, など) 、タイトル、本文、フッター、破壊的変更」のような内容でcommit メッセージを書く 多少規約が厳しめに感じたが、それくらいの方が規約を用いる意味があるか。または続けやすい形で一部取り入れるのも良いのかも。 また、コミットメッセージをわかりやすく書いていきたいと考えた時、副次的に以下のような考えにも至った。 コミットメッセージをわかりやすくしたい ↓ コミットに含む内容をわかりやすくしないと、わかりやすいメッセージは作れない ↓ 適切な範囲でコミットを切る意識が育てられる! 総論:大規模開発において、Commitメッセージが残す情報は重要であるため、試しにでも実施してみようと思った。 普段使っているSourceTreeでは複数行にわたるCommitメッセージが書きやすいため、試しやすいと思った。 shiiyan( @shiiyannn ) Product Dev Division / Service Dev Section に所属しているshiiyanです。 PHPerKaigi2022のDay1とDay2をオンラインで参加させていただきました。 印象に残ったいくつかの発表に感想を書きます。 day1 - MongoDB に溜まった約1.6億レコード、データ量1TBのあらゆるサイトの記事データを BigQuery で高速検索できるようにした話 植江田さん 大規模データ処理関して、最近業務上でも課題がありました。こちらの発表は課題解決のヒントになれるかと思い、PHPerKaigiの中に特に興味を持ちました。 MongoDBに保存されたデータについて以下のことが紹介されました。 様々なサイトから記事をクロールしクリップする クロールした記事データをMongoDBで保存 1日で10万件のレコードが保存される 1日10万レコードならば、1ヶ月で300万レコードとなり、数年経てば約1.6億ととんでもない規模になっていくことがわかりました。 データ移転中の課題について以下のことが紹介されました。 動的スキーマから静的スキーマへの移行課題 存在しないカラムがあるとエラーとなる カラムの順番が変わるとエラーになる 処理時間とサーバーストレージ容量の課題 移行処理が完了までに20時間以上が必要 移移行処理が完了までに6000以上のcsvファイルが必要 課題に対しての解決法について以下のことが紹介されました。 存在しないカラムにnullを入れる PHPの連想配列でカラムの順番を固定した ストリームコピー( stream_copy_to_stream )を利用すれば2倍高速した 動的スキーマを採用したデータストアでは、カラムが利用中で増えても、カラムの順番が変わってもエラーなく使い続けます。マイグレーションが不要でスキーマの変更やメンテナンスがしやすい一方で、静的スキーマのデータストアに移行する時に、スキーマの整備という手順が発生するという知見を得られました。 また、ファイルを開いて一行ずつコピーする以外に、ストリームコピーというやり方を今回で新しく学びました。高速化というメリットがあるので、今後PHPでファイルに書き出す処理を実装する時に活用できると思いました。 day2 - コミットメッセージ規約「Conventional Commits」を導入してみよう!02さん 今まではコミットメッセージを割と雑で書いていました。そのせいで、PRのコンフリクト対応時やgit rebase時に苦労した経験がありました。 『コミットは他人が見るものだから、他人が書き手の意図を理解できないと❌』というのが発表の内容にありました。まさに、その通りだと思いました。コミットはpushするだけのものではなく、将来の自分や他人がその意図を理解できないと意味がないと理解しました。 Conventional Commitsというコミットメッセージの軽量規約も紹介されました。 featやdocsなどのコミットメッセージのプリフィックス前から知っているものの、コミットメッセージ規約を勉強するのは今回が初めてでした。 発表に利用された サンプルリポジトリ のコミット履歴を眺めると、規約に沿って書いたコミットメッセージがあるとソースコードを見なくても、何をやったのかを想像できることが実感できました。 発表の後、choreというプリフィックスはいつ利用されるかを調べました。 chore (updating grunt tasks etc; no production code change) 私の理解では、featやdocsなど明確な目的があるプリフィックス以外、本番コードに影響しないその他的な変更があった時に使います。 また、破壊的変更(Breaking changes)があるときに必ずフッター部分で明言することも覚えました。 今後はぜひ規約に合ったコミットメッセージを書いて、コミットメッセージが役立つようにしたいと決めました。 最後に 今回計 4 名のメンバーが登壇する機会をいただき、 PHP コミュニティの盛り上がりに貢献することができ大変有意義な時間となりました。 また自身の発表以外にも、多くのスピーカーの発表を通して各々が新たな知見や気づきを持って帰れたと考えております。 業務でお忙しいにも関わらず、スタッフの方々には多くの時間をカンファレンス準備へ割いていただいたかと思います。この場を借りて心より御礼申し上げます。 今回はトーク編の記事となっております。他にもスポンサー編、アンカンファレンス編、スタッフ編の記事を投稿する予定です。 それでは、来年もまた皆様にお会いできることを楽しみにしております!
はじめに こんにちは。バックエンドエンジニアの小笠原です。 今回は、2022年2月18日から2022年3月4日にかけて発生していたこちらの障害に対し私達開発チームが実施した、session.cookieで定義しているCookieのkey名を変更するという影響範囲の大きい対応について、実施に至るまでの経緯や対応過程についてご紹介したいと思います。 ショップオーナー向けに掲載していたお知らせの内容 背景 全ては iOS14.5から端末識別子の取得に同意が必要になったことから始まった ことの発端は、iOS14.5以降からIDFA(端末ごとに持つ固有識別子)の取得に端末所有者の許可が必要になったことでした。 この変更は、端末所有者側から見ると情報の活用範囲を自身で管理できることでよりプライバシーに配慮されるようになった良い変更と言えるでしょう。 一方で、広告出稿側から見た場合は拒否をしたユーザーの広告トラッキングが出来なくなることで広告の効果測定が大幅に制限される、という問題が発生してしまいます。 この問題に対して、Facebookピクセルという広告効果測定ツールを提供しているMeta社(旧Facebook社)は、広告効果測定の仕様を変更して合算イベント測定による集計を行うことでIDFAの取得を拒否したユーザーについても広告の効果測定ができるように対策を行いました。 BASEにおいてもInstagram広告Appがこの影響を受けるので、何らかの対応を行う必要に迫られました。 合算イベント測定に対応する際の詳細については本記事の主題ではないのでここでは省略させていただきますが、結論としてeTLD+1なドメインを認証することで合算イベント測定を使用可能になるということがわかったため、当時開発チームはショップ開設時に選択することができるドメイン群をPublic Suffix List(PSL)に登録するという対応を行っていました。 Public Suffix List(PSL)とは Public Suffix List(PSL)とは、jpやcomなどのTop Level Domain(TLD)と、co.jpやmeguro.tokyo.jpのような実質的にTLDのように振る舞うことが期待されるeffective Top Level Domain (eTLD)を管理しているリストのことで、GitHub上で管理・運営されています。 このリストに対して必要な情報を添えてPull Requestを送ることで、誰でも任意のドメインの追加を申請することが可能です。 https://github.com/publicsuffix/list つまり、PSLに任意のドメインを登録することでそのドメインをeTLDとして扱うようにすることができ、これによってショップのURLがeTLD+1と認識されるため、ショップ単位でドメイン認証を行うことで合算イベント測定を使用可能になる、ということです。 この対応のため、開発チームはショップ開設時に選択することができる以下のドメイン群をPSLに登録する申請を行いました。 base.ec official.ec buyshop.jp fashionstore.jp handcrafted.jp kawaiishop.jp supersale.jp theshop.jp shopselect.net base.shop リポジトリのPull Request履歴を確認すると、登録申請をしたのは2021年9月14日で、マージされたのは2021年12月5日だということがわかります。 https://github.com/publicsuffix/list/pull/1420 PSLに登録されたドメインにはCookieを保存できない ところで、 PSLに登録したドメインにはCookieを保存することができなくなってしまいます。 仮にjpのようなTLDに対してCookieを保存可能にしてしまうと、そのドメインを使用している全てのWebサイトでそのCookieを共有できることになってしまいます。TLDは不特定多数の利用者が様々な目的でサブドメインを取得して運用していることが多く、このような広範囲に対してCookieを参照可能な状態にしてしまうことはセキュリティリスクが高いため推奨されるものではありません。 そのため、TLDにはCookieを保存できないルールになっています。そして、TLDと同様の振る舞いをするeTLDに対しても同じことが言えるため、TLDと同様にeTLDに対してもCookieを保存できません。 つまり、PSLにドメインを登録するということは、そのドメインに対してCookieを保存できなくなる、ということを意味します。 PSLへドメインを登録したことによってどのような影響が出てしまったのか BASEのショップでも例に漏れずCookieを利用しており、例えば「シークレットECショップへのログイン情報」「カートへ商品を追加する際の商品情報」などはCookieの THEBASE というkey名に保存して管理していました。そして、これらのCookieはショップ毎に割り当てられているサブドメインに対してではなく、前項で紹介したPSLに登録したドメインに対してCookieを保存する処理になっていました。 つまり、これらの情報をCookieに保存できなくなったことで「シークレットECにログインできない」「カートへ商品を入れてもカートの中が空のまま」といった不具合が発生していたことが今回の障害の裏側で発生していた事象でした。 なぜPSLにドメインを登録してから数ヶ月経過してから問題が顕在化し始めたのか PSLにドメインを登録したのは2021年12月5日ですが、この障害を開発チームが認識したのは2022年2月19日の段階でした。 なぜおよそ2ヶ月ほど経過するまでこの不具合に気がつくことができなかったのかというと、それはブラウザが最新のPSLを取り込んだタイミングが関係していたようでした。 実は、各ブラウザは常に最新のPSLを参照しているわけではなく、任意のアップデートのタイミングでその時点の最新のPSLのスナップショットをビルドに含めて参照しています。 さらに、以下の表のように必ずしもアップデート時に最新のPSLへと更新しているわけではなく、その更新周期には規則性がないこともわかりました。 ブラウザ名 PSLの更新周期 FireFox Firefox96(2022/01/12リリース)時点ではBASEの登録したドメインは含まれておらず、Firefox97(2022/02/08)には含まれていた Chrome chrome97(2022/01/04リリース)時点で 2021/10/27 のPSLを取り込んで以後、更新されていない 上記の通り、直近のFirefox97のリリースによってこの障害に遭遇する購入者が徐々に増えてきたのではないか、と推測されました。 障害への対応内容 base.shopなどのeTLDに対してCookieが保存できないという問題に対して、今回はショップのURLに該当するサブドメインに対してCookieを保存するように変更するという方法を採用しました。 これは、ショップ毎にサブドメインを割り当てているBASEの仕組みを考えると、基本的にはショップの中でsessionが保持できれば購入者の買い物体験は阻害されないであろう、という判断によるものです。 実現方法として、チームでは以下2点の選択肢が挙がりました。 Cookieのdomain属性でサブドメインを指定する Cookieのdomain属性を指定しない domain属性を指定しなかった場合は一番狭い範囲に対してCookieが保存されるため、挙動としては「サブドメインに対してCookieを発行するように変更する」という点でどちらの対応を実施しても同じ意味となります。 今回はセキュリティの入門書として有名である『体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践』にdomain属性を指定しない状態が最もCookieの送信範囲が狭く安全な状態であるという言及があったことから、後者のdomain属性を指定しないように修正する方針に決まりました。 問題点 さて、前置きが長くなってしまいましたが、ここからが本記事の本題となります。 対応方針が決まったところで検証環境で動作確認をしていると「対応後のソースコードでもシークレットECにログインできない」という障害が稀に発生することがありました。 修正前と修正後の動作確認結果の比較 この現象は eTLD+1をドメイン属性にもつ THEBASE のCookieと、eTLDをドメイン属性にもつ THEBASE のCookieが同時に送信されているケースで発生していることがわかりました。 これは、今まで不具合が発生していたショップ(Cookieが保存できていなかったショップ)では修正後のCookieのみが保存されているために不具合が解消された一方で、今まで正常にログインできていたショップ(Cookieが保存できていたショップ)で新たに不具合が発生するようになった、ということです。 そもそもなぜ同じkeyのCookieが二種類できてしまうのか THEBASE のCookieは有効期限をセッションに設定していたため、この現象に遭遇した場合はブラウザを再起動すれば古いCookieが削除されて問題を解消することができると予想されました。 ところが、実際にブラウザを閉じてショップを開きなおしても、本来であれば消えるはずの前回アクセスした際のCookieが残ったままになってしまっていることが発生していました。 実は、この問題はブラウザが「前回開いたサイトを復元する」機能を実現するために、ブラウザを閉じた後も有効期限がセッションになっているCookieを保持し続ける挙動をすることが原因で発生しているらしいことがわかりました。 さらに、同名のCookieが存在する場合のCookieの取扱もブラウザによって異なっていることが私たちを混乱させました。 ブラウザ Cookieの並び順 同名のCookieが複数ある時シークレットECにログインできるか FireFox 古いCookieが新しいCookieよりも先に並ぶ できない Chrome 古いCookieが新しいCookieよりも先に並ぶ できない Safari 新しいCookieが古いCookieよりも先に並ぶ できる このように、ブラウザによって挙動が異なっており、いつCookieが削除されるのかがブラウザ依存であるという状態であることから、Cookieのdomain指定方法を変更するだけでは障害から復旧できないことがわかってしまいました。 解決案の模索 この問題に対して、私達のチームでは2つの案について検討しました。一つ目の案はこの現象を許容したままで対応をリリースすること、そして二つ目の案はsession.cookieで定義しているCookieのkey名を変更した上で対応をリリースすること、でした。 この2つの案の比較検討と障害の影響範囲の把握のため、開発メンバーで協力してソースコード上でsession.cookieの定義を使用している全ての参照箇所を洗い出しました。 以下の表は、この2つの案に対してそれぞれ比較検討した内容を表にまとめたものです。 案1:Cookieの重複を許容する 案2:Cookieのkey名を変更する 影響範囲 FireFox97とSafari以外のブラウザを使用している購入者 全ての購入者 メリット すぐにリリースできる 完全に不具合が発生しなくなる デメリット Cookieが二重で登録されてしまった購入者には、ブラウザキャッシュを削除してもらう必要がある セッションがリセットされるので、再ログイン等が必要になる 工数 なし リグレッションテストが膨大 影響期間 ブラウザの旧Cookieが消えるまで(つまりいつ収束するか不明) デプロイのタイミングのみ どちらの案でも発生するデメリットとして、デプロイを跨いで購入者が操作した際に以下の影響が出るという問題がありました。 改善リリースデプロイ前に抽選・定期便・コミュニティ限定商品をカートへ追加して未購入状態の場合、改善リリースデプロイ後にはカート内の商品が全て消えてしまう コミュニティ会員ページへログイン済みの状態でも、再度ログインが必要になってしまう シークレットECがかかったショップページへアクセスをしている状態でも、再度PW入力が必要になってしまう デプロイをまたいで購入をしたユーザーの場合、決済が走っているもののCookieを持ち越せないために購入完了画面が表示されないことで、購入に失敗したと誤解をして重複購入してしまう 修正対応リリース前後で問題が発生するケース そして我々はsession.cookieを修正してCookieのKey名を変えた 最終的に、Cookieのkey名を変更する案2の方がより購入者に優しいだろう、ということで決まりました。 一時的な不便を全購入者に要求してしまうことにはなるのですが、購入者に要求する操作としては再ログインやカートへの再度の商品追加など、通常のWebブラウジングの操作の範囲内で対処できるものとなっています。 逆に、案1の場合はブラウザのキャッシュを削除するという通常のWebブラウジングでは行わない操作を購入者に要求してしまう上に、BASE以外のサイトのキャッシュも削除してしまうことになります。もちろん特定のサイトのみのCookieを削除することもブラウザの機能としては可能ですが、その操作はさらに難易度の高いものです。 また、案1については重複したCookieが削除されるタイミングがブラウザ依存であるため、インシデントの収束タイミングを把握できないという問題点がある以上避けるべきだろう、という意見もありました。 そうして、上記のような理由から安全かつ完全な形で障害から復旧させる方法である、Cookieのkey名を変更してからリリースする、という方法を実施する決断を行いました。 リリースに当たっては、万全を期すために調査で判明したsession.cookieの定義を使用している処理を全て網羅するテストケースを作成すると共に、QAチームが使用しているリグレッションテスト項目を共有してもらい、購入者の一般的な操作を全て動作確認することでより安全性を高めました。 これによってさらに障害の復旧までに時間を要することにはなりますが、より安全かつ完全に対応するためには必要な作業だというのが開発チームの共通認識でした。 おわりに 今回、障害の発生を認識してから収束するまでの間に2週間という時間がかかってしまった点と、対応の副作用によって購入者様の皆様にご迷惑をおかけしてしまったことは大変申し訳なかったと感じています。 サービスを提供していく上で、障害を起こさないように気をつけることは重要なことです。そして、万が一障害が発生してしまった際には、如何に素早く影響を最小限に留めて適切な対処で障害を解決することができるか、という点もまた重要なことです。 BASEでは、このようにBASEが提供するサービスを利用してくださる購入者やショップオーナーの皆様のことを第一に考えてサービスを共に発展させていく仲間を募集しております。 カジュアル面談も実施しておりますので、ぜひお気軽にお問い合わせください。 https://open.talentio.com/r/1/c/binc/homes/4380
フロントエンドエンジニアの @rry です。 自分は BASE の Sales Promotion というチームで主に新規機能開発を行っています。このチームでは主にオーナーさんの使う管理画面に新しく機能追加をしています。 そこで、管理画面で使っている API Client と型を、 OpenAPI Generator を使って自動生成するようにしてみたのでそのお話を書きたいと思います。 そもそも OpenAPI とは? https://www.openapis.org/ OpenAPI とは、RESTful Web サービスを記述、生成、使用、および視覚化するための仕様です。 ※ 以前は OpenAPI ではなく仕様自体も Swagger と呼ばれていましたが、現在は仕様自体については OpneAPI と呼ばれており、Swagger というのは OpenAPI を使ったツール群のことをさすようになりました。まぎらわしいので Swagger ではなく主に OpenAPI と呼びます(ツール群のほうも「OpenAPI のツール」と呼んでいきます) BASE では YAML ファイルで記述しています。 OpenAPI とそのツール群を使うことでなにができる? API の仕様(スキーマ)を定義 定義の一元管理ができる API ドキュメントを生成 ドキュメントのメンテナンスが楽 API モックサーバーを立てられる API が出来上がっていなくても先にフロントエンドの開発ができる API Client を自動生成 API Client のコードをフロントエンドで書かずにすむ! API リクエスト・レスポンスの型を自動生成 スキーマから生成した型を使うことでより型安全になる 周辺ツールでいろいろできる バックエンドの実装と定義したスキーマが乖離した場合に自動テストが落ちるようにしたりもできる バックエンドの実装が乖離しないようにできる OpenAPI のようなスキーマを中心にした開発のことを、「スキーマ駆動開発」といいます。 OpenAPI を使ったスキーマ駆動開発をすることでなにがうれしいの?どういう問題を解決するの?というところは、以下のスライドが参考になるのでそちらをどうぞ。 BASE 既存システムへの OpenAPI 導入の背景について BASE では最近カートの大規模リプレイスを行いました。 BASE Tech Talk #1 〜Next.jsを使ったカート大規模リプレイスPJの裏側〜 - connpass 新しいカートのアーキテクチャでは、既に OpenAPI が導入されておりスキーマ駆動開発を行っていました。自分もフロントエンドの開発で OpenAPI から生成した API Client を利用したりしていました。 しかし BASE のオーナーさんの使う管理画面など、カート以外のシステムでは既存の API 定義は OpenAPI ではなく API Blueprint を利用していました。 API Blueprint を使った開発では API の仕様(スキーマ)を定義 API ドキュメントを生成 API モックサーバーを立てられる これらのことはできますが、API Client や型を自動生成することはできず、毎回手動で API Client と型を定義していました。 手動で定義したり API の仕様が変わったときにそれらの追従をすることが大変だと思い、カート開発のときと同様の開発体験を得たかった自分は「今回の PJ から OpenAPI を使ったスキーマ駆動開発をしよう!API Client と型を自動生成していこう!」と呼びかけ、そのための仕組みを導入することにしました。 API Client って何?自動生成ってどういうこと? API にリクエストを送るためのコードを API Client と呼んでいます。 // このような感じのコード export class FooApiClient extends APIClient { async getBar () { return this .request < APIResponseWith < Bar >>( { url: ` ${ BASE_PATH } /foo/bar` , } ) } } BASE では今まで上記のような API Client を手動で書いていたのですが、これからは OpenAPI から自動生成する API Client を利用していくことにしました。 以下は実際にどのようにして API Client と型を自動生成しているのかについて詳しく説明していきます。 ① API Client を自動生成する仕組みの概要 OpenAPI からどのようにして API Client を生成しているかをまとめました。 OpenAPI の個別のファイル群を編集 一つの大きな merged.yaml という OpenAPI ファイルを swagger-merger を使って生成 merged.yaml を元に openapi-generator-cli を使って API Client やスキーマの型を生成 その他便利関数と一緒に GitHub Packages を使って npm パッケージとして配信 openapi-generator-cli の typescript-fetch を使って fetch API の API Client を生成しています。 API Client を生成する流れはこのような感じですが、他にも merged.yaml を元に Docker を利用して色々しています。ReDoc / SwaggerUI を立てて API ドキュメントを読んだり、API Sprout を使って API モックサーバーを立てたりもしています。 どのような開発体験になるか まずバックエンドエンジニアが API を作る前に、OpenAPI の YAML ファイルだけを追加した PR を出して API のスキーマについてフロントエンドエンジニアと共にレビューします。PR がマージされると GitHub Actions が自動で API Client の npm パッケージを配信してくれます。 フロントエンドは配信されたパッケージを利用して、スキーマに沿った API へのリクエスト・レスポンスを実現することができます。 また、便利関数として API モックサーバーへのリクエストもできるようにしています。 これにより API の開発を待たずしてフロントエンド側の実装を進めていくことができます。 このようにして自動生成した API Client は以下のような形で使うことができます。 import { apiConfig , FooApi , FooBarResponse } from 'api-client' const client = new FooApi ( apiConfig ) const result: FooBarResponse = await client.getBar () ② OpenAPI の個別のファイル群について OpenAPI の個別のファイル群は、API Client を生成しやすいようにいくつかの命名規則に沿って作られています。 ディレクトリ構成は以下のとおりです。 ├── README.md ├── docker-compose.yaml ├── src ├── _components.yaml - components 定義 ├── _paths.yaml - paths 定義 ├── components - 共通 components 定義 │ └── error_response.yaml ├── merged.yaml - Docker から参照するためのファイル。特にいじらない ├── openapi.yaml - ベースファイル └── services - サービスディレクトリ └── <service_name> ├── components - service の components 定義 │ ├── xxx_request.yaml │ └── xxx_response.yaml ├── definitions - service の definitions 定義 │ └── user.yaml ├── examples - examples 定義 │ ├── <paths_name> │ │ ├── default.yaml │ │ └── xxx.yaml │ └── 400_example.yaml └── <paths>.yaml src/openapi.yaml の tags に name を定義 src/_paths.yaml に path を定義 src/services/ 配下に yaml ファイルを作成 yaml ファイルの内容が src/merged.yaml に反映される というのがザックリとした編集方法です。 OpenAPI は $ref というキーワードを使って外部ファイルを参照可能なため、 path API エンドポイント リクエスト・レスポンスの schema example これらを services 配下にまとめて、細かくファイル分割をして管理しやすい形にしています。 ③ API Client やスキーマの型を生成するにあたっての命名規則 ファイル名やその他命名など細かい規則をいくつか設けていますが、その中でも API Client の生成に影響するものをまとめました。 フォルダの命名規則 src/services/* 配下 各 API のスキーマを置く場所 API の URL と同じ構成にする この際 path に api が入っている場合は api を抜く 例) /apps/api/foo/bar なら、 src/services/apps/foo/bar.yaml となる 生成される API Client はフラットな階層に一律出力されるため、そもそもの命名としてユニーク性が必要です。そのため URL の構成に従って命名しています。 tags と operationId tags フォルダの path の services から親となるリソースまでの path をつなげる 例) src/services/apps/foo/bar.yaml なら appsFoo になる tags は services の各まとまりごとに同じものを用いる 例) /services/apps/foo/bar.yaml と /services/apps/foo/baz.yaml は同じ tags appsFoo を使う operationId リクエストメソッド + リソース名 例) /services/apps/foo/bar.yaml の GET リクエストだったら getBar となる API Client が生成されるとき、tags が API クラス名で operationId がメソッド名となります。そのため上記の命名規則で生成されるクラスは以下のような形になります。 export class AppsFooApi extends runtime.BaseAPI { async getBar ( requestParameters: GetBarRequest = {} , initOverrides?: RequestInit ) : Promise < AppsFooBarResponse > { // ... } } requestBody と responses のスキーマと examples これらは別ファイルに models として切り出すようにしています。 get : tags : - appsFoo operationId : getBar responses : '200' : description : OK content : application/json : schema : $ref : ./components/bar_response.yaml examples : default : $ref : ./examples/default.yaml '500' : description : Internal Server Error content : application/json : schema : $ref : ../../../components/error_response.yaml examples : barInternalError : $ref : ./examples/bar_internal_error.yaml # ... # ./components/bar_response.yaml title : appsFooBarResponse type : object properties : status : type : number bar : type : string nullable : true # ./examples/default.yaml value : status : 200 bar : null models として切り出して個別に title を定義することで、レスポンスの型の命名が自動的に InlineResponseXXX のようになってしまうのを防ぎます。 また、examples も default のように名前を定義することで、API モックサーバーへリクエストするときに意図したレスポンスを返してもらうことができるようにしています。 import { mockApiConfig , AppsFooApi } from 'api-client' const client = new AppsFooApi ( mockApiConfig ( { status : 500 } )) const result = await client.getBar ( {} ) // 500 エラーが返ってくる // example の value は OpenAPI 定義の examples の key を指定 const client = new AppsFooApi ( mockApiConfig ( { example: 'default' } )) const result = await client.getBar ( {} ) // default で設定した example の値が返ってくる ④ その他 API Client を利用する上で用意した便利関数 非同期関数の catch や try / catch でエラーが起きたときのハンドリングを行う便利関数も用意しています。 apiErrorType を使って起こったエラーを3つのパターンに整形する APIError: API から返ってきたエラーレスポンス Error: それ以外の何かしらのエラー null: 401エラーの場合は一律でエラーハンドリングしており何もしないため null を返す isAPIError を使ってエラーが APIError なのか Error なのかを判別する const client = new AppsFooApi ( apiConfig ) const result = await client.getBar ( {} ) . catch (async ( e ) => { const error = await apiErrorType ( e ) // 401 のときは自動的に FlashMessage が出るようにしているため早期リターン if ( ! error ) return if ( isAPIError ( error )) { // APIError で 404 など返ってきたときのエラーハンドリング } else { // そうでないただのエラーが返ってきたときのエラーハンドリング console .log ( error.message ) } } ) また、エラーハンドリングについての考え方は今まで実装者の判断に委ねていた部分がありましたが、エラーハンドリングのやり方についても別記事でまとめて認識合わせをしたりしました。 Web サービスを開発するときのエラーハンドリングについて ユーザーに表示するエラーメッセージを管理するのはフロントエンド?バックエンド? 注意したいのがここで返ってくる title や detail などは、ユーザーに表示するための文言ではなくあくまで開発者に何のエラーか教えてあげるための文言だということです。 エラーを返しているのは API であって、API を操作するのはフロントエンドのコード(つまりフロントエンド開発者)なので、開発者がわかるエラーメッセージで十分です。 ユーザーに表示するエラー文言については、デザイナーと相談して決めることがほとんどかと思います。ここは細かい調整が行いやすいフロントエンドで管理するのが良いでしょう。ユーザーに表示する領域はフロントエンドの領域です。 ⑤ GitHub Actions を使った GitHub Packages の配信 src/merged.yaml または api-client/package.json に変更があった PR が main ブランチにマージされた場合は GitHub Actions で GitHub Packages を配信するようにしています。 - uses : docker://openapitools/openapi-generator-cli with : args : generate -g typescript-fetch -i src/merged.yaml -o api-client/src/generated --additional-properties=modelPropertyNaming=camelCase,supportsES6= true ,withInterfaces= true ,typescriptThreePlus= true - run : | cd api-client yarn install --frozen-lockfile yarn build - env : GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} run : | cd api-client npm config set //npm.pkg.github.com/:_authToken=$GITHUB_TOKEN npm publish しかし、main にマージした際に何らかの理由で npm publish が失敗したらどうしましょう?🤔 そんなときのために、CI で publish できるかもチェックしています。 can-npm-publish を利用して npm publish ができない場合は CI が落ちて気づけるように GitHub Actions を設定しています。便利ですね。 - env : GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} run : | cd api-client npm config set //npm.pkg.github.com/:_authToken=$GITHUB_TOKEN yarn run can-npm-publish --verbose 以上、①〜⑤まで API Client と型の自動生成をする上でのポイントをあげました。 このような形で現在は自分のやっている PJ 以外でも OpenAPI から生成した API Client と型が利用されるようになってきています。 使っていく上でのメリットとこれからの課題 OpenAPI を利用したスキーマ駆動開発をやってみて感じたメリットは以下の通りです。 API の実装を待たずしてフロントエンド開発ができる API モックサーバーを使うことで、外部連携などが必要な複雑な API であったとしてもフロントエンドの動作確認や開発を楽に行うことができる API Client やスキーマの型をいちいち手動で書く必要がなくなる 仕様と実装の乖離がなくなる このように、開発を楽に&速くすることができました! 🙌 とはいえ、全体を通した課題はまだまだ感じます。 フロントエンドの開発にとってメリットを感じられることは大きいのですが、バックエンドの開発にとってはどうでしょうか。 バックエンドの実装と定義したスキーマが乖離した場合に自動テストが落ちるようにしたりなどの設定がまだできていない バックエンドは仕様と実装の乖離が起こっても検知できないので、API に変更がある場合はコミュニケーションでなんとかする必要がある といったように、現状はフロントエンドの開発ではスキーマ駆動開発の良さを享受できるけどバックエンドの開発ではフロントエンドほどの恩恵は受けられていない状況です。 とはいえバックエンドの開発でも、API の実装がフロントエンドの実装のブロッカーにならずにすむというというのはバックエンド開発者の精神衛生上とても良いことだと、うれしいフィードバックを受けたりもしました。 今後バックエンドの状態が変わり次第 OpenAPI のツールを入れるなりして、さらにより良いスキーマ駆動開発を推進していければいいなと思っています。 おわりに OpenAPI を利用したスキーマ駆動開発で得られるメリットはとても大きいものです。 ぜひ API Client と型を自動生成して各 PJ で活用してみてください!