TECH PLAY

BASE株式会社

BASE株式会社 の技術ブログ

576

こんにちは。BASEの藤川です。 緊急事態宣言も続く状況下で、当社もリモートワーク(Work From Home)中心の仕事の進め方をしています。ネット系企業は、幸いにしてVPN、Slack、GitHubやドキュメント管理ツール、その他仕事に必要なSaaSやZOOMがオンライン化しているため仕事の作業そのものは、それほど違和感なく自宅からでもできているのではないかと思います。 でも、仕事というのは作業だけで済むものではありません。業績を上げるための作業を生み出す活動を始めとする考えるタイミングであったり、不確実なものを埋めていくためにお互い議論するタイミングなど、曖昧なプロセスの先に、決定をして作業の的を絞り込んでいくプロセスが不可欠で、ここで複数人のチームワークが不可欠です。 今、一緒に仕事をしている仲間においては、コロナ以前から社内で人間関係を構築済みの人と、コロナ禍においてリモートだけで人間関係を構築している人と二分されています。当社の採用活動でも緊急事態宣言が出てからは、オンラインコミュニケーションのみで内定を出しておりますた、他部署の方などで最短で退職されてしまうと、せっかくそこまで大きくない会社にも関わらず一度も会うことなく去ってしまう人もでてくる可能性があるという状況です。 そのため一日でも長くいただくために、入社される方へのオンボーディングおよび心理的安全性、チームの信頼関係の構築に対するケアは、マネジメントにおいても最重要項目として常に議論をしています。 そもそも、オンラインコミュニケーションにおいては何が足りないのだろうか?と思っていたのですが、一言でいうと、 良い思い出を作るイベントが圧倒的に少ない ではないか?という事を考えています。 オンラインコミュニケーションで起きていたことが職場でも起きる時代 僕は高校一年生の頃からパソコン通信をやっており、オンラインのコミュニケーション経験は長いです。オンラインではパソコン通信でも、その後のインターネットの世界においても、新しいネットワークが大きくなる時に、必ず見かけてきた風景として、 オフ会によって人間関係の距離が変わる オフ会を開催して、誰かと誰かが仲良くなって重鎮化していき、ある種の既得権みたいなのが生まれ、また新しい人が入ってきて新しい関係性が生まれる、常に人間関係のシャッフルが生まれ、コミュニティの歴史が長くなってくると、必ず起きる問題が、 新参者が入れない問題 というのが起きます。 よく組織設計の話で、ダンパー数というものが語られることがあります。 ・5~9人=「社会集団(クリーク)」…最も親しい友人やパートナーの数 ・12~15人=「シンパシー・グループ」…ほぼどのような状況下でも心から信頼できる人の数 ・30~50人=「一団(バンド)」…危険な国を安全に往来できる小さな団体 ・150人=「フレンドシップ・グループ」…共同体の中で一緒に暮らすのに最適な人数 ・500人=「部族・種族(トライブ)」…出会うと会釈する程度の顔見知りの人数 ・1500人=「共同体(コミュニティ)」…人間の長期記憶の情報数の限界、頭の中で名前と顔が一致する人数 出典:チームを成功へ導く魔法の数字たち 「7」「30~50」「150」 https://www.sankeibiz.jp/smp/workstyle/news/190401/wsa1904010700003-s4.htm ダンパー数は組織の人数規模を考慮する時に参考にする数字なのですが、肌感覚としても一致はしていて、小規模のコミュニティにおいては、お互いの距離感は均等に短く、何もしなくても親密になりやすいです。これが人数が30〜50人であるとか、100人を超えてきて、なおかつ参加したタイミングがバラバラともなれば、相互の距離感が不均衡になりがちです、そして、不均衡が生まれれば生まれるほど、既に存在する人間関係や情報の非対称性の中で孤独感を感じる人が出てきます。 特に何十人の規模のオフ会ともなると、人脈を持つものと持たざるものの差は大きく、また、それを切り開くだけのコミュ力や、そのコミュニティで名を売るんだぞ!という根性を持ち合わせていないと、家に帰った後にどっと疲れて、このコミュニティは内輪ばっかりで、疎外感に満ちあふれていたなどの言説が駆け巡ってトラブルになることを、この目で散々見てきました。 こう言ったことが、SlackとZOOM主体のリモートワークの働き方においても適用されるのではないかと考えています。 以前入社した100人ぐらいの会社が、実は1000人以上のグループの一社だったということを入社日に参加したグループ全体の定例会議で知ることになり、なんか大企業に来ちゃったなぁということに疎外感を得たことがあります。自分のアクセス可能なネットワークと、手の届かない全体感の規模感のギャップというのはストレスを感じるという経験をしたことがあります。 そのような会議の後に、心の支えになるのは隣の席の同僚だったり、同期入社した人だったり、即座に小さなコミュニティを構築し、雑談をすることが心のクッションになったりします。 しかし、コロナ禍においてフルリモートのような状態で働くと、自分の部屋でパソコンを閉じても、隣の人と雑談をして共通見解などの意見を交わして、息を抜く所がありません。まして真面目に自粛していて、外にも全く出歩かない独身の方だと、プライベートのネットワークが補完することもできず、どうにも仕事モードを発散できないような人もいるのではないでしょうか。 職場で「何も考えずとも」得られていたもの そういう時に起こりうる疎外感や寂しさと言った無用な感情を埋めてくれる存在は、同じ部署のメンバーやプロジェクトのメンバーなのですが、まだ人間関係ができていないと難しいですし、仕事の中で培われるというよりは、一緒にランチに行く時に路上やエレベーターで会話するシーンや、タバコ部屋の議論とか、仕事の隙間に培われていいた何かというのは確実にあったような気がします。 このようなタイミングで培われるものを「遊び」と定義してみます。 仕事は何かの目的を持って、無駄なく最適化された活動をすることが生産性が高いと表現されます。それが有能な会社員の行動であれば、無駄を許容することはその逆になるので、一人でやるのは難しいです。 Slackで雑談するにしても、相手にしてもらえる人がいればこそ。まして、一緒にタバコを吸いに行くとか、コーヒーを飲みながら雑談するとか、仕事終わりに飲みに行くなど、最適化された活動以外のものは、概ね気の合う数人レベルのマイクロコミュニティとして行われるものではないでしょうか。 ZOOMの活用で、会議室の移動は一瞬でできるようになり、仕事そのものは大変効率化されました。時間ギリギリまで会議していても、10秒後には次の会議室に入ることができ、我々は仕事マシンとして仕事をし続けられるようになりました。その一方で、移動のタイミングで生まれていた、アジェンダのない会話 = 「遊び」は消え去りました。そこで副次的に得られていた相手との心理的安全性や、仕事をする意義みたいなものを学ぶために、実は大切なものではなかったのか?というのが仮設になります。 更に、この会社で働いてよかったこと、自分の成長につながったなぁという手応えは、なんだかんだと新機能のリリースイベントであったり、会議室での議論でのお互いの顔の表情であったり、ホワイトボードに書いたものという手触り感、プロジェクトの打ち上げでの会話、メンバーの笑顔など視覚情報によって脳内に記憶が定着され、それ故に実感を得られていたのはないか?と思うことがあります。 この思い出こそが、10年後20年後に振り返った時に脳の中に残っていることが人生にとって大切なのではないか? 残念ながら、これはZOOM上のバーチャル背景の集合体では得られにくいのではないでしょうか?この1〜2年で働いていたことは人生の記憶やストックとして積み上がるのだろうか。 このような実感を得られるイベントを、オンライン主体の変わりなき日常風景の中で、どうすればみんなが実感できるのだろうなぁというのを常に考えています。何か工夫をされている会社さんやマネージャの方がいたら是非教えてほしいです。 オンラインで「仕事の思い出」は生み出せるのか コロナ以前であれば、合宿、社内イベント、飲み会などは、それらを補完し、思い出を業績に変えていく装置だったように思えます。打算的に言うと、あえて遊びを作ることで、仕事の生産性を生み出す装置であったという見方ができます。 組織を語る表現において、歯車というのはネガティブな表現とされますが、歯車と歯車の間には遊びは必要だし、それらが適切に回って全体が駆動されなければ、そもそも良いプロダクトは作れないのです。 こう言った遊びの構築を、それをオンラインでは補完できるのだろうか? 間違っても、その処方箋はZOOM飲みではないなとは個人的な感覚としてはあるのですが、無駄や行間をあえて作って、そこから何かを生み出していくというのは、全員に高度なネットリテラシーが問われるようにも思えます。 もしかしたらヒントは、ツイキャスや17LIVEのようなライブ配信の関係性にあるのかもと思わなくもないのですが、とりあえず今回は、Youtubeの動画を作ってみて、こういう考えを深めるきっかけを作ってみました。 こちらの動画は、採用候補者の方にBASEを検討する際に見ていただきたい動画として、BASE社におけるPHPの現在・過去・未来というタイトルで作っています。今回は、当社の社員として活躍いただいているyakkunへのインタビューという形で収録したのですが、更に、最後のサイドトークに今回のリモートワークについての話にも言及しています。 www.youtube.com 今後も当社のメンバーとこのような会話を公開用のコンテンツとして作っていくことで、1on1とはまた違った思い出が作れないかと思って作っています。全社員できるかな?! 今回の内容は「最近は他の言語がメインにしてきた人がBASE社でPHPを使う気持ち」という長く続くことに成功したサービスに携わる人達であれば共通の経験がある話をしています。 ラジオ感覚で聞ける話ですので、もしよかったら是非見てみてください。
アバター
こんにちは。Product Dev Divisionに所属している 大津 です。 PHPカンファレンス2021 のトーク募集が始まりましたね。 僭越ながら、私は過去にPHPカンファレンス 2019 と 2020 に2回登壇したことがあり、今年もトーク応募をしてカンファレンスを盛り上げていこうと思っています! そこで今後登壇する人のお力に少しでもなればと思い、トークするまでの流れを私なりにまとめてみました。 ぜひ参考にしてみてください! 話したい内容を軽く決める まずは話したい内容をなんとなく決めます。 これは普段から思っていることでも良いですし、社内向けドキュメントとして書いた内容でも良いです。 はたまた、誰も話してなさそうなテーマだから自分が話してみよう!みたいな動機でも良いです。 大事なのは自分がトーク準備の過程を楽しめそうなテーマであるか?ということです。 ターゲットを決める どんな人に向けてトークをするのか具体化しましょう。 例えばWebエンジニアがターゲットだとしても、初心者〜上級者、フロントエンド〜バックエンドなど色んなセグメントがあります。 私はトークを聴く人のペルソナのようなものを、なんとなく作っています。 どのくらいの知識を持っているか どんな課題感を持っているか 自分が話したいことを知ることでどんな状態になるか 例えば私が過去に話した PHPerのためのテストコード入門 では、以下のようなペルソナを考えていました。 参考にしてみてください。 ex.「PHPerのためのテストコード入門」ターゲット1 職業プログラマなりたての新人エンジニア テストコードを言われるがままに描き始めているが、テストコードを書くモチベーションが湧かない 発表を聞いてテストコードを書く理由を知ることで、意味があることをしているんだとモチベーションを高めたい ex.「PHPerのためのテストコード入門」ターゲット2 そこそこコードを書いているエンジニア とりあえずテストコードを書け!と言っているが、何故テストコードを書かなければいけないんですか?と言われると言葉に詰まる 発表を聞いてテストコードを書く意義を伝えられるようになりたい ターゲットに合わせた登壇内容を決める ターゲットが決まったら、ターゲットの達成したい目的を果たせるような登壇内容にしてみましょう。 例えばターゲットが職業プログラマなりたての新人エンジニアであれば、一部のマニアしか分からない内容を書く必要ありません。 逆にベテラン向けの話であれば、初歩的なことはある程度省いても良いでしょう。 この段階で、話すこと話さないことが決まるので、トークの最初に話すこと話さないことをスライドに載せても良いかもしれません。 プロポーザルを出す トーク内容が決まったらプロポーザルを出しましょう。 プロポーザルとは、自分が話すトークの概要です。下記のようなトークの募集が始まった際に、プロポーザルを出します。 PHPerKaigi 2021 PHPカンファレンス 2021 例として僕が今までに出したプロポーザルのリンクをいくつか貼ります。 拙筆ではございますが、どれも採択されたプロポーザルです。参考にしてみてください。 テストピラミッドを意識したテストコード実装戦略 PHPerのためのテストコード入門 リーダブルコミットのすゝめ よくある採択されないプロポーザルについては、こちらのスライドが良くまとまっているので読むと良いでしょう。 プロポーザル・アンチパターン - Speaker Deck プロポーザルの構成についてはこちらのスライドが参考になります。 登壇の可能性をあげる!カンファレンスプロポーザルの書き方のススメ - builderscon::blog また、過去に採択されたトークを見て分析するのも良いでしょう。 トークの採択をするのはカンファレンススタッフですが、スタッフも人間である以上カンファレンスが盛り上がるかということやそれぞれの好みを軸に選ぶはずです。 例えば、10月に開催されるPHPカンファレンス 2021では、採択に関するルールで 「採択者はPHPプログラマー歴が長い人から2年程度の短い人まで、過去登壇してくれた方を中心にお願いしております。」 と書いてあります。 余裕があればこの辺りも見てみましょう。 トーク内容と構成を練る 無事採択されましたら、いよいよトークの内容を考えていきます。 私からトーク内容を練る上で、重要な点をいくつかまとめます。 十分に時間を割こう 目安ですが、私はスライド作成や発表練習などトークの準備時間を最低でもトークする時間 × 120分用意しています。 例えば、25分の発表であれば、25分 × 120分で50時間準備時間に費やしています。 ※LTはこの限りではありません 気をつけて欲しいのは、ここにはデモなどの環境を用意する時間は含まれていません。 作成する内容に合わせて、追加で時間を設けると良いでしょう。 想定外を考慮する 資料を作っていると、以下のようなことに陥ることがあります。 プロポーザルに書いた(想定していた)内容と違う点を見つけてしまった 調べれば調べるほどよく分からなくなり、何もわからんという状態になる 今伝えたいことが、今回のトークの時間だけでは足りないことに気づいた この辺りはプロポーザルを書いた時点ではわからない不確定要素です。 できるだけ不確定要素は取り除いた方が良いものの、想定外はあるという前提で資料作成のスケジューリングをした方があとあと自分が楽になるかと思います。 1回トークのレビューをしてもらう 資料がある程度完成したら、一度他の人の前でトークしてFBをもらいましょう。 誤字脱字などがないか スライドに書いてある内容が本当に正しいか 聞いている人が理解しやすいような構成、流れになっているか トークの時間に間に合う分量か 喋りのスピードは適切か 一番良いのは、トークのターゲットにあたる人と今回のトークテーマについて詳しい人へレビューをお願いすることです。 企業に所属している人は、スライドのレビューもしてもらう スライドは、トーク中だけでなくトーク後のことも考えて作成しなければなりません。 基本的にトークで使ったスライドは、発表後も多くの人の目に触れます。 カンファレンスがトークの録画をしていて、後日カンファレンスに参加していない人や別のトークを聴講していて見れなかった人が動画を見る 公開したスライドのURLが拡散されて、カンファレンスに参加していない人がスライドを見る 内容が企業にとって不都合がないか、広報へ確認の依頼を出しましょう。 また企業によっては、指定したスライドテーマを使うよう指示されることもあるかと思います。 その際には、作成したスライドが企業のブランドポリシーに反していないかのチェックをお願いしましょう。 カンファレンス当日にトークをする いよいよカンファレンス当日です。 トークをするにあたって、考えておきたいのは質疑応答です。 事前に「こんな質問が出そうだから、その時はこう回答しよう」とかある程度考えておくと良いでしょう。 また、答えられない質問やいわゆるマサカリが来たときは、素直にわからないということを伝えましょう。 この辺りの話は、以下のスライドへ譲ります。 LT・登壇でマサカリに備えるたった一つの心得 トーク終了後 登壇お疲れ様でした!トーク後にぜひやってほしいことが2点あります! スライド共有サービスにアップロードしよう トークで使ったスライドを、slideshareやspeakerdeckなどでアップロードしましょう。 スライドのURLをツイート等すると、トークを聞いてくれた人だけでなくトークを聞いていない人の目にも触れるチャンスになります。 参考:大津のspeakerdeck また、自分が登壇した内容のスライド一覧は、自分のポートフォリオの一部として活用できます。 ぜひ自身のブランディングのためにもアップロードしていきましょう。 懇親会にはできるだけ参加しましょう 懇親会では、スピーカーは比較的声をかけてもらえやすい立場にあります。 話す内容はトークのFBだったり、質疑応答だったり…トークした内容について貴重なご意見をもらえるチャンスでもありますので積極的に参加しましょう。 ただし体調不良だったり先約がある場合は、そちらを優先しましょう!(無理せず!) 前日まで徹夜でスライドを作っていたりすると、この懇親会がしんどくなり良いチャンスを逃してしまうかもしれません。 懇親会の参加も含めて、余裕を持ったスライド作りのスケジューリングをしましょう。 最後に トークするまでの流れについてご紹介しました! ちなみに弊社のSlackでは、外部カンファレンスへの登壇についてワイワイする #iikanji-conference-toudan というチャンネルがあります。 今後も外部カンファレンスへの登壇に向けてワイワイしていきます!
アバター
この3ヶ月で行ったBDIの内容を紹介します こんにちは、デザイナーの河越です。 BASEのデザインチームが月2回行っている社内勉強会「BDI」。リモートワークになってからも継続的に開催しています♪ 今回は4月~6月に開催したBDIの内容をご紹介したいと思います! BDIとは? 『BDI』は「BASE Design Inspiration」の略。 2018年の秋頃から活動している、デザイナーがやりたいことを持ち寄って、 デザインに関する幅広い知見をみんなで楽しく学ぶことを目的とした任意参加の社内勉強会です。 BASEのデザイナーであれば、デザイナーだけでなく誰でも参加することができます。 Inspirationの名の通り新たなひらめきにつながる新しいトピックを取り上げることも多くあります。 1月~3月の振り返りはこちら devblog.thebase.in BASEのデザイナーがどんな活動をしているのか気になっている方に読んでいただきたいのはもちろん、 リモート環境での社内勉強会のネタ探しにもぜひご活用くださいね! 4月 発想メソッド「マンダラート」でアイディアを湧き出させる会 4月最初のBDIは、アイディア出しのフレームワーク「マンダラート」を実践で使う企画からスタートしました。 マンダラートとは、一つのキーワードに対して8つのマス目を作り、そのマス目一つ一つにアイデアを書き込むことで目標を達成するためのアイディアを見つける手法。 あの大谷翔平選手も目標設定のために使っていて、現在注目が集まっているフレームワークの一つだそうです⚾️(私は初めて聞きました!) BDI用にアレンジしたマンダラートでは、デザインリサーチPJメンバーのショップオーナーインタビューの報告をもとに、マンダラートを使って『オーナーズが持っている課題に対してどのような解決策があるのか』をアイディア出ししました! デザイナー以外も参加して楽しめるように、Miroを使ってワイワイと。 最終的には出てきたアイディアを発表し合うことで、オーナーズへの知見が溜まるだけでなくメンバーへの理解も深まった会でした。 社内で「ものづくり」をしている人のお話を聞くLT会 「今年のGWもおうちにいる時間が長そうだね」ということで、ゴールデンウィーク直前に実施した社内LT会。 おうち時間を楽しく過ごすヒントをもらうべく、社内でものづくりをしている3名の方にお話してもらいました! 3名はそれぞれ、ボードゲーム・ペーパーアイテム・家具を作っていて、 なぜものづくりをしているのか どうやって作っているのか 今後の展望は? と、いう角度からのお話を聞きました。 ものづくりへの熱い思いのこもったLTで「売上をやみくもに上げるより、自分の作品を好きになってもらいたい」「角度にこだわり抜いた座椅子を作って自分の生活を豊かにしたい」といった色々な価値観を知ることができました。 今回の3名だけでなく、オーナーさんが持っている考えとも通じるところがありそうで、ユーザーへの理解が深まる会となりました。 何よりLTをしてくれた3名のこだわりを知れて、作品のストーリーをより深く知ることができ、それぞれの作品のファンになりました...! GWでものづくりをしてみようかな、と考える方が増えたBDIになったかなと思います! 5月 Ownersになってみよう第2弾! ブランドのロゴを作ってみよう🎨 3月に開催した『Ownersになってみよう企画』をシリーズ化し、第1段で制作したブランドのコンセプトを元に、ロゴ作りのワークショップをやってみました。 ロゴはどんなプロセスで作られて、どんな所で使われるかのLT ロゴが使われるシーンをブレストしながら、コンセプトのブラッシュアップ ロゴのラフ案をたくさん作成するモクモク会 2回分のBDIの時間を使って、架空のブランドのロゴやシンボルマークを作成しました。 丁寧なLTがロゴ作成未経験者にもわかりやすい内容で、ブランディングの大切さ/大変さを実感できる回となりました。 フォント決めやロゴタイプのモチーフ決めには議論が活発化して2時間で納めるのは大変でしたが、複数人で集まって一つのMiroボードにモクモク作業する工程で「みんなでデザインするって楽しいな」と改めて感じました。 次回以降の『オーナーズになってみよう企画』ではパッケージや商品のデザインを進めていきます! 6月 「ページ追加 App」リリースまでのあれこれを聞く座談会 5月末にリリースされた「ページ追加 App」について、PJメンバーにリリースまでの道のりを語ってもらう座談会を開催しました。 https://baseu.jp/20203 PJを担当したPMやデザイナーからリリース前に悩んだことの振り返りや、リリース後のユーザーからの反応を聞きました。 機能を利用しているショップの事例を紹介するパートでは、想像以上に工夫して使ってくださるショップを知れてみんなで盛り上がりました! 事前に全社からPJメンバーに聞きたいことを募集して質問を選んでおいたので、より内容の濃いウラバナシを聞くことができたし、発表するメンバーの負担も少なくできたのではないかと思います! デザインツール「Figma」を使いこなそう!!!〜基礎編〜 BASEのデザインチームでも利用しているデザインツール「Figma」。 デザイナー以外のメンバーに向けて、Figmaの基本的な使い方や、Auto Layoutの組み方を学ぶ実践型のワークショップを開催しました。 Figmaマスターの渡邊さんがFigma特有の「Frame」という概念や、「Auto Layout」の組み方を丁寧に説明してくださいました。エンジニアからは「Flexboxに似ていてわかりやすい!」という声も。 資料作成にFigmaを使っているPMや、Figmaでデータを受け取るエンジニア陣から大好評の企画となりました! お互いの仕事についての理解が深まることで、よりスムーズなコミュニケーションが取れるようになりそうです◎ まとめ 月2回ペースで開催しているBDI。4~6月はLTや座談会を多く取り入れてみました。 結果としてデザイナー以外のメンバーもROM専で参加しやすくなり、参加者が徐々に増えて大盛況となりました。 PJやチームが違っても、こういった機会でコミュニケーションが取れる機会があるのは良いなと感じています。また、実況コメントを流しながらのLTなど、オンラインでもLT会を盛り上げる知見も溜まってきたので、参考になったら嬉しいです。 これからも楽しくてためになる勉強会を開催していきます。7月以降もお楽しみに!
アバター
こんにちは!! BASE BANK 株式会社 Dev Division にてSoftware Developerをしている永野( @glassmonkey )です。 普段はGo/Python/PHPを主に生業に開発・運用から何でもござれの精神でフルサイクルエンジニアをしています。 現在、自分たちのプロダクトである YELL BANK の分析基盤を構築しています。 その際に、BigQueryで扱っているデータをGoogle App Script(以下GAS)、Googleスプレッドシートとデータポータルで簡易CRMをビジネスサイドのメンバーである猪瀬 ( @Masahiro_Inose )と協力して作ったのでそのご紹介です。 いざ実施してみるとハマってる点もそこそこ多く、意外とGASやBigQueryの連携している情報が少なかったので、誰かの助けになれば幸いです。 thebase.in 簡易CRMツールをスプレッドシートベースで作成した背景 弊社で開発している「YELL BANK」では、プロダクト改善のためにユーザーインタビューなど定性情報を顧客に直接聞く形で得ていくスタイルを積極的に実践しています。 その記録を一元管理する場所がなかったこともあり、CRMツールが必要な状況でした。 CRMツールとしてはSalesforceなどが有名ではありますが、様々なデータがBigQueryに集約をする状況でありました。 今回は以下の2点で慣れているスプレッドシートベースのシステムの採用をしました。 プロダクトとして仮説検証フェーズなので見たい内容は定まってないこと データ分析を主として実施しているビジネスサイドのメンバーが自身でカスタマイズ容易なこと 謝辞 一部改変をしておりますが、コードなどは 【GAS/BigQuery】日付に応じて異なるクエリを実行 から大部分を引用させていただきました。 この場を借りてお礼を申し上げます。 構成 GAS周りの基盤を私が作成して、集計のスプレッドシート構成からダッシュボードの構成まではビジネスサイドのメンバーで分担して作成しました。 簡易CRMの構成図 準備 GASのメンテは基本的には普段コードを書かないビジネスサイドのメンバーが扱うので、都度追加するコードはシンプルである必要がありました。 そこで、OAuth認証に関連するところやAPI経由でBigQueryを実行する処理は共通処理として切り出すことにしました。 GCPプロジェクトの準備 GASからBigQueryをOAuth経由で実行するので、その設定が必要です。 今回は内部的に利用するので、UserTypeは内部を選びます。 0auth同意画面 アプリ名とサポートメールとデベロッパーの連絡先情報を入力します。内部用なので適当で良いです。 Oauth設定画面(メールアドレスなど) BigQueryのAPIが有効になっているか確認します。 このあとAPIを利用するので、もし無効になっていたら有効にしておきましょう。 BigQuery API確認画面 GASの共通プロジェクトの設定 まず最初に共通GASプロジェクト設定します。 GASのメンテは基本的には普段コードを書かないビジネスサイドのメンバーが扱うので、都度追加するコードはシンプルである必要がありました。 API経由でBigQueryを実行する処理は共通処理として切り出すことにしました。 認証用のOAuth設定を予め共通プロジェクトとの連携で行っておくことで、ビジネスサイドのメンバーが権限周りを考慮せずにすむようにもしました。 各種画面のスクリーンショットは2021年6月時点のものになります。 1. 新しいプロジェクトから共通プロジェクトを作成する 新しいプロジェクトを作成します。 新しいプロジェクトその すると下記のように無題のGASのエディターが開きます。 GAS エディター 2. ライブラリを追加する GASからスプレッドシートのアクセスを簡易的にしてくれる、gas-underscoreを入れます。 詳細は simula-innovation/gas-underscore を参照ください。 READMEから"プロジェクトキー"である M3i7wmUA_5n0NSEaa6NnNqOBao7QLBR4j を使うと良いとありますが、新エディターの場合使えませんので注意ください。 github.com 旧エディターを利用する場合 (非推奨) Add a libraryから"プロジェクトキー"である M3i7wmUA_5n0NSEaa6NnNqOBao7QLBR4j を入力します。 バージョンは最新の2を選びます。 なお、この方法はいずれ廃止になる可能性があるのでおすすめはしません。 新エディターを使う場合 左カラムのライブラリの + をクリック gas-underscoreの"スクリプトID"は 1PcEHcGVC1njZd8SfXtmgQk19djwVd2GrrW1gd7U5hNk033tzi6IUvIAV なのでそれを入力する。調べ方は後述します。 ] 調べ方としては、旧エディターで得た情報をベースにはなりますが、インストールするとGASのマニフェストファイルからスクリプトIDがわかるのでそれを利用します。 旧エディタの場合は 表示 > マニフェストファイルを表示 を選択すると見ることができます。 他にもプロジェクトキーしかわからないライブラリがある場合はこれを使うとわかるので便利です。 GASマニフェストファイルの一部 余談ですが、公式のリポのREADMEには一応 Pull Request を出しました。古いリポジトリなのでメンテされてない可能性が高いので、forkした 私のリポジトリ も記載しておきます。 github.com 3. サービスにBigQueryを追加する 今回はBigQueryのAPIを利用するので追加します。 1. 左カラムのサービスの + をクリック 2. BigQueryを選びます。バージョンは2021年6月時点で最新のv2を選択しています。 4. GCPプロジェクトと連携させる 歯車マークのアイコンからスクリプトの設定に遷移します。 プロジェクトを変更からGCPプロジェクトの連携設定を追加します。 ここでうまくいかない場合は、連携先のGCPプロジェクトのOAuth認証が正しく設定できていない可能性があります。 ここのプロジェクト番号とはGCPのダッシュボードのプロジェクト情報で確認できます。 プロジェクト情報 5. GASの共通コードを用意する。 集約したいスプレッドシートのID、シートの名前、実行したいクエリは都度変わるとのでそこを外部から渡せるように共通処理を書いておきます。 共通処理内部でBigQueryへのAPIリクエストを秘匿するようにしていので、ここはビジネスサイドのメンバーは意識しなくて済むようしておきます。 プロジェクト番号のところは、前述したOAuth設定したBigQueryを実行するgcpプロジェクト番号に読み替えください。 function run ( spreadsheetId , sheetName , query ) { /** * コンフィグ設定 **/ const projectId = 'プロジェクト番号' ; // 出力先シートSpreadsheet const sheet = SpreadsheetApp . openById ( spreadsheetId ) ; //出力するシート名 const workSpace = sheet . getSheetByName ( sheetName ) ; const request = { query : query , useLegacySql : false } ; execBigQuery ( projectId , workSpace , request ) ; } ; function execBigQuery ( projectId , ws , request ) { let queryResults = BigQuery . Jobs . query ( request , projectId ) ; const jobId = queryResults . jobReference . jobId ; //BigQuery実行 queryResults = checkOnQueryJobStatus ( projectId , queryResults , jobId ) ; //結果を取り出す const rows = getAllResults ( projectId , queryResults , jobId ) ; //結果を書き出す outputToSpreadsheet ( rows , ws , queryResults ) ; } function checkOnQueryJobStatus ( projectId , queryResults , jobId ) { var sleepTimeMs = 500 ; while ( ! queryResults . jobComplete ) { Utilities . sleep ( sleepTimeMs ) ; sleepTimeMs *= 2 ; queryResults = BigQuery . Jobs . getQueryResults ( projectId , jobId ) ; } return queryResults ; } // Get all the rows of results. function getAllResults ( projectId , queryResults , jobId ) { var rows = queryResults . rows ; while ( queryResults . pageToken ) { queryResults = BigQuery . Jobs . getQueryResults ( projectId , jobId , { pageToken : queryResults . pageToken }) ; rows = rows . concat ( queryResults . rows ) ; } return rows ; } // Clear sheet data and output BigQuery results to sheet. function outputToSpreadsheet ( rows , ws , queryResults ) { if ( rows ) { ws . clear () ; // Append the headers and return header cols. const headers = appendHeader ( ws , queryResults ) ; // Append the results and return all spreadsheet data. const data = appendResults ( rows , headers ) ; ws . getRange ( 2 , 1 , rows . length , headers . length ) . setValues ( data ) ; return data ; } else { Logger . log ( 'No rows returned.' ) ; } } function appendHeader ( ws , queryResults ) { const headers = queryResults . schema . fields . map ( function ( field ) { return field . name ; }) ; ws . appendRow ( headers ) ; return headers ; } function appendResults ( rows , headers ) { const data = new Array ( rows . length ) ; for ( var i = 0 ; i < rows . length ; i ++ ) { var cols = rows [ i ] . f ; data [ i ] = new Array ( cols . length ) ; for ( var j = 0 ; j < cols . length ; j ++ ) { data [ i ][ j ] = cols [ j ] . v ; } } return data ; } 詳細は後ほど記載しますが、各種呼び出しは以下のような形になります。 実際にシートに集計クエリを書くビジネスサイドのメンバーとしては、集計クエリをどこのシートに出力するかのみ考慮すれば良いようにしました。 function example (){ //シートID, シート名, クエリを指定する 共通プロジェクトの名前 . run ( SpreadsheetApp . getActiveSpreadsheet () . getId () , "集計結果シート" , `何か集計用SQL` ) } シートごとの設定編 共通プロジェクトのスクリプトIDをメモ 集計シートからBigQueryを実行できるように共通プロジェクト呼び出しをします。 共通プロジェクトのスクリプトIDをメモします。 スプレッドシートに紐づくGASを開く 集約用のデータを扱うスプレッドシートを用意します。 スプレッドシートの ツール > スクリプトエディタ からシートと紐づくGASを用意します。 スプレッドシートと紐付いているGASはコンテナにシート名の記載があるので、念の為確認するといいでしょう。 ライブラリの+から共通プロジェクトを呼び出しをします。 ここで呼び出しをすることで 共通プロジェクトの名前.run で共通処理を呼び出しできるようになります。 BigQueryを共通処理と同様にサービス設定に記載します。 呼び出し関数を用意する 何かの集計結果のSQLを集計結果シートに出力する場合は下記のように書くことで実行できます。 基本的にはここの実装はビジネスサイドのメンバーが書くことになるので、 集計したいクエリを書くことに専念できるような呼び出し方としました。 また、関数として切り出すとボタン配置などやデバッグがしやすいのでおすすめです。 function example (){ //シートID, シート名, クエリを指定する 共通プロジェクトの名前 . run ( SpreadsheetApp . getActiveSpreadsheet () . getId () , "集計結果シート" , `何か集計用SQL` ) } 呼び出し用のボタンを用意する gasの実行には色々方法がありますが、今回は不要な集計処理を走らせたくなかったのと必要なときに実行できるようにボタンを用意する方式にしました。 適当にボタンとなる図形を 挿入 > 図形描画 から配置します。 図形右上の︙からスクリプトの割当を選びます。 用意した関数名を入力します。 これが完了すると用意した図形を押下すると集計クエリが出力されるようになります。 結果と使用感 Google Data Studioの連携部分に関してはGoogle Data Studioで何を出すかで変わってくるので割愛します。 結果としてビジネスサイドのメンバーが作ってくれたダッシュボードの一部ですがご紹介します。 架空の数字ですが、イメージは伝わると思います。 簡易CRMのイメージ図 当初の目的通り、ユーザーインタビューなどの定性情報を得にいく時にも、顧客とのコミュニケーション記録を有効に活用できるようになったとのことでした。 過去にコミュニケーションしたショップがファネルの上位のフェーズになったといった変化を発見できるようになったとのことです。 感想 データ分析もより専門性のあるビジネスサイドのメンバーに委譲する流れができた点も良かったと考えています。このおかげで全体としては2週間ほどでCRMのシステムを構築できました。 BigQueryをGAS経由で呼び出せるといろいろ楽なので、誰かの助けになれば幸いです。 そのようなスピード感を持って一緒に開発するアプリケーションエンジニアやカスタマーサクセスのメンバーを募集中です。 open.talentio.com open.talentio.com
アバター
Owners Experience Backend Group で Engineering Manager をしています、炭田( @tac_tanden )です。2021 年 4 月末に『ユニコーン企業のひみつ ―Spotify で学んだソフトウェアづくりと働き方』という本が発売されました。 自分含め、多くのメンバーが「読んでみたい!」と話題になっていた中で、翻訳者の角谷さまのご厚意で献本いただいたので、遅ればせながらレビュー記事を書かせていただきます。 🦄 テック企業のみなさま、テックブログの記事の候補に一冊いかがでしょうか!!!q 🙏 » 🦄 書籍『ユニコーン企業のひみつ』を貴社テックブログでレビューしていただける企業さまを募集します https://t.co/TdfL58TwpS — Kakutani Shintaro (@kakutani) April 7, 2021 著者について この本の著者を見たとき驚きました。 "Jonathan Rasmusson(ジョナサン・ラスマセン)"。自分が好きで何度も読んだ『アジャイルサムライ−達人開発者への道−』の著者の、あのジョナサン・ラスマセンさんが書いた、Spotify での経験をまとめた本ということで否が応でも期待が高まりました。 実際、読み終わった後はジョナサン・ラスマセンさんがつぶさに観察した Spotify での開発チームや文化が完結にまとめられていて、読んでいてすごくワクワクしました。 この本のテーマ 冒頭の『日本の読者の皆さんへ』の章でジョナサン・ラスマセンさんは以下のように書かれています。 『アジャイルは今や「ふつう」になりました。(中略)本書を『ユニコーン企業のひみつ』と題したのは、ものすごく成功しているソフトウェア企業(Spotify、Amazon、Googleなど)は「アジャイルでいつもやっていること」を超えた先のやり方を見つけ出しているからです。』 (vii 日本の読者の皆さんへ) ものすごく成功しているソフトウェア企業(Spotify、Amazon、Google など)はアジャイルを超えたさらに一歩先を進んでいる。その一歩先を Spotify はどう歩んでいるのか。知りたくないかい?という風に自分は理解しました。 これだけでなんだかワクワクしてきますよね。自分は Web アプリケーションエンジニアとしてずっとサービス開発に携わってきた中で、アジャイルでの開発の有効性について理解しているつもりです(スクラムマスターの資格を取りに行ったくらいアジャイルやスクラムの考え方が好きです)。 アジャイルのその先の一旦を垣間見れるということで、冒頭からフツフツとテンションが上がっていき、ページ数がそれほど多くないコンパクトな書籍ということも相まって、一気に 2 時間ほどで読み終えることができました。 アジャイルのその先 〜 Spotifyモデル 〜 Spotify モデルは自分自身はこの本を読むまで恥ずかしながら知りませんでした。2012 年(約 10 年も前なのですね...)に、Henrik Kniberg と Anders Ivarsson によって "Scaling Agile @ Spotify with Tribes, Squads, Chapters & Guilds" として発表されたのが初出のようです。 https://blog.crisp.se/wp-content/uploads/2012/11/SpotifyScaling.pdf Spotify モデルについて他に詳しく解説されている記事がいくつもあるので、ここではこれくらいにとどめ、自分はこの本の中でいくつか気になった Spotify の開発組織やスタイルについて取り上げたいと思います。 スクワッド 日本語で分隊と訳されることが多い squad ですが、もともとの語源は square(四角)です。昔、銃などの武器がなかった時代に、軍隊では四角い陣形を組んで戦ったことが多かったことに由来するそうです。自分はここから、一致団結して集団としてフォーメーションを組んで開発に立ち向かう姿をイメージしました。 また、この本からスクワッドはチームというよりは小さなスタートアップに近いように感じられました。小さなスタートアップ企業として、与えられた領域の全てに責任と権限を持ち、与えられたミッションを達成するために高速に開発イテレーション(リリースと検証)を回していく。理想のチーム形態の 1 つだと思います。 Spotify ではこのスクワッドが何をするにしても基本となり開発が進むようです。色々なスキルや経験をもった良き仲間とスクワッドを作っていく過程は楽しそうですよね。 ちなみに BASE でも企画・機能の策定から動作確認、リリースまで責任をもって行う複数の役割のメンバーで構成されたチーム単位で機能の開発をしているので、近い部分もありつつ違いも多かったので、スクワッドについて解説している第3章は特に興味深く読ませていただきました。 devblog.thebase.in カンパニーベット カンパニーベットは会社で取り組みたい最重要事項を並べた ToDo リストです。 会社で最もフォーカスしたいものを、全社向けの OKR のような形で表明する場合もありますが、「ベット」(賭ける)のほうが重要度に対して生々しさを感じてネーミングとして、素敵だなと思いました。 また、この章で紹介されている DIBB(Data, Insight, Belief, Bet)も、いわゆる OODA ループ(Observe, Orient, Decide, Act)をより具体化したものになっていて、過程がはっきりイメージしやすいので、チームに取り入れやすいのではないかと感じました。 会社やチームに限らず、時間は有限ですべてのことを完了させることはできないので、どこかでやることやらないことを分ける必要がありますが、カンパニーベットと DIBB は得られる結果がとてもシンプルになるはずなので、面白い仕組みだなと率直に思います。 会社やチームだけでなく、個人でも何かの目標ややりたいことに対して試してみるのもありなのではないでしょうか? まとめ 以上が、簡単ではありますが自分が『ユニコーン企業のひみつ』で紹介されている Spotify モデルの一部です。この本では他にもスタートアップとは何かや、開発組織をスクワッドを使ってスケールさせる方法、Google や Apple などとの文化の比較も掲載されていて、単純に読み物として面白かったです。 また、説明も具体的にかかれているので、気になった部分や仕組みを一部取り入れてみることもしやすいのではないかなと感じました。 注意点 この本の英語版が世に出たのが 2020 年 3 月です。また、ジョナサン・ラスマセンさん自身はすでに Spotify を退職されているようで、この本の原型といえるようなブログポストが 2017 年 11 月投稿されていました。 The Spotify Playbook /* */ agilewarrior.wordpress.com つまり、この本で描かれている Spotify の文化や開発スタイルは少なくとも 4-5 年前のもので、2021 年の今では Spotify は別の開発スタイルになっていてもおかしくないですね。 実際、冒頭で紹介した Spotify モデルを初めてまとめた "Scaling Agile @ Spotify" にはこう書かれていました。 Disclaimer: We didn’t invent this model. Spotify is (like any good agile company) evolving fast. This article is only a snapshot of our current way of working - a journey in progress, not a journey completed. By the time you read this, things have already changed. Scaling Agile @ Spotifywith Tribes, Squads, Chapters & Guilds p.1 この記事を読んでいるときにはすでにやり方は変わっているはず。終わりのない旅なんだ。 進化のスピードへの危機感を持つとともに更に刺激を受けました。また、Spotify でもやはり試行錯誤の連続なんだなと再確認できてよかったです。 最後に 今回、翻訳者の角谷さまのご厚意で献本いただき、拝読させていただきました。 この本がきっかけとなり社内でも開発スタイルや組織についての議論が活発になり、学ぶことが多かったです。改めて感謝申し上げます。 本当にありがとうございました。 お知らせ BASE 株式会社では、「BASE」の開発を一緒に盛り上げてくれるエンジニアを募集しています。一緒にかっこいい開発チームを作りましょう! ぜひよろしくお願いします! open.talentio.com open.talentio.com
アバター
こんにちは!アプリチームのEMをしている竜口です! 今回はリモート下でチームのコミュニケーションに課題があったので、それをどう改善していったかを紹介していきたいと思います。 初手、どうありたいかを決める やったこと いきなりxxxを始めます/やってみます!!と言ってもチームメンバーとしては意図、どうありたいかがわからないと、どう行動するべきかわからないと思います。 なので、何の為に改善して、どうありたいんだっけ?って部分を明文化しメンバーに伝えました。 実際はもっと肉付けしたものですが、下記がメンバーに伝えたものです。 1. 改善、課題解決していく 2. 開発をより早く、より確実にしていく 3. チームで開発していく 正直内容としては、特に真新しいものでもなく当たり前なものだと思いますが、今どのようなチームを目指しているのかというのを、メンバーと認識合わせました。 効果 個人的には、これが一番良かったかなと思っていて、この共通認識をもつことでメンバーも意識的に改善に向かっていってもらえたかなと思っています。 ツールに頼ってみる、tandem導入 やったこと tandem を簡単に説明すると A virtual office for remote teams を実現させるサービスで、 リモートで一緒に働く上でチーム感での会話のしやすさの実現、お互いの状態を緩く把握することで一緒のオフィスで働いてる感を醸成してくれます。 youtu.be 最初はアプリチームで導入し、その後アプリ開発に関わる他のプロジェクトメンバーも含めて導入しました。 効果 Slack or Zoomでコミュニケーションとってる時と比べ、ツールとしての会話のしやすさであったり会話できそうな状態か否かがわかりやすいのもあってコミュニケーションは増えていきました。 参考までに実際のtandemの様子ですが、各部屋に誰がいるのか分かるとこや、特定の人が緑色の場合はactive/オレンジだとinactiveという風に話しかけて良さそうな雰囲気も醸成できて話しかけるハードルや他の人の話してる雰囲気が伝わったりします。 朝会の後に意味のない時間を作る やったこと 毎日、朝会と言ってやること/困りごとを共有する場を15分程設けています。 その中で朝会後にすぐ抜ける必要のない運用にしてみました。朝会が終わってすぐ抜けてもいいし、話題がなくてもただそこにいてもいいし、何か特定の人に聞きたいことあれば聞いていいし、雑談してもいい。 効果 雑談が増えたのと、朝会でみんなの時間取るほどではないしSlackで聞くほどではない疑問とかが自然と出てきてよいです。 このルールだとtandemからの抜けにくさが出るかなと思ったのですが、お昼ご飯時なのもあり抜けやすさがあり、そこもメンバーのお気持ち的にも良かったのかなと。 作業タイムを作る やったこと ただただtandemつなぎっぱなしで作業する時間を2時間/1週間入れてみました。 ただ同じ空間で働いてる感を作りたかったので、ルールとしては強制参加だけど話したりカメラ繋いだりは強制しない形でやってみました。 効果 雑談や軽い相談も出てきてよかったです。ただずっとオンラインで繋げることによる集中力下がるというフィードバックがあったり時間や運用の仕方は、まだまだ調整が必要な部分もあります。 またtandemに Crosstalk という機能があり、同じ部屋にいる特定の人と話す時、会話に関係ない人にはその会話が小さくしか聞こえない機能があり、会話の指向性がでて話題に関係ない人のストレスにもなりにくくよかったです。 まとめ 今回は比較的導入が簡単なものをやってみて、以前よりチーム内での会話が増えて、一人で問題を抱え込むような形は改善されたかなとは思います。 以下メンバーの生の声です! - 朝会の後、いま取り組んでる課題とかに自然と移行できていい - Tandemは、いまみてるGitHubのURLとかを自動で取ってきて表示してくれる機能があって、IssueやPRの話などをしやすい - 朝回の前はあえて話すことないかなーと思っていたりするけど、意味のない時間に入ると敷居が下がって話せたりする - Tandemは、いざってとき会話するハードルが低い。ちょっと確認したいけど込み入った問題で確認したいぐらいの温度感のときに会話しやすいのが助かる ただまだ改善の余地はあって、チームとしてプロダクトを成長させる為に勉強会等を今後やっていきたいと思います。 そして!今アプリチームでは一緒にプロダクトを成長させていける方を募集しています! iOS 採用情報 / カジュアル面談 Android 採用情報 / カジュアル面談
アバター
BASE株式会社 Owners Experience Frontend チームのパンダ( @Panda_Program )です。 BASE では BASE の UI を構築するための社内コンポーネントライブラリ「BBQ」を使ってフロントエンドの開発をしています。 BBQ は Vue2 + Storybook v5 で作成されています。現在、フロントエンドの有志たちで Storybook のバージョンを最新の v6.2 にする対応をしています。 この記事では、Vue2 + Storybook v5 のコンポーネントを v6 向けに書き換える方法を紹介します。 なお、本記事ではStorybook v6 自体の機能の説明や、 main.js や preview.js の書き方といった Storybook の環境構築の方法には触れません。 Storybook コンポーネントを v5 から v6 に書き換える ここでは button-group を v5 から v6 に書き換えた例を紹介します。 まずは v5、v6 の書き方をそれぞれご覧ください。その後、変更点をそれぞれ解説をしていきます。 v5 の button-group.stories.js // bbq/stories/elements/button-group.stories.js import { action } from '@storybook/addon-actions' import { number, select, text, withKnobs } from '@storybook/addon-knobs' import { storiesOf } from '@storybook/vue' import { withInfo } from 'storybook-addon-vue-info' import { ButtonGroup } from '../../elements/buttonGroup/button-group.vue' import README from '../../elements/buttonGroup/README.md' import { devices } from '../../values/Devices' // Storybook コンポーネント名 const buttonStories = storiesOf( 'Elements/ButtonGroup' , module) buttonStories // addon-knob .addDecorator(withKnobs) // addon-info .addDecorator(withInfo) .add( 'ButtonGroup' , () => { return { components: { ButtonGroup } , // Vue Template template: ` <div : class = "'theme-'+device" > <p> <h2>デフォルト</h2> <div>アイコン+ラベル</div> <bbq-button-group :tag= "tag" :items= "iconsAndLabels" @change= "({index}) => {this.selected = index; change(index)}" :selected= "selected" :width= "width" /> <div>アイコン</div> <bbq-button-group :tag= "tag" :items= "icons" @change= "({index}) => {this.selected = index; change(index)}" :selected= "selected" :width= "width" /> <div>ラベル</div> <bbq-button-group :tag= "tag" :items= "labels" @change= "({index}) => {this.selected = index; change(index)}" :selected= "selected" :width= "width" /> </p> <p> <h2>カスタムUI</h2> <bbq-button-group :tag= "tag" :items= "['a','b', 'c', 'd']" @change= "({index}) => change(index)" :selected= "selected" :width= "width" > <template v-slot= "{items, change}" > <button v- for = "(item, index) in items" @click= "change(index)" > {{ item }} : {{ index }} </button> </template> </bbq-button-group> </p> </div> `, // Data data() { return { device: select(`device`, devices, 'pc' ), selected: number( 'selected' , 0), width: select( 'width' , [ '' , 'full' ] ), } } , // Props props: { tag: { default : text( 'tag' , 'ul' ) } , } , // Computed computed: { icons() { return [{ icon: 'list' } , { icon: 'grid' }] } , labels() { return [{ label: 'ドラッグで並び替え' } , { label: '数値で並び替え' }] } , iconsAndLabels() { return [ { icon: 'list' , label: 'リストで並び替え' } , { icon: 'grid' , label: 'グリッドで並び替え' } , { icon: 'attentionCircle' , label: '念で並び替え' } , ] } , } , // methods methods: { change: action( 'change' ), } , } } , // Parameters { notes: README, } ) v6 の button-group.stories.js // bbq/elements/buttonGroup/button-group.stories.js import { devices } from '../../values/Devices' import ButtonGroup from './ButtonGroup' import README from './README.md' export default { // Storybook コンポーネント名 title: "V6/Elements/ButtonGroup/Vue" , // import したコンポーネントを指定 component: ButtonGroup, // parameters parameters: { notes: { README } , docs: { extractComponentDescription: ((_, { notes } ) => notes?.README) } } , argTypes: { // addon-knob の select で定義していた変数 device: { options: devices, defaultValue: devices [ 0 ] , control: { type: "select" } } , width: { options: [ "" , "full" ] , control: { type: "select" } } , // addon-action で定義していた関数 change: { action: 'changed' } } } ; const Template = (args, { argTypes } ) => ( { components: { ButtonGroup } , props: Object .keys(argTypes), template: ` <div : class = "'theme-'+device" > <div> <h2>デフォルト</h2> <div>アイコン+ラベル</div> <bbq-button-group :tag= "tag" :items= "iconsAndLabels" @change= "({index}) => {this.selected = index; change(index)}" :selected= "selected" :width= "width" /> <div>アイコン</div> <bbq-button-group :tag= "tag" :items= "icons" @change= "({index}) => {this.selected = index; change(index)}" :selected= "selected" :width= "width" /> <div>ラベル</div> <bbq-button-group :tag= "tag" :items= "labels" @change= "({index}) => {this.selected = index; change(index)}" :selected= "selected" :width= "width" /> </div> <div> <h2>カスタムUI</h2> <bbq-button-group :tag= "tag" :items= "['a','b', 'c', 'd']" @change= "({index}) => change(index)" :selected= "selected" :width= "width" > <template v-slot= "{items, change}" > <button v- for = "(item, index) in items" @click= "change(index)" > {{ item }} : {{ index }} </button> </template> </bbq-button-group> </div> </div> ` } ); export const Default = Template.bind( {} ) Default.args = { // Default コンポーネントに与える Props selected: 0, tag: "ul" , icons: [{ icon: 'list' } , { icon: 'grid' }] , labels: [{ label: 'ドラッグで並び替え' } , { label: '数値で並び替え' }] , iconsAndLabels: [ { icon: 'list' , label: 'リストで並び替え' } , { icon: 'grid' , label: 'グリッドで並び替え' } , { icon: 'attentionCircle' , label: '念で並び替え' } , ] , } ; なお、 devices の定義は const devices = ['pc', 'sp'] です。 Storybookv6の変更点 上記、新旧ファイルの変更点を抜粋してコードを比較します。 Storybook 上のコンポーネント名 Storybook で表示されるコンポーネント名の定義方法の変更点です。 // v5 import { storiesOf } from '@storybook/vue' const buttonStories = storiesOf( 'Elements/ButtonGroup' , module) // v6 export default { title: "Elements/ButtonGroup" , // ... } v6 では @storybook/vue を import する必要がなくなりました。その代わりに、default export するオブジェクト内にコンポーネントのメタ情報を記述します。 表示するコンポーネントを指定 コンポーネントを指定する箇所も変更になっています。 // v5 import { storiesOf } from '@storybook/vue' import { ButtonGroup } from '../../elements/buttonGroup/button-group.vue' const buttonStories = storiesOf( 'Elements/ButtonGroup' , module) buttonStories.add( 'ButtonGroup' , () => { return { components: { ButtonGroup } , // ... } } ) // v6 import ButtonGroup from './ButtonGroup' export default { // ... component: ButtonGroup, } v6 ではコンポーネントを default export するオブジェクトのプロパティに追加します。 parametersを定義する箇所の変更 v5 では add メソッドの第三引数だった parameters の定義箇所が、v6 では default export するオブジェクトに変更になりました。ここではマークダウンファイル README.md を v6 で読み込める書き方を紹介します。 // v5 import { storiesOf } from '@storybook/vue' import { withInfo } from 'storybook-addon-vue-info' import README from '../../elements/buttonGroup/README.md' const buttonStories = storiesOf( 'Elements/ButtonGroup' , module) buttonStories .addDecorator(withInfo) // decorator で addon-vue-info を活用している .add( 'ButtonGroup' , () => { ... } , { notes: README } ) // v6 import README from './README.md' export default { // ... parameters: { notes: { README } , docs: { extractComponentDescription: ((_, { notes } ) => notes?.README) } } , } v6 では Storybook 上の Docs タブで README.md を表示できます。このため、 @storybook/addon-notes 、 storybook-addon-vue-info は不要になります。 今回は v6 のコンポーネント内で定義しましたが、 preview.js に以下のように記述すると Storybook の全コンポーネントに parameters が追加されるため、 docs を各ファイルで記述すること避けられます( 「Migrating from notes/info addons」 )。 // preview.js import { addParameters } from '@storybook/client-api' ; addParameters( { docs: { extractComponentDescription: ((_, { notes } ) => notes?.README) } , } ); なお、今回は既存資産を活かすためにマークダウンファイルをそのまま使いましたが、 MDXを用いる方法も公式で紹介されています。 addon-knob の書き換え v5 では addon-knob を使うと Storybook 上でコンポーネントに与える値を画面上で変更できました。 v6 では addon-essentials に含まれている controls を使えば同様のことができます。 以下では knob の number 、 select 、 text 関数を書き換えています。 // v5 import { number, select, text, withKnobs } from '@storybook/addon-knobs' import { devices } from '../../values/Devices' buttonStories. add( // ... data() { return { device: select(`device`, devices, 'pc' ), selected: number( 'selected' , 0), width: select( 'width' , [ '' , 'full' ] ), } } , props: { tag: { default : text( 'tag' , 'ul' ) } , } , } ) // v6 import { devices } from '../../values/Devices' export default { // ... argTypes: { // select 関数で作成していた値 device: { options: devices, defaultValue: devices [ 0 ] , // 初期値の設定 control: { type: "select" } // この行は省略可能 } , width: { options: [ "" , "full" ] , control: { type: "select" } // この行は省略可能 } , } ; const Template = (args, { argTypes } ) => ( { ... } ); export const Default = Template.bind( {} ) Default.args = { selected: 0, // number 関数で作成していた値 tag: "ul" , // text 関数で作成していた値 } ; select 関数の代わりになる control: { type: "select" } で定義した値は、 defaultValue で初期値を設定できます。 なお、 export default の中で定義している device 、 width は以下のように Default.args で定義することも可能です。 // v6 Default.args = { device: devices, width: [ "" , "full" ] , selected: 0, tag: "ul" , } ; (参考: Dealing with complex values ) addon-actions の action 関数の書き換え addon-actions を使ったダミーのコールバック関数の定義方法も変更になりました。 // v5 import { action } from '@storybook/addon-actions' buttonStories .add( // ... () => { // ... methods: { change: action( 'changed' ), } , } } ) // v6 export default { argTypes: { change: { action: 'changed' } } } ; (参考: addon-actions ) ただし、以前のように action 関数を用いても問題なく動作するため、書き換えは必須ではありません。 コンポーネントに渡すデータの定義の変更 v5 で記述していた data, props, computed で定義していた値を Storybook の画面上で自由に変更したい場合は、 argTypes や args に集約可能です。 // v5 buttonStories .add( // ... () => { // ... // Data data() { return { device: select(`device`, devices, 'pc' ), selected: number( 'selected' , 0), width: select( 'width' , [ '' , 'full' ] ), } } , // Props props: { tag: { default : text( 'tag' , 'ul' ) } , } , // Computed computed: { icons() { return [{ icon: 'list' } , { icon: 'grid' }] } , labels() { return [{ label: 'ドラッグで並び替え' } , { label: '数値で並び替え' }] } , iconsAndLabels() { return [ { icon: 'list' , label: 'リストで並び替え' } , { icon: 'grid' , label: 'グリッドで並び替え' } , { icon: 'attentionCircle' , label: '念で並び替え' } , ] } , } , // ... // v6 export default { // ... argTypes: { // addon-knob の select で定義していた変数 device: { options: devices, control: { type: "select" } } , width: { options: [ "" , "full" ] , control: { type: "select" } } , } ; // ... export const Default = Template.bind( {} ) Default.args = { selected: 0, tag: "ul" , icons: [{ icon: 'list' } , { icon: 'grid' }] , labels: [{ label: 'ドラッグで並び替え' } , { label: '数値で並び替え' }] , iconsAndLabels: [ { icon: 'list' , label: 'リストで並び替え' } , { icon: 'grid' , label: 'グリッドで並び替え' } , { icon: 'attentionCircle' , label: '念で並び替え' } , ] , } ; ただし、data や props、computed をそのまま残すことも可能です。その場合、 props 以外は GUI 上で値を変更できません。Storybook の GUI 上で変更したい値であれば、args で記述すれば良いと思います。 BASE BBQ の.Storybook では様々なパターンがあるため、まずは v6 の書き換えを優先しています。このため、data 等で定義している値は一旦 args に集約し、コンポーネントごとの細かい調整は個別に対応する予定です。 以上のパターンで BASE の BBQ で作成された Storybook コンポーネントの大抵のケースを網羅しています。 その他、より詳しい変更点は、 Storybook 6 Migration Guide をご覧ください。 addon について v6 で利用可能な Essential addons には、今までの主要な.addon の機能がまとめられています。 Docs Controls Actions Viewport Backgrounds Toolbars & globals Storybook で開発するにあたり、 @storybook/addon-essentials は開発体験を向上させてくれるため、v6 からは必須といっても過言ではないでしょう。 ここでは、先程紹介した例で使用している addon のみ取り上げます。 addon-knob v6 では deprecated addon-essentials の controls を代わりに使う addon-info knob と同様に v6 では deprecated addon-essentials の docs を代わりに使う addon-action v7 で deprecated になる予定 v6 ではまだ使えるが、可能なら control に置き換えると次のバージョンアップがスムーズになる v6 で addon-action を使いたい場合は、以下のように記述すれば OK です。 const Template = (args, { argTypes } ) => ( { // ... template: `...`, methods: { change: action( "changed" ), } } ); 表示を確認する v6 の書き方に変更したコンポーネントを実際にStorybook で表示すると以下のようになります。 StorybookのButtonGroup ンポーネント control で値を変更して様々な props の表示ケースを確認できるようになりました。 また、「Docs」というタブをクリックすると README が表示されています。 StorybookのButtonGroupコンポーネントのDocs これで v6 への書き換えが完了しました。 Storybook v6 で向上した開発体験 v5 と異なり、v6 では以下の点で開発体験が向上しました。 コンポーネントに与えるデータを Vue の外(args, argTypes)で定義できる args として定義した値は addon-knob を使わなくても Storybook 上で値を書き換えられる 上記の例では取り上げていませんが、args を変えることでコンポーネントのバリエーションを容易に作成できる( using-args ) インストールする addon の数や、stories ファイルのボイラープレートが減った おわりに 今回は Vue2 + Storybook v5 の環境で Storybook を v6 にアップデートする詳細な方法を紹介しました。 Storybook のバージョンアップにあたり本記事の内容が参考になれば幸いです。 多くの方はお気づきだと思いますが、v5 から v6 への書き換えといってもパターンが決まっています。 このため、手順さえ分かってしまえばプログラムで機械的に置換するだけで対応できます。 この考え方をもとに、TypeScript Compiler API を使ってメタプログラミングで v5 のコンポーネントを v6 に書き換えたので、次回の記事でその方法をご紹介しようと思います。
アバター
この度は、5/29(土)にオンラインで開催された PHP カンファレンス沖縄 2021 にゴールドスポンサーとして協賛し、また 4 名のメンバーが登壇しました。 登壇者 4 名から発表内容の補足など、PHP カンファレンス沖縄 の参加レポートをお届けします! phpcon.okinawa.jp 発表内容と補足 杉浦のセッション内容について こんにちは!BASE 株式会社でバックエンドの開発をしている杉浦( yutakasugiura )です。この度、PHP カンファレンス沖縄 で、BASE のスポンサートークとして「変化する時代のエンジニアリング」について発表させていただきました。 発表内容の意図 PHP に関するカンファレンスでしたが、スポンサーセッションの発表内容は PHP 以外のことでも良いとのことでしたので、広い意味でエンジニアリングについてお話ししました。普段の業務におけるエンジニアリングとは PHP のような言語が話題の中心ですが、せっかく頂いた機会なので、より大きな視点でエンジニアリングを語ることにしました。 登壇資料 発表内容の要旨 企業活動におけるエンジニアリングはマーケットニーズに従属的です。2010 年代に CakePHP がフレームワークとして支持されたのも「便利な web サービスを早く使いたい!」というニーズが背景にありますし、これほど AWS が広まった理由も「いつアクセスが爆発的に増加するかわからない!」という課題への対処として有効だったからです。スマホの普及によって、web サービスへのニーズが 2010 年代を通じて爆発的に増加したからこそ、これらのエンジニアリングが時代の潮流となったと言えます。 一方で、注意しなければならないことは、エンジニアリング単体で物事を考えると、ニーズにそっぽを向いてしまう可能性があると言うことです。純粋な研究開発は別として、企業活動の最終目的は、顧客にサービスを提供することにあり、その手段としてエンジニアリングがあります。考えてみれば当たり前の事実ですが、当たり前だからこそ、この単純な事実を忘れやすいというのも、また真理です。 web 業界は誕生してまもないこともあり、ニーズと技術が乖離してしまうという経験値が少ない業界です。ですが、歴史を広く見渡せば、時代の変化によって、顧客ニーズとエンジニアリングが乖離してしまい、ビジネスとしては幸せにならなかった例は枚挙にいとまがありません。 今回のセッションでは、1980 年代の DRAM(ダイナミック・ランダム・アクセス・メモリー)におけるエンジニアリングの失敗例を挙げましたが、1 つの技術に精通するエンジニアほど、ニーズに乖離してしまうというのは往々にしてあります。科学技術の世界ではパラダイムシフト、ビジネスの世界ではイノベーションのジレンマがよく知られますが、専門家であっても、常識の変化を機敏に感じ取ることは容易ではないのです。 2021 年の時点で web 業界は成長産業ですし、2040 年ぐらいまで急成長が続くことは、ほぼ確実な未来です。ですが、この疑いない状況が未来永劫、永遠に続くのか?と問われれば、その回答は「その限りではない」と断言します。 だからこそ、エンジニアは細心の注意を払って「このエンジニアリングによってどんなニーズが満たされるのか?」「顧客ニーズはどのように変化していくのか?」を、常日頃から考え続けなければならないのでしょう。 感謝 技術に関するカンファレンスの登壇は初めての経験でした。貴重な体験をさせていただき、関係者の皆様に御礼申し上げます!ありがとうございました! 炭田のセッション内容について こんにちは!BASE 株式会社の炭田( @tac_tanden )です。今回の PHP カンファレンス沖縄 2021 にて「PHP で throw しない例外ハンドリング」をテーマに 30 分間発表させていただきました。 セッション内容について PHP で throw せずに例外ハンドリングを行う事例をご紹介する前に、そもそも例外とは何かという部分を整理したり、PHP 以外の言語でどのように throw しないで例外ハンドリングを行っているのかを説明させていただいました。 特に、前半の例外の概念を説明するのに苦労し、発表でもわかりにくい部分があったかと存じます。例外については様々な意見や見解があるかと思いますが、みなさまの議論や考えの整理にこの発表が少しでもお役立てれば幸いです。 カンファレンスでの発表について 今回自分は初めてのカンファレンスの発表だったのですが、発表を通して色々な方と関わることができ、とても楽しいカンファレンスでの発表体験になりました! 発表中に twitter で様々なコメントをいただいたのも、すごく嬉しかったです。今後も機会があればぜひ発表したいと思いました。この場をお借りして、運用の皆様、参加者の皆様にお礼申し上げます。ありがとうございました! セッション動画はこちらになりますので、興味のある方ぜひご覧いただけると嬉しいです! https://www.youtube.com/watch?v=kOhsJCW9YIE&t=14093s 大津のセッション内容について 2021 年 2 月から BASE 株式会社に入社しました大津( @cocoeyes02 )です。PHP カンファレンス沖縄 2021 では Git のコミットにまつわるトークをさせていただきました。 また、セッションの動画はこちらになります。 コミットへのFBをもらうとGood 最後のスライドでこのセッションは自戒が含まれていると書いていますが、それは私自身過去にコミットによる FB をたくさん受けたことがあるからです。 例えば NG 例として挙げたコミットメッセージ「一旦コミット」「PR で指摘したので反映」「バグを直した」は、全て過去に僕が書いたコミットメッセージだったりします。このコミットメッセージが何故ダメなのか FB をもらって、ようやく理解できるようになりました。 コミットを見るのは他人と言う話をしましたが、リーダブルなコミットを書くことによるメリットを受け取れたか判断するのも他人です。今回のトークの tips を参考にしつつ他人から FB をもらうと、リーダブルなコミットを書けているのか判断できるので良い思います! スタッフの皆様ありがとうございました! 最初のオープニングから、最後の懇親会まで楽しく過ごすことができました! 今年はコロナ渦ということもあって、直接沖縄の会場へ向かうことは叶いませんでしたが次回は是非現地にて登壇したいと思っています! 東口のセッション内容について はいさい!BASE BANK 株式会社の東口 ( @hgsgtk )です。当カンファレンスでは、プロダクト開発のため E2E テスト環境を整備してきた中で、泥臭くなりがちなテスト環境・テストデータの考え方と工夫について発表させていただきました。 懇親会等、PHP コミュニティの方とも楽しくコミュニケーションさせていただきました。前回の PHP カンファレンス沖縄では直接沖縄現地に伺い、PHP コミュニティの方と直接話したりソーキそばなどを堪能したり楽しい時間でしたので、また来年・再来年とコロナが落ち着いてきた際に、直接沖縄の会場に伺えればいいなと思います。 最後に 今回弊社は計 4 名のメンバーが登壇して発表する機会をいただき、とても充実した時間を過ごすことができました! また自身の発表だけでなく、多くの方々の発表を通して様々な知識にふれることができ、各々が新たな知見や視点を持ち帰って来れたと考えております。 それもひとえに PHP カンファレンス沖縄実行委員会の皆様のおかげです。心より感謝申し上げます。 それでは、来年もまた皆様にお会いできることを楽しみにしております!
アバター
BASE株式会社Data Strategyチーム兼 Data Platformチームの楊(@wyang)です。 ショッピングアプリ「BASE」では、 前回公開した記事 の通り、商品検索基盤をCloudSearchからAWS Elasticsearch Serviceへ移行しました。 この記事では、レスポンス速度改善と検索精度改善をメインにご紹介します。 1. レスポンス速度改善について 1-1. ElasticsearchのProfile API この Profile API を利用すると検索クエリを分析し、どんなクエリが発行されたのか、どのくらいの処理時間がかかっているのかなどを簡単に知ることが出来ます。 GET items/_search { "query": { "bool": { "filter": [ { "range": { "date_field": { "gte": "now-180d" } } } ] } }, "profile": "true", // profile: trueで有効になる "size": 0 } BASEではAWS Elasticsearch Serviceを使っている都合上、X-Packの Search Profiler を利用することはできませんが、こちらではより手軽にパフォーマンス分析をすることができます。 1-2. 改善された変更点 1-2-1. 時間Rangeクエリの丸め込み BASEでは長期間でログインされていないショップの制御など、検索の要所要所でdate型データに対する絞り込みをしています。 GET items/_search { "query": { "bool": { "filter": [ { "range": { "date_field": { "gte": "now-180d" } } } ] } } } しかし、 Elasticsearch公式のドキュメント )にもある通り、時刻によるフィルタリングはfilter cacheに載らないため、毎回絞り込みが行われレスポンス速度が低速になる傾向がありました。 ドキュメントのガイドのように時刻を丸め込むことにより、filterクエリがfilter cacheの対象となり、レスポンス速度が大幅に改善されました。 GET items/_search { "query": { "bool": { "filter": { "range": { "date_field": { "gte": "1600819200000" } } } } } } 1-2-2. 必要最低限のフィルターを利用、不要なドキュメントの削除 Profile APIで時間がかかるフィルターにあたりをつけた後、検索インデックスで管理する必要がないフィールドとフィルターを削除しました。この部分は、インデックス更新バッチ側で制御しており、不要なドキュメントをそもそも登録しないなどの対応で、インデックスサイズを軽量化させました。結果として、クエリの軽量化にも繋がり、レスポンス速度が改善されました。 1-2-3. search_analyzerの使用 シノニム辞書を利用した検索を実現するために、インデックス時と検索時でanalyzerを分けて管理しました。 詳しい内容は Elastic社公式の記事 にもありますが、 インデックスサイズに影響が出ない。 用語の統計全体は同じに保たれる。 同義語ルールを変更するにあたり、ドキュメントの再インデックスは必要ない。 など、大きなデメリットはなく、こちらの設計を採用しています。 2. 精度改善について 2.1. A/Bテスト運用 Firebase Remote Config による検索の A/Bテストを実施しています。アプリのアップデートを配布せずとも、Remote Configのコンソール上で設定を修正するだけで、指定の比率でテストサイズを設定できるようになりました。 2-2. A/Bテストの評価指標 実際に改善に繋がったかどうかを以下の指標で判断しています。 商品閲覧につながった検索率 商品閲覧につながった検索のうち、商品閲覧位置(表示順位)の最小値の平均値 検索結果を1件も返せなかった率 検索結果のうち上位k件の商品閲覧率(SERP@k) 商品お気に入りにつながった率 検索実行からレスポンスを返すまでの時間 閲覧率だけでなく他の評価項目(商品の多様性など)も含めました。 2-3. これまでに効果があった変更点 2-3-1. 複数語検索時の絞り込み条件の緩和 ユーザーが複数語で検索した場合、語順を考慮する検索(type: phrase)と、語順を考慮しない検索(type: best_fields)では、語順を考慮した検索のほうが成績が良いことがわかりました。ただし、一部のキーワードの結果に対してはヒット件数が0件になってしまうため、ヒット件数が一定件数以下になった場合に、条件を緩和して語順を考えないクエリで再検索をするようにしています。結果、CTR改善に繋げることができました。 2-3-2. function_score BASEのおすすめ順では、ユーザが検索したキーワードに該当するドキュメントをスコアリングする際にfunction_scoreを利用しています。function_scoreにより、ショップ情報や、商品情報の複数の要素に対して、それぞれ重みづけをしたオリジナルのスコアを設定することができます。 GET items/_search { ... "function_score": { "functions": [ { "field_value_factor": { "field": "score_field_A" // スコアに関するfield } }, { "gauss": { "date_field_B": { // 日付に関するfield "origin": "now", "scale": "180d", "decay": "0.5" } } } ], "score_mode": "multiply", "boost_mode": "multiply" } } 上記のクエリでは、score_field_Aとdate_field_Bのスコアを乗算してスコアリングをしています。 gaussを利用する場合では、origin(now:現在時刻)を基準として、scale(180d: 180日)の日付差の地点に対して、decay(0.5)の減衰をする正規分布のスコアを設定することができます。 その他 日々登録される商品データのサイズや、更新反映までに求められる時間を考慮し、 Bulkで投入するリクエストデータのサイズ refresh_interval に対する調整をそれぞれ行い、効率的にドキュメントを更新できる設定にしています。 おわり 今回は移行後におけるレスポンス速度改善と検索精度改善についてご紹介しました。AWS Elasticsearch Serviceへの移行を行うことで継続的な検索性能の改修、改善をしやすい環境を作ることができました。これからさらなる改善を行なっていきたいと思います。
アバター
こんにちは、BASE株式会社Data Strategyチームの杉です。 ショッピングアプリ「BASE」では、検索にAmazon Cloudsearchを使用していました。今回、検索基盤をAmazon Elasticsearch Service(以下、ES)に移行し、Data Strategyチームで管理をする方針にしました。 この記事では商品が更新された際などにどのように検知し、データをESにいれるようにしたかなど、基盤の部分をメインにご紹介をします。 1. 背景 検索は新しいショップに出会うきっかけを作ってくれたり、探していた商品をいち早く見つけることができることができます。 そのため、検索機能はどのECサイトなどでも見かける存在であり、活用している人も多いのではないでしょうか。 例えばショッピングアプリ「BASE」の検索はこのような画面になっています。 このようにさまざまな便利さをもっている検索機能ですが、ショッピングアプリ「BASE」では継続的な検索性能の改修、改善ができていないという問題がありました。 これらの課題に対し、今回検索基盤の移行を行うことで検索性能改善への第一歩を進めました。 2. 新基盤の移行について 今回の移行はAmazon CloudsearchからESへデータを移行するだけに見えます。 しかし、内部的には管理をData Strategyチームに移行するため、商品が更新された際に検知、データの取得や同期など全体的に新しく作り直す必要がありました。 また、実際に移行計画を進めていくと、様々な問題の壁に当たりました。 例えば Data Strategyチームの管理に変えるために参照するデータベースを変える必要がある データを全てロードし直す必要がある 商品更新時にできる限りリアルタイムに更新をしたいがどう実装するべきか 商品情報に関するテーブルがたくさんある などが挙げられます。 これらの課題が存在したため、手探りでlogstashを試したりembulkでデータ同期を試みたりもしながら、現在の基盤を作りました。 2-1. システム構成 今回は商品検索のみの移行をしましたが、検索を行う上で商品情報に関するテーブルは多くあります。 また、これらのテーブルがそれぞれ更新が起きた際にESにもデータを同期させる必要があります。 これらを考慮し、最終的に以下のシステム構成で実装を行いました。 商品情報の更新 定期的に動いているbatchがS3から前回起動情報を取得 データ更新の有無の確認 更新分のデータを取得 ESのデータを更新 S3へ更新後の情報を記録 という流れで動いています。これを短い時間で繰り返すことでリアルタイムに近い間隔でデータを更新することが実現できました。この「短い時間」は定期実行時間を何分毎と設定しているわけではなく、可能な最短時間で動かしています。 S3に入っている前回起動情報はデータ同期をしているテーブルのそれぞれの実行情報を記録しています。このような記録をし、都度取得をすることで急にDBの同期が上手くいかなくなった場合にも、自動で復旧するような仕組みになっています。 さらに、batchはデータの取得からESのデータ挿入まで全てPythonで作りました。このPythonでの実装時にいくつかの細かい設定をしました。 こちらはinsert時のコード例です。 es = Elasticsearch( ..., timeout=timeout # (1) ) retry_count = 0 while retry_count < retry_max: # (2) try : ... es.bulk(body) time.sleep( 1 ) # (3) break except BaseException : ... time.sleep( 1 ) retry_count += 1 (1) timeoutを明示的に書く timeoutを書かない場合、デフォルトの秒数となります。しかし、実際に動かしてみると稀にtimeoutになることがありました。 bulk時にはサイズで区切っていたのですが、商品情報は商品説明文などが長いことがあり、想定よりサイズが大きくなってしまうことがありました。この際にtimeoutが発生してしまっていたのですが、伸ばすことでtimeoutでのエラーはなくなりました。 (2) retry処理をいれる こちらも同様にbulk時の処理で稀に失敗することがありました。 しかし同じデータをもう一度試すと成功することも多かったため、retry処理をいれることで対応をしました。 (3) sleepをいれる 今回、商品情報が更新されたら更新分を全て更新する仕様です。こちらも頻繁に起こることではありませんが、ある時間に大量の商品情報更新がかかることもあります。 その際に短時間に何度もbulkを行うと、内部キューが溜まりすぎてしまうことがありました。上限値を超えた場合、破棄されてしまうため、データが欠損してしまう恐れがあります。 ESの設定で上限値を変更するという手段もありますが、上限値を変えても送る量が上限値を超えないという保証はなかったため、スピードを緩和させることで対応をしました。 このように、とても細かい部分の設定ではありますが、これらの処理をいれることでデータの欠損もなくスムーズにESにデータをいれるためのシステムを実装することができました。 2-2. 初期ロード 通常の商品が追加や更新された際のデータ更新はシステム構成で書いたような内容で行われています。 しかし、今回ESには商品情報がゼロの状態から始めるため、今までのデータを入れ直す必要がありました。 対応策としては、別途batchを作り初期ロード専用の作業を行いました。 初期ロードのbatchは以下のような流れでデータをいれるようにしました。 メインの商品テーブル以外は一気に全部取得 user_idもしくはitem_idごとにデータをまとめる 商品テーブルをID区切りでデータを取得し、2のデータとjoin これをIDを変えながら繰り返すことで今までのデータを全部入れました。 通常時のESのinsertはupdateもしくはdeleteを使用していますが、初期ロードではindexを使い少しでも速くなるようにしました。 2-3. 例外テーブル システム構成でも書いたように、今回商品に関するテーブルは多くあります。 テーブルに何かしらの変更に起きた際に更新をかけるという方法でうまくいかないテーブルも存在しました。 具体的には batchで計算した結果を格納するため同時刻に何十万、何百万のレコードがinsertされるテーブル 更新頻度がとても高く、約1分間に紐づくレコードが何十万、何百万存在するテーブル があります。 これらのテーブルに関しては、上の処理とは別の処理も加えてテーブル内容の同期を行なっています。 2-3-1. batch計算結果を格納しているテーブル こちらに関しては、常にinsertが走っているわけではなく、dailyやweeklyといった頻度であったことから、insert時に全てのデータを更新するのではなく、少しずつ更新をする方針にしました。 batchでの計算結果の追加 定期的に動いているbatchがS3から前回データを取得 ID区切りでデータを確認 データを取得 前回データとの差分を確認 差分発生データのみESを更新 S3へ更新後の情報を記録 大まかな流れは通常動いているシステムと同様ですが、S3に前回のデータを保存している点と更新分全てを取得するのではなく、ID区切りで他の更新の妨げにならない量に抑えている点が異なるポイントとなります。 このような工夫をすることで、他のテーブルの更新にも影響がでず、スムーズに更新をすることが可能になりました。 2-3-2. 更新頻度が高いテーブル 更新頻度が高いテーブルの問題点としては、同時刻に更新しなくてはいけないレコードが多く、best effortでの更新を行なっているとどんどん詰まっていき、全体の更新が遅くなるということがありました。 更新レコードが多く、更新も常に起こっているためbatch計算結果テーブルのようにIDで区切って少しずついれるようなこともできませんでした。 検討の対象となった更新頻度が高いテーブルでは、多くの情報が入っており、少しの更新でもレコード全体に更新がかかってしまっていました。 そのため、必要な情報のみをS3へ保存し、更新が起きた際にS3の情報と比較をし、必要な情報に更新が起きた際のみ、ESのデータも更新をする方法にしました。 S3に前回情報を保存し、毎回取得することはデータ更新の速度に影響してしまう可能性もありましたが、幸いにも本当に更新すべきデータがかなり減ったため、速度アップにつながりました。 おわり 今回は移行後の基盤についてをメインにご紹介しました。 このように実装を行い、現在のショッピングアプリ「BASE」では移行後のシステムで稼働をしています。 実際には基盤完成後にAPIのresponse timeが遅い問題の対応や、CTRの改善などを行なっています。こちらの詳細については来週ご紹介させていただきます。 移行を行うことで継続的な検索性能の改修、改善をしやすい環境を作ることができました。これからさらなる改善を行なっていきたいと思います。
アバター
はじめに こんにちは。BASEのCSEチームの秋谷です。 CSEチームは社内業務の効率化と財務の信頼性担保することを専門とするチームとして開発や社内の整備を行なっています。そんなCSEの取り組みを紹介できればと思います。 CSEについて詳しくはこちらをご覧ください devblog.thebase.in BASEショップの売上金の担保とJ-SOX対応 BASEではショップの売上を一時的にプラットフォーム側が預かっており、申請があった段階で売上金を引き出せるようになっています。 そのため、ECプラットフォームはショップに対して、以下のことを担保することが大前提です。 データが適切であること 発注、決済データの適切な記録がされていること 上記データから作成される、売上計上のデータの作成が適切であること 集計範囲、抽出がただしくおこなわれていること BASEは2019年12月に上場したこともあり、J-SOXへの対応を求められるようになりました。これにより、より厳格にショップの売上金に対してデータの透明性を担保し、またそれに対して監査を受ける義務が発生しました。 J-SOXとは 財務報告に係る内部統制報告制度の概要 J-SOXにおいては、経営者は財務報告に係る内部統制を構築する責任を有しており、その有効性を自ら評価し、外部に対してその結果を報告することが求められます。また、財務報告に係る内部統制の有効性に関する経営者の評価を外部監査人が監査することによって、その評価の適正性を確保する制度となっています。 監査人による内部統制報告書の監査 J-SOXでは、財務報告に係る内部統制の有効性に関する経営者の評価を監査人が監査することによりその適正性を確保することとされています。また、内部統制監査は、原則として財務諸表監査と同一の監査人が実施することとされ、内部統制監査報告書は財務諸表監査報告書と合わせて作成することが原則とされています。 参照: https://www.pwc.com/jp/ja/knowledge/ipo-guideline/j-sox.html ショップの売上金の検証と経理業務の負担 店舗預かり金について BASEではショップの売上金を店舗預かり金と称しています。この店舗預かり金に対して経理チームがデータを検証し、妥当性を担保しています。 当初は売上データを取得するのに時間がかかってしまったり、日々動いているトランザクションに対して常にSQLを実行しているためキャンセルなどが発生した場合に再集計すると過去の結果が変わってしまったり、Excelでデータの確認作業を行なっていたためExcelが重くなり開けなくなるなどの物理的な問題もありました。 また、売上金を担保するためにBASEシステム上のデータと各決済データとの突合しなければなりませんが、これにもかなりの時間がかかっていました。 reportシステムの構築 そこでCSEでは上記の問題を解決するために、BASEのデータ集計やレポーティングのためのシステム(以下reportシステム)を作成しました。 先述の通り、BASEでは各ショップの売り上げを一時的に預かっているため、特に以下の点について詳しくチェックをしています。 店舗預り金のBASEシステムのデータが適切に作成されているか 例:商品の代金、送料、各種手数料が正しく計算されている 店舗預かり金の構成要素に変更はないか 例:新規決済手段の追加(AmazonPayなど) 決済データの突合 例:BASEシステム上のデータとクレジットカードの決済情報が正しいか この売上データの取得やデータの突合をしやすくしたり、構成要素の変更をわかりやすくするために、日次でBASEシステムの本番DBから必要なデータをAmazon Athenaにインポートしています。 reportシステムの構成について次に詳しく説明します。 構築 reportシステム概要 reportシステムの概要はざっくりこんなかんじです。 1. BASEシステムの本番DB(Amazon Aurora)から、必要なデータをAmazon Athenaに日次で取り込む 2. Amazon Athenaに対してSQLを実行して、レポートとなるCSVを出力 reportシステムの構成 report-dataloader(図中の青枠、青矢印) レポートシステムのデータを準備する Auroraのクローン作成機能でテンポラリDBを作成 Embulkを用いて、テンポラリDBからレポートに関わるテーブル、列のデータをAthenaにロード ( civitaspo/embulk-output-s3_parquet を使用) report(図中の赤枠、赤矢印) レポートのデータを抽出する 店舗預り金に関わるデータを抽出するクエリをAthenaに発行する クエリの結果からCSVを作成する レポートシステム 各決算データの取得機能 各決済データとBASEシステム上のデータを突合するために、各決済システムからデータを取得してAthenaにインポート report-datacollector 各決済の外部のシステムからデータをダウンロードをしてきてS3に保存 決済システムによって取得方法が異なる 各種APIの利用など report-dataloader S3に保存したデータからAthenaのテーブルを作成 report-dataloader reportシステム構築による効果 reportシステムの構築によりAmazon Athenaへクエリを実行することによって、重たいクエリを実行しなくともBASEシステム上で取得していた店舗預かり金と同等のデータが取得可能になり、日次でのデータの突合もし易くなりました。 BASEにはさまざまな決済方法があるため一概には言えませんが、一番トランザクション数の多い決済で約1人/日分の工数の削減出来ています。クエリの実行時間は決済方法に関わらずほぼ同一なので、トランザクション数が多い決済ほど工数が削減されていることになります。 終わりに 上記に挙げた以外にも、BASEではreportシステムには経理業務を改善するための細かな機能を随時追加しています。 ここに挙げた例はCSEの活動の一部です。CSEは Corporate Solutions Engineering の略であり、社内業務のより良く改善できればと思っています。 CSEチームでは、エンジニア力で社内業務の効率化やJ-SOX対応をリードできるコーポレートエンジニアを募集しております。 下記のリンクから気軽にご連絡ください。 https://open.talentio.com/r/1/c/binc/homes/4380
アバター
こんにちは。BASE BANK株式会社 Dev Divisionにて、 Software Developerをしている永野 ( @glassmonkey ) です。 今回は弊社でブロンズスポンサーとして協賛しました。 PHPをメイン言語として使用しているBASE社と異なり、BASE BANK社ではGoをメイン言語として使っているので、今回は初めてBASE BANK社としてスポンサードさせていただきました。色々至らぬこともありましたが、この場を借りてお礼を申し上げます。 登壇の内容に関してですが、業務ではなく趣味で触ってるFlutterネタです。勢いでProporsalに出したら通していただいたので、趣味全開な形になりました。 GoConference2021 Springについて gocon.jp https://gocon.jp/ Go Conferenceは半年に1回行われるプログラミング言語Goに関するカンファレンスです。 今回は初のオンライン開催という記念すべき回でもありました。 本記事のGopherアイコンのライセンスは以下の通りです。 github.com The Go gopher was designed by Renee French. ( http://reneefrench.blogspot.com ) The gopher stickers was made by Takuya Ueda ( https://twitter.com/tenntenn ). Licensed under the Creative Commons 3.0 Attributions license. 感想と振り返り 発表内容について 今回は個人の趣味でハマってるFlutterネタを発表させていただきました。 私自身はGo歴1年ぐらいのまだまだ初心者ではありますが、機会をいただけてよかったです。 speakerdeck.com 発表の様子はこちらです。 youtu.be 発表についての振り返り 話のベースとして触れた gomobile が何かと不安定なので、サンプルアプリを作る際は色々とハマりました。それも登壇ネタになったかなと考えると美味しい思いはできた気がします。 その後の懇親会でも、色々Firebaseの話ができたりGoのイベントなのにモバイルアプリの開発談義ができて大変楽しい時間を過ごせました。 また、「FlutterとGoのAPIの通信する」アプリを予想されてた思われてた方も居たようで、いい意味で予想をひっくり返せたのは良かったです。改めてGoの自由さを紹介できた点は良かったとおもっております。 時間配分を少し間違えて後半駆け足になってしまったのは反省として今後の登壇には活かしたい所存です。 アドリブ気味ではあったのですが、発表時にオープンクエスチョンを投げることができたのも満足でした。 懇親会 Remoで実施されており、前半はテーブルごとで雑談、後半はみんなでわいわいクイズ大会な流れでした。 remo https://remo.co/remo-101 特に @tenntenn さんの出題した go quiz めちゃくちゃ難しかった思いです。一番印象に残ったのはgo 1.16から入るembedに関してのクイズですね。勉強になりました。 これコンパイル通るか… embedが適応されるケース。変数の中身にファイルの内容が格納される。 //go:embed hoge.json var message string ただのコメントアウト(//の後にスペースがあるのでembedではなくコメント扱いになる) // go:embed hoge.json var message string 感想と謝辞 発表時の進行に関しては事前にリハーサルや説明を @micchiebear さんを始めとした運営の皆様の厚いフォローがあったので当日はスムーズに行うことができたので発表に集中できました。運営の皆様ありがとうございました。 また、スポンサードの窓口としても関わらせていただいたのですが、運営の皆様に手厚くフォローしていただいてありがとうございました。 https://gocon.jp/ に自社のロゴが載っているとわかった瞬間感無量でした。 私自身Go歴は1年ぐらいなので全然わからないことだらけではあったのですが、 参加された方々の Go Love が伝わってきてもっと私自身頑張ろうという気持ちにさせていただきました。 特に登壇資料を業務時間を割いて作ってる中フォローしてれた同僚にも感謝です。 宣伝 今回は趣味ネタでFlutter×Goを触れさせていただきましたが、 私たちの普段の業務はGo, Python, PHPを中心に、フロントからインフラまでを一気通貫で開発しています。 また、開発だけでなく・機能をグロース・サポートまで担当します。 そんな開発スタイルに興味あるぞって方は永野( @glassmonkey )にDMを送っていただくか、 下記のリンクから気軽にご連絡ください。 open.talentio.com 最後までご覧いただきありがとうございました。
アバター
こんにちは、デザイナーの河越です。 「BASE」では今年の2月に、注文時に購入者に送られる購入完了メールをリニューアルしました! baseu.jp これまで「BASE」から購入者に送られるメールはほとんどがテキストメールでした。 各ショップが工夫してネットショップ上でブランドの世界観を表現しているにも関わらず、購入者が受け取るメールがショップの世界観とマッチしておらず購入者に違和感を与えてしまうという課題がありました。 そこで今回、「BASE」から送られているあらゆるメールをHTMLメール化する「メール改善プロジェクト」が始まりました。 まず第一弾として購入完了メールがHTMLメール化されましたが、どのような経緯でリニューアルに至ったのか、デザインで工夫した点などを振り返りたいと思います! 購入完了メールのHTMLメール化の経緯 「メール改善プロジェクト」は、デザイナー主導で始まりました。 「BASE」の体験をより良くできそうな箇所があれば、デザイナーが主体的に課題を解決していける環境があるのはBASEの良いところだなと思います☺︎ プロジェクトのキックオフが終わったら、まずは「BASE」から購入者やショップオーナーに送信されているすべてのメールをリストアップしました。 購入完了メールや発送完了メール、お問い合わせ完了メールなど、購入者に送信されているものだけで32種類ものメールがあることがわかりました。 メールを洗い出したあとには、ショップオーナーや購入者の声を一番近くで聞くCS (Customer Support) チームに、メールに関してどのような問い合わせが来るかをヒアリングしました。 その結果「 ブランドのお店で注文をしたのに"BASE"からメールがきて混乱した 」「 銀行振込決済を選択して注文したあと、どこに振り込めば良いのかわかりづらかった 」と感じている購入者がいることがわかりました。 CSチームからのヒアリングを経て 購入者の購入体験を向上させること ショップのブランドブランドの世界観を壊さないこと を念頭に、まずは購入完了メールをリニューアルすることに決めました。 完成したデザイン ショップロゴや、設定しているSNSを表示 ショップがロゴを設定している場合、メールのヘッダー部分にショップロゴが表示されるようになりました。 これにより「"BASE"ってサービスからメール来たけどなぜ?」という戸惑いをなくすことや、ショップの世界観と大幅に解離したメールをなくすことで、注文時から一貫したショップでの購入体験を購入者に提供することを意識しました。 また、フッターパーツに設定しているSNSやショッピングアプリ「BASE」への導線を配置することで、ショップの普段の活動や商品について購入者が知りやすくなりました。 購入した商品がファーストビューに表示させることで、購入直後のわくわく感も醸成できたら良いなと思っています😆 レコメンドエリアを追加 そのショップで販売されているその他の商品を画像付きで表示させることで、リピート率の向上やショップの売上増加を狙う導線を新たに取り入れました。 まとめ 購入完了メールをリニューアルして早2ヶ月。 期待通り「その他の商品」からの購入が増えています。 また、メールが見やすくなったことで社内からも好評をいただいています! これまで以上にカスタマーサポートやカスタマーサクセスの声を聞きながらデザインを作ったことや、影響範囲の大きい品質向上に関われたことで達成感が大きく、このプロジェクトにアサインされてよかったなと思います。 また、「BASE」から送られているHTMLメールがほとんどなかったため、コンポーネントを1から考えて作れる楽しさも感じれました。 購入完了メールは「BASE」を利用しているwebのネットショップからの購入でも、ショッピングアプリ「BASE」からの購入でも見られるので、購入のさいにチェックしていただけると嬉しいです😉 今回は購入完了メールをリニューアルしましたが、「BASE」から購入者に送信しているその他のメールもどんどんリニューアルしていきたいと思います。
アバター
集合写真:写真真ん中よりちょっと上にて、左胸にBASEロゴがあるパーカーを着ている大津 こんにちは。Product Dev Divisionに所属している 大津 です。 今回、PHPerKaigi 2021にコアスタッフとして参加しました。私がなぜコアスタッフとして参加したのかという点も含め、カンファレンスの裏側についてレポートしたいと思います! PHPerKaigi 2021とは PHPerKaigi 2021とは、3月26日(金)〜3月28日(日)の期間で開催されたPHP系の技術カンファレンスです。今年で4回目の開催となります。 phperkaigi.jp オフラインカンファレンスだった例年とは違い、今年はニコニコ生放送を使って進行するオンラインカンファレンス形式になりました。 グリーンバックに写っている人が見る生放送用ディスプレイ (グリーンバックに写っている人が見る生放送用ディスプレイ) 弊社もゴールドポンサーとして協賛し、スピーカーや一般聴講者として数名が参加しました。以下の記事に参加レポートが書かれていますのでそちらも是非ご覧ください! devblog.thebase.in 舞台裏ではどんなことをしたの? PHPerKaigi 2021では以下のような仕事がありました。(私が担当したのはこの中の一部です!) プロポーザルの採択 デザインの採択(ロゴ、ノベルティ(ボックス)、Tシャツ、パンフレット、公式サイトなど) ノベルティ(ボックス)の発注、発送 公式サイトのコーディング 放送時に使う動画や録画されたセッション動画の編集 広報、宣伝 生放送の進行 セッション動画を再生するなどの生放送機材操作 動画終了後のアナウンス Ask The Speakerの司会 TL(Twitter)監視、お問い合わせ対応 アンカンファレンスに参加して賑やかす twitter や discord、ニコニコ生放送でわいわいコメントする ノベルティボックスの発送やセッション動画の編集、生放送の進行はオンラインカンファレンスならではの仕事だったかもしれません。 今年のPHPerKaigiはオンラインだけど、ノベルティ、あります! ステッカーやTシャツ、スポンサーさまご提供ノベルティが入ったステキなボックスがみなさんのお手元に! ノベルティ付きチケットは2/28まで販売中! (写真はデザイナーさんによる試作品です) #phperkaigi https://t.co/bjcXHQuoD9 pic.twitter.com/33Qr5hzOea — PHPerKaigi 2021 @3/26-3/28 (@phperkaigi) 2021年2月25日 生放送と録画用の機材 一般聴講者やスピーカーの方々は終始完全オンラインでしたが、大体のスタッフはオフラインで作業していました。 物理的な受付がない分お問い合わせが増えることを予期して、TL(Twitter)監視やお問い合わせ対応の人数を増やすなどの対応もありました。 生放送の進行をするスタッフも、それぞれが聞きたいセッションを休憩時間にしたり、Ask The Speakerでスタッフも質問を投げかけてみたり、スタッフもアンカンファレンスに参加するなど、スタッフ自身が楽しむということも心がけながら良いオンラインカンファレンス体験を目指して裏で動いていました! スタッフが念(?)を送っている時の様子 コアスタッフとして参加し続ける理由 実はPHPerKaigi 2020でもコアスタッフだったので、コアスタッフは2回目となります。 私がコアスタッフとして参加し続けるモチベーションについてお話しします。 いちエンジニアとしてメリットがある コアスタッフとして参加すると、仕事を通して様々なメリットを得ることができます。 例えば、プロポーザルの採択の際に自分がコアスタッフであれば、自分が聞きたいセッションを採択しやすくすることができます。もちろんプロポーザル採択の観点は色々あるため、必ず叶うというわけではありません。ですが、観点の1つとして「スタッフがいち参加者としてこのセッションを聴いてみたいか」があるため、可能性は十分にあります。 またイベントを運営するにあたって様々な仕事をこなすことになるため、イベントの運営力が上がります。カンファレンス運営のノウハウは、規模を問わず他のイベント運営でも活かすことのできる点は多いです。自分がイベントの開催をすることになった時、とても役に立ちます。 他にも、PHPコミュニティに所属しているスタッフと仲良くなれる、スタッフはカンファレンスに無料で参加できるというようなメリットもあります。 お世話になっているコミュニティに貢献したい BASEでは、私たちが使っている技術スタックのイベント・カンファレンス・勉強会への参加を推奨しています。 その理由の一つとして、「自分たちが使っているOSSや技術情報を発信している方々へ貢献できるから」ということを弊社では挙げています。どのような行動が貢献に繋がるのでしょうか?私なりに例を挙げてみました。 聴講者として参加し、当日のイベントの様子を口頭やブログなどで伝えること スピーカーとして登壇し、自分の知見を広めること。 カンファレンススタッフとしてイベントを盛り上げること。 上記のいずれもコミュニティの活性化へと繋がるので、全て立派なコミュニティへの貢献活動だと私は考えています。その中でも私は、PHPerKaigiのカンファレンススタッフとしてイベントを盛り上げるという形で貢献することを選びました。 PHPerKaigiは、私が技術カンファレンスの中で 初めて登壇した イベントです。そのことがきっかけで他のカンファレンスでも登壇することになり、登壇を通じてエンジニアとして成長することができました。 初めて登壇したカンファレンスがPHPerKaigiで思入れがあることと、同じように誰かへ成長の機会を与える側になりたいという想いで、コアスタッフとして参加していました。 最後に 来年もPHPerKaigiが開催されるならば、コアスタッフで参加したいと思っています。参加した皆さんにとって良いカンファレンスであったのであれば幸いです! また、カンファレンススタッフだけでなくスピーカーとして登壇すること、聴講者として参加ブログを書いてイベントの様子を広めていくという形でも貢献し続けていきたいと思います!
アバター
この3ヶ月で行ったBDIの内容を紹介します こんにちは、デザイナーの渡邊です。 今回はBASEのデザインチームが行っている勉強会「BDI」の内容をご紹介したいと思います。 BDIとは? 『BDI』は「BASE Design Inspiration」の略。 2018年の秋頃から活動している、デザイナーがやりたいことを持ち寄って、 デザインに関する幅広い知見をみんなで楽しく学ぶことを目的とした任意参加の社内勉強会です。 BASEのデザイナーであれば、デザイナーだけでなく誰でも参加することができます。 Inspirationの名の通り新たなひらめきにつながる新しいトピックを取り上げることも多くあります。 過去にはBDIのロゴも制作したりしました↓ devblog.thebase.in BASEのデザイナーがどんな活動をしているのか気になっている方に読んでいただきたいのはもちろん、 社内勉強会やワークショップのネタとしてもご活用くださいね! 1月 新年!書き初めで作字を学ぼう 新年ということで、お正月らしいオンライン書き初め会を開催しました。 デザイナーだけでなくいろんな方に参加していただきたかったので、 作字のコツを解説する簡単なLTを冒頭に実施 初心者歓迎ムードを打ち出し! 手書きOK、写真を撮ってアップするだけ などなど、参加ハードルを下げる工夫をしました。 見る専の人も楽しめるように、ファシリテーターの手元のiPadをミラーリングしてリアルタイムに作字が出来上がる過程も見てもらいました。 できた作品はJamboardに貼り付け、みんなで観賞。 褒め言葉の嵐でさながらボディビル大会のようになり、かなり盛り上がりました! 身近なデザイン4大原則を分析してマスターしよう デザインの基本となる「デザイン4原則」を実際に使われている事例から分析するワークショップをしました。 デザインの基本となる「デザイン4原則」を心理学にも触れながら解説するLT 事例を見ながら4原則のどの要素が使われているかを分析 実際にかんたんなワークショップで実践 と、マジメでしっかりタメになる会でした! 丁寧なLTは初心者にもわかりやすく、デザイナー以外の方も「なにが良いデザインなのか」を判断するための大きなヒントになる内容でした。 スライド/LP/パンフレットを観察しながら4原則と照らし合わせることで、レイアウトへの理解がより深まりました。 また経験豊富なデザイナーが「なんとなく」で出来てしまっている情報の強弱も、改めて原則を学び直すことで根拠あるデザインなんだな〜と振り返ることができた会になったと思います! 2月 ノーコード系ツールを触ってみよう コーディングを必要とせずにシステム開発やアプリケーション開発ができるノーコードツールが盛り上がっているということで、BDIの時間でみんなでおさわり会を開催しました。 前半はWebflow, Wix, STUDIOの3つのノーコードツールについて簡単に解説、実践編ではSTUDIOを実際に触りながら、感想や気付きを共有しました。 複数人の同時編集が出来るのは個人的に衝撃でした...!上手く使いこなせば爆速でLPとか作れちゃうんだろうな、といろんな可能性を感じてかなり盛り上がりました。 1時間で架空ブランドのコンセプトを作ってみよう BASEを利用するショップオーナーの仕事を追体験するため、ブランディングのワークショップをやってみました。 競合調査からターゲット決め、ブランド名を作るところまで1時間の短い時間でこなすのはかなり大変でしたが、 事前にmiroに用意したテンプレートを埋めることで(途中かなり急かしたりしちゃいましたが)やり切りました...! ここで制作したブランドコンセプトたちは後のBDIでロゴ制作、ショップ開設ワークショップに活かしていきます。 3月 俺みたいになるな!しくじりLT会 某人気テレビ番組のフォーマットを拝借し、 4名のBASE社員に、仕事や私生活でのしくじりとそれを経て得た教訓をLT形式で発表してもらいました。 今回は試験的に視聴者のslackコメントをリアルタイムで画面上に流してみたのですが、結果かなり盛り上がりました! ツールは こちら を使用させていただきました。 すぐに反応が返ってくるので、リモートでも登壇者が寂しい気持ちにならずに楽しく発表できてました。(しくじりの内容は社内限定なのでお見せできないのですが、涙と笑いなしには見られない素晴らしいものでした) 90秒ドローイングをやってみよう 美術・デザイン系学校でよく行われるドローイング。シルエットや構造を観察する目が身に付くといわれていますが、社会人になってからもやっているという人は少ない...。というわけでBDIでチャレンジしてみました! ドローイングのwebサイト を利用したり、社員の写真を使いながら集中してドローイング、後半はみんなで講評をしました。人によって書き込む箇所が違ったり、立体感の出し方に気付いて短時間で画力が成長している人が現れたりとなかなかためになる1時間でした。単純に絵を書くことは楽しい!と思い出せる機会にもなりましたので、みなさんも是非挑戦してみてください! まとめ 月2回ペースで開催しているBDI。最近はありがたいことに参加者も増えて盛り上がってきました。 紹介した企画は全てオンライン上で行っています。オフィスに出社できずチーム間のコミュニケーションが少なくなってしまいがちですが、こういった勉強会はゆるっとした絡みを作るのにもとても良い機会だと感じています。 これからもためになって楽しい企画を運営メンバーを中心に日々考えていきます。4月以降もお楽しみに!
アバター
BASE株式会社 ServiceDev Payment Group 所属の田仲です。 現在はエンジニアリングマネージャーを担当していますが、以前はサーバーサイドエンジニアとして開発をしていました。その頃の経験を紹介したいと思います。 BASEのエンジニアはPM・ディレクターから要件を聞き、設計を行います。 仕様や設計を元にサーバーサイドエンジニアはサーバーサイドの実装し、フロントエンドに関してはフロントエンドエンジニアが実装しています。 少し前のプロジェクトになるのですが、「送り状データダウンロードApp」の開発プロジェクトを担当した際にサーバーサイドだけではなくフロントエンドの開発にも携わることになりました。 普段、サーバーサイドエンジニアとして開発を行っているエンジニアがフロントエンドの開発も行うことで良い経験になったという話を紹介したいと思います。 送り状データダウンロードAppについて 商品を発送する際にダンボールなどの箱に送り状を貼りますが、出荷件数が多いショップでは1件づつ送り状を印刷して貼るのは手間が掛かり限界があります。手間を解決するために配送会社の集荷を利用することや外部倉庫への委託などの方法はありますが、費用が掛かるためショップの負担になります。 そのようなショップの課題を解決するために配送業者各社が提供している送り状発行システムを利用して送り状を一括印刷できるようにするためにBASEのシステムからCSVデータを出力できるようにする機能になります。 普段の開発プロセス BASEの開発プロジェクトは、サーバーサイドエンジニアとフロントエンドエンジニアがそれぞれアサインされ協働しながら開発を進めています。 今回の送り状データダウンロードAppの開発プロジェクトでは、サーバーサイドとフロントエンドのタスクはざっくりですが下記のようになりました。 サーバーサイド 固定値の登録・更新(データのCRUD) 各配送会社システムに合わせたCSVファイルの出力処理 フロントエンド 固定値の登録・更新画面 CSV出力処理モーダル 入力値のバリデーション/エラー表示 BASEはフロントエンドはVue.js サーバーサイドはCakePHPを採用しています。 サーバーサイドの担当でいうとAPIの実装がメインになり、フロントエンドの担当でいうと画面UIの作成やサーバーサイドのAPIを利用してデータの登録/更新処理を実装することがメインになります。 フロントエンドを担当した流れ 今回のプロジェクトにアサインされる際、エンジニアマネージャーから提案を受けフロントエンド開発の担当も兼務する形でアサインされることになりました。 組織図上、エンジニアはサーバーサイドとフロントエンドで分かれていますが別職種の開発を担当してはいけないというわけではなく、開発する案件規模やスケジュールなども関係してきますが手を挙げれば前向きに検討してもらえます。 私はサーバーサイドエンジニアとしてBASEに入社しましたが前職でVue.jsの開発経験もあり、フロントエンド開発にも興味があります。個人的に思うところですが、ユーザーに直接に見える部分はフロントエンドの実装になりますし、実装したコードがブラウザ上で目に見えて表現される部分はモチベーションも維持しやすく好きです。 また、BASEではフロントエンド開発を担当したことがなかったので経験してみたかったというのもあります。 苦戦したところ フロントエンドのアーキテクチャの理解 具体的にいえば、単方向のデーターフローを意識することやBASEのコンポーネントライブラリであるBBQの使い方です。 既存のコードを読むのはもちろんのことですが、理解を進めるに辺り参考になったのはドキュメントの存在でした。 BASEではドキュメント共有を主にkibelaを利用して行っているのですが、フロントエンド開発の手順書が用意されておりコードと合わせてドキュメントを参考にすることで比較的早く理解を進めることができました。 BASEのコンポーネントライブラリであるBBQについては、UIコンポーネントのカタログとしてStoryBookが用意されており各コンポーネントの使用方法が書いてあるので、開発環境で試しながら理解を進めることができました。 担当してみて良かったところ デザインの開発経験 サーバーサイドの担当としてアサインされてもプロジェクトの初期フェーズにおいてUIデザインetcに関して話し合いをするタイミングはあるのですが、詳細な部分については実装を進めてみないと分からないということもありました。 今回はフロントエンドも担当することで実装を進めながら、デザイナーの方とFigmaやAbstractなどのツールを使用してコミュニケーションを取りながら開発していきデザインを決定していきました。BASEでのデザイン開発の進め方やツールの使い方を知ることができて良い経験になりました。 コミュニケーションコストの削減 サーバーサイドとフロントエンドの両方を担当していて追加したコードについては大体は分かっていたので、PMからの仕様相談を受けた際なども回答しやすかったです。また実装後に行うQAでのフィードバック対応も原因箇所の特定がしやすく普段よりも素早く対応できたと思います。 フロントエンドの仕組み フロントエンド側がどのように動作しているか?をフロントエンドエンジニアの方やドキュメントを読んでなんとなく理解していたのですが、実際に担当して実装することで「なんとなく理解していた」部分がコードレベルで明確にできて理解することができました。 最後に 実際にできあがった画面が下記になります。 ※ CSV出力処理も担当予定だったのですが、上記の苦戦した部分で想定以上に工数使ってしまい途中で別エンジニアにヘルプを出しました…。 担当が多かった開発でもあったのでリリース時の達成感は強く、SNSなどのユーザーからの反応もいつもより嬉しいものでした。 サーバーサイドとフロントエンドを交互に行き来するような開発で頭の中のスイッチングコストはありましたが、エンジニア同士でのコミュニケーションコストは減ったりもしていたので、開発する案件の規模に影響するものだと思いますが今回のプロジェクトでいうと分業していたとしても最終的に掛かる工数としては大きな差は無かったかなと思いました。 また、コードレビューに関してはフロントエンドエンジニアの方がレビューしてくれるので心強かったです。 BASEはサーバーサイドエンジニアとして入社してもフロントエンド開発できないというわけではなく、手を挙げれば開発することができます。 普段は担当しない領域を担当してみることで不明瞭な部分が明確にできたので、今後の工数見積もりの精度向上やコミュニケーションコストの削減にもつなげることも出来ると思います。 最後にはなりますが、BASEを一緒に支えてくれるエンジニアを絶賛募集中です!より良いプロダクトの開発の為に一緒に働きませんか? open.talentio.com open.talentio.com
アバター
この度は、3/26 (金) 〜 3/28 (日) にオンラインで開催された PHPerKaigi2021 にゴールドポンサーとして協賛し、また 1 名のメンバーが登壇しました。 今回は上記メンバーの他に一般聴講者として参加した 2 名のメンバーからの参加レポートをお届けします! 発表内容と補足(東口) BASE BANK 株式会社の東口 ( @hgsgtk )です。PHPerKaigi 2021 では次の 2 つのトークをしておりました。 実践ATDD 〜TDDから更に歩みを進めたソフトウェア開発へ〜 (40 分) PHPUnit 9 時代のTest Doubleの作り方 (5 分) 1. 実践ATDD 〜TDDから更に歩みを進めたソフトウェア開発へ〜 発表に用いた資料はこちらです。 発表の際には、ニコニコ生放送・Discord・Twitter での実況が行われていました。Twitter でのリアクションは以下の Togetter にまとめています。 https://togetter.com/li/1688539 資料公開後土日にも関わらずたくさん反応いただき、2021 年 3 月 28 日(日)のお昼ごろには 100以上ブックマーク いただきホットエントリー入りする反響でびっくりしました。 翌週以降も『 テスト駆動開発 』を翻訳されている和田卓人さんに直接感想をしていただいたりと、多くの方の目に触れる資料となり「頑張ってよかったな」と感慨深くなりました。 Acceptance test-driven development(ATDD: 受け入れテスト駆動開発)を切り口に、Example-driven development (ATDD, BDD, SBE 等) の歴史と実践の総まとめになっている。圧倒的な情報量の資料 / “実践ATDD 〜TDDから更に歩みを進めたソフトウェア開発へ〜 / ATDD by genba…” https://t.co/CAxmhf0L6z — Takuto Wada (@t_wada) March 31, 2021 以降、収録された発表を見ながら補足情報として入れていた話・Ask the speaker でご質問・意見交換させていただいた内容を抜粋してまとめます。 イテレーション分の受け入れテストをまとめるアイデア テストが増えた場合のテスト速度とフィードバックループについてどういうアイデアがあるかな〜という話を Ask the Speaker でしていました。その中で取り上げさせていただいたのが、「イテレーション分の受け入れテストをまとめる」アイデアです。 これは 『Specification by Example』のChapter 10. Validating frequently で紹介されているアイデアです。 A common special case of breaking long tests into smaller packs is creating the current iteration pack. This pack contains the executable specifications that are affected by the current development phase. ATDD を実践するために用いられるツールには、概ねディレクトリ分けや Tag 等で受け入れテストを分類する機能が備わっています。テスト量が増えた際には、当該イテレーションで関心を持つ受け入れテストのみを絞れるようにすると、開発中のフィードバックループの周りを維持できるかもしれません。 手動テストの扱い 手動テストも Specification に記載するという話を現場での試行錯誤の 1 つのアイデアとして紹介していました。 発表資料から引用 これについて Ask the speaker でいろいろ意見交換させていただきました。 手動テストの Specification には何を書いている? 手動でテストしたいテスト項目について記載している、自動テストでは実行がスキップされるようなイメージ スプリントレビューでデモするような動かし方を書いていたりもする 手動テスト分を毎イテレーションテスター向けのキュー追加するとかできるとよさそう 手動テストの実行結果はどうする? 現状実行結果を記録する仕組みを用意してるわけではないが、イテレーションごとの実行結果としては記録したいモチベーションはたしかに。手動テストをイテレーションごとに Issue 作るなどするアイデアがありそう おすすめの一冊『Specification by Example』 ATDD について知っている方であれば 『実践テスト駆動開発 (Object Oriented SELECTION)』 (通称 GOOS 本)の外側のフィードバックループを想起される方が多いかと思います。 個人的なおすすめとしては、GOOS 本は技術的要素へのフォーカスがメインなので、ATDD 自体の前提となるコラボレーションを重視する考え方などを知るには違う書籍で補完したほうがいいかもしれません。逆に言うと、ある程度 INPUT した上で実践書として GOOS 本を読むと、とても効果が高いと思います。 GOOS 本は 原著 が 2009 年出版ですが、ATDD や BDD のような考え方はそれよりももっと前から実践されているものです。具体的には Ward Cunningham 氏が FIT を作った 2002 年にはそれらの考え方は確認されています。その後にも、2007 年に『 Test Driven - Practical TDD and Acceptance TDD for Java Developers 』が出版されており、そこでも ATDD の考え方と実践方法について触れられています。 様々書籍がある中で個人的に「現場で実践する上で一番良かったな」と思ったのは、 『Specification by Example』 です。 www.manning.com 著者の Gojko Adzic 氏はこれ以前に 『Bridging the Communication Gap』 にて、自身の実践方法について解説しています。しかし、 『Specification by Example』 は数十の企業へのインタビューを通じた実践事例集となっており、インタビューを通じて Gojko Adzic 氏自身の考え方がアップデートされた点なども解説に含んでおり面白いです。 実際に現場で実践するにあたって困ること・気になることについて網羅的に解説されていて、大変参考になりました。 おすすめをもう一冊『Agile Testing Condensed Japanese Edition』 『Specification by Example』 は翻訳版が現在無いのですが英語に抵抗のある方であれば、全体像としてアジャイルテスト自体を抑えてから本腰を上げていただくのが良いのかなと思います。初めての一冊としてのおすすめは『 Agile Testing Condensed Japanese Edition 』です。 leanpub.com Janet GregoryとLisa Crispinによる2019年9月発行の書籍『Agile Testing Condensed』の日本語翻訳版です。アジャイルにおいてどのような考えでテストを行うべきなのか簡潔に書かれています! ソフトウェアテストを実践するに当たり、アジャイルテストという観点から現場を見直してみると、大局観をもって挑めるのでおすすめです。 2. PHPUnit 9 時代のTest Doubleの作り方 発表に用いた資料はこちらです。 PHP 発表資料中に引用したいテストコード付近で若干修正点があったので PR を出したりしておりました(当日発表 1 時間前)。 github.com Lightning Talk で Ask the Speaker の時間があるのは新鮮な体験でした。「PHPUnit のバージョンどのくらい頻繁にあげてる?」みたいな雑談を @goodoo さんとさせていただいてたり、"カンファレンスの廊下感"がありました。 スピーカーとして参加した感想 事前収録を見ながら補足する体験がよかった 事前収録良かった。収録したものを見ながら様々自分でここはこういうのが関連して〜的な補足が入れれたのが自分としては良かったのと、参加した方の感想でもそれがあって、濃密度の高い時間になったという声をいただけておりました。 事前収録のセッションを聞きながら Discord(などのチャット)で登壇者ご本人が適宜補足をしてくれた結果、 めっっっっちゃ濃厚なセッションになったな… すごかった…✨ #phperkaigi #a — うえしー (@ueshiy) March 27, 2021 オンライン + 事前収録の組み合わせならではでした。5 回くらいリテイクしてるので誰もいない部屋で一人ただ喋り続ける時間は大変だったのですが、その分当日に濃密度が上がる点がいいなと想いました。 Ask the speakerに人がたくさん オンラインになってから Ask the speaker の時間は「はてどうしたらいいかな」と困る時間を過ごすことが多かったのですが、PHPerKaigi 2021 では Discord でのボイスチャットでたくさん人がいらっしゃっていて Ask the speaker が過ごしやすかったです。 その中で得られた新たなアイデアや言語化・分析の突破口みたいなのもたくさん会話の中から出て感謝です。 トラックごとにスタッフの方々がファシリテーターとしていらっしゃるのも時間の安心感としてありがたかったです。Track A ではスタッフの @akki_megane さんや ueshiy さんが、トーク前・トーク後に会話を回してくださりとても感謝です。たまたまマイクがオンになってる気配を見て唐突に @koyhoge さんに無茶振りさせていただいたり、オフラインで休憩室でわいわい雑談する感じがあってよかったですね。 質問する身としても、「とりあえずボイスチャット入っとこ」くらいのノリで Ask the Speaker に入れたのもハードルがひくくてよかった気がします。 運営の皆様ありがとうございました 発表資料内では題材システムとして fortee を例に取らせていただきましたが、例に取るくらいめちゃめちゃ作り込まれていて、トークの自動収録システムが用意されていたりと、小並感ある感想ですが「すごいな!」と驚嘆しておりました。 ノベルティのボックスやパンフレットのクオリティも高く、自宅から参加していましたが、「カンファレンスに来た」という感覚が強い時間でした。 コミュニケーションを促進する場の設計・運営をしていただいた PHPerKaigi 2021 運営スタッフの皆様ありがとうございました! 参加レポート(小笠原) BASE に 2021 年 2 月に入社した小笠原奈々( @cureseven )です。土日ほとんどずっと聞いていたので全体を通しての感想を先にお話しします。 学生の頃からちょっとずつ各方面のカンファレンスに参加しているんですが、学生の頃は「エンジニアってどんな感じの人たちで、どんなことしてるんだろう?」の興味で行っていたので内容がさっぱりわかってなかったのですが、社会人になって、業務をしていく中で実体験を元に話が聴けるようになってきたことを実感しました。 反面、新しく聞く話も多く、まだまだ知らないことたくさんあるなあと感じました。 PHPで学ぶ、セッションの基本と応用 fortee.jp あまり体系的に学ぶ機会のない Session の概要がきちんと整理された、初心者にも優しい内容でした。 そもそも Session という言葉の使い方が曖昧で、正しくは「Cookie を使った Session 管理」。元々ステートレス(状態を持たない)なプロトコル World Wide Web を、ビジネス利用しだし、状態を持ちたくなったため生まれたのが Cookie という技術です。 Cookie は 4KB の制限があるので、Session ID というキーを引き回し、それを元にセッションストアにアクセスしデータを取得することによって状態を表現します。 最低限の知識として Cookie に属性が設定されている。 Session 管理を狙った攻撃がある。WAF ではそれを考慮しているので安全に $_SESSION を扱える。 保持された情報によってバグが起こりやすいので、Session 管理の設計は大事。本当に Session 管理すべきなのか設計を見直すことも必要かもしれない。 セッションはファイルなので、サーバの台数を増やしてスケールアウトすると参照するセッションが別れてしまうので共通のセッションストアを見るなどする ということが紹介されていました。 Cookie を使った Session の知識はかなり曖昧でした。この発表を通して用途と注意すべき事項がわかったので、今後の開発に生かすことができそうです。 PHP8になった今の時代に、PHPの「エラー」「例外」そして「Error」をおさらいしておこう fortee.jp エラーと例外とは何かを比較すると以下です。 エラー 正常な処理ができない。難しそうな状況 プログラミング的に間違っている PHP が発生させるもの 例外 事前条件、事後条件が満たされない状態になる時吐くもの 契約プログラミングにおける契約的に間違っている プログラム自体が発生させ、自身でコントロールしたいもの 改めて日本語にするのは難しいですが、感覚的にエラーと例外は自分の中では区分できているかなと思います。 このセッションでは、「エラーも例外もまとめて投げる \Throwable を闇雲に使うべきではない。」「例外を投げるにしても、捕捉してそれをどう扱いたいか明確なものを投げよ」ということを主張されていたと思います。 曖昧な throw を残すことは、何が起こるか不安な状態のままにしておくことと理解しました。 \Throwable は \Error \LogicException \RuntimeException をキャッチします。 \Error \LogicException は本番では起こりえない状況なので throw せず、本番環境に乗せる前に修正すべきです。対して、 \RuntimeException は本番ではどうしても起こるものなので、捕捉して回復する余地のある場合は回復する処理を書き、どうしようもない場合は諦めるのが良いと主張されていました。 今まで正しい理解がないまま \Throwable を書いていたのでこちらのセッションを聞いて理解が進みました。想定されるエラーと例外を考えるようにします。 自前の Exception を定義し、どういうコンテキストのエラーかをエラー名を見ることによって把握できる、という話もありました。 今私が参加しているプロジェクトではちょうど自前の Exception を作っていたのでよかったです。 レイヤーに合わせた抽象度の Error にして渡すのが良い、という話があったので、DDD の考え方は例外にも当てはめるべきということが新たな発見です。意識して書くようにしたいと思いました。 そのコード、フレームワークの外でも動きますか? fortee.jp tech.quartetcom.co.jp このセッションでは、フレームワークに依存しない書き方を実践を通して紹介されました。40 分のセッションのうちに Laravel から Symfony にフレームワーク移行するデモンストレーションが入っていて驚きでした。 フレームワークから業務ドメインのコードを独立させておくことで、後方互換性を高くすると、リプレイス・リニューアルの時にまる写ししなくて済みます。 今私が参加しているプロジェクトでは保守性を高めるために DDD で開発しているので、参考になりました。 デモンストレーションの中でほとんど変更点がなく、名前空間や view の表記方法をちょっと修正したくらいのものを別のフレームワークに持ってきており、フレームワークに依存した書き方をしなければこんなに簡単に移行できるんだと改めて DDD で開発するありがたさがわかりました。 独立パッケージにするのも賢いと思い、検討したいです。 参加レポート(炭田) Service Dev Section に所属しています炭田高輝( @tac_tanden )です。自分は 2021 年 3 月 27 日(土)〜3 月 28 日(日)の 2 日間参加しました。 PHPerKaigi は去年初めて参加して今年で 2 回目の参加です。今年は新型コロナウイルス感染症の影響でニコニコ生放送でのオンライン開催でしたが、どのセッションも熱いトークばかりでオフラインに負けず劣らずとても楽しいカンファレンスでした。この場をお借りして運営スタッフ/スピーカーの方々にお礼申し上げます。ありがとうございました! 各セッションでは、日頃開発をしているだけでは出会うことが中々難しい様々な知見にふれることができ、とても勉強になりました。自分が聴講したセッションの中から特に気になったセッション 3 つのレポートをしたいと思います。 PHPでCSVを安心して扱うために fortee.jp github.com CSV への認識が 180 度変わったセッションでした。恥ずかしながらこのセッションを聞くまでは、CSV は正規化されたお手軽なデータ形式という認識でいました。 なぜこのような認識を持っていたかというと、今まで自分が携わった開発だとサービスの管理画面からデータ入稿をするのに便利だから使う程度の事しかしてこなかったからだと思います。 そのような場面で自分が扱っていた CSV データは「信頼している人が作成した信頼性の高い」という但し書きがついた CSV データだったんだなということを痛感させられました。 CSV データを処理するエンドポイントを、悪意があるユーザを含む様々なユーザが利用する場合、入力される CSV は文字コードや改行コード 1 つとっても様々なものが想定されます。また、クラッキングしようとするような悪意があるデータも含まれる可能性もあり、それらに対して全て考慮した実装にする必要があります。 セッションでは、PHP で CSV を扱う複数の手段とともに、それらの長所/短所やベストプラクティス、様々な文字コードへの対応を解説しながらどう PHP で CSV を扱うのか知ることができともて勉強になりました。次回 CSV を使った機能を開発するときに、ライブラリの利用やその内部実装を参考にさせていただきたいなと思いました。 モックの泥沼から脱却するために、あえてDBにつないでテストしている話 fortee.jp データソースから取得したデータを整形したり何らかのロジックを適用したあとにデータを保存するといった処理のテストを行う場合、データに関連する部分以外の実装を細かなメソッドに切り出して、データに依存しないようにしてテストすることで、データ入出力まわりのテストの複雑さをさけてユニットテストを記述できると思います。 しかしこの場合、細かく切り出した複数のメソッドを協調させて使うようなメソッドのテストが最後にどうしても残ってしまいます。これらのメソッドをテストする場合、どうしても実装が複雑になってしまい、またデータへも依存するのでテスト用のデータの生成するのにもコストがかかります。 場合によっては、このようなメソッドを組み合わせたメソッドのテストは行わない戦略を取る場合もあると思いますが、自分個人としてはそのような Large サイズのテストもできるだけ実装したいと思っています。 このセッションでは、上記のようなデータにも依存し、かつ処理も複雑なテストをする場合、モックを使ったテストではなく DB につないでテストする選択をした場合のメリット/デメリットについて解説されていました。 テスト時にモックはたしかに便利ではあるのですが、どうしても自作自演のテストになってしまい「作成したモックが正しいことをどう担保するのか」がすごく難しくなってしまうと実感しています。なので、発表にあったように「実データ」を実際に用意して、特にユースケースやシナリオといった役割のクラスのメソッドに対してはテストしていくことが良いのかなと改めて考えさせられました。 ちなみに、BASE では実装者がテストで所望のデータを生成できるよう、Fabricate というライブラリを利用しています。 Fabricateの記事 devblog.thebase.in devblog.thebase.in 改めて、テストの意味と役割について考えさせられたセッションでした。 作って理解するDIコンテナ fortee.jp tadsan.fanbox.cc PHPerKaigi 最終日の trackA は DI(Dependency Injection)祭りでした。 その中でも自分がすごく勉強になったのがこちらのセッションでした。依存とは何かの初歩の部分から依存にどう立ち向かうのか、最後には DI コンテナを実装するところまで詳細に解説されていました。特に、依存とはなんなのか、なぜ DI という考え方を取り入れるべきなのかを今一度自分の中で整理することができました。 自分は DI をという概念を利用して実装はしています。ですが、DI コンテナのようなライブラリは利用しておらず、発表中は恥ずかしながら理解が追いつかない部分がありましたが、セッションの後に資料を見返してなんとか理解したつもりです。また、この発表を通じて初めて PHP-DI を知ることができました。 最後に 今回弊社は登壇者含めて計 3 名のメンバーで参加させていただき、たくさんの参加者の方々や発表にふれることができ、とても充実した時間を過ごさせていただきました。 それも実行委員長である長谷川さんをはじめ、実行委員会の皆様のおかげです。心より感謝申し上げます。 それでは、来年もまた皆様にお会いできることを楽しみにしております。最後までお読みいただきありがとうございました。
アバター
こんにちは。BASE BANK 株式会社 Dev Division にて、Engineering Manager をしている東口( @hgsgtk )です。 BASE では @budougumi0617 さんが主催となって Go のコードリーディング会を行っています。昨年は『 私がGoのソースコードを読むときのTips 』にてその様子を少し紹介いただいていました。 その後も隔週の毎月 2 回ペースでコードリーディング会を継続しています。2020 年 12 月以降は前述したブログをきっかけに興味を持っていただいた @dice_zu さんや po3rin さんにもゲスト参加いただき、ゆるゆると継続しています。 当エントリではその中で話題に上がった Go 1.16 の機能についてご紹介いたします。 TL;DR 構造体フィールドタグ json オブジェクト名の中でセミコロン( ; )が許可されました たとえば json:"hoge;hoge" のように ; が JSON オブジェクト名 に含まれる場合も json.Marshal / json.Unmarshal 可能となりました JSONの仕様を定義した RFC7159 によるとセミコロンは文字種として valid であるため 社内で開催中のコードリーディング会はゲスト大歓迎です。興味がある方は身近な社員の方か @hgsgtk にご相談ください Go 1.16 の Minor changes Go 1.16 の Release Notes にて 1.15 からの変更点について見ることが出来ます。 golang.org 私は毎回 Core library の Minor changes to the library を眺めて気になる PR のコードを読んでみるのが好きです。 その中で、コードリーディング会までネタにあげさせていただいたのが encoding/json パッケージの変更点です。 Go 1.16 における Minor changes https://golang.org/doc/go1.16#encoding/json The json struct field tags understood by Marshal, Unmarshal, and related functionality now permit semicolon characters within a JSON object name for a Go struct field. どういった内容なのか、当日参加者で 15 分程度読んだ内容を皆様にご共有いたします。 encoding/json パッケージの Minor changes 構造体フィールドタグ json オブジェクト名の中でセミコロン( ; )が許可されました。具体的には次のようなサンプルコードです。 package main import ( "encoding/json" "fmt" ) func main() { encoded := [] byte ( `{";": "World!"}` ) // セミコロンがKeyのJSON Object type MyObject struct { Hello string `json:";"` } var decoded MyObject if err := json.Unmarshal(encoded, &decoded); err != nil { fmt.Println(err) return } fmt.Printf( "%+v" , decoded) // Output: {Hello:World!} } https://play.golang.org/p/3HUq4N66AK5 変更の背景 「セミコロンが JSON オブジェクト の Key にはいっているものを扱うユースケースがあまり想像つかないね」という話をコードリーディング会ではしていました。どういった背景でこの変更が行われたのでしょうか。起点となった Issue を見てみましょう。 issue: encoding/json: does not recognise semicolon as a valid field name https://github.com/golang/go/issues/39189 この変更の背景根拠となったのは JSON 仕様を規定している RFC7159 のようです。 So pretty much we check JSON spec (RFC-7159) for validity on our "bug" and it seems to us that the spec would treat a semicolon as a normal character. Go内部実装における変更点 ここで行われた内部実装の変更は次の CL から確認できます。 https://go-review.googlesource.com/c/go/+/234818 src/encoding/json/encode.go に入った修正  isValidTag というメソッドにて tag の正当性を検証するのですが、その内部での文字種ルールに ; を追加しています。 strings.ContainsRune( "!#$%&()*+-./:;<=>?@[]^_{|}~ " , c): 余談 Go 2structured tag という提案 Go コードリーディング会の雑談のなかで Go2 のプロポーザルで structured tags というものが提案されていることを知りました。 github.com 現在の構造タグのフォーマットは文字列リテラルですが、この Issue の提案およびディスカッションの結果、具体例として次のようなコードを記述するような機能提案となっています。 package mypackage import json import sqlx type MyStruct struct { // [] 内にstruct定義する Value string [json.Rules{Name: "value" }, sqlx.Name( "value" )] PrivateKey [] byte [json.Rules{Ignore: true }] } 個人的には、文字列リテラルで記述する方法と比較した際に、記述ミス時に気が付きやすい点が嬉しいと思いました。実行時エラーや無視されてしまうのではなくユーザーがスペルミスした際にコンパイルエラーになります。 Issue 内での会話は 2020 年 1 月ごろに落ち着いていますが、「そういうアイデアがあるんだな」と頭の片隅に入れておくといいかもしれません。 おわりに かんたんにですが、encoding/json の minor changes について紹介しました。Go の内部コードリーディングのはじめの一歩として身近な標準ライブラリのちょっとした変更を追ってみると、その周辺コードもちょっとしれたりしておすすめです。 社内で開催中のコードリーディング会はゲスト大歓迎です。興味がある方は身近な社員か @hgsgtk にご相談ください。 こちらも余談ですが、BASE BANK 株式会社は絶賛採用募集中です!もし、「ちょっと興味あるな〜」とおもったら、Go 1.16 の話とか現場での Go 開発についてワイワイはなしましょう! open.talentio.com
アバター
はじめに CTOの川口 ( id:dmnlk ) です。 BASEは現在140万ショップを超え、サービスの安定性・信頼性を維持することは非常に重要になっています。 その中で去年、New Relicを本格的に導入しました。 https://newrelic.com/jp/press-release/20201217 しかしNew Relicを導入しただけで安定性が獲得できるわけではありません。得られるのは可観測性のための武器であり、その使い方を適切に学ばなければ無駄になってしまいます。 今回、New Relic社のご提案もあり社内メンバー向けにオンボーディング会を開いていただけることになりました。 オンボーディングを行うことでまずNew Relic Oneのオブザーバビリティプラットフォーム で何が出来るのか、どのように使うことでBASEシステムの可観測性を得ていくことが出来るのかを開発チームが体系的に学べることを狙うこととしました。 オンボーディングに参加して SREチームのngswです。 当日の参加者は最終的には50名近くとなりました。プロダクトに直接関わるフロントエンド、サーバサイド開発陣のほかにCSE( Corporate Solutions Engineering )チームも参加する形で、大変にぎやかな会になりました。 14:00〜17:00という長丁場の中で、個人的に特に盛り上がったなと感じたところである以下を中心に当日の雰囲気をお伝えできればと思います。 「New Relicに期待することはなにか」 New Relic でできることについて NRQL / Query builder について 「New Relicに期待することはなんですか?」 期待に胸を躍らせる面々 オンボーディング開始の直前まで、Slack上ではNew Relic社のSenior Solutions Consultant 1 の清水毅様と調整を行っておりました。 清水さんにはBASEを強くサポートいただいており、 その中でご提案いただいたのが「各開発チームの代表者を1名ずつ決めて、それぞれに『New Relicに期待すること』をそれぞれ教えていただきたい」というものでした。 当日発表内容は以下になりました。 所属チーム New Relicまたは今日のオンボーディングで期待すること ペイメント New Relic Browserを組込中。 ユーザがどのような形で <BASE> を利用しているか、解明していきたいと考えていて、 他の機能もさわっていきたい CSE 内部統制を効率的かつ有効に進めていけそうな機能を、このオンボーディングの中で見つけていきたい ショップ1 (New Relicを用いることで)開発側でも監視/モニタリングを積極的に行える土壌ができることを期待している フロントエンド1 パフォーマンス改善の足がかりとしてNew Relicを活用していきたい 安定運用を自チームを含めた全開発陣でやっていくことを ショップ2 リリースした機能/APIが期待通りのパフォーマンスがでているか、ある時点から劣化していないかということを追跡したい フロントエンド2 JavaScriptのエラートラッキングや、追いきれていないパフォーマンスのボトルネックの発見を行うためのツールとして期待しており、このオンボーディングで学んでいきたい 基盤 今、アプリから呼び出されるAPIのパフォーマンス改善を行っているところで、ログ追跡がより便利に、かつ簡便になることを期待している SRE インフラ側(たとえばSRE)と開発陣での共通の話題が、たとえばNew Relicのダッシュボードを中心になっていくようなことを期待している ネイティブアプリケーション モバイルアプリから呼んでいるAPIの負荷/ボトルネックなどの、問題の発見と追跡を行いたい BASE BANK社開発陣 マイクロサービス化していく中で、3〜4サービスまたいでログを確認しなければいけないなど、運用に苦しんでいるところがある SLOなどの設定を考えていく中で、そもそものメトリクスをどうとるか(そして投げ入れるか)などのヒントをこのオンボーディングで学んでいきたい DS New Relicをチーム内でどの様に活用できていけるのか、その下地になる知識を仕入れていきたい よかったなと感じたのは、CSEチームの「内部統制に活用したい」というものでした。 この発想を自身は持ち得なかったので、共有いただけたのは個人的な収穫のひとつでありました。 もうひとつ感じたことは「開発陣はパフォーマンスに関心がないわけではなくて、関心はあるのだけれど、追跡に必要な情報へアクセスしづらいがために、結果的に後回しになってしまっている現状がありそう」というものです。これはわれわれSREチームの至っていない点であるなと反省した次第です。 このあと清水さんより New Relic の概要説明がありときには補足も入りながら、New Relicの目指すところはなにかという内容へと進んでいきました。 紙幅の都合もありますし、より正確な内容をご自身の目と耳でご確認いただきたいと考えますので、興味を持たれた方には以下のウェビナーへの参加や過去動画の閲覧をおすすめいたします。 セミナー・ウェブセミナー | New Relic(ニューレリック) Q 内部統制でどういったところにつかえるでしょうか、たとえばDB操作の監視であるとか…… A ログベースの検知であったり、そのキーワード監視ができます。加えていうとリリースプロセスが内部統制の制約をうけるので、モニタリングをQAと連携させることもひとつの重要な観点になります Q バックエンドで稼働するバッチ処理の成功可否など、きちんと検知する仕組みはできるでしょうか A Webフレームワークの機能を利用しているものであれば、組み込むことは難しくないです 補足をするとバッチ処理などは実行と終了だけでなく、処理内容とその出力成果物の整合性など、バッチ処理の価値そのものを追っていくことも大事なことなので、いくつかの観点で複合的に考える必要がでてくるかもしれません New Relic 入門(機能紹介) 「ビジュアル化の重要性を体現的に理解している」と話題になるメンバーのプライベート機 ここからは開発メンバーから代表した数人が、一人ずつZoom画面共有機能を用いてNew Relicに触れていく形になります。「モブプログラミング」でいうドライバー役のようなものを想像いただければ間違い無いかと思います。代表者を含む参加したほとんどのメンバーはNew Relicを今日触るのが初めてと言っていい状態ですから、清水さんがナビゲータ役として位置する形になります。 統合ダッシュボードとしてのNew Relic One Add More Data New Relicにデータを投入するための言語/ツールごとのセットアップ手順がチュートリアルのような形でわかる親切設計です アカウント BASEではサブアカウントを環境ごと(Prd/STG/Dev/……)に対応する形にしました Services BASEではAppNameをリポジトリ名から引用して対応させるようにしました リポジトリAを実行しているEC2は複数台構成で稼働していますが、リポジトリ=AppNameとしているためNew Relicのデータ上ではまとめて表示されるようになっています APM導入後に活用できる各機能 Summary サンプルにしたAppNameでは、MySQLのレスポンス時間が高くなっているグラフが目に付きました Databases SummaryでみつけたMySQLのレスポンス時間が高くなっていた部分を調査していきます QueryTimeが長い部分をグラフで視覚的に確認することができます Slowest query time で sort することにより Slow queries を参照することができます MySQL の Queryそのものと、PHPのStack Traceを同時に確認することができます 「EXPLAINだ」「slowlog を grep して explain して…みたいなことをここでシュッとできる」というようなコメントがSlack上で見受けられました Workloads アカウント(BASEではサブアカウント)ごとのリソース群の健全性状態を一覧できます 自動的に組み込まれていくので手間いらずで便利そうです NRQL / Query builder がやってきた 『ある期間内にどのショップがイベントを行ってリクエストが増大したかがひと目でわかる』ことに感心する一同 個人的にもっともエキサイティングな時間がやってきました。 APMで投入されたデータはNew Relic上でNRDBとして格納され、NRQL(New Relic Query Language)を利用してさまざまな解析が可能となります。 今回のオンボーディングでは以下のNRQLを用いた可視化が披露されました。 FROM Transaction SELECT avaerage(duration) ここでの duration は属性[応答時間]です このNRQLで本番環境すべてのトランザクション平均時間が得られました FROM Transaction SELECT avaerage(duration) TIMESERIES TIMESERIES のおかげで指定期間単位ごとの集計結果を時系列化したグラフが表示されました FROM Transaction SELECT percentile(duration, 99) TIMESERIES percentile(duration, 99) は duration の99パーセンタイルを意味しています TIMESERIES により同様に時系列化したグラフを返してくれました FROM Transaction SELECT percentile(duration, 99) TIMESERIES FACET name name は属性です ここでは実行されたPHPのファイル名でした FACET はグループ化を行ってくれます 個人的には FACET がとても好きです FROM Transaction SELECT percentile(duration, 99) TIMESERIES FACET Base.shop_id LIMIT 30 SINCE 1 week ago Base.shop_id はBASEが埋め込んだ Custom Attribute になります。 Custom Attribute について詳しくは以下をご参照ください アプリケーション固有の属性を活用した性能分析 | Observability Platform - New Relic公式ブログ 意訳すると「直近1週間でBASEショップごとの応答時間99パーセンタイルで(処理時間がよりかかっているものの)上位30件の時系列グラフ」となるでしょうか FROM Transaction SELECT average(duration) TIMESERIES FACET Base.shop_id LIMIT 30 SINCE 1 week ago こちらは先のものを平均時間で置き換えたものです FROM Transaction SELECT average(duration) * count(*) TIMESERIES FACET Base.shop_id LIMIT 30 SINCE 1 week ago 先の平均時間では、各ショップのリクエスト数の多少は考慮されていませんでしたので、 average(duration) * count(*) として考慮する形に変わりました この形にかえることで、より戦略的かつ効果的な、費用対効果の向上に直結するパフォーマンス改善のために必要な、実践的なデータが得られるようになりました ここではNRQLのパワフルさを見せつけられました。 興味を持たれた方は以下をご覧ください。 Get started | New Relic Documentation NRQL Lessonsアプリケーションを使ってNRQLをマスターしよう - New Relic公式ブログ 結びに New Relicの基本的な機能について学べたオンボーディングでした。 まだ導入当初であって即効的なものを期待したわけではなかったのですが、この記事を書いている2021年03末ごろまでで以下のような成果がありました。 特定ショップのページ表示が遅くなった原因の特定 お問い合わせの原因となった不具合がどのリリースと関連するものかの特定 今後は、SRE的観点からダッシュボードを充実させていき、様々な意思決定にNew Relicを利用していきたいと考えています。 サービスの信頼性を獲得することはSREチームだけのミッションではなくバックエンド・フロントエンドエンジニア問わず期待しています。 New Relicを始めとしたツールの支援を受けながら、積極的に改善していくエンジニアを募集しています。 open.talentio.com open.talentio.com SREチームは先日求人を見直しました。 devblog.thebase.in 清水さんのことを心のなかでは 「New Relic アニキ」と呼んでいます ↩
アバター
こんにちは!! BASE株式会社 SREチーム エンジニアリングマネージャの富塚( @tomy103rider )です。 2021年3月現在、SREチームは私含め3名で、最近私は成長するサービスを一緒に支えていって頂けるSREの仲間を求めて採用などをメインに業務を行っています。 はじめに 突然ですが、皆さんのチームの求人票は誰が作っていますか?そしてそれを読んだことはありますか? 思い出してみると元々のSREチームの求人票は私と前のマネージャが考えて作ったものでした。 その内容を改めて確認すると、 見ていただいている方に現在のSREチームの思いが伝わる内容だろうか? チームのメンバー自身も迎えたい仲間のイメージができる内容だろうか? 採用に関わるCTOにもそのイメージは共有できているだろうか? などと感じる部分が出てきたため、このタイミングで見直してアップデートすることにしました。 今回はこのときの話を紹介しようと思います。 どうやって見直す? まずは今までのようにマネージャが考えて作るのではなく、SREチームとして今どういう仲間を求めているのかを認識を合わせながら自分たちで考えて見直し、その内容を最終的にCTOとすり合わせることを今回のゴールにしました。 また、話し合いを始めるにあたって、 過去や今の求人票を検討したときの経緯を見れると良さそう 過去の求人票の歴史を見れると良さそう そもそも社内にオープンに見れる状態になっていても良さそう という意見が出て、過去の歴史を残すとなるとこれはGitHub上でやってみては?ということになり、試しに見直しの過程やアップデートをGitHub上に残してみることにしました。 見直しの過程をIssueに残す! 新たに作成したRepositoryのIssueに「やっていくぞ!」とCTOがビシッとコメントを残してスタートです。 スタート直後の様子 数回の見直しMTGをやりながら、このような雰囲気で議事録的なコメントやカジュアルなコメントを割と何でもこのIssueに残していきました。 求人票のアップデートはPull Requestベースで! 最終的に1つの求人票を作るために、見直しMTGで話し合った結果の大枠を反映した内容をまず私が作成し、 Pull Requestの様子1 これをみんなでレビューしていき、途中CTOのアドバイスを貰ったりして、まずはSREチームの中で認識があった状態にしました。 Pull Requestの様子2 そして最後にCTOにレビューしてもらい・・・ Pull Requestの様子3 無事 approvedとなったことで、今回のゴールであるCTOとの認識合わせもできました。 やってみてどうだった? 折角なので最後にチームのメンバーに今回の感想をIssueのコメントに残してもらいました。 N氏コメント A氏コメント 抜粋すると 前の内容に比べてより今求めているイメージが表現できたかも! 採用についての当事者意識を持つキッカケになった! 履歴を残したことが将来の財産になりそうな期待が! というような良い感想をもってくれたようです。 まとめ チームの規模感や状況によって今回のようなやり方が適しているかというのはあるかと思いますが、私としては見直しの過程を財産として残せたこと、みんなで新たな仲間のイメージを持てたこと、とても有意義な時間にできたのではと思いました。 そして・・・ 今回のアップデート後の求人票が open.talentio.com こちらです!!! ここまでの内容すべてが前フリだったかのようになりますが・・・ 現在SREチームでは成長するサービスを一緒に支えていって頂ける仲間を募集していますので、求人票をご覧いただいて少しでも興味を持っていただけましたら、まずはカジュアルにでもお話できればと思いますので是非ご連絡ください!!!!!
アバター